163 lines
4.4 KiB
TypeScript
163 lines
4.4 KiB
TypeScript
"use client";
|
|
|
|
import { formatDistanceToNow } from "date-fns";
|
|
import React, { useEffect, useState, useCallback } from "react";
|
|
import DocumentIcon from "@/components/Icons/Document";
|
|
import FolderIcon from "@/components/Icons/Folder";
|
|
import MenuIcon from "@/components/Icons/Menu";
|
|
import ContextMenuContainer from "./ContextMenu/container";
|
|
import RenamePopup from "./ContextMenu/rename";
|
|
import Pterodactyl from "@/components/Pterodactyl";
|
|
|
|
interface FileAttributes {
|
|
name: string;
|
|
modified_at: string;
|
|
size: number;
|
|
is_file: boolean;
|
|
}
|
|
|
|
interface FileProps {
|
|
attributes: FileAttributes;
|
|
}
|
|
|
|
interface ContextMenuState {
|
|
show: boolean;
|
|
x: number;
|
|
y: number;
|
|
file: FileProps | null;
|
|
}
|
|
|
|
interface RenamePopupState {
|
|
show: boolean;
|
|
}
|
|
|
|
const initialContextMenuState: ContextMenuState = {
|
|
show: false,
|
|
x: 0,
|
|
y: 0,
|
|
file: null,
|
|
};
|
|
|
|
const initialRenamePopupState: RenamePopupState = {
|
|
show: false,
|
|
};
|
|
|
|
const Index = () => {
|
|
const [apiKey, setApiKey] = useState<string>("");
|
|
const [fileList, setFileList] = useState<FileProps[]>([]);
|
|
const [contextMenu, setContextMenu] = useState<ContextMenuState>(
|
|
initialContextMenuState
|
|
);
|
|
const [renamePopup, setRenamePopup] = useState<RenamePopupState>(
|
|
initialRenamePopupState
|
|
);
|
|
const [selectedFile, setSelectedFile] = useState<FileProps | null>(null);
|
|
const [ptero, setPtero] = useState<Pterodactyl | null>(null);
|
|
const [serverId, setServerId] = useState<string>("");
|
|
|
|
const setCredentials = useCallback(async () => {
|
|
setApiKey("ptlc_N77A2hEczFmSwGXm4cEXh4Gw3ZP0Ygr5NaBkGlE7pjU");
|
|
setServerId("ec46691a");
|
|
}, []);
|
|
|
|
const showRenamePopup = useCallback(() => {
|
|
setContextMenu(initialContextMenuState);
|
|
setRenamePopup({ show: true });
|
|
}, []);
|
|
|
|
const closeRenamePopup = useCallback(() => {
|
|
setRenamePopup(initialRenamePopupState);
|
|
}, []);
|
|
|
|
const formateDate = (isoDate: string) => {
|
|
const date = new Date(isoDate);
|
|
return formatDistanceToNow(date, { addSuffix: true });
|
|
};
|
|
|
|
const handleRenameFile = useCallback(
|
|
(file: FileProps, newName: string) => {
|
|
if (ptero) {
|
|
ptero.files.rename(file, newName);
|
|
}
|
|
setRenamePopup(initialRenamePopupState);
|
|
},
|
|
[ptero]
|
|
);
|
|
|
|
const handleClickContextMenu = useCallback(
|
|
(e: React.MouseEvent<HTMLDivElement>, file: FileProps) => {
|
|
e.preventDefault();
|
|
setContextMenu({ show: true, x: e.pageX, y: e.pageY, file });
|
|
},
|
|
[]
|
|
);
|
|
|
|
const handleContextMenuClose = useCallback(() => {
|
|
setContextMenu(initialContextMenuState);
|
|
}, []);
|
|
|
|
const formatDate = useCallback((isoDate: string) => {
|
|
return formatDistanceToNow(new Date(isoDate), { addSuffix: true });
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const setupApplication = async () => {
|
|
await setCredentials();
|
|
while (!apiKey) {
|
|
await new Promise((r) => setTimeout(r, 200));
|
|
}
|
|
const pterodactylInstance = new Pterodactyl(serverId, apiKey);
|
|
setPtero(pterodactylInstance);
|
|
const files = await pterodactylInstance.files.fetchFiles();
|
|
setFileList(files);
|
|
};
|
|
|
|
setupApplication();
|
|
}, [apiKey, serverId, setCredentials]);
|
|
|
|
return (
|
|
<>
|
|
{renamePopup.show && (
|
|
<RenamePopup
|
|
file={selectedFile}
|
|
renameCallback={handleRenameFile}
|
|
closeRemapFunction={closeRenamePopup}
|
|
/>
|
|
)}
|
|
{contextMenu.show && (
|
|
<ContextMenuContainer
|
|
x={contextMenu.x}
|
|
y={contextMenu.y}
|
|
closeContextMenu={handleContextMenuClose}
|
|
renameFunction={showRenamePopup}
|
|
setFile={setSelectedFile}
|
|
file={contextMenu.file}
|
|
/>
|
|
)}
|
|
|
|
{fileList.map((file: FileProps) => (
|
|
<div
|
|
className="flex justify-between gap-4 bg-content mb-1 hover:bg-neutral-content pl-4 pr-4 pt-1 pb-1 rounded-md"
|
|
key={file.attributes.name}
|
|
>
|
|
<div className="w-10">
|
|
{file.attributes.is_file ? <DocumentIcon /> : <FolderIcon />}
|
|
</div>
|
|
<div className="w-48 text-left">{file.attributes.name}</div>
|
|
<div className="w-48 text-right">
|
|
{file.attributes.is_file ? file.attributes.size + " bytes" : ""}
|
|
</div>
|
|
<div title={file.attributes.modified_at} className="w-60 text-right">
|
|
{formateDate(file.attributes.modified_at)}
|
|
</div>
|
|
<div onClick={(e) => handleClickContextMenu(e, file)}>
|
|
<MenuIcon />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default Index;
|