minecluster/src/components/files/MineclusterFiles.jsx

147 lines
4 KiB
React
Raw Normal View History

import { useState, useEffect, useMemo, useRef } from "react";
import Box from "@mui/material/Box";
import {
FileBrowser,
FileContextMenu,
FileList,
FileNavbar,
FileToolbar,
setChonkyDefaults,
ChonkyActions,
} from "chonky";
import { ChonkyIconFA } from "chonky-icon-fontawesome";
import {
getServerFiles,
createServerFolder,
deleteServerItem,
getServerItem,
} from "@mcl/queries";
import "@mcl/css/header.css";
export default function MineclusterFiles(props) {
// Chonky configuration
setChonkyDefaults({ iconComponent: ChonkyIconFA });
const fileActions = useMemo(
() => [
ChonkyActions.CreateFolder,
ChonkyActions.UploadFiles,
ChonkyActions.DownloadFiles,
ChonkyActions.CopyFiles,
ChonkyActions.DeleteFiles,
],
[],
);
const { server: serverName } = props;
const inputRef = useRef(null);
const [dirStack, setDirStack] = useState(["."]);
const [files, setFiles] = useState([]);
const updateFiles = () =>
getServerFiles(serverName, dirStack.join("/")).then((f) =>
setFiles(f ?? []),
);
useEffect(() => {
updateFiles();
}, [dirStack]);
const getFolderChain = () => {
if (dirStack.length === 1) return [{ id: "home", name: "/", isDir: true }];
return dirStack.map((d, i) => ({ id: `${d}-${i}`, name: d, isDir: true }));
};
const openParentFolder = () => setDirStack(dirStack.slice(0, -1));
function openFolder(payload) {
const { targetFile: file } = payload;
if (!file || !file.isDir) return;
setDirStack([...dirStack, file.name]);
}
function createFolder() {
const name = prompt("What is the name of the new folder?");
const path = [...dirStack, name].join("/");
createServerFolder(serverName, path).then(updateFiles);
}
function deleteItems(files) {
Promise.all(
files.map((f) =>
deleteServerItem(serverName, [...dirStack, f.name].join("/"), f.isDir),
),
)
.catch((e) => console.error("Error deleting some files!", e))
.then(updateFiles);
}
function uploadFileSelection(e) {
if (!e.target.files || e.target.files.length === 0) return;
const { files } = e.target;
Promise.all([...files].map((f) => uploadFile(f)))
.catch((e) => console.log("Error uploading a file", e))
.then(updateFiles);
}
async function uploadFile(file) {
const formData = new FormData();
formData.append("file", file);
formData.append("name", serverName);
formData.append("path", [...dirStack, name].join("/"));
await fetch("/api/files/upload", {
method: "POST",
body: formData,
});
}
async function downloadFiles(files) {
Promise.all(
files.map((f) =>
getServerItem(serverName, f.name, [...dirStack, f.name].join("/")),
),
)
.then(() => console.log("Done"))
.catch((e) => console.error("Error Downloading files!", e));
}
function fileClick(chonkyEvent) {
const { id: clickEvent, payload } = chonkyEvent;
console.log(chonkyEvent);
if (clickEvent === "open_parent_folder") return openParentFolder();
if (clickEvent === "create_folder") return createFolder();
if (clickEvent === "upload_files") return inputRef.current.click();
if (clickEvent === "download_files")
return downloadFiles(chonkyEvent.state.selectedFilesForAction);
if (clickEvent === "delete_files")
return deleteItems(chonkyEvent.state.selectedFilesForAction);
if (clickEvent !== "open_files") return console.log(clickEvent);
openFolder(payload);
}
return (
<Box className="minecluster-files" sx={{ height: "calc(100vh - 6rem)" }}>
<input
type="file"
id="file"
ref={inputRef}
style={{ display: "none" }}
onChange={uploadFileSelection}
multiple
/>
<FileBrowser
files={files}
folderChain={getFolderChain()}
onFileAction={fileClick}
fileActions={fileActions}
darkMode={true}
>
<FileNavbar />
<FileToolbar />
<FileList />
<FileContextMenu />
</FileBrowser>
</Box>
);
}