From 487da69a15d3957651c19f4e273501258daefd0a Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 6 Jun 2025 18:21:23 +0300 Subject: Replace "Note about sharing" prompt with an inline hint (#8452) * add pwi warning to share menu, remove prompt * add pwi label to web, remove prompt * add an option to the PWI menu * conditionally reorder items on web --- src/components/Menu/index.tsx | 45 ++++--- src/components/Menu/index.web.tsx | 39 +++--- .../PostControls/PostMenu/PostMenuItems.tsx | 15 ++- .../PostControls/ShareMenu/ShareMenuItems.tsx | 56 +++------ .../PostControls/ShareMenu/ShareMenuItems.web.tsx | 133 ++++++++++----------- 5 files changed, 142 insertions(+), 146 deletions(-) (limited to 'src') diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index c5ccfa5ec..94438724c 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -1,5 +1,11 @@ -import React from 'react' -import {Pressable, StyleProp, View, ViewStyle} from 'react-native' +import {cloneElement, Fragment, isValidElement, useMemo} from 'react' +import { + Pressable, + type StyleProp, + type TextStyle, + View, + type ViewStyle, +} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import flattenReactChildren from 'react-keyed-flatten-children' @@ -16,12 +22,12 @@ import { useMenuItemContext, } from '#/components/Menu/context' import { - ContextType, - GroupProps, - ItemIconProps, - ItemProps, - ItemTextProps, - TriggerProps, + type ContextType, + type GroupProps, + type ItemIconProps, + type ItemProps, + type ItemTextProps, + type TriggerProps, } from '#/components/Menu/types' import {Text} from '#/components/Typography' @@ -39,7 +45,7 @@ export function Root({ control?: Dialog.DialogControlProps }>) { const defaultControl = Dialog.useDialogControl() - const context = React.useMemo( + const context = useMemo( () => ({ control: control || defaultControl, }), @@ -276,16 +282,21 @@ export function ContainerItem({ ) } -export function LabelText({children}: {children: React.ReactNode}) { +export function LabelText({ + children, + style, +}: { + children: React.ReactNode + style?: StyleProp +}) { const t = useTheme() return ( {children} @@ -304,20 +315,20 @@ export function Group({children, style}: GroupProps) { style, ]}> {flattenReactChildren(children).map((child, i) => { - return React.isValidElement(child) && + return isValidElement(child) && (child.type === Item || child.type === ContainerItem) ? ( - + {i > 0 ? ( ) : null} - {React.cloneElement(child, { + {cloneElement(child, { // @ts-expect-error cloneElement is not aware of the types style: { borderRadius: 0, borderWidth: 0, }, })} - + ) : null })} diff --git a/src/components/Menu/index.web.tsx b/src/components/Menu/index.web.tsx index 7d6e50556..8d26c8f03 100644 --- a/src/components/Menu/index.web.tsx +++ b/src/components/Menu/index.web.tsx @@ -1,5 +1,11 @@ -import React from 'react' -import {Pressable, type StyleProp, View, type ViewStyle} from 'react-native' +import {forwardRef, useCallback, useId, useMemo, useState} from 'react' +import { + Pressable, + type StyleProp, + type TextStyle, + View, + type ViewStyle, +} from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {DropdownMenu} from 'radix-ui' @@ -29,10 +35,10 @@ import {Text} from '#/components/Typography' export {useMenuContext} export function useMenuControl(): Dialog.DialogControlProps { - const id = React.useId() - const [isOpen, setIsOpen] = React.useState(false) + const id = useId() + const [isOpen, setIsOpen] = useState(false) - return React.useMemo( + return useMemo( () => ({ id, ref: {current: null}, @@ -56,13 +62,13 @@ export function Root({ }>) { const {_} = useLingui() const defaultControl = useMenuControl() - const context = React.useMemo( + const context = useMemo( () => ({ control: control || defaultControl, }), [control, defaultControl], ) - const onOpenChange = React.useCallback( + const onOpenChange = useCallback( (open: boolean) => { if (context.control.isOpen && !open) { context.control.close() @@ -96,7 +102,7 @@ export function Root({ ) } -const RadixTriggerPassThrough = React.forwardRef( +const RadixTriggerPassThrough = forwardRef( ( props: { children: ( @@ -355,18 +361,23 @@ export function ItemRadio({selected}: {selected: boolean}) { ) } -export function LabelText({children}: {children: React.ReactNode}) { +export function LabelText({ + children, + style, +}: { + children: React.ReactNode + style?: StyleProp +}) { const t = useTheme() return ( {children} diff --git a/src/components/PostControls/PostMenu/PostMenuItems.tsx b/src/components/PostControls/PostMenu/PostMenuItems.tsx index 7f33f3348..01ddd0bcf 100644 --- a/src/components/PostControls/PostMenu/PostMenuItems.tsx +++ b/src/components/PostControls/PostMenu/PostMenuItems.tsx @@ -48,7 +48,7 @@ import { useProfileMuteMutationQueue, } from '#/state/queries/profile' import {useToggleReplyVisibilityMutation} from '#/state/queries/threadgate' -import {useSession} from '#/state/session' +import {useRequireAuth, useSession} from '#/state/session' import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' import * as Toast from '#/view/com/util/Toast' import {useDialogControl} from '#/components/Dialog' @@ -113,6 +113,7 @@ let PostMenuItems = ({ const {mutateAsync: deletePostMutate} = usePostDeleteMutation() const {mutateAsync: pinPostMutate, isPending: isPinPending} = usePinnedPostMutation() + const requireSignIn = useRequireAuth() const hiddenPosts = useHiddenPosts() const {hidePost} = useHiddenPostsApi() const feedFeedback = useFeedFeedbackContext() @@ -397,6 +398,8 @@ let PostMenuItems = ({ openLink(url) } + const onSignIn = () => requireSignIn(() => {}) + const gate = useGate() const isDiscoverDebugUser = IS_INTERNAL || @@ -434,7 +437,7 @@ let PostMenuItems = ({ )} - {(!hideInPWI || hasSession) && ( + {!hideInPWI || hasSession ? ( <> + ) : ( + + {_(msg`Sign in to view post`)} + + )} diff --git a/src/components/PostControls/ShareMenu/ShareMenuItems.tsx b/src/components/PostControls/ShareMenu/ShareMenuItems.tsx index c090c3e2d..1c04f3174 100644 --- a/src/components/PostControls/ShareMenu/ShareMenuItems.tsx +++ b/src/components/PostControls/ShareMenu/ShareMenuItems.tsx @@ -14,6 +14,8 @@ import {isIOS} from '#/platform/detection' import {useProfileShadow} from '#/state/cache/profile-shadow' import {useSession} from '#/state/session' import * as Toast from '#/view/com/util/Toast' +import {atoms as a} from '#/alf' +import {Admonition} from '#/components/Admonition' import {useDialogControl} from '#/components/Dialog' import {SendViaChatDialog} from '#/components/dms/dialogs/ShareViaChatDialog' import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as ArrowOutOfBoxIcon} from '#/components/icons/ArrowOutOfBox' @@ -21,7 +23,6 @@ import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/i import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard' import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlaneIcon} from '#/components/icons/PaperPlane' import * as Menu from '#/components/Menu' -import * as Prompt from '#/components/Prompt' import {useDevMode} from '#/storage/hooks/dev-mode' import {RecentChats} from './RecentChats' import {type ShareMenuItemsProps} from './ShareMenuItems.types' @@ -30,11 +31,9 @@ let ShareMenuItems = ({ post, onShare: onShareProp, }: ShareMenuItemsProps): React.ReactNode => { - const {hasSession, currentAccount} = useSession() + const {hasSession} = useSession() const {_} = useLingui() const navigation = useNavigation() - const pwiWarningShareControl = useDialogControl() - const pwiWarningCopyControl = useDialogControl() const sendViaChatControl = useDialogControl() const [devModeEnabled] = useDevMode() @@ -52,9 +51,6 @@ let ShareMenuItems = ({ ) }, [postAuthor]) - const showLoggedOutWarning = - postAuthor.did !== currentAccount?.did && hideInPWI - const onSharePost = () => { logger.metric('share:press:nativeShare', {}, {statsig: true}) const url = toShareUrl(href) @@ -117,13 +113,7 @@ let ShareMenuItems = ({ { - if (showLoggedOutWarning) { - pwiWarningShareControl.open() - } else { - onSharePost() - } - }}> + onPress={onSharePost}> Share via... @@ -133,13 +123,7 @@ let ShareMenuItems = ({ { - if (showLoggedOutWarning) { - pwiWarningCopyControl.open() - } else { - onCopyLink() - } - }}> + onPress={onCopyLink}> Copy link to post @@ -147,6 +131,16 @@ let ShareMenuItems = ({ + {hideInPWI && ( + + + + This post is only visible to logged-in users. + + + + )} + {devModeEnabled && ( - - - - { - const {hasSession, currentAccount} = useSession() + const {hasSession} = useSession() const {gtMobile} = useBreakpoints() const {_} = useLingui() const navigation = useNavigation() - const loggedOutWarningPromptControl = useDialogControl() const embedPostControl = useDialogControl() const sendViaChatControl = useDialogControl() const [devModeEnabled] = useDevMode() @@ -56,9 +54,6 @@ let ShareMenuItems = ({ ) }, [postAuthor]) - const showLoggedOutWarning = - postAuthor.did !== currentAccount?.did && hideInPWI - const onCopyLink = () => { logger.metric('share:press:copyLink', {}, {statsig: true}) const url = toShareUrl(href) @@ -84,92 +79,86 @@ let ShareMenuItems = ({ shareText(postAuthor.did) } + const copyLinkItem = ( + + + Copy link to post + + + + ) + return ( <> - + {!hideInPWI && copyLinkItem} + + {hasSession && ( { - if (showLoggedOutWarning) { - loggedOutWarningPromptControl.open() - } else { - onCopyLink() - } + logger.metric('share:press:openDmSearch', {}, {statsig: true}) + sendViaChatControl.open() }}> - Copy link to post + Send via direct message - + + + )} + + {canEmbed && ( + { + logger.metric('share:press:embed', {}, {statsig: true}) + embedPostControl.open() + }}> + {_(msg`Embed post`)} + + )} + + {hideInPWI && ( + <> + {hasSession && } + {copyLinkItem} + + Note: This post is only visible to logged-in users. + + + )} - {hasSession && ( + {devModeEnabled && ( + <> + { - logger.metric('share:press:openDmSearch', {}, {statsig: true}) - sendViaChatControl.open() - }}> + testID="postAtUriShareBtn" + label={_(msg`Copy post at:// URI`)} + onPress={onShareATURI}> - Send via direct message + Copy post at:// URI - + - )} - - {canEmbed && ( { - logger.metric('share:press:embed', {}, {statsig: true}) - embedPostControl.open() - }}> - {_(msg`Embed post`)} - + testID="postAuthorDIDShareBtn" + label={_(msg`Copy author DID`)} + onPress={onShareAuthorDID}> + + Copy author DID + + - )} - - - {devModeEnabled && ( - <> - - - - - Copy post at:// URI - - - - - - Copy author DID - - - - )} - - {canEmbed && (