import React, {memo} from 'react' import {TouchableOpacity} from 'react-native' import {AppBskyActorDefs} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {useQueryClient} from '@tanstack/react-query' import * as Toast from 'view/com/util/Toast' import {EventStopper} from 'view/com/util/EventStopper' import {useSession} from 'state/session' import * as Menu from '#/components/Menu' import {useTheme} from '#/alf' import {usePalette} from 'lib/hooks/usePalette' import {HITSLOP_10} from 'lib/constants' import {shareUrl} from 'lib/sharing' import {toShareUrl} from 'lib/strings/url-helpers' import {makeProfileLink} from 'lib/routes/links' import {useAnalytics} from 'lib/analytics/analytics' import {useModalControls} from 'state/modals' import { RQKEY as profileQueryKey, useProfileBlockMutationQueue, useProfileFollowMutationQueue, useProfileMuteMutationQueue, } from 'state/queries/profile' import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox' import {ListSparkle_Stroke2_Corner0_Rounded as List} from '#/components/icons/ListSparkle' import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute' import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as Unmute} from '#/components/icons/Speaker' import {Flag_Stroke2_Corner0_Rounded as Flag} from '#/components/icons/Flag' import {PersonCheck_Stroke2_Corner0_Rounded as PersonCheck} from '#/components/icons/PersonCheck' import {PersonX_Stroke2_Corner0_Rounded as PersonX} from '#/components/icons/PersonX' import {PeopleRemove2_Stroke2_Corner0_Rounded as UserMinus} from '#/components/icons/PeopleRemove2' import {logger} from '#/logger' import {Shadow} from 'state/cache/types' import * as Prompt from '#/components/Prompt' let ProfileMenu = ({ profile, }: { profile: Shadow }): React.ReactNode => { const {_} = useLingui() const {currentAccount, hasSession} = useSession() const t = useTheme() // TODO ALF this const pal = usePalette('default') const {track} = useAnalytics() const {openModal} = useModalControls() const queryClient = useQueryClient() const isSelf = currentAccount?.did === profile.did const [queueMute, queueUnmute] = useProfileMuteMutationQueue(profile) const [queueBlock, queueUnblock] = useProfileBlockMutationQueue(profile) const [, queueUnfollow] = useProfileFollowMutationQueue(profile) const blockPromptControl = Prompt.usePromptControl() const invalidateProfileQuery = React.useCallback(() => { queryClient.invalidateQueries({ queryKey: profileQueryKey(profile.did), }) }, [queryClient, profile.did]) const onPressShare = React.useCallback(() => { track('ProfileHeader:ShareButtonClicked') shareUrl(toShareUrl(makeProfileLink(profile))) }, [track, profile]) const onPressAddRemoveLists = React.useCallback(() => { track('ProfileHeader:AddToListsButtonClicked') openModal({ name: 'user-add-remove-lists', subject: profile.did, handle: profile.handle, displayName: profile.displayName || profile.handle, onAdd: invalidateProfileQuery, onRemove: invalidateProfileQuery, }) }, [track, profile, openModal, invalidateProfileQuery]) const onPressMuteAccount = React.useCallback(async () => { if (profile.viewer?.muted) { track('ProfileHeader:UnmuteAccountButtonClicked') 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()}`)) } } } else { track('ProfileHeader:MuteAccountButtonClicked') 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()}`)) } } } }, [profile.viewer?.muted, track, queueUnmute, _, queueMute]) const blockAccount = React.useCallback(async () => { if (profile.viewer?.blocking) { track('ProfileHeader:UnblockAccountButtonClicked') 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()}`)) } } } else { track('ProfileHeader:BlockAccountButtonClicked') 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()}`)) } } } }, [profile.viewer?.blocking, track, _, queueUnblock, queueBlock]) const onPressUnfollowAccount = React.useCallback(async () => { track('ProfileHeader:UnfollowButtonClicked') 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()}`)) } } }, [_, queueUnfollow, track]) const onPressReportAccount = React.useCallback(() => { track('ProfileHeader:ReportAccountButtonClicked') openModal({ name: 'report', did: profile.did, }) }, [track, openModal, profile]) return ( {({props}) => { return ( ) }} Share {hasSession && ( <> Add to Lists {!isSelf && ( <> {profile.viewer?.following && (profile.viewer.blocking || profile.viewer.blockedBy) && ( Unfollow Account )} {!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}