@applicaster/zapp-react-native-utils 13.0.0-rc.61 → 13.0.0-rc.63

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-utils",
3
- "version": "13.0.0-rc.61",
3
+ "version": "13.0.0-rc.63",
4
4
  "description": "Applicaster Zapp React Native utilities package",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "homepage": "https://github.com/applicaster/quickbrick#readme",
30
30
  "dependencies": {
31
- "@applicaster/applicaster-types": "13.0.0-rc.61",
31
+ "@applicaster/applicaster-types": "13.0.0-rc.63",
32
32
  "buffer": "^5.2.1",
33
33
  "camelize": "^1.0.0",
34
34
  "dayjs": "^1.11.10",
@@ -0,0 +1,673 @@
1
+ import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
2
+ import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
3
+ import { parseJsonIfNeeded } from "@applicaster/zapp-react-native-utils/functionUtils";
4
+
5
+ export const JS_BRIDGE_EVENTS = {
6
+ DISMISS_HOOK: "DISMISS_HOOK",
7
+ LOG: "LOG",
8
+ GET_SESSION_STORAGE: "GET_SESSION_STORAGE",
9
+ SET_SESSION_STORAGE: "SET_SESSION_STORAGE",
10
+ GET_LOCAL_STORAGE: "GET_LOCAL_STORAGE",
11
+ SET_LOCAL_STORAGE: "SET_LOCAL_STORAGE",
12
+ GET_SECURE_LOCAL_STORAGE: "GET_SECURE_LOCAL_STORAGE",
13
+ SET_SECURE_LOCAL_STORAGE: "SET_SECURE_LOCAL_STORAGE",
14
+ CLOSE_WEBVIEW: "CLOSE_WEBVIEW",
15
+ NAVIGATE_TO_SCREEN: "NAVIGATE_TO_SCREEN",
16
+ NAVIGATE_TO_ENTRY: "NAVIGATE_TO_ENTRY",
17
+ OPEN_URL_SCHEME: "OPEN_URL_SCHEME",
18
+ OPEN_NATIVE_SHARE: "OPEN_NATIVE_SHARE",
19
+ ADD_SESSION_STORAGE_LISTENER: "ADD_SESSION_STORAGE_LISTENER",
20
+ REMOVE_SESSION_STORAGE_LISTENER: "REMOVE_SESSION_STORAGE_LISTENER",
21
+ ADD_LOCAL_STORAGE_LISTENER: "ADD_LOCAL_STORAGE_LISTENER",
22
+ REMOVE_LOCAL_STORAGE_LISTENER: "REMOVE_LOCAL_STORAGE_LISTENER",
23
+ };
24
+
25
+ export const INVOKE_APPLICASTER_CALLBACK = "__invoke__applicaster__callback";
26
+
27
+ const APPLICASTER_CALLBACK_KEY = "__applicaster__js2native__callbacks";
28
+ const getSessionStorageItem = "getSessionStorageItem";
29
+ const setSessionStorageItem = "setSessionStorageItem";
30
+ const getLocalStorageItem = "getLocalStorageItem";
31
+ const setLocalStorageItem = "setLocalStorageItem";
32
+ const getSecureLocalStorageItem = "getSecureLocalStorageItem";
33
+ const setSecureLocalStorageItem = "setSecureLocalStorageItem";
34
+ const addSessionStorageListenerFn = "addSessionStorageListener";
35
+ const addLocalStorageListenerFn = "addLocalStorageListener";
36
+
37
+ const activeListeners: { [key: string]: () => void } = {};
38
+
39
+ const sendMessage = `
40
+ function sendMessage(type, payload, meta) {
41
+ if (window.ReactNativeWebView && typeof window.ReactNativeWebView.postMessage === 'function') {
42
+ window.ReactNativeWebView.postMessage(JSON.stringify({
43
+ type: type,
44
+ payload: payload,
45
+ meta: meta
46
+ }));
47
+ }
48
+ }
49
+ `;
50
+
51
+ const consolePolyfill = `
52
+ var consoleLog = console.log;
53
+ var consoleWarn = console.warn;
54
+ var consoleError = console.error;
55
+
56
+ function logInConsole(level, args) {
57
+ var serializableArgs = [];
58
+ try {
59
+ serializableArgs = args.map(function(arg) {
60
+ try {
61
+ return JSON.parse(JSON.stringify(arg));
62
+ } catch (e) {
63
+ if (typeof arg === 'function') return '[Function]';
64
+ if (typeof arg === 'symbol') return String(arg);
65
+ if (arg instanceof Error) return { name: arg.name, message: arg.message, stack: arg.stack };
66
+ return '[Unserializable]';
67
+ }
68
+ });
69
+ } catch(e) {
70
+ serializableArgs = ['Error processing console arguments'];
71
+ }
72
+
73
+ sendMessage("${JS_BRIDGE_EVENTS.LOG}", {
74
+ level: level,
75
+ messages: JSON.stringify(serializableArgs)
76
+ });
77
+ }
78
+
79
+ function log() {
80
+ var args = [].slice.call(arguments);
81
+ consoleLog.apply(null, args);
82
+ try {
83
+ logInConsole("log", args);
84
+ } catch (e) {
85
+ consoleError("Error in custom console.log:", e);
86
+ }
87
+ };
88
+
89
+ function warn() {
90
+ var args = [].slice.call(arguments);
91
+ consoleWarn.apply(null, args);
92
+ try {
93
+ logInConsole("warn", args);
94
+ } catch (e) {
95
+ consoleError("Error in custom console.warn:", e);
96
+ }
97
+ }
98
+
99
+ function error() {
100
+ var args = [].slice.call(arguments);
101
+ consoleError.apply(null, args);
102
+ try {
103
+ logInConsole("error", args);
104
+ } catch (e) {
105
+ consoleError("Error in custom console.error:", e);
106
+ }
107
+ }
108
+
109
+ setTimeout(function() {
110
+ console.log = log;
111
+ console.warn = warn;
112
+ console.error = error;
113
+ }, 0);
114
+ `;
115
+
116
+ const callbackHandling = `
117
+ window.Applicaster = window.Applicaster || {};
118
+ window.Applicaster.${APPLICASTER_CALLBACK_KEY} = window.Applicaster.${APPLICASTER_CALLBACK_KEY} || {};
119
+
120
+ var ${INVOKE_APPLICASTER_CALLBACK} = function(key, args) {
121
+ var callback = window.Applicaster.${APPLICASTER_CALLBACK_KEY}[key];
122
+
123
+ var deleteCallback = true;
124
+ if (typeof callback === "function") {
125
+ var result = callback.apply(null, args);
126
+ if (result === false) {
127
+ deleteCallback = false;
128
+ }
129
+ }
130
+ if(deleteCallback) {
131
+ delete window.Applicaster.${APPLICASTER_CALLBACK_KEY}[key];
132
+ }
133
+ }
134
+
135
+ window.Applicaster.${INVOKE_APPLICASTER_CALLBACK} = ${INVOKE_APPLICASTER_CALLBACK};
136
+ `;
137
+
138
+ export function invokeWebCallback(key, args) {
139
+ const argsArray = Array.isArray(args) ? args : [args];
140
+ let stringifiedArgs = "[]";
141
+
142
+ try {
143
+ stringifiedArgs = JSON.stringify(argsArray);
144
+ } catch (e) {
145
+ try {
146
+ stringifiedArgs = JSON.stringify([
147
+ { error: "Failed to stringify arguments", details: e.message },
148
+ ]);
149
+ } catch {
150
+ stringifiedArgs = "[]";
151
+ }
152
+ }
153
+
154
+ return `
155
+ (function() {
156
+ var callbackFn = window.Applicaster && window.Applicaster.${APPLICASTER_CALLBACK_KEY};
157
+ if (callbackFn && typeof callbackFn["${key}"] === 'function') {
158
+ try {
159
+ setTimeout(function() {
160
+ callbackFn["${key}"].apply(null, ${stringifiedArgs});
161
+ }, 0);
162
+ } catch (e) {
163
+ console.error("Error invoking web callback ${key}:", e);
164
+ }
165
+ } else {
166
+ // console.warn("Web callback ${key} not found or not a function.");
167
+ }
168
+ return true;
169
+ })();
170
+ `;
171
+ }
172
+
173
+ const hookCallback = `
174
+ function hookCallback(options) {
175
+ var _opts = {};
176
+
177
+ if (typeof options !== "undefined") {
178
+ _opts = options;
179
+ }
180
+
181
+ var success = typeof _opts.success === 'boolean' ? _opts.success : false;
182
+ var error = _opts.error || undefined;
183
+ var payload = _opts.payload || undefined;
184
+
185
+ sendMessage("${JS_BRIDGE_EVENTS.DISMISS_HOOK}", {
186
+ success: success,
187
+ error: error,
188
+ payload: payload
189
+ })
190
+ };
191
+ `;
192
+
193
+ const uuid = `
194
+ function uuid() {
195
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
196
+ var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
197
+ return v.toString(16);
198
+ });
199
+ }
200
+ `;
201
+
202
+ const registerCallback = `
203
+ function registerCallback(cb, persistent) {
204
+ var index = uuid();
205
+ window.Applicaster = window.Applicaster || {};
206
+ window.Applicaster.${APPLICASTER_CALLBACK_KEY} = window.Applicaster.${APPLICASTER_CALLBACK_KEY} || {};
207
+
208
+ window.Applicaster.${APPLICASTER_CALLBACK_KEY}[index] = function() {
209
+ var result = cb.apply(null, arguments);
210
+ return persistent ? false : result;
211
+ };
212
+ return index;
213
+ }
214
+ `;
215
+
216
+ const getSetStorage = `
217
+ function getSetStorage(type, payload) {
218
+ return new Promise(function(resolve, reject) {
219
+ var index = registerCallback(function(result) {
220
+ if (result && result.success === false) {
221
+ reject(result.error || new Error("Storage operation failed"));
222
+ } else {
223
+ resolve(result);
224
+ }
225
+ });
226
+ sendMessage(type, payload, index);
227
+ });
228
+ }
229
+ `;
230
+
231
+ const getInSessionStorage = `
232
+ function ${getSessionStorageItem}(key, namespace) {
233
+ return getSetStorage(
234
+ "${JS_BRIDGE_EVENTS.GET_SESSION_STORAGE}",
235
+ { key: key, namespace: namespace }
236
+ );
237
+ }
238
+ `;
239
+
240
+ const setInSessionStorage = `
241
+ function ${setSessionStorageItem}(key, value, namespace) {
242
+ try {
243
+ JSON.stringify(value);
244
+ return getSetStorage(
245
+ "${JS_BRIDGE_EVENTS.SET_SESSION_STORAGE}",
246
+ { key: key, value: value, namespace: namespace }
247
+ );
248
+ } catch (e) {
249
+ return Promise.reject(new Error("Value is not serializable: " + e.message));
250
+ }
251
+ }
252
+ `;
253
+
254
+ const getInLocalStorage = `
255
+ function ${getLocalStorageItem}(key, namespace) {
256
+ return getSetStorage(
257
+ "${JS_BRIDGE_EVENTS.GET_LOCAL_STORAGE}",
258
+ { key: key, namespace: namespace }
259
+ );
260
+ }
261
+ `;
262
+
263
+ const setInLocalStorage = `
264
+ function ${setLocalStorageItem}(key, value, namespace) {
265
+ try {
266
+ JSON.stringify(value);
267
+ return getSetStorage(
268
+ "${JS_BRIDGE_EVENTS.SET_LOCAL_STORAGE}",
269
+ { key: key, value: value, namespace: namespace }
270
+ );
271
+ } catch (e) {
272
+ return Promise.reject(new Error("Value is not serializable: " + e.message));
273
+ }
274
+ }
275
+ `;
276
+
277
+ const getInSecureLocalStorage = `
278
+ function ${getSecureLocalStorageItem}(key, namespace) {
279
+ return getSetStorage(
280
+ "${JS_BRIDGE_EVENTS.GET_SECURE_LOCAL_STORAGE}",
281
+ { key: key, namespace: namespace }
282
+ );
283
+ }
284
+ `;
285
+
286
+ const setInSecureLocalStorage = `
287
+ function ${setSecureLocalStorageItem}(key, value, namespace) {
288
+ try {
289
+ JSON.stringify(value);
290
+ return getSetStorage(
291
+ "${JS_BRIDGE_EVENTS.SET_SECURE_LOCAL_STORAGE}",
292
+ { key: key, value: value, namespace: namespace }
293
+ );
294
+ } catch (e) {
295
+ return Promise.reject(new Error("Value is not serializable: " + e.message));
296
+ }
297
+ }
298
+ `;
299
+
300
+ const addSessionStorageListener = `
301
+ function ${addSessionStorageListenerFn}(optionsOrCallback, callback) {
302
+ var opts = {};
303
+ var cb = callback;
304
+
305
+ if (typeof optionsOrCallback === 'function') {
306
+ cb = optionsOrCallback;
307
+ } else if (typeof optionsOrCallback === 'object' && optionsOrCallback !== null) {
308
+ opts = optionsOrCallback;
309
+ } else if (optionsOrCallback !== undefined && optionsOrCallback !== null) {
310
+ console.error("Invalid arguments for addSessionStorageListener. Expected ({ key, namespace }, callback) or (callback).");
311
+ return function() {};
312
+ }
313
+
314
+ if (typeof cb !== 'function') {
315
+ console.error("addSessionStorageListener requires a callback function.");
316
+ return function() {};
317
+ }
318
+
319
+ var listenerId = registerCallback(cb, true);
320
+
321
+ sendMessage("${JS_BRIDGE_EVENTS.ADD_SESSION_STORAGE_LISTENER}", { key: opts.key, namespace: opts.namespace }, listenerId);
322
+
323
+ return function removeListener() {
324
+ sendMessage("${JS_BRIDGE_EVENTS.REMOVE_SESSION_STORAGE_LISTENER}", { listenerId: listenerId });
325
+ if (window.Applicaster && window.Applicaster.${APPLICASTER_CALLBACK_KEY}) {
326
+ delete window.Applicaster.${APPLICASTER_CALLBACK_KEY}[listenerId];
327
+ }
328
+ };
329
+ }
330
+ `;
331
+
332
+ const addLocalStorageListener = `
333
+ function ${addLocalStorageListenerFn}(optionsOrCallback, callback) {
334
+ var opts = {};
335
+ var cb = callback;
336
+
337
+ if (typeof optionsOrCallback === 'function') {
338
+ cb = optionsOrCallback;
339
+ } else if (typeof optionsOrCallback === 'object' && optionsOrCallback !== null) {
340
+ opts = optionsOrCallback;
341
+ } else if (optionsOrCallback !== undefined && optionsOrCallback !== null) {
342
+ console.error("Invalid arguments for addLocalStorageListener. Expected ({ key, namespace }, callback) or (callback).");
343
+ return function() {};
344
+ }
345
+
346
+ if (typeof cb !== 'function') {
347
+ console.error("addLocalStorageListener requires a callback function.");
348
+ return function() {};
349
+ }
350
+
351
+ var listenerId = registerCallback(cb, true);
352
+
353
+ sendMessage("${JS_BRIDGE_EVENTS.ADD_LOCAL_STORAGE_LISTENER}", { key: opts.key, namespace: opts.namespace }, listenerId);
354
+
355
+ return function removeListener() {
356
+ sendMessage("${JS_BRIDGE_EVENTS.REMOVE_LOCAL_STORAGE_LISTENER}", { listenerId: listenerId });
357
+ if (window.Applicaster && window.Applicaster.${APPLICASTER_CALLBACK_KEY}) {
358
+ delete window.Applicaster.${APPLICASTER_CALLBACK_KEY}[listenerId];
359
+ }
360
+ };
361
+ }
362
+ `;
363
+
364
+ const closeWebview = `
365
+ function closeWebview() {
366
+ sendMessage("${JS_BRIDGE_EVENTS.CLOSE_WEBVIEW}")
367
+ }
368
+ `;
369
+
370
+ const navigateToScreen = `
371
+ function navigateToScreen(screenId) {
372
+ if(!screenId) { console.error("navigateToScreen requires a screenId"); return; }
373
+ sendMessage("${JS_BRIDGE_EVENTS.NAVIGATE_TO_SCREEN}", { screenId: screenId });
374
+ }
375
+ `;
376
+
377
+ const navigateToEntry = `
378
+ function navigateToEntry(entry) {
379
+ if(!entry) { console.error("navigateToEntry requires an entry object"); return; }
380
+ sendMessage("${JS_BRIDGE_EVENTS.NAVIGATE_TO_ENTRY}", { entry: entry });
381
+ }
382
+ `;
383
+
384
+ const openUrlScheme = `
385
+ function openUrlScheme(url) {
386
+ if(!url || typeof url !== 'string') { console.error("openUrlScheme requires a valid URL string"); return; }
387
+ sendMessage("${JS_BRIDGE_EVENTS.OPEN_URL_SCHEME}", { url: url });
388
+ }
389
+ `;
390
+
391
+ const runInvokeShare = `
392
+ function invokeShare(item, options) {
393
+ if(!item) { console.error("invokeShare requires an item object"); return; }
394
+ sendMessage("${JS_BRIDGE_EVENTS.OPEN_NATIVE_SHARE}", { item: item, options: options });
395
+ }
396
+ `;
397
+
398
+ const initializeBridge = (payload, hookPlugin, screens, safeAreaInsets) => `
399
+ (function() {
400
+ window.Applicaster = window.Applicaster || {};
401
+
402
+ ${sendMessage}
403
+ ${consolePolyfill}
404
+ ${hookCallback}
405
+ ${uuid}
406
+ ${callbackHandling}
407
+ ${registerCallback}
408
+ ${getSetStorage}
409
+ ${getInSessionStorage}
410
+ ${setInSessionStorage}
411
+ ${getInLocalStorage}
412
+ ${setInLocalStorage}
413
+ ${getInSecureLocalStorage}
414
+ ${setInSecureLocalStorage}
415
+ ${addSessionStorageListener}
416
+ ${addLocalStorageListener}
417
+ ${closeWebview}
418
+ ${navigateToScreen}
419
+ ${navigateToEntry}
420
+ ${openUrlScheme}
421
+ ${runInvokeShare}
422
+
423
+ var JS2Native = {
424
+ screenHook: {
425
+ data: {
426
+ payload: (function() { try { return JSON.parse(${payload}); } catch(e) { console.error('Failed to parse screenHook payload:', e); return null; } })(),
427
+ plugin: (function() { try { return JSON.parse(${hookPlugin}); } catch(e) { console.error('Failed to parse screenHook plugin:', e); return null; } })()
428
+ },
429
+ callback: hookCallback
430
+ },
431
+ sessionStorage: {
432
+ getItem: ${getSessionStorageItem},
433
+ setItem: ${setSessionStorageItem},
434
+ addListener: ${addSessionStorageListenerFn}
435
+ },
436
+ localStorage: {
437
+ getItem: ${getLocalStorageItem},
438
+ setItem: ${setLocalStorageItem},
439
+ getSecuredItem: ${getSecureLocalStorageItem},
440
+ setSecuredItem: ${setSecureLocalStorageItem},
441
+ addListener: ${addLocalStorageListenerFn}
442
+ },
443
+ nativeEnvironment: true,
444
+ navigation: {
445
+ closeWebview: closeWebview,
446
+ navigateToScreen: navigateToScreen,
447
+ navigateToEntry: navigateToEntry,
448
+ screens: (function() { try { return JSON.parse(${screens}); } catch(e) { console.error('Failed to parse navigation screens:', e); return []; } })(),
449
+ openUrlScheme: openUrlScheme
450
+ },
451
+ safeAreaInsets: (function() { try { return JSON.parse(${safeAreaInsets}); } catch(e) { console.error('Failed to parse safeAreaInsets:', e); return {}; } })(),
452
+ nativeShare: invokeShare
453
+ };
454
+
455
+ window.Applicaster.JS2Native = JS2Native;
456
+ window.JS2Native = JS2Native;
457
+
458
+ return JS2Native;
459
+ })();
460
+ `;
461
+
462
+ export const jsBridge = (hooksProps, screens, safeAreaInsets) => `
463
+ var Applicaster = window.Applicaster || {};
464
+ window.Applicaster = Applicaster;
465
+
466
+ ${initializeBridge(
467
+ JSON.stringify(hooksProps?.payload),
468
+ JSON.stringify(hooksProps?.hookPlugin),
469
+ JSON.stringify(Object.values(screens || {})),
470
+ JSON.stringify(safeAreaInsets || {})
471
+ )}
472
+
473
+ true;
474
+ `;
475
+
476
+ // handler for removing listeners
477
+ async function removeStorageListenerHandler(payload: { listenerId?: string }) {
478
+ const { listenerId } = payload || {};
479
+
480
+ if (!listenerId) {
481
+ return;
482
+ }
483
+
484
+ const removeListener = activeListeners[listenerId];
485
+
486
+ if (removeListener) {
487
+ try {
488
+ removeListener();
489
+ delete activeListeners[listenerId];
490
+ } catch (error) {
491
+ //
492
+ }
493
+ }
494
+ }
495
+
496
+ function log({ level, messages }) {
497
+ try {
498
+ const parsedMessages = parseJsonIfNeeded(messages);
499
+ const logFn = console[level] || console.log;
500
+
501
+ if (Array.isArray(parsedMessages)) {
502
+ logFn("JS LOG:", ...parsedMessages);
503
+ } else {
504
+ logFn("JS LOG:", parsedMessages);
505
+ }
506
+ } catch (e) {
507
+ // console.error("Error parsing or logging JS message:", e, messages);
508
+ }
509
+ }
510
+
511
+ async function storageGetItem({ key, namespace }) {
512
+ try {
513
+ const result = await sessionStorage.getItem(key, namespace);
514
+
515
+ return { success: true, value: result };
516
+ } catch (error) {
517
+ return { success: false, error: error.message };
518
+ }
519
+ }
520
+
521
+ async function storageSetItem({ key, value, namespace }) {
522
+ try {
523
+ const result = await sessionStorage.setItem(key, value, namespace);
524
+
525
+ return { success: !!result };
526
+ } catch (error) {
527
+ return { success: false, error: error.message };
528
+ }
529
+ }
530
+
531
+ async function localStorageGetItem({ key, namespace }) {
532
+ try {
533
+ const result = await localStorage.getItem(key, namespace);
534
+
535
+ return { success: true, value: result };
536
+ } catch (error) {
537
+ return { success: false, error: error.message };
538
+ }
539
+ }
540
+
541
+ async function localStorageSetItem({ key, value, namespace }) {
542
+ try {
543
+ const result = await localStorage.setItem(key, value, namespace);
544
+
545
+ return { success: !!result };
546
+ } catch (error) {
547
+ return { success: false, error: error.message };
548
+ }
549
+ }
550
+
551
+ async function secureStorageGetItem({ key, namespace }) {
552
+ try {
553
+ const result = await localStorage.getKeychainItem(key, namespace);
554
+
555
+ return { success: true, value: result };
556
+ } catch (error) {
557
+ return { success: false, error: error.message };
558
+ }
559
+ }
560
+
561
+ async function secureStorageSetItem({ key, value, namespace }) {
562
+ try {
563
+ const result = await localStorage.setKeychainItem(key, value, namespace);
564
+
565
+ return { success: !!result };
566
+ } catch (error) {
567
+ return { success: false, error: error.message };
568
+ }
569
+ }
570
+
571
+ // handler for adding listeners
572
+ async function addStorageListenerHandler(
573
+ storageType: "local" | "session",
574
+ payload: { key?: string; namespace?: string },
575
+ callbackKey: string,
576
+ webViewRef: React.RefObject<any>
577
+ ) {
578
+ if (!webViewRef?.current?.injectJavaScript) {
579
+ return;
580
+ }
581
+
582
+ const storage = storageType === "local" ? localStorage : sessionStorage;
583
+ const { key, namespace } = payload || {};
584
+
585
+ const listenerCallback = (eventData: {
586
+ key: string;
587
+ namespace: string;
588
+ value: any;
589
+ }) => {
590
+ const run = invokeWebCallback(callbackKey, [eventData]);
591
+
592
+ if (webViewRef?.current?.injectJavaScript) {
593
+ webViewRef.current.injectJavaScript(run);
594
+ } else {
595
+ removeStorageListenerHandler({ listenerId: callbackKey });
596
+ }
597
+ };
598
+
599
+ try {
600
+ const removeListener = storage.addListener(
601
+ { key, namespace },
602
+ listenerCallback
603
+ );
604
+
605
+ activeListeners[callbackKey] = removeListener;
606
+ } catch (error) {
607
+ const errorRun = invokeWebCallback(callbackKey, [
608
+ { success: false, error: `Failed to add listener: ${error.message}` },
609
+ ]);
610
+
611
+ if (webViewRef?.current?.injectJavaScript) {
612
+ webViewRef.current.injectJavaScript(errorRun);
613
+
614
+ webViewRef.current.injectJavaScript(
615
+ `delete window.Applicaster.${APPLICASTER_CALLBACK_KEY}['${callbackKey}']; true;`
616
+ );
617
+ }
618
+
619
+ delete activeListeners[callbackKey];
620
+ }
621
+ }
622
+
623
+ export function resolveBridgeAction(
624
+ type: string,
625
+ webViewRef: React.RefObject<any>
626
+ ) {
627
+ switch (type) {
628
+ case JS_BRIDGE_EVENTS.LOG:
629
+ return (payload) => log(payload);
630
+
631
+ case JS_BRIDGE_EVENTS.GET_SESSION_STORAGE:
632
+ return storageGetItem;
633
+ case JS_BRIDGE_EVENTS.SET_SESSION_STORAGE:
634
+ return storageSetItem;
635
+ case JS_BRIDGE_EVENTS.GET_LOCAL_STORAGE:
636
+ return localStorageGetItem;
637
+ case JS_BRIDGE_EVENTS.SET_LOCAL_STORAGE:
638
+ return localStorageSetItem;
639
+ case JS_BRIDGE_EVENTS.GET_SECURE_LOCAL_STORAGE:
640
+ return secureStorageGetItem;
641
+ case JS_BRIDGE_EVENTS.SET_SECURE_LOCAL_STORAGE:
642
+ return secureStorageSetItem;
643
+
644
+ case JS_BRIDGE_EVENTS.ADD_LOCAL_STORAGE_LISTENER:
645
+ return (payload, callbackKey) =>
646
+ addStorageListenerHandler("local", payload, callbackKey, webViewRef);
647
+
648
+ case JS_BRIDGE_EVENTS.REMOVE_LOCAL_STORAGE_LISTENER:
649
+ return (payload) => removeStorageListenerHandler(payload);
650
+
651
+ case JS_BRIDGE_EVENTS.ADD_SESSION_STORAGE_LISTENER:
652
+ return (payload, callbackKey) =>
653
+ addStorageListenerHandler("session", payload, callbackKey, webViewRef);
654
+
655
+ case JS_BRIDGE_EVENTS.REMOVE_SESSION_STORAGE_LISTENER:
656
+ return (payload) => removeStorageListenerHandler(payload);
657
+
658
+ default:
659
+ return async (_payload, callbackKey) => {
660
+ if (callbackKey && webViewRef?.current?.injectJavaScript) {
661
+ const errorResult = {
662
+ success: false,
663
+ error: `Unknown action type: ${type}`,
664
+ };
665
+
666
+ const errorRun = invokeWebCallback(callbackKey, [errorResult]);
667
+ webViewRef.current.injectJavaScript(errorRun);
668
+ }
669
+
670
+ return Promise.reject(new Error(`Unknown action type: ${type}`));
671
+ };
672
+ }
673
+ }
@@ -0,0 +1,55 @@
1
+ import { DeviceEventEmitter, NativeModules } from "react-native";
2
+ import { platformSelect } from "../reactUtils";
3
+
4
+ const { BackgroundTimer: RNBackgroundTimer } = NativeModules;
5
+
6
+ class BackgroundTimer {
7
+ private uniqueId: number;
8
+ private readonly callbacks: Record<number, () => void>;
9
+
10
+ constructor() {
11
+ this.uniqueId = 0;
12
+ this.callbacks = {};
13
+
14
+ const EventEmitter = platformSelect({
15
+ android: DeviceEventEmitter,
16
+ ios: undefined,
17
+ default: undefined,
18
+ });
19
+
20
+ EventEmitter?.addListener("BackgroundTimer.timer.fired", (id: number) => {
21
+ if (this.callbacks[id]) {
22
+ const callback = this.callbacks[id];
23
+ delete this.callbacks[id];
24
+ callback();
25
+ }
26
+ });
27
+ }
28
+
29
+ setTimeout(callback, timeout): number | NodeJS.Timeout {
30
+ if (!RNBackgroundTimer) {
31
+ return global.setTimeout(callback, timeout);
32
+ }
33
+
34
+ this.uniqueId += 1;
35
+ const timeoutId = this.uniqueId;
36
+
37
+ this.callbacks[timeoutId] = callback;
38
+ RNBackgroundTimer.setTimeout(timeoutId, timeout);
39
+
40
+ return timeoutId;
41
+ }
42
+
43
+ clearTimeout(timeoutId: number | NodeJS.Timeout): void {
44
+ if (!RNBackgroundTimer) {
45
+ return global.clearTimeout(timeoutId as NodeJS.Timeout);
46
+ }
47
+
48
+ if (this.callbacks[timeoutId as number]) {
49
+ delete this.callbacks[timeoutId as number];
50
+ RNBackgroundTimer.clearTimeout(timeoutId);
51
+ }
52
+ }
53
+ }
54
+
55
+ export default new BackgroundTimer();
package/time/Timer.ts CHANGED
@@ -1,19 +1,19 @@
1
+ import BackgroundTimer from "./BackgroundTimer";
2
+
1
3
  export class Timer {
2
4
  private startTime: number;
3
- private remaining: number;
4
- private timeoutId: NodeJS.Timeout | null;
5
+ private timeoutId: number | NodeJS.Timeout | null;
5
6
 
6
7
  constructor(
7
8
  private callback: () => void,
8
- private duration: number
9
+ private remaining: number = null
9
10
  ) {
10
- this.remaining = this.duration;
11
11
  this.timeoutId = null;
12
12
  }
13
13
 
14
14
  start(): void {
15
15
  this.startTime = Date.now();
16
- this.timeoutId = setTimeout(this.callback, this.remaining);
16
+ this.timeoutId = BackgroundTimer.setTimeout(this.callback, this.remaining);
17
17
 
18
18
  this.remaining = 0;
19
19
  }
@@ -29,6 +29,6 @@ export class Timer {
29
29
  }
30
30
 
31
31
  clear(): void {
32
- clearTimeout(this.timeoutId);
32
+ BackgroundTimer.clearTimeout(this.timeoutId);
33
33
  }
34
34
  }