From ef65c8b9fceadec403a4cd1b473cc2b45d356c52 Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Wed, 13 Dec 2023 11:34:10 +0200 Subject: [PATCH] Performance: Improve opening inserter in post editor (#57006) * Performance: Improve opening inserter in post editor * make selector private --- .../src/components/inserter/menu.js | 10 +-- .../src/store/private-selectors.js | 45 +++++++++++ packages/block-editor/src/store/selectors.js | 74 ++----------------- packages/block-editor/src/store/utils.js | 74 +++++++++++++++++++ 4 files changed, 129 insertions(+), 74 deletions(-) create mode 100644 packages/block-editor/src/store/utils.js diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index a6d752848538e7..4f028eb69c6662 100644 --- a/packages/block-editor/src/components/inserter/menu.js +++ b/packages/block-editor/src/components/inserter/menu.js @@ -22,6 +22,7 @@ import { useDebouncedInput } from '@wordpress/compose'; /** * Internal dependencies */ +import { unlock } from '../../lock-unlock'; import Tips from './tips'; import InserterPreviewPanel from './preview-panel'; import BlockTypesTab from './block-types-tab'; @@ -68,12 +69,11 @@ function InserterMenu( } ); const { showPatterns, inserterItems } = useSelect( ( select ) => { - const { __experimentalGetAllowedPatterns, getInserterItems } = - select( blockEditorStore ); + const { hasAllowedPatterns, getInserterItems } = unlock( + select( blockEditorStore ) + ); return { - showPatterns: !! __experimentalGetAllowedPatterns( - destinationRootClientId - ).length, + showPatterns: hasAllowedPatterns( destinationRootClientId ), inserterItems: getInserterItems( destinationRootClientId ), }; }, diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index c4220e6e7e516c..98a75122f47245 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -10,7 +10,12 @@ import { getBlockOrder, getBlockParents, getBlockEditingMode, + getSettings, + __experimentalGetParsedPattern, + canInsertBlockType, + __experimentalGetAllowedPatterns, } from './selectors'; +import { getUserPatterns, checkAllowListRecursive } from './utils'; /** * Returns true if the block interface is hidden, or false otherwise. @@ -236,3 +241,43 @@ export const getInserterMediaCategories = createSelector( state.registeredInserterMediaCategories, ] ); + +/** + * Returns whether there is at least one allowed pattern for inner blocks children. + * This is useful for deferring the parsing of all patterns until needed. + * + * @param {Object} state Editor state. + * @param {string} [rootClientId=null] Target root client ID. + * + * @return {boolean} If there is at least one allowed pattern. + */ +export const hasAllowedPatterns = createSelector( + ( state, rootClientId = null ) => { + const patterns = state.settings.__experimentalBlockPatterns; + const userPatterns = getUserPatterns( state ); + const { allowedBlockTypes } = getSettings( state ); + return [ ...userPatterns, ...patterns ].some( + ( { name, inserter = true } ) => { + if ( ! inserter ) { + return false; + } + const { blocks } = __experimentalGetParsedPattern( + state, + name + ); + return ( + checkAllowListRecursive( blocks, allowedBlockTypes ) && + blocks.every( ( { name: blockName } ) => + canInsertBlockType( state, blockName, rootClientId ) + ) + ); + } + ); + }, + ( state, rootClientId ) => [ + ...__experimentalGetAllowedPatterns.getDependants( + state, + rootClientId + ), + ] +); diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index c0441cd3b3755e..b6d455333c7a52 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -26,8 +26,12 @@ import { createRegistrySelector } from '@wordpress/data'; /** * Internal dependencies */ +import { + getUserPatterns, + checkAllowListRecursive, + checkAllowList, +} from './utils'; import { orderBy } from '../utils/sorting'; -import { PATTERN_TYPES } from '../components/inserter/block-patterns-tab/utils'; /** * A block selection object. @@ -1481,22 +1485,6 @@ export function getTemplateLock( state, rootClientId ) { return getBlockListSettings( state, rootClientId )?.templateLock ?? false; } -const checkAllowList = ( list, item, defaultResult = null ) => { - if ( typeof list === 'boolean' ) { - return list; - } - if ( Array.isArray( list ) ) { - // TODO: when there is a canonical way to detect that we are editing a post - // the following check should be changed to something like: - // if ( list.includes( 'core/post-content' ) && getEditorMode() === 'post-content' && item === null ) - if ( list.includes( 'core/post-content' ) && item === null ) { - return true; - } - return list.includes( item ); - } - return defaultResult; -}; - /** * Determines if the given block type is allowed to be inserted into the block list. * This function is not exported and not memoized because using a memoized selector @@ -2249,58 +2237,6 @@ export const __experimentalGetDirectInsertBlock = createSelector( ] ); -const checkAllowListRecursive = ( blocks, allowedBlockTypes ) => { - if ( typeof allowedBlockTypes === 'boolean' ) { - return allowedBlockTypes; - } - - const blocksQueue = [ ...blocks ]; - while ( blocksQueue.length > 0 ) { - const block = blocksQueue.shift(); - - const isAllowed = checkAllowList( - allowedBlockTypes, - block.name || block.blockName, - true - ); - if ( ! isAllowed ) { - return false; - } - - block.innerBlocks?.forEach( ( innerBlock ) => { - blocksQueue.push( innerBlock ); - } ); - } - - return true; -}; - -function getUserPatterns( state ) { - const userPatterns = - state?.settings?.__experimentalReusableBlocks ?? EMPTY_ARRAY; - const userPatternCategories = - state?.settings?.__experimentalUserPatternCategories ?? []; - const categories = new Map(); - userPatternCategories.forEach( ( userCategory ) => - categories.set( userCategory.id, userCategory ) - ); - return userPatterns.map( ( userPattern ) => { - return { - name: `core/block/${ userPattern.id }`, - id: userPattern.id, - type: PATTERN_TYPES.user, - title: userPattern.title.raw, - categories: userPattern.wp_pattern_category.map( ( catId ) => - categories && categories.get( catId ) - ? categories.get( catId ).slug - : catId - ), - content: userPattern.content.raw, - syncStatus: userPattern.wp_pattern_sync_status, - }; - } ); -} - export const __experimentalUserPatternCategories = createSelector( ( state ) => { return state?.settings?.__experimentalUserPatternCategories; diff --git a/packages/block-editor/src/store/utils.js b/packages/block-editor/src/store/utils.js new file mode 100644 index 00000000000000..0103b5192154c4 --- /dev/null +++ b/packages/block-editor/src/store/utils.js @@ -0,0 +1,74 @@ +/** + * Internal dependencies + */ +import { PATTERN_TYPES } from '../components/inserter/block-patterns-tab/utils'; + +const EMPTY_ARRAY = []; + +export function getUserPatterns( state ) { + const userPatterns = + state?.settings?.__experimentalReusableBlocks ?? EMPTY_ARRAY; + const userPatternCategories = + state?.settings?.__experimentalUserPatternCategories ?? []; + const categories = new Map(); + userPatternCategories.forEach( ( userCategory ) => + categories.set( userCategory.id, userCategory ) + ); + return userPatterns.map( ( userPattern ) => { + return { + name: `core/block/${ userPattern.id }`, + id: userPattern.id, + type: PATTERN_TYPES.user, + title: userPattern.title.raw, + categories: userPattern.wp_pattern_category.map( ( catId ) => + categories && categories.get( catId ) + ? categories.get( catId ).slug + : catId + ), + content: userPattern.content.raw, + syncStatus: userPattern.wp_pattern_sync_status, + }; + } ); +} + +export const checkAllowList = ( list, item, defaultResult = null ) => { + if ( typeof list === 'boolean' ) { + return list; + } + if ( Array.isArray( list ) ) { + // TODO: when there is a canonical way to detect that we are editing a post + // the following check should be changed to something like: + // if ( list.includes( 'core/post-content' ) && getEditorMode() === 'post-content' && item === null ) + if ( list.includes( 'core/post-content' ) && item === null ) { + return true; + } + return list.includes( item ); + } + return defaultResult; +}; + +export const checkAllowListRecursive = ( blocks, allowedBlockTypes ) => { + if ( typeof allowedBlockTypes === 'boolean' ) { + return allowedBlockTypes; + } + + const blocksQueue = [ ...blocks ]; + while ( blocksQueue.length > 0 ) { + const block = blocksQueue.shift(); + + const isAllowed = checkAllowList( + allowedBlockTypes, + block.name || block.blockName, + true + ); + if ( ! isAllowed ) { + return false; + } + + block.innerBlocks?.forEach( ( innerBlock ) => { + blocksQueue.push( innerBlock ); + } ); + } + + return true; +};