Skip to content
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

[RNMobile] [Embed block] Detect when an embeddable URL is pasted into an empty paragraph #35204

Merged
merged 33 commits into from Oct 13, 2021

Conversation

jd-alexander
Copy link
Contributor

@jd-alexander jd-alexander commented Sep 29, 2021

gutenberg-mobile PR wordpress-mobile/gutenberg-mobile#4048

Description

This PR implements functionality that allows the Paragraph block to detect when an embeddable URL is pasted to trigger the opening of an Embed-related bottom sheet. The options are as follows.

  1. Create embed - allows you to create an Embed block that represents the pasted URL.
  2. Create link - allows you to create a link within the paragraph block.

This functionality is supported by the Paragraph block and the following blocks, that contain a paragraph:

  • Media & Text block
  • Cover block

How has this been tested?

Paragraph block

Create Embed ✅

  1. Paste a URL into an empty paragraph block. eg, https://twitter.com/automattic/status/1395447061336711181?s=20
  2. Notice the bottom sheet is displayed with two options : Create embed and Create link.
  3. Select the Create embed option.
  4. Expect the paragraph to be transformed into an Embed block.
Click to see screenshots
Android iOS

Create link ✅

  1. Paste a URL into an empty paragraph block. eg, https://twitter.com/automattic/status/1395447061336711181?s=20
  2. Notice the bottom sheet is displayed with two options : Create embed and Create link.
  3. Select the Create link option.
  4. Expect a link within the paragraph block.
Click to see screenshots
Android iOS

No embed or link ✅

  1. Paste a URL at the end of paragraph within a paragraph block. eg, https://twitter.com/automattic/status/1395447061336711181?s=20
  2. Notice the bottom sheet is not displayed and the link is not active.
Click to see screenshots

Create Embed - Undo operations ❌

  1. Paste a URL into an empty paragraph block. eg, https://twitter.com/automattic/status/1395447061336711181?s=20
  2. Notice the bottom sheet is displayed with two options : Create embed and Create link.
  3. Select the Create embed option.
  4. Expect the paragraph to be transformed into an Embed block.
  5. Press the undo button.
  6. Notice the undo operations reloads the preview.
  7. Press undo again and the Embed block displays "Embed previews not yet available."
  8. Press undo again the block is finally removed from the canvas.
Click to see screenshots

Media & Text block

Create Embed ✅

  1. Paste a URL into an empty paragraph block within a Media & Text block. eg, https://twitter.com/automattic/status/1395447061336711181?s=20
  2. Notice the bottom sheet is displayed with two options : Create embed and Create link.
  3. Select the Create embed option.
  4. Expect the paragraph to be transformed into an Embed block.
Click to see screenshots
Android iOS

Create link ✅

  1. Paste a URL into an empty paragraph block within a Media & Text block. eg, https://twitter.com/automattic/status/1395447061336711181?s=20
  2. Notice the bottom sheet is displayed with two options : Create embed and Create link.
  3. Select the Create link option.
  4. Expect a link within the paragraph block.
Click to see screenshots
Android iOS

Types of changes

  1. The embed block's native transforms now contain the web transformations that are required for the Create embed feature.
  2. The rich-text utils now contain a function that will create a link within a paragraph.
  3. The __unstableEmbedURLOnPaste prop was passed to the native Paragraph block so that the underlying rich-text component can processed embed related URLs on paste.
  4. An EmbedHandlerPicker component was created that intercepts pasted embed URLs in the onPaste of the rich-text component and allows the user to create an embed or convert the URL to a link within the paragraph.

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • I've tested my changes with keyboard and screen readers.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.
  • I've updated all React Native files affected by any refactorings/renamings in this PR (please manually search all *.native.js files for terms that need renaming or removal).

@jd-alexander jd-alexander added [Block] Embed Affects the Embed Block Mobile App - i.e. Android or iOS Native mobile impl of the block editor. (Note: used in scripts, ping mobile folks to change) labels Sep 29, 2021
@github-actions
Copy link

github-actions bot commented Sep 29, 2021

Size Change: -3 B (0%)

Total Size: 1.07 MB

