2022-08-06 05:22:07 +00:00
|
|
|
import React, {useContext} from "react";
|
2022-11-30 20:13:54 -07:00
|
|
|
import {
|
2022-12-19 10:40:01 -07:00
|
|
|
Button as SButton,
|
2022-11-30 20:13:54 -07:00
|
|
|
CardContent,
|
2022-11-30 20:21:00 -07:00
|
|
|
CardDescription,
|
2022-11-30 20:13:54 -07:00
|
|
|
CardHeader,
|
2022-11-30 20:21:00 -07:00
|
|
|
CardMeta,
|
2022-12-09 11:22:20 -07:00
|
|
|
Container,
|
2022-11-30 20:13:54 -07:00
|
|
|
Dropdown,
|
2022-12-19 10:40:01 -07:00
|
|
|
Header as SHeader,
|
2022-11-30 20:13:54 -07:00
|
|
|
Image,
|
2022-12-19 10:40:01 -07:00
|
|
|
Modal,
|
2022-11-30 20:13:54 -07:00
|
|
|
PlaceholderLine,
|
|
|
|
|
TableCell,
|
|
|
|
|
TableHeaderCell,
|
|
|
|
|
} from "semantic-ui-react";
|
2022-08-06 05:22:07 +00:00
|
|
|
import {
|
|
|
|
|
CardGroupCentered,
|
2023-01-04 09:14:33 -07:00
|
|
|
CardLink,
|
|
|
|
|
CardPosterLink,
|
|
|
|
|
cardTitleWrapper,
|
2022-08-06 05:22:07 +00:00
|
|
|
ExternalCardLink,
|
|
|
|
|
FileIcon,
|
|
|
|
|
humanFileSize,
|
2023-01-04 09:14:33 -07:00
|
|
|
isoDatetimeToString,
|
2022-08-06 05:22:07 +00:00
|
|
|
mimetypeColor,
|
|
|
|
|
PageContainer,
|
|
|
|
|
Paginator,
|
|
|
|
|
textEllipsis,
|
2023-01-04 09:14:33 -07:00
|
|
|
useTitle
|
2022-08-06 05:22:07 +00:00
|
|
|
} from "./Common";
|
2023-01-31 18:08:07 -07:00
|
|
|
import {useQuery, useSearchFiles} from "../hooks/customHooks";
|
2022-08-06 05:22:07 +00:00
|
|
|
import {Route, Routes} from "react-router-dom";
|
|
|
|
|
import {CardPlacholder} from "./Placeholder";
|
2022-11-04 09:58:38 -06:00
|
|
|
import {ArchiveCard, ArchiveRowCells} from "./Archive";
|
2022-08-06 05:22:07 +00:00
|
|
|
import Grid from "semantic-ui-react/dist/commonjs/collections/Grid";
|
2023-01-04 11:16:40 -07:00
|
|
|
import {StatusContext, ThemeContext} from "../contexts/contexts";
|
|
|
|
|
import {Button, Card, CardIcon, Icon, Placeholder, Segment} from "./Theme";
|
2022-10-24 09:07:08 -06:00
|
|
|
import {SelectableTable} from "./Tables";
|
2022-11-04 09:58:38 -06:00
|
|
|
import {VideoCard, VideoRowCells} from "./Videos";
|
2022-12-09 11:22:20 -07:00
|
|
|
import _ from 'lodash';
|
2023-01-31 18:08:07 -07:00
|
|
|
import {FileBrowser} from "./FileBrowser";
|
|
|
|
|
import {refreshDirectoryFiles, refreshFiles} from "../api";
|
|
|
|
|
import {useSubscribeEventName} from "../Events";
|
2022-01-29 16:50:22 -07:00
|
|
|
|
|
|
|
|
|
2023-01-31 18:08:07 -07:00
|
|
|
export function FilesPage() {
|
2022-10-25 15:57:44 -06:00
|
|
|
useTitle('Files');
|
|
|
|
|
|
2023-01-31 18:08:07 -07:00
|
|
|
return <FileBrowser/>;
|
2022-02-01 14:11:14 -07:00
|
|
|
}
|
|
|
|
|
|
2022-12-09 11:22:20 -07:00
|
|
|
function EbookCard({file}) {
|
|
|
|
|
const {s} = useContext(ThemeContext);
|
|
|
|
|
let {ebook, suffix} = file;
|
2022-12-17 14:42:46 -07:00
|
|
|
|
|
|
|
|
const downloadUrl = `/download/${encodeURIComponent(file.path)}`;
|
|
|
|
|
const isEpub = file['mimetype'].startsWith('application/epub');
|
2022-12-19 10:40:01 -07:00
|
|
|
const viewerUrl = isEpub ? `/epub.html?url=${downloadUrl}` : null;
|
2022-12-09 11:22:20 -07:00
|
|
|
|
|
|
|
|
let cover = <CardIcon><FileIcon file={file}/></CardIcon>;
|
2022-12-17 14:42:46 -07:00
|
|
|
if (ebook && ebook.cover_path) {
|
|
|
|
|
const coverSrc = `/media/${encodeURIComponent(ebook.cover_path)}`;
|
2022-12-19 10:40:01 -07:00
|
|
|
cover = <CardPosterLink to={viewerUrl || downloadUrl} poster_url={coverSrc} external={true}/>;
|
2022-12-09 11:22:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
suffix = suffix ? _.trimStart(suffix, '.').toUpperCase() : null;
|
|
|
|
|
|
2022-12-19 10:40:01 -07:00
|
|
|
const downloadButton = <ExternalCardLink to={downloadUrl}>
|
|
|
|
|
<SButton
|
|
|
|
|
icon='download'
|
|
|
|
|
content='Download'
|
|
|
|
|
labelPosition='left'/>
|
|
|
|
|
</ExternalCardLink>;
|
|
|
|
|
|
|
|
|
|
const detailsButton = <Button
|
|
|
|
|
icon='file alternate'
|
|
|
|
|
content='Details'
|
|
|
|
|
labelPosition='left'
|
|
|
|
|
disabled={!ebook}
|
|
|
|
|
onClick={() => setDetailsOpen(true)}
|
|
|
|
|
/>;
|
|
|
|
|
const [detailsOpen, setDetailsOpen] = React.useState(false);
|
|
|
|
|
const detailsModal = <Modal closeIcon
|
|
|
|
|
open={detailsOpen}
|
|
|
|
|
onClose={() => setDetailsOpen(false)}
|
|
|
|
|
trigger={detailsButton}
|
|
|
|
|
>
|
|
|
|
|
<Modal.Header><SHeader as='h1'>{ebook.title || file.title}</SHeader></Modal.Header>
|
|
|
|
|
<Modal.Content>
|
|
|
|
|
<SHeader as='h3'>Author: {ebook.creator}</SHeader>
|
|
|
|
|
<p>{humanFileSize(ebook.size)}</p>
|
|
|
|
|
<SHeader as='h5'>eBook Path</SHeader>
|
|
|
|
|
<pre>{ebook ? ebook.ebook_path : file.path}</pre>
|
|
|
|
|
<SHeader as='h5'>Cover Path</SHeader>
|
|
|
|
|
<pre>{ebook && ebook.cover_path ? ebook.cover_path : 'N/A'}</pre>
|
|
|
|
|
</Modal.Content>
|
|
|
|
|
<Modal.Actions>
|
|
|
|
|
{downloadButton}
|
|
|
|
|
</Modal.Actions>
|
|
|
|
|
</Modal>;
|
|
|
|
|
|
2022-12-09 11:22:20 -07:00
|
|
|
const color = mimetypeColor(file.mimetype);
|
|
|
|
|
return <Card color={color}>
|
|
|
|
|
{cover}
|
|
|
|
|
<CardContent {...s}>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<Container textAlign='left'>
|
2022-12-19 10:40:01 -07:00
|
|
|
<ExternalCardLink to={viewerUrl || downloadUrl}>
|
|
|
|
|
{ebook ? ebook.title : file.title}
|
|
|
|
|
</ExternalCardLink>
|
2022-12-09 11:22:20 -07:00
|
|
|
</Container>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardMeta>
|
|
|
|
|
<pre {...s}>{suffix}</pre>
|
2022-12-19 10:40:01 -07:00
|
|
|
{detailsModal}
|
2022-12-09 11:22:20 -07:00
|
|
|
</CardMeta>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-06 05:22:07 +00:00
|
|
|
function ImageCard({file}) {
|
|
|
|
|
const {s} = useContext(ThemeContext);
|
|
|
|
|
const url = `/media/${encodeURIComponent(file.path)}`;
|
|
|
|
|
|
2022-09-29 08:57:46 -06:00
|
|
|
let poster = <CardIcon><FileIcon file={file}/></CardIcon>;
|
2022-08-06 05:22:07 +00:00
|
|
|
if (file.size && file.size < 50000000) {
|
|
|
|
|
// Image is less than 5mb, use it.
|
|
|
|
|
poster = <Image wrapped
|
|
|
|
|
src={url}
|
|
|
|
|
style={{position: 'relative', width: '100%'}}
|
|
|
|
|
/>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return <Card color={mimetypeColor(file.mimetype)}>
|
|
|
|
|
<ExternalCardLink to={url}>
|
|
|
|
|
{poster}
|
|
|
|
|
</ExternalCardLink>
|
2022-11-30 20:21:00 -07:00
|
|
|
<CardContent {...s}>
|
|
|
|
|
<CardHeader>
|
2022-08-06 05:22:07 +00:00
|
|
|
<ExternalCardLink to={url} className='no-link-underscore card-link'>
|
2022-12-19 10:58:45 -07:00
|
|
|
<p>{textEllipsis(file.title || file.stem || file.path)}</p>
|
2022-08-06 05:22:07 +00:00
|
|
|
</ExternalCardLink>
|
2022-11-30 20:21:00 -07:00
|
|
|
</CardHeader>
|
|
|
|
|
<CardMeta {...s}>
|
2022-12-23 16:04:09 -07:00
|
|
|
<p>{isoDatetimeToString(file.modified / 1000)}</p>
|
2022-11-30 20:21:00 -07:00
|
|
|
</CardMeta>
|
|
|
|
|
<CardDescription {...s}>
|
2022-08-06 05:22:07 +00:00
|
|
|
<p>{humanFileSize(file.size)}</p>
|
2022-11-30 20:21:00 -07:00
|
|
|
</CardDescription>
|
|
|
|
|
</CardContent>
|
2022-08-06 05:22:07 +00:00
|
|
|
</Card>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function FileCard({file}) {
|
|
|
|
|
const {s} = useContext(ThemeContext);
|
|
|
|
|
|
2022-12-15 14:25:47 -07:00
|
|
|
const isEbookType = file.mimetype && (
|
|
|
|
|
file.mimetype.startsWith('application/epub') || file.mimetype.startsWith('application/x-mobipocket-ebook')
|
|
|
|
|
);
|
|
|
|
|
|
2022-08-06 05:22:07 +00:00
|
|
|
if (file.model === 'video' && file['video']) {
|
|
|
|
|
return <VideoCard key={file['path']} file={file}/>;
|
|
|
|
|
} else if (file.model === 'archive' && file['archive']) {
|
|
|
|
|
return <ArchiveCard key={file['path']} file={file}/>;
|
|
|
|
|
} else if (file.mimetype && file.mimetype.startsWith('image/')) {
|
|
|
|
|
return <ImageCard key={file['path']} file={file}/>;
|
2022-12-15 14:25:47 -07:00
|
|
|
} else if (isEbookType) {
|
2022-12-09 11:22:20 -07:00
|
|
|
return <EbookCard key={file['path']} file={file}/>;
|
2022-08-06 05:22:07 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-15 14:25:47 -07:00
|
|
|
const url = `/download/${encodeURIComponent(file.path)}`;
|
2022-08-06 05:22:07 +00:00
|
|
|
const color = mimetypeColor(file.mimetype);
|
2022-10-24 17:15:15 -06:00
|
|
|
const size = file.size !== null && file.size !== undefined ? humanFileSize(file.size) : null;
|
|
|
|
|
|
2022-08-06 05:22:07 +00:00
|
|
|
return <Card color={color}>
|
|
|
|
|
<ExternalCardLink to={url}>
|
2022-09-29 08:57:46 -06:00
|
|
|
<CardIcon>
|
2022-08-06 05:22:07 +00:00
|
|
|
<FileIcon file={file}/>
|
2022-09-29 08:57:46 -06:00
|
|
|
</CardIcon>
|
2022-08-06 05:22:07 +00:00
|
|
|
</ExternalCardLink>
|
2022-11-30 20:13:54 -07:00
|
|
|
<CardContent {...s}>
|
|
|
|
|
<CardHeader>
|
2022-08-06 05:22:07 +00:00
|
|
|
<ExternalCardLink to={url}>
|
2022-12-19 10:58:45 -07:00
|
|
|
{cardTitleWrapper(file.title || file.stem || file.path)}
|
2022-08-06 05:22:07 +00:00
|
|
|
</ExternalCardLink>
|
2022-11-30 20:13:54 -07:00
|
|
|
</CardHeader>
|
2022-12-23 16:04:09 -07:00
|
|
|
<p>{isoDatetimeToString(file.modified / 1000)}</p>
|
2022-10-24 17:15:15 -06:00
|
|
|
<p>{size}</p>
|
2022-11-30 20:13:54 -07:00
|
|
|
</CardContent>
|
2022-08-06 05:22:07 +00:00
|
|
|
</Card>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function FileCards({files}) {
|
2023-01-04 09:14:33 -07:00
|
|
|
if (!_.isEmpty(files)) {
|
2022-08-06 05:22:07 +00:00
|
|
|
return <CardGroupCentered>
|
2022-11-05 15:24:06 -06:00
|
|
|
{files.map(i => <FileCard key={i['path']} file={i}/>)}
|
2022-08-06 05:22:07 +00:00
|
|
|
</CardGroupCentered>
|
2022-11-05 15:24:06 -06:00
|
|
|
} else if (files && files.length === 0) {
|
2023-01-04 09:14:33 -07:00
|
|
|
return <Segment>No results!</Segment>
|
2022-08-06 05:22:07 +00:00
|
|
|
} else {
|
|
|
|
|
return <CardGroupCentered><CardPlacholder/></CardGroupCentered>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-24 09:07:08 -06:00
|
|
|
function ImageRowCells({file}) {
|
2022-08-06 05:22:07 +00:00
|
|
|
const url = `/media/${encodeURIComponent(file.path)}`;
|
|
|
|
|
|
|
|
|
|
let poster = <FileIcon file={file} size='large'/>;
|
|
|
|
|
if (file.size && file.size < 50000000) {
|
|
|
|
|
// Image is less than 5mb, use it.
|
|
|
|
|
poster = <Image wrapped src={url} width='50px'/>;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-24 09:07:08 -06:00
|
|
|
// Fragment for SelectableRow
|
|
|
|
|
return (<React.Fragment>
|
|
|
|
|
<TableCell>
|
|
|
|
|
<center>
|
|
|
|
|
{poster}
|
|
|
|
|
</center>
|
2022-09-23 16:22:57 -06:00
|
|
|
</TableCell>
|
2022-10-24 09:07:08 -06:00
|
|
|
<TableCell>
|
|
|
|
|
<ExternalCardLink to={url}>
|
2022-12-19 10:58:45 -07:00
|
|
|
<p>{textEllipsis(file.title || file.stem || file.path)}</p>
|
2022-10-24 09:07:08 -06:00
|
|
|
</ExternalCardLink>
|
|
|
|
|
</TableCell>
|
|
|
|
|
</React.Fragment>)
|
|
|
|
|
}
|
2022-08-06 05:22:07 +00:00
|
|
|
|
2022-12-28 19:49:18 -07:00
|
|
|
export function EbookRowCells({file}) {
|
|
|
|
|
const {ebook} = file;
|
|
|
|
|
|
|
|
|
|
const downloadUrl = `/download/${encodeURIComponent(file.path)}`;
|
|
|
|
|
const viewerUrl = file['mimetype'].startsWith('application/epub') ? `/epub.html?url=${downloadUrl}` : null;
|
|
|
|
|
|
|
|
|
|
let cover = <CardIcon><FileIcon file={file}/></CardIcon>;
|
|
|
|
|
if (ebook && ebook.cover_path) {
|
|
|
|
|
const coverSrc = `/media/${encodeURIComponent(ebook.cover_path)}`;
|
|
|
|
|
cover = <Image wrapped src={coverSrc} width='50px'/>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fragment for SelectableRow
|
|
|
|
|
return (<React.Fragment>
|
|
|
|
|
<TableCell>
|
|
|
|
|
<center>{cover}</center>
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell>
|
|
|
|
|
<CardLink to={viewerUrl || downloadUrl} newTab={true}>
|
|
|
|
|
{textEllipsis(ebook.title || file.title)}
|
|
|
|
|
</CardLink>
|
|
|
|
|
</TableCell>
|
|
|
|
|
</React.Fragment>)
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-24 09:07:08 -06:00
|
|
|
function FileRow({file}) {
|
2022-12-28 19:49:18 -07:00
|
|
|
const isEbookType = file.mimetype && (
|
|
|
|
|
file.mimetype.startsWith('application/epub') || file.mimetype.startsWith('application/x-mobipocket-ebook')
|
|
|
|
|
);
|
|
|
|
|
|
2022-08-06 05:22:07 +00:00
|
|
|
if (file.model === 'video' && 'video' in file) {
|
2022-10-24 09:07:08 -06:00
|
|
|
return <VideoRowCells file={file}/>;
|
2022-08-06 05:22:07 +00:00
|
|
|
} else if (file.model === 'archive' && 'archive' in file) {
|
2022-10-24 09:07:08 -06:00
|
|
|
return <ArchiveRowCells file={file}/>;
|
2022-08-06 05:22:07 +00:00
|
|
|
} else if (file.mimetype && file.mimetype.startsWith('image/')) {
|
2022-10-24 09:07:08 -06:00
|
|
|
return <ImageRowCells file={file}/>;
|
2022-12-28 19:49:18 -07:00
|
|
|
} else if (isEbookType) {
|
|
|
|
|
return <EbookRowCells key={file['path']} file={file}/>;
|
2022-08-06 05:22:07 +00:00
|
|
|
}
|
|
|
|
|
const onDownloadFile = async () => window.open(`/media/${file.key}`);
|
|
|
|
|
|
2022-10-24 09:07:08 -06:00
|
|
|
// Fragment for SelectableRow
|
|
|
|
|
return <React.Fragment>
|
2022-08-06 05:22:07 +00:00
|
|
|
<TableCell>
|
|
|
|
|
<center><FileIcon file={file} size='large'/></center>
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell onClick={() => onDownloadFile()}>
|
|
|
|
|
{textEllipsis(file.title)}
|
|
|
|
|
</TableCell>
|
2022-10-24 09:07:08 -06:00
|
|
|
</React.Fragment>
|
2022-08-06 05:22:07 +00:00
|
|
|
}
|
|
|
|
|
|
2022-10-24 17:15:15 -06:00
|
|
|
export function FileTable({files, selectOn, onSelect, footer, selectedKeys}) {
|
2023-01-04 09:14:33 -07:00
|
|
|
if (!files) {
|
|
|
|
|
return <Placeholder>
|
|
|
|
|
<PlaceholderLine/>
|
|
|
|
|
<PlaceholderLine/>
|
|
|
|
|
<PlaceholderLine/>
|
|
|
|
|
</Placeholder>
|
|
|
|
|
} else if (files && files.length > 0) {
|
2022-10-24 09:07:08 -06:00
|
|
|
const headerContents = ['Poster', 'Title'];
|
|
|
|
|
const rows = files.map(i => <FileRow key={i['key']} file={i}/>);
|
|
|
|
|
return <SelectableTable
|
|
|
|
|
headerContents={headerContents}
|
|
|
|
|
selectOn={selectOn}
|
|
|
|
|
onSelect={onSelect}
|
2022-10-24 17:15:15 -06:00
|
|
|
selectedKeys={selectedKeys}
|
2022-10-24 09:07:08 -06:00
|
|
|
footer={footer}
|
|
|
|
|
rows={rows}
|
|
|
|
|
/>;
|
2022-08-06 05:22:07 +00:00
|
|
|
} else {
|
2023-01-04 19:45:42 -07:00
|
|
|
return <Segment>No results!</Segment>
|
2022-08-06 05:22:07 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function FilesView({
|
|
|
|
|
files,
|
|
|
|
|
limit,
|
|
|
|
|
setLimit,
|
|
|
|
|
showLimit = false,
|
|
|
|
|
activePage,
|
|
|
|
|
totalPages,
|
|
|
|
|
view,
|
|
|
|
|
setView,
|
|
|
|
|
showView = true,
|
|
|
|
|
showSelect = false,
|
2022-09-23 16:22:57 -06:00
|
|
|
selectElem,
|
2022-10-24 17:15:15 -06:00
|
|
|
selectedKeys,
|
2022-08-06 05:22:07 +00:00
|
|
|
onSelect,
|
|
|
|
|
setPage,
|
|
|
|
|
menuColumnsCount,
|
|
|
|
|
menuColumns,
|
|
|
|
|
filterOptions,
|
|
|
|
|
activeFilters,
|
|
|
|
|
setFilters,
|
|
|
|
|
multipleFilters,
|
|
|
|
|
filterPlaceholder,
|
|
|
|
|
}) {
|
|
|
|
|
|
|
|
|
|
let selectButton;
|
|
|
|
|
const [selectOn, setSelectOn] = React.useState(false);
|
|
|
|
|
const toggleSelectOn = () => setSelectOn(!selectOn);
|
|
|
|
|
const selectButtonDisabled = view !== 'list';
|
|
|
|
|
if (showSelect && selectOn) {
|
|
|
|
|
selectButton = <Button active disabled={selectButtonDisabled} icon='checkmark box'
|
|
|
|
|
onClick={toggleSelectOn}/>;
|
|
|
|
|
} else if (showSelect) {
|
|
|
|
|
selectButton = <Button icon='checkmark box' disabled={selectButtonDisabled}
|
|
|
|
|
onClick={toggleSelectOn}/>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let viewButton;
|
|
|
|
|
if (showView) {
|
|
|
|
|
if (view === 'list') {
|
|
|
|
|
viewButton = <Button icon='th' onClick={() => setView('cards')}/>;
|
|
|
|
|
} else {
|
|
|
|
|
viewButton = <Button icon='browser' onClick={() => setView('list')}/>;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let limitDropdown;
|
|
|
|
|
const limitOptions = [
|
|
|
|
|
{key: 12, value: 12, text: '12'},
|
|
|
|
|
{key: 24, value: 24, text: '24'},
|
|
|
|
|
{key: 48, value: 48, text: '48'},
|
|
|
|
|
{key: 96, value: 96, text: '96'},
|
|
|
|
|
];
|
|
|
|
|
if (showLimit) {
|
|
|
|
|
limitDropdown = <Dropdown
|
|
|
|
|
compact
|
|
|
|
|
selection
|
|
|
|
|
placeholder='Limit'
|
|
|
|
|
options={limitOptions}
|
|
|
|
|
value={parseInt(limit || limitOptions[0]['value'])}
|
|
|
|
|
onChange={(e, {value}) => setLimit(value)}
|
|
|
|
|
/>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const paginator = <center style={{marginTop: '2em'}}>
|
|
|
|
|
<Paginator activePage={activePage} totalPages={totalPages} onPageChange={setPage}/>
|
|
|
|
|
</center>;
|
|
|
|
|
|
|
|
|
|
let body;
|
|
|
|
|
if (view === 'list') {
|
2022-10-24 09:07:08 -06:00
|
|
|
const footer = selectOn && selectElem ?
|
|
|
|
|
<>
|
2023-01-04 09:14:33 -07:00
|
|
|
<TableHeaderCell colSpan='3'>{selectElem}</TableHeaderCell>
|
2022-10-24 09:07:08 -06:00
|
|
|
</> : null;
|
2022-10-24 17:15:15 -06:00
|
|
|
body = <FileTable files={files} selectOn={selectOn}
|
|
|
|
|
onSelect={onSelect} footer={footer} selectedKeys={selectedKeys}/>;
|
2022-08-06 05:22:07 +00:00
|
|
|
} else {
|
|
|
|
|
body = <FileCards files={files}/>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const filtersDropdown = <Dropdown selection clearable search
|
|
|
|
|
multiple={multipleFilters || undefined}
|
|
|
|
|
options={filterOptions}
|
|
|
|
|
placeholder={filterPlaceholder || 'Filters'}
|
|
|
|
|
onChange={(e, {value}) => setFilters(value)}
|
|
|
|
|
style={{marginLeft: '0.3em'}}
|
|
|
|
|
value={activeFilters}
|
|
|
|
|
/>;
|
|
|
|
|
|
|
|
|
|
return <>
|
|
|
|
|
<Grid columns={menuColumnsCount || 1} stackable>
|
|
|
|
|
<Grid.Column mobile={8} computer={6}>
|
|
|
|
|
{selectButton}
|
|
|
|
|
{viewButton}
|
|
|
|
|
{limitDropdown}
|
|
|
|
|
{filterOptions && filtersDropdown}
|
|
|
|
|
</Grid.Column>
|
|
|
|
|
{menuColumns}
|
|
|
|
|
</Grid>
|
|
|
|
|
{body}
|
|
|
|
|
{paginator}
|
|
|
|
|
</>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function FilesSearchView({
|
|
|
|
|
showView = true,
|
|
|
|
|
showLimit = false,
|
|
|
|
|
showSelect = false,
|
|
|
|
|
emptySearch = false,
|
|
|
|
|
filterOptions,
|
|
|
|
|
model,
|
|
|
|
|
onSelect,
|
|
|
|
|
setFilters,
|
|
|
|
|
}) {
|
2022-12-09 11:22:20 -07:00
|
|
|
filterOptions = filterOptions || [
|
|
|
|
|
{key: 'video', text: 'Video', value: 'video'},
|
|
|
|
|
{key: 'archive', text: 'Archive', value: 'archive'},
|
|
|
|
|
{key: 'pdf', text: 'PDF', value: 'pdf'},
|
|
|
|
|
{key: 'ebook', text: 'eBook', value: 'ebook'},
|
|
|
|
|
{key: 'image', text: 'Image', value: 'image'},
|
|
|
|
|
{key: 'zip', text: 'ZIP', value: 'zip'},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const {searchFiles, limit, setLimit, totalPages, activePage, setPage, filter} =
|
|
|
|
|
useSearchFiles(24, emptySearch, model);
|
2022-08-06 05:22:07 +00:00
|
|
|
|
|
|
|
|
const {searchParams, updateQuery} = useQuery();
|
|
|
|
|
const setView = (value) => updateQuery({view: value});
|
|
|
|
|
const view = searchParams.get('view');
|
|
|
|
|
|
2022-12-09 11:22:20 -07:00
|
|
|
const setFilter = (value) => updateQuery({'filter': value});
|
2022-08-06 05:22:07 +00:00
|
|
|
|
|
|
|
|
return <FilesView
|
|
|
|
|
files={searchFiles}
|
|
|
|
|
limit={limit}
|
|
|
|
|
setLimit={setLimit}
|
|
|
|
|
showLimit={showLimit}
|
|
|
|
|
activePage={activePage}
|
|
|
|
|
totalPages={totalPages}
|
|
|
|
|
view={view}
|
|
|
|
|
setView={setView}
|
|
|
|
|
showView={showView}
|
|
|
|
|
showSelect={showSelect}
|
|
|
|
|
onSelect={onSelect}
|
|
|
|
|
setPage={setPage}
|
|
|
|
|
filterOptions={filterOptions}
|
2022-12-09 11:22:20 -07:00
|
|
|
setFilters={setFilters || setFilter}
|
2022-08-06 05:22:07 +00:00
|
|
|
/>
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-31 18:08:07 -07:00
|
|
|
export function FilesRoute() {
|
|
|
|
|
return <PageContainer>
|
|
|
|
|
<Routes>
|
|
|
|
|
<Route path='/' exact element={<FilesPage/>}/>
|
|
|
|
|
</Routes>
|
|
|
|
|
</PageContainer>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function FilesRefreshButton() {
|
2023-01-04 11:16:40 -07:00
|
|
|
const {status} = useContext(StatusContext);
|
|
|
|
|
const refreshing = status && status['flags'] && status['flags'].indexOf('refreshing') >= 0;
|
2023-01-31 18:08:07 -07:00
|
|
|
const refreshingDirectory = status && status['flags'] && status['flags'].indexOf('refreshing_directory') >= 0;
|
2022-08-06 05:22:07 +00:00
|
|
|
|
|
|
|
|
const [loading, setLoading] = React.useState(false);
|
|
|
|
|
|
|
|
|
|
const handleClick = async () => {
|
|
|
|
|
setLoading(true);
|
|
|
|
|
await refreshFiles();
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 11:16:40 -07:00
|
|
|
// Clear loading when global refresh event completes.
|
|
|
|
|
useSubscribeEventName('global_refresh_completed', () => setLoading(false));
|
|
|
|
|
|
2023-01-31 18:08:07 -07:00
|
|
|
return <Button icon
|
|
|
|
|
labelPosition='left'
|
|
|
|
|
loading={loading || refreshing || refreshingDirectory}
|
|
|
|
|
onClick={handleClick}
|
|
|
|
|
disabled={loading || refreshing}>
|
|
|
|
|
<Icon name='refresh'/>
|
|
|
|
|
Refresh All
|
|
|
|
|
</Button>;
|
2022-01-29 16:50:22 -07:00
|
|
|
}
|
|
|
|
|
|
2023-01-31 18:08:07 -07:00
|
|
|
export function DirectoryRefreshButton({directory}) {
|
2023-01-04 11:16:40 -07:00
|
|
|
const {status} = useContext(StatusContext);
|
|
|
|
|
const refreshing = status && status['flags'] && status['flags'].indexOf('refreshing') >= 0;
|
2023-01-31 18:08:07 -07:00
|
|
|
const refreshingDirectory = status && status['flags'] && status['flags'].indexOf('refreshing_directory') >= 0;
|
2022-02-01 14:11:14 -07:00
|
|
|
|
2022-08-06 05:22:07 +00:00
|
|
|
const [loading, setLoading] = React.useState(false);
|
|
|
|
|
|
|
|
|
|
const handleClick = async () => {
|
|
|
|
|
setLoading(true);
|
|
|
|
|
await refreshDirectoryFiles(directory);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-31 18:08:07 -07:00
|
|
|
// Clear loading when global refresh event completes.
|
|
|
|
|
useSubscribeEventName('global_refresh_completed', () => setLoading(false));
|
2023-01-04 11:16:40 -07:00
|
|
|
useSubscribeEventName('directory_refresh_completed', () => setLoading(false));
|
|
|
|
|
|
2023-01-31 18:08:07 -07:00
|
|
|
return <Button icon
|
|
|
|
|
labelPosition='left'
|
|
|
|
|
loading={loading || refreshing || refreshingDirectory}
|
|
|
|
|
onClick={handleClick}
|
|
|
|
|
disabled={loading || refreshing || refreshingDirectory}
|
|
|
|
|
>
|
|
|
|
|
<Icon name='refresh'/>
|
|
|
|
|
Refresh Directory
|
|
|
|
|
</Button>
|
2022-01-29 16:50:22 -07:00
|
|
|
}
|