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.
12 KiB
12 KiB
TimeSafari PWA - CapacitorPlatformService Changes Summary
Author: Matthew Raymer
Version: 1.0.0
Created: 2025-10-08 06:24:57 UTC
Overview
This document summarizes the exact changes needed to the existing TimeSafari PWA CapacitorPlatformService
to add DailyNotification plugin functionality. These are the actual modifications required to the existing codebase.
Required Changes to Existing TimeSafari PWA Code
File: src/services/platforms/CapacitorPlatformService.ts
1. Add New Imports (at the top of the file)
// ADD THESE IMPORTS
import { DailyNotification } from '@timesafari/daily-notification-plugin';
import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin';
2. Add New Interfaces (after existing interfaces)
// ADD THESE INTERFACES
interface PlanSummaryAndPreviousClaim {
id: string;
title: string;
description: string;
lastUpdated: string;
previousClaim?: unknown;
}
interface StarredProjectsResponse {
data: Array<PlanSummaryAndPreviousClaim>;
hitLimit: boolean;
}
interface TimeSafariSettings {
accountDid?: string;
activeDid?: string;
apiServer?: string;
starredPlanHandleIds?: string[];
lastAckedStarredPlanChangesJwtId?: string;
[key: string]: unknown;
}
3. Add New Properties (in the class, after existing properties)
export class CapacitorPlatformService implements PlatformService {
// ... existing properties ...
// ADD THESE NEW PROPERTIES
private dailyNotificationService: DailyNotification | null = null;
private integrationService: TimeSafariIntegrationService | null = null;
private dailyNotificationInitialized = false;
}
4. Add New Methods (in the class, after existing methods)
/**
* Initialize DailyNotification plugin with TimeSafari configuration
*/
async initializeDailyNotification(): Promise<void> {
if (this.dailyNotificationInitialized) {
return;
}
try {
logger.log("[CapacitorPlatformService] Initializing DailyNotification plugin...");
// Get current TimeSafari settings
const settings = await this.getTimeSafariSettings();
// Configure DailyNotification 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 || 'https://endorser.ch',
timeout: 30000
}
});
// Initialize TimeSafari Integration Service
this.integrationService = TimeSafariIntegrationService.getInstance();
await this.integrationService.initialize({
activeDid: settings.activeDid || '',
storageAdapter: this.getTimeSafariStorageAdapter(),
endorserApiBaseUrl: settings.apiServer || 'https://endorser.ch'
});
this.dailyNotificationInitialized = true;
logger.log("[CapacitorPlatformService] DailyNotification plugin initialized successfully");
} catch (error) {
logger.error("[CapacitorPlatformService] Failed to initialize DailyNotification plugin:", error);
throw error;
}
}
/**
* Enhanced version of existing TimeSafari loadNewStarredProjectChanges method
*/
async loadNewStarredProjectChanges(): Promise<StarredProjectsResponse> {
// Ensure DailyNotification is initialized
if (!this.dailyNotificationInitialized) {
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
);
return starredProjectChanges;
} catch (error) {
// Same error handling as existing TimeSafari code
logger.warn("[CapacitorPlatformService] Failed to load starred project changes:", error);
return { data: [], hitLimit: false };
}
}
/**
* Get TimeSafari settings using existing database patterns
*/
private async getTimeSafariSettings(): Promise<TimeSafariSettings> {
try {
const result = await this.dbQuery("SELECT * FROM settings WHERE id = 1");
if (!result?.values?.length) {
return {};
}
const settings: TimeSafariSettings = {};
result.columns.forEach((column, index) => {
if (column !== 'id') {
settings[column] = result.values[0][index];
}
});
// Handle JSON field parsing
if (settings.starredPlanHandleIds && typeof settings.starredPlanHandleIds === 'string') {
try {
settings.starredPlanHandleIds = JSON.parse(settings.starredPlanHandleIds);
} catch {
settings.starredPlanHandleIds = [];
}
}
return settings;
} catch (error) {
logger.error("[CapacitorPlatformService] Error getting TimeSafari settings:", error);
return {};
}
}
/**
* Get TimeSafari storage adapter using existing patterns
*/
private getTimeSafariStorageAdapter(): unknown {
return {
store: async (key: string, value: unknown) => {
await this.dbExec(
"INSERT OR REPLACE INTO temp (id, data) VALUES (?, ?)",
[key, JSON.stringify(value)]
);
},
retrieve: async (key: string) => {
const result = await this.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 for DailyNotification plugin
*/
private async handleStarredProjectsSuccess(data: StarredProjectsResponse): Promise<void> {
logger.log("[CapacitorPlatformService] Starred projects success callback:", {
count: data.data.length,
hitLimit: data.hitLimit
});
// Store results in TimeSafari temp table for UI access
await this.dbExec(
"INSERT OR REPLACE INTO temp (id, data) VALUES (?, ?)",
['starred_projects_latest', JSON.stringify(data)]
);
}
private async handleStarredProjectsError(error: Error): Promise<void> {
logger.warn("[CapacitorPlatformService] Failed to load starred project changes:", error);
// Store error in TimeSafari temp table for UI access
await this.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<void> {
logger.log("[CapacitorPlatformService] Starred projects fetch completed:", result);
}
/**
* Get DailyNotification plugin status for debugging
*/
async getDailyNotificationStatus(): Promise<{
initialized: boolean;
platform: string;
capabilities: PlatformCapabilities;
}> {
return {
initialized: this.dailyNotificationInitialized,
platform: Capacitor.getPlatform(),
capabilities: this.getCapabilities()
};
}
5. Modify Existing Method (update the initializeDatabase
method)
private async initializeDatabase(): Promise<void> {
// If already initialized, return immediately
if (this.initialized) {
return;
}
// If initialization is in progress, wait for it
if (this.initializationPromise) {
return this.initializationPromise;
}
try {
// Start initialization
this.initializationPromise = this._initialize();
await this.initializationPromise;
// ADD THIS LINE: Initialize DailyNotification after database is ready
await this.initializeDailyNotification();
} catch (error) {
logger.error(
"[CapacitorPlatformService] Initialize database method failed:",
error,
);
this.initializationPromise = null; // Reset on failure
throw error;
}
}
Required Changes to PlatformServiceMixin
File: src/utils/PlatformServiceMixin.ts
1. Add New Methods (in the methods section)
methods: {
// ... existing methods ...
/**
* Initialize DailyNotification plugin (Capacitor only)
*/
async $initializeDailyNotification(): Promise<void> {
if (this.isCapacitor) {
await this.platformService.initializeDailyNotification();
}
},
/**
* Enhanced loadNewStarredProjectChanges method
*/
async $loadNewStarredProjectChanges(): Promise<StarredProjectsResponse> {
if (this.isCapacitor) {
return await this.platformService.loadNewStarredProjectChanges();
} else {
// Fall back to existing web method
return await this.$loadNewStarredProjectChangesWeb();
}
},
/**
* Get DailyNotification status for debugging
*/
async $getDailyNotificationStatus(): Promise<unknown> {
if (this.isCapacitor) {
return await this.platformService.getDailyNotificationStatus();
}
return { initialized: false, platform: 'web' };
}
}
Required Changes to Vue Components
File: src/views/HomeView.vue
(or similar)
1. Add DailyNotification Initialization (in mounted lifecycle)
export default defineComponent({
name: 'HomeView',
mixins: [PlatformServiceMixin],
async mounted() {
// ADD THIS: Initialize DailyNotification (only on Capacitor)
if (this.isCapacitor) {
await this.$initializeDailyNotification();
}
// ... existing mounted code ...
},
methods: {
// ... existing methods ...
/**
* Enhanced loadNewStarredProjectChanges method
*/
async loadNewStarredProjectChanges(): Promise<void> {
if (this.isCapacitor) {
// Use plugin-enhanced method on Capacitor
const result = await this.$loadNewStarredProjectChanges();
this.numNewStarredProjectChanges = result.data.length;
this.newStarredProjectChangesHitLimit = result.hitLimit;
} else {
// Use existing web method in browser
await this.loadNewStarredProjectChangesWeb();
}
}
}
});
Package.json Changes
Add DailyNotification Plugin Dependency
{
"dependencies": {
"@timesafari/daily-notification-plugin": "ssh://git@173.199.124.46:222/trent_larson/daily-notification-plugin.git"
}
}
Summary of Changes
Files Modified:
-
src/services/platforms/CapacitorPlatformService.ts
- Add imports for DailyNotification plugin
- Add new interfaces for plugin integration
- Add new properties for plugin state
- Add new methods for plugin functionality
- Modify existing
initializeDatabase
method
-
src/utils/PlatformServiceMixin.ts
- Add new methods for plugin integration
- Add platform detection for plugin usage
-
Vue Components (e.g.,
src/views/HomeView.vue
)- Add plugin initialization in mounted lifecycle
- Add enhanced methods for plugin functionality
-
package.json
- Add DailyNotification plugin dependency
Key Benefits:
- Same Interface: Existing methods work exactly the same
- Enhanced Functionality: Background fetching, structured logging, error handling
- Platform Detection: Only initializes on Capacitor platforms
- Gradual Migration: Can be adopted incrementally
- No Breaking Changes: Existing code continues to work
Migration Strategy:
- Add the changes to existing TimeSafari PWA code
- Test on Capacitor platforms (Android, iOS)
- Verify web fallback works correctly
- Gradually migrate individual methods to use plugin features
- Leverage advanced features like background fetching and observability
These are the exact changes needed to integrate the DailyNotification plugin with the existing TimeSafari PWA CapacitorPlatformService architecture.