fix media upload without name, add data result to uploaded payload

This commit is contained in:
dswbx
2025-02-21 07:40:43 +01:00
parent eaa7276173
commit cfa950aa46
6 changed files with 30 additions and 19 deletions

View File

@@ -220,7 +220,7 @@ export class DataController extends Controller {
return c.notFound(); return c.notFound();
} }
const where = c.req.json() as any; const where = (await c.req.json()) as any;
const result = await this.em.repository(entity).count(where); const result = await this.em.repository(entity).count(where);
return c.json({ entity, count: result.count }); return c.json({ entity, count: result.count });
} }

View File

@@ -44,7 +44,8 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
return (await res.blob()) as File; return (await res.blob()) as File;
} }
getFileUploadUrl(file: FileWithPath): string { getFileUploadUrl(file?: FileWithPath): string {
if (!file) return this.getUrl("/upload");
return this.getUrl(`/upload/${file.path}`); return this.getUrl(`/upload/${file.path}`);
} }

View File

@@ -10,4 +10,5 @@ export * from "./api/use-api";
export * from "./api/use-entity"; export * from "./api/use-entity";
export { useAuth } from "./schema/auth/use-auth"; export { useAuth } from "./schema/auth/use-auth";
export { Api, type TApiUser, type AuthState, type ApiOptions } from "../../Api"; export { Api, type TApiUser, type AuthState, type ApiOptions } from "../../Api";
export { FetchPromise } from "modules/ModuleApi";
export type { RepoQueryIn } from "data"; export type { RepoQueryIn } from "data";

View File

