Mobile PWA (cbmobile)¶
Status: Active Purpose: Progressive Web App for door-knocking/canvassing
Overview¶
cbmobile is a Progressive Web App (PWA) for door-knocking and canvassing that targets iOS Safari as the primary platform. The app works reliably offline in areas with poor cell service and syncs when connectivity returns.
Quick Start¶
# Development
pnpm dev # Start Vite dev server with HMR
pnpm generate-api # Generate TypeScript types from FastAPI spec
# Build & Deploy
pnpm build # Production build
pnpm preview # Preview production build
# Testing
pnpm test # Run Vitest in watch mode
pnpm test --run # Run tests once
# Type Checking & Linting
pnpm typecheck # TypeScript type checking
pnpm lint # Run ESLint
pnpm lint:fix # Auto-fix linting issues
Architecture¶
Dual Database Pattern¶
| Database | Purpose |
|---|---|
| RxDB + Dexie | CRUD operations with sync engine |
| DuckDB-WASM | Read-heavy analytics queries on Parquet |
| Event Queue | Append-only log for crash recovery |
API Integration¶
TypeScript types auto-generated from FastAPI's OpenAPI spec:
const { data } = $api.useQuery("get", "/contacts/{contact_id}", {
params: { path: { contact_id: 123 } }
});
Offline Maps¶
- PMTiles - Single-file tile archives for offline use
- MapLibre GL JS - WebGL rendering with clustering
- Stored in IndexedDB, served via service worker
Sync Strategy¶
No Background Sync on iOS. Sync triggers on:
- visibilitychange (app returns to foreground)
- online event
iOS Safari Constraints¶
| Constraint | Solution |
|---|---|
| 7-day storage eviction | Home screen install exempts from timer |
| Service worker killed on background | Re-cache critical assets on every launch |
| No Background Sync API | Sync on visibility/online events |
| WebSocket terminated on background | Reconnect with exponential backoff |
| No navigator.vibrate() | iOS 18+ checkbox switch for haptics |
Critical: Always encourage home screen installation. display: standalone PWAs are exempt from storage eviction.
Key Patterns¶
Conflict Resolution¶
- Last-Write-Wins - Simple fields
- Additive Merging - Interaction logs (append, never overwrite)
CSS Requirements¶
html, body { overscroll-behavior: none; } /* Prevent pull-to-refresh */
* { -webkit-tap-highlight-color: transparent; }
.interactive { touch-action: manipulation; } /* No double-tap zoom */
WebSocket with Offline Queue¶
Messages created offline queue to IndexedDB and flush on reconnection.
Technology Stack¶
- Frontend: TypeScript, Vite, React/Preact
- State: RxDB, DuckDB-WASM
- Maps: MapLibre GL JS, PMTiles
- API: openapi-typescript, openapi-fetch, openapi-react-query
- Testing: Vitest
Roadmap¶
See ROADMAP.md for the full development plan.
| Phase | Status | Description |
|---|---|---|
| 1: Core Infrastructure | ✅ Complete | Device ID, types, store, OPFS, DuckDB |
| 2: User Interface | 🔜 Next | Walkbook list, contact cards, surveys |
| 3: Data & Maps | 📋 Planned | Sync engine, offline maps |
| 4: Polish | 📋 Planned | Native feel, performance |