Bluesky app fork with some witchin' additions 💫

Verify email reminders (#4510)

* Clarify intent

* Increase email reminder period to once per day

* Fallback

* Snooze immediately after account creation, prevent showing right after signup

* Fix e2e test exports

* Remove redundant check

* Better simple date generation

* Replace in DateField

* Use non-string comparison

* Revert change to unrelated code

* Also parse

* Remove side effect

authored by

Eric Bailey and committed by
GitHub
32b40631 853c32b4

+53 -27
+2 -2
src/Navigation.tsx
··· 54 54 import {useUnreadNotifications} from './state/queries/notifications/unread' 55 55 import {useSession} from './state/session' 56 56 import { 57 - setEmailConfirmationRequested, 58 57 shouldRequestEmailConfirmation, 58 + snoozeEmailConfirmationPrompt, 59 59 } from './state/shell/reminders' 60 60 import {AccessibilitySettingsScreen} from './view/screens/AccessibilitySettings' 61 61 import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines' ··· 585 585 586 586 if (currentAccount && shouldRequestEmailConfirmation(currentAccount)) { 587 587 openModal({name: 'verify-email', showReminder: true}) 588 - setEmailConfirmationRequested() 588 + snoozeEmailConfirmationPrompt() 589 589 } 590 590 } 591 591
+11
src/lib/strings/time.ts
··· 19 19 } 20 20 return age 21 21 } 22 + 23 + /** 24 + * Compares two dates by year, month, and day only 25 + */ 26 + export function simpleAreDatesEqual(a: Date, b: Date): boolean { 27 + return ( 28 + a.getFullYear() === b.getFullYear() && 29 + a.getMonth() === b.getMonth() && 30 + a.getDate() === b.getDate() 31 + ) 32 + }
+8
src/state/session/agent.ts
··· 11 11 import {tryFetchGates} from '#/lib/statsig/statsig' 12 12 import {getAge} from '#/lib/strings/time' 13 13 import {logger} from '#/logger' 14 + import {snoozeEmailConfirmationPrompt} from '#/state/shell/reminders' 14 15 import { 15 16 configureModerationForAccount, 16 17 configureModerationForGuest, ··· 189 190 } 190 191 } else { 191 192 agent.setPersonalDetails({birthDate: birthDate.toISOString()}) 193 + } 194 + 195 + try { 196 + // snooze first prompt after signup, defer to next prompt 197 + snoozeEmailConfirmationPrompt() 198 + } catch (e: any) { 199 + logger.error(e, {context: `session: failed snoozeEmailConfirmationPrompt`}) 192 200 } 193 201 194 202 return prepareAgent(agent, gates, moderation, onSessionChange)
+1 -3
src/state/shell/reminders.e2e.ts
··· 1 - export function init() {} 2 - 3 1 export function shouldRequestEmailConfirmation() { 4 2 return false 5 3 } 6 4 7 - export function setEmailConfirmationRequested() {} 5 + export function snoozeEmailConfirmationPrompt() {}
+31 -22
src/state/shell/reminders.ts
··· 1 + import {simpleAreDatesEqual} from '#/lib/strings/time' 2 + import {logger} from '#/logger' 1 3 import * as persisted from '#/state/persisted' 2 - import {toHashCode} from 'lib/strings/helpers' 4 + import {SessionAccount} from '../session' 3 5 import {isOnboardingActive} from './onboarding' 4 - import {SessionAccount} from '../session' 5 6 6 7 export function shouldRequestEmailConfirmation(account: SessionAccount) { 7 - if (!account) { 8 - return false 8 + // ignore logged out 9 + if (!account) return false 10 + // ignore confirmed accounts, this is the success state of this reminder 11 + if (account.emailConfirmed) return false 12 + // wait for onboarding to complete 13 + if (isOnboardingActive()) return false 14 + 15 + const snoozedAt = persisted.get('reminders').lastEmailConfirm 16 + const today = new Date() 17 + 18 + logger.debug('Checking email confirmation reminder', { 19 + today, 20 + snoozedAt, 21 + }) 22 + 23 + // never been snoozed, new account 24 + if (!snoozedAt) { 25 + return true 9 26 } 10 - if (account.emailConfirmed) { 11 - return false 12 - } 13 - if (isOnboardingActive()) { 14 - return false 15 - } 16 - // only prompt once 17 - if (persisted.get('reminders').lastEmailConfirm) { 18 - return false 19 - } 20 - const today = new Date() 21 - // shard the users into 2 day of the week buckets 22 - // (this is to avoid a sudden influx of email updates when 23 - // this feature rolls out) 24 - const code = toHashCode(account.did) % 7 25 - if (code !== today.getDay() && code !== (today.getDay() + 1) % 7) { 27 + 28 + // already snoozed today 29 + if (simpleAreDatesEqual(new Date(Date.parse(snoozedAt)), new Date())) { 26 30 return false 27 31 } 32 + 28 33 return true 29 34 } 30 35 31 - export function setEmailConfirmationRequested() { 36 + export function snoozeEmailConfirmationPrompt() { 37 + const lastEmailConfirm = new Date().toISOString() 38 + logger.debug('Snoozing email confirmation reminder', { 39 + snoozedAt: lastEmailConfirm, 40 + }) 32 41 persisted.write('reminders', { 33 42 ...persisted.get('reminders'), 34 - lastEmailConfirm: new Date().toISOString(), 43 + lastEmailConfirm, 35 44 }) 36 45 }