diff --git a/app/soapbox/actions/accounts.js b/app/soapbox/actions/accounts.js
index b93f0e599..88306c2ad 100644
--- a/app/soapbox/actions/accounts.js
+++ b/app/soapbox/actions/accounts.js
@@ -1050,10 +1050,10 @@ export function fetchPinnedAccountsFail(id, error) {
};
}
-export function accountSearch(params, cancelToken) {
+export function accountSearch(params, signal) {
return (dispatch, getState) => {
dispatch({ type: ACCOUNT_SEARCH_REQUEST, params });
- return api(getState).get('/api/v1/accounts/search', { params, cancelToken }).then(({ data: accounts }) => {
+ return api(getState).get('/api/v1/accounts/search', { params, signal }).then(({ data: accounts }) => {
dispatch(importFetchedAccounts(accounts));
dispatch({ type: ACCOUNT_SEARCH_SUCCESS, accounts });
return accounts;
diff --git a/app/soapbox/components/autosuggest_account_input.js b/app/soapbox/components/autosuggest_account_input.js
deleted file mode 100644
index fd93f52a9..000000000
--- a/app/soapbox/components/autosuggest_account_input.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import { CancelToken } from 'axios';
-import { OrderedSet as ImmutableOrderedSet } from 'immutable';
-import { throttle } from 'lodash';
-import PropTypes from 'prop-types';
-import React from 'react';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { connect } from 'react-redux';
-
-import { accountSearch } from 'soapbox/actions/accounts';
-
-import AutosuggestInput from './autosuggest_input';
-
-const noOp = () => {};
-
-export default @connect()
-class AutosuggestAccountInput extends ImmutablePureComponent {
-
- static propTypes = {
- dispatch: PropTypes.func.isRequired,
- onChange: PropTypes.func.isRequired,
- onSelected: PropTypes.func.isRequired,
- value: PropTypes.string.isRequired,
- limit: PropTypes.number.isRequired,
- }
-
- static defaultProps = {
- value: '',
- limit: 4,
- }
-
- state = {
- accountIds: ImmutableOrderedSet(),
- }
-
- source = CancelToken.source();
-
- refreshCancelToken = () => {
- this.source.cancel();
- this.source = CancelToken.source();
- return this.source;
- }
-
- clearResults = () => {
- this.setState({ accountIds: ImmutableOrderedSet() });
- }
-
- handleAccountSearch = throttle(q => {
- const { dispatch, limit } = this.props;
- const source = this.refreshCancelToken();
-
- const params = { q, limit, resolve: false };
-
- dispatch(accountSearch(params, source.token))
- .then(accounts => {
- const accountIds = accounts.map(account => account.id);
- this.setState({ accountIds: ImmutableOrderedSet(accountIds) });
- })
- .catch(noOp);
-
- }, 900, { leading: true, trailing: true })
-
- handleChange = e => {
- this.handleAccountSearch(e.target.value);
- this.props.onChange(e);
- }
-
- handleSelected = (tokenStart, lastToken, accountId) => {
- this.props.onSelected(accountId);
- }
-
- componentDidUpdate(prevProps) {
- if (this.props.value === '' && prevProps.value !== '') {
- this.clearResults();
- }
- }
-
- render() {
- const { intl, value, onChange, ...rest } = this.props;
- const { accountIds } = this.state;
-
- return (
-
- );
- }
-
-}
diff --git a/app/soapbox/components/autosuggest_account_input.tsx b/app/soapbox/components/autosuggest_account_input.tsx
new file mode 100644
index 000000000..d437e79fc
--- /dev/null
+++ b/app/soapbox/components/autosuggest_account_input.tsx
@@ -0,0 +1,86 @@
+import { OrderedSet as ImmutableOrderedSet } from 'immutable';
+import { throttle } from 'lodash';
+import React, { useState, useRef, useCallback, useEffect } from 'react';
+
+import { accountSearch } from 'soapbox/actions/accounts';
+import AutosuggestInput from 'soapbox/components/autosuggest_input';
+import { useAppDispatch } from 'soapbox/hooks';
+
+import type { Menu } from 'soapbox/components/dropdown_menu';
+
+const noOp = () => {};
+
+interface IAutosuggestAccountInput {
+ onChange: React.ChangeEventHandler,
+ onSelected: (accountId: string) => void,
+ value: string,
+ limit?: number,
+ className?: string,
+ autoSelect?: boolean,
+ menu?: Menu,
+ onKeyDown?: React.KeyboardEventHandler,
+}
+
+const AutosuggestAccountInput: React.FC = ({
+ onChange,
+ onSelected,
+ value = '',
+ limit = 4,
+ ...rest
+}) => {
+ const dispatch = useAppDispatch();
+ const [accountIds, setAccountIds] = useState(ImmutableOrderedSet());
+ const controller = useRef(new AbortController());
+
+ const refreshCancelToken = () => {
+ controller.current.abort();
+ controller.current = new AbortController();
+ };
+
+ const clearResults = () => {
+ setAccountIds(ImmutableOrderedSet());
+ };
+
+ const handleAccountSearch = useCallback(throttle(q => {
+ const params = { q, limit, resolve: false };
+
+ dispatch(accountSearch(params, controller.current.signal))
+ .then((accounts: { id: string }[]) => {
+ const accountIds = accounts.map(account => account.id);
+ setAccountIds(ImmutableOrderedSet(accountIds));
+ })
+ .catch(noOp);
+
+ }, 900, { leading: true, trailing: true }), [limit]);
+
+ const handleChange: React.ChangeEventHandler = e => {
+ refreshCancelToken();
+ handleAccountSearch(e.target.value);
+ onChange(e);
+ };
+
+ const handleSelected = (_tokenStart: string, _lastToken: string, accountId: string) => {
+ onSelected(accountId);
+ };
+
+ useEffect(() => {
+ if (value === '') {
+ clearResults();
+ }
+ }, [value]);
+
+ return (
+
+ );
+};
+
+export default AutosuggestAccountInput;
diff --git a/app/soapbox/features/compose/components/search.tsx b/app/soapbox/features/compose/components/search.tsx
index 401001fc9..d68f82bc2 100644
--- a/app/soapbox/features/compose/components/search.tsx
+++ b/app/soapbox/features/compose/components/search.tsx
@@ -21,7 +21,7 @@ const messages = defineMessages({
action: { id: 'search.action', defaultMessage: 'Search for “{query}”' },
});
-function redirectToAccount(accountId: number, routerHistory: any) {
+function redirectToAccount(accountId: string, routerHistory: any) {
return (_dispatch: any, getState: () => ImmutableMap) => {
const acct = getState().getIn(['accounts', accountId, 'acct']);
@@ -97,7 +97,7 @@ const Search = (props: ISearch) => {
dispatch(showSearch());
};
- const handleSelected = (accountId: number) => {
+ const handleSelected = (accountId: string) => {
dispatch(clearSearch());
dispatch(redirectToAccount(accountId, history));
};