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.
 
 
 
 
 
 

466 lines
15 KiB

/**
* 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<PlanSummaryAndPreviousClaim>;
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<void> {
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<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;
// 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<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
});
}
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
});
}
async handleStarredProjectsComplete(result: unknown): Promise<void> {
// 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<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;
}
// 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 };