Convert AccountNote to TypeScript

This commit is contained in:
Claire 2025-03-13 12:17:26 +01:00
parent ae4152a56a
commit 01aa93b794
2 changed files with 28 additions and 34 deletions

View File

@ -1,4 +1,4 @@
import PropTypes from 'prop-types'; import type { ChangeEventHandler, KeyboardEventHandler } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
@ -12,14 +12,16 @@ 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 = ({ show }) => { const InlineAlert: React.FC<{ show: boolean }> = ({ show }) => {
const [mountMessage, setMountMessage] = useState(false); const [mountMessage, setMountMessage] = useState(false);
useEffect(() => { useEffect(() => {
if (show) { if (show) {
setMountMessage(true); setMountMessage(true);
} else { } else {
setTimeout(() => setMountMessage(false), 200); setTimeout(() => {
setMountMessage(false);
}, 200);
} }
}, [show, setMountMessage]); }, [show, setMountMessage]);
@ -30,19 +32,19 @@ const InlineAlert = ({ show }) => {
); );
}; };
InlineAlert.propTypes = { interface Props {
show: PropTypes.bool, accountId: string;
}; }
const InnerAccountNote = ({ 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(initialValue); const [value, setValue] = useState<string>(initialValue ?? '');
const [saved, setSaved] = useState(false); const [saved, setSaved] = useState(false);
// We need to access the value on unmount // We need to access the value on unmount
const valueRef = useRef(value); const valueRef = useRef<string>(value);
const dirtyRef = useRef(false); const dirtyRef = useRef(false);
// Keep the valueRef in sync with the state // Keep the valueRef in sync with the state
@ -51,18 +53,20 @@ const InnerAccountNote = ({ accountId }) => {
}, [value]); }, [value]);
useEffect(() => { useEffect(() => {
if (initialValue !== valueRef.current) setValue(initialValue); if (initialValue !== valueRef.current) setValue(initialValue ?? '');
}, [initialValue, setValue]); }, [initialValue, setValue]);
useEffect(() => { useEffect(() => {
dirtyRef.current = initialValue !== value; dirtyRef.current = initialValue !== undefined && initialValue !== value;
}, [initialValue, value]); }, [initialValue, value]);
const onSave = useCallback((value) => { const onSave = useCallback((value: string) => {
dispatch(submitAccountNote({ accountId, note: value })); void dispatch(submitAccountNote({ accountId, note: value }));
setSaved(true); setSaved(true);
setTimeout(() => setSaved(false), 2000); setTimeout(() => {
setSaved(false);
}, 2000);
}, [accountId, dispatch, setSaved]); }, [accountId, dispatch, setSaved]);
// Save changes on unmount // Save changes on unmount
@ -72,22 +76,22 @@ const InnerAccountNote = ({ accountId }) => {
}; };
}, [onSave]); }, [onSave]);
const handleChange = useCallback((e) => { const handleChange = useCallback<ChangeEventHandler<HTMLTextAreaElement>>((e) => {
setValue(e.target.value); setValue(e.target.value);
}, [setValue]); }, [setValue]);
const handleKeyDown = useCallback((e) => { const handleKeyDown = useCallback<KeyboardEventHandler<HTMLTextAreaElement>>((e) => {
if (e.keyCode === 27) { if (e.key === 'Escape') {
e.preventDefault(); e.preventDefault();
setValue(initialValue); setValue(initialValue ?? '');
e.target.blur(); e.currentTarget.blur();
} else if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { } else if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
e.preventDefault(); e.preventDefault();
onSave(valueRef.current); onSave(valueRef.current);
e.target.blur(); e.currentTarget.blur();
} }
}, [onSave, initialValue]); }, [onSave, initialValue]);
@ -104,7 +108,7 @@ const InnerAccountNote = ({ accountId }) => {
<Textarea <Textarea
id={`account-note-${accountId}`} id={`account-note-${accountId}`}
className='account__header__account-note__content' className='account__header__account-note__content'
disabled={initialValue === null || value === null} disabled={initialValue === undefined}
placeholder={intl.formatMessage(messages.placeholder)} placeholder={intl.formatMessage(messages.placeholder)}
value={value || ''} value={value || ''}
onChange={handleChange} onChange={handleChange}
@ -115,14 +119,4 @@ const InnerAccountNote = ({ accountId }) => {
); );
}; };
InnerAccountNote.propTypes = { export const AccountNote: React.FC<Props> = ({ accountId }) => (<InnerAccountNote accountId={accountId} key={`account-note-{accountId}`} />);
accountId: PropTypes.string,
};
const AccountNote = ({ accountId }) => (<InnerAccountNote accountId={accountId} key={`account-note-{accountId}`} />);
AccountNote.propTypes = {
accountId: PropTypes.string,
};
export default AccountNote;

View File

@ -42,7 +42,7 @@ import { IconButton } from 'mastodon/components/icon_button';
import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import { ShortNumber } from 'mastodon/components/short_number'; import { ShortNumber } from 'mastodon/components/short_number';
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container'; import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
import AccountNote from 'mastodon/features/account/components/account_note'; import { AccountNote } from 'mastodon/features/account/components/account_note';
import { DomainPill } from 'mastodon/features/account/components/domain_pill'; import { DomainPill } from 'mastodon/features/account/components/domain_pill';
import FollowRequestNoteContainer from 'mastodon/features/account/containers/follow_request_note_container'; import FollowRequestNoteContainer from 'mastodon/features/account/containers/follow_request_note_container';
import { useLinks } from 'mastodon/hooks/useLinks'; import { useLinks } from 'mastodon/hooks/useLinks';