diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 85e87a4191974..d4de54308ac5d 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -597,6 +597,7 @@ const withBlockReset = ( reducer ) => ( state, action ) => { order: mapBlockOrder( action.blocks ), parents: new Map( mapBlockParents( action.blocks ) ), controlledInnerBlocks: {}, + autoInsertedBlocks: action?.autoInsertedBlocks || {}, }; newState.tree = new Map( state?.tree ); @@ -751,14 +752,20 @@ const withResetControlledBlocks = ( reducer ) => ( state, action ) => { * @return {Function} Enhanced reducer function. */ const withAutoInsertBlocks = ( reducer ) => ( state, action ) => { - if ( action.type === 'RESET_BLOCKS' ) { + const { + autoInsertedBlocks, + blocks: originalBlocks, + rootClientId = '', + type, + } = action; + if ( + originalBlocks?.length && + ( 'RESET_BLOCKS' === type || 'REPLACE_INNER_BLOCKS' === type ) + ) { const autoInsertBlockTypes = select( blocksStore ) .getBlockTypes() .filter( ( { autoInsert } ) => { - return ( - autoInsert?.after?.length > 0 || - autoInsert?.before?.length > 0 - ); + return autoInsert?.after?.length > 0; } ) .reduce( ( autoInsertAccumulator, { name, autoInsert } ) => { return autoInsert.after.reduce( @@ -776,19 +783,48 @@ const withAutoInsertBlocks = ( reducer ) => ( state, action ) => { ); }, {} ); - const result = []; - action.blocks.forEach( ( block ) => { - result.push( block ); + const previouslyAutoInsertedBlockNames = new Set( + autoInsertedBlocks + ? autoInsertedBlocks?.[ rootClientId ] + : state?.autoInsertedBlocks?.[ rootClientId ] + ); + const newAutoInsertedBlockNames = new Set(); + const updatedBlocks = []; + originalBlocks.forEach( ( block ) => { + updatedBlocks.push( block ); if ( block.name in autoInsertBlockTypes ) { autoInsertBlockTypes[ block.name ].after.forEach( ( autoInsertBlockName ) => { - result.push( createBlock( autoInsertBlockName ) ); + if ( + ! previouslyAutoInsertedBlockNames.has( + autoInsertBlockName + ) + ) { + updatedBlocks.push( + createBlock( autoInsertBlockName ) + ); + newAutoInsertedBlockNames.add( + autoInsertBlockName + ); + } } ); } } ); - return reducer( state, { ...action, blocks: result } ); + const newState = reducer( state, { + ...action, + blocks: updatedBlocks, + } ); + if ( newAutoInsertedBlockNames.size === 0 ) { + return newState; + } + + return reducer( newState, { + type: 'UPDATE_AUTO_INSERTED_BLOCKS', + rootClientId, + newAutoInsertedBlockNames, + } ); } return reducer( state, action ); @@ -1237,6 +1273,22 @@ export const blocks = pipe( } return state; }, + + autoInsertedBlocks( + state = {}, + { type, rootClientId = '', newAutoInsertedBlockNames } + ) { + if ( 'UPDATE_AUTO_INSERTED_BLOCKS' === type ) { + return { + ...state, + [ rootClientId ]: [ + ...( state[ rootClientId ] || [] ), + ...newAutoInsertedBlockNames, + ], + }; + } + return state; + }, } ); /** diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index fc7903d011ac5..59d04b985bd8c 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -311,6 +311,7 @@ describe( 'state', () => { } ) ), controlledInnerBlocks: {}, + autoInsertedBlocks: {}, } ); expect( state.tree.get( 'chicken' ) ).not.toBe( existingState.tree.get( 'chicken' ) @@ -411,6 +412,7 @@ describe( 'state', () => { } ) ), controlledInnerBlocks: {}, + autoInsertedBlocks: {}, } ); expect( state.tree.get( 'chicken' ) ).not.toBe( existingState.tree.get( 'chicken' ) @@ -576,6 +578,7 @@ describe( 'state', () => { } ) ), controlledInnerBlocks: {}, + autoInsertedBlocks: {}, } ); expect( state.tree.get( '' ).innerBlocks[ 0 ] ).toBe( @@ -703,6 +706,7 @@ describe( 'state', () => { } ) ), controlledInnerBlocks: {}, + autoInsertedBlocks: {}, } ); // The block object of the parent should be updated. @@ -724,6 +728,7 @@ describe( 'state', () => { isIgnoredChange: false, tree: new Map(), controlledInnerBlocks: {}, + autoInsertedBlocks: {}, } ); } ); @@ -2373,7 +2378,7 @@ describe( 'state', () => { } ); } ); - describe.only( 'automatically inserted blocks', () => { + describe( 'automatically inserted blocks', () => { beforeAll( () => { registerBlockType( 'core/auto-inserted-block', { save: noop, @@ -2404,7 +2409,46 @@ describe( 'state', () => { } ); expect( state.byClientId.size ).toBe( 2 ); - console.log( state.order ); + expect( Array.from( state.byClientId.values() ) ).toEqual( [ + { + clientId: 'chicken', + name: 'core/test-block', + }, + expect.objectContaining( { + name: 'core/auto-inserted-block', + } ), + ] ); + expect( state.autoInsertedBlocks ).toEqual( { + '': [ 'core/auto-inserted-block' ], + } ); + } ); + + it( 'should not automatically insert a previously inserted block', () => { + const state = blocks( undefined, { + type: 'RESET_BLOCKS', + blocks: [ + { + clientId: 'chicken', + name: 'core/test-block', + attributes: {}, + innerBlocks: [], + }, + ], + autoInsertedBlocks: { + '': [ 'core/auto-inserted-block' ], + }, + } ); + + expect( state.byClientId.size ).toBe( 1 ); + expect( Array.from( state.byClientId.values() ) ).toEqual( [ + { + clientId: 'chicken', + name: 'core/test-block', + }, + ] ); + expect( state.autoInsertedBlocks ).toEqual( { + '': [ 'core/auto-inserted-block' ], + } ); } ); } ); } );