2023-12-20 03:20:04 +00:00
|
|
|
import { useState, useEffect, useMemo, useRef } from "react";
|
|
|
|
import Box from "@mui/material/Box";
|
2024-02-11 03:57:01 +00:00
|
|
|
import Dropzone from "react-dropzone";
|
|
|
|
|
2023-12-20 03:20:04 +00:00
|
|
|
import {
|
|
|
|
FileBrowser,
|
|
|
|
FileContextMenu,
|
|
|
|
FileList,
|
|
|
|
FileNavbar,
|
|
|
|
FileToolbar,
|
|
|
|
setChonkyDefaults,
|
|
|
|
ChonkyActions,
|
|
|
|
} from "chonky";
|
|
|
|
import { ChonkyIconFA } from "chonky-icon-fontawesome";
|
|
|
|
|
|
|
|
import {
|
|
|
|
getServerFiles,
|
|
|
|
createServerFolder,
|
|
|
|
deleteServerItem,
|
|
|
|
getServerItem,
|
2024-02-11 03:57:01 +00:00
|
|
|
moveServerItems,
|
|
|
|
previewServerItem,
|
2023-12-20 03:20:04 +00:00
|
|
|
} from "@mcl/queries";
|
2024-02-05 02:13:32 +00:00
|
|
|
import { cairoAuthHeader } from "@mcl/util/auth.js";
|
2023-12-20 03:20:04 +00:00
|
|
|
|
2024-01-15 20:30:31 +00:00
|
|
|
import { supportedFileTypes } from "./FilePreview.jsx";
|
2023-12-20 03:20:04 +00:00
|
|
|
|
|
|
|
export default function MineclusterFiles(props) {
|
|
|
|
// Chonky configuration
|
|
|
|
setChonkyDefaults({ iconComponent: ChonkyIconFA });
|
|
|
|
const fileActions = useMemo(
|
|
|
|
() => [
|
|
|
|
ChonkyActions.CreateFolder,
|
|
|
|
ChonkyActions.UploadFiles,
|
|
|
|
ChonkyActions.DownloadFiles,
|
|
|
|
ChonkyActions.CopyFiles,
|
|
|
|
ChonkyActions.DeleteFiles,
|
2024-02-11 03:57:01 +00:00
|
|
|
ChonkyActions.MoveFiles,
|
2023-12-20 03:20:04 +00:00
|
|
|
],
|
|
|
|
[],
|
|
|
|
);
|
2024-01-15 20:30:31 +00:00
|
|
|
const { server: serverId, changePreview } = props;
|
2023-12-20 03:20:04 +00:00
|
|
|
const inputRef = useRef(null);
|
|
|
|
const [dirStack, setDirStack] = useState(["."]);
|
|
|
|
const [files, setFiles] = useState([]);
|
|
|
|
|
2023-12-22 18:30:48 +00:00
|
|
|
const updateFiles = () => {
|
|
|
|
const dir = dirStack.join("/");
|
2024-01-15 20:30:31 +00:00
|
|
|
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",
|
|
|
|
),
|
|
|
|
);
|
2023-12-22 18:30:48 +00:00
|
|
|
};
|
2023-12-20 03:20:04 +00:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
updateFiles();
|
|
|
|
}, [dirStack]);
|
|
|
|
|
|
|
|
const getFolderChain = () => {
|
2023-12-22 18:30:48 +00:00
|
|
|
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,
|
|
|
|
}));
|
2023-12-20 03:20:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const openParentFolder = () => setDirStack(dirStack.slice(0, -1));
|
|
|
|
|
2024-01-15 20:30:31 +00:00
|
|
|
function openItem(payload) {
|
2023-12-20 03:20:04 +00:00
|
|
|
const { targetFile: file } = payload;
|
2023-12-22 18:30:48 +00:00
|
|
|
if (file && file.isDir) return setDirStack(file.id.split("/"));
|
2024-01-15 20:30:31 +00:00
|
|
|
if (!file || file.isDir) return; // Ensure file exists or is dir
|
|
|
|
if (supportedFileTypes.includes(file.name.split(".").pop()))
|
|
|
|
return previewFile(file);
|
|
|
|
return downloadFiles([file]);
|
2023-12-20 03:20:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function createFolder() {
|
|
|
|
const name = prompt("What is the name of the new folder?");
|
|
|
|
const path = [...dirStack, name].join("/");
|
2024-01-15 20:30:31 +00:00
|
|
|
createServerFolder(serverId, path).then(updateFiles);
|
2023-12-20 03:20:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function deleteItems(files) {
|
|
|
|
Promise.all(
|
|
|
|
files.map((f) =>
|
2024-01-15 20:30:31 +00:00
|
|
|
deleteServerItem(serverId, [...dirStack, f.name].join("/"), f.isDir),
|
2023-12-20 03:20:04 +00:00
|
|
|
),
|
|
|
|
)
|
|
|
|
.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;
|
2024-02-11 03:57:01 +00:00
|
|
|
uploadMultipleFiles(files);
|
|
|
|
}
|
|
|
|
|
|
|
|
function uploadMultipleFiles(files) {
|
2023-12-20 03:20:04 +00:00
|
|
|
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);
|
2024-01-15 20:30:31 +00:00
|
|
|
formData.append("id", serverId);
|
2024-01-22 01:01:12 +00:00
|
|
|
formData.append("path", [...dirStack, file.name].join("/"));
|
2023-12-20 03:20:04 +00:00
|
|
|
await fetch("/api/files/upload", {
|
|
|
|
method: "POST",
|
|
|
|
body: formData,
|
2024-02-05 02:13:32 +00:00
|
|
|
headers: cairoAuthHeader(),
|
2023-12-20 03:20:04 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async function downloadFiles(files) {
|
|
|
|
Promise.all(
|
|
|
|
files.map((f) =>
|
2024-01-15 20:30:31 +00:00
|
|
|
getServerItem(serverId, f.name, [...dirStack, f.name].join("/")),
|
2023-12-20 03:20:04 +00:00
|
|
|
),
|
|
|
|
)
|
2023-12-22 18:30:48 +00:00
|
|
|
.then(() => console.log("Done downloading files!"))
|
2023-12-20 03:20:04 +00:00
|
|
|
.catch((e) => console.error("Error Downloading files!", e));
|
|
|
|
}
|
|
|
|
|
2024-01-15 20:30:31 +00:00
|
|
|
function previewFile(file) {
|
|
|
|
const { name } = file;
|
|
|
|
previewServerItem(serverId, [...dirStack, name].join("/")).then(
|
2024-01-22 01:01:12 +00:00
|
|
|
(fileData) =>
|
|
|
|
changePreview(name, fileData, [...dirStack, name].join("/")),
|
2024-01-15 20:30:31 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-02-11 03:57:01 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-12-20 03:20:04 +00:00
|
|
|
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);
|
2024-02-11 03:57:01 +00:00
|
|
|
if (clickEvent === "move_files") return moveFile(payload);
|
2023-12-22 18:30:48 +00:00
|
|
|
if (clickEvent !== "open_files") return; // console.log(clickEvent);
|
2024-01-15 20:30:31 +00:00
|
|
|
openItem(payload);
|
2023-12-20 03:20:04 +00:00
|
|
|
}
|
2024-02-11 03:57:01 +00:00
|
|
|
|
2023-12-20 03:20:04 +00:00
|
|
|
return (
|
2024-02-11 03:57:01 +00:00
|
|
|
<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>
|
2023-12-20 03:20:04 +00:00
|
|
|
);
|
|
|
|
}
|