Skip to content

Commit

Permalink
Core Data: Handle more change detection and saving flows.
Browse files Browse the repository at this point in the history
  • Loading branch information
epiqueras committed Aug 9, 2019
1 parent afaec5e commit bfee37a
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 25 deletions.
2 changes: 2 additions & 0 deletions packages/block-editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
replaceBlocks,
toggleSelection,
setNavigationMode,
__unstableMarkLastChangeAsPersistent,
} = dispatch( 'core/block-editor' );

return {
Expand Down Expand Up @@ -755,6 +756,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
}
},
onReplace( blocks, indexToSelect ) {
__unstableMarkLastChangeAsPersistent();
replaceBlocks( [ ownProps.clientId ], blocks, indexToSelect );
},
onShiftSelection() {
Expand Down
54 changes: 39 additions & 15 deletions packages/core-data/src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,11 @@ export function* saveEntityRecord(
kind,
name,
record,
{ isAutosave = false, getNoticeActionArgs } = { isAutosave: false }
{
isAutosave = false,
getSuccessNoticeActionArgs,
getFailureNoticeActionArgs,
} = { isAutosave: false }
) {
const entities = yield getKindEntities( kind );
const entity = find( entities, { kind, name } );
Expand All @@ -236,14 +240,14 @@ export function* saveEntityRecord(
const recordId = record[ entityIdKey ];

yield { type: 'SAVE_ENTITY_RECORD_START', kind, name, recordId, isAutosave };
let persistedRecord;
if ( isAutosave || getSuccessNoticeActionArgs || getFailureNoticeActionArgs ) {
persistedRecord = yield select( 'getEntityRecord', kind, name, recordId );
}
let error;
try {
const path = `${ entity.baseURL }${ recordId ? '/' + recordId : '' }`;
let persistedRecord;
let updatedRecord;
if ( isAutosave || getNoticeActionArgs ) {
persistedRecord = yield select( 'getEntityRecord', kind, name, recordId );
}

if ( isAutosave ) {
const currentUser = yield select( 'getCurrentUser' );
Expand All @@ -270,7 +274,20 @@ export function* saveEntityRecord(
method: 'POST',
data,
} );
yield receiveAutosaves( persistedRecord.id, updatedRecord );
// An autosave may be processed by the server as a regular save
// when its update is requested by the author and the post had
// draft or auto-draft status.
if ( persistedRecord.id === updatedRecord.id ) {
yield receiveEntityRecords(
kind,
name,
{ ...persistedRecord, ...updatedRecord },
undefined,
true
);
} else {
yield receiveAutosaves( persistedRecord.id, updatedRecord );
}
} else {
updatedRecord = yield apiFetch( {
path,
Expand All @@ -280,21 +297,28 @@ export function* saveEntityRecord(
yield receiveEntityRecords( kind, name, updatedRecord, undefined, true );
}

if ( getNoticeActionArgs ) {
if ( getSuccessNoticeActionArgs ) {
const postType = updatedRecord.type || persistedRecord.type;
const args = postType && getNoticeActionArgs(
persistedRecord,
updatedRecord,
yield select( 'getPostType', postType )
);
if ( args && args.length ) {
yield dispatch(
...args
const args =
postType &&
getSuccessNoticeActionArgs(
persistedRecord,
updatedRecord,
yield select( 'getPostType', postType )
);
if ( args && args.length ) {
yield dispatch( ...args );
}
}
} catch ( _error ) {
error = _error;

if ( getFailureNoticeActionArgs ) {
const args = getFailureNoticeActionArgs( persistedRecord, record, error );
if ( args && args.length ) {
yield dispatch( ...args );
}
}
}
yield {
type: 'SAVE_ENTITY_RECORD_FINISH',
Expand Down
7 changes: 3 additions & 4 deletions packages/core-data/src/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,12 +243,11 @@ export function isAutosavingEntityRecord( state, kind, name, recordId ) {
* @return {boolean} Whether the entity record is saving or not.
*/
export function isSavingEntityRecord( state, kind, name, recordId ) {
const { pending, isAutosave } = get(
return get(
state.entities.data,
[ kind, name, 'saving', recordId ],
{}
[ kind, name, 'saving', recordId, 'pending' ],
false
);
return Boolean( pending && ! isAutosave );
}

/**
Expand Down
7 changes: 5 additions & 2 deletions packages/e2e-tests/specs/change-detection.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ describe( 'Change detection', () => {

it( 'Should prompt if content added without save', async () => {
await clickBlockAppender();
await page.keyboard.type( 'Paragraph' );

await assertIsDirty( true );
} );
Expand Down Expand Up @@ -223,9 +224,9 @@ describe( 'Change detection', () => {
// Keyboard shortcut Ctrl+S save.
await pressKeyWithModifier( 'primary', 'S' );

await releaseSaveIntercept();

await assertIsDirty( true );

await releaseSaveIntercept();
} );

it( 'Should prompt if changes made while save is in-flight', async () => {
Expand All @@ -240,6 +241,7 @@ describe( 'Change detection', () => {
await pressKeyWithModifier( 'primary', 'S' );

await page.type( '.editor-post-title__input', '!' );
await page.waitForSelector( '.editor-post-save-draft' );

await releaseSaveIntercept();

Expand Down Expand Up @@ -279,6 +281,7 @@ describe( 'Change detection', () => {
await pressKeyWithModifier( 'primary', 'S' );

await clickBlockAppender();
await page.keyboard.type( 'Paragraph' );

// Allow save to complete. Disabling interception flushes pending.
await Promise.all( [
Expand Down
13 changes: 12 additions & 1 deletion packages/editor/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
} from './constants';
import {
getNotificationArgumentsForSaveSuccess,
getNotificationArgumentsForSaveFail,
getNotificationArgumentsForTrashFail,
} from './utils/notice-builder';
import { awaitNextStateChange, getRegistry } from './controls';
Expand Down Expand Up @@ -397,7 +398,7 @@ export function* savePost( options = {} ) {
postId,
{
...options,
getNoticeActionArgs: ( previousEntity, entity, type ) => {
getSuccessNoticeActionArgs: ( previousEntity, entity, type ) => {
const args = getNotificationArgumentsForSaveSuccess( {
previousPost: previousEntity,
post: entity,
Expand All @@ -408,6 +409,16 @@ export function* savePost( options = {} ) {
return [ 'core/notices', 'createSuccessNotice', ...args ];
}
},
getFailureNoticeActionArgs: ( previousEntity, edits, error ) => {
const args = getNotificationArgumentsForSaveFail( {
post: previousEntity,
edits,
error,
} );
if ( args && args.length ) {
return [ 'core/notices', 'createErrorNotice', ...args ];
}
},
}
);
yield __experimentalRequestPostUpdateFinish( options );
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ export function isAutosavingPost( state ) {
if ( ! isSavingPost( state ) ) {
return false;
}
return !! state.saving.options.isAutosave;
return !! get( state.saving, [ 'options', 'isAutosave' ] );
}

/**
Expand Down
14 changes: 12 additions & 2 deletions packages/editor/src/store/test/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,25 @@ describe( 'Post generator actions', () => {
() => true,
() => {
const { value } = fulfillment.next( currentPost().id );
value.args[ 3 ] = { ...value.args[ 3 ], getNoticeActionArgs: 'getNoticeActionArgs' };
value.args[ 3 ] = {
...value.args[ 3 ],
getSuccessNoticeActionArgs: 'getSuccessNoticeActionArgs',
getFailureNoticeActionArgs: 'getFailureNoticeActionArgs',
};
expect( value ).toEqual(
dispatch(
'core',
'saveEditedEntityRecord',
'postType',
currentPost().type,
currentPost().id,
{ isAutosave, getNoticeActionArgs: 'getNoticeActionArgs' }
{
isAutosave,
getSuccessNoticeActionArgs:
'getSuccessNoticeActionArgs',
getFailureNoticeActionArgs:
'getFailureNoticeActionArgs',
}
)
);
},
Expand Down

0 comments on commit bfee37a

Please sign in to comment.