From 11ff9fa3a77997a3d322961844c401baa7bae8f2 Mon Sep 17 00:00:00 2001 From: vineyardbovines Date: Mon, 1 Jun 2026 11:18:17 -0400 Subject: [PATCH] Invite friends QA polish - Drawer: move the share button inline with the display name (right-aligned) and keep the drawer open when opening the share sheet - Settings: remove the redundant "Find and invite friends" item and rename the existing contacts link to "Find and invite friends" - Invite dialog: add a "Done" button and a full-width header divider using the Dialog.Header renderLeft + edge-to-edge content padding pattern - NUX announcement: wire up the promo illustration as the full-bleed header background with the "New Feature" tag overlaid - Fix "Try it": mount the invite dialog persistently in NuxDialogs so it survives the announcement being dismissed, then open it after the handoff --- .../invite_friends_announcement_nux.webp | Bin 0 -> 12408 bytes .../nuxs/InviteFriendsAnnouncement.tsx | 76 ++++++++---------- src/components/dialogs/nuxs/index.tsx | 20 ++++- .../inviteFriends/InviteFriendsDialog.tsx | 9 ++- .../inviteFriends/InviteFriendsDialog.web.tsx | 5 +- .../InviteFriendsDialogInner.tsx | 18 ++++- src/screens/Settings/Settings.tsx | 19 +---- src/view/shell/Drawer.tsx | 70 ++++++++-------- 8 files changed, 118 insertions(+), 99 deletions(-) create mode 100644 assets/images/invite_friends_announcement_nux.webp diff --git a/assets/images/invite_friends_announcement_nux.webp b/assets/images/invite_friends_announcement_nux.webp new file mode 100644 index 0000000000000000000000000000000000000000..53e79e1d54f2ba028fca5fe999e18f4db4d1cdb0 GIT binary patch literal 12408 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 literal 0 HcmV?d00001 diff --git a/src/components/dialogs/nuxs/InviteFriendsAnnouncement.tsx b/src/components/dialogs/nuxs/InviteFriendsAnnouncement.tsx index 7e052da1bd..8a8577d807 100644 --- a/src/components/dialogs/nuxs/InviteFriendsAnnouncement.tsx +++ b/src/components/dialogs/nuxs/InviteFriendsAnnouncement.tsx @@ -1,6 +1,6 @@ -import {useCallback, useState} from 'react' +import {useCallback} from 'react' import {View} from 'react-native' -import {LinearGradient} from 'expo-linear-gradient' +import {Image} from 'expo-image' import {msg} from '@lingui/core/macro' import {useLingui} from '@lingui/react' import {Trans} from '@lingui/react/macro' @@ -12,7 +12,6 @@ import {useNuxDialogContext} from '#/components/dialogs/nuxs' import {Sparkle_Stroke2_Corner0_Rounded as SparkleIcon} from '#/components/icons/Sparkle' import {Text} from '#/components/Typography' import {IS_E2E, IS_NATIVE, IS_WEB} from '#/env' -import {InviteFriendsDialog} from '#/features/inviteFriends' import {createIsEnabledCheck} from './utils' export const enabled = createIsEnabledCheck(() => { @@ -26,25 +25,23 @@ export function InviteFriendsAnnouncement() { const {_} = useLingui() const nuxDialogs = useNuxDialogContext() const control = Dialog.useDialogControl() - const inviteFriendsControl = Dialog.useDialogControl() - const [openInviteAfterClose, setOpenInviteAfterClose] = useState(false) Dialog.useAutoOpen(control) const onClose = useCallback(() => { nuxDialogs.dismissActiveNux() - if (openInviteAfterClose) { - // Defer to the next tick so the close animation completes before - // we open the next dialog (avoids visual glitch on iOS). - setOpenInviteAfterClose(false) - inviteFriendsControl.open() - } - }, [nuxDialogs, openInviteAfterClose, inviteFriendsControl]) + }, [nuxDialogs]) const onPressTryIt = useCallback(() => { - setOpenInviteAfterClose(true) - control.close() - }, [control]) + // Close this announcement (which dismisses + unmounts the NUX), then open + // the invite dialog. The invite dialog is mounted persistently by NuxDialogs + // (not here) so it survives the dismissal - the native bottom sheet cannot + // hand off to a second sheet mounted in this same subtree. Defer the open to + // the next tick so the announcement's teardown completes first. + control.close(() => { + setTimeout(() => nuxDialogs.openInviteFriends()) + }) + }, [control, nuxDialogs]) return ( <> @@ -62,38 +59,37 @@ export function InviteFriendsAnnouncement() { ]}> - - - - - New Feature - - - {/* - TODO: drop promo image - */} + style={[ + a.absolute, + a.align_center, + {top: 0, left: 0, right: 0, paddingTop: IS_WEB ? 24 : 20}, + ]}> + + + + New Feature + + + @@ -151,8 +147,6 @@ export function InviteFriendsAnnouncement() { - - ) } diff --git a/src/components/dialogs/nuxs/index.tsx b/src/components/dialogs/nuxs/index.tsx index 2516b22bd3..3e9aac4ce5 100644 --- a/src/components/dialogs/nuxs/index.tsx +++ b/src/components/dialogs/nuxs/index.tsx @@ -28,12 +28,21 @@ import { } from '#/components/dialogs/nuxs/InviteFriendsAnnouncement' import {isSnoozed, snooze, unsnooze} from '#/components/dialogs/nuxs/snoozing' import {type EnabledCheckProps} from '#/components/dialogs/nuxs/utils' +import * as Dialog from '#/components/Dialog' import {useAnalytics} from '#/analytics' import {useGeolocation} from '#/geolocation' +import {InviteFriendsDialog} from '#/features/inviteFriends' type Context = { activeNux: Nux | undefined dismissActiveNux: () => void + /** + * Opens the invite-friends dialog. It is mounted persistently by NuxDialogs + * (not inside the announcement NUX) so it survives the announcement being + * dismissed - the native bottom sheet cannot hand off between two sheets + * mounted in the same subtree. + */ + openInviteFriends: () => void } const queuedNuxs: { @@ -53,6 +62,7 @@ const queuedNuxs: { const Context = createContext({ activeNux: undefined, dismissActiveNux: () => {}, + openInviteFriends: () => {}, }) Context.displayName = 'NuxDialogContext' @@ -105,6 +115,7 @@ function Inner({ const [activeNux, setActiveNux] = useState() const {mutateAsync: saveNux} = useSaveNux() const {mutate: resetNuxs} = useResetNuxs() + const inviteFriendsControl = Dialog.useDialogControl() const snoozeNuxDialog = useCallback(() => { snooze() @@ -188,8 +199,9 @@ function Inner({ return { activeNux, dismissActiveNux, + openInviteFriends: () => inviteFriendsControl.open(), } - }, [activeNux, dismissActiveNux]) + }, [activeNux, dismissActiveNux, inviteFriendsControl]) return ( @@ -198,6 +210,12 @@ function Inner({ {activeNux === Nux.InviteFriendsAnnouncement && ( )} + {/* + Mounted persistently (not inside the announcement) so the invite dialog + survives the announcement NUX being dismissed during the "Try it" + handoff. + */} + ) } diff --git a/src/features/inviteFriends/InviteFriendsDialog.tsx b/src/features/inviteFriends/InviteFriendsDialog.tsx index 39d13c8777..2ba8bd1c33 100644 --- a/src/features/inviteFriends/InviteFriendsDialog.tsx +++ b/src/features/inviteFriends/InviteFriendsDialog.tsx @@ -3,12 +3,17 @@ import {InviteFriendsDialogInner} from './InviteFriendsDialogInner' export function InviteFriendsDialog({ control, + onClose, }: { control: Dialog.DialogControlProps + onClose?: () => void }) { return ( - - + + {/* */} ) diff --git a/src/features/inviteFriends/InviteFriendsDialog.web.tsx b/src/features/inviteFriends/InviteFriendsDialog.web.tsx index 1a28607194..155c533918 100644 --- a/src/features/inviteFriends/InviteFriendsDialog.web.tsx +++ b/src/features/inviteFriends/InviteFriendsDialog.web.tsx @@ -5,6 +5,9 @@ import {type DialogControlProps} from '#/components/Dialog' * Callers may still create a Dialog control and pass it; opening it does * nothing on web. */ -export function InviteFriendsDialog(_props: {control: DialogControlProps}) { +export function InviteFriendsDialog(_props: { + control: DialogControlProps + onClose?: () => void +}) { return null } diff --git a/src/features/inviteFriends/InviteFriendsDialogInner.tsx b/src/features/inviteFriends/InviteFriendsDialogInner.tsx index dc1cdf6483..4a57bb3a00 100644 --- a/src/features/inviteFriends/InviteFriendsDialogInner.tsx +++ b/src/features/inviteFriends/InviteFriendsDialogInner.tsx @@ -13,6 +13,7 @@ import {logger} from '#/logger' import {useProfileQuery} from '#/state/queries/profile' import {useSession} from '#/state/session' import {atoms as a, useTheme} from '#/alf' +import {Button, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import {Divider} from '#/components/Divider' import {Error as ErrorMessage} from '#/components/Error' @@ -120,13 +121,24 @@ export function InviteFriendsDialogInner({ return ( + ( + + )}> {l`Invite Friends`} }> - + diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx index b63e79f7fb..5fd53cde39 100644 --- a/src/screens/Settings/Settings.tsx +++ b/src/screens/Settings/Settings.tsx @@ -64,7 +64,6 @@ import * as Toast from '#/components/Toast' import {Text} from '#/components/Typography' import {useAnalytics} from '#/analytics' import {IS_INTERNAL, IS_IOS, IS_NATIVE} from '#/env' -import {InviteFriendsDialog} from '#/features/inviteFriends' import {useActorStatus} from '#/features/liveNow' import {device, useStorage} from '#/storage' import {useActivitySubscriptionsNudged} from '#/storage/hooks/activity-subscriptions-nudged' @@ -77,7 +76,6 @@ export function SettingsScreen({}: Props) { const {logoutEveryAccount} = useSessionApi() const {accounts, currentAccount} = useSession() const switchAccountControl = useDialogControl() - const inviteFriendsControl = useDialogControl() const signOutPromptControl = Prompt.usePromptControl() const {data: profile} = useProfileQuery({did: currentAccount?.did}) const {data: otherProfiles} = useProfilesQuery({ @@ -210,27 +208,15 @@ export function SettingsScreen({}: Props) { Content and media - {IS_NATIVE && ( - inviteFriendsControl.open()}> - - - Find and invite friends - - - - )} {IS_NATIVE && findContactsEnabled && !ax.features.enabled(ax.features.ImportContactsSettingsDisable) && ( + label={l`Find and invite friends`}> - Find friends from contacts + Find and invite friends )} @@ -316,7 +302,6 @@ export function SettingsScreen({}: Props) { /> - ) } diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx index 2ae286a110..bdd69d9e9a 100644 --- a/src/view/shell/Drawer.tsx +++ b/src/view/shell/Drawer.tsx @@ -91,38 +91,7 @@ let DrawerProfileCard = ({ accessibilityLabel={_(msg`Profile`)} accessibilityHint={_(msg`Navigates to your profile`)} onPress={onPressProfile} - style={[a.gap_sm, a.pr_lg, {position: 'relative'}]}> - {onPressShare && ( - [ - { - position: 'absolute', - top: 4, - right: 16, - width: 32, - height: 32, - borderRadius: 16, - backgroundColor: t.palette.contrast_50, - alignItems: 'center', - justifyContent: 'center', - opacity: pressed ? 0.7 : 1, - zIndex: 1, - }, - ]}> - - - )} + style={[a.gap_sm, a.pr_lg]}> {profile?.displayName || account.handle} {profile && } + {onPressShare && ( + [ + a.ml_auto, + { + width: 32, + height: 32, + borderRadius: 16, + backgroundColor: t.palette.contrast_50, + alignItems: 'center', + justifyContent: 'center', + opacity: pressed ? 0.7 : 1, + }, + ]}> + + + )} ): React.ReactNode => { account={currentAccount} onPressProfile={onPressDrawerHeaderProfile} onPressShare={() => { - setDrawerOpen(false) inviteFriendsControl.open() }} />