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.
This commit is contained in:
2026-10-26 00:19:02 +01:00
parent 9bd7304bb9
commit 91d36b3357
17 changed files with 565 additions and 46 deletions
+18 -1
View File
@@ -18,6 +18,7 @@ import { DEFAULT_LOGS_STATE, logsParamUpdates } from "./lib/logsUrlState";
import { FILES_KEYS } from "./lib/filesUrlState";
import { PLEX_SUB_URL } from "./modules/plex/plexSubUrl";
import { MODE_URL } from "./shell/welcomeMode";
import { CHANNEL_PAGE_KEYS, channelPageParamUpdates } from "./lib/channelPageUrlState";
const SRC = __dirname;
@@ -99,6 +100,22 @@ describe("module URL keys", () => {
expect(shell).toContain(MODE_URL.key);
});
// The channel page opens over any page, so no page's switch or stamp may clear its view's keys as
// its own: they are the shell's, and exactly what the page's encoder writes.
it("leave the channel page's keys to the shell", () => {
const shell: readonly string[] = SHELL_KEYS;
expect(CHANNEL_PAGE_KEYS.filter((k) => !shell.includes(k))).toEqual([]);
const written = channelPageParamUpdates({
tab: "about",
sort: "oldest",
types: { includeNormal: true, includeShorts: true, includeLive: true },
});
expect(Object.keys(written).sort()).toEqual([...CHANNEL_PAGE_KEYS].sort());
expect(Object.keys(channelPageParamUpdates(null)).sort()).toEqual(
[...CHANNEL_PAGE_KEYS].sort(),
);
});
it.each([...REGISTERED_PAGES])("are exactly what %s's encoders write", (page) => {
const declared = [...moduleDef(page).urlKeys].sort();
const writes = WRITES[page];
@@ -120,6 +137,6 @@ describe("module URL keys", () => {
it("the literal scan sees the call sites it is meant to (a regex that matched nothing would pass)", () => {
const seen = new Set(queryKeyLiterals().map(({ key }) => key));
for (const key of ["chan", "pl", "page", "rjob", "link"]) expect(seen).toContain(key);
for (const key of ["pl", "page", "rjob", "link"]) expect(seen).toContain(key);
});
});