From fec513b6299fa91fb18b8006d4ae53caa09eee0d Mon Sep 17 00:00:00 2001 From: Frank Stellmacher <171614930+Psychotoxical@users.noreply.github.com> Date: Sat, 9 May 2026 18:11:22 +0200 Subject: [PATCH] fix(home): swap Because-you-listened rail to AlbumRow under 696 px (#520) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(home): swap Because-you-listened rail to AlbumRow under 696 px The hero-style BecauseCards are tuned for full-rail widths (3 cards at 1052 px+, 2 cards at 696-1051 px). Below that the cards stretched full-width with a fixed 160 px cover stuck on the left and centred text floating in a wide empty area — looked like three over-sized banners stacked vertically instead of a compact recommendation rail. A `ResizeObserver` on the rail wrapper now watches the container width and below 696 px renders a standard `AlbumRow` (which is already perf-tuned for narrow rails: artwork budget, viewport windowing, scroll paging). Wide layouts keep the unchanged hero card layout, so the mainstage view at full width is identical to before. * docs(changelog): add Because-you-listened narrow-layout fix entry --- CHANGELOG.md | 8 ++++ src/components/BecauseYouLikeRail.tsx | 67 +++++++++++++++++++-------- src/styles/components.css | 2 +- 3 files changed, 57 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcb8653b..97dcfcbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -211,6 +211,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Fixed +### Home — Because-you-listened rail compact in narrow layouts + +**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#520](https://github.com/Psychotoxical/psysonic/pull/520)** + +* When the rail container drops below the 2-card threshold (≈ 696 px — sidebar + queue both open, mini, etc.), the home **Because-you-listened** section now switches to the standard `AlbumRow` layout instead of stretching the hero-style cards to full width. The narrow path inherits the rail's existing perf tuning (artwork budget, viewport windowing, scroll paging). +* Wide layouts (>= 696 px) keep the existing 3-up hero cards with the "Similar to X" pill, album metadata, and album release-type pills — full-screen view is unchanged. +* Detection runs through a single `ResizeObserver` on the rail wrapper. The wide path adds zero extra renders. + ### Security — Tauri patch for IPC origin-confusion (GHSA-7gmj-67g7-phm9) **By [@Psychotoxical](https://github.com/Psychotoxical), PR [#509](https://github.com/Psychotoxical/psysonic/pull/509)** diff --git a/src/components/BecauseYouLikeRail.tsx b/src/components/BecauseYouLikeRail.tsx index 1bb1d40e..95c7a5c3 100644 --- a/src/components/BecauseYouLikeRail.tsx +++ b/src/components/BecauseYouLikeRail.tsx @@ -1,4 +1,4 @@ -import React, { memo, useEffect, useMemo, useState } from 'react'; +import React, { memo, useEffect, useMemo, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Play, ListPlus, Music } from 'lucide-react'; @@ -14,6 +14,7 @@ import CachedImage, { useCachedUrl } from './CachedImage'; import { usePlayerStore, songToTrack } from '../store/playerStore'; import { useAuthStore } from '../store/authStore'; import { playAlbum } from '../utils/playAlbum'; +import AlbumRow from './AlbumRow'; const ANCHOR_HISTORY_KEY_PREFIX = 'psysonic_because_anchor_history:'; const PICKS_HISTORY_KEY_PREFIX = 'psysonic_because_picks:'; @@ -125,6 +126,24 @@ export default function BecauseYouLikeRail({ ); const [anchor, setAnchor] = useState(null); const [recs, setRecs] = useState([]); + const containerRef = useRef(null); + const [narrow, setNarrow] = useState(false); + + // 696px ≙ exactly 2 BecauseCards side-by-side (2*340 + 16 gap). Below that + // the hero-style cards stretch full-width and dwarf the rest of the page, + // so we swap in a standard AlbumRow which is already perf-tuned for narrow + // rails (artwork budget, viewport windowing, scroll-paging). + useEffect(() => { + const el = containerRef.current; + if (!el) return; + const ro = new ResizeObserver(entries => { + for (const entry of entries) { + setNarrow(entry.contentRect.width < 696); + } + }); + ro.observe(el); + return () => ro.disconnect(); + }, []); useEffect(() => { let cancelled = false; @@ -208,26 +227,36 @@ export default function BecauseYouLikeRail({ return () => { cancelled = true; }; }, [pool, activeServerId]); - if (!anchor || recs.length === 0) return null; + if (!anchor || recs.length === 0) { + return
; + } + + const sectionTitle = t('home.becauseYouLikeFor', { artist: anchor.name }); return ( -
-
-

- {t('home.becauseYouLikeFor', { artist: anchor.name })} -

-
-
- {recs.map(album => ( - - ))} -
-
+
+ {narrow ? ( + + ) : ( +
+
+

+ {sectionTitle} +

+
+
+ {recs.map(album => ( + + ))} +
+
+ )} +
); } diff --git a/src/styles/components.css b/src/styles/components.css index 259c3016..db186555 100644 --- a/src/styles/components.css +++ b/src/styles/components.css @@ -13916,7 +13916,7 @@ html[data-perf-disable-home-artwork-clip="true"] .home-lite-artwork .song-card-c /* Hide the 3rd card only inside the 2-column range, so: >= 1052px (3 cols) -> 3 cards in one row 696-1051px (2 cols) -> 2 cards in one row, orphan dropped - < 696px (1 col) -> 3 cards stacked, orphan welcome + Below 696px the rail switches to a standard AlbumRow in JS — no layout here. Math: 3*340 + 2*16 = 1052 (3-col cutoff), 2*340 + 1*16 = 696 (2-col cutoff). */ @container (min-width: 696px) and (max-width: 1051px) { .because-card-grid > :nth-child(n+3) {