fix: reapply defaults on discard (#864)
This commit is contained in:
parent
5602812774
commit
6bcf451a18
@ -1,5 +1,3 @@
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
import { currentBackend } from '../backend';
|
||||
import {
|
||||
ADD_DRAFT_ENTRY_MEDIA_FILE,
|
||||
@ -40,16 +38,11 @@ import {
|
||||
SORT_ENTRIES_SUCCESS,
|
||||
} from '../constants';
|
||||
import ValidationErrorTypes from '../constants/validationErrorTypes';
|
||||
import {
|
||||
I18N_FIELD_DUPLICATE,
|
||||
I18N_FIELD_TRANSLATE,
|
||||
duplicateDefaultI18nFields,
|
||||
hasI18n,
|
||||
serializeI18n,
|
||||
} from '../lib/i18n';
|
||||
import { hasI18n, serializeI18n } from '../lib/i18n';
|
||||
import { serializeValues } from '../lib/serializeEntryValues';
|
||||
import { Cursor } from '../lib/util';
|
||||
import { selectFields, updateFieldByKey } from '../lib/util/collection.util';
|
||||
import { createEmptyDraftData, createEmptyDraftI18nData } from '../lib/util/entry.util';
|
||||
import { selectCollectionEntriesCursor } from '../reducers/selectors/cursors';
|
||||
import {
|
||||
selectEntriesSortField,
|
||||
@ -77,7 +70,6 @@ import type {
|
||||
FieldError,
|
||||
I18nSettings,
|
||||
ImplementationMediaFile,
|
||||
ObjectValue,
|
||||
SortDirection,
|
||||
ValueOrNestedValue,
|
||||
ViewFilter,
|
||||
@ -439,10 +431,10 @@ export function emptyDraftCreated(entry: Entry) {
|
||||
/*
|
||||
* Exported simple Action Creators
|
||||
*/
|
||||
export function createDraftFromEntry(entry: Entry) {
|
||||
export function createDraftFromEntry(collection: Collection, entry: Entry) {
|
||||
return {
|
||||
type: DRAFT_CREATE_FROM_ENTRY,
|
||||
payload: { entry },
|
||||
payload: { collection, entry },
|
||||
} as const;
|
||||
}
|
||||
|
||||
@ -625,7 +617,7 @@ export function loadEntry(collection: Collection, slug: string, silent = false)
|
||||
await dispatch(loadMedia());
|
||||
const loadedEntry = await tryLoadEntry(getState(), collection, slug);
|
||||
dispatch(entryLoaded(collection, loadedEntry));
|
||||
dispatch(createDraftFromEntry(loadedEntry));
|
||||
dispatch(createDraftFromEntry(collection, loadedEntry));
|
||||
} catch (error: unknown) {
|
||||
console.error(error);
|
||||
if (error instanceof Error) {
|
||||
@ -878,64 +870,6 @@ export function createEmptyDraft(collection: Collection, search: string) {
|
||||
};
|
||||
}
|
||||
|
||||
export function createEmptyDraftData(
|
||||
fields: Field[],
|
||||
skipField: (field: Field) => boolean = () => false,
|
||||
) {
|
||||
const ddd = fields.reduce((acc, item) => {
|
||||
if (skipField(item)) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
const subfields = 'fields' in item && item.fields;
|
||||
const list = item.widget === 'list';
|
||||
const name = item.name;
|
||||
const defaultValue = (('default' in item ? item.default : null) ?? null) as EntryData;
|
||||
|
||||
function isEmptyDefaultValue(val: EntryData | EntryData[]) {
|
||||
return [[{}], {}].some(e => isEqual(val, e));
|
||||
}
|
||||
|
||||
if (subfields) {
|
||||
if (list && Array.isArray(defaultValue)) {
|
||||
acc[name] = defaultValue;
|
||||
} else {
|
||||
const asList = Array.isArray(subfields) ? subfields : [subfields];
|
||||
|
||||
const subDefaultValue = list
|
||||
? [createEmptyDraftData(asList, skipField)]
|
||||
: createEmptyDraftData(asList, skipField);
|
||||
|
||||
if (!isEmptyDefaultValue(subDefaultValue)) {
|
||||
acc[name] = subDefaultValue;
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (defaultValue !== null) {
|
||||
acc[name] = defaultValue;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {} as ObjectValue);
|
||||
|
||||
return ddd;
|
||||
}
|
||||
|
||||
function createEmptyDraftI18nData(collection: Collection, dataFields: Field[]) {
|
||||
if (!hasI18n(collection)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
function skipField(field: Field) {
|
||||
return field.i18n !== I18N_FIELD_DUPLICATE && field.i18n !== I18N_FIELD_TRANSLATE;
|
||||
}
|
||||
|
||||
const i18nData = createEmptyDraftData(dataFields, skipField);
|
||||
return duplicateDefaultI18nFields(collection, i18nData);
|
||||
}
|
||||
|
||||
export function getMediaAssets({ entry }: { entry: Entry }) {
|
||||
const filesArray = entry.mediaFiles;
|
||||
const assets = filesArray
|
||||
|
@ -327,6 +327,10 @@ const Editor: FC<TranslatedProps<EditorProps>> = ({
|
||||
await dispatch(loadScroll());
|
||||
}, [dispatch]);
|
||||
|
||||
const handleDiscardDraft = useCallback(() => {
|
||||
setVersion(version => version + 1);
|
||||
}, []);
|
||||
|
||||
if (entry && entry.error) {
|
||||
return (
|
||||
<div>
|
||||
@ -356,6 +360,7 @@ const Editor: FC<TranslatedProps<EditorProps>> = ({
|
||||
toggleScroll={handleToggleScroll}
|
||||
scrollSyncActive={scrollSyncActive}
|
||||
loadScroll={handleLoadScroll}
|
||||
onDiscardDraft={handleDiscardDraft}
|
||||
submitted={submitted}
|
||||
slug={slug}
|
||||
t={t}
|
||||
|
@ -76,6 +76,7 @@ interface EditorInterfaceProps {
|
||||
loadScroll: () => void;
|
||||
submitted: boolean;
|
||||
slug: string | undefined;
|
||||
onDiscardDraft: () => void;
|
||||
}
|
||||
|
||||
const EditorInterface = ({
|
||||
@ -97,6 +98,7 @@ const EditorInterface = ({
|
||||
toggleScroll,
|
||||
submitted,
|
||||
slug,
|
||||
onDiscardDraft,
|
||||
}: TranslatedProps<EditorInterfaceProps>) => {
|
||||
const config = useAppSelector(selectConfig);
|
||||
|
||||
@ -413,6 +415,7 @@ const EditorInterface = ({
|
||||
showMobilePreview={showMobilePreview}
|
||||
onMobilePreviewToggle={toggleMobilePreview}
|
||||
className="flex"
|
||||
onDiscardDraft={onDiscardDraft}
|
||||
/>
|
||||
}
|
||||
>
|
||||
|
@ -49,6 +49,7 @@ export interface EditorToolbarProps {
|
||||
className?: string;
|
||||
showMobilePreview: boolean;
|
||||
onMobilePreviewToggle: () => void;
|
||||
onDiscardDraft: () => void;
|
||||
}
|
||||
|
||||
const EditorToolbar = ({
|
||||
@ -75,6 +76,7 @@ const EditorToolbar = ({
|
||||
className,
|
||||
showMobilePreview,
|
||||
onMobilePreviewToggle,
|
||||
onDiscardDraft,
|
||||
}: TranslatedProps<EditorToolbarProps>) => {
|
||||
const canCreate = useMemo(
|
||||
() => ('folder' in collection && collection.create) ?? false,
|
||||
@ -100,10 +102,11 @@ const EditorToolbar = ({
|
||||
color: 'warning',
|
||||
})
|
||||
) {
|
||||
dispatch(deleteLocalBackup(collection, slug));
|
||||
dispatch(loadEntry(collection, slug));
|
||||
await dispatch(deleteLocalBackup(collection, slug));
|
||||
await dispatch(loadEntry(collection, slug));
|
||||
onDiscardDraft();
|
||||
}
|
||||
}, [collection, dispatch, slug]);
|
||||
}, [collection, dispatch, onDiscardDraft, slug]);
|
||||
|
||||
const menuItems: JSX.Element[][] = useMemo(() => {
|
||||
const items: JSX.Element[] = [];
|
||||
|
75
packages/core/src/lib/util/entry.util.ts
Normal file
75
packages/core/src/lib/util/entry.util.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
import { isNotNullish } from './null.util';
|
||||
import {
|
||||
I18N_FIELD_DUPLICATE,
|
||||
I18N_FIELD_TRANSLATE,
|
||||
duplicateDefaultI18nFields,
|
||||
hasI18n,
|
||||
} from '../i18n';
|
||||
|
||||
import type { Collection, EntryData, Field, ObjectValue } from '@staticcms/core/interface';
|
||||
|
||||
export function applyDefaultsToDraftData(
|
||||
fields: Field[],
|
||||
skipField: (field: Field) => boolean = () => false,
|
||||
initialValue?: ObjectValue | null,
|
||||
) {
|
||||
const emptyDraftData = fields.reduce((acc, item) => {
|
||||
const name = item.name;
|
||||
|
||||
if (skipField(item) || isNotNullish(acc[name])) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
const subfields = 'fields' in item && item.fields;
|
||||
const list = item.widget === 'list';
|
||||
const defaultValue = (('default' in item ? item.default : null) ?? null) as EntryData;
|
||||
|
||||
function isEmptyDefaultValue(val: EntryData | EntryData[]) {
|
||||
return [[{}], {}].some(e => isEqual(val, e));
|
||||
}
|
||||
|
||||
if (subfields) {
|
||||
if (list && Array.isArray(defaultValue)) {
|
||||
acc[name] = defaultValue;
|
||||
} else {
|
||||
const asList = Array.isArray(subfields) ? subfields : [subfields];
|
||||
|
||||
const subDefaultValue = list
|
||||
? [applyDefaultsToDraftData(asList, skipField)]
|
||||
: applyDefaultsToDraftData(asList, skipField);
|
||||
|
||||
if (!isEmptyDefaultValue(subDefaultValue)) {
|
||||
acc[name] = subDefaultValue;
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (defaultValue !== null) {
|
||||
acc[name] = defaultValue;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, (initialValue ?? {}) as ObjectValue);
|
||||
|
||||
return emptyDraftData;
|
||||
}
|
||||
|
||||
export function createEmptyDraftData(fields: Field[], skipField?: (field: Field) => boolean) {
|
||||
return applyDefaultsToDraftData(fields, skipField);
|
||||
}
|
||||
|
||||
export function createEmptyDraftI18nData(collection: Collection, dataFields: Field[]) {
|
||||
if (!hasI18n(collection)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
function skipField(field: Field) {
|
||||
return field.i18n !== I18N_FIELD_DUPLICATE && field.i18n !== I18N_FIELD_TRANSLATE;
|
||||
}
|
||||
|
||||
const i18nData = createEmptyDraftData(dataFields, skipField);
|
||||
return duplicateDefaultI18nFields(collection, i18nData);
|
||||
}
|
@ -21,6 +21,8 @@ import {
|
||||
REMOVE_DRAFT_ENTRY_MEDIA_FILE,
|
||||
} from '../constants';
|
||||
import { duplicateI18nFields, getDataPath } from '../lib/i18n';
|
||||
import { fileForEntry } from '../lib/util/collection.util';
|
||||
import { applyDefaultsToDraftData } from '../lib/util/entry.util';
|
||||
import { set } from '../lib/util/object.util';
|
||||
|
||||
import type { EntriesAction } from '../actions/entries';
|
||||
@ -56,10 +58,18 @@ function entryDraftReducer(
|
||||
newRecord: false,
|
||||
};
|
||||
|
||||
const collection = action.payload.collection;
|
||||
|
||||
const file = fileForEntry(collection, entry.slug);
|
||||
const fields = file ? file.fields : 'fields' in collection ? collection.fields : [];
|
||||
|
||||
// Existing Entry
|
||||
return {
|
||||
...newState,
|
||||
entry,
|
||||
entry: {
|
||||
...entry,
|
||||
data: applyDefaultsToDraftData(fields, undefined, entry.data),
|
||||
},
|
||||
original: entry,
|
||||
fieldsErrors: {},
|
||||
hasChanged: false,
|
||||
|
Loading…
x
Reference in New Issue
Block a user