fix: call createDraftFromEntry after entry is loaded instead in Editor (#3418)
* fix: call createDraftFromEntry after entry is loaded instead in Editor
This commit is contained in:
parent
c9b82551ed
commit
2409323dba
@ -64,7 +64,7 @@ describe('editorialWorkflow actions', () => {
|
||||
|
||||
return store.dispatch(actions.loadUnpublishedEntry(collection, slug)).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions).toHaveLength(3);
|
||||
expect(actions).toHaveLength(4);
|
||||
expect(actions[0]).toEqual({
|
||||
type: 'UNPUBLISHED_ENTRY_REQUEST',
|
||||
payload: {
|
||||
@ -80,6 +80,12 @@ describe('editorialWorkflow actions', () => {
|
||||
entry: { ...entry, mediaFiles: [{ file: { name: 'name' }, id: '1', draft: true }] },
|
||||
},
|
||||
});
|
||||
expect(actions[3]).toEqual({
|
||||
type: 'DRAFT_CREATE_FROM_ENTRY',
|
||||
payload: {
|
||||
entry,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -111,7 +117,7 @@ describe('editorialWorkflow actions', () => {
|
||||
|
||||
return store.dispatch(actions.publishUnpublishedEntry('posts', slug)).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions).toHaveLength(6);
|
||||
expect(actions).toHaveLength(7);
|
||||
|
||||
expect(actions[0]).toEqual({
|
||||
type: 'UNPUBLISHED_ENTRY_PUBLISH_REQUEST',
|
||||
@ -155,6 +161,12 @@ describe('editorialWorkflow actions', () => {
|
||||
collection: 'posts',
|
||||
},
|
||||
});
|
||||
expect(actions[6]).toEqual({
|
||||
type: 'DRAFT_CREATE_FROM_ENTRY',
|
||||
payload: {
|
||||
entry,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
import { selectFields } from '../reducers/collections';
|
||||
import { EDITORIAL_WORKFLOW, status, Status } from '../constants/publishModes';
|
||||
import { EDITORIAL_WORKFLOW_ERROR } from 'netlify-cms-lib-util';
|
||||
import { loadEntry, entryDeleted, getMediaAssets } from './entries';
|
||||
import { loadEntry, entryDeleted, getMediaAssets, createDraftFromEntry } from './entries';
|
||||
import { createAssetProxy } from '../valueObjects/AssetProxy';
|
||||
import { addAssets } from './media';
|
||||
import { loadMedia } from './mediaLibrary';
|
||||
@ -290,6 +290,7 @@ export function loadUnpublishedEntry(collection: Collection, slug: string) {
|
||||
}
|
||||
|
||||
dispatch(unpublishedEntryLoaded(collection, { ...entry, mediaFiles }));
|
||||
dispatch(createDraftFromEntry(entry));
|
||||
} catch (error) {
|
||||
if (error.name === EDITORIAL_WORKFLOW_ERROR && error.notUnderEditorialWorkflow) {
|
||||
dispatch(unpublishedEntryRedirected(collection, slug));
|
||||
|
@ -201,10 +201,10 @@ export function emptyDraftCreated(entry: EntryValue) {
|
||||
/*
|
||||
* Exported simple Action Creators
|
||||
*/
|
||||
export function createDraftFromEntry(entry: EntryMap, metadata?: Map<string, unknown>) {
|
||||
export function createDraftFromEntry(entry: EntryValue) {
|
||||
return {
|
||||
type: DRAFT_CREATE_FROM_ENTRY,
|
||||
payload: { entry, metadata },
|
||||
payload: { entry },
|
||||
};
|
||||
}
|
||||
|
||||
@ -339,25 +339,25 @@ export function loadEntry(collection: Collection, slug: string) {
|
||||
const backend = currentBackend(state.config);
|
||||
await waitForMediaLibraryToLoad(dispatch, getState());
|
||||
dispatch(entryLoading(collection, slug));
|
||||
return backend
|
||||
.getEntry(getState(), collection, slug)
|
||||
.then((loadedEntry: EntryValue) => {
|
||||
return dispatch(entryLoaded(collection, loadedEntry));
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
console.error(error);
|
||||
dispatch(
|
||||
notifSend({
|
||||
message: {
|
||||
details: error.message,
|
||||
key: 'ui.toast.onFailToLoadEntries',
|
||||
},
|
||||
kind: 'danger',
|
||||
dismissAfter: 8000,
|
||||
}),
|
||||
);
|
||||
dispatch(entryLoadError(error, collection, slug));
|
||||
});
|
||||
|
||||
try {
|
||||
const loadedEntry = await backend.getEntry(getState(), collection, slug);
|
||||
dispatch(entryLoaded(collection, loadedEntry));
|
||||
dispatch(createDraftFromEntry(loadedEntry));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
dispatch(
|
||||
notifSend({
|
||||
message: {
|
||||
details: error.message,
|
||||
key: 'ui.toast.onFailToLoadEntries',
|
||||
},
|
||||
kind: 'danger',
|
||||
dismissAfter: 8000,
|
||||
}),
|
||||
);
|
||||
dispatch(entryLoadError(error, collection, slug));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,6 @@ import { logoutUser } from 'Actions/auth';
|
||||
import {
|
||||
loadEntry,
|
||||
loadEntries,
|
||||
createDraftFromEntry,
|
||||
createDraftDuplicateFromEntry,
|
||||
createEmptyDraft,
|
||||
discardDraft,
|
||||
@ -30,7 +29,6 @@ import {
|
||||
deleteUnpublishedEntry,
|
||||
} from 'Actions/editorialWorkflow';
|
||||
import { loadDeployPreview } from 'Actions/deploys';
|
||||
import { deserializeValues } from 'Lib/serializeEntryValues';
|
||||
import { selectEntry, selectUnpublishedEntry, selectDeployPreview } from 'Reducers';
|
||||
import { selectFields } from 'Reducers/collections';
|
||||
import { status, EDITORIAL_WORKFLOW } from 'Constants/publishModes';
|
||||
@ -48,7 +46,6 @@ export class Editor extends React.Component {
|
||||
changeDraftField: PropTypes.func.isRequired,
|
||||
changeDraftFieldValidation: PropTypes.func.isRequired,
|
||||
collection: ImmutablePropTypes.map.isRequired,
|
||||
createDraftFromEntry: PropTypes.func.isRequired,
|
||||
createDraftDuplicateFromEntry: PropTypes.func.isRequired,
|
||||
createEmptyDraft: PropTypes.func.isRequired,
|
||||
discardDraft: PropTypes.func.isRequired,
|
||||
@ -198,18 +195,9 @@ export class Editor extends React.Component {
|
||||
|
||||
if (prevProps.entry === this.props.entry) return;
|
||||
|
||||
const { entry, newEntry, fields, collection } = this.props;
|
||||
const { newEntry, collection } = this.props;
|
||||
|
||||
if (entry && !entry.get('isFetching') && !entry.get('error')) {
|
||||
/**
|
||||
* Deserialize entry values for widgets with registered serializers before
|
||||
* creating the entry draft.
|
||||
*/
|
||||
const values = deserializeValues(entry.get('data'), fields);
|
||||
const deserializedEntry = entry.set('data', values);
|
||||
const fieldsMetaData = this.props.entryDraft && this.props.entryDraft.get('fieldsMetaData');
|
||||
this.createDraft(deserializedEntry, fieldsMetaData);
|
||||
} else if (newEntry) {
|
||||
if (newEntry) {
|
||||
prevProps.createEmptyDraft(collection, this.props.location.search);
|
||||
}
|
||||
}
|
||||
@ -224,10 +212,6 @@ export class Editor extends React.Component {
|
||||
this.props.persistLocalBackup(entry, collection);
|
||||
}, 2000);
|
||||
|
||||
createDraft = (entry, metadata) => {
|
||||
if (entry) this.props.createDraftFromEntry(entry, metadata);
|
||||
};
|
||||
|
||||
handleChangeDraftField = (field, value, metadata) => {
|
||||
const entries = [this.props.unPublishedEntry, this.props.publishedEntry].filter(Boolean);
|
||||
this.props.changeDraftField(field, value, metadata, entries);
|
||||
@ -509,7 +493,6 @@ const mapDispatchToProps = {
|
||||
retrieveLocalBackup,
|
||||
persistLocalBackup,
|
||||
deleteLocalBackup,
|
||||
createDraftFromEntry,
|
||||
createDraftDuplicateFromEntry,
|
||||
createEmptyDraft,
|
||||
discardDraft,
|
||||
|
@ -25,7 +25,6 @@ describe('Editor', () => {
|
||||
changeDraftField: jest.fn(),
|
||||
changeDraftFieldValidation: jest.fn(),
|
||||
collection: fromJS({ name: 'posts' }),
|
||||
createDraftFromEntry: jest.fn(),
|
||||
createDraftDuplicateFromEntry: jest.fn(),
|
||||
createEmptyDraft: jest.fn(),
|
||||
discardDraft: jest.fn(),
|
||||
@ -215,32 +214,4 @@ describe('Editor', () => {
|
||||
props.collection,
|
||||
);
|
||||
});
|
||||
|
||||
it('should create draft from entry when done fetching', () => {
|
||||
const { rerender } = render(
|
||||
<Editor
|
||||
{...props}
|
||||
entryDraft={fromJS({ entry: {} })}
|
||||
entry={fromJS({ isFetching: false, mediaFiles: [] })}
|
||||
/>,
|
||||
);
|
||||
|
||||
jest.clearAllMocks();
|
||||
rerender(
|
||||
<Editor
|
||||
{...props}
|
||||
entryDraft={fromJS({
|
||||
entry: {},
|
||||
fieldsMetaData: {},
|
||||
})}
|
||||
entry={fromJS({ isFetching: false, mediaFiles: [{ id: '1' }], data: {} })}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(props.createDraftFromEntry).toHaveBeenCalledTimes(1);
|
||||
expect(props.createDraftFromEntry).toHaveBeenCalledWith(
|
||||
fromJS({ isFetching: false, data: {}, mediaFiles: [{ id: '1' }] }),
|
||||
fromJS({}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -36,12 +36,9 @@ const entryDraftReducer = (state = Map(), action) => {
|
||||
case DRAFT_CREATE_FROM_ENTRY:
|
||||
// Existing Entry
|
||||
return state.withMutations(state => {
|
||||
state.set('entry', action.payload.entry);
|
||||
state.set('entry', fromJS(action.payload.entry));
|
||||
state.setIn(['entry', 'newRecord'], false);
|
||||
// An existing entry may already have metadata. If we surfed away and back to its
|
||||
// editor page, the metadata will have been fetched already, so we shouldn't
|
||||
// clear it as to not break relation lists.
|
||||
state.set('fieldsMetaData', action.payload.metadata || Map());
|
||||
state.set('fieldsMetaData', Map());
|
||||
state.set('fieldsErrors', Map());
|
||||
state.set('hasChanged', false);
|
||||
state.set('key', uuid());
|
||||
|
Loading…
x
Reference in New Issue
Block a user