@janiscommerce/app-push-notification 0.0.1-beta.1 → 0.0.2

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 CHANGED
@@ -2,7 +2,10 @@
2
2
 
3
3
  [Unreleased]
4
4
 
5
+ ## [0.0.1] - 2024-05-03
6
+
5
7
  ### Added
6
8
 
7
- - Added NotificationProvider (HOC) to receive notifications at background and foreground.\
8
- - Added github actions to deploy to npm
9
+ - NotificationProvider (HOC) to receive notifications at background and foreground
10
+ - Github actions to deploy to npm
11
+ - Callback to handle notification
package/README.md CHANGED
@@ -29,12 +29,18 @@ Inside remoteMessage you get the notifications object that contains the informat
29
29
 
30
30
  For more information about this, read https://rnfirebase.io/reference/messaging/remotemessage
31
31
 
32
- This library provides the following components and methods:
32
+ # This library provides the following components and methods:
33
33
 
34
34
  ## Functions
35
35
 
36
36
  <dl>
37
- <dt><a href="#useNotification">useNotification()</a> ⇒ <code>object</code></dt>
37
+ <dt><a href="#NotificationProvider">NotificationProvider(children, appName, events, environment)</a> ⇒ <code>null</code> | <code>React.element</code></dt>
38
+ <dd><p>It is the main component of the package, it is a HOC that is responsible for handling the logic of subscribing to notifications and receiving messages from the Firebase console. The HOC contains listeners to listen to notifications in the foreground and background, so (unless we cancel the subscription), we will receive notifications from the app even when it is closed.</p>
39
+ </dd>
40
+ <dt><a href="#setupBackgroundMessageHandler">setupBackgroundMessageHandler(callback)</a></dt>
41
+ <dd><p>This function is responsible for handling any callbacks from Firebase cloud messaging in the background or with the application closed</p>
42
+ </dd>
43
+ <dt><a href="#usePushNotification">usePushNotification()</a> ⇒ <code>object</code></dt>
38
44
  <dd><p>is a hook, which returns the elements contained within the notifications context. Returns an object containing:</p>
39
45
  <table>
40
46
  <thead>
@@ -45,33 +51,43 @@ This library provides the following components and methods:
45
51
  </thead>
46
52
  <tbody><tr>
47
53
  <td>deviceToken</td>
48
- <td>is the token linked to the device, which we use to subscribe it to notifications.</td>
54
+ <td>Is the token linked to the device, which we use to subscribe it to notifications.</td>
55
+ </tr>
56
+ <tr>
57
+ <td>foregroundNotification</td>
58
+ <td>An object containing all data received when a foreground push notification is triggered.</td>
59
+ </tr>
60
+ <tr>
61
+ <td>backgroundNotification</td>
62
+ <td>An object containing all data received when a background push notification is triggered.</td>
63
+ </tr>
64
+ <tr>
65
+ <td>subscribeError</td>
66
+ <td>An object containing all data received from a notification service subscription failure.</td>
67
+ </tr>
68
+ <tr>
69
+ <td>cancelNotifications</td>
70
+ <td>This util is responsible for making the request to unsubscribe from all notification events. If no arguments are received, the request will be made with the previously registered events.</td>
71
+ </tr>
72
+ <tr>
73
+ <td>addNewEvent</td>
74
+ <td>This function allows you to add a new event to receive notifications.</td>
75
+ </tr>
76
+ <tr>
77
+ <td>deleteReceivedNotification</td>
78
+ <td>An util that clears the foreground or background notification state to the depending on the type it receives by parameter</td>
79
+ </tr>
80
+ <tr>
81
+ <td>getSubscribedEvents</td>
82
+ <td>This function returns an array with the events to which the user is subscribed.</td>
49
83
  </tr>
50
84
  </tbody></table>
51
85
  </dd>
52
- <dt><a href="#NotificationProvider">NotificationProvider(children, foregroundCallback, backgroundCallback, config, events, environment)</a> ⇒ <code>null</code> | <code>React.element</code></dt>
53
- <dd><p>It is the main component of the package, it is a HOC that is responsible for handling the logic of subscribing to notifications and receiving messages from the Firebase console. The HOC contains listeners to listen to notifications in the foreground and background, so (unless we cancel the subscription), we will receive notifications from the app even when it is closed.</p>
54
- </dd>
55
86
  </dl>
56
87
 
57
- <a name="useNotification"></a>
58
-
59
- ## useNotification() ⇒ <code>object</code>
60
- is a hook, which returns the elements contained within the notifications context. Returns an object containing:
61
- | name | description |
62
- |----------|----------|
63
- | deviceToken | is the token linked to the device, which we use to subscribe it to notifications. |
64
-
65
- **Kind**: global function
66
- **Example**
67
- ```js
68
- import {useNotification} from '@janiscommerce/app-push-notification'
69
-
70
- const {} = useNotification()
71
- ```
72
88
  <a name="NotificationProvider"></a>
73
89
 
74
- ## NotificationProvider(children, foregroundCallback, backgroundCallback, config, events, environment) ⇒ <code>null</code> \| <code>React.element</code>
90
+ ## NotificationProvider(children, appName, events, environment) ⇒ <code>null</code> \| <code>React.element</code>
75
91
  It is the main component of the package, it is a HOC that is responsible for handling the logic of subscribing to notifications and receiving messages from the Firebase console. The HOC contains listeners to listen to notifications in the foreground and background, so (unless we cancel the subscription), we will receive notifications from the app even when it is closed.
