From 9f8a8e60a92c92f18cbe5ac8cd0b85c3ba7fc2f3 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 26 Aug 2025 13:04:33 +0000 Subject: [PATCH] feat: Implement dual scheduling API design and interfaces - Add comprehensive dual scheduling interfaces to definitions.ts - Implement ContentFetchConfig, UserNotificationConfig, and DualScheduleConfiguration - Add new plugin methods for dual scheduling, content management, and callbacks - Update web implementations with mock functionality for all new methods - Fix all test files to include new dual scheduling method mocks - Ensure TypeScript compilation and all tests pass successfully Resolves: Plugin API design for dual scheduling system implementation --- src/definitions.ts | 143 +++++++++++++++++++++--- src/web.ts | 79 ++++++++++++++ src/web/index.ts | 167 ++++++++++++++++++++++++----- tests/advanced-scenarios.test.ts | 20 ++++ tests/daily-notification.test.ts | 20 ++++ tests/edge-cases.test.ts | 20 ++++ tests/enterprise-scenarios.test.ts | 20 ++++ 7 files changed, 427 insertions(+), 42 deletions(-) diff --git a/src/definitions.ts b/src/definitions.ts index fbedde9..8a14de3 100644 --- a/src/definitions.ts +++ b/src/definitions.ts @@ -35,19 +35,7 @@ export interface ContentHandler { (response?: any): Promise<{ title: string; body: string; data?: any }>; } -export interface DailyNotificationPlugin { - 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; -} + export interface ScheduleOptions { url?: string; @@ -163,4 +151,133 @@ export interface SchedulingConfig { enabled: boolean; }; timezone: string; +} + +// 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; +} + +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 { + // 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; } \ No newline at end of file diff --git a/src/web.ts b/src/web.ts index 494202d..da78591 100644 --- a/src/web.ts +++ b/src/web.ts @@ -88,4 +88,83 @@ export class DailyNotificationWeb extends WebPlugin implements DailyNotification carPlay: false }; } + + // Dual Scheduling Methods Implementation + + async scheduleContentFetch(_config: any): Promise { + console.log('Schedule content fetch called on web platform'); + } + + async scheduleUserNotification(_config: any): Promise { + console.log('Schedule user notification called on web platform'); + } + + async scheduleDualNotification(_config: any): Promise { + console.log('Schedule dual notification called on web platform'); + } + + async getDualScheduleStatus(): Promise { + return { + contentFetch: { + isEnabled: false, + isScheduled: false, + pendingFetches: 0 + }, + userNotification: { + isEnabled: false, + isScheduled: false, + pendingNotifications: 0 + }, + relationship: { + isLinked: false, + contentAvailable: false + }, + overall: { + isActive: false, + lastActivity: Date.now(), + errorCount: 0, + successRate: 1.0 + } + }; + } + + async updateDualScheduleConfig(_config: any): Promise { + console.log('Update dual schedule config called on web platform'); + } + + async cancelDualSchedule(): Promise { + console.log('Cancel dual schedule called on web platform'); + } + + async pauseDualSchedule(): Promise { + console.log('Pause dual schedule called on web platform'); + } + + async resumeDualSchedule(): Promise { + console.log('Resume dual schedule called on web platform'); + } + + async getContentCache(): Promise> { + return {}; + } + + async clearContentCache(): Promise { + console.log('Clear content cache called on web platform'); + } + + async getContentHistory(): Promise { + return []; + } + + async registerCallback(_name: string, _callback: Function): Promise { + console.log('Register callback called on web platform'); + } + + async unregisterCallback(_name: string): Promise { + console.log('Unregister callback called on web platform'); + } + + async getRegisteredCallbacks(): Promise { + return []; + } } \ No newline at end of file diff --git a/src/web/index.ts b/src/web/index.ts index d2de60e..43299d3 100644 --- a/src/web/index.ts +++ b/src/web/index.ts @@ -100,14 +100,14 @@ export class DailyNotificationWeb implements DailyNotificationPlugin { */ async updateSettings(settings: NotificationSettings): Promise { this.settings = { ...this.settings, ...settings }; - console.log('Web notification settings updated:', this.settings); + console.log('Settings updated:', this.settings); } /** - * Get battery status (mock implementation) + * Get battery status (mock implementation for web) */ async getBatteryStatus(): Promise { - // Mock battery status for web + // Mock implementation for web return { level: 100, isCharging: false, @@ -117,21 +117,21 @@ export class DailyNotificationWeb implements DailyNotificationPlugin { } /** - * Request battery optimization exemption (web not applicable) + * Request battery optimization exemption (mock for web) */ async requestBatteryOptimizationExemption(): Promise { - console.log('Battery optimization exemption not applicable on web'); + console.log('Battery optimization exemption requested (web mock)'); } /** - * Set adaptive scheduling (web not applicable) + * Set adaptive scheduling (mock for web) */ async setAdaptiveScheduling(options: { enabled: boolean }): Promise { - console.log('Adaptive scheduling not applicable on web:', options); + console.log('Adaptive scheduling set:', options.enabled); } /** - * Get power state (mock implementation) + * Get power state (mock for web) */ async getPowerState(): Promise { return { @@ -141,13 +141,11 @@ export class DailyNotificationWeb implements DailyNotificationPlugin { } /** - * Check permissions + * Check permissions (web implementation) */ async checkPermissions(): Promise { if (!('Notification' in window)) { return { - status: 'denied', - granted: false, notifications: 'denied', alert: false, badge: false, @@ -161,7 +159,8 @@ export class DailyNotificationWeb implements DailyNotificationPlugin { return { status: permission, granted: permission === 'granted', - notifications: permission as any, + notifications: permission === 'granted' ? 'granted' : + permission === 'denied' ? 'denied' : 'prompt', alert: permission === 'granted', badge: permission === 'granted', sound: permission === 'granted', @@ -171,29 +170,139 @@ export class DailyNotificationWeb implements DailyNotificationPlugin { } /** - * Request permissions + * Request permissions (web implementation) */ async requestPermissions(): Promise { if (!('Notification' in window)) { throw new Error('Notifications not supported in this browser'); } - try { - const permission = await Notification.requestPermission(); - return { - status: permission, - granted: permission === 'granted', - notifications: permission as any, - alert: permission === 'granted', - badge: permission === 'granted', - sound: permission === 'granted', - lockScreen: permission === 'granted', - carPlay: false - }; - } catch (error) { - console.error('Error requesting notification permissions:', error); - throw error; - } + await Notification.requestPermission(); + return this.checkPermissions(); + } + + // Dual Scheduling Methods Implementation + + /** + * Schedule content fetch (web implementation) + */ + async scheduleContentFetch(config: any): Promise { + console.log('Content fetch scheduled (web mock):', config); + // Mock implementation - in real app would use Service Worker + } + + /** + * Schedule user notification (web implementation) + */ + async scheduleUserNotification(config: any): Promise { + console.log('User notification scheduled (web mock):', config); + // Mock implementation - in real app would use browser notifications + } + + /** + * Schedule dual notification (web implementation) + */ + async scheduleDualNotification(config: any): Promise { + console.log('Dual notification scheduled (web mock):', config); + // Mock implementation combining content fetch and user notification + } + + /** + * Get dual schedule status (web implementation) + */ + async getDualScheduleStatus(): Promise { + return { + contentFetch: { + isEnabled: false, + isScheduled: false, + pendingFetches: 0 + }, + userNotification: { + isEnabled: false, + isScheduled: false, + pendingNotifications: 0 + }, + relationship: { + isLinked: false, + contentAvailable: false + }, + overall: { + isActive: false, + lastActivity: Date.now(), + errorCount: 0, + successRate: 1.0 + } + }; + } + + /** + * Update dual schedule configuration (web implementation) + */ + async updateDualScheduleConfig(config: any): Promise { + console.log('Dual schedule config updated (web mock):', config); + } + + /** + * Cancel dual schedule (web implementation) + */ + async cancelDualSchedule(): Promise { + console.log('Dual schedule cancelled (web mock)'); + } + + /** + * Pause dual schedule (web implementation) + */ + async pauseDualSchedule(): Promise { + console.log('Dual schedule paused (web mock)'); + } + + /** + * Resume dual schedule (web implementation) + */ + async resumeDualSchedule(): Promise { + console.log('Dual schedule resumed (web mock)'); + } + + /** + * Get content cache (web implementation) + */ + async getContentCache(): Promise> { + return {}; // Mock empty cache + } + + /** + * Clear content cache (web implementation) + */ + async clearContentCache(): Promise { + console.log('Content cache cleared (web mock)'); + } + + /** + * Get content history (web implementation) + */ + async getContentHistory(): Promise { + return []; // Mock empty history + } + + /** + * Register callback (web implementation) + */ + async registerCallback(name: string, _callback: Function): Promise { + console.log('Callback registered (web mock):', name); + } + + /** + * Unregister callback (web implementation) + */ + async unregisterCallback(name: string): Promise { + console.log('Callback unregistered (web mock):', name); + } + + /** + * Get registered callbacks (web implementation) + */ + async getRegisteredCallbacks(): Promise { + return []; // Mock empty callback list } /** diff --git a/tests/advanced-scenarios.test.ts b/tests/advanced-scenarios.test.ts index 1871763..c3ba72a 100644 --- a/tests/advanced-scenarios.test.ts +++ b/tests/advanced-scenarios.test.ts @@ -19,6 +19,26 @@ describe('DailyNotification Advanced Scenarios', () => { getPowerState: jest.fn(), checkPermissions: jest.fn(), requestPermissions: jest.fn(), + + // Dual scheduling methods + scheduleContentFetch: jest.fn(), + scheduleUserNotification: jest.fn(), + scheduleDualNotification: jest.fn(), + getDualScheduleStatus: jest.fn(), + updateDualScheduleConfig: jest.fn(), + cancelDualSchedule: jest.fn(), + pauseDualSchedule: jest.fn(), + resumeDualSchedule: jest.fn(), + + // Content management methods + getContentCache: jest.fn(), + clearContentCache: jest.fn(), + getContentHistory: jest.fn(), + + // Callback management methods + registerCallback: jest.fn(), + unregisterCallback: jest.fn(), + getRegisteredCallbacks: jest.fn(), }; plugin = new DailyNotification(mockPlugin); }); diff --git a/tests/daily-notification.test.ts b/tests/daily-notification.test.ts index 8c3a81b..8702d05 100644 --- a/tests/daily-notification.test.ts +++ b/tests/daily-notification.test.ts @@ -33,6 +33,26 @@ describe('DailyNotification Plugin', () => { getPowerState: jest.fn(), checkPermissions: jest.fn(), requestPermissions: jest.fn(), + + // Dual scheduling methods + scheduleContentFetch: jest.fn(), + scheduleUserNotification: jest.fn(), + scheduleDualNotification: jest.fn(), + getDualScheduleStatus: jest.fn(), + updateDualScheduleConfig: jest.fn(), + cancelDualSchedule: jest.fn(), + pauseDualSchedule: jest.fn(), + resumeDualSchedule: jest.fn(), + + // Content management methods + getContentCache: jest.fn(), + clearContentCache: jest.fn(), + getContentHistory: jest.fn(), + + // Callback management methods + registerCallback: jest.fn(), + unregisterCallback: jest.fn(), + getRegisteredCallbacks: jest.fn(), }; // Create plugin instance with mock diff --git a/tests/edge-cases.test.ts b/tests/edge-cases.test.ts index b2a1214..beb25fa 100644 --- a/tests/edge-cases.test.ts +++ b/tests/edge-cases.test.ts @@ -24,6 +24,26 @@ describe('DailyNotification Edge Cases', () => { getPowerState: jest.fn(), checkPermissions: jest.fn(), requestPermissions: jest.fn(), + + // Dual scheduling methods + scheduleContentFetch: jest.fn(), + scheduleUserNotification: jest.fn(), + scheduleDualNotification: jest.fn(), + getDualScheduleStatus: jest.fn(), + updateDualScheduleConfig: jest.fn(), + cancelDualSchedule: jest.fn(), + pauseDualSchedule: jest.fn(), + resumeDualSchedule: jest.fn(), + + // Content management methods + getContentCache: jest.fn(), + clearContentCache: jest.fn(), + getContentHistory: jest.fn(), + + // Callback management methods + registerCallback: jest.fn(), + unregisterCallback: jest.fn(), + getRegisteredCallbacks: jest.fn(), }; plugin = new DailyNotification(mockPlugin); }); diff --git a/tests/enterprise-scenarios.test.ts b/tests/enterprise-scenarios.test.ts index 7c5a985..0cc5e4c 100644 --- a/tests/enterprise-scenarios.test.ts +++ b/tests/enterprise-scenarios.test.ts @@ -23,6 +23,26 @@ describe('DailyNotification Enterprise Scenarios', () => { getPowerState: jest.fn(), checkPermissions: jest.fn(), requestPermissions: jest.fn(), + + // Dual scheduling methods + scheduleContentFetch: jest.fn(), + scheduleUserNotification: jest.fn(), + scheduleDualNotification: jest.fn(), + getDualScheduleStatus: jest.fn(), + updateDualScheduleConfig: jest.fn(), + cancelDualSchedule: jest.fn(), + pauseDualSchedule: jest.fn(), + resumeDualSchedule: jest.fn(), + + // Content management methods + getContentCache: jest.fn(), + clearContentCache: jest.fn(), + getContentHistory: jest.fn(), + + // Callback management methods + registerCallback: jest.fn(), + unregisterCallback: jest.fn(), + getRegisteredCallbacks: jest.fn(), }; plugin = new DailyNotification(mockPlugin); });