banking_dcb_sdk_react_native 0.1.9

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/LICENSE +20 -0
  2. package/PartnerReactNativeSdk.podspec +31 -0
  3. package/README.md +14 -0
  4. package/android/build.gradle +100 -0
  5. package/android/gradle.properties +5 -0
  6. package/android/src/main/AndroidManifest.xml +3 -0
  7. package/android/src/main/AndroidManifestNew.xml +2 -0
  8. package/android/src/main/java/com/partnerreactnativesdk/PartnerReactNativeSdkModule.kt +23 -0
  9. package/android/src/main/java/com/partnerreactnativesdk/PartnerReactNativeSdkPackage.kt +33 -0
  10. package/ios/PartnerReactNativeSdk.h +6 -0
  11. package/ios/PartnerReactNativeSdk.mm +29 -0
  12. package/lib/module/helpers/ServiceNames.js +135 -0
  13. package/lib/module/helpers/ServiceNames.js.map +1 -0
  14. package/lib/module/helpers/analytics/analytics_event_model.js +37 -0
  15. package/lib/module/helpers/analytics/analytics_event_model.js.map +1 -0
  16. package/lib/module/helpers/analytics/analytics_logger.js +73 -0
  17. package/lib/module/helpers/analytics/analytics_logger.js.map +1 -0
  18. package/lib/module/helpers/analytics/event_storage.js +35 -0
  19. package/lib/module/helpers/analytics/event_storage.js.map +1 -0
  20. package/lib/module/helpers/banking_dcb_react_native.js +297 -0
  21. package/lib/module/helpers/banking_dcb_react_native.js.map +1 -0
  22. package/lib/module/helpers/helper.js +4 -0
  23. package/lib/module/helpers/helper.js.map +1 -0
  24. package/lib/module/helpers/network/APICall.js +111 -0
  25. package/lib/module/helpers/network/APICall.js.map +1 -0
  26. package/lib/module/helpers/network/Encryption.js +148 -0
  27. package/lib/module/helpers/network/Encryption.js.map +1 -0
  28. package/lib/module/helpers/network/network_manager.js +112 -0
  29. package/lib/module/helpers/network/network_manager.js.map +1 -0
  30. package/lib/module/helpers/utils/Constants.js +12 -0
  31. package/lib/module/helpers/utils/Constants.js.map +1 -0
  32. package/lib/module/helpers/utils/LibraryConstants.js +115 -0
  33. package/lib/module/helpers/utils/LibraryConstants.js.map +1 -0
  34. package/lib/module/helpers/utils/deviceInfoManager.js +39 -0
  35. package/lib/module/helpers/utils/deviceInfoManager.js.map +1 -0
  36. package/lib/module/helpers/utils/headerManager.js +21 -0
  37. package/lib/module/helpers/utils/headerManager.js.map +1 -0
  38. package/lib/module/helpers/utils/themeManager.js +40 -0
  39. package/lib/module/helpers/utils/themeManager.js.map +1 -0
  40. package/lib/module/helpers/utils/webviewCallback.js +20 -0
  41. package/lib/module/helpers/utils/webviewCallback.js.map +1 -0
  42. package/lib/module/helpers/webview.js +314 -0
  43. package/lib/module/helpers/webview.js.map +1 -0
  44. package/lib/module/index.js +5 -0
  45. package/lib/module/index.js.map +1 -0
  46. package/lib/module/package.json +1 -0
  47. package/lib/typescript/package.json +1 -0
  48. package/lib/typescript/src/helpers/ServiceNames.d.ts +51 -0
  49. package/lib/typescript/src/helpers/ServiceNames.d.ts.map +1 -0
  50. package/lib/typescript/src/helpers/analytics/analytics_event_model.d.ts +16 -0
  51. package/lib/typescript/src/helpers/analytics/analytics_event_model.d.ts.map +1 -0
  52. package/lib/typescript/src/helpers/analytics/analytics_logger.d.ts +17 -0
  53. package/lib/typescript/src/helpers/analytics/analytics_logger.d.ts.map +1 -0
  54. package/lib/typescript/src/helpers/analytics/event_storage.d.ts +8 -0
  55. package/lib/typescript/src/helpers/analytics/event_storage.d.ts.map +1 -0
  56. package/lib/typescript/src/helpers/banking_dcb_react_native.d.ts +33 -0
  57. package/lib/typescript/src/helpers/banking_dcb_react_native.d.ts.map +1 -0
  58. package/lib/typescript/src/helpers/helper.d.ts +1 -0
  59. package/lib/typescript/src/helpers/helper.d.ts.map +1 -0
  60. package/lib/typescript/src/helpers/network/APICall.d.ts +13 -0
  61. package/lib/typescript/src/helpers/network/APICall.d.ts.map +1 -0
  62. package/lib/typescript/src/helpers/network/Encryption.d.ts +24 -0
  63. package/lib/typescript/src/helpers/network/Encryption.d.ts.map +1 -0
  64. package/lib/typescript/src/helpers/network/network_manager.d.ts +21 -0
  65. package/lib/typescript/src/helpers/network/network_manager.d.ts.map +1 -0
  66. package/lib/typescript/src/helpers/utils/Constants.d.ts +10 -0
  67. package/lib/typescript/src/helpers/utils/Constants.d.ts.map +1 -0
  68. package/lib/typescript/src/helpers/utils/LibraryConstants.d.ts +24 -0
  69. package/lib/typescript/src/helpers/utils/LibraryConstants.d.ts.map +1 -0
  70. package/lib/typescript/src/helpers/utils/deviceInfoManager.d.ts +4 -0
  71. package/lib/typescript/src/helpers/utils/deviceInfoManager.d.ts.map +1 -0
  72. package/lib/typescript/src/helpers/utils/headerManager.d.ts +5 -0
  73. package/lib/typescript/src/helpers/utils/headerManager.d.ts.map +1 -0
  74. package/lib/typescript/src/helpers/utils/themeManager.d.ts +12 -0
  75. package/lib/typescript/src/helpers/utils/themeManager.d.ts.map +1 -0
  76. package/lib/typescript/src/helpers/utils/webviewCallback.d.ts +13 -0
  77. package/lib/typescript/src/helpers/utils/webviewCallback.d.ts.map +1 -0
  78. package/lib/typescript/src/helpers/webview.d.ts +21 -0
  79. package/lib/typescript/src/helpers/webview.d.ts.map +1 -0
  80. package/lib/typescript/src/index.d.ts +5 -0
  81. package/lib/typescript/src/index.d.ts.map +1 -0
  82. package/package.json +170 -0
  83. package/react-native.config.js +12 -0
  84. package/src/helpers/ServiceNames.tsx +170 -0
  85. package/src/helpers/analytics/analytics_event_model.tsx +47 -0
  86. package/src/helpers/analytics/analytics_logger.tsx +91 -0
  87. package/src/helpers/analytics/event_storage.tsx +44 -0
  88. package/src/helpers/banking_dcb_react_native.tsx +413 -0
  89. package/src/helpers/helper.tsx +1 -0
  90. package/src/helpers/network/APICall.tsx +154 -0
  91. package/src/helpers/network/Encryption.tsx +179 -0
  92. package/src/helpers/network/network_manager.tsx +122 -0
  93. package/src/helpers/utils/Constants.tsx +10 -0
  94. package/src/helpers/utils/LibraryConstants.tsx +133 -0
  95. package/src/helpers/utils/deviceInfoManager.tsx +37 -0
  96. package/src/helpers/utils/headerManager.tsx +22 -0
  97. package/src/helpers/utils/themeManager.tsx +51 -0
  98. package/src/helpers/utils/webviewCallback.tsx +25 -0
  99. package/src/helpers/webview.tsx +410 -0
  100. package/src/index.tsx +5 -0
