diff --git a/packages/edit-site/src/components/list/added-by.js b/packages/edit-site/src/components/list/added-by.js index 95107f97edc4e5..ede50d8e22c205 100644 --- a/packages/edit-site/src/components/list/added-by.js +++ b/packages/edit-site/src/components/list/added-by.js @@ -1,3 +1,4 @@ +// @ts-check /** * External dependencies */ @@ -6,7 +7,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { __experimentalHStack as HStack, Icon } from '@wordpress/components'; +import { Icon, __experimentalHStack as HStack } from '@wordpress/components'; import { store as coreStore } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; import { useState } from '@wordpress/element'; @@ -18,25 +19,154 @@ import { } from '@wordpress/icons'; import { _x } from '@wordpress/i18n'; +/** @typedef {'wp_template'|'wp_template_part'} TemplateType */ + +/** @type {TemplateType} */ const TEMPLATE_POST_TYPE_NAMES = [ 'wp_template', 'wp_template_part' ]; -function BaseAddedBy( { text, icon, imageUrl, isCustomized, templateType } ) { +/** + * @typedef {'theme'|'plugin'|'site'|'user'} AddedByType + * + * @typedef AddedByData + * @type {Object} + * @property {AddedByType} type The type of the data. + * @property {JSX.Element} icon The icon to display. + * @property {string} [imageUrl] The optional image URL to display. + * @property {string} [text] The text to display. + * @property {boolean} isCustomized Whether the template has been customized. + * + * @param {TemplateType} postType The template post type. + * @param {number} postId The template post id. + * @return {AddedByData} The added by object or null. + */ +export function useAddedBy( postType, postId ) { + return useSelect( + ( select ) => { + const { + getTheme, + getPlugin, + getEntityRecord, + getMedia, + getUser, + getEditedEntityRecord, + } = select( coreStore ); + const template = getEditedEntityRecord( + 'postType', + postType, + postId + ); + + if ( TEMPLATE_POST_TYPE_NAMES.includes( template.type ) ) { + // Added by theme. + // Template originally provided by a theme, but customized by a user. + // Templates originally didn't have the 'origin' field so identify + // older customized templates by checking for no origin and a 'theme' + // or 'custom' source. + if ( + template.has_theme_file && + ( template.origin === 'theme' || + ( ! template.origin && + [ 'theme', 'custom' ].includes( + template.source + ) ) ) + ) { + return { + type: 'theme', + icon: themeIcon, + text: + getTheme( template.theme )?.name?.rendered || + template.theme, + isCustomized: template.source === 'custom', + }; + } + + // Added by plugin. + if ( template.has_theme_file && template.origin === 'plugin' ) { + return { + type: 'plugin', + icon: pluginIcon, + text: + getPlugin( template.theme )?.name || template.theme, + isCustomized: template.source === 'custom', + }; + } + + // Added by site. + // Template was created from scratch, but has no author. Author support + // was only added to templates in WordPress 5.9. Fallback to showing the + // site logo and title. + if ( + ! template.has_theme_file && + template.source === 'custom' && + ! template.author + ) { + const siteData = getEntityRecord( + 'root', + '__unstableBase' + ); + return { + type: 'site', + icon: globeIcon, + imageUrl: siteData?.site_logo + ? getMedia( siteData.site_logo )?.source_url + : undefined, + text: siteData?.name, + isCustomized: false, + }; + } + } + + // Added by user. + const user = getUser( template.author ); + return { + type: 'user', + icon: authorIcon, + imageUrl: user?.avatar_urls?.[ 48 ], + text: user?.nickname, + isCustomized: false, + }; + }, + [ postType, postId ] + ); +} + +/** + * @param {Object} props + * @param {string} props.imageUrl + */ +function AvatarImage( { imageUrl } ) { const [ isImageLoaded, setIsImageLoaded ] = useState( false ); + return ( +
+ setIsImageLoaded( true ) } + alt="" + src={ imageUrl } + /> +
+ ); +} + +/** + * @param {Object} props + * @param {TemplateType} props.postType The template post type. + * @param {number} props.postId The template post id. + */ +export default function AddedBy( { postType, postId } ) { + const { text, icon, imageUrl, isCustomized } = useAddedBy( + postType, + postId + ); + return ( { imageUrl ? ( -
- setIsImageLoaded( true ) } - alt="" - src={ imageUrl } - /> -
+ ) : (
@@ -46,7 +176,7 @@ function BaseAddedBy( { text, icon, imageUrl, isCustomized, templateType } ) { { text } { isCustomized && ( - { templateType === 'wp_template' + { postType === 'wp_template' ? _x( 'Customized', 'template' ) : _x( 'Customized', 'template part' ) } @@ -55,129 +185,3 @@ function BaseAddedBy( { text, icon, imageUrl, isCustomized, templateType } ) { ); } - -function AddedByTheme( { slug, isCustomized, templateType } ) { - const theme = useSelect( - ( select ) => select( coreStore ).getTheme( slug ), - [ slug ] - ); - - return ( - - ); -} - -function AddedByPlugin( { slug, isCustomized, templateType } ) { - const plugin = useSelect( - ( select ) => select( coreStore ).getPlugin( slug ), - [ slug ] - ); - - return ( - - ); -} - -function AddedByAuthor( { id, templateType } ) { - const user = useSelect( - ( select ) => select( coreStore ).getUser( id ), - [ id ] - ); - - return ( - - ); -} - -function AddedBySite( { templateType } ) { - const { name, logoURL } = useSelect( ( select ) => { - const { getEntityRecord, getMedia } = select( coreStore ); - const siteData = getEntityRecord( 'root', '__unstableBase' ); - - return { - name: siteData?.name, - logoURL: siteData?.site_logo - ? getMedia( siteData.site_logo )?.source_url - : undefined, - }; - }, [] ); - - return ( - - ); -} - -export default function AddedBy( { templateType, template } ) { - if ( ! template ) { - return; - } - - if ( TEMPLATE_POST_TYPE_NAMES.includes( templateType ) ) { - // Template originally provided by a theme, but customized by a user. - // Templates originally didn't have the 'origin' field so identify - // older customized templates by checking for no origin and a 'theme' - // or 'custom' source. - if ( - template.has_theme_file && - ( template.origin === 'theme' || - ( ! template.origin && - [ 'theme', 'custom' ].includes( template.source ) ) ) - ) { - return ( - - ); - } - - // Template originally provided by a plugin, but customized by a user. - if ( template.has_theme_file && template.origin === 'plugin' ) { - return ( - - ); - } - - // Template was created from scratch, but has no author. Author support - // was only added to templates in WordPress 5.9. Fallback to showing the - // site logo and title. - if ( - ! template.has_theme_file && - template.source === 'custom' && - ! template.author - ) { - return ; - } - } - - // Simply show the author for templates created from scratch that have an - // author or for any other post type. - return ( - - ); -} diff --git a/packages/edit-site/src/components/list/table.js b/packages/edit-site/src/components/list/table.js index 8f9c039cb0ce1e..a946b81381e8a5 100644 --- a/packages/edit-site/src/components/list/table.js +++ b/packages/edit-site/src/components/list/table.js @@ -101,10 +101,12 @@ export default function Table( { templateType } ) { - + { template ? ( + + ) : null } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js index b4d1572c3393c2..99139b55d87a5c 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js @@ -1,10 +1,14 @@ /** * WordPress dependencies */ -import { __, sprintf } from '@wordpress/i18n'; -import { useDispatch } from '@wordpress/data'; +import { __, sprintf, _x } from '@wordpress/i18n'; +import { useDispatch, useSelect } from '@wordpress/data'; import { pencil } from '@wordpress/icons'; -import { __experimentalUseNavigator as useNavigator } from '@wordpress/components'; +import { + __experimentalUseNavigator as useNavigator, + Icon, +} from '@wordpress/components'; +import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies @@ -14,23 +18,30 @@ import useEditedEntityRecord from '../use-edited-entity-record'; import { unlock } from '../../private-apis'; import { store as editSiteStore } from '../../store'; import SidebarButton from '../sidebar-button'; +import { useAddedBy } from '../list/added-by'; -export default function SidebarNavigationScreenTemplate() { - const { params } = useNavigator(); - const { postType, postId } = params; - const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); +function useTemplateTitleAndDescription( postType, postId ) { const { getDescription, getTitle, record } = useEditedEntityRecord( postType, postId ); - let description = getDescription(); - if ( ! description ) { + const currentTheme = useSelect( + ( select ) => select( coreStore ).getCurrentTheme(), + [] + ); + const addedBy = useAddedBy( postType, postId ); + const isAddedByActiveTheme = + addedBy.type === 'theme' && record.theme === currentTheme?.stylesheet; + const title = getTitle(); + let descriptionText = getDescription(); + + if ( ! descriptionText && addedBy.text ) { if ( record.type === 'wp_template' && record.is_custom ) { - description = __( + descriptionText = __( 'This is a custom template that can be applied manually to any Post or Page.' ); } else if ( record.type === 'wp_template_part' ) { - description = sprintf( + descriptionText = sprintf( // translators: %s: template part title e.g: "Header". __( 'This is your %s template part.' ), getTitle() @@ -38,9 +49,55 @@ export default function SidebarNavigationScreenTemplate() { } } + const description = ( + <> + { descriptionText } + + { addedBy.text && ! isAddedByActiveTheme && ( + + + + { addedBy.imageUrl ? ( + + ) : ( + + ) } + + { addedBy.text } + + + { addedBy.isCustomized && ( + + { postType === 'wp_template' + ? _x( '(Customized)', 'template' ) + : _x( '(Customized)', 'template part' ) } + + ) } + + ) } + + ); + + return { title, description }; +} + +export default function SidebarNavigationScreenTemplate() { + const { params } = useNavigator(); + const { postType, postId } = params; + const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); + const { title, description } = useTemplateTitleAndDescription( + postType, + postId + ); + return ( setCanvasMode( 'edit' ) } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-template/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-template/style.scss new file mode 100644 index 00000000000000..e86c137a574d5f --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-template/style.scss @@ -0,0 +1,25 @@ +.edit-site-sidebar-navigation-screen-template__added-by-description { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: $grid-unit-30; + + &-author { + display: inline-flex; + align-items: center; + + img { + border-radius: $grid-unit-15; + } + + svg { + fill: $gray-600; + } + + &-icon { + width: 24px; + height: 24px; + margin-right: $grid-unit-10; + } + } +} diff --git a/packages/edit-site/src/style.scss b/packages/edit-site/src/style.scss index 8c3713a988cafb..a68971f9011578 100644 --- a/packages/edit-site/src/style.scss +++ b/packages/edit-site/src/style.scss @@ -25,6 +25,7 @@ @import "./components/sidebar-button/style.scss"; @import "./components/sidebar-navigation-item/style.scss"; @import "./components/sidebar-navigation-screen/style.scss"; +@import "./components/sidebar-navigation-screen-template/style.scss"; @import "./components/sidebar-navigation-screen-templates/style.scss"; @import "./components/site-hub/style.scss"; @import "./components/sidebar-navigation-screen-navigation-menus/style.scss";