@exodus/react-native-webview 11.26.1-exodus.9 → 13.16.0-exodus.1

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/README.md +36 -63
  2. package/android/build.gradle +83 -110
  3. package/android/gradle.properties +3 -4
  4. package/android/src/main/AndroidManifest.xml +12 -0
  5. package/android/src/main/AndroidManifestNew.xml +26 -0
  6. package/android/src/main/java/com/reactnativecommunity/webview/RNCBasicAuthCredential.java +11 -0
  7. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebChromeClient.java +407 -0
  8. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java +468 -0
  9. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewClient.java +330 -0
  10. package/android/src/main/java/com/reactnativecommunity/webview/{WebViewConfig.java → RNCWebViewConfig.java} +3 -4
  11. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewFileProvider.java +1 -1
  12. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt +746 -0
  13. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewMessagingModule.kt +9 -0
  14. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewModuleImpl.java +554 -0
  15. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewPackage.java +57 -12
  16. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewWrapper.kt +39 -0
  17. package/android/src/main/java/com/reactnativecommunity/webview/events/SubResourceErrorEvent.kt +25 -0
  18. package/android/src/main/java/com/reactnativecommunity/webview/events/TopCustomMenuSelectionEvent.kt +24 -0
  19. package/android/src/main/java/com/reactnativecommunity/webview/events/TopHttpErrorEvent.kt +25 -0
  20. package/android/src/main/java/com/reactnativecommunity/webview/events/TopNewWindowEvent.kt +25 -0
  21. package/android/src/main/java/com/reactnativecommunity/webview/events/TopRenderProcessGoneEvent.kt +25 -0
  22. package/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewManager.java +570 -0
  23. package/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewModule.java +57 -0
  24. package/android/src/oldarch/com/reactnativecommunity/webview/RNCWebViewManager.java +341 -0
  25. package/android/src/oldarch/com/reactnativecommunity/webview/RNCWebViewModule.java +59 -0
  26. package/apple/RCTConvert+WKDataDetectorTypes.h +11 -0
  27. package/apple/RCTConvert+WKDataDetectorTypes.m +27 -0
  28. package/apple/RNCWebView.h +26 -100
  29. package/apple/RNCWebView.mm +555 -0
  30. package/apple/RNCWebViewDecisionManager.h +20 -0
  31. package/apple/RNCWebViewDecisionManager.m +47 -0
  32. package/apple/RNCWebViewImpl.h +164 -0
  33. package/apple/{RNCWebView.m → RNCWebViewImpl.m} +802 -225
  34. package/apple/RNCWebViewManager.h +4 -8
  35. package/apple/RNCWebViewManager.mm +221 -0
  36. package/apple/RNCWebViewModule.h +23 -0
  37. package/apple/RNCWebViewModule.mm +34 -0
  38. package/index.d.ts +2 -3
  39. package/lib/NativeRNCWebViewModule.d.ts +8 -0
  40. package/lib/NativeRNCWebViewModule.js +1 -0
  41. package/lib/RNCWebViewNativeComponent.d.ts +245 -0
  42. package/lib/RNCWebViewNativeComponent.js +1 -0
  43. package/lib/WebView.android.d.ts +0 -1
  44. package/lib/WebView.android.js +1 -135
  45. package/lib/WebView.d.ts +2 -3
  46. package/lib/WebView.ios.d.ts +0 -1
  47. package/lib/WebView.ios.js +1 -114
  48. package/lib/WebView.js +1 -11
  49. package/lib/WebView.macos.d.ts +6 -0
  50. package/lib/WebView.macos.js +1 -0
  51. package/lib/WebView.styles.d.ts +37 -11
  52. package/lib/WebView.styles.js +1 -33
  53. package/lib/WebView.windows.d.ts +17 -0
  54. package/lib/WebView.windows.js +1 -0
  55. package/lib/WebViewNativeComponent.macos.d.ts +3 -0
  56. package/lib/WebViewNativeComponent.macos.js +1 -0
  57. package/lib/WebViewNativeComponent.windows.d.ts +3 -0
  58. package/lib/WebViewNativeComponent.windows.js +1 -0
  59. package/lib/WebViewShared.d.ts +30 -9
  60. package/lib/WebViewShared.js +1 -174
  61. package/lib/WebViewTypes.d.ts +514 -98
  62. package/lib/WebViewTypes.js +1 -6
  63. package/lib/index.d.ts +0 -1
  64. package/lib/index.js +1 -3
  65. package/lib/validation.d.ts +3 -0
  66. package/lib/validation.js +1 -0
  67. package/package.json +57 -33
  68. package/react-native-webview.podspec +32 -5
  69. package/react-native.config.js +22 -18
  70. package/src/NativeRNCWebViewModule.ts +13 -0
  71. package/src/RNCWebViewNativeComponent.ts +348 -0
  72. package/src/WebView.android.tsx +345 -0
  73. package/src/WebView.ios.tsx +341 -0
  74. package/src/WebView.macos.tsx +252 -0
  75. package/src/WebView.styles.ts +41 -0
  76. package/src/WebView.tsx +25 -0
  77. package/src/WebView.windows.tsx +217 -0
  78. package/src/WebViewNativeComponent.macos.ts +7 -0
  79. package/src/WebViewNativeComponent.windows.ts +8 -0
  80. package/src/WebViewShared.tsx +476 -0
  81. package/src/WebViewTypes.ts +1402 -0
  82. package/src/__tests__/WebViewShared-test.js +323 -0
  83. package/src/__tests__/__snapshots__/WebViewShared-test.js.snap +8 -0
  84. package/src/__tests__/validation-test.js +38 -0
  85. package/src/index.ts +4 -0
  86. package/src/validation.ts +20 -0
  87. package/android/.editorconfig +0 -6
  88. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java +0 -1408
  89. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewModule.java +0 -506
  90. package/apple/RNCWebViewManager.m +0 -278
  91. package/lib/WebViewNativeComponent.android.d.ts +0 -4
  92. package/lib/WebViewNativeComponent.android.js +0 -3
  93. package/lib/WebViewNativeComponent.ios.d.ts +0 -4
  94. package/lib/WebViewNativeComponent.ios.js +0 -3
