9.6 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | duration | completed | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 08-mobile-pwa | 03 | ui |
|
|
|
|
|
|
|
|
|
8min | 2026-03-26 |
Phase 08 Plan 03: Push Notifications, Offline Queue, Install Prompt Summary
Web Push notification pipeline (VAPID subscription -> DB storage -> pywebpush delivery -> service worker display), IndexedDB offline message queue with auto-drain on reconnect, and second-visit PWA install banner for Android and iOS.
Performance
- Duration: 8 min
- Started: 2026-03-26T03:22:15Z
- Completed: 2026-03-26T03:30:47Z
- Tasks: 2
- Files modified: 15
Accomplishments
- Complete push notification pipeline: browser subscribes with VAPID key, subscription stored in PostgreSQL, gateway delivers via pywebpush when user's WebSocket disconnects mid-stream
- IndexedDB offline message queue: messages sent while disconnected are stored and auto-drained on WebSocket reconnection (or when network comes back online)
- Second-visit PWA install banner handles both Android (beforeinstallprompt API) and iOS (manual Share instructions), dismissable with localStorage persistence
- Push permission opt-in embedded in MobileMoreSheet — non-intrusive but discoverable
Task Commits
Each task was committed atomically:
- Task 1: Push notification backend -
7d3a393(feat) - Task 2: Push subscription client, service worker handlers, install prompt, offline queue -
81a2ce1(feat)
Plan metadata: (created in next commit)
Files Created/Modified
Created:
packages/shared/shared/models/push.py- PushSubscription ORM model + Pydantic schemaspackages/shared/shared/api/push.py- Subscribe/unsubscribe/send API endpointsmigrations/versions/012_push_subscriptions.py- push_subscriptions table migrationpackages/portal/components/push-permission.tsx- Opt-in button with permission state machinepackages/portal/components/install-prompt.tsx- Second-visit install banner (Android + iOS)packages/portal/lib/message-queue.ts- IndexedDB offline queue (enqueue + drain)
Modified:
packages/portal/app/sw.ts- Added push + notificationclick event handlerspackages/portal/lib/use-chat-socket.ts- Offline queue integration (enqueue/drain + online status reconnect)packages/portal/app/(dashboard)/layout.tsx- Mount InstallPromptpackages/portal/components/mobile-more-sheet.tsx- Mount PushPermissionpackages/shared/shared/api/__init__.py- Export push_routerpackages/gateway/gateway/main.py- Mount push_routerpackages/gateway/gateway/channels/web.py- Connected user tracking + push trigger on disconnectpackages/gateway/pyproject.toml- Add pywebpush dependency.env/.env.example- VAPID key env vars
Decisions Made
- Migration numbered 012 (not 010 as planned) — migrations 010 and 011 were already used by template-related data migrations created after the plan was written.
- Push router placed in
shared/api/push.pyfollowing all other API routers in the project; plan suggestedgateway/routers/push.pybut the shared pattern was already established. - Push trigger fires via
asyncio.create_task()when the WebSocket send raises during streaming — fire-and-forget, never blocks the response path. applicationServerKeyusesArrayBuffernotUint8Array— TypeScript strict mode requires this distinction forPushManager.subscribe().vibrateoption cast via spread toRecord<string,unknown>— TypeScript'slib.webworkeromitsvibratefromNotificationOptionseven though all major browsers support it.
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 - Bug] Migration number adjusted from 010 to 012
- Found during: Task 1 (migration creation)
- Issue: Migrations 010 and 011 were already used by template data migrations created after the plan was written
- Fix: Created migration as 012 with down_revision 011
- Files modified: migrations/versions/012_push_subscriptions.py
- Verification: Migration file compiles, correct revision chain
- Committed in:
7d3a393
2. [Rule 1 - Bug] urlBase64ToArrayBuffer returns ArrayBuffer (not Uint8Array)
- Found during: Task 2 (build verification)
- Issue: TypeScript strict types reject Uint8Array for applicationServerKey — requires ArrayBuffer
- Fix: Changed return type and implementation to use ArrayBuffer with Uint8Array view
- Files modified: packages/portal/components/push-permission.tsx
- Verification: npm run build passes
- Committed in:
81a2ce1
3. [Rule 1 - Bug] vibrate option cast in service worker
- Found during: Task 2 (build verification)
- Issue: TypeScript lib.webworker types don't include vibrate in NotificationOptions despite browser support
- Fix: Cast notification options to include vibrate via Record<string,unknown> spread
- Files modified: packages/portal/app/sw.ts
- Verification: npm run build passes
- Committed in:
81a2ce1
Total deviations: 3 auto-fixed (1 migration numbering, 2 TypeScript strict type issues from build verification) Impact on plan: All auto-fixes necessary for correctness and build success. No scope creep.
Issues Encountered
None beyond the auto-fixed TypeScript strict type issues above.
User Setup Required
VAPID keys have been pre-generated and added to .env. For production deployments, generate new keys:
cd packages/portal && npx web-push generate-vapid-keys
Then set NEXT_PUBLIC_VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, and VAPID_CLAIMS_EMAIL in your environment.
Next Phase Readiness
Phase 08 (Mobile PWA) is now complete — all 3 plans delivered:
- 08-01: Service worker, offline caching, PWA manifest, web app manifest
- 08-02: Mobile navigation, chat UI improvements, responsive layout
- 08-03: Push notifications, offline queue, install prompt
The portal is now a fully-featured PWA with push notifications, offline support, and installability.
Phase: 08-mobile-pwa Completed: 2026-03-26