Filename Size Change
build/compose/index.min.js 10.4 kB -3 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 931 B
build/admin-manifest/index.min.js 1.09 kB
build/annotations/index.min.js 2.7 kB
build/api-fetch/index.min.js 2.21 kB
build/autop/index.min.js 2.08 kB
build/blob/index.min.js 459 B
build/block-directory/index.min.js 6.2 kB
build/block-directory/style-rtl.css 1.01 kB
build/block-directory/style.css 1.01 kB
build/block-editor/default-editor-styles-rtl.css 378 B
build/block-editor/default-editor-styles.css 378 B
build/block-editor/index.min.js 134 kB
build/block-editor/style-rtl.css 13.9 kB
build/block-editor/style.css 13.9 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 65 B
build/block-library/blocks/archives/style.css 65 B
build/block-library/blocks/audio/editor-rtl.css 58 B
build/block-library/blocks/audio/editor.css 58 B
build/block-library/blocks/audio/style-rtl.css 111 B
build/block-library/blocks/audio/style.css 111 B
build/block-library/blocks/audio/theme-rtl.css 125 B
build/block-library/blocks/audio/theme.css 125 B
build/block-library/blocks/block/editor-rtl.css 161 B
build/block-library/blocks/block/editor.css 161 B
build/block-library/blocks/button/editor-rtl.css 474 B
build/block-library/blocks/button/editor.css 474 B
build/block-library/blocks/button/style-rtl.css 600 B
build/block-library/blocks/button/style.css 600 B
build/block-library/blocks/buttons/editor-rtl.css 315 B
build/block-library/blocks/buttons/editor.css 315 B
build/block-library/blocks/buttons/style-rtl.css 370 B
build/block-library/blocks/buttons/style.css 370 B
build/block-library/blocks/calendar/style-rtl.css 207 B
build/block-library/blocks/calendar/style.css 207 B
build/block-library/blocks/categories/editor-rtl.css 84 B
build/block-library/blocks/categories/editor.css 83 B
build/block-library/blocks/categories/style-rtl.css 79 B
build/block-library/blocks/categories/style.css 79 B
build/block-library/blocks/code/style-rtl.css 90 B
build/block-library/blocks/code/style.css 90 B
build/block-library/blocks/code/theme-rtl.css 131 B
build/block-library/blocks/code/theme.css 131 B
build/block-library/blocks/columns/editor-rtl.css 206 B
build/block-library/blocks/columns/editor.css 205 B
build/block-library/blocks/columns/style-rtl.css 497 B
build/block-library/blocks/columns/style.css 496 B
build/block-library/blocks/cover/editor-rtl.css 546 B
build/block-library/blocks/cover/editor.css 547 B
build/block-library/blocks/cover/style-rtl.css 1.17 kB
build/block-library/blocks/cover/style.css 1.17 kB
build/block-library/blocks/embed/editor-rtl.css 488 B
build/block-library/blocks/embed/editor.css 488 B
build/block-library/blocks/embed/style-rtl.css 417 B
build/block-library/blocks/embed/style.css 417 B
build/block-library/blocks/embed/theme-rtl.css 124 B
build/block-library/blocks/embed/theme.css 124 B
build/block-library/blocks/file/editor-rtl.css 300 B
build/block-library/blocks/file/editor.css 300 B
build/block-library/blocks/file/style-rtl.css 255 B
build/block-library/blocks/file/style.css 255 B
build/block-library/blocks/file/view.min.js 322 B
build/block-library/blocks/freeform/editor-rtl.css 2.44 kB
build/block-library/blocks/freeform/editor.css 2.44 kB
build/block-library/blocks/gallery/editor-rtl.css 977 B
build/block-library/blocks/gallery/editor.css 982 B
build/block-library/blocks/gallery/style-rtl.css 1.6 kB
build/block-library/blocks/gallery/style.css 1.59 kB
build/block-library/blocks/gallery/theme-rtl.css 122 B
build/block-library/blocks/gallery/theme.css 122 B
build/block-library/blocks/group/editor-rtl.css 159 B
build/block-library/blocks/group/editor.css 159 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 114 B
build/block-library/blocks/heading/style.css 114 B
build/block-library/blocks/home-link/style-rtl.css 247 B
build/block-library/blocks/home-link/style.css 247 B
build/block-library/blocks/html/editor-rtl.css 332 B
build/block-library/blocks/html/editor.css 333 B
build/block-library/blocks/image/editor-rtl.css 731 B
build/block-library/blocks/image/editor.css 730 B
build/block-library/blocks/image/style-rtl.css 502 B
build/block-library/blocks/image/style.css 505 B
build/block-library/blocks/image/theme-rtl.css 124 B
build/block-library/blocks/image/theme.css 124 B
build/block-library/blocks/latest-comments/style-rtl.css 284 B
build/block-library/blocks/latest-comments/style.css 284 B
build/block-library/blocks/latest-posts/editor-rtl.css 137 B
build/block-library/blocks/latest-posts/editor.css 137 B
build/block-library/blocks/latest-posts/style-rtl.css 528 B
build/block-library/blocks/latest-posts/style.css 527 B
build/block-library/blocks/list/style-rtl.css 94 B
build/block-library/blocks/list/style.css 94 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 493 B
build/block-library/blocks/media-text/style.css 490 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 568 B
build/block-library/blocks/navigation-link/editor.css 570 B
build/block-library/blocks/navigation-link/style-rtl.css 94 B
build/block-library/blocks/navigation-link/style.css 94 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 300 B
build/block-library/blocks/navigation-submenu/editor.css 299 B
build/block-library/blocks/navigation-submenu/style-rtl.css 195 B
build/block-library/blocks/navigation-submenu/style.css 195 B
build/block-library/blocks/navigation-submenu/view.min.js 343 B
build/block-library/blocks/navigation/editor-rtl.css 1.71 kB
build/block-library/blocks/navigation/editor.css 1.71 kB
build/block-library/blocks/navigation/style-rtl.css 1.64 kB
build/block-library/blocks/navigation/style.css 1.64 kB
build/block-library/blocks/navigation/view.min.js 2.74 kB
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 377 B
build/block-library/blocks/page-list/editor.css 377 B
build/block-library/blocks/page-list/style-rtl.css 198 B
build/block-library/blocks/page-list/style.css 198 B
build/block-library/blocks/paragraph/editor-rtl.css 157 B
build/block-library/blocks/paragraph/editor.css 157 B
build/block-library/blocks/paragraph/style-rtl.css 273 B
build/block-library/blocks/paragraph/style.css 273 B
build/block-library/blocks/post-author/editor-rtl.css 210 B
build/block-library/blocks/post-author/editor.css 210 B
build/block-library/blocks/post-author/style-rtl.css 182 B
build/block-library/blocks/post-author/style.css 181 B
build/block-library/blocks/post-comments-form/style-rtl.css 140 B
build/block-library/blocks/post-comments-form/style.css 140 B
build/block-library/blocks/post-comments/style-rtl.css 360 B
build/block-library/blocks/post-comments/style.css 359 B
build/block-library/blocks/post-excerpt/editor-rtl.css 73 B
build/block-library/blocks/post-excerpt/editor.css 73 B
build/block-library/blocks/post-excerpt/style-rtl.css 69 B
build/block-library/blocks/post-excerpt/style.css 69 B
build/block-library/blocks/post-featured-image/editor-rtl.css 396 B
build/block-library/blocks/post-featured-image/editor.css 397 B
build/block-library/blocks/post-featured-image/style-rtl.css 156 B
build/block-library/blocks/post-featured-image/style.css 156 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 391 B
build/block-library/blocks/post-template/style.css 392 B
build/block-library/blocks/post-terms/style-rtl.css 73 B
build/block-library/blocks/post-terms/style.css 73 B
build/block-library/blocks/post-title/style-rtl.css 60 B
build/block-library/blocks/post-title/style.css 60 B
build/block-library/blocks/preformatted/style-rtl.css 103 B
build/block-library/blocks/preformatted/style.css 103 B
build/block-library/blocks/pullquote/editor-rtl.css 198 B
build/block-library/blocks/pullquote/editor.css 198 B
build/block-library/blocks/pullquote/style-rtl.css 378 B
build/block-library/blocks/pullquote/style.css 378 B
build/block-library/blocks/pullquote/theme-rtl.css 167 B
build/block-library/blocks/pullquote/theme.css 167 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 262 B
build/block-library/blocks/query-pagination/editor.css 255 B
build/block-library/blocks/query-pagination/style-rtl.css 234 B
build/block-library/blocks/query-pagination/style.css 231 B
build/block-library/blocks/query-title/editor-rtl.css 85 B
build/block-library/blocks/query-title/editor.css 85 B
build/block-library/blocks/query/editor-rtl.css 131 B
build/block-library/blocks/query/editor.css 132 B
build/block-library/blocks/quote/style-rtl.css 187 B
build/block-library/blocks/quote/style.css 187 B
build/block-library/blocks/quote/theme-rtl.css 220 B
build/block-library/blocks/quote/theme.css 222 B
build/block-library/blocks/rss/editor-rtl.css 202 B
build/block-library/blocks/rss/editor.css 204 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 165 B
build/block-library/blocks/search/editor.css 165 B
build/block-library/blocks/search/style-rtl.css 374 B
build/block-library/blocks/search/style.css 375 B
build/block-library/blocks/search/theme-rtl.css 64 B
build/block-library/blocks/search/theme.css 64 B
build/block-library/blocks/separator/editor-rtl.css 99 B
build/block-library/blocks/separator/editor.css 99 B
build/block-library/blocks/separator/style-rtl.css 250 B
build/block-library/blocks/separator/style.css 250 B
build/block-library/blocks/separator/theme-rtl.css 172 B
build/block-library/blocks/separator/theme.css 172 B
build/block-library/blocks/shortcode/editor-rtl.css 474 B
build/block-library/blocks/shortcode/editor.css 474 B
build/block-library/blocks/site-logo/editor-rtl.css 769 B
build/block-library/blocks/site-logo/editor.css 769 B
build/block-library/blocks/site-logo/style-rtl.css 165 B
build/block-library/blocks/site-logo/style.css 165 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 84 B
build/block-library/blocks/site-title/editor.css 84 B
build/block-library/blocks/social-link/editor-rtl.css 165 B
build/block-library/blocks/social-link/editor.css 165 B
build/block-library/blocks/social-links/editor-rtl.css 812 B
build/block-library/blocks/social-links/editor.css 811 B
build/block-library/blocks/social-links/style-rtl.css 1.3 kB
build/block-library/blocks/social-links/style.css 1.3 kB
build/block-library/blocks/spacer/editor-rtl.css 307 B
build/block-library/blocks/spacer/editor.css 307 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 471 B
build/block-library/blocks/table/editor.css 472 B
build/block-library/blocks/table/style-rtl.css 481 B
build/block-library/blocks/table/style.css 481 B
build/block-library/blocks/table/theme-rtl.css 188 B
build/block-library/blocks/table/theme.css 188 B
build/block-library/blocks/tag-cloud/style-rtl.css 146 B
build/block-library/blocks/tag-cloud/style.css 146 B
build/block-library/blocks/template-part/editor-rtl.css 636 B
build/block-library/blocks/template-part/editor.css 635 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/term-description/editor-rtl.css 90 B
build/block-library/blocks/term-description/editor.css 90 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 87 B
build/block-library/blocks/verse/style.css 87 B
build/block-library/blocks/video/editor-rtl.css 571 B
build/block-library/blocks/video/editor.css 572 B
build/block-library/blocks/video/style-rtl.css 173 B
build/block-library/blocks/video/style.css 173 B
build/block-library/blocks/video/theme-rtl.css 124 B
build/block-library/blocks/video/theme.css 124 B
build/block-library/common-rtl.css 815 B
build/block-library/common.css 812 B
build/block-library/editor-rtl.css 9.79 kB
build/block-library/editor.css 9.79 kB
build/block-library/index.min.js 148 kB
build/block-library/reset-rtl.css 474 B
build/block-library/reset.css 474 B
build/block-library/style-rtl.css 10.4 kB
build/block-library/style.css 10.4 kB
build/block-library/theme-rtl.css 665 B
build/block-library/theme.css 669 B
build/block-serialization-default-parser/index.min.js 1.09 kB
build/block-serialization-spec-parser/index.min.js 2.79 kB
build/blocks/index.min.js 45.7 kB
build/components/index.min.js 217 kB
build/components/style-rtl.css 15.2 kB
build/components/style.css 15.2 kB
build/core-data/index.min.js 12.4 kB
build/customize-widgets/index.min.js 11.2 kB
build/customize-widgets/style-rtl.css 1.5 kB
build/customize-widgets/style.css 1.49 kB
build/data-controls/index.min.js 614 B
build/data/index.min.js 7.1 kB
build/date/index.min.js 31.5 kB
build/deprecated/index.min.js 428 B
build/dom-ready/index.min.js 304 B
build/dom/index.min.js 4.46 kB
build/edit-navigation/index.min.js 15.3 kB
build/edit-navigation/style-rtl.css 3.76 kB
build/edit-navigation/style.css 3.76 kB
build/edit-post/classic-rtl.css 492 B
build/edit-post/classic.css 494 B
build/edit-post/index.min.js 29.3 kB
build/edit-post/style-rtl.css 7.22 kB
build/edit-post/style.css 7.22 kB
build/edit-site/index.min.js 29.7 kB
build/edit-site/style-rtl.css 5.54 kB
build/edit-site/style.css 5.54 kB
build/edit-widgets/index.min.js 15.7 kB
build/edit-widgets/style-rtl.css 4.12 kB
build/edit-widgets/style.css 4.13 kB
build/editor/index.min.js 37.5 kB
build/editor/style-rtl.css 3.78 kB
build/editor/style.css 3.77 kB
build/element/index.min.js 3.17 kB
build/escape-html/index.min.js 517 B
build/format-library/index.min.js 5.93 kB
build/format-library/style-rtl.css 571 B
build/format-library/style.css 571 B
build/hooks/index.min.js 1.55 kB
build/html-entities/index.min.js 424 B
build/i18n/index.min.js 3.6 kB
build/is-shallow-equal/index.min.js 501 B
build/keyboard-shortcuts/index.min.js 1.72 kB
build/keycodes/index.min.js 1.3 kB
build/list-reusable-blocks/index.min.js 1.85 kB
build/list-reusable-blocks/style-rtl.css 838 B
build/list-reusable-blocks/style.css 838 B
build/media-utils/index.min.js 2.92 kB
build/notices/index.min.js 845 B
build/nux/index.min.js 2.03 kB
build/nux/style-rtl.css 747 B
build/nux/style.css 743 B
build/plugins/index.min.js 1.83 kB
build/primitives/index.min.js 921 B
build/priority-queue/index.min.js 582 B
build/react-i18n/index.min.js 671 B
build/redux-routine/index.min.js 2.63 kB
build/reusable-blocks/index.min.js 2.19 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/rich-text/index.min.js 10.6 kB
build/server-side-render/index.min.js 1.52 kB
build/shortcode/index.min.js 1.48 kB
build/token-list/index.min.js 562 B
build/url/index.min.js 1.74 kB
build/viewport/index.min.js 1.02 kB
build/warning/index.min.js 248 B
build/widgets/index.min.js 7.11 kB
build/widgets/style-rtl.css 1.16 kB
build/widgets/style.css 1.16 kB
build/wordcount/index.min.js 1.04 kB

