Merge branch 'bluesky-social:main' into patch-3
This commit is contained in:
		
						commit
						3ead08ab26
					
				
					 53 changed files with 1575 additions and 1382 deletions
				
			
		|  | @ -7,7 +7,6 @@ import { | |||
| } from 'react-native' | ||||
| import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' | ||||
| import {useNavigation} from '@react-navigation/native' | ||||
| import {useQueryClient} from '@tanstack/react-query' | ||||
| import { | ||||
|   AppBskyActorDefs, | ||||
|   ModerationOpts, | ||||
|  | @ -17,7 +16,7 @@ import { | |||
| import {Trans, msg} from '@lingui/macro' | ||||
| import {useLingui} from '@lingui/react' | ||||
| import {NavigationProp} from 'lib/routes/types' | ||||
| import {isNative, isWeb} from 'platform/detection' | ||||
| import {isNative} from 'platform/detection' | ||||
| import {BlurView} from '../util/BlurView' | ||||
| import * as Toast from '../util/Toast' | ||||
| import {LoadingPlaceholder} from '../util/LoadingPlaceholder' | ||||
|  | @ -28,14 +27,11 @@ import {UserAvatar} from '../util/UserAvatar' | |||
| import {UserBanner} from '../util/UserBanner' | ||||
| import {ProfileHeaderAlerts} from '../util/moderation/ProfileHeaderAlerts' | ||||
| import {formatCount} from '../util/numeric/format' | ||||
| import {NativeDropdown, DropdownItem} from '../util/forms/NativeDropdown' | ||||
| import {Link} from '../util/Link' | ||||
| import {ProfileHeaderSuggestedFollows} from './ProfileHeaderSuggestedFollows' | ||||
| import {useModalControls} from '#/state/modals' | ||||
| import {useLightboxControls, ProfileImageLightbox} from '#/state/lightbox' | ||||
| import { | ||||
|   RQKEY as profileQueryKey, | ||||
|   useProfileMuteMutationQueue, | ||||
|   useProfileBlockMutationQueue, | ||||
|   useProfileFollowMutationQueue, | ||||
| } from '#/state/queries/profile' | ||||
|  | @ -46,9 +42,7 @@ import {BACK_HITSLOP} from 'lib/constants' | |||
| import {isInvalidHandle, sanitizeHandle} from 'lib/strings/handles' | ||||
| import {makeProfileLink} from 'lib/routes/links' | ||||
| import {pluralize} from 'lib/strings/helpers' | ||||
| import {toShareUrl} from 'lib/strings/url-helpers' | ||||
| import {sanitizeDisplayName} from 'lib/strings/display-names' | ||||
| import {shareUrl} from 'lib/sharing' | ||||
| import {s, colors} from 'lib/styles' | ||||
| import {logger} from '#/logger' | ||||
| import {useSession} from '#/state/session' | ||||
|  | @ -57,6 +51,8 @@ import {useRequireAuth} from '#/state/session' | |||
| import {LabelInfo} from '../util/moderation/LabelInfo' | ||||
| import {useProfileShadow} from 'state/cache/profile-shadow' | ||||
| import {atoms as a} from '#/alf' | ||||
| import {ProfileMenu} from 'view/com/profile/ProfileMenu' | ||||
| import * as Prompt from '#/components/Prompt' | ||||
| 
 | ||||
| let ProfileHeaderLoading = (_props: {}): React.ReactNode => { | ||||
|   const pal = usePalette('default') | ||||
|  | @ -108,20 +104,13 @@ let ProfileHeader = ({ | |||
|   const {isDesktop} = useWebMediaQueries() | ||||
|   const [showSuggestedFollows, setShowSuggestedFollows] = React.useState(false) | ||||
|   const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile) | ||||
|   const [queueMute, queueUnmute] = useProfileMuteMutationQueue(profile) | ||||
|   const [queueBlock, queueUnblock] = useProfileBlockMutationQueue(profile) | ||||
|   const queryClient = useQueryClient() | ||||
|   const [__, queueUnblock] = useProfileBlockMutationQueue(profile) | ||||
|   const unblockPromptControl = Prompt.usePromptControl() | ||||
|   const moderation = useMemo( | ||||
|     () => moderateProfile(profile, moderationOpts), | ||||
|     [profile, moderationOpts], | ||||
|   ) | ||||
| 
 | ||||
|   const invalidateProfileQuery = React.useCallback(() => { | ||||
|     queryClient.invalidateQueries({ | ||||
|       queryKey: profileQueryKey(profile.did), | ||||
|     }) | ||||
|   }, [queryClient, profile.did]) | ||||
| 
 | ||||
|   const onPressBack = React.useCallback(() => { | ||||
|     if (navigation.canGoBack()) { | ||||
|       navigation.goBack() | ||||
|  | @ -189,204 +178,23 @@ let ProfileHeader = ({ | |||
|     }) | ||||
|   }, [track, openModal, profile]) | ||||
| 
 | ||||
|   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 () => { | ||||
|     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()}`)) | ||||
|       } | ||||
|     } | ||||
|   }, [track, queueMute, _]) | ||||
| 
 | ||||
|   const onPressUnmuteAccount = React.useCallback(async () => { | ||||
|     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()}`)) | ||||
|       } | ||||
|     } | ||||
|   }, [track, queueUnmute, _]) | ||||
| 
 | ||||
