diff --git a/assets/src/stories-editor/blocks/amp-story-page/copy-paste-handler.js b/assets/src/stories-editor/blocks/amp-story-page/copy-paste-handler.js index 77cb31f5449..56abcfd7cd4 100644 --- a/assets/src/stories-editor/blocks/amp-story-page/copy-paste-handler.js +++ b/assets/src/stories-editor/blocks/amp-story-page/copy-paste-handler.js @@ -13,32 +13,30 @@ import { withDispatch, useSelect, useDispatch } from '@wordpress/data'; /** * Internal dependencies */ -import { copyTextToClipBoard, ensureAllowedBlocksOnPaste, isPageBlock } from '../../helpers'; +import { copyTextToClipBoard, isPageBlock, useIsBlockAllowedOnPage, useDisplayPasteError } from '../../helpers'; function CopyPasteHandler( { children, onCopy, clientId, isSelected } ) { const { - isFirstPage, canUserUseUnfilteredHTML, getCopiedMarkupState, blocksOnPage, } = useSelect( ( select ) => { - const { - getBlockOrder, - getSettings, - } = select( 'core/block-editor' ); + const { getSettings, getBlockOrder } = select( 'core/block-editor' ); const { __experimentalCanUserUseUnfilteredHTML } = getSettings(); const { getCopiedMarkup } = select( 'amp/story' ); return { - isFirstPage: getBlockOrder().indexOf( clientId ) === 0, canUserUseUnfilteredHTML: __experimentalCanUserUseUnfilteredHTML, getCopiedMarkupState: getCopiedMarkup, blocksOnPage: getBlockOrder( clientId ), }; - }, [ clientId ] + }, + [ clientId ] ); - const { insertBlocks } = useDispatch( 'core/block-editor' ); + const { insertBlock } = useDispatch( 'core/block-editor' ); + const isBlockAllowedOnPage = useIsBlockAllowedOnPage(); + const displayPasteError = useDisplayPasteError(); const onPaste = ( event ) => { // Ignore if the Page is not the selected page. @@ -77,9 +75,17 @@ function CopyPasteHandler( { children, onCopy, clientId, isSelected } ) { canUserUseUnfilteredHTML, } ); - if ( content.length > 0 ) { - insertBlocks( ensureAllowedBlocksOnPaste( content, clientId, isFirstPage ), blocksOnPage.length, clientId ); + if ( ! clientId || ! content.length ) { + return; } + + content.forEach( ( pastedBlock ) => { + if ( isBlockAllowedOnPage( pastedBlock.name, clientId ) ) { + insertBlock( pastedBlock, blocksOnPage.length, clientId ); + } else { + displayPasteError( pastedBlock.name ); + } + } ); }; return ( diff --git a/assets/src/stories-editor/components/context-menu/index.js b/assets/src/stories-editor/components/context-menu/index.js index d29b754ed19..545a4b8132a 100644 --- a/assets/src/stories-editor/components/context-menu/index.js +++ b/assets/src/stories-editor/components/context-menu/index.js @@ -25,10 +25,10 @@ import { useDispatch, useSelect } from '@wordpress/data'; import './edit.css'; import { copyTextToClipBoard, - ensureAllowedBlocksOnPaste, isPageBlock, useIsBlockAllowedOnPage, useMoveBlockToPage, + useDisplayPasteError, } from '../../helpers'; import { ALLOWED_MOVABLE_BLOCKS, DISABLE_DUPLICATE_BLOCKS } from '../../constants'; import useOutsideClickChecker from './outside-click-checker'; @@ -63,7 +63,6 @@ const ContextMenu = ( props ) => { const { removeBlock, insertBlock, - insertBlocks, updateBlockAttributes, } = useDispatch( 'core/block-editor' ); @@ -72,6 +71,7 @@ const ContextMenu = ( props ) => { const { __experimentalCanUserUseUnfilteredHTML: canUserUseUnfilteredHTML, isRTL } = getSettings(); const isBlockAllowedOnPage = useIsBlockAllowedOnPage(); + const displayPasteError = useDisplayPasteError(); const copyBlock = ( clientId ) => { const block = getBlock( clientId ); @@ -109,20 +109,27 @@ const ContextMenu = ( props ) => { return; } - const isFirstPage = getBlockOrder().indexOf( pageClientId ) === 0; const blocksOnPage = getBlockOrder( pageClientId ); - insertBlocks( ensureAllowedBlocksOnPaste( content, pageClientId, isFirstPage ), blocksOnPage.length, pageClientId ) - .then( ( { blocks } ) => { - for ( const block of blocks ) { - if ( ALLOWED_MOVABLE_BLOCKS.includes( block.name ) ) { - updateBlockAttributes( block.clientId, { - positionTop: insidePercentageY, - positionLeft: insidePercentageX, + content.forEach( ( pastedBlock ) => { + if ( isBlockAllowedOnPage( pastedBlock.name, pageClientId ) ) { + insertBlock( pastedBlock, blocksOnPage.length, pageClientId ) + .then( ( { blocks } ) => { + blocks.forEach( ( block ) => { + if ( ALLOWED_MOVABLE_BLOCKS.includes( block.name ) ) { + updateBlockAttributes( block.clientId, { + positionTop: insidePercentageY, + positionLeft: insidePercentageX, + } ); + } } ); - } - } - } ) - .catch( () => {} ); + } ) + .catch( () => { + displayPasteError( pastedBlock.name ); + } ); + } else { + displayPasteError( pastedBlock.name ); + } + } ); }; const cutBlock = ( clientId ) => { diff --git a/assets/src/stories-editor/helpers/ensureAllowedBlocksOnPaste.js b/assets/src/stories-editor/helpers/ensureAllowedBlocksOnPaste.js deleted file mode 100644 index 4bd2e1934e7..00000000000 --- a/assets/src/stories-editor/helpers/ensureAllowedBlocksOnPaste.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Internal dependencies - */ -import { ALLOWED_CHILD_BLOCKS } from '../constants'; -import getPageBlockByName from './getPageBlockByName'; - -/** - * Ensure that only allowed blocks are pasted. - * - * @param {[]} blocks Array of blocks. - * @param {string} clientId Page ID. - * @param {boolean} isFirstPage If is first page. - * @return {[]} Filtered blocks. - */ -const ensureAllowedBlocksOnPaste = ( blocks, clientId, isFirstPage ) => { - const allowedBlocks = []; - blocks.forEach( ( block ) => { - switch ( block.name ) { - // Skip copying Page. - case 'amp/amp-story-page': - return; - case 'amp/amp-story-page-attachment': - case 'amp/amp-story-cta': - const currentBlock = getPageBlockByName( clientId, block.name ); - if ( currentBlock || ( isFirstPage && block.name === 'amp/amp-story-cta' ) ) { - return; - } - allowedBlocks.push( block ); - break; - default: - if ( ALLOWED_CHILD_BLOCKS.includes( block.name ) ) { - allowedBlocks.push( block ); - } - break; - } - } ); - return allowedBlocks; -}; - -export default ensureAllowedBlocksOnPaste; diff --git a/assets/src/stories-editor/helpers/index.js b/assets/src/stories-editor/helpers/index.js index 11e589fdc24..2feb6f7b3d2 100644 --- a/assets/src/stories-editor/helpers/index.js +++ b/assets/src/stories-editor/helpers/index.js @@ -46,7 +46,6 @@ export { default as processMedia } from './processMedia'; export { default as addVideoAriaLabel } from './addVideoAriaLabel'; export { default as getCallToActionBlock } from './getCallToActionBlock'; export { default as getPageAttachmentBlock } from './getPageAttachmentBlock'; -export { default as ensureAllowedBlocksOnPaste } from './ensureAllowedBlocksOnPaste'; export { default as isPageBlock } from './isPageBlock'; export { default as copyTextToClipBoard } from './copyTextToClipBoard'; export { default as getPosterImageFromFileObj } from './getPosterImageFromFileObj'; @@ -61,3 +60,4 @@ export { default as getRelativeElementPosition } from './getRelativeElementPosit export { default as isCTABlock } from './isCTABlock'; export { default as useMoveBlockToPage } from './useMoveBlockToPage'; export { default as useIsBlockAllowedOnPage } from './useIsBlockAllowedOnPage'; +export { default as useDisplayPasteError } from './useDisplayPasteError'; diff --git a/assets/src/stories-editor/helpers/test/ensureAllowedBlocksOnPaste.js b/assets/src/stories-editor/helpers/test/ensureAllowedBlocksOnPaste.js deleted file mode 100644 index 8b897ddddcc..00000000000 --- a/assets/src/stories-editor/helpers/test/ensureAllowedBlocksOnPaste.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Internal dependencies - */ -import ensureAllowedBlocksOnPaste from '../ensureAllowedBlocksOnPaste'; - -const mockGetBlocksByClientId = jest.fn( () => [] ); -const mockGetBlockOrder = jest.fn( () => [] ); - -jest.mock( '@wordpress/data', () => { - return { - select: () => ( { - getBlocksByClientId: ( ...args ) => mockGetBlocksByClientId( ...args ), - getBlockOrder: ( ...args ) => mockGetBlockOrder( ...args ), - } ), - }; -} ); - -describe( 'pasting blocks', () => { - describe( 'ensureAllowedBlocksOnPaste', () => { - it( 'should not allow pasting blocks that are not allowed', () => { - const blocks = [ - { - name: 'amp/amp-story-cta', - }, - { - name: 'some-random-block', - }, - { - name: 'amp/amp-story-text', - }, - { - name: 'core/paragraph', - }, - ]; - const filteredBlocks = ensureAllowedBlocksOnPaste( blocks, 'abc123', true ); - expect( filteredBlocks ).toHaveLength( 1 ); - expect( filteredBlocks[ 0 ].name ).toStrictEqual( 'amp/amp-story-text' ); - } ); - } ); -} ); diff --git a/assets/src/stories-editor/helpers/useDisplayPasteError.js b/assets/src/stories-editor/helpers/useDisplayPasteError.js new file mode 100644 index 00000000000..015daf9a821 --- /dev/null +++ b/assets/src/stories-editor/helpers/useDisplayPasteError.js @@ -0,0 +1,33 @@ +/** + * WordPress dependencies + */ +import { getBlockType } from '@wordpress/blocks'; +import { __, sprintf } from '@wordpress/i18n'; +import { useDispatch } from '@wordpress/data'; + +/** + * A hook to display a paste error message. + * + * @return {Function} Returns a function to display a paste error message. + */ +const useDisplayPasteError = () => { + const { createErrorNotice } = useDispatch( 'core/notices' ); + + return ( name ) => { + const blockType = getBlockType( name ); + const removeMessage = sprintf( + // translators: %s: Type of block (i.e. Text, Image etc) + __( 'Unable to paste %s block.', 'amp' ), + blockType.title + ); + createErrorNotice( + removeMessage, + { + type: 'snackbar', + isDismissible: true, + } + ); + }; +}; + +export default useDisplayPasteError;