mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
fix(home): swap Because-you-listened rail to AlbumRow under 696 px (#520)
* 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
This commit is contained in:
committed by
GitHub
parent
268086ac74
commit
fec513b629
@@ -211,6 +211,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## Fixed
|
## 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)
|
### 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)**
|
**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#509](https://github.com/Psychotoxical/psysonic/pull/509)**
|
||||||
|
|||||||
@@ -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 { useNavigate } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Play, ListPlus, Music } from 'lucide-react';
|
import { Play, ListPlus, Music } from 'lucide-react';
|
||||||
@@ -14,6 +14,7 @@ import CachedImage, { useCachedUrl } from './CachedImage';
|
|||||||
import { usePlayerStore, songToTrack } from '../store/playerStore';
|
import { usePlayerStore, songToTrack } from '../store/playerStore';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { playAlbum } from '../utils/playAlbum';
|
import { playAlbum } from '../utils/playAlbum';
|
||||||
|
import AlbumRow from './AlbumRow';
|
||||||
|
|
||||||
const ANCHOR_HISTORY_KEY_PREFIX = 'psysonic_because_anchor_history:';
|
const ANCHOR_HISTORY_KEY_PREFIX = 'psysonic_because_anchor_history:';
|
||||||
const PICKS_HISTORY_KEY_PREFIX = 'psysonic_because_picks:';
|
const PICKS_HISTORY_KEY_PREFIX = 'psysonic_because_picks:';
|
||||||
@@ -125,6 +126,24 @@ export default function BecauseYouLikeRail({
|
|||||||
);
|
);
|
||||||
const [anchor, setAnchor] = useState<Anchor | null>(null);
|
const [anchor, setAnchor] = useState<Anchor | null>(null);
|
||||||
const [recs, setRecs] = useState<SubsonicAlbum[]>([]);
|
const [recs, setRecs] = useState<SubsonicAlbum[]>([]);
|
||||||
|
const containerRef = useRef<HTMLDivElement>(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(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
@@ -208,13 +227,21 @@ export default function BecauseYouLikeRail({
|
|||||||
return () => { cancelled = true; };
|
return () => { cancelled = true; };
|
||||||
}, [pool, activeServerId]);
|
}, [pool, activeServerId]);
|
||||||
|
|
||||||
if (!anchor || recs.length === 0) return null;
|
if (!anchor || recs.length === 0) {
|
||||||
|
return <div ref={containerRef} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sectionTitle = t('home.becauseYouLikeFor', { artist: anchor.name });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div ref={containerRef}>
|
||||||
|
{narrow ? (
|
||||||
|
<AlbumRow title={sectionTitle} albums={recs} disableArtwork={disableArtwork} />
|
||||||
|
) : (
|
||||||
<section className="album-row-section because-you-like-rail">
|
<section className="album-row-section because-you-like-rail">
|
||||||
<div className="album-row-header">
|
<div className="album-row-header">
|
||||||
<h2 className="section-title" style={{ marginBottom: 0 }}>
|
<h2 className="section-title" style={{ marginBottom: 0 }}>
|
||||||
{t('home.becauseYouLikeFor', { artist: anchor.name })}
|
{sectionTitle}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="because-card-grid">
|
<div className="because-card-grid">
|
||||||
@@ -228,6 +255,8 @@ export default function BecauseYouLikeRail({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
/* Hide the 3rd card only inside the 2-column range, so:
|
||||||
>= 1052px (3 cols) -> 3 cards in one row
|
>= 1052px (3 cols) -> 3 cards in one row
|
||||||
696-1051px (2 cols) -> 2 cards in one row, orphan dropped
|
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). */
|
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) {
|
@container (min-width: 696px) and (max-width: 1051px) {
|
||||||
.because-card-grid > :nth-child(n+3) {
|
.because-card-grid > :nth-child(n+3) {
|
||||||
|
|||||||
Reference in New Issue
Block a user