diff --git a/app/src/components/Channels.js b/app/src/components/Channels.js index c6669b3e..44feaecf 100644 --- a/app/src/components/Channels.js +++ b/app/src/components/Channels.js @@ -1,6 +1,5 @@ import React, {useState} from "react"; import {useHotkeys} from "react-hotkeys-hook"; -import {Grid, StatisticLabel, StatisticValue,} from "semantic-ui-react"; import {createChannel, deleteChannel, refreshChannel, tagChannel, tagChannelInfo} from "../api"; import {CollectionTagModal} from "./collections/CollectionTagModal"; import {CollectionReorganizeModal} from "./collections/CollectionReorganizeModal"; @@ -18,11 +17,22 @@ import { WROLModeMessage } from "./Common"; import {Link, useNavigate, useParams} from "react-router"; -import Message from "semantic-ui-react/dist/commonjs/collections/Message"; import {useChannel, useChannels, useOneQuery} from "../hooks/customHooks"; import _ from "lodash"; -import {Button, Form, Header, Loader, Modal, Segment, Statistic} from "./Theme"; -import {toast} from "./ui"; +import { + Button, + Grid, + Group, + Header, + Loading, + Message, + Modal, + Panel, + Stack, + Statistic, + StatisticGroup, + toast, +} from "./ui"; import {RecurringDownloadsTable} from "./admin/Downloads"; import {InputForm, ToggleForm} from "../hooks/useForm"; import {ChannelDownloadForm, DestinationForm, DownloadTagsSelector} from "./Download"; @@ -58,29 +68,16 @@ function ChannelStatistics({statistics}) { return <> } - return + return
Statistics
- - {statistics.video_count} - Videos - - - {humanFileSize(statistics.size, true)} - Total Size - - - {humanFileSize(statistics.largest_video, true)} - Largest Video - - - {secondsToFullDuration(statistics.length)} - Total Duration - - - {humanNumber(statistics.video_tags)} - Video Tags - -
+ + + + + + + + } @@ -97,7 +94,7 @@ export function ChannelEditPage() { useTitle(_.isEmpty(channel) ? null : `${channel.name} Channel`); if (!form.ready) { - return ; + return ; } const handleRefreshChannel = async (e) => { @@ -169,7 +166,7 @@ export function ChannelEditPage() { }; const deleteButton = Delete; const refreshButton = setTagEditModalOpen(true)} - color='violet' + role='primary' style={{marginTop: '1em'}} >Tag; const reorganizeButton = ( setReorganizeModalOpen(true)} obeyWROLMode={true} @@ -222,8 +219,7 @@ export function ChannelEditPage() { {channel?.needs_reorganization && ( - - File Format Changed +

The file name format has changed. Click "Reorganize Files" to move existing files to match the new format. @@ -239,8 +235,8 @@ export function ChannelEditPage() { appliedTagName={channel?.tag_name} onSubmit={handleSubmit} > - - + + - - + + - - - - + + - - - {(channel.url || channel.rss_url) && - - + + {(channel.url || channel.rss_url) && + - - {channel.url && - -

URL
- {channel.url} - - } - {channel.rss_url && - -
RSS URL
- {channel.rss_url} -
-
} - + + {channel.url &&
+
URL
+ {channel.url} +
} + {channel.rss_url &&
+
RSS URL
+ {channel.rss_url} +
} +
- - } + } + {/* Tag Modal */} @@ -314,22 +302,14 @@ export function ChannelEditPage() { /> {/* Downloads Segment */} - - - - -
Downloads
-
- - - -
-
- setDownloadModalOpen(false)}> + + +
Downloads
+ +
+ setDownloadModalOpen(false)}>
New Channel Download
-
+ {channel && channel.statistics && } ; @@ -434,88 +414,74 @@ export function ChannelNewPage() { let messageRow; if (error || success) { - messageRow = - - {error && - } - {success && - } - - + messageRow = + {error && + {messageContent}} + {success && + {messageContent}} + } return <> - +
New Channel
-
e.preventDefault()} > - - - - - - - - - - - - - - + + + + + + + + + + {messageRow} - - - - - + + + + +
Save - - +
+
- -
+ + ; } @@ -534,37 +500,28 @@ export function ChannelsPage() { }, {enableOnFormTags: false}); // Header section matching DomainsPage pattern - const header =
- - - - setSearchStr('')} - onChange={setSearchStr} - onSubmit={null} - inputRef={searchInputRef} - /> - - - - - - - - -
; + const header = + setSearchStr('')} + onChange={setSearchStr} + onSubmit={null} + inputRef={searchInputRef} + /> + + + + ; // Empty state if (channels && channels.length === 0) { return <> {header} - - No channels exist yet! - Create one. + + Create one. ; } diff --git a/app/src/components/Playlists.js b/app/src/components/Playlists.js index 195e3529..158807e7 100644 --- a/app/src/components/Playlists.js +++ b/app/src/components/Playlists.js @@ -1,9 +1,19 @@ import React, {useEffect, useState} from "react"; import {Link, Route, Routes, useNavigate, useParams} from "react-router"; -import {Grid, Image, Input, Message} from "semantic-ui-react"; -import {toast} from "./ui"; - -import {Button, Form, Header, Icon, Loader, Modal, Segment, Table} from "./Theme"; +import {IconMapPin} from "@tabler/icons-react"; +import { + Button, + Group, + Header, + Icon, + Loading, + Message, + Modal, + Panel, + Table, + TextInput, + toast, +} from "./ui"; import { APIButton, BackButton, @@ -35,7 +45,6 @@ import { setPlaylistTag, updatePlaylist, } from "../api"; -import {ThemeContext} from "../contexts/contexts"; import {TagsSelector} from "../Tags"; @@ -119,39 +128,36 @@ export function PlaylistsPage() { } }; - const header =
- - - - setSearchStr('')} - onChange={setSearchStr} - onSubmit={null} - inputRef={searchInputRef} - /> - - - - - - -
; + const header = + setSearchStr('')} + onChange={setSearchStr} + onSubmit={null} + inputRef={searchInputRef} + /> + + ; const createModal = setModalOpen(false)} size='tiny'> New Playlist -
- - - setName(value)}/> - - -
- - + +
; @@ -185,22 +191,24 @@ export function PlaylistsPage() { } -// Icon (name + color) for a playlist item. File items use the FileGroup's mimetype/model icon -// and color (video, pdf, ebook, image, ...) so they match the rest of the UI; zim/url are fixed. -function itemIcon(item) { +// Icon for a playlist item. File items use FileGroup's mimetype/model icon and color (video, +// pdf, ebook, image, ...) so they match the rest of the UI; zim/url are fixed. +function ItemIcon({item}) { if (item.item_kind === 'file' && item.file_group) { const fg = item.file_group; const lowerPath = (fg.primary_path || '').toLowerCase(); - return {name: mimetypeIconName(fg.mimetype, lowerPath), color: mimetypeColor(fg.mimetype, lowerPath)}; + const name = mimetypeIconName(fg.mimetype, lowerPath); + const color = mimetypeColor(fg.mimetype, lowerPath); + return ; } - if (item.item_kind === 'zim') return {name: 'book'}; + if (item.item_kind === 'zim') return ; if (item.item_kind === 'url') { // A map-location URL (e.g. /map?lat=&lon=) gets a map marker; other URLs get the link icon. const u = (item.url || '').toLowerCase(); - if (u.startsWith('/map')) return {name: 'map marker alternate'}; - return {name: 'linkify'}; + if (u.startsWith('/map')) return ; + return ; } - return {name: 'file'}; + return ; } @@ -278,13 +286,13 @@ function ItemTitle({item, label}) { // The ordered items table, shared by the view and edit pages. When `editable`, the reorder/remove // action buttons (and their column) are shown; otherwise the table is read-only. function PlaylistItemsTable({items, editable, onMove, onRemove}) { - return + return
- # - Item + # + Item Title - {editable && } + {editable && } @@ -292,23 +300,23 @@ function PlaylistItemsTable({items, editable, onMove, onRemove}) { const label = itemLabel(item); const poster = item.item_kind === 'file' && item.file_group ? findPosterPath(item.file_group) : null; - return - {String(index + 1).padStart(2, '0')} - + return + {String(index + 1).padStart(2, '0')} + {poster - ? - : } + ? + : } - {editable && - ; @@ -504,47 +511,35 @@ export function PlaylistEditPage() { appliedTagName={playlist.tag_name} onSubmit={handleSave} > - - - - - setName(value)} - /> - - - - - - setDirectory(value || '')} - /> - - - - - - - - setDescription(value)} - /> - - - + setName(e.currentTarget.value)} + /> +
+ + setDirectory(value || '')} + /> +
+
+ setDescription(e.currentTarget.value)} + /> +
{/* Tag Modal */} @@ -559,34 +554,34 @@ export function PlaylistEditPage() { /> {/* Items Segment */} - +
Items
{items.length === 0 - ? - This playlist is empty - {editable && Add a link below.} + ? + {editable && 'Add a link below.'} : }
Add a link
-
- - - setUrl(value)}/> - - - setUrlTitle(value)}/> - - - - - - -
+
{ + e.preventDefault(); + handleAddUrl(); + }}> + + setUrl(e.currentTarget.value)}/> + setUrlTitle(e.currentTarget.value)}/> + + + + ; }