76
92
 
77
93
  **Kind**: global function
@@ -83,12 +99,7 @@ It is the main component of the package, it is a HOC that is responsible for han
83
99
  | Param | Type | Description |
84
100
  | --- | --- | --- |
85
101
  | children | <code>React.element</code> | Component that will be rendered within the HOC, and about which the notification will be displayed |
86
- | foregroundCallback | <code>function</code> | function that will be executed when a foreground notification is received. |
87
- | backgroundCallback | <code>function</code> | function that will be executed when a background notification is received. |
88
- | config | <code>object</code> | It is an object that contains the user's data, which will be used to subscribe the user to notifications. |
89
- | config.appName | <code>string</code> | name of the aplication |
90
- | config.accessToken | <code>string</code> | accessToken provided by janis |
91
- | config.client | <code>string</code> | client provided by janis |
102
+ | appName | <code>string</code> | name of the aplication |
92
103
  | events | <code>Array.&lt;string&gt;</code> | is an array that will contain the events to which the user wants to subscribe |
93
104
  | environment | <code>string</code> | The environment is necessary for the API that we are going to use to subscribe the device to notifications. |
94
105
 
@@ -96,23 +107,46 @@ It is the main component of the package, it is a HOC that is responsible for han
96
107
  ```js
97
108
  import NotificationProvider from '@janiscommerce/app-push-notification'
98
109
 
99
-
100
- //...
101
-
102
- const foregroundCallback = (remoteMessage) => console.log('a new FCM:',remoteMessage)
103
- const backgrounCallback = (remoteMessage) => {
104
- console.log('a new FCM was received in background', remoteMessage)
105
- }
106
-
107
110
  return (
108
- <NotificationProvider
109
- foregroundCallback={foregroundCallback}
110
- backgroundCallback={backgroundCallback}
111
- config={client:'fizzmod', accessToken:'access_token_push', appName:'janisAppName'}
112
- events={['Notification','events','janis']}
111
+ <NotificationProvider
112
+ appName='pickingApp'
113
+ events={["picking:session:created","picking:session:assigned"]}
113
114
  environment='beta'
114
115
  >
115
116
  <MyComponent/>
116
117
  </NotificationProvider>
117
118
  )
118
119
  ```
120
+ <a name="setupBackgroundMessageHandler"></a>
121
+
122
+ ## setupBackgroundMessageHandler(callback)
123
+ This function is responsible for handling any callbacks from Firebase cloud messaging in the background or with the application closed
124
+
125
+ **Kind**: global function
126
+
127
+ | Param | Type | Description |
128
+ | --- | --- | --- |
129
+ | callback | <code>function</code> | is the function that will receive the payload and render it as appropriate |
130
+
131
+ <a name="usePushNotification"></a>
132
+
133
+ ## usePushNotification() ⇒ <code>object</code>
134
+ is a hook, which returns the elements contained within the notifications context. Returns an object containing:
135
+ | name | description |
136
+ |----------|----------|
137
+ | deviceToken | Is the token linked to the device, which we use to subscribe it to notifications. |
138
+ | foregroundNotification | An object containing all data received when a foreground push notification is triggered. |
139
+ | backgroundNotification | An object containing all data received when a background push notification is triggered. |
140
+ | subscribeError | An object containing all data received from a notification service subscription failure. |
141
+ | cancelNotifications | This util is responsible for making the request to unsubscribe from all notification events. If no arguments are received, the request will be made with the previously registered events. |
142
+ | addNewEvent | This function allows you to add a new event to receive notifications. |
143
+ | deleteReceivedNotification | An util that clears the foreground or background notification state to the depending on the type it receives by parameter
144
+ | getSubscribedEvents | This function returns an array with the events to which the user is subscribed. |
145
+
146
+ **Kind**: global function
147
+ **Example**
148
+ ```js
149
+ import {usePushNotification} from '@janiscommerce/app-push-notification'
150
+
151
+ const { deviceToken, foregroundNotification, backgroundNotification} = usePushNotification()
152
+ ```
@@ -2,17 +2,30 @@ import React from 'react';
2
2
 
3
3
  export const NotificationContext = React.createContext(null);
4
4
 
5
+ /** *
6
+ * @function setupBackgroundMessageHandler
7
+ * @description This function is responsible for handling any callbacks from Firebase cloud messaging in the background or with the application closed
8
+ * @param {Function} callback is the function that will receive the payload and render it as appropriate
9
+ */
10
+
5
11
  /**
6
- * @function useNotification
12
+ * @function usePushNotification
7
13
  * @description is a hook, which returns the elements contained within the notifications context. Returns an object containing:
8
14
  * | name | description |
9
15
  * |----------|----------|
10
- * | deviceToken | is the token linked to the device, which we use to subscribe it to notifications. |
16
+ * | deviceToken | Is the token linked to the device, which we use to subscribe it to notifications. |
17
+ * | foregroundNotification | An object containing all data received when a foreground push notification is triggered. |
18
+ * | backgroundNotification | An object containing all data received when a background push notification is triggered. |
19
+ * | subscribeError | An object containing all data received from a notification service subscription failure. |
20
+ * | cancelNotifications | This util is responsible for making the request to unsubscribe from all notification events. If no arguments are received, the request will be made with the previously registered events. |
21
+ * | addNewEvent | This function allows you to add a new event to receive notifications. |
22
+ * | deleteReceivedNotification | An util that clears the foreground or background notification state to the depending on the type it receives by parameter
23
+ * | getSubscribedEvents | This function returns an array with the events to which the user is subscribed. |
11
24
  * @returns {object}
12
25
  * @example
13
- * import {useNotification} from '@janiscommerce/app-push-notification'
14
- *
15
- * const {} = useNotification()
26
+ * import {usePushNotification} from '@janiscommerce/app-push-notification'
27
+ *
28
+ * const { deviceToken, foregroundNotification, backgroundNotification} = usePushNotification()
16
29
  */
