diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 5c4b503..882b168 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -145,7 +145,12 @@ export default function App() { // browser-history entry (history.state._chan = channel id, _chanName = a display name to show // before the detail loads), so Back closes the channel page (after any player opened over it) // and reload returns to the normal app. Channels/videos are reached by id, so it's not in the URL. - const [channelView, setChannelView] = useState<{ id: string; name?: string } | null>(null); + const [channelView, setChannelView] = useState<{ id: string; name?: string } | null>(() => { + // Restore the open channel page across a reload (it rides in history.state, which survives F5), + // unlike the YouTube search which is intentionally dropped (it would re-spend quota). + const c = window.history.state?._chan as string | undefined; + return c ? { id: c, name: (window.history.state?._chanName as string) ?? undefined } : null; + }); const openChannel = useCallback((id: string, name?: string) => { setChannelView({ id, name }); const st = window.history.state || {}; @@ -264,8 +269,9 @@ export default function App() { useEffect(() => { // Drop any stale _yt from a prior session (a reload starts on the normal feed; ytSearch // begins null), so the first Back doesn't resurrect a search we're no longer showing. - const { _yt: _staleYt, _chan: _staleChan, _chanName: _staleChanName, ...rest } = - window.history.state || {}; + // Drop a stale _yt (a reload starts on the normal feed; a search would re-spend quota), but + // KEEP _chan/_chanName so the open channel page survives F5 (channelView restores from it above). + const { _yt: _staleYt, ...rest } = window.history.state || {}; window.history.replaceState({ ...rest, sfPage: page }, ""); function onPop(e: PopStateEvent) { const p = (e.state?.sfPage as Page) ?? "feed"; diff --git a/frontend/src/components/ChannelPage.tsx b/frontend/src/components/ChannelPage.tsx index baa1448..25906c7 100644 --- a/frontend/src/components/ChannelPage.tsx +++ b/frontend/src/components/ChannelPage.tsx @@ -133,16 +133,21 @@ export default function ChannelPage({ {/* Banner + back */}
{ch?.banner_url ? ( -
+ // Keep the banner's real aspect ratio (object-contain, capped height); letterbox with + // the surface colour rather than stretching/cropping it to fill. +
+ +
) : (
)}
- {/* Identity + actions */} -
+ {/* Identity + actions. relative+z-10 so the avatar that overlaps up into the banner + paints ABOVE it (the banner's positioned container would otherwise cover it). */} +