fix(channel): persist across reload, keep banner aspect, fix avatar z-order

- F5 on a channel page kept bouncing to the feed — the App init dropped history.state._chan
  like the (intentionally non-replayed) YouTube search. Now channelView restores from _chan on
  load and the stamp preserves it, so a reload stays on the channel page.
- The banner was stretched/cropped (bg-cover). Render it as an <img object-contain> with a
  capped height, letterboxed on the surface colour, so its real aspect ratio is kept.
- The round avatar's top was clipped by the banner: the banner's position:relative container
  painted above the static identity row. Give that row relative z-10 so the overlapping avatar
  sits on top.
This commit is contained in:
npeter83 2026-06-30 04:24:52 +02:00
parent e185e03c6c
commit b4a1fa079b
2 changed files with 22 additions and 10 deletions

View file

@ -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";