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