diff --git a/.eslintrc.js b/.eslintrc.js index 3f92397..fcaed19 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -16,10 +16,8 @@ module.exports = { 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'warn', // Make TypeScript ESLint less strict. - '@typescript-eslint/member-delimiter-style': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/strict-boolean-expressions': 'off', - '@typescript-eslint/restrict-plus-operands': 'off', '@typescript-eslint/no-floating-promises': 'off', '@typescript-eslint/triple-slash-reference': 'off', '@typescript-eslint/restrict-template-expressions': 'off', diff --git a/imports/dashboard/console/consoleButtons.tsx b/imports/dashboard/console/consoleButtons.tsx index 97c4fe4..4349628 100644 --- a/imports/dashboard/console/consoleButtons.tsx +++ b/imports/dashboard/console/consoleButtons.tsx @@ -5,7 +5,8 @@ import Close from '@mui/icons-material/Close' import PlayArrow from '@mui/icons-material/PlayArrow' const ConsoleButtons = ({ stopStartServer, ws }: { - stopStartServer: (operation: 'START' | 'STOP') => Promise, ws: WebSocket | null + ws: WebSocket | null + stopStartServer: (operation: 'START' | 'STOP') => Promise }) => { const smallScreen = useMediaQuery(useTheme().breakpoints.only('xs')) const [confirmingKill, setConfirmingKill] = useState(false) diff --git a/imports/dashboard/files/editor.tsx b/imports/dashboard/files/editor.tsx index ec5f3bd..b2e2428 100644 --- a/imports/dashboard/files/editor.tsx +++ b/imports/dashboard/files/editor.tsx @@ -4,14 +4,14 @@ import GetApp from '@mui/icons-material/GetApp' // TODO: Refresh button. const Editor = (props: { - name: string, - content: string, - siblingFiles: string[], - handleClose: (setContent: React.Dispatch>) => void, - server: string, - path: string, - ip: string, - setMessage: (message: string) => void, + name: string + content: string + siblingFiles: string[] + handleClose: (setContent: React.Dispatch>) => void + server: string + path: string + ip: string + setMessage: (message: string) => void closeText?: string }) => { const [content, setContent] = useState(props.content) diff --git a/imports/dashboard/files/fileList.tsx b/imports/dashboard/files/fileList.tsx index a556abe..75729ed 100644 --- a/imports/dashboard/files/fileList.tsx +++ b/imports/dashboard/files/fileList.tsx @@ -1,6 +1,6 @@ import React from 'react' import { - List, ListItem, ListItemText, ListItemAvatar, ListItemSecondaryAction, Avatar, IconButton, Checkbox + ListItem, ListItemButton, ListItemText, ListItemAvatar, Avatar, IconButton, Checkbox } from '@mui/material' import { useRouter } from 'next/router' import Folder from '@mui/icons-material/Folder' @@ -10,112 +10,104 @@ import { joinPath } from './fileUtils' const rtd = (num: number) => Math.round(num * 100) / 100 const bytesToGb = (bytes: number) => { - if (bytes < 1024) return bytes + ' bytes' - else if (bytes < (1024 * 1024)) return rtd(bytes / 1024) + ' KB' - else if (bytes < (1024 * 1024 * 1024)) return rtd(bytes / (1024 * 1024)) + ' MB' - else if (bytes < (1024 * 1024 * 1024 * 1024)) return rtd(bytes / (1024 * 1024 * 1024)) + ' GB' + if (bytes < 1024) return `${bytes} bytes` + else if (bytes < (1024 * 1024)) return `${rtd(bytes / 1024)} KB` + else if (bytes < (1024 * 1024 * 1024)) return `${rtd(bytes / (1024 * 1024))} MB` + else if (bytes < (1024 * 1024 * 1024 * 1024)) return `${rtd(bytes / (1024 * 1024 * 1024))} GB` + else return `${rtd(bytes / (1024 * 1024 * 1024 * 1024))} TB` } +const tsts = (ts: number) => new Date(ts * 1000).toISOString().substr(0, 19).replace('T', ' ') export interface File { - folder: boolean, - name: string, - lastModified: number, - size: number, + name: string + size: number + folder: boolean + lastModified: number mimeType: string } -const FileListItem = ({ file, disabled, filesSelected, onItemClick, onCheck, openMenu }: { - file: File, - disabled: boolean, - filesSelected: string[], - onCheck: React.MouseEventHandler, - onItemClick: React.MouseEventHandler, - openMenu: (fileName: string, anchorEl: HTMLButtonElement) => void, +const FileListItem = ({ file, disabled, filesSelected, onItemClick, onCheck, openMenu, url }: { + file: File + url: string + disabled: boolean + filesSelected: string[] + onCheck: React.MouseEventHandler + onItemClick: React.MouseEventHandler + openMenu: (fileName: string, anchorEl: HTMLButtonElement) => void }) => ( - - - - {file.folder ? : } - - - - -
- openMenu(file.name, e.currentTarget)} - size='large' - > - - - e.preventDefault()} style={{ textDecoration: 'none', color: 'inherit' }}> + + openMenu(file.name, e.currentTarget)} size='large' + > + + + +
+ } + > + + + {file.folder ? : } + + - -
-
+ + + ) const FileListItemMemo = React.memo(FileListItem) const FileList = ({ files, path, onClick, openMenu, filesSelected, setFilesSelected, disabled }: { - files: File[], - path: string, - openMenu: (fileName: string, anchorEl: HTMLButtonElement) => void, - onClick: (name: File) => void, - filesSelected: string[], - setFilesSelected: (filesSelected: string[]) => void, + files: File[] + path: string + openMenu: (fileName: string, anchorEl: HTMLButtonElement) => void + onClick: (name: File) => void + filesSelected: string[] + setFilesSelected: (filesSelected: string[]) => void disabled: boolean }) => { const router = useRouter() + // const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0) + // const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0) return ( - +
{files.length ? files.sort((a, b) => { if (a.folder && !b.folder) return -1 else if (!a.folder && b.folder) return 1 else return a.name === b.name ? 0 : (a.name > b.name ? 1 : -1) }).map(file => ( - e.preventDefault()} - style={{ textDecoration: 'none', color: 'inherit' }} - > - e.ctrlKey && !e.shiftKey && !e.metaKey && !e.altKey - ? setFilesSelected(!filesSelected.includes(file.name) - ? [...filesSelected, file.name] - : filesSelected.filter(e => e !== file.name)) - : onClick(file)} - onCheck={() => { - // TODO: Support shift+click. - if (!filesSelected.includes(file.name)) setFilesSelected([...filesSelected, file.name]) - else setFilesSelected(filesSelected.filter(e => e !== file.name)) - }} - /> - + disabled={disabled} + openMenu={openMenu} + filesSelected={filesSelected} + onItemClick={(e) => e.ctrlKey && !e.shiftKey && !e.metaKey && !e.altKey + ? setFilesSelected(!filesSelected.includes(file.name) + ? [...filesSelected, file.name] + : filesSelected.filter(e => e !== file.name)) + : onClick(file)} + onCheck={() => { + // TODO: Support shift+click. + if (!filesSelected.includes(file.name)) setFilesSelected([...filesSelected, file.name]) + else setFilesSelected(filesSelected.filter(e => e !== file.name)) + }} + url={`/dashboard/${router.query.server}/files${file.folder ? joinPath(path, file.name) : path}`} + /> )) : } - +
) } diff --git a/imports/dashboard/files/files.tsx b/imports/dashboard/files/files.tsx index e9b097a..6fe602d 100644 --- a/imports/dashboard/files/files.tsx +++ b/imports/dashboard/files/files.tsx @@ -40,8 +40,8 @@ let euc: (uriComponent: string | number | boolean) => string try { euc = encodeURIComponent } catch (e) { euc = e => e.toString() } const Files = (props: { - path: string, - setServerExists: React.Dispatch>, + path: string + setServerExists: React.Dispatch> setAuthenticated: React.Dispatch> }) => { const router = useRouter() diff --git a/imports/dashboard/files/folderCreationDialog.tsx b/imports/dashboard/files/folderCreationDialog.tsx index 541df62..94b51d8 100644 --- a/imports/dashboard/files/folderCreationDialog.tsx +++ b/imports/dashboard/files/folderCreationDialog.tsx @@ -5,7 +5,7 @@ import { } from '@mui/material' const FolderCreationDialog = ({ handleCreateFolder, handleClose }: { - handleCreateFolder: (name: string) => any, + handleCreateFolder: (name: string) => any handleClose: () => void }) => { const [name, setName] = useState('') diff --git a/imports/dashboard/files/massActionDialog.tsx b/imports/dashboard/files/massActionDialog.tsx index 22bad59..cdd6647 100644 --- a/imports/dashboard/files/massActionDialog.tsx +++ b/imports/dashboard/files/massActionDialog.tsx @@ -7,13 +7,13 @@ import { const MassActionDialog = ({ operation, reload, files, endpoint, handleClose, path, setOverlay, setMessage }: { - reload: () => void, - operation: 'move' | 'copy' | 'compress', - setOverlay: (message: string) => void, - setMessage: (message: string) => void, - handleClose: () => void, - endpoint: string, - files: string[], + reload: () => void + operation: 'move' | 'copy' | 'compress' + setOverlay: (message: string) => void + setMessage: (message: string) => void + handleClose: () => void + endpoint: string + files: string[] path: string }) => { const [newPath, setNewPath] = useState('') diff --git a/imports/dashboard/files/modifyFileDialog.tsx b/imports/dashboard/files/modifyFileDialog.tsx index 05bc71c..f5870f5 100644 --- a/imports/dashboard/files/modifyFileDialog.tsx +++ b/imports/dashboard/files/modifyFileDialog.tsx @@ -5,9 +5,9 @@ import { } from '@mui/material' const ModifyFileDialog = ({ handleEdit, handleClose, operation, filename }: { - handleEdit: (path: string) => any, - handleClose: () => void, - operation: 'move'|'copy'|'rename', + handleEdit: (path: string) => any + handleClose: () => void + operation: 'move'|'copy'|'rename' filename: string }) => { const [path, setPath] = useState(operation === 'rename' ? filename : '') diff --git a/imports/helpers/anchorLink.tsx b/imports/helpers/anchorLink.tsx index 343b20e..453f61e 100644 --- a/imports/helpers/anchorLink.tsx +++ b/imports/helpers/anchorLink.tsx @@ -2,8 +2,8 @@ import React from 'react' import Link from 'next/link' const AnchorLink = (props: React.PropsWithChildren<{ - href: string, - as?: string, + href: string + as?: string prefetch?: boolean }>) => ( diff --git a/imports/helpers/title.tsx b/imports/helpers/title.tsx index 309485c..e068f80 100644 --- a/imports/helpers/title.tsx +++ b/imports/helpers/title.tsx @@ -2,7 +2,10 @@ import React from 'react' import Head from 'next/head' const Title = ({ title, description, url, index }: { - title: string, description: string, url: string, index?: boolean + title: string + description: string + url: string + index?: boolean }) => ( {title} diff --git a/imports/layout.tsx b/imports/layout.tsx index 9bfa1be..0451e74 100644 --- a/imports/layout.tsx +++ b/imports/layout.tsx @@ -2,7 +2,7 @@ import React from 'react' import { AppBar, Toolbar } from '@mui/material' const Layout = (props: React.PropsWithChildren<{ - appBar?: React.ReactNode, + appBar?: React.ReactNode removeToolbar?: boolean }>) => (
void, + server: string + handleClose: () => void runCommand: (command: string) => void }) => { const [command, setCommand] = useState('') diff --git a/imports/servers/serverListItem.tsx b/imports/servers/serverListItem.tsx index bd051c3..891e13e 100644 --- a/imports/servers/serverListItem.tsx +++ b/imports/servers/serverListItem.tsx @@ -12,9 +12,9 @@ import PlayArrow from '@mui/icons-material/PlayArrow' import Comment from '@mui/icons-material/Comment' export const ServerListItem = ({ name, status, openDialog, stopStartServer }: { - name: string, - status: number, - openDialog: () => void, + name: string + status: number + openDialog: () => void stopStartServer: (operation: string, server: string) => void }) => { const router = useRouter() diff --git a/pages/dashboard/[server]/console.tsx b/pages/dashboard/[server]/console.tsx index ce95c8d..c12f018 100644 --- a/pages/dashboard/[server]/console.tsx +++ b/pages/dashboard/[server]/console.tsx @@ -34,7 +34,7 @@ const useInterval = (callback: (...args: any[]) => void, delay: number) => { let id = 0 const CommandTextField = ({ ws, buffer }: { - ws: WebSocket | null, + ws: WebSocket | null buffer: React.MutableRefObject> }) => { const [command, setCommand] = useState('') @@ -128,7 +128,7 @@ const Console = ({ setAuthenticated }: { } } catch (e) { setListening(false) - console.error('Looks like an error occurred while connecting to console.\n' + e) + console.error(`Looks like an error occurred while connecting to console.\n${e}`) } })() return () => { diff --git a/pages/dashboard/[server]/index.tsx b/pages/dashboard/[server]/index.tsx index 137efd9..69c4d54 100644 --- a/pages/dashboard/[server]/index.tsx +++ b/pages/dashboard/[server]/index.tsx @@ -21,22 +21,22 @@ const parseDuration = (durationNano: number): string => { units.seconds = Math.floor(leftoverSeconds / 1000) let res = '' - if (units.days === 1) res += units.days + ' day ' - else if (units.days) res += units.days + ' days ' - if (units.hours === 1) res += units.hours + ' hour ' - else if (units.hours) res += units.hours + ' hours ' - if (units.minutes === 1) res += units.minutes + ' minute ' - else if (units.minutes) res += units.minutes + ' minutes ' - if (units.seconds === 1) res += units.seconds + ' second ' - else if (units.seconds) res += units.seconds + ' seconds ' + if (units.days === 1) res += `${units.days} day ` + else if (units.days) res += `${units.days} days ` + if (units.hours === 1) res += `${units.hours} hour ` + else if (units.hours) res += `${units.hours} hours ` + if (units.minutes === 1) res += `${units.minutes} minute ` + else if (units.minutes) res += `${units.minutes} minutes ` + if (units.seconds === 1) res += `${units.seconds} second ` + else if (units.seconds) res += `${units.seconds} seconds ` return res.trimRight() } interface ServerStatus { - status: 0 | 1 | 2, - uptime: number, - cpuUsage: number, - memoryUsage: number, + status: 0 | 1 | 2 + uptime: number + cpuUsage: number + memoryUsage: number totalMemory: number }