Files
siftlode/frontend/src/lib/channelPageUrlState.ts
T
peter 91d36b3357 fix(channel): the channel page's address names the channel and its own view, not the page under it
Opening a channel kept every key of the page under it (`?q=music&sort=oldest&chan=…`, a sort the
channel did not show), and the channel's own tab, sort and content types lived in its state alone, so a
reload or a bookmark lost them. Now the channel's address is `channelUrl`: the shell's keys and `chan`,
shared by `openChannel` and the channel link's href. The page writes its view (`chtab`, `chsort`,
`chtypes`, lib/channelPageUrlState) and reopens it; these are shell keys, cleared by a page switch and
by closing it in place. While a channel stands over a page, no module store writes its keys (`urlPage`);
Back finds them on the page's own entry. A link written by an older build opens without the page's keys.
2026-10-26 00:19:02 +01:00

58 lines
2.3 KiB
TypeScript

// 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<string, string | null> {
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,
};
}