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 &&
-
-
- {channel.url}
-
- }
- {channel.rss_url &&
-
-
- {channel.rss_url}
-
- }
-
+
+ {channel.url && }
+ {channel.rss_url && }
+
-
- }
+ }
+
{/* Tag Modal */}
@@ -314,22 +302,14 @@ export function ChannelEditPage() {
/>
{/* Downloads Segment */}
-
-
-
-
-
-
-
-
-
-
-
- setDownloadModalOpen(false)}>
+
+
+
+
+
+ setDownloadModalOpen(false)}>
-
+
{channel && channel.statistics && }
>;
@@ -434,88 +414,74 @@ export function ChannelNewPage() {
let messageRow;
if (error || success) {
- messageRow =
-
- {error &&
- }
- {success &&
- }
-
-
+ messageRow =
+ {error &&
+ {messageContent}}
+ {success &&
+ {messageContent}}
+
}
return <>
-
+
-
-
+
+
>;
}
@@ -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 &&
- }
;
@@ -323,7 +331,6 @@ function PlaylistItemsTable({items, editable, onMove, onRemove}) {
export function PlaylistViewPage() {
const {playlistId} = useParams();
const {playlist} = usePlaylist(playlistId);
- const {t} = React.useContext(ThemeContext);
useTitle(playlist && playlist.name ? `${playlist.name} Playlist` : 'Playlist');
@@ -331,7 +338,7 @@ export function PlaylistViewPage() {
return Could not fetch playlist;
}
if (playlist === null) {
- return ;
+ return ;
}
const items = playlist.items || [];
@@ -345,10 +352,10 @@ export function PlaylistViewPage() {
- {playlist.description && {playlist.description}
}
+ {playlist.description && {playlist.description}
}
{items.length === 0
- ? This playlist is empty
+ ?
: }
>;
}
@@ -385,7 +392,7 @@ export function PlaylistEditPage() {
return Could not fetch playlist;
}
if (playlist === null) {
- return ;
+ return ;
}
const items = playlist.items || [];
@@ -463,7 +470,7 @@ export function PlaylistEditPage() {
};
const deleteButton = setTagModalOpen(true)}
- color='violet'
+ role='primary'
disabled={!editable}
style={{marginTop: '1em'}}
>Tag;
@@ -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.length === 0
- ?
- This playlist is empty
- {editable && Add a link below.}
+ ?
+ {editable && 'Add a link below.'}
: }
-
-
- setUrl(value)}/>
-
-
- setUrlTitle(value)}/>
-
-
- Add
-
-
-
-
+
+
>;
}