@deuna/react-native-sdk 1.0.3 → 1.0.4

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.
Files changed (100) hide show
  1. package/lib/module/DeunaSDK.js +85 -77
  2. package/lib/module/DeunaSDK.js.map +1 -1
  3. package/lib/module/components/DeunaWidget.js +27 -25
  4. package/lib/module/components/DeunaWidget.js.map +1 -1
  5. package/lib/module/components/ExternalUrlWebView.js +56 -0
  6. package/lib/module/components/ExternalUrlWebView.js.map +1 -0
  7. package/lib/module/controllers/BaseWebViewController.js +15 -21
  8. package/lib/module/controllers/BaseWebViewController.js.map +1 -1
  9. package/lib/module/controllers/ElementsWidgetController.js +2 -3
  10. package/lib/module/controllers/ElementsWidgetController.js.map +1 -1
  11. package/lib/module/controllers/{OpenInNewTabController.js → ExternalUrlController.js} +4 -4
  12. package/lib/module/controllers/ExternalUrlController.js.map +1 -0
  13. package/lib/module/controllers/PaymentWidgetController.js +2 -2
  14. package/lib/module/controllers/PaymentWidgetController.js.map +1 -1
  15. package/lib/module/helpers/Completer.js +2 -2
  16. package/lib/module/helpers/Completer.js.map +1 -1
  17. package/lib/module/helpers/CrossPlatformBrowser.js +51 -0
  18. package/lib/module/helpers/CrossPlatformBrowser.js.map +1 -0
  19. package/lib/module/helpers/ExternalUrlHelper.js +96 -0
  20. package/lib/module/helpers/ExternalUrlHelper.js.map +1 -0
  21. package/lib/module/helpers/ViewManager.js +32 -0
  22. package/lib/module/helpers/ViewManager.js.map +1 -0
  23. package/lib/module/helpers/{getController.js → buildDeunaWidgetController.js} +2 -2
  24. package/lib/module/helpers/buildDeunaWidgetController.js.map +1 -0
  25. package/lib/module/helpers/getSubmitStrategy.js +1 -1
  26. package/lib/module/helpers/getSubmitStrategy.js.map +1 -1
  27. package/lib/module/interfaces/constants.js +1 -0
  28. package/lib/module/interfaces/constants.js.map +1 -1
  29. package/lib/module/types/helpers/buildElementsLink.js +3 -1
  30. package/lib/module/types/helpers/buildElementsLink.js.map +1 -1
  31. package/lib/module/types/helpers/buildPaymentLink.js +3 -1
  32. package/lib/module/types/helpers/buildPaymentLink.js.map +1 -1
  33. package/lib/module/types/helpers/buildVoucherLink.js +3 -1
  34. package/lib/module/types/helpers/buildVoucherLink.js.map +1 -1
  35. package/lib/module/types/helpers/constants.js +4 -0
  36. package/lib/module/types/helpers/constants.js.map +1 -0
  37. package/lib/module/types/helpers/urlConfig.js +1 -1
  38. package/lib/module/types/helpers/urlConfig.js.map +1 -1
  39. package/lib/typescript/deuna-sdk-react-native/src/DeunaSDK.d.ts +9 -11
  40. package/lib/typescript/deuna-sdk-react-native/src/DeunaSDK.d.ts.map +1 -1
  41. package/lib/typescript/deuna-sdk-react-native/src/components/DeunaWidget.d.ts.map +1 -1
  42. package/lib/typescript/deuna-sdk-react-native/src/components/ExternalUrlWebView.d.ts +7 -0
  43. package/lib/typescript/deuna-sdk-react-native/src/components/ExternalUrlWebView.d.ts.map +1 -0
  44. package/lib/typescript/deuna-sdk-react-native/src/controllers/BaseWebViewController.d.ts +4 -6
  45. package/lib/typescript/deuna-sdk-react-native/src/controllers/BaseWebViewController.d.ts.map +1 -1
  46. package/lib/typescript/deuna-sdk-react-native/src/controllers/ElementsWidgetController.d.ts.map +1 -1
  47. package/lib/typescript/deuna-sdk-react-native/src/controllers/{OpenInNewTabController.d.ts → ExternalUrlController.d.ts} +2 -2
  48. package/lib/typescript/deuna-sdk-react-native/src/controllers/ExternalUrlController.d.ts.map +1 -0
  49. package/lib/typescript/deuna-sdk-react-native/src/helpers/CrossPlatformBrowser.d.ts +25 -0
  50. package/lib/typescript/deuna-sdk-react-native/src/helpers/CrossPlatformBrowser.d.ts.map +1 -0
  51. package/lib/typescript/deuna-sdk-react-native/src/helpers/ExternalUrlHelper.d.ts +48 -0
  52. package/lib/typescript/deuna-sdk-react-native/src/helpers/ExternalUrlHelper.d.ts.map +1 -0
  53. package/lib/typescript/deuna-sdk-react-native/src/helpers/ViewManager.d.ts +19 -0
  54. package/lib/typescript/deuna-sdk-react-native/src/helpers/ViewManager.d.ts.map +1 -0
  55. package/lib/typescript/deuna-sdk-react-native/src/helpers/{getController.d.ts → buildDeunaWidgetController.d.ts} +2 -2
  56. package/lib/typescript/deuna-sdk-react-native/src/helpers/buildDeunaWidgetController.d.ts.map +1 -0
  57. package/lib/typescript/deuna-sdk-react-native/src/interfaces/constants.d.ts +1 -0
  58. package/lib/typescript/deuna-sdk-react-native/src/interfaces/constants.d.ts.map +1 -1
  59. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/buildElementsLink.d.ts.map +1 -1
  60. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/buildPaymentLink.d.ts.map +1 -1
  61. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/buildVoucherLink.d.ts.map +1 -1
  62. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/constants.d.ts +2 -0
  63. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/constants.d.ts.map +1 -0
  64. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/urlConfig.d.ts +1 -0
  65. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/urlConfig.d.ts.map +1 -1
  66. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/callbacks.d.ts +8 -0
  67. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/callbacks.d.ts.map +1 -1
  68. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/initWidgetBase.d.ts +1 -0
  69. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/initWidgetBase.d.ts.map +1 -1
  70. package/package.json +3 -1
  71. package/src/DeunaSDK.ts +102 -90
  72. package/src/components/DeunaWidget.tsx +38 -35
  73. package/src/components/ExternalUrlWebView.tsx +65 -0
  74. package/src/controllers/BaseWebViewController.ts +21 -27
  75. package/src/controllers/ElementsWidgetController.ts +2 -3
  76. package/src/controllers/{OpenInNewTabController.ts → ExternalUrlController.ts} +3 -3
  77. package/src/controllers/PaymentWidgetController.ts +2 -2
  78. package/src/helpers/Completer.ts +2 -2
  79. package/src/helpers/CrossPlatformBrowser.ts +49 -0
  80. package/src/helpers/ExternalUrlHelper.ts +118 -0
  81. package/src/helpers/ViewManager.ts +45 -0
  82. package/src/helpers/{getController.ts → buildDeunaWidgetController.ts} +1 -1
  83. package/src/helpers/getSubmitStrategy.ts +1 -1
  84. package/src/interfaces/constants.ts +2 -0
  85. package/src/types/helpers/buildElementsLink.ts +4 -1
  86. package/src/types/helpers/buildPaymentLink.ts +2 -0
  87. package/src/types/helpers/buildVoucherLink.ts +2 -0
  88. package/src/types/helpers/constants.ts +1 -0
  89. package/src/types/helpers/urlConfig.ts +3 -1
  90. package/src/types/interfaces/callbacks.ts +10 -1
  91. package/src/types/interfaces/initWidgetBase.ts +1 -0
  92. package/lib/module/components/NewTabWebView.js +0 -56
  93. package/lib/module/components/NewTabWebView.js.map +0 -1
  94. package/lib/module/controllers/OpenInNewTabController.js.map +0 -1
  95. package/lib/module/helpers/getController.js.map +0 -1
  96. package/lib/typescript/deuna-sdk-react-native/src/components/NewTabWebView.d.ts +0 -7
  97. package/lib/typescript/deuna-sdk-react-native/src/components/NewTabWebView.d.ts.map +0 -1
  98. package/lib/typescript/deuna-sdk-react-native/src/controllers/OpenInNewTabController.d.ts.map +0 -1
  99. package/lib/typescript/deuna-sdk-react-native/src/helpers/getController.d.ts.map +0 -1
  100. package/src/components/NewTabWebView.tsx +0 -64
