Skip to content

Commit

Permalink
Fix RichText undo interactions
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed Feb 21, 2019
1 parent 5aa7de7 commit 01a154c
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1043,4 +1043,8 @@ in order to switch its temporary id with the real id.
*Parameters*

* id: Reusable block's id.
* updatedId: Updated block's id.
* updatedId: Updated block's id.

### markLastChangeAsPersistent

Returns an action object used in signalling that the last block change should be marked explicitely as persistent.
125 changes: 97 additions & 28 deletions packages/block-editor/src/components/provider/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,37 @@
*/
import { Component } from '@wordpress/element';
import { DropZoneProvider, SlotFillProvider } from '@wordpress/components';
import { withDispatch, withSelect } from '@wordpress/data';
import { compose } from '@wordpress/compose';
import { withDispatch, RegistryConsumer } from '@wordpress/data';
import { createHigherOrderComponent, compose } from '@wordpress/compose';

/**
* Higher-order component which renders the original component with the current
* registry context passed as its `registry` prop.
*
* @param {WPComponent} OriginalComponent Original component.
*
* @return {WPComponent} Enhanced component.
*/
const withRegistry = createHigherOrderComponent(
( OriginalComponent ) => ( props ) => (
<RegistryConsumer>
{ ( registry ) => (
<OriginalComponent
{ ...props }
registry={ registry }
/>
) }
</RegistryConsumer>
),
'withRegistry'
);

class BlockEditorProvider extends Component {
componentDidMount() {
this.isSyncingBlockValue = true;
this.props.updateEditorSettings( this.props.settings );
this.isSyncingIncomingValue = true;
this.props.resetBlocks( this.props.value );
this.attachChangeObserver( this.props.registry );
}

componentDidUpdate( prevProps ) {
Expand All @@ -19,32 +42,88 @@ class BlockEditorProvider extends Component {
updateEditorSettings,
value,
resetBlocks,
blocks,
onInput,
onChange,
isLastBlockChangePersistent,
registry,
} = this.props;

if ( settings !== prevProps.settings ) {
updateEditorSettings( settings );
}

if ( this.isSyncingBlockValue ) {
this.isSyncingBlockValue = false;
} else if ( blocks !== prevProps.blocks ) {
this.isSyncingBlockValue = true;
if ( registry !== prevProps.registry ) {
this.attachChangeObserver( registry );
}

if ( isLastBlockChangePersistent ) {
onChange( blocks );
} else {
onInput( blocks );
}
if ( this.isSyncingOutcomingValue ) {
this.isSyncingOutcomingValue = false;
} else if ( value !== prevProps.value ) {
this.isSyncingBlockValue = true;
this.isSyncingIncomingValue = true;
resetBlocks( value );
}
}

componentWillUnmount() {
if ( this.unsubscribe ) {
this.unsubscribe();
}
}

/**
* Given a registry object, overrides the default dispatch behavior for the
* `core/block-editor` store to interpret a state change and decide whether
* we should call `onChange` or `onInput` depending on whether the change
* is persistent or not.
*
* This needs to be done synchronously after state changes (instead of using
* `componentDidUpdate`) in order to avoid batching these changes.
*
* @param {WPDataRegistry} registry Registry from which block editor
* dispatch is to be overriden.
*/
attachChangeObserver( registry ) {
if ( this.unsubscribe ) {
this.unsubscribe();
}

const {
getBlocks,
isLastBlockChangePersistent,
} = registry.select( 'core/block-editor' );

let blocks = getBlocks();
let isPersistent = isLastBlockChangePersistent();

this.unsubscribe = registry.subscribe( () => {
const {
onChange,
onInput,
} = this.props;
const newBlocks = getBlocks();
const newIsPersistent = isLastBlockChangePersistent();
if ( newBlocks !== blocks && this.isSyncingIncomingValue ) {
this.isSyncingIncomingValue = false;
blocks = newBlocks;
isPersistent = newIsPersistent;
return;
}

if (
newBlocks !== blocks ||
// This happens when a previous input is explicitely marked as persistent.
( newIsPersistent && ! isPersistent )
) {
blocks = newBlocks;
isPersistent = newIsPersistent;

this.isSyncingOutcomingValue = true;
if ( isPersistent ) {
onChange( blocks );
} else {
onInput( blocks );
}
}
} );
}

render() {
const { children } = this.props;

Expand All @@ -59,17 +138,6 @@ class BlockEditorProvider extends Component {
}

export default compose( [
withSelect( ( select ) => {
const {
getBlocks,
isLastBlockChangePersistent,
} = select( 'core/block-editor' );

return {
blocks: getBlocks(),
isLastBlockChangePersistent: isLastBlockChangePersistent(),
};
} ),
withDispatch( ( dispatch ) => {
const {
updateEditorSettings,
Expand All @@ -81,4 +149,5 @@ export default compose( [
resetBlocks,
};
} ),
withRegistry,
] )( BlockEditorProvider );
10 changes: 10 additions & 0 deletions packages/block-editor/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -532,3 +532,13 @@ export function __unstableSaveReusableBlock( id, updatedId ) {
updatedId,
};
}

/**
* Returns an action object used in signalling that the last block change should be marked explicitely as persistent.
*
* @return {Object} Action object.
*/
export function markLastChangeAsPersistent() {
return { type: 'MARK_LAST_CHANGE_AS_PERSISTENT' };
}

16 changes: 11 additions & 5 deletions packages/block-editor/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,17 @@ function withPersistentBlockChange( reducer ) {
let lastAction;

return ( state, action ) => {
const nextState = reducer( state, action );
if ( state !== nextState ) {
nextState.isPersistentChange = (
! isUpdatingSameBlockAttribute( action, lastAction )
);
let nextState = reducer( state, action );
const isExplicitPersistentChange = action.type === 'MARK_LAST_CHANGE_AS_PERSISTENT';

if ( state !== nextState || isExplicitPersistentChange ) {
nextState = {
...nextState,
isPersistentChange: (
isExplicitPersistentChange ||
! isUpdatingSameBlockAttribute( action, lastAction )
),
};
}

lastAction = action;
Expand Down
10 changes: 3 additions & 7 deletions packages/editor/src/components/rich-text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1102,17 +1102,13 @@ const RichTextContainer = compose( [
} ),
withDispatch( ( dispatch ) => {
const {
createUndoLevel,
redo,
undo,
markLastChangeAsPersistent,
enterFormattedText,
exitFormattedText,
} = dispatch( 'core/editor' );
} = dispatch( 'core/block-editor' );

return {
onCreateUndoLevel: createUndoLevel,
onRedo: redo,
onUndo: undo,
onCreateUndoLevel: markLastChangeAsPersistent,
onEnterFormattedText: enterFormattedText,
onExitFormattedText: exitFormattedText,
};
Expand Down

0 comments on commit 01a154c

Please sign in to comment.