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.

765 lines (764 loc) 21.5 kB
import { useState, useRef, useCallback, useEffect } from "react"; import { N as NotificationKit, n as notifications, I as InAppNotificationManager, k as getActiveInAppNotifications, l as configureInAppNotifications, h as showInAppNotification, i as dismissInAppNotification, j as dismissAllInAppNotifications } from "./inApp-DOHkBonY.mjs"; import { g, f, m, b, p, s, v } from "./inApp-DOHkBonY.mjs"; function useNotifications() { const [state, setState] = useState({ isInitialized: false, isInitializing: false, permission: null, token: null, error: null, notifications: [], pendingNotifications: [], subscriptions: [] }); const notificationKitRef = useRef(null); const eventListenersRef = useRef(/* @__PURE__ */ new Map()); const updateState = useCallback((updates) => { setState((prev) => ({ ...prev, ...updates })); }, []); const init = useCallback( async (config) => { try { updateState({ isInitializing: true, error: null }); notificationKitRef.current = NotificationKit.getInstance(); await notificationKitRef.current.init(config); const permission = await notificationKitRef.current.checkPermission(); let token = null; if (permission === "granted") { try { token = await notificationKitRef.current.getToken(); } catch (error) { } } updateState({ isInitialized: true, isInitializing: false, permission, token }); } catch (error) { updateState({ isInitialized: false, isInitializing: false, error }); throw error; } }, [updateState] ); const destroy = useCallback(async () => { try { eventListenersRef.current.forEach((unsubscribe2) => unsubscribe2()); eventListenersRef.current.clear(); if (notificationKitRef.current) { await notificationKitRef.current.destroy(); notificationKitRef.current = null; } updateState({ isInitialized: false, isInitializing: false, permission: null, token: null, error: null, notifications: [], pendingNotifications: [], subscriptions: [] }); } catch (error) { updateState({ error }); throw error; } }, [updateState]); const requestPermission = useCallback(async () => { if (!notificationKitRef.current) { throw new Error("NotificationKit not initialized"); } try { const granted = await notificationKitRef.current.requestPermission(); const permission = await notificationKitRef.current.checkPermission(); let token = null; if (granted) { try { token = await notificationKitRef.current.getToken(); } catch (error) { } } updateState({ permission, token }); return granted; } catch (error) { updateState({ error }); throw error; } }, [updateState]); const checkPermission = useCallback(async () => { if (!notificationKitRef.current) { throw new Error("NotificationKit not initialized"); } try { const permission = await notificationKitRef.current.checkPermission(); updateState({ permission }); return permission; } catch (error) { updateState({ error }); throw error; } }, [updateState]); const getToken = useCallback(async () => { if (!notificationKitRef.current) { throw new Error("NotificationKit not initialized"); } try { const token = await notificationKitRef.current.getToken(); updateState({ token }); return token; } catch (error) { updateState({ error }); throw error; } }, [updateState]); const refreshToken = useCallback(async () => { if (!notificationKitRef.current) { throw new Error("NotificationKit not initialized"); } try { const token = await notificationKitRef.current.getToken(); updateState({ token }); return token; } catch (error) { updateState({ error }); throw error; } }, [updateState]); const subscribe = useCallback( async (topic) => { if (!notificationKitRef.current) { throw new Error("NotificationKit not initialized"); } try { await notificationKitRef.current.subscribe(topic); updateState({ subscriptions: [...state.subscriptions, topic] }); } catch (error) { updateState({ error }); throw error; } }, [state.subscriptions, updateState] ); const unsubscribe = useCallback( async (topic) => { if (!notificationKitRef.current) { throw new Error("NotificationKit not initialized"); } try { await notificationKitRef.current.unsubscribe(topic); updateState({ subscriptions: state.subscriptions.filter((sub) => sub !== topic) }); } catch (error) { updateState({ error }); throw error; } }, [state.subscriptions, updateState] ); const scheduleNotification = useCallback( async (options) => { if (!notificationKitRef.current) { throw new Error("NotificationKit not initialized"); } try { const priorityMap = { low: "low", normal: "default", high: "high", urgent: "max" }; const { at, in: inProp, every, count, until, on, days, timezone, allowWhileIdle, exact, wakeDevice, priority, category, identifier, triggerInBackground, skipIfBatteryLow, respectQuietHours, ...notificationPayload } = options; const scheduleOptions = {}; if (at !== void 0) scheduleOptions.at = at; if (inProp !== void 0) scheduleOptions.in = inProp; if (every !== void 0) scheduleOptions.every = every; if (count !== void 0) scheduleOptions.count = count; if (until !== void 0) scheduleOptions.until = until; if (on !== void 0) scheduleOptions.on = on; if (days !== void 0) scheduleOptions.days = days; if (timezone !== void 0) scheduleOptions.timezone = timezone; if (allowWhileIdle !== void 0) scheduleOptions.allowWhileIdle = allowWhileIdle; if (exact !== void 0) scheduleOptions.exact = exact; if (wakeDevice !== void 0) scheduleOptions.wakeDevice = wakeDevice; if (priority !== void 0) scheduleOptions.priority = priority; if (category !== void 0) scheduleOptions.category = category; if (identifier !== void 0) scheduleOptions.identifier = identifier; if (triggerInBackground !== void 0) scheduleOptions.triggerInBackground = triggerInBackground; if (skipIfBatteryLow !== void 0) scheduleOptions.skipIfBatteryLow = skipIfBatteryLow; if (respectQuietHours !== void 0) scheduleOptions.respectQuietHours = respectQuietHours; const payload = { ...notificationPayload, priority: priorityMap[priority || "normal"] || "default", schedule: scheduleOptions }; await notificationKitRef.current.scheduleLocalNotification( payload ); const pending = await notificationKitRef.current.getPendingLocalNotifications(); updateState({ pendingNotifications: pending }); } catch (error) { updateState({ error }); throw error; } }, [updateState] ); const cancelNotification = useCallback( async (id) => { if (!notificationKitRef.current) { throw new Error("NotificationKit not initialized"); } try { await notificationKitRef.current.cancelLocalNotification(id); const pending = await notificationKitRef.current.getPendingLocalNotifications(); updateState({ pendingNotifications: pending }); } catch (error) { updateState({ error }); throw error; } }, [updateState] ); const getPendingNotifications = useCallback(async () => { if (!notificationKitRef.current) { throw new Error("NotificationKit not initialized"); } try { const pending = await notificationKitRef.current.getPendingLocalNotifications(); updateState({ pendingNotifications: pending }); return pending; } catch (error) { updateState({ error }); throw error; } }, [updateState]); const createChannel = useCallback( async (channel) => { if (!notificationKitRef.current) { throw new Error("NotificationKit not initialized"); } try { await notificationKitRef.current.createChannel(channel); } catch (error) { updateState({ error }); throw error; } }, [updateState] ); const deleteChannel = useCallback( async (channelId) => { if (!notificationKitRef.current) { throw new Error("NotificationKit not initialized"); } try { await notificationKitRef.current.deleteChannel(channelId); } catch (error) { updateState({ error }); throw error; } }, [updateState] ); const listChannels = useCallback(async () => { if (!notificationKitRef.current) { throw new Error("NotificationKit not initialized"); } try { return await notificationKitRef.current.listChannels(); } catch (error) { updateState({ error }); throw error; } }, [updateState]); const addEventListener = useCallback( (event, callback) => { if (!notificationKitRef.current) { throw new Error("NotificationKit not initialized"); } const unsubscribe2 = notificationKitRef.current.on(event, callback); const listenerId = `${event}-${Date.now()}`; eventListenersRef.current.set(listenerId, unsubscribe2); return () => { unsubscribe2(); eventListenersRef.current.delete(listenerId); }; }, [] ); const clearNotifications = useCallback(() => { updateState({ notifications: [] }); }, [updateState]); const clearError = useCallback(() => { updateState({ error: null }); }, [updateState]); const refresh = useCallback(async () => { if (!notificationKitRef.current) { return; } try { const [permission, pending] = await Promise.all([ notificationKitRef.current.checkPermission(), notificationKitRef.current.getPendingLocalNotifications() ]); let token = null; if (permission === "granted") { try { token = await notificationKitRef.current.getToken(); } catch (error) { } } updateState({ permission, token, pendingNotifications: pending }); } catch (error) { updateState({ error }); throw error; } }, [updateState]); const isSupported = useCallback(async () => { if (!notificationKitRef.current) { return false; } try { return await notificationKitRef.current.isSupported(); } catch (error) { return false; } }, []); const showInApp = { show: useCallback(async (options) => { return await notifications.showInApp(options); }, []), success: useCallback(async (title, message) => { return await notifications.success(title, message); }, []), error: useCallback(async (title, message) => { return await notifications.error(title, message); }, []), warning: useCallback(async (title, message) => { return await notifications.warning(title, message); }, []), info: useCallback(async (title, message) => { return await notifications.info(title, message); }, []) }; useEffect(() => { if (!notificationKitRef.current || !state.isInitialized) { return; } const unsubscribeNotification = notificationKitRef.current.on( "notificationReceived", (event) => { const notification = { id: event.notification.id, title: event.notification.title, body: event.notification.body, data: event.notification.data || {} }; updateState({ notifications: [...state.notifications, notification] }); } ); const unsubscribeToken = notificationKitRef.current.on( "tokenRefreshed", (data) => { updateState({ token: data.token ?? null }); } ); const unsubscribePermission = notificationKitRef.current.on( "permissionChanged", (data) => { updateState({ permission: data.status }); } ); const unsubscribeError = notificationKitRef.current.on("error", (data) => { updateState({ error: data.error ?? null }); }); return () => { unsubscribeNotification(); unsubscribeToken(); unsubscribePermission(); unsubscribeError(); }; }, [state.isInitialized, state.notifications, updateState]); return { ...state, init, destroy, requestPermission, checkPermission, getToken, refreshToken, subscribe, unsubscribe, scheduleNotification, cancelNotification, getPendingNotifications, createChannel, deleteChannel, listChannels, addEventListener, showInApp, clearNotifications, clearError, refresh, isSupported }; } function useInAppNotification() { const [state, setState] = useState({ activeNotifications: [], isConfigured: false, config: null }); const managerRef = useRef(null); const showCallbacksRef = useRef(/* @__PURE__ */ new Set()); const dismissCallbacksRef = useRef(/* @__PURE__ */ new Set()); const intervalRef = useRef(null); const updateState = useCallback( (updates) => { setState((prev) => ({ ...prev, ...updates })); }, [] ); const initializeManager = useCallback(() => { if (!managerRef.current) { managerRef.current = InAppNotificationManager.getInstance(); } return managerRef.current; }, []); const updateActiveNotifications = useCallback(() => { const active = getActiveInAppNotifications(); updateState({ activeNotifications: active }); }, [updateState]); const configure = useCallback( (config) => { initializeManager(); configureInAppNotifications(config); updateState({ isConfigured: true, config: { ...state.config, ...config } }); }, [initializeManager, state.config, updateState] ); const show = useCallback( async (options) => { initializeManager(); const id = await showInAppNotification(options, state.config || void 0); updateActiveNotifications(); const notification = getActiveInAppNotifications().find((n) => n.id === id); if (notification) { showCallbacksRef.current.forEach((callback) => { try { callback(notification); } catch (error2) { } }); } return id; }, [initializeManager, state.config, updateActiveNotifications] ); const success = useCallback( async (title, message, options) => { return await show({ title, message: message ?? title, type: "success", ...options }); }, [show] ); const error = useCallback( async (title, message, options) => { return await show({ title, message: message ?? title, type: "error", ...options }); }, [show] ); const warning = useCallback( async (title, message, options) => { return await show({ title, message: message ?? title, type: "warning", ...options }); }, [show] ); const info = useCallback( async (title, message, options) => { return await show({ title, message: message ?? title, type: "info", ...options }); }, [show] ); const dismiss = useCallback( async (id) => { await dismissInAppNotification(id); updateActiveNotifications(); dismissCallbacksRef.current.forEach((callback) => { try { callback(id); } catch (error2) { } }); }, [updateActiveNotifications] ); const dismissAll = useCallback(async () => { const activeIds = state.activeNotifications.map((n) => n.id); await dismissAllInAppNotifications(); updateActiveNotifications(); activeIds.forEach((id) => { dismissCallbacksRef.current.forEach((callback) => { try { callback(id); } catch (error2) { } }); }); }, [state.activeNotifications, updateActiveNotifications]); const getActive = useCallback(() => { return getActiveInAppNotifications(); }, []); const onShow = useCallback( (callback) => { showCallbacksRef.current.add(callback); return () => { showCallbacksRef.current.delete(callback); }; }, [] ); const onDismiss = useCallback((callback) => { dismissCallbacksRef.current.add(callback); return () => { dismissCallbacksRef.current.delete(callback); }; }, []); useEffect(() => { intervalRef.current = setInterval(() => { updateActiveNotifications(); }, 1e3); return () => { if (intervalRef.current) { clearInterval(intervalRef.current); } }; }, [updateActiveNotifications]); useEffect(() => { updateActiveNotifications(); }, [updateActiveNotifications]); useEffect(() => { return () => { showCallbacksRef.current.clear(); dismissCallbacksRef.current.clear(); if (intervalRef.current) { clearInterval(intervalRef.current); } }; }, []); return { ...state, configure, show, success, error, warning, info, dismiss, dismissAll, getActive, hasActive: state.activeNotifications.length > 0, activeCount: state.activeNotifications.length, onShow, onDismiss }; } function useInAppNotificationSimple() { const { show, success, error, warning, info, dismiss, dismissAll, hasActive, activeCount } = useInAppNotification(); return { show, success, error, warning, info, dismiss, dismissAll, hasActive, activeCount }; } function useInAppNotificationQueue() { const [queue, setQueue] = useState([]); const [isProcessing, setIsProcessing] = useState(false); const { show, hasActive } = useInAppNotification(); const enqueue = useCallback((options) => { setQueue((prev) => [...prev, options]); }, []); const processQueue = useCallback(async () => { if (isProcessing || queue.length === 0 || hasActive) { return; } setIsProcessing(true); try { const next = queue[0]; if (next) { await show(next); setQueue((prev) => prev.slice(1)); } } catch (error) { } finally { setIsProcessing(false); } }, [isProcessing, queue, hasActive, show]); const clearQueue = useCallback(() => { setQueue([]); }, []); useEffect(() => { if (!hasActive && queue.length > 0 && !isProcessing) { processQueue(); } }, [hasActive, queue.length, isProcessing, processQueue]); return { queue, queueLength: queue.length, isProcessing, enqueue, processQueue, clearQueue }; } function useInAppNotificationPersistence() { const [persistedNotifications, setPersistedNotifications] = useState([]); const { activeNotifications } = useInAppNotification(); const saveNotifications = useCallback(() => { try { const serialized = activeNotifications.map((n) => ({ id: n.id, options: n.options, timestamp: n.timestamp.toISOString() })); localStorage.setItem( "notification-kit-persisted", JSON.stringify(serialized) ); } catch (error) { } }, [activeNotifications]); const loadNotifications = useCallback(() => { try { const stored = localStorage.getItem("notification-kit-persisted"); if (stored) { const parsed = JSON.parse(stored); setPersistedNotifications( parsed.map((n) => ({ ...n, timestamp: new Date(n.timestamp) })) ); } } catch (error) { } }, []); const clearPersistence = useCallback(() => { try { localStorage.removeItem("notification-kit-persisted"); setPersistedNotifications([]); } catch (error) { } }, []); useEffect(() => { saveNotifications(); }, [activeNotifications, saveNotifications]); useEffect(() => { loadNotifications(); }, [loadNotifications]); return { persistedNotifications, saveNotifications, loadNotifications, clearPersistence }; } export { g as SchedulingUtils, configureInAppNotifications, dismissAllInAppNotifications, dismissInAppNotification, f as format, getActiveInAppNotifications, m as inApp, b as permissions, p as platform, showInAppNotification, s as storage, useInAppNotification, useInAppNotificationPersistence, useInAppNotificationQueue, useInAppNotificationSimple, useNotifications, v as validate }; //# sourceMappingURL=react.esm.js.map