17
30
 
18
- export const useNotification = () => React.useContext(NotificationContext);
31
+ export const usePushNotification = () => React.useContext(NotificationContext);
@@ -1,49 +1,32 @@
1
- import React, {useEffect, useState} from 'react';
1
+ import React, {useEffect, useRef} from 'react';
2
+ import messaging from '@react-native-firebase/messaging';
2
3
  import {NotificationContext} from '../NotificationContext';
3
4
  import {
4
- getFCMToken,
5
- setupBackgroundMessageHandler,
6
- setupForegroundMessageHandler,
7
- DefaultAlert,
8
- isFunction,
9
5
  isString,
10
- isObject,
11
6
  isArray,
12
- topicsSubscription,
7
+ setupForegroundMessageHandler,
8
+ setupNotificationOpenedHandler,
9
+ isObject,
13
10
  } from '../utils';
11
+ import usePushNotification from '../usePushNotification';
14
12
 
15
13
  /**
16
14
  * @function NotificationProvider
17
- * @description It is the main component of the package, it is a HOC that is responsible for handling the logic of subscribing to notifications and receiving messages from the Firebase console. The HOC contains listeners to listen to notifications in the foreground and background, so (unless we cancel the subscription), we will receive notifications from the app even when it is closed.
15
+ * @description It is the main component of the package, it is a HOC that is responsible for handling the logic of subscribing to notifications and receiving messages from the Firebase console. The HOC contains listeners to listen to notifications in the foreground and background, so (unless we cancel the subscription), we will receive notifications from the app even when it is closed.
18
16
  * @param {React.element} children Component that will be rendered within the HOC, and about which the notification will be displayed
19
- * @param {function} foregroundCallback function that will be executed when a foreground notification is received.
20
- * @param {function} backgroundCallback function that will be executed when a background notification is received.
21
- * @param {object} config It is an object that contains the user's data, which will be used to subscribe the user to notifications.
22
- * @param {string} config.appName name of the aplication
23
- * @param {string} config.accessToken accessToken provided by janis
24
- * @param {string} config.client client provided by janis
17
+ * @param {string} appName name of the aplication
25
18
  * @param {Array<string>} events is an array that will contain the events to which the user wants to subscribe
26
19
  * @param {string} environment The environment is necessary for the API that we are going to use to subscribe the device to notifications.
27
20
  * @throws null when not receive a children argument
28
21
  * @returns {null | React.element}
29
22
  * @example
30
- *
23
+ *
31
24
  * import NotificationProvider from '@janiscommerce/app-push-notification'
32
- *
33
- *
34
- * //...
35
- *
36
- * const foregroundCallback = (remoteMessage) => console.log('a new FCM:',remoteMessage)
37
- * const backgrounCallback = (remoteMessage) => {
38
- * console.log('a new FCM was received in background', remoteMessage)
39
- * }
40
- *
25
+ *
41
26
  * return (
42
- * <NotificationProvider
43
- * foregroundCallback={foregroundCallback}
44
- * backgroundCallback={backgroundCallback}
45
- * config={client:'fizzmod', accessToken:'access_token_push', appName:'janisAppName'}
46
- * events={['Notification','events','janis']}
27
+ * <NotificationProvider
28
+ * appName='pickingApp'
29
+ * events={["picking:session:created","picking:session:assigned"]}
47
30
  * environment='beta'
48
31
  * >
49
32
  * <MyComponent/>
@@ -51,63 +34,88 @@ import {
51
34
  * )
52
35
  */
53
36
 
