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] Refactor draggable logic and introduce DraggableTrigger component #40406

Conversation

fluiddot
Copy link
Contributor

@fluiddot fluiddot commented Apr 18, 2022

What?

Refactors the draggable logic.

Why?

The reason for the refactor is mainly to address the following issues spotted during testing:

  • Long-press gesture is not working for buttons located within the block's toolbar.
  • Long-press gesture is disabled when editing a text on iOS (reference).

How?

The implementation is described in the following sections:

Trigger long-press gesture from block component

Originally, the long-press gesture was triggered from a wrapper component over the block list. This was preventing other long-press gestures located in other components to not being triggered, like the inserter button or the up/down arrow buttons within the block's toolbar.

In order to address this issue, a new component named DraggableTrigger has been added to handle the long-press gesture. In the following commits you can see the related changes:

With the introduction of this component, we can now send the clientId of the dragged block through the dragging events, so this also implied updating and simplifying the logic of the BlockDraggable component and its wrapper.

As an additional note, the BlockDraggable component is now rendered in the BlockListBlock component, so the dragging gesture doesn't interfere with the block's toolbar (reference).

Testing Instructions

Drag an unselected block

  1. Open a post/page in the app.
  2. Add several blocks, including text blocks like the Paragraph block.
  3. Long-press on a block that is not selected to activate the dragging mode.
  4. Observe that the dragging mode is enabled and that the block can be dragged around the block list.
  5. Drop the block and observe that the block gets automatically selected.

Drag a selected block

  1. Open a post/page in the app.
  2. Add several blocks, including text blocks like the Paragraph block.
  3. Select a block.
  4. Long-press on the selected block to activate the dragging mode.
    NOTE: Only the block's content will be draggable, the block's toolbar won't enable the dragging mode.
  5. Observe that the dragging mode is enabled and that the block can be dragged around the block list.
  6. Drop the block and observe that the block is still selected.

Long-press gesture still works when editing a text

  1. Open a post/page in the app.
  2. Add several blocks, including text blocks like the Paragraph block.
  3. Tap on a text block to edit its content.
    NOTE: Check that the text input is focused (i.e. the text editing is enabled).
  4. Long-press on the content.
  5. Observe that the long-press gesture works and that doesn't trigger the dragging mode.

Long-press gesture on toolbar buttons

  1. Open a post/page in the app.
  2. Long-press on the inserter button (i.e. ➕ button).
  3. Observe that a picker is displayed and presents extra options in relation to the target position when inserting the block.
  4. Select a block.
  5. Long-press on both up and down arrow buttons.
  6. Observe that a picker is displayed and presents the expected options.

Screenshots or screencast

Android iOS
android-drag-and-drop.mp4
ios-drag-and-drop.MP4

@fluiddot fluiddot added [Feature] Drag and Drop Drag and drop functionality when working with blocks Mobile App - i.e. Android or iOS Native mobile impl of the block editor. (Note: used in scripts, ping mobile folks to change) labels Apr 18, 2022
@github-actions
Copy link

github-actions bot commented Apr 18, 2022

Size Change: 0 B

Total Size: 1.23 MB

ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 993 B
build/admin-manifest/index.min.js 1.24 kB
build/annotations/index.min.js 2.77 kB
build/api-fetch/index.min.js 2.27 kB
build/autop/index.min.js 2.15 kB
build/blob/index.min.js 487 B
build/block-directory/index.min.js 6.51 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 150 kB
build/block-editor/style-rtl.css 15.2 kB
build/block-editor/style.css 15.2 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 150 B
build/block-library/blocks/audio/editor.css 150 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/avatar/editor-rtl.css 116 B
build/block-library/blocks/avatar/editor.css 116 B
build/block-library/blocks/avatar/style-rtl.css 59 B
build/block-library/blocks/avatar/style.css 59 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 445 B
build/block-library/blocks/button/editor.css 445 B
build/block-library/blocks/button/style-rtl.css 560 B
build/block-library/blocks/button/style.css 560 B
build/block-library/blocks/buttons/editor-rtl.css 292 B
build/block-library/blocks/buttons/editor.css 292 B
build/block-library/blocks/buttons/style-rtl.css 275 B
build/block-library/blocks/buttons/style.css 275 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 103 B
build/block-library/blocks/code/style.css 103 B
build/block-library/blocks/code/theme-rtl.css 124 B
build/block-library/blocks/code/theme.css 124 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 406 B
build/block-library/blocks/columns/style.css 406 B
build/block-library/blocks/comment-author-avatar/editor-rtl.css 125 B
build/block-library/blocks/comment-author-avatar/editor.css 125 B
build/block-library/blocks/comment-template/style-rtl.css 127 B
build/block-library/blocks/comment-template/style.css 127 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-query-loop/editor-rtl.css 95 B
build/block-library/blocks/comments-query-loop/editor.css 95 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.56 kB
build/block-library/blocks/cover/style.css 1.56 kB
build/block-library/blocks/embed/editor-rtl.css 293 B
build/block-library/blocks/embed/editor.css 293 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 353 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 961 B
build/block-library/blocks/gallery/editor.css 964 B
build/block-library/blocks/gallery/style-rtl.css 1.51 kB
build/block-library/blocks/gallery/style.css 1.51 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 333 B
build/block-library/blocks/group/editor.css 333 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 76 B
build/block-library/blocks/heading/style.css 76 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 529 B
build/block-library/blocks/image/style.css 535 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 199 B
build/block-library/blocks/latest-posts/editor.css 198 B
build/block-library/blocks/latest-posts/style-rtl.css 447 B
build/block-library/blocks/latest-posts/style.css 446 B
build/block-library/blocks/list/style-rtl.css 88 B
build/block-library/blocks/list/style.css 88 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 708 B
build/block-library/blocks/navigation-link/editor.css 706 B
build/block-library/blocks/navigation-link/style-rtl.css 115 B
build/block-library/blocks/navigation-link/style.css 115 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 299 B
build/block-library/blocks/navigation-submenu/editor.css 299 B
build/block-library/blocks/navigation-submenu/view.min.js 375 B
build/block-library/blocks/navigation/editor-rtl.css 2.03 kB
build/block-library/blocks/navigation/editor.css 2.04 kB
build/block-library/blocks/navigation/style-rtl.css 1.93 kB
build/block-library/blocks/navigation/style.css 1.92 kB
build/block-library/blocks/navigation/view-modal.min.js 2.65 kB
build/block-library/blocks/navigation/view.min.js 395 B
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 363 B
build/block-library/blocks/page-list/editor.css 363 B
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 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 260 B
build/block-library/blocks/paragraph/style.css 260 B
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-comments-form/style-rtl.css 446 B
build/block-library/blocks/post-comments-form/style.css 446 B
build/block-library/blocks/post-comments/style-rtl.css 521 B
build/block-library/blocks/post-comments/style.css 521 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 721 B
build/block-library/blocks/post-featured-image/editor.css 721 B
build/block-library/blocks/post-featured-image/style-rtl.css 153 B
build/block-library/blocks/post-featured-image/style.css 153 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 323 B
build/block-library/blocks/post-template/style.css 323 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 80 B
build/block-library/blocks/post-title/style.css 80 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 370 B
build/block-library/blocks/pullquote/style.css 370 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 221 B
build/block-library/blocks/query-pagination/editor.css 211 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/editor-rtl.css 369 B
build/block-library/blocks/query/editor.css 369 B
build/block-library/blocks/quote/style-rtl.css 213 B
build/block-library/blocks/quote/style.css 213 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/read-more/style-rtl.css 132 B
build/block-library/blocks/read-more/style.css 132 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 397 B
build/block-library/blocks/search/style.css 398 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 140 B
build/block-library/blocks/separator/editor.css 140 B
build/block-library/blocks/separator/style-rtl.css 233 B
build/block-library/blocks/separator/style.css 233 B
build/block-library/blocks/separator/theme-rtl.css 194 B
build/block-library/blocks/separator/theme.css 194 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 759 B
build/block-library/blocks/site-logo/editor.css 759 B
build/block-library/blocks/site-logo/style-rtl.css 181 B
build/block-library/blocks/site-logo/style.css 181 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 177 B
build/block-library/blocks/social-link/editor.css 177 B
build/block-library/blocks/social-links/editor-rtl.css 674 B
build/block-library/blocks/social-links/editor.css 673 B
build/block-library/blocks/social-links/style-rtl.css 1.37 kB
build/block-library/blocks/social-links/style.css 1.36 kB
build/block-library/blocks/spacer/editor-rtl.css 332 B
build/block-library/blocks/spacer/editor.css 332 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 504 B
build/block-library/blocks/table/editor.css 504 B
build/block-library/blocks/table/style-rtl.css 625 B
build/block-library/blocks/table/style.css 625 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 226 B
build/block-library/blocks/tag-cloud/style.css 227 B
build/block-library/blocks/template-part/editor-rtl.css 149 B
build/block-library/blocks/template-part/editor.css 149 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/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 993 B
build/block-library/common.css 990 B
build/block-library/editor-rtl.css 10.2 kB
build/block-library/editor.css 10.2 kB
build/block-library/index.min.js 175 kB
build/block-library/reset-rtl.css 478 B
build/block-library/reset.css 478 B
build/block-library/style-rtl.css 11.5 kB
build/block-library/style.css 11.5 kB
build/block-library/theme-rtl.css 689 B
build/block-library/theme.css 694 B
build/block-serialization-default-parser/index.min.js 1.12 kB
build/block-serialization-spec-parser/index.min.js 2.83 kB
build/blocks/index.min.js 47 kB
build/components/index.min.js 223 kB
build/components/style-rtl.css 15 kB
build/components/style.css 15 kB
build/compose/index.min.js 11.2 kB
build/core-data/index.min.js 14.5 kB
build/customize-widgets/index.min.js 11 kB
build/customize-widgets/style-rtl.css 1.39 kB
build/customize-widgets/style.css 1.39 kB
build/data-controls/index.min.js 663 B
build/data/index.min.js 8.66 kB
build/date/index.min.js 32 kB
build/deprecated/index.min.js 518 B
build/dom-ready/index.min.js 336 B
build/dom/index.min.js 4.58 kB
build/edit-navigation/index.min.js 15.8 kB
build/edit-navigation/style-rtl.css 4.04 kB
build/edit-navigation/style.css 4.05 kB
build/edit-post/classic-rtl.css 546 B
build/edit-post/classic.css 547 B
build/edit-post/index.min.js 30.1 kB
build/edit-post/style-rtl.css 7.18 kB
build/edit-post/style.css 7.18 kB
build/edit-site/index.min.js 47.4 kB
build/edit-site/style-rtl.css 8.02 kB
build/edit-site/style.css 8.01 kB
build/edit-widgets/index.min.js 16.3 kB
build/edit-widgets/style-rtl.css 4.4 kB
build/edit-widgets/style.css 4.39 kB
build/editor/index.min.js 38.5 kB
build/editor/style-rtl.css 3.71 kB
build/editor/style.css 3.71 kB
build/element/index.min.js 4.29 kB
build/escape-html/index.min.js 548 B
build/format-library/index.min.js 6.62 kB
build/format-library/style-rtl.css 571 B
build/format-library/style.css 571 B
build/hooks/index.min.js 1.66 kB
build/html-entities/index.min.js 454 B
build/i18n/index.min.js 3.79 kB
build/is-shallow-equal/index.min.js 535 B
build/keyboard-shortcuts/index.min.js 1.83 kB
build/keycodes/index.min.js 1.41 kB
build/list-reusable-blocks/index.min.js 1.75 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.94 kB
build/notices/index.min.js 957 B
build/nux/index.min.js 2.12 kB
build/nux/style-rtl.css 751 B
build/nux/style.css 749 B
build/plugins/index.min.js 1.98 kB
build/preferences/index.min.js 1.2 kB
build/primitives/index.min.js 949 B
build/priority-queue/index.min.js 611 B
build/react-i18n/index.min.js 704 B
build/react-refresh-entry/index.min.js 8.44 kB
build/react-refresh-runtime/index.min.js 7.31 kB
build/redux-routine/index.min.js 2.69 kB
build/reusable-blocks/index.min.js 2.24 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/rich-text/index.min.js 11.2 kB
build/server-side-render/index.min.js 1.61 kB
build/shortcode/index.min.js 1.52 kB
build/token-list/index.min.js 668 B
build/url/index.min.js 1.99 kB
build/vendors/react-dom.min.js 38.5 kB
build/vendors/react.min.js 4.34 kB
build/viewport/index.min.js 1.08 kB
build/warning/index.min.js 280 B
build/widgets/index.min.js 7.21 kB
build/widgets/style-rtl.css 1.16 kB
build/widgets/style.css 1.16 kB
build/wordcount/index.min.js 1.07 kB

