import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react'; import { Image, View, ImageSourcePropType, } from 'react-native'; import invariant from 'invariant'; import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands'; import RNCWebView from "./WebViewNativeComponent.macos"; import RNCWebViewModule from "./NativeRNCWebView"; import { defaultOriginWhitelist, defaultRenderError, defaultRenderLoading, useWebViewLogic, } from './WebViewShared'; import { MacOSWebViewProps, NativeWebViewMacOS, } from './WebViewTypes'; import styles from './WebView.styles'; const Commands = codegenNativeCommands({ supportedCommands: ['goBack', 'goForward', 'reload', 'stopLoading', 'injectJavaScript', 'requestFocus', 'postMessage', 'loadUrl'], }); const { resolveAssetSource } = Image; const useWarnIfChanges = (value: T, name: string) => { const ref = useRef(value); if (ref.current !== value) { console.warn(`Changes to property ${name} do nothing after the initial render.`); ref.current = value; } } const WebViewComponent = forwardRef<{}, MacOSWebViewProps>(({ javaScriptEnabled = true, cacheEnabled = true, originWhitelist = defaultOriginWhitelist, useSharedProcessPool= true, injectedJavaScript, injectedJavaScriptBeforeContentLoaded, startInLoadingState, onNavigationStateChange, onLoadStart, onError, onLoad, onLoadEnd, onLoadProgress, onHttpError: onHttpErrorProp, onMessage: onMessageProp, renderLoading, renderError, style, containerStyle, source, nativeConfig, allowsInlineMediaPlayback, allowsAirPlayForMediaPlayback, mediaPlaybackRequiresUserAction, incognito, onShouldStartLoadWithRequest: onShouldStartLoadWithRequestProp, ...otherProps }, ref) => { const webViewRef = useRef(null); const onShouldStartLoadWithRequestCallback = useCallback(( shouldStart: boolean, _url: string, lockIdentifier = 0, ) => { RNCWebViewModule.shouldStartLoadWithLockIdentifier(!!shouldStart, lockIdentifier); }, []); const { onLoadingStart, onShouldStartLoadWithRequest, onMessage, viewState, setViewState, lastErrorEvent, onHttpError, onLoadingError, onLoadingFinish, onLoadingProgress, onContentProcessDidTerminate } = useWebViewLogic({ onNavigationStateChange, onLoad, onError, onHttpErrorProp, onLoadEnd, onLoadProgress, onLoadStart, onMessageProp, startInLoadingState, originWhitelist, onShouldStartLoadWithRequestProp, onShouldStartLoadWithRequestCallback, }); useImperativeHandle(ref, () => ({ goForward: () => Commands.goForward(webViewRef.current), goBack: () => Commands.goBack(webViewRef.current), reload: () => { setViewState( 'LOADING', ); Commands.reload(webViewRef.current) }, stopLoading: () => Commands.stopLoading(webViewRef.current), postMessage: (data: string) => Commands.postMessage(webViewRef.current, data), injectJavaScript: (data: string) => Commands.injectJavaScript(webViewRef.current, data), requestFocus: () => Commands.requestFocus(webViewRef.current), }), [setViewState, webViewRef]); useWarnIfChanges(allowsInlineMediaPlayback, 'allowsInlineMediaPlayback'); useWarnIfChanges(allowsAirPlayForMediaPlayback, 'allowsAirPlayForMediaPlayback'); useWarnIfChanges(incognito, 'incognito'); useWarnIfChanges(mediaPlaybackRequiresUserAction, 'mediaPlaybackRequiresUserAction'); let otherView = null; if (viewState === 'LOADING') { otherView = (renderLoading || defaultRenderLoading)(); } else if (viewState === 'ERROR') { invariant(lastErrorEvent != null, 'lastErrorEvent expected to be non-null'); otherView = (renderError || defaultRenderError)( lastErrorEvent.domain, lastErrorEvent.code, lastErrorEvent.description, ); } else if (viewState !== 'IDLE') { console.error(`RNCWebView invalid state encountered: ${viewState}`); } const webViewStyles = [styles.container, styles.webView, style]; const webViewContainerStyle = [styles.container, containerStyle]; const NativeWebView = (nativeConfig?.component as typeof NativeWebViewMacOS | undefined) || RNCWebView; const webView = ( ); return ( {webView} {otherView} );}) // no native implementation for macOS, depends only on permissions const isFileUploadSupported: () => Promise = async () => true; const WebView = Object.assign(WebViewComponent, {isFileUploadSupported}); export default WebView;