From ee4d50274c13f48e28dcac9a510aa924ae2c4009 Mon Sep 17 00:00:00 2001 From: Joel Dean Date: Tue, 16 Mar 2021 18:58:10 -0500 Subject: [PATCH] [RNMobile] Simplify media insertion flow Part 2 - media upload (#29547) * added autoOpenMediaUpload prop to the MediaPlaceholder * Added the auto-opening capabilities to the MediaUpload component. * Added documentation for the new autoOpenMediaUpload prop * renamed autoOpenMediaUpload to autoOpen in the MediaUpload component. * [RNMobile] Simplify media insertion flow - Part 3 component integration (#29548) * Track the clientId of the block that is inserted. * implemented auto opening utilizing last block inserted from the store * added dismissal support for the auto opening picker to the UI tests. * Updated Dismiss button in closePicker function to Cancel --- .../src/components/inserter/menu.native.js | 4 +++ .../components/media-placeholder/README.md | 9 +++++ .../media-placeholder/index.native.js | 2 ++ .../src/components/media-upload/README.md | 9 +++++ .../components/media-upload/index.native.js | 32 +++++++++++++++--- packages/block-library/src/gallery/edit.js | 22 ++++++++++++- .../block-library/src/image/edit.native.js | 29 ++++++++++++++-- .../block-library/src/video/edit.native.js | 33 +++++++++++++++++-- .../gutenberg-editor-gallery.test.js | 3 ++ .../gutenberg-editor-image-@canary.test.js | 3 ++ .../__device-tests__/pages/editor-page.js | 11 +++++++ 11 files changed, 147 insertions(+), 10 deletions(-) diff --git a/packages/block-editor/src/components/inserter/menu.native.js b/packages/block-editor/src/components/inserter/menu.native.js index c71e28a6b69c4..2c538144ec528 100644 --- a/packages/block-editor/src/components/inserter/menu.native.js +++ b/packages/block-editor/src/components/inserter/menu.native.js @@ -36,6 +36,8 @@ function InserterMenu( { insertDefaultBlock, } = useDispatch( blockEditorStore ); + const { addLastBlockInserted } = useDispatch( 'core/editor' ); + const { items, destinationRootClientId, @@ -110,6 +112,8 @@ function InserterMenu( { innerBlocks ); + addLastBlockInserted( newBlock.clientId ); + insertBlock( newBlock, insertionIndex, destinationRootClientId ); }, [ insertBlock, destinationRootClientId, insertionIndex ] diff --git a/packages/block-editor/src/components/media-placeholder/README.md b/packages/block-editor/src/components/media-placeholder/README.md index da47806e7b14f..14647d2ab27c4 100644 --- a/packages/block-editor/src/components/media-placeholder/README.md +++ b/packages/block-editor/src/components/media-placeholder/README.md @@ -63,6 +63,15 @@ This property is similar to the `accept` property. The difference is the format - Required: No - Platform: Web | Mobile +### autoOpenMediaUpload + +If true, the MediaUpload component auto-opens the picker of the respective platform. + +- Type: `Boolean` +- Required: No +- Default: `false` +- Platform: Mobile + ### className Class name added to the placeholder. diff --git a/packages/block-editor/src/components/media-placeholder/index.native.js b/packages/block-editor/src/components/media-placeholder/index.native.js index 1193b8a4122d4..9760fec58e00e 100644 --- a/packages/block-editor/src/components/media-placeholder/index.native.js +++ b/packages/block-editor/src/components/media-placeholder/index.native.js @@ -48,6 +48,7 @@ function MediaPlaceholder( props ) { height, backgroundColor, hideContent, + autoOpenMediaUpload, } = props; // use ref to keep media array current for callbacks during rerenders @@ -160,6 +161,7 @@ function MediaPlaceholder( props ) { } multiple={ multiple } isReplacingMedia={ false } + autoOpen={ autoOpenMediaUpload } render={ ( { open, getMediaOptions } ) => { return ( { const otherMediaOptionsWithIcons = otherMediaOptions.map( ( option ) => { @@ -54,6 +62,10 @@ export class MediaUpload extends Component { this.setState( { otherMediaOptions: otherMediaOptionsWithIcons } ); } ); + + if ( autoOpen ) { + this.onPickerPresent(); + } } getAllSources() { @@ -136,8 +148,20 @@ export class MediaUpload extends Component { } onPickerPresent() { + const { autoOpen } = this.props; + const isIOS = Platform.OS === 'ios'; + if ( this.picker ) { - this.picker.presentPicker(); + // the delay below is required because on iOS this action sheet gets dismissed by the close event of the Inserter + // so this delay allows the Inserter to be closed fully before presenting action sheet. + if ( autoOpen && isIOS ) { + delay( + () => this.picker.presentPicker(), + PICKER_OPENING_DELAY + ); + } else { + this.picker.presentPicker(); + } } } diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index a0ffc6ed9bc33..de4139bc45769 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -34,7 +34,7 @@ import { import { Platform, useEffect, useState, useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { getBlobByURL, isBlobURL, revokeBlobURL } from '@wordpress/blob'; -import { useDispatch, withSelect } from '@wordpress/data'; +import { useDispatch, withSelect, withDispatch } from '@wordpress/data'; import { withViewportMatch } from '@wordpress/viewport'; import { View } from '@wordpress/primitives'; import { store as coreStore } from '@wordpress/core-data'; @@ -81,6 +81,7 @@ function GalleryEdit( props ) { imageSizes, resizedImages, onFocus, + wasBlockJustInserted, } = props; const { columns = defaultColumnsNumber( attributes ), @@ -343,6 +344,9 @@ function GalleryEdit( props ) { onError={ onUploadError } notices={ hasImages ? undefined : noticeUI } onFocus={ onFocus } + autoOpenMediaUpload={ + ! hasImages && isSelected && wasBlockJustInserted() + } /> ); @@ -466,6 +470,22 @@ export default compose( [ resizedImages, }; } ), + withDispatch( ( dispatch, { clientId }, { select } ) => { + return { + wasBlockJustInserted() { + const { clearLastBlockInserted } = dispatch( 'core/editor' ); + const { wasBlockJustInserted } = select( 'core/editor' ); + + const result = wasBlockJustInserted( clientId ); + + if ( result ) { + clearLastBlockInserted(); + return true; + } + return false; + }, + }; + } ), withNotices, withViewportMatch( { isNarrow: '< small' } ), ] )( GalleryEdit ); diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index 3f9baab48611f..5c17a956b8462 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -42,7 +42,7 @@ import { __, sprintf } from '@wordpress/i18n'; import { getProtocol, hasQueryArg } from '@wordpress/url'; import { doAction, hasAction } from '@wordpress/hooks'; import { compose, withPreferredColorScheme } from '@wordpress/compose'; -import { withSelect } from '@wordpress/data'; +import { withSelect, withDispatch } from '@wordpress/data'; import { image as placeholderIcon, textColor, @@ -399,7 +399,13 @@ export class ImageEdit extends Component { render() { const { isCaptionSelected } = this.state; - const { attributes, isSelected, image, clientId } = this.props; + const { + attributes, + isSelected, + image, + clientId, + wasBlockJustInserted, + } = this.props; const { align, url, alt, id, sizeSlug, className } = attributes; const sizeOptionsValid = find( this.sizeOptions, [ @@ -461,6 +467,9 @@ export class ImageEdit extends Component { onSelect={ this.onSelectMediaUploadOption } icon={ this.getPlaceholderIcon() } onFocus={ this.props.onFocus } + autoOpenMediaUpload={ + isSelected && ! url && wasBlockJustInserted() + } /> ); @@ -579,5 +588,21 @@ export default compose( [ imageSizes, }; } ), + withDispatch( ( dispatch, { clientId }, { select } ) => { + return { + wasBlockJustInserted() { + const { clearLastBlockInserted } = dispatch( 'core/editor' ); + const { wasBlockJustInserted } = select( 'core/editor' ); + + const result = wasBlockJustInserted( clientId ); + + if ( result ) { + clearLastBlockInserted(); + return true; + } + return false; + }, + }; + } ), withPreferredColorScheme, ] )( ImageEdit ); diff --git a/packages/block-library/src/video/edit.native.js b/packages/block-library/src/video/edit.native.js index 2d0cf9fbe014e..3f170fa37a9d7 100644 --- a/packages/block-library/src/video/edit.native.js +++ b/packages/block-library/src/video/edit.native.js @@ -19,7 +19,7 @@ import { ToolbarGroup, PanelBody, } from '@wordpress/components'; -import { withPreferredColorScheme } from '@wordpress/compose'; +import { withPreferredColorScheme, compose } from '@wordpress/compose'; import { BlockCaption, MediaPlaceholder, @@ -35,6 +35,7 @@ import { __, sprintf } from '@wordpress/i18n'; import { isURL, getProtocol } from '@wordpress/url'; import { doAction, hasAction } from '@wordpress/hooks'; import { video as SvgIcon, replace } from '@wordpress/icons'; +import { withDispatch } from '@wordpress/data'; /** * Internal dependencies @@ -189,7 +190,12 @@ class VideoEdit extends Component { } render() { - const { setAttributes, attributes, isSelected } = this.props; + const { + setAttributes, + attributes, + isSelected, + wasBlockJustInserted, + } = this.props; const { id, src } = attributes; const { videoContainerHeight } = this.state; @@ -221,6 +227,9 @@ class VideoEdit extends Component { onSelect={ this.onSelectMediaUploadOption } icon={ this.getIcon( ICON_TYPE.PLACEHOLDER ) } onFocus={ this.props.onFocus } + autoOpenMediaUpload={ + isSelected && ! src && wasBlockJustInserted() + } /> ); @@ -361,4 +370,22 @@ class VideoEdit extends Component { } } -export default withPreferredColorScheme( VideoEdit ); +export default compose( [ + withDispatch( ( dispatch, { clientId }, { select } ) => { + return { + wasBlockJustInserted() { + const { clearLastBlockInserted } = dispatch( 'core/editor' ); + const { wasBlockJustInserted } = select( 'core/editor' ); + + const result = wasBlockJustInserted( clientId ); + + if ( result ) { + clearLastBlockInserted(); + return true; + } + return false; + }, + }; + } ), + withPreferredColorScheme, +] )( VideoEdit ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-gallery.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-gallery.test.js index 37211a5db2e0b..4789aa0bd5776 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-gallery.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-gallery.test.js @@ -6,6 +6,9 @@ import { blockNames } from './pages/editor-page'; describe( 'Gutenberg Editor Gallery Block tests', () => { it( 'should be able to add a gallery block', async () => { await editorPage.addNewBlock( blockNames.gallery ); + await editorPage.driver.sleep( 1000 ); + await editorPage.closePicker(); + const galleryBlock = await editorPage.getBlockAtPosition( blockNames.gallery ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js index 30d135e4cdc1e..40a9a1b38a96f 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js @@ -8,6 +8,9 @@ import testData from './helpers/test-data'; describe( 'Gutenberg Editor Image Block tests', () => { it( 'should be able to add an image block', async () => { await editorPage.addNewBlock( blockNames.image ); + await editorPage.driver.sleep( 1000 ); + await editorPage.closePicker(); + let imageBlock = await editorPage.getBlockAtPosition( blockNames.image ); diff --git a/packages/react-native-editor/__device-tests__/pages/editor-page.js b/packages/react-native-editor/__device-tests__/pages/editor-page.js index 8fd19373bba37..a7a73ba8c7aad 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -526,6 +526,17 @@ class EditorPage { return await typeString( this.driver, textViewElement, text, clear ); } + async closePicker() { + if ( isAndroid() ) { + await swipeDown( this.driver ); + } else { + const cancelButton = await this.driver.elementByAccessibilityId( + 'Cancel' + ); + await cancelButton.click(); + } + } + // ============================= // Unsupported Block functions // =============================