diff --git a/app/src/components/Admin.js b/app/src/components/Admin.js index d6235786..04fe60dd 100644 --- a/app/src/components/Admin.js +++ b/app/src/components/Admin.js @@ -23,7 +23,8 @@ import { startDownloads } from "../api"; import TimezoneSelect from 'react-timezone-select'; -import {secondsToDate, secondsToFrequency, textEllipsis, truncateLong, WROLModeMessage} from "./Common"; +import {secondsToDate, secondsToFrequency, TabLinks, textEllipsis, WROLModeMessage} from "./Common"; +import {Route} from "react-router-dom"; class Settings extends React.Component { @@ -440,23 +441,21 @@ class Downloads extends React.Component { } } -class Admin extends React.Component { +export default function Admin(props) { - render() { + const links = [ + {text: 'Downloads', to: '/admin', exact: true}, + {text: 'Settings', to: '/admin/settings'}, + {text: 'WROL Mode', to: '/admin/wrol'}, + ]; - const panes = [ - {menuItem: 'Downloads', render: () => }, - {menuItem: 'Settings', render: () => }, - {menuItem: 'WROL Mode', render: () => }, - ]; - - return ( - - - - ) - } + return ( + <> + + + + + > + ) } - -export default Admin; diff --git a/app/src/components/Apps.js b/app/src/components/Apps.js index 513680da..994e8a4d 100644 --- a/app/src/components/Apps.js +++ b/app/src/components/Apps.js @@ -143,14 +143,12 @@ class OTP extends React.Component { } } -export class AppsRoute extends React.Component { - render() { - return ( - <> - - - - > - ) - } +export function AppsRoute(props) { + return ( + <> + + + + > + ) } diff --git a/app/src/components/Archive.js b/app/src/components/Archive.js index 2adc29eb..cc2c3492 100644 --- a/app/src/components/Archive.js +++ b/app/src/components/Archive.js @@ -1,9 +1,17 @@ import React, {useState} from "react"; -import {Card, Confirm, Container, Icon, Image, Placeholder, Tab} from "semantic-ui-react"; -import Paginator, {ClearButton, ExternalLink, SearchInput, textEllipsis, uploadDate, WROLModeMessage} from "./Common"; +import {Card, Confirm, Container, Icon, Image, Placeholder} from "semantic-ui-react"; +import Paginator, { + ClearButton, + ExternalLink, + SearchInput, + TabLinks, + textEllipsis, + uploadDate, + WROLModeMessage +} from "./Common"; import {deleteArchive, postDownload, refreshArchives} from "../api"; import Button from "semantic-ui-react/dist/commonjs/elements/Button"; -import {Link, NavLink} from "react-router-dom"; +import {Link, Route} from "react-router-dom"; import {ArchivePlaceholder} from "./Placeholder"; import Table from "semantic-ui-react/dist/commonjs/collections/Table"; import {useArchives} from "../hooks/useArchives"; @@ -289,73 +297,16 @@ function Domains() { } -export class ArchiveRoute extends React.Component { - - constructor(props) { - super(props); - - this.panesTos = ['/archive', '/archive/domains', '/archive/manage']; - this.state = { - activeIndex: this.matchPaneTo(), - } - - this.archivePanes = [ - { - menuItem: { - as: NavLink, - content: 'Archives', - id: 'archive', - to: '/archive', - exact: true, - key: 'home', - }, - render: () => - }, - { - menuItem: { - as: NavLink, - content: 'Domains', - id: 'domains', - to: '/archive/domains', - exact: true, - key: 'domains', - }, - render: () => - }, - { - menuItem: { - as: NavLink, - content: 'Manage', - id: 'manage', - to: '/archive/manage', - exact: true, - key: 'manage', - }, - render: () => - }, - ]; - - } - - componentDidUpdate(prevProps, prevState, snapshot) { - if (prevProps.location.pathname !== this.props.location.pathname) { - this.setState({activeIndex: this.matchPaneTo()}); - } - } - - matchPaneTo = () => { - return this.panesTos.indexOf(this.props.location.pathname); - } - - render() { - return ( - - - - ) - } +export function ArchiveRoute(props) { + const links = [ + {text: 'Archives', to: '/archive', exact: true}, + {text: 'Domains', to: '/archive/domains'}, + {text: 'Manage', to: '/archive/manage'}, + ]; + return <> + + + + + > } diff --git a/app/src/components/Common.js b/app/src/components/Common.js index e50fae2f..da319da6 100644 --- a/app/src/components/Common.js +++ b/app/src/components/Common.js @@ -1,6 +1,6 @@ import React, {useEffect, useState} from "react"; -import {Card, Container, Form, Icon, Image, Input, Pagination, Progress} from 'semantic-ui-react'; -import {Link} from "react-router-dom"; +import {Card, Container, Form, Icon, Image, Input, Menu, Pagination, Progress} from 'semantic-ui-react'; +import {Link, NavLink} from "react-router-dom"; import Button from "semantic-ui-react/dist/commonjs/elements/Button"; import Grid from "semantic-ui-react/dist/commonjs/collections/Grid"; import Message from "semantic-ui-react/dist/commonjs/collections/Message"; @@ -635,3 +635,20 @@ export function textEllipsis(str, maxLength, {side = "end", ellipsis = "..."} = } return str; } + +export function TabLinks({links}) { + return ( + + {links.map((link) => + + {link.text} + + )} + + ) +} diff --git a/app/src/components/Files.js b/app/src/components/Files.js index d39226f6..9a2345bf 100644 --- a/app/src/components/Files.js +++ b/app/src/components/Files.js @@ -110,10 +110,8 @@ class Files extends React.Component { } } -export class FilesRoute extends React.Component { - render() { - return ( - - ); - } +export function FilesRoute(props) { + return ( + + ); } diff --git a/app/src/components/Inventory.js b/app/src/components/Inventory.js index b7d44d88..6ded9ded 100644 --- a/app/src/components/Inventory.js +++ b/app/src/components/Inventory.js @@ -13,11 +13,10 @@ import { updateInventory, updateItem } from "../api"; -import {Button, Checkbox, Dropdown, Form, Grid, Header, Portal, Segment, Tab, Table} from "semantic-ui-react"; +import {Button, Checkbox, Dropdown, Form, Grid, Header, Portal, Segment, Table} from "semantic-ui-react"; import {Route} from "react-router-dom"; -import Container from "semantic-ui-react/dist/commonjs/elements/Container"; import {toast} from 'react-semantic-toasts'; -import {arraysEqual, enumerate, replaceNullValues} from './Common'; +import {arraysEqual, enumerate, replaceNullValues, TabLinks} from './Common'; import _ from 'lodash'; class InventorySummary extends React.Component { @@ -1089,27 +1088,16 @@ class InventoryAddList extends React.Component { } } -class InventoryTab extends React.Component { - - render() { - let panes = [ - {menuItem: 'List', render: () => }, - {menuItem: 'Summary', render: () => }, - ]; - return ( - - ) - } -} - -export class InventoryRoute extends React.Component { - render() { - return ( - <> - - - - > - ) - } +export function InventoryRoute(props) { + const links = [ + {text: 'List', to: '/inventory', exact: true}, + {text: 'Summary', to: '/inventory/summary'}, + ]; + return ( + <> + + + + > + ) } diff --git a/app/src/components/Nav.js b/app/src/components/Nav.js index c93d8877..770605c1 100644 --- a/app/src/components/Nav.js +++ b/app/src/components/Nav.js @@ -5,17 +5,10 @@ import {Dropdown, Menu, Responsive} from "semantic-ui-react"; const responsiveWidth = 500; const links = [ - { - text: 'Videos', links: [ - {to: '/videos', text: 'Newest Videos', exact: true}, - {to: '/videos/favorites', text: 'Favorites', exact: true}, - {to: '/videos/channel', text: 'Channels', exact: true}, - {to: '/videos/manage', text: 'Manage', exact: true}, - ] - }, + {text: 'Videos', to: '/videos'}, {text: 'Inventory', to: '/inventory', exact: true}, - {text: 'Archive', to: '/archive', exact: true}, - {text: 'Files', to: '/files', exact: true}, + {text: 'Archive', to: '/archive'}, + {text: 'Files', to: '/files'}, { text: 'Apps', links: [ {to: '/apps/otp', text: 'One Time Pad', exact: true}, @@ -33,7 +26,7 @@ function DropdownLinks(props) { {props.link.links.map((l) => { return ( - + ) })} @@ -41,7 +34,8 @@ function DropdownLinks(props) { ) } -function Link(props) { +function MenuLink(props) { + // Wrapper around NavLink to handle Navlink/Dropdown change. let classes = 'item'; if (props.link.header) { classes = `${classes} header`; @@ -68,17 +62,17 @@ export function NavBar() { return ( {/*Always show WROLPi home button*/} - + {/*Show the links in a menu when on desktop*/} {links.map((link) => { return ( - + ) })} {rightLinks.map((link) => { - return () + return () })} @@ -87,7 +81,7 @@ export function NavBar() { {collapsedLinks.map((link) => { - return () + return () })} diff --git a/app/src/components/Videos.js b/app/src/components/Videos.js index 756536a0..bac210ed 100644 --- a/app/src/components/Videos.js +++ b/app/src/components/Videos.js @@ -1,5 +1,5 @@ import React from 'react'; -import {Link, Route} from "react-router-dom"; +import {NavLink, Route} from "react-router-dom"; import Paginator, { changePageHistory, DEFAULT_LIMIT, @@ -10,8 +10,10 @@ import Paginator, { scrollToTop, searchOrders, secondsToString, + TabLinks, VideoCards, - videoOrders, WROLModeMessage + videoOrders, + WROLModeMessage } from "./Common" import VideoPage from "./VideoPlayer"; import {download, getChannel, getStatistics, getVideo, refresh, searchVideos} from "../api"; @@ -27,8 +29,7 @@ import { Modal, Radio, Segment, - Statistic, - Tab + Statistic } from "semantic-ui-react"; import * as QueryString from 'query-string'; import Container from "semantic-ui-react/dist/commonjs/elements/Container"; @@ -65,7 +66,6 @@ class ManageVideos extends React.Component { return ( - Manage Videos }, - {menuItem: 'Statistics', render: () => }, - ] - return ( - - - - ) - } +function ManageTab(props) { + return ( + + + + + ) } -export class VideosRoute extends React.Component { - - render() { - return ( - <> - - - } - /> - - - }/> - - - - - - } - /> - - - > - ) +function Link(props) { + let classes = 'item'; + if (props.link.header) { + classes = `${classes} header`; } + + return ( + + {props.link.text} + + ) +} + +export function VideosRoute(props) { + + const links = [ + {text: 'Videos', to: '/videos', exact: true}, + {text: 'Favorites', to: '/videos/favorites'}, + {text: 'Channels', to: '/videos/channel',}, + {text: 'Manage', to: '/videos/manage'}, + ]; + + return ( + <> + + + + } + /> + + + }/> + + + + + + } + /> + + > + ) } class Statistics extends React.Component { diff --git a/modules/files/api.py b/modules/files/api.py index 72cee406..2d8a8cb6 100644 --- a/modules/files/api.py +++ b/modules/files/api.py @@ -14,6 +14,7 @@ bp = get_blueprint('Files', '/api/files') def paths_to_files(paths: List[pathlib.Path]): + """Convert Paths to what the React UI expects.""" media_directory = get_media_directory() new_files = [] for path in paths: diff --git a/modules/files/lib.py b/modules/files/lib.py index 438f17c6..6224be4e 100644 --- a/modules/files/lib.py +++ b/modules/files/lib.py @@ -51,9 +51,7 @@ def list_files(directories: List[str]) -> List[Path]: @wrol_mode_check def delete_file(file: str): - """ - Delete a file in the media directory. - """ + """Delete a file in the media directory.""" file = get_media_directory() / file if file.is_dir() or not file.is_file(): raise InvalidFile(f'Invalid file {file}')
}, - {menuItem: 'Statistics', render: () => }, - ] - return ( - - - - ) - } +function ManageTab(props) { + return ( + + + + + ) } -export class VideosRoute extends React.Component { - - render() { - return ( - <> - - - } - /> - - - }/> - - - - - - } - /> - - - > - ) +function Link(props) { + let classes = 'item'; + if (props.link.header) { + classes = `${classes} header`; } + + return ( + + {props.link.text} + + ) +} + +export function VideosRoute(props) { + + const links = [ + {text: 'Videos', to: '/videos', exact: true}, + {text: 'Favorites', to: '/videos/favorites'}, + {text: 'Channels', to: '/videos/channel',}, + {text: 'Manage', to: '/videos/manage'}, + ]; + + return ( + <> + + + + } + /> + + + }/> + + + + + + } + /> + + > + ) } class Statistics extends React.Component { diff --git a/modules/files/api.py b/modules/files/api.py index 72cee406..2d8a8cb6 100644 --- a/modules/files/api.py +++ b/modules/files/api.py @@ -14,6 +14,7 @@ bp = get_blueprint('Files', '/api/files') def paths_to_files(paths: List[pathlib.Path]): + """Convert Paths to what the React UI expects.""" media_directory = get_media_directory() new_files = [] for path in paths: diff --git a/modules/files/lib.py b/modules/files/lib.py index 438f17c6..6224be4e 100644 --- a/modules/files/lib.py +++ b/modules/files/lib.py @@ -51,9 +51,7 @@ def list_files(directories: List[str]) -> List[Path]: @wrol_mode_check def delete_file(file: str): - """ - Delete a file in the media directory. - """ + """Delete a file in the media directory.""" file = get_media_directory() / file if file.is_dir() or not file.is_file(): raise InvalidFile(f'Invalid file {file}')