import { useCallback, useMemo } from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import { Link } from 'react-router-dom'; import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react'; import { blockAccount, unblockAccount, muteAccount, unmuteAccount, followAccountSuccess, unpinAccount, pinAccount, } from 'mastodon/actions/accounts'; import { showAlertForError } from 'mastodon/actions/alerts'; import { openModal } from 'mastodon/actions/modal'; import { initMuteModal } from 'mastodon/actions/mutes'; import { apiFollowAccount } from 'mastodon/api/accounts'; import { Avatar } from 'mastodon/components/avatar'; import { Button } from 'mastodon/components/button'; import { FollowersCounter } from 'mastodon/components/counters'; import { DisplayName } from 'mastodon/components/display_name'; import { Dropdown } from 'mastodon/components/dropdown_menu'; import { FollowButton } from 'mastodon/components/follow_button'; import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; import { ShortNumber } from 'mastodon/components/short_number'; import { Skeleton } from 'mastodon/components/skeleton'; import { VerifiedBadge } from 'mastodon/components/verified_badge'; import { useIdentity } from 'mastodon/identity_context'; import { me } from 'mastodon/initial_state'; import type { MenuItem } from 'mastodon/models/dropdown_menu'; import { useAppSelector, useAppDispatch } from 'mastodon/store'; const messages = defineMessages({ follow: { id: 'account.follow', defaultMessage: 'Follow' }, unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, cancel_follow_request: { id: 'account.cancel_follow_request', defaultMessage: 'Withdraw follow request', }, unblock: { id: 'account.unblock_short', defaultMessage: 'Unblock' }, unmute: { id: 'account.unmute_short', defaultMessage: 'Unmute' }, mute_notifications: { id: 'account.mute_notifications_short', defaultMessage: 'Mute notifications', }, unmute_notifications: { id: 'account.unmute_notifications_short', defaultMessage: 'Unmute notifications', }, mute: { id: 'account.mute_short', defaultMessage: 'Mute' }, block: { id: 'account.block_short', defaultMessage: 'Block' }, 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', }, }); interface AccountProps { size?: number; id: string; hidden?: boolean; minimal?: boolean; defaultAction?: 'block' | 'mute'; withBio?: boolean; } export const Account: React.FC = ({ id, size = 46, hidden, minimal, defaultAction, withBio, }) => { const intl = useIntl(); const { signedIn } = useIdentity(); const account = useAppSelector((state) => state.accounts.get(id)); const relationship = useAppSelector((state) => state.relationships.get(id)); const dispatch = useAppDispatch(); const accountUrl = account?.url; const isRemote = account?.acct !== account?.username; const handleBlock = useCallback(() => { if (relationship?.blocking) { dispatch(unblockAccount(id)); } else { dispatch(blockAccount(id)); } }, [dispatch, id, relationship]); const handleMute = useCallback(() => { if (relationship?.muting) { dispatch(unmuteAccount(id)); } else { dispatch(initMuteModal(account)); } }, [dispatch, id, account, relationship]); const menu = useMemo(() => { let arr: MenuItem[] = []; if (defaultAction === 'mute') { const handleMuteNotifications = () => { 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') { if (isRemote && accountUrl) { arr.push({ text: intl.formatMessage(messages.openOriginalPage), href: accountUrl, }); } if (signedIn) { const handleAddToLists = () => { const openAddToListModal = () => { dispatch( openModal({ modalType: 'LIST_ADDER', modalProps: { accountId: id, }, }), ); }; if (relationship?.following || relationship?.requested || id === me) { openAddToListModal(); } else { dispatch( openModal({ modalType: 'CONFIRM_FOLLOW_TO_LIST', modalProps: { accountId: id, onConfirm: () => { apiFollowAccount(id) .then((relationship) => { dispatch( followAccountSuccess({ relationship, alreadyFollowing: false, }), ); openAddToListModal(); }) .catch((err: unknown) => { dispatch(showAlertForError(err)); }); }, }, }), ); } }; arr.push({ text: intl.formatMessage(messages.addToLists), action: handleAddToLists, }); if (id !== me && (relationship?.following || relationship?.requested)) { const handleEndorseToggle = () => { if (relationship.endorsed) { dispatch(unpinAccount(id)); } else { dispatch(pinAccount(id)); } }; arr.push({ text: intl.formatMessage( // Defined in features/account_timeline/components/account_header.tsx relationship.endorsed ? { id: 'account.unendorse' } : { id: 'account.endorse' }, ), action: handleEndorseToggle, }); } } } return arr; }, [ dispatch, intl, id, accountUrl, relationship, defaultAction, isRemote, signedIn, ]); if (hidden) { return ( <> {account?.display_name} {account?.username} ); } let button: React.ReactNode, dropdown: React.ReactNode; if (menu.length > 0) { dropdown = ( ); } if (defaultAction === 'block') { button = (