fix: Resolve TypeScript compilation errors and test environment issues
- Update interface definitions to match test requirements - Add missing methods to DailyNotificationPlugin interface - Fix ContentHandler signature compatibility - Switch Jest test environment from node to jsdom - Install required jsdom dependencies - Update mock plugin objects with all required methods - Fix timestamp type mismatches in tests
This commit is contained in:
322
src/web/index.ts
322
src/web/index.ts
@@ -1,107 +1,297 @@
|
||||
/**
|
||||
* DailyNotificationWeb implementation
|
||||
* Web platform implementation for the Daily Notification Plugin
|
||||
* DailyNotification Web Implementation
|
||||
*
|
||||
* Web platform implementation with proper mock functionality
|
||||
* Aligned with updated interface definitions
|
||||
*
|
||||
* @author Matthew Raymer
|
||||
* @version 2.0.0
|
||||
*/
|
||||
|
||||
import { WebPlugin } from '@capacitor/core';
|
||||
import type { DailyNotificationPlugin, NotificationOptions, NotificationSettings, NotificationResponse, NotificationStatus, BatteryStatus, PowerState } from '../definitions';
|
||||
import { DailyNotificationPlugin, NotificationOptions, NotificationResponse, NotificationStatus, NotificationSettings, BatteryStatus, PowerState, PermissionStatus } from '../definitions';
|
||||
|
||||
export class DailyNotificationWeb extends WebPlugin implements DailyNotificationPlugin {
|
||||
private nextNotificationTime: number = 0;
|
||||
private lastNotificationTime: number = 0;
|
||||
private settings: NotificationSettings & { adaptiveScheduling?: boolean } = {};
|
||||
export class DailyNotificationWeb implements DailyNotificationPlugin {
|
||||
private notifications: Map<string, NotificationResponse> = new Map();
|
||||
private settings: NotificationSettings = {
|
||||
sound: true,
|
||||
priority: 'default',
|
||||
timezone: 'UTC'
|
||||
};
|
||||
private scheduledNotifications: Set<string> = new Set();
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
name: 'DailyNotification',
|
||||
platforms: ['web']
|
||||
});
|
||||
}
|
||||
|
||||
async initialize(options: NotificationOptions): Promise<void> {
|
||||
// Web implementation - store settings
|
||||
this.settings = { ...options };
|
||||
}
|
||||
|
||||
async scheduleDailyNotification(options: NotificationOptions | any): Promise<void> {
|
||||
// Web implementation using browser notifications
|
||||
if (!('Notification' in window)) {
|
||||
throw new Error('This browser does not support notifications');
|
||||
/**
|
||||
* Schedule a daily notification
|
||||
*/
|
||||
async scheduleDailyNotification(options: NotificationOptions): Promise<void> {
|
||||
// Validate required parameters
|
||||
if (!options.time) {
|
||||
throw new Error('Time parameter is required');
|
||||
}
|
||||
|
||||
if (Notification.permission === 'granted') {
|
||||
new Notification(options.title || 'Daily Update', {
|
||||
body: options.body || 'Your daily update is ready',
|
||||
icon: '/icon.png',
|
||||
badge: '/badge.png',
|
||||
tag: 'daily-notification'
|
||||
});
|
||||
|
||||
this.nextNotificationTime = Date.now() + (24 * 60 * 60 * 1000); // 24 hours from now
|
||||
this.lastNotificationTime = Date.now();
|
||||
} else if (Notification.permission !== 'denied') {
|
||||
const permission = await Notification.requestPermission();
|
||||
if (permission === 'granted') {
|
||||
await this.scheduleDailyNotification(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getLastNotification(): Promise<NotificationResponse | null> {
|
||||
return {
|
||||
title: 'Last Notification',
|
||||
body: 'This was the last notification',
|
||||
timestamp: new Date(this.lastNotificationTime).toISOString()
|
||||
// Create notification content
|
||||
const notification: NotificationResponse = {
|
||||
id: this.generateId(),
|
||||
title: options.title || 'Daily Update',
|
||||
body: options.body || 'Your daily notification is ready',
|
||||
timestamp: Date.now(),
|
||||
url: options.url
|
||||
};
|
||||
|
||||
// Store notification
|
||||
this.notifications.set(notification.id, notification);
|
||||
this.scheduledNotifications.add(notification.id);
|
||||
|
||||
// Schedule the notification using browser APIs if available
|
||||
if ('Notification' in window && 'serviceWorker' in navigator) {
|
||||
await this.scheduleBrowserNotification(notification, options);
|
||||
}
|
||||
|
||||
console.log('Web notification scheduled:', notification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last notification
|
||||
*/
|
||||
async getLastNotification(): Promise<NotificationResponse | null> {
|
||||
const notifications = Array.from(this.notifications.values());
|
||||
if (notifications.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return the most recent notification
|
||||
return notifications.sort((a, b) => b.timestamp - a.timestamp)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel all notifications
|
||||
*/
|
||||
async cancelAllNotifications(): Promise<void> {
|
||||
// Web implementation - clear scheduled notifications
|
||||
this.nextNotificationTime = 0;
|
||||
this.scheduledNotifications.clear();
|
||||
this.notifications.clear();
|
||||
|
||||
// Cancel browser notifications if available
|
||||
if ('Notification' in window) {
|
||||
Notification.requestPermission().then(permission => {
|
||||
if (permission === 'granted') {
|
||||
// Clear any existing browser notifications
|
||||
console.log('Browser notifications cleared');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notification status
|
||||
*/
|
||||
async getNotificationStatus(): Promise<NotificationStatus> {
|
||||
return {
|
||||
lastNotificationTime: this.lastNotificationTime,
|
||||
nextNotificationTime: this.nextNotificationTime,
|
||||
isEnabled: 'Notification' in window,
|
||||
isScheduled: this.scheduledNotifications.size > 0,
|
||||
lastNotificationTime: this.getLastNotificationTime(),
|
||||
nextNotificationTime: this.getNextNotificationTime(),
|
||||
pending: this.scheduledNotifications.size,
|
||||
settings: this.settings,
|
||||
isScheduled: this.nextNotificationTime > 0
|
||||
error: undefined
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update notification settings
|
||||
*/
|
||||
async updateSettings(settings: NotificationSettings): Promise<void> {
|
||||
this.settings = { ...this.settings, ...settings };
|
||||
|
||||
if (settings.time) {
|
||||
this.nextNotificationTime = Date.now() + (24 * 60 * 60 * 1000); // 24 hours from now
|
||||
}
|
||||
console.log('Web notification settings updated:', this.settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get battery status (mock implementation)
|
||||
*/
|
||||
async getBatteryStatus(): Promise<BatteryStatus> {
|
||||
// Web implementation - return mock battery status
|
||||
// Mock battery status for web
|
||||
return {
|
||||
level: 100,
|
||||
isCharging: false,
|
||||
powerState: 1,
|
||||
isOptimizationExempt: true
|
||||
powerState: 0,
|
||||
isOptimizationExempt: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Request battery optimization exemption (web not applicable)
|
||||
*/
|
||||
async requestBatteryOptimizationExemption(): Promise<void> {
|
||||
// Web implementation - no-op
|
||||
console.log('Battery optimization exemption requested (web platform)');
|
||||
console.log('Battery optimization exemption not applicable on web');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set adaptive scheduling (web not applicable)
|
||||
*/
|
||||
async setAdaptiveScheduling(options: { enabled: boolean }): Promise<void> {
|
||||
// Web implementation - store setting
|
||||
this.settings.adaptiveScheduling = options.enabled;
|
||||
console.log('Adaptive scheduling not applicable on web:', options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get power state (mock implementation)
|
||||
*/
|
||||
async getPowerState(): Promise<PowerState> {
|
||||
// Web implementation - return mock power state
|
||||
return {
|
||||
powerState: 1,
|
||||
isOptimizationExempt: true
|
||||
powerState: 0,
|
||||
isOptimizationExempt: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check permissions
|
||||
*/
|
||||
async checkPermissions(): Promise<PermissionStatus> {
|
||||
if (!('Notification' in window)) {
|
||||
return {
|
||||
status: 'denied',
|
||||
granted: false,
|
||||
notifications: 'denied',
|
||||
alert: false,
|
||||
badge: false,
|
||||
sound: false,
|
||||
lockScreen: false,
|
||||
carPlay: false
|
||||
};
|
||||
}
|
||||
|
||||
const permission = Notification.permission;
|
||||
return {
|
||||
status: permission,
|
||||
granted: permission === 'granted',
|
||||
notifications: permission as any,
|
||||
alert: permission === 'granted',
|
||||
badge: permission === 'granted',
|
||||
sound: permission === 'granted',
|
||||
lockScreen: permission === 'granted',
|
||||
carPlay: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Request permissions
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule browser notification using native APIs
|
||||
*/
|
||||
private async scheduleBrowserNotification(notification: NotificationResponse, options: NotificationOptions): Promise<void> {
|
||||
if (!('Notification' in window)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const permission = await Notification.requestPermission();
|
||||
if (permission !== 'granted') {
|
||||
console.warn('Notification permission not granted');
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate next notification time
|
||||
const nextTime = this.calculateNextNotificationTime(options.time!);
|
||||
const delay = nextTime.getTime() - Date.now();
|
||||
|
||||
if (delay > 0) {
|
||||
setTimeout(() => {
|
||||
this.showBrowserNotification(notification, options);
|
||||
}, delay);
|
||||
} else {
|
||||
// Show immediately if time has passed
|
||||
this.showBrowserNotification(notification, options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show browser notification
|
||||
*/
|
||||
private showBrowserNotification(notification: NotificationResponse, options: NotificationOptions): void {
|
||||
if (!('Notification' in window)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const browserNotification = new Notification(notification.title, {
|
||||
body: notification.body,
|
||||
icon: '/favicon.ico',
|
||||
tag: notification.id,
|
||||
requireInteraction: false,
|
||||
silent: !options.sound
|
||||
});
|
||||
|
||||
// Handle notification click
|
||||
browserNotification.onclick = () => {
|
||||
if (notification.url) {
|
||||
window.open(notification.url, '_blank');
|
||||
}
|
||||
browserNotification.close();
|
||||
};
|
||||
|
||||
// Auto-close after 10 seconds
|
||||
setTimeout(() => {
|
||||
browserNotification.close();
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate next notification time
|
||||
*/
|
||||
private calculateNextNotificationTime(timeString: string): Date {
|
||||
const [hours, minutes] = timeString.split(':').map(Number);
|
||||
const now = new Date();
|
||||
const next = new Date(now);
|
||||
|
||||
next.setHours(hours, minutes, 0, 0);
|
||||
|
||||
// If time has passed today, schedule for tomorrow
|
||||
if (next <= now) {
|
||||
next.setDate(next.getDate() + 1);
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate unique ID
|
||||
*/
|
||||
private generateId(): string {
|
||||
return `web-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last notification time
|
||||
*/
|
||||
private async getLastNotificationTime(): Promise<number> {
|
||||
const last = await this.getLastNotification();
|
||||
return last ? last.timestamp : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next notification time
|
||||
*/
|
||||
private getNextNotificationTime(): number {
|
||||
// For web, return 24 hours from now as placeholder
|
||||
return Date.now() + (24 * 60 * 60 * 1000);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user