Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 686 lines 26 kB view raw
1import {memo, useCallback, useMemo} from 'react' 2import {type AppBskyActorDefs} from '@atproto/api' 3import {msg} from '@lingui/core/macro' 4import {useLingui} from '@lingui/react' 5import {Trans} from '@lingui/react/macro' 6import {useNavigation} from '@react-navigation/native' 7import {useQueryClient} from '@tanstack/react-query' 8 9import {HITSLOP_20} from '#/lib/constants' 10import {makeProfileLink} from '#/lib/routes/links' 11import {type NavigationProp} from '#/lib/routes/types' 12import {shareText, shareUrl} from '#/lib/sharing' 13import {toShareUrl, toShareUrlBsky} from '#/lib/strings/url-helpers' 14import {type Shadow} from '#/state/cache/types' 15import {useModalControls} from '#/state/modals' 16import { 17 useDeerVerificationEnabled, 18 useDeerVerificationTrusted, 19 useSetDeerVerificationTrust, 20} from '#/state/preferences/deer-verification' 21import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons' 22import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs' 23import { 24 RQKEY as profileQueryKey, 25 useProfileBlockMutationQueue, 26 useProfileFollowMutationQueue, 27 useProfileMuteMutationQueue, 28} from '#/state/queries/profile' 29import {useSession} from '#/state/session' 30import {EventStopper} from '#/view/com/util/EventStopper' 31import {atoms as a, useTheme} from '#/alf' 32import {Button, ButtonIcon} from '#/components/Button' 33import {useDialogControl} from '#/components/Dialog' 34import {StarterPackDialog} from '#/components/dialogs/StarterPackDialog' 35import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as ArrowOutOfBoxIcon} from '#/components/icons/ArrowOutOfBox' 36import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink' 37import {CircleCheck_Stroke2_Corner0_Rounded as CircleCheckIcon} from '#/components/icons/CircleCheck' 38import {CircleX_Stroke2_Corner0_Rounded as CircleXIcon} from '#/components/icons/CircleX' 39import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard' 40import {DotGrid3x1_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid' 41import {Flag_Stroke2_Corner0_Rounded as Flag} from '#/components/icons/Flag' 42import {ListSparkle_Stroke2_Corner0_Rounded as List} from '#/components/icons/ListSparkle' 43import {Live_Stroke2_Corner0_Rounded as LiveIcon} from '#/components/icons/Live' 44import {MagnifyingGlass_Stroke2_Corner0_Rounded as SearchIcon} from '#/components/icons/MagnifyingGlass' 45import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute' 46import {PeopleRemove2_Stroke2_Corner0_Rounded as UserMinus} from '#/components/icons/PeopleRemove2' 47import { 48 PersonCheck_Stroke2_Corner0_Rounded as PersonCheck, 49 PersonX_Stroke2_Corner0_Rounded as PersonX, 50} from '#/components/icons/Person' 51import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' 52import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as Unmute} from '#/components/icons/Speaker' 53import {StarterPack} from '#/components/icons/StarterPack' 54import * as Menu from '#/components/Menu' 55import { 56 ReportDialog, 57 useReportDialogControl, 58} from '#/components/moderation/ReportDialog' 59import * as Prompt from '#/components/Prompt' 60import * as Toast from '#/components/Toast' 61import {useFullVerificationState} from '#/components/verification' 62import {VerificationCreatePrompt} from '#/components/verification/VerificationCreatePrompt' 63import {VerificationRemovePrompt} from '#/components/verification/VerificationRemovePrompt' 64import {useAnalytics} from '#/analytics' 65import {IS_WEB} from '#/env' 66import {useActorStatus, useLiveNowConfig} from '#/features/liveNow' 67import {EditLiveDialog} from '#/features/liveNow/components/EditLiveDialog' 68import {GoLiveDialog} from '#/features/liveNow/components/GoLiveDialog' 69import {GoLiveDisabledDialog} from '#/features/liveNow/components/GoLiveDisabledDialog' 70import {Dot} from '#/features/nuxs/components/Dot' 71import {Gradient} from '#/features/nuxs/components/Gradient' 72import {useDevMode} from '#/storage/hooks/dev-mode' 73 74let ProfileMenu = ({ 75 profile, 76}: { 77 profile: Shadow<AppBskyActorDefs.ProfileViewDetailed> 78}): React.ReactNode => { 79 const t = useTheme() 80 const ax = useAnalytics() 81 const {_} = useLingui() 82 const {currentAccount, hasSession} = useSession() 83 const {openModal} = useModalControls() 84 const reportDialogControl = useReportDialogControl() 85 const queryClient = useQueryClient() 86 const navigation = useNavigation<NavigationProp>() 87 const isSelf = currentAccount?.did === profile.did 88 const isFollowedBy = profile.viewer?.followedBy 89 const isFollowing = profile.viewer?.following 90 const isBlocked = profile.viewer?.blocking || profile.viewer?.blockedBy 91 const isFollowingBlockedAccount = isFollowing && isBlocked 92 const isLabelerAndNotBlocked = !!profile.associated?.labeler && !isBlocked 93 const [devModeEnabled] = useDevMode() 94 const verification = useFullVerificationState({profile}) 95 const {canGoLive} = useLiveNowConfig() 96 const status = useActorStatus(profile) 97 const statusNudge = useNux(Nux.LiveNowBetaNudge) 98 const statusNudgeActive = 99 isSelf && 100 canGoLive && 101 statusNudge.status === 'ready' && 102 !statusNudge.nux?.completed 103 const {mutate: saveNux} = useSaveNux() 104 105 const deerVerificationEnabled = useDeerVerificationEnabled() 106 const deerVerificationTrusted = useDeerVerificationTrusted().has(profile.did) 107 const setDeerVerificationTrust = useSetDeerVerificationTrust() 108 109 const [queueMute, queueUnmute] = useProfileMuteMutationQueue(profile) 110 const [queueBlock, queueUnblock] = useProfileBlockMutationQueue(profile) 111 const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue( 112 profile, 113 'ProfileMenu', 114 ) 115 116 const blockPromptControl = Prompt.usePromptControl() 117 const loggedOutWarningPromptControl = Prompt.usePromptControl() 118 const goLiveDialogControl = useDialogControl() 119 const goLiveDisabledDialogControl = useDialogControl() 120 const addToStarterPacksDialogControl = useDialogControl() 121 122 const showLoggedOutWarning = useMemo(() => { 123 return ( 124 profile.did !== currentAccount?.did && 125 !!profile.labels?.find(label => label.val === '!no-unauthenticated') 126 ) 127 }, [currentAccount, profile]) 128 129 const invalidateProfileQuery = useCallback(() => { 130 queryClient.invalidateQueries({ 131 queryKey: profileQueryKey(profile.did), 132 }) 133 }, [queryClient, profile.did]) 134 135 const onPressAddToStarterPacks = useCallback(() => { 136 ax.metric('profile:addToStarterPack', {}) 137 addToStarterPacksDialogControl.open() 138 }, [addToStarterPacksDialogControl]) 139 140 const onPressShare = useCallback(() => { 141 shareUrl(toShareUrl(makeProfileLink(profile))) 142 }, [profile]) 143 144 const onPressShareBsky = useCallback(() => { 145 shareUrl(toShareUrlBsky(makeProfileLink(profile))) 146 }, [profile]) 147 148 const onPressAddRemoveLists = useCallback(() => { 149 openModal({ 150 name: 'user-add-remove-lists', 151 subject: profile.did, 152 handle: profile.handle, 153 displayName: profile.displayName || profile.handle, 154 onAdd: invalidateProfileQuery, 155 onRemove: invalidateProfileQuery, 156 }) 157 }, [profile, openModal, invalidateProfileQuery]) 158 159 const onPressMuteAccount = useCallback(async () => { 160 if (profile.viewer?.muted) { 161 try { 162 await queueUnmute() 163 Toast.show(_(msg({message: 'Account unmuted', context: 'toast'}))) 164 } catch (e: any) { 165 if (e?.name !== 'AbortError') { 166 ax.logger.error('Failed to unmute account', {message: e}) 167 Toast.show(_(msg`There was an issue! ${e.toString()}`), { 168 type: 'error', 169 }) 170 } 171 } 172 } else { 173 try { 174 await queueMute() 175 Toast.show(_(msg({message: 'Account muted', context: 'toast'}))) 176 } catch (e: any) { 177 if (e?.name !== 'AbortError') { 178 ax.logger.error('Failed to mute account', {message: e}) 179 Toast.show(_(msg`There was an issue! ${e.toString()}`), { 180 type: 'error', 181 }) 182 } 183 } 184 } 185 }, [ax, profile.viewer?.muted, queueUnmute, _, queueMute]) 186 187 const blockAccount = useCallback(async () => { 188 if (profile.viewer?.blocking) { 189 try { 190 await queueUnblock() 191 Toast.show(_(msg({message: 'Account unblocked', context: 'toast'}))) 192 } catch (e: any) { 193 if (e?.name !== 'AbortError') { 194 ax.logger.error('Failed to unblock account', {message: e}) 195 Toast.show(_(msg`There was an issue! ${e.toString()}`), { 196 type: 'error', 197 }) 198 } 199 } 200 } else { 201 try { 202 await queueBlock() 203 Toast.show(_(msg({message: 'Account blocked', context: 'toast'}))) 204 } catch (e: any) { 205 if (e?.name !== 'AbortError') { 206 ax.logger.error('Failed to block account', {message: e}) 207 Toast.show(_(msg`There was an issue! ${e.toString()}`), { 208 type: 'error', 209 }) 210 } 211 } 212 } 213 }, [ax, profile.viewer?.blocking, _, queueUnblock, queueBlock]) 214 215 const onPressFollowAccount = useCallback(async () => { 216 try { 217 await queueFollow() 218 Toast.show(_(msg({message: 'Account followed', context: 'toast'}))) 219 } catch (e: any) { 220 if (e?.name !== 'AbortError') { 221 ax.logger.error('Failed to follow account', {message: e}) 222 Toast.show(_(msg`There was an issue! ${e.toString()}`), { 223 type: 'error', 224 }) 225 } 226 } 227 }, [_, ax, queueFollow]) 228 229 const onPressUnfollowAccount = useCallback(async () => { 230 try { 231 await queueUnfollow() 232 Toast.show(_(msg({message: 'Account unfollowed', context: 'toast'}))) 233 } catch (e: any) { 234 if (e?.name !== 'AbortError') { 235 ax.logger.error('Failed to unfollow account', {message: e}) 236 Toast.show(_(msg`There was an issue! ${e.toString()}`), { 237 type: 'error', 238 }) 239 } 240 } 241 }, [_, ax, queueUnfollow]) 242 243 const onPressReportAccount = useCallback(() => { 244 reportDialogControl.open() 245 }, [reportDialogControl]) 246 247 const onPressShareATUri = useCallback(() => { 248 shareText(`at://${profile.did}`) 249 }, [profile.did]) 250 251 const onPressShareDID = useCallback(() => { 252 shareText(profile.did) 253 }, [profile.did]) 254 255 const onPressSearch = useCallback(() => { 256 navigation.navigate('ProfileSearch', {name: profile.handle}) 257 }, [navigation, profile.handle]) 258 259 const verificationCreatePromptControl = Prompt.usePromptControl() 260 const verificationRemovePromptControl = Prompt.usePromptControl() 261 const currentAccountVerifications = 262 profile.verification?.verifications?.filter(v => { 263 return v.issuer === currentAccount?.did 264 }) ?? [] 265 266 const enableSquareButtons = useEnableSquareButtons() 267 268 return ( 269 <EventStopper onKeyDown={false}> 270 <Menu.Root> 271 <Menu.Trigger label={_(msg`More options`)}> 272 {({props}) => { 273 return ( 274 <> 275 <Button 276 {...props} 277 testID="profileHeaderDropdownBtn" 278 label={_(msg`More options`)} 279 hitSlop={HITSLOP_20} 280 variant="solid" 281 color="secondary" 282 size="small" 283 shape={enableSquareButtons ? 'square' : 'round'}> 284 {statusNudgeActive && ( 285 <Gradient 286 style={[ 287 enableSquareButtons ? a.rounded_sm : a.rounded_full, 288 ]} 289 /> 290 )} 291 <ButtonIcon icon={Ellipsis} size="sm" /> 292 </Button> 293 294 {statusNudgeActive && <Dot top={1} right={1} />} 295 </> 296 ) 297 }} 298 </Menu.Trigger> 299 300 <Menu.Outer style={{minWidth: 170}}> 301 <Menu.Group> 302 <Menu.Item 303 testID="profileHeaderDropdownShareBtn" 304 label={ 305 IS_WEB ? _(msg`Copy link to profile`) : _(msg`Share via...`) 306 } 307 onPress={() => { 308 if (showLoggedOutWarning) { 309 loggedOutWarningPromptControl.open() 310 } else { 311 onPressShare() 312 } 313 }}> 314 <Menu.ItemText> 315 {IS_WEB ? ( 316 <Trans>Copy link to profile</Trans> 317 ) : ( 318 <Trans>Share via...</Trans> 319 )} 320 </Menu.ItemText> 321 <Menu.ItemIcon 322 icon={IS_WEB ? ChainLinkIcon : ArrowOutOfBoxIcon} 323 /> 324 </Menu.Item> 325 <Menu.Item 326 testID="profileHeaderDropdownShareBtn" 327 label={ 328 IS_WEB 329 ? _(msg`Copy via bsky.app`) 330 : _(msg`Share via bsky.app...`) 331 } 332 onPress={() => { 333 if (showLoggedOutWarning) { 334 loggedOutWarningPromptControl.open() 335 } else { 336 onPressShareBsky() 337 } 338 }}> 339 <Menu.ItemText> 340 {IS_WEB ? ( 341 <Trans>Copy via bsky.app</Trans> 342 ) : ( 343 <Trans>Share via bsky.app...</Trans> 344 )} 345 </Menu.ItemText> 346 <Menu.ItemIcon 347 icon={IS_WEB ? ChainLinkIcon : ArrowOutOfBoxIcon} 348 /> 349 </Menu.Item> 350 <Menu.Item 351 testID="profileHeaderDropdownSearchBtn" 352 label={_(msg`Search posts`)} 353 onPress={onPressSearch}> 354 <Menu.ItemText> 355 <Trans>Search posts</Trans> 356 </Menu.ItemText> 357 <Menu.ItemIcon icon={SearchIcon} /> 358 </Menu.Item> 359 </Menu.Group> 360 361 {hasSession && ( 362 <> 363 <Menu.Divider /> 364 <Menu.Group> 365 {!isSelf && ( 366 <> 367 {(isLabelerAndNotBlocked || isFollowingBlockedAccount) && ( 368 <Menu.Item 369 testID="profileHeaderDropdownFollowBtn" 370 label={ 371 isFollowing 372 ? isFollowedBy 373 ? _(msg`Divorce mutual`) 374 : _(msg`Unfollow account`) 375 : _(msg`Follow account`) 376 } 377 onPress={ 378 isFollowing 379 ? onPressUnfollowAccount 380 : onPressFollowAccount 381 }> 382 <Menu.ItemText> 383 {isFollowing ? ( 384 isFollowedBy ? ( 385 <Trans>Divorce mutual</Trans> 386 ) : ( 387 <Trans>Unfollow account</Trans> 388 ) 389 ) : ( 390 <Trans>Follow account</Trans> 391 )} 392 </Menu.ItemText> 393 <Menu.ItemIcon icon={isFollowing ? UserMinus : Plus} /> 394 </Menu.Item> 395 )} 396 </> 397 )} 398 <Menu.Item 399 testID="profileHeaderDropdownStarterPackAddRemoveBtn" 400 label={_(msg`Add to starter packs`)} 401 onPress={onPressAddToStarterPacks}> 402 <Menu.ItemText> 403 <Trans>Add to starter packs</Trans> 404 </Menu.ItemText> 405 <Menu.ItemIcon icon={StarterPack} /> 406 </Menu.Item> 407 <Menu.Item 408 testID="profileHeaderDropdownListAddRemoveBtn" 409 label={_(msg`Add to lists`)} 410 onPress={onPressAddRemoveLists}> 411 <Menu.ItemText> 412 <Trans>Add to lists</Trans> 413 </Menu.ItemText> 414 <Menu.ItemIcon icon={List} /> 415 </Menu.Item> 416 {!isSelf && 417 deerVerificationEnabled && 418 (deerVerificationTrusted ? ( 419 <Menu.Item 420 testID="profileHeaderDropdownVerificationTrustRemoveButton" 421 label={_(msg`Remove trust`)} 422 onPress={() => 423 setDeerVerificationTrust.remove(profile.did) 424 }> 425 <Menu.ItemText> 426 <Trans>Remove trust</Trans> 427 </Menu.ItemText> 428 <Menu.ItemIcon icon={CircleXIcon} /> 429 </Menu.Item> 430 ) : ( 431 <Menu.Item 432 testID="profileHeaderDropdownVerificationTrustAddButton" 433 label={_(msg`Trust verifier`)} 434 onPress={() => setDeerVerificationTrust.add(profile.did)}> 435 <Menu.ItemText> 436 <Trans>Trust verifier</Trans> 437 </Menu.ItemText> 438 <Menu.ItemIcon icon={CircleCheckIcon} /> 439 </Menu.Item> 440 ))} 441 {isSelf && canGoLive && ( 442 <Menu.Item 443 testID="profileHeaderDropdownListAddRemoveBtn" 444 label={ 445 status.isDisabled 446 ? _(msg`Go live (disabled)`) 447 : status.isActive 448 ? _(msg`Edit live status`) 449 : _(msg`Go live`) 450 } 451 onPress={() => { 452 if (status.isDisabled) { 453 goLiveDisabledDialogControl.open() 454 } else { 455 goLiveDialogControl.open() 456 } 457 saveNux({ 458 id: Nux.LiveNowBetaNudge, 459 data: undefined, 460 completed: true, 461 }) 462 }}> 463 {statusNudgeActive && <Gradient />} 464 <Menu.ItemText> 465 {status.isDisabled ? ( 466 <Trans>Go live (disabled)</Trans> 467 ) : status.isActive ? ( 468 <Trans>Edit live status</Trans> 469 ) : ( 470 <Trans>Go live</Trans> 471 )} 472 </Menu.ItemText> 473 {statusNudgeActive && ( 474 <Menu.ItemText 475 style={[ 476 a.flex_0, 477 { 478 color: t.palette.primary_500, 479 right: IS_WEB ? -8 : -4, 480 }, 481 ]}> 482 <Trans>New</Trans> 483 </Menu.ItemText> 484 )} 485 <Menu.ItemIcon 486 icon={LiveIcon} 487 fill={ 488 statusNudgeActive 489 ? () => t.palette.primary_500 490 : undefined 491 } 492 /> 493 </Menu.Item> 494 )} 495 {verification.viewer.role === 'verifier' && 496 !verification.profile.isViewer && 497 (verification.viewer.hasIssuedVerification ? ( 498 <Menu.Item 499 testID="profileHeaderDropdownVerificationRemoveButton" 500 label={_(msg`Remove verification`)} 501 onPress={() => verificationRemovePromptControl.open()}> 502 <Menu.ItemText> 503 <Trans>Remove verification</Trans> 504 </Menu.ItemText> 505 <Menu.ItemIcon icon={CircleXIcon} /> 506 </Menu.Item> 507 ) : ( 508 <Menu.Item 509 testID="profileHeaderDropdownVerificationCreateButton" 510 label={_(msg`Verify account`)} 511 onPress={() => verificationCreatePromptControl.open()}> 512 <Menu.ItemText> 513 <Trans>Verify account</Trans> 514 </Menu.ItemText> 515 <Menu.ItemIcon icon={CircleCheckIcon} /> 516 </Menu.Item> 517 ))} 518 {!isSelf && ( 519 <> 520 {!profile.viewer?.blocking && 521 !profile.viewer?.mutedByList && ( 522 <Menu.Item 523 testID="profileHeaderDropdownMuteBtn" 524 label={ 525 profile.viewer?.muted 526 ? _(msg`Unmute account`) 527 : _(msg`Mute account`) 528 } 529 onPress={onPressMuteAccount}> 530 <Menu.ItemText> 531 {profile.viewer?.muted ? ( 532 <Trans>Unmute account</Trans> 533 ) : ( 534 <Trans>Mute account</Trans> 535 )} 536 </Menu.ItemText> 537 <Menu.ItemIcon 538 icon={profile.viewer?.muted ? Unmute : Mute} 539 /> 540 </Menu.Item> 541 )} 542 {!profile.viewer?.blockingByList && ( 543 <Menu.Item 544 testID="profileHeaderDropdownBlockBtn" 545 label={ 546 profile.viewer 547 ? _(msg`Unblock account`) 548 : _(msg`Block account`) 549 } 550 onPress={() => blockPromptControl.open()}> 551 <Menu.ItemText> 552 {profile.viewer?.blocking ? ( 553 <Trans>Unblock account</Trans> 554 ) : ( 555 <Trans>Block account</Trans> 556 )} 557 </Menu.ItemText> 558 <Menu.ItemIcon 559 icon={ 560 profile.viewer?.blocking ? PersonCheck : PersonX 561 } 562 /> 563 </Menu.Item> 564 )} 565 <Menu.Item 566 testID="profileHeaderDropdownReportBtn" 567 label={_(msg`Report account`)} 568 onPress={onPressReportAccount}> 569 <Menu.ItemText> 570 <Trans>Report account</Trans> 571 </Menu.ItemText> 572 <Menu.ItemIcon icon={Flag} /> 573 </Menu.Item> 574 </> 575 )} 576 </Menu.Group> 577 </> 578 )} 579 {devModeEnabled ? ( 580 <> 581 <Menu.Divider /> 582 <Menu.Group> 583 <Menu.Item 584 testID="profileHeaderDropdownShareATURIBtn" 585 label={_(msg`Copy at:// URI`)} 586 onPress={onPressShareATUri}> 587 <Menu.ItemText> 588 <Trans>Copy at:// URI</Trans> 589 </Menu.ItemText> 590 <Menu.ItemIcon icon={ClipboardIcon} /> 591 </Menu.Item> 592 <Menu.Item 593 testID="profileHeaderDropdownShareDIDBtn" 594 label={_(msg`Copy DID`)} 595 onPress={onPressShareDID}> 596 <Menu.ItemText> 597 <Trans>Copy DID</Trans> 598 </Menu.ItemText> 599 <Menu.ItemIcon icon={ClipboardIcon} /> 600 </Menu.Item> 601 </Menu.Group> 602 </> 603 ) : null} 604 </Menu.Outer> 605 </Menu.Root> 606 607 <StarterPackDialog 608 control={addToStarterPacksDialogControl} 609 targetDid={profile.did} 610 /> 611 612 <ReportDialog 613 control={reportDialogControl} 614 subject={{ 615 ...profile, 616 $type: 'app.bsky.actor.defs#profileViewDetailed', 617 }} 618 /> 619 620 <Prompt.Basic 621 control={blockPromptControl} 622 title={ 623 profile.viewer?.blocking 624 ? _(msg`Unblock Account?`) 625 : _(msg`Block Account?`) 626 } 627 description={ 628 profile.viewer?.blocking 629 ? _( 630 msg`The account will be able to interact with you after unblocking.`, 631 ) 632 : profile.associated?.labeler 633 ? _( 634 msg`Blocking will not prevent labels from being applied on your account, but it will stop this account from replying in your threads or interacting with you.`, 635 ) 636 : _( 637 msg`Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`, 638 ) 639 } 640 onConfirm={blockAccount} 641 confirmButtonCta={ 642 profile.viewer?.blocking ? _(msg`Unblock`) : _(msg`Block`) 643 } 644 confirmButtonColor={profile.viewer?.blocking ? undefined : 'negative'} 645 /> 646 647 <Prompt.Basic 648 control={loggedOutWarningPromptControl} 649 title={_(msg`Note about sharing`)} 650 description={_( 651 msg`This profile is only visible to logged-in users. It won't be visible to people who aren't signed in.`, 652 )} 653 onConfirm={onPressShare} 654 confirmButtonCta={_(msg`Share anyway`)} 655 /> 656 657 <VerificationCreatePrompt 658 control={verificationCreatePromptControl} 659 profile={profile} 660 /> 661 <VerificationRemovePrompt 662 control={verificationRemovePromptControl} 663 profile={profile} 664 verifications={currentAccountVerifications} 665 /> 666 667 {status.isDisabled ? ( 668 <GoLiveDisabledDialog 669 control={goLiveDisabledDialogControl} 670 status={status} 671 /> 672 ) : status.isActive ? ( 673 <EditLiveDialog 674 control={goLiveDialogControl} 675 status={status} 676 embed={status.embed} 677 /> 678 ) : ( 679 <GoLiveDialog control={goLiveDialogControl} profile={profile} /> 680 )} 681 </EventStopper> 682 ) 683} 684 685ProfileMenu = memo(ProfileMenu) 686export {ProfileMenu}