Technical Documentation - NateEaton/mind-pwa GitHub Wiki

Understanding the MIND Diet Tracker Architecture

This document provides technical details about the MIND Diet Tracker PWA for developers who want to understand, modify, or extend the application.

Architecture Overview

The application follows a modular architecture with these key components:

MIND Diet Tracker
├── Client (client/)
│   ├── Core Modules (core/)
│   │   ├── appManager.js           - Application lifecycle and coordination
│   │   ├── dataService.js          - Data handling (localStorage and IndexedDB)
│   │   ├── stateManager.js         - Centralized state management
│   │   ├── eventHandlers.js        - Event handling and user interactions
│   │   ├── settingsManager.js      - Settings and configuration management
│   │   ├── themeManager.js         - Light/dard theme management
│   │   ├── setupWizard.js          - First-time user setup wizard
│   │   ├── historyModalManager.js  - Historical data editing functionality
│   │   ├── importExportManager.js  - Data import/export operations
│   │   ├── trackingEngine.js       - Date tracking and reset logic
│   │   ├── devTools.js            - Developer utilities and debugging
│   │   └── logger.js              - Configurable logging system
│   ├── User Interface (ui/)
│   │   ├── renderer.js            - UI rendering based on application state
│   │   ├── components.js          - Reusable UI components
│   │   └── templates.js           - HTML templates for dynamic content
│   ├── Utilities (utils/)
│   │   ├── config.js              - Expose environment variables to app
│   │   ├── appUtils.js            - General application utilities
│   │   └── dateUtils.js           - Date manipulation and formatting
│   ├── Cloud Synchronization (cloudSync/)
│   │   ├── cloudSync.js           - Main cloud sync manager
│   │   ├── changeDetectionService.js - Detects data changes for sync
│   │   ├── fileMetadataManager.js - Manages file metadata for sync
│   │   ├── mergeCoordinator.js    - Coordinates data merging operations
│   │   ├── mergeStrategies.js     - Implements merge conflict resolution
│   │   ├── syncOperationHandler.js - Handles individual sync operations
│   │   └── syncUtils.js           - Cloud sync utility functions
│   ├── Cloud Provider Implementations (cloudProviders/)
│   │   ├── googleDriveProvider.js - Google Drive implementation
│   │   └── dropboxProvider.js     - Dropbox implementation
│   ├── Web Components
│   │   ├── app.js                 - Main application entry point
│   │   ├── index.html             - Main application HTML
│   │   ├── icons/                 - Application icons
│   │   └── style.css              - Application styling
│   └── PWA Components
│       ├── serviceWorker.js       - Offline functionality
│       ├── manifest.json          - PWA installation configuration
│       └── version.json          - Application version information
├── Server (server/)
│   ├── server.js                  - Express.js OAuth server
│   ├── logger.js                  - Server-side logging
│   └── package.json               - Server dependencies
├── Infrastructure
│   ├── .env                       - Build-time environment variable definitions
│   ├── src/vite.config.js         - VITE configuration
│   ├── scripts/pre-commit.example    - Example for controlling update-version.cjs during commit
│   ├── scripts/update-version.cjs    - Update version.json during commit 
│   ├── docker-compose.yml         - Docker deployment configuration
│   ├── docker-compose.client-only.yml  - Docker deployment configuration for client-only deployment
│   ├── nginx/nginx.conf           - Nginx reverse proxy configuration
│   ├── nginx/nginx.client-only.conf    - Nginx reverse proxy configuration for client-only deployment
│   ├── deploy-server.sh           - Deployment script for server-enabled 
│   ├── deploy-client.sh           - Deployment script for client-only 
│   ├── start-server.sh            - Container management script for server-enabled deployment
│   └── start-client-only.sh       - Container management script for client-only deployment 
└── Documentation
    ├── README.md                  - Project overview
    ├── LICENSE                    - GPL v3 license
    ├── screenshots/               - Application screenshots
    ├── docs/index.html            - Github Pages web page for application
    └── wiki/                      - Complete documentation

Feature Gating System

The application uses build-time feature gating to create two distinct deployment modes with different capabilities and bundle sizes.

Build-Time Constants

Vite configuration (client/vite.config.js) converts environment variables to embedded constants:

define: {
  __SERVER_FEATURES_ENABLED__: JSON.stringify(env.VITE_SERVER_FEATURES_ENABLED === "true"),
  __DEV_MODE__: JSON.stringify(env.VITE_DEV_MODE === "true"),
  __DEMO_MODE__: JSON.stringify(env.VITE_DEMO_MODE === "true"),
}

Conditional Code Loading

Cloud sync features are conditionally imported based on build-time constants:

// In client code
if (__SERVER_FEATURES_ENABLED__) {
  const { CloudSyncManager } = await import('./cloudSync/cloudSync.js');
  // Initialize cloud sync
}

Deployment Modes

