From 853c32b4d8cfe86ed02c688c32fc99e788c33838 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Tue, 18 Jun 2024 14:21:55 -0700 Subject: [PATCH 1/2] Bump labeler limit to 20 (#4565) * Increase the labeler subscription limit to 20 * Bump @atproto/api@0.12.20 * Fix: correctly count labeler subscriptions --------- Co-authored-by: dan --- package.json | 2 +- src/screens/Profile/Header/ProfileHeaderLabeler.tsx | 6 +++--- yarn.lock | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 4178369035..3e27a81988 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "open-analyzer": "EXPO_PUBLIC_OPEN_ANALYZER=1 yarn build-web" }, "dependencies": { - "@atproto/api": "^0.12.19", + "@atproto/api": "^0.12.20", "@bam.tech/react-native-image-resizer": "^3.0.4", "@braintree/sanitize-url": "^6.0.2", "@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet", 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/yarn.lock b/yarn.lock index 67ff046cb5..700ddfe072 100644 --- a/yarn.lock +++ b/yarn.lock @@ -34,10 +34,10 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@atproto/api@^0.12.19": - version "0.12.19" - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.12.19.tgz#6d842269b6b9cd3fc5864e12824d4fb04cc033cf" - integrity sha512-dsiTpjqBhjGwNW/qG/tLSgUQnmOSvd8hsQr5d8GCUDGK2AEHWl0KNgLPbwxIBEIo8Jg9NHsvqV7BMoix8YreIg== +"@atproto/api@^0.12.20": + version "0.12.20" + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.12.20.tgz#2cada08c24bc61eb1775ee4c8010c7ed9dc5d6f3" + integrity sha512-nt7ZKUQL9j2yQ3tmCCueiIuc0FwdxZYn2fXdLYqltuxlaO5DmaqqULMBKeYJLq4GbvVl/G+ikPJccoSaMWDYOg== dependencies: "@atproto/common-web" "^0.3.0" "@atproto/lexicon" "^0.4.0" From 32b40631851c3c3f5ae2400c4bb89e009e71a9da Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 18 Jun 2024 17:21:34 -0500 Subject: [PATCH 2/2] 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 --- src/Navigation.tsx | 4 +-- src/lib/strings/time.ts | 11 +++++++ src/state/session/agent.ts | 8 +++++ src/state/shell/reminders.e2e.ts | 4 +-- src/state/shell/reminders.ts | 53 +++++++++++++++++++------------- 5 files changed, 53 insertions(+), 27 deletions(-) diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 67b89e2627..5d4ba0e3f7 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -54,8 +54,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' @@ -585,7 +585,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/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, }) }