compressed-size-action

@jd-alexander jd-alexander marked this pull request as ready for review September 30, 2021 06:07
Copy link
Contributor

@fluiddot fluiddot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Paste URL on different blocks

I've done some tests pasting a URL on different blocks and I found out that the embed block could be also created from the following blocks:

  • Cover block
  • Group block
  • List block

Cover block

Works as expected ✅, although I noticed that the embed block extends the cover frame when previewed on mobile mode, however, I think this is a rendering issue that comes from WordPress.

Click to see screenshots
Editor Preview

Group block

Works as expected ✅ .

Click to see screenshots
Editor Preview

List block

Works as expected ✅ .

Click to see screenshots
Before pasting URL After pasting URL

Handle non-empty paragraph block

I tried to reproduce the test case No embed or link as described in the PR description and in my case, instead of pasting the URL as plain text, it's automatically creating an embed block (see attached video).

Click to see video
embed-block-paste-url-native.mp4

I'm wondering whether is expected that the picker shows up on a non-empty paragraph when a URL is pasted. I checked this behavior in the web version and in that case, an embed block is created independently of the content of the Paragraph.

Click to see video
embed-block-paste-url.mp4

Nevertheless, the task states:

Should not to trigger when pasting in the middle of a text

From my POV, I think it makes more sense to only create an embed block on empty paragraphs but I'd double-check with the rest of the team if we're ok diverging from the web version's flow. If the embed block creation should only be applied to empty paragraph blocks, it would be great that we review the logic to prevent creating them automatically.

