Render dropdown menu items lazily (#6437)

This commit is contained in:
dan
2024-11-17 03:15:03 +00:00
committed by GitHub
parent ddf2a64a72
commit e9fe8d90ef
+314 -283
View File
@@ -102,12 +102,77 @@ let PostDropdownBtn = ({
timestamp: string timestamp: string
threadgateRecord?: AppBskyFeedThreadgate.Record threadgateRecord?: AppBskyFeedThreadgate.Record
}): React.ReactNode => { }): React.ReactNode => {
const {hasSession, currentAccount} = useSession()
const theme = useTheme() const theme = useTheme()
const alf = useAlf() const alf = useAlf()
const {gtMobile} = useBreakpoints()
const {_} = useLingui() const {_} = useLingui()
const defaultCtrlColor = theme.palette.default.postCtrl const defaultCtrlColor = theme.palette.default.postCtrl
return (
<EventStopper onKeyDown={false}>
<Menu.Root>
<Menu.Trigger label={_(msg`Open post options menu`)}>
{({props, state}) => {
return (
<Pressable
{...props}
hitSlop={hitSlop}
testID={testID}
style={[
style,
a.rounded_full,
(state.hovered || state.pressed) && [
alf.atoms.bg_contrast_25,
],
]}>
<DotsHorizontal
fill={defaultCtrlColor}
style={{pointerEvents: 'none'}}
size={size}
/>
</Pressable>
)
}}
</Menu.Trigger>
<Menu.Outer>
<PostDropdownMenuItems
testID={testID}
post={post}
postFeedContext={postFeedContext}
record={record}
richText={richText}
timestamp={timestamp}
threadgateRecord={threadgateRecord}
/>
</Menu.Outer>
</Menu.Root>
</EventStopper>
)
}
PostDropdownBtn = memo(PostDropdownBtn)
export {PostDropdownBtn}
let PostDropdownMenuItems = ({
post,
postFeedContext,
record,
richText,
timestamp,
threadgateRecord,
}: {
testID: string
post: Shadow<AppBskyFeedDefs.PostView>
postFeedContext: string | undefined
record: AppBskyFeedPost.Record
richText: RichTextAPI
style?: StyleProp<ViewStyle>
hitSlop?: PressableProps['hitSlop']
size?: 'lg' | 'md' | 'sm'
timestamp: string
threadgateRecord?: AppBskyFeedThreadgate.Record
}): React.ReactNode => {
const {hasSession, currentAccount} = useSession()
const {gtMobile} = useBreakpoints()
const {_} = useLingui()
const langPrefs = useLanguagePrefs() const langPrefs = useLanguagePrefs()
const {mutateAsync: deletePostMutate} = usePostDeleteMutation() const {mutateAsync: deletePostMutate} = usePostDeleteMutation()
const {mutateAsync: pinPostMutate, isPending: isPinPending} = const {mutateAsync: pinPostMutate, isPending: isPinPending} =
@@ -360,308 +425,276 @@ let PostDropdownBtn = ({
}, [isPinned, pinPostMutate, postCid, postUri]) }, [isPinned, pinPostMutate, postCid, postUri])
return ( return (
<EventStopper onKeyDown={false}> <>
<Menu.Root> {isAuthor && (
<Menu.Trigger label={_(msg`Open post options menu`)}> <>
{({props, state}) => {
return (
<Pressable
{...props}
hitSlop={hitSlop}
testID={testID}
style={[
style,
a.rounded_full,
(state.hovered || state.pressed) && [
alf.atoms.bg_contrast_25,
],
]}>
<DotsHorizontal
fill={defaultCtrlColor}
style={{pointerEvents: 'none'}}
size={size}
/>
</Pressable>
)
}}
</Menu.Trigger>
<Menu.Outer>
{isAuthor && (
<>
<Menu.Group>
<Menu.Item
testID="pinPostBtn"
label={
isPinned
? _(msg`Unpin from profile`)
: _(msg`Pin to your profile`)
}
disabled={isPinPending}
onPress={onPressPin}>
<Menu.ItemText>
{isPinned
? _(msg`Unpin from profile`)
: _(msg`Pin to your profile`)}
</Menu.ItemText>
<Menu.ItemIcon
icon={isPinPending ? Loader : PinIcon}
position="right"
/>
</Menu.Item>
</Menu.Group>
<Menu.Divider />
</>
)}
<Menu.Group> <Menu.Group>
{(!hideInPWI || hasSession) && (
<>
<Menu.Item
testID="postDropdownTranslateBtn"
label={_(msg`Translate`)}
onPress={onPressTranslate}>
<Menu.ItemText>{_(msg`Translate`)}</Menu.ItemText>
<Menu.ItemIcon icon={Translate} position="right" />
</Menu.Item>
<Menu.Item
testID="postDropdownCopyTextBtn"
label={_(msg`Copy post text`)}
onPress={onCopyPostText}>
<Menu.ItemText>{_(msg`Copy post text`)}</Menu.ItemText>
<Menu.ItemIcon icon={ClipboardIcon} position="right" />
</Menu.Item>
</>
)}
{hasSession && (
<Menu.Item
testID="postDropdownSendViaDMBtn"
label={_(msg`Send via direct message`)}
onPress={() => sendViaChatControl.open()}>
<Menu.ItemText>
<Trans>Send via direct message</Trans>
</Menu.ItemText>
<Menu.ItemIcon icon={Send} position="right" />
</Menu.Item>
)}
<Menu.Item <Menu.Item
testID="postDropdownShareBtn" testID="pinPostBtn"
label={isWeb ? _(msg`Copy link to post`) : _(msg`Share`)} label={
onPress={() => { isPinned
if (showLoggedOutWarning) { ? _(msg`Unpin from profile`)
loggedOutWarningPromptControl.open() : _(msg`Pin to your profile`)
} else { }
onSharePost() disabled={isPinPending}
} onPress={onPressPin}>
}}>
<Menu.ItemText> <Menu.ItemText>
{isWeb ? _(msg`Copy link to post`) : _(msg`Share`)} {isPinned
? _(msg`Unpin from profile`)
: _(msg`Pin to your profile`)}
</Menu.ItemText> </Menu.ItemText>
<Menu.ItemIcon icon={Share} position="right" /> <Menu.ItemIcon
icon={isPinPending ? Loader : PinIcon}
position="right"
/>
</Menu.Item>
</Menu.Group>
<Menu.Divider />
</>
)}
<Menu.Group>
{(!hideInPWI || hasSession) && (
<>
<Menu.Item
testID="postDropdownTranslateBtn"
label={_(msg`Translate`)}
onPress={onPressTranslate}>
<Menu.ItemText>{_(msg`Translate`)}</Menu.ItemText>
<Menu.ItemIcon icon={Translate} position="right" />
</Menu.Item> </Menu.Item>
{canEmbed && ( <Menu.Item
<Menu.Item testID="postDropdownCopyTextBtn"
testID="postDropdownEmbedBtn" label={_(msg`Copy post text`)}
label={_(msg`Embed post`)} onPress={onCopyPostText}>
onPress={() => embedPostControl.open()}> <Menu.ItemText>{_(msg`Copy post text`)}</Menu.ItemText>
<Menu.ItemText>{_(msg`Embed post`)}</Menu.ItemText> <Menu.ItemIcon icon={ClipboardIcon} position="right" />
<Menu.ItemIcon icon={CodeBrackets} position="right" /> </Menu.Item>
</Menu.Item> </>
)} )}
{hasSession && (
<Menu.Item
testID="postDropdownSendViaDMBtn"
label={_(msg`Send via direct message`)}
onPress={() => sendViaChatControl.open()}>
<Menu.ItemText>
<Trans>Send via direct message</Trans>
</Menu.ItemText>
<Menu.ItemIcon icon={Send} position="right" />
</Menu.Item>
)}
<Menu.Item
testID="postDropdownShareBtn"
label={isWeb ? _(msg`Copy link to post`) : _(msg`Share`)}
onPress={() => {
if (showLoggedOutWarning) {
loggedOutWarningPromptControl.open()
} else {
onSharePost()
}
}}>
<Menu.ItemText>
{isWeb ? _(msg`Copy link to post`) : _(msg`Share`)}
</Menu.ItemText>
<Menu.ItemIcon icon={Share} position="right" />
</Menu.Item>
{canEmbed && (
<Menu.Item
testID="postDropdownEmbedBtn"
label={_(msg`Embed post`)}
onPress={() => embedPostControl.open()}>
<Menu.ItemText>{_(msg`Embed post`)}</Menu.ItemText>
<Menu.ItemIcon icon={CodeBrackets} position="right" />
</Menu.Item>
)}
</Menu.Group>
{hasSession && feedFeedback.enabled && (
<>
<Menu.Divider />
<Menu.Group>
<Menu.Item
testID="postDropdownShowMoreBtn"
label={_(msg`Show more like this`)}
onPress={onPressShowMore}>
<Menu.ItemText>{_(msg`Show more like this`)}</Menu.ItemText>
<Menu.ItemIcon icon={EmojiSmile} position="right" />
</Menu.Item>
<Menu.Item
testID="postDropdownShowLessBtn"
label={_(msg`Show less like this`)}
onPress={onPressShowLess}>
<Menu.ItemText>{_(msg`Show less like this`)}</Menu.ItemText>
<Menu.ItemIcon icon={EmojiSad} position="right" />
</Menu.Item>
</Menu.Group> </Menu.Group>
</>
)}
{hasSession && feedFeedback.enabled && ( {hasSession && (
<> <>
<Menu.Divider /> <Menu.Divider />
<Menu.Group> <Menu.Group>
<Menu.Item <Menu.Item
testID="postDropdownShowMoreBtn" testID="postDropdownMuteThreadBtn"
label={_(msg`Show more like this`)} label={
onPress={onPressShowMore}> isThreadMuted ? _(msg`Unmute thread`) : _(msg`Mute thread`)
<Menu.ItemText>{_(msg`Show more like this`)}</Menu.ItemText> }
<Menu.ItemIcon icon={EmojiSmile} position="right" /> onPress={onToggleThreadMute}>
</Menu.Item> <Menu.ItemText>
{isThreadMuted ? _(msg`Unmute thread`) : _(msg`Mute thread`)}
</Menu.ItemText>
<Menu.ItemIcon
icon={isThreadMuted ? Unmute : Mute}
position="right"
/>
</Menu.Item>
<Menu.Item <Menu.Item
testID="postDropdownShowLessBtn" testID="postDropdownMuteWordsBtn"
label={_(msg`Show less like this`)} label={_(msg`Mute words & tags`)}
onPress={onPressShowLess}> onPress={() => mutedWordsDialogControl.open()}>
<Menu.ItemText>{_(msg`Show less like this`)}</Menu.ItemText> <Menu.ItemText>{_(msg`Mute words & tags`)}</Menu.ItemText>
<Menu.ItemIcon icon={EmojiSad} position="right" /> <Menu.ItemIcon icon={Filter} position="right" />
</Menu.Item> </Menu.Item>
</Menu.Group> </Menu.Group>
</> </>
)} )}
{hasSession && ( {hasSession &&
<> (canHideReplyForEveryone || canDetachQuote || canHidePostForMe) && (
<Menu.Divider /> <>
<Menu.Group> <Menu.Divider />
<Menu.Group>
{canHidePostForMe && (
<Menu.Item <Menu.Item
testID="postDropdownMuteThreadBtn" testID="postDropdownHideBtn"
label={ label={
isThreadMuted ? _(msg`Unmute thread`) : _(msg`Mute thread`) isReply
? _(msg`Hide reply for me`)
: _(msg`Hide post for me`)
} }
onPress={onToggleThreadMute}> onPress={() => hidePromptControl.open()}>
<Menu.ItemText> <Menu.ItemText>
{isThreadMuted {isReply
? _(msg`Unmute thread`) ? _(msg`Hide reply for me`)
: _(msg`Mute thread`)} : _(msg`Hide post for me`)}
</Menu.ItemText>
<Menu.ItemIcon icon={EyeSlash} position="right" />
</Menu.Item>
)}
{canHideReplyForEveryone && (
<Menu.Item
testID="postDropdownHideBtn"
label={
isReplyHiddenByThreadgate
? _(msg`Show reply for everyone`)
: _(msg`Hide reply for everyone`)
}
onPress={
isReplyHiddenByThreadgate
? onToggleReplyVisibility
: () => hideReplyConfirmControl.open()
}>
<Menu.ItemText>
{isReplyHiddenByThreadgate
? _(msg`Show reply for everyone`)
: _(msg`Hide reply for everyone`)}
</Menu.ItemText> </Menu.ItemText>
<Menu.ItemIcon <Menu.ItemIcon
icon={isThreadMuted ? Unmute : Mute} icon={isReplyHiddenByThreadgate ? Eye : EyeSlash}
position="right" position="right"
/> />
</Menu.Item> </Menu.Item>
)}
{canDetachQuote && (
<Menu.Item <Menu.Item
testID="postDropdownMuteWordsBtn" disabled={isDetachPending}
label={_(msg`Mute words & tags`)} testID="postDropdownHideBtn"
onPress={() => mutedWordsDialogControl.open()}> label={
<Menu.ItemText>{_(msg`Mute words & tags`)}</Menu.ItemText> quoteEmbed.isDetached
<Menu.ItemIcon icon={Filter} position="right" /> ? _(msg`Re-attach quote`)
: _(msg`Detach quote`)
}
onPress={
quoteEmbed.isDetached
? onToggleQuotePostAttachment
: () => quotePostDetachConfirmControl.open()
}>
<Menu.ItemText>
{quoteEmbed.isDetached
? _(msg`Re-attach quote`)
: _(msg`Detach quote`)}
</Menu.ItemText>
<Menu.ItemIcon
icon={
isDetachPending
? Loader
: quoteEmbed.isDetached
? Eye
: EyeSlash
}
position="right"
/>
</Menu.Item> </Menu.Item>
</Menu.Group> )}
</> </Menu.Group>
)} </>
)}
{hasSession && {hasSession && (
(canHideReplyForEveryone || canDetachQuote || canHidePostForMe) && ( <>
<> <Menu.Divider />
<Menu.Divider /> <Menu.Group>
<Menu.Group> {!isAuthor && (
{canHidePostForMe && ( <Menu.Item
<Menu.Item testID="postDropdownReportBtn"
testID="postDropdownHideBtn" label={_(msg`Report post`)}
label={ onPress={() => reportDialogControl.open()}>
isReply <Menu.ItemText>{_(msg`Report post`)}</Menu.ItemText>
? _(msg`Hide reply for me`) <Menu.ItemIcon icon={Warning} position="right" />
: _(msg`Hide post for me`) </Menu.Item>
}
onPress={() => hidePromptControl.open()}>
<Menu.ItemText>
{isReply
? _(msg`Hide reply for me`)
: _(msg`Hide post for me`)}
</Menu.ItemText>
<Menu.ItemIcon icon={EyeSlash} position="right" />
</Menu.Item>
)}
{canHideReplyForEveryone && (
<Menu.Item
testID="postDropdownHideBtn"
label={
isReplyHiddenByThreadgate
? _(msg`Show reply for everyone`)
: _(msg`Hide reply for everyone`)
}
onPress={
isReplyHiddenByThreadgate
? onToggleReplyVisibility
: () => hideReplyConfirmControl.open()
}>
<Menu.ItemText>
{isReplyHiddenByThreadgate
? _(msg`Show reply for everyone`)
: _(msg`Hide reply for everyone`)}
</Menu.ItemText>
<Menu.ItemIcon
icon={isReplyHiddenByThreadgate ? Eye : EyeSlash}
position="right"
/>
</Menu.Item>
)}
{canDetachQuote && (
<Menu.Item
disabled={isDetachPending}
testID="postDropdownHideBtn"
label={
quoteEmbed.isDetached
? _(msg`Re-attach quote`)
: _(msg`Detach quote`)
}
onPress={
quoteEmbed.isDetached
? onToggleQuotePostAttachment
: () => quotePostDetachConfirmControl.open()
}>
<Menu.ItemText>
{quoteEmbed.isDetached
? _(msg`Re-attach quote`)
: _(msg`Detach quote`)}
</Menu.ItemText>
<Menu.ItemIcon
icon={
isDetachPending
? Loader
: quoteEmbed.isDetached
? Eye
: EyeSlash
}
position="right"
/>
</Menu.Item>
)}
</Menu.Group>
</>
)} )}
{hasSession && ( {isAuthor && (
<> <>
<Menu.Divider /> <Menu.Item
<Menu.Group> testID="postDropdownEditPostInteractions"
{!isAuthor && ( label={_(msg`Edit interaction settings`)}
<Menu.Item onPress={() => postInteractionSettingsDialogControl.open()}
testID="postDropdownReportBtn" {...(isAuthor
label={_(msg`Report post`)} ? Platform.select({
onPress={() => reportDialogControl.open()}> web: {
<Menu.ItemText>{_(msg`Report post`)}</Menu.ItemText> onHoverIn: prefetchPostInteractionSettings,
<Menu.ItemIcon icon={Warning} position="right" /> },
</Menu.Item> native: {
)} onPressIn: prefetchPostInteractionSettings,
},
{isAuthor && ( })
<> : {})}>
<Menu.Item <Menu.ItemText>
testID="postDropdownEditPostInteractions" {_(msg`Edit interaction settings`)}
label={_(msg`Edit interaction settings`)} </Menu.ItemText>
onPress={() => <Menu.ItemIcon icon={Gear} position="right" />
postInteractionSettingsDialogControl.open() </Menu.Item>
} <Menu.Item
{...(isAuthor testID="postDropdownDeleteBtn"
? Platform.select({ label={_(msg`Delete post`)}
web: { onPress={() => deletePromptControl.open()}>
onHoverIn: prefetchPostInteractionSettings, <Menu.ItemText>{_(msg`Delete post`)}</Menu.ItemText>
}, <Menu.ItemIcon icon={Trash} position="right" />
native: { </Menu.Item>
onPressIn: prefetchPostInteractionSettings, </>
}, )}
}) </Menu.Group>
: {})}> </>
<Menu.ItemText> )}
{_(msg`Edit interaction settings`)}
</Menu.ItemText>
<Menu.ItemIcon icon={Gear} position="right" />
</Menu.Item>
<Menu.Item
testID="postDropdownDeleteBtn"
label={_(msg`Delete post`)}
onPress={() => deletePromptControl.open()}>
<Menu.ItemText>{_(msg`Delete post`)}</Menu.ItemText>
<Menu.ItemIcon icon={Trash} position="right" />
</Menu.Item>
</>
)}
</Menu.Group>
</>
)}
</Menu.Outer>
</Menu.Root>
<Prompt.Basic <Prompt.Basic
control={deletePromptControl} control={deletePromptControl}
@@ -745,9 +778,7 @@ let PostDropdownBtn = ({
onConfirm={onToggleReplyVisibility} onConfirm={onToggleReplyVisibility}
confirmButtonCta={_(msg`Yes, hide`)} confirmButtonCta={_(msg`Yes, hide`)}
/> />
</EventStopper> </>
) )
} }
PostDropdownMenuItems = memo(PostDropdownMenuItems)
PostDropdownBtn = memo(PostDropdownBtn)
export {PostDropdownBtn}