The Vue test app was missing the NotifyReceiver registration in AndroidManifest.xml, preventing alarm broadcasts from being delivered to the BroadcastReceiver. This caused notifications scheduled via setAlarmClock() to fire but not display. Added NotifyReceiver registration matching the working android-test-app configuration. Also includes supporting improvements: - Enhanced alarm scheduling with setAlarmClock() for Doze exemption - Unique request codes based on trigger time to prevent PendingIntent conflicts - Diagnostic methods (isAlarmScheduled, getNextAlarmTime, testAlarm) - TypeScript definitions for new methods Verified: Notification successfully fired at 09:41:00 and was displayed.
1507 lines
44 KiB
TypeScript
1507 lines
44 KiB
TypeScript
/**
|
|
* 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
|
|
*/
|
|
|
|
// Import SPI types from content-fetcher.ts
|
|
import type {
|
|
SchedulingPolicy,
|
|
JsNotificationContentFetcher
|
|
} from './types/content-fetcher';
|
|
|
|
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;
|
|
headers?: Record<string, string>;
|
|
}
|
|
|
|
|
|
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<number>;
|
|
nextNotificationTime: number | Promise<number>;
|
|
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?: Record<string, unknown>;
|
|
};
|
|
}
|
|
|
|
export interface PermissionStatus {
|
|
status?: string;
|
|
granted?: boolean;
|
|
notifications: PermissionState;
|
|
backgroundRefresh?: PermissionState; // iOS only
|
|
alert?: boolean;
|
|
badge?: boolean;
|
|
sound?: boolean;
|
|
lockScreen?: boolean;
|
|
carPlay?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Permission status result for checkPermissionStatus()
|
|
* Returns boolean flags for each permission type
|
|
*/
|
|
export interface PermissionStatusResult {
|
|
notificationsEnabled: boolean;
|
|
exactAlarmEnabled: boolean;
|
|
wakeLockEnabled: boolean;
|
|
allPermissionsGranted: boolean;
|
|
}
|
|
|
|
// Static Daily Reminder Interfaces
|
|
export interface DailyReminderOptions {
|
|
id: string;
|
|
title: string;
|
|
body: string;
|
|
time: string; // HH:mm format (e.g., "09:00")
|
|
sound?: boolean;
|
|
vibration?: boolean;
|
|
priority?: 'low' | 'normal' | 'high';
|
|
repeatDaily?: boolean;
|
|
timezone?: string;
|
|
}
|
|
|
|
export interface DailyReminderInfo {
|
|
id: string;
|
|
title: string;
|
|
body: string;
|
|
time: string;
|
|
sound: boolean;
|
|
vibration: boolean;
|
|
priority: 'low' | 'normal' | 'high';
|
|
repeatDaily: boolean;
|
|
timezone?: string;
|
|
isScheduled: boolean;
|
|
nextTriggerTime?: number;
|
|
createdAt: number;
|
|
lastTriggered?: number;
|
|
}
|
|
|
|
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' | '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<string, string>;
|
|
timeout?: number;
|
|
retryAttempts?: number;
|
|
retryDelay?: number;
|
|
callbacks: {
|
|
apiService?: string;
|
|
database?: string;
|
|
reporting?: string;
|
|
onSuccess?: (data: Record<string, unknown>) => Promise<void>;
|
|
onError?: (error: Error) => Promise<void>;
|
|
onComplete?: (result: ContentFetchResult) => Promise<void>;
|
|
};
|
|
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<string, unknown>;
|
|
}
|
|
|
|
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?: Record<string, unknown>;
|
|
timestamp: number;
|
|
contentAge: number;
|
|
error?: string;
|
|
retryCount: number;
|
|
metadata?: Record<string, unknown>;
|
|
}
|
|
|
|
// ============================================================================
|
|
// DATABASE TYPE DEFINITIONS
|
|
// ============================================================================
|
|
// These types represent the plugin's internal SQLite database schema.
|
|
// The plugin owns its database, and these types are used for TypeScript
|
|
// access through Capacitor interfaces.
|
|
//
|
|
// See: docs/DATABASE_INTERFACES.md for complete documentation
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Recurring schedule pattern stored in database
|
|
* Used to restore schedules after device reboot
|
|
*/
|
|
export interface Schedule {
|
|
/** Unique schedule identifier */
|
|
id: string;
|
|
/** Schedule type: 'fetch' for content fetching, 'notify' for notifications */
|
|
kind: 'fetch' | 'notify';
|
|
/** Cron expression (e.g., "0 9 * * *" for daily at 9 AM) */
|
|
cron?: string;
|
|
/** Clock time in HH:mm format (e.g., "09:00") */
|
|
clockTime?: string;
|
|
/** Whether schedule is enabled */
|
|
enabled: boolean;
|
|
/** Timestamp of last execution (milliseconds since epoch) */
|
|
lastRunAt?: number;
|
|
/** Timestamp of next scheduled execution (milliseconds since epoch) */
|
|
nextRunAt?: number;
|
|
/** Random jitter in milliseconds for timing variation */
|
|
jitterMs: number;
|
|
/** Backoff policy ('exp' for exponential, etc.) */
|
|
backoffPolicy: string;
|
|
/** Optional JSON state for advanced scheduling */
|
|
stateJson?: string;
|
|
}
|
|
|
|
/**
|
|
* Input type for creating a new schedule
|
|
*/
|
|
export interface CreateScheduleInput {
|
|
kind: 'fetch' | 'notify';
|
|
cron?: string;
|
|
clockTime?: string;
|
|
enabled?: boolean;
|
|
jitterMs?: number;
|
|
backoffPolicy?: string;
|
|
stateJson?: string;
|
|
}
|
|
|
|
/**
|
|
* Content cache entry with TTL
|
|
* Stores prefetched content for offline-first display
|
|
*/
|
|
export interface ContentCache {
|
|
/** Unique cache identifier */
|
|
id: string;
|
|
/** Timestamp when content was fetched (milliseconds since epoch) */
|
|
fetchedAt: number;
|
|
/** Time-to-live in seconds */
|
|
ttlSeconds: number;
|
|
/** Content payload (JSON string or base64 encoded) */
|
|
payload: string;
|
|
/** Optional metadata */
|
|
meta?: string;
|
|
}
|
|
|
|
/**
|
|
* Input type for creating a content cache entry
|
|
*/
|
|
export interface CreateContentCacheInput {
|
|
id?: string; // Auto-generated if not provided
|
|
payload: string;
|
|
ttlSeconds: number;
|
|
meta?: string;
|
|
}
|
|
|
|
/**
|
|
* Plugin configuration entry
|
|
* Stores user preferences and plugin settings
|
|
*/
|
|
export interface Config {
|
|
/** Unique configuration identifier */
|
|
id: string;
|
|
/** Optional TimeSafari DID for user-specific configs */
|
|
timesafariDid?: string;
|
|
/** Configuration type (e.g., 'plugin_setting', 'user_preference') */
|
|
configType: string;
|
|
/** Configuration key */
|
|
configKey: string;
|
|
/** Configuration value (stored as string, parsed based on configDataType) */
|
|
configValue: string;
|
|
/** Data type: 'string' | 'boolean' | 'integer' | 'long' | 'float' | 'double' | 'json' */
|
|
configDataType: string;
|
|
/** Whether value is encrypted */
|
|
isEncrypted: boolean;
|
|
/** Timestamp when config was created (milliseconds since epoch) */
|
|
createdAt: number;
|
|
/** Timestamp when config was last updated (milliseconds since epoch) */
|
|
updatedAt: number;
|
|
}
|
|
|
|
/**
|
|
* Input type for creating a configuration entry
|
|
*/
|
|
export interface CreateConfigInput {
|
|
id?: string; // Auto-generated if not provided
|
|
timesafariDid?: string;
|
|
configType: string;
|
|
configKey: string;
|
|
configValue: string;
|
|
configDataType?: string; // Defaults to 'string' if not provided
|
|
isEncrypted?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Callback configuration
|
|
* Stores callback endpoint configurations for execution after events
|
|
*/
|
|
export interface Callback {
|
|
/** Unique callback identifier */
|
|
id: string;
|
|
/** Callback type: 'http' for HTTP requests, 'local' for local handlers, 'queue' for queue */
|
|
kind: 'http' | 'local' | 'queue';
|
|
/** Target URL or identifier */
|
|
target: string;
|
|
/** Optional JSON headers for HTTP callbacks */
|
|
headersJson?: string;
|
|
/** Whether callback is enabled */
|
|
enabled: boolean;
|
|
/** Timestamp when callback was created (milliseconds since epoch) */
|
|
createdAt: number;
|
|
}
|
|
|
|
/**
|
|
* Input type for creating a callback configuration
|
|
*/
|
|
export interface CreateCallbackInput {
|
|
id: string;
|
|
kind: 'http' | 'local' | 'queue';
|
|
target: string;
|
|
headersJson?: string;
|
|
enabled?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Execution history entry
|
|
* Logs fetch/notify/callback execution for debugging and analytics
|
|
*/
|
|
export interface History {
|
|
/** Auto-incrementing history ID */
|
|
id: number;
|
|
/** Reference ID (content ID, schedule ID, etc.) */
|
|
refId: string;
|
|
/** Execution kind: 'fetch' | 'notify' | 'callback' | 'boot_recovery' */
|
|
kind: 'fetch' | 'notify' | 'callback' | 'boot_recovery';
|
|
/** Timestamp when execution occurred (milliseconds since epoch) */
|
|
occurredAt: number;
|
|
/** Execution duration in milliseconds */
|
|
durationMs?: number;
|
|
/** Outcome: 'success' | 'failure' | 'skipped_ttl' | 'circuit_open' */
|
|
outcome: string;
|
|
/** Optional JSON diagnostics */
|
|
diagJson?: string;
|
|
}
|
|
|
|
/**
|
|
* History statistics
|
|
*/
|
|
export interface HistoryStats {
|
|
/** Total number of history entries */
|
|
totalCount: number;
|
|
/** Count by outcome */
|
|
outcomes: Record<string, number>;
|
|
/** Count by kind */
|
|
kinds: Record<string, number>;
|
|
/** Most recent execution timestamp */
|
|
mostRecent?: number;
|
|
/** Oldest execution timestamp */
|
|
oldest?: number;
|
|
}
|
|
|
|
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<void>;
|
|
|
|
/**
|
|
* Configure native fetcher with API credentials (cross-platform)
|
|
*
|
|
* This method provides a cross-platform mechanism for passing API credentials
|
|
* from TypeScript/JavaScript code to native fetcher implementations. The
|
|
* configuration is passed directly without using platform-specific storage
|
|
* mechanisms, keeping the interface consistent across Android, iOS, and web.
|
|
*
|
|
* **Why this exists:**
|
|
* - Native fetchers run in background workers (WorkManager/BGTaskScheduler)
|
|
* - Background workers cannot access JavaScript variables or Capacitor bridge
|
|
* - This method provides direct injection without storage dependencies
|
|
*
|
|
* **When to call:**
|
|
* - After app startup, once API credentials are available
|
|
* - After user login/authentication, when activeDid changes
|
|
* - When API server URL changes (dev/staging/production)
|
|
*
|
|
* **Prerequisites:**
|
|
* - Native fetcher must be registered in `Application.onCreate()` (Android)
|
|
* or `AppDelegate.didFinishLaunching()` (iOS) BEFORE calling this method
|
|
* - Should be called before any background fetches occur
|
|
*
|
|
* **Thread Safety:**
|
|
* - Safe to call from any thread (main thread or background)
|
|
* - Configuration changes take effect immediately for subsequent fetches
|
|
* - Native implementations should use `volatile` fields for thread safety
|
|
เร็ *
|
|
* **Example:**
|
|
* ```typescript
|
|
* import { DailyNotification } from '@capacitor-community/daily-notification';
|
|
*
|
|
* await DailyNotification.configureNativeFetcher({
|
|
* apiBaseUrl: 'http://10.0.2.2:3000', // Android emulator → host localhost
|
|
* activeDid: 'did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F',
|
|
* jwtSecret: 'test-jwt-secret-for-development'
|
|
* });
|
|
* ```
|
|
*
|
|
* **Error Handling:**
|
|
* - Rejects if required parameters are missing
|
|
* - Rejects if no native fetcher is registered
|
|
* - Rejects if native fetcher's `configure()` throws exception
|
|
*
|
|
* @param options Configuration options:
|
|
* - `apiBaseUrl` (required): Base URL for API server.
|
|
* - Android emulator: `"http://10.0.2.2:3000"` (maps to host localhost:3000)
|
|
* - iOS simulator: `"http://localhost:3000"`
|
|
* - Production: `"https://api.timesafari.com"`
|
|
* - `activeDid` (required): Active DID for authentication.
|
|
* Format: `"did:ethr:0x..."`. Used as JWT issuer/subject.
|
|
* - `jwtToken` (required): Pre-generated JWT token (ES256K signed).
|
|
* Generated in TypeScript using TimeSafari's `createEndorserJwtForKey()` function.
|
|
* **Note**: Token should be ES256K signed (DID-based), not HS256.
|
|
*
|
|
* **Architecture Note**: JWT tokens should be generated in TypeScript using TimeSafari's
|
|
* `createEndorserJwtForKey()` function (which uses DID-based ES256K signing), then passed
|
|
* to this method. This avoids the complexity of implementing DID-based JWT signing in Java.
|
|
*
|
|
* @throws {Error} If configuration fails (missing params, no fetcher registered, etc.)
|
|
*
|
|
* @see {@link https://github.com/timesafari/daily-notification-plugin/blob/main/docs/NATIVE_FETCHER_CONFIGURATION.md | Native Fetcher Configuration Guide}
|
|
* for complete documentation and examples
|
|
*/
|
|
configureNativeFetcher(options: {
|
|
apiBaseUrl: string;
|
|
activeDid: string;
|
|
jwtToken: string; // Pre-generated JWT token (ES256K signed) from TypeScript
|
|
}): Promise<void>;
|
|
|
|
// Rolling window management
|
|
maintainRollingWindow(): Promise<void>;
|
|
getRollingWindowStats(): Promise<{
|
|
stats: string;
|
|
maintenanceNeeded: boolean;
|
|
timeUntilNextMaintenance: number;
|
|
}>;
|
|
|
|
// Exact alarm management
|
|
getExactAlarmStatus(): Promise<{
|
|
supported: boolean;
|
|
enabled: boolean;
|
|
canSchedule: boolean;
|
|
fallbackWindow: string;
|
|
}>;
|
|
requestExactAlarmPermission(): Promise<void>;
|
|
openExactAlarmSettings(): Promise<void>;
|
|
|
|
// Reboot recovery management
|
|
getRebootRecoveryStatus(): Promise<{
|
|
inProgress: boolean;
|
|
lastRecoveryTime: number;
|
|
timeSinceLastRecovery: number;
|
|
recoveryNeeded: boolean;
|
|
}>;
|
|
|
|
// Existing methods
|
|
scheduleDailyNotification(options: NotificationOptions): Promise<void>;
|
|
|
|
/**
|
|
* Check if an alarm is scheduled for a given trigger time
|
|
* @param options Object containing triggerAtMillis (number)
|
|
* @returns Object with scheduled (boolean) and triggerAtMillis (number)
|
|
*/
|
|
isAlarmScheduled(options: { triggerAtMillis: number }): Promise<{ scheduled: boolean; triggerAtMillis: number }>;
|
|
|
|
/**
|
|
* Get the next scheduled alarm time from AlarmManager
|
|
* @returns Object with scheduled (boolean) and triggerAtMillis (number | null)
|
|
*/
|
|
getNextAlarmTime(): Promise<{ scheduled: boolean; triggerAtMillis?: number }>;
|
|
|
|
/**
|
|
* Test method: Schedule an alarm to fire in a few seconds
|
|
* Useful for verifying alarm delivery works correctly
|
|
* @param options Object containing secondsFromNow (number, default: 5)
|
|
* @returns Object with scheduled (boolean), secondsFromNow (number), and triggerAtMillis (number)
|
|
*/
|
|
testAlarm(options?: { secondsFromNow?: number }): Promise<{ scheduled: boolean; secondsFromNow: number; triggerAtMillis: number }>;
|
|
|
|
getLastNotification(): Promise<NotificationResponse | null>;
|
|
cancelAllNotifications(): Promise<void>;
|
|
getNotificationStatus(): Promise<NotificationStatus>;
|
|
updateSettings(settings: NotificationSettings): Promise<void>;
|
|
getBatteryStatus(): Promise<BatteryStatus>;
|
|
requestBatteryOptimizationExemption(): Promise<void>;
|
|
setAdaptiveScheduling(options: { enabled: boolean }): Promise<void>;
|
|
getPowerState(): Promise<PowerState>;
|
|
checkPermissions(): Promise<PermissionStatus>;
|
|
requestPermissions(): Promise<PermissionStatus>;
|
|
checkPermissionStatus(): Promise<PermissionStatusResult>;
|
|
requestNotificationPermissions(): Promise<PermissionStatus>;
|
|
isChannelEnabled(channelId?: string): Promise<{ enabled: boolean; channelId: string }>;
|
|
openChannelSettings(channelId?: string): Promise<void>;
|
|
checkStatus(): Promise<NotificationStatus>;
|
|
|
|
// New dual scheduling methods
|
|
scheduleContentFetch(config: ContentFetchConfig): Promise<void>;
|
|
scheduleUserNotification(config: UserNotificationConfig): Promise<void>;
|
|
scheduleDualNotification(config: DualScheduleConfiguration): Promise<void>;
|
|
getDualScheduleStatus(): Promise<DualScheduleStatus>;
|
|
updateDualScheduleConfig(config: DualScheduleConfiguration): Promise<void>;
|
|
cancelDualSchedule(): Promise<void>;
|
|
pauseDualSchedule(): Promise<void>;
|
|
resumeDualSchedule(): Promise<void>;
|
|
|
|
// Content management methods
|
|
getContentCache(): Promise<Record<string, unknown>>;
|
|
clearContentCache(): Promise<void>;
|
|
getContentHistory(): Promise<ContentFetchResult[]>;
|
|
|
|
// Callback management methods
|
|
registerCallback(name: string, callback: (...args: unknown[]) => void): Promise<void>;
|
|
unregisterCallback(name: string): Promise<void>;
|
|
getRegisteredCallbacks(): Promise<string[]>;
|
|
|
|
// ============================================================================
|
|
// DATABASE ACCESS METHODS
|
|
// ============================================================================
|
|
// These methods provide TypeScript/JavaScript access to the plugin's internal
|
|
// SQLite database. Since the plugin owns its database, the host app/webview
|
|
// accesses data through these Capacitor interfaces.
|
|
//
|
|
// Usage Pattern:
|
|
// import { DailyNotification } from '@capacitor-community/daily-notification';
|
|
// const schedules = await DailyNotification.getSchedules({ kind: 'notify' });
|
|
//
|
|
// See: docs/DATABASE_INTERFACES.md for complete documentation
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Get all schedules matching optional filters
|
|
*
|
|
* @param options Optional filters:
|
|
* - kind: Filter by schedule type ('fetch' | 'notify')
|
|
* - enabled: Filter by enabled status (true = only enabled, false = only disabled, undefined = all)
|
|
* @returns Promise resolving to object with schedules array: { schedules: Schedule[] }
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* // Get all enabled notification schedules
|
|
* const result = await DailyNotification.getSchedules({
|
|
* kind: 'notify',
|
|
* enabled: true
|
|
* });
|
|
* const schedules = result.schedules;
|
|
* ```
|
|
*/
|
|
getSchedules(options?: { kind?: 'fetch' | 'notify'; enabled?: boolean }): Promise<{ schedules: Schedule[] }>;
|
|
|
|
/**
|
|
* Get a single schedule by ID
|
|
*
|
|
* @param id Schedule ID
|
|
* @returns Promise resolving to Schedule object or null if not found
|
|
*/
|
|
getSchedule(id: string): Promise<Schedule | null>;
|
|
|
|
/**
|
|
* Create a new recurring schedule
|
|
*
|
|
* @param schedule Schedule configuration
|
|
* @returns Promise resolving to created Schedule object
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* const schedule = await DailyNotification.createSchedule({
|
|
* kind: 'notify',
|
|
* cron: '0 9 * * *', // Daily at 9 AM
|
|
* enabled: true
|
|
* });
|
|
* ```
|
|
*/
|
|
createSchedule(schedule: CreateScheduleInput): Promise<Schedule>;
|
|
|
|
/**
|
|
* Update an existing schedule
|
|
*
|
|
* @param id Schedule ID
|
|
* @param updates Partial schedule updates
|
|
* @returns Promise resolving to updated Schedule object
|
|
*/
|
|
updateSchedule(id: string, updates: Partial<Schedule>): Promise<Schedule>;
|
|
|
|
/**
|
|
* Delete a schedule
|
|
*
|
|
* @param id Schedule ID
|
|
* @returns Promise resolving when deletion completes
|
|
*/
|
|
deleteSchedule(id: string): Promise<void>;
|
|
|
|
/**
|
|
* Enable or disable a schedule
|
|
*
|
|
* @param id Schedule ID
|
|
* @param enabled Enable state
|
|
* @returns Promise resolving when update completes
|
|
*/
|
|
enableSchedule(id: string, enabled: boolean): Promise<void>;
|
|
|
|
/**
|
|
* Calculate next run time from a cron expression or clockTime
|
|
*
|
|
* @param schedule Cron expression (e.g., "0 9 * * *") or clockTime (e.g., "09:00")
|
|
* @returns Promise resolving to timestamp (milliseconds since epoch)
|
|
*/
|
|
calculateNextRunTime(schedule: string): Promise<number>;
|
|
|
|
/**
|
|
* Get content cache by ID or latest cache
|
|
*
|
|
* @param options Optional filters:
|
|
* - id: Specific cache ID (if not provided, returns latest)
|
|
* @returns Promise resolving to ContentCache object or null
|
|
*/
|
|
getContentCacheById(options?: { id?: string }): Promise<ContentCache | null>;
|
|
|
|
/**
|
|
* Get the latest content cache entry
|
|
*
|
|
* @returns Promise resolving to latest ContentCache object or null
|
|
*/
|
|
getLatestContentCache(): Promise<ContentCache | null>;
|
|
|
|
/**
|
|
* Get content cache history
|
|
*
|
|
* @param limit Maximum number of entries to return (default: 10)
|
|
* @returns Promise resolving to object with history array: { history: ContentCache[] }
|
|
*/
|
|
getContentCacheHistory(limit?: number): Promise<{ history: ContentCache[] }>;
|
|
|
|
/**
|
|
* Save content to cache
|
|
*
|
|
* @param content Content cache data
|
|
* @returns Promise resolving to saved ContentCache object
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* await DailyNotification.saveContentCache({
|
|
* id: 'cache_123',
|
|
* payload: JSON.stringify({ title: 'Hello', body: 'World' }),
|
|
* ttlSeconds: 3600,
|
|
* meta: 'fetched_from_api'
|
|
* });
|
|
* ```
|
|
*/
|
|
saveContentCache(content: CreateContentCacheInput): Promise<ContentCache>;
|
|
|
|
/**
|
|
* Clear content cache entries
|
|
*
|
|
* @param options Optional filters:
|
|
* - olderThan: Only clear entries older than this timestamp (milliseconds)
|
|
* @returns Promise resolving when cleanup completes
|
|
*/
|
|
clearContentCacheEntries(options?: { olderThan?: number }): Promise<void>;
|
|
|
|
/**
|
|
* Get configuration value
|
|
*
|
|
* @param key Configuration key
|
|
* @param options Optional filters:
|
|
* - timesafariDid: Filter by TimeSafari DID
|
|
* @returns Promise resolving to Config object or null
|
|
*/
|
|
getConfig(key: string, options?: { timesafariDid?: string }): Promise<Config | null>;
|
|
|
|
/**
|
|
* Get all configurations matching filters
|
|
*
|
|
* @param options Optional filters:
|
|
* - timesafariDid: Filter by TimeSafari DID
|
|
* - configType: Filter by configuration type
|
|
* @returns Promise resolving to array of Config objects
|
|
*/
|
|
getAllConfigs(options?: { timesafariDid?: string; configType?: string }): Promise<{ configs: Config[] }>;
|
|
|
|
/**
|
|
* Set configuration value
|
|
*
|
|
* @param config Configuration data
|
|
* @returns Promise resolving to saved Config object
|
|
*/
|
|
setConfig(config: CreateConfigInput): Promise<Config>;
|
|
|
|
/**
|
|
* Update configuration value
|
|
*
|
|
* @param key Configuration key
|
|
* @param value New value (will be stringified based on dataType)
|
|
* @param options Optional filters:
|
|
* - timesafariDid: Filter by TimeSafari DID
|
|
* @returns Promise resolving to updated Config object
|
|
*/
|
|
updateConfig(key: string, value: string, options?: { timesafariDid?: string }): Promise<Config>;
|
|
|
|
/**
|
|
* Delete configuration
|
|
*
|
|
* @param key Configuration key
|
|
* @param options Optional filters:
|
|
* - timesafariDid: Filter by TimeSafari DID
|
|
* @returns Promise resolving when deletion completes
|
|
*/
|
|
deleteConfig(key: string, options?: { timesafariDid?: string }): Promise<void>;
|
|
|
|
/**
|
|
* Get all callbacks matching filters
|
|
*
|
|
* @param options Optional filters:
|
|
* - enabled: Filter by enabled status
|
|
* @returns Promise resolving to object with callbacks array: { callbacks: Callback[] }
|
|
*/
|
|
getCallbacks(options?: { enabled?: boolean }): Promise<{ callbacks: Callback[] }>;
|
|
|
|
/**
|
|
* Get a single callback by ID
|
|
*
|
|
* @param id Callback ID
|
|
* @returns Promise resolving to Callback object or null
|
|
*/
|
|
getCallback(id: string): Promise<Callback | null>;
|
|
|
|
/**
|
|
* Register a new callback
|
|
*
|
|
* @param callback Callback configuration
|
|
* @returns Promise resolving to created Callback object
|
|
*/
|
|
registerCallbackConfig(callback: CreateCallbackInput): Promise<Callback>;
|
|
|
|
/**
|
|
* Update an existing callback
|
|
*
|
|
* @param id Callback ID
|
|
* @param updates Partial callback updates
|
|
* @returns Promise resolving to updated Callback object
|
|
*/
|
|
updateCallback(id: string, updates: Partial<Callback>): Promise<Callback>;
|
|
|
|
/**
|
|
* Delete a callback
|
|
*
|
|
* @param id Callback ID
|
|
* @returns Promise resolving when deletion completes
|
|
*/
|
|
deleteCallback(id: string): Promise<void>;
|
|
|
|
/**
|
|
* Enable or disable a callback
|
|
*
|
|
* @param id Callback ID
|
|
* @param enabled Enable state
|
|
* @returns Promise resolving when update completes
|
|
*/
|
|
enableCallback(id: string, enabled: boolean): Promise<void>;
|
|
|
|
/**
|
|
* Get execution history
|
|
*
|
|
* @param options Optional filters:
|
|
* - since: Only return entries after this timestamp (milliseconds)
|
|
* - kind: Filter by execution kind ('fetch' | 'notify' | 'callback')
|
|
* - limit: Maximum number of entries to return (default: 50)
|
|
* @returns Promise resolving to object with history array: { history: History[] }
|
|
*/
|
|
getHistory(options?: {
|
|
since?: number;
|
|
kind?: 'fetch' | 'notify' | 'callback';
|
|
limit?: number;
|
|
}): Promise<{ history: History[] }>;
|
|
|
|
/**
|
|
* Get history statistics
|
|
*
|
|
* @returns Promise resolving to history statistics
|
|
*/
|
|
getHistoryStats(): Promise<HistoryStats>;
|
|
|
|
// Phase 1: ActiveDid Management Methods (Option A Implementation)
|
|
setActiveDidFromHost(activeDid: string): Promise<void>;
|
|
onActiveDidChange(callback: (newActiveDid: string) => Promise<void>): void;
|
|
refreshAuthenticationForNewIdentity(activeDid: string): Promise<void>;
|
|
clearCacheForNewIdentity(): Promise<void>;
|
|
updateBackgroundTaskIdentity(activeDid: string): Promise<void>;
|
|
|
|
// Starred Plans Management Methods
|
|
/**
|
|
* Update starred plan IDs from host application
|
|
*
|
|
* This allows the TimeSafari app to dynamically update the list of starred
|
|
* project IDs when users star or unstar projects. The IDs are stored persistently
|
|
* and used for prefetch operations that query for starred project updates.
|
|
*
|
|
* @param options Contains:
|
|
* - planIds: string[] - Array of starred plan handle IDs
|
|
* @returns Promise with success status and plan count
|
|
*/
|
|
updateStarredPlans(options: { planIds: string[] }): Promise<{
|
|
success: boolean;
|
|
planIdsCount: number;
|
|
updatedAt: number;
|
|
}>;
|
|
|
|
/**
|
|
* Get current starred plan IDs
|
|
*
|
|
* Returns the currently stored starred plan IDs from SharedPreferences.
|
|
* This is useful for the host app to verify what IDs are stored.
|
|
*
|
|
* @returns Promise with current starred plan IDs
|
|
*/
|
|
getStarredPlans(): Promise<{
|
|
planIds: string[];
|
|
count: number;
|
|
updatedAt: number;
|
|
}>;
|
|
|
|
// Content Fetching Methods
|
|
/**
|
|
* Trigger an immediate standalone fetch for content updates
|
|
*
|
|
* This method allows manual triggering of content fetches independently of
|
|
* scheduled notifications. Useful for on-demand content refresh, cache warming,
|
|
* or background sync operations.
|
|
*
|
|
* @returns Promise with success status and message
|
|
*/
|
|
triggerImmediateFetch(): Promise<{
|
|
success: boolean;
|
|
message: string;
|
|
}>;
|
|
|
|
// Static Daily Reminder Methods
|
|
/**
|
|
* Schedule a simple daily reminder notification
|
|
* No network content required - just static text
|
|
*/
|
|
scheduleDailyReminder(options: DailyReminderOptions): Promise<void>;
|
|
|
|
/**
|
|
* Cancel a daily reminder notification
|
|
*/
|
|
cancelDailyReminder(reminderId: string): Promise<void>;
|
|
|
|
/**
|
|
* Get all scheduled daily reminders
|
|
*/
|
|
getScheduledReminders(): Promise<DailyReminderInfo[]>;
|
|
|
|
/**
|
|
* Update an existing daily reminder
|
|
*/
|
|
updateDailyReminder(reminderId: string, options: DailyReminderOptions): Promise<void>;
|
|
|
|
// Integration Point Refactor (PR1): SPI Registration Methods
|
|
|
|
/**
|
|
* Set JavaScript content fetcher for foreground operations
|
|
*
|
|
* NOTE: This is a stub in PR1. Full implementation coming in PR3.
|
|
* JS fetchers are ONLY used for foreground/manual refresh.
|
|
* Background workers must use native fetcher.
|
|
*
|
|
* @param fetcher JavaScript fetcher implementation
|
|
*/
|
|
setJsContentFetcher(fetcher: JsNotificationContentFetcher): void;
|
|
|
|
/**
|
|
* Enable or disable native fetcher
|
|
*
|
|
* Native fetcher is required for background workers. If disabled,
|
|
* background fetches will fail gracefully.
|
|
*
|
|
* @param enable Whether to enable native fetcher
|
|
* @returns Promise with enabled and registered status
|
|
*/
|
|
enableNativeFetcher(enable: boolean): Promise<{
|
|
enabled: boolean;
|
|
registered: boolean;
|
|
}>;
|
|
|
|
/**
|
|
* Set scheduling policy configuration
|
|
*
|
|
* Updates the scheduling policy used by the plugin for retry backoff,
|
|
* prefetch timing, deduplication, and cache TTL.
|
|
*
|
|
* @param policy Scheduling policy configuration
|
|
*/
|
|
setPolicy(policy: SchedulingPolicy): Promise<void>;
|
|
}
|
|
|
|
// 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<string, unknown>;
|
|
}
|
|
|
|
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<string, unknown>;
|
|
}
|
|
|
|
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<string, unknown>;
|
|
};
|
|
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<string, unknown>;
|
|
};
|
|
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<string, unknown>;
|
|
};
|
|
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' | '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' | '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<void>;
|
|
handleAppLifecycleEvent(event: AppLifecycleEvent): Promise<void>;
|
|
getCoordinationStatus(): Promise<CoordinationStatus>;
|
|
}
|
|
|
|
/**
|
|
* 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' | '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<void>;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 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<string, unknown>;
|
|
}
|
|
|
|
/**
|
|
* 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?: Record<string, unknown>; // 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: string; name?: string };
|
|
item?: { id: string; name?: string; type?: string };
|
|
relevantProjects?: PlanSummary[];
|
|
previousClaim?: Record<string, unknown>;
|
|
} |