feat(plex): move the Discover/Browse toggle into the header

The Discover ⟷ Browse switch now sits in the header next to the library-scope
pill (All / Movies / Shows), instead of on the chip row / atop the Discover
surface.

It's driven through a small one-way PlexViewBridge: PlexBrowse still OWNS the
sub-view (useHistorySubview) and publishes its top-level view + opener to the
bridge; the always-mounted Header reads it to render PlexViewToggle. A global
sub-view provider was deliberately avoided — PlexBrowse's hook remounts on every
plex entry and re-seeds from history (a page switch clears ?v + drops _sub), so
each entry starts at the Discover root; lifting it out would let the view go
stale across navigation. The toggle hides on deeper views (show/info/person/…)
and on non-plex pages (bridge cleared on unmount). sortView is just SortControl
now; the grid toolbar no longer carries the toggle.

Verified: toggle in the header, switches discover⟷grid, hides on info + on Feed,
and Feed→Plex resets to Discover (no stale view); no console errors.
This commit is contained in:
2026-08-22 18:02:55 +02:00
parent 9798e506cf
commit f85dc12819
5 changed files with 99 additions and 41 deletions
@@ -0,0 +1,33 @@
import { createContext, useContext, useState, type ReactNode } from "react";
import { type Sub } from "./plexSubUrl";
// A one-way bridge so the always-mounted Header can host the Plex Discover ⟷ Browse toggle even though
// the sub-view state lives in (and is owned by) the plex-only PlexBrowse. PlexBrowse PUBLISHES its
// current top-level view + opener here (on mount and on view change) and clears it on unmount; the
// Header READS it to render the toggle beside the library-scope pill.
//
// Why a bridge and not a global sub-view provider: PlexBrowse's `useHistorySubview` REMOUNTS every time
// you enter the plex page, re-seeding the view from history (a page switch clears ?v + drops _sub, so
// each plex entry starts at the Discover root). Lifting that hook to an always-mounted provider would
// lose the remount and let the view go stale across navigation. Keeping it in PlexBrowse preserves the
// reseed; the bridge only mirrors the small slice the header needs.
export type PlexViewControls = {
isDiscover: boolean; // the Discover landing vs the flat Browse grid
toggleable: boolean; // only discover|grid host the toggle; deeper views (show/info/person/…) hide it
open: (v: Sub) => void;
};
const ReadCtx = createContext<PlexViewControls | null>(null);
const PublishCtx = createContext<(c: PlexViewControls | null) => void>(() => {});
export function PlexViewBridge({ children }: { children: ReactNode }) {
const [controls, setControls] = useState<PlexViewControls | null>(null);
return (
<PublishCtx.Provider value={setControls}>
<ReadCtx.Provider value={controls}>{children}</ReadCtx.Provider>
</PublishCtx.Provider>
);
}
export const usePlexViewControls = () => useContext(ReadCtx);
export const usePublishPlexView = () => useContext(PublishCtx);