Client-Only Mode (VITE_SERVER_FEATURES_ENABLED=false):

  • Local data storage and import/export
  • Theme management
  • PWA installation and offline use
  • Smaller bundle size (cloud sync code excluded)

Server-Enabled Mode (VITE_SERVER_FEATURES_ENABLED=true):

  • All client-only features
  • Google Drive and Dropbox synchronization
  • OAuth authentication flows
  • Cross-device data consistency

Server Architecture

The server component handles OAuth authentication flows using Express.js. It exists solely to keep OAuth client secrets secure and manage token refresh operations.

Server Responsibilities

  • OAuth Token Exchange: Converting authorization codes to access/refresh tokens
  • Token Refresh: Handling expired access token renewal
  • Security: Keeping client secrets server-side and away from browser code
  • Provider Abstraction: Unified OAuth handling for Google Drive and Dropbox

OAuth Flow

  1. Client redirects user to /api/{provider}/auth
  2. Server redirects to cloud provider OAuth URL
  3. Provider redirects back to /api/{provider}/callback
  4. Server exchanges authorization code for tokens
  5. Server redirects client with tokens in URL fragment
  6. Client extracts tokens and stores for API calls

API Endpoints

/api/gdrive/auth       - Initiate Google Drive OAuth
/api/gdrive/callback   - Handle Google OAuth callback
/api/gdrive/refresh    - Refresh Google access tokens

/api/dropbox/auth      - Initiate Dropbox OAuth  
/api/dropbox/callback  - Handle Dropbox OAuth callback
/api/dropbox/refresh   - Refresh Dropbox access tokens

Client Architecture

The client is a single-page application emphasizing local-first functionality with optional cloud enhancement.

Core Design Principles

  • Local-First: All functionality works without internet connection
  • Progressive Enhancement: Cloud features are additive, not required
  • Event-Driven: Components communicate through state changes and events
  • Modular: Clear separation between data, UI, cloud, and utility layers

Module Interaction

  • State-Driven: All components interact through centralized state management
  • Publisher-Subscriber: Components subscribe to state changes for reactive updates
  • Service Layer: Core services (data, settings, theme) provide APIs to other modules
  • Feature Isolation: Cloud functionality conditionally loaded and isolated

State Management

The application uses a Redux-style state management system with actions, reducers, and subscribers.

State Structure

{
  // Current tracking context
  currentDayDate: "2024-01-15",
  currentWeekStartDate: "2024-01-14", 
  selectedTrackerDate: "2024-01-15",
  
  // Tracking data
  dailyCounts: {
    "2024-01-15": { leafyGreens: 2, nuts: 1 }
  },
  weeklyCounts: { leafyGreens: 8, nuts: 5 },
  
  // Historical data
  history: [
    {
      weekStartDate: "2024-01-07",
      totals: { leafyGreens: 6, nuts: 3 },
      targets: { leafyGreens: 6, nuts: 5 }
    }
  ],
  
  // Application metadata
  metadata: {
    lastModified: 1642234567890,
    dailyTotalsDirty: false,
    weeklyTotalsDirty: false,
    weekStartDay: "Sunday"
  }
}

Action Types

Key actions supported by the state manager:

  • INITIALIZE_STATE: Set initial state on app load
  • UPDATE_DAILY_COUNT: Modify daily food counts
  • SET_CURRENT_DAY: Update current date tracking
  • RESET_DAILY_COUNTS: Clear daily counts (new day)
  • RESET_WEEKLY_COUNTS: Clear weekly counts (new week)
  • RECALCULATE_WEEKLY_TOTALS: Recompute weekly totals from daily data
  • SET_HISTORY: Update historical weekly data
  • UPDATE_METADATA: Modify application metadata

Publisher-Subscriber Pattern

Components subscribe to state changes for reactive updates:

stateManager.subscribe((newState, action) => {
  // React to state changes
  uiRenderer.updateUI(newState);
});

Data Storage

The application uses a hybrid storage approach combining localStorage and IndexedDB for different data types and access patterns.

Storage Strategy

localStorage (Synchronous):

  • Current week tracking data
  • Application settings and preferences
  • Wizard completion status
  • Quick-access metadata

IndexedDB (Asynchronous):

  • Historical weekly data
  • Large dataset operations
  • Import/export processing

Data Service API

The dataService module provides unified access:

// Synchronous operations
dataService.loadState()
dataService.saveState(state)
dataService.getPreference(key, defaultValue)

// Asynchronous operations
await dataService.saveWeekHistory(weekData)
await dataService.getAllWeekHistory()
await dataService.getWeekHistory(weekStartDate)

Data Schemas

All data follows defined schemas with version tracking for migration support. The current schema version is stored in metadata and checked on application initialization.

Setup Wizard

The setup wizard provides guided first-time configuration through a modal-based flow. The wizard implementation uses a simple step-based state machine.

Wizard Steps

The wizard progresses through these steps based on feature availability:

  • Welcome → First Day of Week → Appearance → [Cloud Sync] → [Provider Selection] → Complete

