Browse Source
- Consolidate 5 notification-system-* files into doc/notification-system.md - Add web-push cleanup guide and Start-on-Login glossary entry - Configure markdownlint for consistent formatting - Remove web-push references, focus on native OS scheduling Reduces maintenance overhead while preserving all essential information in a single, well-formatted reference document.pull/196/head
23 changed files with 874 additions and 2930 deletions
@ -1,27 +1,56 @@ |
|||
{ |
|||
"MD013": { |
|||
"line_length": 80, |
|||
"code_blocks": false, |
|||
"tables": false, |
|||
"headings": false |
|||
"MD013": false, |
|||
"MD033": false, |
|||
"MD041": false, |
|||
"MD024": { |
|||
"siblings_only": true |
|||
}, |
|||
"MD029": { |
|||
"style": "ordered" |
|||
}, |
|||
"MD007": { |
|||
"indent": 2 |
|||
}, |
|||
"MD012": { |
|||
"maximum": 1 |
|||
}, |
|||
"MD012": true, |
|||
"MD022": true, |
|||
"MD025": true, |
|||
"MD026": { |
|||
"punctuation": ".,;:!" |
|||
}, |
|||
"MD030": { |
|||
"ul_single": 1, |
|||
"ol_single": 1, |
|||
"ul_multi": 1, |
|||
"ol_multi": 1 |
|||
}, |
|||
"MD031": true, |
|||
"MD032": true, |
|||
"MD047": true, |
|||
"MD009": true, |
|||
"MD010": true, |
|||
"MD004": { "style": "dash" }, |
|||
"MD029": { "style": "ordered" }, |
|||
"MD041": false, |
|||
"MD025": false, |
|||
"MD024": false, |
|||
"MD034": true, |
|||
"MD035": { |
|||
"style": "---" |
|||
}, |
|||
"MD036": false, |
|||
"MD003": false, |
|||
"MD040": false, |
|||
"MD055": false, |
|||
"MD056": false, |
|||
"MD034": false, |
|||
"MD023": false |
|||
"MD037": true, |
|||
"MD038": true, |
|||
"MD039": true, |
|||
"MD040": true, |
|||
"MD042": true, |
|||
"MD043": false, |
|||
"MD044": false, |
|||
"MD045": true, |
|||
"MD046": { |
|||
"style": "fenced" |
|||
}, |
|||
"MD047": true, |
|||
"MD048": { |
|||
"style": "backtick" |
|||
}, |
|||
"MD049": { |
|||
"style": "underscore" |
|||
}, |
|||
"MD050": { |
|||
"style": "asterisk" |
|||
} |
|||
} |
@ -0,0 +1,19 @@ |
|||
# Glossary |
|||
|
|||
**T (slot time)** — The local wall-clock time a notification is intended to fire (e.g., 08:00). |
|||
|
|||
**T–lead** — The moment **{prefetchLeadMinutes} minutes before T** when the system _attempts_ a background prefetch to refresh content. |
|||
|
|||
- Example: If T = 12:00 and `prefetchLeadMinutes = 20`, then **T–lead = 11:40**. |
|||
- If background prefetch is skipped/denied, delivery still occurs using the most recent cached payload (rolling-window safety). |
|||
- T–lead **governs prefetch attempts, not arming**. We still arm one-shot locals early (rolling window) so closed-app delivery is guaranteed. |
|||
|
|||
**Rolling window** — Always keep **today’s remaining** one-shot locals armed (and optionally tomorrow, within iOS caps) so the OS can deliver while the app is closed. |
|||
|
|||
**TTL (time-to-live)** — Maximum allowed staleness of a payload at **fire time**. If the projected age at T exceeds `ttlSeconds`, we **skip** arming. |
|||
|
|||
**Exact alarm (Android)** — Minute-precise alarm via `AlarmManager.setExactAndAllowWhileIdle`, subject to policy and permission. |
|||
|
|||
**Windowed alarm (Android)** — Batched/inexact alarm via `setWindow(start,len)`; we target **±10 minutes** when exact alarms are unavailable. |
|||
|
|||
**Start-on-Login** — Electron feature that automatically launches the application when the user logs into their system, enabling background notification scheduling and delivery after system reboot. |
@ -1,77 +1,11 @@ |
|||
# TimeSafari Docs |
|||
# TimeSafari — Native-First Notification System (Clean Pack) — 2025-09-07 |
|||
|
|||
## Generating PDF from Markdown on OSx |
|||
This pack contains a single-version **Native-First** documentation set with a clear definition of **T–lead** and aligned terminology. |
|||
|
|||
This uses Pandoc and BasicTex (LaTeX) Installed through Homebrew. |
|||
**Native-First =** OS-scheduled **background prefetch at T–lead** + **pre-armed one-shot local notifications**. Web-push is retired. |
|||
|
|||
### Set Up |
|||
**Included files** |
|||
|
|||
```bash |
|||
brew install pandoc |
|||
|
|||
brew install basictex |
|||
|
|||
# Setting up LaTex packages |
|||
|
|||
# First update tlmgr |
|||
sudo tlmgr update --self |
|||
|
|||
# Then install LaTex packages |
|||
sudo tlmgr install bbding |
|||
sudo tlmgr install enumitem |
|||
sudo tlmgr install environ |
|||
sudo tlmgr install fancyhdr |
|||
sudo tlmgr install framed |
|||
sudo tlmgr install import |
|||
sudo tlmgr install lastpage # Enables Page X of Y |
|||
sudo tlmgr install mdframed |
|||
sudo tlmgr install multirow |
|||
sudo tlmgr install needspace |
|||
sudo tlmgr install ntheorem |
|||
sudo tlmgr install tabu |
|||
sudo tlmgr install tcolorbox |
|||
sudo tlmgr install textpos |
|||
sudo tlmgr install titlesec |
|||
sudo tlmgr install titling # Required for the fancy headers used |
|||
sudo tlmgr install threeparttable |
|||
sudo tlmgr install trimspaces |
|||
sudo tlmgr install tocloft # Required for \tableofcontents generation |
|||
sudo tlmgr install varwidth |
|||
sudo tlmgr install wrapfig |
|||
|
|||
# Install fonts |
|||
sudo tlmgr install cmbright |
|||
sudo tlmgr install collection-fontsrecommended # And set up fonts |
|||
sudo tlmgr install fira |
|||
sudo tlmgr install fontaxes |
|||
sudo tlmgr install libertine # The main font the doc uses |
|||
sudo tlmgr install opensans |
|||
sudo tlmgr install sourceserifpro |
|||
|
|||
``` |
|||
|
|||
#### References |
|||
|
|||
The following guide was adapted to this project except that we install with Brew and have a few more packages. |
|||
|
|||
Guide: <https://daniel.feldroy.com/posts/setting-up-latex-on-mac-os-x> |
|||
|
|||
### Usage |
|||
|
|||
Use the `pandoc` command to generate a PDF. |
|||
|
|||
```bash |
|||
pandoc usage-guide.md -o usage-guide.pdf |
|||
``` |
|||
|
|||
And you can open the PDF with the `open` command. |
|||
|
|||
```bash |
|||
open usage-guide.pdf |
|||
``` |
|||
|
|||
Or use this one-liner |
|||
|
|||
```bash |
|||
pandoc usage-guide.md -o usage-guide.pdf && open usage-guide.pdf |
|||
``` |
|||
- `notification-system.md` (merged comprehensive guide) |
|||
- `web-push-cleanup-guide.md` (cleanup instructions) |
|||
- `GLOSSARY.md` (definitions incl. **T** and **T–lead**) |
|||
|
@ -1,120 +0,0 @@ |
|||
# TimeSafari Notification System — Executive Summary |
|||
|
|||
**Status:** 🚀 Ready for Implementation |
|||
**Date:** 2025-01-27T14:30Z (UTC) |
|||
**Author:** Matthew Raymer |
|||
**Audience:** Executive Leadership, Product Management, Engineering Leadership |
|||
|
|||
--- |
|||
|
|||
## Executive Overview |
|||
|
|||
TimeSafari will implement a **multi-platform notification system** that delivers **1-3 daily notifications** to keep users connected to gratitude, gifts, and collaborative projects. The system will work across **iOS, Android, Web, and Electron** platforms with **offline-first reliability** and **privacy-preserving architecture**. |
|||
|
|||
### Business Value |
|||
|
|||
- **User Engagement:** Daily touchpoints to maintain community connections |
|||
- **Platform Coverage:** Unified experience across all TimeSafari platforms |
|||
- **Privacy-First:** User-controlled data with no external tracking |
|||
- **Reliability:** Offline notifications that work even when app is closed |
|||
|
|||
--- |
|||
|
|||
## Strategic Approach |
|||
|
|||
### Phase 1 (v1): In-App Orchestrator |
|||
|
|||
**Scope:** Multi-daily local notifications with online/offline flows |
|||
|
|||
**Key Capabilities:** |
|||
- **Local Notifications:** OS-level delivery on mobile/desktop |
|||
- **Web Push:** Service Worker-based notifications for web |
|||
- **Offline Reliability:** Notifications fire even when app is closed |
|||
- **Content Prefetching:** Fresh content fetched when app is active |
|||
- **Cross-Platform:** Same user experience across all platforms |
|||
|
|||
### Phase 2 (v2): Native Plugin |
|||
|
|||
**Timeline:** Future enhancement |
|||
**Scope:** Native background scheduling and enhanced capabilities |
|||
|
|||
**Key Capabilities:** |
|||
- **Native Background Work:** OS-level background tasks |
|||
- **Enhanced Scheduling:** More precise timing and reliability |
|||
- **Advanced Features:** Rich media and complex actions |
|||
- **Performance Optimization:** Native SQLite and HTTP |
|||
|
|||
--- |
|||
|
|||
## Platform Strategy |
|||
|
|||
| Platform | v1 Approach | v2 Enhancement | |
|||
|----------|-------------|----------------| |
|||
| **iOS** | Local notifications + Background Runner (optional) | Native BGTaskScheduler | |
|||
| **Android** | Local notifications + Background Runner (optional) | WorkManager + AlarmManager | |
|||
| **Web** | Service Worker push notifications | Enhanced push capabilities | |
|||
| **Electron** | OS notifications while app running | Native background services | |
|||
|
|||
--- |
|||
|
|||
## Technical Architecture |
|||
|
|||
### Core Components |
|||
|
|||
- **Notification Orchestrator:** Central coordination and state management |
|||
- **Platform Adapters:** Platform-specific notification delivery |
|||
- **Database Integration:** SQLite persistence with TimeSafari's existing infrastructure |
|||
- **Content Management:** Template-based notification content with ETag caching |
|||
|
|||
### Integration Points |
|||
|
|||
- **Existing Database:** Extends TimeSafari's SQLite migration system |
|||
- **Platform Services:** Uses established PlatformServiceMixin patterns |
|||
- **Settings System:** Integrates with existing user preferences |
|||
- **Web Push:** Leverages existing Service Worker infrastructure |
|||
|
|||
--- |
|||
|
|||
## Implementation Plan |
|||
|
|||
### Phase 1 Deliverables (v1) |
|||
|
|||
1. **Database Schema:** New notification tables integrated with existing migration system |
|||
2. **Core Service:** Notification orchestrator with platform adapters |
|||
3. **User Interface:** Settings integration for notification preferences |
|||
4. **Testing:** Cross-platform test suite with Playwright |
|||
5. **Documentation:** Complete implementation guide and API reference |
|||
|
|||
### Success Metrics |
|||
|
|||
- **Reliability:** 95%+ notification delivery rate across platforms |
|||
- **Performance:** <2s notification rendering time |
|||
- **User Adoption:** 80%+ opt-in rate for daily notifications |
|||
- **Platform Coverage:** 100% feature parity across iOS/Android/Web/Electron |
|||
|
|||
--- |
|||
|
|||
## Risk Mitigation |
|||
|
|||
### Technical Risks |
|||
|
|||
- **Platform Limitations:** Mitigated by graceful degradation and fallback strategies |
|||
- **Permission Denials:** User education and deep-linking to system settings |
|||
- **Background Restrictions:** Conservative approach with offline-first design |
|||
|
|||
### Business Risks |
|||
|
|||
- **User Fatigue:** Configurable frequency and user-controlled preferences |
|||
- **Privacy Concerns:** Privacy-first architecture with local data storage |
|||
- **Platform Changes:** Abstraction layer protects against OS updates |
|||
|
|||
--- |
|||
|
|||
## Document References |
|||
|
|||
- **Strategic Plan:** `notification-system-plan.md` - Goals, tenets, platform behaviors, acceptance criteria |
|||
- **Implementation Guide:** `notification-system-implementation.md` - Complete code, database schemas, integration specifics |
|||
|
|||
--- |
|||
|
|||
*This executive summary provides the essential business context and strategic direction for TimeSafari's notification system. For detailed technical specifications and implementation guidance, refer to the referenced documents.* |
File diff suppressed because it is too large
@ -1,335 +0,0 @@ |
|||
# TimeSafari Notification System — Strategic Plan |
|||
|
|||
**Status:** 🚀 Active plan |
|||
**Date:** 2025-09-05T05:09Z (UTC) |
|||
**Author:** Matthew Raymer |
|||
**Scope:** v1 (in‑app orchestrator) now; path to v2 (native plugin) next |
|||
**Goal:** We **will deliver** 1..M local notifications/day with content **prefetched** so messages **will display offline**. We **will support** online‑first (API→DB→Schedule) with offline‑first fallback. The system **will enhance** TimeSafari's community-building mission by keeping users connected to gratitude, gifts, and collaborative projects through timely, relevant notifications. |
|||
|
|||
> **Implementation Details:** See `notification-system-implementation.md` for detailed code, database schemas, and integration specifics. |
|||
> **Canonical Ownership:** This document owns Goals, Tenets, Platform behaviors, Acceptance criteria, and Test cases. |
|||
|
|||
--- |
|||
|
|||
## 1) Versioning & Intent |
|||
|
|||
- **v1 (In‑App Orchestrator):** We **will implement** multi‑daily local notifications, online/offline flows, templating, SQLite persistence, and eventing **inside the app** using Capacitor Local Notifications. |
|||
- **v2 (Plugin):** We **will extract** adapters to a Capacitor/Native plugin to gain native schedulers (WorkManager/AlarmManager; BGTask+UNUserNotificationCenter), native HTTP, and native SQLite **with the same TypeScript API**. |
|||
|
|||
> We **will retain** the existing web push + Service Worker foundation; the system **will add** reliable local scheduling on mobile and a unified API across platforms. |
|||
|
|||
--- |
|||
|
|||
## 2) Design Tenets |
|||
|
|||
- **Reliability:** OS‑level delivery once scheduled; no reliance on JS being alive at fire time. |
|||
- **Freshness:** Prefer online‑first within a short prefetch window; degrade gracefully to cached content with TTL. |
|||
- **Extractable:** Clean interfaces (Scheduler, DataStore, Callbacks) so v2 **will swap** adapters without API changes. |
|||
- **Simplicity:** One‑shot notifications per slot; rolling window scheduling to respect platform caps. |
|||
- **Observability:** Persist deliveries and errors; surface minimal metrics; enable ACKs. |
|||
- **Privacy-First:** Follow TimeSafari's privacy-preserving architecture; user-controlled visibility and data sovereignty. |
|||
- **Community-Focused:** Enhance TimeSafari's mission of connecting people through gratitude, gifts, and collaborative projects. |
|||
|
|||
--- |
|||
|
|||
## 3) Architecture Overview |
|||
|
|||
``` |
|||
Application (Vue/TS) |
|||
├─ NotificationOrchestrator (core state) |
|||
│ ├─ Scheduler (adapter) |
|||
│ ├─ DataStore (adapter) |
|||
│ └─ Callbacks (adapter) |
|||
└─ UI (settings, status) |
|||
|
|||
Adapters |
|||
├─ V1: SchedulerCapacitor, DataStoreSqlite, CallbacksHttp |
|||
└─ V2: SchedulerNative, DataStoreNativeSqlite, CallbacksNativeHttp |
|||
|
|||
**Scheduler Adapter:** All notification arming must go through the Scheduler adapter to honor platform timing semantics (exact alarms vs. windowed fallback). |
|||
|
|||
Platform |
|||
├─ iOS/Android: LocalNotifications (+ native bridges later) |
|||
├─ Web: Service Worker + Push (kept) |
|||
└─ Electron: OS notifications (thin adapter) |
|||
``` |
|||
|
|||
**Execution modes (concise):** |
|||
|
|||
- **Online‑First:** wake near slot → fetch (ETag, timeout) → persist → schedule; on failure → Offline‑First. |
|||
- **Offline‑First:** read last good payload from SQLite; if beyond TTL → skip notification (no retry). |
|||
|
|||
--- |
|||
|
|||
## 4) Public API (Shared by v1 & v2) |
|||
|
|||
**Core Types & Interface:** See Implementation document for complete API definitions, type interfaces, and design decisions. |
|||
|
|||
> **Storage semantics:** `'shared'` = app DB; `'private'` = plugin-owned/native DB (v2). (No functional difference in v1.) |
|||
> **Slot Identity & Scheduling Policy** |
|||
> • **SlotId** uses canonical `HHmm` and remains stable across timezone changes. |
|||
> • **Lead window:** default `prefetchLeadMinutes = 20`; no retries once inside the lead. |
|||
> • **TTL policy:** When offline and content is beyond TTL, **we will skip** the notification (no "(cached)" suffix). |
|||
> • **Idempotency:** Duplicate "scheduled" deliveries are prevented by a unique index on `(slot_id, fire_at, status='scheduled')`. |
|||
> • **Time handling:** Slots will follow **local wall-clock** time across TZ/DST; `slotId=HHmm` stays constant and we will **recompute fire times** on offset change. |
|||
|
|||
--- |
|||
|
|||
## 5) Data Model & Retention (SQLite) |
|||
|
|||
**Tables:** `notif_contents`, `notif_deliveries`, `notif_config` (see Implementation document for complete schema) |
|||
|
|||
**Retention:** We **will keep** ~14 days of contents/deliveries (configurable) and **will prune** via a simple daily job that runs on app start/resume. We **will prune** daily but **will not** VACUUM by default on mobile; disk compaction is deferred. |
|||
|
|||
**Payload handling:** We **will template** `{title, body}` **before** scheduling; we **will not** mutate at delivery time. |
|||
|
|||
--- |
|||
|
|||
## 6) Scheduling Policy & Slot Math |
|||
|
|||
- **One‑shot per slot** per day (non‑repeating). |
|||
- **Rolling window:** today's remaining slots; seed tomorrow where platform limits allow. |
|||
- **TZ/DST safe:** We **will recompute** local wall‑times on app resume and whenever timezone/offset changes; then **reschedule**. |
|||
- **Android exactness:** If exact alarms are unavailable or denied, we **will use** `setWindow` semantics via the scheduler adapter. |
|||
- **iOS pending cap:** We **will keep** pending locals within typical caps (~64) by limiting the window and canceling/re‑arming as needed. |
|||
- **Electron rolling window:** On Electron we **will schedule** the **next occurrence per slot** by default; depth (today+tomorrow) **will be** enabled only when auto-launch is on, to avoid drift while the app is closed. |
|||
|
|||
--- |
|||
|
|||
## 7) Timing & Network Requirements |
|||
|
|||
**Summary:** The notification system uses lightweight, ETag-aware content fetching with single attempts inside lead windows. All timing constants and detailed network policies are defined in the Implementation document. |
|||
|
|||
**Key Policies:** |
|||
|
|||
- **Lead policy:** The lead window governs **online-first fetch attempts**, not arming. We **will arm** locals **whenever the app runs**, using the freshest available payload. |
|||
- **TTL policy:** If offline and content is beyond TTL, we will **skip** the notification (no "cached" suffix). |
|||
- **Idempotency:** Duplicate "scheduled" rows are prevented by a unique index on `(slot_id, fire_at, status='scheduled')`. |
|||
- **Wall-clock rule:** Slots will follow **local wall-clock** across TZ/DST; `slotId=HHmm` stays constant and we will **recompute fire times** on offset change. |
|||
- **Resume debounce:** On app resume/open we will **debounce** pipeline entry points by **30s** per app session to avoid burst fetches. |
|||
- **No scheduled background network in v1 (mobile):** Local notifications **will deliver offline once armed**, but **we will not** run timed network jobs when the app is terminated. **Network prefetch will occur only while the app is running** (launch/resume/inside lead). Server-driven push (Web SW) and OS background schedulers are a **v2** capability. |
|||
|
|||
**Platform-Specific Network Access:** |
|||
|
|||
- **iOS:** Foreground/recently backgrounded only; no JS wake when app is killed |
|||
- **Android:** Exact alarms vs. windowed triggers based on permissions |
|||
- **Web:** Service Worker for push notifications only |
|||
- **Electron:** App-running only; no background network access |
|||
|
|||
**Optional Background Prefetch (v1):** |
|||
|
|||
- **Background Runner (optional, v1):** We **will integrate** Capacitor's Background Runner to **opportunistically prefetch** content on iOS/Android when the OS grants background time. This **will not** provide clock-precise execution and **will not** run after user-terminate on iOS. It **will not** be treated as a scheduler. We **will continue** to *arm* local notifications via our rolling window regardless of Runner availability. When Runner fires near a slot (inside `prefetchLeadMinutes`), it **will** refresh content (ETag, 12s timeout) and, behind a flag, **may** cancel & re-arm that slot with the fresh template if within TTL. If no budget or failure, the previously armed local **will** still deliver. |
|||
|
|||
**Implementation Details:** See Implementation document for complete timing constants table, network request profiles, and platform-specific enforcement. |
|||
|
|||
--- |
|||
|
|||
## 8) Platform Essentials |
|||
|
|||
**iOS** |
|||
|
|||
- Local notifications **will** fire without background runtime once scheduled. NSE **will not** mutate locals; delivery-time enrichment requires remote push (future). |
|||
- **Category ID**: `TS_DAILY` with default `OPEN` action |
|||
- **Background budget** is short and OS‑managed; any prefetch work **will complete** promptly. |
|||
- **Mobile local notifications will route via action listeners (not the service worker)**. |
|||
- Background Runner **will** offer **opportunistic** network wake (no guarantees; short runtime; iOS will not run after force-quit). Locals **will** still deliver offline once armed. |
|||
|
|||
**Android** |
|||
|
|||
- Exact alarms on **API 31+** may require `SCHEDULE_EXACT_ALARM`. If exact access is missing on API 31+, we will use a **windowed trigger (default ±10m)** and surface a settings deep-link. |
|||
- **We will deep-link users to the exact-alarm settings when we detect denials.** |
|||
- **Channel defaults**: ID `timesafari.daily`, name "TimeSafari Daily", importance=high (IDs never change) |
|||
- Receivers for reboot/time change **will be handled** by v2 (plugin); in v1, re‑arming **will occur** on app start/resume. |
|||
- **Mobile local notifications will route via action listeners (not the service worker)**. |
|||
- Background Runner **will** offer **opportunistic** network wake (no guarantees; short runtime; iOS will not run after force-quit). Locals **will** still deliver offline once armed. |
|||
|
|||
**Web** |
|||
|
|||
- Requires registered Service Worker + permission; can deliver with browser closed. **Web will not offline-schedule**. |
|||
- Service Worker click handlers are for **web push only**; **mobile locals bypass the SW**. |
|||
- SW examples use `/sw.js` as a placeholder; **wire this to your actual build output path** (e.g., `sw_scripts/notification-click.js` or your combined bundle). |
|||
- **Note**: Service workers are **intentionally disabled** in Electron (`src/main.electron.ts`) and web uses VitePWA plugin for minimal implementation. |
|||
|
|||
**Electron** |
|||
|
|||
- We **will use** native OS notifications with **best-effort scheduling while the app is running**; true background scheduling will be addressed in v2 (native bridges). |
|||
|
|||
**Electron delivery strategy (v1 reality + v2 path)** |
|||
We **will deliver** desktop notifications while the Electron app is running. True **background scheduling when the app is closed** is **out of scope for v1** and **will be addressed** in v2 via native bridges. We **will adopt** one of the following options (in order of fit to our codebase): |
|||
|
|||
**In-app scheduler + auto-launch (recommended now):** Keep the orchestrator in the main process, **start on login** (tray app, hidden window), and use the **Electron `Notification` API** for delivery. This requires no new OS services and aligns with our PlatformServiceFactory/mixin patterns. |
|||
|
|||
**Policy (v1):** If the app is **not running**, Electron will **not** deliver scheduled locals. With **auto-launch enabled**, we **will achieve** near-mobile parity while respecting OS sleep/idle behavior. |
|||
|
|||
**UX notes:** On Windows we **will set** `appUserModelId` so toasts are attributed correctly; on macOS we **will request** notification permission on first use. |
|||
|
|||
**Prerequisites:** We **will require** Node 18+ (global `fetch`) or we **will polyfill** via `undici` for content fetching in the main process. |
|||
|
|||
--- |
|||
|
|||
## 9) Template Engine Contract |
|||
|
|||
**Supported tokens:** `{{headline}}`, `{{summary}}`, `{{date}}` (YYYY-MM-DD), `{{time}}` (HH:MM). |
|||
**Escaping:** HTML-escape all injected values. |
|||
**Limits:** Title ≤ 50 chars; Body ≤ 200 chars; truncate with ellipsis. |
|||
**Fallback:** Missing token → `"[Content]"`. |
|||
**Mutation:** We **will** render templates **before** scheduling; no mutation at delivery time on iOS locals. |
|||
|
|||
--- |
|||
|
|||
## 10) Integration with Existing TimeSafari Infrastructure |
|||
|
|||
**Database:** We **will integrate** with existing migration system in `src/db-sql/migration.ts` following the established `MIGRATIONS` array pattern |
|||
**Settings:** We **will extend** existing Settings type in `src/db/tables/settings.ts` following the established type extension pattern |
|||
**Platform Service:** We **will leverage** existing PlatformServiceMixin database utilities following the established mixin pattern |
|||
**Service Factory:** We **will follow** the existing `PlatformServiceFactory` singleton pattern for notification service creation |
|||
**Capacitor:** We **will integrate** with existing deep link system in `src/main.capacitor.ts` following the established initialization pattern |
|||
**Service Worker:** We **will extend** existing service worker infrastructure following the established `sw_scripts/` pattern (Note: Service workers are intentionally disabled in Electron and have minimal web implementation via VitePWA plugin) |
|||
**API:** We **will use** existing error handling from `src/services/api.ts` following the established `handleApiError` pattern |
|||
**Logging:** We **will use** existing logger from `src/utils/logger` following the established logging patterns |
|||
**Platform Detection:** We **will use** existing `process.env.VITE_PLATFORM` patterns (`web`, `capacitor`, `electron`) |
|||
**Vue Architecture:** We **will follow** Vue 3 + vue-facing-decorator patterns for component integration (Note: The existing `useNotifications` composable in `src/composables/useNotifications.ts` is currently stub functions with eslint-disable comments and needs implementation) |
|||
**State Management:** We **will integrate** with existing settings system via `PlatformServiceMixin.$saveSettings()` for notification preferences (Note: TimeSafari uses PlatformServiceMixin for all state management, not Pinia stores) |
|||
**Identity System:** We **will integrate** with existing `did:ethr:` (Ethereum-based DID) system for user context |
|||
**Testing:** We **will follow** Playwright E2E testing patterns established in TimeSafari |
|||
**Database Architecture:** We **will support** platform-specific database backends: |
|||
|
|||
- **Web**: Absurd SQL (SQLite via IndexedDB) via `WebPlatformService` with worker pattern |
|||
- **Capacitor**: Native SQLite via `CapacitorPlatformService` |
|||
- **Electron**: Native SQLite via `ElectronPlatformService` (extends CapacitorPlatformService) |
|||
|
|||
--- |
|||
|
|||
## 11) Error Taxonomy & Telemetry |
|||
|
|||
**Error Codes:** `FETCH_TIMEOUT`, `ETAG_NOT_MODIFIED`, `SCHEDULE_DENIED`, `EXACT_ALARM_MISSING`, `STORAGE_BUSY`, `TEMPLATE_MISSING_TOKEN`, `PERMISSION_DENIED`. |
|||
|
|||
**Event Envelope:** `code, slotId, whenMs, attempt, networkState, tzOffset, appState, timestamp`. |
|||
|
|||
--- |
|||
|
|||
## 12) Permission UX & Channels/Categories |
|||
|
|||
- We **will request** notification permission **after** user intent (e.g., settings screen), not on first render. |
|||
- **Android:** We **will create** a stable channel ID (e.g., `timesafari.daily`) and **will set** importance appropriately. |
|||
- **iOS:** We **will register** categories for optional actions; grouping may use `threadIdentifier` per slot/day. |
|||
|
|||
--- |
|||
|
|||
## 13) Eventing & Telemetry |
|||
|
|||
**Error Codes:** `FETCH_TIMEOUT`, `ETAG_NOT_MODIFIED`, `SCHEDULE_DENIED`, `EXACT_ALARM_MISSING`, `STORAGE_BUSY`, `TEMPLATE_MISSING_TOKEN`, `PERMISSION_DENIED`. |
|||
|
|||
**Event Envelope:** `code, slotId, whenMs, attempt, networkState, tzOffset, appState, timestamp`. |
|||
|
|||
**Implementation:** See Implementation document for complete error taxonomy, event logging envelope, ACK payload format, and telemetry events. |
|||
|
|||
--- |
|||
|
|||
## 14) Feature Flags & Config |
|||
|
|||
**Key Flags:** `scheduler`, `mode`, `prefetchLeadMinutes`, `ttlSeconds`, `iosCategoryIdentifier`, `androidChannelId`, `prefetchRunner`, `runnerRearm`. |
|||
|
|||
**Storage:** Feature flags **will reside** in `notif_config` table as key-value pairs, separate from user settings. |
|||
|
|||
**Implementation:** See Implementation document for complete feature flags table with defaults and descriptions. |
|||
|
|||
--- |
|||
|
|||
## 15) Acceptance (Definition of Done) → Test Cases |
|||
|
|||
### Explicit Test Checks |
|||
|
|||
- **App killed → locals fire**: Configure slots at 8:00, 12:00, 18:00; kill app; verify notifications fire at each slot on iOS/Android |
|||
- **ETag 304 path**: Server returns 304 → keep previous content; locals fire with cached payload |
|||
- **ETag 200 path**: Server returns 200 → update content and re-arm locals with fresh payload |
|||
- **Offline + beyond TTL**: When offline and content > 24h old → skip notification (no "(cached)" suffix) |
|||
- **iOS pending cap**: Respect ~64 pending limit; cancel/re-arm as needed within rolling window |
|||
- **Exact-alarm denied**: Android permission absent → windowed schedule (±10m) activates; UI shows fallback hint |
|||
- **Permissions disabled** → we will record `SCHEDULE_DENIED` and refrain from queuing locals. |
|||
- **Window fallback** → when exact alarm is absent on Android, verify target fires within **±10m** of slot time (document as an E2E expectation). |
|||
- **Timezone change**: On TZ/DST change → recompute wall-clock times; cancel & re-arm all slots |
|||
- **Lead window respect**: No retries attempted once inside 20min lead window |
|||
- **Idempotency**: Multiple `runFullPipelineNow()` calls don't create duplicate scheduled deliveries |
|||
- **Cooldown guard**: `deliverStoredNow()` has 60s cooldown to prevent double-firing |
|||
|
|||
### Electron-Specific Test Checks |
|||
|
|||
- **Electron running (tray or window) → notifications fire** at configured slots using Electron `Notification` |
|||
- **Electron not running →** no delivery (documented limitation for v1) |
|||
- **Start on Login enabled →** after reboot + login, orchestrator **will re-arm** slots and deliver |
|||
- **Template limits honored** (Title ≤ 50, Body ≤ 200) on Electron notifications |
|||
- **SW scope** not used for Electron (click handlers are **web only**) |
|||
- **Windows appUserModelId** set correctly for toast attribution |
|||
- **macOS notification permission** requested on first use |
|||
|
|||
### Timing-Verifiable Test Checks |
|||
|
|||
- **iOS/Android (app killed):** locals will fire at their slots; no network activity at delivery time. |
|||
- **iOS/Android (resume inside lead):** exactly **one** online-first attempt occurs; if fetch completes within **12s** → content updated; otherwise offline policy applies. |
|||
- **Android (no exact access):** observed delivery is within **±10 min** of slot time. |
|||
- **Web push:** SW push event fetch runs once with **12s** timeout; if it times out, the push still displays (from payload). |
|||
- **Electron (app running):** timer-based locals fire on time; on reboot with **Start on Login**, orchestrator re-arms on first run. |
|||
- **TTL behavior:** offline & stale → **skip** (no notification posted). |
|||
- **ETag path:** with `304`, last payload remains; no duplicate scheduling rows (unique index enforced). |
|||
- **Cooldown:** calling `deliverStoredNow` twice within **60s** for same slot doesn't produce two notifications. |
|||
- **Closed app, armed earlier** → locals fire at slot; title/body match last rendered content (proves "render at schedule time" + adapter API). |
|||
- **Closed app, timezone change before slot** → on next resume, app recomputes and re-arms; already armed notifications will still fire on original wall-time |
|||
- **Mobile closed-app, no background network:** Arm at T–hours; kill app; verify locals fire with last rendered text; confirm **no** network egress at delivery. |
|||
- **Web push as network scheduler:** Send push with empty payload → SW fetches within 12s timeout → shows correct text; confirm behavior with browser closed. |
|||
- **Electron app not running:** No delivery; with **Start on Login**, after reboot first run fetches and re-arms; subsequent slots fire. |
|||
- **Runner fires in background (Android/iOS):** With Runner enabled and app backgrounded for ≥30 min, at least one prefetch **will** occur; content cache **will** update; already-armed locals **will** still fire on time. |
|||
- **Runner re-arm (flagged):** If `runnerRearm=true` and Runner fires inside lead with fresh content + within TTL, the system **will** cancel & re-arm the next slot; delivered text **will** match fresh template. |
|||
|
|||
--- |
|||
|
|||
## 16) Test Matrix (Essentials) |
|||
|
|||
- **Android:** exact vs inexact branch, Doze/App Standby behavior, reboot/time change, permission denial path, deep‑link to exact‑alarm settings. |
|||
- **iOS:** BG fetch budget limits, pending cap windowing, local notification delivery with app terminated, category actions. |
|||
- **Web:** SW lifecycle, push delivery with app closed, click handling, no offline scheduling. |
|||
- **Cross‑cutting:** ETag/304 behavior, TTL policy, templating correctness, event queue drain, SQLite retention job. |
|||
|
|||
--- |
|||
|
|||
## 17) Migration & Rollout Notes |
|||
|
|||
- We **will keep** existing web push flows intact. |
|||
- We **will introduce** the orchestrator behind a feature flag, initially with a small number of slots. |
|||
- We **will migrate** settings to accept multiple times per day. |
|||
- We **will document** platform caveats inside user‑visible settings (e.g., Android exact alarms, iOS cap). |
|||
|
|||
--- |
|||
|
|||
## 18) Security & Privacy |
|||
|
|||
- Tokens **will reside** in Keystore/Keychain (mobile) and **will be injected** at request time; they **will not** be stored in SQLite. |
|||
- Optionally, SQLCipher at rest for mobile; redaction of PII in logs; payload size caps. |
|||
- Content **will be** minimal (title/body); sensitive data **will not be** embedded. |
|||
|
|||
--- |
|||
|
|||
## 19) Non‑Goals (Now) |
|||
|
|||
- Complex action sets and rich media on locals (kept minimal). |
|||
- Delivery‑time mutation of local notifications on iOS (NSE is for remote). |
|||
- Full analytics pipeline (future enhancement). |
|||
|
|||
--- |
|||
|
|||
## 20) Cross-Document Synchronization |
|||
|
|||
**Canonical Ownership:** |
|||
- **This document (Plan):** Goals, Tenets, Platform behaviors, Acceptance criteria, Test cases |
|||
- **Implementation document:** API definitions, Database schemas, Adapter implementations, Code examples |
|||
|
|||
**Synchronization Requirements:** |
|||
- API code blocks must be identical between Plan §4 and Implementation §3 |
|||
- Feature flags must match between Plan §13 and Implementation §15 defaults |
|||
- Test cases must align between Plan §14 acceptance criteria and Implementation examples |
|||
- Error codes must match between Plan §11 taxonomy and Implementation error handling |
|||
- Slot/TTL/Lead policies must be identical between Plan §4 semantics and Implementation §9 logic |
|||
|
|||
--- |
|||
|
|||
*This strategic plan focuses on features and future‑tense deliverables, avoids implementation details, and preserves a clear path from the in‑app orchestrator (v1) to native plugin (v2).* |
@ -0,0 +1,167 @@ |
|||
# TimeSafari — Native-First Notification System |
|||
|
|||
**Status:** Ready for implementation |
|||
**Date:** 2025-09-07 |
|||
**Author:** Matthew Raymer |
|||
|
|||
--- |
|||
|
|||
## Executive Summary |
|||
|
|||
Ship a **single, Native-First** notification system: OS-scheduled **background prefetch at T–lead** + **pre-armed** local notifications. Web-push is retired. |
|||
|
|||
### What we deliver |
|||
|
|||
- **Closed-app delivery:** Pre-armed locals fire even if the app is closed. |
|||
- **Freshness:** One prefetch attempt per slot at **T–lead**; ETag/TTL controls; skip when stale. |
|||
- **Android precision:** Exact alarms with permission; windowed fallback (±10m) otherwise. |
|||
- **Resilience:** Re-arm after reboot/time-change (Android receivers; iOS on next wake/silent push). |
|||
- **Cross-platform:** Same TS API (iOS/Android/Electron). Electron is best-effort while running. |
|||
|
|||
### Success signals |
|||
|
|||
- High delivery reliability, minute-precision on Android with permission. |
|||
- Prefetch budget hit rate at **T–lead**; zero stale deliveries beyond TTL. |
|||
|
|||
--- |
|||
|
|||
## Strategic Plan |
|||
|
|||
### Goal |
|||
|
|||
Deliver 1..M daily notifications with **OS background prefetch at T–lead** and **rolling-window safety** so messages display with fresh content even when the app is closed. |
|||
|
|||
### Tenets |
|||
|
|||
- **Reliability first:** OS delivers once scheduled; no JS at delivery time. |
|||
- **Freshness with guardrails:** Prefetch at **T–lead**; enforce **TTL-at-fire**; ETag-aware. |
|||
- **Single system:** One TS API; native adapters swap under the hood. |
|||
- **Platform honesty:** Android exactness via permission; iOS best-effort budget. |
|||
|
|||
### Architecture (high level) |
|||
|
|||
App (Vue/TS) → Orchestrator (policy) → Native Adapters: |
|||
|
|||
- **SchedulerNative** — AlarmManager (Android) / UNUserNotificationCenter (iOS) |
|||
- **BackgroundPrefetchNative** — WorkManager (Android) / BGTaskScheduler (+ silent push) (iOS) |
|||
- **DataStore** — SQLite |
|||
|
|||
### Scheduling & T–lead |
|||
|
|||
- **Arm** a rolling window (today + tomorrow within iOS cap). |
|||
- **Attempt** a single **online-first** fetch per slot at **T–lead = T − prefetchLeadMinutes**. |
|||
- If prefetch is skipped, the armed local **still fires** using cached content. |
|||
|
|||
### Policies |
|||
|
|||
- **TTL-at-fire:** If (T − fetchedAt) > `ttlSeconds` → **skip** arming. |
|||
- **Android exactness:** Request `SCHEDULE_EXACT_ALARM`; fallback **±10m** window. |
|||
- **Reboot/time change:** Android receivers re-arm next 24h; iOS on next wake/silent push. |
|||
- **No delivery-time mutation:** iOS locals cannot be mutated by NSE; render before scheduling. |
|||
|
|||
--- |
|||
|
|||
## Implementation Guide |
|||
|
|||
### 1) Interfaces (TS stable) |
|||
|
|||
- **SchedulerNative**: `scheduleExact({slotId, whenMs, title, body, extra})`, `scheduleWindow(..., windowLenMs)`, `cancelBySlot`, `rescheduleAll`, `capabilities()` |
|||
- **BackgroundPrefetchNative**: `schedulePrefetch(slotId, atMs)`, `cancelPrefetch(slotId)` |
|||
- **DataStore**: SQLite adapters (notif_contents, notif_deliveries, notif_config) |
|||
- **Public API**: `configure`, `requestPermissions`, `runFullPipelineNow`, `reschedule`, `getState` |
|||
|
|||
### 2) Templating & Arming |
|||
|
|||
- Render `title/body` **before** scheduling; pass via **SchedulerNative**. |
|||
- Route all arming through **SchedulerNative** to centralize Android exact/window semantics. |
|||
|
|||
### 3) T–lead (single attempt) |
|||
|
|||
- Compute T–lead = `whenMs - prefetchLeadMinutes*60_000`. |
|||
- `BackgroundPrefetchNative.schedulePrefetch(slotId, atMs=T–lead)`. |
|||
- On wake: **ETag** fetch (timeout **12s**), persist, optionally cancel & re-arm if within TTL. |
|||
- Never fetch at delivery time. |
|||
|
|||
### 4) TTL-at-fire |
|||
|
|||
`if (whenMs - fetchedAt) > ttlSeconds*1000 → skip` |
|||
|
|||
### 5) Android specifics |
|||
|
|||
- Request `SCHEDULE_EXACT_ALARM`; deep-link if denied; fallback to `setWindow(start,len)` (±10m). |
|||
- Receivers: `BOOT_COMPLETED`, `TIMEZONE_CHANGED`, `TIME_SET` → recompute & re-arm for next 24h and schedule T–lead prefetch. |
|||
|
|||
### 6) iOS specifics |
|||
|
|||
- `BGTaskScheduler` for T–lead prefetch (best-effort). Optional silent push nudge. |
|||
- Locals: `UNCalendarNotificationTrigger` (one-shots); no NSE mutation for locals. |
|||
|
|||
### 7) Network & Timeouts |
|||
|
|||
- Content fetch: **12s** timeout; single attempt at T–lead; ETag/304 respected. |
|||
- ACK/Error: **8s** timeout, fire-and-forget. |
|||
|
|||
### 8) Electron |
|||
|
|||
- Notifications while app is running; recommend **Start-on-Login**. No true background scheduling when fully closed. |
|||
|
|||
### 9) Telemetry |
|||
|
|||
- Record `scheduled|shown|error`; ACK deliveries (8s timeout); include slot/times/TZ/app version. |
|||
|
|||
--- |
|||
|
|||
## Capability Matrix |
|||
|
|||
| Capability | Android (Native) | iOS (Native) | Electron | Web | |
|||
|---|---|---|---|---| |
|||
| Multi-daily locals (closed app) | ✅ | ✅ | ✅ (app running) | — | |
|||
| Prefetch at T–lead (app closed) | ✅ WorkManager | ⚠️ BGTask (best-effort) | ✅ (app running) | — | |
|||
| Re-arm after reboot/time-change | ✅ Receivers | ⚠️ On next wake/silent push | ✅ Start-on-Login | — | |
|||
| Minute-precision alarms | ✅ with exact permission | ❌ not guaranteed | ✅ timer best-effort | — | |
|||
| Delivery-time mutation for locals | ❌ | ❌ | — | — | |
|||
| ETag/TTL enforcement | ✅ | ✅ | ✅ | — | |
|||
| Rolling-window safety | ✅ | ✅ | ✅ | — | |
|||
|
|||
--- |
|||
|
|||
## Acceptance Criteria |
|||
|
|||
### Core |
|||
|
|||
- **Closed-app delivery:** Armed locals fire at T with last rendered content. No delivery-time network. |
|||
- **T–lead prefetch:** Single background attempt at **T–lead**; if skipped, delivery still occurs from cache. |
|||
- **TTL-at-fire:** No armed local violates TTL at T. |
|||
|
|||
### Android |
|||
|
|||
- **Exact permission path:** With `SCHEDULE_EXACT_ALARM` → within ±1m; else **±10m** window. |
|||
- **Reboot recovery:** After reboot, receivers re-arm next 24h and schedule T–lead prefetch. |
|||
- **TZ/DST change:** Recompute & re-arm; future slots align to new wall-clock. |
|||
|
|||
### iOS |
|||
|
|||
- **BGTask budget respected:** Prefetch often runs but may be skipped; delivery still occurs via rolling window. |
|||
- **Force-quit caveat:** No background execution after user terminate; delivery still occurs if pre-armed. |
|||
|
|||
### Electron |
|||
|
|||
- **Running-app rule:** Delivery only while app runs; with Start-on-Login, after reboot the orchestrator re-arms and subsequent slots deliver. |
|||
|
|||
### Network |
|||
|
|||
- Content fetch timeout **12s**; ACK/Error **8s**; no retries inside lead; ETag honored. |
|||
|
|||
### Observability |
|||
|
|||
- Log/telemetry for `scheduled|shown|error`; ACK payload includes slot, times, device TZ, app version. |
|||
|
|||
--- |
|||
|
|||
## Web-Push Cleanup |
|||
|
|||
Web-push functionality has been retired due to unreliability. All web-push related code paths and documentation sections should be removed or marked as deprecated. See `web-push-cleanup-guide.md` for detailed cleanup steps. |
|||
|
|||
--- |
|||
|
|||
_This document consolidates the Native-First notification system strategy, implementation details, capabilities, and acceptance criteria into a single comprehensive reference._ |
@ -0,0 +1,551 @@ |
|||
# TimeSafari Web-Push Cleanup Guide |
|||
|
|||
**Status:** 🚀 Native-First Implementation |
|||
**Date:** 2025-01-27T14:30Z (UTC) |
|||
**Author:** Matthew Raymer |
|||
**Scope:** Web-push code cleanup and deprecation |
|||
**Goal:** Remove or quarantine all web-push code paths and mark as deprecated. |
|||
|
|||
--- |
|||
|
|||
## Executive Summary |
|||
|
|||
This document provides a comprehensive cleanup guide for removing web-push code |
|||
paths from TimeSafari. Web-push has been retired for unreliability, and the |
|||
system now focuses on native mobile reliability with Electron best-effort support. |
|||
|
|||
--- |
|||
|
|||
## Cleanup Strategy |
|||
|
|||
### Phase 1: Identify Web-Push Code Paths |
|||
|
|||
#### Service Worker Files |
|||
|
|||
- [ ] `sw_scripts/notification-click.js` - Mark as deprecated |
|||
- [ ] `sw_scripts/` directory - Review for web-push dependencies |
|||
- [ ] Service worker registration code - Remove or quarantine |
|||
|
|||
#### Web-Specific Code |
|||
|
|||
- [ ] Web push notification handlers |
|||
- [ ] Service worker event listeners |
|||
- [ ] Web notification API usage |
|||
- [ ] Push subscription management |
|||
|
|||
#### Configuration Files |
|||
|
|||
- [ ] VitePWA plugin configuration |
|||
- [ ] Service worker build configuration |
|||
- [ ] Web push manifest files |
|||
|
|||
### Phase 2: Mark as Deprecated |
|||
|
|||
#### Code Comments |
|||
|
|||
```javascript |
|||
// DEPRECATED: Web-push notification handling |
|||
// This code is kept for reference but not used in production |
|||
// Replaced by Native-First notification system |
|||
``` |
|||
|
|||
#### Documentation Updates |
|||
|
|||
- [ ] Mark web-push sections as deprecated |
|||
- [ ] Add deprecation notices |
|||
- [ ] Update README files |
|||
- [ ] Update API documentation |
|||
|
|||
### Phase 3: Remove or Quarantine |
|||
|
|||
#### Complete Removal |
|||
|
|||
- [ ] Web push subscription code |
|||
- [ ] Service worker notification handlers |
|||
- [ ] Web-specific notification APIs |
|||
- [ ] Push message handling |
|||
|
|||
#### Quarantine (Keep for Reference) |
|||
|
|||
- [ ] Service worker registration code |
|||
- [ ] Web push configuration |
|||
- [ ] Historical web-push tests |
|||
|
|||
--- |
|||
|
|||
## Detailed Cleanup Tasks |
|||
|
|||
### 1. Service Worker Cleanup |
|||
|
|||
#### Files to Deprecate |
|||
|
|||
**`sw_scripts/notification-click.js`** |
|||
|
|||
```javascript |
|||
// DEPRECATED: Service worker notification handling |
|||
// This code is kept for reference but not used in production |
|||
// Replaced by Native-First notification system |
|||
|
|||
// Original web-push notification click handler |
|||
self.addEventListener('notificationclick', (event) => { |
|||
// DEPRECATED: Web-push only |
|||
event.notification.close(); |
|||
|
|||
const slotId = event.notification.data?.slotId; |
|||
const route = slotId ? '/#/daily' : '/#/notifications'; |
|||
|
|||
event.waitUntil( |
|||
clients.openWindow(route).catch(() => { |
|||
return clients.openWindow('/'); |
|||
}) |
|||
); |
|||
}); |
|||
``` |
|||
|
|||
**Service Worker Registration** |
|||
|
|||
```javascript |
|||
// DEPRECATED: Service worker registration |
|||
// This code is kept for reference but not used in production |
|||
// Replaced by Native-First notification system |
|||
|
|||
if ('serviceWorker' in navigator && process.env.VITE_PLATFORM === 'web') { |
|||
// DEPRECATED: Web-push only |
|||
navigator.serviceWorker.register('/sw.js') |
|||
.then(registration => { |
|||
console.log('Service Worker registered:', registration); |
|||
}) |
|||
.catch(error => { |
|||
console.error('Service Worker registration failed:', error); |
|||
}); |
|||
} |
|||
``` |
|||
|
|||
### 2. Web Push API Cleanup |
|||
|
|||
#### Push Subscription Management |
|||
|
|||
```javascript |
|||
// DEPRECATED: Web push subscription management |
|||
// This code is kept for reference but not used in production |
|||
// Replaced by Native-First notification system |
|||
|
|||
class WebPushManager { |
|||
// DEPRECATED: Web-push only |
|||
async subscribeToPush() { |
|||
// Implementation kept for reference |
|||
} |
|||
|
|||
// DEPRECATED: Web-push only |
|||
async unsubscribeFromPush() { |
|||
// Implementation kept for reference |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### Push Message Handling |
|||
|
|||
```javascript |
|||
// DEPRECATED: Push message handling |
|||
// This code is kept for reference but not used in production |
|||
// Replaced by Native-First notification system |
|||
|
|||
self.addEventListener('push', (event) => { |
|||
// DEPRECATED: Web-push only |
|||
const data = event.data ? event.data.json() : {}; |
|||
|
|||
const options = { |
|||
body: data.body, |
|||
icon: '/icon-192x192.png', |
|||
badge: '/badge-72x72.png', |
|||
data: data |
|||
}; |
|||
|
|||
event.waitUntil( |
|||
self.registration.showNotification(data.title, options) |
|||
); |
|||
}); |
|||
``` |
|||
|
|||
### 3. Configuration Cleanup |
|||
|
|||
#### VitePWA Plugin Configuration |
|||
|
|||
```javascript |
|||
// DEPRECATED: VitePWA plugin configuration |
|||
// This configuration is kept for reference but not used in production |
|||
// Replaced by Native-First notification system |
|||
|
|||
import { VitePWA } from 'vite-plugin-pwa' |
|||
|
|||
export default defineConfig({ |
|||
plugins: [ |
|||
VitePWA({ |
|||
// DEPRECATED: Web-push only |
|||
registerType: 'autoUpdate', |
|||
workbox: { |
|||
globPatterns: ['**/*.{js,css,html,ico,png,svg}'] |
|||
}, |
|||
includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'masked-icon.svg'], |
|||
manifest: { |
|||
name: 'TimeSafari', |
|||
short_name: 'TimeSafari', |
|||
description: 'TimeSafari App', |
|||
theme_color: '#ffffff', |
|||
icons: [ |
|||
{ |
|||
src: 'pwa-192x192.png', |
|||
sizes: '192x192', |
|||
type: 'image/png' |
|||
} |
|||
] |
|||
} |
|||
}) |
|||
] |
|||
}) |
|||
``` |
|||
|
|||
#### Service Worker Build Configuration |
|||
|
|||
```javascript |
|||
// DEPRECATED: Service worker build configuration |
|||
// This configuration is kept for reference but not used in production |
|||
// Replaced by Native-First notification system |
|||
|
|||
export default defineConfig({ |
|||
build: { |
|||
rollupOptions: { |
|||
input: { |
|||
// DEPRECATED: Web-push only |
|||
sw: 'sw_scripts/notification-click.js' |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
``` |
|||
|
|||
### 4. Test Cleanup |
|||
|
|||
#### Web Push Tests |
|||
|
|||
```javascript |
|||
// DEPRECATED: Web push tests |
|||
// These tests are kept for reference but not used in production |
|||
// Replaced by Native-First notification system |
|||
|
|||
describe('Web Push Notifications (DEPRECATED)', () => { |
|||
// DEPRECATED: Web-push only |
|||
it('should handle push notifications', async () => { |
|||
// Test implementation kept for reference |
|||
}); |
|||
|
|||
// DEPRECATED: Web-push only |
|||
it('should handle notification clicks', async () => { |
|||
// Test implementation kept for reference |
|||
}); |
|||
}); |
|||
``` |
|||
|
|||
#### Service Worker Tests |
|||
|
|||
```javascript |
|||
// DEPRECATED: Service worker tests |
|||
// These tests are kept for reference but not used in production |
|||
// Replaced by Native-First notification system |
|||
|
|||
describe('Service Worker (DEPRECATED)', () => { |
|||
// DEPRECATED: Web-push only |
|||
it('should register service worker', async () => { |
|||
// Test implementation kept for reference |
|||
}); |
|||
|
|||
// DEPRECATED: Web-push only |
|||
it('should handle push events', async () => { |
|||
// Test implementation kept for reference |
|||
}); |
|||
}); |
|||
``` |
|||
|
|||
### 5. Documentation Cleanup |
|||
|
|||
#### README Updates |
|||
|
|||
```markdown |
|||
# TimeSafari Native-First Notification System |
|||
|
|||
## Web-Push Status: DEPRECATED |
|||
|
|||
Web-push has been retired for unreliability. The system now focuses on native mobile reliability with Electron best-effort support. |
|||
|
|||
### Deprecated Features |
|||
- ❌ Web push notifications |
|||
- ❌ Service worker notification handling |
|||
- ❌ Web notification API |
|||
|
|||
### Active Features |
|||
- ✅ Native mobile notifications (Android/iOS) |
|||
- ✅ Electron notifications (best-effort) |
|||
- ✅ OS-scheduled background prefetch |
|||
- ✅ Rolling window safety |
|||
``` |
|||
|
|||
#### API Documentation Updates |
|||
|
|||
```markdown |
|||
## Notification API (Native-First) |
|||
|
|||
### Deprecated Methods |
|||
- `subscribeToPush()` - DEPRECATED: Web-push only |
|||
- `unsubscribeFromPush()` - DEPRECATED: Web-push only |
|||
- `handlePushMessage()` - DEPRECATED: Web-push only |
|||
|
|||
### Active Methods |
|||
- `scheduleExact()` - Native exact scheduling |
|||
- `scheduleWindow()` - Native windowed scheduling |
|||
- `schedulePrefetch()` - Native background prefetch |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## File-by-File Cleanup Checklist |
|||
|
|||
### Service Worker Files |
|||
|
|||
- [ ] `sw_scripts/notification-click.js` - Mark as deprecated |
|||
- [ ] `sw_scripts/` directory - Review for web-push dependencies |
|||
- [ ] Service worker build configuration - Remove or quarantine |
|||
|
|||
### Web-Specific Code |
|||
|
|||
- [ ] `src/main.web.ts` - Remove service worker registration |
|||
- [ ] `src/services/webPush.ts` - Mark as deprecated |
|||
- [ ] `src/utils/serviceWorker.ts` - Mark as deprecated |
|||
- [ ] Web notification API usage - Remove or quarantine |
|||
|
|||
### Configuration Files |
|||
|
|||
- [ ] `vite.config.web.mts` - Remove VitePWA plugin |
|||
- [ ] `package.json` - Remove web-push dependencies |
|||
- [ ] `public/manifest.json` - Mark as deprecated |
|||
- [ ] Service worker build scripts - Remove or quarantine |
|||
|
|||
### Test Files |
|||
|
|||
- [ ] `test-playwright/web-push.spec.ts` - Mark as deprecated |
|||
- [ ] `test/services/webPush.test.ts` - Mark as deprecated |
|||
- [ ] Service worker tests - Mark as deprecated |
|||
|
|||
### Documentation Files |
|||
|
|||
- [ ] `README.md` - Update to reflect native-first approach |
|||
- [ ] `doc/web-push.md` - Mark as deprecated |
|||
- [ ] API documentation - Remove web-push references |
|||
|
|||
--- |
|||
|
|||
## Dependencies to Remove |
|||
|
|||
### NPM Packages |
|||
|
|||
```json |
|||
{ |
|||
"dependencies": { |
|||
// DEPRECATED: Web-push only |
|||
"web-push": "^7.4.0", |
|||
"vite-plugin-pwa": "^0.17.0" |
|||
}, |
|||
"devDependencies": { |
|||
// DEPRECATED: Web-push only |
|||
"workbox-webpack-plugin": "^6.5.0" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### Build Scripts |
|||
|
|||
```json |
|||
{ |
|||
"scripts": { |
|||
// DEPRECATED: Web-push only |
|||
"build:sw": "workbox generateSW", |
|||
"test:sw": "jest --testPathPattern=serviceWorker" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## Migration Guide |
|||
|
|||
### From Web-Push to Native-First |
|||
|
|||
#### Step 1: Remove Web-Push Dependencies |
|||
|
|||
```bash |
|||
# Remove web-push packages |
|||
npm uninstall web-push vite-plugin-pwa workbox-webpack-plugin |
|||
|
|||
# Remove service worker files |
|||
rm -rf sw_scripts/ |
|||
rm -f public/sw.js |
|||
rm -f public/workbox-*.js |
|||
``` |
|||
|
|||
#### Step 2: Update Configuration |
|||
|
|||
```javascript |
|||
// Remove VitePWA plugin from vite.config.web.mts |
|||
export default defineConfig({ |
|||
plugins: [ |
|||
// Remove VitePWA plugin |
|||
// VitePWA({ ... }) |
|||
] |
|||
}) |
|||
``` |
|||
|
|||
#### Step 3: Update Service Registration |
|||
|
|||
```javascript |
|||
// Remove service worker registration from main.web.ts |
|||
// if ('serviceWorker' in navigator) { |
|||
// navigator.serviceWorker.register('/sw.js') |
|||
// } |
|||
``` |
|||
|
|||
#### Step 4: Update Tests |
|||
|
|||
```javascript |
|||
// Remove web-push tests |
|||
// describe('Web Push Notifications', () => { ... }) |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## Verification Checklist |
|||
|
|||
### Code Removal Verification |
|||
|
|||
- [ ] No web-push imports remain |
|||
- [ ] No service worker registration code |
|||
- [ ] No push subscription management |
|||
- [ ] No web notification API usage |
|||
- [ ] No VitePWA plugin configuration |
|||
|
|||
### Documentation Verification |
|||
|
|||
- [ ] All web-push references marked as deprecated |
|||
- [ ] README updated to reflect native-first approach |
|||
- [ ] API documentation updated |
|||
- [ ] Test documentation updated |
|||
|
|||
### Build Verification |
|||
|
|||
- [ ] Web build succeeds without service worker |
|||
- [ ] No service worker files generated |
|||
- [ ] No web-push dependencies in bundle |
|||
- [ ] Native builds work correctly |
|||
|
|||
### Test Verification |
|||
|
|||
- [ ] Web-push tests are marked as deprecated |
|||
- [ ] Native notification tests pass |
|||
- [ ] No web-push test failures |
|||
- [ ] Test suite runs successfully |
|||
|
|||
--- |
|||
|
|||
## Rollback Plan |
|||
|
|||
### Emergency Rollback |
|||
|
|||
If native-first implementation fails, web-push code can be restored: |
|||
|
|||
#### 1. **Restore Dependencies** |
|||
|
|||
```bash |
|||
npm install web-push vite-plugin-pwa workbox-webpack-plugin |
|||
``` |
|||
|
|||
#### 2. **Restore Service Worker Files** |
|||
|
|||
```bash |
|||
git checkout HEAD~1 -- sw_scripts/ |
|||
git checkout HEAD~1 -- public/sw.js |
|||
``` |
|||
|
|||
#### 3. **Restore Configuration** |
|||
|
|||
```bash |
|||
git checkout HEAD~1 -- vite.config.web.mts |
|||
git checkout HEAD~1 -- package.json |
|||
``` |
|||
|
|||
#### 4. **Restore Tests** |
|||
|
|||
```bash |
|||
git checkout HEAD~1 -- test-playwright/web-push.spec.ts |
|||
git checkout HEAD~1 -- test/services/webPush.test.ts |
|||
``` |
|||
|
|||
### Rollback Verification |
|||
|
|||
- [ ] Web-push functionality restored |
|||
- [ ] Service worker registration works |
|||
- [ ] Push notifications work |
|||
- [ ] Tests pass |
|||
|
|||
--- |
|||
|
|||
## Post-Cleanup Tasks |
|||
|
|||
### Code Review |
|||
|
|||
- [ ] Review all changes for completeness |
|||
- [ ] Verify no web-push code remains |
|||
- [ ] Check for orphaned references |
|||
- [ ] Validate native-first implementation |
|||
|
|||
### Testing |
|||
|
|||
- [ ] Run full test suite |
|||
- [ ] Verify native notifications work |
|||
- [ ] Check Electron functionality |
|||
- [ ] Validate mobile builds |
|||
|
|||
### Documentation |
|||
|
|||
- [ ] Update all documentation |
|||
- [ ] Remove web-push references |
|||
- [ ] Update API documentation |
|||
- [ ] Update user guides |
|||
|
|||
--- |
|||
|
|||
## Success Criteria |
|||
|
|||
### Complete Web-Push Removal |
|||
|
|||
- [ ] All web-push code marked as deprecated |
|||
- [ ] Service worker files quarantined |
|||
- [ ] Dependencies removed |
|||
- [ ] Configuration updated |
|||
|
|||
### Native-First Implementation |
|||
|
|||
- [ ] Native notifications work on Android |
|||
- [ ] Native notifications work on iOS |
|||
- [ ] Electron notifications work |
|||
- [ ] Background prefetch works |
|||
|
|||
### Documentation Updated |
|||
|
|||
- [ ] All docs reflect native-first approach |
|||
- [ ] Web-push marked as deprecated |
|||
- [ ] Migration guide provided |
|||
- [ ] Rollback plan documented |
|||
|
|||
--- |
|||
|
|||
_This cleanup guide provides comprehensive instructions for removing web-push |
|||
code paths from TimeSafari. Web-push has been retired for unreliability, and the |
|||
system now focuses on native mobile reliability with Electron best-effort support._ |
Loading…
Reference in new issue