V1 Web PWA · Surface walkthrough

Shell: Notifications

A per-surface walkthrough of the V1 Web PWA: layout, states, interactions, data, and cross-references.

unspecified
9sections3 minread

On this page

Source: apps/oshun/web/src/design-system/components/Toast/, apps/oshun/web/src/components/NotificationsCenterPanel.tsx, apps/oshun/web/src/components/MessageCenter.tsx, push subscription via PwaBootstrap.tsx/sw.js.

Three notification surfaces: ephemeral toasts (right-now feedback), in-app notifications center (history + unread), and push notifications (off-app re-engagement).

Toasts#

ToastProvider wraps the tree at the root layout level.

  • Variants — info, success, warning, error, action (with button)
  • Position — bottom-right desktop, bottom-center mobile (above bottom-nav)
  • Stacking — up to N visible; older toasts collapse or expire
  • Auto-dismiss — info/success 4 s, warning 6 s, error 10 s, action manual
  • Manual dismiss — close button; keyboard Esc dismisses focused toast
  • Action toast — button performs declared action; auto-dismiss after success
  • Screen readerrole="status" for info/success; role="alert" for error
  • Reduced motionprefers-reduced-motion disables slide animation
  • Focus return — focus returns to triggering element when toast dismissed
  • Don't queue infinitely — if N+ toasts queued, oldest drops

In-app notifications center#

NotificationsCenterPanel.tsx — opened from header bell. Customer-message rows are projected from the customer-message-center store through /v1/notifications; mark-read and dismiss writes flow back through /v1/notifications-write to that same store.

  • Bell in header — unread count badge (cap at "99+"); customer-message count is sourced from the shared inbox snapshot
  • Open — bell click; Esc closes
  • Tabs / filters — all / mentions / system / domain-specific
  • List item — title, preview, timestamp (relative), read/unread state for customer-message notifications
  • Click item — marks read through the notifications-write route and navigates to source for customer-message notifications
  • Mark all read — visible action; bulk operation
  • Empty state — calm copy, no doom font
  • Long list — pagination or virtualization; scroll restored
  • Realtime — new notifications appear without reload (OshunQueryProvider subscription)
  • Offline — last-fetched list visible; "you're offline" hint; no broken loaders

Message center#

MessageCenter.tsx/messages plus a header preview.

  • /messages — full inbox/outbox view (see customer/02-home-discovery/messages.md)
  • Header preview — recent N messages; click to open full view
  • Compose — destination, subject, body; send via BFF; optimistic update
  • Delete / archive — soft-delete with undo toast

Push notifications#

sw.js registers push and notificationclick listeners; subscription managed in PwaBootstrap.

  • Permission request — only after a clear user signal (e.g., toggle in profile/notifications); never on first page load
  • SubscriptionpushManager.subscribe; endpoint POSTed to BFF
  • Permission denied — UI shows "Push is blocked; enable in browser settings"
  • Permission revoked — detect via permissionState; show re-enable hint
  • Push payload received → self.registration.showNotification(title, options)
  • Notification click → focuses existing tab if open, else opens canonical route
  • Notification action buttons (snooze / dismiss / open) trigger correct flow
  • Quiet hours respected if configured
  • Unsubscribe — toggle off in profile; SW unsubscribes; BFF informed

Notification preferences#

Where users configure which channels send which notifications.

  • Profile → Notifications — granular per-channel toggles
  • Categories — system / domain-specific / mentions / digest
  • Per-domain — Tara ritual reminders, Arete habit nudges, Nyx sky-event alerts, Veritas story drops, Nisaba study reminders, Metis class deadlines
  • Quiet hours — start/end time; timezone-aware
  • Save — confirmation toast; settings persist across devices

Realtime delivery#

The notifications center subscribes to a realtime channel (Psyche substrate). Behavior:

  • Connected — new notifications stream in
  • Disconnected — reconnect attempted with backoff
  • Reconnect — fetch missed notifications via REST snapshot

States#

  • No notifications — empty state shown
  • Some unread — badge on bell; bold styling on unread items
  • All read — no badge; subdued styling
  • Offline — last-known state shown; banner hint
  • Push blocked — preferences page shows hint to re-enable

Cross-references#

Open questions / known gaps#

  • Confirm whether mentions across domains live in notifications, messages, or both
  • Document quiet-hours edge cases (DST, traveler timezone)
  • Email/SMS fall-through channels — do they exist in V1?