diff --git a/packages/block-editor/src/components/inserter/media-tab/hooks.js b/packages/block-editor/src/components/inserter/media-tab/hooks.js index 3d248c237be7c..3a4c8fa9da7df 100644 --- a/packages/block-editor/src/components/inserter/media-tab/hooks.js +++ b/packages/block-editor/src/components/inserter/media-tab/hooks.js @@ -1,16 +1,17 @@ /** * WordPress dependencies */ -import { useEffect, useState, useRef, useMemo } from '@wordpress/element'; +import { useEffect, useState, useRef } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ import { store as blockEditorStore } from '../../../store'; +import { unlock } from '../../../lock-unlock'; -/** @typedef {import('./api').InserterMediaRequest} InserterMediaRequest */ -/** @typedef {import('./api').InserterMediaItem} InserterMediaItem */ +/** @typedef {import('../../../store/actions').InserterMediaRequest} InserterMediaRequest */ +/** @typedef {import('../../../store/actions').InserterMediaItem} InserterMediaItem */ /** * Fetches media items based on the provided category. @@ -50,48 +51,14 @@ export function useMediaResults( category, query = {} ) { return { mediaList, isLoading }; } -function useInserterMediaCategories() { - const { - inserterMediaCategories, - allowedMimeTypes, - enableOpenverseMediaCategory, - } = useSelect( ( select ) => { - const settings = select( blockEditorStore ).getSettings(); - return { - inserterMediaCategories: settings.inserterMediaCategories, - allowedMimeTypes: settings.allowedMimeTypes, - enableOpenverseMediaCategory: settings.enableOpenverseMediaCategory, - }; - }, [] ); - // The allowed `mime_types` can be altered by `upload_mimes` filter and restrict - // some of them. In this case we shouldn't add the category to the available media - // categories list in the inserter. - const allowedCategories = useMemo( () => { - if ( ! inserterMediaCategories || ! allowedMimeTypes ) { - return; - } - return inserterMediaCategories.filter( ( category ) => { - // Check if Openverse category is enabled. - if ( - ! enableOpenverseMediaCategory && - category.name === 'openverse' - ) { - return false; - } - return Object.values( allowedMimeTypes ).some( ( mimeType ) => - mimeType.startsWith( `${ category.mediaType }/` ) - ); - } ); - }, [ - inserterMediaCategories, - allowedMimeTypes, - enableOpenverseMediaCategory, - ] ); - return allowedCategories; -} - export function useMediaCategories( rootClientId ) { const [ categories, setCategories ] = useState( [] ); + + const inserterMediaCategories = useSelect( + ( select ) => + unlock( select( blockEditorStore ) ).getInserterMediaCategories(), + [] + ); const { canInsertImage, canInsertVideo, canInsertAudio } = useSelect( ( select ) => { const { canInsertBlockType } = select( blockEditorStore ); @@ -112,7 +79,6 @@ export function useMediaCategories( rootClientId ) { }, [ rootClientId ] ); - const inserterMediaCategories = useInserterMediaCategories(); useEffect( () => { ( async () => { const _categories = []; diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index 1740543744aaa..ae4b64a645d3e 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -1893,9 +1893,10 @@ export const registerInserterMediaCategory = ); return; } - const { inserterMediaCategories = [] } = select.getSettings(); + const registeredInserterMediaCategories = + select.getRegisteredInserterMediaCategories(); if ( - inserterMediaCategories.some( + registeredInserterMediaCategories.some( ( { name } ) => name === category.name ) ) { @@ -1905,8 +1906,8 @@ export const registerInserterMediaCategory = return; } if ( - inserterMediaCategories.some( - ( { labels: { name } } ) => name === category.labels?.name + registeredInserterMediaCategories.some( + ( { labels: { name } = {} } ) => name === category.labels?.name ) ) { console.error( @@ -1919,13 +1920,8 @@ export const registerInserterMediaCategory = // private, so extenders can only add new inserter media categories and don't have any // control over the core media categories. dispatch( { - type: 'UPDATE_SETTINGS', - settings: { - inserterMediaCategories: [ - ...inserterMediaCategories, - { ...category, isExternalResource: true }, - ], - }, + type: 'REGISTER_INSERTER_MEDIA_CATEGORY', + category: { ...category, isExternalResource: true }, } ); }; diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index 698da537728e0..c4220e6e7e516 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -164,3 +164,75 @@ export function getOpenedBlockSettingsMenu( state ) { export function getStyleOverrides( state ) { return state.styleOverrides; } + +/** @typedef {import('./actions').InserterMediaCategory} InserterMediaCategory */ +/** + * Returns the registered inserter media categories through the public API. + * + * @param {Object} state Editor state. + * + * @return {InserterMediaCategory[]} Inserter media categories. + */ +export function getRegisteredInserterMediaCategories( state ) { + return state.registeredInserterMediaCategories; +} + +/** + * Returns an array containing the allowed inserter media categories. + * It merges the registered media categories from extenders with the + * core ones. It also takes into account the allowed `mime_types`, which + * can be altered by `upload_mimes` filter and restrict some of them. + * + * @param {Object} state Global application state. + * + * @return {InserterMediaCategory[]} Client IDs of descendants. + */ +export const getInserterMediaCategories = createSelector( + ( state ) => { + const { + settings: { + inserterMediaCategories, + allowedMimeTypes, + enableOpenverseMediaCategory, + }, + registeredInserterMediaCategories, + } = state; + // The allowed `mime_types` can be altered by `upload_mimes` filter and restrict + // some of them. In this case we shouldn't add the category to the available media + // categories list in the inserter. + if ( + ( ! inserterMediaCategories && + ! registeredInserterMediaCategories.length ) || + ! allowedMimeTypes + ) { + return; + } + const coreInserterMediaCategoriesNames = + inserterMediaCategories?.map( ( { name } ) => name ) || []; + const mergedCategories = [ + ...( inserterMediaCategories || [] ), + ...( registeredInserterMediaCategories || [] ).filter( + ( { name } ) => + ! coreInserterMediaCategoriesNames.includes( name ) + ), + ]; + return mergedCategories.filter( ( category ) => { + // Check if Openverse category is enabled. + if ( + ! enableOpenverseMediaCategory && + category.name === 'openverse' + ) { + return false; + } + return Object.values( allowedMimeTypes ).some( ( mimeType ) => + mimeType.startsWith( `${ category.mediaType }/` ) + ); + } ); + }, + ( state ) => [ + state.settings.inserterMediaCategories, + state.settings.allowedMimeTypes, + state.settings.enableOpenverseMediaCategory, + state.registeredInserterMediaCategories, + ] +); diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 18048ce138eb2..4373182d98662 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1949,6 +1949,22 @@ export function styleOverrides( state = new Map(), action ) { return state; } +/** + * Reducer returning a map of the registered inserter media categories. + * + * @param {Array} state Current state. + * @param {Object} action Dispatched action. + * + * @return {Array} Updated state. + */ +export function registeredInserterMediaCategories( state = [], action ) { + switch ( action.type ) { + case 'REGISTER_INSERTER_MEDIA_CATEGORY': + return [ ...state, action.category ]; + } + return state; +} + const combinedReducers = combineReducers( { blocks, isTyping, @@ -1976,6 +1992,7 @@ const combinedReducers = combineReducers( { removalPromptData, blockRemovalRules, openedBlockSettingsMenu, + registeredInserterMediaCategories, } ); function withAutomaticChangeReset( reducer ) { diff --git a/packages/block-editor/src/store/test/actions.js b/packages/block-editor/src/store/test/actions.js index f1f8cb29f1406..e65921e30a6ce 100644 --- a/packages/block-editor/src/store/test/actions.js +++ b/packages/block-editor/src/store/test/actions.js @@ -1279,9 +1279,9 @@ describe( 'actions', () => { fetch: () => {}, } )( { select: { - getSettings: () => ( { - inserterMediaCategories: [ { name: 'a' } ], - } ), + getRegisteredInserterMediaCategories: () => [ + { name: 'a' }, + ], }, } ); expect( console ).toHaveErroredWith( @@ -1296,11 +1296,9 @@ describe( 'actions', () => { fetch: () => {}, } )( { select: { - getSettings: () => ( { - inserterMediaCategories: [ - { labels: { name: 'a' } }, - ], - } ), + getRegisteredInserterMediaCategories: () => [ + { labels: { name: 'a' } }, + ], }, } ); expect( console ).toHaveErroredWith( @@ -1321,18 +1319,14 @@ describe( 'actions', () => { const dispatch = jest.fn(); registerInserterMediaCategory( category )( { select: { - getSettings: () => ( { inserterMediaCategories } ), + getRegisteredInserterMediaCategories: () => + inserterMediaCategories, }, dispatch, } ); expect( dispatch ).toHaveBeenLastCalledWith( { - type: 'UPDATE_SETTINGS', - settings: { - inserterMediaCategories: [ - ...inserterMediaCategories, - { ...category, isExternalResource: true }, - ], - }, + type: 'REGISTER_INSERTER_MEDIA_CATEGORY', + category: { ...category, isExternalResource: true }, } ); } ); } );