@@ -0,0 +1,345 @@
1
+ import React, {
2
+ forwardRef,
3
+ ReactElement,
4
+ useCallback,
5
+ useEffect,
6
+ useImperativeHandle,
7
+ useRef,
8
+ } from 'react';
9
+
10
+ import { Image, View, ImageSourcePropType, HostComponent } from 'react-native';
11
+
12
+ import BatchedBridge from 'react-native/Libraries/BatchedBridge/BatchedBridge';
13
+ import EventEmitter from 'react-native/Libraries/vendor/emitter/EventEmitter';
14
+
15
+ import invariant from 'invariant';
16
+
17
+ import RNCWebView, { Commands, NativeProps } from './RNCWebViewNativeComponent';
18
+ import RNCWebViewModule from './NativeRNCWebViewModule';
19
+ import {
20
+ defaultOriginWhitelist,
21
+ defaultDeeplinkWhitelist,
22
+ defaultRenderError,
23
+ defaultRenderLoading,
24
+ useWebViewLogic,
25
+ } from './WebViewShared';
26
+ import {
27
+ AndroidWebViewProps,
28
+ WebViewSourceUri,
29
+ type WebViewMessageEvent,
30
+ type ShouldStartLoadRequestEvent,
31
+ } from './WebViewTypes';
32
+
33
+ import styles from './WebView.styles';
34
+
35
+ const { resolveAssetSource } = Image;
36
+
37
+ const directEventEmitter = new EventEmitter();
38
+
39
+ const registerCallableModule: (name: string, module: Object) => void =
40
+ // `registerCallableModule()` is available in React Native 0.74 and above.
41
+ // Fallback to use `BatchedBridge.registerCallableModule()` for older versions.
42
+
43
+ require('react-native').registerCallableModule ??
44
+ BatchedBridge.registerCallableModule.bind(BatchedBridge);
45
+
46
+ registerCallableModule('RNCWebViewMessagingModule', {
47
+ onShouldStartLoadWithRequest: (
48
+ event: ShouldStartLoadRequestEvent & { messagingModuleName?: string }
49
+ ) => {
50
+ directEventEmitter.emit('onShouldStartLoadWithRequest', event);
51
+ },
52
+ onMessage: (
53
+ event: WebViewMessageEvent & { messagingModuleName?: string }
54
+ ) => {
55
+ directEventEmitter.emit('onMessage', event);
56
+ },
57
+ });
58
+
59
+ /**
60
+ * A simple counter to uniquely identify WebView instances. Do not use this for anything else.
61
+ */
62
+ let uniqueRef = 0;
63
+
64
+ const WebViewComponent = forwardRef<{}, AndroidWebViewProps>(
65
+ (
66
+ {
67
+ overScrollMode = 'always',
68
+ javaScriptEnabled = true,
69
+ thirdPartyCookiesEnabled = true,
70
+ scalesPageToFit = true,
71
+ allowsFullscreenVideo = false,
72
+ allowFileAccess = false,
73
+ saveFormDataDisabled = false,
74
+ cacheEnabled = true,
75
+ androidLayerType = 'none',
76
+ originWhitelist = defaultOriginWhitelist,
77
+ deeplinkWhitelist = defaultDeeplinkWhitelist,
78
+ setSupportMultipleWindows = true,
79
+ setBuiltInZoomControls = true,
80
+ setDisplayZoomControls = false,
81
+ nestedScrollEnabled = false,
82
+ startInLoadingState,
83
+ onNavigationStateChange,
84
+ onLoadStart,
85
+ onError,
86
+ onLoad,
87
+ onLoadEnd,
88
+ onLoadSubResourceError,
89
+ onLoadProgress,
90
+ onHttpError: onHttpErrorProp,
91
+ onRenderProcessGone: onRenderProcessGoneProp,
92
+ onMessage: onMessageProp,
93
+ onOpenWindow: onOpenWindowProp,
94
+ renderLoading,
95
+ renderError,
96
+ style,
97
+ containerStyle,
98
+ source,
99
+ nativeConfig,
100
+ onShouldStartLoadWithRequest: onShouldStartLoadWithRequestProp,
101
+ injectedJavaScriptObject,
102
+ validateMeta,
103
+ validateData,
104
+ ...otherProps
105
+ },
106
+ ref
107
+ ) => {
108
+ const messagingModuleName = useRef<string>(
109
+ `WebViewMessageHandler${(uniqueRef += 1)}`
110
+ ).current;
111
+ const webViewRef = useRef<React.ComponentRef<
112
+ HostComponent<NativeProps>
113
+ > | null>(null);
114
+
115
+ const onShouldStartLoadWithRequestCallback = useCallback(
116
+ (shouldStart: boolean, url: string, lockIdentifier?: number) => {
117
+ if (lockIdentifier) {
118
+ RNCWebViewModule.shouldStartLoadWithLockIdentifier(
119
+ shouldStart,
120
+ lockIdentifier
121
+ );
122
+ } else if (shouldStart && webViewRef.current) {
123
+ Commands.loadUrl(webViewRef.current, url);
124
+ }
125
+ },
126
+ []
127
+ );
128
+
129
+ const {
130
+ onLoadingStart,
131
+ onShouldStartLoadWithRequest,
132
+ onMessage,
133
+ viewState,
134
+ setViewState,
135
+ lastErrorEvent,
136
+ onHttpError,
137
+ onLoadingError,
138
+ onLoadingSubResourceError,
139
+ onLoadingFinish,
140
+ onLoadingProgress,
141
+ onOpenWindow,
142
+ onRenderProcessGone,
143
+ } = useWebViewLogic({
144
+ onNavigationStateChange,
145
+ onLoad,
146
+ onError,
147
+ onHttpErrorProp,
148
+ onLoadSubResourceError,
149
+ onLoadEnd,
150
+ onLoadProgress,
151
+ onLoadStart,
152
+ onRenderProcessGoneProp,
153
+ onMessageProp,
154
+ onOpenWindowProp,
155
+ startInLoadingState,
156
+ originWhitelist,
157
+ deeplinkWhitelist,
158
+ onShouldStartLoadWithRequestProp,
159
+ onShouldStartLoadWithRequestCallback,
160
+ validateMeta,
161
+ validateData,
162
+ });
163
+
164
+ useImperativeHandle(
165
+ ref,
166
+ () => ({
167
+ goForward: () =>
168
+ webViewRef.current && Commands.goForward(webViewRef.current),
169
+ goBack: () => webViewRef.current && Commands.goBack(webViewRef.current),
170
+ reload: () => {
171
+ setViewState('LOADING');
172
+ if (webViewRef.current) {
173
+ Commands.reload(webViewRef.current);
174
+ }
175
+ },
176
+ stopLoading: () =>
177
+ webViewRef.current && Commands.stopLoading(webViewRef.current),
178
+ postMessage: (data: string) =>
179
+ webViewRef.current && Commands.postMessage(webViewRef.current, data),
180
+ injectJavaScript: (data: string) =>
181
+ webViewRef.current &&
182
+ Commands.injectJavaScript(webViewRef.current, data),
183
+ requestFocus: () =>
184
+ webViewRef.current && Commands.requestFocus(webViewRef.current),
185
+ clearFormData: () =>
186
+ webViewRef.current && Commands.clearFormData(webViewRef.current),
187
+ clearCache: (includeDiskFiles: boolean) =>
188
+ webViewRef.current &&
189
+ Commands.clearCache(webViewRef.current, includeDiskFiles),
190
+ clearHistory: () =>
191
+ webViewRef.current && Commands.clearHistory(webViewRef.current),
192
+ }),
193
+ [setViewState, webViewRef]
194
+ );
195
+
196
+ useEffect(() => {
197
+ const onShouldStartLoadWithRequestSubscription =
198
+ directEventEmitter.addListener(
199
+ 'onShouldStartLoadWithRequest',
200
+ (
201
+ event: ShouldStartLoadRequestEvent & {
202
+ messagingModuleName?: string;
203
+ }
204
+ ) => {
205
+ if (event.messagingModuleName === messagingModuleName) {
206
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
207
+ const { messagingModuleName: _, ...rest } = event;
208
+ onShouldStartLoadWithRequest(rest);
209
+ }
210
+ }
211
+ );
212
+
213
+ const onMessageSubscription = directEventEmitter.addListener(
214
+ 'onMessage',
215
+ (event: WebViewMessageEvent & { messagingModuleName?: string }) => {
216
+ if (event.messagingModuleName === messagingModuleName) {
217
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
218
+ const { messagingModuleName: _, ...rest } = event;
219
+ onMessage(rest);
220
+ }
221
+ }
222
+ );
223
+
224
+ return () => {
225
+ onShouldStartLoadWithRequestSubscription.remove();
226
+ onMessageSubscription.remove();
227
+ };
228
+ }, [messagingModuleName, onMessage, onShouldStartLoadWithRequest]);
229
+
230
+ let otherView: ReactElement | undefined;
231
+ if (viewState === 'LOADING') {
232
+ otherView = (renderLoading || defaultRenderLoading)();
233
+ } else if (viewState === 'ERROR') {
234
+ invariant(
235
+ lastErrorEvent != null,
236
+ 'lastErrorEvent expected to be non-null'
237
+ );
238
+ if (lastErrorEvent) {
239
+ otherView = (renderError || defaultRenderError)(
240
+ lastErrorEvent.domain,
241
+ lastErrorEvent.code,
242
+ lastErrorEvent.description
243
+ );
244
+ }
245
+ } else if (viewState !== 'IDLE') {
246
+ console.error(`RNCWebView invalid state encountered: ${viewState}`);
247
+ }
248
+
249
+ const webViewStyles = [styles.container, styles.webView, style];
250
+ const webViewContainerStyle = [styles.container, containerStyle];
251
+
252
+ if (typeof source !== 'number' && source && 'method' in source) {
253
+ if (source.method === 'POST' && source.headers) {
254
+ console.warn(
255
+ 'WebView: `source.headers` is not supported when using POST.'
256
+ );
257
+ } else if (source.method === 'GET' && source.body) {
258
+ console.warn('WebView: `source.body` is not supported when using GET.');
259
+ }
260
+ }
261
+
262
+ const NativeWebView =
263
+ (nativeConfig?.component as typeof RNCWebView | undefined) || RNCWebView;
264
+
265
+ const sourceResolved = resolveAssetSource(source as ImageSourcePropType);
266
+ const newSource =
267
+ typeof sourceResolved === 'object'
268
+ ? Object.entries(sourceResolved as WebViewSourceUri).reduce(
269
+ (prev, [currKey, currValue]) => {
270
+ return {
271
+ ...prev,
272
+ [currKey]:
273
+ currKey === 'headers' &&
274
+ currValue &&
275
+ typeof currValue === 'object'
276
+ ? Object.entries(currValue).map(([key, value]) => {
277
+ return {
278
+ name: key,
279
+ value,
280
+ };
281
+ })
282
+ : currValue,
283
+ };
284
+ },
285
+ {}
286
+ )
287
+ : sourceResolved;
288
+
289
+ const webView = (
290
+ <NativeWebView
291
+ key="webViewKey"
292
+ {...otherProps}
293
+ messagingEnabled={typeof onMessageProp === 'function'}
294
+ messagingModuleName={messagingModuleName}
295
+ hasOnScroll={!!otherProps.onScroll}
296
+ onLoadingError={onLoadingError}
297
+ onLoadingSubResourceError={onLoadingSubResourceError}
298
+ onLoadingFinish={onLoadingFinish}
299
+ onLoadingProgress={onLoadingProgress}
300
+ onLoadingStart={onLoadingStart}
301
+ onHttpError={onHttpError}
302
+ onRenderProcessGone={onRenderProcessGone}
303
+ onMessage={onMessage}
304
+ onOpenWindow={onOpenWindow}
305
+ hasOnOpenWindowEvent={onOpenWindowProp !== undefined}
306
+ onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
307
+ ref={webViewRef}
308
+ // TODO: find a better way to type this.
309
+ // @ts-expect-error source is old arch
310
+ source={sourceResolved}
311
+ newSource={newSource}
312
+ style={webViewStyles}
313
+ overScrollMode={overScrollMode}
314
+ javaScriptEnabled={javaScriptEnabled}
315
+ thirdPartyCookiesEnabled={thirdPartyCookiesEnabled}
316
+ scalesPageToFit={scalesPageToFit}
317
+ allowsFullscreenVideo={allowsFullscreenVideo}
318
+ allowFileAccess={allowFileAccess}
319
+ saveFormDataDisabled={saveFormDataDisabled}
320
+ cacheEnabled={cacheEnabled}
321
+ androidLayerType={androidLayerType}
322
+ setSupportMultipleWindows={setSupportMultipleWindows}
323
+ setBuiltInZoomControls={setBuiltInZoomControls}
324
+ setDisplayZoomControls={setDisplayZoomControls}
325
+ nestedScrollEnabled={nestedScrollEnabled}
326
+ injectedJavaScriptObject={JSON.stringify(injectedJavaScriptObject)}
327
+ {...nativeConfig?.props}
328
+ />
329
+ );
330
+
331
+ return (
332
+ <View style={webViewContainerStyle}>
333
+ {webView}
334
+ {otherView}
335
+ </View>
336
+ );
337
+ }
338
+ );
339
+
340
+ // native implementation should return "true" only for Android 5+
341
+ const { isFileUploadSupported } = RNCWebViewModule;
342
+
343
+ const WebView = Object.assign(WebViewComponent, { isFileUploadSupported });
344
+
345
+ export default WebView;
@@ -0,0 +1,341 @@
1
+ import React, {
2
+ forwardRef,
3
+ useCallback,
4
+ useImperativeHandle,
5
+ useRef,
6
+ } from 'react';
7
+ import {
8
+ Image,
9
+ View,
10
+ Text,
11
+ Platform,
12
+ ImageSourcePropType,
13
+ HostComponent,
14
+ } from 'react-native';
15
+ import invariant from 'invariant';
16
+
17
+ import RNCWebView, { Commands, NativeProps } from './RNCWebViewNativeComponent';
18
+ import RNCWebViewModule from './NativeRNCWebViewModule';
19
+
20
+ import {
21
+ defaultOriginWhitelist,
22
+ defaultDeeplinkWhitelist,
23
+ defaultRenderError,
24
+ defaultRenderLoading,
25
+ useWebViewLogic,
26
+ versionPasses,
27
+ } from './WebViewShared';
28
+ import {
29
+ IOSWebViewProps,
30
+ DecelerationRateConstant,
31
+ WebViewSourceUri,
32
+ } from './WebViewTypes';
33
+
34
+ import styles from './WebView.styles';
35
+
36
+ const { resolveAssetSource } = Image;
37
+ const processDecelerationRate = (
38
+ decelerationRate: DecelerationRateConstant | number | undefined
39
+ ) => {
40
+ let newDecelerationRate = decelerationRate;
41
+ if (newDecelerationRate === 'normal') {
42
+ newDecelerationRate = 0.998;
43
+ } else if (newDecelerationRate === 'fast') {
44
+ newDecelerationRate = 0.99;
45
+ }
46
+ return newDecelerationRate;
47
+ };
48
+
49
+ // Exodus: Hardcoded minimum iOS versions for security
50
+ // Format: "minVersion <maxVersion, ..." means minVersion <= iOS < maxVersion
51
+ // Last entry has no upper bound
52
+ const hardMinimumIOSVersion = '12.5.6 <13, 13.6.1 <14, 14.8.1 <15, 15.7.1';
53
+
54
+ const useWarnIfChanges = <T extends unknown>(value: T, name: string) => {
55
+ const ref = useRef(value);
56
+ if (ref.current !== value) {
57
+ console.warn(
58
+ `Changes to property ${name} do nothing after the initial render.`
59
+ );
60
+ ref.current = value;
61
+ }
62
+ };
63
+
64
+ const WebViewComponent = forwardRef<{}, IOSWebViewProps>(
65
+ (
66
+ {
67
+ fraudulentWebsiteWarningEnabled = true,
68
+ javaScriptEnabled = true,
69
+ cacheEnabled = true,
70
+ originWhitelist = defaultOriginWhitelist,
71
+ deeplinkWhitelist = defaultDeeplinkWhitelist,
72
+ useSharedProcessPool = true,
73
+ textInteractionEnabled = true,
74
+ injectedJavaScript,
75
+ injectedJavaScriptBeforeContentLoaded,
76
+ injectedJavaScriptForMainFrameOnly = true,
77
+ injectedJavaScriptBeforeContentLoadedForMainFrameOnly = true,
78
+ injectedJavaScriptObject,
79
+ startInLoadingState,
80
+ onNavigationStateChange,
81
+ onLoadStart,
82
+ onError,
83
+ onLoad,
84
+ onLoadEnd,
85
+ onLoadProgress,
86
+ onContentProcessDidTerminate: onContentProcessDidTerminateProp,
87
+ onFileDownload,
88
+ onHttpError: onHttpErrorProp,
89
+ onMessage: onMessageProp,
90
+ onOpenWindow: onOpenWindowProp,
91
+ renderLoading,
92
+ renderError,
93
+ style,
94
+ containerStyle,
95
+ source,
96
+ nativeConfig,
97
+ allowsInlineMediaPlayback,
98
+ allowsPictureInPictureMediaPlayback = true,
99
+ allowsAirPlayForMediaPlayback,
100
+ mediaPlaybackRequiresUserAction,
101
+ dataDetectorTypes,
102
+ incognito,
103
+ decelerationRate: decelerationRateProp,
104
+ onShouldStartLoadWithRequest: onShouldStartLoadWithRequestProp,
105
+ validateMeta,
106
+ validateData,
107
+ minimumIOSVersion,
108
+ unsupportedVersionComponent: UnsupportedVersionComponent,
109
+ ...otherProps
110
+ },
111
+ ref
112
+ ) => {
113
+ const webViewRef = useRef<React.ComponentRef<
114
+ HostComponent<NativeProps>
115
+ > | null>(null);
116
+
117
+ const onShouldStartLoadWithRequestCallback = useCallback(
118
+ (shouldStart: boolean, _url: string, lockIdentifier = 0) => {
119
+ RNCWebViewModule.shouldStartLoadWithLockIdentifier(
120
+ shouldStart,
121
+ lockIdentifier
122
+ );
123
+ },
124
+ []
125
+ );
126
+
127
+ const {
128
+ onLoadingStart,
129
+ onShouldStartLoadWithRequest,
130
+ onMessage,
131
+ viewState,
132
+ setViewState,
133
+ lastErrorEvent,
134
+ onHttpError,
135
+ onLoadingError,
136
+ onLoadingFinish,
137
+ onLoadingProgress,
138
+ onOpenWindow,
139
+ onContentProcessDidTerminate,
140
+ } = useWebViewLogic({
141
+ onNavigationStateChange,
142
+ onLoad,
143
+ onError,
144
+ onHttpErrorProp,
145
+ onLoadEnd,
146
+ onLoadProgress,
147
+ onLoadStart,
148
+ onMessageProp,
149
+ onOpenWindowProp,
150
+ startInLoadingState,
151
+ originWhitelist,
152
+ deeplinkWhitelist,
153
+ onShouldStartLoadWithRequestProp,
154
+ onShouldStartLoadWithRequestCallback,
155
+ onContentProcessDidTerminateProp,
156
+ validateMeta,
157
+ validateData,
158
+ });
159
+
160
+ useImperativeHandle(
161
+ ref,
162
+ () => ({
163
+ goForward: () =>
164
+ webViewRef.current && Commands.goForward(webViewRef.current),
165
+ goBack: () => webViewRef.current && Commands.goBack(webViewRef.current),
166
+ reload: () => {
167
+ setViewState('LOADING');
168
+ if (webViewRef.current) {
169
+ Commands.reload(webViewRef.current);
170
+ }
171
+ },
172
+ stopLoading: () =>
173
+ webViewRef.current && Commands.stopLoading(webViewRef.current),
174
+ postMessage: (data: string) =>
175
+ webViewRef.current && Commands.postMessage(webViewRef.current, data),
176
+ injectJavaScript: (data: string) =>
177
+ webViewRef.current &&
178
+ Commands.injectJavaScript(webViewRef.current, data),
179
+ requestFocus: () =>
180
+ webViewRef.current && Commands.requestFocus(webViewRef.current),
181
+ clearCache: (includeDiskFiles: boolean) =>
182
+ webViewRef.current &&
183
+ Commands.clearCache(webViewRef.current, includeDiskFiles),
184
+ }),
185
+ [setViewState, webViewRef]
186
+ );
187
+
188
+ useWarnIfChanges(allowsInlineMediaPlayback, 'allowsInlineMediaPlayback');
189
+ useWarnIfChanges(
190
+ allowsPictureInPictureMediaPlayback,
191
+ 'allowsPictureInPictureMediaPlayback'
192
+ );
193
+ useWarnIfChanges(
194
+ allowsAirPlayForMediaPlayback,
195
+ 'allowsAirPlayForMediaPlayback'
196
+ );
197
+ useWarnIfChanges(incognito, 'incognito');
198
+ useWarnIfChanges(
199
+ mediaPlaybackRequiresUserAction,
200
+ 'mediaPlaybackRequiresUserAction'
201
+ );
202
+ useWarnIfChanges(dataDetectorTypes, 'dataDetectorTypes');
203
+
204
+ // Exodus: Check iOS version against minimum requirements
205
+ const iosVersion = String(Platform.Version);
206
+ const passesMinimum = minimumIOSVersion
207
+ ? versionPasses(iosVersion, minimumIOSVersion)
208
+ : true;
209
+ const passesHardMinimum = versionPasses(iosVersion, hardMinimumIOSVersion);
210
+
211
+ if (!passesMinimum || !passesHardMinimum) {
212
+ if (UnsupportedVersionComponent) {
213
+ return <UnsupportedVersionComponent />;
214
+ }
215
+ return (
216
+ <View style={{ alignSelf: 'flex-start' }}>
217
+ <Text style={{ color: 'red' }}>
218
+ iOS version is outdated and insecure. Update it to continue.
219
+ </Text>
220
+ </View>
221
+ );
222
+ }
223
+
224
+ let otherView = null;
225
+ if (viewState === 'LOADING') {
226
+ otherView = (renderLoading || defaultRenderLoading)();
227
+ } else if (viewState === 'ERROR') {
228
+ invariant(
229
+ lastErrorEvent != null,
230
+ 'lastErrorEvent expected to be non-null'
231
+ );
232
+ otherView = (renderError || defaultRenderError)(
233
+ lastErrorEvent?.domain,
234
+ lastErrorEvent?.code ?? 0,
235
+ lastErrorEvent?.description ?? ''
236
+ );
237
+ } else if (viewState !== 'IDLE') {
238
+ console.error(`RNCWebView invalid state encountered: ${viewState}`);
239
+ }
240
+
241
+ const webViewStyles = [styles.container, styles.webView, style];
242
+ const webViewContainerStyle = [styles.container, containerStyle];
243
+
244
+ const decelerationRate = processDecelerationRate(decelerationRateProp);
245
+
246
+ const NativeWebView =
247
+ (nativeConfig?.component as typeof RNCWebView | undefined) || RNCWebView;
248
+
249
+ const sourceResolved = resolveAssetSource(source as ImageSourcePropType);
250
+ const newSource =
251
+ typeof sourceResolved === 'object'
252
+ ? Object.entries(sourceResolved as WebViewSourceUri).reduce(
253
+ (prev, [currKey, currValue]) => {
254
+ return {
255
+ ...prev,
256
+ [currKey]:
257
+ currKey === 'headers' &&
258
+ currValue &&
259
+ typeof currValue === 'object'
260
+ ? Object.entries(currValue).map(([key, value]) => {
261
+ return {
262
+ name: key,
263
+ value,
264
+ };
265
+ })
266
+ : currValue,
267
+ };
268
+ },
269
+ {}
270
+ )
271
+ : sourceResolved;
272
+
273
+ const webView = (
274
+ <NativeWebView
275
+ key="webViewKey"
276
+ {...otherProps}
277
+ fraudulentWebsiteWarningEnabled={fraudulentWebsiteWarningEnabled}
278
+ javaScriptEnabled={javaScriptEnabled}
279
+ cacheEnabled={cacheEnabled}
280
+ useSharedProcessPool={useSharedProcessPool}
281
+ textInteractionEnabled={textInteractionEnabled}
282
+ decelerationRate={decelerationRate}
283
+ messagingEnabled={typeof onMessageProp === 'function'}
284
+ messagingModuleName="" // android ONLY
285
+ onLoadingError={onLoadingError}
286
+ onLoadingFinish={onLoadingFinish}
287
+ onLoadingProgress={onLoadingProgress}
288
+ onFileDownload={onFileDownload}
289
+ onLoadingStart={onLoadingStart}
290
+ onHttpError={onHttpError}
291
+ onMessage={onMessage}
292
+ onOpenWindow={onOpenWindowProp && onOpenWindow}
293
+ hasOnOpenWindowEvent={onOpenWindowProp !== undefined}
294
+ onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
295
+ onContentProcessDidTerminate={onContentProcessDidTerminate}
296
+ injectedJavaScript={injectedJavaScript}
297
+ injectedJavaScriptBeforeContentLoaded={
298
+ injectedJavaScriptBeforeContentLoaded
299
+ }
300
+ injectedJavaScriptForMainFrameOnly={injectedJavaScriptForMainFrameOnly}
301
+ injectedJavaScriptBeforeContentLoadedForMainFrameOnly={
302
+ injectedJavaScriptBeforeContentLoadedForMainFrameOnly
303
+ }
304
+ injectedJavaScriptObject={JSON.stringify(injectedJavaScriptObject)}
305
+ dataDetectorTypes={
306
+ !dataDetectorTypes || Array.isArray(dataDetectorTypes)
307
+ ? dataDetectorTypes
308
+ : [dataDetectorTypes]
309
+ }
310
+ allowsAirPlayForMediaPlayback={allowsAirPlayForMediaPlayback}
311
+ allowsInlineMediaPlayback={allowsInlineMediaPlayback}
312
+ allowsPictureInPictureMediaPlayback={
313
+ allowsPictureInPictureMediaPlayback
314
+ }
315
+ incognito={incognito}
316
+ mediaPlaybackRequiresUserAction={mediaPlaybackRequiresUserAction}
317
+ newSource={newSource}
318
+ style={webViewStyles}
319
+ hasOnFileDownload={!!onFileDownload}
320
+ ref={webViewRef}
321
+ // @ts-expect-error old arch only
322
+ source={sourceResolved}
323
+ {...nativeConfig?.props}
324
+ />
325
+ );
326
+
327
+ return (
328
+ <View style={webViewContainerStyle}>
329
+ {webView}
330
+ {otherView}
331
+ </View>
332
+ );
333
+ }
334
+ );
335
+
336
+ // no native implementation for iOS, depends only on permissions
337
+ const isFileUploadSupported: () => Promise<boolean> = async () => true;
338
+
339
+ const WebView = Object.assign(WebViewComponent, { isFileUploadSupported });
340
+
341
+ export default WebView;