@janiscommerce/app-push-notification 0.0.6 → 0.1.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.
- package/CHANGELOG.md +12 -0
- package/lib/NotificationProvider/index.js +47 -39
- package/lib/index.js +2 -0
- package/lib/usePushNotification.js +42 -59
- package/lib/utils/api/SubscribeNotifications/index.js +38 -35
- package/lib/utils/api/cancelNotificationsSubscription/index.js +14 -8
- package/lib/utils/api/setUpdateSubscriptionByTokenRefresh/index.js +47 -0
- package/lib/utils/index.js +14 -25
- package/lib/utils/token/index.js +48 -0
- package/package.json +11 -13
- package/lib/utils/api/UnSubscribeNotifications/index.js +0 -35
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
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
|
+
|
|
5
17
|
## [0.0.6] - 2025-07-13
|
|
6
18
|
|
|
7
19
|
### Added
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import React, {useEffect
|
|
1
|
+
import React, {useEffect} from 'react';
|
|
2
2
|
import messaging from '@react-native-firebase/messaging';
|
|
3
3
|
import {NotificationContext} from '../NotificationContext';
|
|
4
4
|
import {
|
|
5
5
|
isString,
|
|
6
6
|
isArray,
|
|
7
|
+
isFunction,
|
|
7
8
|
setupForegroundMessageHandler,
|
|
8
9
|
setupNotificationOpenedHandler,
|
|
9
10
|
isObject,
|
|
11
|
+
prepareEventsToSubscribe,
|
|
10
12
|
} from '../utils';
|
|
11
13
|
import usePushNotification from '../usePushNotification';
|
|
12
14
|
import {
|
|
@@ -14,6 +16,7 @@ import {
|
|
|
14
16
|
makeNotificationChannels,
|
|
15
17
|
parseNotificationChannel,
|
|
16
18
|
} from '../utils/channel';
|
|
19
|
+
import {getFCMToken, getStoredToken} from '../utils/token';
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
22
|
* @function NotificationProvider
|
|
@@ -25,6 +28,7 @@ import {
|
|
|
25
28
|
* @param {object} additionalInfo fields to be sent as part of the body of the subscription request
|
|
26
29
|
* @param {Array<string | object>} channelConfigs is the configuration that will be used to create new notification channels
|
|
27
30
|
* @param {string} backgroundNotificationSound is the sound that will be played when the app is in the background
|
|
31
|
+
* @param {function} onSubscriptionError is a callback that will be called when the subscription fails
|
|
28
32
|
* @throws null when not receive a children argument
|
|
29
33
|
* @returns {null | React.element}
|
|
30
34
|
* @example
|
|
@@ -44,66 +48,50 @@ import {
|
|
|
44
48
|
|
|
45
49
|
const NotificationProvider = ({
|
|
46
50
|
children,
|
|
47
|
-
appName,
|
|
51
|
+
appName = '',
|
|
48
52
|
events,
|
|
49
|
-
environment,
|
|
53
|
+
environment = '',
|
|
50
54
|
additionalInfo,
|
|
51
55
|
channelConfigs = [],
|
|
52
56
|
backgroundNotificationSound,
|
|
57
|
+
onSubscriptionError,
|
|
53
58
|
}) => {
|
|
54
59
|
if (!children) return null;
|
|
55
60
|
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
!!environment && isString(environment) ? environment : '';
|
|
59
|
-
const validEvents = !!events && isArray(events) ? events : [];
|
|
60
|
-
const validChannelConfigs =
|
|
61
|
-
!!channelConfigs && isArray(channelConfigs) ? channelConfigs : [];
|
|
62
|
-
|
|
63
|
-
const safeBackgroundSound =
|
|
64
|
-
isString(backgroundNotificationSound) && backgroundNotificationSound?.trim()
|
|
65
|
-
? backgroundNotificationSound.trim()
|
|
66
|
-
: 'default';
|
|
67
|
-
|
|
68
|
-
const isRegistered = useRef(false);
|
|
61
|
+
const parsedEvents = prepareEventsToSubscribe(events);
|
|
62
|
+
const hasChannelConfigs = isArray(channelConfigs) && channelConfigs.length;
|
|
69
63
|
const {
|
|
70
64
|
registerDeviceToNotifications,
|
|
71
65
|
updateNotificationState,
|
|
72
66
|
pushEvents,
|
|
73
67
|
...rest
|
|
74
|
-
} = usePushNotification(
|
|
75
|
-
validEnvironment,
|
|
76
|
-
validEvents,
|
|
77
|
-
validAppName,
|
|
78
|
-
isRegistered,
|
|
79
|
-
additionalInfo,
|
|
80
|
-
);
|
|
68
|
+
} = usePushNotification(String(environment), parsedEvents, String(appName));
|
|
81
69
|
|
|
82
|
-
// @function
|
|
70
|
+
// @function foregroundDataHandler
|
|
83
71
|
// @description This function is responsible for updating the state corresponding to 'foregroundNotification' with the data it receives as an argument
|
|
84
72
|
|
|
85
73
|
/* istanbul ignore next */
|
|
86
|
-
const
|
|
74
|
+
const foregroundDataHandler = (data) => {
|
|
87
75
|
if (!data || !isObject(data)) return null;
|
|
88
76
|
|
|
89
77
|
return updateNotificationState({foregroundNotification: data});
|
|
90
78
|
};
|
|
91
79
|
|
|
92
|
-
// @function
|
|
80
|
+
// @function backgroundDataHandler
|
|
93
81
|
// @description This function is responsible for updating the state corresponding to 'backgroundNotification' with the data it receives as an argument
|
|
94
82
|
|
|
95
83
|
/* istanbul ignore next */
|
|
96
|
-
const
|
|
84
|
+
const backgroundDataHandler = (data) => {
|
|
97
85
|
if (!data || !isObject(data)) return null;
|
|
98
86
|
|
|
99
87
|
return updateNotificationState({backgroundNotification: data});
|
|
100
88
|
};
|
|
101
89
|
|
|
102
|
-
// @function
|
|
90
|
+
// @function handleOpeningByNotification
|
|
103
91
|
// @description This function is responsible for saving the information of the notification that forced the opening of the app in the 'backgroundNotification' state
|
|
104
92
|
|
|
105
93
|
/* istanbul ignore next */
|
|
106
|
-
const
|
|
94
|
+
const handleOpeningByNotification = async () => {
|
|
107
95
|
const data = await messaging().getInitialNotification();
|
|
108
96
|
|
|
109
97
|
if (!data || !isObject(data)) return null;
|
|
@@ -113,22 +101,41 @@ const NotificationProvider = ({
|
|
|
113
101
|
|
|
114
102
|
const createNotificationChannels = async () => {
|
|
115
103
|
/* istanbul ignore else */
|
|
116
|
-
if (
|
|
117
|
-
const parsedChannelConfigs =
|
|
104
|
+
if (hasChannelConfigs) {
|
|
105
|
+
const parsedChannelConfigs = channelConfigs
|
|
118
106
|
?.map((config) => parseNotificationChannel(config))
|
|
119
107
|
.filter(Boolean);
|
|
120
108
|
|
|
121
109
|
await makeNotificationChannels(parsedChannelConfigs);
|
|
122
110
|
}
|
|
123
|
-
|
|
111
|
+
|
|
112
|
+
await makeDefaultChannel({
|
|
113
|
+
sound: isString(backgroundNotificationSound)
|
|
114
|
+
? backgroundNotificationSound.trim()
|
|
115
|
+
: 'default',
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const SubscribeToNotifications = async () => {
|
|
120
|
+
try {
|
|
121
|
+
const storedToken = await getStoredToken();
|
|
122
|
+
const fcmToken = await getFCMToken();
|
|
123
|
+
const isSameToken = storedToken === fcmToken;
|
|
124
|
+
|
|
125
|
+
if (Boolean(storedToken) && isSameToken) return;
|
|
126
|
+
|
|
127
|
+
await registerDeviceToNotifications({token: fcmToken, additionalInfo});
|
|
128
|
+
} catch (error) {
|
|
129
|
+
if (onSubscriptionError && isFunction(onSubscriptionError))
|
|
130
|
+
onSubscriptionError(error);
|
|
131
|
+
}
|
|
124
132
|
};
|
|
125
133
|
|
|
126
134
|
useEffect(() => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
registerDeviceToNotifications();
|
|
135
|
+
if (environment && appName && !!pushEvents.length) {
|
|
136
|
+
SubscribeToNotifications();
|
|
130
137
|
}
|
|
131
|
-
}, [
|
|
138
|
+
}, []);
|
|
132
139
|
|
|
133
140
|
useEffect(() => {
|
|
134
141
|
createNotificationChannels();
|
|
@@ -137,12 +144,13 @@ const NotificationProvider = ({
|
|
|
137
144
|
/* istanbul ignore next */
|
|
138
145
|
useEffect(() => {
|
|
139
146
|
const foregroundMessageHandler = setupForegroundMessageHandler(
|
|
140
|
-
|
|
147
|
+
foregroundDataHandler,
|
|
141
148
|
);
|
|
142
149
|
const backgroundMessageHandler = setupNotificationOpenedHandler(
|
|
143
|
-
|
|
150
|
+
backgroundDataHandler,
|
|
144
151
|
);
|
|
145
|
-
|
|
152
|
+
|
|
153
|
+
handleOpeningByNotification();
|
|
146
154
|
|
|
147
155
|
return () => {
|
|
148
156
|
foregroundMessageHandler();
|
package/lib/index.js
CHANGED
|
@@ -2,10 +2,12 @@ import NotificationProvider from './NotificationProvider';
|
|
|
2
2
|
import {usePushNotification} from './NotificationContext';
|
|
3
3
|
import {setupBackgroundMessageHandler} from './utils';
|
|
4
4
|
import cancelNotificationsSubscription from './utils/api/cancelNotificationsSubscription';
|
|
5
|
+
import setUpdateSubscriptionByTokenRefresh from './utils/api/setUpdateSubscriptionByTokenRefresh';
|
|
5
6
|
|
|
6
7
|
export {
|
|
7
8
|
usePushNotification,
|
|
8
9
|
setupBackgroundMessageHandler,
|
|
9
10
|
cancelNotificationsSubscription,
|
|
11
|
+
setUpdateSubscriptionByTokenRefresh,
|
|
10
12
|
};
|
|
11
13
|
export default NotificationProvider;
|
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {useState} from 'react';
|
|
2
2
|
import RequestInstance from '@janiscommerce/app-request';
|
|
3
|
-
import {
|
|
3
|
+
import {isArray, isString, promiseWrapper} from './utils';
|
|
4
4
|
import SubscribeNotifications from './utils/api/SubscribeNotifications';
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
events,
|
|
10
|
-
appName,
|
|
11
|
-
isRegistered,
|
|
12
|
-
additionalInfo,
|
|
13
|
-
) => {
|
|
5
|
+
import {getStoredToken, getFCMToken, updateStoredToken} from './utils/token';
|
|
6
|
+
import cancelNotificationsSubscription from './utils/api/cancelNotificationsSubscription';
|
|
7
|
+
|
|
8
|
+
const usePushNotification = (environment, events, appName) => {
|
|
14
9
|
const [notificationState, setNotificationState] = useState({
|
|
15
|
-
deviceToken: null,
|
|
16
10
|
foregroundNotification: {},
|
|
17
11
|
backgroundNotification: {},
|
|
18
12
|
pushEvents: events,
|
|
@@ -20,7 +14,6 @@ const usePushNotification = (
|
|
|
20
14
|
});
|
|
21
15
|
|
|
22
16
|
const {
|
|
23
|
-
deviceToken,
|
|
24
17
|
foregroundNotification,
|
|
25
18
|
backgroundNotification,
|
|
26
19
|
pushEvents,
|
|
@@ -29,18 +22,25 @@ const usePushNotification = (
|
|
|
29
22
|
|
|
30
23
|
const Request = new RequestInstance({JANIS_ENV: environment});
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
// istanbul ignore next line
|
|
26
|
+
const updateNotificationState = (updateState) =>
|
|
27
|
+
setNotificationState((prevState) => ({...prevState, ...updateState}));
|
|
34
28
|
|
|
35
29
|
const getSubscribedEvents = () => pushEvents;
|
|
36
30
|
|
|
31
|
+
// istanbul ignore next line
|
|
37
32
|
const getDeviceToken = async () => {
|
|
38
|
-
|
|
33
|
+
try {
|
|
34
|
+
const [storedToken] = await promiseWrapper(getStoredToken());
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
if (!fcmToken || !isString(fcmToken)) return null;
|
|
36
|
+
if (storedToken) return storedToken;
|
|
42
37
|
|
|
43
|
-
|
|
38
|
+
const fcmToken = await getFCMToken();
|
|
39
|
+
|
|
40
|
+
return fcmToken;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
/**
|
|
@@ -49,47 +49,39 @@ const usePushNotification = (
|
|
|
49
49
|
* @returns {null}
|
|
50
50
|
*/
|
|
51
51
|
|
|
52
|
-
const registerDeviceToNotifications =
|
|
53
|
-
const token =
|
|
54
|
-
|
|
55
|
-
if (!token) return null;
|
|
56
|
-
|
|
52
|
+
const registerDeviceToNotifications = async (params) => {
|
|
53
|
+
const {token = '', additionalInfo = null} = params;
|
|
57
54
|
try {
|
|
58
|
-
await SubscribeNotifications(
|
|
59
|
-
appName,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
isRegistered.current = true;
|
|
67
|
-
return updateNotificationState({deviceToken: token, pushEvents});
|
|
55
|
+
await SubscribeNotifications(
|
|
56
|
+
{token, events: pushEvents, appName, additionalInfo},
|
|
57
|
+
Request,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
await updateStoredToken(token);
|
|
61
|
+
return updateNotificationState({pushEvents});
|
|
68
62
|
} catch (error) {
|
|
69
|
-
|
|
63
|
+
updateNotificationState({pushEvents: [], subscribeError: error});
|
|
64
|
+
return Promise.reject(error);
|
|
70
65
|
}
|
|
71
|
-
}
|
|
66
|
+
};
|
|
72
67
|
|
|
73
68
|
/**
|
|
74
69
|
* @function updateSuscription
|
|
75
70
|
* @description This function is responsible for updating the subscription to the notification service
|
|
76
|
-
* @param {object}
|
|
71
|
+
* @param {object} additionalInfo all properties that will be sent as additional data in the subscription
|
|
77
72
|
* @returns {Promise}
|
|
78
73
|
*/
|
|
79
74
|
|
|
80
|
-
const updateSuscription = async (
|
|
75
|
+
const updateSuscription = async (additionalInfo) => {
|
|
81
76
|
try {
|
|
82
77
|
const token = await getDeviceToken();
|
|
83
78
|
|
|
84
|
-
|
|
85
|
-
appName,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
request: Request,
|
|
89
|
-
additionalInfo: props,
|
|
90
|
-
});
|
|
79
|
+
await SubscribeNotifications(
|
|
80
|
+
{token, events: pushEvents, appName, additionalInfo},
|
|
81
|
+
Request,
|
|
82
|
+
);
|
|
91
83
|
|
|
92
|
-
return
|
|
84
|
+
return Promise.resolve();
|
|
93
85
|
} catch (error) {
|
|
94
86
|
return Promise.reject(error);
|
|
95
87
|
}
|
|
@@ -117,21 +109,16 @@ const usePushNotification = (
|
|
|
117
109
|
const eventsAreValid = cancelEvents && isArray(cancelEvents);
|
|
118
110
|
const eventsToCancel = eventsAreValid ? cancelEvents : pushEvents;
|
|
119
111
|
try {
|
|
120
|
-
await
|
|
121
|
-
events: eventsToCancel,
|
|
122
|
-
request: Request,
|
|
123
|
-
});
|
|
112
|
+
await cancelNotificationsSubscription({events: eventsToCancel}, Request);
|
|
124
113
|
|
|
125
114
|
if (eventsAreValid) {
|
|
126
115
|
const updatedEvents = pushEvents.filter(
|
|
127
116
|
(e) => !eventsToCancel.includes(e),
|
|
128
117
|
);
|
|
129
118
|
|
|
130
|
-
isRegistered.current = true;
|
|
131
119
|
return updateNotificationState({pushEvents: updatedEvents});
|
|
132
120
|
}
|
|
133
121
|
|
|
134
|
-
isRegistered.current = false;
|
|
135
122
|
return updateNotificationState({pushEvents: []});
|
|
136
123
|
} catch (unsubscribeError) {
|
|
137
124
|
return Promise.reject(unsubscribeError);
|
|
@@ -176,18 +163,14 @@ const usePushNotification = (
|
|
|
176
163
|
*/
|
|
177
164
|
|
|
178
165
|
const addNewEvent = (event) => {
|
|
179
|
-
if (!event || !isString(event)) return
|
|
180
|
-
if (pushEvents.includes(event))
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
166
|
+
if (!event || !isString(event)) return;
|
|
167
|
+
if (pushEvents.includes(event)) return;
|
|
183
168
|
|
|
184
169
|
const updatedEvents = [...pushEvents, event];
|
|
185
|
-
|
|
186
|
-
return updateNotificationState({pushEvents: updatedEvents});
|
|
170
|
+
updateNotificationState({pushEvents: updatedEvents});
|
|
187
171
|
};
|
|
188
172
|
|
|
189
173
|
return {
|
|
190
|
-
deviceToken,
|
|
191
174
|
foregroundNotification,
|
|
192
175
|
backgroundNotification,
|
|
193
176
|
subscribeError,
|
|
@@ -1,46 +1,49 @@
|
|
|
1
|
-
import
|
|
1
|
+
import RequestInstance from '@janiscommerce/app-request';
|
|
2
|
+
import {prepareEventsToSubscribe} from '../../index';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @function SubscribeNotifications
|
|
6
|
+
* @description This function is responsible for subscribing to the notification service
|
|
7
|
+
* @param {object} params - The parameters object
|
|
8
|
+
* @param {string} params.token - The token to subscribe to
|
|
9
|
+
* @param {Array<string>} params.events - The events to subscribe to
|
|
10
|
+
* @param {string} params.appName - The name of the app
|
|
11
|
+
* @param {object} params.additionalInfo - The additional information to subscribe to
|
|
12
|
+
* @param {string} params.env - The environment of the app. It is used to make subscribe request in case of request instance is not provided
|
|
13
|
+
* @param {object} requestInstance - The request instance to make subscribe request
|
|
14
|
+
* @returns {Promise<void>} The response from the subscribe request
|
|
15
|
+
* @throws {Error} When the request fails
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const SubscribeNotifications = async (params, requestInstance = null) => {
|
|
19
|
+
const {
|
|
20
|
+
token = '',
|
|
21
|
+
events = [],
|
|
22
|
+
appName = '',
|
|
23
|
+
additionalInfo = null,
|
|
24
|
+
env = '',
|
|
25
|
+
} = params || {};
|
|
26
|
+
const Request = requestInstance || new RequestInstance({JANIS_ENV: env});
|
|
27
|
+
const parsedEvents = prepareEventsToSubscribe(events);
|
|
2
28
|
|
|
3
|
-
const SubscribeNotifications = async (params = {}) => {
|
|
4
29
|
try {
|
|
5
|
-
|
|
6
|
-
throw new Error('params is not a valid object');
|
|
7
|
-
|
|
8
|
-
const {deviceToken, events, appName, request, additionalInfo} = params;
|
|
9
|
-
|
|
10
|
-
if (!deviceToken || !isString(deviceToken))
|
|
11
|
-
throw new Error('device token is invalid or null');
|
|
12
|
-
if (!events || !isArray(events))
|
|
13
|
-
throw new Error('events to be subscribed to are null');
|
|
14
|
-
if (!appName || !isString(appName))
|
|
15
|
-
throw new Error('application name are invalid or null');
|
|
16
|
-
if (!request) throw new Error('Request is not available');
|
|
17
|
-
|
|
18
|
-
const parsedEvents = events.filter((event) => !!event && isString(event));
|
|
19
|
-
if (!parsedEvents.length)
|
|
20
|
-
throw new Error('events to be suscribed are invalids');
|
|
21
|
-
|
|
22
|
-
const validAdditionalInfo =
|
|
23
|
-
isObject(additionalInfo) && !!Object.keys(additionalInfo).length;
|
|
24
|
-
|
|
25
|
-
const body = {
|
|
26
|
-
token: deviceToken,
|
|
27
|
-
events: parsedEvents,
|
|
28
|
-
platformApplicationName: appName,
|
|
29
|
-
...(validAdditionalInfo && {
|
|
30
|
-
additionalInfo,
|
|
31
|
-
}),
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const response = await request.post({
|
|
30
|
+
await Request.post({
|
|
35
31
|
service: 'notification',
|
|
36
32
|
namespace: 'subscribe',
|
|
37
33
|
pathParams: ['push'],
|
|
38
|
-
body
|
|
34
|
+
body: {
|
|
35
|
+
token,
|
|
36
|
+
events: parsedEvents,
|
|
37
|
+
platformApplicationName: appName,
|
|
38
|
+
...(additionalInfo && {
|
|
39
|
+
additionalInfo,
|
|
40
|
+
}),
|
|
41
|
+
},
|
|
39
42
|
});
|
|
40
43
|
|
|
41
|
-
return
|
|
44
|
+
return Promise.resolve();
|
|
42
45
|
} catch (error) {
|
|
43
|
-
return Promise.reject(error
|
|
46
|
+
return Promise.reject(error);
|
|
44
47
|
}
|
|
45
48
|
};
|
|
46
49
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import RequestInstance from '@janiscommerce/app-request';
|
|
2
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Cancels push notification subscriptions for specified events
|
|
@@ -15,15 +16,16 @@ import RequestInstance from '@janiscommerce/app-request';
|
|
|
15
16
|
* });
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
|
-
const cancelNotificationsSubscription = async (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
const cancelNotificationsSubscription = async (
|
|
20
|
+
params = {},
|
|
21
|
+
requestInstance = null,
|
|
22
|
+
) => {
|
|
23
|
+
const {events = [], env = ''} = params;
|
|
24
|
+
const Request =
|
|
25
|
+
requestInstance || new RequestInstance({JANIS_ENV: String(env)});
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
try {
|
|
28
|
+
await Request.post({
|
|
27
29
|
service: 'notification',
|
|
28
30
|
namespace: 'unsubscribe',
|
|
29
31
|
pathParams: ['push'],
|
|
@@ -31,6 +33,10 @@ const cancelNotificationsSubscription = async (params = {}) => {
|
|
|
31
33
|
events,
|
|
32
34
|
},
|
|
33
35
|
});
|
|
36
|
+
|
|
37
|
+
await AsyncStorage.removeItem('currentToken');
|
|
38
|
+
|
|
39
|
+
return Promise.resolve();
|
|
34
40
|
} catch (error) {
|
|
35
41
|
return Promise.reject(error);
|
|
36
42
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/* istanbul ignore file */
|
|
2
|
+
import messaging from '@react-native-firebase/messaging';
|
|
3
|
+
import cancelNotificationsSubscription from '../cancelNotificationsSubscription';
|
|
4
|
+
import SubscribeNotifications from '../SubscribeNotifications';
|
|
5
|
+
import {promiseWrapper} from '../..';
|
|
6
|
+
import {updateStoredToken} from '../../token';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @function setUpdateSubscriptionByTokenRefresh
|
|
10
|
+
* @description This function is responsible for updating the subscription to the notification service when the token is refreshed
|
|
11
|
+
* @param {object} params - The parameters object
|
|
12
|
+
* @param {Array<string>} params.events - The events to subscribe to
|
|
13
|
+
* @param {string} params.appName - The name of the app
|
|
14
|
+
* @param {string} params.env - The environment of the app. It is used to make unsubscribe and subscribe requests in case of request instance is not provided
|
|
15
|
+
* @param {object} requestInstance - The request instance to make unsubscribe and subscribe requests
|
|
16
|
+
* @param {function} onSubscriptionError - The function to be called when the subscription fails
|
|
17
|
+
* @returns {null}
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const setUpdateSubscriptionByTokenRefresh = async (
|
|
21
|
+
params,
|
|
22
|
+
requestInstance,
|
|
23
|
+
onSubscriptionError = null,
|
|
24
|
+
) => {
|
|
25
|
+
await messaging().registerDeviceForRemoteMessages();
|
|
26
|
+
|
|
27
|
+
const unsubscribe = messaging().onTokenRefresh(async (token) => {
|
|
28
|
+
try {
|
|
29
|
+
const {events, appName, env} = params || {};
|
|
30
|
+
await promiseWrapper(
|
|
31
|
+
cancelNotificationsSubscription({events, env}, requestInstance),
|
|
32
|
+
);
|
|
33
|
+
await SubscribeNotifications(
|
|
34
|
+
{token, events, appName, env},
|
|
35
|
+
requestInstance,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
await updateStoredToken(token);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if (onSubscriptionError) onSubscriptionError(error);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return unsubscribe;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default setUpdateSubscriptionByTokenRefresh;
|
package/lib/utils/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import messaging from '@react-native-firebase/messaging';
|
|
2
|
-
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
3
2
|
|
|
4
3
|
// Helpers
|
|
5
4
|
|
|
@@ -87,37 +86,27 @@ export const promiseWrapper = (promise) =>
|
|
|
87
86
|
.then((data) => [data, null])
|
|
88
87
|
.catch((error) => Promise.resolve([null, error]));
|
|
89
88
|
|
|
90
|
-
// MESSAGING UTILS
|
|
91
|
-
|
|
92
89
|
/**
|
|
93
|
-
* @function
|
|
94
|
-
* @
|
|
95
|
-
* @
|
|
90
|
+
* @function prepareEventsToSubscribe
|
|
91
|
+
* @param {array} events
|
|
92
|
+
* @description parse events to subscribe
|
|
93
|
+
* @returns {array}
|
|
96
94
|
* @example
|
|
97
|
-
*
|
|
98
|
-
*
|
|
95
|
+
* prepareEventsToSubscribe(['event1', 'event2', 3, null]) // ['event1', 'event2']
|
|
96
|
+
* prepareEventsToSubscribe(null) // []
|
|
97
|
+
* prepareEventsToSubscribe('event1') // ['event1']
|
|
99
98
|
*/
|
|
100
99
|
|
|
101
|
-
export const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (!fcmToken) {
|
|
106
|
-
const newFcmToken = await messaging().getToken();
|
|
107
|
-
|
|
108
|
-
if (newFcmToken) {
|
|
109
|
-
await AsyncStorage.setItem('fcmtoken', newFcmToken);
|
|
110
|
-
return newFcmToken;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
100
|
+
export const prepareEventsToSubscribe = (events = []) => {
|
|
101
|
+
if (!isArray(events)) {
|
|
102
|
+
events = [events].filter(Boolean);
|
|
103
|
+
}
|
|
113
104
|
|
|
114
|
-
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error('error', error.message);
|
|
105
|
+
if (!events.length) return [];
|
|
117
106
|
|
|
118
|
-
|
|
119
|
-
}
|
|
107
|
+
return events.filter((event) => !!event && isString(event));
|
|
120
108
|
};
|
|
109
|
+
// MESSAGING UTILS
|
|
121
110
|
|
|
122
111
|
// esto actualmente no funciona con la versión RN de picking. Pero será necesario eventualemnte.
|
|
123
112
|
// A partir de la versión 13 (api >= 33 ) de android el usuario tiene que otorgar los permisos manualmente.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import messaging from '@react-native-firebase/messaging';
|
|
2
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @function getFCMToken
|
|
6
|
+
* @description This function is responsible for getting the FCM token
|
|
7
|
+
* @returns {Promise<string>} The FCM token
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const getFCMToken = async () => {
|
|
11
|
+
try {
|
|
12
|
+
const fcmToken = await messaging().getToken();
|
|
13
|
+
return fcmToken;
|
|
14
|
+
} catch (error) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @function getStoredToken
|
|
21
|
+
* @description This function is responsible for getting the stored token from AsyncStorage
|
|
22
|
+
* @returns {Promise<string>} The stored token
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
export const getStoredToken = async () => {
|
|
26
|
+
try {
|
|
27
|
+
const storedToken = await AsyncStorage.getItem('currentToken');
|
|
28
|
+
return storedToken;
|
|
29
|
+
} catch (error) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @function updateStoredToken
|
|
36
|
+
* @description This function is responsible for updating the stored token in AsyncStorage
|
|
37
|
+
* @param {string} token - The token to be stored
|
|
38
|
+
* @returns {Promise<string>} The stored token
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
export const updateStoredToken = async (token) => {
|
|
42
|
+
try {
|
|
43
|
+
await AsyncStorage.setItem('currentToken', token);
|
|
44
|
+
return token;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
};
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@janiscommerce/app-push-notification",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.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",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"test": "jest",
|
|
8
|
+
"test": "jest --forceExit",
|
|
9
9
|
"lint": "eslint .",
|
|
10
10
|
"build-docs": "jsdoc2md --template template-readme.hbs --files lib/NotificationProvider/index.js lib/NotificationContext.js > README.md",
|
|
11
|
-
"test:coverage": "jest --collectCoverage",
|
|
12
|
-
"validate:code": "npm run lint -- --fix && jest --collectCoverage"
|
|
11
|
+
"test:coverage": "jest --collectCoverage --forceExit",
|
|
12
|
+
"validate:code": "npm run lint -- --fix && jest --collectCoverage --forceExit"
|
|
13
13
|
},
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
@@ -30,14 +30,13 @@
|
|
|
30
30
|
"@janiscommerce/app-request": "^2.2.0",
|
|
31
31
|
"@notifee/react-native": "^7.8.2",
|
|
32
32
|
"@react-native-async-storage/async-storage": "^1.18.1",
|
|
33
|
-
"@react-native-firebase/app": "^18.
|
|
34
|
-
"@react-native-firebase/messaging": "^18.
|
|
35
|
-
"axios": "^1.3.6"
|
|
36
|
-
"react-native-device-info": "^8.5.0"
|
|
33
|
+
"@react-native-firebase/app": "^18.9.0",
|
|
34
|
+
"@react-native-firebase/messaging": "^18.9.0",
|
|
35
|
+
"axios": "^1.3.6"
|
|
37
36
|
},
|
|
38
37
|
"peerDependencies": {
|
|
39
|
-
"react": ">=17.0.2
|
|
40
|
-
"react-native": ">=0.67.5
|
|
38
|
+
"react": ">=17.0.2 <19.0.0",
|
|
39
|
+
"react-native": ">=0.67.5 <0.75.0"
|
|
41
40
|
},
|
|
42
41
|
"devDependencies": {
|
|
43
42
|
"@babel/core": "^7.22.10",
|
|
@@ -48,8 +47,8 @@
|
|
|
48
47
|
"@babel/runtime": "^7.12.5",
|
|
49
48
|
"@janiscommerce/app-request": "^2.2.0",
|
|
50
49
|
"@react-native-community/eslint-config": "^2.0.0",
|
|
51
|
-
"@react-native-firebase/app": "^18.
|
|
52
|
-
"@react-native-firebase/messaging": "^18.
|
|
50
|
+
"@react-native-firebase/app": "^18.9.0",
|
|
51
|
+
"@react-native-firebase/messaging": "^18.9.0",
|
|
53
52
|
"@testing-library/react-native": "^12.0.1",
|
|
54
53
|
"babel-eslint": "^10.1.0",
|
|
55
54
|
"babel-jest": "^26.6.3",
|
|
@@ -72,7 +71,6 @@
|
|
|
72
71
|
"prettier": "^2.4.1",
|
|
73
72
|
"react": "^17.0.2",
|
|
74
73
|
"react-native": "^0.67.5",
|
|
75
|
-
"react-native-device-info": "^8.5.0",
|
|
76
74
|
"react-test-renderer": "^17.0.2"
|
|
77
75
|
},
|
|
78
76
|
"bugs": {
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import {isString, isArray} from '../../index';
|
|
2
|
-
|
|
3
|
-
const UnSubscribeNotifications = async (params = {}) => {
|
|
4
|
-
try {
|
|
5
|
-
if (!params || !Object.keys(params).length)
|
|
6
|
-
throw new Error('params is not a valid object');
|
|
7
|
-
|
|
8
|
-
const {events, request} = params;
|
|
9
|
-
|
|
10
|
-
if (!events || !isArray(events))
|
|
11
|
-
throw new Error('events to be subscribed to are null');
|
|
12
|
-
if (!request) throw new Error('Request is not available');
|
|
13
|
-
|
|
14
|
-
const parsedEvents = events.filter((event) => !!event && isString(event));
|
|
15
|
-
if (!parsedEvents.length)
|
|
16
|
-
throw new Error('events to be suscribed are invalids');
|
|
17
|
-
|
|
18
|
-
const body = {
|
|
19
|
-
events: parsedEvents,
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const response = await request.post({
|
|
23
|
-
service: 'notification',
|
|
24
|
-
namespace: 'unsubscribe',
|
|
25
|
-
pathParams: ['push'],
|
|
26
|
-
body,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
return response;
|
|
30
|
-
} catch (error) {
|
|
31
|
-
return Promise.reject(error?.result || error);
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export default UnSubscribeNotifications;
|