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 changesStarredProjectsResponse
is the response format from yourgetStarredProjectsWithChanges()
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 recoveryenablePerformanceOptimization: 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 authenticationendpoints
- Points to your existing TimeSafari API endpointsstarredProjectsConfig
- Configures the plugin to fetch starred projects exactly like your existing codestarredPlanHandleIds: this.starredPlanHandleIds
- Uses your existing array of starred project IDslastAckedJwtId: this.lastAckedStarredPlanChangesJwtId
- Uses your existing JWT ID for paginationfetchInterval: '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 URLtimeout: 30000
- 30-second timeout for requestsretryAttempts: 3
- Retry failed requests up to 3 timesretryDelay: 1000
- Wait 1 second between retriesmaxConcurrent: 5
- Maximum 5 concurrent requestsdefaultHeaders
- 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 existinggetStarredProjectsWithChanges()
functionrequestConfig
- Defines the HTTP request that matches your existing API call:method: 'POST'
- Same as your existing functionurl
- Same endpoint as your existing functionheaders
- 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 aboveenableRequestLogging: true
- Logs all HTTP requests (useful for debugging)enableResponseLogging: true
- Logs all HTTP responsesenableErrorLogging: true
- Logs all errors with detailed informationredactSensitiveData: 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 functionsgetInstance()
- Uses the singleton pattern (same instance across your app)initialize()
- Sets up the service with your existing TimeSafari configurationstorageAdapter: 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
- Keep your existing code unchanged
- Add the plugin configuration alongside your existing code
- Test both implementations in parallel
- Compare results to ensure compatibility
Phase 2: Gradual Migration
- Replace individual methods one by one
- Use the plugin's enhanced error handling
- Maintain your existing UI and user experience
- Add plugin-specific features gradually
Phase 3: Full Integration
- Replace all TimeSafari request patterns with the plugin
- Remove duplicate code
- Leverage the plugin's advanced features
- 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:
- Review the setup example and understand each component
- Test the plugin configuration with your existing TimeSafari PWA code
- Compare the results between your existing methods and the plugin-enhanced versions
- Gradually migrate to the plugin-enhanced methods as you gain confidence