Files
siftlode/frontend/src/components/PlexScopeToggle.tsx
T
peter c3d88d9b20 refactor: component files export only components; lint fails on a warning
The 43 react-refresh/only-export-components warnings the gate let through meant an edit to a
provider (or AnchoredPopover, Toaster, plexDetailUi, …) could not be hot-swapped: Fast Refresh
reloads a module that also exports hooks or helpers.

- Each provider's contexts, the types its hooks return and the hooks themselves live in a sibling
  context module (navigationContext.ts, channelsContext.ts, …); the provider file keeps the
  component. loadAccountFilters moved next to the feed filters' defaults (feedFiltersInit.ts),
  following navigationInit/playlistsInit.
- Helpers that sat in component files have their own modules: welcomeMode, downloadTitle,
  plexPlayable, channelSync, ui/anchoredPopoverHooks, ui/pageToolbarSlot, ui/pagerContent,
  ui/toastLevels, ui/overlayOrder, ui/tableAlign, lib/plexDetail; the cells of the channel columns
  and the registry's page wrappers are components files of their own (channelCells, modulePages).
- usePersistedTab, an alias of useAccountPersistedState, is gone; its callers use the original.

npm run lint is now eslint --max-warnings 0, so a warning fails the gate like an error does.
2026-09-17 02:59:09 +02:00

50 lines
2.1 KiB
TypeScript

import { useTranslation } from "react-i18next";
import SegmentedToggle from "./ui/SegmentedToggle";
import { usePlex, usePlexActions } from "./plexContext";
import { usePlexViewControls } from "./plexViewContext";
// The Plex "browse target" as a compact header control next to the search box: the three library
// scopes (both | movie | show, which browse TITLES) plus two extra entry points — Playlist and
// Collection — that switch the content area to a grid of the user's playlists / of the collections.
// The scopes read/write the shared provider state (so scope stays in sync everywhere); the two extra
// modes drive PlexBrowse's sub-view through the PlexViewBridge. Picking a scope while in a
// playlists/collections surface returns to the title grid at that scope.
const SCOPES = ["both", "movie", "show"] as const;
export default function PlexScopeToggle() {
const { t } = useTranslation();
const { scope } = usePlex();
const { setScope } = usePlexActions();
const ctl = usePlexViewControls();
const inPlaylists = ctl?.kind === "playlists";
const inCollections = ctl?.kind === "collections";
// The active segment: the browse-mode when in one, else the library scope.
const value = inPlaylists ? "playlists" : inCollections ? "collections" : scope;
const onChange = (v: string) => {
if (v === "playlists" || v === "collections") {
ctl?.open({ kind: v });
return;
}
setScope(v);
// Leaving the playlists/collections surface for a title scope → show the title grid.
if (inPlaylists || inCollections) ctl?.open({ kind: "grid" });
};
return (
<div className="pointer-events-auto shrink-0">
<SegmentedToggle
size="md"
ariaLabel={t("plex.filter.scope")}
options={[
...SCOPES.map((s) => ({ value: s, label: t(`plex.filter.scopeOpt.${s}`) })),
{ value: "playlists", label: t("plex.filter.scopeOpt.playlists") },
{ value: "collections", label: t("plex.filter.scopeOpt.collections") },
]}
value={value}
onChange={onChange}
/>
</div>
);
}