diff --git a/src/App.tsx b/src/App.tsx index 3017b8a..b16da19 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -13,7 +13,6 @@ import HomePage from './pages/HomePage' import { useNewReleaseNotifications } from './hooks/use-new-releases' const LibraryPage = lazy(() => import('./pages/LibraryPage')) -const FoldersPage = lazy(() => import('./pages/FoldersPage')) const DetailPage = lazy(() => import('./pages/DetailPage')) const MusicPage = lazy(() => import('./pages/MusicPage')) const SearchPage = lazy(() => import('./pages/SearchPage')) @@ -234,7 +233,6 @@ export default function App() { } /> } /> } /> - } /> } /> } /> } /> diff --git a/src/components/layout/AppHeader.tsx b/src/components/layout/AppHeader.tsx index f45822a..e3cefd5 100644 --- a/src/components/layout/AppHeader.tsx +++ b/src/components/layout/AppHeader.tsx @@ -33,7 +33,6 @@ const TOP_LEVEL_PATHS = new Set([ '/', '/movies', '/shows', - '/folders', '/playlists', '/music', '/search', @@ -45,7 +44,6 @@ function pageTitleFor(pathname: string): string { if (pathname === '/') return 'Home' if (pathname === '/movies') return 'Movies' if (pathname === '/shows') return 'TV Shows' - if (pathname === '/folders') return 'Folders' if (pathname === '/playlists') return 'Playlists' if (pathname === '/music') return 'Music' if (pathname === '/search') return 'Search' diff --git a/src/components/layout/AppShell.tsx b/src/components/layout/AppShell.tsx index 8a865d0..2b79814 100644 --- a/src/components/layout/AppShell.tsx +++ b/src/components/layout/AppShell.tsx @@ -16,7 +16,6 @@ import { Activity, Radio, Download, - Folders, } from '../../lib/icons' import type { AuthState } from '../../api/types' import AppHeader from './AppHeader' @@ -55,7 +54,6 @@ const NAV_SECTIONS: NavSection[] = [ { to: '/', icon: Home, label: 'Home' }, { to: '/movies', icon: Film, label: 'Movies' }, { to: '/shows', icon: Tv, label: 'Shows' }, - { to: '/folders', icon: Folders, label: 'Folders' }, { to: '/playlists', icon: Playlists, label: 'Playlists' }, { to: '/live', icon: Radio, label: 'Live TV' }, { to: '/requests', icon: Database, label: 'Requests' }, diff --git a/src/pages/FoldersPage.tsx b/src/pages/FoldersPage.tsx deleted file mode 100644 index 1e27b9e..0000000 --- a/src/pages/FoldersPage.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import { useNavigate } from 'react-router-dom' -import { useLibraries, useLibraryItems } from '../hooks/use-jellyfin' -import { Folder } from '../lib/icons' -import type { BaseItemDto } from '../api/types' - -export default function FoldersPage() { - const { data: libraries, isLoading } = useLibraries() - const navigate = useNavigate() - const userLibraries = (libraries || []).filter(l => l.CollectionType) - - return ( -
-
-
- - Library -
-

- Folders -

-

- Browse libraries by folder. Each library section below shows the top-level folders Jellyfin scanned; click one to see only its items. -

-
- - {isLoading ? ( - - ) : userLibraries.length === 0 ? ( -

No libraries found.

- ) : ( -
- {userLibraries.map(library => ( - navigate(`/item/${id}`)} - /> - ))} -
- )} -
- ) -} - -function LibrarySection({ - library, - onOpen, -}: { - library: BaseItemDto - onOpen: (id: string) => void -}) { - const { data, isLoading } = useLibraryItems(library.Id, { - recursive: false, - sortBy: ['SortName'], - limit: 50, - }) - - const subfolders = (data?.Items || []).filter( - (it: BaseItemDto) => it.Id && (it.Type === 'CollectionFolder' || it.IsFolder), - ) - - return ( -
-
-

- {library.Name} -

- - {subfolders.length} {subfolders.length === 1 ? 'folder' : 'folders'} - -
- - {isLoading ? ( - - ) : subfolders.length === 0 ? ( -

- No subfolders in this library. Use Movies / TV Shows in the sidebar to browse everything. -

- ) : ( -
- {subfolders.map(folder => ( - onOpen(folder.Id!)} - /> - ))} -
- )} -
- ) -} - -function FolderCard({ - name, - count, - onClick, -}: { - name: string - count: number - onClick: () => void -}) { - return ( - - ) -} - -function FoldersSkeleton({ compact = false }: { compact?: boolean }) { - const count = compact ? 4 : 8 - return ( -
- {Array.from({ length: count }).map((_, i) => ( -
-
-
-
-
- ))} -
- ) -} diff --git a/src/pages/LibraryPage.tsx b/src/pages/LibraryPage.tsx index f67f290..2146cfd 100644 --- a/src/pages/LibraryPage.tsx +++ b/src/pages/LibraryPage.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState } from 'react' import { useNavigate } from 'react-router-dom' import { motion, AnimatePresence } from 'framer-motion' -import { ArrowDownAZ, Calendar, Shuffle, Film, Tv, Filter, X, Star } from '../lib/icons' +import { ArrowDownAZ, Calendar, Shuffle, Film, Tv, Filter, X, Star, Library } from '../lib/icons' import { useLibraries, useLibraryItems } from '../hooks/use-jellyfin' import PosterCard from '../components/ui/PosterCard' import Select, { type SelectOption } from '../components/ui/Select' @@ -59,6 +59,7 @@ export default function LibraryPage({ type }: Props) { const [onlyHdr, setOnlyHdr] = useState(false) const [surpriseOpen, setSurpriseOpen] = useState(false) const [saveOpen, setSaveOpen] = useState(false) + const [libraryFilter, setLibraryFilter] = useState('') // '' = all const [layoutMode, setLayoutMode] = useState<'grid' | 'map'>(() => { if (typeof window === 'undefined') return 'grid' return (localStorage.getItem(`lib_layout:${type}`) as 'grid' | 'map') || 'grid' @@ -96,9 +97,19 @@ export default function LibraryPage({ type }: Props) { const { data: libraries } = useLibraries() const collectionType = COLLECTION_TYPE_MAP[type] - const library = libraries?.find(l => l.CollectionType === collectionType) - const parentId = library?.Id + const matchingLibraries = useMemo( + () => (libraries || []).filter(l => l.CollectionType === collectionType), + [libraries, collectionType], + ) + const parentId = libraryFilter || undefined const includeItemTypes = ITEM_TYPE_MAP[type] + // If the previously selected library disappeared (e.g., the user + // removed it on the server), fall back to "all libraries". + useEffect(() => { + if (libraryFilter && !matchingLibraries.some(l => l.Id === libraryFilter)) { + setLibraryFilter('') + } + }, [libraryFilter, matchingLibraries]) const filtersForApi = useMemo(() => { const f: string[] = [] @@ -233,6 +244,28 @@ export default function LibraryPage({ type }: Props) { {/* Filters */}
+ {matchingLibraries.length > 1 && ( +