/* FilesTab.tsx - ESP3D WebUI Tablet Files Tab Copyright (c) 2020 Luc Lebosse. All rights reserved. This code is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with This code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ import { FunctionalComponent, Fragment } from "preact" import { useState, useEffect, useRef } from "preact/hooks" import { T } from "../../components/Translations" import { useFilesManager, fileSizeString, getCurrentPath, setFileRef } from "../../hooks/useFilesManager" import type { FileEntry, FilesList, SortField, SortOrder } from "../../types/files.types" import { Loading } from "../../components/Controls" import { useUiContextFn, useModalsContext } from "../../contexts" import { showConfirmationModal } from "../../components/Modal" import { Upload, RefreshCcw, FolderPlus, CornerRightUp, XCircle, ArrowUp, ArrowDown, Minimize, Folder, File, Trash2, Play, } from "preact-feather" import { files } from "../../targets" const FilesTab: FunctionalComponent = () => { const [state, actions] = useFilesManager() const [sortBy, setSortBy] = useState("name") const [sortOrder, setSortOrder] = useState("asc") const [isFullScreen, setIsFullScreen] = useState(false) const fileref = useRef(null) const { modals } = useModalsContext() // Register the file input ref with the hook and update multiple attribute when filesystem changes useEffect(() => { setFileRef(fileref.current) if (fileref.current && state.fileSystem) { fileref.current.multiple = files.capability(state.fileSystem, "UploadMultiple") } }, [state.fileSystem]) // Listen for fullscreen changes useEffect(() => { const handleFullscreenChange = () => { setIsFullScreen(!!document.fullscreenElement) } document.addEventListener("fullscreenchange", handleFullscreenChange) return () => { document.removeEventListener("fullscreenchange", handleFullscreenChange) } }, []) // Sort files const sortFiles = (filesList: FilesList) => { if (!filesList || !filesList.files) return filesList const sorted = { ...filesList } sorted.files = [...filesList.files].sort((a, b) => { // Always keep directories first if (a.size === -1 && b.size !== -1) return -1 if (a.size !== -1 && b.size === -1) return 1 let comparison = 0 switch (sortBy) { case "name": comparison = a.name.localeCompare(b.name) break case "size": // For directories (size -1), compare by name if (a.size === -1 && b.size === -1) { comparison = a.name.localeCompare(b.name) } else { comparison = (a.size || 0) - (b.size || 0) } break case "date": // Compare by date if available, otherwise by name if (a.datetime && b.datetime) { comparison = a.datetime.localeCompare(b.datetime) } else { comparison = a.name.localeCompare(b.name) } break } return sortOrder === "asc" ? comparison : -comparison }) return sorted } const toggleSort = (field: SortField) => { useUiContextFn.haptic() if (sortBy === field) { setSortOrder(sortOrder === "asc" ? "desc" : "asc") } else { setSortBy(field) setSortOrder("asc") } } const currentPath = getCurrentPath() const isError = state.filesList?.status === T("S22") || state.filesList?.status === T("S110") return (
actions.filesSelected(e)} /> {/* Toolbar */}
{/* File System Selection */}
{files.supported.map((fs: any) => { if (fs.depend && !fs.depend()) return null return ( ) })}
{/* Sort Buttons */} {state.fileSystem != "" && !state.isLoading && (
Sort:
)} {/* Action Buttons */} {state.fileSystem != "" && !state.isLoading && (
{files.capability(state.fileSystem, "Upload") && ( )} {files.capability(state.fileSystem, "CreateDir") && ( )}
)} {/* Minimize/Exit Button - only show when in fullscreen context */} {isFullScreen && ( )}
{/* Current Path */}
{state.filePath ? state.filePath : "/"}
{/* Files List */}
{ e.preventDefault() }} onDragLeave={(e) => { e.preventDefault() }} onDragEnd={(e) => { e.preventDefault() }} onDrop={(e) => { e.preventDefault() }}> {state.isLoading && state.fileSystem != "" && (
)} {!state.isLoading && state.fileSystem != "" && state.filesList && ( {currentPath[state.fileSystem] != "/" && (
{ useUiContextFn.haptic() const newpath = currentPath[state.fileSystem].substring( 0, currentPath[state.fileSystem].lastIndexOf("/") ) currentPath[state.fileSystem] = newpath.length == 0 ? "/" : newpath actions.onRefresh(e, false) }}>
...
)} {sortFiles(state.filesList).files.map((line: FileEntry) => { return (
{ useUiContextFn.haptic() actions.ElementClicked(e as unknown as Event, line) }}> {line.size == -1 ? : } {line.name}
{line.datetime && {line.datetime}} {line.size != -1 && ( {fileSizeString(line.size)} {files.capability( state.fileSystem, "Process", currentPath[state.fileSystem], line.name ) && ( )} )} {files.capability( state.fileSystem, line.size == -1 ? "DeleteDir" : "DeleteFile", currentPath[state.fileSystem], line.name ) && ( )}
) })}
)}
{/* Footer */} {!state.isLoading && state.filesList && (state.filesList.occupation || state.filesList.status) && ( )}
) } export { FilesTab }