compressed-size-action

@fluiddot fluiddot force-pushed the rnmobile/feature/drag-and-drop-refactor-draggable branch from 382781d to 8e51dd7 Compare April 18, 2022 11:11
@fluiddot fluiddot requested a review from geriux April 18, 2022 11:49
@fluiddot fluiddot marked this pull request as ready for review April 18, 2022 11:49
@fluiddot fluiddot self-assigned this Apr 18, 2022
@fluiddot fluiddot force-pushed the rnmobile/feature/drag-and-drop-refactor-draggable branch from 8e51dd7 to 0230191 Compare April 18, 2022 15:38
@fluiddot fluiddot requested a review from ellatrix as a code owner April 18, 2022 15:38
@geriux
Copy link
Member

geriux commented Apr 19, 2022

Hey there 👋 great work with the refactor! I'm still testing and reviewing the code but so far encountered a bug on iOS while scrolling:

ScrollingBug.mov

It looks like there's an issue with the scroll, in the video, I'm scrolling really fast but I've tried scrolling a little slower and it's the same, the dragging functionality stays activated showing the chip and the indicator. Trying to scroll doesn't work and the only way to make it work again is to try to activate the dragging in a block 😅

This only happens on iOS, on Android is working fine.

Edit: By the way this happens on both simulator and device

