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 { previewServerItem } from "../../util/queries"; import { cairoAuthHeader } from "@mcl/util/auth.js"; import { supportedFileTypes } from "./FilePreview.jsx"; 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: 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; 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("id", serverId); formData.append("path", [...dirStack, file.name].join("/")); await fetch("/api/files/upload", { method: "POST", body: formData, headers: cairoAuthHeader(), }); } 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 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 !== "open_files") return; // console.log(clickEvent); openItem(payload); } return ( ); }