Merge pull request #1978 from payloadcms/fix/1897

fix: removes old media when replaced and re-renders on save
This commit is contained in:
James Mikrut
2023-02-07 11:53:05 -05:00
committed by GitHub
10 changed files with 111 additions and 48 deletions

View File

@@ -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}>

View File

@@ -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,
}

View File

@@ -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

View File

@@ -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`}>

View File

@@ -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[];

View File

@@ -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,

View File

@@ -15,7 +15,7 @@ export type Props = IndexProps & {
id?: string
permissions: CollectionPermission
isLoading: boolean
initialState?: Fields
internalState?: Fields
apiURL: string
action: string
hasSavePermission: boolean