[FEATURE] Initial File Preview
This commit is contained in:
parent
cb118e07c0
commit
e96c326c1d
5 changed files with 117 additions and 7 deletions
74
src/components/files/FilePreview.jsx
Normal file
74
src/components/files/FilePreview.jsx
Normal file
|
@ -0,0 +1,74 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import Button from "@mui/material/Button";
|
||||
import DialogTitle from "@mui/material/DialogTitle";
|
||||
import DialogContent from "@mui/material/DialogContent";
|
||||
import DialogActions from "@mui/material/DialogActions";
|
||||
import Dialog from "@mui/material/Dialog";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
|
||||
const textFileTypes = ["properties", "txt", "yaml", "yml", "json", "env"];
|
||||
const imageFileTypes = ["png", "jpeg", "jpg"];
|
||||
|
||||
export const supportedFileTypes = [...textFileTypes, ...imageFileTypes];
|
||||
|
||||
export function useFilePreview(isOpen = false) {
|
||||
const [open, setOpen] = useState(isOpen);
|
||||
const dialogToggle = () => setOpen(!open);
|
||||
return [open, dialogToggle];
|
||||
}
|
||||
|
||||
function TextPreview(props) {
|
||||
const { fileText } = props;
|
||||
return <div style={{ whiteSpace: "break-spaces" }}>{fileText}</div>;
|
||||
}
|
||||
|
||||
export default function FilePreview(props) {
|
||||
const [fileText, setFileText] = useState();
|
||||
const theme = useTheme();
|
||||
const fullScreen = useMediaQuery(theme.breakpoints.down("md"));
|
||||
|
||||
const { previewData, open, dialogToggle } = props;
|
||||
const { fileData, name } = previewData ?? {};
|
||||
const ext = name ? name.split(".").pop() : null;
|
||||
const isTextFile = textFileTypes.includes(ext);
|
||||
|
||||
async function onPreviewChange() {
|
||||
if (isTextFile) setFileText(await fileData.text());
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
onPreviewChange();
|
||||
}, [fileData]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
sx={
|
||||
fullScreen
|
||||
? {}
|
||||
: {
|
||||
"& .mcl-MuiDialog-paper": {
|
||||
width: "100%",
|
||||
maxHeight: 525,
|
||||
maxWidth: "80%",
|
||||
},
|
||||
}
|
||||
}
|
||||
maxWidth="xs"
|
||||
open={open}
|
||||
fullScreen={fullScreen}
|
||||
>
|
||||
<Toolbar sx={{ display: { sm: "none" } }} />
|
||||
<DialogTitle>{name}</DialogTitle>
|
||||
<DialogContent>
|
||||
<TextPreview fileText={fileText} />
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button autoFocus onClick={dialogToggle}>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
|
@ -17,6 +17,9 @@ import {
|
|||
deleteServerItem,
|
||||
getServerItem,
|
||||
} from "@mcl/queries";
|
||||
import { previewServerItem } from "../../util/queries";
|
||||
|
||||
import { supportedFileTypes } from "./FilePreview.jsx";
|
||||
|
||||
export default function MineclusterFiles(props) {
|
||||
// Chonky configuration
|
||||
|
@ -31,7 +34,7 @@ export default function MineclusterFiles(props) {
|
|||
],
|
||||
[],
|
||||
);
|
||||
const { server: serverId } = props;
|
||||
const { server: serverId, changePreview } = props;
|
||||
const inputRef = useRef(null);
|
||||
const [dirStack, setDirStack] = useState(["."]);
|
||||
const [files, setFiles] = useState([]);
|
||||
|
@ -65,10 +68,13 @@ export default function MineclusterFiles(props) {
|
|||
|
||||
const openParentFolder = () => setDirStack(dirStack.slice(0, -1));
|
||||
|
||||
function openFolder(payload) {
|
||||
function openItem(payload) {
|
||||
const { targetFile: file } = payload;
|
||||
if (file && file.isDir) return setDirStack(file.id.split("/"));
|
||||
if (file && !file.isDir) return downloadFiles([file]);
|
||||
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() {
|
||||
|
@ -116,6 +122,13 @@ export default function MineclusterFiles(props) {
|
|||
.catch((e) => console.error("Error Downloading files!", e));
|
||||
}
|
||||
|
||||
function previewFile(file) {
|
||||
const { name } = file;
|
||||
previewServerItem(serverId, [...dirStack, name].join("/")).then(
|
||||
(fileData) => changePreview(name, fileData),
|
||||
);
|
||||
}
|
||||
|
||||
function fileClick(chonkyEvent) {
|
||||
const { id: clickEvent, payload } = chonkyEvent;
|
||||
if (clickEvent === "open_parent_folder") return openParentFolder();
|
||||
|
@ -126,7 +139,7 @@ export default function MineclusterFiles(props) {
|
|||
if (clickEvent === "delete_files")
|
||||
return deleteItems(chonkyEvent.state.selectedFilesForAction);
|
||||
if (clickEvent !== "open_files") return; // console.log(clickEvent);
|
||||
openFolder(payload);
|
||||
openItem(payload);
|
||||
}
|
||||
return (
|
||||
<Box className="minecluster-files" sx={{ height: "calc(100vh - 6rem)" }}>
|
||||
|
|
|
@ -26,7 +26,7 @@ export default function RconDialog(props) {
|
|||
sx={
|
||||
fullScreen
|
||||
? {}
|
||||
: { "& .MuiDialog-paper": { width: "80%", maxHeight: 525 } }
|
||||
: { "& .mcl-MuiDialog-paper": { width: "80%", maxHeight: 525 } }
|
||||
}
|
||||
maxWidth="xs"
|
||||
open={open}
|
||||
|
|
|
@ -1,20 +1,36 @@
|
|||
import { useEffect } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSearchParams, useNavigate } from "react-router-dom";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import FilePreview, {
|
||||
useFilePreview,
|
||||
} from "@mcl/components/files/FilePreview.jsx";
|
||||
import MineclusterFiles from "@mcl/components/files/MineclusterFiles.jsx";
|
||||
|
||||
export default function Files() {
|
||||
const [open, dialogToggle] = useFilePreview();
|
||||
const [previewData, setPreviewData] = useState();
|
||||
const [searchParams] = useSearchParams();
|
||||
const currentServer = searchParams.get("server");
|
||||
const nav = useNavigate();
|
||||
useEffect(() => {
|
||||
if (!currentServer) nav("/");
|
||||
}, [currentServer]);
|
||||
|
||||
function changePreview(name, fileData) {
|
||||
setPreviewData({ name, fileData });
|
||||
dialogToggle();
|
||||
}
|
||||
|
||||
return (
|
||||
<Box className="edit" sx={{ height: "100%" }}>
|
||||
<MineclusterFiles server={currentServer} />
|
||||
<FilePreview
|
||||
open={open}
|
||||
dialogToggle={dialogToggle}
|
||||
previewData={previewData}
|
||||
/>
|
||||
<MineclusterFiles server={currentServer} changePreview={changePreview} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -50,6 +50,13 @@ export const createServerFolder = async (serverId, path) =>
|
|||
export const deleteServerItem = async (serverId, path, isDir) =>
|
||||
fetchApiCore("/files/item", { id: serverId, path, isDir }, "DELETE");
|
||||
|
||||
export async function previewServerItem(serverId, path) {
|
||||
const resp = await fetchApiCore("/files/item", { id: serverId, path });
|
||||
if (!resp.status === 200) return console.log("AHHHH");
|
||||
const blob = await resp.blob();
|
||||
return blob;
|
||||
}
|
||||
|
||||
export const getServerItem = async (serverId, name, path) =>
|
||||
fetchApiCore("/files/item", { id: serverId, path })
|
||||
.then((resp) =>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue