Compare commits

..

2 Commits

Author SHA1 Message Date
Juan Di Toro 0d2a73b508 Merge branch 'develop' of git.lumeweb.com:LumeWeb/portal-dashboard into develop 2024-03-28 23:59:19 +01:00
Juan Di Toro 3c767f8e05 fix: pinning progress bar is now fully working 2024-03-28 23:59:11 +01:00
6 changed files with 121 additions and 70 deletions

View File

@ -2,7 +2,7 @@ import { Button } from "~/components/ui/button";
import logoPng from "~/images/lume-logo.png?url"; import logoPng from "~/images/lume-logo.png?url";
import lumeColorLogoPng from "~/images/lume-color-logo.png?url"; import lumeColorLogoPng from "~/images/lume-color-logo.png?url";
import discordLogoPng from "~/images/discord-logo.png?url"; import discordLogoPng from "~/images/discord-logo.png?url";
import { Link, useLocation } from "@remix-run/react"; import { Form, Link, useLocation } from "@remix-run/react";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@ -46,6 +46,10 @@ import {
TooltipProvider, TooltipProvider,
} from "./ui/tooltip"; } from "./ui/tooltip";
import filesize from "./lib/filesize"; import filesize from "./lib/filesize";
import { z } from "zod";
import { getFormProps, useForm, useInputControl } from "@conform-to/react";
import { getZodConstraint, parseWithZod } from "@conform-to/zod";
import { ErrorList } from "./forms";
export const GeneralLayout = ({ children }: React.PropsWithChildren) => { export const GeneralLayout = ({ children }: React.PropsWithChildren) => {
const location = useLocation(); const location = useLocation();
@ -171,96 +175,139 @@ export const GeneralLayout = ({ children }: React.PropsWithChildren) => {
); );
}; };
const UploadFormSchema = z.object({
uppyFiles: z.array(z.any()).min(1, {
message: "At least one file must be uploaded",
}),
});
const UploadFileForm = () => { const UploadFileForm = () => {
const { const {
getRootProps, getRootProps,
getInputProps, getInputProps,
getFiles, getFiles,
upload,
state, state,
removeFile, removeFile,
cancelAll, cancelAll,
failedFiles, failedFiles,
upload,
} = useUppy(); } = useUppy();
const [form, fields] = useForm({
constraint: getZodConstraint(UploadFormSchema),
shouldValidate: "onInput",
onValidate: ({ formData }) => {
const result = parseWithZod(formData, {
schema: UploadFormSchema,
});
return result;
},
onSubmit: (e) => {
e.preventDefault();
return upload();
},
});
const inputProps = getInputProps();
const files = useInputControl({
key: fields.uppyFiles.key,
name: fields.uppyFiles.name,
formId: form.id
});
const isUploading = state === "uploading"; const isUploading = state === "uploading";
const isCompleted = state === "completed"; const isCompleted = state === "completed";
const hasErrored = state === "error"; const hasErrored = state === "error";
const hasStarted = state !== "idle" && state !== "initializing"; const hasStarted = state !== "idle" && state !== "initializing";
const isValid = form.valid || getFiles().length > 0;
const getFailedState = (id: string) => const getFailedState = (id: string) =>
failedFiles.find((file) => file.id === id); failedFiles.find((file) => file.id === id);
console.log({ files: getFiles() });
return ( return (
<> <>
<DialogHeader className="mb-6"> <DialogHeader className="mb-6">
<DialogTitle>Upload Files</DialogTitle> <DialogTitle>Upload Files</DialogTitle>
</DialogHeader> </DialogHeader>
{!hasStarted ? ( <Form {...getFormProps(form)} className="flex flex-col">
<div {!hasStarted ? (
{...getRootProps()} <div
className="border border-border rounded text-primary-2 bg-primary-dark h-48 flex flex-col items-center justify-center"> {...getRootProps()}
<input className="border border-border rounded text-primary-2 bg-primary-dark h-48 flex flex-col items-center justify-center">
hidden <input
aria-hidden hidden
name="uppyFiles[]" aria-hidden
key={new Date().toISOString()} key={new Date().toISOString()}
multiple multiple
{...getInputProps()} name={fields.uppyFiles.name}
/> {...inputProps}
<CloudUploadIcon className="w-24 h-24 stroke stroke-primary-dark" /> value={files.value}
<p>Drag & Drop Files or Browse</p> onChange={() => {
//@ts-expect-error -- conform has shitty typeinference for controls
files.change(getFiles());
}}
/>
<CloudUploadIcon className="w-24 h-24 stroke stroke-primary-dark" />
<p>Drag & Drop Files or Browse</p>
</div>
) : null}
<div className="w-full space-y-3 max-h-48 overflow-y-auto">
{getFiles().map((file) => (
<UploadFileItem
key={file.id}
file={file}
onRemove={(id) => {
removeFile(id);
}}
failedState={getFailedState(file.id)}
/>
))}
</div> </div>
) : null}
<div className="w-full space-y-3 max-h-48 overflow-y-auto"> <ErrorList
{getFiles().map((file) => ( errors={[
<UploadFileItem ...(fields.uppyFiles.errors ?? []),
key={file.id} ...(hasErrored ? ["An error occurred"] : []),
file={file} ]}
onRemove={(id) => { />
removeFile(id);
}}
failedState={getFailedState(file.id)}
/>
))}
</div>
{hasErrored ? ( {hasStarted && !hasErrored ? (
<div className="text-red-500"> <div className="flex flex-col items-center gap-y-2 w-full text-primary-1">
<p>An error occurred</p> <CloudCheckIcon className="w-32 h-32" />
</div> {isCompleted
) : null} ? "Upload completed"
: `${getFiles().length} files being uploaded`}
</div>
) : null}
{hasStarted && !hasErrored ? ( {isUploading ? (
<div className="flex flex-col items-center gap-y-2 w-full text-primary-1"> <DialogClose asChild onClick={cancelAll}>
<CloudCheckIcon className="w-32 h-32" /> <Button type="button" size={"lg"} className="mt-6">
{isCompleted Cancel
? "Upload completed" </Button>
: `${getFiles().length} files being uploaded`} </DialogClose>
</div> ) : null}
) : null}
{isUploading ? ( {isCompleted ? (
<DialogClose asChild onClick={cancelAll}> <DialogClose asChild>
<Button size={"lg"} className="mt-6"> <Button type="button" size={"lg"} className="mt-6">
Cancel Close
</Button>
</DialogClose>
) : null}
{!hasStarted && !isCompleted && !isUploading ? (
<Button
type="submit"
size={"lg"}
className="mt-6"
disabled={!isValid}>
Upload
</Button> </Button>
</DialogClose> ) : null}
) : null} </Form>
{isCompleted ? (
<DialogClose asChild>
<Button size={"lg"} className="mt-6">
Close
</Button>
</DialogClose>
) : null}
{!hasStarted && !isCompleted && !isUploading ? (
<Button size={"lg"} className="mt-6" onClick={upload} disabled={getFiles().length === 0}>
Upload
</Button>
) : null}
</> </>
); );
}; };

