import {Button, Icon, Placeholder} from "./Theme"; import {Checkbox, Confirm, PlaceholderLine, TableCell, TableFooter, TableHeaderCell, TableRow} from "semantic-ui-react"; import {FileIcon, humanFileSize} from "./Common"; import React, {useEffect} from "react"; import {deleteFile, fetchFile} from "../api"; import _ from 'lodash'; import {SortableTable} from "./SortableTable"; import {useBrowseFiles} from "../hooks/customHooks"; import {DirectoryRefreshButton, FileRowTagIcon, FilesRefreshButton} from "./Files"; import {FilePreviewContext} from "./FilePreview"; function depthIndentation(path) { // Repeated spaces for every folder a path is in. const depth = (path.match(/\//g) || []).length; return '\xa0\xa0\xa0\xa0'.repeat(depth); } function Folder({folder, onFolderClick, sortData, selectedPath, onFileClick, onSelect}) { // Creates a single table row for a folder, or a row for itself and indented rows for its children. let {path, children, is_empty} = folder; const pathWithNoTrailingSlash = path.substring(0, path.length - 1); const name = path.substring(pathWithNoTrailingSlash.lastIndexOf('/') + 1); const f = onSelect(folder['path'])}/> onFolderClick(path)} className='file-path' colSpan={2} disabled={is_empty}> {depthIndentation(pathWithNoTrailingSlash)} {is_empty ? : } {name} ; if (children) { // Folder has children, recursively display them. children = sortData(children); let childPaths = []; _.forEach(children, (p, k) => { childPaths = [...childPaths, ]; }); return {f} {childPaths} } else { // Folder has no children. return f; } } function File({file, onFileClick, selectedPath, onSelect}) { const {path, size} = file; const name = path.substring(path.lastIndexOf('/') + 1); return onSelect(path)}/> onFileClick(file)} className='file-path'> {depthIndentation(path)} {/*null size to make icon the correct size*/} {name} {humanFileSize(size)} } function Path({path, onFolderClick, onFileClick, sortData, selectedPath, onSelect}) { if (path['path'].endsWith('/')) { return } else { return } } export function FileBrowser() { const [selectedPath, setSelectedPath] = React.useState(null); const [deleteOpen, setDeleteOpen] = React.useState(false); const [fileSelected, setFileSelected] = React.useState(false); const [folderSelected, setFolderSelected] = React.useState(false); const {browseFiles, openFolders, setOpenFolders, fetchFiles} = useBrowseFiles(); const headers = [ {key: 'select', text: ''}, {key: 'path', text: 'Path', sortBy: i => i['path'].toLowerCase()}, {key: 'size', text: 'Size', sortBy: 'size'}, ]; const {setPreviewFile, setCallbacks} = React.useContext(FilePreviewContext); useEffect(() => { // Fetch files after tagging. setCallbacks([fetchFiles]); return () => setCallbacks(null); }, []); const onSelect = (path) => { if (path.endsWith('/') && selectedPath === path) { // Deselect folder. setFileSelected(false); setFolderSelected(false); setSelectedPath(null); } else if (path.endsWith('/')) { // Select folder. setFileSelected(false); setFolderSelected(true); setSelectedPath(path); } else if (selectedPath === path) { // Deselect file. setFileSelected(false); setFolderSelected(false); setSelectedPath(null); } else { // Select file. setFileSelected(true); setFolderSelected(false); setSelectedPath(path); } } const onDelete = async () => { await deleteFile(selectedPath); setDeleteOpen(false); await fetchFiles(); }; const footer = {folderSelected ? : } setDeleteOpen(false)} /> ; const onFolderClick = async (folder) => { let newFolders; if (openFolders.indexOf(folder) >= 0) { // Remove the folder that was clicked on, as well as its sub-folders. newFolders = openFolders.filter(i => !i.startsWith(folder)); } else { // Add the new folder to the opened folders. newFolders = [...openFolders, folder]; } console.debug(`newFolders=${newFolders}`); setOpenFolders(newFolders); } if (browseFiles === null) { return ; } return <> setPreviewFile(i)} sortData={sortData} selectedPath={selectedPath} onSelect={onSelect} />} footer={footer} /> }