-
Notifications
You must be signed in to change notification settings - Fork 4.7k
[RNMobile][Embed block] Implement WP embed preview component #34004
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Size Change: 0 B Total Size: 1.04 MB ℹ️ View Unchanged
|
05eedbe to
a594681
Compare
We really only need to calculate the height as the width should fit the block's size. # Conflicts: # packages/components/src/sandbox/index.native.js
| type="text/javascript" | ||
| dangerouslySetInnerHTML={ { | ||
| __html: observeAndResizeJS, | ||
| __html: customJS || observeAndResizeJS, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The WP embed uses a different method to observe for size changes so we have to provide a way to inject custom Javascript.
| const wpEmbedHtml = useMemo( () => { | ||
| const doc = new window.DOMParser().parseFromString( html, 'text/html' ); | ||
| const iframe = doc.querySelector( 'iframe' ); | ||
|
|
||
| if ( iframe ) { | ||
| iframe.removeAttribute( 'style' ); | ||
| } | ||
|
|
||
| const blockQuote = doc.querySelector( 'blockquote' ); | ||
|
|
||
| if ( blockQuote ) { | ||
| blockQuote.innerHTML = ''; | ||
| } | ||
|
|
||
| return doc.body.innerHTML; | ||
| }, [ html ] ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This parsing process is similar to the web version:
gutenberg/packages/block-library/src/embed/wp-embed-preview.js
Lines 72 to 87 in b2b35ba
| const __html = useMemo( () => { | |
| const doc = new window.DOMParser().parseFromString( html, 'text/html' ); | |
| const iframe = doc.querySelector( 'iframe' ); | |
| if ( iframe ) { | |
| iframe.removeAttribute( 'style' ); | |
| } | |
| const blockQuote = doc.querySelector( 'blockquote' ); | |
| if ( blockQuote ) { | |
| blockQuote.style.display = 'none'; | |
| } | |
| return doc.body.innerHTML; | |
| }, [ html ] ); |
The main difference is how the blockquote element is disabled, in the web version this is done by setting the display style to none. However, this can't be done in the native version because the library we use (jsdom-jscore-rn) for parsing the DOM doesn't provide the style attribute. Instead the element is disabled by removing the HTML content.
| import jsdom from 'jsdom-jscore-rn'; |
gutenberg/packages/react-native-editor/src/jsdom-patches.js
Lines 251 to 258 in b2b35ba
| class DOMParser { | |
| // This is required for the stripHTML function, but it doesn't necessarily | |
| // conform to the DOM standard. | |
| // See https://github.com/wordpress-mobile/gutenberg-mobile/pull/1771 | |
| parseFromString( string ) { | |
| return jsdom.html( string ); | |
| } | |
| } |
blockQuote.style.display = 'none';
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here what we need to test is basically that the preview is rendered the same as it's in the web version and the post/page preview. The reason for having to modify the HTML of the preview is explained thoroughly in this issue.
| <SandBox | ||
| customJS={ observeAndResizeJS } | ||
| html={ wpEmbedHtml } | ||
| { ...rest } | ||
| /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For rendering the content we use the SandBox component as for other providers, but providing a custom script for observing the size changes and custom HTML.
13832a1 to
bd4b511
Compare
| const observeAndResizeJS = ` | ||
| ( function() { | ||
| if ( ! document.body || ! window.parent ) { | ||
| return; | ||
| } | ||
| function sendResize( { data: { secret, message, value } = {} } ) { | ||
| if ( | ||
| [ secret, message, value ].some( | ||
| ( attribute ) => ! attribute | ||
| ) || | ||
| message !== 'height' | ||
| ) { | ||
| return; | ||
| } | ||
| document | ||
| .querySelectorAll( 'iframe[data-secret="' + secret + '"' ) | ||
| .forEach( ( iframe ) => { | ||
| if ( +iframe.height !== value ) { | ||
| iframe.height = value; | ||
| } | ||
| } ); | ||
| // The function postMessage is exposed by the react-native-webview library | ||
| // to communicate between React Native and the WebView, in this case, | ||
| // we use it for notifying resize changes. | ||
| window.ReactNativeWebView.postMessage(JSON.stringify( { | ||
| action: 'resize', | ||
| height: value, | ||
| })); | ||
| } | ||
| window.addEventListener( 'message', sendResize ); | ||
| } )();`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code was extracted from the web version and converted to text because it has to be injected into the WebView.
gutenberg/packages/block-library/src/embed/wp-embed-preview.js
Lines 15 to 44 in b2b35ba
| /** | |
| * Checks for WordPress embed events signaling the height change when iframe | |
| * content loads or iframe's window is resized. The event is sent from | |
| * WordPress core via the window.postMessage API. | |
| * | |
| * References: | |
| * window.postMessage: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage | |
| * WordPress core embed-template on load: https://github.com/WordPress/WordPress/blob/HEAD/wp-includes/js/wp-embed-template.js#L143 | |
| * WordPress core embed-template on resize: https://github.com/WordPress/WordPress/blob/HEAD/wp-includes/js/wp-embed-template.js#L187 | |
| * | |
| * @param {WPSyntheticEvent} event Message event. | |
| */ | |
| function resizeWPembeds( { data: { secret, message, value } = {} } ) { | |
| if ( | |
| [ secret, message, value ].some( | |
| ( attribute ) => ! attribute | |
| ) || | |
| message !== 'height' | |
| ) { | |
| return; | |
| } | |
| ownerDocument | |
| .querySelectorAll( `iframe[data-secret="${ secret }"` ) | |
| .forEach( ( iframe ) => { | |
| if ( +iframe.height !== value ) { | |
| iframe.height = value; | |
| } | |
| } ); | |
| } |
NOTE: The argument passed in querySelectorAll has been updated to prevent potential incompatibilities using template literals.
ceyhun
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried testing this by merging in current trunk, and tried enabling WP embeds by adding wp-embed to this list:
| const PREVIEWABLE_PROVIDERS = [ 'youtube', 'twitter' ]; |
But it seems like providerNameSlug is different for WP embeds, for example it was plugin-directory for https://wordpress.org/plugins/wordpress-seo/, and wordpress-news for https://wordpress.org/news/2021/07/tatum/ links.
I was able to workaround that by adding 'wp-embed' === type here:
gutenberg/packages/block-library/src/embed/edit.native.js
Lines 250 to 255 in 6ef8d95
| previewable={ | |
| previewable && | |
| PREVIEWABLE_PROVIDERS.includes( | |
| providerNameSlug | |
| ) | |
| } |
After that I was able to test it using WPiOS and WPAndroid. I tried rotating the devices to test resize logic and also tested alignment options. I've tested with a WP post URL from the same site creating the post itself. I've also tested YouTube embed again as well to check if previously injected JS and component behaviour isn't changed.
Everything is working great 🎉 I just have a question about the injected JS code.
| } | ||
| document | ||
| .querySelectorAll( 'iframe[data-secret="' + secret + '"' ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we know what kind of secret is passed in there? What would be the best way to test if this part is working correctly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not totally sure but after checking this commit from WordPress repo, I think it has to do with sandboxing the iframe. I also found the reference where the secret is generated.
This part is a bit tricky to test because it requires manually editing the script to validate that it actually changes the height, one easy way is to add the following code alert('Editing iframe height'); into line 37. If it works, you'll see an alert popup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I as able to get an alert both in initial render and also on device rotation. Thanks for the tip!
| const wpEmbedHtml = useMemo( () => { | ||
| const doc = new window.DOMParser().parseFromString( html, 'text/html' ); | ||
| const iframe = doc.querySelector( 'iframe' ); | ||
|
|
||
| if ( iframe ) { | ||
| iframe.removeAttribute( 'style' ); | ||
| } | ||
|
|
||
| const blockQuote = doc.querySelector( 'blockquote' ); | ||
|
|
||
| if ( blockQuote ) { | ||
| blockQuote.innerHTML = ''; | ||
| } | ||
|
|
||
| return doc.body.innerHTML; | ||
| }, [ html ] ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh good point, I'll update with
Thanks for the review 🙇 ! Ok, I'll review your questions regarding the injected JS code 👀 . |
# Conflicts: # packages/block-library/src/embed/embed-preview.native.js # packages/components/src/sandbox/index.native.js
|
@ceyhun the PR is ready for another review, let me know if you would like me to expand any of the questions you had related to the injected JS code, thanks 🙇 ! |
ceyhun
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gave this another test run, also checking if the correct providers are enabled/disabled. LGTM ![]()

gutenberg-mobilePR: wordpress-mobile/gutenberg-mobile#3853Description
Implement the native version of the WordPress embed preview component.
How has this been tested?
Screenshots
iOS:

Android:


Types of changes
New feature
Checklist:
*.native.jsfiles for terms that need renaming or removal).