Skip to content

Commit

Permalink
Transfer queue
Browse files Browse the repository at this point in the history
  • Loading branch information
longern committed Jul 19, 2024
1 parent 0025020 commit 1582ffa
Show file tree
Hide file tree
Showing 9 changed files with 288 additions and 93 deletions.
26 changes: 13 additions & 13 deletions functions/webdav/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ export interface RequestHandlerParams {

export const WEBDAV_ENDPOINT = "/webdav/";

export const ROOT_OBJECT = {
key: "",
uploaded: new Date(),
httpMetadata: {
contentType: "application/x-directory",
contentDisposition: undefined,
contentLanguage: undefined,
},
customMetadata: undefined,
size: 0,
etag: undefined,
};

export function notFound() {
return new Response("Not found", { status: 404 });
}
Expand Down Expand Up @@ -42,16 +55,3 @@ export async function* listAll(
if (r2Objects.truncated) cursor = r2Objects.cursor;
} while (r2Objects.truncated);
}

export const ROOT_OBJECT = {
key: "",
uploaded: new Date(),
httpMetadata: {
contentType: "application/x-directory",
contentDisposition: undefined,
contentLanguage: undefined,
},
customMetadata: undefined,
size: 0,
etag: undefined,
};
37 changes: 20 additions & 17 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import React, { useState } from "react";
import Header from "./Header";
import Main from "./Main";
import ProgressDialog from "./ProgressDialog";
import { TransferQueueProvider } from "./app/transferQueue";

const globalStyles = (
<GlobalStyles styles={{ "html, body, #root": { height: "100%" } }} />
Expand All @@ -29,24 +30,26 @@ function App() {
<ThemeProvider theme={theme}>
<CssBaseline />
{globalStyles}
<Stack sx={{ height: "100%" }}>
<Header
search={search}
onSearchChange={(newSearch: string) => setSearch(newSearch)}
setShowProgressDialog={setShowProgressDialog}
<TransferQueueProvider>
<Stack sx={{ height: "100%" }}>
<Header
search={search}
onSearchChange={(newSearch: string) => setSearch(newSearch)}
setShowProgressDialog={setShowProgressDialog}
/>
<Main search={search} onError={setError} />
</Stack>
<Snackbar
autoHideDuration={5000}
open={Boolean(error)}
message={error?.message}
onClose={() => setError(null)}
/>
<Main search={search} onError={setError} />
</Stack>
<Snackbar
autoHideDuration={5000}
open={Boolean(error)}
message={error?.message}
onClose={() => setError(null)}
/>
<ProgressDialog
open={showProgressDialog}
onClose={() => setShowProgressDialog(false)}
/>
<ProgressDialog
open={showProgressDialog}
onClose={() => setShowProgressDialog(false)}
/>
</TransferQueueProvider>
</ThemeProvider>
);
}
Expand Down
11 changes: 1 addition & 10 deletions src/FileGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ListItemText,
} from "@mui/material";
import MimeIcon from "./MimeIcon";
import { humanReadableSize } from "./app/utils";

