polish(channel): compact header — stats inline, slimmer banner
- Move the channel stats (subscribers / videos / views / joined) onto one meta line beside the handle under the name, dropping the separate stats row. - Cap the banner at 150px tall so it no longer dominates the page; the wide YouTube-style crop is kept. Net: the video grid starts much higher.
This commit is contained in:
parent
c59b53a51b
commit
50a7774588
1 changed files with 21 additions and 16 deletions
|
|
@ -123,6 +123,14 @@ export default function ChannelPage({
|
||||||
const joined = ch?.published_at
|
const joined = ch?.published_at
|
||||||
? new Date(ch.published_at).toLocaleDateString(i18n.language, { year: "numeric", month: "short" })
|
? new Date(ch.published_at).toLocaleDateString(i18n.language, { year: "numeric", month: "short" })
|
||||||
: null;
|
: null;
|
||||||
|
// Handle + stats on one compact meta line under the name (saves the separate stats row).
|
||||||
|
const metaParts = [
|
||||||
|
ch?.handle ? `@${ch.handle.replace(/^@/, "")}` : null,
|
||||||
|
ch?.subscriber_count != null ? t("channel.subscribers", { formatted: formatViews(ch.subscriber_count) }) : null,
|
||||||
|
ch?.video_count != null ? t("channel.videoCount", { count: ch.video_count }) : null,
|
||||||
|
ch?.total_view_count != null ? t("channel.totalViews", { formatted: formatViews(ch.total_view_count) }) : null,
|
||||||
|
joined ? t("channel.joined", { date: joined }) : null,
|
||||||
|
].filter(Boolean) as string[];
|
||||||
const tabClass = (active: boolean) =>
|
const tabClass = (active: boolean) =>
|
||||||
active
|
active
|
||||||
? "pb-2 text-sm font-medium border-b-2 border-fg text-fg"
|
? "pb-2 text-sm font-medium border-b-2 border-fg text-fg"
|
||||||
|
|
@ -136,7 +144,10 @@ export default function ChannelPage({
|
||||||
// Match YouTube's banner: the stored bannerExternalUrl is the full 16:9 template at a
|
// Match YouTube's banner: the stored bannerExternalUrl is the full 16:9 template at a
|
||||||
// low default size, so request a crisp wide version (=w1707) and object-cover the
|
// low default size, so request a crisp wide version (=w1707) and object-cover the
|
||||||
// desktop "safe area" — the centre 2560×423 (~6:1) band — at full width.
|
// desktop "safe area" — the centre 2560×423 (~6:1) band — at full width.
|
||||||
<div className="w-full overflow-hidden bg-surface" style={{ aspectRatio: "2560 / 423" }}>
|
<div
|
||||||
|
className="w-full overflow-hidden bg-surface"
|
||||||
|
style={{ aspectRatio: "2560 / 423", maxHeight: "150px" }}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
src={`${ch.banner_url}=w1707`}
|
src={`${ch.banner_url}=w1707`}
|
||||||
alt=""
|
alt=""
|
||||||
|
|
@ -173,9 +184,14 @@ export default function ChannelPage({
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{ch?.handle && (
|
<div className="text-sm text-muted mt-0.5 flex flex-wrap gap-x-1.5 gap-y-0.5">
|
||||||
<div className="text-sm text-muted truncate">@{ch.handle.replace(/^@/, "")}</div>
|
{metaParts.map((part, i) => (
|
||||||
)}
|
<span key={i}>
|
||||||
|
{i > 0 && <span className="mr-1.5">·</span>}
|
||||||
|
{part}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 shrink-0 pb-1">
|
<div className="flex items-center gap-2 shrink-0 pb-1">
|
||||||
<a
|
<a
|
||||||
|
|
@ -212,17 +228,6 @@ export default function ChannelPage({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stats */}
|
|
||||||
<div className="flex flex-wrap items-center gap-x-2.5 gap-y-1 text-sm text-muted mt-3">
|
|
||||||
{ch?.subscriber_count != null && (
|
|
||||||
<span>{t("channel.subscribers", { formatted: formatViews(ch.subscriber_count) })}</span>
|
|
||||||
)}
|
|
||||||
{ch?.video_count != null && <span>· {t("channel.videoCount", { count: ch.video_count })}</span>}
|
|
||||||
{ch?.total_view_count != null && (
|
|
||||||
<span>· {t("channel.totalViews", { formatted: formatViews(ch.total_view_count) })}</span>
|
|
||||||
)}
|
|
||||||
{joined && <span>· {t("channel.joined", { date: joined })}</span>}
|
|
||||||
</div>
|
|
||||||
{exploring && (
|
{exploring && (
|
||||||
<div className="flex items-center gap-2 text-xs text-muted mt-2">
|
<div className="flex items-center gap-2 text-xs text-muted mt-2">
|
||||||
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
||||||
|
|
@ -231,7 +236,7 @@ export default function ChannelPage({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Tabs */}
|
{/* Tabs */}
|
||||||
<div className="flex items-center gap-4 mt-4 border-b border-border">
|
<div className="flex items-center gap-4 mt-3 border-b border-border">
|
||||||
<button onClick={() => setTab("videos")} className={tabClass(tab === "videos")}>
|
<button onClick={() => setTab("videos")} className={tabClass(tab === "videos")}>
|
||||||
{t("channel.tabVideos")}
|
{t("channel.tabVideos")}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue