import {Button, Header, Icon, Modal, ModalActions, ModalContent, ModalHeader, Placeholder, Table} from "./Theme"; import { Checkbox, Form, IconGroup, Input, PlaceholderLine, TableBody, TableCell, TableFooter, TableHeader, TableHeaderCell, TableRow } from "semantic-ui-react"; import {APIButton, DirectorySearch, ErrorMessage, FileIcon, humanFileSize, useIsIgnoredDirectory} from "./Common"; import React, {useEffect, useState} from "react"; import {deleteFile, ignoreDirectory, makeDirectory, movePaths, renamePath, unignoreDirectory} from "../api"; import _ from 'lodash'; import {SortableTable} from "./SortableTable"; import {useBrowseFiles, useMediaDirectory, useWROLMode} from "../hooks/customHooks"; import {FileRowTagIcon, FilesRefreshButton} from "./Files"; import {FilePreviewContext} from "./FilePreview"; import {SettingsContext} from "../contexts/contexts"; import {BulkTagModal} from "./BulkTagModal"; 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); } export function pathDirectory(path) { // Return the full path of the path's directory. // pathDirectory('/some/directory/file.txt') -> '/some/directory' return path.substring(0, path.lastIndexOf('/')); } export function pathName(path) { // Return the file's name. // pathName('/some/directory/file.txt') -> 'file.txt' // pathName('/some/directory') -> 'directory' if (path.endsWith('/')) { // Path is a directory, return its name. path = path.substring(0, path.length - 1); } return path.substring(path.lastIndexOf('/') + 1); } export function splitPathParentAndName(path) { if (path.endsWith('/')) { // Path is a directory, return its name. path = path.substring(0, path.length - 1); } const slashIndex = path.lastIndexOf('/'); return [path.substring(0, slashIndex), path.substring(slashIndex + 1, path.length)]; } function Folder({folder, onFolderClick, sortData, selectedPaths, onFileClick, onSelect, disabled}) { // 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 ignored = useIsIgnoredDirectory(path); const pathWithNoTrailingSlash = path.substring(0, path.length - 1); const name = path.substring(pathWithNoTrailingSlash.lastIndexOf('/') + 1); const f = = 0} onChange={() => onSelect(folder['path'])}/> onFolderClick(path)} className='file-path' colSpan={2} disabled={is_empty}> {depthIndentation(pathWithNoTrailingSlash)} {is_empty ? : } {ignored && } {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, selectedPaths, onSelect, disabled}) { const {path, size} = file; return = 0} onChange={() => onSelect(path)}/> onFileClick(file)} className='file-path'> {depthIndentation(path)} {/*null size to make icon the correct size*/} {pathName(path)} {humanFileSize(size)} } function Path({path, onFolderClick, onFileClick, sortData, selectedPaths, onSelect, disabled}) { if (path['path'].endsWith('/')) { return } else { return } } export function FileBrowser() { const [selectedPaths, setSelectedPaths] = React.useState([]); const [renameOpen, setRenameOpen] = React.useState(false); const [moveOpen, setMoveOpen] = React.useState(false); const [makeDirectoryOpen, setMakeDirectoryOpen] = React.useState(false); const [ignoreDirectoryOpen, setIgnoreDirectoryOpen] = React.useState(false); const [bulkTagOpen, setBulkTagOpen] = React.useState(false); const [pending, setPending] = React.useState(false); // Only true if only one path is selected, and it is a directory. const [singleDirectorySelected, setSingleDirectorySelected] = React.useState(false); // Only true if single directory is selected, and it is in settings['ignored_directories'] const [isDirectoryIgnored, setIsDirectoryIgnored] = React.useState(false); const {settings, fetchSettings} = React.useContext(SettingsContext); const selectedPathsCount = selectedPaths ? selectedPaths.length : 0; const {browseFiles, openFolders, setOpenFolders, fetchFiles} = useBrowseFiles(); const wrolModeEnabled = useWROLMode(); 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(openFolders)]); return () => setCallbacks(null); }, []); useEffect(() => { if (selectedPaths.length === 1 && selectedPaths[0].endsWith('/')) { setSingleDirectorySelected(true); if (!_.isEmpty(settings)) { // Used to toggle between ignore/un-ignore (eye/eye lash) icon. const directory = selectedPaths[0].slice(0, -1); setIsDirectoryIgnored(settings['ignored_directories'].indexOf(directory) >= 0); } } else { setSingleDirectorySelected(false); } }, [selectedPaths, settings]); const onIgnore = async () => { setIgnoreDirectoryOpen(false); await fetchSettings(); } const onSelect = (path) => { let newSelectedPaths; // Select or deselect if the paths was previously selected. if (selectedPathsCount && selectedPaths.indexOf(path) >= 0) { newSelectedPaths = selectedPaths.filter(i => i !== path); } else { newSelectedPaths = [...selectedPaths, path]; } setSelectedPaths(newSelectedPaths); console.debug('selectedPaths', newSelectedPaths); } const onDelete = async () => { await deleteFile(selectedPaths); await reset(); }; const handleMakeDirectory = async () => { setMakeDirectoryOpen(false); await reset(); } const reset = async () => { setSelectedPaths([]); setPending(true); try { await fetchFiles(); } finally { setPending(false); } } const footer = setMakeDirectoryOpen(false)} parent={selectedPaths.length ? selectedPaths[0] : null} onSubmit={handleMakeDirectory} /> } export function MoveModal({open, paths, onClose, onSubmit}) { const [destination, setDestination] = React.useState(''); const [pending, setPending] = React.useState(false); const handleDirectory = (value) => { setDestination(value); } const handleMove = async () => { try { setPending(true); await movePaths(destination, paths); onClose(); } catch (e) { console.error(e); setPending(false); } if (onSubmit) { await onSubmit(); } } const fileCell = i =>
{i}
; const destinationRows = paths.map(i => {fileCell(i)} {destination ? fileCell(destination + '/' + pathName(i)) : fileCell(pathName(i))} ); return Move Files/Directories

Search for a directory to move your files into:

Source Destination {destinationRows}
} export function IgnoreDirectoryModal({open, onClose, onSubmit, directory, ignored}) { const mediaDirectory = useMediaDirectory(); const [pending, setPending] = React.useState(false); const handleIgnore = async () => { try { setPending(true); if (ignored) { await unignoreDirectory(`${mediaDirectory}/${directory}`); } else { await ignoreDirectory(`${mediaDirectory}/${directory}`); } if (onSubmit) { onSubmit(); } } finally { setPending(false); } } const header = ignored ? 'Remove Ignore Directory' : 'Ignore Directory'; const content = ignored ? 'Remove ignore of the following directory? You must refresh your files after this.' : 'Ignore the following directory? The files within will not be indexed and will not show in search results.'; let submitButton = ; return {header} {content}
{mediaDirectory}/{directory}
{submitButton}
} export function MakeDirectoryModal({open, onClose, parent, onSubmit}) { const [value, setValue] = useState(''); const mediaDirectory = useMediaDirectory(); const handleInputChange = (e, props) => { if (e) { e.preventDefault(); } setValue(props.value); } const handleSubmit = async (e) => { if (e) { e.preventDefault(); } try { const path = parent ? `${parent}/${value}` : value; await makeDirectory(path); setValue(''); if (onSubmit) { await onSubmit(); } } catch (e) { console.error(e); } } return Make Directory
{mediaDirectory}/{parent}{value}
}