/** * Daily Notification Plugin Definitions * * TypeScript definitions for the Daily Notification Plugin * Aligned with Android implementation and test requirements * * @author Matthew Raymer * @version 2.0.0 */ export interface NotificationResponse { id: string; title: string; body: string; timestamp: number; url?: string; } export interface NotificationOptions { url?: string; time?: string; title?: string; body?: string; sound?: boolean; priority?: 'high' | 'default' | 'low' | 'min' | 'max' | 'normal'; timezone?: string; retryCount?: number; retryInterval?: number; offlineFallback?: boolean; contentHandler?: ContentHandler; headers?: Record; } export interface ContentHandler { (response?: any): Promise<{ title: string; body: string; data?: any }>; } export interface ScheduleOptions { url?: string; time?: string; sound?: boolean; priority?: 'high' | 'default' | 'low' | 'min' | 'max' | 'normal'; timezone?: string; retryCount?: number; retryInterval?: number; offlineFallback?: boolean; contentHandler?: ContentHandler; headers?: Record; } export interface NotificationSettings { url?: string; time?: string; sound?: boolean; priority?: string; timezone?: string; retryCount?: number; retryInterval?: number; offlineFallback?: boolean; } export interface NotificationStatus { isEnabled?: boolean; isScheduled?: boolean; lastNotificationTime: number | Promise; nextNotificationTime: number | Promise; pending?: number; settings: NotificationSettings; error?: string; } export interface BatteryStatus { level: number; isCharging: boolean; powerState: number; isOptimizationExempt: boolean; } export interface PowerState { powerState: number; isOptimizationExempt: boolean; } export interface NotificationEvent extends Event { detail: { id: string; action: string; data?: any; }; } export interface PermissionStatus { status?: string; granted?: boolean; notifications: PermissionState; backgroundRefresh?: PermissionState; // iOS only alert?: boolean; badge?: boolean; sound?: boolean; lockScreen?: boolean; carPlay?: boolean; } export type PermissionState = 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied' | 'provisional' | 'ephemeral' | 'unknown'; // Additional interfaces for enhanced functionality export interface NotificationMetrics { scheduledTime: number; actualDeliveryTime?: number; contentAge: number; engagement?: 'TAPPED' | 'DISMISSED' | 'IGNORED'; failureReason?: string; platformInfo: PlatformInfo; } export interface PlatformInfo { oem?: string; osVersion: string; appState: string; } export interface FallbackContent { title: string; body: string; isEmergency: boolean; age?: string; } export interface CachePolicy { maxSize: number; evictionPolicy: 'LRU' | 'FIFO' | 'TTL'; ttl?: number; cleanupInterval: number; } export interface NetworkConfig { timeout: number; retryAttempts: number; retryDelay: number; offlineFallback: boolean; } export interface SchedulingConfig { exactAlarms: boolean; adaptiveScheduling: boolean; quietHours?: { start: string; end: string; enabled: boolean; }; timezone: string; } export interface ConfigureOptions { dbPath?: string; storage?: 'shared' | 'tiered'; ttlSeconds?: number; prefetchLeadMinutes?: number; maxNotificationsPerDay?: number; retentionDays?: number; // Phase 2: TimeSafari ActiveDid Integration Enhancement activeDidIntegration?: { platform: 'android' | 'ios' | 'web' | 'electron'; storageType: 'plugin-managed' | 'host-managed'; jwtExpirationSeconds?: number; apiServer?: string; // Phase 2: Host-provided activeDid configuration activeDid?: string; // Initial activeDid from host hostCredentials?: { platform?: string; // Platform identifier accessToken?: string; // Optional access token }; autoSync?: boolean; // Auto-sync activeDid changes identityChangeGraceSeconds?: number; // Grace period for activeDid changes }; } // Dual Scheduling System Interfaces export interface ContentFetchConfig { enabled: boolean; schedule: string; // Cron expression url?: string; headers?: Record; timeout?: number; retryAttempts?: number; retryDelay?: number; callbacks: { apiService?: string; database?: string; reporting?: string; onSuccess?: (data: any) => Promise; onError?: (error: Error) => Promise; onComplete?: (result: ContentFetchResult) => Promise; }; contentHandler?: ContentHandler; cachePolicy?: CachePolicy; networkConfig?: NetworkConfig; // Phase 2: TimeSafari Endorser.ch API configuration timesafariConfig?: { activeDid: string; // Required activeDid for authentication endpoints?: { offersToPerson?: string; offersToPlans?: string; projectsLastUpdated?: string; }; syncConfig?: { enableParallel?: boolean; // Enable parallel API requests maxConcurrent?: number; // Max concurrent requests batchSize?: number; // Batch size for requests }; credentialConfig?: { jwtSecret?: string; // JWT secret for signing tokenExpirationMinutes?: number; // Token expiration refreshThresholdMinutes?: number; // Refresh threshold }; errorPolicy?: { maxRetries?: number; backoffMultiplier?: number; activeDidChangeRetries?: number; // Special retry for activeDid changes }; }; } export interface UserNotificationConfig { enabled: boolean; schedule: string; // Cron expression title?: string; body?: string; sound?: boolean; vibration?: boolean; priority?: 'low' | 'normal' | 'high'; badge?: boolean; actions?: NotificationAction[]; category?: string; userInfo?: Record; } export interface NotificationAction { id: string; title: string; icon?: string; destructive?: boolean; authenticationRequired?: boolean; } export interface DualScheduleConfiguration { contentFetch: ContentFetchConfig; userNotification: UserNotificationConfig; relationship?: { autoLink: boolean; // Automatically link content to notification contentTimeout: number; // How long to wait for content before notification fallbackBehavior: 'skip' | 'show_default' | 'retry'; }; } export interface ContentFetchResult { success: boolean; data?: any; timestamp: number; contentAge: number; error?: string; retryCount: number; metadata?: Record; } export interface DualScheduleStatus { contentFetch: { isEnabled: boolean; isScheduled: boolean; lastFetchTime?: number; nextFetchTime?: number; lastFetchResult?: ContentFetchResult; pendingFetches: number; }; userNotification: { isEnabled: boolean; isScheduled: boolean; lastNotificationTime?: number; nextNotificationTime?: number; pendingNotifications: number; }; relationship: { isLinked: boolean; contentAvailable: boolean; lastLinkTime?: number; }; overall: { isActive: boolean; lastActivity: number; errorCount: number; successRate: number; }; } // Enhanced DailyNotificationPlugin interface with dual scheduling export interface DailyNotificationPlugin { // Configuration methods configure(options: ConfigureOptions): Promise; // Rolling window management maintainRollingWindow(): Promise; getRollingWindowStats(): Promise<{ stats: string; maintenanceNeeded: boolean; timeUntilNextMaintenance: number; }>; // Exact alarm management getExactAlarmStatus(): Promise<{ supported: boolean; enabled: boolean; canSchedule: boolean; fallbackWindow: string; }>; requestExactAlarmPermission(): Promise; openExactAlarmSettings(): Promise; // Reboot recovery management getRebootRecoveryStatus(): Promise<{ inProgress: boolean; lastRecoveryTime: number; timeSinceLastRecovery: number; recoveryNeeded: boolean; }>; // Existing methods scheduleDailyNotification(options: NotificationOptions | ScheduleOptions): Promise; getLastNotification(): Promise; cancelAllNotifications(): Promise; getNotificationStatus(): Promise; updateSettings(settings: NotificationSettings): Promise; getBatteryStatus(): Promise; requestBatteryOptimizationExemption(): Promise; setAdaptiveScheduling(options: { enabled: boolean }): Promise; getPowerState(): Promise; checkPermissions(): Promise; requestPermissions(): Promise; // New dual scheduling methods scheduleContentFetch(config: ContentFetchConfig): Promise; scheduleUserNotification(config: UserNotificationConfig): Promise; scheduleDualNotification(config: DualScheduleConfiguration): Promise; getDualScheduleStatus(): Promise; updateDualScheduleConfig(config: DualScheduleConfiguration): Promise; cancelDualSchedule(): Promise; pauseDualSchedule(): Promise; resumeDualSchedule(): Promise; // Content management methods getContentCache(): Promise>; clearContentCache(): Promise; getContentHistory(): Promise; // Callback management methods registerCallback(name: string, callback: Function): Promise; unregisterCallback(name: string): Promise; getRegisteredCallbacks(): Promise; // Phase 1: ActiveDid Management Methods (Option A Implementation) setActiveDidFromHost(activeDid: string): Promise; onActiveDidChange(callback: (newActiveDid: string) => Promise): void; refreshAuthenticationForNewIdentity(activeDid: string): Promise; clearCacheForNewIdentity(): Promise; updateBackgroundTaskIdentity(activeDid: string): Promise; } // Phase 1: TimeSafari Endorser.ch API Interfaces export interface OffersResponse { data: OfferSummaryRecord[]; hitLimit: boolean; } export interface OfferSummaryRecord { jwtId: string; handleId: string; issuedAt: string; offeredByDid: string; recipientDid: string; unit: string; amount: number; amountGiven: number; amountGivenConfirmed: number; objectDescription: string; validThrough?: string; fullClaim?: Record; } export interface OffersToPlansResponse { data: OfferToPlanSummaryRecord[]; hitLimit: boolean; } export interface OfferToPlanSummaryRecord { jwtId: string; planId: string; handleId: string; issuedAt: string; offeredByDid: string; unit: string; amount: number; amountGiven: number; objectDescription: string; validThrough?: string; } export interface PlansLastUpdatedResponse { data: PlanSummaryWithPreviousClaim[]; hitLimit: boolean; } export interface PlanSummaryWithPreviousClaim { plan: PlanSummary; wrappedClaimBefore?: Record; } export interface PlanSummary { jwtId: string; handleId: string; name: string; description: string; issuerDid: string; agentDid: string; startTime: string; endTime: string; locLat?: number; locLon?: number; url?: string; }; // Phase 2: Detailed TimeSafari Notification Types export interface TimeSafariNotificationBundle { offersToPerson?: OffersResponse; offersToProjects?: OffersToPlansResponse; projectUpdates?: PlansLastUpdatedResponse; fetchTimestamp: number; success: boolean; error?: string; metadata?: { activeDid: string; fetchDurationMs: number; cachedResponses: number; networkResponses: number; }; } export interface TimeSafariUserConfig { activeDid: string; // Required for all operations lastKnownOfferId?: string; lastKnownPlanId?: string; starredPlanIds?: string[]; fetchOffersToPerson?: boolean; fetchOffersToProjects?: boolean; fetchProjectUpdates?: boolean; notificationPreferences?: { offers: boolean; projects: boolean; people: boolean; items: boolean; }; } // Enhanced notification types per specification export interface TimeSafariOfferNotification { type: 'offer'; subtype: 'new_to_me' | 'changed_to_me' | 'new_to_projects' | 'changed_to_projects' | 'new_to_favorites' | 'changed_to_favorites'; offer: OfferSummaryRecord; relevantProjects?: PlanSummary[]; notificationPriority: 'high' | 'medium' | 'low'; } export interface TimeSafariProjectNotification { type: 'project'; subtype: 'local_and_new' | 'local_and_changed' | 'with_content_and_new' | 'favorite_and_changed'; project: PlanSummary; changes?: { fields: string[]; previousValues?: Record; }; relevantOffers?: OfferSummaryRecord[]; notificationPriority: 'high' | 'medium' | 'low'; } export interface TimeSafariPersonNotification { type: 'person'; subtype: 'local_and_new' | 'local_and_changed' | 'with_content_and_new' | 'favorite_and_changed'; personDid: string; changes?: { fields: string[]; previousValues?: Record; }; relevantProjects?: PlanSummary[]; notificationPriority: 'high' | 'medium' | 'low'; } export interface TimeSafariItemNotification { type: 'item'; subtype: 'local_and_new' | 'local_and_changed' | 'favorite_and_changed'; itemId: string; changes?: { fields: string[]; previousValues?: Record; }; relevantContext?: 'project' | 'offer' | 'person'; notificationPriority: 'high' | 'medium' | 'low'; } // Union type for TimeSafari notifications export type TimeSafariNotification = | TimeSafariOfferNotification | TimeSafariProjectNotification | TimeSafariPersonNotification | TimeSafariItemNotification; // Enhanced ActiveDid Management Events export interface ActiveDidChangeEventEnhanced extends ActiveDidChangeEvent { sourceComponent: string; // 'host' | 'plugin' | 'background' | 'sync' changeReason: 'user_switch' | 'session_expired' | 'background_refresh' | 'setup'; transitionDurationMs?: number; relatedNotifications?: TimeSafariNotification[]; } // TimeSafari-specific Platform Configuration export interface TimeSafariPlatformConfig { platform: 'android' | 'ios' | 'web' | 'electron'; storageType: 'plugin-managed' | 'host-managed'; syncStrategy: 'immediate' | 'batched' | 'scheduled'; permissions: { notifications: boolean; backgroundRefresh: boolean; networkAccess: boolean; }; capabilities: { pushNotifications: boolean; backgroundTasks: boolean; identityManagement: boolean; cryptoSigning: boolean; }; } export interface ActiveDidIntegrationConfig { platform: 'android' | 'ios' | 'web' | 'electron'; storageType: 'plugin-managed' | 'host-managed'; jwtExpirationSeconds?: number; apiServer?: string; }; export interface ActiveDidChangeEvent { activeDid: string; timestamp: number; source: 'host' | 'plugin'; }; // MARK: - Phase 3: TimeSafari Background Coordination Interfaces /** * Phase 3: Extended DailyNotificationPlugin interface with TimeSafari coordination */ export interface EnhancedDailyNotificationPlugin extends DailyNotificationPlugin { // Phase 1: ActiveDid Management (already extended in parent) // Phase 3: TimeSafari Background Coordination coordinateBackgroundTasks(): Promise; handleAppLifecycleEvent(event: AppLifecycleEvent): Promise; getCoordinationStatus(): Promise; } /** * Phase 3: App lifecycle events for TimeSafari coordination */ export type AppLifecycleEvent = | 'app_background' | 'app_foreground' | 'app_resumed' | 'app_paused' | 'app_visibility_change' | 'app_hidden' | 'app_visible' | 'app_blur' | 'app_focus'; /** * Phase 3: Coordination status for debugging and monitoring */ export interface CoordinationStatus { platform: 'android' | 'ios' | 'web' | 'electron'; coordinationActive: boolean; coordinationPaused: boolean; autoSync?: boolean; appBackgrounded?: boolean; appHidden?: boolean; visibilityState?: DocumentVisibilityState; focused?: boolean; lastActiveDidChange?: number; lastCoordinationTimestamp?: number; lastAppBackgrounded?: number; lastAppForegrounded?: number; lastCoordinationSuccess?: number; lastCoordinationFailure?: number; coordinationErrors?: string[]; activeDidTracking?: string; } /** * Phase 3: PlatformServiceMixin coordination configuration */ export interface PlatformServiceMixinConfig { enableAutoCoordination?: boolean; coordinationTimeout?: number; // Max time for coordination attempts enableLifecycleEvents?: boolean; enableBackgroundSync?: boolean; enableStatePersistence?: boolean; coordinationGracePeriod?: number; // Grace period for coordination eventHandlers?: { [K in AppLifecycleEvent]?: () => Promise; }; } /** * Phase 3: WorkManager coordination data */ export interface WorkManagerCoordinationData { timesafariCoordination: boolean; coordinationTimestamp: number; activeDidTracking: string; platformCoordinationVersion?: number; coordinationTimeouts?: { maxCoordinationAge: number; maxExecutionTime: number; maxRetryAge: number; }; } /** * Phase 3: Background execution constraints */ export interface BackgroundExecutionConstraints { devicePowerMode?: 'normal' | 'low_power' | 'critical'; appForegroundState?: 'foreground' | 'background' | 'inactive'; activeDidStability?: 'stable' | 'changing' | 'unknown'; coordinationFreshness?: 'fresh' | 'stale' | 'expired'; networkAvailability?: 'cellular' | 'wifi' | 'offline'; batteryLevel?: 'high' | 'medium' | 'low' | 'critical'; } /** * Phase 3: Coordination report */ export interface CoordinationReport { success: boolean; operation: string; duration: number; constraints: BackgroundExecutionConstraints; errors?: string[]; timestamp: number; activeDid?: string; authUsed: boolean; platformSpecific?: Record; } /** * Phase 3: TimeSafari state synchronization data */ export interface TimeSafariSyncData { authenticationState: { activeDid: string; jwtExpiration?: number; tokenRefreshNeeded: boolean; }; notificationState: { lastDelivery: number; lastDeliveryId?: string; pendingDeliveries: string[]; }; backgroundTaskState: { lastBackgroundExecution: number; lastCoordinationSuccess: number; pendingCoordinationTasks: string[]; }; activeDidHistory: { changes: Array<{ did: string; timestamp: number; source: string; }>; pendingUpdates: string[]; }; } // MARK: - Phase 4: TimeSafari Notification Types /** * Phase 4: TimeSafari-specific notification interfaces */ export interface TimeSafariNotificationBundle { offersToPerson?: OffersResponse; offersToProjects?: OffersToPlansResponse; projectUpdates?: PlansLastUpdatedResponse; fetchTimestamp: number; success: boolean; error?: string; metadata?: { activeDid: string; fetchDurationMs: number; cachedResponses: number; networkResponses: number; }; } export interface TimeSafariUserConfig { activeDid: string; starredPlanIds?: string[]; lastKnownOfferId?: string; lastKnownPlanId?: string; fetchOffersToPerson?: boolean; fetchOffersToProjects?: boolean; fetchProjectUpdates?: boolean; notificationPreferences?: { offers: boolean; projects: boolean; people: boolean; items: boolean; }; } // TimeSafari notification subtype types export type TimeSafariOfferSubtype = | 'new_to_me' | 'changed_to_me' | 'new_to_projects' | 'changed_to_projects' | 'new_to_favorites' | 'changed_to_favorites'; export type TimeSafariProjectSubtype = | 'local_and_new' | 'local_and_changed' | 'with_content_and_new' | 'favorite_and_changed'; export type TimeSafariPersonSubtype = | 'local_and_new' | 'local_and_changed' | 'with_content_and_new' | 'favorite_and_changed'; export type TimeSafariItemSubtype = | 'local_and_new' | 'local_and_changed' | 'favorite_and_changed'; // Individual notification interfaces export interface TimeSafariOfferNotification { type: 'offer'; subtype: TimeSafariOfferSubtype; offer: OfferSummaryRecord; // Simplified to single type initially relevantProjects?: PlanSummary[]; notificationPriority: 'high' | 'medium' | 'low'; timestamp: number; } export interface TimeSafariProjectNotification { type: 'project'; subtype: TimeSafariProjectSubtype; project: PlanSummary; previousClaim?: any; // Previous claim data notificationPriority: 'high' | 'medium' | 'low'; timestamp: number; } export interface TimeSafariPersonNotification { type: 'person'; subtype: TimeSafariPersonSubtype; person: { did: string; name?: string; }; notificationPriority: 'high' | 'medium' | 'low'; timestamp: number; personDid: string; // Add missing property } export interface TimeSafariItemNotification { type: 'item'; subtype: TimeSafariItemSubtype; item: { id: string; name?: string; type?: string; }; notificationPriority: 'high' | 'medium' | 'low'; timestamp: number; itemId: string; // Add missing property } // Union type for all TimeSafari notification types export type TimeSafariNotificationType = | TimeSafariOfferNotification | TimeSafariProjectNotification | TimeSafariPersonNotification | TimeSafariItemNotification; // Enhanced notification interface for Phase 4 export interface EnhancedTimeSafariNotification { type: 'offer' | 'project' | 'person' | 'item'; subtype: string; notificationPriority: 'high' | 'medium' | 'low'; timestamp: number; disabled: boolean; sound: boolean; vibration: boolean; badge: boolean; priority: 'low' | 'normal' | 'high'; metadata?: { generatedAt: number; platform: string; userDid?: string; preferencesVersion?: number; fallback?: boolean; message?: string; }; // Type-specific properties (union approach) offer?: OfferSummaryRecord; project?: PlanSummary; person?: { did: any; name?: string }; item?: { id: string; name?: string; type?: string }; relevantProjects?: PlanSummary[]; previousClaim?: any; }