@janiscommerce/app-push-notification 0.1.0 → 0.2.0-beta.1
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.
- package/lib/NotificationProvider/index.js +4 -2
- package/lib/entities/Storage/constants/defaultStorageConfig.js +8 -0
- package/lib/entities/Storage/helpers/storeNotification/index.js +33 -0
- package/lib/entities/Storage/index.js +8 -0
- package/lib/entities/Storage/instance/index.js +7 -0
- package/lib/entities/Storage/utils/getStoredNotification/index.js +25 -0
- package/lib/entities/Storage/utils/hasNotificationStore/index.js +15 -0
- package/lib/entities/Storage/utils/removeOldestNotification/index.js +19 -0
- package/lib/entities/Storage/utils/removeStoredNotification/index.js +29 -0
- package/lib/entities/Storage/utils/saveNotification/index.js +26 -0
- package/lib/index.js +1 -1
- package/lib/utils/index.js +0 -48
- package/lib/utils/notificationHandlers/index.js +3 -0
- package/lib/utils/notificationHandlers/setupBackgroundMessageHandler/index.js +42 -0
- package/lib/utils/notificationHandlers/setupForegroundMessageHandler/index.js +14 -0
- package/lib/utils/notificationHandlers/setupNotificationOpenedHandler/index.js +19 -0
- package/package.json +13 -8
- package/CHANGELOG.md +0 -47
|
@@ -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,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,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
|
|
package/lib/utils/index.js
CHANGED
|
@@ -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,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.
|
|
3
|
+
"version": "0.2.0-beta.1",
|
|
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": "
|
|
73
|
-
"react-native": "
|
|
74
|
-
"react-
|
|
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
|