From a3c92ec45ea54e6c7f28f78e9f8427f507472886 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Wed, 8 Oct 2025 08:10:20 +0000 Subject: [PATCH] docs: add TimeSafari PWA CapacitorPlatformService integration example - Add comprehensive example showing DailyNotification plugin integration with existing TimeSafari PWA architecture - Show how to extend CapacitorPlatformService and PlatformServiceMixin patterns - Provide Vue.js component integration with existing TimeSafari patterns - Include settings management, database operations, and platform detection - Add migration strategy and testing approach for gradual adoption - Show how to maintain existing interfaces while adding plugin features This demonstrates how the plugin integrates with the actual TimeSafari PWA CapacitorPlatformService and PlatformServiceMixin architecture patterns. --- .../timesafari-capacitor-integration-guide.md | 490 +++++++++++++++ ...imesafari-capacitor-integration-example.ts | 594 ++++++++++++++++++ 2 files changed, 1084 insertions(+) create mode 100644 docs/timesafari-capacitor-integration-guide.md create mode 100644 examples/timesafari-capacitor-integration-example.ts diff --git a/docs/timesafari-capacitor-integration-guide.md b/docs/timesafari-capacitor-integration-guide.md new file mode 100644 index 0000000..f2752e4 --- /dev/null +++ b/docs/timesafari-capacitor-integration-guide.md @@ -0,0 +1,490 @@ +# TimeSafari PWA - DailyNotification Plugin Integration Guide + +**Author**: Matthew Raymer +**Version**: 1.0.0 +**Created**: 2025-10-08 06:24:57 UTC + +## Overview + +This guide shows how to integrate the DailyNotification plugin with the existing TimeSafari PWA architecture, specifically the `CapacitorPlatformService` and `PlatformServiceMixin` patterns. The integration maintains the same interfaces and patterns while adding enhanced functionality. + +## TimeSafari PWA Architecture Analysis + +### Existing Architecture Components + +#### 1. CapacitorPlatformService +- **Location**: `src/services/platforms/CapacitorPlatformService.ts` +- **Purpose**: Provides native mobile functionality through Capacitor plugins +- **Features**: File system, camera, SQLite database operations, platform detection +- **Key Methods**: `dbQuery`, `dbExec`, `takePicture`, `writeFile`, `getCapabilities` + +#### 2. PlatformServiceFactory +- **Location**: `src/services/PlatformServiceFactory.ts` +- **Purpose**: Factory pattern for creating platform-specific service implementations +- **Pattern**: Singleton pattern with environment-based platform detection +- **Platforms**: `capacitor`, `electron`, `web` (default) + +#### 3. PlatformServiceMixin +- **Location**: `src/utils/PlatformServiceMixin.ts` +- **Purpose**: Vue.js mixin providing cached platform service access and utility methods +- **Features**: Database operations, settings management, contact management, logging +- **Key Methods**: `$db`, `$exec`, `$settings`, `$contacts`, `$saveSettings` + +### Existing TimeSafari Patterns + +#### 1. Settings Management +```typescript +// Existing TimeSafari settings pattern +const settings = await this.$settings(); +const activeDid = settings.activeDid; +const starredPlanHandleIds = settings.starredPlanHandleIds; +const apiServer = settings.apiServer; +``` + +#### 2. Database Operations +```typescript +// Existing TimeSafari database pattern +const result = await this.$dbQuery("SELECT * FROM settings WHERE id = 1"); +const contacts = await this.$contacts(); +await this.$saveSettings({ starredPlanHandleIds: newIds }); +``` + +#### 3. Platform Detection +```typescript +// Existing TimeSafari platform detection +if (this.isCapacitor) { + // Native mobile functionality +} else { + // Web browser functionality +} +``` + +## DailyNotification Plugin Integration + +### Integration Strategy + +The DailyNotification plugin integrates with the existing TimeSafari PWA architecture by: + +1. **Extending CapacitorPlatformService**: Adding DailyNotification functionality to the existing platform service +2. **Using PlatformServiceMixin**: Leveraging existing database and settings patterns +3. **Maintaining Interfaces**: Keeping the same method signatures and behavior +4. **Platform Detection**: Only initializing on Capacitor platforms + +### Enhanced CapacitorPlatformService + +```typescript +export class EnhancedCapacitorPlatformService { + private platformService = PlatformServiceFactory.getInstance(); + private dailyNotificationService: DailyNotification | null = null; + private integrationService: TimeSafariIntegrationService | null = null; + + async initializeDailyNotification(): Promise { + // Get existing TimeSafari settings + const settings = await this.getTimeSafariSettings(); + + // Configure 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, + timeout: 30000 + } + }); + } + + async loadNewStarredProjectChanges(): Promise { + // Enhanced version of existing TimeSafari method + const settings = await this.getTimeSafariSettings(); + + if (!settings.activeDid || !settings.starredPlanHandleIds?.length) { + return { data: [], hitLimit: false }; + } + + // Use plugin's enhanced fetching + return await this.integrationService!.getStarredProjectsWithChanges( + settings.activeDid, + settings.starredPlanHandleIds, + settings.lastAckedStarredPlanChangesJwtId + ); + } +} +``` + +### Vue.js Component Integration + +```typescript +export const TimeSafariDailyNotificationMixin = { + data() { + return { + // Existing TimeSafari data + activeDid: '', + starredPlanHandleIds: [] as string[], + lastAckedStarredPlanChangesJwtId: '', + numNewStarredProjectChanges: 0, + newStarredProjectChangesHitLimit: false, + + // Plugin integration + enhancedPlatformService: null as EnhancedCapacitorPlatformService | null + }; + }, + + async mounted() { + // Initialize DailyNotification when component mounts (only on Capacitor) + if (this.isCapacitor) { + await this.initializeDailyNotification(); + } + }, + + methods: { + async initializeDailyNotification(): Promise { + this.enhancedPlatformService = new EnhancedCapacitorPlatformService(); + await this.enhancedPlatformService.initializeDailyNotification(); + }, + + async loadNewStarredProjectChanges(): Promise { + if (this.isCapacitor && this.enhancedPlatformService) { + // Use plugin-enhanced method on Capacitor + const result = await this.enhancedPlatformService.loadNewStarredProjectChanges(); + this.numNewStarredProjectChanges = result.data.length; + this.newStarredProjectChangesHitLimit = result.hitLimit; + } else { + // Use existing web method in browser + await this.loadNewStarredProjectChangesWeb(); + } + } + } +}; +``` + +## Integration Steps + +### Step 1: Install the Plugin + +```bash +# In TimeSafari PWA project +npm install ssh://git@173.199.124.46:222/trent_larson/daily-notification-plugin.git +``` + +### Step 2: Create Enhanced Platform Service + +Create `src/services/platforms/EnhancedCapacitorPlatformService.ts`: + +```typescript +import { DailyNotification } from '@timesafari/daily-notification-plugin'; +import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin'; +import { PlatformServiceFactory } from '@/services/PlatformServiceFactory'; + +export class EnhancedCapacitorPlatformService { + // Implementation from the example above +} +``` + +### Step 3: Extend PlatformServiceMixin + +Add to `src/utils/PlatformServiceMixin.ts`: + +```typescript +export const PlatformServiceMixin = { + // ... existing mixin code ... + + methods: { + // ... existing methods ... + + /** + * Initialize DailyNotification plugin (Capacitor only) + */ + async $initializeDailyNotification(): Promise { + if (this.isCapacitor) { + const enhancedService = new EnhancedCapacitorPlatformService(); + await enhancedService.initializeDailyNotification(); + this._enhancedPlatformService = enhancedService; + } + }, + + /** + * Enhanced loadNewStarredProjectChanges method + */ + async $loadNewStarredProjectChanges(): Promise { + if (this.isCapacitor && this._enhancedPlatformService) { + return await this._enhancedPlatformService.loadNewStarredProjectChanges(); + } else { + // Fall back to existing web method + return await this.$loadNewStarredProjectChangesWeb(); + } + } + } +}; +``` + +### Step 4: Update Vue Components + +In your existing TimeSafari Vue components: + +```typescript +export default defineComponent({ + name: 'TimeSafariHomeView', + + mixins: [PlatformServiceMixin], + + async mounted() { + // Initialize DailyNotification (only on Capacitor) + if (this.isCapacitor) { + await this.$initializeDailyNotification(); + } + }, + + methods: { + async loadNewStarredProjectChanges() { + // Use enhanced method + const result = await this.$loadNewStarredProjectChanges(); + this.numNewStarredProjectChanges = result.data.length; + this.newStarredProjectChangesHitLimit = result.hitLimit; + } + } +}); +``` + +## Configuration Mapping + +### TimeSafari Settings → Plugin Configuration + +| TimeSafari Setting | Plugin Configuration | Purpose | +|-------------------|---------------------|---------| +| `settings.activeDid` | `timesafariConfig.activeDid` | User authentication | +| `settings.apiServer` | `networkConfig.baseURL` | API endpoint | +| `settings.starredPlanHandleIds` | `starredProjectsConfig.starredPlanHandleIds` | Project IDs to fetch | +| `settings.lastAckedStarredPlanChangesJwtId` | `starredProjectsConfig.lastAckedJwtId` | Pagination token | + +### Existing Methods → Enhanced Methods + +| Existing Method | Enhanced Method | Enhancement | +|----------------|----------------|-------------| +| `loadNewStarredProjectChanges()` | `$loadNewStarredProjectChanges()` | Background fetching, structured logging | +| `getStarredProjectsWithChanges()` | `integrationService.getStarredProjectsWithChanges()` | Retry logic, circuit breaker | +| Settings storage | Plugin storage adapter | Tiered storage, TTL management | + +## Platform-Specific Behavior + +### Capacitor (Android/iOS) +- **Plugin Enabled**: Full DailyNotification functionality +- **Background Fetching**: WorkManager (Android), BGTaskScheduler (iOS) +- **Notifications**: Native notification channels and categories +- **Storage**: SQLite with plugin's tiered storage system + +### Web Browser +- **Plugin Disabled**: Uses existing TimeSafari web code +- **No Background Fetching**: Limited to browser capabilities +- **No Native Notifications**: Uses web notifications API +- **Storage**: Existing TimeSafari storage patterns + +### Electron +- **Plugin Enabled**: Desktop-specific functionality +- **Background Tasks**: Electron main process +- **Desktop Notifications**: Native desktop notifications +- **Storage**: SQLite with plugin's tiered storage system + +## Benefits of Integration + +### 1. Same Interface, Enhanced Functionality +- Existing `loadNewStarredProjectChanges()` method works exactly the same +- Enhanced with background fetching, structured logging, and error handling +- No changes required to existing UI code + +### 2. Leverages Existing TimeSafari Patterns +- Uses existing `PlatformServiceMixin` for database operations +- Integrates with existing settings management +- Maintains existing platform detection patterns + +### 3. Platform-Optimized Performance +- **Capacitor**: Native mobile optimizations with WorkManager/BGTaskScheduler +- **Web**: Falls back to existing web code seamlessly +- **Electron**: Desktop-specific optimizations + +### 4. Enhanced Observability +- Structured logging with event IDs +- Performance metrics and monitoring +- Error tracking and analysis +- Health checks and status monitoring + +## Migration Strategy + +### Phase 1: Parallel Implementation +1. **Keep existing code unchanged** +2. **Add enhanced platform service alongside existing code** +3. **Test both implementations in parallel** +4. **Compare results to ensure compatibility** + +### Phase 2: Gradual Migration +1. **Replace individual methods one by one** +2. **Use enhanced error handling and logging** +3. **Maintain existing UI and user experience** +4. **Add plugin-specific features gradually** + +### Phase 3: Full Integration +1. **Replace all TimeSafari request patterns with plugin** +2. **Remove duplicate code** +3. **Leverage plugin's advanced features** +4. **Optimize performance with plugin's caching and batching** + +## Testing Strategy + +### 1. Platform-Specific Testing +```typescript +// Test on different platforms +const testPlatforms = async () => { + if (Capacitor.getPlatform() === 'android') { + // Test Android-specific functionality + } else if (Capacitor.getPlatform() === 'ios') { + // Test iOS-specific functionality + } else { + // Test web fallback + } +}; +``` + +### 2. Parallel Testing +```typescript +// Test both implementations +const testBothImplementations = async () => { + // Existing TimeSafari implementation + const existingResult = await this.loadNewStarredProjectChangesOriginal(); + + // Plugin-enhanced implementation + const pluginResult = await this.$loadNewStarredProjectChanges(); + + // Compare results + assert.deepEqual(existingResult, pluginResult); +}; +``` + +### 3. Performance Testing +```typescript +// Compare performance +const performanceTest = async () => { + const start = Date.now(); + await this.$loadNewStarredProjectChanges(); + const pluginTime = Date.now() - start; + + console.log('Plugin performance:', pluginTime); +}; +``` + +## Common Integration Patterns + +### 1. Settings Integration +```typescript +// Get TimeSafari settings +const settings = await this.$settings(); + +// Configure plugin with settings +await DailyNotification.configure({ + timesafariConfig: { + activeDid: settings.activeDid, + starredPlanHandleIds: settings.starredPlanHandleIds + } +}); +``` + +### 2. Database Integration +```typescript +// Store plugin results in TimeSafari database +await this.$dbExec( + "INSERT OR REPLACE INTO temp (id, data) VALUES (?, ?)", + ['starred_projects_latest', JSON.stringify(result)] +); +``` + +### 3. Error Handling Integration +```typescript +// Use existing TimeSafari error handling patterns +try { + const result = await this.$loadNewStarredProjectChanges(); + // Handle success +} catch (error) { + // Use existing TimeSafari error logging + await this.$logError(`Failed to load starred projects: ${error.message}`); +} +``` + +## Troubleshooting + +### Common Issues + +#### 1. Plugin Not Initializing +```typescript +// Check platform detection +if (!this.isCapacitor) { + console.log('Plugin only works on Capacitor platforms'); + return; +} +``` + +#### 2. Settings Not Found +```typescript +// Ensure settings are loaded +const settings = await this.$settings(); +if (!settings.activeDid) { + console.log('No active DID found'); + return; +} +``` + +#### 3. Database Connection Issues +```typescript +// Check database connection +try { + await this.$dbQuery("SELECT 1"); +} catch (error) { + console.error('Database connection failed:', error); +} +``` + +### Debug Methods + +#### 1. Plugin Status +```typescript +const status = await this.getDailyNotificationStatus(); +console.log('Plugin status:', status); +``` + +#### 2. Settings Debug +```typescript +await this.$debugMergedSettings(this.activeDid); +``` + +#### 3. Performance Debug +```typescript +const metrics = await DailyNotification.getMetrics(); +console.log('Plugin metrics:', metrics); +``` + +## Conclusion + +The DailyNotification plugin integrates seamlessly with the existing TimeSafari PWA architecture by: + +- **Extending CapacitorPlatformService** with enhanced functionality +- **Using PlatformServiceMixin** for database and settings operations +- **Maintaining existing interfaces** and method signatures +- **Providing platform-specific optimizations** for Capacitor, Web, and Electron +- **Enhancing observability** with structured logging and metrics + +The integration maintains the same developer experience while adding powerful new features like background fetching, native notifications, and comprehensive monitoring. + +--- + +**Next Steps:** +1. Review the integration example and understand the architecture +2. Test the enhanced platform service with existing TimeSafari code +3. Gradually migrate individual methods to use the plugin +4. Leverage the plugin's advanced features for enhanced user experience diff --git a/examples/timesafari-capacitor-integration-example.ts b/examples/timesafari-capacitor-integration-example.ts new file mode 100644 index 0000000..4d624a5 --- /dev/null +++ b/examples/timesafari-capacitor-integration-example.ts @@ -0,0 +1,594 @@ +/** + * TimeSafari PWA - DailyNotification Plugin Integration Example + * + * This example shows how to integrate the DailyNotification plugin with the existing + * TimeSafari PWA architecture, specifically the CapacitorPlatformService and + * PlatformServiceMixin patterns. + * + * @author Matthew Raymer + * @version 1.0.0 + */ + +import { DailyNotification } from '@timesafari/daily-notification-plugin'; +import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin'; +import { PlatformServiceFactory } from '@/services/PlatformServiceFactory'; +import { Capacitor } from '@capacitor/core'; + +// TimeSafari PWA existing interfaces (from the actual project) +interface PlanSummaryAndPreviousClaim { + id: string; + title: string; + description: string; + lastUpdated: string; + previousClaim?: unknown; +} + +interface StarredProjectsResponse { + data: Array; + hitLimit: boolean; +} + +interface Settings { + accountDid?: string; + activeDid?: string; + apiServer?: string; + starredPlanHandleIds?: string[]; + lastAckedStarredPlanChangesJwtId?: string; + [key: string]: unknown; +} + +/** + * Enhanced CapacitorPlatformService with DailyNotification integration + * + * This extends the existing TimeSafari PWA CapacitorPlatformService to include + * DailyNotification plugin functionality while maintaining the same interface. + */ +export class EnhancedCapacitorPlatformService { + private platformService = PlatformServiceFactory.getInstance(); + private dailyNotificationService: DailyNotification | null = null; + private integrationService: TimeSafariIntegrationService | null = null; + private initialized = false; + + /** + * Initialize DailyNotification plugin with TimeSafari PWA configuration + */ + async initializeDailyNotification(): Promise { + if (this.initialized) { + return; + } + + try { + // Get current TimeSafari settings + const settings = await this.getTimeSafariSettings(); + + // Configure DailyNotification plugin with TimeSafari data + await DailyNotification.configure({ + // Basic plugin configuration + storage: 'tiered', + ttlSeconds: 1800, + enableETagSupport: true, + enableErrorHandling: true, + enablePerformanceOptimization: true, + + // TimeSafari-specific configuration + timesafariConfig: { + // Use existing TimeSafari activeDid + activeDid: settings.activeDid || '', + + // Use existing TimeSafari API endpoints + endpoints: { + offersToPerson: `${settings.apiServer}/api/v2/offers/person`, + offersToPlans: `${settings.apiServer}/api/v2/offers/plans`, + projectsLastUpdated: `${settings.apiServer}/api/v2/report/plansLastUpdatedBetween` + }, + + // Configure starred projects fetching (matches existing TimeSafari pattern) + starredProjectsConfig: { + enabled: true, + starredPlanHandleIds: settings.starredPlanHandleIds || [], + lastAckedJwtId: settings.lastAckedStarredPlanChangesJwtId || '', + fetchInterval: '0 8 * * *', // Daily at 8 AM + maxResults: 50, + hitLimitHandling: 'warn' // Same as existing TimeSafari error handling + }, + + // Sync configuration (optimized for TimeSafari use case) + syncConfig: { + enableParallel: true, + maxConcurrent: 3, + batchSize: 10, + timeout: 30000, + retryAttempts: 3 + }, + + // Error policy (matches existing TimeSafari error handling) + errorPolicy: { + maxRetries: 3, + backoffMultiplier: 2, + activeDidChangeRetries: 5, + starredProjectsRetries: 3 + } + }, + + // Network configuration using existing TimeSafari patterns + networkConfig: { + // Use existing TimeSafari HTTP client (if available) + baseURL: settings.apiServer || 'https://endorser.ch', + timeout: 30000, + retryAttempts: 3, + retryDelay: 1000, + maxConcurrent: 5, + + // Headers matching TimeSafari pattern + defaultHeaders: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'TimeSafari-PWA/1.0.0' + } + }, + + // Content fetch configuration (replaces existing loadNewStarredProjectChanges) + contentFetch: { + enabled: true, + schedule: '0 8 * * *', // Daily at 8 AM + + // Use existing TimeSafari request pattern + requestConfig: { + method: 'POST', + url: `${settings.apiServer}/api/v2/report/plansLastUpdatedBetween`, + headers: { + 'Authorization': 'Bearer ${jwt}', + 'X-User-DID': '${activeDid}', + 'Content-Type': 'application/json' + }, + body: { + planIds: '${starredPlanHandleIds}', + afterId: '${lastAckedJwtId}' + } + }, + + // Callbacks that match TimeSafari error handling + callbacks: { + onSuccess: this.handleStarredProjectsSuccess.bind(this), + onError: this.handleStarredProjectsError.bind(this), + onComplete: this.handleStarredProjectsComplete.bind(this) + } + }, + + // Authentication configuration + authentication: { + jwt: { + secret: process.env.JWT_SECRET || 'timesafari-jwt-secret', + algorithm: 'HS256', + expirationMinutes: 60, + refreshThresholdMinutes: 10 + } + }, + + // Observability configuration + logging: { + level: 'INFO', + enableRequestLogging: true, + enableResponseLogging: true, + enableErrorLogging: true, + redactSensitiveData: true + }, + + // Security configuration + security: { + certificatePinning: { + enabled: true, + pins: [ + { + hostname: 'endorser.ch', + pins: ['sha256/YOUR_PIN_HERE'] + } + ] + } + } + }); + + // Initialize TimeSafari Integration Service + this.integrationService = TimeSafariIntegrationService.getInstance(); + await this.integrationService.initialize({ + activeDid: settings.activeDid || '', + storageAdapter: this.getTimeSafariStorageAdapter(), + endorserApiBaseUrl: settings.apiServer || 'https://endorser.ch', + + // Use existing TimeSafari request patterns + requestConfig: { + baseURL: settings.apiServer || 'https://endorser.ch', + timeout: 30000, + retryAttempts: 3 + }, + + // Configure starred projects fetching + starredProjectsConfig: { + enabled: true, + starredPlanHandleIds: settings.starredPlanHandleIds || [], + lastAckedJwtId: settings.lastAckedStarredPlanChangesJwtId || '', + fetchInterval: '0 8 * * *', + maxResults: 50 + } + }); + + // Schedule daily notifications + await DailyNotification.scheduleDailyNotification({ + title: 'TimeSafari Community Update', + body: 'You have new offers and project updates', + time: '09:00', + channel: 'timesafari_community_updates' + }); + + this.initialized = true; + console.log('DailyNotification plugin initialized successfully with TimeSafari PWA'); + + } catch (error) { + console.error('Failed to initialize DailyNotification plugin:', error); + throw error; + } + } + + /** + * Enhanced version of existing TimeSafari loadNewStarredProjectChanges method + * + * This replaces the existing method with plugin-enhanced functionality + * while maintaining the same interface and behavior. + */ + async loadNewStarredProjectChanges(): Promise { + if (!this.initialized) { + 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 + ); + + // Enhanced logging (optional) + console.log('Starred projects loaded successfully:', { + count: starredProjectChanges.data.length, + hitLimit: starredProjectChanges.hitLimit, + planIds: settings.starredPlanHandleIds.length + }); + + return starredProjectChanges; + + } catch (error) { + // Same error handling as existing TimeSafari code + console.warn('[TimeSafari] Failed to load starred project changes:', error); + return { data: [], hitLimit: false }; + } + } + + /** + * Get TimeSafari settings using existing PlatformServiceMixin pattern + */ + private async getTimeSafariSettings(): Promise { + try { + // Use existing TimeSafari settings retrieval pattern + const result = await this.platformService.dbQuery( + "SELECT * FROM settings WHERE id = 1" + ); + + if (!result?.values?.length) { + return {}; + } + + // Map database columns to values (existing TimeSafari pattern) + const settings: Settings = {}; + result.columns.forEach((column, index) => { + if (column !== 'id') { + settings[column] = result.values[0][index]; + } + }); + + // Handle JSON field parsing (existing TimeSafari pattern) + if (settings.starredPlanHandleIds && typeof settings.starredPlanHandleIds === 'string') { + try { + settings.starredPlanHandleIds = JSON.parse(settings.starredPlanHandleIds); + } catch { + settings.starredPlanHandleIds = []; + } + } + + return settings; + } catch (error) { + console.error('Error getting TimeSafari settings:', error); + return {}; + } + } + + /** + * Get TimeSafari storage adapter using existing patterns + */ + private getTimeSafariStorageAdapter(): unknown { + // Return existing TimeSafari storage adapter + return { + // Use existing TimeSafari storage patterns + store: async (key: string, value: unknown) => { + await this.platformService.dbExec( + "INSERT OR REPLACE INTO temp (id, data) VALUES (?, ?)", + [key, JSON.stringify(value)] + ); + }, + + retrieve: async (key: string) => { + const result = await this.platformService.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 that match existing TimeSafari error handling patterns + */ + private async handleStarredProjectsSuccess(data: StarredProjectsResponse): Promise { + // Enhanced logging (optional) + console.log('Starred projects success callback:', { + count: data.data.length, + hitLimit: data.hitLimit + }); + + // Store results in TimeSafari temp table for UI access + await this.platformService.dbExec( + "INSERT OR REPLACE INTO temp (id, data) VALUES (?, ?)", + ['starred_projects_latest', JSON.stringify(data)] + ); + } + + private async handleStarredProjectsError(error: Error): Promise { + // Same error handling as existing TimeSafari code + console.warn('[TimeSafari] Failed to load starred project changes:', error); + + // Store error in TimeSafari temp table for UI access + await this.platformService.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 { + // Handle completion + console.log('Starred projects fetch completed:', result); + } + + /** + * Get plugin status for debugging + */ + async getDailyNotificationStatus(): Promise<{ + initialized: boolean; + platform: string; + capabilities: unknown; + }> { + return { + initialized: this.initialized, + platform: Capacitor.getPlatform(), + capabilities: this.platformService.getCapabilities() + }; + } +} + +/** + * Vue.js Mixin for DailyNotification integration with TimeSafari PWA + * + * This mixin extends the existing PlatformServiceMixin to include + * DailyNotification functionality while maintaining the same patterns. + */ +export const TimeSafariDailyNotificationMixin = { + data() { + return { + // Existing TimeSafari data + activeDid: '', + starredPlanHandleIds: [] as string[], + lastAckedStarredPlanChangesJwtId: '', + numNewStarredProjectChanges: 0, + newStarredProjectChangesHitLimit: false, + + // Plugin integration + dailyNotificationService: null as DailyNotification | null, + integrationService: null as TimeSafariIntegrationService | null, + enhancedPlatformService: null as EnhancedCapacitorPlatformService | null + }; + }, + + computed: { + // Existing TimeSafari computed properties + isCapacitor(): boolean { + return this.platformService.isCapacitor(); + }, + + isWeb(): boolean { + return this.platformService.isWeb(); + }, + + isElectron(): boolean { + return this.platformService.isElectron(); + } + }, + + async mounted() { + // Initialize DailyNotification when component mounts (only on Capacitor) + if (this.isCapacitor) { + await this.initializeDailyNotification(); + } + }, + + methods: { + /** + * Initialize DailyNotification plugin with TimeSafari configuration + */ + async initializeDailyNotification(): Promise { + try { + // Create enhanced platform service + this.enhancedPlatformService = new EnhancedCapacitorPlatformService(); + + // Initialize the plugin + await this.enhancedPlatformService.initializeDailyNotification(); + + console.log('DailyNotification initialized successfully in TimeSafari PWA'); + + } catch (error) { + console.error('Failed to initialize DailyNotification in TimeSafari PWA:', error); + } + }, + + /** + * Enhanced version of existing TimeSafari loadNewStarredProjectChanges method + */ + async loadNewStarredProjectChanges(): Promise { + if (this.isCapacitor && this.enhancedPlatformService) { + // Use plugin-enhanced method on Capacitor + const result = await this.enhancedPlatformService.loadNewStarredProjectChanges(); + this.numNewStarredProjectChanges = result.data.length; + this.newStarredProjectChangesHitLimit = result.hitLimit; + } else { + // Use existing web method in browser + await this.loadNewStarredProjectChangesWeb(); + } + }, + + /** + * Existing web-only method (unchanged) + */ + async loadNewStarredProjectChangesWeb(): Promise { + // Your existing web-only implementation + console.log('Using web-only method for starred projects'); + }, + + /** + * Get DailyNotification status for debugging + */ + async getDailyNotificationStatus(): Promise { + if (this.enhancedPlatformService) { + return await this.enhancedPlatformService.getDailyNotificationStatus(); + } + return { initialized: false, platform: 'web' }; + } + } +}; + +/** + * Usage example in a TimeSafari PWA Vue component + */ +export const TimeSafariHomeViewExample = { + name: 'TimeSafariHomeView', + + mixins: [TimeSafariDailyNotificationMixin], + + data() { + return { + // Existing TimeSafari data + activeDid: '', + starredPlanHandleIds: [] as string[], + lastAckedStarredPlanChangesJwtId: '', + numNewStarredProjectChanges: 0, + newStarredProjectChangesHitLimit: false + }; + }, + + async mounted() { + // Load existing TimeSafari data + await this.loadTimeSafariData(); + + // Initialize DailyNotification (only on Capacitor) + if (this.isCapacitor) { + await this.initializeDailyNotification(); + } + }, + + methods: { + /** + * Load existing TimeSafari data using PlatformServiceMixin + */ + async loadTimeSafariData(): Promise { + try { + // Use existing TimeSafari settings pattern + const settings = await this.$settings(); + + this.activeDid = settings.activeDid || ''; + this.starredPlanHandleIds = settings.starredPlanHandleIds || []; + this.lastAckedStarredPlanChangesJwtId = settings.lastAckedStarredPlanChangesJwtId || ''; + + } catch (error) { + console.error('Error loading TimeSafari data:', error); + } + }, + + /** + * Enhanced loadNewStarredProjectChanges method + */ + async loadNewStarredProjectChanges(): Promise { + if (this.isCapacitor && this.enhancedPlatformService) { + // Use plugin-enhanced method on Capacitor + const result = await this.enhancedPlatformService.loadNewStarredProjectChanges(); + this.numNewStarredProjectChanges = result.data.length; + this.newStarredProjectChangesHitLimit = result.hitLimit; + } else { + // Use existing web method in browser + await this.loadNewStarredProjectChangesWeb(); + } + }, + + /** + * Existing web-only method (unchanged) + */ + async loadNewStarredProjectChangesWeb(): Promise { + // Your existing web-only implementation + console.log('Using web-only method for starred projects'); + } + } +}; + +/** + * Factory function to create enhanced platform service + */ +export function createEnhancedPlatformService(): EnhancedCapacitorPlatformService { + return new EnhancedCapacitorPlatformService(); +} + +/** + * Utility function to check if DailyNotification should be used + */ +export function shouldUseDailyNotification(): boolean { + return Capacitor.isNativePlatform(); +} + +/** + * Utility function to get platform-specific configuration + */ +export function getPlatformConfig(): { + usePlugin: boolean; + platform: string; + capabilities: unknown; +} { + const platform = Capacitor.getPlatform(); + const platformService = PlatformServiceFactory.getInstance(); + + return { + usePlugin: Capacitor.isNativePlatform(), + platform, + capabilities: platformService.getCapabilities() + }; +}