Fix handling of Escape key

This commit is contained in:
Claire 2025-03-13 12:24:27 +01:00
parent 01aa93b794
commit 83697d18e7

View File

@ -9,7 +9,10 @@ import { submitAccountNote } from 'mastodon/actions/account_notes';
import { useAppDispatch, useAppSelector } from 'mastodon/store'; import { useAppDispatch, useAppSelector } from 'mastodon/store';
const messages = defineMessages({ const messages = defineMessages({
placeholder: { id: 'account_note.placeholder', defaultMessage: 'Click to add a note' }, placeholder: {
id: 'account_note.placeholder',
defaultMessage: 'Click to add a note',
},
}); });
const InlineAlert: React.FC<{ show: boolean }> = ({ show }) => { const InlineAlert: React.FC<{ show: boolean }> = ({ show }) => {
@ -26,8 +29,15 @@ const InlineAlert: React.FC<{ show: boolean }> = ({ show }) => {
}, [show, setMountMessage]); }, [show, setMountMessage]);
return ( return (
<span aria-live='polite' role='status' className='inline-alert' style={{ opacity: show ? 1 : 0 }}> <span
{mountMessage && <FormattedMessage id='generic.saved' defaultMessage='Saved' />} aria-live='polite'
role='status'
className='inline-alert'
style={{ opacity: show ? 1 : 0 }}
>
{mountMessage && (
<FormattedMessage id='generic.saved' defaultMessage='Saved' />
)}
</span> </span>
); );
}; };
@ -39,7 +49,9 @@ interface Props {
const InnerAccountNote: React.FC<Props> = ({ accountId }) => { const InnerAccountNote: React.FC<Props> = ({ accountId }) => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const initialValue = useAppSelector(state => state.relationships.get(accountId)?.get('note')); const initialValue = useAppSelector((state) =>
state.relationships.get(accountId)?.get('note'),
);
const [value, setValue] = useState<string>(initialValue ?? ''); const [value, setValue] = useState<string>(initialValue ?? '');
const [saved, setSaved] = useState(false); const [saved, setSaved] = useState(false);
@ -60,14 +72,17 @@ const InnerAccountNote: React.FC<Props> = ({ accountId }) => {
dirtyRef.current = initialValue !== undefined && initialValue !== value; dirtyRef.current = initialValue !== undefined && initialValue !== value;
}, [initialValue, value]); }, [initialValue, value]);
const onSave = useCallback((value: string) => { const onSave = useCallback(
void dispatch(submitAccountNote({ accountId, note: value })); (value: string) => {
void dispatch(submitAccountNote({ accountId, note: value }));
setSaved(true); setSaved(true);
setTimeout(() => { setTimeout(() => {
setSaved(false); setSaved(false);
}, 2000); }, 2000);
}, [accountId, dispatch, setSaved]); },
[accountId, dispatch, setSaved],
);
// Save changes on unmount // Save changes on unmount
useEffect(() => { useEffect(() => {
@ -76,33 +91,43 @@ const InnerAccountNote: React.FC<Props> = ({ accountId }) => {
}; };
}, [onSave]); }, [onSave]);
const handleChange = useCallback<ChangeEventHandler<HTMLTextAreaElement>>((e) => { const handleChange = useCallback<ChangeEventHandler<HTMLTextAreaElement>>(
setValue(e.target.value); (e) => {
}, [setValue]); setValue(e.target.value);
},
[setValue],
);
const handleKeyDown = useCallback<KeyboardEventHandler<HTMLTextAreaElement>>((e) => { const handleKeyDown = useCallback<KeyboardEventHandler<HTMLTextAreaElement>>(
if (e.key === 'Escape') { (e) => {
e.preventDefault(); if (e.key === 'Escape') {
e.preventDefault();
setValue(initialValue ?? ''); setValue(initialValue ?? '');
e.currentTarget.blur(); e.currentTarget.blur();
} else if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { } else if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
e.preventDefault(); e.preventDefault();
onSave(valueRef.current); onSave(value);
e.currentTarget.blur(); e.currentTarget.blur();
} }
}, [onSave, initialValue]); },
[onSave, initialValue, value, setValue],
);
const handleBlur = useCallback(() => { const handleBlur = useCallback(() => {
if (dirtyRef.current) onSave(valueRef.current); if (dirtyRef.current) onSave(value);
}, [onSave]); }, [onSave, value]);
return ( return (
<div className='account__header__account-note'> <div className='account__header__account-note'>
<label htmlFor={`account-note-${accountId}`}> <label htmlFor={`account-note-${accountId}`}>
<FormattedMessage id='account.account_note_header' defaultMessage='Personal note' /> <InlineAlert show={saved} /> <FormattedMessage
id='account.account_note_header'
defaultMessage='Personal note'
/>{' '}
<InlineAlert show={saved} />
</label> </label>
<Textarea <Textarea
@ -119,4 +144,6 @@ const InnerAccountNote: React.FC<Props> = ({ accountId }) => {
); );
}; };
export const AccountNote: React.FC<Props> = ({ accountId }) => (<InnerAccountNote accountId={accountId} key={`account-note-{accountId}`} />); export const AccountNote: React.FC<Props> = ({ accountId }) => (
<InnerAccountNote accountId={accountId} key={`account-note-{accountId}`} />
);