minecluster/src/components/files/MineclusterFiles.tsx

198 lines
5.8 KiB
TypeScript
Raw Normal View History

2024-03-29 16:58:17 -06:00
// @ts-nocheck
import { useState, useEffect, useMemo, useRef } from "react";
import Box from "@mui/material/Box";
import Dropzone from "react-dropzone";
import {
FileBrowser,
FileContextMenu,
FileList,
FileNavbar,
FileToolbar,
setChonkyDefaults,
ChonkyActions,
2024-03-29 16:58:17 -06:00
} from "@aperturerobotics/chonky";
import { ChonkyIconFA } from "@aperturerobotics/chonky-icon-fontawesome";
import {
getServerFiles,
createServerFolder,
deleteServerItem,
getServerItem,
moveServerItems,
previewServerItem,
2024-04-06 20:38:10 -06:00
uploadServerItem,
} from "@mcl/api/clients/files";
2024-04-06 20:38:10 -06:00
import { supportedFileTypes } from "./FilePreview.tsx";
export default function MineclusterFiles(props) {
// Chonky configuration
setChonkyDefaults({ iconComponent: ChonkyIconFA });
const fileActions = useMemo(
() => [
ChonkyActions.CreateFolder,
ChonkyActions.UploadFiles,
ChonkyActions.DownloadFiles,
ChonkyActions.CopyFiles,
ChonkyActions.DeleteFiles,
ChonkyActions.MoveFiles,
],
[],
);
const { server: serverId, changePreview } = props;
const inputRef = useRef(null);
const [dirStack, setDirStack] = useState(["."]);
const [files, setFiles] = useState([]);
const updateFiles = () => {
const dir = dirStack.join("/");
getServerFiles(serverId, dir)
.then((f) => {
const files = f.map((fi) => ({ ...fi, id: `${dir}/${fi.name}` }));
setFiles(files ?? []);
})
.catch(() =>
console.error(
"Couldn't update files, server likely hasn't started yet",
),
);
};
useEffect(() => {
updateFiles();
}, [dirStack]);
const getFolderChain = () => {
if (dirStack.length === 1) return [{ id: "./", name: "Home", isDir: true }];
return dirStack.map((d, i) => ({
id: `${dirStack.slice(0, i + 1).join("/")}`,
name: i === 0 ? "Home" : d,
isDir: true,
}));
};
const openParentFolder = () => setDirStack(dirStack.slice(0, -1));
function openItem(payload) {
const { targetFile: file } = payload;
if (file && file.isDir) return setDirStack(file.id.split("/"));
if (!file || file.isDir) return; // Ensure file exists or is dir
if (supportedFileTypes.includes(file.name.split(".").pop()))
return previewFile(file);
return downloadFiles([file]);
}
function createFolder() {
const name = prompt("What is the name of the new folder?");
const path = [...dirStack, name].join("/");
createServerFolder(serverId, path).then(updateFiles);
}
function deleteItems(files) {
Promise.all(
files.map((f) =>
deleteServerItem(serverId, [...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;
uploadMultipleFiles(files);
}
function uploadMultipleFiles(files) {
Promise.all([...files].map((f) => uploadFile(f)))
.catch((e) => console.log("Error uploading a file", e))
.then(updateFiles);
}
async function uploadFile(file) {
const filePath = file.path.startsWith("/") ? file.path : `/${file.path}`;
const formData = new FormData();
formData.append("file", file);
formData.append("id", serverId);
const path = `${[...dirStack].join("/")}${filePath}`;
formData.append("path", path);
2024-04-06 20:38:10 -06:00
await uploadServerItem(formData);
}
async function downloadFiles(files) {
Promise.all(
files.map((f) =>
getServerItem(serverId, f.name, [...dirStack, f.name].join("/")),
),
)
.then(() => console.log("Done downloading files!"))
.catch((e) => console.error("Error Downloading files!", e));
}
function previewFile(file) {
const { name } = file;
previewServerItem(serverId, [...dirStack, name].join("/")).then(
(fileData) =>
changePreview(name, fileData, [...dirStack, name].join("/")),
);
}
function moveFile(movePayload) {
const { files: filePayload, destination: destinationPayload } = movePayload;
if (!destinationPayload.isDir || filePayload.length === 0) return;
const files = filePayload.map((f) => f.name);
const dest = destinationPayload.id;
const origin = dirStack.join("/");
moveServerItems(serverId, files, dest, origin).then(updateFiles);
}
function fileClick(chonkyEvent) {
const { id: clickEvent, payload } = 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 === "move_files") return moveFile(payload);
if (clickEvent !== "open_files") return; // console.log(clickEvent);
openItem(payload);
}
return (
<Dropzone onDrop={uploadMultipleFiles}>
{({ getRootProps }) => (
<Box
className="minecluster-files"
sx={{ height: "calc(100vh - 6rem)" }}
onDrop={getRootProps().onDrop}
>
<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>
)}
</Dropzone>
);
}