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.
This commit is contained in:
@@ -1,12 +1,4 @@
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import { useCallback, useEffect, useMemo, useState, type ReactNode } from "react";
|
||||
import { coercePlexFilters, EMPTY_PLEX_FILTERS, type PlexFilters } from "../lib/api";
|
||||
import { parseSortState, type SortState } from "../lib/columnSort";
|
||||
import { parsePlexField, type PlexSearchField } from "../lib/plexSearch";
|
||||
@@ -20,7 +12,14 @@ import {
|
||||
import { getAccountRaw, LS, setAccountRaw } from "../lib/storage";
|
||||
import { logFilterChange } from "../lib/filterLog";
|
||||
import { useUrlMirror } from "../lib/useUrlMirror";
|
||||
import { useNavigation } from "./NavigationProvider";
|
||||
import { useNavigation } from "./navigationContext";
|
||||
import {
|
||||
PlexActionsContext,
|
||||
PlexStateContext,
|
||||
type PlexActions,
|
||||
type PlexJumpTarget,
|
||||
type PlexState,
|
||||
} from "./plexContext";
|
||||
|
||||
// Owns the Plex module's shared local state — the left-rail library scope / watch-state / sort /
|
||||
// expanded filters, the header search term, and the "open this playlist" hand-off — lifted out of
|
||||
@@ -33,73 +32,8 @@ import { useNavigation } from "./NavigationProvider";
|
||||
// hand-off (the rail sets it, PlexBrowse consumes it into a history subview then clears it). The Plex
|
||||
// search term is deliberately its OWN state, NOT the feed's `filters.q`, so a Plex search never leaks
|
||||
// into (and is never greeted by a stale value from) the feed.
|
||||
|
||||
// A "jump straight to this title" hand-off: PlexBrowse opens a movie's info page (like the card's ⓘ),
|
||||
// a show's drill-down (like clicking a show card), or — for E24's Home hero, whose whole promise is a
|
||||
// single Play button — the PLAYER for a movie/episode rating_key. `play` exists because the player
|
||||
// lives inside this module with its lazy hls.js and its provider tree; mounting a second one on the
|
||||
// landing page to avoid one hand-off would be the expensive way round.
|
||||
export type PlexJumpTarget = {
|
||||
id: string;
|
||||
type: "movie" | "show" | "play";
|
||||
/** The jump ARRIVED WITH a page change — another module set this and sent the user here in one
|
||||
* gesture (the Home surface plays a Plex title). The opened sub-view then REPLACES the page entry
|
||||
* that hand-off just pushed, instead of stacking on top of it, so Back returns to where the user
|
||||
* actually came from rather than to a module root they never visited. Omitted for a jump made
|
||||
* from INSIDE the Plex module (the header typeahead), where the entry being replaced would be a
|
||||
* sub-view the user really was looking at. */
|
||||
replace?: boolean;
|
||||
};
|
||||
|
||||
type PlexState = {
|
||||
scope: string; // movie | show | both (unified cross-library scope)
|
||||
show: string;
|
||||
sort: SortState; // {key,dir}; healed against the scope's options at the point of use
|
||||
filters: PlexFilters;
|
||||
q: string;
|
||||
field: PlexSearchField; // search scope-pill; healed against `scope` at the point of use
|
||||
playlistOpen: number | null;
|
||||
jumpTo: PlexJumpTarget | null; // typeahead → grid hand-off ("open this title")
|
||||
};
|
||||
|
||||
interface PlexActions {
|
||||
setScope: (v: string) => void;
|
||||
setShow: (v: string) => void;
|
||||
setSort: (v: SortState) => void;
|
||||
setFilters: (f: PlexFilters) => void;
|
||||
setQ: (q: string) => void;
|
||||
setField: (f: PlexSearchField) => void;
|
||||
setPlaylistOpen: (id: number | null) => void;
|
||||
setJumpTo: (t: PlexJumpTarget | null) => void;
|
||||
}
|
||||
|
||||
const StateContext = createContext<PlexState>({
|
||||
scope: "both",
|
||||
show: "all",
|
||||
sort: PLEX_SORT_FALLBACK,
|
||||
filters: EMPTY_PLEX_FILTERS,
|
||||
q: "",
|
||||
field: "all",
|
||||
playlistOpen: null,
|
||||
jumpTo: null,
|
||||
});
|
||||
const ActionsContext = createContext<PlexActions>({
|
||||
setScope: () => {},
|
||||
setShow: () => {},
|
||||
setSort: () => {},
|
||||
setFilters: () => {},
|
||||
setQ: () => {},
|
||||
setField: () => {},
|
||||
setPlaylistOpen: () => {},
|
||||
setJumpTo: () => {},
|
||||
});
|
||||
|
||||
export function usePlex(): PlexState {
|
||||
return useContext(StateContext);
|
||||
}
|
||||
export function usePlexActions(): PlexActions {
|
||||
return useContext(ActionsContext);
|
||||
}
|
||||
//
|
||||
// The contexts, their value types and the hooks live in plexContext.ts.
|
||||
|
||||
// The per-account init readers, shared by the useState seeds and the shared-link seed's base.
|
||||
function readPlexScope(): string {
|
||||
@@ -268,8 +202,8 @@ export function PlexProvider({ children }: { children: ReactNode }) {
|
||||
useUrlMirror(plexParamUpdates(page === "plex" ? { scope, sort, show, filters } : null));
|
||||
|
||||
return (
|
||||
<ActionsContext.Provider value={actions}>
|
||||
<StateContext.Provider value={state}>{children}</StateContext.Provider>
|
||||
</ActionsContext.Provider>
|
||||
<PlexActionsContext.Provider value={actions}>
|
||||
<PlexStateContext.Provider value={state}>{children}</PlexStateContext.Provider>
|
||||
</PlexActionsContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user