You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

18 KiB

DailyNotification Setup Explanation - Detailed Breakdown

Author: Matthew Raymer
Version: 1.0.0
Created: 2025-10-08 06:24:57 UTC

Overview

This document provides a detailed explanation of the DailyNotification setup example, breaking down each component and explaining how it integrates with your existing TimeSafari PWA code.

File Structure Breakdown

1. Interface Definitions (Lines 16-28)

// Your existing TimeSafari PWA interfaces
interface PlanSummaryAndPreviousClaim {
  id: string;
  title: string;
  description: string;
  lastUpdated: string;
  previousClaim?: unknown;
}

interface StarredProjectsResponse {
  data: Array<PlanSummaryAndPreviousClaim>;
  hitLimit: boolean;
}

What this means:

  • These are the exact same interfaces you already use in your TimeSafari PWA
  • PlanSummaryAndPreviousClaim represents a single project with its changes
  • StarredProjectsResponse is the response format from your getStarredProjectsWithChanges() function
  • The plugin uses these same interfaces to maintain compatibility with your existing code

2. TimeSafariHomeView Class (Lines 31-47)

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;
}

What this means:

  • This represents your existing TimeSafari PWA HomeView component
  • All the properties (activeDid, starredPlanHandleIds, etc.) are your existing data
  • The plugin integration properties are new additions that work alongside your existing code
  • The plugin doesn't replace your existing properties - it enhances them

Core Setup Method Breakdown

3. setupDailyNotification() Method (Lines 52-223)

This is the main configuration method that sets up the plugin to work with your existing TimeSafari code.

Step 1: Basic Plugin Configuration (Lines 58-63)

await DailyNotification.configure({
  // Basic plugin configuration
  storage: 'tiered',
  ttlSeconds: 1800,
  enableETagSupport: true,
  enableErrorHandling: true,
  enablePerformanceOptimization: true,

What this means:

  • storage: 'tiered' - Uses a multi-level storage system (memory + disk + network)
  • ttlSeconds: 1800 - Data expires after 30 minutes (1800 seconds)
  • enableETagSupport: true - Uses HTTP ETags for efficient caching (only fetch if data changed)
  • enableErrorHandling: true - Built-in retry logic and error recovery
  • enablePerformanceOptimization: true - Automatic performance optimizations

Step 2: TimeSafari-Specific Configuration (Lines 66-103)

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
    maxResults: 50,
    hitLimitHandling: 'warn'
  }
}

What this means:

  • activeDid: this.activeDid - Uses your existing user DID for authentication
  • endpoints - Points to your existing TimeSafari API endpoints
  • starredProjectsConfig - Configures the plugin to fetch starred projects exactly like your existing code
    • starredPlanHandleIds: this.starredPlanHandleIds - Uses your existing array of starred project IDs
    • lastAckedJwtId: this.lastAckedStarredPlanChangesJwtId - Uses your existing JWT ID for pagination
    • fetchInterval: '0 8 * * *' - Fetches daily at 8 AM (cron expression)
    • hitLimitHandling: 'warn' - Same error handling as your existing code

Step 3: Network Configuration (Lines 106-121)

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'
  }
}

What this means:

  • httpClient: this.axios - Uses your existing axios instance (no new HTTP client needed)
  • baseURL: this.apiServer - Uses your existing API server URL
  • timeout: 30000 - 30-second timeout for requests
  • retryAttempts: 3 - Retry failed requests up to 3 times
  • retryDelay: 1000 - Wait 1 second between retries
  • maxConcurrent: 5 - Maximum 5 concurrent requests
  • defaultHeaders - Headers that match your existing request pattern

Step 4: Content Fetch Configuration (Lines 124-149)

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)
  }
}

What this means:

  • contentFetch - Configures the plugin to fetch content exactly like your existing getStarredProjectsWithChanges() function
  • requestConfig - Defines the HTTP request that matches your existing API call:
    • method: 'POST' - Same as your existing function
    • url - Same endpoint as your existing function
    • headers - Same headers as your existing function (with template variables)
    • body - Same request body as your existing function (with template variables)
  • callbacks - Functions that handle the response exactly like your existing error handling

Step 5: Authentication Configuration (Lines 152-159)

authentication: {
  jwt: {
    secret: process.env.JWT_SECRET || 'your-jwt-secret',
    algorithm: 'HS256',
    expirationMinutes: 60,
    refreshThresholdMinutes: 10
  }
}

What this means:

  • Configures JWT authentication for API requests
  • Uses the same JWT secret as your existing TimeSafari PWA
  • Tokens expire after 60 minutes and refresh when 10 minutes remain
  • This ensures the plugin uses the same authentication as your existing code

Step 6: Observability Configuration (Lines 162-168)

logging: {
  level: 'INFO',
  enableRequestLogging: true,
  enableResponseLogging: true,
  enableErrorLogging: true,
  redactSensitiveData: true
}

What this means:

  • level: 'INFO' - Logs informational messages and above
  • enableRequestLogging: true - Logs all HTTP requests (useful for debugging)
  • enableResponseLogging: true - Logs all HTTP responses
  • enableErrorLogging: true - Logs all errors with detailed information
  • redactSensitiveData: true - Automatically removes sensitive data from logs

Step 7: Security Configuration (Lines 171-181)

security: {
  certificatePinning: {
    enabled: true,
    pins: [
      {
        hostname: 'endorser.ch',
        pins: ['sha256/YOUR_PIN_HERE']
      }
    ]
  }
}

What this means:

  • certificatePinning - Prevents man-in-the-middle attacks by pinning SSL certificates
  • Only allows connections to endorser.ch with the specified certificate
  • This adds an extra layer of security to your API requests

