diff --git a/src/frontend/src/pages/ChannelDetail.tsx b/src/frontend/src/pages/ChannelDetail.tsx index e601e74..ccb0c2e 100644 --- a/src/frontend/src/pages/ChannelDetail.tsx +++ b/src/frontend/src/pages/ChannelDetail.tsx @@ -12,6 +12,7 @@ import { ExternalLink, Film, Grid3X3, + LayoutList, List, ListMusic, Loader, @@ -33,6 +34,7 @@ import { QualityLabel } from '../components/QualityLabel'; import { DownloadProgressBar } from '../components/DownloadProgressBar'; import { SkeletonChannelHeader, SkeletonTable } from '../components/Skeleton'; import { ContentCard } from '../components/ContentCard'; +import { ContentListItem } from '../components/ContentListItem'; import { Pagination } from '../components/Pagination'; import { Modal } from '../components/Modal'; import { useToast } from '../components/Toast'; @@ -121,8 +123,12 @@ export function ChannelDetail() { const [contentTypeFilter, setContentTypeFilter] = useState(''); const [sortKey, setSortKey] = useState(null); const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc'); - const [viewMode, setViewMode] = useState<'table' | 'card'>(() => { - try { return (localStorage.getItem('tubearr-content-view') as 'table' | 'card') || 'table'; } + const [viewMode, setViewMode] = useState<'table' | 'card' | 'list'>(() => { + try { + const stored = localStorage.getItem('tubearr-content-view'); + if (stored === 'table' || stored === 'card' || stored === 'list') return stored; + return 'table'; + } catch { return 'table'; } }); @@ -312,12 +318,9 @@ export function ChannelDetail() { setContentPage(1); }, []); - const handleViewToggle = useCallback(() => { - setViewMode((prev) => { - const next = prev === 'table' ? 'card' : 'table'; - try { localStorage.setItem('tubearr-content-view', next); } catch { /* ignore */ } - return next; - }); + const handleSetViewMode = useCallback((mode: 'table' | 'card' | 'list') => { + setViewMode(mode); + try { localStorage.setItem('tubearr-content-view', mode); } catch { /* ignore */ } }, []); const togglePlaylist = useCallback((id: number | 'uncategorized') => { @@ -685,6 +688,37 @@ export function ChannelDetail() { [selectedIds, toggleSelect, toggleMonitored, downloadContent], ); + const renderListView = useCallback( + (items: ContentItem[]) => ( +
+ {items.length === 0 ? ( +
+ No content found for this channel. +
+ ) : ( + items.map((item) => ( + toggleMonitored.mutate({ contentId: id, monitored })} + onDownload={(id) => downloadContent.mutate(id)} + /> + )) + )} +
+ ), + [selectedIds, toggleSelect, toggleMonitored, downloadContent], + ); + const renderPlaylistGroups = useCallback( (groups: { id: number | 'uncategorized'; title: string; items: ContentItem[] }[]) => (
@@ -726,13 +760,17 @@ export function ChannelDetail() { {group.items.length} - {isExpanded ? renderTable(group.items) : null} + {isExpanded ? ( + viewMode === 'card' ? renderCardGrid(group.items) : + viewMode === 'list' ? renderListView(group.items) : + renderTable(group.items) + ) : null}
); })} ), - [expandedPlaylists, togglePlaylist, renderTable], + [expandedPlaylists, togglePlaylist, renderTable, renderCardGrid, renderListView, viewMode], ); // ── Loading / Error states ── @@ -1324,27 +1362,76 @@ export function ChannelDetail() { - {/* View toggle */} - + + + + {contentError ? (