feat(engagement): localize limits and streaks

Use reported user timezones for daily response limits, expose couple streak state and reminders, and improve swipe and live-drawing synchronization with focused tests and documentation.
This commit is contained in:
jjbrunton committed 2026-08-28 20:28:31 +01:00
1 parent 61ec3692b7
commit b60c788362
42 files changed
+1308 -217

No files matched your search

@@ -16,6 +16,7 @@ interface PreferencesRow extends QueryResultRow {
pack_changes_enabled: boolean;
new_packs_enabled: boolean;
streak_milestones_enabled: boolean;
streak_reminders_enabled: boolean;
weekly_summary_enabled: boolean;
unpaired_reminders_enabled: boolean;
catchup_reminders_enabled: boolean;
@@ -49,6 +50,7 @@ const profileColumns: Record<keyof ProfileUpdate, string> = {
onboarding_version: 'onboarding_version',
public_key_jwk: 'public_key_jwk',
push_token: 'push_token',
timezone: 'timezone',
};
function toPreferences(row: PreferencesRow): NotificationPreferences {
@@ -61,6 +63,7 @@ function toPreferences(row: PreferencesRow): NotificationPreferences {
pack_changes_enabled: row.pack_changes_enabled,
new_packs_enabled: row.new_packs_enabled,
streak_milestones_enabled: row.streak_milestones_enabled,
streak_reminders_enabled: row.streak_reminders_enabled,
weekly_summary_enabled: row.weekly_summary_enabled,
unpaired_reminders_enabled: row.unpaired_reminders_enabled,
catchup_reminders_enabled: row.catchup_reminders_enabled,
@@ -18,6 +18,8 @@ const profileUpdateSchema = z.object({
onboarding_version: z.number().int().min(0).optional(),
public_key_jwk: z.record(z.unknown()).nullable().optional(),
push_token: z.string().trim().min(1).max(512).nullable().optional(),
// IANA zone from the client; the daily response limit buckets on the user's local day.
timezone: z.string().trim().min(1).max(64).nullable().optional(),
}).strict().refine((body) => Object.keys(body).length > 0, 'At least one profile field is required');
const preferenceSchema = z.object({
@@ -6,6 +6,7 @@ export const notificationPreferenceKeys = [
'pack_changes_enabled',
'new_packs_enabled',
'streak_milestones_enabled',
'streak_reminders_enabled',
'weekly_summary_enabled',
'unpaired_reminders_enabled',
'catchup_reminders_enabled',
@@ -30,6 +31,7 @@ export interface ProfileUpdate {
onboarding_version?: number;
public_key_jwk?: Record<string, unknown> | null;
push_token?: string | null;
timezone?: string | null;
}
export interface FeedbackSubmission {