feat(managers): clickable links in descriptions + real SVG country flags

- linkify: bare http(s) URLs inside channel descriptions become clickable links
  (new tab, rel=noopener noreferrer) — new lib/linkify.tsx, used in the shared
  ChannelAboutContent so both the About overlay and the channel detail page get it
- Country flag: the flag EMOJI never renders on Windows/Chrome (showed the small-caps
  'hu' fallback), so swap it for a real self-hosted SVG flag via the flag-icons package;
  applies to the About overlay + the channel detail page
This commit is contained in:
2026-07-19 20:42:28 +02:00
parent 8abb78b0bd
commit b741a8fd28
5 changed files with 53 additions and 8 deletions
+8 -8
View File
@@ -1,13 +1,9 @@
import { useTranslation } from "react-i18next";
import { ExternalLink } from "lucide-react";
import type { ChannelDetail } from "../lib/api";
import { linkify } from "../lib/linkify";
// --- About-tab metadata helpers (shared by the channel page and the manager's About overlay) ---
// ISO 3166-1 alpha-2 code → 🇺🇸 regional-indicator flag emoji.
function countryFlag(code: string): string {
if (!/^[A-Za-z]{2}$/.test(code)) return "";
return String.fromCodePoint(...[...code.toUpperCase()].map((c) => 0x1f1e6 + c.charCodeAt(0) - 65));
}
function displayName(code: string, lang: string, type: "region" | "language"): string {
try {
return new Intl.DisplayNames([lang], { type }).of(type === "region" ? code.toUpperCase() : code) ?? code;
@@ -51,7 +47,7 @@ export default function ChannelAboutContent({ ch }: { ch: ChannelDetail }) {
return (
<>
{ch.description ? (
<p className="text-sm whitespace-pre-wrap break-words leading-relaxed">{ch.description}</p>
<p className="text-sm whitespace-pre-wrap break-words leading-relaxed">{linkify(ch.description)}</p>
) : (
<p className="text-sm text-muted">{t("channel.noDescription")}</p>
)}
@@ -76,8 +72,12 @@ export default function ChannelAboutContent({ ch }: { ch: ChannelDetail }) {
{ch.country && (
<div className="flex gap-2">
<span className="text-muted w-24 shrink-0">{t("channel.country")}</span>
<span>
{countryFlag(ch.country)} {displayName(ch.country, i18n.language, "region")}
<span className="inline-flex items-center gap-2">
{/^[A-Za-z]{2}$/.test(ch.country) && (
// Real SVG flag (flag-icons) — Windows/Chrome don't render flag emoji.
<span className={`fi fi-${ch.country.toLowerCase()} rounded-sm shrink-0`} aria-hidden />
)}
{displayName(ch.country, i18n.language, "region")}
</span>
</div>
)}