Skip to content

2025-12-30: Offline Capability

Task Metadata

  • 📆 Date: 2025-12-30
  • 🚥 Status: Complete Status

1. Context

  • Goal: Enable the application to function without an active internet connection. Users should be able to:

    1. Load the application shell (PWA).
    2. View their previously loaded dashboard/meals (Read Cache).
    3. Add meals while offline, which sync automatically when connectivity returns (Write Queue).
  • Trigger: "Next Up" task in Phase 1 Foundation roadmap.

2. Approach

  • Technical Strategy: Hybrid Offline Support (PWA + React Query).
    • App Shell: Use vite-plugin-pwa to cache HTML, JS, and CSS assets, allowing the app to boot offline.
    • Data Caching (Read): Migrate from manual useEffect fetching to TanStack Query (React Query). This provides out-of-the-box caching, background refetching, and robust state management.
    • Data Persistence (Write): Use persistQueryClient (part of React Query) or a custom optimistic update pattern to handle offline mutations.
  • Key Decisions (Confirmed):
    • Strategy Selection: Read the detailed comparison here.
    • 1. React Query for Data: Chosen over Amplify DataStore.
    • Role: Manages the "Furniture" (API responses, JSON data).
    • Reasoning: Preserves custom backend logic/schema. Lightweight. Handles "Optimistic Updates" for instant UI feedback.
    • 2. Vite PWA for App Shell:
    • Role: Manages the "House" (HTML, JS, CSS).
    • Reasoning: Ensures the app starts without internet (fixing the "No Internet" browser error), allowing React Query to take over.

3. Impact Analysis

  • Files to Modify:
    • frontend/package.json (Add @tanstack/react-query, vite-plugin-pwa).
    • frontend/vite.config.js (Configure PWA).
    • frontend/src/main.jsx (Wrap app in QueryClientProvider).
    • frontend/src/hooks/useMeals.js (Rewrite to use useQuery and useMutation).
    • frontend/src/pages/Tracker.jsx (Adapt to new hook signature if needed).
  • Risks:
    • Migration Friction: Replacing useMeals logic requires careful testing to ensure daily totals are still calculated correctly.
    • Cache Invalidation: Need to ensure that adding a meal immediately updates the list (Optimistic Updates).

4. Execution Plan

  • Step 1: Install Dependencies
    • @tanstack/react-query (Core Logic)
    • @tanstack/react-query-persist-client (Offline Persistence)
    • idb-keyval (Storage Adapter for Persistence)
    • vite-plugin-pwa (App Shell Caching)
  • Step 2: Service Layer Setup
    • Create src/services/ApiMealService.js to encapsulate AppSync logic.
  • Step 3: Query Client Configuration
    • Setup QueryClient in src/main.jsx with persistQueryClient plugin.
  • Step 4: PWA Configuration
    • Configure vite.config.js and manifest.json.
  • Step 5: Refactor useMeals
    • Rewrite useMeals.js to use useQuery (Read) and useMutation (Write) calling the Service Layer.
  • Step 6: Verify
    • Test offline load (PWA) and offline writes (Sync Queue).

5. Execution Notes

  • Implementation Successful: Implemented the "PWA + React Query" strategy.
  • Cache Duration: Configured Persistence maxAge to 30 Days and In-Memory gcTime to Infinity.
    • Reasoning: To address the "Cold Cache" problem, allowing users to view meal history for the past month while offline (e.g., on a flight), provided the data was previously fetched.
    • Trade-off: Slightly increased IndexedDB storage usage, which is acceptable for text-based data.
    • Correction: Initially attempted to set gcTime to 30 days, but discovered that TanStack Query's internal setTimeout implementation overflows at ~24.8 days, causing immediate cache removal. Switching to Infinity for in-memory cache solved this while preserving the 30-day disk persistence limit.
  • Freshness (staleTime): Configured to 10 Seconds. Ensures multi-device sync works when switching tabs/apps.
    • Reasoning: Reduces redundant network requests when a user quickly navigates between days (e.g., comparing today vs. yesterday) or reloads a view. The UI remains instant and "quiet" (no spinners) for recently viewed data.
  • Architecture: Created frontend/src/services/ApiMealService.js to decouple API logic from the UI, facilitating easier testing and potential future swapping of the storage engine.
  • PWA: Configured vite-plugin-pwa to cache the app shell (HTML/JS/CSS), ensuring the app loads instantly even without a network connection.
  • Identified Tech Debt: We are not currently persisting the Timezone in the DynamoDB Meal record.
    • Risk: While UserDate ensures data integrity for the current "4 AM Rule", we cannot retroactively re-calculate daily summaries if we ever change that rule (e.g., to a 5 AM rule) because we lack the original timezone context for the CreatedAt timestamp.
    • Action: Logged for a future backend update.

6. User Approval & Notes

User Feedback: Implementing this was great since I got to learn about React Query and optimistic UI. Figuring out gcTime vs staleTime vs maxAge and what React Query is trying to do was interesting, also around figuring out the different query states and updating/invalidating specific query keys which you can do across keys :)