@@ -1,3 +1,4 @@
import type { DB } from "core";
import { import {
type ComponentPropsWithRef, type ComponentPropsWithRef,
type ComponentPropsWithoutRef, type ComponentPropsWithoutRef,
@@ -23,6 +24,8 @@ export type FileState = {
progress: number; progress: number;
}; };
export type FileStateWithData = FileState & { data: DB["media"] };
export type DropzoneRenderProps = { export type DropzoneRenderProps = {
wrapperRef: RefObject<HTMLDivElement>; wrapperRef: RefObject<HTMLDivElement>;
inputProps: ComponentPropsWithRef<"input">; inputProps: ComponentPropsWithRef<"input">;
@@ -50,7 +53,7 @@ export type DropzoneProps = {
autoUpload?: boolean; autoUpload?: boolean;
onRejected?: (files: FileWithPath[]) => void; onRejected?: (files: FileWithPath[]) => void;
onDeleted?: (file: FileState) => void; onDeleted?: (file: FileState) => void;
onUploaded?: (files: FileState[]) => void; onUploaded?: (files: FileStateWithData[]) => void;
placeholder?: { placeholder?: {
show?: boolean; show?: boolean;
text?: string; text?: string;
@@ -172,15 +175,16 @@ export function Dropzone({
setUploading(false); setUploading(false);
return; return;
} else { } else {
const uploaded: FileStateWithData[] = [];
for (const file of pendingFiles) { for (const file of pendingFiles) {
try { try {
await uploadFileProgress(file); uploaded.push(await uploadFileProgress(file));
} catch (e) { } catch (e) {
handleUploadError(e); handleUploadError(e);
} }
} }
setUploading(false); setUploading(false);
onUploaded?.(files); onUploaded?.(uploaded);
} }
})(); })();
} }
@@ -220,8 +224,8 @@ export function Dropzone({
setFiles((prev) => prev.filter((f) => f.path !== path)); setFiles((prev) => prev.filter((f) => f.path !== path));
} }
function uploadFileProgress(file: FileState) { function uploadFileProgress(file: FileState): Promise<FileStateWithData> {
return new Promise<void>((resolve, reject) => { return new Promise((resolve, reject) => {
if (!file.body) { if (!file.body) {
console.error("File has no body"); console.error("File has no body");
reject(); reject();
@@ -279,17 +283,19 @@ export function Dropzone({
const response = JSON.parse(xhr.responseText); const response = JSON.parse(xhr.responseText);
console.log("Response:", file, response); console.log("Response:", file, response);
console.log("New state", response.state); const newState = {
replaceFileState(file.path, {
...response.state, ...response.state,
progress: 1, progress: 1,
state: "uploaded" state: "uploaded"
}); };
replaceFileState(file.path, newState);
resolve({ ...response, ...file, ...newState });
} catch (e) { } catch (e) {
setFileState(file.path, "uploaded", 1); setFileState(file.path, "uploaded", 1);
console.error("Error parsing response", e); console.error("Error parsing response", e);
reject(e);
} }
resolve();
} else { } else {
setFileState(file.path, "failed", 1); setFileState(file.path, "failed", 1);
console.error("Upload failed with status: ", xhr.status, xhr.statusText); console.error("Upload failed with status: ", xhr.status, xhr.statusText);
@@ -327,8 +333,8 @@ export function Dropzone({
} }
async function uploadFile(file: FileState) { async function uploadFile(file: FileState) {
await uploadFileProgress(file); const result = await uploadFileProgress(file);
onUploaded?.([file]); onUploaded?.([result]);
} }
const openFileInput = () => inputRef.current?.click(); const openFileInput = () => inputRef.current?.click();
@@ -496,9 +502,9 @@ const Preview: React.FC<PreviewProps> = ({ file, handleUpload, handleDelete }) =
/> />
</div> </div>
<div className="flex flex-col px-1.5 py-1"> <div className="flex flex-col px-1.5 py-1">
<p className="truncate">{file.name}</p> <p className="truncate select-text">{file.name}</p>
<div className="flex flex-row justify-between text-sm font-mono opacity-50 text-nowrap gap-2"> <div className="flex flex-row justify-between text-sm font-mono opacity-50 text-nowrap gap-2">
<span className="truncate">{file.type}</span> <span className="truncate select-text">{file.type}</span>
<span>{(file.size / 1024).toFixed(1)} KB</span> <span>{(file.size / 1024).toFixed(1)} KB</span>
</div> </div>
</div> </div>

View File

@@ -10,7 +10,7 @@ import { mediaItemsToFileStates } from "./helper";
export type DropzoneContainerProps = { export type DropzoneContainerProps = {
children?: ReactNode; children?: ReactNode;
initialItems?: MediaFieldSchema[]; initialItems?: MediaFieldSchema[] | false;
entity?: { entity?: {
name: string; name: string;
id: number; id: number;
@@ -18,6 +18,7 @@ export type DropzoneContainerProps = {
}; };
media?: Pick<TAppMediaConfig, "entity_name" | "storage">; media?: Pick<TAppMediaConfig, "entity_name" | "storage">;
query?: RepoQueryIn; query?: RepoQueryIn;
randomFilename?: boolean;
} & Omit<Partial<DropzoneProps>, "children" | "initialItems">; } & Omit<Partial<DropzoneProps>, "children" | "initialItems">;
const DropzoneContainerContext = createContext<DropzoneRenderProps>(undefined!); const DropzoneContainerContext = createContext<DropzoneRenderProps>(undefined!);
@@ -28,6 +29,7 @@ export function DropzoneContainer({
entity, entity,
query, query,
children, children,
randomFilename,
...props ...props
}: DropzoneContainerProps) { }: DropzoneContainerProps) {
const id = useId(); const id = useId();
@@ -57,12 +59,12 @@ export function DropzoneContainer({
...query ...query
}); });
const $q = useApiQuery(selectApi, { enabled: !initialItems }); const $q = useApiQuery(selectApi, { enabled: initialItems !== false && !initialItems });
const getUploadInfo = useEvent((file) => { const getUploadInfo = useEvent((file) => {
const url = entity const url = entity
? api.media.getEntityUploadUrl(entity.name, entity.id, entity.field) ? api.media.getEntityUploadUrl(entity.name, entity.id, entity.field)
: api.media.getFileUploadUrl(file); : api.media.getFileUploadUrl(randomFilename ? undefined : file);
return { return {
url, url,
@@ -79,7 +81,7 @@ export function DropzoneContainer({
return api.media.deleteFile(file.path); return api.media.deleteFile(file.path);
}); });
const actualItems = initialItems ?? (($q.data || []) as MediaFieldSchema[]); const actualItems = (initialItems || $q.data || []) as MediaFieldSchema[];
const _initialItems = mediaItemsToFileStates(actualItems, { baseUrl }); const _initialItems = mediaItemsToFileStates(actualItems, { baseUrl });
const key = id + JSON.stringify(_initialItems); const key = id + JSON.stringify(_initialItems);

View File

@@ -12,6 +12,7 @@ export { useDropzone as useMediaDropzone };
export type { export type {
PreviewComponentProps, PreviewComponentProps,
FileState, FileState,
FileStateWithData,
DropzoneProps, DropzoneProps,
DropzoneRenderProps DropzoneRenderProps
} from "./Dropzone"; } from "./Dropzone";