UNPKG

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

Version:
631 lines (620 loc) 21.5 kB
'use strict'; var effector = require('effector'); var async = require('effector-storage/rn/async'); var reactNative = require('react-native'); var effectorReact = require('effector-react'); var React2 = require('react'); var notifee = require('@notifee/react-native'); var messaging = require('@react-native-firebase/messaging'); var uuid = require('uuid'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var React2__default = /*#__PURE__*/_interopDefault(React2); var notifee__default = /*#__PURE__*/_interopDefault(notifee); var messaging__default = /*#__PURE__*/_interopDefault(messaging); 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 = reactNative.Platform.OS === "android" && reactNative.Platform.Version < 33; // src/model/push-settings.ts var initialState = { fcmToken: null, isNotificationEnabled: isPermisionsGrantedByDefault, needFirstRequestPermission: true }; var $pushSettings = effector.createStore(initialState); var updateFcmToken = effector.createEvent(); var enablePush = effector.createEvent(); var disablePush = effector.createEvent(); var skipRequestPermissions = effector.createEvent(); var resetPushSettings = effector.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 })); async.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__default.default.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 === notifee.AuthorizationStatus.AUTHORIZED || authorizationStatus === notifee.AuthorizationStatus.PROVISIONAL; }; function hasPermission() { return __async(this, null, function* () { const status = yield notifee__default.default.getNotificationSettings(); return isPermitted(status.authorizationStatus); }); } function requestPermission() { return __async(this, null, function* () { const status = yield notifee__default.default.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__default.default.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__default.default.cancelAllNotifications(notificationIds)); } catch (e) { reject(e); } })); } function cancelNotification(notificationId) { return new Promise((resolve, reject) => { try { resolve(notifee__default.default.cancelNotification(notificationId)); } catch (e) { reject(e); } }); } function getInitialPush() { return new Promise((resolve, reject) => { try { if (initialRemotePushWasGetting) { return resolve(null); } const initialPush = messaging__default.default().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__default.default.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__default.default().getToken(); return fcmToken; } catch (e) { } return fcmToken; }); var removeToken = () => __async(void 0, null, function* () { yield messaging__default.default().deleteToken(); }); var openSettings = reactNative.Platform.select({ ios: reactNative.Linking.openSettings, android: notifee__default.default.openNotificationSettings }); var setApplicationIconBadgeNumber = (number) => { try { notifee__default.default.setBadgeCount(number); } catch (e) { } }; var incrementApplicationIconBadgeNumber = () => { try { notifee__default.default.incrementBadgeCount(); } catch (e) { } }; var decrementApplicationIconBadgeNumber = () => { try { notifee__default.default.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 reactNative.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 reactNative.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: notifee.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 = reactNative.Platform.OS === "android"; var PushNotificationService = ({ channelId, channelName, children }) => { const subscriptions = React2__default.default.useRef({}); const lastOpened = React2__default.default.useRef(null); const lastReceived = React2__default.default.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__default.default.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__default.default().onNotificationOpenedApp((remoteMessage) => { if (remoteMessage && isAndroid) { const pushNotification = mapRemotePushToLocalPush(remoteMessage); onOpenNotification(pushNotification); } }); const onForegroundEventUnsubscribe = notifee__default.default.onForegroundEvent( (_0) => __async(void 0, [_0], function* ({ type, detail }) { if (!detail.notification) { return; } const pushNotification = detail.notification; switch (type) { case notifee.EventType.DELIVERED: onReceiveNotification(pushNotification); break; case notifee.EventType.PRESS: onOpenNotification(pushNotification); break; } }) ); notifee__default.default.onBackgroundEvent((_0) => __async(void 0, [_0], function* ({ type, detail }) { if (!detail.notification) { return; } const pushNotification = detail.notification; switch (type) { case notifee.EventType.DELIVERED: onReceiveNotification(pushNotification); break; case notifee.EventType.PRESS: onOpenNotification(pushNotification); break; } })); yield notifee__default.default.createChannel({ id: channelId, name: channelName, sound: "default", lights: true, vibration: true, importance: notifee.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__default.default.displayNotification(__spreadProps(__spreadValues({}, pushNotification), { ios: { attachments: iosAttachment, badgeCount: iosBadgeCount }, android: __spreadProps(__spreadValues({ channelId, importance: notifee.AndroidImportance.HIGH, smallIcon: "ic_small_icon" }, androidAttachment ? { largeIcon: androidAttachment.picture, style: androidAttachment } : { style: { type: notifee.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__default.default().onMessage(onMessageReceived); const unsubscribe2 = () => { onNotificationOpenedAppUnsubscribe(); onForegroundEventUnsubscribe(); onMessageReceivedUnsubscribe(); }; return unsubscribe2; }); const unsubscribe = init(); return () => { unsubscribe.then((result) => result()); }; }, [channelId, channelName]); const value = React2.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__default.default.createElement(PushNotificationServiceContext.Provider, { value }, children); }; function usePush({ condition, onReceived, onOpened }) { const mounted = React2__default.default.useRef(false); const { subscribe, unsubscribe } = React2__default.default.useContext( PushNotificationServiceContext ); const uuid$1 = React2__default.default.useMemo(() => uuid.v4(), []); const onReceive = React2.useCallback( (notification) => __async(this, null, function* () { if (condition(notification)) { yield onReceived(notification); return true; } return false; }), [condition, onReceived] ); const onOpen = React2.useCallback( (notification) => __async(this, null, function* () { if (condition(notification)) { yield onOpened(notification); return true; } return false; }), [condition, onOpened] ); React2__default.default.useEffect(() => { subscribe(uuid$1, !mounted.current, { onReceive, onOpen }); mounted.current = true; return () => { unsubscribe(uuid$1); }; }, [onReceive, onOpen, subscribe, unsubscribe, uuid$1]); return null; } // src/model/hooks/use-register-device.ts var useRegisterDevice = ({ isNotificationsAvailable, updatePushToken }) => { const { fcmToken, isNotificationEnabled } = effectorReact.useStore($pushSettings); const register = React2.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 ] ); React2.useEffect(() => { register(); const focusEvent = reactNative.Platform.OS === "android" ? "focus" : "change"; const onChangeAppState = () => { if (reactNative.AppState.currentState === "active") { register(); } }; const subscription = reactNative.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] = React2.useState(false); const enabledPush = effectorReact.useStore($pushEnabled); const disablePush2 = effectorReact.useEvent(disablePush); const enablePush2 = effectorReact.useEvent(enablePush); const [hasPermissions, setHasPermissions] = React2.useState(enabledPush); const [enabledPushNotifications, setEnabledPushNotifications] = React2.useState(enabledPush); const { unregister: unregister2 } = useUnregisterDevice(); React2.useEffect(() => { setEnabledPushNotifications(hasPermissions && enabledPush); }, [hasPermissions, enabledPush]); React2.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 = reactNative.AppState.addEventListener("change", callback); return () => { subscription.remove(); }; }, []); const changeEnabledPushesHandler = React2.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 }; }; exports.$fcmToken = $fcmToken; exports.$needFirstRequestPermission = $needFirstRequestPermission; exports.$pushEnabled = $pushEnabled; exports.$pushSettings = $pushSettings; exports.PushNotificationService = PushNotificationService; exports.disablePush = disablePush; exports.enablePush = enablePush; exports.pushService = utils_exports; exports.resetPushSettings = resetPushSettings; exports.skipRequestPermissions = skipRequestPermissions; exports.updateFcmToken = updateFcmToken; exports.usePush = usePush; exports.usePushAvailable = usePushAvailable; exports.useRegisterDevice = useRegisterDevice; exports.useUnregisterDevice = useUnregisterDevice;