|   const onPressBlockAccount = React.useCallback(async () => { | ||||
|     track('ProfileHeader:BlockAccountButtonClicked') | ||||
|     openModal({ | ||||
|       name: 'confirm', | ||||
|       title: _(msg`Block Account`), | ||||
|       message: _( | ||||
|         msg`Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`, | ||||
|       ), | ||||
|       onPressConfirm: async () => { | ||||
|         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()}`)) | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|     }) | ||||
|   }, [track, queueBlock, openModal, _]) | ||||
| 
 | ||||
|   const onPressUnblockAccount = React.useCallback(async () => { | ||||
|   const unblockAccount = React.useCallback(async () => { | ||||
|     track('ProfileHeader:UnblockAccountButtonClicked') | ||||
|     openModal({ | ||||
|       name: 'confirm', | ||||
|       title: _(msg`Unblock Account`), | ||||
|       message: _( | ||||
|         msg`The account will be able to interact with you after unblocking.`, | ||||
|       ), | ||||
|       onPressConfirm: async () => { | ||||
|         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()}`)) | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|     }) | ||||
|   }, [track, queueUnblock, openModal, _]) | ||||
| 
 | ||||
|   const onPressReportAccount = React.useCallback(() => { | ||||
|     track('ProfileHeader:ReportAccountButtonClicked') | ||||
|     openModal({ | ||||
|       name: 'report', | ||||
|       did: profile.did, | ||||
|     }) | ||||
|   }, [track, openModal, profile]) | ||||
|     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()}`)) | ||||
|       } | ||||
|     } | ||||
|   }, [_, queueUnblock, track]) | ||||
| 
 | ||||
|   const isMe = React.useMemo( | ||||
|     () => currentAccount?.did === profile.did, | ||||
|     [currentAccount, profile], | ||||
|   ) | ||||
|   const dropdownItems: DropdownItem[] = React.useMemo(() => { | ||||
|     let items: DropdownItem[] = [ | ||||
|       { | ||||
|         testID: 'profileHeaderDropdownShareBtn', | ||||
|         label: isWeb ? _(msg`Copy link to profile`) : _(msg`Share`), | ||||
|         onPress: onPressShare, | ||||
|         icon: { | ||||
|           ios: { | ||||
|             name: 'square.and.arrow.up', | ||||
|           }, | ||||
|           android: 'ic_menu_share', | ||||
|           web: 'share', | ||||
|         }, | ||||
|       }, | ||||
|     ] | ||||
|     if (hasSession) { | ||||
|       items.push({label: 'separator'}) | ||||
|       items.push({ | ||||
|         testID: 'profileHeaderDropdownListAddRemoveBtn', | ||||
|         label: _(msg`Add to Lists`), | ||||
|         onPress: onPressAddRemoveLists, | ||||
|         icon: { | ||||
|           ios: { | ||||
|             name: 'list.bullet', | ||||
|           }, | ||||
|           android: 'ic_menu_add', | ||||
|           web: 'list', | ||||
|         }, | ||||
|       }) | ||||
|       if (!isMe) { | ||||
|         if (!profile.viewer?.blocking) { | ||||
|           if (!profile.viewer?.mutedByList) { | ||||
|             items.push({ | ||||
|               testID: 'profileHeaderDropdownMuteBtn', | ||||
|               label: profile.viewer?.muted | ||||
|                 ? _(msg`Unmute Account`) | ||||
|                 : _(msg`Mute Account`), | ||||
|               onPress: profile.viewer?.muted | ||||
|                 ? onPressUnmuteAccount | ||||
|                 : onPressMuteAccount, | ||||
|               icon: { | ||||
|                 ios: { | ||||
|                   name: 'speaker.slash', | ||||
|                 }, | ||||
|                 android: 'ic_lock_silent_mode', | ||||
|                 web: 'comment-slash', | ||||
|               }, | ||||
|             }) | ||||
|           } | ||||
|         } | ||||
|         if (!profile.viewer?.blockingByList) { | ||||
|           items.push({ | ||||
|             testID: 'profileHeaderDropdownBlockBtn', | ||||
|             label: profile.viewer?.blocking | ||||
|               ? _(msg`Unblock Account`) | ||||
|               : _(msg`Block Account`), | ||||
|             onPress: profile.viewer?.blocking | ||||
|               ? onPressUnblockAccount | ||||
|               : onPressBlockAccount, | ||||
|             icon: { | ||||
|               ios: { | ||||
|                 name: 'person.fill.xmark', | ||||
|               }, | ||||
|               android: 'ic_menu_close_clear_cancel', | ||||
|               web: 'user-slash', | ||||
|             }, | ||||
|           }) | ||||
|         } | ||||
|         items.push({ | ||||
|           testID: 'profileHeaderDropdownReportBtn', | ||||
|           label: _(msg`Report Account`), | ||||
|           onPress: onPressReportAccount, | ||||
|           icon: { | ||||
|             ios: { | ||||
|               name: 'exclamationmark.triangle', | ||||
|             }, | ||||
|             android: 'ic_menu_report_image', | ||||
|             web: 'circle-exclamation', | ||||
|           }, | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|     return items | ||||
|   }, [ | ||||
|     isMe, | ||||
|     hasSession, | ||||
|     profile.viewer?.muted, | ||||
|     profile.viewer?.mutedByList, | ||||
|     profile.viewer?.blocking, | ||||
|     profile.viewer?.blockingByList, | ||||
|     onPressShare, | ||||
|     onPressUnmuteAccount, | ||||
|     onPressMuteAccount, | ||||
|     onPressUnblockAccount, | ||||
|     onPressBlockAccount, | ||||
|     onPressReportAccount, | ||||
|     onPressAddRemoveLists, | ||||
|     _, | ||||
|   ]) | ||||
| 
 | ||||
|   const blockHide = | ||||
|     !isMe && (profile.viewer?.blocking || profile.viewer?.blockedBy) | ||||
|  | @ -427,7 +235,7 @@ let ProfileHeader = ({ | |||
|             profile.viewer?.blockingByList ? null : ( | ||||
|               <TouchableOpacity | ||||
|                 testID="unblockBtn" | ||||
|                 onPress={onPressUnblockAccount} | ||||
|                 onPress={() => unblockPromptControl.open()} | ||||
|                 style={[styles.btn, styles.mainBtn, pal.btn]} | ||||
|                 accessibilityRole="button" | ||||
|                 accessibilityLabel={_(msg`Unblock`)} | ||||
|  | @ -516,17 +324,7 @@ let ProfileHeader = ({ | |||
|               )} | ||||
|             </> | ||||
|           ) : null} | ||||
|           {dropdownItems?.length ? ( | ||||
|             <NativeDropdown | ||||
|               testID="profileHeaderDropdownBtn" | ||||
|               items={dropdownItems} | ||||
|               accessibilityLabel={_(msg`More options`)} | ||||
|               accessibilityHint=""> | ||||
|               <View style={[styles.btn, styles.secondaryBtn, pal.btn]}> | ||||
|                 <FontAwesomeIcon icon="ellipsis" size={20} style={[pal.text]} /> | ||||
|               </View> | ||||
|             </NativeDropdown> | ||||
|           ) : undefined} | ||||
|           <ProfileMenu profile={profile} /> | ||||
|         </View> | ||||
|         <View pointerEvents="none"> | ||||
|           <Text | ||||
|  | @ -670,6 +468,18 @@ let ProfileHeader = ({ | |||
|           /> | ||||
|         </View> | ||||
|       </TouchableWithoutFeedback> | ||||
|       <Prompt.Basic | ||||
|         control={unblockPromptControl} | ||||
|         title={_(msg`Unblock Account?`)} | ||||
|         description={_( | ||||
|           msg`The account will be able to interact with you after unblocking.`, | ||||
|         )} | ||||
|         onConfirm={unblockAccount} | ||||
|         confirmButtonCta={ | ||||
|           profile.viewer?.blocking ? _(msg`Unblock`) : _(msg`Block`) | ||||
|         } | ||||
|         confirmButtonColor="negative" | ||||
|       /> | ||||
|     </View> | ||||
|   ) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										315
									
								
								src/view/com/profile/ProfileMenu.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								src/view/com/profile/ProfileMenu.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,315 @@ | |||
| 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<AppBskyActorDefs.ProfileViewDetailed> | ||||
| }): 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 ( | ||||
|     <EventStopper onKeyDown={false}> | ||||
|       <Menu.Root> | ||||
|         <Menu.Trigger label={_(`More options`)}> | ||||
|           {({props}) => { | ||||
|             return ( | ||||
|               <TouchableOpacity | ||||
|                 {...props} | ||||
|                 hitSlop={HITSLOP_10} | ||||
|                 testID="profileHeaderDropdownBtn" | ||||
|                 style={[ | ||||
|                   { | ||||
|                     flexDirection: 'row', | ||||
|                     alignItems: 'center', | ||||
|                     justifyContent: 'center', | ||||
|                     paddingVertical: 7, | ||||
|                     borderRadius: 50, | ||||
|                     marginLeft: 6, | ||||
|                     paddingHorizontal: 14, | ||||
|                   }, | ||||
|                   pal.btn, | ||||
|                 ]}> | ||||
|                 <FontAwesomeIcon | ||||
|                   icon="ellipsis" | ||||
|                   size={20} | ||||
|                   style={t.atoms.text} | ||||
|                 /> | ||||
|               </TouchableOpacity> | ||||
|             ) | ||||
|           }} | ||||
|         </Menu.Trigger> | ||||
| 
 | ||||
|         <Menu.Outer style={{minWidth: 170}}> | ||||
|           <Menu.Group> | ||||
|             <Menu.Item | ||||
|               testID="profileHeaderDropdownShareBtn" | ||||
|               label={_(msg`Share`)} | ||||
|               onPress={onPressShare}> | ||||
|               <Menu.ItemText> | ||||
|                 <Trans>Share</Trans> | ||||
|               </Menu.ItemText> | ||||
|               <Menu.ItemIcon icon={Share} /> | ||||
|             </Menu.Item> | ||||
|           </Menu.Group> | ||||
|           {hasSession && ( | ||||
|             <> | ||||
|               <Menu.Divider /> | ||||
|               <Menu.Group> | ||||
|                 <Menu.Item | ||||
|                   testID="profileHeaderDropdownListAddRemoveBtn" | ||||
|                   label={_(msg`Add to Lists`)} | ||||
|                   onPress={onPressAddRemoveLists}> | ||||
|                   <Menu.ItemText> | ||||
|                     <Trans>Add to Lists</Trans> | ||||
|                   </Menu.ItemText> | ||||
|                   <Menu.ItemIcon icon={List} /> | ||||
|                 </Menu.Item> | ||||
|                 {!isSelf && ( | ||||
|                   <> | ||||
|                     {profile.viewer?.following && | ||||
|                       (profile.viewer.blocking || profile.viewer.blockedBy) && ( | ||||
|                         <Menu.Item | ||||
|                           testID="profileHeaderDropdownUnfollowBtn" | ||||
|                           label={_(msg`Unfollow Account`)} | ||||
|                           onPress={onPressUnfollowAccount}> | ||||
|                           <Menu.ItemText> | ||||
|                             <Trans>Unfollow Account</Trans> | ||||
|                           </Menu.ItemText> | ||||
|                           <Menu.ItemIcon icon={UserMinus} /> | ||||
|                         </Menu.Item> | ||||
|                       )} | ||||
|                     {!profile.viewer?.blocking && | ||||
|                       !profile.viewer?.mutedByList && ( | ||||
|                         <Menu.Item | ||||
|                           testID="profileHeaderDropdownMuteBtn" | ||||
|                           label={ | ||||
|                             profile.viewer?.muted | ||||
|                               ? _(msg`Unmute Account`) | ||||
|                               : _(msg`Mute Account`) | ||||
|                           } | ||||
|                           onPress={onPressMuteAccount}> | ||||
|                           <Menu.ItemText> | ||||
|                             {profile.viewer?.muted ? ( | ||||
|                               <Trans>Unmute Account</Trans> | ||||
|                             ) : ( | ||||
|                               <Trans>Mute Account</Trans> | ||||
|                             )} | ||||
|                           </Menu.ItemText> | ||||
|                           <Menu.ItemIcon | ||||
|                             icon={profile.viewer?.muted ? Unmute : Mute} | ||||
|                           /> | ||||
|                         </Menu.Item> | ||||
|                       )} | ||||
|                     {!profile.viewer?.blockingByList && ( | ||||
|                       <Menu.Item | ||||
|                         testID="profileHeaderDropdownBlockBtn" | ||||
|                         label={ | ||||
|                           profile.viewer | ||||
|                             ? _(msg`Unblock Account`) | ||||
|                             : _(msg`Block Account`) | ||||
|                         } | ||||
|                         onPress={() => blockPromptControl.open()}> | ||||
|                         <Menu.ItemText> | ||||
|                           {profile.viewer?.blocking ? ( | ||||
|                             <Trans>Unblock Account</Trans> | ||||
|                           ) : ( | ||||
|                             <Trans>Block Account</Trans> | ||||
|                           )} | ||||
|                         </Menu.ItemText> | ||||
|                         <Menu.ItemIcon | ||||
|                           icon={ | ||||
|                             profile.viewer?.blocking ? PersonCheck : PersonX | ||||
|                           } | ||||
|                         /> | ||||
|                       </Menu.Item> | ||||
|                     )} | ||||
|                     <Menu.Item | ||||
|                       testID="profileHeaderDropdownReportBtn" | ||||
|                       label={_(msg`Report Account`)} | ||||
|                       onPress={onPressReportAccount}> | ||||
|                       <Menu.ItemText> | ||||
|                         <Trans>Report Account</Trans> | ||||
|                       </Menu.ItemText> | ||||
|                       <Menu.ItemIcon icon={Flag} /> | ||||
|                     </Menu.Item> | ||||
|                   </> | ||||
|                 )} | ||||
|               </Menu.Group> | ||||
|             </> | ||||
|           )} | ||||
|         </Menu.Outer> | ||||
|       </Menu.Root> | ||||
| 
 | ||||
|       <Prompt.Basic | ||||
|         control={blockPromptControl} | ||||
|         title={ | ||||
|           profile.viewer?.blocking | ||||
|             ? _(msg`Unblock Account?`) | ||||
|             : _(msg`Block Account?`) | ||||
|         } | ||||
|         description={ | ||||
|           profile.viewer?.blocking | ||||
|             ? _( | ||||
|                 msg`The account will be able to interact with you after unblocking.`, | ||||
|               ) | ||||
|             : _( | ||||
|                 msg`Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`, | ||||
|               ) | ||||
|         } | ||||
|         onConfirm={blockAccount} | ||||
|         confirmButtonCta={ | ||||
|           profile.viewer?.blocking ? _(msg`Unblock`) : _(msg`Block`) | ||||
|         } | ||||
|         confirmButtonColor={profile.viewer?.blocking ? undefined : 'negative'} | ||||
|       /> | ||||
|     </EventStopper> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| ProfileMenu = memo(ProfileMenu) | ||||
| export {ProfileMenu} | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue