diff --git a/docs/designers-developers/developers/data/data-core-editor.md b/docs/designers-developers/developers/data/data-core-editor.md index 905e9cc077847d..6b08fa9b2187df 100644 --- a/docs/designers-developers/developers/data/data-core-editor.md +++ b/docs/designers-developers/developers/data/data-core-editor.md @@ -892,6 +892,18 @@ _Returns_ - `boolean`: Whether or not the permalink is editable. +# **isPostAutosavingLocked** + +Returns whether post autosaving is locked. + +_Parameters_ + +- _state_ `Object`: Global application state. + +_Returns_ + +- `boolean`: Is locked. + # **isPostLocked** Returns whether the post is locked. diff --git a/packages/editor/src/store/reducer.js b/packages/editor/src/store/reducer.js index a88e05f30ac775..69ab09f7bf0c8c 100644 --- a/packages/editor/src/store/reducer.js +++ b/packages/editor/src/store/reducer.js @@ -238,6 +238,27 @@ export function postSavingLock( state = {}, action ) { return state; } +/** + * Post autosaving lock. + * + * When post autosaving is locked, the post will not autosave. + * + * @param {PostAutosavingLockState} state Current state. + * @param {Object} action Dispatched action. + * + * @return {PostLockState} Updated state. + */ +export function postAutosavingLock( state = {}, action ) { + switch ( action.type ) { + case 'LOCK_POST_AUTOSAVING': + return { ...state, [ action.lockName ]: true }; + + case 'UNLOCK_POST_AUTOSAVING': + return omit( state, action.lockName ); + } + return state; +} + export const reusableBlocks = combineReducers( { data( state = {}, action ) { switch ( action.type ) { @@ -393,4 +414,5 @@ export default optimist( combineReducers( { postSavingLock, isReady, editorSettings, + postAutosavingLock, } ) ); diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js index daf7adbd9ee2ae..eda959abba370c 100644 --- a/packages/editor/src/store/selectors.js +++ b/packages/editor/src/store/selectors.js @@ -535,6 +535,11 @@ export const isEditedPostAutosaveable = createRegistrySelector( ( select ) => fu return false; } + // A post is not autosavable when there is a post autosave lock. + if ( isPostAutosavingLocked( state ) ) { + return false; + } + const postType = getCurrentPostType( state ); const postId = getCurrentPostId( state ); const hasFetchedAutosave = select( 'core' ).hasFetchedAutosaves( postType, postId ); @@ -1100,6 +1105,17 @@ export function isPostSavingLocked( state ) { return Object.keys( state.postSavingLock ).length > 0; } +/** + * Returns whether post autosaving is locked. + * + * @param {Object} state Global application state. + * + * @return {boolean} Is locked. + */ +export function isPostAutosavingLocked( state ) { + return Object.keys( state.postAutosavingLock ).length > 0; +} + /** * Returns whether the edition of the post has been taken over. * diff --git a/packages/editor/src/store/test/reducer.js b/packages/editor/src/store/test/reducer.js index 3fc6461b34e538..156ce5b9b3602a 100644 --- a/packages/editor/src/store/test/reducer.js +++ b/packages/editor/src/store/test/reducer.js @@ -15,6 +15,7 @@ import { saving, reusableBlocks, postSavingLock, + postAutosavingLock, } from '../reducer'; describe( 'state', () => { @@ -489,4 +490,49 @@ describe( 'state', () => { expect( state ).toEqual( {} ); } ); } ); + + describe( 'postAutosavingLock', () => { + it( 'returns empty object by default', () => { + const state = postAutosavingLock( undefined, {} ); + + expect( state ).toEqual( {} ); + } ); + + it( 'returns correct post locks when locks added and removed', () => { + let state = postAutosavingLock( undefined, { + type: 'LOCK_POST_AUTOSAVING', + lockName: 'test-lock', + } ); + + expect( state ).toEqual( { + 'test-lock': true, + } ); + + state = postAutosavingLock( deepFreeze( state ), { + type: 'LOCK_POST_AUTOSAVING', + lockName: 'test-lock-2', + } ); + + expect( state ).toEqual( { + 'test-lock': true, + 'test-lock-2': true, + } ); + + state = postAutosavingLock( deepFreeze( state ), { + type: 'UNLOCK_POST_AUTOSAVING', + lockName: 'test-lock', + } ); + + expect( state ).toEqual( { + 'test-lock-2': true, + } ); + + state = postAutosavingLock( deepFreeze( state ), { + type: 'UNLOCK_POST_AUTOSAVING', + lockName: 'test-lock-2', + } ); + + expect( state ).toEqual( {} ); + } ); + } ); } ); diff --git a/packages/editor/src/store/test/selectors.js b/packages/editor/src/store/test/selectors.js index 73e0938ffccce4..99d0efac340932 100644 --- a/packages/editor/src/store/test/selectors.js +++ b/packages/editor/src/store/test/selectors.js @@ -159,6 +159,7 @@ const { getPermalink, getPermalinkParts, isPostSavingLocked, + isPostAutosavingLocked, canUserUseUnfilteredHTML, } = selectors; @@ -1157,6 +1158,28 @@ describe( 'selectors', () => { } ); } ); + describe( 'isPostAutosavingLocked', () => { + it( 'should return true if the post has postAutosavingLocks', () => { + const state = { + postAutosavingLock: { example: true }, + currentPost: {}, + saving: {}, + }; + + expect( isPostAutosavingLocked( state ) ).toBe( true ); + } ); + + it( 'should return false if the post has no postAutosavingLocks', () => { + const state = { + postAutosavingLock: {}, + currentPost: {}, + saving: {}, + }; + + expect( isPostAutosavingLocked( state ) ).toBe( false ); + } ); + } ); + describe( 'isEditedPostSaveable', () => { it( 'should return false if the post has no title, excerpt, content', () => { const state = { @@ -1408,6 +1431,7 @@ describe( 'selectors', () => { return true; }, getAutosave() {}, + postAutosavingLock: {}, }; expect( isEditedPostAutosaveable( state ) ).toBe( true ); @@ -1440,6 +1464,7 @@ describe( 'selectors', () => { excerpt: 'foo', }; }, + postAutosavingLock: {}, }; expect( isEditedPostAutosaveable( state ) ).toBe( false ); @@ -1471,6 +1496,7 @@ describe( 'selectors', () => { excerpt: 'foo', }; }, + postAutosavingLock: {}, }; expect( isEditedPostAutosaveable( state ) ).toBe( true ); @@ -1505,12 +1531,23 @@ describe( 'selectors', () => { [ variantField ]: 'bar', }; }, + postAutosavingLock: {}, }; expect( isEditedPostAutosaveable( state ) ).toBe( true ); } } } ); + + it( 'should return false if autosaving is locked', () => { + const state = { + currentPost: {}, + saving: {}, + postAutosavingLock: { example: true }, + }; + + expect( isEditedPostAutosaveable( state ) ).toBe( false ); + } ); } ); describe( 'isEditedPostEmpty', () => {