import React, {memo} from 'react' import {AppBskyActorDefs} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' import {HITSLOP_20} from '#/lib/constants' import {makeProfileLink} from '#/lib/routes/links' import {shareUrl} from '#/lib/sharing' import {toShareUrl} from '#/lib/strings/url-helpers' import {logger} from '#/logger' import {Shadow} from '#/state/cache/types' import {useModalControls} from '#/state/modals' import { RQKEY as profileQueryKey, useProfileBlockMutationQueue, useProfileFollowMutationQueue, useProfileMuteMutationQueue, } from '#/state/queries/profile' import {useSession} from '#/state/session' import {EventStopper} from '#/view/com/util/EventStopper' import * as Toast from '#/view/com/util/Toast' import {Button, ButtonIcon} from '#/components/Button' import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox' import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid' import {Flag_Stroke2_Corner0_Rounded as Flag} from '#/components/icons/Flag' import {ListSparkle_Stroke2_Corner0_Rounded as List} from '#/components/icons/ListSparkle' import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute' import {PeopleRemove2_Stroke2_Corner0_Rounded as UserMinus} from '#/components/icons/PeopleRemove2' import { PersonCheck_Stroke2_Corner0_Rounded as PersonCheck, PersonX_Stroke2_Corner0_Rounded as PersonX, } from '#/components/icons/Person' import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as Unmute} from '#/components/icons/Speaker' import * as Menu from '#/components/Menu' import * as Prompt from '#/components/Prompt' import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog' let ProfileMenu = ({ profile, }: { profile: Shadow }): React.ReactNode => { const {_} = useLingui() const {currentAccount, hasSession} = useSession() const {openModal} = useModalControls() const reportDialogControl = useReportDialogControl() const queryClient = useQueryClient() const isSelf = currentAccount?.did === profile.did const isFollowing = profile.viewer?.following const isBlocked = profile.viewer?.blocking || profile.viewer?.blockedBy const isFollowingBlockedAccount = isFollowing && isBlocked const isLabelerAndNotBlocked = !!profile.associated?.labeler && !isBlocked const [queueMute, queueUnmute] = useProfileMuteMutationQueue(profile) const [queueBlock, queueUnblock] = useProfileBlockMutationQueue(profile) const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue( profile, 'ProfileMenu', ) const blockPromptControl = Prompt.usePromptControl() const loggedOutWarningPromptControl = Prompt.usePromptControl() const showLoggedOutWarning = React.useMemo(() => { return ( profile.did !== currentAccount?.did && !!profile.labels?.find(label => label.val === '!no-unauthenticated') ) }, [currentAccount, profile]) const invalidateProfileQuery = React.useCallback(() => { queryClient.invalidateQueries({ queryKey: profileQueryKey(profile.did), }) }, [queryClient, profile.did]) const onPressShare = React.useCallback(() => { shareUrl(toShareUrl(makeProfileLink(profile))) }, [profile]) const onPressAddRemoveLists = React.useCallback(() => { openModal({ name: 'user-add-remove-lists', subject: profile.did, handle: profile.handle, displayName: profile.displayName || profile.handle, onAdd: invalidateProfileQuery, onRemove: invalidateProfileQuery, }) }, [profile, openModal, invalidateProfileQuery]) const onPressMuteAccount = React.useCallback(async () => { if (profile.viewer?.muted) { try { await queueUnmute() Toast.show(_(msg`Account unmuted`)) } catch (e: any) { if (e?.name !== 'AbortError') { logger.error('Failed to unmute account', {message: e}) Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark') } } } else { try { await queueMute() Toast.show(_(msg`Account muted`)) } catch (e: any) { if (e?.name !== 'AbortError') { logger.error('Failed to mute account', {message: e}) Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark') } } } }, [profile.viewer?.muted, queueUnmute, _, queueMute]) const blockAccount = React.useCallback(async () => { if (profile.viewer?.blocking) { try { await queueUnblock() Toast.show(_(msg`Account unblocked`)) } catch (e: any) { if (e?.name !== 'AbortError') { logger.error('Failed to unblock account', {message: e}) Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark') } } } else { try { await queueBlock() Toast.show(_(msg`Account blocked`)) } catch (e: any) { if (e?.name !== 'AbortError') { logger.error('Failed to block account', {message: e}) Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark') } } } }, [profile.viewer?.blocking, _, queueUnblock, queueBlock]) const onPressFollowAccount = React.useCallback(async () => { try { await queueFollow() Toast.show(_(msg`Account followed`)) } catch (e: any) { if (e?.name !== 'AbortError') { logger.error('Failed to follow account', {message: e}) Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark') } } }, [_, queueFollow]) const onPressUnfollowAccount = React.useCallback(async () => { try { await queueUnfollow() Toast.show(_(msg`Account unfollowed`)) } catch (e: any) { if (e?.name !== 'AbortError') { logger.error('Failed to unfollow account', {message: e}) Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark') } } }, [_, queueUnfollow]) const onPressReportAccount = React.useCallback(() => { reportDialogControl.open() }, [reportDialogControl]) return ( {({props}) => { return ( ) }} { if (showLoggedOutWarning) { loggedOutWarningPromptControl.open() } else { onPressShare() } }}> Share {hasSession && ( <> {!isSelf && ( <> {(isLabelerAndNotBlocked || isFollowingBlockedAccount) && ( {isFollowing ? ( Unfollow Account ) : ( Follow Account )} )} )} Add to Lists {!isSelf && ( <> {!profile.viewer?.blocking && !profile.viewer?.mutedByList && ( {profile.viewer?.muted ? ( Unmute Account ) : ( Mute Account )} )} {!profile.viewer?.blockingByList && ( blockPromptControl.open()}> {profile.viewer?.blocking ? ( Unblock Account ) : ( Block Account )} )} Report Account )} )} ) } ProfileMenu = memo(ProfileMenu) export {ProfileMenu}