From acfff0e339761acdbafa018199a9c1e85f7c6d8c Mon Sep 17 00:00:00 2001 From: Jason Johnston Date: Mon, 10 Apr 2023 16:42:53 -0400 Subject: [PATCH] [RNMobile] Update Sandbox component (#49663) * Add ref forwarding * Fix typo * Enable non touch play events * Allow parent to register window event listeners * Catch calling non-functions --------- Co-authored-by: jhnstn --- .../components/src/sandbox/index.native.js | 106 ++++++++++++------ 1 file changed, 70 insertions(+), 36 deletions(-) diff --git a/packages/components/src/sandbox/index.native.js b/packages/components/src/sandbox/index.native.js index 12125c31edf67..f374e07dec4e3 100644 --- a/packages/components/src/sandbox/index.native.js +++ b/packages/components/src/sandbox/index.native.js @@ -14,6 +14,8 @@ import { useRef, useState, useEffect, + forwardRef, + useCallback, } from '@wordpress/element'; import { usePreferredColorScheme } from '@wordpress/compose'; @@ -98,7 +100,6 @@ const observeAndResizeJS = ` // get an DOM mutations for that, so do the resize when the window is resized, too. window.addEventListener( 'resize', sendResize, true ); window.addEventListener( 'orientationchange', sendResize, true ); - widow.addEventListener( 'click', sendResize, true ); })(); `; @@ -171,20 +172,23 @@ const style = ` const EMPTY_ARRAY = []; -function Sandbox( { - containerStyle, - customJS, - html = '', - lang = 'en', - providerUrl = '', - scripts = EMPTY_ARRAY, - styles = EMPTY_ARRAY, - title = '', - type, - url, -} ) { +const Sandbox = forwardRef( function Sandbox( + { + containerStyle, + customJS, + html = '', + lang = 'en', + providerUrl = '', + scripts = EMPTY_ARRAY, + styles = EMPTY_ARRAY, + title = '', + type, + url, + onWindowEvents = {}, + }, + ref +) { const colorScheme = usePreferredColorScheme(); - const ref = useRef(); const [ height, setHeight ] = useState( 0 ); const [ contentHtml, setContentHtml ] = useState( getHtmlDoc() ); @@ -239,6 +243,21 @@ function Sandbox( { return '' + renderToString( htmlDoc ); } + const getInjectedJavaScript = useCallback( () => { + // Allow parent to override the resize observers with prop.customJS (legacy support) + let injectedJS = customJS || observeAndResizeJS; + + // Add any event listeners that were passed in. + Object.keys( onWindowEvents ).forEach( ( eventType ) => { + injectedJS += ` + window.addEventListener( '${ eventType }', function( event ) { + window.ReactNativeWebView.postMessage( JSON.stringify( { type: '${ eventType }', ...event.data } ) ); + });`; + } ); + + return injectedJS; + }, [ customJS, onWindowEvents ] ); + function updateContentHtml( forceRerender = false ) { const newContentHtml = getHtmlDoc(); @@ -253,25 +272,6 @@ function Sandbox( { } } - function checkMessageForResize( event ) { - // Attempt to parse the message data as JSON if passed as string. - let data = event.nativeEvent.data || {}; - - if ( 'string' === typeof data ) { - try { - data = JSON.parse( data ); - } catch ( e ) {} - } - - // Update the state only if the message is formatted as we expect, - // i.e. as an object with a 'resize' action. - if ( 'resize' !== data.action ) { - return; - } - - setHeight( data.height ); - } - function getSizeStyle() { const contentHeight = Math.ceil( height ); @@ -282,6 +282,39 @@ function Sandbox( { setIsLandscape( dimensions.window.width >= dimensions.window.height ); } + const onMessage = useCallback( + ( message ) => { + let data = message?.nativeEvent?.data; + + try { + data = JSON.parse( data ); + } catch ( e ) { + return; + } + + // check for resize event + if ( 'resize' === data?.action ) { + setHeight( data.height ); + } + + // Forward the event to parent event listeners + Object.keys( onWindowEvents ).forEach( ( eventType ) => { + if ( data?.type === eventType ) { + try { + onWindowEvents[ eventType ]( data ); + } catch ( e ) { + // eslint-disable-next-line no-console + console.warn( + `Error handling event ${ eventType }`, + e + ); + } + } + } ); + }, + [ onWindowEvents ] + ); + useEffect( () => { const dimensionsChangeSubscription = Dimensions.addEventListener( 'change', @@ -314,7 +347,7 @@ function Sandbox( { sandboxStyles[ 'sandbox-webview__container' ], containerStyle, ] } - injectedJavaScript={ customJS || observeAndResizeJS } + injectedJavaScript={ getInjectedJavaScript() } key={ key } ref={ ref } source={ { baseUrl: providerUrl, html: contentHtml } } @@ -326,14 +359,15 @@ function Sandbox( { getSizeStyle(), Platform.isAndroid && workaroundStyles.webView, ] } - onMessage={ checkMessageForResize } + onMessage={ onMessage } scrollEnabled={ false } setBuiltInZoomControls={ false } showsHorizontalScrollIndicator={ false } showsVerticalScrollIndicator={ false } + mediaPlaybackRequiresUserAction={ false } /> ); -} +} ); const workaroundStyles = StyleSheet.create( { webView: {