Update design of account search dropdown in collection editor (#38739)

This commit is contained in:
diondiondion authored and GitHub committed 2026-04-17 15:13:18 +00:00
1 parent ea33d7fba6
commit 05a1c170c2
8 files changed
+192 -66

No files matched your search

@@ -10,8 +10,13 @@ import { AccountListItem } from 'mastodon/components/account_list_item';
import { Avatar } from 'mastodon/components/avatar';
import { Button } from 'mastodon/components/button';
import { DisplayName } from 'mastodon/components/display_name';
import { useAccountHandle } from 'mastodon/components/display_name/default';
import { EmptyState } from 'mastodon/components/empty_state';
import { FormStack, ComboboxField } from 'mastodon/components/form_fields';
import {
ListItemContent,
ListItemWrapper,
} from 'mastodon/components/list_item';
import {
Article,
ItemList,
@@ -19,6 +24,7 @@ import {
} from 'mastodon/components/scrollable_list/components';
import { useAccount } from 'mastodon/hooks/useAccount';
import { useSearchAccounts } from 'mastodon/hooks/useSearchAccounts';
import { domain } from 'mastodon/initial_state';
import {
addCollectionItem,
getCollectionItemIds,
@@ -55,25 +61,90 @@ const AddedAccountItem: React.FC<{
return <AccountListItem accountId={accountId} renderButton={renderButton} />;
};
const SuggestedAccountItem: React.FC<{ account: ApiMutedAccountJSON }> = (
props,
) => {
const account = useAccount(props.account.id);
const SuggestedAccountItem: React.FC<{ id: string }> = ({ id }) => {
const account = useAccount(id);
const handle = useAccountHandle(account, domain);
if (!account) return null;
return (
<>
<Avatar account={account} />
<DisplayName account={account} />
</>
<ListItemWrapper
className={classes.suggestion}
icon={<Avatar account={account} size={40} />}
>
<ListItemContent subtitle={handle}>
<DisplayName account={account} variant='simple' />
</ListItemContent>
</ListItemWrapper>
);
};
const renderAccountItem = (account: ApiMutedAccountJSON) => (
<SuggestedAccountItem account={account} />
<SuggestedAccountItem id={account.id} />
);
type GroupKey = 'available' | 'mustFollow' | 'disabled';
function groupSuggestions(accounts: ApiMutedAccountJSON[]) {
const { available, disabled } = Object.groupBy(accounts, (account) => {
if (getIsItemDisabled(account)) {
return 'disabled';
}
// if (account.locked && !relationship?.following) {
// return 'mustFollow';
// }
return 'available';
});
// Returning a new object ensures a fixed property order
return { available, disabled };
}
const renderGroupTitle = (groupKey: GroupKey, titleId: string) => {
if (groupKey === 'available') {
return null;
}
let title: React.ReactElement;
let description: React.ReactElement;
if (groupKey === 'mustFollow') {
title = (
<FormattedMessage
id='collections.suggestions.must_follow'
defaultMessage='Must follow first'
/>
);
description = (
<FormattedMessage
id='collections.suggestions.must_follow_desc'
defaultMessage='These accounts review all follow requests. Followers can add them to collections.'
/>
);
} else {
title = (
<FormattedMessage
id='collections.suggestions.can_not_add'
defaultMessage='Can’t be added'
/>
);
description = (
<FormattedMessage
id='collections.suggestions.can_not_add_desc'
defaultMessage='These accounts may have opted out of discovery, or they might be on a server that doesn’t support collections.'
/>
);
}
return (
<ListItemWrapper className={classes.suggestionGroup}>
<ListItemContent id={titleId} subtitle={description}>
{title}
</ListItemContent>
</ListItemWrapper>
);
};
const getItemId = (account: ApiMutedAccountJSON) => account.id;
// Disable accounts who can't be added to a collection
@@ -259,10 +330,11 @@ export const CollectionAccounts: React.FC<{
onKeyDown={handleSearchKeyDown}
disabled={hasMaxAccounts}
isLoading={isLoadingSuggestions}
items={suggestedAccounts}
items={groupSuggestions(suggestedAccounts)}
getItemId={getItemId}
getIsItemDisabled={getIsItemDisabled}
renderItem={renderAccountItem}
renderGroupTitle={renderGroupTitle}
onSelectItem={handleSelectItem}
status={
hasMaxAccounts
@@ -276,18 +276,16 @@ export const CollectionDetails: React.FC = () => {
</FormStack>
<div className={classes.stickyFooter}>
<div className={classes.actionWrapper}>
<Button type='submit'>
{id ? (
<FormattedMessage id='lists.save' defaultMessage='Save' />
) : (
<FormattedMessage
id='collections.create_collection'
defaultMessage='Create collection'
/>
)}
</Button>
</div>
<Button type='submit'>
{id ? (
<FormattedMessage id='lists.save' defaultMessage='Save' />
) : (
<FormattedMessage
id='collections.create_collection'
defaultMessage='Create collection'
/>
)}
</Button>
</div>
</form>
);
@@ -58,3 +58,19 @@
.scrollableWrapper {
margin-inline: -16px;
}
.suggestion {
padding: 4px 0;
[aria-disabled='true'] & {
opacity: 0.6;
}
}
.suggestionGroup {
padding: 4px 0;
// Undo default group styles:
font-weight: 400;
text-transform: none;
}