From 1fabcf1fe9f307be9c74ea3172abb23ed4b5cb4d Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Thu, 2 Oct 2025 08:53:48 +0000 Subject: [PATCH] analysis: TimeSafari host application integration patterns - Documented active_identity table structure and access methods - Analyzed TimeSafari's PlatformServiceMixin patterns - Created three integration options: 1. Host-managed activeDid (plugin receives from host) 2. Plugin-lookup activeDid (plugin queries active_identity table) 3. Hybrid approach (recommended combination) - Detailed service layer integration points - Cross-platform considerations for Android/Capacitor/Web/iOS/Electron - Security isolation recommendations for plugin/host database access This analysis provides clear guidance for hosting the plugin within TimeSafari applications. --- doc/TIMESAFARI_INTEGRATION_ANALYSIS.md | 343 +++++++++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 doc/TIMESAFARI_INTEGRATION_ANALYSIS.md diff --git a/doc/TIMESAFARI_INTEGRATION_ANALYSIS.md b/doc/TIMESAFARI_INTEGRATION_ANALYSIS.md new file mode 100644 index 0000000..14bcac4 --- /dev/null +++ b/doc/TIMESAFARI_INTEGRATION_ANALYSIS.md @@ -0,0 +1,343 @@ +# TimeSafari Daily Notification Plugin Integration Analysis + +**Author**: Matthew Raymer +**Version**: 1.0.0 +**Created**: 2025-10-02 11:00:00 UTC +**Last Updated**: 2025-10-02 11:00:00 UTC + +## Overview + +This document analyzes how the Daily Notification Plugin would integrate with TimeSafari, focusing on `activeDid` access and plugin hosting architecture. + +## ActiveDid Management in TimeSafari + +### Database Schema: `active_identity` Table + +**Table Structure:** +```sql +CREATE TABLE active_identity ( + id INTEGER PRIMARY KEY DEFAULT 1, + activeDid TEXT NOT NULL, + lastUpdated DATETIME DEFAULT (datetime('now')) +); +``` + +**Key Characteristics:** +- Single row table with `id = 1` always +- `activeDid` contains the currently active user's DID +- `lastUpdated` tracks when the active identity was switched +- Single source of truth for current user identity + +### Access Methods + +**1. Platform Service Level:** +```typescript +// In CapacitorPlatformService, WebPlatformService, ElectronPlatformService +async updateActiveDid(did: string): Promise { + await this.dbExec( + "INSERT OR REPLACE INTO active_identity (id, activeDid, lastUpdated) VALUES (1, ?, ?)", + [did, new Date().toISOString()] + ); +} + +async getActiveIdentity(): Promise<{ activeDid: string }> { + const result = await this.dbQuery( + "SELECT activeDid FROM active_identity WHERE id = 1" + ); + return { + activeDid: (result?.values?.[0]?.[0] as string) || "" + }; +} +``` + +**2. Vue Mixin Level:** +```typescript +// In PlatformServiceMixin.ts +async $updateActiveDid(newDid: string | null): Promise { + await this.$dbExec( + "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1", + [newDid || ""] + ); +} + +async $getActiveIdentity(): Promise<{ activeDid: string }> { + const result = await this.$dbExec( + "SELECT activeDid FROM active_identity WHERE id = 1" + ); + return { activeDid: result.activeDid || "" }; +} +``` + +**3. Component Level Usage:** +```typescript +// In Vue components +const activeIdentity = await this.$getActiveIdentity(); +const activeDid = activeIdentity.activeDid || ""; +``` + +## Plugin Integration Options + +### Option 1: Host Application Manages activeDid + +**Pros:** +- Simple integration +- Plugin doesn't need database access +- Clear separation of concerns + +**Implementation:** +```typescript +// In TimeSafari application +import { DailyNotification } from '@timesafari/daily-notification-plugin'; + +// On app initialization or user switch +async function initializeNotifications() { + const activeIdentity = await this.$getActiveIdentity(); + const activeDid = activeIdentity.activeDid; + + if (activeDid) { + await DailyNotification.setActiveDid(activeDid); + await DailyNotification.configure({ + apiServer: 'https://endorser.ch', + activeDid: activeDid, + // ... other config + }); + } +} +``` + +**When to Use:** +- Host application already has database access +- Plugin should be stateless regarding identity +- Simple notification scheduling only + +### Option 2: Plugin Looks Up activeDid + +**Pros:** +- Plugin manages its own identity context +- Automatic activeDid synchronization +- Background tasks have direct access + +**Implementation:** +```typescript +// Plugin has database access via @capacitor-community/sqlite +interface EnhancedDailyNotificationPlugin { + async initializeFromTimeSafari(): Promise { + // Plugin accesses TimeSafari's active_identity table + const result = await this.dbQuery( + "SELECT activeDid FROM active_identity WHERE id = 1" + ); + const activeDid = result?.values?.[0]?.[0] as string; + + await this.setActiveDid(activeDid); + await this.scheduleBackgroundTasks(); + } +} +``` + +**When to Use:** +- Plugin needs autonomous background operation +- Host application integration should be minimal +- Plugin operates independently of app lifecycle + +### Option 3: Hybrid Approach (Recommended) + +**Implementation:** +```typescript +// Host provides activeDid when app is active +// Plugin looks up activeDid for background operations + +interface HybridDailyNotificationPlugin { + // Host-initiated activeDid setting + async setActiveDidFromHost(activeDid: string): Promise; + + // Plugin-initiated activeDid lookup for background + async refreshActiveDidFromDatabase(): Promise; + + // Automatic mode that uses both approaches + async enableAutoActiveDidMode(): Promise; +} + +// In TimeSafari app +async function initializeNotifications() { + const activeIdentity = await this.$getActiveIdentity(); + const activeDid = activeIdentity.activeDid; + + if (activeDid) { + await DailyNotification.setActiveDidFromHost(activeDid); + await DailyNotification.enableAutoActiveDidMode(); + } +} +``` + +## Recommended Plugin Hosting Architecture + +### Service Layer Integration + +**Location**: `src/services/DailyNotificationService.ts` + +```typescript +// src/services/DailyNotificationService.ts +import { DailyNotification } from '@timesafari/daily-notification-plugin'; +import { PlatformServiceFactory } from './PlatformServiceFactory'; + +export class DailyNotificationService { + private plugin: DailyNotification; + private platform = PlatformServiceFactory.getInstance(); + private currentActiveDid: string | null = null; + + async initialize(): Promise { + // Initialize plugin with current active identity + const activeIdentity = await this.platform.getActiveIdentity(); + this.currentActiveDid = activeIdentity.activeDid; + + if (this.currentActiveDid) { + await this.plugin.setActiveDid(this.currentActiveDid); + await this.plugin.configure({ + apiServer: import.meta.env.VITE_ENDORSER_API_SERVER, + storageType: 'plugin-managed', + notifications: { + offers: true, + projects: true, + people: true, + items: true + } + }); + + // Listen for identity changes + this.setupIdentityChangeListener(); + } + } + + private setupIdentityChangeListener(): void { + // Subscribe to identity changes in TimeSafari + document.addEventListener('activeDidChanged', (event: CustomEvent) => { + this.handleActiveDidChange(event.detail.activeDid); + }); + } + + private async handleActiveDidChange(newActiveDid: string): Promise { + if (newActiveDid !== this.currentActiveDid) { + this.currentActiveDid = newActiveDid; + await this.plugin.setActiveDid(newActiveDid); + logger.info(`[DailyNotificationService] ActiveDid updated to: ${newActiveDid}`); + +``` + +### PlatformServiceMixin Integration + +**Location**: `src/utils/PlatformServiceMixin.ts` + +```typescript +// Add to PlatformServiceMixin.ts +async $notifyDailyNotificationService(): Promise { + // Trigger notification service update when activeDid changes + const event = new CustomEvent('activeDidChanged', { + detail: { activeDid: this.currentActiveDid } + }); + document.dispatchEvent(event); +} + +// Modify existing $updateActiveDid method +async $updateActiveDid(newDid: string | null): Promise { + // ... existing logic ... + + // Notify DailyNotification service + await this.$notifyDailyNotificationService(); +} +``` + +### App Initialization Integration + +**Location**: `src/main.ts` or App component + +```typescript +// src/main.ts +import { DailyNotificationService } from '@/services/DailyNotificationService'; + +async function initializeNotifications() { + try { + const notificationService = new DailyNotificationService(); + await notificationService.initialize(); + + logger.info('[App] Daily notification service initialized successfully'); + } catch (error) { + logger.error('[App] Failed to initialize notification service:', error); + } +} + +// Call after platform service initialization +initializePlatform().then(async () => { + await initializeNotifications(); + // ... rest of app initialization +}); +``` + +## Cross-Platform Considerations + +### Android (Capacitor) +- **Database Access**: Plugin uses `@capacitor-community/sqlite` +- **identity Access**: Can read TimeSafari's active_identity table directly +- **Background Tasks**: WorkManager can query active_identity independently + +### Web (absurd-sql) +- **Database Access**: Plugin delegates to host application +- **identity Access**: Host provides activeDid, plugin doesn't access database +- **Background Tasks**: Service Worker coordination with host database + +### iOS (Capacitor) +- **Database Access**: Plugin uses CapacitorSQLite +- **identity Access**: Can read TimeSafari's active_identity table +- **Background Tasks**: BGTaskScheduler coordination + +### Electron +- **Database Access**: Plugin uses `@capacitor-community/sqlite` + filesystem +- **identity Access**: Direct file-based database access +- **Background Tasks**: Electron main process coordination + +## Security Considerations + +### activeDid Isolation +- Plugin should not modify active_identity table +- Plugin should only read current activeDid +- Identity changes should be initiated by TimeSafari host application + +### Token Management +- JWT tokens should use activeDid for both `iss` and `sub` +- Token generation should happen in plugin context +- Host application should not handle authentication tokens + +### Background Execution +- Background tasks should validate activeDid hasn't changed +- Implement cache invalidation when activeDid changes +- Handle edge cases where activeDid becomes empty/invalid + +## Implementation Priority + +### Phase 1: Basic Integration +1. Host-managed activeDid (Option 1) +2. Service layer integration +3. Basic notification scheduling + +### Phase 2: Enhanced Integration +1. Hybrid activeDid management (Option 3) +2. Background database access +3. Automatic identity synchronization + +### Phase 3: Advanced Features +1. Cross-platform optimization +2. Advanced background task coordination +3. Performance monitoring and analytics + +## Success Criteria + +- [ ] Plugin correctly receives activeDid from TimeSafari host +- [ ] Background notifications work across identity changes +- [] Cross-platform consistency maintained +- [ ] Minimal impact on TimeSafari performance +- [ ] Secure isolation between plugin and host database operations + +--- + +**Status**: Architectural analysis complete - Ready for implementation planning +**Next Steps**: Choose integration approach and begin Phase 1 implementation +**Dependencies**: TimeSafari active_identity table access, PlatformServiceMixin integration