forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
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}