partner_react_native_sdk 0.1.0

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