@fluiddot
Copy link
Contributor Author

Hey there 👋 great work with the refactor! I'm still testing and reviewing the code but so far encountered a bug on iOS while scrolling:

ScrollingBug.mov
It looks like there's an issue with the scroll, in the video, I'm scrolling really fast but I've tried scrolling a little slower and it's the same, the dragging functionality stays activated showing the chip and the indicator. Trying to scroll doesn't work and the only way to make it work again is to try to activate the dragging in a block 😅

This only happens on iOS, on Android is working fine.

@geriux Thanks for sharing the issue 🙇! I'll take a deeper look in case I overlooked anything during the refactor 👀 .

@fluiddot
Copy link
Contributor Author

Hey there 👋 great work with the refactor! I'm still testing and reviewing the code but so far encountered a bug on iOS while scrolling:
ScrollingBug.mov
It looks like there's an issue with the scroll, in the video, I'm scrolling really fast but I've tried scrolling a little slower and it's the same, the dragging functionality stays activated showing the chip and the indicator. Trying to scroll doesn't work and the only way to make it work again is to try to activate the dragging in a block 😅
This only happens on iOS, on Android is working fine.

@geriux Thanks for sharing the issue 🙇! I'll take a deeper look in case I overlooked anything during the refactor 👀 .

As far as I checked, on iOS, the property maxDist (i.e. maximum distance) of the long-press gesture can make it fail even when it's active if you move the finger further than the distance set. Per the documentation of the property, this shouldn't occur as the maximum distance should only produce a failure if the gesture is not active 🙃 .

Maximum distance, expressed in points, that defines how far the finger is allowed to travel during a long press gesture. If the finger travels further than the defined distance and the handler hasn't yet activated, it will fail to recognize the gesture.

However, this is not the case for iOS which I confirm that it produces the gesture to fail.

As a workaround, I'm going to listen for the end event of the pan gesture and disable dragging in that case.

@fluiddot fluiddot force-pushed the rnmobile/feature/drag-and-drop-refactor-draggable branch from 0230191 to fdc19f7 Compare April 19, 2022 10:45
@fluiddot
Copy link
Contributor Author

@geriux Regarding the issue you spotted and outlined in this comment, I've just pushed a fix in 06fd88a to address it. I tested locally on both platforms and looks like it can no longer be reproduced, in any case, let me know if you consider it fixed and of course, if you find anything while testing. Thanks 🙇 !

@fluiddot
Copy link
Contributor Author

@geriux I've just pushed a couple of commits to fix the following issues I spotted while testing:

Prevent onDragEnd event to be called upon mounting

I noticed when adding console logs that the onDragEnd event was always being called when opening the editor. This doesn't produce any issue as no block is dragged, but I fixed to prevent potential side effects in the future.

Reduce calls to event handlers of pan and long-press gestures

Similar to the previous issue, I spotted that some of the event handlers of tap and long-press gestures were being called several times (actually on every frame) which is not expected, specifically the onTouchesMove event of pan gesture and onActive of long-press gesture. This behavior doesn't impact the dragging logic but I fixed them to improve performance.

OnTouchesMove event of pan gesture is expected that's called several times as it's triggered every time the finger moves, but since we only use it for enabling the gesture after the long-press gesture, I introduced a fix to ensure the logic is only executed once.

For long-press gesture's onActive, I initially thought that onActive would be triggered only once (when transitioning to the active state), however, I saw that it's called on every frame 🙃 . Similarly to the pan gesture, I added a fix to ensure that it's only executed once.

@geriux
Copy link
Member

geriux commented Apr 19, 2022

@geriux I've just pushed a couple of commits to fix the following issues I spotted while testing:

Prevent onDragEnd event to be called upon mounting

I noticed when adding console logs that the onDragEnd event was always being called when opening the editor. This doesn't produce any issue as no block is dragged, but I fixed to prevent potential side effects in the future.

