Skip to content

Commit

Permalink
Fix reusable blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed Jan 8, 2019
1 parent dc11fbb commit 7c3028c
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 22 deletions.
17 changes: 17 additions & 0 deletions packages/block-editor/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -498,3 +498,20 @@ export function undo() {
export function createUndoLevel() {
return { type: 'CREATE_UNDO_LEVEL' };
}

/**
* Returns an action object used in signalling that a temporary reusable blocks have been saved
* in order to switch its temporary id with the real id.
*
* @param {string} id Reusable block's id.
* @param {string} updatedId Updated block's id.
*
* @return {Object} Action object.
*/
export function __unstableSaveResuableBlock( id, updatedId ) {
return {
type: 'SAVE_REUSABLE_BLOCK_SUCCESS',
id,
updatedId,
};
}
112 changes: 100 additions & 12 deletions packages/block-editor/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,28 @@ function getFlattenedBlockAttributes( blocks ) {
return flattenBlocks( blocks, ( block ) => block.attributes );
}

/**
* Given a block order map object, returns *all* of the block client IDs that are
* a descendant of the given root client ID.
*
* Calling this with `rootClientId` set to `''` results in a list of client IDs
* that are in the post. That is, it excludes blocks like fetched reusable
* blocks which are stored into state but not visible.
*
* @param {Object} blocksOrder Object that maps block client IDs to a list of
* nested block client IDs.
* @param {?string} rootClientId The root client ID to search. Defaults to ''.
*
* @return {Array} List of descendant client IDs.
*/
function getNestedBlockClientIds( blocksOrder, rootClientId = '' ) {
return reduce( blocksOrder[ rootClientId ], ( result, clientId ) => [
...result,
clientId,
...getNestedBlockClientIds( blocksOrder, clientId ),
], [] );
}

/**
* Returns an object against which it is safe to perform mutating operations,
* given the original object and its current working copy.
Expand Down Expand Up @@ -231,6 +253,79 @@ const withInnerBlocksRemoveCascade = ( reducer ) => ( state, action ) => {
return reducer( state, action );
};

/**
* Higher-order reducer which targets the combined blocks reducer and handles
* the `RESET_BLOCKS` action. When dispatched, this action will replace all
* blocks that exist in the post, leaving blocks that exist only in state (e.g.
* reusable blocks) alone.
*
* @param {Function} reducer Original reducer function.
*
* @return {Function} Enhanced reducer function.
*/
const withBlockReset = ( reducer ) => ( state, action ) => {
if (
state &&
( action.type === 'RESET_BLOCKS' || action.type === 'INIT_BLOCKS' )
) {
const visibleClientIds = getNestedBlockClientIds( state.order );
return {
...state,
byClientId: {
...omit( state.byClientId, visibleClientIds ),
...getFlattenedBlocksWithoutAttributes( action.blocks ),
},
attributes: {
...omit( state.attributes, visibleClientIds ),
...getFlattenedBlockAttributes( action.blocks ),
},
order: {
...omit( state.order, visibleClientIds ),
...mapBlockOrder( action.blocks ),
},
};
}

return reducer( state, action );
};

/**
* Higher-order reducer which targets the combined blocks reducer and handles
* the `SAVE_REUSABLE_BLOCK_SUCCESS` action. This action can't be handled by
* regular reducers and needs a higher-order reducer since it needs access to
* both `byClientId` and `attributes` simultaneously.
*
* @param {Function} reducer Original reducer function.
*
* @return {Function} Enhanced reducer function.
*/
const withSaveReusableBlock = ( reducer ) => ( state, action ) => {
if ( state && action.type === 'SAVE_REUSABLE_BLOCK_SUCCESS' ) {
const { id, updatedId } = action;

// If a temporary reusable block is saved, we swap the temporary id with the final one
if ( id === updatedId ) {
return state;
}

state = { ...state };

state.attributes = mapValues( state.attributes, ( attributes, clientId ) => {
const { name } = state.byClientId[ clientId ];
if ( name === 'core/block' && attributes.ref === id ) {
return {
...attributes,
ref: updatedId,
};
}

return attributes;
} );
}

return reducer( state, action );
};

/**
* Undoable reducer returning the editor post state, including blocks parsed
* from current HTML markup.
Expand All @@ -257,12 +352,13 @@ export const editor = flow( [
shouldOverwriteState,
} ),
] )( {
blocks: combineReducers( {
blocks: flow(
combineReducers,
withBlockReset,
withSaveReusableBlock,
)( {
byClientId( state = {}, action ) {
switch ( action.type ) {
case 'INIT_BLOCKS':
case 'RESET_BLOCKS':
return getFlattenedBlocksWithoutAttributes( action.blocks );
case 'RECEIVE_BLOCKS':
return {
...state,
Expand Down Expand Up @@ -314,10 +410,6 @@ export const editor = flow( [

attributes( state = {}, action ) {
switch ( action.type ) {
case 'INIT_BLOCKS':
case 'RESET_BLOCKS':
return getFlattenedBlockAttributes( action.blocks );

case 'RECEIVE_BLOCKS':
return {
...state,
Expand Down Expand Up @@ -391,10 +483,6 @@ export const editor = flow( [

order( state = {}, action ) {
switch ( action.type ) {
case 'INIT_BLOCKS':
case 'RESET_BLOCKS':
return mapBlockOrder( action.blocks );

case 'RECEIVE_BLOCKS':
return {
...state,
Expand Down
9 changes: 8 additions & 1 deletion packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,13 @@ function getPostMeta( state, key ) {
return get( state, [ 'settings', '__experimentalMetaSource', 'value', key ] );
}

/**
* Returns the available reusable blocks
*
* @param {Object} state Global application state.
*
* @return {Array} Reusable blocks
*/
function getReusableBlocks( state ) {
return get( state, [ 'settings', 'reusableBlocks' ], EMPTY_ARRAY );
return get( state, [ 'settings', '__experimentalReusableBlocks' ], EMPTY_ARRAY );
}
18 changes: 14 additions & 4 deletions packages/editor/src/components/provider/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ function computeProviderStateFromProps( props ) {
settings: props.settings,
meta: props.meta,
onMetaChange: props.onMetaChange,
reusableBlocks: props.reusableBlocks,
editorSettings: {
...props.settings,
__experimentalMetaSource: {
value: props.meta,
onChange: props.onMetaChange,
},
__experimentalReusableBlocks: props.reusableBlocks,
},
};
}
Expand Down Expand Up @@ -93,7 +95,8 @@ class EditorProvider extends Component {
if (
props.settings === state.settings &&
props.meta === state.meta &&
props.onMetaChange === state.onMetaChange
props.onMetaChange === state.onMetaChange &&
props.reusableBlocks === state.reusableBlocks
) {
return null;
}
Expand Down Expand Up @@ -128,10 +131,17 @@ class EditorProvider extends Component {

export default compose( [
withSelect( ( select ) => {
const {
isEditorReady,
getEditorBlocks,
getEditedPostAttribute,
__experimentalGetReusableBlocks,
} = select( 'core/editor' );
return {
isReady: select( 'core/editor' ).isEditorReady(),
blocks: select( 'core/editor' ).getEditorBlocks(),
meta: select( 'core/editor' ).getEditedPostAttribute( 'meta' ),
isReady: isEditorReady(),
blocks: getEditorBlocks(),
meta: getEditedPostAttribute( 'meta' ),
reusableBlocks: __experimentalGetReusableBlocks(),
};
} ),
withDispatch( ( dispatch ) => {
Expand Down
2 changes: 2 additions & 0 deletions packages/editor/src/store/effects/reusable-blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ export const saveReusableBlocks = async ( action, store ) => {
dataDispatch( 'core/notices' ).createSuccessNotice( message, {
id: REUSABLE_BLOCK_NOTICE_ID,
} );

dataDispatch( 'core/block-editor' ).__unstableSaveResuableBlock( id, updatedReusableBlock.id );
} catch ( error ) {
dispatch( { type: 'SAVE_REUSABLE_BLOCK_FAILURE', id } );
dataDispatch( 'core/notices' ).createErrorNotice( error.message, {
Expand Down
15 changes: 10 additions & 5 deletions packages/editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -814,12 +814,17 @@ export function __experimentalIsFetchingReusableBlock( state, ref ) {
*
* @return {Array} An array of all reusable blocks.
*/
export function __experimentalGetReusableBlocks( state ) {
return map(
export const __experimentalGetReusableBlocks = createSelector(
( state ) => {
return map(
state.reusableBlocks.data,
( value, ref ) => __experimentalGetReusableBlock( state, ref )
);
},
( state ) => [
state.reusableBlocks.data,
( value, ref ) => __experimentalGetReusableBlock( state, ref )
);
}
]
);

/**
* Returns state object prior to a specified optimist transaction ID, or `null`
Expand Down

0 comments on commit 7c3028c

Please sign in to comment.