UNPKG

notification-kit

Version:

A unified notification library for React + Capacitor apps. One API for push notifications, in-app notifications, and local notifications across Web, iOS, and Android.

1,766 lines (1,765 loc) 90.5 kB
class Logger { static isDebugEnabled = false; static enableDebug() { Logger.isDebugEnabled = true; } static disableDebug() { Logger.isDebugEnabled = false; } static debug(...args) { if (Logger.isDebugEnabled && typeof console !== "undefined") { console.log("[notification-kit]", ...args); } } static info(...args) { if (typeof console !== "undefined") { console.info("[notification-kit]", ...args); } } static warn(...args) { if (typeof console !== "undefined") { console.warn("[notification-kit]", ...args); } } static error(...args) { if (typeof console !== "undefined") { console.error("[notification-kit]", ...args); } } } class DynamicLoader { static loadedModules = /* @__PURE__ */ new Map(); static loadingPromises = /* @__PURE__ */ new Map(); /** * Check if Capacitor is available */ static isCapacitorAvailable() { try { return typeof window !== "undefined" && "Capacitor" in window; } catch { return false; } } /** * Load Capacitor Core dynamically */ static async loadCapacitorCore() { const cacheKey = "@capacitor/core"; if (this.loadedModules.has(cacheKey)) { return this.loadedModules.get(cacheKey); } if (this.loadingPromises.has(cacheKey)) { return this.loadingPromises.get(cacheKey); } const loadPromise = (async () => { try { if (!this.isCapacitorAvailable()) { return null; } const module = await import("@capacitor/core"); this.loadedModules.set(cacheKey, module); return module; } catch (error) { Logger.warn("Capacitor Core not available. Some features may be limited."); return null; } })(); this.loadingPromises.set(cacheKey, loadPromise); return loadPromise; } /** * Load Capacitor Push Notifications */ static async loadPushNotifications() { const cacheKey = "@capacitor/push-notifications"; if (this.loadedModules.has(cacheKey)) { return this.loadedModules.get(cacheKey); } if (this.loadingPromises.has(cacheKey)) { return this.loadingPromises.get(cacheKey); } const loadPromise = (async () => { try { const module = await import("@capacitor/push-notifications"); this.loadedModules.set(cacheKey, module); return module; } catch (error) { throw new Error( "Push notifications require @capacitor/push-notifications. Please install it: yarn add @capacitor/push-notifications" ); } })(); this.loadingPromises.set(cacheKey, loadPromise); return loadPromise; } /** * Load Capacitor Local Notifications */ static async loadLocalNotifications() { const cacheKey = "@capacitor/local-notifications"; if (this.loadedModules.has(cacheKey)) { return this.loadedModules.get(cacheKey); } if (this.loadingPromises.has(cacheKey)) { return this.loadingPromises.get(cacheKey); } const loadPromise = (async () => { try { const module = await import("@capacitor/local-notifications"); this.loadedModules.set(cacheKey, module); return module; } catch (error) { throw new Error( "Local notifications require @capacitor/local-notifications. Please install it: yarn add @capacitor/local-notifications" ); } })(); this.loadingPromises.set(cacheKey, loadPromise); return loadPromise; } /** * Load Capacitor Preferences */ static async loadPreferences() { const cacheKey = "@capacitor/preferences"; if (this.loadedModules.has(cacheKey)) { return this.loadedModules.get(cacheKey); } if (this.loadingPromises.has(cacheKey)) { return this.loadingPromises.get(cacheKey); } const loadPromise = (async () => { try { const module = await import("@capacitor/preferences"); this.loadedModules.set(cacheKey, module); return module; } catch (error) { return null; } })(); this.loadingPromises.set(cacheKey, loadPromise); return loadPromise; } /** * Load Firebase */ static async loadFirebase() { const cacheKey = "firebase/app"; if (this.loadedModules.has(cacheKey)) { return this.loadedModules.get(cacheKey); } if (this.loadingPromises.has(cacheKey)) { return this.loadingPromises.get(cacheKey); } const loadPromise = (async () => { try { const module = await import("firebase/app"); this.loadedModules.set(cacheKey, module); return module; } catch (error) { throw new Error( "Firebase provider requires firebase. Please install it: yarn add firebase" ); } })(); this.loadingPromises.set(cacheKey, loadPromise); return loadPromise; } /** * Load Firebase Messaging */ static async loadFirebaseMessaging() { const cacheKey = "firebase/messaging"; if (this.loadedModules.has(cacheKey)) { return this.loadedModules.get(cacheKey); } if (this.loadingPromises.has(cacheKey)) { return this.loadingPromises.get(cacheKey); } const loadPromise = (async () => { try { const module = await import("firebase/messaging"); this.loadedModules.set(cacheKey, module); return module; } catch (error) { throw new Error( "Firebase messaging requires firebase. Please install it: yarn add firebase" ); } })(); this.loadingPromises.set(cacheKey, loadPromise); return loadPromise; } /** * Load OneSignal */ static async loadOneSignal() { const cacheKey = "react-onesignal"; if (this.loadedModules.has(cacheKey)) { return this.loadedModules.get(cacheKey); } if (this.loadingPromises.has(cacheKey)) { return this.loadingPromises.get(cacheKey); } const loadPromise = (async () => { try { const module = await import("react-onesignal"); this.loadedModules.set(cacheKey, module); return module; } catch (error) { throw new Error( "OneSignal provider requires react-onesignal. Please install it: yarn add react-onesignal" ); } })(); this.loadingPromises.set(cacheKey, loadPromise); return loadPromise; } /** * Get current platform without Capacitor */ static async getPlatform() { const capacitor = await this.loadCapacitorCore(); if (capacitor && capacitor.Capacitor.isNativePlatform()) { return capacitor.Capacitor.getPlatform(); } else if (typeof window !== "undefined") { if (window.navigator.userAgent.includes("Electron")) { return "electron"; } return "web"; } else { return "unknown"; } } /** * Check if platform is native */ static async isNativePlatform() { const capacitor = await this.loadCapacitorCore(); return capacitor ? capacitor.Capacitor.isNativePlatform() : false; } /** * Clear module cache */ static clearCache() { this.loadedModules.clear(); this.loadingPromises.clear(); } } function toCapacitorImportance(importance) { const map = { 1: 1, 2: 2, 3: 3, 4: 4, 5: 5 }; return map[importance] || 3; } function fromCapacitorImportance(importance) { const map = { 1: 1, 2: 2, 3: 3, 4: 4, 5: 5 }; return map[importance] || 3; } function toCapacitorChannel(channel) { const capacitorChannel = { id: channel.id, name: channel.name, description: channel.description ?? "", importance: channel.importance ? toCapacitorImportance(channel.importance) : void 0, visibility: channel.visibility // Capacitor uses similar values }; if (channel.sound !== void 0) { capacitorChannel.sound = channel.sound; } if (channel.vibration !== void 0) { capacitorChannel.vibration = channel.vibration; } if (channel.lights !== void 0) { capacitorChannel.lights = channel.lights; } return capacitorChannel; } function fromCapacitorChannel(channel) { const notificationChannel = { id: channel.id, name: channel.name, description: channel.description ?? "", importance: channel.importance ? fromCapacitorImportance(channel.importance) : "default", visibility: channel.visibility }; if (channel.sound !== void 0) { notificationChannel.sound = channel.sound; } if (channel.vibration !== void 0) { notificationChannel.vibration = channel.vibration; } if (channel.lights !== void 0) { notificationChannel.lights = channel.lights; } return notificationChannel; } function toCapacitorLocalNotification(options) { const notification = { id: typeof options.id === "string" ? parseInt(options.id, 10) : options.id || Date.now(), title: options.title || "", body: options.body || "", largeBody: options.body }; if (options.summaryText !== void 0) { notification.summaryText = options.summaryText; } if (options.at) { notification.schedule = { at: options.at }; } if (options.sound !== void 0) { notification.sound = options.sound; } if (options.smallIcon !== void 0) { notification.smallIcon = options.smallIcon; } if (options.largeIcon !== void 0) { notification.largeIcon = options.largeIcon; } if (options.color !== void 0) { notification.iconColor = options.color; } if (options.channelId !== void 0) { notification.channelId = options.channelId; } if (options.ongoing !== void 0) { notification.ongoing = options.ongoing; } if (options.autoCancel !== void 0) { notification.autoCancel = options.autoCancel; } if (options.group !== void 0) { notification.group = options.group; } if (options.groupSummary !== void 0) { notification.groupSummary = options.groupSummary; } if (options.extra || options.data) { notification.extra = options.extra || options.data; } return notification; } function fromCapacitorLocalNotification(notification) { const payload = { id: notification.id.toString(), title: notification.title, body: notification.body }; if (notification.summaryText !== void 0) { payload.summaryText = notification.summaryText; } if (notification.sound !== void 0) { payload.sound = notification.sound; } if (notification.smallIcon !== void 0) { payload.smallIcon = notification.smallIcon; } if (notification.largeIcon !== void 0) { payload.largeIcon = notification.largeIcon; } if (notification.iconColor !== void 0) { payload.color = notification.iconColor; } if (notification.channelId !== void 0) { payload.channelId = notification.channelId; } if (notification.ongoing !== void 0) { payload.ongoing = notification.ongoing; } if (notification.autoCancel !== void 0) { payload.autoCancel = notification.autoCancel; } if (notification.group !== void 0) { payload.group = notification.group; } if (notification.groupSummary !== void 0) { payload.groupSummary = notification.groupSummary; } if (notification.extra !== void 0) { payload.extra = notification.extra; payload.data = notification.extra; } return payload; } function toPlatformCapabilities(providerCaps) { return { pushNotifications: providerCaps.pushNotifications || false, localNotifications: true, // Always true if Capacitor is available inAppNotifications: true, // Always true notificationChannels: providerCaps.channels || false, notificationActions: providerCaps.actions || false, notificationBadging: providerCaps.badges || false, notificationSound: providerCaps.sounds || false, notificationVibration: providerCaps.vibration || false, notificationLights: providerCaps.lights || false, notificationGrouping: providerCaps.groups || false, notificationImportance: providerCaps.channels || false, notificationVisibility: providerCaps.channels || false, notificationLockScreen: providerCaps.channels || false, notificationFullScreen: false, notificationHeadsUp: providerCaps.channels || false, notificationOngoing: providerCaps.channels || false, notificationProgress: providerCaps.progress || false, notificationBigText: providerCaps.bigText || false, notificationBigPicture: providerCaps.bigPicture || false, notificationInbox: providerCaps.inbox || false, notificationMedia: providerCaps.richMedia || false, notificationCustom: providerCaps.customData || false, notificationScheduling: providerCaps.scheduling || false, notificationGeofencing: providerCaps.geofencing || false, notificationTriggers: providerCaps.triggers || false, serviceWorker: providerCaps.webPush || false, webPushProtocol: providerCaps.webPush || false, backgroundSync: providerCaps.backgroundSync || false, foregroundService: false, criticalAlerts: false, provisionalAuth: false, appBadge: providerCaps.badges || false, quietHours: providerCaps.quietHours || false, doNotDisturb: false }; } class NotificationKit { static instance = null; provider = null; config = null; initialized = false; eventListeners = /* @__PURE__ */ new Map(); platform = "unknown"; capabilities = null; constructor() { } /** * Get singleton instance */ static getInstance() { if (!NotificationKit.instance) { NotificationKit.instance = new NotificationKit(); } return NotificationKit.instance; } /** * Static init method for convenience */ static async init(config) { return NotificationKit.getInstance().init(config); } /** * Initialize notification kit with configuration */ async init(config) { if (this.initialized) { return; } try { this.config = config; await this.detectPlatform(); await this.initializeProvider(); await this.setupEventListeners(); this.initialized = true; this.emit("ready", { platform: this.platform, capabilities: this.capabilities }); } catch (error) { this.emit("error", { error, context: "initialization" }); throw error; } } /** * Destroy notification kit instance */ async destroy() { if (this.provider) { await this.provider.destroy(); this.provider = null; } this.eventListeners.clear(); this.initialized = false; this.config = null; NotificationKit.instance = null; } /** * Check if notification kit is initialized */ isInitialized() { return this.initialized; } /** * Get current platform */ getPlatform() { return this.platform; } /** * Get platform capabilities */ getCapabilities() { return this.capabilities; } /** * Get current provider */ getProvider() { return this.provider; } /** * Request notification permission */ async requestPermission() { this.ensureInitialized(); try { const granted = await this.provider.requestPermission(); this.emit("permissionChanged", { granted, status: granted ? "granted" : "denied" }); return granted; } catch (error) { this.emit("error", { error, context: "permission" }); throw error; } } /** * Check notification permission status */ async checkPermission() { this.ensureInitialized(); try { return await this.provider.checkPermission(); } catch (error) { this.emit("error", { error, context: "permission" }); throw error; } } /** * Get notification token */ async getToken() { this.ensureInitialized(); try { const token = await this.provider.getToken(); this.emit("tokenReceived", { token }); return token; } catch (error) { this.emit("error", { error, context: "token" }); throw error; } } /** * Subscribe to topic */ async subscribe(topic) { this.ensureInitialized(); try { await this.provider.subscribe(topic); this.emit("subscribed", { topic }); } catch (error) { this.emit("error", { error, context: "subscription" }); throw error; } } /** * Unsubscribe from topic */ async unsubscribe(topic) { this.ensureInitialized(); try { await this.provider.unsubscribe(topic); this.emit("unsubscribed", { topic }); } catch (error) { this.emit("error", { error, context: "subscription" }); throw error; } } /** * Send push notification */ async sendPushNotification(payload) { this.ensureInitialized(); try { await this.provider.sendNotification(payload); this.emit("notificationSent", { payload, type: "push" }); } catch (error) { this.emit("error", { error, context: "push" }); throw error; } } /** * Schedule local notification */ async scheduleLocalNotification(options) { this.ensureInitialized(); try { const localNotificationsModule = await DynamicLoader.loadLocalNotifications(); if (!localNotificationsModule) { throw new Error("Local notifications are not available on this platform"); } const { LocalNotifications } = localNotificationsModule; const capacitorNotification = toCapacitorLocalNotification(options); await LocalNotifications.schedule({ notifications: [capacitorNotification] }); this.emit("notificationScheduled", { options, type: "local" }); } catch (error) { this.emit("error", { error, context: "local" }); throw error; } } /** * Cancel local notification */ async cancelLocalNotification(id) { this.ensureInitialized(); try { const localNotificationsModule = await DynamicLoader.loadLocalNotifications(); if (!localNotificationsModule) { throw new Error("Local notifications are not available on this platform"); } const { LocalNotifications } = localNotificationsModule; const numericId = typeof id === "string" ? parseInt(id, 10) : id; await LocalNotifications.cancel({ notifications: [{ id: numericId }] }); this.emit("notificationCancelled", { id, type: "local" }); } catch (error) { this.emit("error", { error, context: "local" }); throw error; } } /** * Get pending local notifications */ async getPendingLocalNotifications() { this.ensureInitialized(); try { const localNotificationsModule = await DynamicLoader.loadLocalNotifications(); if (!localNotificationsModule) { throw new Error("Local notifications are not available on this platform"); } const { LocalNotifications } = localNotificationsModule; const result = await LocalNotifications.getPending(); return result.notifications.map((n) => ({ id: n.id.toString(), title: n.title, body: n.body, data: n.extra, platform: this.platform, type: "local", timestamp: /* @__PURE__ */ new Date() })); } catch (error) { this.emit("error", { error, context: "local" }); throw error; } } /** * Show in-app notification */ async showInAppNotification(options) { try { const { showInAppNotification: showInAppNotification2 } = await Promise.resolve().then(() => inApp$1); const id = await showInAppNotification2(options, this.config?.inApp); this.emit("notificationShown", { options, type: "inApp", id }); return id; } catch (error) { this.emit("error", { error, context: "inApp" }); throw error; } } /** * Check if notifications are supported */ async isSupported() { try { const { platform: platform2 } = await Promise.resolve().then(() => platform$1); const capabilities = await platform2.getCapabilities(); return capabilities.pushNotifications || capabilities.localNotifications || false; } catch (error) { return false; } } /** * Create notification channel (Android) */ async createChannel(channel) { if (this.platform !== "android") { return; } try { const localNotificationsModule = await DynamicLoader.loadLocalNotifications(); if (!localNotificationsModule) { throw new Error("Local notifications are not available on this platform"); } const { LocalNotifications } = localNotificationsModule; const capacitorChannel = toCapacitorChannel(channel); await LocalNotifications.createChannel(capacitorChannel); this.emit("channelCreated", { channel }); } catch (error) { this.emit("error", { error, context: "channel" }); throw error; } } /** * Delete notification channel (Android) */ async deleteChannel(channelId) { if (this.platform !== "android") { return; } try { const localNotificationsModule = await DynamicLoader.loadLocalNotifications(); if (!localNotificationsModule) { throw new Error("Local notifications are not available on this platform"); } const { LocalNotifications } = localNotificationsModule; await LocalNotifications.deleteChannel({ id: channelId }); this.emit("channelDeleted", { channelId }); } catch (error) { this.emit("error", { error, context: "channel" }); throw error; } } /** * List notification channels (Android) */ async listChannels() { if (this.platform !== "android") { return []; } try { const localNotificationsModule = await DynamicLoader.loadLocalNotifications(); if (!localNotificationsModule) { throw new Error("Local notifications are not available on this platform"); } const { LocalNotifications } = localNotificationsModule; const result = await LocalNotifications.listChannels(); return result.channels.map((channel) => fromCapacitorChannel(channel)); } catch (error) { this.emit("error", { error, context: "channel" }); throw error; } } /** * Add event listener */ on(event, callback) { const listeners = this.eventListeners.get(event) || []; listeners.push(callback); this.eventListeners.set(event, listeners); return () => { const currentListeners = this.eventListeners.get(event) || []; const index = currentListeners.indexOf(callback); if (index > -1) { currentListeners.splice(index, 1); this.eventListeners.set(event, currentListeners); } }; } /** * Remove event listener */ off(event, callback) { if (!callback) { this.eventListeners.delete(event); return; } const listeners = this.eventListeners.get(event) || []; const index = listeners.indexOf(callback); if (index > -1) { listeners.splice(index, 1); this.eventListeners.set(event, listeners); } } /** * Emit event to listeners */ emit(event, data) { const listeners = this.eventListeners.get(event) || []; const notificationEvent = { id: Date.now().toString(), type: event, timestamp: /* @__PURE__ */ new Date(), data, ...data }; listeners.forEach((callback) => { try { callback(notificationEvent); } catch (error) { } }); } /** * Detect current platform */ async detectPlatform() { this.platform = await DynamicLoader.getPlatform(); } /** * Initialize provider based on configuration */ async initializeProvider() { if (!this.config) { throw new Error("Configuration is required"); } try { if (this.config.provider === "firebase") { const { FirebaseProvider } = await import("./FirebaseProvider-CbcLVLcm.js"); this.provider = new FirebaseProvider(); } else if (this.config.provider === "onesignal") { const { OneSignalProvider } = await import("./OneSignalProvider-oOlRK-Y1.js"); this.provider = new OneSignalProvider(); } else { throw new Error(`Unknown provider: ${this.config.provider}`); } await this.provider.init(this.config.config); const providerCapabilities = await this.provider.getCapabilities(); this.capabilities = toPlatformCapabilities(providerCapabilities); } catch (error) { throw new Error(`Failed to initialize provider: ${error}`); } } /** * Setup event listeners for provider */ async setupEventListeners() { if (!this.provider) { return; } this.provider.onMessage((payload) => { this.emit("notificationReceived", { payload, type: "push", platform: this.platform }); }); this.provider.onTokenRefresh((token) => { this.emit("tokenRefreshed", { token }); }); this.provider.onError((error) => { this.emit("error", { error, context: "provider" }); }); if (this.platform !== "web") { try { const { LocalNotifications } = await import("@capacitor/local-notifications"); LocalNotifications.addListener( "localNotificationReceived", (notification) => { this.emit("notificationReceived", { payload: fromCapacitorLocalNotification(notification), type: "local", platform: this.platform }); } ); LocalNotifications.addListener( "localNotificationActionPerformed", (action) => { this.emit("notificationActionPerformed", { action: action.actionId, notification: action.notification, inputValue: action.inputValue, platform: this.platform }); } ); } catch (error) { } } } /** * Ensure notification kit is initialized */ ensureInitialized() { if (!this.initialized) { throw new Error("NotificationKit must be initialized before use"); } } } const notifications = { /** * Initialize notification kit */ init: (config) => NotificationKit.getInstance().init(config), /** * Request permission */ requestPermission: () => NotificationKit.getInstance().requestPermission(), /** * Check permission */ checkPermission: () => NotificationKit.getInstance().checkPermission(), /** * Check if permission is granted */ isPermissionGranted: async () => { const kit = NotificationKit.getInstance(); const status = await kit.checkPermission(); return status === "granted"; }, /** * Get permission state */ getPermissionState: () => NotificationKit.getInstance().checkPermission(), /** * Get token */ getToken: () => NotificationKit.getInstance().getToken(), /** * Delete token */ deleteToken: async () => { const kit = NotificationKit.getInstance(); const provider = kit.getProvider(); if (provider && "deleteToken" in provider) { await provider.deleteToken(); } else { throw new Error("deleteToken not supported by current provider"); } }, /** * Subscribe to topic */ subscribe: (topic) => NotificationKit.getInstance().subscribe(topic), /** * Unsubscribe from topic */ unsubscribe: (topic) => NotificationKit.getInstance().unsubscribe(topic), /** * Schedule local notification */ schedule: (options) => NotificationKit.getInstance().scheduleLocalNotification(options), /** * Cancel local notification */ cancel: (id) => NotificationKit.getInstance().cancelLocalNotification(id), /** * Get pending notifications */ getPending: () => NotificationKit.getInstance().getPendingLocalNotifications(), /** * Get delivered notifications */ getDelivered: async () => { const kit = NotificationKit.getInstance(); if (kit.getPlatform() === "web") { throw new Error("getDelivered not supported on web platform"); } const localNotificationsModule = await DynamicLoader.loadLocalNotifications(); if (!localNotificationsModule) { throw new Error("Local notifications are not available on this platform"); } const { LocalNotifications } = localNotificationsModule; const result = await LocalNotifications.getDeliveredNotifications(); return result.notifications; }, /** * Remove delivered notification */ removeDelivered: async (id) => { const kit = NotificationKit.getInstance(); if (kit.getPlatform() === "web") { throw new Error("removeDelivered not supported on web platform"); } const localNotificationsModule = await DynamicLoader.loadLocalNotifications(); if (!localNotificationsModule) { throw new Error("Local notifications are not available on this platform"); } const { LocalNotifications } = localNotificationsModule; await LocalNotifications.removeDeliveredNotifications({ notifications: [{ id: parseInt(id, 10), title: "", body: "" }] }); }, /** * Remove all delivered notifications */ removeAllDelivered: async () => { const kit = NotificationKit.getInstance(); if (kit.getPlatform() === "web") { throw new Error("removeAllDelivered not supported on web platform"); } const localNotificationsModule = await DynamicLoader.loadLocalNotifications(); if (!localNotificationsModule) { throw new Error("Local notifications are not available on this platform"); } const { LocalNotifications } = localNotificationsModule; await LocalNotifications.removeAllDeliveredNotifications(); }, /** * Cancel all pending notifications */ cancelAll: async () => { const kit = NotificationKit.getInstance(); if (kit.getPlatform() === "web") { throw new Error("cancelAll not supported on web platform"); } const localNotificationsModule = await DynamicLoader.loadLocalNotifications(); if (!localNotificationsModule) { throw new Error("Local notifications are not available on this platform"); } const { LocalNotifications } = localNotificationsModule; const pending = await LocalNotifications.getPending(); if (pending.notifications.length > 0) { await LocalNotifications.cancel({ notifications: pending.notifications.map((n) => ({ id: n.id })) }); } }, /** * Listen for push notifications */ onPush: (callback) => { return NotificationKit.getInstance().on("notificationReceived", (event) => { if (event.type.startsWith("push.")) { callback(event.notification); } }); }, /** * Listen for push notification opened */ onPushOpened: (callback) => { return NotificationKit.getInstance().on("notificationActionPerformed", (event) => { if (event.type === "push.opened" || event.type === "push.action" && event.actionId === "tap") { callback(event.notification); } }); }, /** * Show in-app notification */ showInApp: (options) => NotificationKit.getInstance().showInAppNotification(options), /** * Success notification */ success: (title, message) => NotificationKit.getInstance().showInAppNotification({ title, message: message ?? title, type: "success" }), /** * Error notification */ error: (title, message) => NotificationKit.getInstance().showInAppNotification({ title, message: message ?? title, type: "error" }), /** * Warning notification */ warning: (title, message) => NotificationKit.getInstance().showInAppNotification({ title, message: message ?? title, type: "warning" }), /** * Info notification */ info: (title, message) => NotificationKit.getInstance().showInAppNotification({ title, message: message ?? title, type: "info" }), /** * Add event listener */ on: (event, callback) => NotificationKit.getInstance().on(event, callback), /** * Remove event listener */ off: (event, callback) => NotificationKit.getInstance().off(event, callback) }; class PermissionManager { platform; constructor() { this.platform = "unknown"; } /** * Request notification permissions */ async requestPermission() { try { await this.ensurePlatform(); if (this.platform === "web") { return await this.requestWebPermission(); } else { return await this.requestNativePermission(); } } catch (error) { return false; } } /** * Check current permission status */ async checkPermission() { try { await this.ensurePlatform(); if (this.platform === "web") { return await this.checkWebPermission(); } else { return await this.checkNativePermission(); } } catch (error) { return "denied"; } } /** * Check if permission is granted */ async isPermissionGranted() { const status = await this.checkPermission(); return status === "granted"; } /** * Check if permission can be requested */ async canRequestPermission() { const status = await this.checkPermission(); return status === "prompt" || status === "default"; } /** * Open system settings for notifications */ async openSettings() { if (this.platform === "web") { throw new Error("Cannot open settings from web platform"); } } /** * Request web notification permission */ async requestWebPermission() { if (!("Notification" in window)) { throw new Error("Notifications not supported in this browser"); } if (Notification.permission === "granted") { return true; } if (Notification.permission === "denied") { return false; } const permission = await Notification.requestPermission(); return permission === "granted"; } /** * Check web notification permission */ async checkWebPermission() { if (!("Notification" in window)) { return "denied"; } const permission = Notification.permission; if (permission === "default") { return "prompt"; } return permission; } /** * Request native notification permission */ async requestNativePermission() { try { const pushNotificationsModule = await DynamicLoader.loadPushNotifications(); if (!pushNotificationsModule) { throw new Error("Push notifications are not available on this platform"); } const { PushNotifications } = pushNotificationsModule; const result = await PushNotifications.requestPermissions(); return result.receive === "granted"; } catch (error) { return false; } } /** * Check native notification permission */ async checkNativePermission() { try { const pushNotificationsModule = await DynamicLoader.loadPushNotifications(); if (!pushNotificationsModule) { throw new Error("Push notifications are not available on this platform"); } const { PushNotifications } = pushNotificationsModule; const result = await PushNotifications.checkPermissions(); if (result.receive === "granted") { return "granted"; } else if (result.receive === "denied") { return "denied"; } else if (result.receive === "prompt") { return "prompt"; } else { return "unknown"; } } catch (error) { return "denied"; } } /** * Detect current platform */ async detectPlatform() { return DynamicLoader.getPlatform(); } /** * Ensure platform is detected */ async ensurePlatform() { if (this.platform === "unknown") { this.platform = await this.detectPlatform(); } } } const permissionManager = new PermissionManager(); const permissions = { request: () => permissionManager.requestPermission(), check: () => permissionManager.checkPermission(), isGranted: () => permissionManager.isPermissionGranted(), canRequest: () => permissionManager.canRequestPermission(), openSettings: () => permissionManager.openSettings() }; class PlatformManager { detection = null; capabilities = null; /** * Detect current platform */ async detect() { if (this.detection) { return this.detection; } const platform2 = await this.getPlatform(); const isCapacitor = await DynamicLoader.isNativePlatform(); const isHybrid = isCapacitor; const isNative = isCapacitor; const isWeb = platform2 === "web"; const isMobile = platform2 === "ios" || platform2 === "android"; const isDesktop = platform2 === "electron" || !isMobile && isWeb; const isTablet = false; const userAgent = typeof navigator !== "undefined" ? navigator.userAgent : ""; const version = this.getVersion(); this.detection = { platform: platform2, isCapacitor, isHybrid, isNative, isWeb, isMobile, isDesktop, isTablet, version, userAgent, supportedFeatures: this.getSupportedFeatures(platform2), limitations: this.getLimitations(platform2), warnings: this.getWarnings(platform2) }; return this.detection; } /** * Get platform capabilities */ async getCapabilities(platform2) { const targetPlatform = platform2 || await this.getPlatform(); if (this.capabilities && !platform2) { return this.capabilities; } const capabilities = this.buildCapabilities(targetPlatform); if (!platform2) { this.capabilities = capabilities; } return capabilities; } /** * Check if feature is supported */ async isSupported(feature, platform2) { const capabilities = await this.getCapabilities(platform2); return capabilities[feature] || false; } /** * Get platform defaults */ getDefaults() { return { sound: "default", badge: "1", icon: "notification-icon", web: { sound: "default.mp3" }, ios: { sound: "default.caf", badge: "auto" }, android: { sound: "default", icon: "ic_notification" }, electron: { sound: "default" } }; } /** * Get platform compatibility matrix */ getCompatibility() { return { pushNotifications: true, localNotifications: true, inAppNotifications: true, channels: true, actions: true, badges: true, sounds: true, criticalAlerts: false }; } /** * Get current platform */ async getPlatform() { return DynamicLoader.getPlatform(); } /** * Get platform version */ getVersion() { if (typeof window !== "undefined") { return window.navigator.userAgent; } return "unknown"; } /** * Build platform capabilities */ buildCapabilities(platform2) { const defaults = this.getDefaults(); const platformDefaults = defaults[platform2] || {}; return { pushNotifications: false, localNotifications: false, inAppNotifications: false, notificationChannels: false, notificationActions: false, notificationBadging: false, notificationSound: false, notificationVibration: false, notificationLights: false, notificationGrouping: false, notificationImportance: false, notificationVisibility: false, notificationLockScreen: false, notificationFullScreen: false, notificationHeadsUp: false, notificationOngoing: false, notificationProgress: false, notificationBigText: false, notificationBigPicture: false, notificationInbox: false, notificationMedia: false, notificationCustom: false, notificationScheduling: false, notificationGeofencing: false, notificationTriggers: false, serviceWorker: false, webPushProtocol: false, backgroundSync: false, foregroundService: false, criticalAlerts: false, provisionalAuth: false, appBadge: false, quietHours: false, doNotDisturb: false, ...platformDefaults }; } /** * Get supported features for platform */ getSupportedFeatures(platform2) { const capabilities = this.buildCapabilities(platform2); return Object.entries(capabilities).filter(([, supported]) => supported).map(([feature]) => feature); } /** * Get platform limitations */ getLimitations(platform2) { const limitations = []; switch (platform2) { case "web": limitations.push("No local notifications"); limitations.push("No notification channels"); limitations.push("No app badging"); limitations.push("Requires HTTPS for push notifications"); limitations.push("Service worker required"); break; case "ios": limitations.push("No notification channels"); limitations.push("No notification lights"); limitations.push("Critical alerts require entitlement"); limitations.push("Limited customization"); break; case "android": limitations.push("Notification channels required (API 26+)"); limitations.push("Background restrictions"); limitations.push("Battery optimization affects delivery"); break; case "electron": limitations.push("Platform-specific implementation"); limitations.push("Limited mobile features"); break; } return limitations; } /** * Get platform warnings */ getWarnings(platform2) { const warnings = []; switch (platform2) { case "web": if (typeof window !== "undefined" && !window.isSecureContext) { warnings.push("HTTPS required for push notifications"); } if (typeof navigator !== "undefined" && !("serviceWorker" in navigator)) { warnings.push("Service Worker not supported"); } break; case "ios": warnings.push("iOS notification permissions are sensitive"); warnings.push("Users may have notifications disabled globally"); break; case "android": warnings.push("Android notification behavior varies by OEM"); warnings.push("Users may have battery optimization enabled"); break; } return warnings; } } const platformManager = new PlatformManager(); const platform = { detect: () => platformManager.detect(), getCapabilities: (platform2) => platformManager.getCapabilities(platform2), isSupported: (feature, platform2) => platformManager.isSupported(feature, platform2), getDefaults: () => platformManager.getDefaults(), getCompatibility: () => platformManager.getCompatibility() }; const platform$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, PlatformManager, platform, platformManager }, Symbol.toStringTag, { value: "Module" })); class StorageManager { platform; config; prefix; constructor(config = {}) { this.platform = "unknown"; this.config = { prefix: "notification_kit_", adapter: "preferences", encryption: false, ttl: 0, ...config }; this.prefix = this.config.prefix; } /** * Store data */ async set(key, value) { await this.ensurePlatform(); const fullKey = this.prefix + key; const data = this.prepareData(value); try { if (this.platform === "web" || this.config.adapter === "localStorage") { await this.setWebStorage(fullKey, data); } else { await this.setNativeStorage(fullKey, data); } } catch (error) { throw error; } } /** * Get data */ async get(key) { await this.ensurePlatform(); const fullKey = this.prefix + key; try { let data; if (this.platform === "web" || this.config.adapter === "localStorage") { data = await this.getWebStorage(fullKey); } else { data = await this.getNativeStorage(fullKey); } if (!data) { return null; } return this.parseData(data); } catch (error) { return null; } } /** * Remove data */ async remove(key) { await this.ensurePlatform(); const fullKey = this.prefix + key; try { if (this.platform === "web" || this.config.adapter === "localStorage") { await this.removeWebStorage(fullKey); } else { await this.removeNativeStorage(fullKey); } } catch (error) { throw error; } } /** * Clear all data */ async clear() { await this.ensurePlatform(); try { if (this.platform === "web" || this.config.adapter === "localStorage") { await this.clearWebStorage(); } else { await this.clearNativeStorage(); } } catch (error) { throw error; } } /** * Get all keys */ async keys() { await this.ensurePlatform(); try { if (this.platform === "web" || this.config.adapter === "localStorage") { return await this.getWebStorageKeys(); } else { return await this.getNativeStorageKeys(); } } catch (error) { return []; } } /** * Check if key exists */ async has(key) { const value = await this.get(key); return value !== null; } /** * Get storage size */ async size() { const keys = await this.keys(); return keys.length; } /** * Set web storage */ async setWebStorage(key, data) { if (this.config.adapter === "sessionStorage") { sessionStorage.setItem(key, data); } else { localStorage.setItem(key, data); } } /** * Get web storage */ async getWebStorage(key) { if (this.config.adapter === "sessionStorage") { return sessionStorage.getItem(key); } else { return localStorage.getItem(key); } } /** * Remove web storage */ async removeWebStorage(key) { if (this.config.adapter === "sessionStorage") { sessionStorage.removeItem(key); } else { localStorage.removeItem(key); } } /** * Clear web storage */ async clearWebStorage() { const keys = await this.getWebStorageKeys(); for (const key of keys) { await this.removeWebStorage(key); } } /** * Get web storage keys */ async getWebStorageKeys() { const storage2 = this.config.adapter === "sessionStorage" ? sessionStorage : localStorage; const keys = []; for (let i = 0; i < storage2.length; i++) { const key = storage2.key(i); if (key && key.startsWith(this.prefix)) { keys.push(key.substring(this.prefix.length)); } } return keys; } /** * Set native storage */ async setNativeStorage(key, data) { const preferencesModule = await DynamicLoader.loadPreferences(); if (!preferencesModule) { return this.setWebStorage(key, data); } await preferencesModule.Preferences.set({ key, value: data }); } /** * Get native storage */ async getNativeStorage(key) { const preferencesModule = await DynamicLoader.loadPreferences(); if (!preferencesModule) { return this.getWebStorage(key); } const result = await preferencesModule.Preferences.get({ key }); return result.value; } /** * Remove native storage */ async removeNativeStorage(key) { const preferencesModule = await DynamicLoader.loadPreferences(); if (!preferencesModule) { return this.removeWebStorage(key); } await preferencesModule.Preferences.remove({ key }); } /** * Clear native storage */ async clearNativeStorage() { const preferencesModule = await DynamicLoader.loadPreferences(); if (!preferencesModule) { return this.clearWebStorage(); } const keys = await this.getNativeStorageKeys(); for (const key of keys) { await preferencesModule.Preferences.remove({ key: this.prefix + key }); } } /** * Get native storage keys */ async getNativeStorageKeys() { const preferencesModule = await DynamicLoader.loadPreferences(); if (!preferencesModule) { return this.getWebStorageKeys(); } const result = await preferencesModule.Preferences.keys(); return result.keys.filter((key) => key.startsWith(this.prefix)).map((key) => key.substring(this.prefix.length)); } /** * Prepare data for storage */ prepareData(value) { const data = { value, timestamp: Date.now(), ttl: this.config.ttl }; let serialized = JSON.stringify(data); if (this.config.encryption) { serialized = this.encrypt(serialized); } return serialized; } /** * Parse data from storage */ parseData(data) { try { let decrypted = data; if (this.config.encryption) { decrypted = this.decrypt(data); } const parsed = JSON.parse(decrypted); if (this.config.ttl && this.config.ttl > 0) { const now = Date.now(); const age = now - parsed.timestamp; if (age > this.config.ttl) { return null; } } return parsed.value; } catch (error) { return null; } } /** * Encrypt data (basic implementation) */ encrypt(data) { return btoa(data); } /** * Decrypt data (basic implementation) */ decrypt(data) { return atob(data); } /** * Ensure platform is detected */ async ensurePlatform() { if (this.platform === "unknown") { this.platform = await DynamicLoader.getPlatform(); } } } const storage = new StorageManager(); const createStorage = (config) => new StorageManager(config); class SchedulingUtils { /** * Calculate next scheduled time */ static calculateNextScheduledTime(schedule) { if (!schedule) { return null; } const now = /* @__PURE__ */ new Date(); if (schedule.at) { return this.calculateAtTime(schedule.at, now); } if (schedule.on) { return this.calculateOnTime(schedule.on, now); }