From c22ecefcbf4b69c5ca0edbe8019c65c3c3f3fbf6 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 18 Nov 2024 12:40:36 +0100 Subject: [PATCH] Extract selectors from useResolveEditedEntity hook (#67031) Co-authored-by: youknowriad Co-authored-by: jsnajdr --- packages/core-data/src/private-selectors.ts | 150 ++++++++++++++++- .../edit-post/src/components/layout/index.js | 15 +- packages/edit-post/src/store/index.js | 3 - .../edit-post/src/store/private-selectors.js | 61 ------- packages/edit-post/src/store/selectors.js | 11 +- .../editor/use-resolve-edited-entity.js | 154 ++---------------- 6 files changed, 184 insertions(+), 210 deletions(-) delete mode 100644 packages/edit-post/src/store/private-selectors.js diff --git a/packages/core-data/src/private-selectors.ts b/packages/core-data/src/private-selectors.ts index b2f6fa7def9858..02fe152ed0abb6 100644 --- a/packages/core-data/src/private-selectors.ts +++ b/packages/core-data/src/private-selectors.ts @@ -6,8 +6,9 @@ import { createSelector, createRegistrySelector } from '@wordpress/data'; /** * Internal dependencies */ -import type { State } from './selectors'; +import { getDefaultTemplateId, getEntityRecord, type State } from './selectors'; import { STORE_NAME } from './name'; +import { unlock } from './lock-unlock'; type EntityRecordKey = string | number; @@ -105,3 +106,150 @@ export function getEntityRecordPermissions( export function getRegisteredPostMeta( state: State, postType: string ) { return state.registeredPostMeta?.[ postType ] ?? {}; } + +function normalizePageId( value: number | string | undefined ): string | null { + if ( ! value || ! [ 'number', 'string' ].includes( typeof value ) ) { + return null; + } + + // We also need to check if it's not zero (`'0'`). + if ( Number( value ) === 0 ) { + return null; + } + + return value.toString(); +} + +interface SiteData { + show_on_front?: string; + page_on_front?: string | number; + page_for_posts?: string | number; +} + +export const getHomePage = createRegistrySelector( ( select ) => + createSelector( + () => { + const siteData = select( STORE_NAME ).getEntityRecord( + 'root', + 'site' + ) as SiteData | undefined; + if ( ! siteData ) { + return null; + } + const homepageId = + siteData?.show_on_front === 'page' + ? normalizePageId( siteData.page_on_front ) + : null; + if ( homepageId ) { + return { postType: 'page', postId: homepageId }; + } + const frontPageTemplateId = select( + STORE_NAME + ).getDefaultTemplateId( { + slug: 'front-page', + } ); + return { postType: 'wp_template', postId: frontPageTemplateId }; + }, + ( state ) => [ + // @ts-expect-error + getEntityRecord( state, 'root', 'site' ), + getDefaultTemplateId( state, { + slug: 'front-page', + } ), + ] + ) +); + +export const getPostsPageId = createRegistrySelector( ( select ) => () => { + const siteData = select( STORE_NAME ).getEntityRecord( 'root', 'site' ) as + | SiteData + | undefined; + return siteData?.show_on_front === 'page' + ? normalizePageId( siteData.page_for_posts ) + : null; +} ); + +export const getTemplateId = createRegistrySelector( + ( select ) => ( state, postType, postId ) => { + const homepage = unlock( select( STORE_NAME ) ).getHomePage(); + + if ( ! homepage ) { + return; + } + + // For the front page, we always use the front page template if existing. + if ( + postType === 'page' && + postType === homepage?.postType && + postId.toString() === homepage?.postId + ) { + // The /lookup endpoint cannot currently handle a lookup + // when a page is set as the front page, so specifically in + // that case, we want to check if there is a front page + // template, and instead of falling back to the home + // template, we want to fall back to the page template. + const templates = select( STORE_NAME ).getEntityRecords( + 'postType', + 'wp_template', + { + per_page: -1, + } + ); + if ( ! templates ) { + return; + } + const id = templates.find( ( { slug } ) => slug === 'front-page' ) + ?.id; + if ( id ) { + return id; + } + // If no front page template is found, continue with the + // logic below (fetching the page template). + } + + const editedEntity = select( STORE_NAME ).getEditedEntityRecord( + 'postType', + postType, + postId + ); + if ( ! editedEntity ) { + return; + } + const postsPageId = unlock( select( STORE_NAME ) ).getPostsPageId(); + // Check if the current page is the posts page. + if ( postType === 'page' && postsPageId === postId.toString() ) { + return select( STORE_NAME ).getDefaultTemplateId( { + slug: 'home', + } ); + } + // First see if the post/page has an assigned template and fetch it. + const currentTemplateSlug = editedEntity.template; + if ( currentTemplateSlug ) { + const currentTemplate = select( STORE_NAME ) + .getEntityRecords( 'postType', 'wp_template', { + per_page: -1, + } ) + ?.find( ( { slug } ) => slug === currentTemplateSlug ); + if ( currentTemplate ) { + return currentTemplate.id; + } + } + // If no template is assigned, use the default template. + let slugToCheck; + // In `draft` status we might not have a slug available, so we use the `single` + // post type templates slug(ex page, single-post, single-product etc..). + // Pages do not need the `single` prefix in the slug to be prioritized + // through template hierarchy. + if ( editedEntity.slug ) { + slugToCheck = + postType === 'page' + ? `${ postType }-${ editedEntity.slug }` + : `single-${ postType }-${ editedEntity.slug }`; + } else { + slugToCheck = postType === 'page' ? 'page' : `single-${ postType }`; + } + return select( STORE_NAME ).getDefaultTemplateId( { + slug: slugToCheck, + } ); + } +); diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index 225788a15a8e1d..aec14eab989f03 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -399,10 +399,10 @@ function Layout( { } = useSelect( ( select ) => { const { get } = select( preferencesStore ); - const { isFeatureActive, getEditedPostTemplateId } = unlock( - select( editPostStore ) + const { isFeatureActive } = select( editPostStore ); + const { canUser, getPostType, getTemplateId } = unlock( + select( coreStore ) ); - const { canUser, getPostType } = select( coreStore ); const supportsTemplateMode = settings.supportsTemplateMode; const isViewable = @@ -433,7 +433,7 @@ function Layout( { isViewable && canViewTemplate && ! isEditingTemplate - ? getEditedPostTemplateId() + ? getTemplateId( currentPostType, currentPostId ) : null, enablePaddingAppender: ! isZoomOut() && @@ -441,7 +441,12 @@ function Layout( { ! DESIGN_POST_TYPES.includes( currentPostType ), }; }, - [ currentPostType, isEditingTemplate, settings.supportsTemplateMode ] + [ + currentPostType, + currentPostId, + isEditingTemplate, + settings.supportsTemplateMode, + ] ); const [ paddingAppenderRef, paddingStyle ] = usePaddingAppender( enablePaddingAppender diff --git a/packages/edit-post/src/store/index.js b/packages/edit-post/src/store/index.js index 17033b759292d7..93dae7606d0e3c 100644 --- a/packages/edit-post/src/store/index.js +++ b/packages/edit-post/src/store/index.js @@ -9,9 +9,7 @@ import { createReduxStore, register } from '@wordpress/data'; import reducer from './reducer'; import * as actions from './actions'; import * as selectors from './selectors'; -import * as privateSelectors from './private-selectors'; import { STORE_NAME } from './constants'; -import { unlock } from '../lock-unlock'; /** * Store definition for the edit post namespace. @@ -26,4 +24,3 @@ export const store = createReduxStore( STORE_NAME, { selectors, } ); register( store ); -unlock( store ).registerPrivateSelectors( privateSelectors ); diff --git a/packages/edit-post/src/store/private-selectors.js b/packages/edit-post/src/store/private-selectors.js deleted file mode 100644 index 246b2754d895ab..00000000000000 --- a/packages/edit-post/src/store/private-selectors.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * WordPress dependencies - */ -import { createRegistrySelector } from '@wordpress/data'; -import { store as coreStore } from '@wordpress/core-data'; -import { store as editorStore } from '@wordpress/editor'; - -export const getEditedPostTemplateId = createRegistrySelector( - ( select ) => () => { - const { - id: postId, - type: postType, - slug, - } = select( editorStore ).getCurrentPost(); - const { getEntityRecord, getEntityRecords, canUser } = - select( coreStore ); - const siteSettings = canUser( 'read', { - kind: 'root', - name: 'site', - } ) - ? getEntityRecord( 'root', 'site' ) - : undefined; - // First check if the current page is set as the posts page. - const isPostsPage = +postId === siteSettings?.page_for_posts; - if ( isPostsPage ) { - return select( coreStore ).getDefaultTemplateId( { slug: 'home' } ); - } - const currentTemplate = - select( editorStore ).getEditedPostAttribute( 'template' ); - if ( currentTemplate ) { - const templateWithSameSlug = getEntityRecords( - 'postType', - 'wp_template', - { per_page: -1 } - )?.find( ( template ) => template.slug === currentTemplate ); - if ( ! templateWithSameSlug ) { - return templateWithSameSlug; - } - return templateWithSameSlug.id; - } - let slugToCheck; - // In `draft` status we might not have a slug available, so we use the `single` - // post type templates slug(ex page, single-post, single-product etc..). - // Pages do not need the `single` prefix in the slug to be prioritized - // through template hierarchy. - if ( slug ) { - slugToCheck = - postType === 'page' - ? `${ postType }-${ slug }` - : `single-${ postType }-${ slug }`; - } else { - slugToCheck = postType === 'page' ? 'page' : `single-${ postType }`; - } - - if ( postType ) { - return select( coreStore ).getDefaultTemplateId( { - slug: slugToCheck, - } ); - } - } -); diff --git a/packages/edit-post/src/store/selectors.js b/packages/edit-post/src/store/selectors.js index 8d85249e8100ba..f6516dd0206c00 100644 --- a/packages/edit-post/src/store/selectors.js +++ b/packages/edit-post/src/store/selectors.js @@ -14,8 +14,6 @@ import deprecated from '@wordpress/deprecated'; * Internal dependencies */ import { unlock } from '../lock-unlock'; -import { getEditedPostTemplateId } from './private-selectors'; - const { interfaceStore } = unlock( editorPrivateApis ); const EMPTY_ARRAY = []; const EMPTY_OBJECT = {}; @@ -555,8 +553,13 @@ export function areMetaBoxesInitialized( state ) { * @return {Object?} Post Template. */ export const getEditedPostTemplate = createRegistrySelector( - ( select ) => ( state ) => { - const templateId = getEditedPostTemplateId( state ); + ( select ) => () => { + const { id: postId, type: postType } = + select( editorStore ).getCurrentPost(); + const templateId = unlock( select( coreStore ) ).getTemplateId( + postType, + postId + ); if ( ! templateId ) { return undefined; } diff --git a/packages/edit-site/src/components/editor/use-resolve-edited-entity.js b/packages/edit-site/src/components/editor/use-resolve-edited-entity.js index eb19b3cea95375..4f873738704141 100644 --- a/packages/edit-site/src/components/editor/use-resolve-edited-entity.js +++ b/packages/edit-site/src/components/editor/use-resolve-edited-entity.js @@ -32,33 +32,10 @@ const authorizedPostTypes = [ 'page', 'post' ]; export function useResolveEditedEntity() { const { params = {} } = useLocation(); const { postId, postType } = params; - const { hasLoadedAllDependencies, homepageId, postsPageId } = useSelect( - ( select ) => { - const { getEntityRecord } = select( coreDataStore ); - const siteData = getEntityRecord( 'root', 'site' ); - const _homepageId = - siteData?.show_on_front === 'page' && - [ 'number', 'string' ].includes( - typeof siteData.page_on_front - ) && - !! +siteData.page_on_front // We also need to check if it's not zero(`0`). - ? siteData.page_on_front.toString() - : null; - const _postsPageId = - siteData?.show_on_front === 'page' && - [ 'number', 'string' ].includes( - typeof siteData.page_for_posts - ) - ? siteData.page_for_posts.toString() - : null; - return { - hasLoadedAllDependencies: !! siteData, - homepageId: _homepageId, - postsPageId: _postsPageId, - }; - }, - [] - ); + const homePage = useSelect( ( select ) => { + const { getHomePage } = unlock( select( coreDataStore ) ); + return getHomePage(); + }, [] ); /** * This is a hook that recreates the logic to resolve a template for a given WordPress postID postTypeId @@ -74,111 +51,15 @@ export function useResolveEditedEntity() { postTypesWithoutParentTemplate.includes( postType ) && postId ) { - return undefined; + return; } // Don't trigger resolution for multi-selected posts. if ( postId && postId.includes( ',' ) ) { - return undefined; + return; } - const { - getEditedEntityRecord, - getEntityRecords, - getDefaultTemplateId, - } = select( coreDataStore ); - - function resolveTemplateForPostTypeAndId( - postTypeToResolve, - postIdToResolve - ) { - // For the front page, we always use the front page template if existing. - if ( - postTypeToResolve === 'page' && - homepageId === postIdToResolve - ) { - // The /lookup endpoint cannot currently handle a lookup - // when a page is set as the front page, so specifically in - // that case, we want to check if there is a front page - // template, and instead of falling back to the home - // template, we want to fall back to the page template. - const templates = getEntityRecords( - 'postType', - TEMPLATE_POST_TYPE, - { - per_page: -1, - } - ); - if ( templates ) { - const id = templates?.find( - ( { slug } ) => slug === 'front-page' - )?.id; - if ( id ) { - return id; - } - - // If no front page template is found, continue with the - // logic below (fetching the page template). - } else { - // Still resolving `templates`. - return undefined; - } - } - - const editedEntity = getEditedEntityRecord( - 'postType', - postTypeToResolve, - postIdToResolve - ); - if ( ! editedEntity ) { - return undefined; - } - // Check if the current page is the posts page. - if ( - postTypeToResolve === 'page' && - postsPageId === postIdToResolve - ) { - return getDefaultTemplateId( { slug: 'home' } ); - } - // First see if the post/page has an assigned template and fetch it. - const currentTemplateSlug = editedEntity.template; - if ( currentTemplateSlug ) { - const currentTemplate = getEntityRecords( - 'postType', - TEMPLATE_POST_TYPE, - { - per_page: -1, - } - )?.find( ( { slug } ) => slug === currentTemplateSlug ); - if ( currentTemplate ) { - return currentTemplate.id; - } - } - // If no template is assigned, use the default template. - let slugToCheck; - // In `draft` status we might not have a slug available, so we use the `single` - // post type templates slug(ex page, single-post, single-product etc..). - // Pages do not need the `single` prefix in the slug to be prioritized - // through template hierarchy. - if ( editedEntity.slug ) { - slugToCheck = - postTypeToResolve === 'page' - ? `${ postTypeToResolve }-${ editedEntity.slug }` - : `single-${ postTypeToResolve }-${ editedEntity.slug }`; - } else { - slugToCheck = - postTypeToResolve === 'page' - ? 'page' - : `single-${ postTypeToResolve }`; - } - return getDefaultTemplateId( { - slug: slugToCheck, - } ); - } - - if ( ! hasLoadedAllDependencies ) { - return undefined; - } + const { getTemplateId } = unlock( select( coreDataStore ) ); // If we're rendering a specific page, we need to resolve its template. // The site editor only supports pages for now, not other CPTs. @@ -187,18 +68,19 @@ export function useResolveEditedEntity() { postId && authorizedPostTypes.includes( postType ) ) { - return resolveTemplateForPostTypeAndId( postType, postId ); + return getTemplateId( postType, postId ); } // If we're rendering the home page, and we have a static home page, resolve its template. - if ( homepageId ) { - return resolveTemplateForPostTypeAndId( 'page', homepageId ); + if ( homePage?.postType === 'page' ) { + return getTemplateId( 'page', homePage?.postId ); } - // If we're not rendering a specific page, use the front page template. - return getDefaultTemplateId( { slug: 'front-page' } ); + if ( homePage?.postType === 'wp_template' ) { + return homePage?.postId; + } }, - [ homepageId, postsPageId, hasLoadedAllDependencies, postId, postType ] + [ homePage, postId, postType ] ); const context = useMemo( () => { @@ -211,18 +93,18 @@ export function useResolveEditedEntity() { } // TODO: for post types lists we should probably not render the front page, but maybe a placeholder // with a message like "Select a page" or something similar. - if ( homepageId ) { - return { postType: 'page', postId: homepageId }; + if ( homePage?.postType === 'page' ) { + return { postType: 'page', postId: homePage?.postId }; } return {}; - }, [ homepageId, postType, postId ] ); + }, [ homePage, postType, postId ] ); if ( postTypesWithoutParentTemplate.includes( postType ) && postId ) { return { isReady: true, postType, postId, context }; } - if ( hasLoadedAllDependencies ) { + if ( !! homePage ) { return { isReady: resolvedTemplateId !== undefined, postType: TEMPLATE_POST_TYPE,