@@ -0,0 +1,410 @@
1
+ import { WebView as RNWebView } from 'react-native-webview';
2
+ import {
3
+ BackHandler,
4
+ Platform,
5
+ Linking,
6
+ SafeAreaView,
7
+ ActivityIndicator,
8
+ View,
9
+ ToastAndroid,
10
+ StatusBar,
11
+ } from 'react-native';
12
+ import { useEffect, useRef, useState } from 'react';
13
+
14
+ import {
15
+ type WebViewProps,
16
+ type WebViewNavigation,
17
+ } from 'react-native-webview';
18
+ import {
19
+ WebViewCallback,
20
+ type WebViewCallbackFunction,
21
+ } from './utils/webviewCallback';
22
+ import {
23
+ check,
24
+ PERMISSIONS,
25
+ request,
26
+ RESULTS,
27
+ type Permission,
28
+ } from 'react-native-permissions';
29
+ import FileViewer from 'react-native-file-viewer';
30
+ import RNFS from 'react-native-fs';
31
+ import CookieManager from '@react-native-cookies/cookies';
32
+ import { AnalyticsLogger } from './analytics/analytics_logger';
33
+
34
+ interface ExtendedWebViewProps extends WebViewProps {
35
+ onPermissionRequest?: (event: any) => void;
36
+ androidPermissions?: {
37
+ camera?: boolean;
38
+ microphone?: boolean;
39
+ geolocation?: boolean;
40
+ };
41
+ }
42
+ interface WebViewCustomProps {
43
+ url: string;
44
+ options?: Partial<ExtendedWebViewProps>;
45
+ onCallback?: WebViewCallbackFunction;
46
+ hostName?: string;
47
+ whitelistedUrls?: string[];
48
+ onPageFinished?: () => void;
49
+ }
50
+ const clearAllCookies = async () => {
51
+ try {
52
+ await CookieManager.clearAll(true);
53
+ console.log('clearing cookies');
54
+ } catch (e) {
55
+ console.error('Failed to clear cookies:', e);
56
+ }
57
+ };
58
+
59
+ export const WebView = ({
60
+ url,
61
+ options,
62
+ onCallback,
63
+ hostName,
64
+ whitelistedUrls = [],
65
+ onPageFinished,
66
+ }: WebViewCustomProps) => {
67
+ const webviewRef = useRef<RNWebView | null>(null);
68
+ const [canGoBack, setCanGoBack] = useState(false);
69
+ const _analyticsLogger = new AnalyticsLogger();
70
+ // const [hasRedirected, setHasRedirected] = useState(false);
71
+ const [, setToast] = useState<{ visible: boolean; message: string } | null>(
72
+ null
73
+ );
74
+ const handleExternalUrl = async (url: string) => {
75
+ try {
76
+ await new Promise((resolve) => setTimeout(resolve, 200));
77
+ await Linking.openURL(url);
78
+ // _analyticsLogger.logEvent({ event: 'REACT_NATIVE_OPEN_URL_EXTERNALLY' });
79
+ } catch (error) {
80
+ // Implement your toast here
81
+ if (Platform.OS === 'android') {
82
+ ToastAndroid.show(
83
+ 'Oops! Something went wrong. Please try again.',
84
+ ToastAndroid.LONG
85
+ );
86
+ }
87
+ console.log('Application not found to open link');
88
+ }
89
+ };
90
+ const showToast_with_code_message = (syntheticEvent: any) => {
91
+ const { nativeEvent } = syntheticEvent;
92
+ const errorCodesToHandle = [-2, -6, -8, -10];
93
+ if (errorCodesToHandle.includes(nativeEvent.code)) {
94
+ const message = `WebView Error: [${nativeEvent.code}] ${nativeEvent.description}`;
95
+ setToast({ visible: true, message });
96
+
97
+ if (nativeEvent.code === -2) {
98
+ if (Platform.OS === 'android') {
99
+ ToastAndroid.show(
100
+ 'There seems to be a network issue. Please try again.',
101
+ ToastAndroid.LONG
102
+ );
103
+ }
104
+ clearAllCookies();
105
+ onCallback?.(WebViewCallback.redirect('WEBVIEW_ERROR'));
106
+ } else {
107
+ if (Platform.OS === 'android') {
108
+ ToastAndroid.show(
109
+ 'Oops! Something went wrong. Please try again.',
110
+ ToastAndroid.LONG
111
+ );
112
+ }
113
+ clearAllCookies();
114
+ onCallback?.(WebViewCallback.redirect('WEBVIEW_ERROR'));
115
+ }
116
+
117
+ setTimeout(() => setToast(null), 6000);
118
+ }
119
+ };
120
+
121
+ const getQueryParam = (url: string, param: string): string | null => {
122
+ const match = url.match(new RegExp('[?&]' + param + '=([^&#]*)'));
123
+ return match && match[1] ? decodeURIComponent(match[1]) : null;
124
+ };
125
+
126
+ const downloadAndOpenFile = async (fileUrl: string) => {
127
+ try {
128
+ console.log('Starting file download...', fileUrl);
129
+
130
+ // Get cookies using CookieManager
131
+ let cookieString = '';
132
+ try {
133
+ const cookies = await CookieManager.get(fileUrl);
134
+ cookieString = Object.keys(cookies)
135
+ .map((key) => `${key}=${cookies[key]?.value ?? ''}`)
136
+ .join('; ');
137
+ } catch (e) {
138
+ console.error('Failed to get cookies:', e);
139
+ }
140
+
141
+ // Create a clean filename from the URL
142
+ const timestamp = new Date().getTime();
143
+ // Extract only the last part of the path for the filename
144
+ const urlParts = fileUrl.split('/');
145
+ const lastPart = urlParts[urlParts.length - 1];
146
+ const fileExt = lastPart?.split('.').pop() || 'pdf';
147
+ const fileName = `${timestamp}.${fileExt}`;
148
+
149
+ const downloadPath = Platform.select({
150
+ ios: `${RNFS.DocumentDirectoryPath}/${fileName}`,
151
+ android: `${RNFS.DownloadDirectoryPath}/${fileName}`,
152
+ });
153
+
154
+ if (!downloadPath) {
155
+ throw new Error('Could not determine download path');
156
+ }
157
+
158
+ console.log('Downloading to:', downloadPath);
159
+
160
+ const options = {
161
+ fromUrl: fileUrl,
162
+ toFile: downloadPath,
163
+ headers: {
164
+ 'Cookie': cookieString,
165
+ 'Accept': 'application/pdf',
166
+ 'Content-Type': 'application/pdf',
167
+ },
168
+ background: true,
169
+ begin: (res: any) => {
170
+ console.log('Download started with headers:', res);
171
+ },
172
+ };
173
+
174
+ const response = await RNFS.downloadFile(options).promise;
175
+
176
+ if (response.statusCode === 200) {
177
+ console.log('File downloaded to:', downloadPath);
178
+
179
+ if (Platform.OS === 'android') {
180
+ ToastAndroid.show('File downloaded successfully', ToastAndroid.SHORT);
181
+ }
182
+
183
+ await FileViewer.open(downloadPath, {
184
+ showOpenWithDialog: true,
185
+ onDismiss: () => {
186
+ if (Platform.OS === 'ios') {
187
+ RNFS.unlink(downloadPath).catch(console.error);
188
+ }
189
+ },
190
+ });
191
+ } else {
192
+ throw new Error(`Download failed with status ${response.statusCode}`);
193
+ }
194
+ } catch (error) {
195
+ console.error('File download/open error:', error);
196
+ }
197
+ };
198
+
199
+ const handleOnShouldStartLoadWithRequest = (event: WebViewNavigation) => {
200
+ const { url } = event;
201
+ console.log('URL:', url);
202
+ if (url === 'about:blank' || url === 'about:srcdoc') {
203
+ return true;
204
+ }
205
+ // _analyticsLogger.logEvent({ event: 'REACT_NATIVE_LOADING_URL ', url });
206
+
207
+ if (
208
+ url?.includes('/session-expired?status=') ||
209
+ url?.includes('/redirect?status=')
210
+ ) {
211
+ console.log('overriding url');
212
+ const status = getQueryParam(url, 'status');
213
+
214
+ console.log('Status thrown from webview ', status);
215
+ // _analyticsLogger.logEvent({
216
+ // event: 'REACT_NATIVE_WEBVIEW_CALLBACK ',
217
+ // status,
218
+ // });
219
+
220
+ StatusBar.setBackgroundColor('#7E7E7EFF');
221
+ clearAllCookies();
222
+ onCallback?.(WebViewCallback.redirect(status ?? undefined));
223
+ return false;
224
+ }
225
+ if (url?.includes('/api/user/redirect')) {
226
+ console.log('overriding redirect url');
227
+
228
+ StatusBar.setBackgroundColor('#7E7E7EFF');
229
+ clearAllCookies();
230
+ onCallback?.(WebViewCallback.redirect('SESSION_EXPIRED'));
231
+ return false;
232
+ }
233
+
234
+ // Handle whitelisted URLs
235
+ const isWhitelisted =
236
+ whitelistedUrls.some((white) => url.includes(white)) ||
237
+ (hostName && url.includes(hostName));
238
+
239
+ if (
240
+ !isWhitelisted &&
241
+ url &&
242
+ url !== 'about:blank' &&
243
+ url !== 'about:srcdoc'
244
+ ) {
245
+ console.log('External URL detected:', url);
246
+ handleExternalUrl(url);
247
+ return false;
248
+ }
249
+ if (
250
+ url.includes('.pdf') ||
251
+ url.includes('/statements/') ||
252
+ url.includes('/download_statements')
253
+ ) {
254
+ console.log('File download URL detected:', url);
255
+ downloadAndOpenFile(url);
256
+ return false;
257
+ }
258
+
259
+ return true;
260
+ };
261
+
262
+ const handleBackButton = () => {
263
+ if (canGoBack && webviewRef.current) {
264
+ webviewRef.current.goBack();
265
+ return true;
266
+ }
267
+ return false;
268
+ };
269
+
270
+ useEffect(() => {
271
+ const backHandler = BackHandler.addEventListener(
272
+ 'hardwareBackPress',
273
+ handleBackButton
274
+ );
275
+ return () => {
276
+ backHandler.remove();
277
+ };
278
+ }, [canGoBack]);
279
+
280
+ const Loading = () => (
281
+ <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
282
+ <ActivityIndicator size="large" />
283
+ </View>
284
+ );
285
+
286
+ // Cookie injection script
287
+
288
+ const injectedJavaScript = `
289
+ window.onerror = function(message, source, lineno, colno, error) {
290
+ if (message.includes('RATE_LIMIT_USER')) {
291
+ window.ReactNativeWebView.postMessage(JSON.stringify({
292
+ type: 'RATE_LIMIT_ERROR'
293
+ }));
294
+ }
295
+ };
296
+ true;
297
+ `;
298
+
299
+ const requestPermission = async (permission: Permission) => {
300
+ try {
301
+ const result = await check(permission);
302
+
303
+ if (result === RESULTS.DENIED) {
304
+ const requestResult = await request(permission);
305
+ return requestResult === RESULTS.GRANTED;
306
+ }
307
+
308
+ return result === RESULTS.GRANTED;
309
+ } catch (error) {
310
+ console.error('Permission request failed:', error);
311
+ return false;
312
+ }
313
+ };
314
+
315
+ const handlePermissionRequest = async (event: any) => {
316
+ const { resources } = event.nativeEvent;
317
+ console.log('Permission requested:', resources);
318
+
319
+ const permissionsToRequest: Permission[] = [];
320
+
321
+ if (resources.includes('video')) {
322
+ permissionsToRequest.push(
323
+ Platform.OS === 'ios'
324
+ ? PERMISSIONS.IOS.CAMERA
325
+ : PERMISSIONS.ANDROID.CAMERA
326
+ );
327
+ }
328
+ if (resources.includes('microphone')) {
329
+ permissionsToRequest.push(
330
+ Platform.OS === 'ios'
331
+ ? PERMISSIONS.IOS.MICROPHONE
332
+ : PERMISSIONS.ANDROID.RECORD_AUDIO
333
+ );
334
+ }
335
+ if (resources.includes('geolocation')) {
336
+ permissionsToRequest.push(
337
+ Platform.OS === 'ios'
338
+ ? PERMISSIONS.IOS.LOCATION_WHEN_IN_USE
339
+ : PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION
340
+ );
341
+ }
342
+
343
+ try {
344
+ const results = await Promise.all(
345
+ permissionsToRequest.map((permission) => requestPermission(permission))
346
+ );
347
+
348
+ const granted = results.every((result) => result === true);
349
+ if (granted) {
350
+ // @ts-ignore - grant exists on the event
351
+ event.nativeEvent.grant();
352
+ } else {
353
+ // @ts-ignore - deny exists on the event
354
+ event.nativeEvent.deny();
355
+ }
356
+ } catch (error) {
357
+ console.error('Permission handling failed:', error);
358
+ // @ts-ignore - deny exists on the event
359
+ event.nativeEvent.deny();
360
+ }
361
+ };
362
+
363
+ const handleWebViewLoadEnd = (syntheticEvent: any) => {
364
+ const loadedUrl = syntheticEvent?.nativeEvent?.url || url;
365
+
366
+ onPageFinished?.();
367
+ };
368
+
369
+ return (
370
+ <SafeAreaView style={{ flex: 1, backgroundColor: 'white' }}>
371
+ <RNWebView
372
+ ref={webviewRef}
373
+ source={{ uri: url }}
374
+ onPermissionRequest={
375
+ handlePermissionRequest as ExtendedWebViewProps['onPermissionRequest']
376
+ }
377
+ geolocationEnabled={true}
378
+ androidPermissions={{
379
+ camera: true,
380
+ microphone: true,
381
+ geolocation: true,
382
+ }}
383
+ onError={(e) => {
384
+ showToast_with_code_message(e);
385
+ options?.onError?.(e as any);
386
+ }}
387
+ {...options}
388
+ startInLoadingState={true}
389
+ renderLoading={() => <Loading />}
390
+ onLoadEnd={handleWebViewLoadEnd}
391
+ originWhitelist={['*']}
392
+ javaScriptEnabled={true}
393
+ domStorageEnabled={true}
394
+ mediaPlaybackRequiresUserAction={false}
395
+ allowsInlineMediaPlayback={true}
396
+ onShouldStartLoadWithRequest={handleOnShouldStartLoadWithRequest}
397
+ onNavigationStateChange={(navState) => {
398
+ setCanGoBack(navState.canGoBack);
399
+ }}
400
+ injectedJavaScript={injectedJavaScript}
401
+ decelerationRate={Platform.OS === 'ios' ? 'normal' : 0.9}
402
+ allowsBackForwardNavigationGestures={Platform.OS === 'ios'}
403
+ sharedCookiesEnabled={true}
404
+ setSupportMultipleWindows={false}
405
+ mixedContentMode="always"
406
+ allowsLinkPreview={true}
407
+ />
408
+ </SafeAreaView>
409
+ );
410
+ };
package/src/index.tsx ADDED
@@ -0,0 +1,5 @@
1
+ export { BankingDcbReactNative } from './helpers/banking_dcb_react_native';
2
+
3
+ export { WebView } from './helpers/webview';
4
+ import type { WebViewCallbackFunction } from './helpers/utils/webviewCallback';
5
+ export type { WebViewCallbackFunction };