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:
@@ -0,0 +1,52 @@
|
||||
import { createContext, useContext } from "react";
|
||||
import type { Page } from "../lib/urlState";
|
||||
import type { ChannelView } from "./navigationInit";
|
||||
|
||||
// The navigation contexts + their hooks, split out of NavigationProvider so that file exports only
|
||||
// components (Fast Refresh can then hot-swap the provider without remounting the tree).
|
||||
//
|
||||
// TWO contexts on purpose: a STABLE actions context (setPage/openChannel/… never change reference)
|
||||
// and a CHANGING state context (page/channelView/ytSearch). Context consumers re-render whenever the
|
||||
// value changes, so deep memo'd leaves (VideoCard, PlayerModal) subscribe to actions ONLY and never
|
||||
// re-render on a page/channel switch — preserving the memo that keeps feed appends from re-rendering
|
||||
// every existing card.
|
||||
|
||||
export interface NavigationState {
|
||||
page: Page;
|
||||
/** The open channel page (null = none). Overlays `page`; rides in history.state._chan. */
|
||||
channelView: ChannelView | null;
|
||||
/** Live YouTube search term (null = normal feed). A feed sub-view; rides in history.state._yt. */
|
||||
ytSearch: string | null;
|
||||
}
|
||||
|
||||
export interface NavigationActions {
|
||||
/** Switch modules. Closes every open overlay first and pushes only once their history entries are
|
||||
* back (`lib/history.closeOverlaysThenNavigate`), so over an open player the push lands a moment
|
||||
* after the call; with nothing open it is synchronous. The requested page being already current
|
||||
* (with no channel page over it) pushes nothing — but the overlays still close. */
|
||||
setPage: (next: Page) => void;
|
||||
openChannel: (id: string, name?: string) => void;
|
||||
closeChannel: () => void;
|
||||
enterYtSearch: (q: string) => void;
|
||||
exitYtSearch: () => void;
|
||||
}
|
||||
|
||||
export const NavigationStateContext = createContext<NavigationState>({
|
||||
page: "feed",
|
||||
channelView: null,
|
||||
ytSearch: null,
|
||||
});
|
||||
export const NavigationActionsContext = createContext<NavigationActions>({
|
||||
setPage: () => {},
|
||||
openChannel: () => {},
|
||||
closeChannel: () => {},
|
||||
enterYtSearch: () => {},
|
||||
exitYtSearch: () => {},
|
||||
});
|
||||
|
||||
export function useNavigation(): NavigationState {
|
||||
return useContext(NavigationStateContext);
|
||||
}
|
||||
export function useNavigationActions(): NavigationActions {
|
||||
return useContext(NavigationActionsContext);
|
||||
}
|
||||
Reference in New Issue
Block a user