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
This commit is contained in:
@@ -35,19 +35,7 @@ export interface ContentHandler {
|
||||
(response?: any): Promise<{ title: string; body: string; data?: any }>;
|
||||
}
|
||||
|
||||
export interface DailyNotificationPlugin {
|
||||
scheduleDailyNotification(options: NotificationOptions | ScheduleOptions): Promise<void>;
|
||||
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>;
|
||||
}
|
||||
|
||||
|
||||
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<string, string>;
|
||||
timeout?: number;
|
||||
retryAttempts?: number;
|
||||
retryDelay?: number;
|
||||
callbacks: {
|
||||
apiService?: string;
|
||||
database?: string;
|
||||
reporting?: string;
|
||||
onSuccess?: (data: any) => Promise<void>;
|
||||
onError?: (error: Error) => Promise<void>;
|
||||
onComplete?: (result: ContentFetchResult) => Promise<void>;
|
||||
};
|
||||
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<string, any>;
|
||||
}
|
||||
|
||||
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<string, any>;
|
||||
}
|
||||
|
||||
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<void>;
|
||||
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>;
|
||||
|
||||
// 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, any>>;
|
||||
clearContentCache(): Promise<void>;
|
||||
getContentHistory(): Promise<ContentFetchResult[]>;
|
||||
|
||||
// Callback management methods
|
||||
registerCallback(name: string, callback: Function): Promise<void>;
|
||||
unregisterCallback(name: string): Promise<void>;
|
||||
getRegisteredCallbacks(): Promise<string[]>;
|
||||
}
|
||||
79
src/web.ts
79
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<void> {
|
||||
console.log('Schedule content fetch called on web platform');
|
||||
}
|
||||
|
||||
async scheduleUserNotification(_config: any): Promise<void> {
|
||||
console.log('Schedule user notification called on web platform');
|
||||
}
|
||||
|
||||
async scheduleDualNotification(_config: any): Promise<void> {
|
||||
console.log('Schedule dual notification called on web platform');
|
||||
}
|
||||
|
||||
async getDualScheduleStatus(): Promise<any> {
|
||||
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<void> {
|
||||
console.log('Update dual schedule config called on web platform');
|
||||
}
|
||||
|
||||
async cancelDualSchedule(): Promise<void> {
|
||||
console.log('Cancel dual schedule called on web platform');
|
||||
}
|
||||
|
||||
async pauseDualSchedule(): Promise<void> {
|
||||
console.log('Pause dual schedule called on web platform');
|
||||
}
|
||||
|
||||
async resumeDualSchedule(): Promise<void> {
|
||||
console.log('Resume dual schedule called on web platform');
|
||||
}
|
||||
|
||||
async getContentCache(): Promise<Record<string, any>> {
|
||||
return {};
|
||||
}
|
||||
|
||||
async clearContentCache(): Promise<void> {
|
||||
console.log('Clear content cache called on web platform');
|
||||
}
|
||||
|
||||
async getContentHistory(): Promise<any[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
async registerCallback(_name: string, _callback: Function): Promise<void> {
|
||||
console.log('Register callback called on web platform');
|
||||
}
|
||||
|
||||
async unregisterCallback(_name: string): Promise<void> {
|
||||
console.log('Unregister callback called on web platform');
|
||||
}
|
||||
|
||||
async getRegisteredCallbacks(): Promise<string[]> {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
167
src/web/index.ts
167
src/web/index.ts
@@ -100,14 +100,14 @@ export class DailyNotificationWeb implements DailyNotificationPlugin {
|
||||
*/
|
||||
async updateSettings(settings: NotificationSettings): Promise<void> {
|
||||
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<BatteryStatus> {
|
||||
// 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<void> {
|
||||
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<void> {
|
||||
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<PowerState> {
|
||||
return {
|
||||
@@ -141,13 +141,11 @@ export class DailyNotificationWeb implements DailyNotificationPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check permissions
|
||||
* Check permissions (web implementation)
|
||||
*/
|
||||
async checkPermissions(): Promise<PermissionStatus> {
|
||||
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<PermissionStatus> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<any> {
|
||||
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<void> {
|
||||
console.log('Dual schedule config updated (web mock):', config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel dual schedule (web implementation)
|
||||
*/
|
||||
async cancelDualSchedule(): Promise<void> {
|
||||
console.log('Dual schedule cancelled (web mock)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause dual schedule (web implementation)
|
||||
*/
|
||||
async pauseDualSchedule(): Promise<void> {
|
||||
console.log('Dual schedule paused (web mock)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume dual schedule (web implementation)
|
||||
*/
|
||||
async resumeDualSchedule(): Promise<void> {
|
||||
console.log('Dual schedule resumed (web mock)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content cache (web implementation)
|
||||
*/
|
||||
async getContentCache(): Promise<Record<string, any>> {
|
||||
return {}; // Mock empty cache
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear content cache (web implementation)
|
||||
*/
|
||||
async clearContentCache(): Promise<void> {
|
||||
console.log('Content cache cleared (web mock)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content history (web implementation)
|
||||
*/
|
||||
async getContentHistory(): Promise<any[]> {
|
||||
return []; // Mock empty history
|
||||
}
|
||||
|
||||
/**
|
||||
* Register callback (web implementation)
|
||||
*/
|
||||
async registerCallback(name: string, _callback: Function): Promise<void> {
|
||||
console.log('Callback registered (web mock):', name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister callback (web implementation)
|
||||
*/
|
||||
async unregisterCallback(name: string): Promise<void> {
|
||||
console.log('Callback unregistered (web mock):', name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get registered callbacks (web implementation)
|
||||
*/
|
||||
async getRegisteredCallbacks(): Promise<string[]> {
|
||||
return []; // Mock empty callback list
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user