/* Files.js - ESP3D WebUI component file Copyright (c) 2021 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 { Fragment, TargetedMouseEvent } from "preact" import type { FunctionalComponent } from "preact" import { useEffect, useRef, useState } from "preact/hooks" import { T } from "../Translations" import { useFilesManager, fileSizeString, getCurrentPath, setFileRef } from "../../hooks/useFilesManager" import type { FileEntry, PanelMenuItem } from "../../types/files.types" import { FilesTab } from "../../pages/tablet/FilesTab" import { Loading, ButtonImg, FullScreenButton, CloseButton, ContainerHelper } from "../Controls" import { useUiContextFn, useModalsContext } from "../../contexts" import { showConfirmationModal } from "../Modal" import { HardDrive, Upload, RefreshCcw, FolderPlus, CornerRightUp, XCircle } from "preact-feather" import { files } from "../../targets" import { Folder, File, Trash2, Play } from "preact-feather" import { Menu as PanelMenu } from "./" import { eventBus } from "../../hooks/eventBus" const FilesPanel: FunctionalComponent = () => { const id = "filesPanel" const [state, actions] = useFilesManager() const [menu, setMenu] = useState(null) const fileref = useRef(null) const dropRef = useRef(null) const { modals } = useModalsContext() const [isFullScreen, setIsFullScreen] = useState(false) // 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]) useEffect(() => { const listenerId = eventBus.on( "updateState", (data: any) => { if (data.id === "filesPanel") { setIsFullScreen(data.isFullScreen) } }, "filesPanel-fullscreen" ) return () => { eventBus.off("updateState", listenerId) } }, []) useEffect(() => { const newMenu = () => { const isError = state.filesList?.status === T("S22") || state.filesList?.status === T("S110") const rawMenuItems = [ { capability: "CreateDir", label: T("S90"), icon: ( ), onClick: actions.showCreateDirModal, disabled: isError, }, { capability: "Upload", label: T("S89"), displayToggle: () => ( ), onClick: actions.openFileUploadBrowser, disabled: isError, }, { divider: true }, { label: T("S50"), onClick: actions.onRefresh, icon: ( ), }, ] const capabilities = ["CreateDir", "Upload"].filter((cap) => files.capability(state.fileSystem, cap)) return rawMenuItems.filter((item) => { if (item.capability) return capabilities.includes(item.capability) if (item.divider && capabilities.length <= 0) return false return true }) } setMenu(newMenu()) }, [state.fileSystem, state.filesList?.status]) // Render compact panel view const renderCompactView = () => { const currentPath = getCurrentPath() return (
{!isFullScreen && ( actions.filesSelected(e)} />
{state.filePath ? state.filePath : ""}
{ dropRef.current?.classList.add("drop-zone--over") e.preventDefault() }} onDragLeave={(e) => { dropRef.current?.classList.remove("drop-zone--over") e.preventDefault() }} onDragEnd={(e) => { dropRef.current?.classList.remove("drop-zone--over") e.preventDefault() }} onDrop={(e) => { dropRef.current?.classList.remove("drop-zone--over") const dt = e.dataTransfer as DataTransfer if (dt && dt.files.length) { const length = dt.items.length if (!fileref.current || (!fileref.current.multiple && length > 1)) { e.preventDefault() return } } if (fileref.current) (fileref.current as unknown as { files: FileList }).files = dt.files actions.filesSelected(e as unknown as Event) e.preventDefault() }}> {state.isLoading && state.fileSystem != "" && (
} label={T("S28")} btooltip data-tooltip={T("S28")} onClick={actions.onCancel} />
)} {!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 ) }}>
)} {state.filesList.files.map((line: FileEntry) => { return (
) => { useUiContextFn.haptic() actions.ElementClicked(e as unknown as Event, line) }}> {line.size == -1 ? : }
{line.size != -1 && (
{fileSizeString(line.size)}
{files.capability( state.fileSystem, "Process", currentPath[state.fileSystem], line.name ) && ( } onClick={( e: TargetedMouseEvent ) => { e.currentTarget.blur() useUiContextFn.haptic() const cmd = files.command( state.fileSystem, "play", currentPath[state.fileSystem], line.name ) actions.sendSerialCmd(cmd.cmd) }} /> )} {!files.capability( state.fileSystem, "Process", currentPath[state.fileSystem], line.name ) &&
} )} {files.capability( state.fileSystem, line.size == -1 ? "DeleteDir" : "DeleteFile", currentPath[state.fileSystem], line.name ) && ( } onClick={(e: TargetedMouseEvent) => { useUiContextFn.haptic() e.currentTarget.blur() const content = (
{line.size == -1 ? T("S101") : T("S100")}:
  • {line.name}
  • ) showConfirmationModal({ modals, title: T("S26"), content, button1: { cb: () => { actions.deleteCommand(line) }, text: T("S27"), }, button2: { text: T("S28"), }, }) }} /> )}
    ) })} )}
    )} {isFullScreen && }
    ) } return renderCompactView() } const FilesPanelElement = { id: "filesPanel", content: , name: "S65", icon: "HardDrive", show: "showfilespanel", onstart: "openfilesonstart", settingid: "files", } export { FilesPanel, FilesPanelElement }