Skip to content

Commit

Permalink
Merge pull request #3298 from WordPress/fix/3296-dirty-reset-post
Browse files Browse the repository at this point in the history
State: Refactor post dirty state as higher-order reducer
  • Loading branch information
aduth authored Nov 6, 2017
2 parents 4e61b96 + 0304b60 commit a116256
Show file tree
Hide file tree
Showing 11 changed files with 755 additions and 807 deletions.
18 changes: 15 additions & 3 deletions editor/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import optimist from 'redux-optimist';
import { combineReducers } from 'redux';
import {
flow,
partialRight,
difference,
get,
reduce,
Expand All @@ -26,7 +28,8 @@ import { getBlockTypes, getBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { combineUndoableReducers } from './utils/undoable-reducer';
import withHistory from './utils/with-history';
import withChangeDetection from './utils/with-change-detection';
import { STORE_DEFAULTS } from './store-defaults';

/***
Expand Down Expand Up @@ -63,7 +66,16 @@ export function getPostRawValue( value ) {
* @param {Object} action Dispatched action
* @return {Object} Updated state
*/
export const editor = combineUndoableReducers( {
export const editor = flow( [
combineReducers,

// Track undo history, starting at editor initialization.
partialRight( withHistory, { resetTypes: [ 'SETUP_EDITOR' ] } ),

// Track whether changes exist, starting at editor initialization and
// resetting at each post save.
partialRight( withChangeDetection, { resetTypes: [ 'SETUP_EDITOR', 'RESET_POST' ] } ),
] )( {
edits( state = {}, action ) {
switch ( action.type ) {
case 'EDIT_POST':
Expand Down Expand Up @@ -261,7 +273,7 @@ export const editor = combineUndoableReducers( {

return state;
},
}, { resetTypes: [ 'SETUP_EDITOR' ] } );
} );

/**
* Reducer returning the last-known state of the current post, in the format
Expand Down
118 changes: 38 additions & 80 deletions editor/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import {
first,
get,
has,
isEqual,
last,
reduce,
some,
keys,
without,
compact,
Expand Down Expand Up @@ -149,7 +147,7 @@ export function isEditorSidebarPanelOpened( state, panel ) {
* @return {Boolean} Whether undo history exists
*/
export function hasEditorUndo( state ) {
return state.editor.history.past.length > 0;
return state.editor.past.length > 0;
}

/**
Expand All @@ -160,7 +158,7 @@ export function hasEditorUndo( state ) {
* @return {Boolean} Whether redo history exists
*/
export function hasEditorRedo( state ) {
return state.editor.history.future.length > 0;
return state.editor.future.length > 0;
}

/**
Expand All @@ -181,49 +179,9 @@ export function isEditedPostNew( state ) {
* @param {Object} state Global application state
* @return {Boolean} Whether unsaved values exist
*/
export const isEditedPostDirty = createSelector(
( state ) => {
const edits = getPostEdits( state );
const currentPost = getCurrentPost( state );
const hasEditedAttributes = some( edits, ( value, key ) => {
return ! isEqual( value, currentPost[ key ] );
} );

if ( hasEditedAttributes ) {
return true;
}

if ( isMetaBoxStateDirty( state ) ) {
return true;
}

// This is a cheaper operation that still must occur after checking
// attributes, because a post initialized with attributes different
// from its saved copy should be considered dirty.
if ( ! hasEditorUndo( state ) ) {
return false;
}

// Check whether there are differences between editor from its original
// state (when history was last reset) and currently. Any difference in
// attributes, block type, order should consistute needing save.
const { history } = state.editor;
const originalEditor = history.past[ 0 ];
const currentEditor = history.present;
return some( [
'blocksByUid',
'blockOrder',
], ( key ) => ! isEqual(
originalEditor[ key ],
currentEditor[ key ]
) );
},
( state ) => [
state.editor,
state.currentPost,
state.metaBoxes,
]
);
export function isEditedPostDirty( state ) {
return state.editor.isDirty || isMetaBoxStateDirty( state );
}

/**
* Returns true if there are no unsaved values for the current edit session and if
Expand Down Expand Up @@ -298,7 +256,7 @@ export function getCurrentPostLastRevisionId( state ) {
* @return {Object} Object of key value pairs comprising unsaved edits
*/
export function getPostEdits( state ) {
return state.editor.edits;
return state.editor.present.edits;
}

/**
Expand All @@ -311,9 +269,9 @@ export function getPostEdits( state ) {
* @return {*} Post attribute value
*/
export function getEditedPostAttribute( state, attributeName ) {
return state.editor.edits[ attributeName ] === undefined ?
return state.editor.present.edits[ attributeName ] === undefined ?
state.currentPost[ attributeName ] :
state.editor.edits[ attributeName ];
state.editor.present.edits[ attributeName ];
}

/**
Expand Down Expand Up @@ -432,9 +390,9 @@ export function getDocumentTitle( state ) {
* @return {String} Raw post excerpt
*/
export function getEditedPostExcerpt( state ) {
return state.editor.edits.excerpt === undefined ?
return state.editor.present.edits.excerpt === undefined ?
state.currentPost.excerpt :
state.editor.edits.excerpt;
state.editor.present.edits.excerpt;
}

/**
Expand Down Expand Up @@ -464,7 +422,7 @@ export function getEditedPostPreviewLink( state ) {
*/
export const getBlock = createSelector(
( state, uid ) => {
const block = state.editor.blocksByUid[ uid ];
const block = state.editor.present.blocksByUid[ uid ];
if ( ! block ) {
return null;
}
Expand Down Expand Up @@ -495,15 +453,15 @@ export const getBlock = createSelector(
};
},
( state, uid ) => [
get( state, [ 'editor', 'blocksByUid', uid ] ),
get( state, 'editor.edits.meta' ),
get( state, [ 'editor', 'present', 'blocksByUid', uid ] ),
get( state, [ 'editor', 'present', 'edits', 'meta' ] ),
get( state, 'currentPost.meta' ),
]
);

function getPostMeta( state, key ) {
return has( state, [ 'editor', 'edits', 'meta', key ] ) ?
get( state, [ 'editor', 'edits', 'meta', key ] ) :
return has( state, [ 'editor', 'edits', 'present', 'meta', key ] ) ?
get( state, [ 'editor', 'edits', 'present', 'meta', key ] ) :
get( state, [ 'currentPost', 'meta', key ] );
}

Expand All @@ -517,11 +475,11 @@ function getPostMeta( state, key ) {
*/
export const getBlocks = createSelector(
( state ) => {
return state.editor.blockOrder.map( ( uid ) => getBlock( state, uid ) );
return state.editor.present.blockOrder.map( ( uid ) => getBlock( state, uid ) );
},
( state ) => [
state.editor.blockOrder,
state.editor.blocksByUid,
state.editor.present.blockOrder,
state.editor.present.blocksByUid,
]
);

Expand Down Expand Up @@ -575,7 +533,7 @@ export function getSelectedBlock( state ) {
*/
export const getMultiSelectedBlockUids = createSelector(
( state ) => {
const { blockOrder } = state.editor;
const { blockOrder } = state.editor.present;
const { start, end } = state.blockSelection;
if ( start === end ) {
return [];
Expand All @@ -591,7 +549,7 @@ export const getMultiSelectedBlockUids = createSelector(
return blockOrder.slice( startIndex, endIndex + 1 );
},
( state ) => [
state.editor.blockOrder,
state.editor.present.blockOrder,
state.blockSelection.start,
state.blockSelection.end,
],
Expand All @@ -607,11 +565,11 @@ export const getMultiSelectedBlockUids = createSelector(
export const getMultiSelectedBlocks = createSelector(
( state ) => getMultiSelectedBlockUids( state ).map( ( uid ) => getBlock( state, uid ) ),
( state ) => [
state.editor.blockOrder,
state.editor.present.blockOrder,
state.blockSelection.start,
state.blockSelection.end,
state.editor.blocksByUid,
state.editor.edits.meta,
state.editor.present.blocksByUid,
state.editor.present.edits.meta,
state.currentPost.meta,
]
);
Expand Down Expand Up @@ -707,7 +665,7 @@ export function getMultiSelectedBlocksEndUid( state ) {
* @return {Array} Ordered unique IDs of post blocks
*/
export function getBlockUids( state ) {
return state.editor.blockOrder;
return state.editor.present.blockOrder;
}

/**
Expand All @@ -719,7 +677,7 @@ export function getBlockUids( state ) {
* @return {Number} Index at which block exists in order
*/
export function getBlockIndex( state, uid ) {
return state.editor.blockOrder.indexOf( uid );
return state.editor.present.blockOrder.indexOf( uid );
}

/**
Expand All @@ -731,7 +689,7 @@ export function getBlockIndex( state, uid ) {
* @return {Boolean} Whether block is first in post
*/
export function isFirstBlock( state, uid ) {
return first( state.editor.blockOrder ) === uid;
return first( state.editor.present.blockOrder ) === uid;
}

/**
Expand All @@ -743,7 +701,7 @@ export function isFirstBlock( state, uid ) {
* @return {Boolean} Whether block is last in post
*/
export function isLastBlock( state, uid ) {
return last( state.editor.blockOrder ) === uid;
return last( state.editor.present.blockOrder ) === uid;
}

/**
Expand All @@ -756,7 +714,7 @@ export function isLastBlock( state, uid ) {
*/
export function getPreviousBlock( state, uid ) {
const order = getBlockIndex( state, uid );
return state.editor.blocksByUid[ state.editor.blockOrder[ order - 1 ] ] || null;
return state.editor.present.blocksByUid[ state.editor.present.blockOrder[ order - 1 ] ] || null;
}

/**
Expand All @@ -769,7 +727,7 @@ export function getPreviousBlock( state, uid ) {
*/
export function getNextBlock( state, uid ) {
const order = getBlockIndex( state, uid );
return state.editor.blocksByUid[ state.editor.blockOrder[ order + 1 ] ] || null;
return state.editor.present.blocksByUid[ state.editor.present.blockOrder[ order + 1 ] ] || null;
}

/**
Expand Down Expand Up @@ -860,7 +818,7 @@ export function isTyping( state ) {
*/
export function getBlockInsertionPoint( state ) {
if ( getEditorMode( state ) !== 'visual' ) {
return state.editor.blockOrder.length;
return state.editor.present.blockOrder.length;
}

const position = getBlockSiblingInserterPosition( state );
Expand All @@ -878,7 +836,7 @@ export function getBlockInsertionPoint( state ) {
return getBlockIndex( state, selectedBlock.uid ) + 1;
}

return state.editor.blockOrder.length;
return state.editor.present.blockOrder.length;
}

/**
Expand Down Expand Up @@ -948,20 +906,20 @@ export function didPostSaveRequestFail( state ) {
* @return {?String} Suggested post format
*/
export function getSuggestedPostFormat( state ) {
const blocks = state.editor.blockOrder;
const blocks = state.editor.present.blockOrder;

let name;
// If there is only one block in the content of the post grab its name
// so we can derive a suitable post format from it.
if ( blocks.length === 1 ) {
name = state.editor.blocksByUid[ blocks[ 0 ] ].name;
name = state.editor.present.blocksByUid[ blocks[ 0 ] ].name;
}

// If there are two blocks in the content and the last one is a text blocks
// grab the name of the first one to also suggest a post format from it.
if ( blocks.length === 2 ) {
if ( state.editor.blocksByUid[ blocks[ 1 ] ].name === 'core/paragraph' ) {
name = state.editor.blocksByUid[ blocks[ 0 ] ].name;
if ( state.editor.present.blocksByUid[ blocks[ 1 ] ].name === 'core/paragraph' ) {
name = state.editor.present.blocksByUid[ blocks[ 0 ] ].name;
}
}

Expand Down Expand Up @@ -1004,9 +962,9 @@ export const getEditedPostContent = createSelector(
return serialize( getBlocks( state ) );
},
( state ) => [
state.editor.edits.content,
state.editor.blocksByUid,
state.editor.blockOrder,
state.editor.present.edits.content,
state.editor.present.blocksByUid,
state.editor.present.blockOrder,
],
);

Expand Down
Loading

0 comments on commit a116256

Please sign in to comment.