@kode-frontend/react-native-push-notification
Version:
Push notification service for React Native apps
631 lines (620 loc) • 21.5 kB
JavaScript
;
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;