# TimeSafari PWA - CapacitorPlatformService Changes Summary **Author**: Matthew Raymer **Version**: 1.0.0 **Created**: 2025-10-08 06:24:57 UTC ## Overview This document summarizes the **exact changes** needed to the existing TimeSafari PWA `CapacitorPlatformService` to add DailyNotification plugin functionality. These are the actual modifications required to the existing codebase. ## Required Changes to Existing TimeSafari PWA Code ### File: `src/services/platforms/CapacitorPlatformService.ts` #### 1. Add New Imports (at the top of the file) ```typescript // ADD THESE IMPORTS import { DailyNotification } from '@timesafari/daily-notification-plugin'; import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin'; ``` #### 2. Add New Interfaces (after existing interfaces) ```typescript // ADD THESE INTERFACES interface PlanSummaryAndPreviousClaim { id: string; title: string; description: string; lastUpdated: string; previousClaim?: unknown; } interface StarredProjectsResponse { data: Array; hitLimit: boolean; } interface TimeSafariSettings { accountDid?: string; activeDid?: string; apiServer?: string; starredPlanHandleIds?: string[]; lastAckedStarredPlanChangesJwtId?: string; [key: string]: unknown; } ``` #### 3. Add New Properties (in the class, after existing properties) ```typescript export class CapacitorPlatformService implements PlatformService { // ... existing properties ... // ADD THESE NEW PROPERTIES private dailyNotificationService: DailyNotification | null = null; private integrationService: TimeSafariIntegrationService | null = null; private dailyNotificationInitialized = false; } ``` #### 4. Add New Methods (in the class, after existing methods) ```typescript /** * Initialize DailyNotification plugin with TimeSafari configuration */ async initializeDailyNotification(): Promise { if (this.dailyNotificationInitialized) { return; } try { logger.log("[CapacitorPlatformService] Initializing DailyNotification plugin..."); // Get current TimeSafari settings const settings = await this.getTimeSafariSettings(); // Configure DailyNotification plugin with TimeSafari data await DailyNotification.configure({ timesafariConfig: { activeDid: settings.activeDid || '', endpoints: { projectsLastUpdated: `${settings.apiServer}/api/v2/report/plansLastUpdatedBetween` }, starredProjectsConfig: { enabled: true, starredPlanHandleIds: settings.starredPlanHandleIds || [], lastAckedJwtId: settings.lastAckedStarredPlanChangesJwtId || '', fetchInterval: '0 8 * * *' } }, networkConfig: { baseURL: settings.apiServer || 'https://endorser.ch', timeout: 30000 } }); // Initialize TimeSafari Integration Service this.integrationService = TimeSafariIntegrationService.getInstance(); await this.integrationService.initialize({ activeDid: settings.activeDid || '', storageAdapter: this.getTimeSafariStorageAdapter(), endorserApiBaseUrl: settings.apiServer || 'https://endorser.ch' }); this.dailyNotificationInitialized = true; logger.log("[CapacitorPlatformService] DailyNotification plugin initialized successfully"); } catch (error) { logger.error("[CapacitorPlatformService] Failed to initialize DailyNotification plugin:", error); throw error; } } /** * Enhanced version of existing TimeSafari loadNewStarredProjectChanges method */ async loadNewStarredProjectChanges(): Promise { // Ensure DailyNotification is initialized if (!this.dailyNotificationInitialized) { await this.initializeDailyNotification(); } const settings = await this.getTimeSafariSettings(); if (!settings.activeDid || !settings.starredPlanHandleIds?.length) { return { data: [], hitLimit: false }; } try { // Use plugin's enhanced fetching with same interface as existing TimeSafari code const starredProjectChanges = await this.integrationService!.getStarredProjectsWithChanges( settings.activeDid, settings.starredPlanHandleIds, settings.lastAckedStarredPlanChangesJwtId ); return starredProjectChanges; } catch (error) { // Same error handling as existing TimeSafari code logger.warn("[CapacitorPlatformService] Failed to load starred project changes:", error); return { data: [], hitLimit: false }; } } /** * Get TimeSafari settings using existing database patterns */ private async getTimeSafariSettings(): Promise { try { const result = await this.dbQuery("SELECT * FROM settings WHERE id = 1"); if (!result?.values?.length) { return {}; } const settings: TimeSafariSettings = {}; result.columns.forEach((column, index) => { if (column !== 'id') { settings[column] = result.values[0][index]; } }); // Handle JSON field parsing if (settings.starredPlanHandleIds && typeof settings.starredPlanHandleIds === 'string') { try { settings.starredPlanHandleIds = JSON.parse(settings.starredPlanHandleIds); } catch { settings.starredPlanHandleIds = []; } } return settings; } catch (error) { logger.error("[CapacitorPlatformService] Error getting TimeSafari settings:", error); return {}; } } /** * Get TimeSafari storage adapter using existing patterns */ private getTimeSafariStorageAdapter(): unknown { return { store: async (key: string, value: unknown) => { await this.dbExec( "INSERT OR REPLACE INTO temp (id, data) VALUES (?, ?)", [key, JSON.stringify(value)] ); }, retrieve: async (key: string) => { const result = await this.dbQuery( "SELECT data FROM temp WHERE id = ?", [key] ); if (result?.values?.length) { try { return JSON.parse(result.values[0][0] as string); } catch { return null; } } return null; } }; } /** * Callback handlers for DailyNotification plugin */ private async handleStarredProjectsSuccess(data: StarredProjectsResponse): Promise { logger.log("[CapacitorPlatformService] Starred projects success callback:", { count: data.data.length, hitLimit: data.hitLimit }); // Store results in TimeSafari temp table for UI access await this.dbExec( "INSERT OR REPLACE INTO temp (id, data) VALUES (?, ?)", ['starred_projects_latest', JSON.stringify(data)] ); } private async handleStarredProjectsError(error: Error): Promise { logger.warn("[CapacitorPlatformService] Failed to load starred project changes:", error); // Store error in TimeSafari temp table for UI access await this.dbExec( "INSERT OR REPLACE INTO temp (id, data) VALUES (?, ?)", ['starred_projects_error', JSON.stringify({ error: error.message, timestamp: Date.now() })] ); } private async handleStarredProjectsComplete(result: unknown): Promise { logger.log("[CapacitorPlatformService] Starred projects fetch completed:", result); } /** * Get DailyNotification plugin status for debugging */ async getDailyNotificationStatus(): Promise<{ initialized: boolean; platform: string; capabilities: PlatformCapabilities; }> { return { initialized: this.dailyNotificationInitialized, platform: Capacitor.getPlatform(), capabilities: this.getCapabilities() }; } ``` #### 5. Modify Existing Method (update the `initializeDatabase` method) ```typescript private async initializeDatabase(): Promise { // If already initialized, return immediately if (this.initialized) { return; } // If initialization is in progress, wait for it if (this.initializationPromise) { return this.initializationPromise; } try { // Start initialization this.initializationPromise = this._initialize(); await this.initializationPromise; // ADD THIS LINE: Initialize DailyNotification after database is ready await this.initializeDailyNotification(); } catch (error) { logger.error( "[CapacitorPlatformService] Initialize database method failed:", error, ); this.initializationPromise = null; // Reset on failure throw error; } } ``` ## Required Changes to PlatformServiceMixin ### File: `src/utils/PlatformServiceMixin.ts` #### 1. Add New Methods (in the methods section) ```typescript methods: { // ... existing methods ... /** * Initialize DailyNotification plugin (Capacitor only) */ async $initializeDailyNotification(): Promise { if (this.isCapacitor) { await this.platformService.initializeDailyNotification(); } }, /** * Enhanced loadNewStarredProjectChanges method */ async $loadNewStarredProjectChanges(): Promise { if (this.isCapacitor) { return await this.platformService.loadNewStarredProjectChanges(); } else { // Fall back to existing web method return await this.$loadNewStarredProjectChangesWeb(); } }, /** * Get DailyNotification status for debugging */ async $getDailyNotificationStatus(): Promise { if (this.isCapacitor) { return await this.platformService.getDailyNotificationStatus(); } return { initialized: false, platform: 'web' }; } } ``` ## Required Changes to Vue Components ### File: `src/views/HomeView.vue` (or similar) #### 1. Add DailyNotification Initialization (in mounted lifecycle) ```typescript export default defineComponent({ name: 'HomeView', mixins: [PlatformServiceMixin], async mounted() { // ADD THIS: Initialize DailyNotification (only on Capacitor) if (this.isCapacitor) { await this.$initializeDailyNotification(); } // ... existing mounted code ... }, methods: { // ... existing methods ... /** * Enhanced loadNewStarredProjectChanges method */ async loadNewStarredProjectChanges(): Promise { if (this.isCapacitor) { // Use plugin-enhanced method on Capacitor const result = await this.$loadNewStarredProjectChanges(); this.numNewStarredProjectChanges = result.data.length; this.newStarredProjectChangesHitLimit = result.hitLimit; } else { // Use existing web method in browser await this.loadNewStarredProjectChangesWeb(); } } } }); ``` ## Package.json Changes ### Add DailyNotification Plugin Dependency ```json { "dependencies": { "@timesafari/daily-notification-plugin": "ssh://git@173.199.124.46:222/trent_larson/daily-notification-plugin.git" } } ``` ## Summary of Changes ### Files Modified: 1. **`src/services/platforms/CapacitorPlatformService.ts`** - Add imports for DailyNotification plugin - Add new interfaces for plugin integration - Add new properties for plugin state - Add new methods for plugin functionality - Modify existing `initializeDatabase` method 2. **`src/utils/PlatformServiceMixin.ts`** - Add new methods for plugin integration - Add platform detection for plugin usage 3. **Vue Components (e.g., `src/views/HomeView.vue`)** - Add plugin initialization in mounted lifecycle - Add enhanced methods for plugin functionality 4. **`package.json`** - Add DailyNotification plugin dependency ### Key Benefits: - **Same Interface**: Existing methods work exactly the same - **Enhanced Functionality**: Background fetching, structured logging, error handling - **Platform Detection**: Only initializes on Capacitor platforms - **Gradual Migration**: Can be adopted incrementally - **No Breaking Changes**: Existing code continues to work ### Migration Strategy: 1. **Add the changes** to existing TimeSafari PWA code 2. **Test on Capacitor platforms** (Android, iOS) 3. **Verify web fallback** works correctly 4. **Gradually migrate** individual methods to use plugin features 5. **Leverage advanced features** like background fetching and observability --- **These are the exact changes needed to integrate the DailyNotification plugin with the existing TimeSafari PWA CapacitorPlatformService architecture.**