@deuna/react-native-sdk 1.0.3 → 1.0.6

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 (124) hide show
  1. package/lib/module/DeunaSDK.js +87 -78
  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 +59 -23
  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} +20 -10
  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/interfaces/types.js.map +1 -1
  30. package/lib/module/types/base.js.map +1 -1
  31. package/lib/module/types/envs.js +48 -0
  32. package/lib/module/types/envs.js.map +1 -0
  33. package/lib/module/types/fraudProviders.js +4 -0
  34. package/lib/module/types/fraudProviders.js.map +1 -0
  35. package/lib/module/types/helpers/buildElementsLink.js +15 -6
  36. package/lib/module/types/helpers/buildElementsLink.js.map +1 -1
  37. package/lib/module/types/helpers/buildPaymentLink.js +7 -3
  38. package/lib/module/types/helpers/buildPaymentLink.js.map +1 -1
  39. package/lib/module/types/helpers/buildVoucherLink.js +3 -1
  40. package/lib/module/types/helpers/buildVoucherLink.js.map +1 -1
  41. package/lib/module/types/helpers/constants.js +4 -0
  42. package/lib/module/types/helpers/constants.js.map +1 -0
  43. package/lib/module/types/helpers/urlConfig.js +1 -1
  44. package/lib/module/types/helpers/urlConfig.js.map +1 -1
  45. package/lib/module/types/utils/addSearchParamWhen.js +8 -0
  46. package/lib/module/types/utils/addSearchParamWhen.js.map +1 -0
  47. package/lib/typescript/deuna-sdk-react-native/src/DeunaSDK.d.ts +9 -11
  48. package/lib/typescript/deuna-sdk-react-native/src/DeunaSDK.d.ts.map +1 -1
  49. package/lib/typescript/deuna-sdk-react-native/src/components/DeunaWidget.d.ts.map +1 -1
  50. package/lib/typescript/deuna-sdk-react-native/src/components/ExternalUrlWebView.d.ts +7 -0
  51. package/lib/typescript/deuna-sdk-react-native/src/components/ExternalUrlWebView.d.ts.map +1 -0
  52. package/lib/typescript/deuna-sdk-react-native/src/controllers/BaseWebViewController.d.ts +9 -7
  53. package/lib/typescript/deuna-sdk-react-native/src/controllers/BaseWebViewController.d.ts.map +1 -1
  54. package/lib/typescript/deuna-sdk-react-native/src/controllers/ElementsWidgetController.d.ts.map +1 -1
  55. package/lib/typescript/deuna-sdk-react-native/src/controllers/{OpenInNewTabController.d.ts → ExternalUrlController.d.ts} +2 -2
  56. package/lib/typescript/deuna-sdk-react-native/src/controllers/ExternalUrlController.d.ts.map +1 -0
  57. package/lib/typescript/deuna-sdk-react-native/src/helpers/CrossPlatformBrowser.d.ts +25 -0
  58. package/lib/typescript/deuna-sdk-react-native/src/helpers/CrossPlatformBrowser.d.ts.map +1 -0
  59. package/lib/typescript/deuna-sdk-react-native/src/helpers/ExternalUrlHelper.d.ts +48 -0
  60. package/lib/typescript/deuna-sdk-react-native/src/helpers/ExternalUrlHelper.d.ts.map +1 -0
  61. package/lib/typescript/deuna-sdk-react-native/src/helpers/ViewManager.d.ts +19 -0
  62. package/lib/typescript/deuna-sdk-react-native/src/helpers/ViewManager.d.ts.map +1 -0
  63. package/lib/typescript/deuna-sdk-react-native/src/helpers/{getController.d.ts → buildDeunaWidgetController.d.ts} +4 -2
  64. package/lib/typescript/deuna-sdk-react-native/src/helpers/buildDeunaWidgetController.d.ts.map +1 -0
  65. package/lib/typescript/deuna-sdk-react-native/src/interfaces/constants.d.ts +1 -0
  66. package/lib/typescript/deuna-sdk-react-native/src/interfaces/constants.d.ts.map +1 -1
  67. package/lib/typescript/deuna-sdk-react-native/src/interfaces/types.d.ts +4 -0
  68. package/lib/typescript/deuna-sdk-react-native/src/interfaces/types.d.ts.map +1 -1
  69. package/lib/typescript/deuna-sdk-react-native/src/types/base.d.ts +2 -0
  70. package/lib/typescript/deuna-sdk-react-native/src/types/base.d.ts.map +1 -1
  71. package/lib/typescript/deuna-sdk-react-native/src/types/envs.d.ts +45 -0
  72. package/lib/typescript/deuna-sdk-react-native/src/types/envs.d.ts.map +1 -0
  73. package/lib/typescript/deuna-sdk-react-native/src/types/fraudProviders.d.ts +68 -0
  74. package/lib/typescript/deuna-sdk-react-native/src/types/fraudProviders.d.ts.map +1 -0
  75. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/buildElementsLink.d.ts +15 -0
  76. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/buildElementsLink.d.ts.map +1 -1
  77. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/buildPaymentLink.d.ts.map +1 -1
  78. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/buildVoucherLink.d.ts.map +1 -1
  79. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/constants.d.ts +2 -0
  80. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/constants.d.ts.map +1 -0
  81. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/urlConfig.d.ts +1 -0
  82. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/urlConfig.d.ts.map +1 -1
  83. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/callbacks.d.ts +8 -0
  84. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/callbacks.d.ts.map +1 -1
  85. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/initWidgetBase.d.ts +2 -0
  86. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/initWidgetBase.d.ts.map +1 -1
  87. package/lib/typescript/deuna-sdk-react-native/src/types/utils/addSearchParamWhen.d.ts +3 -0
  88. package/lib/typescript/deuna-sdk-react-native/src/types/utils/addSearchParamWhen.d.ts.map +1 -0
  89. package/package.json +3 -1
  90. package/src/DeunaSDK.ts +103 -90
  91. package/src/components/DeunaWidget.tsx +38 -35
  92. package/src/components/ExternalUrlWebView.tsx +65 -0
  93. package/src/controllers/BaseWebViewController.ts +70 -29
  94. package/src/controllers/ElementsWidgetController.ts +2 -3
  95. package/src/controllers/{OpenInNewTabController.ts → ExternalUrlController.ts} +3 -3
  96. package/src/controllers/PaymentWidgetController.ts +2 -2
  97. package/src/helpers/Completer.ts +2 -2
  98. package/src/helpers/CrossPlatformBrowser.ts +49 -0
  99. package/src/helpers/ExternalUrlHelper.ts +118 -0
  100. package/src/helpers/ViewManager.ts +45 -0
  101. package/src/helpers/{getController.ts → buildDeunaWidgetController.ts} +41 -12
  102. package/src/helpers/getSubmitStrategy.ts +1 -1
  103. package/src/interfaces/constants.ts +2 -0
  104. package/src/interfaces/types.ts +4 -0
  105. package/src/types/base.ts +2 -0
  106. package/src/types/envs.ts +50 -0
  107. package/src/types/fraudProviders.ts +98 -0
  108. package/src/types/helpers/buildElementsLink.ts +19 -11
  109. package/src/types/helpers/buildPaymentLink.ts +7 -2
  110. package/src/types/helpers/buildVoucherLink.ts +2 -0
  111. package/src/types/helpers/constants.ts +1 -0
  112. package/src/types/helpers/urlConfig.ts +3 -1
  113. package/src/types/interfaces/callbacks.ts +10 -1
  114. package/src/types/interfaces/initWidgetBase.ts +2 -0
  115. package/src/types/utils/addSearchParamWhen.ts +11 -0
  116. package/lib/module/components/NewTabWebView.js +0 -56
  117. package/lib/module/components/NewTabWebView.js.map +0 -1
  118. package/lib/module/controllers/OpenInNewTabController.js.map +0 -1
  119. package/lib/module/helpers/getController.js.map +0 -1
  120. package/lib/typescript/deuna-sdk-react-native/src/components/NewTabWebView.d.ts +0 -7
  121. package/lib/typescript/deuna-sdk-react-native/src/components/NewTabWebView.d.ts.map +0 -1
  122. package/lib/typescript/deuna-sdk-react-native/src/controllers/OpenInNewTabController.d.ts.map +0 -1
  123. package/lib/typescript/deuna-sdk-react-native/src/helpers/getController.d.ts.map +0 -1
  124. package/src/components/NewTabWebView.tsx +0 -64
