Study: lesson reminders on by default for new accounts; one-time backfill turns them on for existing accounts; v2.46.0
Publish Container / build (push) Successful in 16m7s

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-19 16:53:03 -04:00
1 parent a11adf8ad2
commit 30a7e8a6cb
5 files changed
+27 -2

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "siteforge", "name": "siteforge",
"private": true, "private": true,
"version": "2.45.3", "version": "2.46.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
+6
View File
@@ -44,6 +44,7 @@ import {
scheduleReengagementEmails, scheduleReengagementEmails,
scheduleWeeklyDigestEmails, scheduleWeeklyDigestEmails,
backfillCompletedStudies, backfillCompletedStudies,
backfillReminderDefault,
} from './server/study-helpers.js' } from './server/study-helpers.js'
import { recordVisitor } from './server/routes/analytics.js' import { recordVisitor } from './server/routes/analytics.js'
@@ -210,6 +211,11 @@ Promise.all([
.catch(err => { .catch(err => {
console.error('[study] completed-study backfill failed:', err) console.error('[study] completed-study backfill failed:', err)
}) })
.then(() => backfillReminderDefault())
.then(updated => { if (updated > 0) console.log(`[study] turned lesson reminders on for ${updated} existing accounts`) })
.catch(err => {
console.error('[study] reminder-default backfill failed:', err)
})
.finally(() => { .finally(() => {
createBackupSnapshot('startup').catch(() => {}) createBackupSnapshot('startup').catch(() => {})
setInterval(() => { setInterval(() => {
+1
View File
@@ -1421,6 +1421,7 @@ export function sanitizeStudyUsers(value) {
remindersPausedUntil: typeof item?.remindersPausedUntil === 'string' ? item.remindersPausedUntil : null, remindersPausedUntil: typeof item?.remindersPausedUntil === 'string' ? item.remindersPausedUntil : null,
reminderFrequency: item?.reminderFrequency === 'weekly' ? 'weekly' : 'immediate', reminderFrequency: item?.reminderFrequency === 'weekly' ? 'weekly' : 'immediate',
weeklyDigestSentAt: typeof item?.weeklyDigestSentAt === 'string' ? item.weeklyDigestSentAt : null, weeklyDigestSentAt: typeof item?.weeklyDigestSentAt === 'string' ? item.weeklyDigestSentAt : null,
remindersDefaultedAt: typeof item?.remindersDefaultedAt === 'string' ? item.remindersDefaultedAt : null,
}) })
} }
return out.slice(0, MAX_STUDY_USERS) return out.slice(0, MAX_STUDY_USERS)
+2 -1
View File
@@ -117,7 +117,8 @@ export function register(app) {
passwordHash: hashStudyPassword(password), passwordHash: hashStudyPassword(password),
displayName, displayName,
subscribeNewsletter: subscribe, subscribeNewsletter: subscribe,
studyRemindersEnabled: false, studyRemindersEnabled: true,
remindersDefaultedAt: now,
pendingEmailChange: null, pendingEmailChange: null,
emailVerified: false, emailVerified: false,
pendingEmailVerification: { pendingEmailVerification: {
+17
View File
@@ -736,6 +736,23 @@ export function extractResendMessageId(result) {
return '' return ''
} }
// One-off pass: lesson reminders are now on by default, so switch them on for accounts that
// predate the default. Each account is marked as it is handled, so a later opt-out sticks.
export async function backfillReminderDefault() {
const now = new Date().toISOString()
let updated = 0
for (const user of state.studyUsers) {
if (user.remindersDefaultedAt) continue
if (user.studyRemindersEnabled !== true) {
user.studyRemindersEnabled = true
updated++
}
user.remindersDefaultedAt = now
}
if (state.studyUsers.some(user => user.remindersDefaultedAt === now)) queueStudyUsersWrite()
return updated
}
// ── Study reminder scheduler ─────────────────────────────────────────────── // ── Study reminder scheduler ───────────────────────────────────────────────
export async function scheduleStudyReminders(sendStudyReminderEmail) { export async function scheduleStudyReminders(sendStudyReminderEmail) {