Merge pull request #1978 from payloadcms/fix/1897
fix: removes old media when replaced and re-renders on save
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import AnimateHeight from 'react-animate-height';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Thumbnail from '../Thumbnail';
|
||||
@@ -6,11 +6,27 @@ import Button from '../Button';
|
||||
import Meta from './Meta';
|
||||
import Chevron from '../../icons/Chevron';
|
||||
import { Props } from './types';
|
||||
import { FileSizes, Upload } from '../../../../uploads/types';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'file-details';
|
||||
|
||||
// sort to the same as imageSizes
|
||||
const sortSizes = (sizes: FileSizes, imageSizes: Upload['imageSizes']) => {
|
||||
if (!imageSizes || imageSizes.length === 0) return sizes;
|
||||
|
||||
const orderedSizes: FileSizes = {};
|
||||
|
||||
imageSizes.forEach(({ name }) => {
|
||||
if (sizes[name]) {
|
||||
orderedSizes[name] = sizes[name];
|
||||
}
|
||||
});
|
||||
|
||||
return orderedSizes;
|
||||
};
|
||||
|
||||
const FileDetails: React.FC<Props> = (props) => {
|
||||
const {
|
||||
doc,
|
||||
@@ -21,6 +37,7 @@ const FileDetails: React.FC<Props> = (props) => {
|
||||
const {
|
||||
upload: {
|
||||
staticURL,
|
||||
imageSizes,
|
||||
},
|
||||
} = collection;
|
||||
|
||||
@@ -34,6 +51,12 @@ const FileDetails: React.FC<Props> = (props) => {
|
||||
url,
|
||||
} = doc;
|
||||
|
||||
const [orderedSizes, setOrderedSizes] = useState<FileSizes>(() => sortSizes(sizes, imageSizes));
|
||||
|
||||
useEffect(() => {
|
||||
setOrderedSizes(sortSizes(sizes, imageSizes));
|
||||
}, [sizes, imageSizes]);
|
||||
|
||||
const [moreInfoOpen, setMoreInfoOpen] = useState(false);
|
||||
const { t } = useTranslation('upload');
|
||||
|
||||
@@ -94,7 +117,7 @@ const FileDetails: React.FC<Props> = (props) => {
|
||||
height={moreInfoOpen ? 'auto' : 0}
|
||||
>
|
||||
<ul className={`${baseClass}__sizes`}>
|
||||
{Object.entries(sizes).map(([key, val]) => {
|
||||
{Object.entries(orderedSizes).map(([key, val]) => {
|
||||
if (val?.filename) {
|
||||
return (
|
||||
<li key={key}>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { SanitizedCollectionConfig } from '../../../../collections/config/types';
|
||||
import { FileSizes } from '../../../../uploads/types';
|
||||
import { Data } from '../../forms/Form/types';
|
||||
|
||||
export type Props = {
|
||||
collection: SanitizedCollectionConfig
|
||||
doc: Record<string, unknown>
|
||||
doc: Data & {
|
||||
sizes?: FileSizes
|
||||
}
|
||||
handleRemove?: () => void,
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ const DefaultEditView: React.FC<Props> = (props) => {
|
||||
onSave,
|
||||
permissions,
|
||||
isLoading,
|
||||
initialState,
|
||||
internalState,
|
||||
apiURL,
|
||||
action,
|
||||
hasSavePermission,
|
||||
@@ -90,7 +90,7 @@ const DefaultEditView: React.FC<Props> = (props) => {
|
||||
action={action}
|
||||
onSuccess={onSave}
|
||||
disabled={!hasSavePermission}
|
||||
initialState={initialState}
|
||||
initialState={internalState}
|
||||
>
|
||||
<FormLoadingOverlayToggle
|
||||
formIsLoading={isLoading}
|
||||
@@ -149,6 +149,7 @@ const DefaultEditView: React.FC<Props> = (props) => {
|
||||
<Upload
|
||||
data={data}
|
||||
collection={collection}
|
||||
internalState={internalState}
|
||||
/>
|
||||
)}
|
||||
<RenderFields
|
||||
|
||||
@@ -6,7 +6,8 @@ import useField from '../../../../forms/useField';
|
||||
import Button from '../../../../elements/Button';
|
||||
import FileDetails from '../../../../elements/FileDetails';
|
||||
import Error from '../../../../forms/Error';
|
||||
import { Props, Data } from './types';
|
||||
import { Props } from './types';
|
||||
import reduceFieldsToValues from '../../../../forms/Form/reduceFieldsToValues';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
@@ -26,6 +27,11 @@ const validate = (value) => {
|
||||
};
|
||||
|
||||
const Upload: React.FC<Props> = (props) => {
|
||||
const {
|
||||
collection,
|
||||
internalState,
|
||||
} = props;
|
||||
|
||||
const inputRef = useRef(null);
|
||||
const dropRef = useRef(null);
|
||||
const [selectingFile, setSelectingFile] = useState(false);
|
||||
@@ -33,13 +39,7 @@ const Upload: React.FC<Props> = (props) => {
|
||||
const [dragCounter, setDragCounter] = useState(0);
|
||||
const [replacingFile, setReplacingFile] = useState(false);
|
||||
const { t } = useTranslation('upload');
|
||||
|
||||
const {
|
||||
data = {} as Data,
|
||||
collection,
|
||||
} = props;
|
||||
|
||||
const { filename } = data;
|
||||
const [doc, setDoc] = useState(reduceFieldsToValues(internalState || {}, true));
|
||||
|
||||
const {
|
||||
value,
|
||||
@@ -99,6 +99,11 @@ const Upload: React.FC<Props> = (props) => {
|
||||
}
|
||||
}, [selectingFile, inputRef, setSelectingFile]);
|
||||
|
||||
useEffect(() => {
|
||||
setDoc(reduceFieldsToValues(internalState || {}, true));
|
||||
setReplacingFile(false);
|
||||
}, [internalState]);
|
||||
|
||||
useEffect(() => {
|
||||
const div = dropRef.current;
|
||||
if (div) {
|
||||
@@ -118,10 +123,6 @@ const Upload: React.FC<Props> = (props) => {
|
||||
return () => null;
|
||||
}, [handleDragIn, handleDragOut, handleDrop, value]);
|
||||
|
||||
useEffect(() => {
|
||||
setReplacingFile(false);
|
||||
}, [data]);
|
||||
|
||||
const classes = [
|
||||
baseClass,
|
||||
dragging && `${baseClass}--dragging`,
|
||||
@@ -134,9 +135,9 @@ const Upload: React.FC<Props> = (props) => {
|
||||
showError={showError}
|
||||
message={errorMessage}
|
||||
/>
|
||||
{(filename && !replacingFile) && (
|
||||
{(doc.filename && !replacingFile) && (
|
||||
<FileDetails
|
||||
doc={data}
|
||||
doc={doc}
|
||||
collection={collection}
|
||||
handleRemove={() => {
|
||||
setReplacingFile(true);
|
||||
@@ -144,7 +145,7 @@ const Upload: React.FC<Props> = (props) => {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{(!filename || replacingFile) && (
|
||||
{(!doc.filename || replacingFile) && (
|
||||
<div className={`${baseClass}__upload`}>
|
||||
{value && (
|
||||
<div className={`${baseClass}__file-selected`}>
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { SanitizedCollectionConfig } from '../../../../../../collections/config/types';
|
||||
|
||||
export type Data = {
|
||||
filename: string
|
||||
mimeType: string
|
||||
filesize: number
|
||||
}
|
||||
import { Fields } from '../../../../forms/Form/types';
|
||||
|
||||
export type Props = {
|
||||
data?: Data
|
||||
internalState?: Fields
|
||||
data?: Fields
|
||||
collection: SanitizedCollectionConfig
|
||||
adminThumbnail?: string
|
||||
mimeTypes?: string[];
|
||||
|
||||
@@ -40,13 +40,18 @@ const EditView: React.FC<IndexProps> = (props) => {
|
||||
const { params: { id } = {} } = useRouteMatch<Record<string, string>>();
|
||||
const { state: locationState } = useLocation();
|
||||
const history = useHistory();
|
||||
const [initialState, setInitialState] = useState<Fields>();
|
||||
const [internalState, setInternalState] = useState<Fields>();
|
||||
const [updatedAt, setUpdatedAt] = useState<string>();
|
||||
const { user } = useAuth();
|
||||
const { getVersions, preferencesKey, getDocPermissions, docPermissions } = useDocumentInfo();
|
||||
const { getPreference } = usePreferences();
|
||||
const { t } = useTranslation('general');
|
||||
|
||||
const [{ data, isLoading: isLoadingData, isError }] = usePayloadAPI(
|
||||
(isEditing ? `${serverURL}${api}/${slug}/${id}` : null),
|
||||
{ initialParams: { 'fallback-locale': 'null', depth: 0, draft: 'true' } },
|
||||
);
|
||||
|
||||
const onSave = useCallback(async (json: any) => {
|
||||
getVersions();
|
||||
getDocPermissions();
|
||||
@@ -55,26 +60,21 @@ const EditView: React.FC<IndexProps> = (props) => {
|
||||
setRedirect(`${admin}/collections/${collection.slug}/${json?.doc?.id}`);
|
||||
} else {
|
||||
const state = await buildStateFromSchema({ fieldSchema: collection.fields, data: json.doc, user, id, operation: 'update', locale, t });
|
||||
setInitialState(state);
|
||||
setInternalState(state);
|
||||
}
|
||||
}, [admin, collection, isEditing, getVersions, user, id, t, locale, getDocPermissions]);
|
||||
|
||||
const [{ data, isLoading: isLoadingData, isError }] = usePayloadAPI(
|
||||
(isEditing ? `${serverURL}${api}/${slug}/${id}` : null),
|
||||
{ initialParams: { 'fallback-locale': 'null', depth: 0, draft: 'true' } },
|
||||
);
|
||||
|
||||
const dataToRender = (locationState as Record<string, unknown>)?.data || data;
|
||||
|
||||
useEffect(() => {
|
||||
const awaitInitialState = async () => {
|
||||
const awaitInternalState = async () => {
|
||||
setUpdatedAt(dataToRender?.updatedAt);
|
||||
const state = await buildStateFromSchema({ fieldSchema: fields, data: dataToRender, user, operation: isEditing ? 'update' : 'create', id, locale, t });
|
||||
await getPreference(preferencesKey);
|
||||
setInitialState(state);
|
||||
setInternalState(state);
|
||||
};
|
||||
|
||||
awaitInitialState();
|
||||
awaitInternalState();
|
||||
}, [dataToRender, fields, isEditing, id, user, locale, isLoadingData, preferencesKey, getPreference, t]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -92,7 +92,7 @@ const EditView: React.FC<IndexProps> = (props) => {
|
||||
const apiURL = `${serverURL}${api}/${slug}/${id}${collection.versions.drafts ? '?draft=true' : ''}`;
|
||||
const action = `${serverURL}${api}/${slug}${isEditing ? `/${id}` : ''}?locale=${locale}&depth=0&fallback-locale=null`;
|
||||
const hasSavePermission = (isEditing && docPermissions?.update?.permission) || (!isEditing && (docPermissions as CollectionPermission)?.create?.permission);
|
||||
const isLoading = !initialState || !docPermissions || isLoadingData;
|
||||
const isLoading = !internalState || !docPermissions || isLoadingData;
|
||||
|
||||
return (
|
||||
<EditDepthContext.Provider value={1}>
|
||||
@@ -107,7 +107,7 @@ const EditView: React.FC<IndexProps> = (props) => {
|
||||
permissions: docPermissions,
|
||||
isEditing,
|
||||
onSave,
|
||||
initialState,
|
||||
internalState,
|
||||
hasSavePermission,
|
||||
apiURL,
|
||||
action,
|
||||
|
||||
@@ -15,7 +15,7 @@ export type Props = IndexProps & {
|
||||
id?: string
|
||||
permissions: CollectionPermission
|
||||
isLoading: boolean
|
||||
initialState?: Fields
|
||||
internalState?: Fields
|
||||
apiURL: string
|
||||
action: string
|
||||
hasSavePermission: boolean
|
||||
|
||||
Reference in New Issue
Block a user