diff options
author | Samuel Newman <mozzius@protonmail.com> | 2025-07-02 00:36:04 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-07-01 14:36:04 -0700 |
commit | bc072570d27e1f397406daea355570f5aec95647 (patch) | |
tree | 0d698c0bababd9b5e221df763a1ab15744ebdb71 /src/components/dialogs/nuxs | |
parent | 8f9a8ddce022e328b07b793c3f1500e1c423ef73 (diff) | |
download | voidsky-bc072570d27e1f397406daea355570f5aec95647.tar.zst |
Activity notification settings (#8485)
Co-authored-by: Eric Bailey <git@esb.lol> Co-authored-by: Samuel Newman <mozzius@protonmail.com> Co-authored-by: hailey <me@haileyok.com>
Diffstat (limited to 'src/components/dialogs/nuxs')
-rw-r--r-- | src/components/dialogs/nuxs/ActivitySubscriptions.tsx | 177 | ||||
-rw-r--r-- | src/components/dialogs/nuxs/index.tsx | 19 | ||||
-rw-r--r-- | src/components/dialogs/nuxs/utils.ts | 15 |
3 files changed, 202 insertions, 9 deletions
diff --git a/src/components/dialogs/nuxs/ActivitySubscriptions.tsx b/src/components/dialogs/nuxs/ActivitySubscriptions.tsx new file mode 100644 index 000000000..b9f3979ed --- /dev/null +++ b/src/components/dialogs/nuxs/ActivitySubscriptions.tsx @@ -0,0 +1,177 @@ +import {useCallback} from 'react' +import {View} from 'react-native' +import {Image} from 'expo-image' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {isWeb} from '#/platform/detection' +import {atoms as a, useTheme, web} from '#/alf' +import {Button, ButtonText} from '#/components/Button' +import * as Dialog from '#/components/Dialog' +import {useNuxDialogContext} from '#/components/dialogs/nuxs' +import {Sparkle_Stroke2_Corner0_Rounded as SparkleIcon} from '#/components/icons/Sparkle' +import {Text} from '#/components/Typography' + +export function ActivitySubscriptionsNUX() { + const t = useTheme() + const {_} = useLingui() + const nuxDialogs = useNuxDialogContext() + const control = Dialog.useDialogControl() + + Dialog.useAutoOpen(control) + + const onClose = useCallback(() => { + nuxDialogs.dismissActiveNux() + }, [nuxDialogs]) + + return ( + <Dialog.Outer control={control} onClose={onClose}> + <Dialog.Handle /> + + <Dialog.ScrollableInner + label={_(msg`Introducing activity notifications`)} + style={[web({maxWidth: 400})]} + contentContainerStyle={[ + { + paddingTop: 0, + paddingLeft: 0, + paddingRight: 0, + }, + ]}> + <View + style={[ + a.align_center, + a.overflow_hidden, + t.atoms.bg_contrast_25, + { + gap: isWeb ? 16 : 24, + paddingTop: isWeb ? 24 : 48, + borderTopLeftRadius: a.rounded_md.borderRadius, + borderTopRightRadius: a.rounded_md.borderRadius, + }, + ]}> + <View + style={[ + a.pl_sm, + a.pr_md, + a.py_sm, + a.rounded_full, + a.flex_row, + a.align_center, + a.gap_xs, + { + backgroundColor: t.palette.primary_100, + }, + ]}> + <SparkleIcon fill={t.palette.primary_800} size="sm" /> + <Text + style={[ + a.font_bold, + { + color: t.palette.primary_800, + }, + ]}> + <Trans>New Feature</Trans> + </Text> + </View> + + <View style={[a.relative, a.w_full]}> + <View + style={[ + a.absolute, + t.atoms.bg_contrast_25, + t.atoms.shadow_md, + { + shadowOpacity: 0.4, + top: 5, + bottom: 0, + left: '17%', + right: '17%', + width: '66%', + borderTopLeftRadius: 40, + borderTopRightRadius: 40, + }, + ]} + /> + <View + style={[ + a.overflow_hidden, + { + aspectRatio: 398 / 228, + }, + ]}> + <Image + accessibilityIgnoresInvertColors + source={require('../../../../assets/images/activity_notifications_announcement.webp')} + style={[ + a.w_full, + { + aspectRatio: 398 / 268, + }, + ]} + alt={_( + msg`A screenshot of a profile page with a bell icon next to the follow button, indicating the new activity notifications feature.`, + )} + /> + </View> + </View> + </View> + <View + style={[ + a.align_center, + a.px_xl, + isWeb ? [a.pt_xl, a.gap_xl, a.pb_sm] : [a.pt_3xl, a.gap_3xl], + ]}> + <View style={[a.gap_md, a.align_center]}> + <Text + style={[ + a.text_3xl, + a.leading_tight, + a.font_heavy, + a.text_center, + { + fontSize: isWeb ? 28 : 32, + maxWidth: 300, + }, + ]}> + <Trans>Get notified when someone posts</Trans> + </Text> + <Text + style={[ + a.text_md, + a.leading_snug, + a.text_center, + { + maxWidth: 340, + }, + ]}> + <Trans> + You can now choose to be notified when specific people post. If + there’s someone you want timely updates from, go to their + profile and find the new bell icon near the follow button. + </Trans> + </Text> + </View> + + {!isWeb && ( + <Button + label={_(msg`Close`)} + size="large" + variant="solid" + color="primary" + onPress={() => { + control.close() + }} + style={[a.w_full, {maxWidth: 280}]}> + <ButtonText> + <Trans>Close</Trans> + </ButtonText> + </Button> + )} + </View> + + <Dialog.Close /> + </Dialog.ScrollableInner> + </Dialog.Outer> + ) +} diff --git a/src/components/dialogs/nuxs/index.tsx b/src/components/dialogs/nuxs/index.tsx index 11377e1de..8096a0141 100644 --- a/src/components/dialogs/nuxs/index.tsx +++ b/src/components/dialogs/nuxs/index.tsx @@ -11,12 +11,12 @@ import { import {useProfileQuery} from '#/state/queries/profile' import {type SessionAccount, useSession} from '#/state/session' import {useOnboardingState} from '#/state/shell' -import {InitialVerificationAnnouncement} from '#/components/dialogs/nuxs/InitialVerificationAnnouncement' +import {ActivitySubscriptionsNUX} from '#/components/dialogs/nuxs/ActivitySubscriptions' /* * NUXs */ import {isSnoozed, snooze, unsnooze} from '#/components/dialogs/nuxs/snoozing' -import {isDaysOld} from '#/components/dialogs/nuxs/utils' +import {isExistingUserAsOf} from '#/components/dialogs/nuxs/utils' type Context = { activeNux: Nux | undefined @@ -33,9 +33,12 @@ const queuedNuxs: { }) => boolean }[] = [ { - id: Nux.InitialVerificationAnnouncement, + id: Nux.ActivitySubscriptions, enabled: ({currentProfile}) => { - return isDaysOld(2, currentProfile.createdAt) + return isExistingUserAsOf( + '2025-07-03T00:00:00.000Z', + currentProfile.createdAt, + ) }, }, ] @@ -111,7 +114,7 @@ function Inner({ } React.useEffect(() => { - if (snoozed) return + if (snoozed) return // comment this out to test if (!nuxs) return for (const {id, enabled} of queuedNuxs) { @@ -119,7 +122,7 @@ function Inner({ // check if completed first if (nux && nux.completed) { - continue + continue // comment this out to test } // then check gate (track exposure) @@ -172,9 +175,7 @@ function Inner({ return ( <Context.Provider value={ctx}> {/*For example, activeNux === Nux.NeueTypography && <NeueTypography />*/} - {activeNux === Nux.InitialVerificationAnnouncement && ( - <InitialVerificationAnnouncement /> - )} + {activeNux === Nux.ActivitySubscriptions && <ActivitySubscriptionsNUX />} </Context.Provider> ) } diff --git a/src/components/dialogs/nuxs/utils.ts b/src/components/dialogs/nuxs/utils.ts index 0cc510484..ba8f0169d 100644 --- a/src/components/dialogs/nuxs/utils.ts +++ b/src/components/dialogs/nuxs/utils.ts @@ -16,3 +16,18 @@ export function isDaysOld(days: number, createdAt?: string) { if (isOldEnough) return true return false } + +export function isExistingUserAsOf(date: string, createdAt?: string) { + /* + * Should never happen because we gate NUXs to only accounts with a valid + * profile and a `createdAt` (see `nuxs/index.tsx`). But if it ever did, the + * account is either old enough to be pre-onboarding, or some failure happened + * during account creation. Fail closed. - esb + */ + if (!createdAt) return false + + const threshold = Date.parse(date) + const then = new Date(createdAt).getTime() + + return then < threshold +} |