Browse Source
- 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.research/notification-plugin-enhancement
1 changed files with 343 additions and 0 deletions
@ -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<void> { |
|||
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<void> { |
|||
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<void> { |
|||
// 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<void>; |
|||
|
|||
// Plugin-initiated activeDid lookup for background |
|||
async refreshActiveDidFromDatabase(): Promise<string>; |
|||
|
|||
// Automatic mode that uses both approaches |
|||
async enableAutoActiveDidMode(): Promise<void>; |
|||
} |
|||
|
|||
// 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<void> { |
|||
// 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<void> { |
|||
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<void> { |
|||
// 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<void> { |
|||
// ... 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 |
Loading…
Reference in new issue