Undo operations

I did a quick test on the web version and the undo operation doesn't work as expected either. Looks like the undo/redo operations are not working properly in the embed block in general, I'd open a new issue as a follow-up but I wouldn't block this PR due to this.

Click to see video
embed-block-paste-url-undo.mp4

Comment on lines 32 to 37
function onPickerSelect( value ) {
const selectedItem = pickerOptions.find(
( item ) => item.value === value
);
selectedItem.onSelect();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if wrapping this callback with useCallback would prevent potential re-renders 🤔 .

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! Just to verify, once this is done, I would need to utilize some form of tracing mechanism to verify the number of re-renders taking place. What tool do you utilize to do this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fluiddot I did this operation here be43155 579def2

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! Just to verify, once this is done, I would need to utilize some form of tracing mechanism to verify the number of re-renders taking place. What tool do you utilize to do this?

I completely missed this comment, sorry. I usually measure renders by adding logs but I think you could use Flipper and the React devtools that are already included for this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a quick test and found out that doesn't prevent re-renders of the Picker component because the parent is the one that forces the render. The only way I managed to prevent extra renders is by memoizing the component (see attached diff patch). Nevertheless, it's a good practice to wrap callbacks passed in props with useCallback, so let's stick with this change 👍 .

In the following screenshots, you can see that independently on using useCallback it renders the Picker component three times:

Without useCallback With useCallback
no-callback with-callback
Click here to see the diff patch
diff --git a/packages/block-editor/src/components/rich-text/embed-handler-picker.native.js b/packages/block-editor/src/components/rich-text/embed-handler-picker.native.js
index 686e55b241a2aca54102fd5ec8974f585e0e8fa9..c4ffc0894e38a74b17effbe6710ae145c04329bd 100644
--- a/packages/block-editor/src/components/rich-text/embed-handler-picker.native.js
+++ b/packages/block-editor/src/components/rich-text/embed-handler-picker.native.js
@@ -10,6 +10,7 @@ import {
 	forwardRef,
 	useRef,
 	useImperativeHandle,
+	memo,
 	useCallback,
 } from '@wordpress/element';
 import { Picker } from '@wordpress/components';
@@ -30,7 +31,7 @@ const DEFAULT_PICKER_OPTIONS = [
 	},
 ];
 
