- Playlists: a link over a playlist the account does not hold plays alone; the rail's own fallback no longer counts as a pick that gives the player up. - The linked opener opens or clears nothing once the address names another player, and stops looking (and fetching its row) once it settled. - Files: a list the server's cap cut — the sorted or searched one too — keeps the link, with a notice of its own (EN/HU). - History: an in-place view rewrite (search refine, count, channel to channel, sub-view) drops a reloaded entry's overlay marks (replaceView); a landing on a channel opened from a search keeps the search; the Feed press on a search opens the plain feed. - Files/Playlists: an emptied search is written at once. - Client log: plf/cf (column text filters) are typed keys; the shell has none of its own. - Contract: writers outside lib/ take a literal or a lib/ function's result; every URL key is sorted typed or not; the writer regexes are held by a fixture; sub-views named; the bookmark net reads page tags. - Feed defaults in one constant; one live-search queue helper; Scheduler writes through the Activity codec; notRun copy (EN/HU) names Forward and a plain "Search YouTube" button.
70 lines
3.0 KiB
TypeScript
70 lines
3.0 KiB
TypeScript
import { createContext, useContext } from "react";
|
|
import type { Page } from "../../lib/urlState";
|
|
import { DEFAULT_YT_COUNT } from "../../lib/ytSearchUrlState";
|
|
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, named in the address (`yt`). */
|
|
ytSearch: string | null;
|
|
/** How many results the search gathers (`ytn`). */
|
|
ytCount: number;
|
|
/** The search on screen was asked for in this document (sent from the search box, or run again by
|
|
* a press), not only reopened by a reload, a link or Back. An API search runs only when asked. */
|
|
ytSearchAsked: boolean;
|
|
}
|
|
|
|
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.
|
|
*
|
|
* `then` runs once the navigation has happened (the push made, or found unnecessary) — for a
|
|
* request that belongs to the NEW entry, which over an open player does not exist yet at the
|
|
* call (`lib/homeIntent`). */
|
|
setPage: (next: Page, then?: () => void) => void;
|
|
openChannel: (id: string, name?: string) => void;
|
|
closeChannel: () => void;
|
|
enterYtSearch: (q: string) => void;
|
|
exitYtSearch: () => void;
|
|
/** Run the search on screen: the "Search YouTube" press. */
|
|
askYtSearch: () => void;
|
|
setYtCount: (n: number) => void;
|
|
}
|
|
|
|
export const NavigationStateContext = createContext<NavigationState>({
|
|
page: "feed",
|
|
channelView: null,
|
|
ytSearch: null,
|
|
ytCount: DEFAULT_YT_COUNT,
|
|
ytSearchAsked: false,
|
|
});
|
|
export const NavigationActionsContext = createContext<NavigationActions>({
|
|
setPage: () => {},
|
|
openChannel: () => {},
|
|
closeChannel: () => {},
|
|
enterYtSearch: () => {},
|
|
exitYtSearch: () => {},
|
|
askYtSearch: () => {},
|
|
setYtCount: () => {},
|
|
});
|
|
|
|
export function useNavigation(): NavigationState {
|
|
return useContext(NavigationStateContext);
|
|
}
|
|
export function useNavigationActions(): NavigationActions {
|
|
return useContext(NavigationActionsContext);
|
|
}
|