import { Backdrop, Button, Checkbox, CircularProgress, Container, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Paper, SpeedDial, SpeedDialAction, SpeedDialIcon, Typography } from '@mui/material' import DeleteForeverIcon from '@mui/icons-material/DeleteForever' import FolderIcon from '@mui/icons-material/Folder' import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile' import VideoFileIcon from '@mui/icons-material/VideoFile' import { Buffer } from 'buffer' import { useEffect, useMemo, useState, useTransition } from 'react' import { useSelector } from 'react-redux' import { BehaviorSubject, Subject, combineLatestWith, map, share } from 'rxjs' import { useObservable } from './hooks/observable' import { RootState } from './stores/store' import { DeleteRequest, DirectoryEntry } from './types' export default function Downloaded() { const settings = useSelector((state: RootState) => state.settings) const [openDialog, setOpenDialog] = useState(false) const serverAddr = `${window.location.protocol}//${settings.serverAddr}:${settings.serverPort}` const files$ = useMemo(() => new Subject(), []) const selected$ = useMemo(() => new BehaviorSubject([]), []) const [isPending, startTransition] = useTransition() const fetcher = () => fetch(`${serverAddr}/downloaded`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ subdir: '' }) }) .then(res => res.json()) .then(data => files$.next(data)) const fetcherSubfolder = (sub: string) => { const folders = sub.startsWith('/') ? sub.substring(1).split('/') : sub.split('/') const relpath = folders.length >= 2 ? folders.slice(-(folders.length - 1)).join('/') : folders.pop() const _upperLevel = folders.slice(1, -1) const upperLevel = _upperLevel.length === 2 ? ['.', ..._upperLevel].join('/') : _upperLevel.join('/') fetch(`${serverAddr}/downloaded`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ subdir: relpath }) }) .then(res => res.json()) .then(data => { files$.next(sub ? [{ name: '..', isDirectory: true, path: upperLevel, }, ...data] : data ) }) } const selectable$ = useMemo(() => files$.pipe( combineLatestWith(selected$), map(([data, selected]) => data.map(x => ({ ...x, selected: selected.includes(x.name) }))), share() ), []) const selectable = useObservable(selectable$, []) const addSelected = (name: string) => { selected$.value.includes(name) ? selected$.next(selected$.value.filter(val => val !== name)) : selected$.next([...selected$.value, name]) } const deleteSelected = () => { Promise.all(selectable .filter(entry => entry.selected) .map(entry => fetch(`${serverAddr}/delete`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ path: entry.path, shaSum: entry.shaSum, } as DeleteRequest) })) ).then(fetcher) } useEffect(() => { fetcher() }, [settings.serverAddr, settings.serverPort]) const onFileClick = (path: string) => startTransition(() => { window.open(`${serverAddr}/d/${Buffer.from(path).toString('hex')}`) }) const onFolderClick = (path: string) => startTransition(() => { fetcherSubfolder(path) }) return ( theme.zIndex.drawer + 1 }} open={!(files$.observed) || isPending} > {'Archive'} {selectable.length === 0 && 'No files found'} {selectable.map((file, idx) => ( addSelected(file.name)} /> } disablePadding > file.isDirectory ? onFolderClick(file.path) : onFileClick(file.path) }> {file.isDirectory ? : file.isVideo ? : } ))} } > } tooltipTitle={`Delete selected`} tooltipOpen onClick={() => { if (selected$.value.length > 0) { setOpenDialog(true) } }} /> setOpenDialog(false)} > Are you sure? You're deleting:
    {selected$.value.map((entry, idx) => (
  • {entry}
  • ))}
) }