-export default forwardRef( ( {}, ref ) => {
+const EmbedHandlerPicker = forwardRef( ( {}, ref ) => {
 	const pickerRef = useRef();
 	const pickerOptions = useRef( DEFAULT_PICKER_OPTIONS ).current;
 
@@ -62,3 +63,4 @@ export default forwardRef( ( {}, ref ) => {
 		/>
 	);
 } );
+export default memo( EmbedHandlerPicker );

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a quick test and found out that doesn't prevent re-renders of the Picker component because the parent is the one that forces the render. The only way I managed to prevent extra renders is by memoizing the component (see attached diff patch). Nevertheless, it's a good practice to wrap callbacks passed in props with useCallback, so let's stick with this change 👍 .

Understood 🙇🏾

In the following screenshots, you can see that independently on using useCallback it renders the Picker component three times:

Indeed! I was able to fire up DevTools and observe the profiler in action.

@guarani
Copy link
Contributor

guarani commented Sep 30, 2021

👋 Hi @jd-alexander, let me know if you'd like a review from me. I think I was requested automatically here as code owner, but not sure if you need my review here since @fluiddot is also reviewing. No problem either way, just seeking clarification.

@jd-alexander
Copy link
Contributor Author

👋 Hi @jd-alexander, let me know if you'd like a review from me. I think I was requested automatically here as code owner, but not sure if you need my review here since @fluiddot is also reviewing. No problem either way, just seeking clarification.

Hey @guarani @fluiddot is covering the reviews here so we are good. Thanks for checking in 😎

@jd-alexander
Copy link
Contributor Author

Thanks for the thorough tests and feedback @fluidot 

💯

Handle non-empty paragraph block


I tried to reproduce the test case No embed or link as described in the PR description and in my case, instead of pasting the URL as plain text, it's automatically creating an embed block (see attached video).




Interesting. When I try to reproduce this I still end up getting the link as a text. Even on the web, I get the text sometimes but other times an Embed block is created.

I'm wondering whether is expected that the picker shows up on a non-empty paragraph when a URL is pasted. I checked this behavior in the web version and in that case, an embed block is created independently of the content of the Paragraph.



From my POV, I think it makes more sense to only create an embed block on empty paragraphs but I'd double-check with the rest of the team if we're ok diverging from the web version's flow. If the embed block creation should only be applied to empty paragraph blocks, it would be great that we review the logic to prevent creating them automatically.



Yeah, now looking at the issue/specs I am thinking that may be the best solution as well. I will take a look at the logic that is causing the split to take place and do the disabling there if an embed paste is taking place and the paragraph is non-empty.




Undo operations







I did a quick test on the web version and the undo operation doesn't work as expected either. Looks like the undo/redo operations are not working properly in the embed block in general, I'd open a new issue as a follow-up but I wouldn't block this PR due to this.




I am going to check if there are any existing issues because this bug seems pretty critical to me. But yes, I agree that it should be a blocker since it happening there as well. We should leave a note when the functionality is being tested so no one is caught off guard with the behavior if they attempt to make undo or redo operations.

@jd-alexander
Copy link
Contributor Author

jd-alexander commented Oct 5, 2021

  • I created an issue to track the undo/redo issue of the Embed block Embed block: Undo & redo operations do not work as expected. #35335
  • When you paste a URL it's now visible within the paragraph 1b41af9
  • Only when a paragraph is empty will there be an Embed bottom sheet - I am wondering if this operation can be simplified with an isEmpty I tried using it but got errors when text was empty. 8f38899
  • Utilized useCallback to lessen picker re-renders be43155 579def2
  • I didn't extract the onPaste logic because it would indeed require a lot of paths to be tested to verify that the refactor works as expected, and I think that's out of the scope of the PR right now.

Copy link
Contributor

@fluiddot fluiddot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work @jd-alexander 💯 .

Embed block automatically created on some blocks 🟢

I confirm that this issue is no longer present 🎊 . However, I noticed a new one related to pasting an URL copied from non-paragraph blocks.

Paste an URL copied from a non-paragraph block ❌

When pasting an URL copied from a non-paragraph block like the Heading block, the picker is shown but when selecting the "Create embed" option it creates the block of the copied URL instead of an embed block.

You can reproduce the issue with the following steps:

  1. Have an embeddable URL in the clipboard
  2. Add a Heading block
  3. Paste the URL
  4. Observe that the URL is pasted as plain text
  5. Select and copy the URL from the Heading block
  6. Add a Paragraph block
  7. Paste the URL
  8. Observe that the embed handler picker is shown
  9. Tap on the "Create embed" option
  10. Observe that a Heading block with URL is created instead an embed block
Click here to display the video
embed-block-paste-url-non-paragraph-block.mp4

I think we would need to refactor the onPaste function to handle this case. I checked how this behavior works in the web editor, looks like it's expected that we paste the block of the copied URL no the embed block, so we have to figure out a way to prevent displaying the embed handler picker in this case.

@jd-alexander
Copy link
Contributor Author

@fluiddot I added the fixes here. I will do another run-through of tests later to confirm that nothing was broken.

Comment on lines 478 to 483
const derivedBlock = content[ 0 ];
const { name } = derivedBlock;

// When an URL is pasted in an empty paragraph then the EmbedHandlerPicker should showcase options allowing the transformation of that URL
// into either an Embed block or a link within the target paragraph. If the paragraph is non-empty, the URL is pasted as text.
if ( __unstableEmbedURLOnPaste && name === 'core/embed' ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Android, this logic fails because it is not resolving the pasted URL as a block. The content that is parsed by the PasteHandler is a string. Further details on the differences in the clipboard behavior as expounded on this comment #35204 (comment) refactoring to accommodate this case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should handle the else case of this if block, probably we could call the same as in line 504:

onReplace( content, content.length - 1, -1 );

This way if we copy, for example, a Heading block that contains an URL, it will paste that block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@carlosgprim we may have to tag-team on resolving the final Heading block issue with the Embed Paste URL. This is what is taking place.

There are cases where copying a URL from a Heading block results in a string when pasted instead of being pasted as a block when it’s processed by the PasteHandler therefore the bottom sheet is still shown. I am not sure how we can figure out the source of the copy/paste if only a string is returned. Still investigating.

return;
}

mode = 'BLOCKS';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should preserve this line to be executed (if the conditions of line 448 are satisfied) because it's used in the pasteHandler call below. Maybe this has to do with the issue you encountered in this comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes. Good eye!

Comment on lines 478 to 483
const derivedBlock = content[ 0 ];
const { name } = derivedBlock;

// When an URL is pasted in an empty paragraph then the EmbedHandlerPicker should showcase options allowing the transformation of that URL
// into either an Embed block or a link within the target paragraph. If the paragraph is non-empty, the URL is pasted as text.
if ( __unstableEmbedURLOnPaste && name === 'core/embed' ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should handle the else case of this if block, probably we could call the same as in line 504:

onReplace( content, content.length - 1, -1 );

This way if we copy, for example, a Heading block that contains an URL, it will paste that block.

…/gutenberg into rnmobile/embed-block-paste-url

# Conflicts:
#	packages/block-editor/src/components/rich-text/index.native.js
@fluiddot
Copy link
Contributor

While testing I identified the following test cases that would be great to include and run them with these changes:

Paste URL with HTML tags

Preparation:

  1. Add a paragraph block
  2. Paste the URL into the paragraph block
  3. Copy the URL

At this point, the clipboard content will be: <p>https://www.youtube.com/watch?v=ssfHW5lwFZg</p>

Empty paragraph

  1. Add a paragraph block
  2. Paste the URL by following the preparation steps
  3. Observe that the embed handler picker shows up
  4. Observe that tapping on "Create embed" creates an embed block

Non-empty paragraph

  1. Add a paragraph
  2. Add some text and move the caret to the end of the content
  3. Paste the URL by following the preparation steps
  4. Observe that the embed handler picker doesn't show up
  5. Observe that the URL is pasted as plain text

Paste URL plain text

Preparation:

  1. Add an embed block
  2. Set a valid URL
  3. Once the block is ready, tap on the Edit URL button located in the toolbar (✏️ icon)
  4. Copy the URL from the link field

At this point, the clipboard content will be: https://www.youtube.com/watch?v=ssfHW5lwFZg

Empty paragraph

  1. Add a paragraph block
  2. Paste the URL by following the preparation steps
  3. Observe that the embed handler picker shows up
  4. Observe that tapping on "Create embed" creates an embed block

Non-empty paragraph

  1. Add a paragraph block
  2. Add some text and move the caret to the end of the content
  3. Paste the URL by following the preparation steps
  4. Observe that the embed handler picker doesn't show up
  5. Observe that the URL is pasted as plain text

Paste URL from block

Preparation:

  1. Add a heading block
  2. Paste the URL into the heading block
  3. Copy the URL

At this point, the clipboard content will be: <h2>https://www.youtube.com/watch?v=ssfHW5lwFZg</h2>

Empty paragraph

  1. Add a paragraph block
  2. Paste the URL by following the preparation steps
  3. Observe that the paragraph block is replaced by the copied heading block containing the URL

Non-empty paragraph

  1. Add a paragraph block
  2. Add some text and move the caret to the end of the content
  3. Paste the URL by following the preparation steps
  4. Observe that the paragraph block is split and appends the copied heading block containing the URL

@fluiddot
Copy link
Contributor

fluiddot commented Oct 11, 2021

@jd-alexander heads up that I pushed a small refactor in this commit related to the paste logic so it covers the test cases I mentioned in the previous comment 🎊 .

@jd-alexander
Copy link
Contributor Author

Thanks for the collaboration to get this to the finish line @fluiddot I will run your test cases along with the previous test cases I had as well.

@jd-alexander
Copy link
Contributor Author

@fluiddot as you will see in the latest commit, I had to remove the __unstableEmbedURLOnPaste from the mode logic so that the Heading block would be processed since the __unstableEmbedURLOnPaste isn't on the RichText instance within the Heading block. Without doing this, pastes wouldn't be reflected.

@jd-alexander
Copy link
Contributor Author

Testing round on iPhone 11 & Google Pixel 4 XL, here are the results:

Paragraph block

  • Create Embed ✅
  • Create link ✅
  • No embed or link ✅

Paste URL on other blocks

  • Media & Text block ✅

  • Cover block 🟡 - Embed works here. I have noticed rendering issues with WordPress.

  • Group block ✅

  • Column block ✅

Embed block is not created on the blocks below

The following blocks are derivatives of the RichText component hence, I tested them to verify no regressions had occurred.

  • Heading block ✅
  • Quote block ✅
  • List block ✅
  • Preformatted block ✅
  • Verse ✅
  • Search ✅
  • File block ✅

@fluiddot
Copy link
Contributor

@fluiddot as you will see in the latest commit, I had to remove the __unstableEmbedURLOnPaste from the mode logic so that the Heading block would be processed since the __unstableEmbedURLOnPaste isn't on the RichText instance within the Heading block. Without doing this, pastes wouldn't be reflected.

I'm wondering if it's really necessary to remove the __unstableEmbedURLOnPaste as in the web editor we're including it 🤔 :

if (
__unstableEmbedURLOnPaste &&
isEmpty( value ) &&
isURL( plainText.trim() )
) {
mode = 'BLOCKS';
}

I think the culprit here is the ! isPastedURL condition that was recently added here.

@fluiddot
Copy link
Contributor

@fluiddot as you will see in the latest commit, I had to remove the __unstableEmbedURLOnPaste from the mode logic so that the Heading block would be processed since the __unstableEmbedURLOnPaste isn't on the RichText instance within the Heading block. Without doing this, pastes wouldn't be reflected.

I'm wondering if it's really necessary to remove the __unstableEmbedURLOnPaste as in the web editor we're including it 🤔 :

if (
__unstableEmbedURLOnPaste &&
isEmpty( value ) &&
isURL( plainText.trim() )
) {
mode = 'BLOCKS';
}

I think the culprit here is the ! isPastedURL condition that was recently added here.

@jd-alexander I confirm that it was caused by that condition because after removing it, the issue couldn't be reproduced. I'd like to suggest making the following modifications, I tested them locally and all the test cases succeed 🟢 :

diff --git a/packages/block-editor/src/components/rich-text/index.native.js b/packages/block-editor/src/components/rich-text/index.native.js
index be05f24b2b62cdc948626f1161edc3ee778edb47..bdd49831331212f83aa07f29e92ec47dfe505ffd 100644
--- a/packages/block-editor/src/components/rich-text/index.native.js
+++ b/packages/block-editor/src/components/rich-text/index.native.js
@@ -451,7 +451,11 @@ function RichTextWrapper(
 						createLinkInParagraph( plainText.trim(), onReplace ),
 				} );
 
-			if ( isPastedURL ) {
+			if (
+				__unstableEmbedURLOnPaste &&
+				isEmpty( value ) &&
+				isPastedURL
+			) {
 				mode = 'BLOCKS';
 			}
 
@@ -463,7 +467,7 @@ function RichTextWrapper(
 				preserveWhiteSpace,
 			} );
 
-			if ( typeof content === 'string' && ! isPastedURL ) {
+			if ( typeof content === 'string' ) {
 				let valueToInsert = create( { html: content } );
 
 				addActiveFormats( valueToInsert, activeFormats );

Note that I added the same conditions of the web version for setting the BLOCKS value in the mode variable. I think it makes sense to only force the blocks mode when the __unstableEmbedURLOnPaste flag is enabled and if we're pasting a URL on an empty block.

@jd-alexander
Copy link
Contributor Author

@fluiddot as you will see in the latest commit, I had to remove the __unstableEmbedURLOnPaste from the mode logic so that the Heading block would be processed since the __unstableEmbedURLOnPaste isn't on the RichText instance within the Heading block. Without doing this, pastes wouldn't be reflected.

I'm wondering if it's really necessary to remove the __unstableEmbedURLOnPaste as in the web editor we're including it 🤔 :

if (
__unstableEmbedURLOnPaste &&
isEmpty( value ) &&
isURL( plainText.trim() )
) {
mode = 'BLOCKS';
}

I think the culprit here is the ! isPastedURL condition that was recently added here.

@jd-alexander I confirm that it was caused by that condition because after removing it, the issue couldn't be reproduced. I'd like to suggest making the following modifications, I tested them locally and all the test cases succeed 🟢 :

diff --git a/packages/block-editor/src/components/rich-text/index.native.js b/packages/block-editor/src/components/rich-text/index.native.js
index be05f24b2b62cdc948626f1161edc3ee778edb47..bdd49831331212f83aa07f29e92ec47dfe505ffd 100644
--- a/packages/block-editor/src/components/rich-text/index.native.js
+++ b/packages/block-editor/src/components/rich-text/index.native.js
@@ -451,7 +451,11 @@ function RichTextWrapper(
 						createLinkInParagraph( plainText.trim(), onReplace ),
 				} );
 
-			if ( isPastedURL ) {
+			if (
+				__unstableEmbedURLOnPaste &&
+				isEmpty( value ) &&
+				isPastedURL
+			) {
 				mode = 'BLOCKS';
 			}
 
@@ -463,7 +467,7 @@ function RichTextWrapper(
 				preserveWhiteSpace,
 			} );
 
-			if ( typeof content === 'string' && ! isPastedURL ) {
+			if ( typeof content === 'string' ) {
 				let valueToInsert = create( { html: content } );
 
 				addActiveFormats( valueToInsert, activeFormats );

Note that I added the same conditions of the web version for setting the BLOCKS value in the mode variable. I think it makes sense to only force the blocks mode when the __unstableEmbedURLOnPaste flag is enabled and if we're pasting a URL on an empty block.

Ah, that makes sense! I will give it a test locally so that we can merge this 🙇🏾

Copy link
Contributor

@fluiddot fluiddot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🎊 !

I tested the following test cases:

Paragraph block

  • Create Embed ✅
  • Create link ✅
  • No embed or link ✅

Paste URL on other blocks

  • Media & Text block ✅
  • Cover block 🟡 - (known rendering issues with WordPress)
  • Group block ✅
  • Column block ✅

Embed block is not created on the blocks below

  • Heading block ✅
  • Quote block ✅
  • List block ✅
  • Preformatted block ✅
  • Verse ✅
  • Search ✅
  • File block ✅

Paste URL with HTML tags

  • Empty paragraph ✅
  • Non-empty paragraph ✅

Paste URL plain text

  • Empty paragraph ✅
  • Non-empty paragraph ✅

Paste URL from block

  • Empty paragraph ✅
  • Non-empty paragraph ✅

Tested on Simulator - iPhone 12 Pro Max (iOS 14.5) and Samsung Galaxy S20 FE 5G (Android 10).

@jd-alexander
Copy link
Contributor Author

Thanks for testing @fluiddot I also did a round of tests to verify that the paste functionality was not broken across the editor.

I tested the following test cases:

Paragraph block

  • Create Embed ✅
  • Create link ✅
  • No embed or link ✅

Paste URL on other blocks

  • Media & Text block ✅
  • Cover block 🟡 - (known rendering issues with WordPress)
  • Group block ✅
  • Column block ✅

Embed block is not created on the blocks below

  • Heading block ✅
  • Quote block ✅
  • List block ✅
  • Preformatted block ✅
  • Verse ✅
  • Search ✅
  • File block ✅

Paste URL with HTML tags

  • Empty paragraph ✅
  • Non-empty paragraph ✅

Paste URL plain text

  • Empty paragraph ✅
  • Non-empty paragraph ✅

Paste URL from block

  • Empty paragraph ✅
  • Non-empty paragraph ✅

Tested on Simulator - iPhone 11 (iOS 14.4) and Google Pixel 4 XL (Android 10).

@jd-alexander jd-alexander merged commit 5430baf into trunk Oct 13, 2021
@jd-alexander jd-alexander deleted the rnmobile/embed-block-paste-url branch October 13, 2021 21:16
@github-actions github-actions bot added this to the Gutenberg 11.8 milestone Oct 13, 2021
@fluiddot
Copy link
Contributor

@jd-alexander heads up that I updated the PR's description to expand the info related to which blocks this functionality is also available.

New description:

This functionality is supported by the Paragraph block and the following blocks, that contain a paragraph:

  • Media & Text block
  • Cover block

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Embed Affects the Embed Block Mobile App - i.e. Android or iOS Native mobile impl of the block editor. (Note: used in scripts, ping mobile folks to change)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants