@janiscommerce/app-push-notification 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,11 +5,13 @@ import {
5
5
  isString,
6
6
  isArray,
7
7
  isFunction,
8
- setupForegroundMessageHandler,
9
- setupNotificationOpenedHandler,
10
8
  isObject,
11
9
  prepareEventsToSubscribe,
12
10
  } from '../utils';
11
+ import {
12
+ setupForegroundMessageHandler,
13
+ setupNotificationOpenedHandler,
14
+ } from '../utils/notificationHandlers';
13
15
  import usePushNotification from '../usePushNotification';
14
16
  import {
15
17
  makeDefaultChannel,
@@ -0,0 +1,8 @@
1
+ /* istanbul ignore file */
2
+
3
+ const DEFAULT_STORAGE_CONFIG = {
4
+ maxStorageQuantity: 50,
5
+ expirationTime: 7200, // 5 days in minutes
6
+ };
7
+
8
+ export default DEFAULT_STORAGE_CONFIG;
@@ -0,0 +1,33 @@
1
+ import saveNotification from '../../utils/saveNotification';
2
+ import hasNotificationStore from '../../utils/hasNotificationStore';
3
+
4
+ /**
5
+ * Stores a notification under a type, initializing storage when needed.
6
+ *
7
+ * If the store does not exist for the given type, it saves the notification
8
+ * using the provided storageConfig (if any). Otherwise, it appends without
9
+ * passing storageConfig.
10
+ *
11
+ * @param {{ type: string, notification: object, storageConfig?: object }} params - Store parameters.
12
+ * @param {string} params.type - Storage key (notification type/channel).
13
+ * @param {object} params.notification - Notification payload to store.
14
+ * @param {object} [params.storageConfig] - Optional storage configuration for initialization.
15
+ * @returns {void}
16
+ */
17
+ const storeNotification = ({
18
+ type = '',
19
+ notification = {},
20
+ storageConfig = {},
21
+ }) => {
22
+ if (!hasNotificationStore(type)) {
23
+ saveNotification({
24
+ type,
25
+ notification,
26
+ storageConfig,
27
+ });
28
+ return;
29
+ }
30
+ saveNotification({type, notification});
31
+ };
32
+
33
+ export default storeNotification;
@@ -0,0 +1,8 @@
1
+ export {default as Storage} from './instance';
2
+ export {default as DEFAULT_STORAGE_CONFIG} from './constants/defaultStorageConfig';
3
+ export {default as saveNotification} from './utils/saveNotification';
4
+ export {default as removeOldestNotification} from './utils/removeOldestNotification';
5
+ export {default as removeStoredNotification} from './utils/removeStoredNotification';
6
+ export {default as getStoredNotification} from './utils/getStoredNotification';
7
+ export {default as storeNotification} from './helpers/storeNotification';
8
+ export {default as hasNotificationStore} from './utils/hasNotificationStore';
@@ -0,0 +1,7 @@
1
+ import MMKVStorage from '@janiscommerce/app-storage';
2
+
3
+ const Storage = new MMKVStorage({
4
+ id: 'push-notification',
5
+ });
6
+
7
+ export default Storage;
@@ -0,0 +1,25 @@
1
+ import Storage from '../../instance';
2
+
3
+ /**
4
+ * Retrieves a stored notification by type and messageId.
5
+ *
6
+ * @param {{ type: string, messageId: string|number }} params - Query parameters.
7
+ * @param {string} params.type - Storage key (notification type/channel).
8
+ * @param {string|number} params.messageId - Unique identifier of the notification.
9
+ * @returns {object} The found notification object, or an empty object when not found.
10
+ */
11
+ const getStoredNotification = ({type = '', messageId}) => {
12
+ if (!type) return {};
13
+
14
+ const storedNotifications = Storage.get(type) || [];
15
+
16
+ if (!storedNotifications?.length) return {};
17
+
18
+ const foundNotification = storedNotifications.find(
19
+ (notification) => notification.messageId === messageId,
20
+ );
21
+
22
+ return foundNotification || {};
23
+ };
24
+
25
+ export default getStoredNotification;
@@ -0,0 +1,15 @@
1
+ import Storage from '../../instance';
2
+
3
+ /**
4
+ * Checks whether there is an existing store entry for the given notification type.
5
+ *
6
+ * @param {string} notificationType - Storage key (notification type/channel).
7
+ * @returns {boolean} True if the key exists and a value is set; otherwise false.
8
+ */
9
+ const hasNotificationStore = (notificationType = '') => {
10
+ if (!Storage.db.contains(notificationType)) return false;
11
+
12
+ return !!Storage.get(notificationType);
13
+ };
14
+
15
+ export default hasNotificationStore;
@@ -0,0 +1,19 @@
1
+ import Storage from '../../instance';
2
+
3
+ /**
4
+ * Removes the oldest (first) stored notification for a given event/type.
5
+ *
6
+ * @param {string} notificationEvent - Storage key (notification type/event).
7
+ * @returns {void}
8
+ */
9
+ const removeOldestNotification = (notificationEvent = '') => {
10
+ const storedNotifications = Storage.get(notificationEvent) || [];
11
+
12
+ if (!storedNotifications?.length) return;
13
+
14
+ storedNotifications.shift();
15
+
16
+ Storage.set(notificationEvent, storedNotifications);
17
+ };
18
+
19
+ export default removeOldestNotification;
@@ -0,0 +1,29 @@
1
+ import Storage from '../../instance';
2
+
3
+ /**
4
+ * Removes a stored notification matching the given type and messageId.
5
+ *
6
+ * @param {{ type: string, messageId: string|number }} params - Removal parameters.
7
+ * @param {string} params.type - Storage key (notification type/channel).
8
+ * @param {string|number} params.messageId - Identifier of the notification to remove.
9
+ * @returns {void}
10
+ */
11
+ const removeStoredNotification = ({type = '', messageId = ''}) => {
12
+ if (!type || !messageId) return;
13
+
14
+ const storedNotifications = Storage.get(type) || [];
15
+
16
+ if (!storedNotifications?.length) return;
17
+
18
+ const notificationIndex = storedNotifications?.findIndex(
19
+ (notification) => notification.messageId === messageId,
20
+ );
21
+
22
+ if (notificationIndex < 0) return;
23
+
24
+ storedNotifications.splice(notificationIndex, 1);
25
+
26
+ Storage.set(type, storedNotifications);
27
+ };
28
+
29
+ export default removeStoredNotification;
@@ -0,0 +1,26 @@
1
+ import Storage from '../../instance';
2
+ import {isObject} from '../../../../utils';
3
+
4
+ /**
5
+ * Appends a notification to the stored list for a given type.
6
+ *
7
+ * When a storageConfig is provided, it will be forwarded to the underlying
8
+ * storage set operation.
9
+ *
10
+ * @param {{ type: string, notification: object, storageConfig?: object }} params - Save parameters.
11
+ * @param {string} params.type - Storage key (notification type/channel).
12
+ * @param {object} params.notification - Notification payload to store.
13
+ * @param {object} [params.storageConfig] - Optional storage configuration.
14
+ * @returns {void}
15
+ */
16
+ const saveNotification = ({type = '', notification = {}, storageConfig}) => {
17
+ if (!type || !isObject(notification) || !Object?.keys(notification).length)
18
+ return;
19
+
20
+ const storedNotifications = Storage.get(type) || [];
21
+ const notifications = [...storedNotifications, notification];
22
+
23
+ Storage.set(type, notifications, storageConfig);
24
+ };
25
+
26
+ export default saveNotification;
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import NotificationProvider from './NotificationProvider';
2
2
  import {usePushNotification} from './NotificationContext';
3
- import {setupBackgroundMessageHandler} from './utils';
3
+ import {setupBackgroundMessageHandler} from './utils/notificationHandlers';
4
4
  import cancelNotificationsSubscription from './utils/api/cancelNotificationsSubscription';
5
5
  import setUpdateSubscriptionByTokenRefresh from './utils/api/setUpdateSubscriptionByTokenRefresh';
6
6
 
@@ -1,7 +1,3 @@
1
- import messaging from '@react-native-firebase/messaging';
2
-
3
- // Helpers
4
-
5
1
  /**
6
2
  * @function isFunction
7
3
  * @param {function} fn
@@ -106,47 +102,3 @@ export const prepareEventsToSubscribe = (events = []) => {
106
102
 
107
103
  return events.filter((event) => !!event && isString(event));
108
104
  };
109
- // MESSAGING UTILS
110
-
111
- // esto actualmente no funciona con la versión RN de picking. Pero será necesario eventualemnte.
112
- // A partir de la versión 13 (api >= 33 ) de android el usuario tiene que otorgar los permisos manualmente.
113
- // por lo que deberemos agregarlo dentro del package futuro y sumarlo a picking una vez que esté actualizado.
114
-
115
- // export const requestUserPermission = async () => {
116
- // const permission = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
117
-
118
- // return permission;
119
- // };
120
-
121
- /** *
122
- * @function setupForegroundMessageHandler
123
- * @description This function is responsible for handling any callbacks from Firebase cloud messaging in the foreground and rendering them using a callback
124
- * @param {Function} callback is the function that will receive the payload and render it as appropriate
125
- */
126
-
127
- export const setupForegroundMessageHandler = (callback) =>
128
- messaging().onMessage(async (remoteMessage) => {
129
- callback(remoteMessage);
130
- });
131
-
132
- /** *
133
- * @function setupBackgroundMessageHandler
134
- * @description This function is responsible for handling any callbacks from Firebase cloud messaging in the background or with the application closed
135
- * @param {Function} callback is the function that will receive the payload and render it as appropriate
136
- */
137
-
138
- export const setupBackgroundMessageHandler = (callback = () => {}) =>
139
- messaging().setBackgroundMessageHandler(async (remoteMessage) => {
140
- callback(remoteMessage);
141
- });
142
-
143
- /** *
144
- * @function setupNotificationOpenedHandler
145
- * @description This function is responsible for handling any Firebase Cloud Messaging callbacks that the app will have from the background
146
- * @param {Function} callback is the function that will receive the payload and render it as appropriate
147
- */
148
-
149
- export const setupNotificationOpenedHandler = (callback) =>
150
- messaging().onNotificationOpenedApp(async (remoteMessage) => {
151
- callback(remoteMessage);
152
- });
@@ -0,0 +1,3 @@
1
+ export {default as setupForegroundMessageHandler} from './setupForegroundMessageHandler';
2
+ export {default as setupBackgroundMessageHandler} from './setupBackgroundMessageHandler';
3
+ export {default as setupNotificationOpenedHandler} from './setupNotificationOpenedHandler';
@@ -0,0 +1,42 @@
1
+ import messaging from '@react-native-firebase/messaging';
2
+ import {
3
+ DEFAULT_STORAGE_CONFIG,
4
+ storeNotification,
5
+ removeOldestNotification,
6
+ Storage,
7
+ } from '../../../entities/Storage/index';
8
+
9
+ /**
10
+ * @function setupBackgroundMessageHandler
11
+ * @description This function is responsible for handling any callbacks from Firebase cloud messaging in the background or with the application closed
12
+ * @param {object.<{notificationEvent: {maxStorageQuantity: number, expirationTime: number}}>} storageConfigs configuration for handling the storage of notifications in mmkv: this applicates
13
+
14
+ * @param {Function} callback is the function that will receive the payload and render it as appropriate
15
+ */
16
+
17
+ const setupBackgroundMessageHandler = (
18
+ storageConfigs = {},
19
+ callback = () => {},
20
+ ) =>
21
+ messaging().setBackgroundMessageHandler(async (remoteMessage) => {
22
+ const {event: type} = remoteMessage?.data || {};
23
+ const {maxStorageQuantity, expirationTime} = {
24
+ ...DEFAULT_STORAGE_CONFIG,
25
+ ...storageConfigs[type],
26
+ };
27
+ const storedQuantity = Storage.get(type)?.length || 0;
28
+
29
+ if (storedQuantity >= maxStorageQuantity) {
30
+ removeOldestNotification(type);
31
+ }
32
+
33
+ storeNotification({
34
+ type,
35
+ notification: remoteMessage,
36
+ storageConfig: {expiresAt: expirationTime},
37
+ });
38
+
39
+ await Promise.resolve(callback(remoteMessage));
40
+ });
41
+
42
+ export default setupBackgroundMessageHandler;
@@ -0,0 +1,14 @@
1
+ import messaging from '@react-native-firebase/messaging';
2
+
3
+ /**
4
+ * @function setupForegroundMessageHandler
5
+ * @description This function is responsible for handling any callbacks from Firebase cloud messaging in the foreground and rendering them using a callback
6
+ * @param {Function} callback is the function that will receive the payload and render it as appropriate
7
+ */
8
+
9
+ const setupForegroundMessageHandler = (callback) =>
10
+ messaging().onMessage(async (remoteMessage) => {
11
+ callback(remoteMessage);
12
+ });
13
+
14
+ export default setupForegroundMessageHandler;
@@ -0,0 +1,19 @@
1
+ import messaging from '@react-native-firebase/messaging';
2
+ import {removeStoredNotification} from '../../../entities/Storage';
3
+
4
+ /**
5
+ * @function setupNotificationOpenedHandler
6
+ * @description This function is responsible for handling any Firebase Cloud Messaging callbacks that the app will have from the background
7
+ * @param {Function} callback is the function that will receive the payload and render it as appropriate
8
+ */
9
+
10
+ const setupNotificationOpenedHandler = (callback) =>
11
+ messaging().onNotificationOpenedApp(async (remoteMessage) => {
12
+ const {data, messageId} = remoteMessage || {};
13
+ const {event: type = ''} = data || {};
14
+
15
+ removeStoredNotification({type, messageId});
16
+ await Promise.resolve(callback(remoteMessage));
17
+ });
18
+
19
+ export default setupNotificationOpenedHandler;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@janiscommerce/app-push-notification",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "commonjs",
5
5
  "description": "This package will take care of performing the main actions for registration to receive notifications in the foreground and background.",
6
6
  "main": "lib/index.js",
@@ -27,14 +27,15 @@
27
27
  "author": "Janis",
28
28
  "license": "ISC",
29
29
  "dependencies": {
30
+ "@notifee/react-native": "^7.8.2"
31
+ },
32
+ "peerDependencies": {
33
+ "@janiscommerce/app-storage": ">=1.0.0",
30
34
  "@janiscommerce/app-request": "^2.2.0",
31
- "@notifee/react-native": "^7.8.2",
32
35
  "@react-native-async-storage/async-storage": "^1.18.1",
33
36
  "@react-native-firebase/app": "^18.9.0",
34
37
  "@react-native-firebase/messaging": "^18.9.0",
35
- "axios": "^1.3.6"
36
- },
37
- "peerDependencies": {
38
+ "axios": "^1.3.6",
38
39
  "react": ">=17.0.2 <19.0.0",
39
40
  "react-native": ">=0.67.5 <0.75.0"
40
41
  },
@@ -46,6 +47,8 @@
46
47
  "@babel/preset-env": "^7.22.10",
47
48
  "@babel/runtime": "^7.12.5",
48
49
  "@janiscommerce/app-request": "^2.2.0",
50
+ "@janiscommerce/app-storage": "^1.0.0",
51
+ "@react-native-async-storage/async-storage": "^1.18.1",
49
52
  "@react-native-community/eslint-config": "^2.0.0",
50
53
  "@react-native-firebase/app": "^18.9.0",
51
54
  "@react-native-firebase/messaging": "^18.9.0",
@@ -69,9 +72,11 @@
69
72
  "metro-react-native-babel-preset": "^0.66.2",
70
73
  "nock": "^13.0.11",
71
74
  "prettier": "^2.4.1",
72
- "react": "^17.0.2",
73
- "react-native": "^0.67.5",
74
- "react-test-renderer": "^17.0.2"
75
+ "react": "18.2.0",
76
+ "react-native": "0.71.6",
77
+ "react-native-device-info": "^10.12.0",
78
+ "react-native-mmkv": "^2.12.2",
79
+ "react-test-renderer": "18.2.0"
75
80
  },
