4.7 KiB
4.7 KiB
Offline Sync & PWA Architecture Documentation
This document describes the offline capabilities and synchronization mechanism implemented in the Whisky Vault application. It is designed to be easily parsed by both humans and AI agents.
🏗️ Architecture Overview
The application uses a Stale-While-Revalidate (SWR) pattern for reading data and a Background Sync Queue for writing data.
Technical Stack
- Database Engine: Dexie.js (IndexedDB wrapper)
- Framework: Next.js (App Router)
- State Management:
dexie-react-hooks(useLiveQuery) - Backend/Auth: Supabase
- PWA: Service Worker (
sw.js) & Web App Manifest
🗄️ Database Schema (IndexedDB)
The database WhiskyVault is defined in src/lib/db.ts.
| Table | Indexing | Purpose |
|---|---|---|
pending_scans |
++id, temp_id, timestamp |
Stores images (Base64) and metadata of bottles scanned while offline. |
pending_tastings |
++id, bottle_id, pending_bottle_id |
Stores tasting notes. Can link to a real bottle_id or a pending_bottle_id. |
cache_tags |
id, category, name |
Local cache for whisky tags (aroma, flavor, etc.). |
cache_buddies |
id, name |
Local cache for user's buddies. |
cache_bottles |
id, name, distillery |
Local cache for bottle details (SWR). |
cache_tastings |
id, bottle_id, created_at |
Local cache for tasting notes (SWR). |
🔄 Synchronization Logic
1. Reading Data (SWR Pattern)
Implemented in hooks like useBottleData.
graph TD
A[Component Mounts] --> B[useLiveQuery: Get from Dexie]
B --> C[UI renders cached data]
C --> D{Is Online?}
D -- Yes --> E[Fetch from Supabase]
E --> F[Update Dexie Table]
F --> G[useLiveQuery triggers re-render]
D -- No --> H[Keep showing cache]
2. Writing Data (Background Sync)
Implemented in src/components/UploadQueue.tsx.
The Reconciliation Logic
A critical feature is the ability to link tasting notes to a bottle that hasn't been synced to the server yet.
- Offline Scan: User takes a photo. It's stored in
pending_scanswith atemp_id(UUID). - Offline Tasting: User adds a note. It's stored in
pending_tastingswithpending_bottle_idset to the scan'stemp_id. - Sync Phase 1 (Scans): When online,
magicScan(AI analysis) andsaveBottleare called. A realbottle_idis returned from Supabase. - Reconciliation: The code searches
pending_tastingsfor all entries matching thetemp_idand updates them with the realbottle_id. - Sync Phase 2 (Tastings): The reconciled tasting notes are then uploaded to Supabase.
📱 PWA Features
Service Worker (public/sw.js) - "Offline-Modus v10 + SWR"
The Service Worker implements a robust "Cache-First, Network-Background" strategy:
- Pre-Caching: The landing page (
/) and core static assets are cached individually (sequentially) during installation to prevent total failure on single-file 404s. - Manifest Path: Corrected to
/manifest.webmanifestto match Next.js defaults. - SWR Navigation & Assets: Both load instantly from cache. Updates happen in the background via
fetchWithTimeoutandAbortController. - Universal Root Fallback: Deep links (like
/bottles/[id]) fallback to/if not cached, allowing Next.js to take over. - Network Stability: Added a 2-second stabilization delay in
UploadQueue.tsxbefore background sync starts after a network change. - RSC Data Resiliency: Requests to
/_next/data/return an empty JSON object if they fail. - Indicator: Located in the application header for constant status visibility.
- Offline-Modus: Formerly called "Bunker", this system ensures all core assets are ready for a zero-connectivity environment.
Manifest (src/app/manifest.ts)
Defines the app's appearance when installed:
- Display:
standalone(removes browser UI) - Theme Color:
#d97706(Amber) - Background:
#000000
🛠️ Key Components & Hooks
src/lib/db.ts: Database definition and TypeScript interfaces.src/hooks/useBottleData.ts: Example of the SWR pattern implementation.src/components/UploadQueue.tsx: The "Sync Engine" UI and logic coordinator.src/components/CameraCapture.tsx: Integrated scanning feature.src/components/TastingNoteForm.tsx: Integrated tasting note feature.src/components/PWARegistration.tsx: Client-side Service Worker registration handler.
⚠️ Edge Case Handling
- Partial Sync: If a scan succeeds but a tasting fails, the tasting remains in the queue with the now-valid
bottle_id. - Stale Cache: Caches are overwritten on every successful online fetch to ensure eventual consistency.