feat(random-mix): context-aware remix button + genre reset chip

Remix button now targets the active context: relabels to "{{genre}} neu
mixen" when a genre is selected and re-fetches that genre instead of the
global random pool. "Alle Songs" chip added as first genre option to
reset back to the full library mix without leaving the page.

Also fixes album-grid cards stretching to full page height on macOS
WebKit (align-items: start on .album-grid-wrap).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Psychotoxical
2026-04-10 22:19:22 +02:00
parent dcc3e52ad1
commit 549677ffd4
9 changed files with 41 additions and 2 deletions
+3
View File
@@ -217,6 +217,8 @@ export const deTranslation = {
title: 'Zufallsmix',
remix: 'Neu mixen',
remixTooltip: 'Neue Songs laden',
remixGenre: '{{genre}} neu mixen',
remixTooltipGenre: 'Neue {{genre}}-Songs laden',
playAll: 'Alle abspielen',
trackTitle: 'Titel',
trackArtist: 'Künstler',
@@ -238,6 +240,7 @@ export const deTranslation = {
blacklistToggle: 'Keyword-Filter',
genreMixTitle: 'Genre-Mix',
genreMixDesc: 'Top 20 Genres nach Songanzahl — klicken für einen Zufallsmix',
genreMixAll: 'Alle Songs',
genreMixLoadMore: '10 weitere laden',
genreMixNoGenres: 'Keine Genres auf dem Server gefunden.',
shuffleGenres: 'Andere Genres anzeigen',
+3
View File
@@ -218,6 +218,8 @@ export const enTranslation = {
title: 'Random Mix',
remix: 'Remix',
remixTooltip: 'Load new random songs',
remixGenre: 'Remix {{genre}}',
remixTooltipGenre: 'Load new {{genre}} songs',
playAll: 'Play All',
trackTitle: 'Title',
trackArtist: 'Artist',
@@ -239,6 +241,7 @@ export const enTranslation = {
blacklistToggle: 'Keyword Filter',
genreMixTitle: 'Genre Mix',
genreMixDesc: 'Top 20 genres by song count — click to load a random mix',
genreMixAll: 'All Songs',
genreMixLoadMore: 'Load 10 more',
genreMixNoGenres: 'No genres found on server.',
shuffleGenres: 'Show different genres',
+3
View File
@@ -217,6 +217,8 @@ export const frTranslation = {
title: 'Mix aléatoire',
remix: 'Remixer',
remixTooltip: 'Charger de nouveaux morceaux aléatoires',
remixGenre: 'Remixer {{genre}}',
remixTooltipGenre: 'Charger de nouveaux morceaux {{genre}}',
playAll: 'Tout lire',
trackTitle: 'Titre',
trackArtist: 'Artiste',
@@ -238,6 +240,7 @@ export const frTranslation = {
blacklistToggle: 'Filtre par mot-clé',
genreMixTitle: 'Mix par genre',
genreMixDesc: 'Top 20 genres par nombre de morceaux — cliquer pour un mix aléatoire',
genreMixAll: 'Tous les morceaux',
genreMixLoadMore: 'Charger 10 de plus',
genreMixNoGenres: 'Aucun genre trouvé sur le serveur.',
shuffleGenres: 'Afficher d\'autres genres',
+3
View File
@@ -217,6 +217,8 @@ export const nbTranslation = {
title: 'Tilfeldig miks',
remix: 'Remiks',
remixTooltip: 'Last inn nye tilfeldige sanger',
remixGenre: 'Remiks {{genre}}',
remixTooltipGenre: 'Last inn nye {{genre}}-sanger',
playAll: 'Spill alt',
trackTitle: 'Tittel',
trackArtist: 'Artist',
@@ -238,6 +240,7 @@ export const nbTranslation = {
blacklistToggle: 'Nøkkelordfilter',
genreMixTitle: 'Sjangermiks',
genreMixDesc: 'Topp 20 sjangre etter antall sanger - klikk for å laste en tilfeldig miks',
genreMixAll: 'Alle sanger',
genreMixLoadMore: 'Last 10 til',
genreMixNoGenres: 'Ingen sjangre funnet på tjeneren.',
shuffleGenres: 'Vis andre sjangre',
+3
View File
@@ -217,6 +217,8 @@ export const nlTranslation = {
title: 'Willekeurige mix',
remix: 'Opnieuw mixen',
remixTooltip: 'Nieuwe willekeurige nummers laden',
remixGenre: '{{genre}} mixen',
remixTooltipGenre: 'Nieuwe {{genre}}-nummers laden',
playAll: 'Alles afspelen',
trackTitle: 'Titel',
trackArtist: 'Artiest',
@@ -238,6 +240,7 @@ export const nlTranslation = {
blacklistToggle: 'Trefwoordfilter',
genreMixTitle: 'Genremix',
genreMixDesc: 'Top 20 genres op aantal nummers — klik voor een willekeurige mix',
genreMixAll: 'Alle nummers',
genreMixLoadMore: '10 meer laden',
genreMixNoGenres: 'Geen genres gevonden op server.',
shuffleGenres: 'Andere genres tonen',
+3
View File
@@ -223,6 +223,8 @@ export const ruTranslation = {
title: 'Случайный микс',
remix: 'Новый микс',
remixTooltip: 'Подобрать другие случайные треки',
remixGenre: 'Новый микс: {{genre}}',
remixTooltipGenre: 'Подобрать другие треки жанра {{genre}}',
playAll: 'Воспроизвести всё',
trackTitle: 'Название',
trackArtist: 'Исполнитель',
@@ -245,6 +247,7 @@ export const ruTranslation = {
blacklistToggle: 'Фильтр по словам',
genreMixTitle: 'Микс по жанру',
genreMixDesc: 'Топ-20 жанров по числу треков — нажмите, чтобы собрать микс',
genreMixAll: 'Все треки',
genreMixLoadMore: 'Ещё 10 жанров',
genreMixNoGenres: 'На сервере нет данных о жанрах.',
shuffleGenres: 'Другие жанры',
+3
View File
@@ -217,6 +217,8 @@ export const zhTranslation = {
title: '随机混音',
remix: '重新混音',
remixTooltip: '加载新的随机歌曲',
remixGenre: '重混 {{genre}}',
remixTooltipGenre: '加载新的{{genre}}歌曲',
playAll: '全部播放',
trackTitle: '标题',
trackArtist: '艺术家',
@@ -238,6 +240,7 @@ export const zhTranslation = {
blacklistToggle: '关键词过滤',
genreMixTitle: '流派混音',
genreMixDesc: '按歌曲数量排列的前 20 个流派 — 点击获取随机混音',
genreMixAll: '所有歌曲',
genreMixLoadMore: '加载 10 首更多',
genreMixNoGenres: '服务器上未找到流派。',
shuffleGenres: '显示其他流派',
+19 -2
View File
@@ -182,8 +182,17 @@ export default function RandomMix() {
<h1 className="page-title">{t('randomMix.title')}</h1>
<div style={{ display: 'flex', gap: '0.5rem' }}>
<button className="btn btn-surface" onClick={fetchSongs} disabled={loading} data-tooltip={t('randomMix.remixTooltip')}>
<RefreshCw size={18} className={loading ? 'spin' : ''} /> {t('randomMix.remix')}
<button
className="btn btn-surface"
onClick={selectedGenre ? () => loadGenreMix(selectedGenre) : fetchSongs}
disabled={selectedGenre ? genreMixLoading : loading}
data-tooltip={selectedGenre
? t('randomMix.remixTooltipGenre', { genre: selectedGenre })
: t('randomMix.remixTooltip')
}
>
<RefreshCw size={18} className={(selectedGenre ? genreMixLoading : loading) ? 'spin' : ''} />
{selectedGenre ? t('randomMix.remixGenre', { genre: selectedGenre }) : t('randomMix.remix')}
</button>
{(() => {
const isGenreLoading = selectedGenre && !genreMixComplete;
@@ -313,6 +322,14 @@ export default function RandomMix() {
<span style={{ fontSize: 12, color: 'var(--text-muted)' }}>{t('randomMix.genreMixNoGenres')}</span>
) : (
<>
<button
className={`btn ${selectedGenre === null ? 'btn-primary' : 'btn-surface'}`}
style={{ fontSize: 12, padding: '4px 12px' }}
onClick={() => { setSelectedGenre(null); setGenreMixSongs([]); setGenreMixComplete(false); fetchSongs(); }}
disabled={genreMixLoading}
>
{t('randomMix.genreMixAll')}
</button>
{displayedGenres.map(genre => (
<button
key={genre}
+1
View File
@@ -392,6 +392,7 @@
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: var(--space-4);
align-items: start;
}
.album-grid-wrap .album-card {