diff --git a/packages/editor/src/components/autosave-monitor/index.js b/packages/editor/src/components/autosave-monitor/index.js index f3a7d81bd6481..c1be1b20e0a40 100644 --- a/packages/editor/src/components/autosave-monitor/index.js +++ b/packages/editor/src/components/autosave-monitor/index.js @@ -7,14 +7,33 @@ import { withSelect, withDispatch } from '@wordpress/data'; export class AutosaveMonitor extends Component { componentDidUpdate( prevProps ) { - const { isDirty, editsReference, isAutosaveable } = this.props; + const { isDirty, editsReference, isAutosaveable, isAutosaving } = this.props; + + // The edits reference is held for comparison to avoid scheduling an + // autosave if an edit has not been made since the last autosave + // completion. This is assigned when the autosave completes, and reset + // when an edit occurs. + // + // See: https://github.com/WordPress/gutenberg/issues/12318 + + if ( editsReference !== prevProps.editsReference ) { + this.didAutosaveForEditsReference = false; + } + + if ( ! isAutosaving && prevProps.isAutosaving ) { + this.didAutosaveForEditsReference = true; + } if ( prevProps.isDirty !== isDirty || prevProps.isAutosaveable !== isAutosaveable || prevProps.editsReference !== editsReference ) { - this.toggleTimer( isDirty && isAutosaveable ); + this.toggleTimer( + isDirty && + isAutosaveable && + ! this.didAutosaveForEditsReference + ); } } @@ -45,6 +64,7 @@ export default compose( [ isEditedPostAutosaveable, getEditorSettings, getReferenceByDistinctEdits, + isAutosavingPost, } = select( 'core/editor' ); const { autosaveInterval } = getEditorSettings(); @@ -53,6 +73,7 @@ export default compose( [ isDirty: isEditedPostDirty(), isAutosaveable: isEditedPostAutosaveable(), editsReference: getReferenceByDistinctEdits(), + isAutosaving: isAutosavingPost(), autosaveInterval, }; } ), diff --git a/packages/editor/src/components/autosave-monitor/test/index.js b/packages/editor/src/components/autosave-monitor/test/index.js index e518586d6e719..2e7ecd1b3b2a3 100644 --- a/packages/editor/src/components/autosave-monitor/test/index.js +++ b/packages/editor/src/components/autosave-monitor/test/index.js @@ -82,6 +82,28 @@ describe( 'AutosaveMonitor', () => { expect( toggleTimer ).toHaveBeenCalledWith( false ); } ); + + it( 'should avoid scheduling autosave if still dirty but already autosaved for edits', () => { + // Explanation: When a published post is autosaved, it's still in a + // dirty state since the edits are not saved to the post until the + // user clicks "Update". To avoid recurring autosaves, ensure that + // an edit has occurred since the last autosave had completed. + + const beforeReference = []; + const afterReference = []; + + // A post is non-dirty while autosave is in-flight. + wrapper.setProps( { isDirty: false, isAutosaving: true, isAutosaveable: true, editsReference: beforeReference } ); + toggleTimer.mockClear(); + wrapper.setProps( { isDirty: true, isAutosaving: false, isAutosaveable: true, editsReference: beforeReference } ); + + expect( toggleTimer ).toHaveBeenCalledWith( false ); + + // Once edit occurs after autosave, resume scheduling. + wrapper.setProps( { isDirty: true, isAutosaving: false, isAutosaveable: true, editsReference: afterReference } ); + + expect( toggleTimer ).toHaveBeenCalledWith( false ); + } ); } ); describe( '#componentWillUnmount()', () => {