export interface FileItem {
key: string;
Expand All @@ -16,16 +17,6 @@ export interface FileItem {
customMetadata?: { thumbnail?: string };
}

function humanReadableSize(size: number) {
const units = ["B", "KB", "MB", "GB", "TB"];
let i = 0;
while (size >= 1024) {
size /= 1024;
i++;
}
return `${size.toFixed(1)} ${units[i]}`;
}

function extractFilename(key: string) {
return key.split("/").pop();
}
Expand Down
30 changes: 21 additions & 9 deletions src/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,8 @@ import React, { useCallback, useEffect, useMemo, useState } from "react";
import FileGrid, { encodeKey, FileItem, isDirectory } from "./FileGrid";
import MultiSelectToolbar from "./MultiSelectToolbar";
import UploadDrawer, { UploadFab } from "./UploadDrawer";
import {
copyPaste,
fetchPath,
processUploadQueue,
uploadQueue,
} from "./app/transfer";
import { copyPaste, fetchPath } from "./app/transfer";
import { useTransferQueue, useUploadEnqueue } from "./app/transferQueue";

function Centered({ children }: { children: React.ReactNode }) {
return (
Expand Down Expand Up @@ -105,6 +101,7 @@ function DropZone({
onDrop={(event) => {
event.preventDefault();
onDrop(event.dataTransfer.files);
setDragging(false);
}}
>
{children}
Expand All @@ -124,6 +121,10 @@ function Main({
const [loading, setLoading] = useState(true);
const [multiSelected, setMultiSelected] = useState<string[] | null>(null);
const [showUploadDrawer, setShowUploadDrawer] = useState(false);
const [lastUploadKey, setLastUploadKey] = useState<string | null>(null);

const transferQueue = useTransferQueue();
const uploadEnqueue = useUploadEnqueue();

const fetchFiles = useCallback(() => {
setLoading(true);
Expand All @@ -140,6 +141,19 @@ function Main({
fetchFiles();
}, [fetchFiles]);

useEffect(() => {
if (!transferQueue.length) return;
const lastFile = transferQueue[transferQueue.length - 1];
if (lastFile.loaded < lastFile.total) setLastUploadKey(lastFile.remoteKey);
else if (lastUploadKey) {
fetchPath(cwd).then((files) => {
setFiles(files);
setMultiSelected(null);
});
setLastUploadKey(null);
}
}, [cwd, fetchFiles, lastUploadKey, transferQueue]);

const filteredFiles = useMemo(
() =>
(search
Expand Down Expand Up @@ -173,11 +187,9 @@ function Main({
) : (
<DropZone
onDrop={async (files) => {
uploadQueue.push(
uploadEnqueue(
...Array.from(files).map((file) => ({ file, basedir: cwd }))
);
await processUploadQueue();
fetchFiles();
}}
>
<FileGrid
Expand Down
66 changes: 63 additions & 3 deletions src/ProgressDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
import { Dialog, DialogContent, DialogTitle, Tab, Tabs } from "@mui/material";
import { useState } from "react";
import {
CircularProgress,
Dialog,
DialogContent,
DialogTitle,
List,
ListItem,
ListItemText,
Tab,
Tabs,
Tooltip,
Typography,
} from "@mui/material";
import { useMemo, useState } from "react";
import { TransferTask, useTransferQueue } from "./app/transferQueue";
import { humanReadableSize } from "./app/utils";
import {
CheckCircleOutline as CheckCircleOutlineIcon,
ErrorOutline as ErrorOutlineIcon,
} from "@mui/icons-material";

function ProgressDialog({
open,
Expand All @@ -9,6 +27,14 @@ function ProgressDialog({
onClose: () => void;
}) {
const [tab, setTab] = useState(0);
const transferQueue: TransferTask[] = useTransferQueue();

const tasks = useMemo(() => {
const taskType = tab === 0 ? "download" : "upload";
return Object.values(transferQueue).filter(
(task) => task.type === taskType
);
}, [tab, transferQueue]);

return (
<Dialog open={open} onClose={onClose} fullWidth maxWidth="xs">
Expand All @@ -21,7 +47,41 @@ function ProgressDialog({
<Tab label="Downloads" />
<Tab label="Uploads" />
</Tabs>
<DialogContent>{tab === 0 ? "Downloads" : "Uploads"}</DialogContent>
{tasks.length === 0 ? (
<DialogContent>
<Typography textAlign="center" color="text.secondary">
No tasks
</Typography>
</DialogContent>
) : (
<DialogContent sx={{ padding: 0 }}>
<List>
{tasks.map((task) => (
<ListItem key={task.name}>
<ListItemText
primary={task.name}
secondary={`${humanReadableSize(
task.loaded
)} / ${humanReadableSize(task.total)}`}
/>
{task.error ? (
<Tooltip title={task.error.message}>
<ErrorOutlineIcon color="error" />
</Tooltip>
) : task.loaded === 0 ? null : task.loaded === task.total ? (
<CheckCircleOutlineIcon color="success" />
) : (
<CircularProgress
variant="determinate"
size={24}
value={(task.loaded / task.total) * 100}
/>
)}
</ListItem>
))}
</List>
</DialogContent>
)}
</Dialog>
);
}
Expand Down
10 changes: 6 additions & 4 deletions src/UploadDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
Image as ImageIcon,
Upload as UploadIcon,
} from "@mui/icons-material";
import { createFolder, processUploadQueue, uploadQueue } from "./app/transfer";
import { createFolder } from "./app/transfer";
import { useUploadEnqueue } from "./app/transferQueue";

function IconCaptionButton({
icon,
Expand Down Expand Up @@ -64,6 +65,8 @@ function UploadDrawer({
cwd: string;
onUpload: () => void;
}) {
const uploadEnqueue = useUploadEnqueue();

const handleUpload = useCallback(
(action: string) => () => {
const input = document.createElement("input");
Expand All @@ -84,14 +87,13 @@ function UploadDrawer({
input.onchange = async () => {
if (!input.files) return;
const files = Array.from(input.files);
uploadQueue.push(...files.map((file) => ({ file, basedir: cwd })));
await processUploadQueue();
uploadEnqueue(...files.map((file) => ({ file, basedir: cwd })));
setOpen(false);
onUpload();
};
input.click();
},
[cwd, onUpload, setOpen]
[cwd, onUpload, setOpen, uploadEnqueue]
);

const takePhoto = useMemo(() => handleUpload("photo"), [handleUpload]);
Expand Down
Loading

0 comments on commit 1582ffa

Please sign in to comment.