style(13-03): apply Prettier formatting across repo

Mechanical reformat — no logic changes. 398 files changed, 19125
insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc
(singleQuote:true, semi:true, tabWidth:2, trailingComma:all,
printWidth:100). Isolated per D-13-08 for reviewability.
This commit is contained in:
Lucas Berger
2026-06-11 20:35:18 -04:00
parent 4bc0445173
commit 982438dc10
398 changed files with 19050 additions and 16382 deletions
@@ -15,26 +15,26 @@ requirements: [NOTIF-01, NOTIF-02, NOTIF-03]
must_haves:
truths:
- "A single master on/off toggle in a Settings sheet (opened from the avatar) enables/disables all FamilySync push notifications (D-09)"
- "On app open, if OS permission is still granted but the push subscription is missing/expired, the app silently re-subscribes — no user action (D-10)"
- "If the OS permission itself was revoked (denied) and notifications were previously enabled, a persistent permission-denied banner appears with OS-specific re-enable instructions (D-10)"
- "The avatar in AppNav (phone + desktop) is a real button opening the Settings sheet (a11y: aria-label, 44px target)"
- 'A single master on/off toggle in a Settings sheet (opened from the avatar) enables/disables all FamilySync push notifications (D-09)'
- 'On app open, if OS permission is still granted but the push subscription is missing/expired, the app silently re-subscribes — no user action (D-10)'
- 'If the OS permission itself was revoked (denied) and notifications were previously enabled, a persistent permission-denied banner appears with OS-specific re-enable instructions (D-10)'
- 'The avatar in AppNav (phone + desktop) is a real button opening the Settings sheet (a11y: aria-label, 44px target)'
artifacts:
- path: "apps/pwa/src/components/SettingsSheet.tsx"
provides: "Settings bottom sheet with the master NotificationToggle (D-09)"
exports: ["SettingsSheet"]
- path: "apps/pwa/src/components/PermissionDeniedBanner.tsx"
provides: "persistent OS-revoked banner with re-enable instructions (D-10)"
exports: ["PermissionDeniedBanner"]
- path: 'apps/pwa/src/components/SettingsSheet.tsx'
provides: 'Settings bottom sheet with the master NotificationToggle (D-09)'
exports: ['SettingsSheet']
- path: 'apps/pwa/src/components/PermissionDeniedBanner.tsx'
provides: 'persistent OS-revoked banner with re-enable instructions (D-10)'
exports: ['PermissionDeniedBanner']
key_links:
- from: "apps/pwa/src/hooks/usePushSubscription.ts"
to: "pushManager.getSubscription"
via: "mount health-check → silent re-subscribe when permission granted but no subscription"
pattern: "getSubscription"
- from: "apps/pwa/src/components/AppNav.tsx"
to: "apps/pwa/src/components/SettingsSheet.tsx"
via: "avatar button onClick opens settings"
pattern: "onOpenSettings"
- from: 'apps/pwa/src/hooks/usePushSubscription.ts'
to: 'pushManager.getSubscription'
via: 'mount health-check → silent re-subscribe when permission granted but no subscription'
pattern: 'getSubscription'
- from: 'apps/pwa/src/components/AppNav.tsx'
to: 'apps/pwa/src/components/SettingsSheet.tsx'
via: 'avatar button onClick opens settings'
pattern: 'onOpenSettings'
---
<objective>
@@ -140,19 +140,21 @@ Output: usePushSubscription gains the mount health-check + permission state; Set
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| Boundary | Description |
| ---------------------------- | ------------------------------------------------------------------------------------------ |
| client permission state → UI | Notification.permission + localStorage drive which surface shows; no server trust involved |
## STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-05-23 | Tampering | silent re-subscribe without permission | mitigate | health-check only re-subscribes when Notification.permission==='granted'; never forces an OS dialog |
| T-05-24 | Information Disclosure | XSS via copy | mitigate | all copy is plain-text JSX children (no dangerouslySetInnerHTML), matching existing InstallPrompt convention |
| T-05-25 | Repudiation | toggle off leaves stale server subscription | mitigate | setEnabled off calls DELETE /api/push/subscription (Plan 05-04) so the server prunes the row |
| Threat ID | Category | Component | Disposition | Mitigation Plan |
| --------- | ---------------------- | ------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------ |
| T-05-23 | Tampering | silent re-subscribe without permission | mitigate | health-check only re-subscribes when Notification.permission==='granted'; never forces an OS dialog |
| T-05-24 | Information Disclosure | XSS via copy | mitigate | all copy is plain-text JSX children (no dangerouslySetInnerHTML), matching existing InstallPrompt convention |
| T-05-25 | Repudiation | toggle off leaves stale server subscription | mitigate | setEnabled off calls DELETE /api/push/subscription (Plan 05-04) so the server prunes the row |
</threat_model>
<verification>
@@ -161,10 +163,11 @@ Output: usePushSubscription gains the mount health-check + permission state; Set
</verification>
<success_criteria>
- Single master toggle (D-09) in an avatar-opened Settings sheet.
- Silent re-subscribe on app open when permission still granted (D-10).
- Permission-denied banner only in the OS-revoked-after-enabled case (D-10), with re-enable instructions.
</success_criteria>
</success_criteria>
<output>
Create `.planning/phases/05-web-push-notifications/05-08-SUMMARY.md` when done.