Skip to content

Commit

Permalink
multi-file urdf uploading (#295)
Browse files Browse the repository at this point in the history
* multi-file urdf uploading

* format

* fix test
  • Loading branch information
codekansas authored Aug 17, 2024
1 parent 4ac8960 commit cd95fbc
Show file tree
Hide file tree
Showing 23 changed files with 691 additions and 659 deletions.
5 changes: 3 additions & 2 deletions frontend/src/components/auth/AuthBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,14 @@ export const AuthBlockInner = () => {

interface AuthBlockProps {
title?: string;
onClosed?: () => void;
}

const AuthBlock: React.FC<AuthBlockProps> = ({ title }) => {
const AuthBlock: React.FC<AuthBlockProps> = ({ title, onClosed }) => {
return (
<Card className="w-[400px] shadow-md bg-white dark:bg-gray-800 text-black dark:text-white rounded-lg">
<CardHeader>
<Header title={title} />
<Header title={title} onClosed={onClosed} />
</CardHeader>
<AuthBlockInner />
</Card>
Expand Down
25 changes: 20 additions & 5 deletions frontend/src/components/auth/RequireAuthentication.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
import React from "react";
import { useNavigate } from "react-router-dom";

import { useAuthentication } from "hooks/useAuth";

import AuthBlock from "./AuthBlock";

interface Props {
children: React.ReactNode;
onClosed?: () => void;
}

const RequireAuthentication = (props: Props) => {
const { children } = props;
const { children, onClosed: onClosedDefault } = props;
const { isAuthenticated } = useAuthentication();

const navigate = useNavigate();

const onClosed =
onClosedDefault ||
(() => {
navigate(-1);
});

return isAuthenticated ? (
<>{children}</>
) : (
<div className="flex justify-center items-center">
<div className="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center">
<AuthBlock />
<>
<div className="justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 z-50 outline-none focus:outline-none">
<div className="relative w-auto my-6 mx-auto max-w-3xl">
<div className="border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-white outline-none focus:outline-none">
<AuthBlock onClosed={onClosed} />
</div>
</div>
</div>
</div>
<div className="opacity-25 fixed inset-0 z-40 bg-black"></div>
</>
);
};

Expand Down
45 changes: 10 additions & 35 deletions frontend/src/components/listing/FileUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ type DirectionOptions = "rtl" | "ltr" | undefined;

type FileUploaderContextType = {
dropzoneState: DropzoneState;
isLOF: boolean;
isFileTooBig: boolean;
removeFileFromSet: (index: number) => void;
activeIndex: number;
Expand Down Expand Up @@ -75,19 +74,16 @@ export const FileUploader = forwardRef<
const { addErrorAlert } = useAlertQueue();

const [isFileTooBig, setIsFileTooBig] = useState(false);
const [isLOF, setIsLOF] = useState(false);
const [activeIndex, setActiveIndex] = useState(-1);
const {
accept = {
"image/*": [".jpg", ".jpeg", ".png", ".gif", ".webp"],
"application/xm": [".urdf"],
"application/xml": [".urdf"],
},
maxFiles = 1,
maxSize = 4 * 1024 * 1024,
multiple = true,
} = dropzoneOptions;

const reSelectAll = maxFiles === 1 ? true : reSelect;
const direction: DirectionOptions = dir === "rtl" ? "rtl" : "ltr";

const removeFileFromSet = useCallback(
Expand Down Expand Up @@ -165,14 +161,12 @@ export const FileUploader = forwardRef<

const newValues: File[] = value ? [...value] : [];

if (reSelectAll) {
if (reSelect) {
newValues.splice(0, newValues.length);
}

files.forEach((file) => {
if (newValues.length < maxFiles) {
newValues.push(file);
}
newValues.push(file);
});

onValueChange(newValues);
Expand All @@ -192,21 +186,16 @@ export const FileUploader = forwardRef<
}
}
},
[reSelectAll, value],
[reSelect, value],
);

useEffect(() => {
if (!value) return;
if (value.length === maxFiles) {
setIsLOF(true);
return;
}
setIsLOF(false);
}, [value, maxFiles]);
}, [value]);

const opts = dropzoneOptions
? dropzoneOptions
: { accept, maxFiles, maxSize, multiple };
: { accept, maxSize, multiple };

const dropzoneState = useDropzone({
...opts,
Expand All @@ -219,7 +208,6 @@ export const FileUploader = forwardRef<
<FileUploaderContext.Provider
value={{
dropzoneState,
isLOF,
isFileTooBig,
removeFileFromSet,
activeIndex,
Expand Down Expand Up @@ -319,16 +307,10 @@ export const FileInput = forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, children, ...props }, ref) => {
const { dropzoneState, isFileTooBig, isLOF } = useFileUpload();
const rootProps = isLOF ? {} : dropzoneState.getRootProps();
const { dropzoneState, isFileTooBig } = useFileUpload();
const rootProps = dropzoneState.getRootProps();
return (
<div
ref={ref}
{...props}
className={`relative w-full ${
isLOF ? "opacity-50 cursor-not-allowed " : "cursor-pointer "
}`}
>
<div ref={ref} {...props} className="relative w-full cursor-pointer">
<div
className={cn(
`w-full rounded-lg duration-300 ease-in-out border-2 border-dashed
Expand All @@ -345,12 +327,7 @@ export const FileInput = forwardRef<
>
{children}
</div>
<Input
ref={dropzoneState.inputRef}
disabled={isLOF}
{...dropzoneState.getInputProps()}
className={`${isLOF ? "cursor-not-allowed" : ""}`}
/>
<Input ref={dropzoneState.inputRef} {...dropzoneState.getInputProps()} />
</div>
);
});
Expand All @@ -361,12 +338,10 @@ export const FileSubmitButton = forwardRef<
HTMLButtonElement,
React.ButtonHTMLAttributes<HTMLButtonElement>
>(({ children, className, ...props }, ref) => {
const { isLOF } = useFileUpload();
return (
<button
ref={ref}
type="button"
disabled={!isLOF}
className={cn(
buttonVariants({ variant: "primary" }),
"w-full",
Expand Down
11 changes: 4 additions & 7 deletions frontend/src/components/listing/ListingArtifacts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ import { components } from "gen/api";
import { useAlertQueue } from "hooks/useAlertQueue";
import { useAuthentication } from "hooks/useAuth";

import ListingImages from "components/listing/ListingImages";
import ListingMeshes from "components/listing/ListingMeshes";
import Spinner from "components/ui/Spinner";

import ListingImages from "./ListingImages";
import ListingSTLs from "./ListingSTLs";
import ListingURDFs from "./ListingURDFs";

interface Props {
listingId: string;
edit: boolean;
Expand Down Expand Up @@ -54,18 +52,17 @@ const ListingArtifacts = (props: Props) => {
<Spinner />
</div>
) : (
<div className="flex flex-col">
<div className="flex flex-col gap-4 my-4">
<ListingImages
listingId={listingId}
edit={edit}
allArtifacts={artifacts}
/>
<ListingURDFs
<ListingMeshes
listingId={listingId}
edit={edit}
allArtifacts={artifacts}
/>
<ListingSTLs listingId={listingId} edit={edit} allArtifacts={artifacts} />
</div>
);
};
Expand Down
42 changes: 19 additions & 23 deletions frontend/src/components/listing/ListingFileUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@ import {
import Spinner from "components/ui/Spinner";

interface Props {
artifactType: string;
fileExtensions: string[];
accept: {
[key: string]: string[];
};
maxSize: number;
listingId: string;
onUpload: (artifact: components["schemas"]["UploadArtifactResponse"]) => void;
}

const ListingFileUpload = (props: Props) => {
const { artifactType, fileExtensions, maxSize, listingId, onUpload } = props;
const { accept, maxSize, listingId, onUpload } = props;

const { addErrorAlert } = useAlertQueue();
const auth = useAuthentication();
Expand All @@ -38,43 +39,38 @@ const ListingFileUpload = (props: Props) => {

setUploading(true);
(async () => {
await Promise.all(
files.map(async (file: File) => {
const { data, error } = await auth.api.upload(file, {
artifact_type: artifactType,
listing_id: listingId,
});
const { data, error } = await auth.api.upload(files, {
listing_id: listingId,
});

if (error) {
addErrorAlert(error);
} else {
setFiles(null);
onUpload(data);
}
}),
);
if (error) {
addErrorAlert(error);
} else {
setFiles(null);
onUpload(data);
}
setUploading(false);
})();
}, [files, auth, listingId, addErrorAlert]);

const fileExtensions = Object.values(accept).flat();

return uploading ? (
<div className="my-4 w-full flex justify-center">
<div className="w-full flex justify-center mt-4">
<Spinner />
</div>
) : (
<FileUploader
value={files}
onValueChange={setFiles}
dropzoneOptions={{
accept: {
"image/*": fileExtensions,
},
accept,
maxSize,
}}
className="relative bg-background rounded-lg pt-4 pb-2 px-2"
className="relative bg-background mt-4 rounded-lg"
>
<FileInput>
<div className="flex justify-center pt-3 pb-4 w-full h-32">
<div className="flex justify-center w-full h-32">
<div className="align-middle h-full justify-center flex flex-col">
<div className="text-center">Drag and drop or click to browse</div>
<div className="text-center">
Expand Down
Loading

0 comments on commit cd95fbc

Please sign in to comment.