docs: add activeDid change integration with DailyNotification plugin
- Add example showing how CapacitorPlatformService handles activeDid changes - Show integration with existing TimeSafari PWA activeDid change detection system - Demonstrate plugin reconfiguration when user switches identities - Include change listener system and settings synchronization - Add comprehensive guide explaining activeDid change flow - Show how to extend existing updateActiveDid method for plugin integration This ensures the DailyNotification plugin automatically adapts to activeDid changes while maintaining existing TimeSafari PWA patterns.
This commit is contained in:
422
docs/activedid-change-integration-guide.md
Normal file
422
docs/activedid-change-integration-guide.md
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
# TimeSafari PWA - ActiveDid Change Integration Guide
|
||||||
|
|
||||||
|
**Author**: Matthew Raymer
|
||||||
|
**Version**: 1.0.0
|
||||||
|
**Created**: 2025-10-08 06:24:57 UTC
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This guide explains how the DailyNotification plugin integrates with the existing TimeSafari PWA activeDid change detection system. The TimeSafari PWA has a sophisticated activeDid change management system that the plugin must integrate with.
|
||||||
|
|
||||||
|
## TimeSafari PWA ActiveDid Change System
|
||||||
|
|
||||||
|
### How ActiveDid Changes Work
|
||||||
|
|
||||||
|
#### 1. Database Storage
|
||||||
|
```sql
|
||||||
|
-- ActiveDid is stored in active_identity table (single source of truth)
|
||||||
|
UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. PlatformServiceMixin Change Detection
|
||||||
|
```typescript
|
||||||
|
// In PlatformServiceMixin.ts
|
||||||
|
watch: {
|
||||||
|
currentActiveDid: {
|
||||||
|
handler(newDid: string | null, oldDid: string | null) {
|
||||||
|
if (newDid !== oldDid) {
|
||||||
|
logger.debug(`[PlatformServiceMixin] ActiveDid changed from ${oldDid} to ${newDid}`);
|
||||||
|
// Clear caches that might be affected by the change
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Update Method
|
||||||
|
```typescript
|
||||||
|
// In PlatformServiceMixin.ts
|
||||||
|
async $updateActiveDid(newDid: string | null): Promise<void> {
|
||||||
|
const oldDid = this._currentActiveDid;
|
||||||
|
this._currentActiveDid = newDid;
|
||||||
|
|
||||||
|
if (newDid !== oldDid) {
|
||||||
|
// Write to active_identity table (single source of truth)
|
||||||
|
await this.$dbExec(
|
||||||
|
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
||||||
|
[newDid || ""]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Usage in Components
|
||||||
|
```typescript
|
||||||
|
// In IdentitySwitcherView.vue
|
||||||
|
async switchAccount(did?: string) {
|
||||||
|
// Update the active DID in the active_identity table
|
||||||
|
await this.$updateActiveDid(did);
|
||||||
|
|
||||||
|
// Check if we need to load user-specific settings for the new DID
|
||||||
|
if (did) {
|
||||||
|
const newSettings = await this.$accountSettings(did);
|
||||||
|
// Update UI with new settings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## DailyNotification Plugin Integration
|
||||||
|
|
||||||
|
### How the Plugin Handles ActiveDid Changes
|
||||||
|
|
||||||
|
#### 1. Enhanced updateActiveDid Method
|
||||||
|
```typescript
|
||||||
|
// In CapacitorPlatformService.ts
|
||||||
|
async updateActiveDid(did: string): Promise<void> {
|
||||||
|
const oldDid = this.currentActiveDid;
|
||||||
|
|
||||||
|
// Update the database (existing TimeSafari pattern)
|
||||||
|
await this.dbExec(
|
||||||
|
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
||||||
|
[did],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update local tracking
|
||||||
|
this.currentActiveDid = did;
|
||||||
|
|
||||||
|
// Notify listeners of the change
|
||||||
|
this.notifyActiveDidChange(did, oldDid);
|
||||||
|
|
||||||
|
// Update DailyNotification plugin if initialized
|
||||||
|
if (this.dailyNotificationInitialized) {
|
||||||
|
await this.updateDailyNotificationActiveDid(did, oldDid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Plugin Reconfiguration
|
||||||
|
```typescript
|
||||||
|
// In CapacitorPlatformService.ts
|
||||||
|
private async updateDailyNotificationActiveDid(newDid: string, oldDid: string | null): Promise<void> {
|
||||||
|
try {
|
||||||
|
logger.log(`[CapacitorPlatformService] Updating DailyNotification plugin activeDid from ${oldDid} to ${newDid}`);
|
||||||
|
|
||||||
|
// Get new settings for the new activeDid
|
||||||
|
const newSettings = await this.getTimeSafariSettings();
|
||||||
|
|
||||||
|
// Reconfigure DailyNotification plugin with new activeDid
|
||||||
|
await DailyNotification.configure({
|
||||||
|
timesafariConfig: {
|
||||||
|
activeDid: newDid,
|
||||||
|
endpoints: {
|
||||||
|
projectsLastUpdated: `${newSettings.apiServer}/api/v2/report/plansLastUpdatedBetween`
|
||||||
|
},
|
||||||
|
starredProjectsConfig: {
|
||||||
|
enabled: true,
|
||||||
|
starredPlanHandleIds: newSettings.starredPlanHandleIds || [],
|
||||||
|
lastAckedJwtId: newSettings.lastAckedStarredPlanChangesJwtId || '',
|
||||||
|
fetchInterval: '0 8 * * *'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update TimeSafari Integration Service
|
||||||
|
if (this.integrationService) {
|
||||||
|
await this.integrationService.initialize({
|
||||||
|
activeDid: newDid,
|
||||||
|
storageAdapter: this.getTimeSafariStorageAdapter(),
|
||||||
|
endorserApiBaseUrl: newSettings.apiServer || 'https://endorser.ch'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`[CapacitorPlatformService] Failed to update DailyNotification plugin activeDid:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Change Listener System
|
||||||
|
```typescript
|
||||||
|
// In CapacitorPlatformService.ts
|
||||||
|
private activeDidChangeListeners: Array<(newDid: string | null, oldDid: string | null) => void> = [];
|
||||||
|
|
||||||
|
addActiveDidChangeListener(listener: (newDid: string | null, oldDid: string | null) => void): void {
|
||||||
|
this.activeDidChangeListeners.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeActiveDidChangeListener(listener: (newDid: string | null, oldDid: string | null) => void): void {
|
||||||
|
const index = this.activeDidChangeListeners.indexOf(listener);
|
||||||
|
if (index > -1) {
|
||||||
|
this.activeDidChangeListeners.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private notifyActiveDidChange(newDid: string | null, oldDid: string | null): void {
|
||||||
|
this.activeDidChangeListeners.forEach(listener => {
|
||||||
|
try {
|
||||||
|
listener(newDid, oldDid);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('[CapacitorPlatformService] Error in activeDid change listener:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration Flow
|
||||||
|
|
||||||
|
### 1. User Switches Identity
|
||||||
|
```
|
||||||
|
User clicks "Switch Account" → IdentitySwitcherView.switchAccount() →
|
||||||
|
$updateActiveDid() → CapacitorPlatformService.updateActiveDid() →
|
||||||
|
DailyNotification plugin reconfiguration
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Plugin Reconfiguration Process
|
||||||
|
```
|
||||||
|
1. Update database (existing TimeSafari pattern)
|
||||||
|
2. Update local tracking
|
||||||
|
3. Notify change listeners
|
||||||
|
4. Reconfigure DailyNotification plugin
|
||||||
|
5. Update TimeSafari Integration Service
|
||||||
|
6. Log the change
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Settings Update Process
|
||||||
|
```
|
||||||
|
1. Get new settings for new activeDid
|
||||||
|
2. Update plugin configuration with new settings
|
||||||
|
3. Update integration service with new activeDid
|
||||||
|
4. Clear any cached data for old activeDid
|
||||||
|
5. Initialize new activeDid-specific data
|
||||||
|
```
|
||||||
|
|
||||||
|
## Required Changes to Existing Code
|
||||||
|
|
||||||
|
### File: `src/services/platforms/CapacitorPlatformService.ts`
|
||||||
|
|
||||||
|
#### 1. Add New Properties
|
||||||
|
```typescript
|
||||||
|
export class CapacitorPlatformService implements PlatformService {
|
||||||
|
// ... existing properties ...
|
||||||
|
|
||||||
|
// ADD THESE NEW PROPERTIES
|
||||||
|
private dailyNotificationService: DailyNotification | null = null;
|
||||||
|
private integrationService: TimeSafariIntegrationService | null = null;
|
||||||
|
private dailyNotificationInitialized = false;
|
||||||
|
|
||||||
|
// ActiveDid change tracking
|
||||||
|
private currentActiveDid: string | null = null;
|
||||||
|
private activeDidChangeListeners: Array<(newDid: string | null, oldDid: string | null) => void> = [];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Modify Existing updateActiveDid Method
|
||||||
|
```typescript
|
||||||
|
async updateActiveDid(did: string): Promise<void> {
|
||||||
|
const oldDid = this.currentActiveDid;
|
||||||
|
|
||||||
|
// Update the database (existing TimeSafari pattern)
|
||||||
|
await this.dbExec(
|
||||||
|
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
||||||
|
[did],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update local tracking
|
||||||
|
this.currentActiveDid = did;
|
||||||
|
|
||||||
|
// Notify listeners of the change
|
||||||
|
this.notifyActiveDidChange(did, oldDid);
|
||||||
|
|
||||||
|
// Update DailyNotification plugin if initialized
|
||||||
|
if (this.dailyNotificationInitialized) {
|
||||||
|
await this.updateDailyNotificationActiveDid(did, oldDid);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`[CapacitorPlatformService] ActiveDid updated from ${oldDid} to ${did}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Add New Methods
|
||||||
|
```typescript
|
||||||
|
// Add these new methods to the class
|
||||||
|
private async updateDailyNotificationActiveDid(newDid: string, oldDid: string | null): Promise<void> { /* ... */ }
|
||||||
|
private async getCurrentActiveDid(): Promise<string | null> { /* ... */ }
|
||||||
|
addActiveDidChangeListener(listener: (newDid: string | null, oldDid: string | null) => void): void { /* ... */ }
|
||||||
|
removeActiveDidChangeListener(listener: (newDid: string | null, oldDid: string | null) => void): void { /* ... */ }
|
||||||
|
private notifyActiveDidChange(newDid: string | null, oldDid: string | null): void { /* ... */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
## PlatformServiceMixin Integration
|
||||||
|
|
||||||
|
### File: `src/utils/PlatformServiceMixin.ts`
|
||||||
|
|
||||||
|
#### 1. Add DailyNotification Methods
|
||||||
|
```typescript
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Vue Component Integration
|
||||||
|
|
||||||
|
### File: `src/views/HomeView.vue` (or similar)
|
||||||
|
|
||||||
|
#### 1. Add DailyNotification Initialization
|
||||||
|
```typescript
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'HomeView',
|
||||||
|
|
||||||
|
mixins: [PlatformServiceMixin],
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
// Initialize DailyNotification (only on Capacitor)
|
||||||
|
if (this.isCapacitor) {
|
||||||
|
await this.$initializeDailyNotification();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Benefits
|
||||||
|
|
||||||
|
### 1. Seamless ActiveDid Change Handling
|
||||||
|
- **Automatic Reconfiguration**: Plugin automatically reconfigures when activeDid changes
|
||||||
|
- **Settings Synchronization**: New activeDid settings are automatically loaded and applied
|
||||||
|
- **Error Handling**: Robust error handling for activeDid change failures
|
||||||
|
- **Logging**: Comprehensive logging for debugging activeDid changes
|
||||||
|
|
||||||
|
### 2. Integration with Existing Patterns
|
||||||
|
- **Uses Existing Methods**: Leverages existing `updateActiveDid` method
|
||||||
|
- **Database Consistency**: Uses existing `active_identity` table pattern
|
||||||
|
- **Change Detection**: Integrates with existing change detection system
|
||||||
|
- **Settings Management**: Uses existing settings retrieval patterns
|
||||||
|
|
||||||
|
### 3. Enhanced Functionality
|
||||||
|
- **Background Fetching**: Continues to work with new activeDid
|
||||||
|
- **Notification Scheduling**: Updates notifications for new activeDid
|
||||||
|
- **Storage Management**: Manages storage for multiple activeDids
|
||||||
|
- **Observability**: Tracks activeDid changes in logs and metrics
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### 1. ActiveDid Change Testing
|
||||||
|
```typescript
|
||||||
|
// Test activeDid change handling
|
||||||
|
const testActiveDidChange = async () => {
|
||||||
|
// Switch to new activeDid
|
||||||
|
await platformService.updateActiveDid('new-did-123');
|
||||||
|
|
||||||
|
// Verify plugin is reconfigured
|
||||||
|
const status = await platformService.getDailyNotificationStatus();
|
||||||
|
assert.equal(status.currentActiveDid, 'new-did-123');
|
||||||
|
|
||||||
|
// Test starred projects fetch with new activeDid
|
||||||
|
const result = await platformService.loadNewStarredProjectChanges();
|
||||||
|
assert.ok(result.data !== undefined);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Settings Synchronization Testing
|
||||||
|
```typescript
|
||||||
|
// Test settings synchronization
|
||||||
|
const testSettingsSynchronization = async () => {
|
||||||
|
// Switch activeDid
|
||||||
|
await platformService.updateActiveDid('new-did-123');
|
||||||
|
|
||||||
|
// Verify settings are loaded for new activeDid
|
||||||
|
const settings = await platformService.getTimeSafariSettings();
|
||||||
|
assert.equal(settings.activeDid, 'new-did-123');
|
||||||
|
|
||||||
|
// Verify plugin configuration is updated
|
||||||
|
const config = await DailyNotification.getConfiguration();
|
||||||
|
assert.equal(config.timesafariConfig.activeDid, 'new-did-123');
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Error Handling Testing
|
||||||
|
```typescript
|
||||||
|
// Test error handling during activeDid changes
|
||||||
|
const testErrorHandling = async () => {
|
||||||
|
try {
|
||||||
|
// Switch to invalid activeDid
|
||||||
|
await platformService.updateActiveDid('invalid-did');
|
||||||
|
|
||||||
|
// Verify error is handled gracefully
|
||||||
|
const status = await platformService.getDailyNotificationStatus();
|
||||||
|
assert.ok(status.initialized); // Plugin should remain initialized
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// Verify error is logged
|
||||||
|
assert.ok(error.message.includes('activeDid'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Issues and Solutions
|
||||||
|
|
||||||
|
### Issue 1: Plugin Not Updating on ActiveDid Change
|
||||||
|
**Solution**: Ensure `updateDailyNotificationActiveDid` is called in the `updateActiveDid` method
|
||||||
|
|
||||||
|
### Issue 2: Settings Not Loading for New ActiveDid
|
||||||
|
**Solution**: Verify `getTimeSafariSettings` uses the current activeDid from database
|
||||||
|
|
||||||
|
### Issue 3: Cached Data from Old ActiveDid
|
||||||
|
**Solution**: Clear plugin caches when activeDid changes
|
||||||
|
|
||||||
|
### Issue 4: Background Fetching with Wrong ActiveDid
|
||||||
|
**Solution**: Ensure plugin reconfiguration includes new activeDid in all requests
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The DailyNotification plugin integrates seamlessly with the existing TimeSafari PWA activeDid change system by:
|
||||||
|
|
||||||
|
- **Extending the existing `updateActiveDid` method** to handle plugin reconfiguration
|
||||||
|
- **Using the existing change detection patterns** from PlatformServiceMixin
|
||||||
|
- **Maintaining database consistency** with the existing `active_identity` table
|
||||||
|
- **Providing robust error handling** for activeDid change failures
|
||||||
|
- **Supporting multiple activeDids** with proper isolation and cleanup
|
||||||
|
|
||||||
|
The integration ensures that the plugin automatically adapts to activeDid changes while maintaining the same interface and behavior as the existing TimeSafari PWA code.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Key Takeaway**: The CapacitorPlatformService **does know** when activeDid changes through the existing `updateActiveDid` method, and the DailyNotification plugin integrates with this system to automatically reconfigure when the user switches identities.
|
||||||
804
examples/capacitor-platform-service-activedid-integration.ts
Normal file
804
examples/capacitor-platform-service-activedid-integration.ts
Normal file
@@ -0,0 +1,804 @@
|
|||||||
|
/**
|
||||||
|
* TimeSafari PWA - CapacitorPlatformService ActiveDid Integration Example
|
||||||
|
*
|
||||||
|
* This example shows how to extend the existing CapacitorPlatformService
|
||||||
|
* to handle activeDid changes and integrate with the DailyNotification plugin.
|
||||||
|
*
|
||||||
|
* This represents the ACTUAL CHANGES needed to handle activeDid changes
|
||||||
|
* in the existing TimeSafari PWA CapacitorPlatformService.
|
||||||
|
*
|
||||||
|
* @author Matthew Raymer
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// EXISTING TIMESAFARI PWA CODE (unchanged)
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
import { Filesystem, Directory, Encoding } from "@capacitor/filesystem";
|
||||||
|
import {
|
||||||
|
Camera,
|
||||||
|
CameraResultType,
|
||||||
|
CameraSource,
|
||||||
|
CameraDirection,
|
||||||
|
} from "@capacitor/camera";
|
||||||
|
import { Capacitor } from "@capacitor/core";
|
||||||
|
import { Share } from "@capacitor/share";
|
||||||
|
import {
|
||||||
|
SQLiteConnection,
|
||||||
|
SQLiteDBConnection,
|
||||||
|
CapacitorSQLite,
|
||||||
|
DBSQLiteValues,
|
||||||
|
} from "@capacitor-community/sqlite";
|
||||||
|
|
||||||
|
import { runMigrations } from "@/db-sql/migration";
|
||||||
|
import { QueryExecResult } from "@/interfaces/database";
|
||||||
|
import {
|
||||||
|
ImageResult,
|
||||||
|
PlatformService,
|
||||||
|
PlatformCapabilities,
|
||||||
|
} from "../PlatformService";
|
||||||
|
import { logger } from "../../utils/logger";
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// NEW IMPORTS FOR DAILYNOTIFICATION PLUGIN
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
import { DailyNotification } from '@timesafari/daily-notification-plugin';
|
||||||
|
import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin';
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// EXISTING INTERFACES (unchanged)
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
interface QueuedOperation {
|
||||||
|
type: "run" | "query" | "rawQuery";
|
||||||
|
sql: string;
|
||||||
|
params: unknown[];
|
||||||
|
resolve: (value: unknown) => void;
|
||||||
|
reject: (reason: unknown) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// NEW INTERFACES FOR DAILYNOTIFICATION PLUGIN
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EXTENDED CapacitorPlatformService with DailyNotification and ActiveDid Integration
|
||||||
|
*
|
||||||
|
* This shows the ACTUAL CHANGES needed to the existing TimeSafari PWA
|
||||||
|
* CapacitorPlatformService class to handle activeDid changes and integrate
|
||||||
|
* with the DailyNotification plugin.
|
||||||
|
*/
|
||||||
|
export class CapacitorPlatformService implements PlatformService {
|
||||||
|
// =================================================
|
||||||
|
// EXISTING PROPERTIES (unchanged)
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
/** Current camera direction */
|
||||||
|
private currentDirection: CameraDirection = CameraDirection.Rear;
|
||||||
|
|
||||||
|
private sqlite: SQLiteConnection;
|
||||||
|
private db: SQLiteDBConnection | null = null;
|
||||||
|
private dbName = "timesafari.sqlite";
|
||||||
|
private initialized = false;
|
||||||
|
private initializationPromise: Promise<void> | null = null;
|
||||||
|
private operationQueue: Array<QueuedOperation> = [];
|
||||||
|
private isProcessingQueue: boolean = false;
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// NEW PROPERTIES FOR DAILYNOTIFICATION PLUGIN
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
private dailyNotificationService: DailyNotification | null = null;
|
||||||
|
private integrationService: TimeSafariIntegrationService | null = null;
|
||||||
|
private dailyNotificationInitialized = false;
|
||||||
|
|
||||||
|
// ActiveDid change tracking
|
||||||
|
private currentActiveDid: string | null = null;
|
||||||
|
private activeDidChangeListeners: Array<(newDid: string | null, oldDid: string | null) => void> = [];
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// EXISTING CONSTRUCTOR (unchanged)
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.sqlite = new SQLiteConnection(CapacitorSQLite);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// MODIFIED METHOD: Enhanced updateActiveDid with DailyNotification Integration
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enhanced updateActiveDid method that handles DailyNotification plugin updates
|
||||||
|
*
|
||||||
|
* This method extends the existing updateActiveDid method to also update
|
||||||
|
* the DailyNotification plugin when the activeDid changes.
|
||||||
|
*/
|
||||||
|
async updateActiveDid(did: string): Promise<void> {
|
||||||
|
const oldDid = this.currentActiveDid;
|
||||||
|
|
||||||
|
// Update the database (existing TimeSafari pattern)
|
||||||
|
await this.dbExec(
|
||||||
|
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
||||||
|
[did],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update local tracking
|
||||||
|
this.currentActiveDid = did;
|
||||||
|
|
||||||
|
// Notify listeners of the change
|
||||||
|
this.notifyActiveDidChange(did, oldDid);
|
||||||
|
|
||||||
|
// Update DailyNotification plugin if initialized
|
||||||
|
if (this.dailyNotificationInitialized) {
|
||||||
|
await this.updateDailyNotificationActiveDid(did, oldDid);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
`[CapacitorPlatformService] ActiveDid updated from ${oldDid} to ${did}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// NEW METHOD: Update DailyNotification Plugin ActiveDid
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update DailyNotification plugin when activeDid changes
|
||||||
|
*
|
||||||
|
* This method reconfigures the DailyNotification plugin with the new
|
||||||
|
* activeDid and updates all related settings.
|
||||||
|
*/
|
||||||
|
private async updateDailyNotificationActiveDid(newDid: string, oldDid: string | null): Promise<void> {
|
||||||
|
try {
|
||||||
|
logger.log(`[CapacitorPlatformService] Updating DailyNotification plugin activeDid from ${oldDid} to ${newDid}`);
|
||||||
|
|
||||||
|
// Get new settings for the new activeDid
|
||||||
|
const newSettings = await this.getTimeSafariSettings();
|
||||||
|
|
||||||
|
// Reconfigure DailyNotification plugin with new activeDid
|
||||||
|
await DailyNotification.configure({
|
||||||
|
timesafariConfig: {
|
||||||
|
activeDid: newDid,
|
||||||
|
endpoints: {
|
||||||
|
offersToPerson: `${newSettings.apiServer}/api/v2/offers/person`,
|
||||||
|
offersToPlans: `${newSettings.apiServer}/api/v2/offers/plans`,
|
||||||
|
projectsLastUpdated: `${newSettings.apiServer}/api/v2/report/plansLastUpdatedBetween`
|
||||||
|
},
|
||||||
|
starredProjectsConfig: {
|
||||||
|
enabled: true,
|
||||||
|
starredPlanHandleIds: newSettings.starredPlanHandleIds || [],
|
||||||
|
lastAckedJwtId: newSettings.lastAckedStarredPlanChangesJwtId || '',
|
||||||
|
fetchInterval: '0 8 * * *'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update TimeSafari Integration Service
|
||||||
|
if (this.integrationService) {
|
||||||
|
await this.integrationService.initialize({
|
||||||
|
activeDid: newDid,
|
||||||
|
storageAdapter: this.getTimeSafariStorageAdapter(),
|
||||||
|
endorserApiBaseUrl: newSettings.apiServer || 'https://endorser.ch'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log(`[CapacitorPlatformService] DailyNotification plugin updated successfully for activeDid: ${newDid}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`[CapacitorPlatformService] Failed to update DailyNotification plugin activeDid:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// NEW METHOD: ActiveDid Change Listener Management
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add listener for activeDid changes
|
||||||
|
*
|
||||||
|
* This method allows components to register listeners that will be
|
||||||
|
* notified when the activeDid changes.
|
||||||
|
*/
|
||||||
|
addActiveDidChangeListener(listener: (newDid: string | null, oldDid: string | null) => void): void {
|
||||||
|
this.activeDidChangeListeners.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove listener for activeDid changes
|
||||||
|
*/
|
||||||
|
removeActiveDidChangeListener(listener: (newDid: string | null, oldDid: string | null) => void): void {
|
||||||
|
const index = this.activeDidChangeListeners.indexOf(listener);
|
||||||
|
if (index > -1) {
|
||||||
|
this.activeDidChangeListeners.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify all listeners of activeDid change
|
||||||
|
*/
|
||||||
|
private notifyActiveDidChange(newDid: string | null, oldDid: string | null): void {
|
||||||
|
this.activeDidChangeListeners.forEach(listener => {
|
||||||
|
try {
|
||||||
|
listener(newDid, oldDid);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('[CapacitorPlatformService] Error in activeDid change listener:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// NEW METHOD: Initialize DailyNotification Plugin
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize DailyNotification plugin with TimeSafari configuration
|
||||||
|
*
|
||||||
|
* This method should be called after the database is initialized
|
||||||
|
* to set up the DailyNotification plugin with TimeSafari-specific settings.
|
||||||
|
*/
|
||||||
|
async initializeDailyNotification(): Promise<void> {
|
||||||
|
if (this.dailyNotificationInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.log("[CapacitorPlatformService] Initializing DailyNotification plugin...");
|
||||||
|
|
||||||
|
// Get current TimeSafari settings
|
||||||
|
const settings = await this.getTimeSafariSettings();
|
||||||
|
|
||||||
|
// Get current activeDid
|
||||||
|
const currentActiveDid = await this.getCurrentActiveDid();
|
||||||
|
|
||||||
|
// Configure DailyNotification plugin with TimeSafari data
|
||||||
|
await DailyNotification.configure({
|
||||||
|
// Basic plugin configuration
|
||||||
|
storage: 'tiered',
|
||||||
|
ttlSeconds: 1800,
|
||||||
|
enableETagSupport: true,
|
||||||
|
enableErrorHandling: true,
|
||||||
|
enablePerformanceOptimization: true,
|
||||||
|
|
||||||
|
// TimeSafari-specific configuration
|
||||||
|
timesafariConfig: {
|
||||||
|
// Use current activeDid
|
||||||
|
activeDid: currentActiveDid || '',
|
||||||
|
|
||||||
|
// Use existing TimeSafari API endpoints
|
||||||
|
endpoints: {
|
||||||
|
offersToPerson: `${settings.apiServer}/api/v2/offers/person`,
|
||||||
|
offersToPlans: `${settings.apiServer}/api/v2/offers/plans`,
|
||||||
|
projectsLastUpdated: `${settings.apiServer}/api/v2/report/plansLastUpdatedBetween`
|
||||||
|
},
|
||||||
|
|
||||||
|
// Configure starred projects fetching (matches existing TimeSafari pattern)
|
||||||
|
starredProjectsConfig: {
|
||||||
|
enabled: true,
|
||||||
|
starredPlanHandleIds: settings.starredPlanHandleIds || [],
|
||||||
|
lastAckedJwtId: settings.lastAckedStarredPlanChangesJwtId || '',
|
||||||
|
fetchInterval: '0 8 * * *', // Daily at 8 AM
|
||||||
|
maxResults: 50,
|
||||||
|
hitLimitHandling: 'warn' // Same as existing TimeSafari error handling
|
||||||
|
},
|
||||||
|
|
||||||
|
// Sync configuration (optimized for TimeSafari use case)
|
||||||
|
syncConfig: {
|
||||||
|
enableParallel: true,
|
||||||
|
maxConcurrent: 3,
|
||||||
|
batchSize: 10,
|
||||||
|
timeout: 30000,
|
||||||
|
retryAttempts: 3
|
||||||
|
},
|
||||||
|
|
||||||
|
// Error policy (matches existing TimeSafari error handling)
|
||||||
|
errorPolicy: {
|
||||||
|
maxRetries: 3,
|
||||||
|
backoffMultiplier: 2,
|
||||||
|
activeDidChangeRetries: 5, // Special retry for activeDid changes
|
||||||
|
starredProjectsRetries: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Network configuration using existing TimeSafari patterns
|
||||||
|
networkConfig: {
|
||||||
|
baseURL: settings.apiServer || 'https://endorser.ch',
|
||||||
|
timeout: 30000,
|
||||||
|
retryAttempts: 3,
|
||||||
|
retryDelay: 1000,
|
||||||
|
maxConcurrent: 5,
|
||||||
|
|
||||||
|
// Headers matching TimeSafari pattern
|
||||||
|
defaultHeaders: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'User-Agent': 'TimeSafari-PWA/1.0.0'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Content fetch configuration (replaces existing loadNewStarredProjectChanges)
|
||||||
|
contentFetch: {
|
||||||
|
enabled: true,
|
||||||
|
schedule: '0 8 * * *', // Daily at 8 AM
|
||||||
|
|
||||||
|
// Use existing TimeSafari request pattern
|
||||||
|
requestConfig: {
|
||||||
|
method: 'POST',
|
||||||
|
url: `${settings.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 TimeSafari error handling
|
||||||
|
callbacks: {
|
||||||
|
onSuccess: this.handleStarredProjectsSuccess.bind(this),
|
||||||
|
onError: this.handleStarredProjectsError.bind(this),
|
||||||
|
onComplete: this.handleStarredProjectsComplete.bind(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize TimeSafari Integration Service
|
||||||
|
this.integrationService = TimeSafariIntegrationService.getInstance();
|
||||||
|
await this.integrationService.initialize({
|
||||||
|
activeDid: currentActiveDid || '',
|
||||||
|
storageAdapter: this.getTimeSafariStorageAdapter(),
|
||||||
|
endorserApiBaseUrl: settings.apiServer || 'https://endorser.ch',
|
||||||
|
|
||||||
|
// Use existing TimeSafari request patterns
|
||||||
|
requestConfig: {
|
||||||
|
baseURL: settings.apiServer || 'https://endorser.ch',
|
||||||
|
timeout: 30000,
|
||||||
|
retryAttempts: 3
|
||||||
|
},
|
||||||
|
|
||||||
|
// Configure starred projects fetching
|
||||||
|
starredProjectsConfig: {
|
||||||
|
enabled: true,
|
||||||
|
starredPlanHandleIds: settings.starredPlanHandleIds || [],
|
||||||
|
lastAckedJwtId: settings.lastAckedStarredPlanChangesJwtId || '',
|
||||||
|
fetchInterval: '0 8 * * *',
|
||||||
|
maxResults: 50
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dailyNotificationInitialized = true;
|
||||||
|
this.currentActiveDid = currentActiveDid;
|
||||||
|
|
||||||
|
logger.log("[CapacitorPlatformService] DailyNotification plugin initialized successfully");
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("[CapacitorPlatformService] Failed to initialize DailyNotification plugin:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// NEW METHOD: Get Current ActiveDid
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current activeDid from the database
|
||||||
|
*
|
||||||
|
* This method retrieves the current activeDid from the active_identity table
|
||||||
|
* using the existing TimeSafari pattern.
|
||||||
|
*/
|
||||||
|
private async getCurrentActiveDid(): Promise<string | null> {
|
||||||
|
try {
|
||||||
|
const result = await this.dbQuery(
|
||||||
|
"SELECT activeDid FROM active_identity WHERE id = 1"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result?.values?.length) {
|
||||||
|
const activeDid = result.values[0][0] as string | null;
|
||||||
|
return activeDid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("[CapacitorPlatformService] Error getting current activeDid:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// NEW METHOD: Enhanced loadNewStarredProjectChanges
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enhanced version of existing TimeSafari loadNewStarredProjectChanges method
|
||||||
|
*
|
||||||
|
* This method replaces the existing TimeSafari PWA method with plugin-enhanced
|
||||||
|
* functionality while maintaining the same interface and behavior.
|
||||||
|
*/
|
||||||
|
async loadNewStarredProjectChanges(): Promise<StarredProjectsResponse> {
|
||||||
|
// Ensure DailyNotification is initialized
|
||||||
|
if (!this.dailyNotificationInitialized) {
|
||||||
|
await this.initializeDailyNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = await this.getTimeSafariSettings();
|
||||||
|
const currentActiveDid = await this.getCurrentActiveDid();
|
||||||
|
|
||||||
|
if (!currentActiveDid || !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(
|
||||||
|
currentActiveDid,
|
||||||
|
settings.starredPlanHandleIds,
|
||||||
|
settings.lastAckedStarredPlanChangesJwtId
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enhanced logging (optional)
|
||||||
|
logger.log("[CapacitorPlatformService] Starred projects loaded successfully:", {
|
||||||
|
count: starredProjectChanges.data.length,
|
||||||
|
hitLimit: starredProjectChanges.hitLimit,
|
||||||
|
planIds: settings.starredPlanHandleIds.length,
|
||||||
|
activeDid: currentActiveDid
|
||||||
|
});
|
||||||
|
|
||||||
|
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 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// NEW METHOD: Get TimeSafari Settings
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get TimeSafari settings using existing database patterns
|
||||||
|
*
|
||||||
|
* This method uses the existing TimeSafari database patterns to retrieve
|
||||||
|
* settings that are needed for the DailyNotification plugin configuration.
|
||||||
|
*/
|
||||||
|
private async getTimeSafariSettings(): Promise<TimeSafariSettings> {
|
||||||
|
try {
|
||||||
|
// Get current activeDid
|
||||||
|
const currentActiveDid = await this.getCurrentActiveDid();
|
||||||
|
|
||||||
|
if (!currentActiveDid) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use existing TimeSafari settings retrieval pattern
|
||||||
|
const result = await this.dbQuery(
|
||||||
|
"SELECT * FROM settings WHERE accountDid = ?",
|
||||||
|
[currentActiveDid]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!result?.values?.length) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map database columns to values (existing TimeSafari pattern)
|
||||||
|
const settings: TimeSafariSettings = {};
|
||||||
|
result.columns.forEach((column, index) => {
|
||||||
|
if (column !== 'id') {
|
||||||
|
settings[column] = result.values[0][index];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set activeDid from current value
|
||||||
|
settings.activeDid = currentActiveDid;
|
||||||
|
|
||||||
|
// Handle JSON field parsing (existing TimeSafari pattern)
|
||||||
|
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 {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// NEW METHOD: Get TimeSafari Storage Adapter
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get TimeSafari storage adapter using existing patterns
|
||||||
|
*
|
||||||
|
* This method creates a storage adapter that uses the existing TimeSafari
|
||||||
|
* database patterns for the DailyNotification plugin.
|
||||||
|
*/
|
||||||
|
private getTimeSafariStorageAdapter(): unknown {
|
||||||
|
// Return existing TimeSafari storage adapter
|
||||||
|
return {
|
||||||
|
// Use existing TimeSafari storage patterns
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// NEW METHODS: Callback Handlers
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback handler for successful starred projects fetch
|
||||||
|
*/
|
||||||
|
private async handleStarredProjectsSuccess(data: StarredProjectsResponse): Promise<void> {
|
||||||
|
// Enhanced logging (optional)
|
||||||
|
logger.log("[CapacitorPlatformService] Starred projects success callback:", {
|
||||||
|
count: data.data.length,
|
||||||
|
hitLimit: data.hitLimit,
|
||||||
|
activeDid: this.currentActiveDid
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback handler for starred projects fetch errors
|
||||||
|
*/
|
||||||
|
private async handleStarredProjectsError(error: Error): Promise<void> {
|
||||||
|
// Same error handling as existing TimeSafari code
|
||||||
|
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(),
|
||||||
|
activeDid: this.currentActiveDid
|
||||||
|
})]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback handler for starred projects fetch completion
|
||||||
|
*/
|
||||||
|
private async handleStarredProjectsComplete(result: unknown): Promise<void> {
|
||||||
|
// Handle completion
|
||||||
|
logger.log("[CapacitorPlatformService] Starred projects fetch completed:", {
|
||||||
|
result,
|
||||||
|
activeDid: this.currentActiveDid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// NEW METHOD: Get DailyNotification Status
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get DailyNotification plugin status for debugging
|
||||||
|
*/
|
||||||
|
async getDailyNotificationStatus(): Promise<{
|
||||||
|
initialized: boolean;
|
||||||
|
platform: string;
|
||||||
|
capabilities: PlatformCapabilities;
|
||||||
|
currentActiveDid: string | null;
|
||||||
|
}> {
|
||||||
|
return {
|
||||||
|
initialized: this.dailyNotificationInitialized,
|
||||||
|
platform: Capacitor.getPlatform(),
|
||||||
|
capabilities: this.getCapabilities(),
|
||||||
|
currentActiveDid: this.currentActiveDid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// MODIFIED METHOD: Enhanced initializeDatabase
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// NEW: 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// EXISTING METHODS (unchanged - showing key ones)
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
private async _initialize(): Promise<void> {
|
||||||
|
if (this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create/Open database
|
||||||
|
this.db = await this.sqlite.createConnection(
|
||||||
|
this.dbName,
|
||||||
|
false,
|
||||||
|
"no-encryption",
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.db.open();
|
||||||
|
|
||||||
|
// Run migrations
|
||||||
|
await this.runCapacitorMigrations();
|
||||||
|
|
||||||
|
this.initialized = true;
|
||||||
|
logger.log(
|
||||||
|
"[CapacitorPlatformService] SQLite database initialized successfully",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Start processing the queue after initialization
|
||||||
|
this.processQueue();
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(
|
||||||
|
"[CapacitorPlatformService] Error initializing SQLite database:",
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
throw new Error(
|
||||||
|
"[CapacitorPlatformService] Failed to initialize database",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... (all other existing methods remain unchanged)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the capabilities of the Capacitor platform
|
||||||
|
* @returns Platform capabilities object
|
||||||
|
*/
|
||||||
|
getCapabilities(): PlatformCapabilities {
|
||||||
|
const platform = Capacitor.getPlatform();
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasFileSystem: true,
|
||||||
|
hasCamera: true,
|
||||||
|
isMobile: true, // Capacitor is always mobile
|
||||||
|
isIOS: platform === "ios",
|
||||||
|
hasFileDownload: false, // Mobile platforms need sharing
|
||||||
|
needsFileHandlingInstructions: true, // Mobile needs instructions
|
||||||
|
isNativeApp: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see PlatformService.dbQuery
|
||||||
|
*/
|
||||||
|
async dbQuery(sql: string, params?: unknown[]): Promise<QueryExecResult> {
|
||||||
|
await this.waitForInitialization();
|
||||||
|
return this.queueOperation<QueryExecResult>("query", sql, params || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see PlatformService.dbExec
|
||||||
|
*/
|
||||||
|
async dbExec(
|
||||||
|
sql: string,
|
||||||
|
params?: unknown[],
|
||||||
|
): Promise<{ changes: number; lastId?: number }> {
|
||||||
|
await this.waitForInitialization();
|
||||||
|
return this.queueOperation<{ changes: number; lastId?: number }>(
|
||||||
|
"run",
|
||||||
|
sql,
|
||||||
|
params || [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... (all other existing methods remain unchanged)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if running on Capacitor platform.
|
||||||
|
* @returns true, as this is the Capacitor implementation
|
||||||
|
*/
|
||||||
|
isCapacitor(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if running on Electron platform.
|
||||||
|
* @returns false, as this is Capacitor, not Electron
|
||||||
|
*/
|
||||||
|
isElectron(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if running on web platform.
|
||||||
|
* @returns false, as this is not web
|
||||||
|
*/
|
||||||
|
isWeb(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... (all other existing methods remain unchanged)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user