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
JavaScript
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