From 113ab02e9d6038d66e0ea812c1e9793267459d8f Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Tue, 23 Apr 2024 23:52:40 +0100 Subject: [PATCH 1/4] Refactor: Remove post actions hook. --- .../src/components/post-actions/actions.js | 445 +++++++----------- .../src/components/post-actions/index.js | 33 +- packages/editor/src/private-apis.js | 4 +- 3 files changed, 187 insertions(+), 295 deletions(-) diff --git a/packages/editor/src/components/post-actions/actions.js b/packages/editor/src/components/post-actions/actions.js index 7b91c2c5c96e3e..96865c4bb164e7 100644 --- a/packages/editor/src/components/post-actions/actions.js +++ b/packages/editor/src/components/post-actions/actions.js @@ -8,7 +8,7 @@ import { decodeEntities } from '@wordpress/html-entities'; import { store as coreStore } from '@wordpress/core-data'; import { __, _n, sprintf } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; -import { useMemo, useState } from '@wordpress/element'; +import { useState } from '@wordpress/element'; import { Button, @@ -33,7 +33,7 @@ function getItemTitle( item ) { return decodeEntities( item.title?.rendered || '' ); } -const trashPostAction = { +export const trashPostAction = { id: 'move-to-trash', label: __( 'Move to Trash' ), isPrimary: true, @@ -172,200 +172,180 @@ const trashPostAction = { }, }; -function usePermanentlyDeletePostAction() { - const { createSuccessNotice, createErrorNotice } = - useDispatch( noticesStore ); - const { deleteEntityRecord } = useDispatch( coreStore ); - - return useMemo( - () => ( { - id: 'permanently-delete', - label: __( 'Permanently delete' ), - supportsBulk: true, - isEligible( { status } ) { - return status === 'trash'; - }, - async callback( posts, onActionPerformed ) { - const promiseResult = await Promise.allSettled( - posts.map( ( post ) => { - return deleteEntityRecord( - 'postType', - post.type, - post.id, - { force: true }, - { throwOnError: true } - ); - } ) - ); - // 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" permanently deleted.' ), - getItemTitle( posts[ 0 ] ) - ); +export const permanentlyDeletePostAction = { + id: 'permanently-delete', + label: __( 'Permanently delete' ), + supportsBulk: true, + isEligible( { status } ) { + return status === 'trash'; + }, + callback( posts, onActionPerformed ) { + return async ( { dispatch } ) => { + const { createSuccessNotice, createErrorNotice } = + dispatch( noticesStore ); + const { deleteEntityRecord } = dispatch( coreStore ); + const promiseResult = await Promise.allSettled( + posts.map( ( post ) => { + return deleteEntityRecord( + 'postType', + post.type, + post.id, + { force: true }, + { throwOnError: true } + ); + } ) + ); + // 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" permanently deleted.' ), + getItemTitle( posts[ 0 ] ) + ); + } else { + successMessage = __( + 'The posts were permanently deleted.' + ); + } + createSuccessNotice( successMessage, { + type: 'snackbar', + id: 'permanently-delete-post-action', + } ); + if ( onActionPerformed ) { + onActionPerformed( posts ); + } + } else { + // If there was at lease one failure. + let errorMessage; + // If we were trying to permanently delete a single post. + if ( promiseResult.length === 1 ) { + if ( promiseResult[ 0 ].reason?.message ) { + errorMessage = promiseResult[ 0 ].reason.message; } else { - successMessage = __( - 'The posts were permanently deleted.' + errorMessage = __( + 'An error occurred while permanently deleting the post.' ); } - createSuccessNotice( successMessage, { - type: 'snackbar', - id: 'permanently-delete-post-action', - } ); - if ( onActionPerformed ) { - onActionPerformed( posts ); - } + // If we were trying to permanently delete multiple posts } else { - // If there was at lease one failure. - let errorMessage; - // If we were trying to permanently delete a single post. - if ( promiseResult.length === 1 ) { - if ( promiseResult[ 0 ].reason?.message ) { - errorMessage = promiseResult[ 0 ].reason.message; - } else { - errorMessage = __( - 'An error occurred while permanently deleting the post.' - ); - } - // If we were trying to permanently delete multiple posts - } 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 permanently deleting the posts.' - ); - } else if ( errorMessages.size === 1 ) { - errorMessage = sprintf( - /* translators: %s: an error message */ - __( - 'An error occurred while permanently deleting the posts: %s' - ), - [ ...errorMessages ][ 0 ] - ); - } else { - errorMessage = sprintf( - /* translators: %s: a list of comma separated error messages */ - __( - 'Some errors occurred while permanently deleting the posts: %s' - ), - [ ...errorMessages ].join( ',' ) - ); + 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 ); } } - createErrorNotice( errorMessage, { - type: 'snackbar', - } ); - } - }, - } ), - [ createSuccessNotice, createErrorNotice, deleteEntityRecord ] - ); -} - -function useRestorePostAction() { - const { createSuccessNotice, createErrorNotice } = - useDispatch( noticesStore ); - const { editEntityRecord, saveEditedEntityRecord } = - useDispatch( coreStore ); - - return useMemo( - () => ( { - id: 'restore', - label: __( 'Restore' ), - isPrimary: true, - icon: backup, - supportsBulk: true, - isEligible( { status } ) { - return status === 'trash'; - }, - async callback( posts, onActionPerformed ) { - try { - for ( const post of posts ) { - await editEntityRecord( - 'postType', - post.type, - post.id, - { - status: 'draft', - } + if ( errorMessages.size === 0 ) { + errorMessage = __( + 'An error occurred while permanently deleting the posts.' + ); + } else if ( errorMessages.size === 1 ) { + errorMessage = sprintf( + /* translators: %s: an error message */ + __( + 'An error occurred while permanently deleting the posts: %s' + ), + [ ...errorMessages ][ 0 ] ); - await saveEditedEntityRecord( - 'postType', - post.type, - post.id, - { throwOnError: true } + } else { + errorMessage = sprintf( + /* translators: %s: a list of comma separated error messages */ + __( + 'Some errors occurred while permanently deleting the posts: %s' + ), + [ ...errorMessages ].join( ',' ) ); } + } + createErrorNotice( errorMessage, { + type: 'snackbar', + } ); + } + }; + }, +}; - createSuccessNotice( - posts.length > 1 - ? sprintf( - /* translators: The number of posts. */ - __( '%d posts have been restored.' ), - posts.length - ) - : sprintf( - /* translators: The number of posts. */ - __( '"%s" has been restored.' ), - getItemTitle( posts[ 0 ] ) - ), +export const restorePostAction = { + id: 'restore', + label: __( 'Restore' ), + isPrimary: true, + icon: backup, + supportsBulk: true, + isEligible( { status } ) { + return status === 'trash'; + }, + callback( posts, onActionPerformed ) { + return async ( { dispatch } ) => { + const { createSuccessNotice, createErrorNotice } = + dispatch( noticesStore ); + const { editEntityRecord, saveEditedEntityRecord } = + dispatch( coreStore ); + try { + for ( const post of posts ) { + await editEntityRecord( 'postType', post.type, post.id, { + status: 'draft', + } ); + await saveEditedEntityRecord( + 'postType', + post.type, + post.id, { - type: 'snackbar', - id: 'restore-post-action', + throwOnError: true, } ); - if ( onActionPerformed ) { - onActionPerformed( posts ); - } - } catch ( error ) { - let errorMessage; - if ( - error.message && - error.code !== 'unknown_error' && - error.message - ) { - errorMessage = error.message; - } else if ( posts.length > 1 ) { - errorMessage = __( - 'An error occurred while restoring the posts.' - ); - } else { - errorMessage = __( - 'An error occurred while restoring the post.' - ); - } + } - createErrorNotice( errorMessage, { type: 'snackbar' } ); + createSuccessNotice( + posts.length > 1 + ? sprintf( + /* translators: The number of posts. */ + __( '%d posts have been restored.' ), + posts.length + ) + : sprintf( + /* translators: The number of posts. */ + __( '"%s" has been restored.' ), + getItemTitle( posts[ 0 ] ) + ), + { + type: 'snackbar', + id: 'restore-post-action', + } + ); + if ( onActionPerformed ) { + onActionPerformed( posts ); } - }, - } ), - [ - createSuccessNotice, - createErrorNotice, - editEntityRecord, - saveEditedEntityRecord, - ] - ); -} + } catch ( error ) { + let errorMessage; + if ( + error.message && + error.code !== 'unknown_error' && + error.message + ) { + errorMessage = error.message; + } else if ( posts.length > 1 ) { + errorMessage = __( + 'An error occurred while restoring the posts.' + ); + } else { + errorMessage = __( + 'An error occurred while restoring the post.' + ); + } + + createErrorNotice( errorMessage, { type: 'snackbar' } ); + } + }; + }, +}; -const viewPostAction = { +export const viewPostAction = { id: 'view-post', label: __( 'View' ), isPrimary: true, @@ -382,7 +362,7 @@ const viewPostAction = { }, }; -const editPostAction = { +export const editPostAction = { id: 'edit-post', label: __( 'Edit' ), isPrimary: true, @@ -396,7 +376,7 @@ const editPostAction = { } }, }; -const postRevisionsAction = { +export const postRevisionsAction = { id: 'view-post-revisions', label: __( 'View revisions' ), isPrimary: false, @@ -422,7 +402,7 @@ const postRevisionsAction = { }, }; -const renamePostAction = { +export const renamePostAction = { id: 'rename-post', label: __( 'Rename' ), isEligible( post ) { @@ -500,7 +480,7 @@ const renamePostAction = { }, }; -const resetTemplateAction = { +export const resetTemplateAction = { id: 'reset-template', label: __( 'Reset' ), isEligible: isTemplateRevertable, @@ -612,7 +592,7 @@ function isTemplateRemovable( template ) { ); } -const deleteTemplateAction = { +export const deleteTemplateAction = { id: 'delete-template', label: __( 'Delete' ), isEligible: isTemplateRemovable, @@ -663,7 +643,7 @@ const deleteTemplateAction = { }, }; -const renameTemplateAction = { +export const renameTemplateAction = { id: 'rename-template', label: __( 'Rename' ), isEligible: ( template ) => { @@ -767,96 +747,3 @@ const renameTemplateAction = { ); }, }; - -export function usePostActions( onActionPerformed, actionIds = null ) { - const permanentlyDeletePostAction = usePermanentlyDeletePostAction(); - const restorePostAction = useRestorePostAction(); - return useMemo( - () => { - // By default, return all actions... - const defaultActions = [ - editPostAction, - resetTemplateAction, - viewPostAction, - restorePostAction, - deleteTemplateAction, - permanentlyDeletePostAction, - postRevisionsAction, - renamePostAction, - renameTemplateAction, - trashPostAction, - ]; - - // ... unless `actionIds` was specified, in which case we find the - // actions matching the given IDs. - const actions = actionIds - ? actionIds.map( ( actionId ) => - defaultActions.find( ( { id } ) => actionId === id ) - ) - : defaultActions; - - if ( onActionPerformed ) { - for ( let i = 0; i < actions.length; ++i ) { - if ( actions[ i ].callback ) { - const existingCallback = actions[ i ].callback; - actions[ i ] = { - ...actions[ i ], - callback: ( items, _onActionPerformed ) => { - existingCallback( items, ( _items ) => { - if ( _onActionPerformed ) { - _onActionPerformed( _items ); - } - onActionPerformed( - actions[ i ].id, - _items - ); - } ); - }, - }; - } - if ( actions[ i ].RenderModal ) { - const ExistingRenderModal = actions[ i ].RenderModal; - actions[ i ] = { - ...actions[ i ], - RenderModal: ( props ) => { - return ( - { - if ( props.onActionPerformed ) { - props.onActionPerformed( - _items - ); - } - onActionPerformed( - actions[ i ].id, - _items - ); - } } - /> - ); - }, - }; - } - } - } - return actions; - }, - - // Disable reason: if provided, `actionIds` is a shallow array of - // strings, and the strings themselves should be part of the useMemo - // dependencies. Two different disable statements are needed, as the - // first flags what it thinks are missing dependencies, and the second - // flags the array spread operation. - // - // eslint-disable-next-line react-hooks/exhaustive-deps - [ - // eslint-disable-next-line react-hooks/exhaustive-deps - ...( actionIds || [] ), - permanentlyDeletePostAction, - restorePostAction, - onActionPerformed, - ] - ); -} diff --git a/packages/editor/src/components/post-actions/index.js b/packages/editor/src/components/post-actions/index.js index 48d83f992e7056..a26d85d57c0b6b 100644 --- a/packages/editor/src/components/post-actions/index.js +++ b/packages/editor/src/components/post-actions/index.js @@ -15,7 +15,12 @@ import { moreVertical } from '@wordpress/icons'; * Internal dependencies */ import { unlock } from '../../lock-unlock'; -import { usePostActions } from './actions'; +import { + viewPostAction, + postRevisionsAction, + renamePostAction, + trashPostAction, +} from './actions'; import { store as editorStore } from '../../store'; import { TEMPLATE_POST_TYPE, @@ -31,13 +36,6 @@ const { kebabCase, } = unlock( componentsPrivateApis ); -const POST_ACTIONS_WHILE_EDITING = [ - 'view-post', - 'view-post-revisions', - 'rename-post', - 'move-to-trash', -]; - export default function PostActions( { onActionPerformed, buttonProps } ) { const { postType, item } = useSelect( ( select ) => { const { getCurrentPostType, getCurrentPost } = select( editorStore ); @@ -46,16 +44,23 @@ export default function PostActions( { onActionPerformed, buttonProps } ) { item: getCurrentPost(), }; } ); - const allActions = usePostActions( - onActionPerformed, - POST_ACTIONS_WHILE_EDITING - ); const actions = useMemo( () => { - return allActions.filter( ( action ) => { + const eligibleActions = [ + viewPostAction, + postRevisionsAction, + renamePostAction, + trashPostAction, + ].filter( ( action ) => { return ! action.isEligible || action.isEligible( item ); } ); - }, [ allActions, item ] ); + eligibleActions.forEach( ( action ) => { + action.onActionPerformed = ( _item ) => { + onActionPerformed( action.id, _item ); + action.onActionPerformed?.( _item ); + }; + } ); + }, [ item, onActionPerformed ] ); if ( [ diff --git a/packages/editor/src/private-apis.js b/packages/editor/src/private-apis.js index aae3762794b4d6..d5ff8cc0d624aa 100644 --- a/packages/editor/src/private-apis.js +++ b/packages/editor/src/private-apis.js @@ -23,7 +23,7 @@ import PostViewLink from './components/post-view-link'; import PreviewDropdown from './components/preview-dropdown'; import PreferencesModal from './components/preferences-modal'; import PostActions from './components/post-actions'; -import { usePostActions } from './components/post-actions/actions'; +import postActions from './components/post-actions/actions'; import PostCardPanel from './components/post-card-panel'; import PostStatus from './components/post-status'; import ToolsMoreMenuGroup from './components/more-menu/tools-more-menu-group'; @@ -47,7 +47,7 @@ lock( privateApis, { PostViewLink, PreviewDropdown, PreferencesModal, - usePostActions, + postActions, PostCardPanel, PostStatus, ToolsMoreMenuGroup, From 3d745c9fdd6fc8ca7089c1fa12e6c9d472b0f545 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 24 Apr 2024 12:07:50 +0100 Subject: [PATCH 2/4] implement the thunk based approach --- package-lock.json | 2 + packages/dataviews/package.json | 1 + packages/dataviews/src/bulk-actions.js | 11 +++- packages/dataviews/src/item-actions.js | 29 ++++++++- .../src/components/page-pages/index.js | 61 +++++++++++-------- .../src/components/page-patterns/index.js | 33 +++++----- .../src/components/page-templates/index.js | 54 +++++++++------- .../src/components/post-actions/actions.js | 52 ++++++---------- .../src/components/post-actions/index.js | 27 ++++++-- packages/editor/src/private-apis.js | 2 +- 10 files changed, 166 insertions(+), 106 deletions(-) diff --git a/package-lock.json b/package-lock.json index 68ee222ddb1be3..23e229d1fcf868 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53796,6 +53796,7 @@ "@wordpress/a11y": "file:../a11y", "@wordpress/components": "file:../components", "@wordpress/compose": "file:../compose", + "@wordpress/data": "file:../data", "@wordpress/element": "file:../element", "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", @@ -68987,6 +68988,7 @@ "@wordpress/a11y": "file:../a11y", "@wordpress/components": "file:../components", "@wordpress/compose": "file:../compose", + "@wordpress/data": "file:../data", "@wordpress/element": "file:../element", "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", diff --git a/packages/dataviews/package.json b/packages/dataviews/package.json index 394c41e35cea3c..b8d4e04c0c4e07 100644 --- a/packages/dataviews/package.json +++ b/packages/dataviews/package.json @@ -32,6 +32,7 @@ "@wordpress/a11y": "file:../a11y", "@wordpress/components": "file:../components", "@wordpress/compose": "file:../compose", + "@wordpress/data": "file:../data", "@wordpress/element": "file:../element", "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", diff --git a/packages/dataviews/src/bulk-actions.js b/packages/dataviews/src/bulk-actions.js index 5e4139fe0622e7..36765c0561d512 100644 --- a/packages/dataviews/src/bulk-actions.js +++ b/packages/dataviews/src/bulk-actions.js @@ -8,6 +8,7 @@ import { } from '@wordpress/components'; import { __, sprintf, _n } from '@wordpress/i18n'; import { useMemo, useState, useCallback, useEffect } from '@wordpress/element'; +import { useRegistry } from '@wordpress/data'; /** * Internal dependencies @@ -69,6 +70,7 @@ function ActionWithModal( { } function BulkActionItem( { action, selectedItems, setActionWithModal } ) { + const registry = useRegistry(); const eligibleItems = useMemo( () => { return selectedItems.filter( ( item ) => action.isEligible( item ) ); }, [ action, selectedItems ] ); @@ -84,7 +86,14 @@ function BulkActionItem( { action, selectedItems, setActionWithModal } ) { if ( shouldShowModal ) { setActionWithModal( action ); } else { - await action.callback( eligibleItems ); + const returnResult = await action.callback( eligibleItems ); + if ( typeof returnResult === 'function' ) { + await returnResult( { + registry, + select: registry.select, + dispatch: registry.dispatch, + } ); + } } } } suffix={ diff --git a/packages/dataviews/src/item-actions.js b/packages/dataviews/src/item-actions.js index db4da0d4924896..0bf83022257e03 100644 --- a/packages/dataviews/src/item-actions.js +++ b/packages/dataviews/src/item-actions.js @@ -10,6 +10,7 @@ import { import { __ } from '@wordpress/i18n'; import { useMemo, useState } from '@wordpress/element'; import { moreVertical } from '@wordpress/icons'; +import { useRegistry } from '@wordpress/data'; /** * Internal dependencies @@ -79,6 +80,7 @@ function ActionWithModal( { action, item, ActionTrigger } ) { } function ActionsDropdownMenuGroup( { actions, item } ) { + const registry = useRegistry(); return ( { actions.map( ( action ) => { @@ -96,7 +98,18 @@ function ActionsDropdownMenuGroup( { actions, item } ) { action.callback( [ item ] ) } + onClick={ async () => { + const returnResult = await action.callback( [ + item, + ] ); + if ( typeof returnResult === 'function' ) { + await returnResult( { + registry, + select: registry.select, + dispatch: registry.dispatch, + } ); + } + } } /> ); } ) } @@ -105,6 +118,7 @@ function ActionsDropdownMenuGroup( { actions, item } ) { } export default function ItemActions( { item, actions, isCompact } ) { + const registry = useRegistry(); const { primaryActions, eligibleActions } = useMemo( () => { // If an action is eligible for all items, doesn't need // to provide the `isEligible` function. @@ -148,7 +162,18 @@ export default function ItemActions( { item, actions, isCompact } ) { action.callback( [ item ] ) } + onClick={ async () => { + const returnResult = await action.callback( [ + item, + ] ); + if ( typeof returnResult === 'function' ) { + await returnResult( { + registry, + select: registry.select, + dispatch: registry.dispatch, + } ); + } + } } /> ); } ) } diff --git a/packages/edit-site/src/components/page-pages/index.js b/packages/edit-site/src/components/page-pages/index.js index e98896ab2e8cc9..307761e91b2c29 100644 --- a/packages/edit-site/src/components/page-pages/index.js +++ b/packages/edit-site/src/components/page-pages/index.js @@ -34,7 +34,7 @@ import AddNewPageModal from '../add-new-page'; import Media from '../media'; import { unlock } from '../../lock-unlock'; -const { usePostActions } = unlock( editorPrivateApis ); +const { postActions } = unlock( editorPrivateApis ); const { useLocation, useHistory } = unlock( routerPrivateApis ); @@ -189,15 +189,15 @@ function FeaturedImage( { item, viewType } ) { ); } -const PAGE_ACTIONS = [ - 'edit-post', - 'view-post', - 'restore', - 'permanently-delete', - 'view-post-revisions', - 'rename-post', - 'move-to-trash', -]; +const { + editPostAction, + viewPostAction, + restorePostAction, + permanentlyDeletePostAction, + postRevisionsAction, + renamePostAction, + trashPostAction, +} = postActions; export default function PagePages() { const postType = 'page'; @@ -350,20 +350,33 @@ export default function PagePages() { ], [ authors, view.type ] ); - const onActionPerformed = useCallback( - ( actionId, items ) => { - if ( actionId === 'edit-post' ) { - const post = items[ 0 ]; - history.push( { - postId: post.id, - postType: post.type, - canvas: 'edit', - } ); - } - }, - [ history ] - ); - const actions = usePostActions( onActionPerformed, PAGE_ACTIONS ); + + const actions = useMemo( () => { + const onEditPostActionPerformed = ( items ) => { + const post = items[ 0 ]; + history.push( { + postId: post.id, + postType: post.type, + canvas: 'edit', + } ); + }; + return [ + { + ...editPostAction, + onActionPerformed: ( ...args ) => { + onEditPostActionPerformed( ...args ); + editPostAction.onActionPerformed?.( ...args ); + }, + }, + viewPostAction, + restorePostAction, + permanentlyDeletePostAction, + postRevisionsAction, + renamePostAction, + trashPostAction, + ]; + }, [ history ] ); + const onChangeView = useCallback( ( newView ) => { if ( newView.type !== view.type ) { diff --git a/packages/edit-site/src/components/page-patterns/index.js b/packages/edit-site/src/components/page-patterns/index.js index 102f1b3cd075e9..580749a28ed69b 100644 --- a/packages/edit-site/src/components/page-patterns/index.js +++ b/packages/edit-site/src/components/page-patterns/index.js @@ -67,7 +67,7 @@ import { useAddedBy } from '../page-templates/hooks'; const { ExperimentalBlockEditorProvider, useGlobalStyle } = unlock( blockEditorPrivateApis ); -const { usePostActions } = unlock( editorPrivateApis ); +const { postActions } = unlock( editorPrivateApis ); const { useHistory } = unlock( routerPrivateApis ); const EMPTY_ARRAY = []; @@ -252,6 +252,8 @@ function Title( { item, categoryId } ) { ); } +const { editPostAction, postRevisionsAction } = postActions; + export default function DataviewsPatterns() { const { categoryType, @@ -394,9 +396,9 @@ export default function DataviewsPatterns() { }, [ patterns, view, fields, type ] ); const history = useHistory(); - const onActionPerformed = useCallback( - ( actionId, items ) => { - if ( actionId === 'edit-post' ) { + const actions = useMemo( () => { + if ( type === TEMPLATE_PART_POST_TYPE ) { + const onEditPostActionPerformed = ( items ) => { const post = items[ 0 ]; history.push( { postId: post.id, @@ -405,21 +407,18 @@ export default function DataviewsPatterns() { categoryType: type, canvas: 'edit', } ); - } - }, - [ history, categoryId, type ] - ); - const [ editAction, viewRevisionsAction ] = usePostActions( - onActionPerformed, - [ 'edit-post', 'view-post-revisions' ] - ); - const actions = useMemo( () => { - if ( type === TEMPLATE_PART_POST_TYPE ) { + }; return [ - editAction, + { + ...editPostAction, + onActionPerformed: ( ...args ) => { + onEditPostActionPerformed( ...args ); + editPostAction.onActionPerformed?.( ...args ); + }, + }, renameAction, duplicateTemplatePartAction, - viewRevisionsAction, + postRevisionsAction, resetAction, deleteAction, ]; @@ -431,7 +430,7 @@ export default function DataviewsPatterns() { resetAction, deleteAction, ]; - }, [ type, editAction, viewRevisionsAction ] ); + }, [ type, history, categoryId ] ); const onChangeView = useCallback( ( newView ) => { if ( newView.type !== view.type ) { diff --git a/packages/edit-site/src/components/page-templates/index.js b/packages/edit-site/src/components/page-templates/index.js index a64cb63cfbc8c4..66fdb897ffdb03 100644 --- a/packages/edit-site/src/components/page-templates/index.js +++ b/packages/edit-site/src/components/page-templates/index.js @@ -45,7 +45,7 @@ import { import usePatternSettings from '../page-patterns/use-pattern-settings'; import { unlock } from '../../lock-unlock'; -const { usePostActions } = unlock( editorPrivateApis ); +const { postActions } = unlock( editorPrivateApis ); const { ExperimentalBlockEditorProvider, useGlobalStyle } = unlock( blockEditorPrivateApis @@ -187,13 +187,13 @@ function Preview( { item, viewType } ) { ); } -const TEMPLATE_ACTIONS = [ - 'edit-post', - 'reset-template', - 'rename-template', - 'view-post-revisions', - 'delete-template', -]; +const { + editPostAction, + resetTemplateAction, + renameTemplateAction, + postRevisionsAction, + deleteTemplateAction, +} = postActions; export default function PageTemplates() { const { params } = useLocation(); @@ -335,21 +335,29 @@ export default function PageTemplates() { return filterSortAndPaginate( records, view, fields ); }, [ records, view, fields ] ); - const onActionPerformed = useCallback( - ( actionId, items ) => { - if ( actionId === 'edit-post' ) { - const post = items[ 0 ]; - history.push( { - postId: post.id, - postType: post.type, - canvas: 'edit', - } ); - } - }, - [ history ] - ); - - const actions = usePostActions( onActionPerformed, TEMPLATE_ACTIONS ); + const actions = useMemo( () => { + const onEditPostActionPerformed = ( items ) => { + const post = items[ 0 ]; + history.push( { + postId: post.id, + postType: post.type, + canvas: 'edit', + } ); + }; + return [ + { + ...editPostAction, + onActionPerformed: ( ...args ) => { + onEditPostActionPerformed( ...args ); + editPostAction.onActionPerformed?.( ...args ); + }, + }, + resetTemplateAction, + renameTemplateAction, + postRevisionsAction, + deleteTemplateAction, + ]; + }, [ history ] ); const onChangeView = useCallback( ( newView ) => { diff --git a/packages/editor/src/components/post-actions/actions.js b/packages/editor/src/components/post-actions/actions.js index 96865c4bb164e7..bcbee087e0b510 100644 --- a/packages/editor/src/components/post-actions/actions.js +++ b/packages/editor/src/components/post-actions/actions.js @@ -43,7 +43,7 @@ export const trashPostAction = { }, supportsBulk: true, hideModalHeader: true, - RenderModal: ( { items: posts, closeModal, onActionPerformed } ) => { + RenderModal: ( { items: posts, closeModal } ) => { const { createSuccessNotice, createErrorNotice } = useDispatch( noticesStore ); const { deleteEntityRecord } = useDispatch( coreStore ); @@ -158,9 +158,7 @@ export const trashPostAction = { type: 'snackbar', } ); } - if ( onActionPerformed ) { - onActionPerformed( posts ); - } + this?.onActionPerformed?.( posts ); closeModal(); } } > @@ -179,7 +177,7 @@ export const permanentlyDeletePostAction = { isEligible( { status } ) { return status === 'trash'; }, - callback( posts, onActionPerformed ) { + callback( posts ) { return async ( { dispatch } ) => { const { createSuccessNotice, createErrorNotice } = dispatch( noticesStore ); @@ -215,9 +213,7 @@ export const permanentlyDeletePostAction = { type: 'snackbar', id: 'permanently-delete-post-action', } ); - if ( onActionPerformed ) { - onActionPerformed( posts ); - } + this?.onActionPerformed?.( posts ); } else { // If there was at lease one failure. let errorMessage; @@ -280,7 +276,7 @@ export const restorePostAction = { isEligible( { status } ) { return status === 'trash'; }, - callback( posts, onActionPerformed ) { + callback( posts ) { return async ( { dispatch } ) => { const { createSuccessNotice, createErrorNotice } = dispatch( noticesStore ); @@ -318,9 +314,7 @@ export const restorePostAction = { id: 'restore-post-action', } ); - if ( onActionPerformed ) { - onActionPerformed( posts ); - } + this?.onActionPerformed?.( posts ); } catch ( error ) { let errorMessage; if ( @@ -353,12 +347,10 @@ export const viewPostAction = { isEligible( post ) { return post.status !== 'trash'; }, - callback( posts, onActionPerformed ) { + callback( posts ) { const post = posts[ 0 ]; window.open( post.link, '_blank' ); - if ( onActionPerformed ) { - onActionPerformed( posts ); - } + this?.onActionPerformed?.( posts ); }, }; @@ -370,10 +362,8 @@ export const editPostAction = { isEligible( { status } ) { return status !== 'trash'; }, - callback( posts, onActionPerformed ) { - if ( onActionPerformed ) { - onActionPerformed( posts ); - } + callback( posts ) { + this?.onActionPerformed?.( posts ); }, }; export const postRevisionsAction = { @@ -390,15 +380,13 @@ export const postRevisionsAction = { post?._links?.[ 'version-history' ]?.[ 0 ]?.count ?? 0; return lastRevisionId && revisionsCount > 1; }, - callback( posts, onActionPerformed ) { + callback( posts ) { const post = posts[ 0 ]; const href = addQueryArgs( 'revision.php', { revision: post?._links?.[ 'predecessor-version' ]?.[ 0 ]?.id, } ); document.location.href = href; - if ( onActionPerformed ) { - onActionPerformed( posts ); - } + this?.onActionPerformed?.( posts ); }, }; @@ -408,7 +396,7 @@ export const renamePostAction = { isEligible( post ) { return post.status !== 'trash'; }, - RenderModal: ( { items, closeModal, onActionPerformed } ) => { + RenderModal: ( { items, closeModal } ) => { const [ item ] = items; const originalTitle = decodeEntities( typeof item.title === 'string' ? item.title : item.title.rendered @@ -435,7 +423,7 @@ export const renamePostAction = { createSuccessNotice( __( 'Name updated' ), { type: 'snackbar', } ); - onActionPerformed?.( items ); + this?.onActionPerformed?.( items ); } catch ( error ) { const errorMessage = error.message && error.code !== 'unknown_error' @@ -486,7 +474,7 @@ export const resetTemplateAction = { isEligible: isTemplateRevertable, supportsBulk: true, hideModalHeader: true, - RenderModal: ( { items, closeModal, onActionPerformed } ) => { + RenderModal: ( { items, closeModal } ) => { const { revertTemplate } = unlock( useDispatch( editorStore ) ); const { saveEditedEntityRecord } = useDispatch( coreStore ); const { createSuccessNotice, createErrorNotice } = @@ -563,7 +551,7 @@ export const resetTemplateAction = { variant="primary" onClick={ async () => { await onConfirm( items ); - onActionPerformed?.( items ); + this?.onActionPerformed?.( items ); closeModal(); } } > @@ -598,7 +586,7 @@ export const deleteTemplateAction = { isEligible: isTemplateRemovable, supportsBulk: true, hideModalHeader: true, - RenderModal: ( { items: templates, closeModal, onActionPerformed } ) => { + RenderModal: ( { items: templates, closeModal } ) => { const { removeTemplates } = unlock( useDispatch( editorStore ) ); return ( @@ -631,7 +619,7 @@ export const deleteTemplateAction = { await removeTemplates( templates, { allowUndo: false, } ); - onActionPerformed?.( templates ); + this?.onActionPerformed?.( templates ); closeModal(); } } > @@ -657,7 +645,7 @@ export const renameTemplateAction = { } return true; }, - RenderModal: ( { items: templates, closeModal, onActionPerformed } ) => { + RenderModal: ( { items: templates, closeModal } ) => { const template = templates[ 0 ]; const title = decodeEntities( template.title.rendered ); const [ editedTitle, setEditedTitle ] = useState( title ); @@ -699,7 +687,7 @@ export const renameTemplateAction = { type: 'snackbar', } ); - onActionPerformed?.( templates ); + this?.onActionPerformed?.( templates ); } catch ( error ) { const fallbackErrorMessage = template.type === TEMPLATE_POST_TYPE diff --git a/packages/editor/src/components/post-actions/index.js b/packages/editor/src/components/post-actions/index.js index a26d85d57c0b6b..ce9c4085cdb9b4 100644 --- a/packages/editor/src/components/post-actions/index.js +++ b/packages/editor/src/components/post-actions/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useSelect } from '@wordpress/data'; +import { useRegistry, useSelect } from '@wordpress/data'; import { useState, useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { @@ -54,10 +54,13 @@ export default function PostActions( { onActionPerformed, buttonProps } ) { ].filter( ( action ) => { return ! action.isEligible || action.isEligible( item ); } ); - eligibleActions.forEach( ( action ) => { - action.onActionPerformed = ( _item ) => { - onActionPerformed( action.id, _item ); - action.onActionPerformed?.( _item ); + return eligibleActions.map( ( action ) => { + return { + ...action, + onActionPerformed: ( args ) => { + onActionPerformed?.( action.id, ...args ); + action.onActionPerformed?.( ...args ); + }, }; } ); }, [ item, onActionPerformed ] ); @@ -141,6 +144,7 @@ function ActionWithModal( { action, item, ActionTrigger } ) { // Copied as is from packages/dataviews/src/item-actions.js function ActionsDropdownMenuGroup( { actions, item } ) { + const registry = useRegistry(); return ( { actions.map( ( action ) => { @@ -158,7 +162,18 @@ function ActionsDropdownMenuGroup( { actions, item } ) { action.callback( [ item ] ) } + onClick={ async () => { + const returnResult = await action.callback( [ + item, + ] ); + if ( typeof returnResult === 'function' ) { + await returnResult( { + registry, + select: registry.select, + dispatch: registry.dispatch, + } ); + } + } } /> ); } ) } diff --git a/packages/editor/src/private-apis.js b/packages/editor/src/private-apis.js index d5ff8cc0d624aa..c66fc85760b01d 100644 --- a/packages/editor/src/private-apis.js +++ b/packages/editor/src/private-apis.js @@ -23,7 +23,7 @@ import PostViewLink from './components/post-view-link'; import PreviewDropdown from './components/preview-dropdown'; import PreferencesModal from './components/preferences-modal'; import PostActions from './components/post-actions'; -import postActions from './components/post-actions/actions'; +import * as postActions from './components/post-actions/actions'; import PostCardPanel from './components/post-card-panel'; import PostStatus from './components/post-status'; import ToolsMoreMenuGroup from './components/more-menu/tools-more-menu-group'; From d7498128c785e0d495524ce8bd256efd82673dc3 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 24 Apr 2024 12:45:40 +0100 Subject: [PATCH 3/4] pass action to the render modal --- packages/dataviews/src/bulk-actions.js | 11 ++++++++++- packages/dataviews/src/item-actions.js | 1 + .../src/components/post-actions/actions.js | 19 ++++++++++--------- .../src/components/post-actions/index.js | 3 ++- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/dataviews/src/bulk-actions.js b/packages/dataviews/src/bulk-actions.js index 36765c0561d512..caea89a918f3d2 100644 --- a/packages/dataviews/src/bulk-actions.js +++ b/packages/dataviews/src/bulk-actions.js @@ -53,6 +53,15 @@ function ActionWithModal( { const onCloseModal = useCallback( () => { setActionWithModal( undefined ); }, [ setActionWithModal ] ); + const actionWithCallbacks = useMemo( () => { + return { + ...action, + onActionPerformed: ( ...args ) => { + onMenuOpenChange( false ); + action.onActionPerformed( ...args ); + }, + }; + }, [ action, onMenuOpenChange ] ); return ( onMenuOpenChange( false ) } + action={ actionWithCallbacks } /> ); diff --git a/packages/dataviews/src/item-actions.js b/packages/dataviews/src/item-actions.js index 0bf83022257e03..42fcfe756e89e3 100644 --- a/packages/dataviews/src/item-actions.js +++ b/packages/dataviews/src/item-actions.js @@ -72,6 +72,7 @@ function ActionWithModal( { action, item, ActionTrigger } ) { setIsModalOpen( false ) } + action={ action } /> ) } diff --git a/packages/editor/src/components/post-actions/actions.js b/packages/editor/src/components/post-actions/actions.js index bcbee087e0b510..da91f010712c97 100644 --- a/packages/editor/src/components/post-actions/actions.js +++ b/packages/editor/src/components/post-actions/actions.js @@ -43,7 +43,7 @@ export const trashPostAction = { }, supportsBulk: true, hideModalHeader: true, - RenderModal: ( { items: posts, closeModal } ) => { + RenderModal( { items: posts, closeModal, action } ) { const { createSuccessNotice, createErrorNotice } = useDispatch( noticesStore ); const { deleteEntityRecord } = useDispatch( coreStore ); @@ -158,7 +158,7 @@ export const trashPostAction = { type: 'snackbar', } ); } - this?.onActionPerformed?.( posts ); + action.onActionPerformed?.( posts ); closeModal(); } } > @@ -396,7 +396,7 @@ export const renamePostAction = { isEligible( post ) { return post.status !== 'trash'; }, - RenderModal: ( { items, closeModal } ) => { + RenderModal: ( { items, closeModal, action } ) => { const [ item ] = items; const originalTitle = decodeEntities( typeof item.title === 'string' ? item.title : item.title.rendered @@ -423,7 +423,7 @@ export const renamePostAction = { createSuccessNotice( __( 'Name updated' ), { type: 'snackbar', } ); - this?.onActionPerformed?.( items ); + action.onActionPerformed?.( items ); } catch ( error ) { const errorMessage = error.message && error.code !== 'unknown_error' @@ -474,7 +474,7 @@ export const resetTemplateAction = { isEligible: isTemplateRevertable, supportsBulk: true, hideModalHeader: true, - RenderModal: ( { items, closeModal } ) => { + RenderModal: ( { items, closeModal, action } ) => { const { revertTemplate } = unlock( useDispatch( editorStore ) ); const { saveEditedEntityRecord } = useDispatch( coreStore ); const { createSuccessNotice, createErrorNotice } = @@ -509,6 +509,7 @@ export const resetTemplateAction = { id: 'revert-template-action', } ); + action.onActionPerformed?.( items ); } catch ( error ) { let fallbackErrorMessage; if ( items[ 0 ].type === TEMPLATE_POST_TYPE ) { @@ -586,7 +587,7 @@ export const deleteTemplateAction = { isEligible: isTemplateRemovable, supportsBulk: true, hideModalHeader: true, - RenderModal: ( { items: templates, closeModal } ) => { + RenderModal: ( { items: templates, closeModal, action } ) => { const { removeTemplates } = unlock( useDispatch( editorStore ) ); return ( @@ -619,7 +620,7 @@ export const deleteTemplateAction = { await removeTemplates( templates, { allowUndo: false, } ); - this?.onActionPerformed?.( templates ); + action.onActionPerformed?.( templates ); closeModal(); } } > @@ -645,7 +646,7 @@ export const renameTemplateAction = { } return true; }, - RenderModal: ( { items: templates, closeModal } ) => { + RenderModal: ( { items: templates, closeModal, action } ) => { const template = templates[ 0 ]; const title = decodeEntities( template.title.rendered ); const [ editedTitle, setEditedTitle ] = useState( title ); @@ -687,7 +688,7 @@ export const renameTemplateAction = { type: 'snackbar', } ); - this?.onActionPerformed?.( templates ); + action.onActionPerformed?.( templates ); } catch ( error ) { const fallbackErrorMessage = template.type === TEMPLATE_POST_TYPE diff --git a/packages/editor/src/components/post-actions/index.js b/packages/editor/src/components/post-actions/index.js index ce9c4085cdb9b4..acf9596359dd4e 100644 --- a/packages/editor/src/components/post-actions/index.js +++ b/packages/editor/src/components/post-actions/index.js @@ -57,7 +57,7 @@ export default function PostActions( { onActionPerformed, buttonProps } ) { return eligibleActions.map( ( action ) => { return { ...action, - onActionPerformed: ( args ) => { + onActionPerformed: ( ...args ) => { onActionPerformed?.( action.id, ...args ); action.onActionPerformed?.( ...args ); }, @@ -135,6 +135,7 @@ function ActionWithModal( { action, item, ActionTrigger } ) { setIsModalOpen( false ) } + action={ action } /> ) } From 731c0718327cba586c7481e59d10223644b3a77a Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 24 Apr 2024 12:54:07 +0100 Subject: [PATCH 4/4] fix onActionPerformed may not exist --- packages/dataviews/src/bulk-actions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dataviews/src/bulk-actions.js b/packages/dataviews/src/bulk-actions.js index caea89a918f3d2..8509081673369a 100644 --- a/packages/dataviews/src/bulk-actions.js +++ b/packages/dataviews/src/bulk-actions.js @@ -58,7 +58,7 @@ function ActionWithModal( { ...action, onActionPerformed: ( ...args ) => { onMenuOpenChange( false ); - action.onActionPerformed( ...args ); + action.onActionPerformed?.( ...args ); }, }; }, [ action, onMenuOpenChange ] );