Files
Dramlog-Prod/OFFLINE_SYNC_PWA.md
robin ceb964a07d docs: correct routes in offline sync documentation
fix: remove non-existent routes from sw.js pre-caching
2025-12-20 23:10:18 +01:00

4.3 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.

  1. Offline Scan: User takes a photo. It's stored in pending_scans with a temp_id (UUID).
  2. Offline Tasting: User adds a note. It's stored in pending_tastings with pending_bottle_id set to the scan's temp_id.
  3. Sync Phase 1 (Scans): When online, magicScan (AI analysis) and saveBottle are called. A real bottle_id is returned from Supabase.
  4. Reconciliation: The code searches pending_tastings for all entries matching the temp_id and updates them with the real bottle_id.
  5. Sync Phase 2 (Tastings): The reconciled tasting notes are then uploaded to Supabase.

📱 PWA Features

Service Worker (public/sw.js) - "Ironclad" Strategy

The Service Worker implements an aggressive offline reliability strategy:

  • Pre-Caching: The landing page (/) and core static assets are cached during installation.
  • Fail-Fast Navigation: Navigation requests have a 3-second timeout to prevent UI hangs during network transitions.
  • Root App Shell Fallback: If a URL is not found in the cache while offline, the SW serves the Root (/). This allows Next.js to bootstrap and handle the routing client-side using local Dexie data.
  • RSC Data Resiliency: Requests to /_next/data/ return an empty JSON object if they fail, preventing "Application Error" screens.
  • Stale-While-Revalidate: Applied to static assets to ensure immediate UI response.

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.