From 86c395c70ecb7e97daa5d6802a2ded6117b27b5a Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Wed, 8 Oct 2025 07:25:37 +0000 Subject: [PATCH] docs: add DailyNotification setup guide and practical example - Add comprehensive setup guide showing exact configuration for TimeSafari PWA - Provide step-by-step instructions for integrating with existing loadNewStarredProjectChanges() - Include complete working example with all configuration options - Show Vue.js component integration patterns - Add troubleshooting and testing guidance - Demonstrate how to maintain existing interfaces while adding plugin features This provides a practical, copy-paste ready setup for TimeSafari PWA integration. --- docs/daily-notification-setup-guide.md | 387 +++++++++++++++ .../daily-notification-timesafari-setup.ts | 466 ++++++++++++++++++ 2 files changed, 853 insertions(+) create mode 100644 docs/daily-notification-setup-guide.md create mode 100644 examples/daily-notification-timesafari-setup.ts diff --git a/docs/daily-notification-setup-guide.md b/docs/daily-notification-setup-guide.md new file mode 100644 index 0000000..4f8f757 --- /dev/null +++ b/docs/daily-notification-setup-guide.md @@ -0,0 +1,387 @@ +# DailyNotification Setup Guide for TimeSafari PWA + +**Author**: Matthew Raymer +**Version**: 1.0.0 +**Created**: 2025-10-08 06:24:57 UTC + +## Overview + +This guide shows you exactly how to set up the DailyNotification plugin in your existing TimeSafari PWA to work with your current `loadNewStarredProjectChanges()` and `getStarredProjectsWithChanges()` methods. + +## Step-by-Step Setup + +### Step 1: Install the Plugin + +```bash +# In your TimeSafari PWA project +npm install ssh://git@173.199.124.46:222/trent_larson/daily-notification-plugin.git +``` + +### Step 2: Import the Plugin + +```typescript +// In your TimeSafari PWA file (e.g., HomeView.vue or main.ts) +import { DailyNotification } from '@timesafari/daily-notification-plugin'; +import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin'; +``` + +### Step 3: Configure the Plugin + +Add this configuration to your existing TimeSafari PWA code: + +```typescript +// In your TimeSafari PWA class/component +async setupDailyNotification() { + try { + // Configure the plugin with your existing TimeSafari settings + await DailyNotification.configure({ + // Basic configuration + storage: 'tiered', + ttlSeconds: 1800, + enableETagSupport: true, + + // TimeSafari-specific configuration + timesafariConfig: { + // Your existing activeDid + activeDid: this.activeDid, + + // Your existing API endpoints + endpoints: { + offersToPerson: `${this.apiServer}/api/v2/offers/person`, + offersToPlans: `${this.apiServer}/api/v2/offers/plans`, + projectsLastUpdated: `${this.apiServer}/api/v2/report/plansLastUpdatedBetween` + }, + + // Configure starred projects fetching (matches your existing pattern) + starredProjectsConfig: { + enabled: true, + starredPlanHandleIds: this.starredPlanHandleIds, + lastAckedJwtId: this.lastAckedStarredPlanChangesJwtId, + fetchInterval: '0 8 * * *', // Daily at 8 AM + maxResults: 50, + hitLimitHandling: 'warn' + } + }, + + // Network configuration using your existing axios instance + networkConfig: { + httpClient: this.axios, // Your existing axios instance + baseURL: this.apiServer, // Your existing API server + timeout: 30000, + retryAttempts: 3 + }, + + // Content fetch configuration (replaces your existing method) + contentFetch: { + enabled: true, + schedule: '0 8 * * *', // Daily at 8 AM + + // Your existing request pattern + requestConfig: { + method: 'POST', + url: `${this.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 your existing error handling + callbacks: { + onSuccess: this.handleStarredProjectsSuccess.bind(this), + onError: this.handleStarredProjectsError.bind(this) + } + } + }); + + // Initialize TimeSafari Integration Service + this.integrationService = TimeSafariIntegrationService.getInstance(); + await this.integrationService.initialize({ + activeDid: this.activeDid, + storageAdapter: this.getTimeSafariStorageAdapter(), + endorserApiBaseUrl: this.apiServer + }); + + console.log('DailyNotification setup completed successfully!'); + + } catch (error) { + console.error('Failed to setup DailyNotification:', error); + } +} +``` + +### Step 4: Replace Your Existing Method + +Replace your existing `loadNewStarredProjectChanges()` method with this enhanced version: + +```typescript +// Enhanced version of your existing method +async loadNewStarredProjectChanges() { + if (this.activeDid && this.starredPlanHandleIds.length > 0) { + try { + // Use plugin's enhanced fetching with same interface as your existing code + const starredProjectChanges = await this.integrationService.getStarredProjectsWithChanges( + this.activeDid, + this.starredPlanHandleIds, + this.lastAckedStarredPlanChangesJwtId + ); + + // Same handling as your existing code + this.numNewStarredProjectChanges = starredProjectChanges.data.length; + this.newStarredProjectChangesHitLimit = starredProjectChanges.hitLimit; + + } catch (error) { + // Same error handling as your existing code + console.warn('[HomeView] Failed to load starred project changes:', error); + this.numNewStarredProjectChanges = 0; + this.newStarredProjectChangesHitLimit = false; + } + } else { + this.numNewStarredProjectChanges = 0; + this.newStarredProjectChangesHitLimit = false; + } +} +``` + +### Step 5: Add Callback Handlers + +Add these callback handlers that match your existing error handling patterns: + +```typescript +// Callback handlers that match your existing error handling +async handleStarredProjectsSuccess(data: { data: Array; hitLimit: boolean }) { + // Same handling as your existing code + this.numNewStarredProjectChanges = data.data.length; + this.newStarredProjectChangesHitLimit = data.hitLimit; + + // Update UI (your existing method) + this.updateStarredProjectsUI(data); +} + +async handleStarredProjectsError(error: Error) { + // Same error handling as your existing code + console.warn('[HomeView] Failed to load starred project changes:', error); + this.numNewStarredProjectChanges = 0; + this.newStarredProjectChangesHitLimit = false; +} +``` + +### Step 6: Initialize in Your Component + +Add the setup to your existing TimeSafari PWA component: + +```typescript +// In your existing TimeSafari PWA component (e.g., HomeView.vue) +export default defineComponent({ + name: 'HomeView', + + data() { + return { + // Your existing data + activeDid: '', + starredPlanHandleIds: [] as string[], + lastAckedStarredPlanChangesJwtId: '', + numNewStarredProjectChanges: 0, + newStarredProjectChangesHitLimit: false, + + // Plugin integration + integrationService: null as TimeSafariIntegrationService | null + }; + }, + + async mounted() { + // Setup DailyNotification when component mounts + await this.setupDailyNotification(); + }, + + methods: { + // Add the setupDailyNotification method from Step 3 + async setupDailyNotification() { /* ... */ }, + + // Replace your existing loadNewStarredProjectChanges with Step 4 version + async loadNewStarredProjectChanges() { /* ... */ }, + + // Add callback handlers from Step 5 + async handleStarredProjectsSuccess(data) { /* ... */ }, + async handleStarredProjectsError(error) { /* ... */ }, + + // Your existing methods (unchanged) + updateStarredProjectsUI(data) { /* ... */ }, + getTimeSafariStorageAdapter() { /* ... */ } + } +}); +``` + +## Complete Example + +Here's a complete example showing how to integrate the plugin into your existing TimeSafari PWA: + +```typescript +// Complete integration example +import { DailyNotification } from '@timesafari/daily-notification-plugin'; +import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin'; + +class TimeSafariHomeView { + // Your existing properties + activeDid: string = ''; + starredPlanHandleIds: string[] = []; + lastAckedStarredPlanChangesJwtId: string = ''; + numNewStarredProjectChanges: number = 0; + newStarredProjectChangesHitLimit: boolean = false; + apiServer: string = 'https://endorser.ch'; + axios: AxiosInstance; + + // Plugin integration + private integrationService: TimeSafariIntegrationService | null = null; + + constructor(axiosInstance: AxiosInstance) { + this.axios = axiosInstance; + } + + async setupDailyNotification() { + await DailyNotification.configure({ + timesafariConfig: { + activeDid: this.activeDid, + endpoints: { + projectsLastUpdated: `${this.apiServer}/api/v2/report/plansLastUpdatedBetween` + }, + starredProjectsConfig: { + enabled: true, + starredPlanHandleIds: this.starredPlanHandleIds, + lastAckedJwtId: this.lastAckedStarredPlanChangesJwtId, + fetchInterval: '0 8 * * *' + } + }, + networkConfig: { + httpClient: this.axios, + baseURL: this.apiServer, + timeout: 30000 + }, + contentFetch: { + enabled: true, + schedule: '0 8 * * *', + callbacks: { + onSuccess: this.handleStarredProjectsSuccess.bind(this), + onError: this.handleStarredProjectsError.bind(this) + } + } + }); + + this.integrationService = TimeSafariIntegrationService.getInstance(); + await this.integrationService.initialize({ + activeDid: this.activeDid, + storageAdapter: this.getTimeSafariStorageAdapter(), + endorserApiBaseUrl: this.apiServer + }); + } + + async loadNewStarredProjectChanges() { + if (this.activeDid && this.starredPlanHandleIds.length > 0) { + try { + const starredProjectChanges = await this.integrationService!.getStarredProjectsWithChanges( + this.activeDid, + this.starredPlanHandleIds, + this.lastAckedStarredPlanChangesJwtId + ); + + this.numNewStarredProjectChanges = starredProjectChanges.data.length; + this.newStarredProjectChangesHitLimit = starredProjectChanges.hitLimit; + + } catch (error) { + console.warn('[HomeView] Failed to load starred project changes:', error); + this.numNewStarredProjectChanges = 0; + this.newStarredProjectChangesHitLimit = false; + } + } else { + this.numNewStarredProjectChanges = 0; + this.newStarredProjectChangesHitLimit = false; + } + } + + async handleStarredProjectsSuccess(data: { data: Array; hitLimit: boolean }) { + this.numNewStarredProjectChanges = data.data.length; + this.newStarredProjectChangesHitLimit = data.hitLimit; + this.updateStarredProjectsUI(data); + } + + async handleStarredProjectsError(error: Error) { + console.warn('[HomeView] Failed to load starred project changes:', error); + this.numNewStarredProjectChanges = 0; + this.newStarredProjectChangesHitLimit = false; + } + + // Your existing methods (unchanged) + private updateStarredProjectsUI(data: { data: Array; hitLimit: boolean }) { + // Your existing UI update logic + } + + private getTimeSafariStorageAdapter() { + // Your existing storage adapter + return {}; + } +} +``` + +## What You Get + +After following this setup, you'll have: + +✅ **Same Interface**: Your existing `loadNewStarredProjectChanges()` method works exactly the same +✅ **Enhanced Features**: Background fetching, structured logging, metrics +✅ **Better Error Handling**: Enhanced error handling with retry logic +✅ **Performance Improvements**: Caching, batching, and optimization +✅ **Observability**: Structured logging with event IDs and metrics +✅ **Background Notifications**: Daily notifications with your starred projects data + +## Testing + +Test the integration by calling your existing method: + +```typescript +// Test the enhanced method +await homeView.loadNewStarredProjectChanges(); + +// Check the results (same as before) +console.log('New starred project changes:', homeView.numNewStarredProjectChanges); +console.log('Hit limit:', homeView.newStarredProjectChangesHitLimit); +``` + +## Troubleshooting + +### Common Issues + +1. **Plugin not found**: Make sure you've installed the plugin correctly +2. **Configuration errors**: Check that all required fields are provided +3. **Network errors**: Verify your axios instance and API server URL +4. **Authentication errors**: Check your JWT token and activeDid + +### Debug Mode + +Enable debug logging to troubleshoot issues: + +```typescript +await DailyNotification.configure({ + logging: { + level: 'DEBUG', + enableRequestLogging: true, + enableResponseLogging: true, + enableErrorLogging: true + } +}); +``` + +## Next Steps + +1. **Test the integration** with your existing TimeSafari PWA code +2. **Compare results** between your existing method and the plugin-enhanced version +3. **Gradually migrate** other request methods to use the plugin +4. **Leverage advanced features** like background fetching and observability + +--- + +**That's it!** Your TimeSafari PWA now has enhanced daily notification capabilities while maintaining the same interface and behavior you're already familiar with. diff --git a/examples/daily-notification-timesafari-setup.ts b/examples/daily-notification-timesafari-setup.ts new file mode 100644 index 0000000..5858046 --- /dev/null +++ b/examples/daily-notification-timesafari-setup.ts @@ -0,0 +1,466 @@ +/** + * DailyNotification Setup for TimeSafari PWA + * + * This example shows exactly how to configure the DailyNotification plugin + * to work with your existing TimeSafari PWA request patterns, specifically + * the loadNewStarredProjectChanges() and getStarredProjectsWithChanges() methods. + * + * @author Matthew Raymer + * @version 1.0.0 + */ + +import { DailyNotification } from '@timesafari/daily-notification-plugin'; +import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin'; +import { AxiosInstance } from 'axios'; + +// Your existing TimeSafari PWA interfaces +interface PlanSummaryAndPreviousClaim { + id: string; + title: string; + description: string; + lastUpdated: string; + previousClaim?: unknown; +} + +interface StarredProjectsResponse { + data: Array; + hitLimit: boolean; +} + +// Your existing TimeSafari PWA class structure +class TimeSafariHomeView { + // Your existing properties + activeDid: string = ''; + starredPlanHandleIds: string[] = []; + lastAckedStarredPlanChangesJwtId: string = ''; + numNewStarredProjectChanges: number = 0; + newStarredProjectChangesHitLimit: boolean = false; + apiServer: string = 'https://endorser.ch'; + axios: AxiosInstance; + + // Plugin integration + private dailyNotificationService: DailyNotification | null = null; + private integrationService: TimeSafariIntegrationService | null = null; + + constructor(axiosInstance: AxiosInstance) { + this.axios = axiosInstance; + } + + /** + * DailyNotification Setup - This is what you need to add to your TimeSafari PWA + */ + async setupDailyNotification(): Promise { + try { + console.log('Setting up DailyNotification for TimeSafari PWA...'); + + // Step 1: Configure the DailyNotification plugin + await DailyNotification.configure({ + // Basic plugin configuration + storage: 'tiered', + ttlSeconds: 1800, + enableETagSupport: true, + enableErrorHandling: true, + enablePerformanceOptimization: true, + + // TimeSafari-specific configuration + timesafariConfig: { + // Required: Your existing activeDid + activeDid: this.activeDid, + + // Your existing API endpoints + endpoints: { + offersToPerson: `${this.apiServer}/api/v2/offers/person`, + offersToPlans: `${this.apiServer}/api/v2/offers/plans`, + projectsLastUpdated: `${this.apiServer}/api/v2/report/plansLastUpdatedBetween` + }, + + // Configure starred projects fetching (matches your existing pattern) + starredProjectsConfig: { + enabled: true, + starredPlanHandleIds: this.starredPlanHandleIds, + lastAckedJwtId: this.lastAckedStarredPlanChangesJwtId, + fetchInterval: '0 8 * * *', // Daily at 8 AM (same as your existing schedule) + maxResults: 50, + hitLimitHandling: 'warn' // Same as your existing error handling + }, + + // Sync configuration (optimized for your use case) + syncConfig: { + enableParallel: true, + maxConcurrent: 3, + batchSize: 10, + timeout: 30000, + retryAttempts: 3 + }, + + // Error policy (matches your existing error handling) + errorPolicy: { + maxRetries: 3, + backoffMultiplier: 2, + activeDidChangeRetries: 5, + starredProjectsRetries: 3 + } + }, + + // Network configuration using your existing axios instance + networkConfig: { + // Use your existing axios instance + httpClient: this.axios, + baseURL: this.apiServer, + timeout: 30000, + retryAttempts: 3, + retryDelay: 1000, + maxConcurrent: 5, + + // Headers matching your existing pattern + defaultHeaders: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'TimeSafari-PWA/1.0.0' + } + }, + + // Content fetch configuration (replaces your existing loadNewStarredProjectChanges) + contentFetch: { + enabled: true, + schedule: '0 8 * * *', // Daily at 8 AM + + // Your existing request pattern + requestConfig: { + method: 'POST', + url: `${this.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 your existing 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 || 'your-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'] + } + ] + } + } + }); + + // Step 2: Initialize TimeSafari Integration Service + this.integrationService = TimeSafariIntegrationService.getInstance(); + await this.integrationService.initialize({ + activeDid: this.activeDid, + storageAdapter: this.getTimeSafariStorageAdapter(), + endorserApiBaseUrl: this.apiServer, + + // Use your existing request patterns + requestConfig: { + httpClient: this.axios, + baseURL: this.apiServer, + timeout: 30000, + retryAttempts: 3 + }, + + // Configure starred projects fetching + starredProjectsConfig: { + enabled: true, + starredPlanHandleIds: this.starredPlanHandleIds, + lastAckedJwtId: this.lastAckedStarredPlanChangesJwtId, + fetchInterval: '0 8 * * *', + maxResults: 50 + } + }); + + // Step 3: 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' + }); + + console.log('DailyNotification setup completed successfully!'); + + } catch (error) { + console.error('Failed to setup DailyNotification:', error); + throw error; + } + } + + /** + * Enhanced version of your existing loadNewStarredProjectChanges method + * + * This replaces your existing method with plugin-enhanced functionality + * while maintaining the same interface and behavior. + */ + async loadNewStarredProjectChanges(): Promise { + if (this.activeDid && this.starredPlanHandleIds.length > 0) { + try { + // Use plugin's enhanced fetching with same interface as your existing code + const starredProjectChanges = await this.integrationService!.getStarredProjectsWithChanges( + this.activeDid, + this.starredPlanHandleIds, + this.lastAckedStarredPlanChangesJwtId + ); + + // Same handling as your existing code + this.numNewStarredProjectChanges = starredProjectChanges.data.length; + this.newStarredProjectChangesHitLimit = starredProjectChanges.hitLimit; + + // Enhanced logging (optional) + console.log('Starred projects loaded successfully:', { + count: this.numNewStarredProjectChanges, + hitLimit: this.newStarredProjectChangesHitLimit, + planIds: this.starredPlanHandleIds.length + }); + + } catch (error) { + // Same error handling as your existing code + console.warn('[HomeView] Failed to load starred project changes:', error); + this.numNewStarredProjectChanges = 0; + this.newStarredProjectChangesHitLimit = false; + } + } else { + this.numNewStarredProjectChanges = 0; + this.newStarredProjectChangesHitLimit = false; + } + } + + /** + * Callback handlers that match your existing error handling patterns + */ + async handleStarredProjectsSuccess(data: StarredProjectsResponse): Promise { + // Same handling as your existing code + this.numNewStarredProjectChanges = data.data.length; + this.newStarredProjectChangesHitLimit = data.hitLimit; + + // Update UI (your existing method) + this.updateStarredProjectsUI(data); + + // Enhanced logging (optional) + console.log('Starred projects success callback:', { + count: data.data.length, + hitLimit: data.hitLimit + }); + } + + async handleStarredProjectsError(error: Error): Promise { + // Same error handling as your existing code + console.warn('[HomeView] Failed to load starred project changes:', error); + this.numNewStarredProjectChanges = 0; + this.newStarredProjectChangesHitLimit = false; + + // Enhanced error handling (optional) + console.error('Starred projects error callback:', { + error: error.message, + activeDid: this.activeDid, + planCount: this.starredPlanHandleIds.length + }); + } + + async handleStarredProjectsComplete(result: unknown): Promise { + // Handle completion + console.log('Starred projects fetch completed:', result); + } + + /** + * Your existing methods (unchanged) + */ + private updateStarredProjectsUI(data: StarredProjectsResponse): void { + // Your existing UI update logic + console.log('Updating UI with starred projects data:', data); + } + + private getTimeSafariStorageAdapter(): unknown { + // Return your existing TimeSafari storage adapter + return { + // Your existing storage adapter implementation + }; + } +} + +// Usage example - This is what you add to your TimeSafari PWA +export async function setupDailyNotificationForTimeSafari( + axiosInstance: AxiosInstance, + activeDid: string, + starredPlanHandleIds: string[], + lastAckedJwtId: string, + apiServer: string = 'https://endorser.ch' +): Promise { + + console.log('Setting up DailyNotification for TimeSafari PWA...'); + + // Create your existing HomeView instance + const homeView = new TimeSafariHomeView(axiosInstance); + + // Set up your existing TimeSafari data + homeView.activeDid = activeDid; + homeView.starredPlanHandleIds = starredPlanHandleIds; + homeView.lastAckedStarredPlanChangesJwtId = lastAckedJwtId; + homeView.apiServer = apiServer; + + // Setup DailyNotification plugin + await homeView.setupDailyNotification(); + + // Test the enhanced method + await homeView.loadNewStarredProjectChanges(); + + console.log('DailyNotification setup completed successfully!'); + + return homeView; +} + +// Vue.js component integration example +export const TimeSafariDailyNotificationMixin = { + data() { + return { + // Your existing data + activeDid: '', + starredPlanHandleIds: [] as string[], + lastAckedStarredPlanChangesJwtId: '', + numNewStarredProjectChanges: 0, + newStarredProjectChangesHitLimit: false, + + // Plugin integration + dailyNotificationService: null as DailyNotification | null, + integrationService: null as TimeSafariIntegrationService | null + }; + }, + + async mounted() { + // Setup DailyNotification when component mounts + await this.setupDailyNotification(); + }, + + methods: { + async setupDailyNotification() { + try { + // Configure DailyNotification plugin + await DailyNotification.configure({ + timesafariConfig: { + activeDid: this.activeDid, + endpoints: { + projectsLastUpdated: `${this.apiServer}/api/v2/report/plansLastUpdatedBetween` + }, + starredProjectsConfig: { + enabled: true, + starredPlanHandleIds: this.starredPlanHandleIds, + lastAckedJwtId: this.lastAckedStarredPlanChangesJwtId, + fetchInterval: '0 8 * * *' + } + }, + + networkConfig: { + httpClient: this.axios, + baseURL: this.apiServer, + timeout: 30000 + }, + + contentFetch: { + enabled: true, + schedule: '0 8 * * *', + callbacks: { + onSuccess: this.handleStarredProjectsSuccess, + onError: this.handleStarredProjectsError + } + } + }); + + // Initialize integration service + this.integrationService = TimeSafariIntegrationService.getInstance(); + await this.integrationService.initialize({ + activeDid: this.activeDid, + storageAdapter: this.getTimeSafariStorageAdapter(), + endorserApiBaseUrl: this.apiServer + }); + + } catch (error) { + console.error('Failed to setup DailyNotification:', error); + } + }, + + // Your existing methods (enhanced) + async loadNewStarredProjectChanges() { + if (this.activeDid && this.starredPlanHandleIds.length > 0) { + try { + const starredProjectChanges = await this.integrationService.getStarredProjectsWithChanges( + this.activeDid, + this.starredPlanHandleIds, + this.lastAckedStarredPlanChangesJwtId + ); + + this.numNewStarredProjectChanges = starredProjectChanges.data.length; + this.newStarredProjectChangesHitLimit = starredProjectChanges.hitLimit; + + } catch (error) { + console.warn('[HomeView] Failed to load starred project changes:', error); + this.numNewStarredProjectChanges = 0; + this.newStarredProjectChangesHitLimit = false; + } + } else { + this.numNewStarredProjectChanges = 0; + this.newStarredProjectChangesHitLimit = false; + } + }, + + handleStarredProjectsSuccess(data: StarredProjectsResponse) { + this.numNewStarredProjectChanges = data.data.length; + this.newStarredProjectChangesHitLimit = data.hitLimit; + this.updateStarredProjectsUI(data); + }, + + handleStarredProjectsError(error: Error) { + console.warn('[HomeView] Failed to load starred project changes:', error); + this.numNewStarredProjectChanges = 0; + this.newStarredProjectChangesHitLimit = false; + }, + + // Your existing methods + updateStarredProjectsUI(data: StarredProjectsResponse) { + // Your existing UI update logic + }, + + getTimeSafariStorageAdapter() { + // Your existing storage adapter + return {}; + } + } +}; + +// Export for use in your TimeSafari PWA +export { TimeSafariHomeView };