package/src/DeunaSDK.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  BaseDeuna,
3
+ ClosedAction,
3
4
  CustomStyles,
4
5
  GetStateFn,
5
6
  InitElementsWidgetParams,
@@ -18,21 +19,25 @@ import {
18
19
  VoucherWidgetCallbacks,
19
20
  } from './types';
20
21
  import {
21
- BaseWebViewController,
22
22
  DeunaWebViewController,
23
23
  WebViewDelegate,
24
24
  } from './controllers/BaseWebViewController';
25
25
  import { PaymentWidgetController } from './controllers/PaymentWidgetController';
26
- import { OpenInNewTabController } from './controllers/OpenInNewTabController';
27
- import { getWidgetController } from './helpers/getController';
28
- import { Completer } from './helpers/Completer';
26
+ import { buildDeunaWidgetController } from './helpers/buildDeunaWidgetController';
29
27
  import { ElementsWidgetController } from './controllers/ElementsWidgetController';
30
28
  import { DownloadType, Mode, OnDownloadFile } from './interfaces/types';
31
- import { Platform } from 'react-native';
32
29
  import { DeviceFingerprintController } from './controllers/DeviceFingerprintController';
33
- import { DEVICE_FINGERPRINT_URL } from './interfaces/constants';
30
+ import {
31
+ DEVICE_FINGERPRINT_URL,
32
+ DOMAINS_MUST_BE_USE_CROSS_PLATFORM_BROWSER,
33
+ } from './interfaces/constants';
34
34
  import { SubmitStrategy } from './helpers/SubmitStrategy';
