diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 4a55a43256..06f7ee3557 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -56,8 +56,8 @@ import {useModalControls} from './state/modals' import {useUnreadNotifications} from './state/queries/notifications/unread' import {useSession} from './state/session' import { - setEmailConfirmationRequested, shouldRequestEmailConfirmation, + snoozeEmailConfirmationPrompt, } from './state/shell/reminders' import {AccessibilitySettingsScreen} from './view/screens/AccessibilitySettings' import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines' @@ -597,7 +597,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) { if (currentAccount && shouldRequestEmailConfirmation(currentAccount)) { openModal({name: 'verify-email', showReminder: true}) - setEmailConfirmationRequested() + snoozeEmailConfirmationPrompt() } } diff --git a/src/lib/strings/time.ts b/src/lib/strings/time.ts index 1194e0240c..bfefea9bc3 100644 --- a/src/lib/strings/time.ts +++ b/src/lib/strings/time.ts @@ -19,3 +19,14 @@ export function getAge(birthDate: Date): number { } return age } + +/** + * Compares two dates by year, month, and day only + */ +export function simpleAreDatesEqual(a: Date, b: Date): boolean { + return ( + a.getFullYear() === b.getFullYear() && + a.getMonth() === b.getMonth() && + a.getDate() === b.getDate() + ) +} diff --git a/src/screens/Profile/Header/ProfileHeaderLabeler.tsx b/src/screens/Profile/Header/ProfileHeaderLabeler.tsx index 6588eb2e1d..d266decb32 100644 --- a/src/screens/Profile/Header/ProfileHeaderLabeler.tsx +++ b/src/screens/Profile/Header/ProfileHeaderLabeler.tsx @@ -82,7 +82,7 @@ let ProfileHeaderLabeler = ({ preferences?.moderationPrefs.labelers.find(l => l.did === profile.did) const canSubscribe = isSubscribed || - (preferences ? preferences?.moderationPrefs.labelers.length < 9 : false) + (preferences ? preferences?.moderationPrefs.labelers.length <= 20 : false) const {mutateAsync: likeMod, isPending: isLikePending} = useLikeMutation() const {mutateAsync: unlikeMod, isPending: isUnlikePending} = useUnlikeMutation() @@ -328,8 +328,8 @@ function CantSubscribePrompt({ Unable to subscribe - We're sorry! You can only subscribe to ten labelers, and you've - reached your limit of ten. + We're sorry! You can only subscribe to twenty labelers, and you've + reached your limit of twenty. diff --git a/src/state/session/agent.ts b/src/state/session/agent.ts index 48f5614bd8..6afb3af88c 100644 --- a/src/state/session/agent.ts +++ b/src/state/session/agent.ts @@ -11,6 +11,7 @@ import { import {tryFetchGates} from '#/lib/statsig/statsig' import {getAge} from '#/lib/strings/time' import {logger} from '#/logger' +import {snoozeEmailConfirmationPrompt} from '#/state/shell/reminders' import { configureModerationForAccount, configureModerationForGuest, @@ -191,6 +192,13 @@ export async function createAgentAndCreateAccount( agent.setPersonalDetails({birthDate: birthDate.toISOString()}) } + try { + // snooze first prompt after signup, defer to next prompt + snoozeEmailConfirmationPrompt() + } catch (e: any) { + logger.error(e, {context: `session: failed snoozeEmailConfirmationPrompt`}) + } + return prepareAgent(agent, gates, moderation, onSessionChange) } diff --git a/src/state/shell/reminders.e2e.ts b/src/state/shell/reminders.e2e.ts index e8c12792ac..94809a680d 100644 --- a/src/state/shell/reminders.e2e.ts +++ b/src/state/shell/reminders.e2e.ts @@ -1,7 +1,5 @@ -export function init() {} - export function shouldRequestEmailConfirmation() { return false } -export function setEmailConfirmationRequested() {} +export function snoozeEmailConfirmationPrompt() {} diff --git a/src/state/shell/reminders.ts b/src/state/shell/reminders.ts index ee924eb001..db6ee9391b 100644 --- a/src/state/shell/reminders.ts +++ b/src/state/shell/reminders.ts @@ -1,36 +1,45 @@ +import {simpleAreDatesEqual} from '#/lib/strings/time' +import {logger} from '#/logger' import * as persisted from '#/state/persisted' -import {toHashCode} from 'lib/strings/helpers' -import {isOnboardingActive} from './onboarding' import {SessionAccount} from '../session' +import {isOnboardingActive} from './onboarding' export function shouldRequestEmailConfirmation(account: SessionAccount) { - if (!account) { - return false - } - if (account.emailConfirmed) { - return false - } - if (isOnboardingActive()) { - return false - } - // only prompt once - if (persisted.get('reminders').lastEmailConfirm) { - return false - } + // ignore logged out + if (!account) return false + // ignore confirmed accounts, this is the success state of this reminder + if (account.emailConfirmed) return false + // wait for onboarding to complete + if (isOnboardingActive()) return false + + const snoozedAt = persisted.get('reminders').lastEmailConfirm const today = new Date() - // shard the users into 2 day of the week buckets - // (this is to avoid a sudden influx of email updates when - // this feature rolls out) - const code = toHashCode(account.did) % 7 - if (code !== today.getDay() && code !== (today.getDay() + 1) % 7) { + + logger.debug('Checking email confirmation reminder', { + today, + snoozedAt, + }) + + // never been snoozed, new account + if (!snoozedAt) { + return true + } + + // already snoozed today + if (simpleAreDatesEqual(new Date(Date.parse(snoozedAt)), new Date())) { return false } + return true } -export function setEmailConfirmationRequested() { +export function snoozeEmailConfirmationPrompt() { + const lastEmailConfirm = new Date().toISOString() + logger.debug('Snoozing email confirmation reminder', { + snoozedAt: lastEmailConfirm, + }) persisted.write('reminders', { ...persisted.get('reminders'), - lastEmailConfirm: new Date().toISOString(), + lastEmailConfirm, }) }