diff --git a/src/components/dialogs/nuxs/InviteFriendsAnnouncement.tsx b/src/components/dialogs/nuxs/InviteFriendsAnnouncement.tsx
index 70f4057d50..1ea4cd5899 100644
--- a/src/components/dialogs/nuxs/InviteFriendsAnnouncement.tsx
+++ b/src/components/dialogs/nuxs/InviteFriendsAnnouncement.tsx
@@ -4,6 +4,7 @@ import {Image} from 'expo-image'
import {Trans, useLingui} from '@lingui/react/macro'
import {useCallOnce} from '#/lib/once'
+import {Nux} from '#/state/queries/nuxs'
import {atoms as a, useTheme, web} from '#/alf'
import {themes} from '#/alf/themes'
import {Button, ButtonText} from '#/components/Button'
@@ -13,6 +14,7 @@ import {Sparkle_Stroke2_Corner0_Rounded as SparkleIcon} from '#/components/icons
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
import {IS_E2E, IS_NATIVE, IS_WEB} from '#/env'
+import {InviteFriendsDialog} from '#/features/inviteFriends'
import {createIsEnabledCheck} from './utils'
export const enabled = createIsEnabledCheck(() => {
@@ -22,6 +24,30 @@ export const enabled = createIsEnabledCheck(() => {
})
export function InviteFriendsAnnouncement() {
+ const {activeNux} = useNuxDialogContext()
+ /*
+ * The invite dialog's control lives here (outside the activeNux
+ * conditional) so the dialog survives the announcement being dismissed
+ * during the "Try it" handoff - the native bottom sheet cannot hand off to
+ * a second sheet that unmounts along with the first.
+ */
+ const inviteFriendsControl = Dialog.useDialogControl()
+
+ return (
+ <>
+ {activeNux === Nux.InviteFriendsAnnouncement && (
+
+ )}
+
+ >
+ )
+}
+
+function Announcement({
+ inviteFriendsControl,
+}: {
+ inviteFriendsControl: Dialog.DialogControlProps
+}) {
const t = useTheme()
const {t: l} = useLingui()
const ax = useAnalytics()
@@ -43,15 +69,18 @@ export function InviteFriendsAnnouncement() {
const onPressTryIt = useCallback(() => {
ax.metric('invite:nux:tryItPressed', {})
- // 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 frame so the announcement's teardown completes first.
+ // Close this announcement (which dismisses the NUX and unmounts this
+ // component), then open the invite dialog. Its control lives in the
+ // parent, which stays mounted, so the dialog survives the dismissal.
+ // Defer the open to the next frame so the announcement's teardown
+ // completes first.
control.close(() => {
- requestAnimationFrame(() => nuxDialogs.openInviteFriends())
+ requestAnimationFrame(() => {
+ ax.metric('invite:dialog:open', {logContext: 'NuxAnnouncement'})
+ inviteFriendsControl.open()
+ })
})
- }, [ax, control, nuxDialogs])
+ }, [ax, control, inviteFriendsControl])
return (
<>
diff --git a/src/components/dialogs/nuxs/index.tsx b/src/components/dialogs/nuxs/index.tsx
index 444f050406..41daa8b9f0 100644
--- a/src/components/dialogs/nuxs/index.tsx
+++ b/src/components/dialogs/nuxs/index.tsx
@@ -18,7 +18,6 @@ import {
import {useProfileQuery} from '#/state/queries/profile'
import {type SessionAccount, useSession} from '#/state/session'
import {useOnboardingState} from '#/state/shell'
-import * as Dialog from '#/components/Dialog'
import {
DraftsAnnouncement,
enabled as isDraftsAnnouncementEnabled,
@@ -30,19 +29,11 @@ import {
import {isSnoozed, snooze, unsnooze} from '#/components/dialogs/nuxs/snoozing'
import {type EnabledCheckProps} from '#/components/dialogs/nuxs/utils'
import {useAnalytics} from '#/analytics'
-import {InviteFriendsDialog} from '#/features/inviteFriends'
import {useGeolocation} from '#/geolocation'
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: {
@@ -62,7 +53,6 @@ const queuedNuxs: {
const Context = createContext({
activeNux: undefined,
dismissActiveNux: () => {},
- openInviteFriends: () => {},
})
Context.displayName = 'NuxDialogContext'
@@ -115,7 +105,6 @@ function Inner({
const [activeNux, setActiveNux] = useState()
const {mutateAsync: saveNux} = useSaveNux()
const {mutate: resetNuxs} = useResetNuxs()
- const inviteFriendsControl = Dialog.useDialogControl()
const snoozeNuxDialog = useCallback(() => {
snooze()
@@ -199,26 +188,19 @@ function Inner({
return {
activeNux,
dismissActiveNux,
- openInviteFriends: () => {
- ax.metric('invite:dialog:open', {logContext: 'NuxAnnouncement'})
- inviteFriendsControl.open()
- },
}
- }, [ax, activeNux, dismissActiveNux, inviteFriendsControl])
+ }, [activeNux, dismissActiveNux])
return (
{/*For example, activeNux === Nux.NeueTypography && */}
{activeNux === Nux.DraftsAnnouncement && }
- {activeNux === Nux.InviteFriendsAnnouncement && (
-
- )}
{/*
- Mounted persistently (not inside the announcement) so the invite dialog
- survives the announcement NUX being dismissed during the "Try it"
- handoff.
+ Mounted unconditionally: it gates the announcement on `activeNux`
+ internally, so it can keep the invite-friends dialog mounted across
+ the announcement's dismissal during the "Try it" handoff.
*/}
-
+
)
}