Add dropdown to lists of accounts in web UI (#34391)

This commit is contained in:
Eugen Rochko 2025-04-10 16:02:52 +02:00 committed by GitHub
parent de19af3650
commit 5d817a758d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,4 +1,4 @@
import { useCallback } from 'react'; import { useCallback, useMemo } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
@ -12,6 +12,7 @@ import {
muteAccount, muteAccount,
unmuteAccount, unmuteAccount,
} from 'mastodon/actions/accounts'; } from 'mastodon/actions/accounts';
import { openModal } from 'mastodon/actions/modal';
import { initMuteModal } from 'mastodon/actions/mutes'; import { initMuteModal } from 'mastodon/actions/mutes';
import { Avatar } from 'mastodon/components/avatar'; import { Avatar } from 'mastodon/components/avatar';
import { Button } from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
@ -23,7 +24,7 @@ import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
import { ShortNumber } from 'mastodon/components/short_number'; import { ShortNumber } from 'mastodon/components/short_number';
import { Skeleton } from 'mastodon/components/skeleton'; import { Skeleton } from 'mastodon/components/skeleton';
import { VerifiedBadge } from 'mastodon/components/verified_badge'; import { VerifiedBadge } from 'mastodon/components/verified_badge';
import { me } from 'mastodon/initial_state'; import type { MenuItem } from 'mastodon/models/dropdown_menu';
import { useAppSelector, useAppDispatch } from 'mastodon/store'; import { useAppSelector, useAppDispatch } from 'mastodon/store';
const messages = defineMessages({ const messages = defineMessages({
@ -46,6 +47,14 @@ const messages = defineMessages({
mute: { id: 'account.mute_short', defaultMessage: 'Mute' }, mute: { id: 'account.mute_short', defaultMessage: 'Mute' },
block: { id: 'account.block_short', defaultMessage: 'Block' }, block: { id: 'account.block_short', defaultMessage: 'Block' },
more: { id: 'status.more', defaultMessage: 'More' }, more: { id: 'status.more', defaultMessage: 'More' },
addToLists: {
id: 'account.add_or_remove_from_list',
defaultMessage: 'Add or Remove from lists',
},
openOriginalPage: {
id: 'account.open_original_page',
defaultMessage: 'Open original page',
},
}); });
export const Account: React.FC<{ export const Account: React.FC<{
@ -60,6 +69,7 @@ export const Account: React.FC<{
const account = useAppSelector((state) => state.accounts.get(id)); const account = useAppSelector((state) => state.accounts.get(id));
const relationship = useAppSelector((state) => state.relationships.get(id)); const relationship = useAppSelector((state) => state.relationships.get(id));
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const accountUrl = account?.url;
const handleBlock = useCallback(() => { const handleBlock = useCallback(() => {
if (relationship?.blocking) { if (relationship?.blocking) {
@ -77,13 +87,62 @@ export const Account: React.FC<{
} }
}, [dispatch, id, account, relationship]); }, [dispatch, id, account, relationship]);
const handleMuteNotifications = useCallback(() => { const menu = useMemo(() => {
dispatch(muteAccount(id, true)); let arr: MenuItem[] = [];
}, [dispatch, id]);
const handleUnmuteNotifications = useCallback(() => { if (defaultAction === 'mute') {
dispatch(muteAccount(id, false)); const handleMuteNotifications = () => {
}, [dispatch, id]); dispatch(muteAccount(id, true));
};
const handleUnmuteNotifications = () => {
dispatch(muteAccount(id, false));
};
arr = [
{
text: intl.formatMessage(
relationship?.muting_notifications
? messages.unmute_notifications
: messages.mute_notifications,
),
action: relationship?.muting_notifications
? handleUnmuteNotifications
: handleMuteNotifications,
},
];
} else if (defaultAction !== 'block') {
const handleAddToLists = () => {
dispatch(
openModal({
modalType: 'LIST_ADDER',
modalProps: {
accountId: id,
},
}),
);
};
arr = [
{
text: intl.formatMessage(messages.addToLists),
action: handleAddToLists,
},
];
if (accountUrl) {
arr.unshift(
{
text: intl.formatMessage(messages.openOriginalPage),
href: accountUrl,
},
null,
);
}
}
return arr;
}, [dispatch, intl, id, accountUrl, relationship, defaultAction]);
if (hidden) { if (hidden) {
return ( return (
@ -94,68 +153,42 @@ export const Account: React.FC<{
); );
} }
let buttons; let button: React.ReactNode, dropdown: React.ReactNode;
if (account && account.id !== me && relationship) { if (menu.length > 0) {
const { requested, blocking, muting } = relationship; dropdown = (
<Dropdown
if (requested) { items={menu}
buttons = <FollowButton accountId={id} />; icon='ellipsis-h'
} else if (blocking) { iconComponent={MoreHorizIcon}
buttons = ( title={intl.formatMessage(messages.more)}
<Button />
text={intl.formatMessage(messages.unblock)} );
onClick={handleBlock}
/>
);
} else if (muting) {
const menu = [
{
text: intl.formatMessage(
relationship.muting_notifications
? messages.unmute_notifications
: messages.mute_notifications,
),
action: relationship.muting_notifications
? handleUnmuteNotifications
: handleMuteNotifications,
},
];
buttons = (
<>
<Dropdown
items={menu}
icon='ellipsis-h'
iconComponent={MoreHorizIcon}
title={intl.formatMessage(messages.more)}
/>
<Button
text={intl.formatMessage(messages.unmute)}
onClick={handleMute}
/>
</>
);
} else if (defaultAction === 'mute') {
buttons = (
<Button text={intl.formatMessage(messages.mute)} onClick={handleMute} />
);
} else if (defaultAction === 'block') {
buttons = (
<Button
text={intl.formatMessage(messages.block)}
onClick={handleBlock}
/>
);
} else {
buttons = <FollowButton accountId={id} />;
}
} else {
buttons = <FollowButton accountId={id} />;
} }
let muteTimeRemaining; if (defaultAction === 'block') {
button = (
<Button
text={intl.formatMessage(
relationship?.blocking ? messages.unblock : messages.block,
)}
onClick={handleBlock}
/>
);
} else if (defaultAction === 'mute') {
button = (
<Button
text={intl.formatMessage(
relationship?.muting ? messages.unmute : messages.mute,
)}
onClick={handleMute}
/>
);
} else {
button = <FollowButton accountId={id} />;
}
let muteTimeRemaining: React.ReactNode;
if (account?.mute_expires_at) { if (account?.mute_expires_at) {
muteTimeRemaining = ( muteTimeRemaining = (
@ -165,7 +198,7 @@ export const Account: React.FC<{
); );
} }
let verification; let verification: React.ReactNode;
const firstVerifiedField = account?.fields.find((item) => !!item.verified_at); const firstVerifiedField = account?.fields.find((item) => !!item.verified_at);
@ -211,7 +244,12 @@ export const Account: React.FC<{
</div> </div>
</Link> </Link>
{!minimal && <div className='account__relationship'>{buttons}</div>} {!minimal && (
<div className='account__relationship'>
{dropdown}
{button}
</div>
)}
</div> </div>
{account && {account &&