From d23898a28d9ce11ea92dbe7dbcfde8f15fface57 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Wed, 13 Dec 2023 09:37:28 +0200 Subject: [PATCH 1/2] Performance: Improve opening inserter in post editor --- .../data/data-core-block-editor.md | 13 ++++++ .../src/components/inserter/menu.js | 6 +-- packages/block-editor/src/store/selectors.js | 40 +++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index 38a93552bcbef2..f25d5d1b8b2cf7 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -846,6 +846,19 @@ _Returns_ - `string|false`: Block Template Lock +### hasAllowedPatterns + +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. + +_Parameters_ + +- _state_ `Object`: Editor state. +- _rootClientId_ `?string`: Optional target root client ID. + +_Returns_ + +- `boolean`: If there is at least one allowed pattern. + ### hasBlockMovingClientId Returns whether block moving mode is enabled. diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index a6d752848538e7..6fd81e9c76c807 100644 --- a/packages/block-editor/src/components/inserter/menu.js +++ b/packages/block-editor/src/components/inserter/menu.js @@ -68,12 +68,10 @@ function InserterMenu( } ); const { showPatterns, inserterItems } = useSelect( ( select ) => { - const { __experimentalGetAllowedPatterns, getInserterItems } = + const { hasAllowedPatterns, getInserterItems } = select( blockEditorStore ); return { - showPatterns: !! __experimentalGetAllowedPatterns( - destinationRootClientId - ).length, + showPatterns: hasAllowedPatterns( destinationRootClientId ), inserterItems: getInserterItems( destinationRootClientId ), }; }, diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index c0441cd3b3755e..017874917e7b1c 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2358,6 +2358,46 @@ const getAllAllowedPatterns = createSelector( ] ); +/** + * 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 Optional 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 + ), + ] +); + /** * Returns the list of allowed patterns for inner blocks children. * From eb576e98e4d495fc65a3b6e0756bea8d5e5628c1 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Wed, 13 Dec 2023 10:50:27 +0200 Subject: [PATCH 2/2] make selector private --- .../data/data-core-block-editor.md | 13 -- .../src/components/inserter/menu.js | 6 +- .../src/store/private-selectors.js | 45 +++++++ packages/block-editor/src/store/selectors.js | 114 +----------------- packages/block-editor/src/store/utils.js | 74 ++++++++++++ 5 files changed, 128 insertions(+), 124 deletions(-) create mode 100644 packages/block-editor/src/store/utils.js diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index f25d5d1b8b2cf7..38a93552bcbef2 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -846,19 +846,6 @@ _Returns_ - `string|false`: Block Template Lock -### hasAllowedPatterns - -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. - -_Parameters_ - -- _state_ `Object`: Editor state. -- _rootClientId_ `?string`: Optional target root client ID. - -_Returns_ - -- `boolean`: If there is at least one allowed pattern. - ### hasBlockMovingClientId Returns whether block moving mode is enabled. diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index 6fd81e9c76c807..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,8 +69,9 @@ function InserterMenu( } ); const { showPatterns, inserterItems } = useSelect( ( select ) => { - const { hasAllowedPatterns, getInserterItems } = - select( blockEditorStore ); + const { hasAllowedPatterns, getInserterItems } = unlock( + select( blockEditorStore ) + ); return { 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 017874917e7b1c..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; @@ -2358,46 +2294,6 @@ const getAllAllowedPatterns = createSelector( ] ); -/** - * 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 Optional 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 - ), - ] -); - /** * Returns the list of allowed patterns for inner blocks children. * 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; +};