-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Editor: Reshape editor state for optimized and accurate dirtiness detection #10844
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -102,6 +102,26 @@ export function isEditedPostNew( state ) { | |||||||||||||||||||||||||||||||
return getCurrentPost( state ).status === 'auto-draft'; | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||
* Returns true if content includes unsaved changes, or false otherwise. | ||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||
* @param {Object} state Editor state. | ||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||
* @return {boolean} Whether content includes unsaved changes. | ||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||
export function hasChangedContent( state ) { | ||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||
state.editor.present.blocks.isDirty || | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
// `edits` is intended to contain only values which are different from | ||||||||||||||||||||||||||||||||
// the saved post, so the mere presence of a property is an indicator | ||||||||||||||||||||||||||||||||
// that the value is different than what is known to be saved. While | ||||||||||||||||||||||||||||||||
// content in Visual mode is represented by the blocks state, in Text | ||||||||||||||||||||||||||||||||
// mode it is tracked by `edits.content`. | ||||||||||||||||||||||||||||||||
'content' in state.editor.present.edits | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really get what this second condition is checking for. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'll add a comment to clarify, but it's a combination of two things which require this consideration:
|
||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||
* Returns true if there are unsaved values for the current edit session, or | ||||||||||||||||||||||||||||||||
* false if the editing state matches the saved or new post. | ||||||||||||||||||||||||||||||||
|
@@ -111,7 +131,23 @@ export function isEditedPostNew( state ) { | |||||||||||||||||||||||||||||||
* @return {boolean} Whether unsaved values exist. | ||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||
export function isEditedPostDirty( state ) { | ||||||||||||||||||||||||||||||||
return state.editor.isDirty || inSomeHistory( state, isEditedPostDirty ); | ||||||||||||||||||||||||||||||||
if ( hasChangedContent( state ) ) { | ||||||||||||||||||||||||||||||||
return true; | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
// Edits should contain only fields which differ from the saved post (reset | ||||||||||||||||||||||||||||||||
// at initial load and save complete). Thus, a non-empty edits state can be | ||||||||||||||||||||||||||||||||
// inferred to contain unsaved values. | ||||||||||||||||||||||||||||||||
if ( Object.keys( state.editor.present.edits ).length > 0 ) { | ||||||||||||||||||||||||||||||||
return true; | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
// Edits and change detectiona are reset at the start of a save, but a post | ||||||||||||||||||||||||||||||||
// is still considered dirty until the point at which the save completes. | ||||||||||||||||||||||||||||||||
// Because the save is performed optimistically, the prior states are held | ||||||||||||||||||||||||||||||||
// until committed. These can be referenced to determine whether there's a | ||||||||||||||||||||||||||||||||
// chance that state may be reverted into one considered dirty. | ||||||||||||||||||||||||||||||||
return inSomeHistory( state, isEditedPostDirty ); | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So if I understand properly. We conder a post dirty until it's completely saved (successful response)? right? any particular reason? should we clarify why in a comment? (I know it's not new in this PR though) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It's because change detection (now only for blocks) is reset at the start of the save, but we still want to avoid allowing a user to reload while that request is still pending. It's covered in this test: gutenberg/test/e2e/specs/change-detection.test.js Lines 216 to 230 in 26ccc15
I'll see if I can form a useful comment here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Added in rebased fd7a8ec. |
||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||
|
@@ -451,9 +487,17 @@ export function isEditedPostAutosaveable( state ) { | |||||||||||||||||||||||||||||||
return true; | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
// To avoid an expensive content serialization, use the content dirtiness | ||||||||||||||||||||||||||||||||
// flag in place of content field comparison against the known autosave. | ||||||||||||||||||||||||||||||||
// This is not strictly accurate, and relies on a tolerance toward autosave | ||||||||||||||||||||||||||||||||
// request failures for unnecessary saves. | ||||||||||||||||||||||||||||||||
if ( hasChangedContent( state ) ) { | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this is the fix for the performance issue. It might not be easy but I wonder if we can add an e2e test checking that we don't run There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Hmm, might not be too hard! I'll give it a shot. |
||||||||||||||||||||||||||||||||
return true; | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
// If the title, excerpt or content has changed, the post is autosaveable. | ||||||||||||||||||||||||||||||||
const autosave = getAutosave( state ); | ||||||||||||||||||||||||||||||||
return [ 'title', 'excerpt', 'content' ].some( ( field ) => ( | ||||||||||||||||||||||||||||||||
return [ 'title', 'excerpt' ].some( ( field ) => ( | ||||||||||||||||||||||||||||||||
autosave[ field ] !== getEditedPostAttribute( state, field ) | ||||||||||||||||||||||||||||||||
) ); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is everything below here just indented? It's a bit hard to see the code changes, if there are any.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, no changes aside from indentation.
GitHub has an option to hide whitespace-only changes, which should help make things a bit clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Funny that this Higher order reducer moved from "edits" to just "blocks". It almost feels like we need an
editBlocks
:)