From 6883e1b62be3c41fb634c649614d94381b2d447c Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Wed, 10 Jan 2024 19:08:48 +0000 Subject: [PATCH] Fix regression: Content locking does not stops automatically when an outside block is selected. --- .../src/components/block-list/index.js | 30 ++++++++- .../block-editor/src/hooks/content-lock-ui.js | 61 ++++--------------- packages/block-editor/src/store/actions.js | 31 +++++++++- packages/block-editor/src/store/reducer.js | 16 +++++ packages/block-editor/src/store/selectors.js | 11 ++++ 5 files changed, 97 insertions(+), 52 deletions(-) diff --git a/packages/block-editor/src/components/block-list/index.js b/packages/block-editor/src/components/block-list/index.js index ab57605563e1d6..2187fc8107e149 100644 --- a/packages/block-editor/src/components/block-list/index.js +++ b/packages/block-editor/src/components/block-list/index.js @@ -17,7 +17,7 @@ import { useMergeRefs, useDebounce, } from '@wordpress/compose'; -import { createContext, useMemo, useCallback } from '@wordpress/element'; +import { createContext, useMemo, useCallback, useEffect } from '@wordpress/element'; /** * Internal dependencies @@ -113,6 +113,29 @@ function Root( { className, ...settings } ) { ); } +function StopEditingAsBlocksOnOutsideSelect( { clientId } ) { + const { + __unstableStopEditingAsBlocks, + } = useDispatch( blockEditorStore ); + const isBlockOrDescendantSelected = useSelect( + ( select ) => { + const { isBlockSelected, hasSelectedInnerBlock } = + select( blockEditorStore ); + return ( + isBlockSelected( clientId ) || + hasSelectedInnerBlock( clientId, true ) + ); + }, + [ clientId ] + ); + useEffect( () => { + if ( ! isBlockOrDescendantSelected ) { + __unstableStopEditingAsBlocks( clientId ); + } + }, [ isBlockOrDescendantSelected, clientId, __unstableStopEditingAsBlocks ] ); + return null; +} + export default function BlockList( settings ) { return ( @@ -128,17 +151,19 @@ function Items( { __experimentalAppenderTagName, layout = defaultLayout, } ) { - const { order, selectedBlocks, visibleBlocks } = useSelect( + const { order, selectedBlocks, visibleBlocks, temporarilyEditingAsBlocks } = useSelect( ( select ) => { const { getBlockOrder, getSelectedBlockClientIds, __unstableGetVisibleBlocks, + __unstableGetTemporarilyEditingAsBlocks, } = select( blockEditorStore ); return { order: getBlockOrder( rootClientId ), selectedBlocks: getSelectedBlockClientIds(), visibleBlocks: __unstableGetVisibleBlocks(), + temporarilyEditingAsBlocks: __unstableGetTemporarilyEditingAsBlocks(), }; }, [ rootClientId ] @@ -163,6 +188,7 @@ function Items( { ) ) } { order.length < 1 && placeholder } + { !! temporarilyEditingAsBlocks && } { - const { isBlockSelected, hasSelectedInnerBlock } = - select( blockEditorStore ); - return ( - isBlockSelected( clientId ) || - hasSelectedInnerBlock( clientId, true ) - ); - }, - [ clientId ] - ); - useEffect( () => { - if ( ! isBlockOrDescendantSelected ) { - stopEditingAsBlock(); - } - }, [ isBlockOrDescendantSelected, stopEditingAsBlock ] ); - return null; -} +// The implementation of content locking is mainly in this file, although the mechanism +// to stop temporarily editing as blocks when an outside block is selected is on component StopEditingAsBlocksOnOutsideSelect +// at block-editor/src/components/block-list/index.js. +// Besides the components on this file and the file referenced above the implementation +// also includes artifacts on the store (actions, reducers, and selector). function ContentLockControlsPure( { clientId, isSelected } ) { const { getBlockListSettings, getSettings } = useSelect( blockEditorStore ); - const focusModeToRevert = useRef(); const { templateLock, isLockedByParent, isEditingAsBlocks } = useSelect( ( select ) => { const { @@ -59,6 +41,7 @@ function ContentLockControlsPure( { clientId, isSelected } ) { const { updateSettings, updateBlockListSettings, + __unstableStopEditingAsBlocks, __unstableSetTemporarilyEditingAsBlocks, } = useDispatch( blockEditorStore ); const isContentLocked = @@ -67,25 +50,8 @@ function ContentLockControlsPure( { clientId, isSelected } ) { useDispatch( blockEditorStore ); const stopEditingAsBlock = useCallback( () => { - __unstableMarkNextChangeAsNotPersistent(); - updateBlockAttributes( clientId, { - templateLock: 'contentOnly', - } ); - updateBlockListSettings( clientId, { - ...getBlockListSettings( clientId ), - templateLock: 'contentOnly', - } ); - updateSettings( { focusMode: focusModeToRevert.current } ); - __unstableSetTemporarilyEditingAsBlocks(); - }, [ - clientId, - updateSettings, - updateBlockListSettings, - getBlockListSettings, - __unstableMarkNextChangeAsNotPersistent, - updateBlockAttributes, - __unstableSetTemporarilyEditingAsBlocks, - ] ); + __unstableStopEditingAsBlocks( clientId ); + }, [ clientId, __unstableStopEditingAsBlocks ] ); if ( ! isContentLocked && ! isEditingAsBlocks ) { return null; @@ -99,10 +65,6 @@ function ContentLockControlsPure( { clientId, isSelected } ) { <> { showStopEditingAsBlocks && ( <> - { @@ -127,11 +89,12 @@ function ContentLockControlsPure( { clientId, isSelected } ) { ...getBlockListSettings( clientId ), templateLock: false, } ); - focusModeToRevert.current = + const focusModeToRevert = getSettings().focusMode; updateSettings( { focusMode: true } ); __unstableSetTemporarilyEditingAsBlocks( - clientId + clientId, + focusModeToRevert ); onClose(); } } diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index da9beb0ba73a95..ca8d2603578c6e 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -1686,13 +1686,42 @@ export function setBlockVisibility( updates ) { * removed anytime without any warning, causing breakage on any plugin or theme invoking it. * * @param {?string} temporarilyEditingAsBlocks The block's clientId being temporaritly edited as blocks. + * @param {?string} focusModeToRevert The focus mode to revert after temporarily edit as blocks finishes. */ export function __unstableSetTemporarilyEditingAsBlocks( - temporarilyEditingAsBlocks + temporarilyEditingAsBlocks, + focusModeToRevert ) { return { type: 'SET_TEMPORARILY_EDITING_AS_BLOCKS', temporarilyEditingAsBlocks, + focusModeToRevert, + }; +} + +/** + * Action that stops temporarily editing as blocks. + * + * DO-NOT-USE in production. + * This action is created for internal/experimental only usage and may be + * removed anytime without any warning, causing breakage on any plugin or theme invoking it. + * + * @param {string} clientId The block's clientId. + */ +export function __unstableStopEditingAsBlocks( clientId ) { + return ( { select, dispatch } ) => { + const focusModeToRevert = + select.__unstableGetTemporarilyEditingFocusModeToRevert(); + dispatch.__unstableMarkNextChangeAsNotPersistent(); + dispatch.updateBlockAttributes( clientId, { + templateLock: 'contentOnly', + } ); + dispatch.updateBlockListSettings( clientId, { + ...select.getBlockListSettings( clientId ), + templateLock: 'contentOnly', + } ); + dispatch.updateSettings( { focusMode: focusModeToRevert } ); + dispatch.__unstableSetTemporarilyEditingAsBlocks(); }; } diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 11811afd83f6fe..fa6c8942e66add 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1906,6 +1906,21 @@ export function temporarilyEditingAsBlocks( state = '', action ) { return state; } +/** + * Reducer returning the focus mode that should be used when temporarily edit as blocks finishes. + * + * @param {Object} state Current state. + * @param {Object} action Dispatched action. + * + * @return {Object} Updated state. + */ +export function temporarilyEditingFocusModeRevert( state = '', action ) { + if ( action.type === 'SET_TEMPORARILY_EDITING_AS_BLOCKS' ) { + return action.focusModeToRevert; + } + return state; +} + /** * Reducer returning a map of block client IDs to block editing modes. * @@ -2024,6 +2039,7 @@ const combinedReducers = combineReducers( { highlightedBlock, lastBlockInserted, temporarilyEditingAsBlocks, + temporarilyEditingFocusModeRevert, blockVisibility, blockEditingModes, styleOverrides, diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 55d157c6927a2d..1cf3705454f9bd 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2767,6 +2767,17 @@ export function __unstableGetTemporarilyEditingAsBlocks( state ) { return state.temporarilyEditingAsBlocks; } +/** + * DO-NOT-USE in production. + * This selector is created for internal/experimental only usage and may be + * removed anytime without any warning, causing breakage on any plugin or theme invoking it. + * + * @param {Object} state Global application state. + */ +export function __unstableGetTemporarilyEditingFocusModeToRevert( state ) { + return state.temporarilyEditingFocusModeRevert; +} + export function __unstableHasActiveBlockOverlayActive( state, clientId ) { // Prevent overlay on blocks with a non-default editing mode. If the mdoe is // 'disabled' then the overlay is redundant since the block can't be