feat(plex): P1.5 — Plex as a nav module + integrated search + filter sidebar

Per user feedback: promote Plex from a hidden feed-Source option to a first-class
left-nav module, with its own filter sidebar and the shared top search box.

- Plex is now a nav-rail module (page='plex', gated on me.plex_enabled) — its own
  page → correct browser Back/Forward; removed the Feed Source-dropdown 'plex' hack.
- PlexSidebar.tsx: left filter column (library scope, watch-state for movies:
  all/unwatched/in_progress/watched, sort) — per-account persisted (App state).
- Header search box now also serves the Plex page (integrated search); the live
  YouTube-search escalation stays feed-only.
- PlexBrowse.tsx reworked: infinite scroll (IntersectionObserver, no 'Load more'),
  content-visibility for smoother long-list scroll, show drill-down rides history
  (useHistorySubview) so Back returns to the grid; dropped the stray 'YouTube feed'
  button on the show page.
- backend /browse gains a per-user watch-state filter (show=, movies only).
- plex i18n (navLabel + filter.*) en/hu/de.

Note: episode-title 'Episode N' on some shows (e.g. Westworld) is Plex-side
(unmatched show) — we mirror what Plex has; Match/Refresh in Plex + re-sync fixes it.
This commit is contained in:
npeter83 2026-07-05 03:29:20 +02:00
parent 62636319b1
commit 219a935121
14 changed files with 290 additions and 133 deletions

View file

@ -74,7 +74,6 @@ export default function Feed({
view,
canRead,
isDemo = false,
plexEnabled = false,
onOpenWizard,
ytSearch,
onYtSearch,
@ -87,8 +86,6 @@ export default function Feed({
view: "grid" | "list";
canRead: boolean;
isDemo?: boolean;
// When the optional Plex module is on, offer "Plex" as a feed source (App swaps to PlexBrowse).
plexEnabled?: boolean;
onOpenWizard: () => void;
// Live YouTube search: the active search term (null = normal feed). Entering a search and
// leaving it both step browser history (the search is a feed sub-view with its own history
@ -563,7 +560,6 @@ export default function Feed({
<option value="organic">{t("feed.source.organic")}</option>
<option value="all">{t("feed.source.all")}</option>
<option value="search">{t("feed.source.only")}</option>
{plexEnabled && <option value="plex">{t("feed.source.plex")}</option>}
</select>
</label>
</>