76
81
  "bugs": {
77
82
  "url": "https://github.com/janis-commerce/app-push-notification/issues"
package/CHANGELOG.md DELETED
@@ -1,47 +0,0 @@
1
- # Changelog
2
-
3
- ## [Unreleased]
4
-
5
- ## [0.1.0] - 2025-11-11
6
-
7
- ### Added
8
-
9
- - Support up to react 19
10
-
11
- ## [0.0.7] - 2025-07-23
12
-
13
- ### Fixed
14
-
15
- - The subscription request is only made when the user is not subscribed.
16
-
17
- ## [0.0.6] - 2025-07-13
18
-
19
- ### Added
20
-
21
- - cancelNotificationSuscription method
22
-
23
- ## [0.0.5] - 2025-06-17
24
-
25
- ### Added
26
-
27
- - Sound to notifications
28
-
29
- ## [0.0.4] - 2024-06-25
30
-
31
- ### Added
32
-
33
- - Added notification channels
34
-
35
- ## [0.0.3] - 2024-06-13
36
-
37
- ### Added
38
-
39
- - additional info to send in subscription
40
-
41
- ## [0.0.2] - 2024-05-03
42
-
43
- ### Added
44
-
45
- - NotificationProvider (HOC) to receive notifications at background and foreground
46
- - Github actions to deploy to npm
47
- - Callback to handle notification