@inngageregistry/inngage-react 4.0.2 → 4.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/dist/Inngage.d.ts +1 -1
- package/dist/Inngage.js +2 -2
- package/dist/firebase/notifications_config.d.ts +1 -1
- package/dist/firebase/notifications_config.js +0 -3
- package/dist/firebase/notifications_listener.d.ts +5 -1
- package/dist/firebase/notifications_listener.js +188 -74
- package/package.json +1 -1
package/dist/Inngage.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ declare class Inngage {
|
|
|
25
25
|
private apiService;
|
|
26
26
|
constructor();
|
|
27
27
|
static getInstance(): Inngage;
|
|
28
|
-
static notificationListener(firebaseListenCallback?: (data: Record<string, string>) => void, imageNotification?: string, colorNotification?: string): void;
|
|
28
|
+
static notificationListener(firebaseListenCallback?: (data: Record<string, string>) => void, imageNotification?: string, colorNotification?: string, blockDeepLink?: boolean): void;
|
|
29
29
|
static subscribe({ appToken, friendlyIdentifier, customFields, phoneNumber, email, }: SubscriptionProps): Promise<Response>;
|
|
30
30
|
static sendEvent({ eventName, conversionEvent, conversionValue, conversionNotId, eventValues, }: SendEventProps): Promise<Response>;
|
|
31
31
|
static addUserData({ identifier, customFields, email, phoneNumber }: AddUserDataProps): Promise<Response>;
|
package/dist/Inngage.js
CHANGED
|
@@ -83,9 +83,9 @@ class Inngage {
|
|
|
83
83
|
}
|
|
84
84
|
return Inngage.instance;
|
|
85
85
|
}
|
|
86
|
-
static notificationListener(firebaseListenCallback, imageNotification, colorNotification) {
|
|
86
|
+
static notificationListener(firebaseListenCallback, imageNotification, colorNotification, blockDeepLink) {
|
|
87
87
|
try {
|
|
88
|
-
InngageNotificationMessage(firebaseListenCallback, imageNotification, colorNotification);
|
|
88
|
+
InngageNotificationMessage(firebaseListenCallback, imageNotification, colorNotification, { blockDeepLink });
|
|
89
89
|
}
|
|
90
90
|
catch (e) {
|
|
91
91
|
console.log(e);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare const ANDROID_CHANNEL_ID = "inngage_default_channel";
|
|
2
|
-
export declare const messagingInstance: import("@react-native-firebase/messaging").
|
|
2
|
+
export declare const messagingInstance: import("@react-native-firebase/messaging").Messaging;
|
|
3
3
|
export declare const ensureAndroidChannel: () => Promise<void>;
|
|
@@ -18,9 +18,6 @@ export const ensureAndroidChannel = async () => {
|
|
|
18
18
|
id: ANDROID_CHANNEL_ID,
|
|
19
19
|
name: 'Inngage Notifications',
|
|
20
20
|
importance: AndroidImportance.HIGH,
|
|
21
|
-
// você pode ajustar sound, vibration, lights etc. se quiser:
|
|
22
|
-
// sound: 'default',
|
|
23
|
-
// vibration: true,
|
|
24
21
|
});
|
|
25
22
|
androidChannelCreated = true;
|
|
26
23
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
type ListenCallback = (data: Record<string, string>) => void;
|
|
1
2
|
export declare const messagingHeadlessTask: () => void;
|
|
2
3
|
export declare const useInAppHandler: () => {
|
|
3
4
|
showInApp: boolean;
|
|
4
5
|
setShowInApp: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
5
6
|
};
|
|
6
|
-
export declare const InngageNotificationMessage: (firebaseListenCallback?:
|
|
7
|
+
export declare const InngageNotificationMessage: (firebaseListenCallback?: ListenCallback, imageNotification?: string, colorNotification?: string, options?: {
|
|
8
|
+
blockDeepLink?: boolean;
|
|
9
|
+
}) => {
|
|
7
10
|
unsubscribe: () => void;
|
|
8
11
|
};
|
|
12
|
+
export {};
|
|
@@ -3,46 +3,48 @@ import { AppState, Linking, Platform } from 'react-native';
|
|
|
3
3
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
4
4
|
import { setBackgroundMessageHandler, onMessage, onNotificationOpenedApp, getInitialNotification, } from '@react-native-firebase/messaging';
|
|
5
5
|
import InAppBrowser from 'react-native-inappbrowser-reborn';
|
|
6
|
+
import notifee, { EventType } from '@notifee/react-native';
|
|
6
7
|
import { InngageProperties } from '../models/inngage_properties';
|
|
7
8
|
import * as ApiService from '../services/api_services';
|
|
8
|
-
import notifee, { EventType } from '@notifee/react-native';
|
|
9
9
|
import { toStringValue } from '../utils';
|
|
10
10
|
import { ANDROID_CHANNEL_ID, messagingInstance, ensureAndroidChannel, } from './notifications_config';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
await AsyncStorage.setItem('inapp', toStringValue(additional));
|
|
19
|
-
}
|
|
20
|
-
return Promise.resolve();
|
|
21
|
-
});
|
|
22
|
-
};
|
|
23
|
-
export const useInAppHandler = () => {
|
|
24
|
-
const [showInApp, setShowInApp] = useState(false);
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
const checkInAppData = async () => {
|
|
27
|
-
const inAppData = await AsyncStorage.getItem('inapp');
|
|
28
|
-
if (inAppData)
|
|
29
|
-
setShowInApp(true);
|
|
30
|
-
};
|
|
31
|
-
checkInAppData();
|
|
32
|
-
const subscription = AppState.addEventListener('change', (next) => {
|
|
33
|
-
if (next === 'active')
|
|
34
|
-
checkInAppData();
|
|
35
|
-
});
|
|
36
|
-
return () => subscription.remove();
|
|
37
|
-
}, []);
|
|
38
|
-
return { showInApp, setShowInApp };
|
|
39
|
-
};
|
|
11
|
+
const KEY_INAPP = 'inapp';
|
|
12
|
+
const KEY_PENDING_REMOTE_MESSAGES = 'PENDING_REMOTE_MESSAGES_QUEUE';
|
|
13
|
+
const KEY_LAST_DELIVERED_MESSAGE_ID = 'LAST_DELIVERED_MESSAGE_ID';
|
|
14
|
+
const KEY_LAST_CLICK_HANDLED_MESSAGE_ID = 'LAST_CLICK_HANDLED_MESSAGE_ID';
|
|
15
|
+
const KEY_LAST_INITIAL_HANDLED_ID = 'LAST_INITIAL_HANDLED_ID';
|
|
16
|
+
const KEY_LAST_CALLBACK_DELIVERED_ID = 'LAST_CALLBACK_DELIVERED_ID';
|
|
17
|
+
let currentBlockDeepLink = false;
|
|
40
18
|
let currentFirebaseListenCallback;
|
|
41
19
|
let currentImageNotification;
|
|
42
20
|
let currentColorNotification;
|
|
43
21
|
let foregroundUnsubscribe = null;
|
|
44
22
|
let openedAppUnsubscribe = null;
|
|
45
23
|
let initialNotificationHandled = false;
|
|
24
|
+
let notifeeForegroundUnsubscribe = null;
|
|
25
|
+
let appStateSubscription = null;
|
|
26
|
+
const safeJsonParse = (raw) => {
|
|
27
|
+
if (!raw)
|
|
28
|
+
return null;
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(raw);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const deliverToCallbackOnce = async (msg) => {
|
|
37
|
+
if (!currentFirebaseListenCallback)
|
|
38
|
+
return;
|
|
39
|
+
const id = msg.messageId ?? msg.data?.notId ?? '';
|
|
40
|
+
if (id) {
|
|
41
|
+
const last = await AsyncStorage.getItem(KEY_LAST_CALLBACK_DELIVERED_ID);
|
|
42
|
+
if (last === id)
|
|
43
|
+
return;
|
|
44
|
+
await AsyncStorage.setItem(KEY_LAST_CALLBACK_DELIVERED_ID, id);
|
|
45
|
+
}
|
|
46
|
+
currentFirebaseListenCallback((msg.data ?? {}));
|
|
47
|
+
};
|
|
46
48
|
const normalizeAndroidImageResource = (res) => {
|
|
47
49
|
if (!res)
|
|
48
50
|
return undefined;
|
|
@@ -54,6 +56,53 @@ const normalizeAndroidImageResource = (res) => {
|
|
|
54
56
|
}
|
|
55
57
|
return res;
|
|
56
58
|
};
|
|
59
|
+
const enqueuePendingMessage = async (msg, maxItems = 20) => {
|
|
60
|
+
try {
|
|
61
|
+
const raw = await AsyncStorage.getItem(KEY_PENDING_REMOTE_MESSAGES);
|
|
62
|
+
const queue = safeJsonParse(raw) ?? [];
|
|
63
|
+
queue.push(msg);
|
|
64
|
+
const trimmed = queue.length > maxItems ? queue.slice(queue.length - maxItems) : queue;
|
|
65
|
+
await AsyncStorage.setItem(KEY_PENDING_REMOTE_MESSAGES, JSON.stringify(trimmed));
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
console.error(e);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const dequeueAllPendingMessages = async () => {
|
|
72
|
+
try {
|
|
73
|
+
const raw = await AsyncStorage.getItem(KEY_PENDING_REMOTE_MESSAGES);
|
|
74
|
+
const queue = safeJsonParse(raw) ?? [];
|
|
75
|
+
if (queue.length > 0) {
|
|
76
|
+
await AsyncStorage.removeItem(KEY_PENDING_REMOTE_MESSAGES);
|
|
77
|
+
}
|
|
78
|
+
return queue;
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
console.error(e);
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const flushPendingQueue = async () => {
|
|
86
|
+
try {
|
|
87
|
+
if (!currentFirebaseListenCallback)
|
|
88
|
+
return;
|
|
89
|
+
const queue = await dequeueAllPendingMessages();
|
|
90
|
+
if (queue.length === 0)
|
|
91
|
+
return;
|
|
92
|
+
const lastDeliveredId = await AsyncStorage.getItem(KEY_LAST_DELIVERED_MESSAGE_ID);
|
|
93
|
+
for (const item of queue) {
|
|
94
|
+
if (item.messageId && item.messageId === lastDeliveredId)
|
|
95
|
+
continue;
|
|
96
|
+
if (item.messageId) {
|
|
97
|
+
await AsyncStorage.setItem(KEY_LAST_DELIVERED_MESSAGE_ID, item.messageId);
|
|
98
|
+
}
|
|
99
|
+
await deliverToCallbackOnce({ messageId: item.messageId, data: item.data });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
console.error(e);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
57
106
|
const handleNotification = async (remoteMessage) => {
|
|
58
107
|
const data = remoteMessage?.data ?? {};
|
|
59
108
|
const notId = data?.notId;
|
|
@@ -67,55 +116,111 @@ const handleNotification = async (remoteMessage) => {
|
|
|
67
116
|
const url = data?.url;
|
|
68
117
|
const type = data?.type;
|
|
69
118
|
if (url) {
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
119
|
+
if (currentBlockDeepLink) {
|
|
120
|
+
if (InngageProperties.getDebugMode()) {
|
|
121
|
+
console.log('[SDK] blockDeepLink ativo; ignorando abertura de url:', { url, type });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
if (type === 'inapp') {
|
|
126
|
+
try {
|
|
127
|
+
if (await InAppBrowser.isAvailable()) {
|
|
128
|
+
await InAppBrowser.open(url, {
|
|
129
|
+
dismissButtonStyle: 'close',
|
|
130
|
+
preferredBarTintColor: '#453AA4',
|
|
131
|
+
preferredControlTintColor: 'white',
|
|
132
|
+
enableDefaultShare: true,
|
|
133
|
+
enableBarCollapsing: true,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
Linking.openURL(url);
|
|
138
|
+
}
|
|
80
139
|
}
|
|
81
|
-
|
|
82
|
-
|
|
140
|
+
catch (error) {
|
|
141
|
+
console.error(error);
|
|
83
142
|
}
|
|
84
143
|
}
|
|
85
|
-
|
|
86
|
-
console.error(
|
|
144
|
+
else if (type === 'deep') {
|
|
145
|
+
Linking.openURL(url).catch((err) => console.error('Erro ao abrir o link:', err));
|
|
87
146
|
}
|
|
88
147
|
}
|
|
89
|
-
else if (type === 'deep') {
|
|
90
|
-
Linking.openURL(url).catch((err) => console.error('Erro ao abrir o link:', err));
|
|
91
|
-
}
|
|
92
148
|
}
|
|
93
149
|
await ApiService.sendNotification(request);
|
|
94
150
|
};
|
|
95
151
|
const handleUniqueRemoteMessage = async (remoteMessage) => {
|
|
96
152
|
try {
|
|
97
|
-
const lastId = await AsyncStorage.getItem('LAST_REMOTE_MESSAGE_ID');
|
|
98
153
|
const newId = remoteMessage?.messageId;
|
|
99
|
-
|
|
100
|
-
|
|
154
|
+
const lastClickId = await AsyncStorage.getItem(KEY_LAST_CLICK_HANDLED_MESSAGE_ID);
|
|
155
|
+
await deliverToCallbackOnce({ messageId: remoteMessage.messageId, data: remoteMessage.data });
|
|
156
|
+
if (!newId) {
|
|
101
157
|
await handleNotification(remoteMessage);
|
|
158
|
+
return;
|
|
102
159
|
}
|
|
160
|
+
if (lastClickId === newId)
|
|
161
|
+
return;
|
|
162
|
+
await AsyncStorage.setItem(KEY_LAST_CLICK_HANDLED_MESSAGE_ID, newId);
|
|
163
|
+
await handleNotification(remoteMessage);
|
|
103
164
|
}
|
|
104
165
|
catch (e) {
|
|
105
166
|
console.error(e);
|
|
106
167
|
}
|
|
107
168
|
};
|
|
108
|
-
export const
|
|
169
|
+
export const messagingHeadlessTask = () => {
|
|
170
|
+
return setBackgroundMessageHandler(messagingInstance, async (remoteMessage) => {
|
|
171
|
+
if (InngageProperties.getDebugMode()) {
|
|
172
|
+
console.log('INNGAGE BACKGROUND AND CLOSED DATA: ', remoteMessage);
|
|
173
|
+
}
|
|
174
|
+
const additional = remoteMessage?.data?.additional_data;
|
|
175
|
+
if (additional != null) {
|
|
176
|
+
await AsyncStorage.setItem(KEY_INAPP, toStringValue(additional));
|
|
177
|
+
}
|
|
178
|
+
const data = (remoteMessage?.data ?? {});
|
|
179
|
+
await enqueuePendingMessage({
|
|
180
|
+
messageId: remoteMessage?.messageId,
|
|
181
|
+
data,
|
|
182
|
+
ts: Date.now(),
|
|
183
|
+
});
|
|
184
|
+
return Promise.resolve();
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
export const useInAppHandler = () => {
|
|
188
|
+
const [showInApp, setShowInApp] = useState(false);
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
const checkInAppData = async () => {
|
|
191
|
+
const inAppData = await AsyncStorage.getItem(KEY_INAPP);
|
|
192
|
+
if (inAppData)
|
|
193
|
+
setShowInApp(true);
|
|
194
|
+
};
|
|
195
|
+
checkInAppData();
|
|
196
|
+
const subscription = AppState.addEventListener('change', (next) => {
|
|
197
|
+
if (next === 'active')
|
|
198
|
+
checkInAppData();
|
|
199
|
+
});
|
|
200
|
+
return () => subscription.remove();
|
|
201
|
+
}, []);
|
|
202
|
+
return { showInApp, setShowInApp };
|
|
203
|
+
};
|
|
204
|
+
export const InngageNotificationMessage = (firebaseListenCallback, imageNotification, colorNotification, options) => {
|
|
109
205
|
currentFirebaseListenCallback = firebaseListenCallback;
|
|
110
206
|
currentImageNotification = imageNotification;
|
|
111
207
|
currentColorNotification = colorNotification;
|
|
208
|
+
currentBlockDeepLink = options?.blockDeepLink ?? false;
|
|
112
209
|
ensureAndroidChannel().catch(() => { });
|
|
210
|
+
if (!appStateSubscription) {
|
|
211
|
+
appStateSubscription = AppState.addEventListener('change', (next) => {
|
|
212
|
+
if (next === 'active') {
|
|
213
|
+
flushPendingQueue().catch(() => { });
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
113
217
|
if (!foregroundUnsubscribe) {
|
|
114
218
|
const handleMessage = async (remoteMessage) => {
|
|
115
219
|
const notificationData = remoteMessage.data ?? {};
|
|
116
220
|
if (notificationData.additional_data != null) {
|
|
117
|
-
await AsyncStorage.setItem(
|
|
221
|
+
await AsyncStorage.setItem(KEY_INAPP, toStringValue(notificationData.additional_data));
|
|
118
222
|
}
|
|
223
|
+
currentFirebaseListenCallback?.(notificationData);
|
|
119
224
|
if (notifee) {
|
|
120
225
|
const title = remoteMessage.notification?.title ??
|
|
121
226
|
notificationData.title ??
|
|
@@ -145,39 +250,35 @@ export const InngageNotificationMessage = (firebaseListenCallback, imageNotifica
|
|
|
145
250
|
}
|
|
146
251
|
: undefined,
|
|
147
252
|
});
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
else {
|
|
162
|
-
if (InngageProperties.getDebugMode()) {
|
|
163
|
-
console.log('[SDK] Notifee não instalado; mensagem em foreground: ', remoteMessage);
|
|
253
|
+
if (!notifeeForegroundUnsubscribe) {
|
|
254
|
+
notifeeForegroundUnsubscribe = notifee.onForegroundEvent(({ type, detail }) => {
|
|
255
|
+
switch (type) {
|
|
256
|
+
case EventType.DISMISSED:
|
|
257
|
+
if (InngageProperties.getDebugMode()) {
|
|
258
|
+
console.log('User dismissed notification', detail.notification);
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
261
|
+
case EventType.PRESS:
|
|
262
|
+
handleNotification(remoteMessage);
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
});
|
|
164
266
|
}
|
|
165
267
|
}
|
|
268
|
+
else if (InngageProperties.getDebugMode()) {
|
|
269
|
+
console.log('[SDK] Notifee não instalado; mensagem em foreground: ', remoteMessage);
|
|
270
|
+
}
|
|
166
271
|
if (InngageProperties.getDebugMode()) {
|
|
167
272
|
console.log('Remote message received in foreground: ', remoteMessage);
|
|
168
273
|
}
|
|
169
|
-
if (currentFirebaseListenCallback && remoteMessage) {
|
|
170
|
-
currentFirebaseListenCallback(notificationData);
|
|
171
|
-
}
|
|
172
274
|
};
|
|
173
275
|
foregroundUnsubscribe = onMessage(messagingInstance, handleMessage);
|
|
174
276
|
}
|
|
175
277
|
if (!openedAppUnsubscribe) {
|
|
176
|
-
openedAppUnsubscribe = onNotificationOpenedApp(messagingInstance, (remoteMessage) => {
|
|
278
|
+
openedAppUnsubscribe = onNotificationOpenedApp(messagingInstance, async (remoteMessage) => {
|
|
177
279
|
if (!remoteMessage)
|
|
178
280
|
return;
|
|
179
|
-
|
|
180
|
-
handleNotification(remoteMessage);
|
|
281
|
+
await handleUniqueRemoteMessage(remoteMessage);
|
|
181
282
|
});
|
|
182
283
|
}
|
|
183
284
|
if (!initialNotificationHandled) {
|
|
@@ -186,6 +287,15 @@ export const InngageNotificationMessage = (firebaseListenCallback, imageNotifica
|
|
|
186
287
|
.then(async (value) => {
|
|
187
288
|
if (!value)
|
|
188
289
|
return;
|
|
290
|
+
const id = value.messageId ??
|
|
291
|
+
value.data?.notId ??
|
|
292
|
+
'';
|
|
293
|
+
if (id) {
|
|
294
|
+
const last = await AsyncStorage.getItem(KEY_LAST_INITIAL_HANDLED_ID);
|
|
295
|
+
if (last === id)
|
|
296
|
+
return;
|
|
297
|
+
await AsyncStorage.setItem(KEY_LAST_INITIAL_HANDLED_ID, id);
|
|
298
|
+
}
|
|
189
299
|
await handleUniqueRemoteMessage(value);
|
|
190
300
|
})
|
|
191
301
|
.catch((e) => console.error(e));
|
|
@@ -194,8 +304,12 @@ export const InngageNotificationMessage = (firebaseListenCallback, imageNotifica
|
|
|
194
304
|
unsubscribe: () => {
|
|
195
305
|
foregroundUnsubscribe?.();
|
|
196
306
|
openedAppUnsubscribe?.();
|
|
307
|
+
notifeeForegroundUnsubscribe?.();
|
|
308
|
+
appStateSubscription?.remove();
|
|
197
309
|
foregroundUnsubscribe = null;
|
|
198
310
|
openedAppUnsubscribe = null;
|
|
311
|
+
notifeeForegroundUnsubscribe = null;
|
|
312
|
+
appStateSubscription = null;
|
|
199
313
|
initialNotificationHandled = false;
|
|
200
314
|
},
|
|
201
315
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inngageregistry/inngage-react",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Inngage Plugin for React Native applications for marketing campaign optimization using Push Notification.",
|
|
5
5
|
"author": "Inngage Developer",
|
|
6
6
|
"license": "ISC",
|