Browse Source
- Add example showing DailyNotification plugin integration ONLY in Capacitor classes - Remove all isCapacitor flags since plugin is only used on Capacitor platforms - Show actual changes needed to existing TimeSafari PWA CapacitorPlatformService - Include activeDid change handling and plugin reconfiguration - Provide clean summary of exact code changes needed - Focus on Capacitor-specific implementation without platform detection This gives a cleaner integration approach where plugin code only touches Capacitor classes and doesn't need platform detection flags.master
2 changed files with 1323 additions and 0 deletions
@ -0,0 +1,577 @@ |
|||
# TimeSafari PWA - CapacitorPlatformService Clean Changes |
|||
|
|||
**Author**: Matthew Raymer |
|||
**Version**: 1.0.0 |
|||
**Created**: 2025-10-08 06:24:57 UTC |
|||
|
|||
## Overview |
|||
|
|||
This document shows the **exact changes** needed to the existing TimeSafari PWA `CapacitorPlatformService` to add DailyNotification plugin functionality. The plugin code **ONLY touches Capacitor classes** - no `isCapacitor` flags needed. |
|||
|
|||
## Required Changes to Existing TimeSafari PWA Code |
|||
|
|||
### File: `src/services/platforms/CapacitorPlatformService.ts` |
|||
|
|||
#### 1. Add New Imports (at the top of the file) |
|||
|
|||
```typescript |
|||
// ADD THESE IMPORTS |
|||
import { DailyNotification } from '@timesafari/daily-notification-plugin'; |
|||
import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin'; |
|||
``` |
|||
|
|||
#### 2. Add New Interfaces (after existing interfaces) |
|||
|
|||
```typescript |
|||
// 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) |
|||
|
|||
```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; |
|||
} |
|||
``` |
|||
|
|||
#### 4. 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; |
|||
|
|||
// Update DailyNotification plugin if initialized |
|||
if (this.dailyNotificationInitialized) { |
|||
await this.updateDailyNotificationActiveDid(did, oldDid); |
|||
} |
|||
|
|||
logger.debug( |
|||
`[CapacitorPlatformService] ActiveDid updated from ${oldDid} to ${did}` |
|||
); |
|||
} |
|||
``` |
|||
|
|||
#### 5. Modify Existing initializeDatabase Method |
|||
|
|||
```typescript |
|||
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; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### 6. Add New Methods (in the class, after existing methods) |
|||
|
|||
```typescript |
|||
/** |
|||
* 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(); |
|||
|
|||
// 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; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 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(); |
|||
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 }; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Update DailyNotification plugin when activeDid changes |
|||
*/ |
|||
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); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Get current activeDid from the database |
|||
*/ |
|||
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; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Get TimeSafari settings using existing database patterns |
|||
*/ |
|||
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 {}; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Get TimeSafari storage adapter using existing patterns |
|||
*/ |
|||
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; |
|||
} |
|||
}; |
|||
} |
|||
|
|||
/** |
|||
* 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 |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 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 |
|||
}; |
|||
} |
|||
``` |
|||
|
|||
## Package.json Changes |
|||
|
|||
### Add DailyNotification Plugin Dependency |
|||
|
|||
```json |
|||
{ |
|||
"dependencies": { |
|||
"@timesafari/daily-notification-plugin": "ssh://git@173.199.124.46:222/trent_larson/daily-notification-plugin.git" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## Summary of Changes |
|||
|
|||
### Files Modified: |
|||
1. **`src/services/platforms/CapacitorPlatformService.ts`** |
|||
- Add imports for DailyNotification plugin |
|||
- Add new interfaces for plugin integration |
|||
- Add new properties for plugin state |
|||
- Modify existing `updateActiveDid` method |
|||
- Modify existing `initializeDatabase` method |
|||
- Add new methods for plugin functionality |
|||
|
|||
2. **`package.json`** |
|||
- Add DailyNotification plugin dependency |
|||
|
|||
### Key Benefits: |
|||
- **Same Interface**: Existing methods work exactly the same |
|||
- **Enhanced Functionality**: Background fetching, structured logging, error handling |
|||
- **ActiveDid Change Handling**: Plugin automatically reconfigures when activeDid changes |
|||
- **No Platform Flags**: Plugin code only touches Capacitor classes |
|||
- **No Breaking Changes**: Existing code continues to work |
|||
|
|||
### Migration Strategy: |
|||
1. **Add the changes** to existing TimeSafari PWA CapacitorPlatformService |
|||
2. **Test on Capacitor platforms** (Android, iOS) |
|||
3. **Verify activeDid changes** work correctly |
|||
4. **Gradually migrate** individual methods to use plugin features |
|||
5. **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. The plugin code ONLY touches Capacitor classes - no `isCapacitor` flags needed.** |
@ -0,0 +1,746 @@ |
|||
/** |
|||
* TimeSafari PWA - CapacitorPlatformService Clean Integration Example |
|||
* |
|||
* This example shows the ACTUAL CHANGES needed to the existing TimeSafari PWA |
|||
* CapacitorPlatformService to add DailyNotification plugin functionality. |
|||
* |
|||
* The plugin code ONLY touches Capacitor classes - no isCapacitor flags needed. |
|||
* |
|||
* @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 Integration |
|||
* |
|||
* This shows the ACTUAL CHANGES needed to the existing TimeSafari PWA |
|||
* CapacitorPlatformService class. The plugin code ONLY touches this class. |
|||
*/ |
|||
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; |
|||
|
|||
// =================================================
|
|||
// 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; |
|||
|
|||
// 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 |
|||
*/ |
|||
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: Initialize DailyNotification Plugin
|
|||
// =================================================
|
|||
|
|||
/** |
|||
* 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(); |
|||
|
|||
// 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 |
|||
*/ |
|||
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 |
|||
*/ |
|||
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 |
|||
*/ |
|||
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)
|
|||
} |
Loading…
Reference in new issue