View File

@ -60,7 +60,7 @@ export class PinningProcess {
let allStatuses = Array.from(PinningProcess.instances.values()); let allStatuses = Array.from(PinningProcess.instances.values());
let inProgress = allStatuses.some((status) => { let inProgress = allStatuses.some((status) => {
PinningProcess.checkStatus(status.id); PinningProcess.checkStatus(status.id);
return status.status !== "completed"; return status.status !== "processing";
}); });
while (inProgress) { while (inProgress) {

View File

@ -63,6 +63,7 @@ export const usePinning = () => {
return { return {
progressStatus: context.query.status, progressStatus: context.query.status,
progressData: context.query.data, progressData: context.query.data,
fetchProgress: context.query.refetch,
pinStatus, pinStatus,
pinData, pinData,
unpinStatus, unpinStatus,

View File

@ -33,7 +33,7 @@ export const PinningProvider = ({ children }: React.PropsWithChildren) => {
const queryResult = useQuery({ const queryResult = useQuery({
queryKey: ["pin-progress"], queryKey: ["pin-progress"],
refetchInterval: (query) => { refetchInterval: (query) => {
if (!query.state.data || !query.state.data.items.length) { if (!query.state.data?.items || query.state.data.items.length === 0) {
return false; return false;
} }

View File

@ -53,6 +53,10 @@ export const columns: ColumnDef<FileItem>[] = [
// enableSorting: false, // enableSorting: false,
// enableHiding: false, // enableHiding: false,
// }, // },
{
accessorKey: "name",
header: () => null,
},
{ {
accessorKey: "cid", accessorKey: "cid",
header: "CID", header: "CID",

View File

@ -113,7 +113,7 @@ const PinFilesSchema = z.object({
}); });
const PinFilesForm = ({ close }: { close: () => void }) => { const PinFilesForm = ({ close }: { close: () => void }) => {
const { bulkPin, pinStatus, pinData } = usePinning(); const { bulkPin, pinStatus, fetchProgress } = usePinning();
const [form, fields] = useForm({ const [form, fields] = useForm({
id: "pin-files", id: "pin-files",
constraint: getZodConstraint(PinFilesSchema), constraint: getZodConstraint(PinFilesSchema),
@ -131,13 +131,12 @@ const PinFilesForm = ({ close }: { close: () => void }) => {
}, },
}); });
console.log({pinStatus, pinData})
useEffect(() => { useEffect(() => {
if (pinStatus === "success") { if (pinStatus === "success") {
fetchProgress();
close(); close();
} }
}, [pinStatus, close]); }, [pinStatus, fetchProgress, close]);
return ( return (
<> <>
@ -154,8 +153,8 @@ const PinFilesForm = ({ close }: { close: () => void }) => {
errors={fields.cids.errors || pinStatus === "error" ? ["An error occurred, please try again"] : []} errors={fields.cids.errors || pinStatus === "error" ? ["An error occurred, please try again"] : []}
/> />
<Button type="submit" className="w-full"> <Button type="submit" className="w-full" disabled={pinStatus === "pending"}>
Pin Content {pinStatus === "pending" ? "Processing..." : "Pin Content"}
</Button> </Button>
</form> </form>
</> </>