Reduce calls to event handlers of pan and long-press gestures

Similar to the previous issue, I spotted that some of the event handlers of tap and long-press gestures were being called several times (actually on every frame) which is not expected, specifically the onTouchesMove event of pan gesture and onActive of long-press gesture. This behavior doesn't impact the dragging logic but I fixed them to improve performance.

OnTouchesMove event of pan gesture is expected that's called several times as it's triggered every time the finger moves, but since we only use it for enabling the gesture after the long-press gesture, I introduced a fix to ensure the logic is only executed once.

For long-press gesture's onActive, I initially thought that onActive would be triggered only once (when transitioning to the active state), however, I saw that it's called on every frame 🙃 . Similarly to the pan gesture, I added a fix to ensure that it's only executed once.

Awesome! Its good to have those checks. Thanks for the changes! I'll continue with the review and testing 🚀

Copy link
Member

@geriux geriux left a comment

Choose a reason for hiding this comment

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

Hey @fluiddot 👋

After some testing I think I found another bug:

TextEditing.mov

The steps I followed:

  • Move a text-based block (Paragraph, heading)
  • Edit the block's text content
  • Press the enter key to add another Paragraph block
  • Select another text-based block and try to drag & drop it (outside the text input)

It's happening on both iOS and Android.

onBlockDrop( {
// Dropping is only allowed at root level
srcRootClientId: '',
srcClientIds: [ currentClientId ],
type: 'block',
} );
selectBlock( currentClientId );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The dragged block gets automatically selected after it's dropped.

Comment on lines +270 to +275
canDragBlock: hasSelectedBlock()
? ! isAnyTextInputFocused
: true,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We determine if the dragging can be enabled by checking if any text input is focused while a block is selected.

} else if ( wasBeingDragged.current ) {
stopDraggingBlock();
wasBeingDragged.current = false;
if ( isBeingDragged !== wasBeingDragged.current ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The logic of this side effect will only be executed if isBeingDragged value has changed.

<Animated.View style={ wrapperStyles }>
{ children( { isDraggable: true } ) }
</Animated.View>
<DraggableTrigger id={ clientId } enabled={ enabled && canDragBlock }>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We now can pass the clientId as the id of the draggable component, which will be passed through the dragging events.

/>
) }
<BlockDraggable
enabled={ ! hasParent }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The drag & drop only works for root-level blocks, for this reason, we disable the BlockDraggable component when the block has a parent. In the future, once the drag & drop functionality support nested blocks, we could enable this.