35
35
  import { getSubmitStrategy } from './helpers/getSubmitStrategy';
36
+ import { WebViewManager } from './helpers/ViewManager';
37
+ import {
38
+ ExternalUrlBrowser,
39
+ ExternalUrlHelper,
40
+ } from './helpers/ExternalUrlHelper';
36
41
 
37
42
  export class DeunaSDK extends BaseDeuna {
38
43
  constructor(
@@ -42,8 +47,6 @@ export class DeunaSDK extends BaseDeuna {
42
47
  super();
43
48
  }
44
49
 
45
- mode: Mode | null = null;
46
-
47
50
  setCustomStyleFn?: SetCustomStyle | undefined;
48
51
  refetchOrderFn?: RefetchOrder | undefined;
49
52
  getStateFn?: GetStateFn | undefined;
@@ -52,23 +55,12 @@ export class DeunaSDK extends BaseDeuna {
52
55
  closeFn?: (() => void) | undefined;
53
56
 
54
57
  private listeners: Set<() => void> = new Set();
55
- webViewController: DeunaWebViewController | null = null;
56
- newTabWebViewController: BaseWebViewController | null = null;
57
- deviceFingerprintController: DeviceFingerprintController | null = null;
58
- submitStrategy: SubmitStrategy | null = null;
59
58
 
60
- modalDismissPromise: Completer<void> | null = null;
61
- newTabModalDismissPromise: Completer<void> | null = null;
59
+ deunaWidgetManager = new WebViewManager<DeunaWebViewController>();
60
+ externalUrlHelper = new ExternalUrlHelper();
62
61
 
63
- get safeWebViewController(): DeunaWebViewController {
64
- if (!this.webViewController) {
65
- const errorMessage =
66
- 'A variant of the init method must be called first. Please call initPaymentWidget, initVoucherWidget, or another init method before using this functionality.';
67
- console.error(errorMessage);
68
- throw new Error(errorMessage);
69
- }
70
- return this.webViewController;
71
- }
62
+ deviceFingerprintController: DeviceFingerprintController | null = null;
63
+ submitStrategy: SubmitStrategy | null = null;
72
64
 
73
65
  /**
74
66
  * Generates a device fingerprint, creates a invisible webview to get the device fingerprint
@@ -108,12 +100,11 @@ export class DeunaSDK extends BaseDeuna {
108
100
  mode?: Mode;
109
101
  }): void => {
110
102
  const { controller, mode } = params;
111
- this.mode = mode ?? Mode.MODAL;
112
- if (this.mode === Mode.MODAL && Platform.OS === 'ios') {
113
- this.modalDismissPromise = new Completer<void>();
114
- }
115
- this.webViewController = controller;
116
- this.webViewController.delegate = this.buildDelegate();
103
+ this.deunaWidgetManager.initialize({
104
+ controller,
105
+ mode: mode ?? Mode.MODAL,
106
+ delegate: this.buildDelegate(),
107
+ });
117
108
  this.notifyListeners();
118
109
  };
119
110
 
@@ -129,7 +120,7 @@ export class DeunaSDK extends BaseDeuna {
129
120
  }
130
121
  ) {
131
122
  this.setWidgetController({
132
- controller: getWidgetController(this.config, {
123
+ controller: buildDeunaWidgetController(this.config, {
133
124
  widget: 'payment',
134
125
  ...props,
135
126
  }),
@@ -146,7 +137,7 @@ export class DeunaSDK extends BaseDeuna {
146
137
  props: InitElementsWidgetParams & { mode?: Mode; sessionId?: string }
147
138
  ) {
148
139
  this.setWidgetController({
149
- controller: getWidgetController(this.config, {
140
+ controller: buildDeunaWidgetController(this.config, {
150
141
  widget: 'elements',
151
142
  ...props,
152
143
  }),
@@ -161,7 +152,7 @@ export class DeunaSDK extends BaseDeuna {
161
152
  */
162
153
  async initNextAction(props: InitNextActionWidgetParams & { mode: Mode }) {
163
154
  this.setWidgetController({
164
- controller: getWidgetController(this.config, {
155
+ controller: buildDeunaWidgetController(this.config, {
165
156
  widget: 'nextAction',
166
157
  ...props,
167
158
  }),
@@ -181,7 +172,7 @@ export class DeunaSDK extends BaseDeuna {
181
172
  }
182
173
  ) {
183
174
  this.setWidgetController({
184
- controller: getWidgetController(this.config, {
175
+ controller: buildDeunaWidgetController(this.config, {
185
176
  widget: 'voucher',
186
177
  ...props,
187
178
  }),
@@ -190,7 +181,7 @@ export class DeunaSDK extends BaseDeuna {
190
181
  }
191
182
 
192
183
  isValid = async (): Promise<boolean> => {
193
- return this.safeWebViewController.isValid();
184
+ return this.deunaWidgetManager.controller.isValid();
194
185
  };
195
186
 
196
187
  submit = async (): Promise<SubmitResult> => {
@@ -198,7 +189,7 @@ export class DeunaSDK extends BaseDeuna {
198
189
  const submitStrategy = await getSubmitStrategy(this);
199
190
 
200
191
  if (!submitStrategy) {
201
- return this.safeWebViewController.submit();
192
+ return this.deunaWidgetManager.controller.submit();
202
193
  }
203
194
 
204
195
  this.submitStrategy = submitStrategy;
@@ -207,57 +198,63 @@ export class DeunaSDK extends BaseDeuna {
207
198
  };
208
199
 
209
200
  setCustomStyle = async (style: Partial<CustomStyles>): Promise<void> => {
210
- return this.safeWebViewController.setCustomStyle(style);
201
+ return this.deunaWidgetManager.controller.setCustomStyle(style);
211
202
  };
212
203
 
213
204
  refetchOrder = async (): Promise<Json | null> => {
214
- return this.safeWebViewController.refetchOrder();
205
+ return this.deunaWidgetManager.controller.refetchOrder();
215
206
  };
216
207
 
217
208
  getWidgetState = async (): Promise<State> => {
218
- return this.safeWebViewController.getWidgetState();
209
+ return this.deunaWidgetManager.controller.getWidgetState();
219
210
  };
220
211
 
221
212
  /**
222
213
  * Closes the DEUNA widget and releases the resources
223
214
  */
224
215
  close = async (): Promise<void> => {
225
- if (this.webViewController instanceof PaymentWidgetController) {
226
- this.webViewController.callbacks.onClosed?.(
227
- this.webViewController.closedAction
228
- );
229
- } else if (this.webViewController instanceof ElementsWidgetController) {
230
- this.webViewController.callbacks.onClosed?.(
231
- this.webViewController.closedAction
232
- );
216
+ if (!this.deunaWidgetManager.isInitialized) {
217
+ return;
233
218
  }
234
- this.onCloseNewTab();
235
- await this.newTabModalDismissPromise?.wait;
236
- this.newTabWebViewController?.dispose();
237
219
 
238
- this.webViewController?.dispose();
239
- // reset all prop
240
- this.newTabWebViewController = null;
241
- this.webViewController = null;
242
- this.mode = null;
243
-
244
- await this.submitStrategy?.deunaSDK.close();
245
- this.submitStrategy = null;
246
-
247
- this.notifyListeners();
248
-
249
- // if the widget was shown in modal mode, wait for the modal to be dismissed
250
- await this.modalDismissPromise?.wait;
251
- this.modalDismissPromise = null;
252
- this.onDestroyed?.();
253
- };
254
-
255
- onModalDismissed = () => {
256
- this.modalDismissPromise?.complete();
257
- };
258
-
259
- onNewTabDismissed = () => {
260
- this.newTabModalDismissPromise?.complete();
220
+ try {
221
+ let onClosedCallback: ((action: ClosedAction) => void) | undefined;
222
+ const closedAction = this.deunaWidgetManager.controller.closedAction;
223
+
224
+ if (
225
+ this.deunaWidgetManager.controller instanceof PaymentWidgetController
226
+ ) {
227
+ onClosedCallback =
228
+ this.deunaWidgetManager.controller.callbacks.onClosed;
229
+ } else if (
230
+ this.deunaWidgetManager.controller instanceof ElementsWidgetController
231
+ ) {
232
+ onClosedCallback =
233
+ this.deunaWidgetManager.controller.callbacks.onClosed;
234
+ }
235
+
236
+ // If the external url was opened in a Safari View Controller or a Custom Chrome Tab
237
+ // we need to wait until the browser is dismissed
238
+ await this.externalUrlHelper.waitForClose();
239
+
240
+ // Destroy the main webview and dismiss the modal
241
+ this.deunaWidgetManager.destroy();
242
+
243
+ // Close the submit strategy if it exists
244
+ await this.submitStrategy?.deunaSDK.close();
245
+ this.submitStrategy = null;
246
+
247
+ // Notify listeners that the widget has been closed
248
+ this.notifyListeners();
249
+
250
+ // Notify the user that the widget has been closed
251
+ this.onDestroyed?.();
252
+
253
+ // Notify the widget that the user has closed the widget
254
+ onClosedCallback?.(closedAction);
255
+ } catch (error) {
256
+ console.error('Error closing the widget', error);
257
+ }
261
258
  };
262
259
 
263
260
  addListener = (listener: () => void) => {
@@ -273,21 +270,22 @@ export class DeunaSDK extends BaseDeuna {
273
270
  };
274
271
 
275
272
  private notifyDownloadFile = (url: string) => {
276
- if (this.safeWebViewController instanceof PaymentWidgetController) {
277
- this.safeWebViewController.callbacks.onDownloadFile?.({
273
+ if (this.deunaWidgetManager.controller instanceof PaymentWidgetController) {
274
+ this.deunaWidgetManager.controller.callbacks.onDownloadFile?.({
278
275
  type: DownloadType.URL,
279
276
  data: url,
280
277
  });
281
278
  }
282
279
  };
283
280
 
284
- onCloseNewTab = () => {
285
- if (this.webViewController) {
286
- this.webViewController.redirectUrl = null;
281
+ /**
282
+ * Closes the external URL Webview
283
+ */
284
+ onCloseExternalUrl = async () => {
285
+ if (this.externalUrlHelper.externalUrlWebViewController) {
286
+ await this.externalUrlHelper.closeWebView();
287
+ this.notifyListeners();
287
288
  }
288
- this.newTabWebViewController?.dispose();
289
- this.newTabWebViewController = null;
290
- this.notifyListeners();
291
289
  };
292
290
 
293
291
  /**
@@ -298,19 +296,33 @@ export class DeunaSDK extends BaseDeuna {
298
296
  */
299
297
  buildDelegate = (): WebViewDelegate => {
300
298
  return {
301
- onOpenInNewTab: (url) => {
302
- this.newTabWebViewController = new OpenInNewTabController(url);
303
- if (Platform.OS === 'ios') {
304
- this.newTabModalDismissPromise = new Completer<void>();
299
+ onOpenExternalUrl: (url) => {
300
+ try {
301
+ const host = new URL(url).host;
302
+ let browser = ExternalUrlBrowser.WEB_VIEW;
303
+ // Check if the URL is from a domain that must be opened in a cross platform browser (SafariView or Chrome Custom Tab)
304
+ for (const domain of DOMAINS_MUST_BE_USE_CROSS_PLATFORM_BROWSER) {
305
+ if (host.includes(domain)) {
306
+ browser = ExternalUrlBrowser.CROSS_PLATFORM_BROWSER;
307
+ break;
308
+ }
309
+ }
310
+
311
+ this.externalUrlHelper.openUrl({
312
+ url,
313
+ browser,
314
+ delegate: {
315
+ onCloseExternalUrl: this.onCloseExternalUrl,
316
+ onFileDownload: this.notifyDownloadFile,
317
+ },
318
+ });
319
+ this.notifyListeners();
320
+ } catch (error) {
321
+ console.error('Error opening external URL', error);
305
322
  }
306
- this.newTabWebViewController.delegate = {
307
- onFileDownload: this.notifyDownloadFile,
308
- onNewTabWindowClose: this.onCloseNewTab,
309
- };
310
- this.notifyListeners();
311
323
  },
324
+ onCloseExternalUrl: this.onCloseExternalUrl,
312
325
  onCloseButtonPressed: this.close, // Close the payment widget when the user presses the close button
313
- onCloseSubWebView: this.onCloseNewTab, // Close the new tab web view when the DEUNA widget emits an error event or when the purchase is successful
314
326
  onFileDownload: this.notifyDownloadFile, // Notify the user when a request to download a file is made
315
327
  };
316
328
  };
@@ -3,10 +3,9 @@ import { useEffect, useRef, useState } from 'react';
3
3
 
4
4
  import { DeunaSDK } from '../DeunaSDK';
5
5
  import { DeunaWebView } from './DeunaWebView';
6
- import { NewTabWebView } from './NewTabWebView';
7
- import { DeunaWebViewController } from '../controllers/BaseWebViewController';
8
6
  import { Mode } from '../interfaces/types';
9
7
  import { DeviceFingerprintWebView } from './DeviceFingerprintWebView';
8
+ import { ExternalUrlWebView } from './ExternalUrlWebView';
10
9
 
11
10
  interface DeunaWidgetProps {
12
11
  instance: DeunaSDK;
@@ -52,7 +51,9 @@ export const DeunaWidget = (props: DeunaWidgetProps) => {
52
51
  const DeunaWidgetContainer = (props: DeunaWidgetProps) => {
53
52
  const { instance } = props;
54
53
  const instanceRef = useRef(instance);
55
- const [mode, setMode] = useState<Mode | null>(instance.mode);
54
+ const [isVisible, setIsVisible] = useState(
55
+ instanceRef.current.deunaWidgetManager.isInitialized
56
+ );
56
57
 
57
58
  useEffect(() => {
58
59
  instanceRef.current = instance;
@@ -62,74 +63,76 @@ const DeunaWidgetContainer = (props: DeunaWidgetProps) => {
62
63
  // has changed
63
64
  useEffect(() => {
64
65
  const listener = () => {
65
- setMode(instanceRef.current.mode);
66
+ setIsVisible(instanceRef.current.deunaWidgetManager.isInitialized);
66
67
  };
67
68
 
68
69
  instanceRef.current.addListener(listener);
69
70
 
70
71
  return () => {
71
72
  instanceRef.current.removeListener(listener);
72
- instanceRef.current.webViewController?.dispose();
73
+ if (instanceRef.current.deunaWidgetManager.isInitialized) {
74
+ instanceRef.current.deunaWidgetManager.controller?.dispose();
75
+ }
73
76
  };
74
77
  }, []);
75
78
 
76
- const isModal = mode === Mode.MODAL;
77
- const isEmbedded = mode === Mode.EMBEDDED;
78
-
79
79
  /**
80
80
  * This function is used to close the widget
81
81
  */
82
82
  const onClose = () => {
83
- const controller = instanceRef.current
84
- .webViewController as DeunaWebViewController;
83
+ const controller = instanceRef.current.deunaWidgetManager.controller;
85
84
  controller.closedAction = 'userAction';
86
85
  instanceRef.current.close();
87
86
  };
88
87
 
89
88
  // render the widget if mode is not null
89
+ const viewManager = instanceRef.current.deunaWidgetManager;
90
+ const controller = viewManager.isInitialized ? viewManager.controller : null;
91
+
92
+ const isModal = isVisible && viewManager.mode === Mode.MODAL;
93
+ const isEmbedded = isVisible && viewManager.mode === Mode.EMBEDDED;
94
+
90
95
  return (
91
96
  <>
92
- <Modal
93
- presentationStyle="pageSheet"
94
- animationType="slide"
95
- onRequestClose={onClose}
96
- onDismiss={instanceRef.current.onModalDismissed}
97
- visible={isModal}
98
- >
99
- {isModal && (
97
+ {isModal && (
98
+ <Modal
99
+ presentationStyle="pageSheet"
100
+ animationType="slide"
101
+ onRequestClose={onClose}
102
+ >
100
103
  <SafeAreaView style={styles.container}>
101
104
  <View style={styles.container}>
102
105
  <DeunaWebView
103
- url={instanceRef.current.webViewController?.url ?? ''}
104
- onWebView={instanceRef.current.webViewController?.setWebView}
105
- onMessage={instanceRef.current.webViewController?.onMessage}
106
- onLoad={instanceRef.current.webViewController?.onLoad}
107
- onError={instanceRef.current.webViewController?.onError}
106
+ url={controller?.url ?? ''}
107
+ onWebView={controller?.setWebView}
108
+ onMessage={controller?.onMessage}
109
+ onLoad={controller?.onLoad}
110
+ onError={controller?.onError}
108
111
  onShouldStartLoadWithRequest={
109
- instanceRef.current.webViewController
110
- ?.onShouldStartLoadWithRequest
112
+ controller?.onShouldStartLoadWithRequest
111
113
  }
112
114
  />
113
- <NewTabWebView instance={instanceRef.current} />
115
+ <ExternalUrlWebView instance={instanceRef.current} />
114
116
  </View>
115
117
  </SafeAreaView>
116
- )}
117
- </Modal>
118
+ </Modal>
119
+ )}
118
120
 
119
- {isEmbedded && <NewTabWebView instance={instanceRef.current} />}
121
+ {isEmbedded && <ExternalUrlWebView instance={instanceRef.current} />}
120
122
 
121
123
  {isEmbedded && (
122
124
  <DeunaWebView
123
- url={instanceRef.current.webViewController?.url ?? ''}
124
- onWebView={instanceRef.current.webViewController?.setWebView}
125
- onMessage={instanceRef.current.webViewController?.onMessage}
126
- onLoad={instanceRef.current.webViewController?.onLoad}
127
- onError={instanceRef.current.webViewController?.onError}
125
+ url={controller?.url ?? ''}
126
+ onWebView={controller?.setWebView}
127
+ onMessage={controller?.onMessage}
128
+ onLoad={controller?.onLoad}
129
+ onError={controller?.onError}
128
130
  onShouldStartLoadWithRequest={
129
- instanceRef.current.webViewController?.onShouldStartLoadWithRequest
131
+ controller?.onShouldStartLoadWithRequest
130
132
  }
131
133
  />
132
134
  )}
135
+
133
136
  <DeviceFingerprintWebView instance={instanceRef.current} />
134
137
  </>
135
138
  );
@@ -0,0 +1,65 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import { DeunaSDK } from '../DeunaSDK';
3
+ import { Modal, SafeAreaView, StyleSheet } from 'react-native';
4
+ import { DeunaWebView } from './DeunaWebView';
5
+
6
+ interface ExternalUrlWebViewProps {
7
+ instance: DeunaSDK;
8
+ }
9
+
10
+ export const ExternalUrlWebView = (props: ExternalUrlWebViewProps) => {
11
+ const { instance } = props;
12
+ const instanceRef = useRef<DeunaSDK>(instance);
13
+ const [visible, setVisible] = useState(false);
14
+
15
+ useEffect(() => {
16
+ instanceRef.current = instance;
17
+ }, [instance]);
18
+
19
+ // Listen when the DeunaSDK instance configuration
20
+ // has changed
21
+ useEffect(() => {
22
+ const ref = instanceRef.current;
23
+ const listener = () => {
24
+ const isVisible = !!ref.externalUrlHelper.externalUrlWebViewController;
25
+ setVisible(isVisible);
26
+ };
27
+
28
+ instanceRef.current.addListener(listener);
29
+
30
+ return () => {
31
+ ref.removeListener(listener);
32
+ ref.externalUrlHelper.externalUrlWebViewController?.dispose();
33
+ };
34
+ }, []);
35
+
36
+ const externalUrlWebViewController =
37
+ instanceRef.current.externalUrlHelper.externalUrlWebViewController;
38
+
39
+ return (
40
+ <>
41
+ {visible && (
42
+ <Modal
43
+ presentationStyle="pageSheet"
44
+ animationType="slide"
45
+ onRequestClose={instanceRef.current.onCloseExternalUrl}
46
+ >
47
+ <SafeAreaView style={styles.container}>
48
+ <DeunaWebView
49
+ url={externalUrlWebViewController?.url ?? ''}
50
+ onWebView={externalUrlWebViewController?.setWebView}
51
+ onMessage={externalUrlWebViewController?.onMessage}
52
+ onLoad={externalUrlWebViewController?.onLoad}
53
+ onError={externalUrlWebViewController?.onError}
54
+ onShouldStartLoadWithRequest={
55
+ externalUrlWebViewController?.onShouldStartLoadWithRequest
56
+ }
57
+ />
58
+ </SafeAreaView>
59
+ </Modal>
60
+ )}
61
+ </>
62
+ );
63
+ };
64
+
65
+ const styles = StyleSheet.create({ container: { flex: 1 } });
@@ -13,20 +13,19 @@ import { Platform } from 'react-native';
13
13
  import { ShouldStartLoadRequest } from 'react-native-webview/lib/WebViewTypes';
14
14
 
15
15
  export interface WebViewDelegate {
16
- onOpenInNewTab?: (url: string) => void;
16
+ onOpenExternalUrl?: (url: string) => void;
17
17
  onCloseButtonPressed?: () => void;
18
- onCloseSubWebView?: () => void;
19
18
  onFileDownload?: (url: string) => void;
20
- onNewTabWindowClose?: () => void;
19
+ onCloseExternalUrl?: () => void;
21
20
  }
22
21
 
23
22
  export enum WebViewEventType {
24
23
  consoleLog = 'consoleLog',
25
24
  eventDispatch = 'eventDispatch',
26
25
  jsExecutor = 'jsExecutor',
27
- openInNewTab = 'openInNewTab',
26
+ openExternalUrl = 'openExternalUrl',
28
27
  redirect = 'redirect',
29
- newTabWindowClose = 'newTabWindowClose',
28
+ closeExternalUrl = 'closeExternalUrl',
30
29
  }
31
30
 
32
31
  const DOWNLOAD_FILE_REGEX =
@@ -34,7 +33,6 @@ const DOWNLOAD_FILE_REGEX =
34
33
 
35
34
  export abstract class BaseWebViewController {
36
35
  initialized = false;
37
- redirectUrl: string | null = null;
38
36
  webView: WebView | null = null;
39
37
  url: string | null = null;
40
38
 
@@ -78,26 +76,22 @@ export abstract class BaseWebViewController {
78
76
  * @returns boolean indicating if the URL should be loaded in current WebView
79
77
  */
80
78
  onShouldStartLoadWithRequest = (request: ShouldStartLoadRequest) => {
81
- // Prevent loading if it's the redirect URL
82
- if (this.redirectUrl === request.url) {
83
- return false;
79
+ if (this.url === request.url) {
80
+ return true;
84
81
  }
85
82
 
86
- const isNewNavigation = request.isTopFrame && request.url !== this.url;
87
- const isClickNavigation = request.navigationType === 'click';
88
- const shouldHandleInNewTab = !this.urlMustBeLoadedInTheSameWebView(
83
+ const isAndroid = Platform.OS === 'android';
84
+
85
+ const isNewNavigation = isAndroid
86
+ ? request.url.startsWith('https://')
87
+ : request.navigationType === 'click';
88
+
89
+ const shouldOpenExternally = !this.urlMustBeLoadedInTheSameWebView(
89
90
  request.url
90
91
  );
91
92
 
92
- // For iOS, check both click navigation and new navigation
93
- // For Android, only check new navigation
94
- const shouldOpenInNewTab =
95
- Platform.OS === 'ios'
96
- ? (isClickNavigation || isNewNavigation) && shouldHandleInNewTab
97
- : isNewNavigation && shouldHandleInNewTab;
98
-
99
- if (shouldOpenInNewTab) {
100
- this.delegate?.onOpenInNewTab?.(request.url);
93
+ if (isNewNavigation && shouldOpenExternally) {
94
+ this.delegate?.onOpenExternalUrl?.(request.url);
101
95
  return false;
102
96
  }
103
97
 
@@ -158,18 +152,18 @@ export abstract class DeunaWebViewController extends BaseWebViewController {
158
152
  this.jsExecutor.requests.get(requestId)?.(data);
159
153
  this.jsExecutor.requests.delete(requestId);
160
154
  },
161
- [WebViewEventType.openInNewTab]: () => {
162
- this.delegate?.onOpenInNewTab?.(eventData.url);
155
+ [WebViewEventType.openExternalUrl]: () => {
156
+ this.delegate?.onOpenExternalUrl?.(eventData.url);
163
157
  },
164
158
  [WebViewEventType.redirect]: () => {
165
159
  if (this.redirectUrl) {
166
160
  return;
167
161
  }
168
162
  this.redirectUrl = eventData.url;
169
- this.delegate?.onOpenInNewTab?.(eventData.url);
163
+ this.delegate?.onOpenExternalUrl?.(eventData.url);
170
164
  },
171
- [WebViewEventType.newTabWindowClose]: () => {
172
- this.delegate?.onNewTabWindowClose?.();
165
+ [WebViewEventType.closeExternalUrl]: () => {
166
+ this.delegate?.onCloseExternalUrl?.();
173
167
  },
174
168
  };
175
169
 
@@ -180,7 +174,7 @@ export abstract class DeunaWebViewController extends BaseWebViewController {
180
174
  this.webView?.injectJavaScript(
181
175
  `
182
176
  window.open = function(url, target, features) {
183
- window.ReactNativeWebView.postMessage(JSON.stringify({ type: '${WebViewEventType.openInNewTab}', url }));
177
+ window.ReactNativeWebView.postMessage(JSON.stringify({ type: '${WebViewEventType.openExternalUrl}', url }));
184
178
  };
185
179
  console.log = function(message) {
186
180
  window.ReactNativeWebView.postMessage(JSON.stringify({ type: '${WebViewEventType.consoleLog}', message }));
@@ -37,12 +37,11 @@ export class ElementsWidgetController extends DeunaWebViewController {
37
37
  this.delegate?.onCloseButtonPressed?.();
38
38
  },
39
39
  [ElementsEventType.vaultSaveSuccess]: () => {
40
- this.delegate?.onCloseSubWebView?.();
40
+ this.delegate?.onCloseExternalUrl?.();
41
41
  this.callbacks.onSuccess?.(event.data);
42
42
  },
43
43
  [ElementsEventType.vaultSaveError]: () => {
44
- this.delegate?.onCloseSubWebView?.();
45
- this.delegate?.onCloseSubWebView?.();
44
+ this.delegate?.onCloseExternalUrl?.();
46
45
  const { metadata } = elementsEvent.data;
47
46
 
48
47
  if (metadata) {
@@ -5,7 +5,7 @@ import {
5
5
  } from './BaseWebViewController';
6
6
  import { DeunaLogs } from '../DeunaLogs';
7
7
 
8
- export class OpenInNewTabController extends BaseWebViewController {
8
+ export class ExternalUrlController extends BaseWebViewController {
9
9
  constructor(readonly url: string) {
10
10
  super();
11
11
  this.url = url;
@@ -16,7 +16,7 @@ export class OpenInNewTabController extends BaseWebViewController {
16
16
  };
17
17
 
18
18
  onError = (event: any) => {
19
- console.warn(event);
19
+ DeunaLogs.error(`EXTERNAL URL ERROR`, event);
20
20
  };
21
21
 
22
22
  urlMustBeLoadedInTheSameWebView = (url: string) => {
@@ -41,7 +41,7 @@ export class OpenInNewTabController extends BaseWebViewController {
41
41
  window.ReactNativeWebView.postMessage(JSON.stringify({ type: '${WebViewEventType.consoleLog}', message }));
42
42
  };
43
43
  window.close = function() {
44
- window.ReactNativeWebView.postMessage(JSON.stringify({ type: '${WebViewEventType.newTabWindowClose}', data: '' }));
44
+ window.ReactNativeWebView.postMessage(JSON.stringify({ type: '${WebViewEventType.closeExternalUrl}', data: '' }));
45
45
  };
46
46
  (function() {
47
47
  setTimeout(function() {
@@ -85,11 +85,11 @@ export class PaymentWidgetController extends DeunaWebViewController {
85
85
  this.callbacks.onPaymentProcessing?.();
86
86
  },
87
87
  [CheckoutEventType.purchase]: () => {
88
- this.delegate?.onCloseSubWebView?.();
88
+ this.delegate?.onCloseExternalUrl?.();
89
89
  this.callbacks.onSuccess?.(checkoutEvent.data.order);
90
90
  },
91
91
  [CheckoutEventType.purchaseError]: () => {
92
- this.delegate?.onCloseSubWebView?.();
92
+ this.delegate?.onCloseExternalUrl?.();
93
93
  const { metadata } = checkoutEvent.data;
94
94
 
95
95
  if (metadata) {