From ee5014da8b5aa85c62467d81f4641ef7262eac6e Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 13 May 2024 10:55:30 +0100 Subject: [PATCH] Revert "Update: Move pattern actions to the editor package. (#60785)" This reverts commit 66ade7093fb80dd656169c3ded366bd2e7ec3cd5. --- package-lock.json | 6 +- packages/edit-site/package.json | 1 + .../dataviews-pattern-actions.js | 247 ++++++++++++++- .../src/components/page-patterns/index.js | 18 +- packages/editor/package.json | 2 - .../src/components/post-actions/actions.js | 290 ++---------------- 6 files changed, 291 insertions(+), 273 deletions(-) diff --git a/package-lock.json b/package-lock.json index 893097f0ab78d2..586c6b3eec9380 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54145,6 +54145,7 @@ "@wordpress/widgets": "file:../widgets", "@wordpress/wordcount": "file:../wordcount", "change-case": "^4.1.2", + "client-zip": "^2.4.4", "clsx": "^2.1.1", "colord": "^2.9.2", "fast-deep-equal": "^3.1.3", @@ -54240,8 +54241,6 @@ "@wordpress/url": "file:../url", "@wordpress/warning": "file:../warning", "@wordpress/wordcount": "file:../wordcount", - "change-case": "^4.1.2", - "client-zip": "^2.4.4", "clsx": "^2.1.1", "date-fns": "^3.6.0", "deepmerge": "^4.3.0", @@ -69217,6 +69216,7 @@ "@wordpress/widgets": "file:../widgets", "@wordpress/wordcount": "file:../wordcount", "change-case": "^4.1.2", + "client-zip": "^2.4.4", "clsx": "^2.1.1", "colord": "^2.9.2", "fast-deep-equal": "^3.1.3", @@ -69294,8 +69294,6 @@ "@wordpress/url": "file:../url", "@wordpress/warning": "file:../warning", "@wordpress/wordcount": "file:../wordcount", - "change-case": "^4.1.2", - "client-zip": "^2.4.4", "clsx": "^2.1.1", "date-fns": "^3.6.0", "deepmerge": "^4.3.0", diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index a77fc84a4b3d19..1cf07bec6740df 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -69,6 +69,7 @@ "@wordpress/widgets": "file:../widgets", "@wordpress/wordcount": "file:../wordcount", "change-case": "^4.1.2", + "client-zip": "^2.4.4", "clsx": "^2.1.1", "colord": "^2.9.2", "fast-deep-equal": "^3.1.3", diff --git a/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js b/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js index a37ee426709cb7..afa69e9752c5b4 100644 --- a/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js +++ b/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js @@ -1,10 +1,25 @@ +/** + * External dependencies + */ +import { paramCase as kebabCase } from 'change-case'; +import { downloadZip } from 'client-zip'; + /** * WordPress dependencies */ +import { downloadBlob } from '@wordpress/blob'; import { __, _x, sprintf } from '@wordpress/i18n'; - +import { + Button, + __experimentalHStack as HStack, + __experimentalVStack as VStack, + __experimentalText as Text, +} from '@wordpress/components'; import { useDispatch } from '@wordpress/data'; import { store as noticesStore } from '@wordpress/notices'; +import { decodeEntities } from '@wordpress/html-entities'; +import { store as reusableBlocksStore } from '@wordpress/reusable-blocks'; +import { store as editorStore } from '@wordpress/editor'; import { privateApis as routerPrivateApis } from '@wordpress/router'; import { privateApis as patternsPrivateApis } from '@wordpress/patterns'; @@ -12,6 +27,7 @@ import { privateApis as patternsPrivateApis } from '@wordpress/patterns'; * Internal dependencies */ import { unlock } from '../../lock-unlock'; +import { store as editSiteStore } from '../../store'; import { PATTERN_TYPES, TEMPLATE_PART_POST_TYPE, @@ -23,6 +39,235 @@ const { useHistory, useLocation } = unlock( routerPrivateApis ); const { CreatePatternModalContents, useDuplicatePatternProps } = unlock( patternsPrivateApis ); +function getJsonFromItem( item ) { + return JSON.stringify( + { + __file: item.type, + title: item.title || item.name, + content: item.patternPost.content.raw, + syncStatus: item.patternPost.wp_pattern_sync_status, + }, + null, + 2 + ); +} + +export const exportJSONaction = { + id: 'export-pattern', + label: __( 'Export as JSON' ), + supportsBulk: true, + isEligible: ( item ) => item.type === PATTERN_TYPES.user, + callback: async ( items ) => { + if ( items.length === 1 ) { + return downloadBlob( + `${ kebabCase( items[ 0 ].title || items[ 0 ].name ) }.json`, + getJsonFromItem( items[ 0 ] ), + 'application/json' + ); + } + const nameCount = {}; + const filesToZip = items.map( ( item ) => { + const name = kebabCase( item.title || item.name ); + nameCount[ name ] = ( nameCount[ name ] || 0 ) + 1; + return { + name: `${ + name + + ( nameCount[ name ] > 1 + ? '-' + ( nameCount[ name ] - 1 ) + : '' ) + }.json`, + lastModified: new Date(), + input: getJsonFromItem( item ), + }; + } ); + return downloadBlob( + __( 'patterns-export' ) + '.zip', + await downloadZip( filesToZip ).blob(), + 'application/zip' + ); + }, +}; + +const canDeleteOrReset = ( item ) => { + const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE; + const isUserPattern = item.type === PATTERN_TYPES.user; + return isUserPattern || ( isTemplatePart && item.isCustom ); +}; + +export const deleteAction = { + id: 'delete-pattern', + label: __( 'Delete' ), + isEligible: ( item ) => { + const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE; + const hasThemeFile = isTemplatePart && item.templatePart.has_theme_file; + return canDeleteOrReset( item ) && ! hasThemeFile; + }, + hideModalHeader: true, + supportsBulk: true, + RenderModal: ( { items, closeModal, onPerform } ) => { + const { __experimentalDeleteReusableBlock } = + useDispatch( reusableBlocksStore ); + const { createErrorNotice, createSuccessNotice } = + useDispatch( noticesStore ); + const { removeTemplates } = unlock( useDispatch( editorStore ) ); + + const deletePattern = async () => { + const promiseResult = await Promise.allSettled( + items.map( ( item ) => { + return __experimentalDeleteReusableBlock( item.id ); + } ) + ); + // If all the promises were fulfilled with success. + if ( + promiseResult.every( ( { status } ) => status === 'fulfilled' ) + ) { + let successMessage; + if ( promiseResult.length === 1 ) { + successMessage = sprintf( + /* translators: The posts's title. */ + __( '"%s" deleted.' ), + items[ 0 ].title + ); + } else { + successMessage = __( 'The patterns were deleted.' ); + } + createSuccessNotice( successMessage, { + type: 'snackbar', + id: 'edit-site-page-trashed', + } ); + } else { + // If there was at lease one failure. + let errorMessage; + // If we were trying to delete a single pattern. + if ( promiseResult.length === 1 ) { + if ( promiseResult[ 0 ].reason?.message ) { + errorMessage = promiseResult[ 0 ].reason.message; + } else { + errorMessage = __( + 'An error occurred while deleting the pattern.' + ); + } + // If we were trying to delete multiple patterns. + } else { + const errorMessages = new Set(); + const failedPromises = promiseResult.filter( + ( { status } ) => status === 'rejected' + ); + for ( const failedPromise of failedPromises ) { + if ( failedPromise.reason?.message ) { + errorMessages.add( failedPromise.reason.message ); + } + } + if ( errorMessages.size === 0 ) { + errorMessage = __( + 'An error occurred while deleting the patterns.' + ); + } else if ( errorMessages.size === 1 ) { + errorMessage = sprintf( + /* translators: %s: an error message */ + __( + 'An error occurred while deleting the patterns: %s' + ), + [ ...errorMessages ][ 0 ] + ); + } else { + errorMessage = sprintf( + /* translators: %s: a list of comma separated error messages */ + __( + 'Some errors occurred while deleting the patterns: %s' + ), + [ ...errorMessages ].join( ',' ) + ); + } + createErrorNotice( errorMessage, { + type: 'snackbar', + } ); + } + } + }; + const deleteItem = () => { + if ( items[ 0 ].type === TEMPLATE_PART_POST_TYPE ) { + removeTemplates( items ); + } else { + deletePattern(); + } + if ( onPerform ) { + onPerform(); + } + closeModal(); + }; + let questionMessage; + if ( items.length === 1 ) { + questionMessage = sprintf( + // translators: %s: The page's title. + __( 'Are you sure you want to delete "%s"?' ), + decodeEntities( items[ 0 ].title || items[ 0 ].name ) + ); + } else if ( + items.length > 1 && + items[ 0 ].type === TEMPLATE_PART_POST_TYPE + ) { + questionMessage = sprintf( + // translators: %d: The number of template parts (2 or more). + __( 'Are you sure you want to delete %d template parts?' ), + items.length + ); + } else { + questionMessage = sprintf( + // translators: %d: The number of patterns (2 or more). + __( 'Are you sure you want to delete %d patterns?' ), + items.length + ); + } + return ( + + { questionMessage } + + + + + + ); + }, +}; + +export const resetAction = { + id: 'reset-action', + label: __( 'Reset' ), + isEligible: ( item ) => { + const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE; + const hasThemeFile = isTemplatePart && item.templatePart.has_theme_file; + return canDeleteOrReset( item ) && hasThemeFile; + }, + hideModalHeader: true, + RenderModal: ( { items, closeModal } ) => { + const [ item ] = items; + const { removeTemplate } = useDispatch( editSiteStore ); + return ( + + + { __( 'Reset to default and clear all customizations?' ) } + + + + + + + ); + }, +}; + export const duplicatePatternAction = { id: 'duplicate-pattern', label: _x( 'Duplicate', 'action label' ), diff --git a/packages/edit-site/src/components/page-patterns/index.js b/packages/edit-site/src/components/page-patterns/index.js index f8314d65f34ff5..724f60ba391034 100644 --- a/packages/edit-site/src/components/page-patterns/index.js +++ b/packages/edit-site/src/components/page-patterns/index.js @@ -47,6 +47,9 @@ import { OPERATOR_IS, } from '../../utils/constants'; import { + exportJSONaction, + resetAction, + deleteAction, duplicatePatternAction, duplicateTemplatePartAction, } from './dataviews-pattern-actions'; @@ -380,13 +383,20 @@ export default function DataviewsPatterns() { if ( type === TEMPLATE_PART_POST_TYPE ) { return [ editAction, - duplicateTemplatePartAction, ...templatePartActions, + duplicateTemplatePartAction, + resetAction, + deleteAction, ].filter( Boolean ); } - return [ editAction, duplicatePatternAction, ...patternActions ].filter( - Boolean - ); + return [ + editAction, + ...patternActions, + duplicatePatternAction, + exportJSONaction, + resetAction, + deleteAction, + ].filter( Boolean ); }, [ editAction, type, templatePartActions, patternActions ] ); const onChangeView = useCallback( ( newView ) => { diff --git a/packages/editor/package.json b/packages/editor/package.json index d8a54c778b3ae3..65fa7deae1828c 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -64,8 +64,6 @@ "@wordpress/url": "file:../url", "@wordpress/warning": "file:../warning", "@wordpress/wordcount": "file:../wordcount", - "change-case": "^4.1.2", - "client-zip": "^2.4.4", "clsx": "^2.1.1", "date-fns": "^3.6.0", "deepmerge": "^4.3.0", diff --git a/packages/editor/src/components/post-actions/actions.js b/packages/editor/src/components/post-actions/actions.js index 719ca69ee51fea..4656e34dd67289 100644 --- a/packages/editor/src/components/post-actions/actions.js +++ b/packages/editor/src/components/post-actions/actions.js @@ -1,13 +1,6 @@ -/** - * External dependencies - */ -import { paramCase as kebabCase } from 'change-case'; -import { downloadZip } from 'client-zip'; - /** * WordPress dependencies */ -import { downloadBlob } from '@wordpress/blob'; import { external, trash, backup } from '@wordpress/icons'; import { addQueryArgs } from '@wordpress/url'; import { useDispatch, useSelect } from '@wordpress/data'; @@ -16,8 +9,6 @@ import { store as coreStore } from '@wordpress/core-data'; import { __, _n, sprintf, _x } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { useMemo, useState } from '@wordpress/element'; -import { store as reusableBlocksStore } from '@wordpress/reusable-blocks'; -import { privateApis as patternsPrivateApis } from '@wordpress/patterns'; import { Button, @@ -40,17 +31,6 @@ import { store as editorStore } from '../../store'; import { unlock } from '../../lock-unlock'; import isTemplateRevertable from '../../store/utils/is-template-revertable'; -// Patterns. -export const { - PATTERN_TYPES, - PATTERN_DEFAULT_CATEGORY, - PATTERN_USER_CATEGORY, - EXCLUDED_PATTERN_SOURCES, - PATTERN_SYNC_TYPES, - CreatePatternModalContents, - useDuplicatePatternProps, -} = unlock( patternsPrivateApis ); - function getItemTitle( item ) { if ( typeof item.title === 'string' ) { return decodeEntities( item.title ); @@ -700,19 +680,10 @@ export const duplicatePostAction = { }, }; -const isTemplatePartRevertable = ( item ) => { - const hasThemeFile = item.templatePart.has_theme_file; - return canDeleteOrReset( item ) && hasThemeFile; -}; - const resetTemplateAction = { id: 'reset-template', label: __( 'Reset' ), - isEligible: ( item ) => { - return item.type === TEMPLATE_PART_POST_TYPE - ? isTemplatePartRevertable( item ) - : isTemplateRevertable( item ); - }, + isEligible: isTemplateRevertable, icon: backup, supportsBulk: true, hideModalHeader: true, @@ -723,47 +694,40 @@ const resetTemplateAction = { onActionPerformed, } ) => { const [ isBusy, setIsBusy ] = useState( false ); - const { revertTemplate, removeTemplates } = unlock( - useDispatch( editorStore ) - ); + const { revertTemplate } = unlock( useDispatch( editorStore ) ); const { saveEditedEntityRecord } = useDispatch( coreStore ); const { createSuccessNotice, createErrorNotice } = useDispatch( noticesStore ); const onConfirm = async () => { try { - if ( items[ 0 ].type === TEMPLATE_PART_POST_TYPE ) { - await removeTemplates( items ); - } else { - for ( const template of items ) { - if ( template.type === TEMPLATE_POST_TYPE ) { - await revertTemplate( template, { - allowUndo: false, - } ); - await saveEditedEntityRecord( - 'postType', - template.type, - template.id - ); - } - } - createSuccessNotice( - items.length > 1 - ? sprintf( - /* translators: The number of items. */ - __( '%s items reset.' ), - items.length - ) - : sprintf( - /* translators: The template/part's name. */ - __( '"%s" reset.' ), - decodeEntities( getItemTitle( items[ 0 ] ) ) - ), - { - type: 'snackbar', - id: 'revert-template-action', - } + for ( const template of items ) { + await revertTemplate( template, { + allowUndo: false, + } ); + await saveEditedEntityRecord( + 'postType', + template.type, + template.id ); } + + createSuccessNotice( + items.length > 1 + ? sprintf( + /* translators: The number of items. */ + __( '%s items reset.' ), + items.length + ) + : sprintf( + /* translators: The template/part's name. */ + __( '"%s" reset.' ), + decodeEntities( items[ 0 ].title.rendered ) + ), + { + type: 'snackbar', + id: 'revert-template-action', + } + ); } catch ( error ) { let fallbackErrorMessage; if ( items[ 0 ].type === TEMPLATE_POST_TYPE ) { @@ -1024,202 +988,6 @@ const renameTemplateAction = { }, }; -function getJsonFromItem( item ) { - return JSON.stringify( - { - __file: item.type, - title: item.title || item.name, - content: item.patternPost.content.raw, - syncStatus: item.patternPost.wp_pattern_sync_status, - }, - null, - 2 - ); -} - -export const exportPatternAsJSONAction = { - id: 'export-pattern', - label: __( 'Export as JSON' ), - supportsBulk: true, - isEligible: ( item ) => item.type === PATTERN_TYPES.user, - callback: async ( items ) => { - if ( items.length === 1 ) { - return downloadBlob( - `${ kebabCase( items[ 0 ].title || items[ 0 ].name ) }.json`, - getJsonFromItem( items[ 0 ] ), - 'application/json' - ); - } - const nameCount = {}; - const filesToZip = items.map( ( item ) => { - const name = kebabCase( item.title || item.name ); - nameCount[ name ] = ( nameCount[ name ] || 0 ) + 1; - return { - name: `${ - name + - ( nameCount[ name ] > 1 - ? '-' + ( nameCount[ name ] - 1 ) - : '' ) - }.json`, - lastModified: new Date(), - input: getJsonFromItem( item ), - }; - } ); - return downloadBlob( - __( 'patterns-export' ) + '.zip', - await downloadZip( filesToZip ).blob(), - 'application/zip' - ); - }, -}; - -const canDeleteOrReset = ( item ) => { - const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE; - const isUserPattern = item.type === PATTERN_TYPES.user; - return isUserPattern || ( isTemplatePart && item.isCustom ); -}; - -export const deletePatternAction = { - id: 'delete-pattern', - label: __( 'Delete' ), - isEligible: ( item ) => { - const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE; - const hasThemeFile = isTemplatePart && item.templatePart.has_theme_file; - return canDeleteOrReset( item ) && ! hasThemeFile; - }, - hideModalHeader: true, - supportsBulk: true, - RenderModal: ( { items, closeModal, onPerform } ) => { - const { __experimentalDeleteReusableBlock } = - useDispatch( reusableBlocksStore ); - const { createErrorNotice, createSuccessNotice } = - useDispatch( noticesStore ); - const { removeTemplates } = unlock( useDispatch( editorStore ) ); - - const deletePattern = async () => { - const promiseResult = await Promise.allSettled( - items.map( ( item ) => { - return __experimentalDeleteReusableBlock( item.id ); - } ) - ); - // If all the promises were fulfilled with success. - if ( - promiseResult.every( ( { status } ) => status === 'fulfilled' ) - ) { - let successMessage; - if ( promiseResult.length === 1 ) { - successMessage = sprintf( - /* translators: The posts's title. */ - __( '"%s" deleted.' ), - items[ 0 ].title - ); - } else { - successMessage = __( 'The patterns were deleted.' ); - } - createSuccessNotice( successMessage, { - type: 'snackbar', - id: 'edit-site-page-trashed', - } ); - } else { - // If there was at lease one failure. - let errorMessage; - // If we were trying to delete a single pattern. - if ( promiseResult.length === 1 ) { - if ( promiseResult[ 0 ].reason?.message ) { - errorMessage = promiseResult[ 0 ].reason.message; - } else { - errorMessage = __( - 'An error occurred while deleting the pattern.' - ); - } - // If we were trying to delete multiple patterns. - } else { - const errorMessages = new Set(); - const failedPromises = promiseResult.filter( - ( { status } ) => status === 'rejected' - ); - for ( const failedPromise of failedPromises ) { - if ( failedPromise.reason?.message ) { - errorMessages.add( failedPromise.reason.message ); - } - } - if ( errorMessages.size === 0 ) { - errorMessage = __( - 'An error occurred while deleting the patterns.' - ); - } else if ( errorMessages.size === 1 ) { - errorMessage = sprintf( - /* translators: %s: an error message */ - __( - 'An error occurred while deleting the patterns: %s' - ), - [ ...errorMessages ][ 0 ] - ); - } else { - errorMessage = sprintf( - /* translators: %s: a list of comma separated error messages */ - __( - 'Some errors occurred while deleting the patterns: %s' - ), - [ ...errorMessages ].join( ',' ) - ); - } - createErrorNotice( errorMessage, { - type: 'snackbar', - } ); - } - } - }; - const deleteItem = () => { - if ( items[ 0 ].type === TEMPLATE_PART_POST_TYPE ) { - removeTemplates( items ); - } else { - deletePattern(); - } - if ( onPerform ) { - onPerform(); - } - closeModal(); - }; - let questionMessage; - if ( items.length === 1 ) { - questionMessage = sprintf( - // translators: %s: The page's title. - __( 'Are you sure you want to delete "%s"?' ), - decodeEntities( items[ 0 ].title || items[ 0 ].name ) - ); - } else if ( - items.length > 1 && - items[ 0 ].type === TEMPLATE_PART_POST_TYPE - ) { - questionMessage = sprintf( - // translators: %d: The number of template parts (2 or more). - __( 'Are you sure you want to delete %d template parts?' ), - items.length - ); - } else { - questionMessage = sprintf( - // translators: %d: The number of patterns (2 or more). - __( 'Are you sure you want to delete %d patterns?' ), - items.length - ); - } - return ( - - { questionMessage } - - - - - - ); - }, -}; - export function usePostActions( postType, onActionPerformed ) { const { postTypeObject } = useSelect( ( select ) => { @@ -1258,8 +1026,6 @@ export function usePostActions( postType, onActionPerformed ) { : false, ! isTemplateOrTemplatePart && renamePostAction, isTemplateOrTemplatePart && renameTemplateAction, - isPattern && exportPatternAsJSONAction, - isPattern && deletePatternAction, ! isTemplateOrTemplatePart && trashPostAction, ].filter( Boolean );