Allow managing collection accounts (#37812)
This commit is contained in:
1 parent
cff25c186b
commit
a644a4a762
15 files changed
+590
-75
No files matched your search
@@ -1,67 +1,363 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
|
||||
import CancelIcon from '@/material-icons/400-24px/cancel.svg?react';
|
||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
||||
import type { ApiCollectionJSON } from 'mastodon/api_types/collections';
|
||||
import { Account } from 'mastodon/components/account';
|
||||
import { Avatar } from 'mastodon/components/avatar';
|
||||
import { Button } from 'mastodon/components/button';
|
||||
import { FormStack } from 'mastodon/components/form_fields';
|
||||
import { Callout } from 'mastodon/components/callout';
|
||||
import { DisplayName } from 'mastodon/components/display_name';
|
||||
import { EmptyState } from 'mastodon/components/empty_state';
|
||||
import { FormStack, ComboboxField } from 'mastodon/components/form_fields';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { IconButton } from 'mastodon/components/icon_button';
|
||||
import ScrollableList from 'mastodon/components/scrollable_list';
|
||||
import { useSearchAccounts } from 'mastodon/features/lists/use_search_accounts';
|
||||
import {
|
||||
addCollectionItem,
|
||||
removeCollectionItem,
|
||||
} from 'mastodon/reducers/slices/collections';
|
||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||
|
||||
import type { TempCollectionState } from './state';
|
||||
import { getInitialState } from './state';
|
||||
import { getCollectionEditorState } from './state';
|
||||
import classes from './styles.module.scss';
|
||||
import { WizardStepHeader } from './wizard_step_header';
|
||||
|
||||
const MIN_ACCOUNT_COUNT = 1;
|
||||
const MAX_ACCOUNT_COUNT = 25;
|
||||
|
||||
const AddedAccountItem: React.FC<{
|
||||
accountId: string;
|
||||
isRemovable: boolean;
|
||||
onRemove: (id: string) => void;
|
||||
}> = ({ accountId, isRemovable, onRemove }) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const handleRemoveAccount = useCallback(() => {
|
||||
onRemove(accountId);
|
||||
}, [accountId, onRemove]);
|
||||
|
||||
return (
|
||||
<Account minimal key={accountId} id={accountId}>
|
||||
{isRemovable && (
|
||||
<IconButton
|
||||
title={intl.formatMessage({
|
||||
id: 'collections.remove_account',
|
||||
defaultMessage: 'Remove this account',
|
||||
})}
|
||||
icon='remove'
|
||||
iconComponent={CancelIcon}
|
||||
onClick={handleRemoveAccount}
|
||||
/>
|
||||
)}
|
||||
</Account>
|
||||
);
|
||||
};
|
||||
|
||||
interface SuggestionItem {
|
||||
id: string;
|
||||
isSelected: boolean;
|
||||
}
|
||||
|
||||
const SuggestedAccountItem: React.FC<SuggestionItem> = ({ id, isSelected }) => {
|
||||
const account = useAppSelector((state) => state.accounts.get(id));
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Avatar account={account} />
|
||||
<DisplayName account={account} />
|
||||
{isSelected && (
|
||||
<Icon
|
||||
id='checked'
|
||||
icon={CheckIcon}
|
||||
className={classes.selectedSuggestionIcon}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const renderAccountItem = (item: SuggestionItem) => (
|
||||
<SuggestedAccountItem id={item.id} isSelected={item.isSelected} />
|
||||
);
|
||||
|
||||
const getItemId = (item: SuggestionItem) => item.id;
|
||||
const getIsItemSelected = (item: SuggestionItem) => item.isSelected;
|
||||
|
||||
export const CollectionAccounts: React.FC<{
|
||||
collection?: ApiCollectionJSON | null;
|
||||
}> = ({ collection }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const history = useHistory();
|
||||
const location = useLocation<TempCollectionState>();
|
||||
const { id, initialItemIds } = getCollectionEditorState(
|
||||
collection,
|
||||
location.state,
|
||||
);
|
||||
const isEditMode = !!id;
|
||||
const collectionItems = collection?.items;
|
||||
|
||||
const { id } = getInitialState(collection, location.state);
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
// This state is only used when creating a new collection.
|
||||
// In edit mode, the collection will be updated instantly
|
||||
const [addedAccountIds, setAccountIds] = useState(initialItemIds);
|
||||
const accountIds = useMemo(
|
||||
() =>
|
||||
isEditMode
|
||||
? (collectionItems
|
||||
?.map((item) => item.account_id)
|
||||
.filter((id): id is string => !!id) ?? [])
|
||||
: addedAccountIds,
|
||||
[isEditMode, collectionItems, addedAccountIds],
|
||||
);
|
||||
|
||||
const hasMaxAccounts = accountIds.length === MAX_ACCOUNT_COUNT;
|
||||
const hasMinAccounts = accountIds.length === MIN_ACCOUNT_COUNT;
|
||||
const hasTooFewAccounts = accountIds.length < MIN_ACCOUNT_COUNT;
|
||||
const canSubmit = !hasTooFewAccounts;
|
||||
|
||||
const {
|
||||
accountIds: suggestedAccountIds,
|
||||
isLoading: isLoadingSuggestions,
|
||||
searchAccounts,
|
||||
} = useSearchAccounts();
|
||||
|
||||
const suggestedItems = suggestedAccountIds.map((id) => ({
|
||||
id,
|
||||
isSelected: accountIds.includes(id),
|
||||
}));
|
||||
|
||||
const handleSearchValueChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchValue(e.target.value);
|
||||
searchAccounts(e.target.value);
|
||||
},
|
||||
[searchAccounts],
|
||||
);
|
||||
|
||||
const handleSearchKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const toggleAccountItem = useCallback((item: SuggestionItem) => {
|
||||
setAccountIds((ids) =>
|
||||
ids.includes(item.id)
|
||||
? ids.filter((id) => id !== item.id)
|
||||
: [...ids, item.id],
|
||||
);
|
||||
}, []);
|
||||
|
||||
const instantRemoveAccountItem = useCallback(
|
||||
(accountId: string) => {
|
||||
const itemId = collectionItems?.find(
|
||||
(item) => item.account_id === accountId,
|
||||
)?.id;
|
||||
if (itemId && id) {
|
||||
if (
|
||||
window.confirm(
|
||||
intl.formatMessage({
|
||||
id: 'collections.confirm_account_removal',
|
||||
defaultMessage:
|
||||
'Are you sure you want to remove this account from this collection?',
|
||||
}),
|
||||
)
|
||||
) {
|
||||
void dispatch(removeCollectionItem({ collectionId: id, itemId }));
|
||||
}
|
||||
}
|
||||
},
|
||||
[collectionItems, dispatch, id, intl],
|
||||
);
|
||||
|
||||
const instantToggleAccountItem = useCallback(
|
||||
(item: SuggestionItem) => {
|
||||
if (accountIds.includes(item.id)) {
|
||||
instantRemoveAccountItem(item.id);
|
||||
} else {
|
||||
if (id) {
|
||||
void dispatch(
|
||||
addCollectionItem({ collectionId: id, accountId: item.id }),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
[accountIds, dispatch, id, instantRemoveAccountItem],
|
||||
);
|
||||
|
||||
const handleRemoveAccountItem = useCallback(
|
||||
(accountId: string) => {
|
||||
if (isEditMode) {
|
||||
instantRemoveAccountItem(accountId);
|
||||
} else {
|
||||
setAccountIds((ids) => ids.filter((id) => id !== accountId));
|
||||
}
|
||||
},
|
||||
[isEditMode, instantRemoveAccountItem],
|
||||
);
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
(e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!canSubmit) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!id) {
|
||||
history.push(`/collections/new/details`);
|
||||
history.push(`/collections/new/details`, {
|
||||
account_ids: accountIds,
|
||||
});
|
||||
}
|
||||
},
|
||||
[id, history],
|
||||
[canSubmit, id, history, accountIds],
|
||||
);
|
||||
|
||||
return (
|
||||
<FormStack as='form' onSubmit={handleSubmit}>
|
||||
{!id && (
|
||||
<WizardStepHeader
|
||||
step={1}
|
||||
title={
|
||||
<form onSubmit={handleSubmit} className={classes.form}>
|
||||
<FormStack className={classes.formFieldStack}>
|
||||
{!id && (
|
||||
<WizardStepHeader
|
||||
step={1}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='collections.create.accounts_title'
|
||||
defaultMessage='Who will you feature in this collection?'
|
||||
/>
|
||||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id='collections.create.accounts_subtitle'
|
||||
defaultMessage='Only accounts you follow who have opted into discovery can be added.'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<ComboboxField
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='collections.create.accounts_title'
|
||||
defaultMessage='Who will you feature in this collection?'
|
||||
id='collections.search_accounts_label'
|
||||
defaultMessage='Search for accounts to add…'
|
||||
/>
|
||||
}
|
||||
description={
|
||||
<FormattedMessage
|
||||
id='collections.create.accounts_subtitle'
|
||||
defaultMessage='Only accounts you follow who have opted into discovery can be added.'
|
||||
/>
|
||||
hint={
|
||||
hasMaxAccounts ? (
|
||||
<FormattedMessage
|
||||
id='collections.search_accounts_max_reached'
|
||||
defaultMessage='You have added the maximum number of accounts'
|
||||
/>
|
||||
) : undefined
|
||||
}
|
||||
value={hasMaxAccounts ? '' : searchValue}
|
||||
onChange={handleSearchValueChange}
|
||||
onKeyDown={handleSearchKeyDown}
|
||||
disabled={hasMaxAccounts}
|
||||
isLoading={isLoadingSuggestions}
|
||||
items={suggestedItems}
|
||||
getItemId={getItemId}
|
||||
getIsItemSelected={getIsItemSelected}
|
||||
renderItem={renderAccountItem}
|
||||
onSelectItem={
|
||||
isEditMode ? instantToggleAccountItem : toggleAccountItem
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<div className='actions'>
|
||||
<Button type='submit'>
|
||||
{id ? (
|
||||
<FormattedMessage id='lists.save' defaultMessage='Save' />
|
||||
) : (
|
||||
|
||||
{hasMinAccounts && (
|
||||
<Callout>
|
||||
<FormattedMessage
|
||||
id='collections.continue'
|
||||
defaultMessage='Continue'
|
||||
id='collections.hints.can_not_remove_more_accounts'
|
||||
defaultMessage='Collections must contain at least {count, plural, one {# account} other {# accounts}}. Removing more accounts is not possible.'
|
||||
values={{ count: MIN_ACCOUNT_COUNT }}
|
||||
/>
|
||||
</Callout>
|
||||
)}
|
||||
|
||||
<div className={classes.scrollableWrapper}>
|
||||
<ScrollableList
|
||||
scrollKey='collection-items'
|
||||
className={classes.scrollableInner}
|
||||
emptyMessage={
|
||||
<EmptyState
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='collections.accounts.empty_title'
|
||||
defaultMessage='This collection is empty'
|
||||
/>
|
||||
}
|
||||
message={
|
||||
<FormattedMessage
|
||||
id='collections.accounts.empty_description'
|
||||
defaultMessage='Add up to {count} accounts you follow'
|
||||
values={{
|
||||
count: MAX_ACCOUNT_COUNT,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
// TODO: Re-add `bindToDocument={!multiColumn}`
|
||||
>
|
||||
{accountIds.map((accountId) => (
|
||||
<AddedAccountItem
|
||||
key={accountId}
|
||||
accountId={accountId}
|
||||
isRemovable={!isEditMode || !hasMinAccounts}
|
||||
onRemove={handleRemoveAccountItem}
|
||||
/>
|
||||
))}
|
||||
</ScrollableList>
|
||||
</div>
|
||||
</FormStack>
|
||||
{!isEditMode && (
|
||||
<div className={classes.stickyFooter}>
|
||||
{hasTooFewAccounts ? (
|
||||
<Callout icon={false} className={classes.submitDisabledCallout}>
|
||||
<FormattedMessage
|
||||
id='collections.hints.add_more_accounts'
|
||||
defaultMessage='Add at least {count, plural, one {# account} other {# accounts}} to continue'
|
||||
values={{ count: MIN_ACCOUNT_COUNT }}
|
||||
/>
|
||||
</Callout>
|
||||
) : (
|
||||
<div className={classes.actionWrapper}>
|
||||
<FormattedMessage
|
||||
id='collections.hints.accounts_counter'
|
||||
defaultMessage='{count} / {max} accounts'
|
||||
values={{ count: accountIds.length, max: MAX_ACCOUNT_COUNT }}
|
||||
>
|
||||
{(text) => (
|
||||
<div className={classes.itemCountReadout}>{text}</div>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
{canSubmit && (
|
||||
<Button type='submit'>
|
||||
{id ? (
|
||||
<FormattedMessage id='lists.save' defaultMessage='Save' />
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id='collections.continue'
|
||||
defaultMessage='Continue'
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</FormStack>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
};
|
||||
@@ -16,7 +16,8 @@ import { updateCollection } from 'mastodon/reducers/slices/collections';
|
||||
import { useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import type { TempCollectionState } from './state';
|
||||
import { getInitialState } from './state';
|
||||
import { getCollectionEditorState } from './state';
|
||||
import classes from './styles.module.scss';
|
||||
import { WizardStepHeader } from './wizard_step_header';
|
||||
|
||||
export const CollectionDetails: React.FC<{
|
||||
@@ -26,10 +27,8 @@ export const CollectionDetails: React.FC<{
|
||||
const history = useHistory();
|
||||
const location = useLocation<TempCollectionState>();
|
||||
|
||||
const { id, initialName, initialDescription, initialTopic } = getInitialState(
|
||||
collection,
|
||||
location.state,
|
||||
);
|
||||
const { id, initialName, initialDescription, initialTopic, initialItemIds } =
|
||||
getCollectionEditorState(collection, location.state);
|
||||
|
||||
const [name, setName] = useState(initialName);
|
||||
const [description, setDescription] = useState(initialDescription);
|
||||
@@ -76,13 +75,14 @@ export const CollectionDetails: React.FC<{
|
||||
name,
|
||||
description,
|
||||
tag_name: topic || null,
|
||||
account_ids: initialItemIds,
|
||||
};
|
||||
|
||||
history.replace('/collections/new', payload);
|
||||
history.push('/collections/new/settings', payload);
|
||||
}
|
||||
},
|
||||
[id, dispatch, name, description, topic, history],
|
||||
[id, name, description, topic, dispatch, history, initialItemIds],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -155,7 +155,7 @@ export const CollectionDetails: React.FC<{
|
||||
maxLength={40}
|
||||
/>
|
||||
|
||||
<div className='actions'>
|
||||
<div className={classes.actionWrapper}>
|
||||
<Button type='submit'>
|
||||
{id ? (
|
||||
<FormattedMessage id='lists.save' defaultMessage='Save' />
|
||||
|
||||
@@ -25,7 +25,8 @@ import {
|
||||
import { useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import type { TempCollectionState } from './state';
|
||||
import { getInitialState } from './state';
|
||||
import { getCollectionEditorState } from './state';
|
||||
import classes from './styles.module.scss';
|
||||
import { WizardStepHeader } from './wizard_step_header';
|
||||
|
||||
export const CollectionSettings: React.FC<{
|
||||
@@ -35,8 +36,8 @@ export const CollectionSettings: React.FC<{
|
||||
const history = useHistory();
|
||||
const location = useLocation<TempCollectionState>();
|
||||
|
||||
const { id, initialDiscoverable, initialSensitive, ...temporaryState } =
|
||||
getInitialState(collection, location.state);
|
||||
const { id, initialDiscoverable, initialSensitive, ...editorState } =
|
||||
getCollectionEditorState(collection, location.state);
|
||||
|
||||
const [discoverable, setDiscoverable] = useState(initialDiscoverable);
|
||||
const [sensitive, setSensitive] = useState(initialSensitive);
|
||||
@@ -71,13 +72,14 @@ export const CollectionSettings: React.FC<{
|
||||
});
|
||||
} else {
|
||||
const payload: ApiCreateCollectionPayload = {
|
||||
name: temporaryState.initialName,
|
||||
description: temporaryState.initialDescription,
|
||||
name: editorState.initialName,
|
||||
description: editorState.initialDescription,
|
||||
discoverable,
|
||||
sensitive,
|
||||
account_ids: editorState.initialItemIds,
|
||||
};
|
||||
if (temporaryState.initialTopic) {
|
||||
payload.tag_name = temporaryState.initialTopic;
|
||||
if (editorState.initialTopic) {
|
||||
payload.tag_name = editorState.initialTopic;
|
||||
}
|
||||
|
||||
void dispatch(
|
||||
@@ -94,7 +96,7 @@ export const CollectionSettings: React.FC<{
|
||||
});
|
||||
}
|
||||
},
|
||||
[id, discoverable, sensitive, dispatch, history, temporaryState],
|
||||
[id, discoverable, sensitive, dispatch, history, editorState],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -180,7 +182,7 @@ export const CollectionSettings: React.FC<{
|
||||
/>
|
||||
</Fieldset>
|
||||
|
||||
<div className='actions'>
|
||||
<div className={classes.actionWrapper}>
|
||||
<Button type='submit'>
|
||||
{id ? (
|
||||
<FormattedMessage id='lists.save' defaultMessage='Save' />
|
||||
|
||||
@@ -15,7 +15,7 @@ export type TempCollectionState =
|
||||
* Resolve initial editor state. Temporary location state
|
||||
* trumps stored data, otherwise initial values are returned.
|
||||
*/
|
||||
export function getInitialState(
|
||||
export function getCollectionEditorState(
|
||||
collection: ApiCollectionJSON | null | undefined,
|
||||
locationState: TempCollectionState,
|
||||
) {
|
||||
@@ -27,10 +27,19 @@ export function getInitialState(
|
||||
language = '',
|
||||
discoverable = true,
|
||||
sensitive = false,
|
||||
items,
|
||||
} = collection ?? {};
|
||||
|
||||
const collectionItemIds =
|
||||
items?.map((item) => item.account_id).filter(onlyExistingIds) ?? [];
|
||||
|
||||
const initialItemIds = (
|
||||
locationState?.account_ids ?? collectionItemIds
|
||||
).filter(onlyExistingIds);
|
||||
|
||||
return {
|
||||
id,
|
||||
initialItemIds,
|
||||
initialName: locationState?.name ?? name,
|
||||
initialDescription: locationState?.description ?? description,
|
||||
initialTopic: locationState?.tag_name ?? tag?.name ?? '',
|
||||
@@ -39,3 +48,5 @@ export function getInitialState(
|
||||
initialSensitive: locationState?.sensitive ?? sensitive,
|
||||
};
|
||||
}
|
||||
|
||||
const onlyExistingIds = (id?: string): id is string => !!id;
|
||||
@@ -13,3 +13,76 @@
|
||||
font-size: 15px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* Make form stretch full height of the column */
|
||||
.form {
|
||||
--bottom-spacing: 0;
|
||||
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
padding-bottom: var(--bottom-spacing);
|
||||
|
||||
@media (width < 760px) {
|
||||
--bottom-spacing: var(--mobile-bottom-nav-height);
|
||||
}
|
||||
}
|
||||
|
||||
.selectedSuggestionIcon {
|
||||
box-sizing: border-box;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-left: auto;
|
||||
padding: 2px;
|
||||
border-radius: 100%;
|
||||
color: var(--color-text-on-brand-base);
|
||||
background: var(--color-bg-brand-base);
|
||||
|
||||
[data-highlighted='true'] & {
|
||||
color: var(--color-bg-brand-base);
|
||||
background: var(--color-text-on-brand-base);
|
||||
}
|
||||
}
|
||||
|
||||
.formFieldStack {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.scrollableWrapper {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
margin-inline: -8px;
|
||||
}
|
||||
|
||||
.scrollableInner {
|
||||
margin-inline: -8px;
|
||||
}
|
||||
|
||||
.submitDisabledCallout {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.stickyFooter {
|
||||
position: sticky;
|
||||
bottom: var(--bottom-spacing);
|
||||
padding: 16px;
|
||||
background-image: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
var(--color-bg-primary) 32px
|
||||
);
|
||||
}
|
||||
|
||||
.itemCountReadout {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.actionWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: min-content;
|
||||
min-width: 240px;
|
||||
margin-inline: auto;
|
||||
gap: 8px;
|
||||
}
|
||||
@@ -165,10 +165,11 @@ const ListMembers: React.FC<{
|
||||
const [mode, setMode] = useState<Mode>('remove');
|
||||
|
||||
const {
|
||||
accountIds: searchAccountIds = [],
|
||||
accountIds: searchAccountIds,
|
||||
isLoading: loadingSearchResults,
|
||||
searchAccounts: handleSearch,
|
||||
} = useSearchAccounts({
|
||||
resetOnInputClear: false,
|
||||
onSettled: (value) => {
|
||||
if (value.trim().length === 0) {
|
||||
setSearching(false);
|
||||
|
||||
@@ -8,13 +8,15 @@ import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
|
||||
import { useAppDispatch } from 'mastodon/store';
|
||||
|
||||
export function useSearchAccounts({
|
||||
resetOnInputClear = true,
|
||||
onSettled,
|
||||
}: {
|
||||
onSettled?: (value: string) => void;
|
||||
resetOnInputClear?: boolean;
|
||||
} = {}) {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const [accountIds, setAccountIds] = useState<string[]>();
|
||||
const [accountIds, setAccountIds] = useState<string[]>([]);
|
||||
const [loadingState, setLoadingState] = useState<
|
||||
'idle' | 'loading' | 'error'
|
||||
>('idle');
|
||||
@@ -29,6 +31,9 @@ export function useSearchAccounts({
|
||||
|
||||
if (value.trim().length === 0) {
|
||||
onSettled?.('');
|
||||
if (resetOnInputClear) {
|
||||
setAccountIds([]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user