forked from trent_larson/crowd-funder-for-time-pwa
Replace monolithic notification-system-implementation-plan.md with focused strategic plan (notification-system-plan.md) and detailed implementation guide (notification-system-implementation.md). Both documents now perfectly aligned with TimeSafari codebase patterns including: - Actual Settings type extension pattern (JSON strings for complex objects) - Real useNotifications composable stub signatures with eslint-disable - Verified logger exports and safeStringify usage - Confirmed PlatformServiceMixin.$saveSettings integration - Validated migration system registerMigration patterns Documents are production-ready with accurate code examples verified against actual TimeSafari infrastructure.
458 lines
26 KiB
Markdown
458 lines
26 KiB
Markdown
# 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
|
||
|
||
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)
|
||
|
||
```ts
|
||
export type NotificationTime = { hour: number; minute: number }; // local wall-clock
|
||
export type SlotId = string; // Format: "HHmm" (e.g., "0800", "1200", "1800") - stable across TZ changes
|
||
|
||
export type FetchSpec = {
|
||
method: 'GET'|'POST';
|
||
url: string;
|
||
headers?: Record<string,string>;
|
||
bodyJson?: Record<string,unknown>;
|
||
timeoutMs?: number;
|
||
};
|
||
|
||
export type CallbackProfile = {
|
||
fetchContent: FetchSpec;
|
||
ackDelivery?: Omit<FetchSpec,'bodyJson'|'timeoutMs'>;
|
||
reportError?: Omit<FetchSpec,'bodyJson'|'timeoutMs'>;
|
||
heartbeat?: Omit<FetchSpec,'bodyJson'> & { intervalMinutes?: number };
|
||
};
|
||
|
||
export type ConfigureOptions = {
|
||
times: NotificationTime[]; // 1..M daily
|
||
timezone?: string; // Default: system timezone
|
||
ttlSeconds?: number; // Default: 86400 (24h)
|
||
prefetchLeadMinutes?: number; // Default: 20
|
||
storage: 'shared'|'private'; // Required
|
||
contentTemplate: { title: string; body: string }; // Required
|
||
callbackProfile?: CallbackProfile; // Optional
|
||
};
|
||
|
||
export interface MultiDailyNotification {
|
||
requestPermissions(): Promise<void>;
|
||
configure(o: ConfigureOptions): Promise<void>;
|
||
runFullPipelineNow(): Promise<void>; // API→DB→Schedule (today's remaining)
|
||
deliverStoredNow(slotId?: SlotId): Promise<void>; // 60s cooldown guard
|
||
reschedule(): Promise<void>;
|
||
getState(): Promise<{
|
||
nextOccurrences: Array<{ slotId: SlotId; when: string }>; // ISO
|
||
lastFetchAt?: string; lastDeliveryAt?: string;
|
||
pendingCount: number; exactAlarmCapable?: boolean;
|
||
}>;
|
||
}
|
||
```
|
||
|
||
> **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`
|
||
|
||
**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. v1 will schedule **the next occurrence per slot** by default; a **configurable depth** (0=today, 1=today+tomorrow) may be enabled as long as the iOS pending cap is respected.
|
||
- **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) 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)**.
|
||
|
||
**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)**.
|
||
|
||
**Web**
|
||
- Requires registered Service Worker + permission; can deliver with browser closed. **Web will not offline-schedule**.
|
||
- Service worker click handlers apply to **web push only**; local notifications on mobile do **not** pass through 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.
|
||
|
||
---
|
||
|
||
## 8) 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.
|
||
|
||
## 9) 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)
|
||
|
||
---
|
||
|
||
## 10) 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`.
|
||
|
||
---
|
||
|
||
## 11) 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.
|
||
|
||
---
|
||
|
||
## 12) Eventing & Telemetry
|
||
|
||
### Error Taxonomy
|
||
**Finite error code set:**
|
||
- `FETCH_TIMEOUT` - Network request exceeded timeout
|
||
- `ETAG_NOT_MODIFIED` - Server returned 304 (expected)
|
||
- `SCHEDULE_DENIED` - OS denied notification scheduling
|
||
- `EXACT_ALARM_MISSING` - Android exact alarm permission absent
|
||
- `STORAGE_BUSY` - Database locked or unavailable
|
||
- `TEMPLATE_MISSING_TOKEN` - Required template variable not found
|
||
- `PERMISSION_DENIED` - User denied notification permissions
|
||
|
||
### Event Logging Envelope
|
||
```ts
|
||
{
|
||
code: string, // Error code from taxonomy
|
||
slotId: string, // Affected slot
|
||
whenMs: number, // Scheduled time
|
||
attempt: number, // Retry attempt (1-based)
|
||
networkState: string, // 'online' | 'offline'
|
||
tzOffset: number, // Current timezone offset
|
||
appState: string, // 'foreground' | 'background' | 'killed'
|
||
timestamp: number // UTC timestamp
|
||
}
|
||
```
|
||
|
||
### ACK Payload Format
|
||
```ts
|
||
{
|
||
slotId: string,
|
||
fireAt: number, // Scheduled time
|
||
deliveredAt: number, // Actual delivery time
|
||
deviceTz: string, // Device timezone
|
||
appVersion: string, // App version
|
||
buildId: string // Build identifier
|
||
}
|
||
```
|
||
|
||
- **Event queue (v1):** In-memory queue for `delivery`, `error`, `heartbeat` events. Background/native work **will enqueue**; foreground **will drain** and publish to the UI. **v2 will migrate** to SQLite-backed queue for persistence.
|
||
- **Callbacks (optional):** `ackDelivery`, `reportError`, `heartbeat` **will post** to server endpoints when configured.
|
||
- **Minimal metrics:** pending count, last fetch, last delivery, next occurrences.
|
||
|
||
---
|
||
|
||
## 13) Feature Flags & Config
|
||
|
||
### Feature Flags Table
|
||
| Flag | Default | Description | Location |
|
||
|------|---------|-------------|----------|
|
||
| `scheduler` | `'capacitor'` | Scheduler implementation | `notif_config` table |
|
||
| `mode` | `'auto'` | Online-first inside lead, else offline-first | `notif_config` table |
|
||
| `prefetchLeadMinutes` | `20` | Lead time for prefetch attempts | `notif_config` table |
|
||
| `ttlSeconds` | `86400` | Content staleness threshold (24h) | `notif_config` table |
|
||
| `iosCategoryIdentifier` | `'TS_DAILY'` | iOS notification category | `notif_config` table |
|
||
| `androidChannelId` | `'timesafari.daily'` | Android channel ID (never changes) | `notif_config` table |
|
||
|
||
**Storage:** Feature flags **will reside** in `notif_config` table as key-value pairs, separate from user settings.
|
||
|
||
---
|
||
|
||
## 14) 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
|
||
|
||
---
|
||
|
||
## 15) 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.
|
||
|
||
---
|
||
|
||
## 16) 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).
|
||
|
||
---
|
||
|
||
## 17) 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.
|
||
|
||
---
|
||
|
||
## 18) 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).
|
||
|
||
---
|
||
|
||
## 19) Cross-Doc Sync Hygiene
|
||
|
||
### Canonical Ownership
|
||
- **This document (Plan)**: Canonical for Goals, Tenets, Platform behaviors, Acceptance criteria, Test cases
|
||
- **Implementation document**: Canonical for API definitions, Database schemas, Adapter implementations, Code examples
|
||
|
||
### PR Checklist
|
||
When changing notification system behavior, update both documents:
|
||
- [ ] **API changes**: Update types/interfaces in both Plan §4 and Implementation §3
|
||
- [ ] **Schema changes**: Update Plan §5 and Implementation §2
|
||
- [ ] **Slot/TTL changes**: Update Plan §4 semantics and Implementation §6 logic
|
||
- [ ] **Template changes**: Update Plan §9 contract and Implementation examples
|
||
- [ ] **Error codes**: Update Plan §11 taxonomy and Implementation error handling
|
||
|
||
### Synchronization Points
|
||
- **API code blocks**: Must be identical between Plan §4 and Implementation §3 (Public API (Shared))
|
||
- **Feature flags**: Must match between Plan §12 table and Implementation defaults
|
||
- **Test cases**: Plan §13 acceptance criteria must align with Implementation test examples
|
||
- **Slot/TTL/Lead policies**: Must be identical between Plan §4 policy and Implementation §3 policy
|
||
|
||
---
|
||
|
||
## 21) Privacy & Security Alignment
|
||
|
||
### Privacy-First Architecture
|
||
- **User-Controlled Visibility:** Notification preferences **will be** user-controlled with explicit opt-in/opt-out
|
||
- **Data Sovereignty:** All notification data **will reside** on user's device; no external tracking or analytics
|
||
- **Minimal Data Collection:** We **will collect** only essential data for notification delivery (slot times, content templates)
|
||
- **DID Integration:** Notifications **will be** associated with user's Decentralized Identifier (DID) for privacy-preserving identity
|
||
|
||
### Security Considerations
|
||
- **Content Encryption:** Sensitive notification content **will be** encrypted at rest using device keystore
|
||
- **Secure Transmission:** All API calls **will use** HTTPS with proper certificate validation
|
||
- **Input Validation:** All notification content **will be** validated and sanitized before storage
|
||
- **Access Control:** Notification settings **will be** protected by user authentication
|
||
|
||
### Compliance with TimeSafari Principles
|
||
- **Privacy-Preserving:** Follows TimeSafari's privacy-preserving claims architecture
|
||
- **User Agency:** Users maintain full control over their notification experience
|
||
- **Transparency:** Clear communication about what data is collected and how it's used
|
||
- **Minimal Footprint:** Notification system **will have** minimal impact on user privacy
|
||
|
||
---
|
||
|
||
## 23) Platform-Specific Implementation Details
|
||
|
||
### Web Platform (`VITE_PLATFORM=web`)
|
||
- **Database:** Uses Absurd SQL (SQLite via IndexedDB) via `WebPlatformService` with worker pattern
|
||
- **Notifications:** Web push notifications via Service Worker (minimal implementation)
|
||
- **Local Scheduling:** **Not supported** - web cannot schedule local notifications offline
|
||
- **API Integration:** Direct HTTP calls for content fetching
|
||
- **Storage:** Notification preferences stored in Absurd SQL database
|
||
- **Testing:** Playwright E2E tests run on web platform
|
||
|
||
### Capacitor Platform (`VITE_PLATFORM=capacitor`)
|
||
- **Database:** Uses native SQLite via `CapacitorPlatformService`
|
||
- **Notifications:** Local notifications via `@capacitor/local-notifications`
|
||
- **Local Scheduling:** **Fully supported** - OS-level notification scheduling
|
||
- **API Integration:** HTTP calls with mobile-optimized timeouts and retry logic
|
||
- **Storage:** Notification preferences stored in native SQLite database
|
||
- **Testing:** Playwright E2E tests run on mobile devices (Android/iOS)
|
||
|
||
### Electron Platform (`VITE_PLATFORM=electron`)
|
||
- **Database:** Uses native SQLite via `ElectronPlatformService` (extends CapacitorPlatformService)
|
||
- **Notifications:** OS-level notifications via Electron's notification API
|
||
- **Local Scheduling:** **Supported** - desktop OS notification scheduling
|
||
- **API Integration:** Same as Capacitor platform
|
||
- **Storage:** Same as Capacitor platform (via inherited service)
|
||
- **Testing:** Same as Capacitor platform
|
||
|
||
### Cross-Platform Considerations
|
||
- **Feature Detection:** Use `process.env.VITE_PLATFORM` for platform-specific behavior
|
||
- **Database Abstraction:** PlatformServiceMixin handles database differences transparently
|
||
- **API Consistency:** Same TypeScript API across all platforms
|
||
- **Fallback Behavior:** Web platform gracefully degrades to push-only notifications
|
||
|
||
---
|
||
## 24) TimeSafari Architecture Compliance
|
||
|
||
### Design Pattern Adherence
|
||
- **Factory Pattern:** Notification service follows `PlatformServiceFactory` singleton pattern
|
||
- **Mixin Pattern:** Database access uses existing `PlatformServiceMixin` pattern
|
||
- **Migration Pattern:** Database changes follow existing `MIGRATIONS` array pattern
|
||
- **Error Handling:** Uses existing `handleApiError` from `src/services/api.ts`
|
||
- **Logging:** Uses existing logger from `src/utils/logger` with established patterns
|
||
- **Platform Detection:** Uses existing `Capacitor.isNativePlatform()` and `VITE_PLATFORM` patterns
|
||
|
||
### File Organization Compliance
|
||
- **Services:** Follows existing `src/services/` organization with factory and adapters
|
||
- **Database:** Extends existing `src/db-sql/migration.ts` and `src/db/tables/settings.ts`
|
||
- **Utils:** Extends existing `src/utils/PlatformServiceMixin.ts`
|
||
- **Main Entry:** Integrates with existing `src/main.capacitor.ts` initialization
|
||
- **Service Workers:** Follows existing `sw_scripts/` organization
|
||
|
||
### Type Safety Compliance
|
||
- **Settings Extension:** Follows existing Settings type extension pattern
|
||
- **Interface Definitions:** Uses existing TypeScript interface patterns
|
||
- **Error Types:** Follows existing error handling type patterns
|
||
- **Platform Types:** Uses existing platform detection type patterns
|
||
|
||
---
|
||
|
||
## Sync Checklist
|
||
|
||
| Sync item | Plan | Impl | Status |
|
||
| ------------------------------ | --------------------- | --------------------- | --------- |
|
||
| Public API block identical | §4 | §3 | ✅ |
|
||
| `getState()` fields present | §4 | §8 Orchestrator | ✅ |
|
||
| Capacitor action handlers | §7 (iOS/Android note) | §9 Bootstrap | ✅ |
|
||
| Electron fetch prereq/polyfill | §7 | §9 Electron | ✅ |
|
||
| Android ±10m fallback | §7 | §7 SchedulerCapacitor | ✅ |
|
||
| Retention (no VACUUM v1) | §5 | `$pruneNotifData` | ✅ |
|
||
|
||
---
|
||
|
||
*This strategic plan focuses on features and future‑tense deliverables, avoids implementation details, and preserves a clear path from the in‑app implementation (v1) to the native plugin (v2). For detailed implementation specifics, see `notification-system-implementation.md`.*
|