mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
Creating isEmpty function to check if dict/array have some contents.
This commit is contained in:
parent
92bd1de8a0
commit
dad276d6ec
10 changed files with 189 additions and 159 deletions
|
|
@ -19,6 +19,7 @@ import {
|
|||
ExternalCardLink,
|
||||
FileIcon,
|
||||
HelpHeader,
|
||||
isEmpty,
|
||||
mimetypeColor,
|
||||
PageContainer,
|
||||
SearchInput,
|
||||
|
|
@ -113,9 +114,9 @@ function ArchivePage() {
|
|||
</>);
|
||||
|
||||
let alternativesList = <Loader active/>;
|
||||
if (alternatives && alternatives.length > 0) {
|
||||
if (!isEmpty(alternatives)) {
|
||||
alternativesList = <FileCards files={alternatives}/>;
|
||||
} else if (alternatives && alternatives.length === 0) {
|
||||
} else if (isEmpty(alternatives)) {
|
||||
alternativesList = <p>No alternatives available</p>;
|
||||
}
|
||||
|
||||
|
|
@ -235,7 +236,7 @@ export function Domains() {
|
|||
</PlaceholderHeader>
|
||||
</Placeholder>
|
||||
</>)
|
||||
} else if (!domains || domains.length === 0) {
|
||||
} else if (isEmpty(domains)) {
|
||||
return <Message>
|
||||
<Message.Header>No domains yet.</Message.Header>
|
||||
<Message.Content>Archive some webpages!</Message.Content>
|
||||
|
|
@ -289,7 +290,7 @@ function Archives() {
|
|||
useTitle('Archives');
|
||||
|
||||
let filterOptions = [];
|
||||
if (domains && domains.length > 0) {
|
||||
if (!isEmpty(domains)) {
|
||||
domains.forEach(i => {
|
||||
filterOptions = [...filterOptions, {text: i['domain'], key: i['domain'], value: i['domain']}]
|
||||
});
|
||||
|
|
@ -379,7 +380,7 @@ function Archives() {
|
|||
const selectElm = <div style={{marginTop: '0.5em'}}>
|
||||
<Button
|
||||
color='red'
|
||||
disabled={!selectedArchives || selectedArchives.length === 0}
|
||||
disabled={isEmpty(selectedArchives)}
|
||||
onClick={() => setDeleteOpen(true)}
|
||||
>Delete</Button>
|
||||
<Confirm
|
||||
|
|
@ -392,14 +393,14 @@ function Archives() {
|
|||
<Button
|
||||
color='grey'
|
||||
onClick={() => invertSelection()}
|
||||
disabled={!archives || archives.length === 0}
|
||||
disabled={isEmpty(archives)}
|
||||
>
|
||||
Invert
|
||||
</Button>
|
||||
<Button
|
||||
color='yellow'
|
||||
onClick={() => clearSelection()}
|
||||
disabled={(archives && archives.length === 0) || selectedArchives.length === 0}
|
||||
disabled={isEmpty(archives) || isEmpty(selectedArchives)}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
|
|
@ -428,6 +429,34 @@ function Archives() {
|
|||
/>
|
||||
}
|
||||
|
||||
export function ArchiveRowCells({file}) {
|
||||
const {archive} = file;
|
||||
|
||||
const archiveUrl = `/archive/${archive.id}`;
|
||||
const posterUrl = archive.screenshot_path ? `/media/${encodeURIComponent(archive.screenshot_path)}` : null;
|
||||
|
||||
let poster;
|
||||
if (posterUrl) {
|
||||
poster = <CardLink to={archiveUrl}>
|
||||
<Image wrapped src={posterUrl} width='50px'/>
|
||||
</CardLink>;
|
||||
} else {
|
||||
poster = <FileIcon file={file} size='large'/>;
|
||||
}
|
||||
|
||||
// Fragment for SelectableRow
|
||||
return (<React.Fragment>
|
||||
<TableCell>
|
||||
<center>{poster}</center>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<CardLink to={archiveUrl}>
|
||||
{textEllipsis(archive.title || archive.stem, 100)}
|
||||
</CardLink>
|
||||
</TableCell>
|
||||
</React.Fragment>)
|
||||
}
|
||||
|
||||
export function ArchiveRoute() {
|
||||
const links = [
|
||||
{text: 'Archives', to: '/archive', end: true},
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {createChannel, deleteChannel, downloadChannel, refreshChannel, updateCha
|
|||
import Container from "semantic-ui-react/dist/commonjs/elements/Container";
|
||||
import {
|
||||
frequencyOptions,
|
||||
humanFileSize,
|
||||
humanFileSize, isEmpty,
|
||||
RequiredAsterisk,
|
||||
secondsToDate,
|
||||
secondsToFrequency,
|
||||
|
|
@ -574,7 +574,7 @@ export function Channels() {
|
|||
</TableBody>
|
||||
</Table>
|
||||
</>
|
||||
} else if (channels.length === 0) {
|
||||
} else if (isEmpty(channels)) {
|
||||
return <>
|
||||
{header}
|
||||
<Message>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import React, {useContext, useEffect, useState} from "react";
|
||||
import {Card, Container, IconGroup, Image, Input, Modal, Pagination, Responsive, TableCell} from 'semantic-ui-react';
|
||||
import {Card, Container, IconGroup, Input, Modal, Pagination, Responsive} from 'semantic-ui-react';
|
||||
import {Link, NavLink} from "react-router-dom";
|
||||
import Message from "semantic-ui-react/dist/commonjs/collections/Message";
|
||||
import {useDownloaders, useHotspot, useThrottle} from "../hooks/customHooks";
|
||||
import {StatusContext, ThemeContext} from "../contexts/contexts";
|
||||
import {Button, CardIcon, darkTheme, Form, Header, Icon, lightTheme, Menu, Popup, Statistic} from "./Theme";
|
||||
import {Button, darkTheme, Form, Header, Icon, lightTheme, Menu, Popup, Statistic} from "./Theme";
|
||||
|
||||
export const API_URI = `http://${window.location.host}/api`;
|
||||
export const VIDEOS_API = `${API_URI}/videos`;
|
||||
|
|
@ -133,122 +133,6 @@ export function ExternalCardLink({to, ...props}) {
|
|||
</a>
|
||||
}
|
||||
|
||||
export function VideoCard({file}) {
|
||||
const {video} = file;
|
||||
const {s} = useContext(ThemeContext);
|
||||
|
||||
let video_url = `/videos/video/${video.id}`;
|
||||
const upload_date = uploadDate(video.upload_date);
|
||||
// A video may not have a channel.
|
||||
const channel = video.channel ? video.channel : null;
|
||||
let channel_url = null;
|
||||
if (channel) {
|
||||
channel_url = `/videos/channel/${channel.id}/video`;
|
||||
video_url = `/videos/channel/${channel.id}/video/${video.id}`;
|
||||
}
|
||||
const poster_url = video.poster_path ? `/media/${encodeURIComponent(video.poster_path)}` : null;
|
||||
|
||||
let imageLabel = null;
|
||||
if (video.favorite) {
|
||||
imageLabel = {corner: 'left', icon: 'heart', color: 'green'};
|
||||
}
|
||||
let poster = <CardIcon><FileIcon file={file}/></CardIcon>;
|
||||
if (poster_url) {
|
||||
poster = <Image wrapped
|
||||
src={poster_url}
|
||||
label={imageLabel}
|
||||
style={{position: 'relative', width: '100%'}}
|
||||
/>;
|
||||
}
|
||||
|
||||
return (<Card color={mimetypeColor(file.mimetype)}>
|
||||
<Link to={video_url}>
|
||||
{poster}
|
||||
</Link>
|
||||
<Duration video={video}/>
|
||||
<Card.Content {...s}>
|
||||
<Card.Header>
|
||||
<Container textAlign='left'>
|
||||
<Link to={video_url} className="no-link-underscore card-link">
|
||||
<p {...s}>{textEllipsis(video.title || video.stem || video.video_path, 100)}</p>
|
||||
</Link>
|
||||
</Container>
|
||||
</Card.Header>
|
||||
<Card.Description>
|
||||
<Container textAlign='left'>
|
||||
{channel && <Link to={channel_url} className="no-link-underscore card-link">
|
||||
<b {...s}>{channel.name}</b>
|
||||
</Link>}
|
||||
<p {...s}>{upload_date}</p>
|
||||
</Container>
|
||||
</Card.Description>
|
||||
</Card.Content>
|
||||
</Card>)
|
||||
}
|
||||
|
||||
export function VideoRowCells({file}) {
|
||||
const {video} = file;
|
||||
|
||||
let video_url = `/videos/video/${video.id}`;
|
||||
const poster_url = video.poster_path ? `/media/${encodeURIComponent(video.poster_path)}` : null;
|
||||
|
||||
let poster;
|
||||
let imageLabel = video.favorite ? {corner: 'left', icon: 'heart', color: 'green'} : null;
|
||||
if (poster_url) {
|
||||
poster = <CardLink to={video_url}>
|
||||
<Image wrapped
|
||||
src={poster_url}
|
||||
label={imageLabel}
|
||||
width='50px'
|
||||
/>
|
||||
</CardLink>
|
||||
} else {
|
||||
poster = <FileIcon file={file} size='large'/>;
|
||||
}
|
||||
|
||||
// Fragment for SelectableRow
|
||||
return <React.Fragment>
|
||||
<TableCell>
|
||||
<center>
|
||||
{poster}
|
||||
</center>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<CardLink to={video_url}>
|
||||
{textEllipsis(video.title || video.stem || video.video_path, 100)}
|
||||
</CardLink>
|
||||
</TableCell>
|
||||
</React.Fragment>
|
||||
}
|
||||
|
||||
export function ArchiveRowCells({file}) {
|
||||
const {archive} = file;
|
||||
|
||||
const archiveUrl = `/archive/${archive.id}`;
|
||||
const posterUrl = archive.screenshot_path ? `/media/${encodeURIComponent(archive.screenshot_path)}` : null;
|
||||
|
||||
let poster;
|
||||
if (posterUrl) {
|
||||
poster = <CardLink to={archiveUrl}>
|
||||
<Image wrapped src={posterUrl} width='50px'/>
|
||||
</CardLink>;
|
||||
} else {
|
||||
poster = <FileIcon file={file} size='large'/>;
|
||||
}
|
||||
|
||||
// Fragment for SelectableRow
|
||||
return (<React.Fragment>
|
||||
<TableCell>
|
||||
<center>{poster}</center>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<CardLink to={archiveUrl}>
|
||||
{textEllipsis(archive.title || archive.stem, 100)}
|
||||
</CardLink>
|
||||
</TableCell>
|
||||
</React.Fragment>)
|
||||
}
|
||||
|
||||
export function RequiredAsterisk() {
|
||||
return <span style={{color: '#db2828'}}> *</span>
|
||||
}
|
||||
|
|
@ -410,6 +294,16 @@ export function arraysEqual(a, b) {
|
|||
return true;
|
||||
}
|
||||
|
||||
export function isEmpty(i) {
|
||||
const str = Object.prototype.toString.call(i);
|
||||
if (str === '[object Object]') {
|
||||
return Object.keys(i).length === 0;
|
||||
} else if (str === '[object Array]') {
|
||||
return i.length === 0;
|
||||
}
|
||||
return !i;
|
||||
}
|
||||
|
||||
export function scrollToTop() {
|
||||
window.scrollTo({
|
||||
top: 0, behavior: "auto"
|
||||
|
|
|
|||
|
|
@ -4,28 +4,27 @@ import {Card, Confirm, Divider, Dropdown, Image, PlaceholderLine, TableCell, Tab
|
|||
import 'react-keyed-file-browser/dist/react-keyed-file-browser.css';
|
||||
import {deleteFile, refreshDirectoryFiles, refreshFiles} from "../api";
|
||||
import {
|
||||
ArchiveRowCells,
|
||||
CardGroupCentered,
|
||||
ExternalCardLink,
|
||||
FileIcon,
|
||||
humanFileSize,
|
||||
isEmpty,
|
||||
mimetypeColor,
|
||||
PageContainer,
|
||||
Paginator,
|
||||
textEllipsis,
|
||||
uploadDate,
|
||||
useTitle,
|
||||
VideoCard,
|
||||
VideoRowCells
|
||||
useTitle
|
||||
} from "./Common";
|
||||
import {useBrowseFiles, useQuery, useSearchFiles} from "../hooks/customHooks";
|
||||
import {Route, Routes} from "react-router-dom";
|
||||
import {CardPlacholder} from "./Placeholder";
|
||||
import {ArchiveCard} from "./Archive";
|
||||
import {ArchiveCard, ArchiveRowCells} from "./Archive";
|
||||
import Grid from "semantic-ui-react/dist/commonjs/collections/Grid";
|
||||
import {ThemeContext} from "../contexts/contexts";
|
||||
import {Button, CardIcon, Header, Icon, Placeholder, Segment} from "./Theme";
|
||||
import {SelectableTable} from "./Tables";
|
||||
import {VideoCard, VideoRowCells} from "./Videos";
|
||||
|
||||
const icons = {
|
||||
File: <Icon name='file'/>,
|
||||
|
|
@ -199,11 +198,11 @@ function FileCard({file}) {
|
|||
}
|
||||
|
||||
export function FileCards({files}) {
|
||||
if (files && files.length > 0) {
|
||||
if (!isEmpty(files)) {
|
||||
return <CardGroupCentered>
|
||||
{files.map((i) => <FileCard key={i['path']} file={i}/>)}
|
||||
</CardGroupCentered>
|
||||
} else if (files && files.length === 0) {
|
||||
} else if (isEmpty(files)) {
|
||||
return <Header as='h4'>No files found.</Header>
|
||||
} else {
|
||||
return <CardGroupCentered><CardPlacholder/></CardGroupCentered>
|
||||
|
|
@ -256,7 +255,7 @@ function FileRow({file}) {
|
|||
}
|
||||
|
||||
export function FileTable({files, selectOn, onSelect, footer, selectedKeys}) {
|
||||
if (files && files.length > 0) {
|
||||
if (!isEmpty(files)) {
|
||||
const headerContents = ['Poster', 'Title'];
|
||||
const rows = files.map(i => <FileRow key={i['key']} file={i}/>);
|
||||
return <SelectableTable
|
||||
|
|
@ -267,7 +266,7 @@ export function FileTable({files, selectOn, onSelect, footer, selectedKeys}) {
|
|||
footer={footer}
|
||||
rows={rows}
|
||||
/>;
|
||||
} else if (files && files.length === 0) {
|
||||
} else if (isEmpty(files)) {
|
||||
return <Segment><p>No results!</p></Segment>
|
||||
} else {
|
||||
return <Placeholder>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import {
|
|||
} from "semantic-ui-react";
|
||||
import {Route, Routes} from "react-router-dom";
|
||||
import {toast} from 'react-semantic-toasts';
|
||||
import {arraysEqual, enumerate, PageContainer, replaceNullValues, TabLinks, useTitle} from './Common';
|
||||
import {arraysEqual, enumerate, isEmpty, PageContainer, replaceNullValues, TabLinks, useTitle} from './Common';
|
||||
import _ from 'lodash';
|
||||
import {ThemeContext} from "../contexts/contexts";
|
||||
import {useInventory} from "../hooks/customHooks";
|
||||
|
|
@ -50,7 +50,7 @@ function InventorySummary() {
|
|||
</TableRow>
|
||||
}
|
||||
|
||||
if (byCategory && byCategory.length > 0) {
|
||||
if (!isEmpty(byCategory)) {
|
||||
return <Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
|
|
@ -77,7 +77,7 @@ function InventorySummary() {
|
|||
</TableRow>
|
||||
}
|
||||
|
||||
if (bySubcategory && bySubcategory.length > 0) {
|
||||
if (!isEmpty(bySubcategory )) {
|
||||
return <Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
|
|
@ -106,7 +106,7 @@ function InventorySummary() {
|
|||
</TableRow>
|
||||
}
|
||||
|
||||
if (byName && byName.length > 0) {
|
||||
if (!isEmpty(byName)) {
|
||||
|
||||
return <Table>
|
||||
<TableHeader>
|
||||
|
|
@ -309,7 +309,7 @@ class InventoryList extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
if (this.props.items.length === 0) {
|
||||
if (isEmpty(this.props.items)) {
|
||||
return <ThemeContext.Consumer>
|
||||
{({t}) => (
|
||||
<p {...t}>Add some items using the form above!</p>
|
||||
|
|
@ -386,7 +386,7 @@ class InventoryList extends React.Component {
|
|||
<Button
|
||||
color='red'
|
||||
onClick={this.handleRemove}
|
||||
disabled={this.state.checkboxes.length === 0}
|
||||
disabled={isEmpty(this.state.checkboxes)}
|
||||
floated='right'
|
||||
>
|
||||
Remove
|
||||
|
|
@ -394,7 +394,7 @@ class InventoryList extends React.Component {
|
|||
<Button
|
||||
secondary
|
||||
onClick={this.handleSave}
|
||||
disabled={this.state.checkboxes.length === 0}
|
||||
disabled={isEmpty(this.state.checkboxes)}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, {useContext} from "react";
|
||||
import {HelpPopup, humanFileSize, PageContainer, TabLinks, useTitle, WROLModeMessage} from "./Common";
|
||||
import {HelpPopup, humanFileSize, isEmpty, PageContainer, TabLinks, useTitle, WROLModeMessage} from "./Common";
|
||||
import {Route, Routes} from "react-router-dom";
|
||||
import {getMapImportStatus, importMapFiles} from "../api";
|
||||
import {
|
||||
|
|
@ -131,7 +131,7 @@ class ManageMap extends React.Component {
|
|||
|
||||
this.setState({'ready': false});
|
||||
const {files, selectedPaths} = this.state;
|
||||
if (!selectedPaths || selectedPaths.length === 0) {
|
||||
if (isEmpty(selectedPaths)) {
|
||||
// No files are selected, import all.
|
||||
let paths = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
|
|
@ -192,13 +192,13 @@ class ManageMap extends React.Component {
|
|||
pending.length > 1 ? 'Importing...' : `Importing ${pending[0]}`
|
||||
: null;
|
||||
|
||||
let disabled = dockerized || !files || files.length === 0 || import_running;
|
||||
let disabled = dockerized || isEmpty(files) || import_running;
|
||||
let importButton = <Button
|
||||
primary
|
||||
disabled={disabled}
|
||||
onClick={this.import}
|
||||
>
|
||||
{selectedPaths.length > 0 ? 'Import Selected' : 'Import All'}
|
||||
{!isEmpty(selectedPaths) ? 'Import Selected' : 'Import All'}
|
||||
</Button>;
|
||||
|
||||
let rows;
|
||||
|
|
@ -213,7 +213,7 @@ class ManageMap extends React.Component {
|
|||
</Placeholder>
|
||||
</TableCell>
|
||||
</TableRow>;
|
||||
} else if (files.length === 0) {
|
||||
} else if (isEmpty(files)) {
|
||||
rows = <TableRow>
|
||||
<TableCell/><TableCell colSpan={5}>No PBF map files were found in <b>map/pbf</b></TableCell>
|
||||
</TableRow>;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import {
|
|||
TabPane as TAB_PANE,
|
||||
TextArea as TEXTAREA
|
||||
} from "semantic-ui-react";
|
||||
import {isEmpty} from "./Common";
|
||||
|
||||
export const darkTheme = 'dark';
|
||||
export const lightTheme = 'light';
|
||||
|
|
@ -30,7 +31,7 @@ export const systemTheme = 'system';
|
|||
export const themeSessionKey = 'color-scheme';
|
||||
|
||||
export function ThemeWrapper({children, ...props}) {
|
||||
if (Object.keys(props).length > 0) {
|
||||
if (!isEmpty(props)) {
|
||||
console.log(props);
|
||||
console.error('ThemeWrapper does not support props!');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, {useContext, useState} from 'react';
|
||||
import {deleteVideos} from "../api";
|
||||
import {Link, useNavigate, useParams} from "react-router-dom";
|
||||
import {humanFileSize, humanNumber, PageContainer, secondsToTimestamp, uploadDate, useTitle, VideoCard} from "./Common";
|
||||
import {humanFileSize, humanNumber, PageContainer, secondsToTimestamp, uploadDate, useTitle} from "./Common";
|
||||
import {Confirm} from "semantic-ui-react";
|
||||
import Grid from "semantic-ui-react/dist/commonjs/collections/Grid";
|
||||
import Container from "semantic-ui-react/dist/commonjs/elements/Container";
|
||||
|
|
@ -9,6 +9,7 @@ import {VideoPlaceholder} from "./Placeholder";
|
|||
import {useChannel} from "../hooks/customHooks";
|
||||
import {ThemeContext} from "../contexts/contexts";
|
||||
import {Button, darkTheme, Header, Icon, Segment, Tab, TabPane} from "./Theme";
|
||||
import {VideoCard} from "./Videos";
|
||||
|
||||
const MEDIA_PATH = '/media';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,42 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import React, {useContext, useEffect, useState} from 'react';
|
||||
import {Link, Route, Routes, useParams} from "react-router-dom";
|
||||
import {
|
||||
CardLink,
|
||||
defaultSearchOrder,
|
||||
defaultVideoOrder,
|
||||
Duration,
|
||||
FileIcon,
|
||||
isEmpty,
|
||||
mimetypeColor,
|
||||
PageContainer,
|
||||
scrollToTop,
|
||||
SearchInput,
|
||||
TabLinks,
|
||||
textEllipsis,
|
||||
uploadDate,
|
||||
useTitle
|
||||
} from "./Common"
|
||||
import VideoPage from "./VideoPlayer";
|
||||
import {Confirm, Dropdown, PlaceholderHeader, PlaceholderLine, StatisticLabel, StatisticValue} from "semantic-ui-react";
|
||||
import {
|
||||
Card,
|
||||
Confirm,
|
||||
Container,
|
||||
Dropdown,
|
||||
Image,
|
||||
PlaceholderHeader,
|
||||
PlaceholderLine,
|
||||
StatisticLabel,
|
||||
StatisticValue,
|
||||
TableCell
|
||||
} from "semantic-ui-react";
|
||||
import {Channels, EditChannel, NewChannel} from "./Channels";
|
||||
import {useChannel, useQuery, useSearchVideos, useVideo, useVideoStatistics} from "../hooks/customHooks";
|
||||
import {FilesView} from "./Files";
|
||||
import Grid from "semantic-ui-react/dist/commonjs/collections/Grid";
|
||||
import Icon from "semantic-ui-react/dist/commonjs/elements/Icon";
|
||||
import {Button, Header, Loader, Placeholder, Segment, Statistic, StatisticGroup} from "./Theme";
|
||||
import {Button, CardIcon, Header, Loader, Placeholder, Segment, Statistic, StatisticGroup} from "./Theme";
|
||||
import {deleteVideos} from "../api";
|
||||
import {ThemeContext} from "../contexts/contexts";
|
||||
|
||||
export function VideoWrapper() {
|
||||
const {videoId} = useParams();
|
||||
|
|
@ -149,7 +168,7 @@ function Videos({filter}) {
|
|||
const selectElm = <div style={{marginTop: '0.5em'}}>
|
||||
<Button
|
||||
color='red'
|
||||
disabled={!selectedVideos || selectedVideos.length === 0}
|
||||
disabled={isEmpty(selectedVideos)}
|
||||
onClick={() => setDeleteOpen(true)}
|
||||
>Delete</Button>
|
||||
<Confirm
|
||||
|
|
@ -162,14 +181,14 @@ function Videos({filter}) {
|
|||
<Button
|
||||
color='grey'
|
||||
onClick={() => invertSelection()}
|
||||
disabled={!videos || videos.length === 0}
|
||||
disabled={isEmpty(videos)}
|
||||
>
|
||||
Invert
|
||||
</Button>
|
||||
<Button
|
||||
color='yellow'
|
||||
onClick={() => clearSelection()}
|
||||
disabled={(videos && videos.length === 0) || selectedVideos.length === 0}
|
||||
disabled={isEmpty(videos) || isEmpty(selectedVideos)}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
|
|
@ -279,3 +298,90 @@ export function VideosRoute(props) {
|
|||
)
|
||||
}
|
||||
|
||||
export function VideoCard({file}) {
|
||||
const {video} = file;
|
||||
const {s} = useContext(ThemeContext);
|
||||
|
||||
let video_url = `/videos/video/${video.id}`;
|
||||
const upload_date = uploadDate(video.upload_date);
|
||||
// A video may not have a channel.
|
||||
const channel = video.channel ? video.channel : null;
|
||||
let channel_url = null;
|
||||
if (channel) {
|
||||
channel_url = `/videos/channel/${channel.id}/video`;
|
||||
video_url = `/videos/channel/${channel.id}/video/${video.id}`;
|
||||
}
|
||||
const poster_url = video.poster_path ? `/media/${encodeURIComponent(video.poster_path)}` : null;
|
||||
|
||||
let imageLabel = null;
|
||||
if (video.favorite) {
|
||||
imageLabel = {corner: 'left', icon: 'heart', color: 'green'};
|
||||
}
|
||||
let poster = <CardIcon><FileIcon file={file}/></CardIcon>;
|
||||
if (poster_url) {
|
||||
poster = <Image wrapped
|
||||
src={poster_url}
|
||||
label={imageLabel}
|
||||
style={{position: 'relative', width: '100%'}}
|
||||
/>;
|
||||
}
|
||||
|
||||
return (<Card color={mimetypeColor(file.mimetype)}>
|
||||
<Link to={video_url}>
|
||||
{poster}
|
||||
</Link>
|
||||
<Duration video={video}/>
|
||||
<Card.Content {...s}>
|
||||
<Card.Header>
|
||||
<Container textAlign='left'>
|
||||
<Link to={video_url} className="no-link-underscore card-link">
|
||||
<p {...s}>{textEllipsis(video.title || video.stem || video.video_path, 100)}</p>
|
||||
</Link>
|
||||
</Container>
|
||||
</Card.Header>
|
||||
<Card.Description>
|
||||
<Container textAlign='left'>
|
||||
{channel && <Link to={channel_url} className="no-link-underscore card-link">
|
||||
<b {...s}>{channel.name}</b>
|
||||
</Link>}
|
||||
<p {...s}>{upload_date}</p>
|
||||
</Container>
|
||||
</Card.Description>
|
||||
</Card.Content>
|
||||
</Card>)
|
||||
}
|
||||
|
||||
export function VideoRowCells({file}) {
|
||||
const {video} = file;
|
||||
|
||||
let video_url = `/videos/video/${video.id}`;
|
||||
const poster_url = video.poster_path ? `/media/${encodeURIComponent(video.poster_path)}` : null;
|
||||
|
||||
let poster;
|
||||
let imageLabel = video.favorite ? {corner: 'left', icon: 'heart', color: 'green'} : null;
|
||||
if (poster_url) {
|
||||
poster = <CardLink to={video_url}>
|
||||
<Image wrapped
|
||||
src={poster_url}
|
||||
label={imageLabel}
|
||||
width='50px'
|
||||
/>
|
||||
</CardLink>
|
||||
} else {
|
||||
poster = <FileIcon file={file} size='large'/>;
|
||||
}
|
||||
|
||||
// Fragment for SelectableRow
|
||||
return <React.Fragment>
|
||||
<TableCell>
|
||||
<center>
|
||||
{poster}
|
||||
</center>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<CardLink to={video_url}>
|
||||
{textEllipsis(video.title || video.stem || video.video_path, 100)}
|
||||
</CardLink>
|
||||
</TableCell>
|
||||
</React.Fragment>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React, {useContext} from "react";
|
|||
import {clearCompletedDownloads, clearFailedDownloads, killDownload, postDownload} from "../../api";
|
||||
import {Link} from "react-router-dom";
|
||||
import {
|
||||
DisableDownloadsToggle,
|
||||
DisableDownloadsToggle, isEmpty,
|
||||
secondsToElapsedPopup,
|
||||
secondsToFrequency,
|
||||
textEllipsis,
|
||||
|
|
@ -301,7 +301,7 @@ export function Downloads() {
|
|||
);
|
||||
|
||||
let onceTable = tablePlaceholder;
|
||||
if (onceDownloads && onceDownloads.length === 0) {
|
||||
if (isEmpty(onceDownloads)) {
|
||||
onceTable = <p {...t}>No downloads are scheduled.</p>
|
||||
} else if (onceDownloads) {
|
||||
onceTable = (
|
||||
|
|
@ -320,7 +320,7 @@ export function Downloads() {
|
|||
}
|
||||
|
||||
let recurringTable = tablePlaceholder;
|
||||
if (recurringDownloads && recurringDownloads.length === 0) {
|
||||
if (isEmpty(recurringDownloads)) {
|
||||
recurringTable = <p {...t}>No recurring downloads are scheduled.</p>
|
||||
} else if (recurringDownloads) {
|
||||
recurringTable = <Table>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue