Files
siftlode/frontend/src/features/video/ChannelNameLink.tsx
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

53 lines
2.3 KiB
TypeScript

import { forwardRef, type AnchorHTMLAttributes } from "react";
import { useTranslation } from "react-i18next";
import { isPlainLeftClick } from "../../lib/nav";
import { channelUrl } from "../../lib/urlState";
// A channel name that is a REAL link. The player showed it as a `<button>`, so the browser's own
// gestures did nothing on it: middle-click and Ctrl/Cmd-click opened no tab, on a thing that looks
// exactly like a link (the YouTube icon right next to it IS an anchor and does open one). The video
// card's channel name had the same `<button>`, so it renders through here too.
//
// The href is the same URL `openChannel` pushes (`channelUrl`: the channel over the current page,
// without the page's own keys) — so a new tab lands where the in-app navigation would have. A plain left click is still handled in-app, because
// the SPA path does more than navigate (the player has to close first and reclaim its own
// Back-to-close history entry; see lib/history.closeOverlaysThenNavigate).
type Props = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href" | "onClick" | "title"> & {
channelId: string;
title: string | null | undefined;
/** The in-app navigation, run only for a plain left click. */
onOpenInApp: () => void;
/** The native tooltip (`title` is taken by the channel's name). */
tooltip?: string;
/** Keep EVERY click on the link — the browser's own gestures too — from reaching a clickable
* ancestor (the video card's row). */
stopPropagation?: boolean;
};
const ChannelNameLink = forwardRef<HTMLAnchorElement, Props>(function ChannelNameLink(
{ channelId, title, onOpenInApp, tooltip, stopPropagation, ...rest },
ref,
) {
const { t } = useTranslation();
return (
<a
{...rest}
ref={ref}
href={channelUrl(channelId)}
title={tooltip}
onClick={(e) => {
if (stopPropagation) e.stopPropagation();
if (!isPlainLeftClick(e)) return; // let the browser open its tab/window
e.preventDefault();
onOpenInApp();
}}
>
{/* A channel first seen through a live search can have no title yet. An empty anchor would
still be in the tab order, announced by nothing but its href. */}
{title || t("player.channel")}
</a>
);
});
export default ChannelNameLink;