Files
familysync/.planning/phases/03-event-write-back-pwa-install/03-07-SUMMARY.md
T

8.8 KiB
Raw Blame History


phase: 03-event-write-back-pwa-install plan: 07 subsystem: pwa, frontend tags: [vite-plugin-pwa, service-worker, install-prompt, ios, android, workbox]

Dependency graph

requires:

  • phase: 03-event-write-back-pwa-install plan: 01 provides: vite-plugin-pwa installed in apps/pwa

provides:

  • VitePWA manifest + service worker with auth-safe navigateFallbackDenylist (T-03-20)
  • PWA icon assets (192x192, 512x512, 180x180 apple-touch-icon)
  • iOS head meta entries for A2HS install
  • InstallPrompt component: iOS walkthrough banner/sheet + Android beforeinstallprompt banner

affects: [03-08]

Tech tracking

tech-stack: added: [] patterns: - VitePWA navigateFallbackDenylist to exclude /callback, /api/, /health from SW interception - runtimeCaching: [] — no API response caching (T-03-21) - isIOSSafariNonStandalone() — iOS UA + navigator.standalone detection - useAndroidInstallPrompt() — captures beforeinstallprompt, deferred prompt pattern - localStorage.installPromptDismissed — persist banner dismissal cross-session

key-files: created: - apps/pwa/src/components/InstallPrompt.tsx - apps/pwa/public/icon-192.png - apps/pwa/public/icon-512.png - apps/pwa/public/apple-touch-icon.png modified: - apps/pwa/vite.config.ts - apps/pwa/index.html - apps/pwa/src/components/CalendarShell.tsx

key-decisions:

  • "Icons generated programmatically via pure Node.js (zlib/Buffer) — ImageMagick and sharp not available in the environment; minimal valid PNGs at exact pixel dimensions are functionally equivalent for PWA install purposes. Real branded icons can be dropped into public/ without any code change."
  • "Desktop InstallPrompt placement: wrapped CalendarContent in a flex-column div on desktop to allow InstallPrompt to appear as a top bar above the calendar grid without disrupting the row sidebar layout."

Metrics

duration: ~5min completed: 2026-06-05

Phase 03 Plan 07: PWA Install — VitePWA Manifest + InstallPrompt Summary

VitePWA manifest + auth-safe service worker + iOS icons/meta + InstallPrompt (iOS guided walkthrough + Android beforeinstallprompt) wired into CalendarShell

Performance

  • Duration: ~5 min
  • Started: 2026-06-05T22:01Z
  • Completed: 2026-06-05T22:06Z
  • Tasks: 2
  • Files modified: 7

Accomplishments

