Skip to content

Commit

Permalink
Site Logo: Add option to set site icon from Site Logo block (#35892)
Browse files Browse the repository at this point in the history
* Add toggle for syncing site icon to site logo

* Sync icon when first uploading logo, if no icon exists

* Do not clear the site icon when toggling off syncing, instead restore to original icon

* Link to Customizer from Site Logo syncing help text

* Sync site icon on uploading new image or resetting image

* Simplify logic

* Automatically sync icon if none exists and adding logo for the first time

* Add translated string for site icon property

* Link directly to Site Icon settings in Customizer

* Adjust copy

* Correct opening customizer in new tab

* Persist toggle state in an attribute

* Turn the toggle off if the logo and icon fall out of sync

If a user syncs the logo and icon via the toggle and saves changes, but later
changes the site icon in the Customizer, we want the toggle to be reset to the
`off` position when they next edit the block.

* Discard unsaved changes to logo and icon when removing last Logo block

* Prevent uncontrolled input when sync attribute undefined

* Tighten up copy

* Force syncing on initial selection

* Get up-to-date logoBlockCount when removing block

Previously we were getting the logoBlockCount on block creation, but
this value could become out of date if more Site Logo blocks were added
afterward.

Now we count the number of blocks at the time of block removal, so that it
is guaranteed up-to-date, and we ensure that changes are only discarded
if there are no other Site Logo blocks on the page.

* Add shouldSyncIcon attr to core blocks documentation
  • Loading branch information
stacimc authored Dec 21, 2021
1 parent 0ba3b09 commit e329250
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 9 deletions.
2 changes: 1 addition & 1 deletion docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ Display a graphic to represent this site. Update the block, and the changes appl
- **Name:** core/site-logo
- **Category:** theme
- **Supports:** align, color (~~background~~, ~~text~~), ~~alignWide~~, ~~html~~
- **Attributes:** isLink, linkTarget, width
- **Attributes:** isLink, linkTarget, shouldSyncIcon, width

## Site Tagline

Expand Down
6 changes: 5 additions & 1 deletion packages/block-library/src/site-logo/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@
"linkTarget": {
"type": "string",
"default": "_self"
},
"shouldSyncIcon": {
"type": "boolean"
}
},
"example": {
"viewportWidth": 500,
"attributes": {
"width": 350
"width": 350,
"className": "block-editor-block-types-list__site-logo-example"
}
},
"supports": {
Expand Down
131 changes: 124 additions & 7 deletions packages/block-library/src/site-logo/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import { includes, pick } from 'lodash';
* WordPress dependencies
*/
import { isBlobURL } from '@wordpress/blob';
import { useEffect, useState, useRef } from '@wordpress/element';
import {
createInterpolateElement,
useEffect,
useState,
useRef,
} from '@wordpress/element';
import { __, isRTL } from '@wordpress/i18n';
import {
MenuItem,
Expand Down Expand Up @@ -53,14 +58,17 @@ const ACCEPT_MEDIA_STRING = 'image/*';

const SiteLogo = ( {
alt,
attributes: { align, width, height, isLink, linkTarget },
attributes: { align, width, height, isLink, linkTarget, shouldSyncIcon },
containerRef,
isSelected,
setAttributes,
setLogo,
logoUrl,
siteUrl,
logoId,
iconId,
setIcon,
canUserEdit,
} ) => {
const clientWidth = useClientWidth( containerRef, [ align ] );
const isLargeViewport = useViewportMatch( 'medium' );
Expand All @@ -84,6 +92,15 @@ const SiteLogo = ( {
};
}, [] );

useEffect( () => {
// Turn the `Use as site icon` toggle off if it is on but the logo and icon have
// fallen out of sync. This can happen if the toggle is saved in the `on` position,
// but changes are later made to the site icon in the Customizer.
if ( shouldSyncIcon && logoId !== iconId ) {
setAttributes( { shouldSyncIcon: false } );
}
}, [] );

useEffect( () => {
if ( ! isSelected ) {
setIsEditingImage( false );
Expand Down Expand Up @@ -250,6 +267,25 @@ const SiteLogo = ( {
</ResizableBox>
);

const syncSiteIconHelpText = createInterpolateElement(
__(
'Site Icons are what you see in browser tabs, bookmark bars, and within the WordPress mobile apps. To use a custom icon that is different from your site logo, use the <a>Site Icon settings</a>.'
),
{
a: (
// eslint-disable-next-line jsx-a11y/anchor-has-content
<a
href={
siteUrl +
'/wp-admin/customize.php?autofocus[section]=title_tagline'
}
target="_blank"
rel="noopener noreferrer"
/>
),
}
);

return (
<>
<InspectorControls>
Expand Down Expand Up @@ -286,6 +322,19 @@ const SiteLogo = ( {
/>
</>
) }
{ canUserEdit && (
<>
<ToggleControl
label={ __( 'Use as site icon' ) }
onChange={ ( value ) => {
setAttributes( { shouldSyncIcon: value } );
setIcon( value ? logoId : undefined );
} }
checked={ !! shouldSyncIcon }
help={ syncSiteIconHelpText }
/>
</>
) }
</PanelBody>
</InspectorControls>
<BlockControls group="block">
Expand All @@ -308,14 +357,15 @@ export default function LogoEdit( {
setAttributes,
isSelected,
} ) {
const { width } = attributes;
const { className: styleClass, width, shouldSyncIcon } = attributes;
const [ logoUrl, setLogoUrl ] = useState();
const ref = useRef();

const {
siteLogoId,
canUserEdit,
url,
siteIconId,
mediaItemData,
isRequestingMediaItem,
} = useSelect( ( select ) => {
Expand All @@ -328,6 +378,7 @@ export default function LogoEdit( {
const _readOnlyLogo = siteData?.site_logo;
const _canUserEdit = canUser( 'update', 'settings' );
const _siteLogoId = _canUserEdit ? _siteLogo : _readOnlyLogo;
const _siteIconId = siteSettings?.site_icon;
const mediaItem =
_siteLogoId &&
select( coreStore ).getMedia( _siteLogoId, {
Expand All @@ -339,6 +390,7 @@ export default function LogoEdit( {
_siteLogoId,
{ context: 'view' },
] );

return {
siteLogoId: _siteLogoId,
canUserEdit: _canUserEdit,
Expand All @@ -349,14 +401,59 @@ export default function LogoEdit( {
alt: mediaItem.alt_text,
},
isRequestingMediaItem: _isRequestingMediaItem,
siteIconId: _siteIconId,
};
}, [] );

const { getGlobalBlockCount } = useSelect( blockEditorStore );
const { editEntityRecord } = useDispatch( coreStore );
const setLogo = ( newValue ) =>

useEffect( () => {
// Cleanup function to discard unsaved changes to the icon and logo when
// the block is removed.
return () => {
// Do nothing if the block is being rendered in the styles preview or the
// block inserter.
if (
styleClass?.includes(
'block-editor-block-types-list__site-logo-example'
) ||
styleClass?.includes(
'block-editor-block-styles__block-preview-container'
)
) {
return;
}

const logoBlockCount = getGlobalBlockCount( 'core/site-logo' );

// Only discard unsaved changes if we are removing the last Site Logo block
// on the page.
if ( logoBlockCount === 0 ) {
editEntityRecord( 'root', 'site', undefined, {
site_logo: undefined,
site_icon: undefined,
} );
}
};
}, [] );

const setLogo = ( newValue, shouldForceSync = false ) => {
// `shouldForceSync` is used to force syncing when the attribute
// may not have updated yet.
if ( shouldSyncIcon || shouldForceSync ) {
setIcon( newValue );
}

editEntityRecord( 'root', 'site', undefined, {
site_logo: newValue,
} );
};

const setIcon = ( newValue ) =>
editEntityRecord( 'root', 'site', undefined, {
site_icon: newValue,
} );

let alt = null;
if ( mediaItemData ) {
Expand All @@ -365,7 +462,24 @@ export default function LogoEdit( {
setLogoUrl( mediaItemData.url );
}
}
const onSelectLogo = ( media ) => {

const onInitialSelectLogo = ( media ) => {
// Initialize the syncSiteIcon toggle. If we currently have no Site logo and no
// site icon, automatically sync the logo to the icon.
if ( shouldSyncIcon === undefined ) {
const shouldForceSync = ! siteIconId;
setAttributes( { shouldSyncIcon: shouldForceSync } );

// Because we cannot rely on the `shouldSyncIcon` attribute to have updated by
// the time `setLogo` is called, pass an argument to force the syncing.
onSelectLogo( media, shouldForceSync );
return;
}

onSelectLogo( media );
};

const onSelectLogo = ( media, shouldForceSync = false ) => {
if ( ! media ) {
return;
}
Expand All @@ -377,7 +491,7 @@ export default function LogoEdit( {
return;
}

setLogo( media.id );
setLogo( media.id, shouldForceSync );
};

const onRemoveLogo = () => {
Expand Down Expand Up @@ -423,6 +537,9 @@ export default function LogoEdit( {
setLogo={ setLogo }
logoId={ mediaItemData?.id || siteLogoId }
siteUrl={ url }
setIcon={ setIcon }
iconId={ siteIconId }
canUserEdit={ canUserEdit }
/>
);
}
Expand Down Expand Up @@ -481,7 +598,7 @@ export default function LogoEdit( {
) }
{ ! logoUrl && canUserEdit && (
<MediaPlaceholder
onSelect={ onSelectLogo }
onSelect={ onInitialSelectLogo }
accept={ ACCEPT_MEDIA_STRING }
allowedTypes={ ALLOWED_MEDIA_TYPES }
onError={ onUploadError }
Expand Down
17 changes: 17 additions & 0 deletions packages/block-library/src/site-logo/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,23 @@ function register_block_core_site_logo_setting() {

add_action( 'rest_api_init', 'register_block_core_site_logo_setting', 10 );

/**
* Register a core site setting for a site icon
*/
function register_block_core_site_icon_setting() {
register_setting(
'general',
'site_icon',
array(
'show_in_rest' => true,
'type' => 'integer',
'description' => __( 'Site icon.' ),
)
);
}

add_action( 'rest_api_init', 'register_block_core_site_icon_setting', 10 );

/**
* Registers the `core/site-logo` block on the server.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const TRANSLATED_SITE_PROPERTIES = {
title: __( 'Title' ),
description: __( 'Tagline' ),
site_logo: __( 'Logo' ),
site_icon: __( 'Icon' ),
show_on_front: __( 'Show on front' ),
page_on_front: __( 'Page on front' ),
};
Expand Down

0 comments on commit e329250

Please sign in to comment.