const panGesture = Gesture.Pan()
.manualActivation( true )
.onTouchesDown( ( event ) => {
const { x = 0, y = 0 } = event.allTouches[ 0 ];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We get the touch data from the first finger put over the screen.

.onTouchesMove( ( _, state ) => {
'worklet';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

By default the handler functions are treated as worklets, so looks like it's not necessary to marked as that.

enabled={ enabled }
minDurationMs={ minDuration }
maxDist={ maxDistance }
simultaneousHandlers={ panGestureRef }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This props is required to allow the pan gesture to be triggered along with the long-press.

Comment on lines +102 to +110
const providerValue = useMemo( () => {
return { panGestureRef, isDragging, draggingId };
}, [] );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The provider component would cause a re-render if we pass this object in the value prop, as it would create a new instance on every render. In order to prevent this, the value is memoized. Note that the dependency is array is empty as all values referenced don't change after the component is mounted:

  • panGestureRef is a React reference, only the value of current will change which is accessed by reference.
  • isDragging and draggingId are shared values whose values are accessed by reference via the value property.

@fluiddot
Copy link
Contributor Author

fluiddot commented Apr 19, 2022

@geriux looks like I had some comments within the code pending publication 😅, sorry for posting them now. Hopefully, they will help us with future reviews.

@fluiddot
Copy link
Contributor Author

Hey @fluiddot 👋

After some testing I think I found another bug:

TextEditing.mov
The steps I followed:

  • Move a text-based block (Paragraph, heading)
  • Edit the block's text content
  • Press the enter key to add another Paragraph block
  • Select another text-based block and try to drag & drop it (outside the text input)

It's happening on both iOS and Android.

@geriux I'm thinking of addressing this bug in a different PR where I'd like also to tackle wordpress-mobile/gutenberg-mobile#4775. This way we allow the next tasks to use these changes, wdyt?

@geriux
Copy link
Member

geriux commented Apr 20, 2022

@geriux I'm thinking of addressing this bug in a different PR where I'd like also to tackle wordpress-mobile/gutenberg-mobile#4775. This way we allow the next tasks to use these changes, wdyt?

Sounds good! Thanks!

Copy link
Member

@geriux geriux left a comment

Choose a reason for hiding this comment

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

LGTM! Really nice work with this refactor! 👏 Tested it on both iOS and Android and it's working well except for the reported bug that will be handled as agreed in another PR.

@fluiddot
Copy link
Contributor Author

Looks like we need to include changes from 1e06cba to make Unit Tests / PHP PR check pass. I'll update the upstream branch rnmobile/feature/drag-and-drop with the latest changes from trunk.

@fluiddot fluiddot force-pushed the rnmobile/feature/drag-and-drop-refactor-draggable branch from 61f6c72 to 0a42d31 Compare April 20, 2022 11:22
@fluiddot fluiddot merged commit 3996c65 into rnmobile/feature/drag-and-drop Apr 20, 2022
@fluiddot fluiddot deleted the rnmobile/feature/drag-and-drop-refactor-draggable branch April 20, 2022 13:55
fluiddot added a commit that referenced this pull request May 11, 2022
* [Mobile] - Drag & drop blocks - Fetch and share blocks layout size and position coordinates (#39089)

* Mobile - Block list - Extract block list context into a separate file and add support to store the blocks layouts data and coordinates.

* Mobile - Block list - Adds block list item cell to get the onLayout data and use updateBlocksLayouts to store it. It is needed to use CellRendererComponent to be able to get the right position coordinates

* Mobile - Block list - Store block layouts data for inner blocks in a deep level

* Mobile - BlockList ItemCell - Destructuring props

* Mobile - BlockListContext - Rename findByRootId to findBlockLayoutByClientId

* Mobile - BlockListContext - Rename deleteByClientId to deleteBlockLayoutByClientId

* Mobile - BlockListContext - Store default context and use it for initialization

* Mobile - BlockListContext - Add param's docs

* Mobile - Block list context - Export findBlockLayoutByClientId

* Mobile - Block list context - Update comments

* Mobile - Block list context - Unit tests

* Mobile - Block list context - update unit tests

* [Mobile] - Draggable component (#39551)

* Mobile - Add Draggable component

* Mobile - Draggable - Fix composition of gestures, enable Pan gesture once LongPress is recognized

* Mobile - Draggable component - Track if the gesture Pan started, if it didn't and long pressure was activated it should call onDragEnd

* Mobile - Draggable component - Add props documentation

* Mobile - Draggable component - Update documentation

* Mobile - Draggable component - Refactor that includes:
- Usage of only one onEnd callback for both gestures.
- Removes the hasPanStarted value since the onEnd callback is unified
- Adds shouldCancelWhenOutside for the Pan gesture.
- Removes the simultaneousWithExternalGesture since the Pan gesture is manually activated and composed with the LongPress gesture.
- Add isIOS check within onTouchesMove for the state.fail() call.

* Mobile - Draggable component - Use Platform from @wordpress/element

* [RNMobile] `BlockDraggable` component (#39617)

* Use animated scroll handler in KeyboardAwareFlatList

* Add hook for scrolling the block list while dragging

* Improve scroll animation in useScrollWhenDragging

* Add draggable chip component

* Add block draggable component

* Remove icon prop from draggable chip component

* Add draggable placeholder

* Fix draggable chip location

* Wrap BlockListItemCell with BlockDraggable

* Fix block draggable placeholder style

* Animate scale property instead of opacity of draggable chip

* Fix draggable placeholder container height calculation

* Fix BlockDraggable height animation

* Move draggable to BlockDraggableWrapper

* Disable isDragging when long-press gesture ends

* Fix onLayout calculation in block list item cell

* Add findBlockLayoutByPosition helper

* Set up dragging block by position

* Remove animate scroll velocity

* Remove useScrollWhenDragging hook

This hook will be introduced in a separate PR

* Remove react-native-reanimated mock

* Rename CHIP_OFFSET_TO_TOUCH_POSITION constant

* Remove unused shared values of chip component

* Stop dragging when no block is found

* Fix drag position calculation

* Update html text input styles

* Unify container component within html text input

* Use only a single client id in block draggable

* Add documentation to block draggable components

* Add documentation to block draggable chip component

* Add documentation to findBlockLayoutByPosition

* Update scrollOffsetTarget calculation

* Fix typos in block draggable

* Add draggable wrapper container style

* Add dark mode styles for draggable chip

* Add dark mode styles for block draggable

* Get container height from blocks layout data

* Replace inline callback functions with useCallback hook

* Update collapse/expand animation when dragging a block

* Force draggable chip to be displayed upfront

* Remove refs from dependencies arrays

References can be omitted from the dependencies arrays since React guarantees that they are inmutable.

* [RNMobile] Add `useScrollWhenDragging` hook (#39705)

* Introduce useScrollWhenDragging hook

* Cancel animation timer on stop scrolling

* Add documentation to useScrollWhenDragging hook

* Replace Dimensions with useWindowDimensions hook

* [RNMobile] Prevent draggable gesture when any text input is focused (#39890)

* [Mobile] Adds useBlockDropZone and DroppingInsertionPoint (#39891)

* Adds useBlockDropZone and DroppingInsertionPoint

* Fix dropping insertion point position when scrolling

* Mobile - DroppingInsertionPoint - Fixes:
- Avoid showing the indicator when no blocks are being dragged.
- Allow showing the dropping indicator at the end of the content.
- Prevent checks within useState for isBlockBeingDragged, if no blocks are being dragged.
- Prevent looking for a block layout if the clientId is undefined.

* Mobile - DroppingInsertionPoint - Add documentation

* Mobile - useBlockDropZone - Add documentation

* Mobile - useBlockDropZone - Add missing dependencies

* Mobile - Block list context - Updates getBlockLayoutsOrderedByYCoord to make it compatible with hermes, currently it doesn't support using the native .sort.

It also adds documentation of the function.

* Mobile -  Updates:

useBlockDropZone:
  - Avoid using showInsertionPoint to avoid re-renders and bad performance.
  - Passes the current target index as a shared value to pass it to DroppingInsertionPoint.

DroppingInsertionPoint:
  - Detects when targetBlockIndex changes from the dragging animation to update the indicator's position.
  - Fixes an issue where the last element couldn't be reached.

* Mobile - Block list - Revert changes for the insertion point checks

* Mobile - Updates documentation:
- BlockListContext: Some typos and clarifications.
- DroppingInsertionPoint: Update component description.
- useBlockDropZone: Update comments.

* Mobile - DroppingInsertionPoint - Remove usage of useCallback

* Mobile - DroppingInsertionPoint - Remove static styles from useAnimatedStyles and merge both in a separate constant.

* Mobile - DroppingInsertionPoint - Move component to the BlockDraggable folder, which is where it's rendered from.

* Mobile - DroppingInsertionPoint - Remove usage of hasStartedDraggingOver in favor of isDragging.

* Mobile - DroppingInsertionPoint - Remove unneeded braces.

Co-authored-by: Carlos Garcia <fluiddot@gmail.com>

* [RNMobile] Add `useOnBlockDrop` hook to update blocks order when dropping a block (#39884)

* Add useOnBlockDrop hook

* Add currentClientId ref to block draggable

* Add onBlockDrop to block drop zone hook

* Trigger onBlockDrop event when dragging finishes

* Fix content overflow when dragging a selected block

* [RNMobile] Update drag & drop animations (#40104)

* Update drag&drop block animation

* Keep block layout data instead of clientId in block draggable

* Automatically select the dropped block

* Delay opacity animation when dropping a block

* Check current client Id existence on stop dragging

* [RNMobile] Disable text editing when long-pressing a block in favor of drag & drop gesture (#40101)

* Reduce default min duration for long-press gesture

* Consume long click event on Android if Aztec text input is not focused

* Ensure that drag events are not executed when text input focused

* Replace onLongPress with long-press gesture handler in Button component

* Use LongPressGestureHandler in button component

* Update block-mover snapshots

* [RNMobile] Refactor draggable logic and introduce `DraggableTrigger` component (#40406)

* Present block mover picker only after state updates

* Refactor draggable component

* Use DraggableTrigger in BlockDraggable

* Move BlockDraggable render to BlockListBlock component

* Fix long-press gesture when editing a text on iOS

* Memoize draggable provider value to prevent re-renders

* Fix dragging not being disabled after scrolling

* Reduce calls to event handlers of pan and long-press gestures

* Prevent onDragEnd event to be called upon mounting

* Add DEFAULT_IOS_LONG_PRESS_MIN_DURATION constant

* [RNMobile] Update animation of drag & drop chip component (#40409)

* Use layout animations when rendering the chip component

Additionally, the block icon is now provided from the BlockDraggable component.

* Fix chip layout calculation

* Set block icon as state value

* Update enter/exit animation duration of chip component

* Mobile - DroppingInsertionPoint - Update indicator position for selected blocks (#40510)

* Mobile - DroppingInsertionPoint - Update the indicator's position for the current selected block for both top and bottom positions depending on the current position when dragging.

* Replace parseInt to Math.floor

* [RNMobile] Add haptic feedback to drag & drop feature (#40423)

* Add generate haptic feedback function into RN bridge

* Add haptic feedback to drag & drop

* Reduce haptic feedback intensity on iOS

* [RNMobile] Fix issues related to editing text and dragging gesture (#40480)

* Add input state functionality to Aztec

* Control drag enabling with Aztec input state

* Force disabling text editing when dragging

* Add documentation to AztecInputState

* Update changelog

* Add tests for Aztec input state

* Update focus change listener logic

* Update listen to focus change event test

* Fix react-native-aztec module mock

* Fix wrong call to removeFocusChangeListener

* Fix updating currentFocusedElement value

* Check if an inner block is selected when enabling dragging

* Wrap draggable long-press handler with useCallback

* Add documentation to notifyListeners function

* Mobile - Draggable - Disable multiple touches (#40519)

* Mobile - Draggable - Disable multiple touches by getting the first touch during onTouchesMove, since using the maxPointers config works unexpectedly on Android.

* Mobile - Draggable - Order touch events by ID and use the first element

* Pass isPanActive to the LongPress onEnd callback to not update the isDragging flag when the panning event is active

* Mobile - Draggable - Store the first touch ID instead of picking the first event within the allTouches event array.

* Mobile - DroppingInsertionPoint - Hide indicator when it overflows outside the content (#40658)

* Mobile - Provider - Adds SafeAreaProvider

* Mobile - DroppingInsertionPoint - Hide indicator if the current position overflows over the header or footer

* Fix tests, adds react-native-safe-area-context mock

* Mobile - DroppingInsertionPoint - Use the height value from the useSafeAreaFrame instead

* Mobile - DroppingInsertionPoint - Update mocks

* Mobile - Disable long press event in media blocks (#40651)

* Mobile - Disable long press event in media blocks

* Mobile - Media & Text - Remove extra param

* Mobile - Media & Text - Show replace media button for both Image and video

* Mobile - DraggingInsertionPoint - Prevent crash when accessing a null element (#40689)

* Mobile - DraggingInsertionPoint - Fix crash when there's only one block in the editor, the previousElement is null.

* Mobile - DroppingInsertionPoint - Avoid having NaN values

* Mobile - Draggable - Add onTouchesCancelled to handle manually ending the drag & drop event in cases like sending the app to background or opening the notifications center on iOS (#40729)

* [RNMobile] Fix Android crash when closing the editor while dragging (#40810)

* Remove Reanimated transitive dependency on Android

* Test Android crash fix by changing Reanimated version

* Bump react-native-reanimated dependency version

* Bump react-native-reanimated dependency on Android

* Update package-lock.json file

* Bump react-native-reanimated dependency version

* Revert "Test Android crash fix by changing Reanimated version"

This reverts commit d01cdae.

* Mobile - AztecView - Trigger notifyInputChange on focus/blur (#40788)

* Mobile - BlockDraggable - Set isEditingText to false and update it with the initialization of the AztecView listener

* Mobile - AztecView - Move notifyInputChange to the focus/blur functions within AztecInputState, to fix an issue where these are called directly.

* [RNMobile] Disable dragging when a screen reader is enabled (#40852)

* [RNMobile] Allow dragging from block toolbar (#40886)

* Enable dragging from block toolbar

* Ensure root block is dragged for nested blocks

* Add draggingClientId prop to BlockDraggable

With this change the `BlockDraggable` component might receive two different client ids, one is the client id of the block where the component is rendered, and the other (which is optional) is used for identifying the block to be dragged.

* Set dragging always enabled for block toolbar

The block toolbar is only visible when the block is selected so it's safe to allow dragging in all cases, as it won't affect other UI elements.

* Update dragging enabled calculation

In order to prevent issues related to disabling the long-press gesture in elements within the block UI, the logic for enabling the dragging has been updated. Now it will enabled in the following cases:
- The block doesn't have inner blocks. This applies to root blocks and nested blocks without further nested blocks.
- Blocks with nested blocks if the block is selected.
- Blocks with nested blocks if none of the nested blocks is selected.

* Update Podfile.lock

* Update react-native-editor changelog

* Fix text block query locator in Android E2E tests

* Fix Cover block E2E test

* Fix Spacer block E2E test

Co-authored-by: Gerardo Pacheco <gerardo.pacheco@automattic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Drag and Drop Drag and drop functionality when working with blocks 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.

2 participants