refactor(plex): scope module state in PlexProvider

Lift the Plex module's shared local state (scope / watch-state / sort / expanded
filters / search term / open-playlist hand-off) out of App into a split-context
PlexProvider with stable useCallback setters. PlexSidebar, PlexBrowse, and the
header search box read it via usePlex()/usePlexActions() instead of props drilled
through App; PlexBrowse is now prop-free. App loses six useStates + the filters
JSON memo/serialise helpers. (Platform S6b.2)
This commit is contained in:
2026-07-18 23:53:10 +02:00
parent d0fc061795
commit 6c32e5c8d6
6 changed files with 148 additions and 98 deletions
+8 -25
View File
@@ -34,6 +34,7 @@ import { DetailCustomizeMenu, Filterable, useArtBackdrop, useDetailPrefs } from
import PlexPlaylistAdd, { type PlexAddTarget } from "./PlexPlaylistAdd";
import PlexCollectionEditor from "./PlexCollectionEditor";
import { PageToolbar } from "./PageShell";
import { usePlex, usePlexActions } from "./PlexProvider";
// Lazy: the rich player (pulls in hls.js) loads only when something is first played.
const PlexPlayer = lazy(() => import("./PlexPlayer"));
@@ -54,34 +55,16 @@ type Sub =
// (useHistorySubview) so browser Back returns to the grid. Playback lands in P2 — a card announces
// for now. Rendered by App for page==="plex" (its own nav module).
type Props = {
q: string;
scope: string; // movie | show | both (unified cross-library scope)
setScope: (v: string) => void;
show: string;
sort: string;
filters: PlexFilters;
setFilters: (f: PlexFilters) => void;
// The sidebar opens a playlist by setting this; PlexBrowse turns it into a history subview + clears it.
openPlaylist?: number | null;
onPlaylistOpened?: () => void;
};
const PAGE = 40;
export default function PlexBrowse({
q,
scope,
setScope,
show,
sort,
filters,
setFilters,
openPlaylist,
onPlaylistOpened,
}: Props) {
export default function PlexBrowse() {
const { t } = useTranslation();
const qc = useQueryClient();
// Scope / watch-state / sort / filters live in PlexProvider (edited mainly from PlexSidebar); the
// search term (q) comes from the shared header box; the rail hands off a playlist to open via the
// shared `playlistOpen` slot, which we consume into a history subview then clear.
const { q, scope, show, sort, filters, playlistOpen: openPlaylist } = usePlex();
const { setScope, setFilters, setPlaylistOpen } = usePlexActions();
const dq = useDebounced(q.trim(), 350);
const sub = useHistorySubview<Sub>({ kind: "grid" });
@@ -90,7 +73,7 @@ export default function PlexBrowse({
useEffect(() => {
if (openPlaylist != null) {
sub.open({ kind: "playlist", id: openPlaylist });
onPlaylistOpened?.();
setPlaylistOpen(null);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [openPlaylist]);