UNPKG

@kode-frontend/react-native-push-notification

Version:
609 lines (601 loc) 20.1 kB
import { createStore, createEvent } from 'effector'; import { persist } from 'effector-storage/rn/async'; import { Platform, Linking, AppState, Image } from 'react-native'; import { useStore, useEvent } from 'effector-react'; import React2, { useMemo, useCallback, useEffect, useState } from 'react'; import notifee, { AndroidStyle, EventType, AndroidImportance, AuthorizationStatus } from '@notifee/react-native'; import messaging from '@react-native-firebase/messaging'; import { v4 } from 'uuid'; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; var isPermisionsGrantedByDefault = Platform.OS === "android" && Platform.Version < 33; // src/model/push-settings.ts var initialState = { fcmToken: null, isNotificationEnabled: isPermisionsGrantedByDefault, needFirstRequestPermission: true }; var $pushSettings = createStore(initialState); var updateFcmToken = createEvent(); var enablePush = createEvent(); var disablePush = createEvent(); var skipRequestPermissions = createEvent(); var resetPushSettings = createEvent(); $pushSettings.on(updateFcmToken, (state, fcmToken) => __spreadProps(__spreadValues({}, state), { fcmToken })).on(enablePush, (state) => __spreadProps(__spreadValues({}, state), { isNotificationEnabled: true, needFirstRequestPermission: false })).on(disablePush, (state) => __spreadProps(__spreadValues({}, state), { isNotificationEnabled: false })).on(skipRequestPermissions, (state) => __spreadProps(__spreadValues({}, state), { isNotificationEnabled: false, needFirstRequestPermission: false })).on(resetPushSettings, (state) => __spreadProps(__spreadValues({}, initialState), { needFirstRequestPermission: state.needFirstRequestPermission, isNotificationEnabled: state.isNotificationEnabled })); persist({ store: $pushSettings, key: "pushSettings" }); var $pushEnabled = $pushSettings.map( (settings) => Boolean(settings.isNotificationEnabled && settings.fcmToken) ); var $fcmToken = $pushSettings.map((settings) => settings.fcmToken); var $needFirstRequestPermission = $pushSettings.map( (settings) => settings.needFirstRequestPermission ); var PushNotificationServiceContext = React2.createContext({ subscribe: () => null, unsubscribe: () => null }); // src/push-notification-service/utils.ts var utils_exports = {}; __export(utils_exports, { cancelAllNotifications: () => cancelAllNotifications, cancelNotification: () => cancelNotification, checkPermission: () => checkPermission, decrementApplicationIconBadgeNumber: () => decrementApplicationIconBadgeNumber, getAndroidAttachment: () => getAndroidAttachment, getInitialLocalPush: () => getInitialLocalPush, getInitialPush: () => getInitialPush, getIosAttachment: () => getIosAttachment, getScheduledLocalNotifications: () => getScheduledLocalNotifications, getToken: () => getToken, hasPermission: () => hasPermission, incrementApplicationIconBadgeNumber: () => incrementApplicationIconBadgeNumber, mapRemotePushToLocalPush: () => mapRemotePushToLocalPush, openSettings: () => openSettings, removeToken: () => removeToken, requestPermission: () => requestPermission, setApplicationIconBadgeNumber: () => setApplicationIconBadgeNumber }); var initialRemotePushWasGetting = false; var initialLocalPushWasGetting = false; var isPermitted = (authorizationStatus) => { return authorizationStatus === AuthorizationStatus.AUTHORIZED || authorizationStatus === AuthorizationStatus.PROVISIONAL; }; function hasPermission() { return __async(this, null, function* () { const status = yield notifee.getNotificationSettings(); return isPermitted(status.authorizationStatus); }); } function requestPermission() { return __async(this, null, function* () { const status = yield notifee.requestPermission(); return isPermitted(status.authorizationStatus); }); } function checkPermission() { return __async(this, null, function* () { const enabledNotifications = yield hasPermission(); if (!enabledNotifications && !isPermisionsGrantedByDefault) { return yield requestPermission(); } return enabledNotifications; }); } var createUUIDService = () => { let id = 0; return { getUuid: () => { return String(++id); } }; }; var UUIDService = createUUIDService(); function getScheduledLocalNotifications() { return new Promise((resolve, reject) => { try { const notifications = notifee.getTriggerNotifications(); resolve(notifications); } catch (e) { reject(e); } }); } function cancelAllNotifications() { return new Promise((resolve, reject) => __async(this, null, function* () { try { const notifications = yield getScheduledLocalNotifications(); const notificationIds = notifications.map( (item) => { var _a; return (_a = item.notification.id) != null ? _a : ""; } ); resolve(notifee.cancelAllNotifications(notificationIds)); } catch (e) { reject(e); } })); } function cancelNotification(notificationId) { return new Promise((resolve, reject) => { try { resolve(notifee.cancelNotification(notificationId)); } catch (e) { reject(e); } }); } function getInitialPush() { return new Promise((resolve, reject) => { try { if (initialRemotePushWasGetting) { return resolve(null); } const initialPush = messaging().getInitialNotification(); resolve(initialPush); initialRemotePushWasGetting = true; } catch (e) { reject(e); } }); } function getInitialLocalPush() { return new Promise((resolve, reject) => { try { if (initialLocalPushWasGetting) { return resolve(null); } const initialPush = notifee.getInitialNotification(); resolve(initialPush); initialLocalPushWasGetting = true; } catch (e) { reject(e); } }); } var getToken = () => __async(void 0, null, function* () { try { const enabledNotifications = yield checkPermission(); if (!enabledNotifications) { return null; } } catch (e) { return null; } let fcmToken = null; try { fcmToken = yield messaging().getToken(); return fcmToken; } catch (e) { } return fcmToken; }); var removeToken = () => __async(void 0, null, function* () { yield messaging().deleteToken(); }); var openSettings = Platform.select({ ios: Linking.openSettings, android: notifee.openNotificationSettings }); var setApplicationIconBadgeNumber = (number) => { try { notifee.setBadgeCount(number); } catch (e) { } }; var incrementApplicationIconBadgeNumber = () => { try { notifee.incrementBadgeCount(); } catch (e) { } }; var decrementApplicationIconBadgeNumber = () => { try { notifee.decrementBadgeCount(); } catch (e) { } }; var getIosAttachment = (message) => __async(void 0, null, function* () { var _a, _b, _c, _d; if ((_b = (_a = message.data) == null ? void 0 : _a.fcm_options) == null ? void 0 : _b.image) { try { yield Image.prefetch(message.data.fcm_options.image); } catch (e) { delete message.data.fcm_options.image; } } const attachment = ((_d = (_c = message.data) == null ? void 0 : _c.fcm_options) == null ? void 0 : _d.image) ? [ { url: message.data.fcm_options.image } ] : []; return attachment; }); var getAndroidAttachment = (message) => __async(void 0, null, function* () { var _a, _b, _c, _d; if ((_b = (_a = message.notification) == null ? void 0 : _a.android) == null ? void 0 : _b.imageUrl) { try { yield Image.prefetch(message.notification.android.imageUrl); } catch (e) { delete message.notification.android.imageUrl; } } const attachment = ((_d = (_c = message.notification) == null ? void 0 : _c.android) == null ? void 0 : _d.imageUrl) ? { type: AndroidStyle.BIGPICTURE, picture: message.notification.android.imageUrl } : void 0; return attachment; }); var mapRemotePushToLocalPush = (remotePush) => { var _a, _b, _c, _d; const pushNotification = { id: UUIDService.getUuid(), title: (_b = (_a = remotePush.notification) == null ? void 0 : _a.title) != null ? _b : "", body: (_d = (_c = remotePush.notification) == null ? void 0 : _c.body) != null ? _d : "", data: remotePush.data }; return pushNotification; }; // src/push-notification-service/push-notification-service.tsx var isAndroid = Platform.OS === "android"; var PushNotificationService = ({ channelId, channelName, children }) => { const subscriptions = React2.useRef({}); const lastOpened = React2.useRef(null); const lastReceived = React2.useRef(null); const onOpenNotification = (pushNotification) => __async(void 0, null, function* () { lastOpened.current = pushNotification; const onOpenResults = Object.values(subscriptions.current).map( (e) => e.onOpen(pushNotification) ); if ((yield Promise.all(onOpenResults)).filter(Boolean).length) { lastOpened.current = null; } }); const onReceiveNotification = (pushNotification) => __async(void 0, null, function* () { lastReceived.current = pushNotification; const onReceiveResults = Object.values(subscriptions.current).map( (e) => e.onReceive(pushNotification) ); if ((yield Promise.all(onReceiveResults)).filter(Boolean).length) { lastReceived.current = null; } }); React2.useEffect(() => { const init = () => __async(void 0, null, function* () { const initialPush = yield getInitialPush(); if (initialPush && isAndroid) { const pushNotification = mapRemotePushToLocalPush(initialPush); onOpenNotification(pushNotification); } const initialLocalPush = yield getInitialLocalPush(); if (initialLocalPush && isAndroid) { onOpenNotification(initialLocalPush.notification); } const onNotificationOpenedAppUnsubscribe = messaging().onNotificationOpenedApp((remoteMessage) => { if (remoteMessage && isAndroid) { const pushNotification = mapRemotePushToLocalPush(remoteMessage); onOpenNotification(pushNotification); } }); const onForegroundEventUnsubscribe = notifee.onForegroundEvent( (_0) => __async(void 0, [_0], function* ({ type, detail }) { if (!detail.notification) { return; } const pushNotification = detail.notification; switch (type) { case EventType.DELIVERED: onReceiveNotification(pushNotification); break; case EventType.PRESS: onOpenNotification(pushNotification); break; } }) ); notifee.onBackgroundEvent((_0) => __async(void 0, [_0], function* ({ type, detail }) { if (!detail.notification) { return; } const pushNotification = detail.notification; switch (type) { case EventType.DELIVERED: onReceiveNotification(pushNotification); break; case EventType.PRESS: onOpenNotification(pushNotification); break; } })); yield notifee.createChannel({ id: channelId, name: channelName, sound: "default", lights: true, vibration: true, importance: AndroidImportance.HIGH }); const onMessageReceived = (remoteMessage) => __async(void 0, null, function* () { var _a, _b, _c; if (!remoteMessage.notification) { return; } const pushNotification = mapRemotePushToLocalPush(remoteMessage); const iosAttachment = yield getIosAttachment(remoteMessage); const androidAttachment = yield getAndroidAttachment(remoteMessage); const iosBadgeCount = Number((_b = (_a = remoteMessage.notification.ios) == null ? void 0 : _a.badge) != null ? _b : 0); notifee.displayNotification(__spreadProps(__spreadValues({}, pushNotification), { ios: { attachments: iosAttachment, badgeCount: iosBadgeCount }, android: __spreadProps(__spreadValues({ channelId, importance: AndroidImportance.HIGH, smallIcon: "ic_small_icon" }, androidAttachment ? { largeIcon: androidAttachment.picture, style: androidAttachment } : { style: { type: AndroidStyle.BIGTEXT, text: (_c = pushNotification.body) != null ? _c : "" } }), { // pressAction is needed if you want the notification to open the app when pressed pressAction: { id: "default" } }) })); }); const onMessageReceivedUnsubscribe = messaging().onMessage(onMessageReceived); const unsubscribe2 = () => { onNotificationOpenedAppUnsubscribe(); onForegroundEventUnsubscribe(); onMessageReceivedUnsubscribe(); }; return unsubscribe2; }); const unsubscribe = init(); return () => { unsubscribe.then((result) => result()); }; }, [channelId, channelName]); const value = useMemo( () => ({ subscribe: (uuid, mounted, events) => __async(void 0, null, function* () { subscriptions.current[uuid] = events; if (lastOpened.current && mounted && !lastReceived.current && (yield events.onOpen(lastOpened.current))) { lastOpened.current = null; } if (lastReceived.current && mounted && (yield events.onReceive(lastReceived.current))) { lastReceived.current = null; } }), unsubscribe: (uuid) => { delete subscriptions.current[uuid]; } }), [] ); return /* @__PURE__ */ React2.createElement(PushNotificationServiceContext.Provider, { value }, children); }; function usePush({ condition, onReceived, onOpened }) { const mounted = React2.useRef(false); const { subscribe, unsubscribe } = React2.useContext( PushNotificationServiceContext ); const uuid = React2.useMemo(() => v4(), []); const onReceive = useCallback( (notification) => __async(this, null, function* () { if (condition(notification)) { yield onReceived(notification); return true; } return false; }), [condition, onReceived] ); const onOpen = useCallback( (notification) => __async(this, null, function* () { if (condition(notification)) { yield onOpened(notification); return true; } return false; }), [condition, onOpened] ); React2.useEffect(() => { subscribe(uuid, !mounted.current, { onReceive, onOpen }); mounted.current = true; return () => { unsubscribe(uuid); }; }, [onReceive, onOpen, subscribe, unsubscribe, uuid]); return null; } // src/model/hooks/use-register-device.ts var useRegisterDevice = ({ isNotificationsAvailable, updatePushToken }) => { const { fcmToken, isNotificationEnabled } = useStore($pushSettings); const register = useCallback( (..._0) => __async(void 0, [..._0], function* ({ force } = {}) { if (!isNotificationsAvailable || !isNotificationEnabled) { return; } try { const token = yield utils_exports.getToken(); if (!token) { return; } if (fcmToken !== token || force) { updatePushToken(token, { onSuccess: () => { updateFcmToken(token); } }); } } catch (e) { } }), [ isNotificationsAvailable, isNotificationEnabled, fcmToken, updatePushToken ] ); useEffect(() => { register(); const focusEvent = Platform.OS === "android" ? "focus" : "change"; const onChangeAppState = () => { if (AppState.currentState === "active") { register(); } }; const subscription = AppState.addEventListener(focusEvent, onChangeAppState); return () => { subscription.remove(); }; }, [register]); return { registerDevice: register }; }; // src/model/hooks/use-unregister-device.ts var unregister = () => __async(void 0, null, function* () { try { yield utils_exports.removeToken(); updateFcmToken(null); } catch (e) { } }); var useUnregisterDevice = () => { return { unregister }; }; var usePushAvailable = ({ onFailEnabledPush }) => { const [isLoading, setLoaderState] = useState(false); const enabledPush = useStore($pushEnabled); const disablePush2 = useEvent(disablePush); const enablePush2 = useEvent(enablePush); const [hasPermissions, setHasPermissions] = useState(enabledPush); const [enabledPushNotifications, setEnabledPushNotifications] = useState(enabledPush); const { unregister: unregister2 } = useUnregisterDevice(); useEffect(() => { setEnabledPushNotifications(hasPermissions && enabledPush); }, [hasPermissions, enabledPush]); useEffect(() => { const checkPermissions = () => __async(void 0, null, function* () { const hasPermission2 = yield utils_exports.hasPermission(); setHasPermissions(hasPermission2); }); checkPermissions(); const callback = (state) => { if (state === "active") { checkPermissions(); } }; const subscription = AppState.addEventListener("change", callback); return () => { subscription.remove(); }; }, []); const changeEnabledPushesHandler = useCallback( (state) => __async(void 0, null, function* () { setLoaderState(true); try { if (!state) { setEnabledPushNotifications(false); disablePush2(); unregister2(); utils_exports.cancelAllNotifications(); return; } const hasPermission2 = yield utils_exports.checkPermission(); if (hasPermission2) { setEnabledPushNotifications(true); } else { onFailEnabledPush(); } enablePush2(); } finally { setLoaderState(false); } }), [onFailEnabledPush, enablePush2, unregister2, disablePush2] ); return { enabled: enabledPushNotifications, isLoading, changeNotificationEnabled: changeEnabledPushesHandler }; }; export { $fcmToken, $needFirstRequestPermission, $pushEnabled, $pushSettings, PushNotificationService, disablePush, enablePush, utils_exports as pushService, resetPushSettings, skipRequestPermissions, updateFcmToken, usePush, usePushAvailable, useRegisterDevice, useUnregisterDevice };