54
- const NotificationProvider = ({
55
- children,
56
- foregroundCallback,
57
- backgroundCallback,
58
- config,
59
- events,
60
- environment,
61
- }) => {
37
+ const NotificationProvider = ({children, appName, events, environment}) => {
62
38
  if (!children) return null;
63
39
 
64
- const [deviceToken, setDeviceToken] = useState(null);
65
- const validForegroundCallback = isFunction(foregroundCallback)
66
- ? foregroundCallback
67
- : DefaultAlert;
68
- const validBackgroundCallback = isFunction(backgroundCallback)
69
- ? backgroundCallback
70
- : /* istanbul ignore next */ (remoteMessage) => remoteMessage;
71
- const validConfig = config && isObject(config) ? config : {};
72
- const validEvents = events && isArray(events) ? events : [];
73
-
74
- // eslint-disable-next-line no-unused-vars
75
- const parsedConfig = {...validConfig, environment, events: validEvents};
76
-
77
- const getDeviceToken = async () => {
78
- const fcmToken = await getFCMToken();
79
- if (!fcmToken || !isString(fcmToken)) return null;
80
-
81
- setDeviceToken(fcmToken)
82
- return fcmToken;
40
+ const validAppName = !!appName && isString(appName) ? appName : '';
41
+ const validEnvironment =
42
+ !!environment && isString(environment) ? environment : '';
43
+ const validEvents = !!events && isArray(events) ? events : [];
44
+
45
+ const isRegistered = useRef(false);
46
+ const {
47
+ registerDeviceToNotifications,
48
+ updateNotificationState,
49
+ pushEvents,
50
+ ...rest
51
+ } = usePushNotification(
52
+ validEnvironment,
53
+ validEvents,
54
+ validAppName,
55
+ isRegistered,
56
+ );
57
+
58
+ // @function handlerForegroundData
59
+ // @description This function is responsible for updating the state corresponding to 'foregroundNotification' with the data it receives as an argument
60
+
61
+ /* istanbul ignore next */
62
+ const handlerForegroundData = (data) => {
63
+ if (!data || !isObject(data)) return null;
64
+
65
+ return updateNotificationState({foregroundNotification: data});
66
+ };
67
+
68
+ // @function handlerBackgroundData
69
+ // @description This function is responsible for updating the state corresponding to 'backgroundNotification' with the data it receives as an argument
70
+
71
+ /* istanbul ignore next */
72
+ const handlerBackgroundData = (data) => {
73
+ if (!data || !isObject(data)) return null;
74
+
75
+ return updateNotificationState({backgroundNotification: data});
83
76
  };
84
77
 
85
- // eslint-disable-next-line
86
- const registerDevice = async () => {
87
- const newDeviceToken = await getDeviceToken();
78
+ // @function handleAppOpeningByNotification
79
+ // @description This function is responsible for saving the information of the notification that forced the opening of the app in the 'backgroundNotification' state
80
+
81
+ /* istanbul ignore next */
82
+ const handleAppOpeningByNotification = async () => {
83
+ const data = await messaging().getInitialNotification();
84
+
85
+ if (!data || !isObject(data)) return null;
88
86
 
89
- if (!newDeviceToken) return null;
90
- // eslint-disable-next-line
91
- validEvents.forEach(async (event) => {
92
- if (!event || !isString(event)) return null;
93
- await topicsSubscription(event);
94
- });
87
+ return updateNotificationState({backgroundNotification: data});
95
88
  };
96
89
 
97
90
  useEffect(() => {
98
- registerDevice();
91
+ const alreadySuscribed = isRegistered.current;
92
+ if (environment && appName && !!pushEvents.length && !alreadySuscribed) {
93
+ registerDeviceToNotifications();
94
+ }
95
+ }, [pushEvents]);
96
+
97
+ /* istanbul ignore next */
98
+ useEffect(() => {
99
99
  const foregroundMessageHandler = setupForegroundMessageHandler(
100
- validForegroundCallback,
100
+ handlerForegroundData,
101
+ );
102
+ const backgroundMessageHandler = setupNotificationOpenedHandler(
103
+ handlerBackgroundData,
101
104
  );
105
+ handleAppOpeningByNotification();
102
106
 
103
107
  return () => {
104
108
  foregroundMessageHandler();
105
- setupBackgroundMessageHandler(validBackgroundCallback);
109
+ backgroundMessageHandler();
106
110
  };
107
111
  }, []);
108
112
 
113
+ const contextValues = {
114
+ ...rest,
115
+ };
116
+
109
117
  return (
110
- <NotificationContext.Provider value={{deviceToken}}>
118
+ <NotificationContext.Provider value={contextValues}>
111
119
  {children}
112
120
  </NotificationContext.Provider>
113
121
  );
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import NotificationProvider from './NotificationProvider';
2
- import {useNotification} from './NotificationContext';
3
- import {topicsSubscription, topicsUnsubscription} from './utils';
2
+ import {usePushNotification} from './NotificationContext';
3
+ import {setupBackgroundMessageHandler} from './utils';
4
4
 
5
- export {useNotification, topicsSubscription, topicsUnsubscription};
5
+ export {usePushNotification, setupBackgroundMessageHandler};
6
6
  export default NotificationProvider;
@@ -0,0 +1,167 @@
1
+ import {useCallback, useState} from 'react';
2
+ import RequestInstance from '@janiscommerce/app-request';
3
+ import {getFCMToken, isArray, isString} from './utils';
4
+ import SubscribeNotifications from './utils/api/SubscribeNotifications';
5
+ import UnSubscribeNotifications from './utils/api/UnSubscribeNotifications';
6
+
7
+ const usePushNotification = (environment, events, appName, isRegistered) => {
8
+ const [notificationState, setNotificationState] = useState({
9
+ deviceToken: null,
10
+ foregroundNotification: {},
11
+ backgroundNotification: {},
12
+ pushEvents: events,
13
+ subscribeError: {},
14
+ });
15
+
16
+ const {
17
+ deviceToken,
18
+ foregroundNotification,
19
+ backgroundNotification,
20
+ pushEvents,
21
+ subscribeError,
22
+ } = notificationState;
23
+
24
+ const Request = new RequestInstance({JANIS_ENV: environment});
25
+
26
+ const updateNotificationState = (state) =>
27
+ setNotificationState({...notificationState, ...state});
28
+
29
+ const getSubscribedEvents = () => pushEvents;
30
+
31
+ const getDeviceToken = async () => {
32
+ if (deviceToken) return deviceToken;
33
+
34
+ const fcmToken = await getFCMToken();
35
+ if (!fcmToken || !isString(fcmToken)) return null;
36
+
37
+ return fcmToken;
38
+ };
39
+
40
+ /**
41
+ * @function registerDeviceToNotifications
42
+ * @description This function is responsible for registering the device to the notification microservice.
43
+ * @returns {null}
44
+ */
45
+
46
+ const registerDeviceToNotifications = useCallback(async () => {
47
+ const token = await getDeviceToken();
48
+
49
+ if (!token) return null;
50
+
51
+ try {
52
+ await SubscribeNotifications({
53
+ appName,
54
+ events: pushEvents,
55
+ deviceToken: token,
56
+ request: Request,
57
+ });
58
+
59
+ isRegistered.current = true;
60
+ return updateNotificationState({deviceToken: token, pushEvents});
61
+ } catch (error) {
62
+ return updateNotificationState({pushEvents: [], subscribeError: error});
63
+ }
64
+ }, [pushEvents]);
65
+
66
+ /**
67
+ * @function cancelNotifications
68
+ * @description This util is responsible for making the request to unsubscribe from all notification events. If no arguments are received, the request will be made with the previously registered events.
69
+ * @param {Array<string>} events is the list of events to which I want to unsubscribe the device
70
+ * @returns {Promise}
71
+ *
72
+ * @example
73
+ *
74
+ * import {usePushNotification} from '@janiscommerce/app-push-notification
75
+ *
76
+ * const {cancelNotifications} = usePushNotification()
77
+ */
78
+
79
+ const cancelNotifications = async (cancelEvents) => {
80
+ const eventsAreValid = cancelEvents && isArray(cancelEvents);
81
+ const eventsToCancel = eventsAreValid ? cancelEvents : pushEvents;
82
+ try {
83
+ await UnSubscribeNotifications({
84
+ events: eventsToCancel,
85
+ request: Request,
86
+ });
87
+
88
+ if (eventsAreValid) {
89
+ const updatedEvents = pushEvents.filter(
90
+ (e) => !eventsToCancel.includes(e),
91
+ );
92
+
93
+ isRegistered.current = true;
94
+ return updateNotificationState({pushEvents: updatedEvents});
95
+ }
96
+
97
+ isRegistered.current = false;
98
+ return updateNotificationState({pushEvents: []});
99
+ } catch (unsubscribeError) {
100
+ return Promise.reject(unsubscribeError);
101
+ }
102
+ };
103
+
104
+ /**
105
+ * @function deleteReceivedNotification
106
+ * @description This utility allows you to reset the state corresponding to the type of notification received as an argument.
107
+ * @param {string} notificationType the type of notification you want to delete, it can be a foreground or background notification
108
+ * @returns {null}
109
+ *
110
+ * @example
111
+ * import {usePushNotification} from '@janiscommerce/app-push-notification
112
+ *
113
+ * const {deleteReceivedNotification} = usePushNotification()
114
+ *
115
+ * const resetForegroundNotification = () => {
116
+ * deleteReceivedNotification('foreground')
117
+ * }
118
+ */
119
+
120
+ const deleteReceivedNotification = (params = {}) => {
121
+ const {type = ''} = params;
122
+ const allowTypes = ['foreground', 'background'];
123
+ const deleteNotification = {
124
+ foreground: () => updateNotificationState({foregroundNotification: {}}),
125
+ background: () => updateNotificationState({backgroundNotification: {}}),
126
+ };
127
+
128
+ if (!type || !allowTypes.includes(type)) return null;
129
+
130
+ const restartNotification = deleteNotification[type];
131
+
132
+ return restartNotification();
133
+ };
134
+
135
+ /**
136
+ * @function addNewEvent
137
+ * @description This function allows you to add a new event to receive notifications
138
+ * @param {string} event
139
+ */
140
+
141
+ const addNewEvent = (event) => {
142
+ if (!event || !isString(event)) return null;
143
+ if (pushEvents.includes(event)) {
144
+ return null;
145
+ }
146
+
147
+ const updatedEvents = [...pushEvents, event];
148
+ isRegistered.current = false;
149
+ return updateNotificationState({pushEvents: updatedEvents});
150
+ };
151
+
152
+ return {
153
+ deviceToken,
154
+ foregroundNotification,
155
+ backgroundNotification,
156
+ subscribeError,
157
+ cancelNotifications,
158
+ addNewEvent,
159
+ pushEvents,
160
+ registerDeviceToNotifications,
161
+ updateNotificationState,
162
+ getSubscribedEvents,
163
+ deleteReceivedNotification,
164
+ };
165
+ };
166
+
167
+ export default usePushNotification;
@@ -1,42 +1,40 @@
1
- import axios from 'axios';
2
- import {isString, isArray, validateOauthData, getHeaders} from '../../index';
1
+ import {isString, isArray} from '../../index';
3
2
 
4
3
  const SubscribeNotifications = async (params = {}) => {
5
4
  try {
6
5
  if (!params || !Object.keys(params).length)
7
6
  throw new Error('params is not a valid object');
8
7
 
9
- const {client, accessToken, deviceToken, events, appName, environment} =
10
- params;
8
+ const {deviceToken, events, appName, request} = params;
11
9
 
12
- if (!validateOauthData(accessToken, client))
13
- throw new Error('accessToken and client are required');
14
10
  if (!deviceToken || !isString(deviceToken))
15
11
  throw new Error('device token is invalid or null');
16
12
  if (!events || !isArray(events))
17
13
  throw new Error('events to be subscribed to are null');
18
14
  if (!appName || !isString(appName))
19
15
  throw new Error('application name are invalid or null');
20
- if (!environment || !isString(environment))
21
- throw new Error('environment is invalid or null');
16
+ if (!request) throw new Error('Request is not available');
22
17
 
23
18
  const parsedEvents = events.filter((event) => !!event && isString(event));
24
19
  if (!parsedEvents.length)
25
20
  throw new Error('events to be suscribed are invalids');
26
21
 
27
- const headers = getHeaders({client, accessToken});
28
- const validUrl = `https://notifications.${environment}.in/api/push/register`;
29
22
  const body = {
30
- deviceToken,
23
+ token: deviceToken,
31
24
  events: parsedEvents,
32
25
  platformApplicationName: appName,
33
26
  };
34
27
 
35
- const {data} = await axios.post(validUrl, body, {headers});
28
+ const response = await request.post({
29
+ service: 'notification',
30
+ namespace: 'subscribe',
31
+ pathParams: ['push'],
32
+ body,
33
+ });
36
34
 
37
- return data;
35
+ return response;
38
36
  } catch (error) {
39
- return Promise.reject(error?.response?.data?.message || error);
37
+ return Promise.reject(error?.result || error);
40
38
  }
41
39
  };
42
40
 
@@ -0,0 +1,35 @@
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;
@@ -1,7 +1,5 @@
1
- import {Alert} from 'react-native';
2
1
  import messaging from '@react-native-firebase/messaging';
3
2
  import AsyncStorage from '@react-native-async-storage/async-storage';
4
- import DeviceInfo from 'react-native-device-info';
5
3
 
6
4
  // Helpers
7
5
 
@@ -75,180 +73,19 @@ export const isBoolean = (bool) => typeof bool === 'boolean';
75
73
  export const isArray = (arr) => !!(arr instanceof Array);
76
74
 
77
75
  /**
78
- * @function validateOauthData
79
- * @param {string} accessToken
80
- * @param {string} client
81
- * @returns {boolean} - true or false
82
- * @example validateOauthData([], 'fizzmodarg') => false
83
- * @example validateOauthData('34234sdfrdf', 'fizzmodarg') => true
84
- */
85
- export const validateOauthData = (accessToken, client) => {
86
- if (!accessToken || !client) return false;
87
- if (!isString(accessToken) || !isString(client)) return false;
88
- return true;
89
- };
90
-
91
- export const formatDeviceDataForUserAgent = (deviceData) => {
92
- if (!isObject(deviceData) || !Object.keys(deviceData).length) return {};
93
-
94
- const keysToCheck = [
95
- 'janis-app-package-name',
96
- 'janis-app-version',
97
- 'janis-app-name',
98
- 'janis-app-build',
99
- 'janis-app-device-os-name',
100
- 'janis-app-device-os-version',
101
- 'janis-app-device-id',
102
- 'janis-app-device-name',
103
- ];
104
-
105
- const hasSomeValidValues = keysToCheck.some(
106
- (key) => isString(deviceData[key]) && !!deviceData[key],
107
- );
108
-
109
- if (!hasSomeValidValues) return {};
110
-
111
- const userAgentParts = [];
112
-
113
- keysToCheck.forEach((key) => {
114
- const value =
115
- !deviceData[key] || !isString(deviceData[key])
116
- ? `unknown ${key}`
117
- : deviceData[key];
118
- userAgentParts.push(value);
119
- });
120
-
121
- return {
122
- 'user-agent': `${userAgentParts[0]}/${userAgentParts[1]} (${userAgentParts[2]}; ${userAgentParts[3]}) ${userAgentParts[4]}/${userAgentParts[5]} (${userAgentParts[6]}; ${userAgentParts[7]})`,
123
- };
124
- };
125
-
126
- /**
127
- * @function getDeviceData
128
- * @description return data from device user
129
- * @returns {{
130
- * 'application-name': string,
131
- * 'build-number': string,
132
- * 'app-version': string,
133
- * 'bundle-id': string,
134
- * 'os-name': string,
135
- * 'device-id': string,
136
- * 'device-name': string
137
- * }} - Object with device data
138
- * @example getDeviceData() => {applicationName: 'AppName', buildNumber: '434', appVersion: '1.5.0', bundleId: 'com.janis.appname', osName: 'android', osVersion: '11', deviceId: '34hf83hf89ahfjo', deviceName: 'Pixel 2'}
139
- */
140
-
141
- export const getDeviceData = () => {
142
- const applicationName = DeviceInfo.getApplicationName() || '';
143
- const buildNumber = DeviceInfo.getBuildNumber() || '';
144
- const appVersion = DeviceInfo.getVersion() || '';
145
- const bundleId = DeviceInfo.getBundleId() || '';
146
- const osName = DeviceInfo.getSystemName() || '';
147
- const osVersion = DeviceInfo.getSystemVersion() || '';
148
- const deviceId = DeviceInfo.getUniqueId() || '';
149
- const deviceName = DeviceInfo.getModel() || '';
150
-
151
- return {
152
- 'janis-app-name': applicationName,
153
- 'janis-app-build': buildNumber,
154
- 'janis-app-version': appVersion,
155
- 'janis-app-package-name': bundleId,
156
- 'janis-app-device-os-name': osName,
157
- 'janis-app-device-os-version': osVersion,
158
- 'janis-app-device-id': deviceId,
159
- 'janis-app-device-name': deviceName,
160
- };
161
- };
162
-
163
- const filterValidHeaders = (headers) => {
164
- if (!headers || !isObject(headers) || !Object.keys(headers).length) return {};
165
-
166
- return Object.fromEntries(
167
- Object.entries(headers).filter(([, value]) => !!value && !!isString(value)),
168
- );
169
- };
170
-
171
- /**
172
- * @function getHeaders
173
- * @param {object} [params={}] - object with params
174
- * @param {object} [deviceDataHeaders={}] - headers with the device info
175
- * @param {object} [customHeaders={}] - extra custom headers
176
- * @param {string} params.client - client name for janis api
177
- * @param {string} params.accessToken - access token for janis api
178
- * @param {number} params.page - number of page
179
- * @param {number} params.pageSize - quantity per page
180
- * @param {boolean} params.getTotals - request api totals
181
- * @param {boolean} params.getOnlyTotals - request api totals without body response
182
- * @description get correct headers for janis api
183
- * @returns {object}
76
+ * @function promiseWrapper
77
+ * @param {function} fn
78
+ * @description wrapper to execute promise and return tuple with data and error
79
+ * @returns {array<data, error>}
184
80
  * @example
185
- * const params = {
186
- * client: 'my-client',
187
- * accessToken: 'my-access-token',
188
- * page: 1,
189
- * pageSize: 10,
190
- * getTotals: true,
191
- * getOnlyTotals: false
192
- * };
193
- * const deviceDataHeaders = {
194
- * 'janis-app-name': 'MyApp',
195
- * 'janis-app-version': '1.0.0',
196
- * 'janis-app-device-os-name': 'iOS',
197
- * 'janis-app-device-os-version': '14.5',
198
- * 'janis-app-device-name': 'iPhone 12',
199
- * 'janis-app-device-id': '123456789'
200
- * };
201
- * const customHeaders = {
202
- * 'custom-header': 'custom-value'
203
- * };
204
- * const headers = getHeaders(params, deviceDataHeaders, customHeaders);
205
- * // {
206
- * // 'content-Type': 'application/json',
207
- * // 'janis-api-key': 'Bearer',
208
- * // 'janis-client': 'my-client',
209
- * // 'janis-api-secret': 'my-access-token',
210
- * // 'x-janis-page': 1,
211
- * // 'x-janis-page-size': 10,
212
- * // 'x-janis-totals': true,
213
- * // 'x-janis-only-totals': false,
214
- * // 'user-agent': 'MyApp/1.0.0 (iOS 14.5; iPhone 12; 123456789)',
215
- * // 'custom-header': 'custom-value'
216
- * // }
81
+ * import {promiseWrapper} from '@janiscommerce/apps-helpers'
82
+ * const [data, error] = await promiseWrapper(promise())
217
83
  */
218
84
 
219
- export const getHeaders = (params = {}, customHeaders = {}) => {
220
- const deviceDataHeaders = getDeviceData();
221
-
222
- const validCustomHeaders = filterValidHeaders(customHeaders);
223
- const validDeviceDataHeaders = filterValidHeaders(deviceDataHeaders);
224
- const validUserAgentHeader = formatDeviceDataForUserAgent(
225
- validDeviceDataHeaders,
226
- );
227
-
228
- const baseHeaders = {
229
- 'content-Type': 'application/json',
230
- 'janis-api-key': 'Bearer',
231
- ...validUserAgentHeader,
232
- ...validDeviceDataHeaders,
233
- ...validCustomHeaders,
234
- };
235
-
236
- if (!isObject(params)) return baseHeaders;
237
- const {client, accessToken, page, pageSize, getTotals, getOnlyTotals} =
238
- params;
239
-
240
- return {
241
- ...baseHeaders,
242
- ...(isString(client) && client && {'janis-client': client}),
243
- ...(isString(accessToken) &&
244
- accessToken && {'janis-api-secret': accessToken}),
245
- ...(isNumber(page) && page && {'x-janis-page': page}),
246
- ...(isNumber(pageSize) && pageSize && {'x-janis-page-size': pageSize}),
247
- ...(isBoolean(getTotals) && getTotals && {'x-janis-totals': getTotals}),
248
- ...(isBoolean(getOnlyTotals) &&
249
- getOnlyTotals && {'x-janis-only-totals': getOnlyTotals}),
250
- };
251
- };
85
+ export const promiseWrapper = (promise) =>
86
+ promise
87
+ .then((data) => [data, null])
88
+ .catch((error) => Promise.resolve([null, error]));
252
89
 
253
90
  // MESSAGING UTILS
254
91
 
@@ -258,7 +95,7 @@ export const getHeaders = (params = {}, customHeaders = {}) => {
258
95
  * @returns {Promise<string>}
259
96
  * @example
260
97
  *
261
- * getFCMToken() => JDF6GJS364uhaGGe384gJHIQs23nbRNFG2859gJSD9gBivajeSJD
98
+ * getFCMToken() => 'JDF6GJS364uhaGGe384gJHIQs23nbRNFG2859gJSD9gBivajeSJD'
262
99
  */
263
100
 
264
101
  export const getFCMToken = async () => {
@@ -309,58 +146,18 @@ export const setupForegroundMessageHandler = (callback) =>
309
146
  * @param {Function} callback is the function that will receive the payload and render it as appropriate
310
147
  */
311
148
 
312
- export const setupBackgroundMessageHandler = (callback) =>
149
+ export const setupBackgroundMessageHandler = (callback = () => {}) =>
313
150
  messaging().setBackgroundMessageHandler(async (remoteMessage) => {
314
151
  callback(remoteMessage);
315
152
  });
316
153
 
317
154
  /** *
318
- * @name DefaultAlert
319
- * @description a default alert component to be used when a foregroundCallback function is not passed by parameters
320
- * @param {object} remoteMessage the object that was received from fcm
321
- * @throws null when not receive a valid object as remoteMessage
322
- * @returns an alert with title and body received from FCM remoteMessage
323
- *
155
+ * @function setupNotificationOpenedHandler
156
+ * @description This function is responsible for handling any Firebase Cloud Messaging callbacks that the app will have from the background
157
+ * @param {Function} callback is the function that will receive the payload and render it as appropriate
324
158
  */
325
159
 
326
- export const DefaultAlert = (remoteMessage = {}) => {
327
- if (!remoteMessage || !Object.keys(remoteMessage).length) return null;
328
-
329
- const {notification = {}} = remoteMessage;
330
- const {title, body} = notification;
331
-
332
- const validTitle = isString(title) ? title : 'A new FCM message arrived!';
333
- const validateBody = isString(body) ? body : undefined;
334
-
335
- return Alert.alert(validTitle, validateBody);
336
- };
337
-
338
- /* istanbul ignore next */
339
- export const topicsSubscription = async (topics) => {
340
- try {
341
- if (!topics || !isString(topics)) return null;
342
-
343
- await messaging().subscribeToTopic(topics);
344
-
345
- return {message: `suscribed to ${topics} topic!`};
346
- } catch (reason) {
347
- console.error(reason?.message);
348
-
349
- return Promise.reject(reason?.message);
350
- }
351
- };
352
-
353
- /* istanbul ignore next */
354
- export const topicsUnsubscription = async (topic) => {
355
- try {
356
- if (!topic || !isString(topic)) return null;
357
-
358
- await messaging().unsubscribeFromTopic(topic);
359
-
360
- return {message: `unsuscribed to ${topic} topic!`};
361
- } catch (reason) {
362
- console.error(reason?.message);
363
-
364
- return Promise.reject(reason?.message);
365
- }
366
- };
160
+ export const setupNotificationOpenedHandler = (callback) =>
161
+ messaging().onNotificationOpenedApp(async (remoteMessage) => {
162
+ callback(remoteMessage);
163
+ });
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@janiscommerce/app-push-notification",
3
- "version": "0.0.1-beta.1",
3
+ "version": "0.0.2",
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
8
  "test": "jest",
9
9
  "lint": "eslint .",
10
- "build-docs": "jsdoc2md --template template-readme.hbs --files lib/*.js lib/NotificationProvider/*.js > README.md",
10
+ "build-docs": "jsdoc2md --template template-readme.hbs --files lib/NotificationProvider/index.js lib/NotificationContext.js > README.md",
11
11
  "test:coverage": "jest --collectCoverage",
12
12
  "validate:code": "npm run lint -- --fix && jest --collectCoverage"
13
13
  },
@@ -27,30 +27,33 @@
27
27
  "author": "Janis",
28
28
  "license": "ISC",
29
29
  "dependencies": {
30
+ "@janiscommerce/app-request": "^2.2.0",
30
31
  "@react-native-async-storage/async-storage": "^1.18.1",
31
32
  "@react-native-firebase/app": "^18.3.1",
32
33
  "@react-native-firebase/messaging": "^18.3.1",
33
34
  "axios": "^1.3.6",
34
- "react-native-device-info": "^10.12.0"
35
+ "react-native-device-info": "^8.5.0"
35
36
  },
36
37
  "peerDependencies": {
37
38
  "react": ">=17.0.2 <=18.2.0",
38
39
  "react-native": ">=0.67.5 <=0.72.0"
39
40
  },
40
41
  "devDependencies": {
41
- "@babel/core": "^7.23.6",
42
+ "@babel/core": "^7.22.10",
42
43
  "@babel/eslint-parser": "^7.5.4",
43
44
  "@babel/plugin-proposal-class-properties": "^7.18.6",
44
45
  "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
45
46
  "@babel/preset-env": "^7.22.10",
46
47
  "@babel/runtime": "^7.12.5",
48
+ "@janiscommerce/app-request": "^2.2.0",
47
49
  "@react-native-community/eslint-config": "^2.0.0",
48
50
  "@react-native-firebase/app": "^18.3.1",
49
51
  "@react-native-firebase/messaging": "^18.3.1",
50
52
  "@testing-library/react-native": "^12.0.1",
51
- "babel-jest": "^28.0.0",
53
+ "babel-eslint": "^10.1.0",
54
+ "babel-jest": "^26.6.3",
52
55
  "babel-loader": "8.2.4",
53
- "eslint": "^8.56.0",
56
+ "eslint": "7.14.0",
54
57
  "eslint-config-airbnb": "^18.2.1",
55
58
  "eslint-config-prettier": "^8.1.0",
56
59
  "eslint-plugin-import": "^2.22.1",
@@ -68,6 +71,7 @@
68
71
  "prettier": "^2.4.1",
69
72
  "react": "^17.0.2",
70
73
  "react-native": "^0.67.5",
74
+ "react-native-device-info": "^8.5.0",
71
75
  "react-test-renderer": "^17.0.2"
72
76
  },
73
77
  "bugs": {