main pages
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
import { useState } from 'react'
|
||||
import { useHasTmdbKey } from '../hooks/use-external'
|
||||
import LazyMount from '../components/ui/LazyMount'
|
||||
import { usePreferencesStore } from '../stores/preferences-store'
|
||||
import { regionForUser } from '../lib/format'
|
||||
import DiscoverFilters, {
|
||||
DEFAULT_FILTERS,
|
||||
hasAnyActiveFilters,
|
||||
type DiscoverFilterState,
|
||||
} from '../components/discover/DiscoverFilters'
|
||||
import TonightHero from '../components/discover/TonightHero'
|
||||
import { MoodChips, MoodRow } from '../components/discover/MoodChips'
|
||||
import Roulette from '../components/discover/Roulette'
|
||||
import DecadeStrip from '../components/discover/DecadeStrip'
|
||||
import CanonicalLists from '../components/discover/CanonicalLists'
|
||||
import OnThisDay from '../components/discover/OnThisDay'
|
||||
import LibraryGapFinder from '../components/discover/LibraryGapFinder'
|
||||
import {
|
||||
BrowseSection,
|
||||
genreTiles,
|
||||
languageTiles,
|
||||
studioTiles,
|
||||
networkTiles,
|
||||
type BrowseKey,
|
||||
} from '../components/discover/BrowseGrid'
|
||||
import { NoKey, Hero } from './discover/chrome'
|
||||
import { FilteredGrid } from './discover/filtered-grid'
|
||||
import {
|
||||
TrendingDayRow,
|
||||
TrendingWeekRow,
|
||||
PopularMoviesRow,
|
||||
PopularTvRow,
|
||||
UpcomingMoviesRow,
|
||||
TopRatedMoviesRow,
|
||||
TopRatedTvRow,
|
||||
CultClassicsRow,
|
||||
} from './discover/rows'
|
||||
|
||||
/**
|
||||
* Discover page - intent-driven entry to TMDB content the user doesn't
|
||||
* already have.
|
||||
*
|
||||
* Layout:
|
||||
* 1. Compact page header
|
||||
* 2. Sticky filter bar (movie/tv + advanced filters)
|
||||
* 3. Editorial spotlight - one big "pick of the day"
|
||||
* 4. Mood chips - 10 vibes that resolve to a single content row
|
||||
* 5. 3-4 curated rows (Trending / Acclaimed / Coming soon / etc.)
|
||||
* 6. Browse-by sections: genre, language, studio, network - compact
|
||||
* tile grids with inline expansion when a tile is clicked
|
||||
*
|
||||
* Active filters take over the page with the existing FilteredGrid - that
|
||||
* mode is unchanged so the filter workflow stays familiar.
|
||||
*/
|
||||
export default function DiscoverPage() {
|
||||
const hasTmdb = useHasTmdbKey()
|
||||
const prefs = usePreferencesStore()
|
||||
const region = prefs.region || regionForUser()
|
||||
const [filters, setFilters] = useState<DiscoverFilterState>(() => ({
|
||||
...DEFAULT_FILTERS,
|
||||
watchRegion: region,
|
||||
}))
|
||||
const [moodId, setMoodId] = useState<string | null>(null)
|
||||
const [decade, setDecade] = useState<string | null>(null)
|
||||
const [expanded, setExpanded] = useState<BrowseKey | null>(null)
|
||||
|
||||
if (!hasTmdb) return <NoKey />
|
||||
|
||||
const filterMode = hasAnyActiveFilters(filters)
|
||||
const kind = filters.kind
|
||||
|
||||
// Reset the mood / expansion when toggling movie<->tv since both are
|
||||
// kind-scoped and the previously-active mood may not exist on the
|
||||
// other side.
|
||||
function onFiltersChange(next: DiscoverFilterState) {
|
||||
if (next.kind !== filters.kind) {
|
||||
setMoodId(null)
|
||||
setDecade(null)
|
||||
setExpanded(null)
|
||||
}
|
||||
setFilters(next)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="pb-12" style={{ isolation: 'isolate' }}>
|
||||
<Hero />
|
||||
|
||||
<div
|
||||
className="sticky top-0 py-3 -mt-1 mb-3 border-b border-border/60"
|
||||
style={{ zIndex: 25, backgroundColor: 'var(--color-void)' }}
|
||||
>
|
||||
<DiscoverFilters filters={filters} onChange={onFiltersChange} region={region} />
|
||||
</div>
|
||||
|
||||
{filterMode ? (
|
||||
<FilteredGrid filters={filters} />
|
||||
) : (
|
||||
<>
|
||||
<LazyMount><TonightHero kind={kind} /></LazyMount>
|
||||
|
||||
<div className="px-7 mb-2 flex items-center justify-end">
|
||||
<Roulette kind={kind} moodId={moodId} />
|
||||
</div>
|
||||
|
||||
<MoodChips activeId={moodId} onChange={setMoodId} kind={kind} />
|
||||
|
||||
{moodId && (
|
||||
<div className="mb-4">
|
||||
<MoodRow moodId={moodId} kind={kind} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DecadeStrip kind={kind} active={decade} onChange={setDecade} />
|
||||
|
||||
<div className="relative z-10">
|
||||
{kind === 'movie' ? (
|
||||
<>
|
||||
<LazyMount><TrendingDayRow /></LazyMount>
|
||||
<LazyMount><PopularMoviesRow /></LazyMount>
|
||||
<LazyMount><UpcomingMoviesRow region={region} /></LazyMount>
|
||||
<LazyMount><TopRatedMoviesRow /></LazyMount>
|
||||
<LazyMount><CultClassicsRow /></LazyMount>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<LazyMount><TrendingWeekRow /></LazyMount>
|
||||
<LazyMount><PopularTvRow /></LazyMount>
|
||||
<LazyMount><TopRatedTvRow /></LazyMount>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{kind === 'movie' && <LazyMount><OnThisDay /></LazyMount>}
|
||||
|
||||
{kind === 'movie' && <LazyMount><LibraryGapFinder /></LazyMount>}
|
||||
|
||||
{kind === 'movie' && <LazyMount><CanonicalLists /></LazyMount>}
|
||||
|
||||
<div className="mt-4 mb-2 px-7">
|
||||
<div className="flex items-center gap-3 text-text-4">
|
||||
<span className="flex-1 h-px bg-border" />
|
||||
<span className="text-[10.5px] uppercase tracking-[0.2em] font-semibold">Browse by</span>
|
||||
<span className="flex-1 h-px bg-border" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<BrowseSection
|
||||
eyebrow="By genre"
|
||||
title="Genres"
|
||||
subtitle="Top of each genre, missing from your shelves"
|
||||
tiles={genreTiles()}
|
||||
expanded={expanded}
|
||||
onSelect={setExpanded}
|
||||
/>
|
||||
|
||||
{kind === 'movie' && (
|
||||
<BrowseSection
|
||||
eyebrow="By language"
|
||||
title="Around the world"
|
||||
subtitle="Top-rated cinema by original language"
|
||||
tiles={languageTiles()}
|
||||
expanded={expanded}
|
||||
onSelect={setExpanded}
|
||||
/>
|
||||
)}
|
||||
|
||||
{kind === 'movie' && (
|
||||
<BrowseSection
|
||||
eyebrow="By studio"
|
||||
title="From the studios"
|
||||
subtitle="Films grouped by who made them"
|
||||
tiles={studioTiles()}
|
||||
expanded={expanded}
|
||||
onSelect={setExpanded}
|
||||
/>
|
||||
)}
|
||||
|
||||
{kind === 'tv' && (
|
||||
<BrowseSection
|
||||
eyebrow="By network"
|
||||
title="On the networks"
|
||||
subtitle="Shows grouped by where they air"
|
||||
tiles={networkTiles()}
|
||||
expanded={expanded}
|
||||
onSelect={setExpanded}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user