diff --git a/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js b/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js index 1185d1eec61401..eb2c5e38db5ba5 100644 --- a/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js +++ b/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js @@ -6,7 +6,12 @@ import { partial, first, castArray, last, compact } from 'lodash'; /** * WordPress dependencies */ -import { ToolbarButton, Picker } from '@wordpress/components'; +import { + getClipboard, + setClipboard, + ToolbarButton, + Picker, +} from '@wordpress/components'; import { getBlockType, getDefaultBlockName, @@ -19,7 +24,7 @@ import { __, sprintf } from '@wordpress/i18n'; import { withDispatch, withSelect } from '@wordpress/data'; import { withInstanceId, compose } from '@wordpress/compose'; import { moreHorizontalMobile } from '@wordpress/icons'; -import { useRef } from '@wordpress/element'; +import { useRef, useState } from '@wordpress/element'; /** * Internal dependencies */ @@ -40,15 +45,20 @@ const BlockActionsMenu = ( { anchorNodeRef, getBlocksByClientId, selectedBlockClientId, - updateClipboard, - createInfoNotice, + createSuccessNotice, duplicateBlock, removeBlocks, pasteBlock, - isPasteEnabled, + canInsertBlockType, + rootClientId, } ) => { + const [ clipboard, setCurrentClipboard ] = useState( getClipboard() ); const pickerRef = useRef(); const moversOptions = { keys: [ 'icon', 'actionTitle' ] }; + const clipboardBlock = clipboard && rawHandler( { HTML: clipboard } )[ 0 ]; + const isPasteEnabled = + clipboardBlock && + canInsertBlockType( clipboardBlock.name, rootClientId ); const { actionTitle: { @@ -120,11 +130,19 @@ const BlockActionsMenu = ( { deleteOption, ] ); + function onPasteBlock() { + if ( ! clipboard ) { + return; + } + + pasteBlock( rawHandler( { HTML: clipboard } )[ 0 ] ); + } + function onPickerSelect( value ) { switch ( value ) { case deleteOption.value: onDelete(); - createInfoNotice( + createSuccessNotice( // translators: displayed right after the block is removed. __( 'Block removed' ) ); @@ -140,31 +158,33 @@ const BlockActionsMenu = ( { break; case copyButtonOption.value: const copyBlock = getBlocksByClientId( selectedBlockClientId ); - updateClipboard( serialize( copyBlock ) ); - createInfoNotice( + const serializedBlock = serialize( copyBlock ); + setCurrentClipboard( serializedBlock ); + setClipboard( serializedBlock ); + createSuccessNotice( // translators: displayed right after the block is copied. __( 'Block copied' ) ); break; case cutButtonOption.value: const cutBlock = getBlocksByClientId( selectedBlockClientId ); - updateClipboard( serialize( cutBlock ) ); + setClipboard( serialize( cutBlock ) ); removeBlocks( selectedBlockClientId ); - createInfoNotice( + createSuccessNotice( // translators: displayed right after the block is cut. __( 'Block cut' ) ); break; case pasteButtonOption.value: - pasteBlock(); - createInfoNotice( + onPasteBlock(); + createSuccessNotice( // translators: displayed right after the block is pasted. __( 'Block pasted' ) ); break; case duplicateButtonOption.value: duplicateBlock(); - createInfoNotice( + createSuccessNotice( // translators: displayed right after the block is duplicated. __( 'Block duplicated' ) ); @@ -228,7 +248,6 @@ export default compose( getSelectedBlockClientIds, canInsertBlockType, } = select( 'core/block-editor' ); - const { getClipboard } = select( 'core/editor' ); const normalizedClientIds = castArray( clientIds ); const block = getBlock( normalizedClientIds ); const blockName = getBlockName( normalizedClientIds ); @@ -250,13 +269,6 @@ export default compose( const isEmptyDefaultBlock = isExactlyOneBlock && isDefaultBlock && isEmptyContent; - const clipboard = getClipboard(); - const clipboardBlock = - clipboard && rawHandler( { HTML: clipboard } )[ 0 ]; - const isPasteEnabled = - clipboardBlock && - canInsertBlockType( clipboardBlock.name, rootClientId ); - return { isFirst: firstIndex === 0, isLast: lastIndex === blockOrder.length - 1, @@ -266,16 +278,11 @@ export default compose( getBlocksByClientId, selectedBlockClientId: getSelectedBlockClientIds(), currentIndex: firstIndex, - isPasteEnabled, - clipboardBlock, + canInsertBlockType, }; } ), withDispatch( - ( - dispatch, - { clientIds, rootClientId, currentIndex, clipboardBlock }, - { select } - ) => { + ( dispatch, { clientIds, rootClientId, currentIndex }, { select } ) => { const { moveBlocksDown, moveBlocksUp, @@ -285,25 +292,22 @@ export default compose( replaceBlocks, } = dispatch( 'core/block-editor' ); const { openGeneralSidebar } = dispatch( 'core/edit-post' ); - const { updateClipboard, createInfoNotice } = dispatch( - 'core/editor' - ); const { getBlockSelectionEnd, getBlock } = select( 'core/block-editor' ); + const { createSuccessNotice } = dispatch( 'core/notices' ); return { onMoveDown: partial( moveBlocksDown, clientIds, rootClientId ), onMoveUp: partial( moveBlocksUp, clientIds, rootClientId ), openGeneralSidebar: () => openGeneralSidebar( 'edit-post/block' ), - updateClipboard, - createInfoNotice, + createSuccessNotice, duplicateBlock() { return duplicateBlocks( clientIds ); }, removeBlocks, - pasteBlock: () => { + pasteBlock: ( clipboardBlock ) => { const canReplaceBlock = isUnmodifiedDefaultBlock( getBlock( getBlockSelectionEnd() ) ); diff --git a/packages/block-editor/src/components/inserter/menu.native.js b/packages/block-editor/src/components/inserter/menu.native.js index e1bc3349c07067..44cc04f428afd4 100644 --- a/packages/block-editor/src/components/inserter/menu.native.js +++ b/packages/block-editor/src/components/inserter/menu.native.js @@ -25,6 +25,7 @@ import { BottomSheet, BottomSheetConsumer, InserterButton, + getClipboard, } from '@wordpress/components'; /** @@ -115,6 +116,42 @@ export class InserterMenu extends Component { this.setState( { numberOfColumns, itemWidth, maxWidth } ); } + /** + * Processes the inserter items to check + * if there's any copied block in the clipboard + * to add it as an extra item + */ + getItems() { + const { + items, + canInsertBlockType, + destinationRootClientId, + getBlockType, + } = this.props; + + const clipboard = getClipboard(); + const clipboardBlock = + clipboard && rawHandler( { HTML: clipboard } )[ 0 ]; + const shouldAddClipboardBlock = + clipboardBlock && + canInsertBlockType( clipboardBlock.name, destinationRootClientId ); + + return shouldAddClipboardBlock + ? [ + { + ...pick( getBlockType( clipboardBlock.name ), [ + 'name', + 'icon', + ] ), + id: 'clipboard', + initialAttributes: clipboardBlock.attributes, + innerBlocks: clipboardBlock.innerBlocks, + }, + ...items, + ] + : items; + } + renderItem( { item } ) { const { itemWidth, maxWidth } = this.state; const { onSelect } = this.props; @@ -129,8 +166,8 @@ export class InserterMenu extends Component { } render() { - const { items } = this.props; const { numberOfColumns } = this.state; + const items = this.getItems(); return ( { diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index 57e33a8a0350f1..f03892096361fd 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -81,6 +81,7 @@ export { default as LinkSettingsNavigation } from './mobile/link-settings/link-s export { default as Image, IMAGE_DEFAULT_FOCAL_POINT } from './mobile/image'; export { default as ImageEditingButton } from './mobile/image/image-editing-button'; export { default as InserterButton } from './mobile/inserter-button'; +export { setClipboard, getClipboard } from './mobile/clipboard'; // Utils export { colorsUtils } from './mobile/color-settings/utils'; diff --git a/packages/components/src/mobile/clipboard/index.native.js b/packages/components/src/mobile/clipboard/index.native.js new file mode 100644 index 00000000000000..1079fb2f60ce1f --- /dev/null +++ b/packages/components/src/mobile/clipboard/index.native.js @@ -0,0 +1,18 @@ +const createClipboard = () => { + let currentClipboard; + + const setClipboard = ( clipboard ) => { + currentClipboard = clipboard; + }; + + const getClipboard = () => currentClipboard; + + return { + setClipboard, + getClipboard, + }; +}; + +const clipboard = createClipboard(); + +export const { setClipboard, getClipboard } = clipboard; diff --git a/packages/components/src/notice/list.native.js b/packages/components/src/notice/list.native.js index 6c43e25abbd60c..b28e83c72b2cb0 100644 --- a/packages/components/src/notice/list.native.js +++ b/packages/components/src/notice/list.native.js @@ -59,18 +59,17 @@ class NoticeList extends Component { export default compose( [ withSelect( ( select ) => { - const { getNotices } = select( 'core/editor' ); + const { getNotices } = select( 'core/notices' ); return { notices: getNotices(), }; } ), withDispatch( ( dispatch ) => { - const { removeNotice, removeAllNotices } = dispatch( 'core/editor' ); + const { removeNotice } = dispatch( 'core/notices' ); return { removeNotice, - removeAllNotices, }; } ), ] )( NoticeList ); diff --git a/packages/editor/src/components/provider/index.native.js b/packages/editor/src/components/provider/index.native.js index 858d772d62c55a..76bb230c250bc2 100644 --- a/packages/editor/src/components/provider/index.native.js +++ b/packages/editor/src/components/provider/index.native.js @@ -146,7 +146,7 @@ class NativeEditorProvider extends Component { this.subscriptionParentShowNotice = subscribeShowNotice( ( payload ) => { - this.props.createInfoNotice( payload.message ); + this.props.createSuccessNotice( payload.message ); } ); } @@ -307,9 +307,7 @@ export default compose( [ }; } ), withDispatch( ( dispatch ) => { - const { editPost, resetEditorBlocks, createInfoNotice } = dispatch( - 'core/editor' - ); + const { editPost, resetEditorBlocks } = dispatch( 'core/editor' ); const { updateSettings, clearSelectedBlock, @@ -318,13 +316,14 @@ export default compose( [ } = dispatch( 'core/block-editor' ); const { switchEditorMode } = dispatch( 'core/edit-post' ); const { addEntities, receiveEntityRecords } = dispatch( 'core' ); + const { createSuccessNotice } = dispatch( 'core/notices' ); return { updateSettings, addEntities, clearSelectedBlock, insertBlock, - createInfoNotice, + createSuccessNotice, editTitle( title ) { editPost( { title } ); }, diff --git a/packages/editor/src/store/actions.native.js b/packages/editor/src/store/actions.native.js index c682d08d9c8192..49d40749cb1c7a 100644 --- a/packages/editor/src/store/actions.native.js +++ b/packages/editor/src/store/actions.native.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { v4 as uuid } from 'uuid'; - /** * WordPress dependencies */ @@ -30,57 +25,3 @@ export function togglePostTitleSelection( isSelected = true ) { export function* autosave() { RNReactNativeGutenbergBridge.editorDidAutosave(); } - -/** - * Returns an action object to set the clipboard data. - * - * @param {Object} clipboard Stored clipboard data. - * - * @return {Object} Action object. - */ -export function updateClipboard( clipboard ) { - return { - type: 'UPDATE_CLIPBOARD', - clipboard, - }; -} - -/** - * Returns an action object to create an info notice. - * - * @param {Object} message The displayed message of the notice. - * - * @return {Object} Action object. - */ -export function createInfoNotice( message ) { - const notice = { status: 'info', content: message, id: uuid() }; - return { - type: 'CREATE_NOTICE', - notice, - }; -} - -/** - * Returns an action object to remove all notices. - * - * @return {Object} Action object. - */ -export function removeAllNotices() { - return { - type: 'REMOVE_ALL_NOTICES', - }; -} - -/** - * Returns an action object to remove a notice by id. - * - * @param {Object} id The id of the notice to remove. - * - * @return {Object} Action object. - */ -export function removeNotice( id ) { - return { - type: 'REMOVE_NOTICE', - id, - }; -} diff --git a/packages/editor/src/store/selectors.native.js b/packages/editor/src/store/selectors.native.js index 474686799bc73d..2edfc373213fa2 100644 --- a/packages/editor/src/store/selectors.native.js +++ b/packages/editor/src/store/selectors.native.js @@ -55,25 +55,3 @@ export const isEditedPostAutosaveable = createRegistrySelector( return false; } ); - -/** - * Returns the current clipboard data. - * - * @param {Object} state Global application state. - * - * @return {Object} Current clipboard data. - */ -export function getClipboard( state ) { - return state.clipboard; -} - -/** - * Returns the current notice data. - * - * @param {Object} state Global application state. - * - * @return {Object} Current notice data. - */ -export function getNotices( state ) { - return state.notices; -}