@@ -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 } });
@@ -11,30 +11,29 @@ import {
11
11
  import { submitError, WidgetConfig } from '../interfaces';
12
12
  import { Platform } from 'react-native';
13
13
  import { ShouldStartLoadRequest } from 'react-native-webview/lib/WebViewTypes';
14
+ import { InitFraudProvidersProps } from '../types/fraudProviders';
15
+ import { DeunaSDK } from '../DeunaSDK';
14
16
 
15
17
  export interface WebViewDelegate {
16
- onOpenInNewTab?: (url: string) => void;
18
+ onOpenExternalUrl?: (url: string) => void;
17
19
  onCloseButtonPressed?: () => void;
18
- onCloseSubWebView?: () => void;
19
20
  onFileDownload?: (url: string) => void;
20
- onNewTabWindowClose?: () => void;
21
+ onCloseExternalUrl?: () => void;
21
22
  }
22
23
 
23
24
  export enum WebViewEventType {
24
25
  consoleLog = 'consoleLog',
25
26
  eventDispatch = 'eventDispatch',
26
27
  jsExecutor = 'jsExecutor',
27
- openInNewTab = 'openInNewTab',
28
+ openExternalUrl = 'openExternalUrl',
28
29
  redirect = 'redirect',
29
- newTabWindowClose = 'newTabWindowClose',
30
+ closeExternalUrl = 'closeExternalUrl',
30
31
  }
31
32
 
32
33
  const DOWNLOAD_FILE_REGEX =
33
34
  /\.(pdf|doc|docx|xls|xlsx|zip|rar|txt|mp3|mp4|jpg|jpeg|png|gif)$/i;
34
35
 
35
36
  export abstract class BaseWebViewController {
36
- initialized = false;
37
- redirectUrl: string | null = null;
38
37
  webView: WebView | null = null;
39
38
  url: string | null = null;
40
39
 
@@ -78,26 +77,22 @@ export abstract class BaseWebViewController {
78
77
  * @returns boolean indicating if the URL should be loaded in current WebView
79
78
  */
80
79
  onShouldStartLoadWithRequest = (request: ShouldStartLoadRequest) => {
81
- // Prevent loading if it's the redirect URL
82
- if (this.redirectUrl === request.url) {
83
- return false;
80
+ if (this.url === request.url) {
81
+ return true;
84
82
  }
85
83
 
86
- const isNewNavigation = request.isTopFrame && request.url !== this.url;
87
- const isClickNavigation = request.navigationType === 'click';
88
- const shouldHandleInNewTab = !this.urlMustBeLoadedInTheSameWebView(
84
+ const isAndroid = Platform.OS === 'android';
85
+
86
+ const isNewNavigation = isAndroid
87
+ ? request.url.startsWith('https://')
88
+ : request.navigationType === 'click';
89
+
90
+ const shouldOpenExternally = !this.urlMustBeLoadedInTheSameWebView(
89
91
  request.url
90
92
  );
91
93
 
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);
94
+ if (isNewNavigation && shouldOpenExternally) {
95
+ this.delegate?.onOpenExternalUrl?.(request.url);
101
96
  return false;
102
97
  }
103
98
 
@@ -118,13 +113,51 @@ export abstract class DeunaWebViewController extends BaseWebViewController {
118
113
  redirectUrl: string | null = null;
119
114
  closedAction: ClosedAction = 'systemAction';
120
115
 
116
+ isWebViewInitialized = false;
117
+
118
+ fraudId = '';
119
+
121
120
  constructor(readonly widgetConfig: WidgetConfig) {
122
121
  super();
122
+ this.init();
123
+ }
124
+
125
+ private init() {
126
+ const { fraudCredentials, sdkInstance } = this.widgetConfig;
127
+ if (!sdkInstance || !fraudCredentials) {
128
+ return;
129
+ }
130
+
131
+ if (fraudCredentials) {
132
+ this.generateFraudId({ sdkInstance, fraudCredentials });
133
+ }
134
+ }
135
+
136
+ private async generateFraudId({
137
+ sdkInstance,
138
+ fraudCredentials,
139
+ }: {
140
+ sdkInstance: DeunaSDK;
141
+ fraudCredentials: Partial<InitFraudProvidersProps>;
142
+ }) {
143
+ try {
144
+ this.fraudId = await sdkInstance.getSessionId(fraudCredentials);
145
+ if (this.isWebViewInitialized) {
146
+ this.jsExecutor.execute(this.getFraudIdFnString());
147
+ }
148
+ } catch (e) {
149
+ console.log('❌ fraudId', e);
150
+ }
151
+ }
152
+
153
+ private getFraudIdFnString() {
154
+ return `window.getFraudId = function(){ return '${this.fraudId}';}`;
123
155
  }
124
156
 
125
157
  abstract onEventDispatch: (event: Record<string, any>) => void;
126
158
 
127
159
  onLoad = () => {
160
+ this.isWebViewInitialized = true;
128
161
  this.setXprops();
129
162
  };
130
163
 
@@ -158,18 +191,18 @@ export abstract class DeunaWebViewController extends BaseWebViewController {
158
191
  this.jsExecutor.requests.get(requestId)?.(data);
159
192
  this.jsExecutor.requests.delete(requestId);
160
193
  },
161
- [WebViewEventType.openInNewTab]: () => {
162
- this.delegate?.onOpenInNewTab?.(eventData.url);
194
+ [WebViewEventType.openExternalUrl]: () => {
195
+ this.delegate?.onOpenExternalUrl?.(eventData.url);
163
196
  },
164
197
  [WebViewEventType.redirect]: () => {
165
198
  if (this.redirectUrl) {
166
199
  return;
167
200
  }
168
201
  this.redirectUrl = eventData.url;
169
- this.delegate?.onOpenInNewTab?.(eventData.url);
202
+ this.delegate?.onOpenExternalUrl?.(eventData.url);
170
203
  },
171
- [WebViewEventType.newTabWindowClose]: () => {
172
- this.delegate?.onNewTabWindowClose?.();
204
+ [WebViewEventType.closeExternalUrl]: () => {
205
+ this.delegate?.onCloseExternalUrl?.();
173
206
  },
174
207
  };
175
208
 
@@ -180,7 +213,7 @@ export abstract class DeunaWebViewController extends BaseWebViewController {
180
213
  this.webView?.injectJavaScript(
181
214
  `
182
215
  window.open = function(url, target, features) {
183
- window.ReactNativeWebView.postMessage(JSON.stringify({ type: '${WebViewEventType.openInNewTab}', url }));
216
+ window.ReactNativeWebView.postMessage(JSON.stringify({ type: '${WebViewEventType.openExternalUrl}', url }));
184
217
  };
185
218
  console.log = function(message) {
186
219
  window.ReactNativeWebView.postMessage(JSON.stringify({ type: '${WebViewEventType.consoleLog}', message }));
@@ -205,7 +238,15 @@ export abstract class DeunaWebViewController extends BaseWebViewController {
205
238
  onSubmit: function (fn) {
206
239
  window.submit = fn;
207
240
  },
208
- }
241
+ getFraudId: function(){
242
+ if(typeof window.getFraudId === 'function'){
243
+ return window.getFraudId();
244
+ }
245
+ return "";
246
+ }
247
+ };
248
+
249
+ ${this.fraudId.length > 0 ? this.getFraudIdFnString() : ''}
209
250
  `
210
251
  );
211
252
  };
@@ -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) {
@@ -17,14 +17,14 @@ export class Completer<T> {
17
17
 
18
18
  complete(value: T): void {
19
19
  if (this.completed) return;
20
- this.completed = true;
21
20
  this.resolvePromise?.(value);
21
+ this.completed = true;
22
22
  }
23
23
 
24
24
  completeError(error: any): void {
25
25
  if (this.completed) return;
26
- this.completed = true;
27
26
  this.rejectPromise?.(error);
27
+ this.completed = true;
28
28
  }
29
29
 
30
30
  isCompleted(): boolean {
@@ -0,0 +1,49 @@
1
+ import * as WebBrowser from 'expo-web-browser';
2
+ import { Platform } from 'react-native';
3
+
4
+ /**
5
+ * This class is used to open a browser in a SafariView Controller (iOS) or
6
+ * a Chrome Custom Tab (Android) depending on the platform.
7
+ *
8
+ * It is used to open the external URLs in the DEUNA widget.
9
+ */
10
+ class CrossPlatformBrowser {
11
+ private initialized = false;
12
+ /**
13
+ * Initialize the browser
14
+ */
15
+ async initialize() {
16
+ if (this.initialized) return;
17
+ await WebBrowser.maybeCompleteAuthSession();
18
+ this.initialized = true;
19
+ }
20
+
21
+ /**
22
+ * Open an URL in a SafariView Controller (iOS) or a Chrome Custom Tab (Android) depending on the platform
23
+ * @param url - The URL to open
24
+ */
25
+ async openBrowser(url: string) {
26
+ try {
27
+ await this.initialize();
28
+ await WebBrowser.openBrowserAsync(url);
29
+ } catch (error) {
30
+ console.error('Error opening browser', error);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Close the browser
36
+ */
37
+ async closeBrowser() {
38
+ if (Platform.OS !== 'ios') {
39
+ return;
40
+ }
41
+ try {
42
+ await WebBrowser.dismissBrowser();
43
+ } catch (error) {
44
+ console.error('Error closing browser', error);
45
+ }
46
+ }
47
+ }
48
+
49
+ export const crossPlatformBrowser = new CrossPlatformBrowser();
@@ -0,0 +1,118 @@
1
+ import { AppState, Platform } from 'react-native';
2
+ import { WebViewDelegate } from '../controllers/BaseWebViewController';
3
+ import { ExternalUrlController } from '../controllers/ExternalUrlController';
4
+ import { Completer } from './Completer';
5
+ import { crossPlatformBrowser } from './CrossPlatformBrowser';
6
+
7
+ export enum ExternalUrlBrowser {
8
+ WEB_VIEW = 'web_view',
9
+ CROSS_PLATFORM_BROWSER = 'cross_platform_browser',
10
+ }
11
+
12
+ interface OpenUrlParams {
13
+ url: string;
14
+ browser: ExternalUrlBrowser;
15
+ delegate: WebViewDelegate;
16
+ }
17
+
18
+ interface ExternalUrlManager<T extends ExternalUrlBrowser> {
19
+ type: T;
20
+ }
21
+
22
+ export interface WebViewManager
23
+ extends ExternalUrlManager<ExternalUrlBrowser.WEB_VIEW> {
24
+ controller: ExternalUrlController;
25
+ }
26
+
27
+ export interface CrossPlatformBrowserManager
28
+ extends ExternalUrlManager<ExternalUrlBrowser.CROSS_PLATFORM_BROWSER> {
29
+ type: ExternalUrlBrowser.CROSS_PLATFORM_BROWSER;
30
+ }
31
+
32
+ export class ExternalUrlHelper {
33
+ private manager: ExternalUrlManager<ExternalUrlBrowser> | null = null;
34
+ private completer: Completer<void> | null = null;
35
+
36
+ constructor() {
37
+ this.startCloseChecker();
38
+ }
39
+
40
+ get externalUrlWebViewController() {
41
+ if (this.manager?.type === ExternalUrlBrowser.WEB_VIEW) {
42
+ return (this.manager as WebViewManager).controller;
43
+ }
44
+ return null;
45
+ }
46
+
47
+ /**
48
+ * Wait until the webview or cross platform browser is closed
49
+ */
50
+ async waitForClose() {
51
+ if (this.completer) {
52
+ await this.completer.wait;
53
+ this.completer = null;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Listen when the app is brought back to the foreground
59
+ */
60
+ private startCloseChecker() {
61
+ AppState.addEventListener('change', (state) => {
62
+ if (
63
+ state === 'active' &&
64
+ this.manager?.type === ExternalUrlBrowser.CROSS_PLATFORM_BROWSER
65
+ ) {
66
+ this.manager = null;
67
+ this.completeClose();
68
+ }
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Opens the webview or cross platform browser
74
+ */
75
+ async openUrl(params: OpenUrlParams) {
76
+ const { url, browser, delegate } = params;
77
+
78
+ const mapper = {
79
+ [ExternalUrlBrowser.WEB_VIEW]: async () => {
80
+ const controller = new ExternalUrlController(url);
81
+ controller.delegate = delegate;
82
+ this.manager = {
83
+ type: ExternalUrlBrowser.WEB_VIEW,
84
+ controller,
85
+ } as WebViewManager;
86
+ },
87
+ [ExternalUrlBrowser.CROSS_PLATFORM_BROWSER]: async () => {
88
+ this.completer = new Completer<void>();
89
+ this.manager = {
90
+ type: ExternalUrlBrowser.CROSS_PLATFORM_BROWSER,
91
+ };
92
+
93
+ await crossPlatformBrowser.openBrowser(url);
94
+ if (Platform.OS === 'ios') {
95
+ this.completer?.complete();
96
+ }
97
+ },
98
+ };
99
+ await mapper[browser]();
100
+ }
101
+
102
+ /**
103
+ * Notify that the browser has been closed
104
+ */
105
+ private completeClose() {
106
+ this.completer?.complete();
107
+ }
108
+
109
+ /**
110
+ * Closes the web view
111
+ */
112
+ async closeWebView() {
113
+ if (this.manager?.type === ExternalUrlBrowser.WEB_VIEW) {
114
+ (this.manager as WebViewManager).controller?.dispose();
115
+ this.manager = null;
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,45 @@
1
+ import {
2
+ BaseWebViewController,
3
+ WebViewDelegate,
4
+ } from '../controllers/BaseWebViewController';
5
+ import { Mode } from '../interfaces';
6
+
7
+ export class WebViewManager<
8
+ T extends BaseWebViewController = BaseWebViewController,
9
+ > {
10
+ private _controller: T | null = null;
11
+ private _mode: Mode | null = null;
12
+
13
+ initialize = (params: {
14
+ controller: T;
15
+ delegate: WebViewDelegate;
16
+ mode: Mode;
17
+ }): void => {
18
+ this._controller = params.controller;
19
+ this._controller.delegate = params.delegate;
20
+ this._mode = params.mode;
21
+ };
22
+
23
+ get isInitialized(): boolean {
24
+ return !!this._controller;
25
+ }
26
+
27
+ get mode(): Mode | null {
28
+ return this._mode;
29
+ }
30
+
31
+ get controller(): T {
32
+ if (!this._controller) {
33
+ throw new Error('WebView not initialized');
34
+ }
35
+ return this._controller;
36
+ }
37
+
38
+ /**
39
+ * Destroy the webview and the modal (if it exists)
40
+ */
41
+ destroy = () => {
42
+ this._controller?.dispose();
43
+ this._controller = null;
44
+ };
45
+ }