-
Notifications
You must be signed in to change notification settings - Fork 5
Description
🔎 Summary
Current reminders may be missed when the tab is unfocused or connectivity is flaky. This epic delivers reliable, OS-level notifications using Web Push + Service Worker, adds offline-first data entry with background sync, and introduces snooze / DND / timezone-smart scheduling—so medication & appointment alerts fire on time, even if the browser is closed.
💡 Goals
- Deliver push notifications that fire when the app isn’t open (desktop + mobile browsers).
- Support offline create/edit of meds/appointments/logs and sync later.
- Add Snooze (e.g., +10m, +30m, +2h), Do-Not-Disturb windows, and travel/timezone awareness.
- Maintain a single source of truth in Postgres; clients are resilient with optimistic updates + retry queues.
✅ Acceptance Criteria
-
Registration & Permissions
- Users can opt-in to notifications; permission state is reflected in Settings → Notifications.
- Each device registers a Web Push subscription (VAPID) and is listed under “My Devices”.
-
Push Delivery
- Medication/appointment alerts fire via OS notification even if all SymptomSync tabs are closed.
- Action buttons: Mark Taken / Snooze / Dismiss update the backend within ≤ 5s (online) or queue offline.
-
Offline-First
- Creating/editing meds, appointments, and logs works offline; changes sync automatically when back online.
- Background Sync retries until success; local UI shows “Pending sync” status.
-
Scheduling
- Reminders respect user timezone and DST moves; when timezone changes, next events re-computed.
- DND window (e.g., 22:00–07:00) defers alerts until the window ends, with a single consolidated reminder.
-
Settings
- Per-type toggles: Meds / Appointments / Logs.
- Global volume: All / Critical-only / None.
- Snooze presets configurable.
-
Observability
- Dashboard (admin-only): send success %, delivery latency p50/p95, opt-in rate, offline queue depth.
🧱 Architecture / Approach
1) Service Worker (frontend/web)
-
sw.js:- Handles
push→showNotification()with actions (taken,snooze,dismiss). - Handles
notificationclick→ focus/open client; postMessage action → page; fallback to fetch to API. - Registers Background Sync:
sync-reminder-queue.
- Handles
2) Web Push (Supabase Edge Functions or Next.js API route)
-
Use VAPID keys (
VAPID_PUBLIC_KEY,VAPID_PRIVATE_KEY) stored in Supabase secrets. -
Table
user_push_subscriptionsstores{ user_id, endpoint, p256dh, auth, ua, last_seen_at }. -
Reminders scanner job (Supabase cron, every minute):
- Query due reminders (meds/appointments) honoring timezone & DND.
- Send push to each active device; record send status in
notification_events.
3) Offline Queue (IndexedDB)
idbstore for pending mutations (create/update meds/appts/logs, reminder actions).- On regain connectivity or Background Sync, flush queue → Supabase (REST/RPC).
4) Timezone & DND
- Persist
user_settings:timezone,dnd_start,dnd_end,snooze_presets. - On client TZ change, call RPC to recompute
next_fire_atfor upcoming schedules.
🗄 Database (Supabase / Postgres)
-
user_push_subscriptionsid uuid pk,user_id uuid fk,endpoint text unique,p256dh text,auth text,ua text,created_at,last_seen_at- Indexes:
(user_id),(last_seen_at desc)
-
notification_eventsid uuid pk,user_id,entity_type enum('med','appt'),entity_id uuid,scheduled_at timestamptz,sent_at timestamptz,status enum('queued','sent','failed'),error text?- Indexes:
(user_id, scheduled_at),(status)
-
user_settings(extend if exists)timezone text,dnd_start time,dnd_end time,snooze_presets int[] default {10,30,120},notify_meds bool,notify_appts bool,notify_logs bool
RLS: row ownership by
auth.uid(); separate service role for cron/edge function.
🔌 APIs / Edge Functions
-
POST /api/notifications/subscribe→ save subscription (upsert by endpoint). -
POST /api/notifications/unsubscribe→ delete by endpoint. -
POST /api/notifications/action→{type:'taken'|'snooze'|'dismiss', entityType, entityId, context}snoozeshiftsnext_fire_atby preset;takenwrites adherence event + schedules next dose.
-
POST /api/admin/notifications/test(admin only) → ping selected user/device. -
CRON function
reminders_dispatcher: scans due items; sends viaweb-push.
🖥 Frontend (Next.js + Shadcn + Tailwind)
-
Settings → Notifications
- Permission state badge; toggle per type; DND editor; Snooze presets (chips).
- “My Devices” list with last seen date; revoke device.
-
Medication card & Appointment row
- “Enable reminders” switch; “Snooze” quick actions.
- Status chips:
Due,Snoozed until 10:30,Done.
-
Service Worker utils
registerSW(),subscribePush(),queueMutation(),flushQueue(); toasts for offline/queued/synced.
🔐 Security
- Validate subscription ownership; rate-limit subscribe/unsubscribe.
- Sign reminder actions with session/JWT; verify on API/edge function.
- Sanitize notification payloads (no user-generated HTML).
📊 Observability
- Log per-send results to
notification_events; aggregate for KPIs. - Metrics tiles (admin): opt-in %, delivery p95, fail % by UA, avg queue flush latency.
🧪 Testing
- Unit: SW notification action handlers; timezone rollovers; DND deferral; snooze math.
- Integration: Subscribe/unsubscribe flow; reminder scan → push → action update lifecycle.
- E2E (Playwright): Permissions prompt, offline edits, background sync, action buttons in notifications (where supported).
- Load: 10k reminders/min scan stays < 500ms DB time; push queue throughput sustained.
🚀 Rollout Plan
- Phase 1 (behind flag): subscription UI + test push + settings page.
- Phase 2: meds only; snooze + taken actions; offline queue for meds.
- Phase 3: appointments + DND; background sync for all entities.
- Phase 4: admin metrics dashboard; gradual default-on.
⚠️ Risks & Mitigations
- iOS Web Push limitations: support iOS 16.4+ only; show “Add to Home Screen” CTA if needed; fall back to in-app toasts.
- User fatigue: DND by default (22:00–07:00), daily digest option, easy per-type toggles.
- Rate limits: batch sends; exponential backoff; dead-letter log (
failed) for replays.
📋 Tasks
- DB: create
user_push_subscriptions,notification_events; extenduser_settings; RLS - Backend: subscribe/unsubscribe/action routes (or Supabase Edge functions)
- CRON:
reminders_dispatcherjob; batching + retries - Frontend: Settings UI; devices list; permission gating
- SW: push handler, actions, background sync, IDB queue
- Meds/Appts UIs: snooze/taken/dismiss; status chips
- Analytics: metrics aggregation + admin tiles
- Docs: README “Notifications & Offline” section; env vars; VAPID keygen steps
- Tests: unit/integration/E2E + load scripts
- Rollout: feature flag + progressive enablement
Env vars (new):
VAPID_PUBLIC_KEY,VAPID_PRIVATE_KEYREMINDER_SCAN_CRON="* * * * *"(or Supabase schedule)PUSH_BATCH_SIZE=100,PUSH_MAX_RETRIES=3
This epic removes “missed reminder” edge cases, makes SymptomSync truly set-and-forget, and lays a solid PWA foundation for future features (adherence streaks, caregiver sharing, and refill nudges).
Metadata
Metadata
Assignees
Labels
Projects
Status