Integration Service Setup (Lines 185-207)

// 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
  }
});

What this means:

  • TimeSafariIntegrationService - A service that provides enhanced versions of your existing TimeSafari functions
  • getInstance() - Uses the singleton pattern (same instance across your app)
  • initialize() - Sets up the service with your existing TimeSafari configuration
  • storageAdapter: this.getTimeSafariStorageAdapter() - Uses your existing storage system
  • The service provides methods that work exactly like your existing functions but with enhanced features

Enhanced Method Breakdown

4. loadNewStarredProjectChanges() Method (Lines 231-262)

async loadNewStarredProjectChanges(): Promise<void> {
  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;
  }
}

What this means:

  • This is an enhanced version of your existing loadNewStarredProjectChanges() method
  • The interface is identical - same parameters, same return values, same error handling
  • The behavior is identical - same logic, same data handling, same UI updates
  • The only difference is that it uses the plugin's enhanced getStarredProjectsWithChanges() method
  • Your existing code that calls this method doesn't need to change at all

Callback Handlers Breakdown

5. Success Handler (Lines 267-280)

async handleStarredProjectsSuccess(data: StarredProjectsResponse): Promise<void> {
  // 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
  });
}

What this means:

  • This handler is called when the starred projects fetch succeeds
  • It does exactly the same thing as your existing success handling
  • It updates your existing properties (numNewStarredProjectChanges, newStarredProjectChangesHitLimit)
  • It calls your existing UI update method (updateStarredProjectsUI)
  • It adds optional enhanced logging for debugging

6. Error Handler (Lines 282-294)

async handleStarredProjectsError(error: Error): Promise<void> {
  // 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
  });
}

What this means:

  • This handler is called when the starred projects fetch fails
  • It does exactly the same thing as your existing error handling
  • It logs the same warning message as your existing code
  • It resets your existing properties to the same default values
  • It adds optional enhanced error logging with more context

Vue.js Integration Breakdown

7. Vue.js Mixin (Lines 349-463)

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: {
    // ... methods
  }
};

What this means:

  • This is a Vue.js mixin that you can add to your existing TimeSafari PWA components
  • It adds the plugin functionality to your existing Vue components
  • It preserves all your existing data properties
  • It adds the plugin setup to the mounted() lifecycle hook
  • It provides all the enhanced methods as component methods

Usage Example Breakdown

8. Usage Function (Lines 318-346)

export async function setupDailyNotificationForTimeSafari(
  axiosInstance: AxiosInstance,
  activeDid: string,
  starredPlanHandleIds: string[],
  lastAckedJwtId: string,
  apiServer: string = 'https://endorser.ch'
): Promise<TimeSafariHomeView> {
  
  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;
}

What this means:

  • This is a helper function that sets up the plugin for your TimeSafari PWA
  • It takes your existing TimeSafari data as parameters
  • It creates your existing HomeView instance
  • It sets up your existing data properties
  • It configures the plugin with your existing configuration
  • It tests the enhanced method to ensure everything works
  • It returns your enhanced HomeView instance

Key Benefits Explained

1. Same Interface, Enhanced Functionality

  • Your existing loadNewStarredProjectChanges() method works exactly the same
  • Your existing getStarredProjectsWithChanges() function is enhanced with better error handling
  • Your existing UI code doesn't need to change at all
  • Your existing data properties are preserved and enhanced

2. Enhanced Error Handling

  • Built-in retry logic for failed requests
  • Exponential backoff for rate limiting
  • Circuit breaker pattern for service failures
  • Structured logging with event IDs for debugging

3. Performance Improvements

  • HTTP ETag support for efficient caching
  • Request batching for multiple API calls
  • Connection pooling for HTTP requests
  • Automatic performance optimizations

4. Background Fetching

  • Automatically fetches starred projects in the background
  • Schedules daily notifications with your data
  • Works even when the app is in the background
  • Provides fallback content when network is unavailable

5. 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 your existing code unchanged
  2. Add the plugin configuration alongside your 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 the plugin's enhanced error handling
  3. Maintain your existing UI and user experience
  4. Add plugin-specific features gradually

Phase 3: Full Integration

  1. Replace all TimeSafari request patterns with the plugin
  2. Remove duplicate code
  3. Leverage the plugin's advanced features
  4. Optimize performance with the plugin's caching and batching

Common Questions

Q: Do I need to change my existing code?

A: No, your existing code doesn't need to change. The plugin enhances your existing methods while maintaining the same interface.

Q: Will this break my existing functionality?

A: No, the plugin is designed to work alongside your existing code. You can test both implementations in parallel.

Q: How do I know if the plugin is working?

A: The plugin provides enhanced logging and metrics. You can compare the results between your existing code and the plugin-enhanced version.

Q: Can I roll back if something goes wrong?

A: Yes, you can easily roll back by using your existing methods instead of the plugin-enhanced versions.

Q: What are the performance benefits?

A: The plugin provides HTTP ETag support, request batching, connection pooling, and automatic performance optimizations.

Conclusion

The DailyNotification setup example shows how to enhance your existing TimeSafari PWA code with advanced features while maintaining the same interface and behavior. The plugin works alongside your existing code, providing enhanced error handling, performance improvements, background fetching, and observability without requiring changes to your existing methods or UI.


Next Steps:

  1. Review the setup example and understand each component
  2. Test the plugin configuration with your existing TimeSafari PWA code
  3. Compare the results between your existing methods and the plugin-enhanced versions
  4. Gradually migrate to the plugin-enhanced methods as you gain confidence