Cloud sync steps are only shown when __SERVER_FEATURES_ENABLED__ is true.

State Persistence

The wizard saves progress to localStorage during OAuth flows, allowing resumption after provider redirects. The wizard integrates with the OAuth flow by storing state before redirecting to server endpoints.

Date Handling and Tracking Engine

The trackingEngine manages date transitions and ensures data integrity across day and week boundaries.

Date Transition Detection

The checkDateAndReset() function handles date changes:

  1. Compare current state date with system date
  2. If dates differ, determine reset type (day, week, or multi-day gap)
  3. Archive completed periods if needed
  4. Reset tracking data for new period
  5. Update metadata with reset information

Week Transitions

When a new week begins:

  1. Archive current week data to history
  2. Clear daily and weekly counts
  3. Update current week start date based on user preference

Multi-Day Gaps

The system handles absences gracefully:

  • Detect gaps between last used date and current date
  • Archive only completed weeks, not partial data
  • Reset directly to current date without creating empty periods

Cloud Synchronization

The cloud sync system handles data synchronization across devices with conflict resolution and network awareness.

Sync Architecture

CloudSyncManager: Coordinates all sync operations and schedules automatic syncing Change Detection: Monitors state metadata dirty flags to determine what needs syncing Merge Strategies: Resolves conflicts using timestamp-based rules with fallback to higher values Provider Interface: Unified API for Google Drive and Dropbox implementations

Conflict Resolution

The system uses a two-tier resolution strategy:

  1. Primary: Most recent timestamp wins
  2. Secondary: Higher value wins (only when timestamps identical)

Special handling for week transitions where reset operations take precedence over individual food entries.

Sync Process

  1. Check authentication status
  2. Analyze local dirty flags and remote file metadata
  3. Download remote data if changed
  4. Merge local and remote data using conflict resolution
  5. Upload merged data to cloud
  6. Update local metadata and clear dirty flags

Progressive Web App Features

The application implements PWA technologies for native app-like behavior across platforms.

Service Worker

The service worker (serviceWorker.js) provides:

  • Offline functionality through resource caching
  • Background sync for queued operations
  • App update management

Web App Manifest

The manifest (manifest.json) defines:

  • App identity and branding
  • Installation behavior
  • Display preferences
  • Platform-specific icons

Platform Integration

  • iOS Safari: Specific optimizations for Safari PWA limitations
  • Android Chrome: Full PWA feature support
  • Desktop: Installation support across Windows, macOS, and Linux

Developer Utilities

The application includes debugging and development assistance tools.

Logging System

Configurable logging with module-based organization:

  • Debug, info, warn, and error levels
  • Per-module logger instances
  • Development vs production log filtering

Development Mode Features

When __DEV_MODE__ is enabled:

  • Test date override for simulating date transitions
  • Extended debugging information
  • Development-only UI elements

Debug Tools

Available through the About dialog when developer mode is active:

  • Manual date override for testing transitions
  • Cloud file management for debugging sync issues
  • State inspection and manipulation tools

Performance Considerations

Bundle Optimization

  • Feature Gating: Cloud sync code excluded from client-only builds
  • Tree Shaking: Unused code automatically removed
  • Code Splitting: Lazy loading of optional features
  • Asset Optimization: Image and CSS optimization during build

Storage Efficiency

  • Selective Persistence: Only changed data written to storage
  • Efficient Serialization: Optimized data formats
  • Memory Management: Careful handling of large historical datasets

Network Optimization

  • Incremental Sync: Only changed data synchronized
  • Compression: Data compressed before transmission
  • Network Awareness: Wi-Fi only mode for data conservation
  • Retry Logic: Exponential backoff for failed operations

Extending the Application

Adding Food Groups

Food groups are defined in the foodGroups array in app.js. Each group requires:

{
  id: "unique_id",
  name: "Display Name", 
  frequency: "day" | "week",
  target: number,
  unit: "servings",
  type: "positive" | "limit",
  description: "Serving size information"
}

Adding Cloud Providers

  1. Implement provider class in cloudProviders/ following the common interface
  2. Add OAuth endpoints to server for the new provider
  3. Update setup wizard and settings UI for provider selection
  4. Test authentication and sync flows

Modifying Tracking Logic

The tracking engine can be extended to support:

  • Different reset schedules
  • Custom tracking periods
  • Additional data types beyond food counts
  • Alternative conflict resolution strategies

Code Organization

Module Structure

  • Single Responsibility: Each module focused on one domain
  • Explicit Dependencies: Clear import/export relationships
  • Interface Contracts: Well-defined APIs between modules
  • Error Isolation: Contained error handling within module boundaries

Coding Standards

  • ES6+ Features: Modern JavaScript with backward compatibility
  • JSDoc Documentation: Function and class documentation
  • Consistent Naming: Descriptive variable and function names
  • Type Safety: Runtime validation where appropriate