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
@@ -24,6 +24,7 @@ import {
} from "../../lib/channelsUrlState";
import type { FilterMap } from "../../lib/tableColumns";
import { useNavigation, useNavigationActions } from "../../features/navigation/navigationContext";
import { urlPage } from "../../features/navigation/urlPage";
import {
ChannelLayoutContext,
ChannelsActionsContext,
@@ -96,7 +97,8 @@ function readChannelsUrlSeed(): ChannelsUrlState | null {
}
export function ChannelsProvider({ children }: { children: ReactNode }) {
const { page } = useNavigation();
const nav = useNavigation();
const { page } = nav;
const { setPage } = useNavigationActions();
const [search, setSearchState] = useState("");
@@ -246,10 +248,11 @@ export function ChannelsProvider({ children }: { children: ReactNode }) {
);
// Mirror the content state (tab + sort + column filters) into the address bar under the namespaced
// keys so a manager view is shareable. Gated on page===channels: off the manager the keys are cleared
// keys so a manager view is shareable. Gated on urlPage===channels: off the manager (or under a channel
// page) the keys are cleared
// (localStorage still holds the state; it re-mirrors on return). The name `search` stays OUT — it's a
// deliberately transient rail-search, like the feed's and plex's.
useUrlMirror(channelsParamUpdates(page === "channels" ? { view, sort, filters } : null));
useUrlMirror(channelsParamUpdates(urlPage(nav) === "channels" ? { view, sort, filters } : null));
return (
<ChannelsActionsContext.Provider value={actions}>