// The channel page's address-bar keys. The page opens over any page (`?chan=`, the shell's), so its own // keys are the shell's too: no module owns them, a page switch clears them with `chan`, and the page // under it writes none of ITS keys while the channel stands over it — the address names only what the // screen shows. The watch state is not here: it is an account preference that follows every channel // (`channelShow`). export const CHANNEL_PAGE_KEYS = ["chtab", "chsort", "chtypes"] as const; export type ChannelTab = "videos" | "about"; type ChannelContentTypes = { includeNormal: boolean; includeShorts: boolean; includeLive: boolean; }; export type ChannelPageView = { tab: ChannelTab; sort: string; types: ChannelContentTypes }; const DEFAULT_SORT = "newest"; // The content types as letters: n(ormal) s(horts) l(ive); "none" for an empty choice. const TYPE_LETTERS: [keyof ChannelContentTypes, string][] = [ ["includeNormal", "n"], ["includeShorts", "s"], ["includeLive", "l"], ]; /** The address-bar delta for the channel page's view; `null` clears every key (the page closed). The * content types are always written: a page opened fresh takes them from the feed, so a link without * them would reopen with whatever the feed of the browser opening it holds. */ export function channelPageParamUpdates(v: ChannelPageView | null): Record { if (v == null) return { chtab: null, chsort: null, chtypes: null }; const letters = TYPE_LETTERS.filter(([k]) => v.types[k]) .map(([, l]) => l) .join(""); return { chtab: v.tab === "about" ? "about" : null, chsort: v.sort && v.sort !== DEFAULT_SORT ? v.sort : null, chtypes: letters || "none", }; } /** The view a channel page's address names; what it leaves out comes from `fresh` (the defaults of a * page opened without a link: the videos tab, newest first, the feed's content types). */ export function channelPageViewFrom( params: URLSearchParams, fresh: ChannelPageView, ): ChannelPageView { const raw = params.get("chtypes"); const types = raw == null ? fresh.types : (Object.fromEntries( TYPE_LETTERS.map(([k, l]) => [k, raw !== "none" && raw.includes(l)]), ) as ChannelContentTypes); return { tab: params.get("chtab") === "about" ? "about" : fresh.tab, sort: params.get("chsort") || fresh.sort, types, }; }