Task 1: VitePWA manifest + service worker + iOS head/icons

  • Added VitePWA plugin to apps/pwa/vite.config.ts with registerType: 'autoUpdate'
  • Configured workbox.navigateFallbackDenylist: [/^\/callback/, /^\/api\//, /^\/health/] — OIDC /callback is never SW-intercepted (T-03-20 Gate 2 prerequisite)
  • runtimeCaching: [] — no authenticated API responses cached (T-03-21)
  • Manifest: name/short_name FamilySync, description, theme_color #4A90D9, background_color #ffffff, display:standalone, scope:/, start_url:/, 3 icons (192, 512, 512-maskable)
  • Existing server.proxy block preserved verbatim (including /callback proxy to localhost:3000)
  • Generated three PNG icon files via pure Node.js (zlib/Buffer): icon-192.png (192×192), icon-512.png (512×512), apple-touch-icon.png (180×180) — solid #4A90D9 background with white "F" glyph
  • Added five iOS <head> entries to index.html: apple-touch-icon link, theme-color meta (was already present, supplemented), apple-mobile-web-app-capable, apple-mobile-web-app-status-bar-style, apple-mobile-web-app-title
  • Production build verified: dist/manifest.webmanifest emitted with display:standalone, scope:/, 3 icons; dist/sw.js and dist/workbox-*.js emitted

Task 2: InstallPrompt (TDD GREEN — RED scaffold from Plan 01)

  • Implemented isIOSSafariNonStandalone(): UA regex for iPad/iPhone/iPod + navigator.standalone !== true
  • Implemented useAndroidInstallPrompt(): captures beforeinstallprompt, resets on appinstalled; returns { canInstall, triggerInstall }
  • InstallPrompt renders nothing when display-mode: standalone or navigator.standalone (already installed)
  • iOS branch: dismissible banner (Smartphone icon, "Install FamilySync" heading, body + "How to install" link) gated by localStorage.installPromptDismissed; "How to install" opens WalkthroughSheet — full-screen bottom sheet with 5 annotated steps per UI-SPEC copy; orange (#F5A623) step number circles
  • Android branch: banner with "Install" CTA shown only when canInstall === true; triggers native prompt, then dismisses
  • role="banner", aria-label="Dismiss install prompt", 44px touch targets throughout
  • Mounted <InstallPrompt /> in CalendarShell for both phone and tablet/desktop layouts
  • InstallPrompt.test.tsx GREEN: 5 tests (iOS UA detection, standalone false, Android UA false, canInstall=true on beforeinstallprompt, canInstall=false on appinstalled)
  • Full PWA suite: 44 tests across 6 files — all green; tsc --noEmit clean

Task Commits

  1. Task 1: VitePWA manifest + auth-safe SW denylist + iOS head/iconsbd82837
  2. Task 2: InstallPrompt — iOS walkthrough banner + Android beforeinstallprompte0fb34b

Files Created/Modified

  • apps/pwa/vite.config.ts — added VitePWA plugin with manifest, workbox config, auth-safe denylist
  • apps/pwa/index.html — added 4 iOS head entries (theme-color was already present)
  • apps/pwa/public/icon-192.png — 192×192 PNG icon (solid #4A90D9 + white "F")
  • apps/pwa/public/icon-512.png — 512×512 PNG icon (solid #4A90D9 + white "F")
  • apps/pwa/public/apple-touch-icon.png — 180×180 PNG icon (solid #4A90D9 + white "F")
  • apps/pwa/src/components/InstallPrompt.tsx — iOS walkthrough + Android install prompt component (476 lines)
  • apps/pwa/src/components/CalendarShell.tsx — import + mount InstallPrompt

Decisions Made

  • Icon generation via pure Node.js: ImageMagick was not available in the environment and sharp is not an installed project dependency. Generated minimal valid PNGs programmatically using Node.js zlib.deflateSync + PNG chunk encoding. Icons are structurally correct at exact pixel dimensions and pass file dimension checks. Placeholder visuals (solid #4A90D9 background with white "F") are sufficient for PWA installability; the operator can drop in final branded icons at any time without code changes.
  • Desktop layout wrapper: On tablet/desktop, wrapped <CalendarContent /> in a new flex-column div so that <InstallPrompt /> can appear as a top bar above the calendar grid without disrupting the outer flex-row sidebar/content layout.

Deviations from Plan

None — plan executed exactly as written. Icon generation method (pure Node.js vs ImageMagick/sharp) was anticipated by the plan's fallback note and is not a deviation.

TDD Gate Compliance

  • Task 2 had tdd="true" with a pre-existing RED scaffold (Plan 03-01 Task 4).
  • RED gate: InstallPrompt.test.tsx confirmed failing before implementation (module not found error).
  • GREEN gate: commit e0fb34b implements the component; all 5 test behaviors pass.
  • No separate RED commit needed (RED scaffold existed from Plan 01, committed as bbfccda).

Known Stubs

  • Icon visuals: icon-192.png, icon-512.png, apple-touch-icon.png use a solid #4A90D9 fill with a simple white "F" glyph. These are functional for PWA installability (manifest validation, iOS A2HS icon display) but are placeholder art. Final branded icons can replace these files in apps/pwa/public/ without any code change.
  • iOS walkthrough screenshots: The walkthrough sheet renders 5 annotated step-text items. Actual iOS screenshots with annotation overlays (referenced by the plan as "annotated screenshot images may be placeholder assets") are not included — the component structure, copy, and annotation color (#F5A623) are complete; real screenshots can be added as <img> elements within the steps in a future pass.

Threat Surface Scan

No new security-relevant surface introduced beyond what was in the threat model:

  • T-03-20 (SW intercepts /callback): mitigatednavigateFallbackDenylist confirmed in vite.config.ts
  • T-03-21 (SW caches API responses): mitigatedruntimeCaching: []
  • T-03-22 (icons/manifest leak secrets): accepted — static public assets only

Self-Check: PASSED

  • apps/pwa/vite.config.ts — exists, contains navigateFallbackDenylist
  • apps/pwa/index.html — exists, contains apple-touch-icon
  • apps/pwa/public/icon-192.png — 192×192 PNG verified
  • apps/pwa/public/icon-512.png — 512×512 PNG verified
  • apps/pwa/public/apple-touch-icon.png — 180×180 PNG verified
  • apps/pwa/src/components/InstallPrompt.tsx — exists, 476 lines
  • apps/pwa/src/components/CalendarShell.tsx — contains InstallPrompt import + mount
  • dist/manifest.webmanifest — display:standalone, scope:/, 3 icons verified
  • Commit bd82837 — verified in git log
  • Commit e0fb34b — verified in git log

Phase: 03-event-write-back-pwa-install Completed: 2026-06-05