feat(home): add Your Mixes section with DailyQ and WeeklyQ cards

Static section (no API call) with video-background cards that navigate
to DynamicSuggestView and WeeklySuggestView. Positioned after Qobuz
Playlists in default Home order, toggleable via Home Settings.
This commit is contained in:
vicrodh committed 2026-02-20 00:54:08 -06:00
1 parent 494ddce28b
commit fc5da6cfc4
8 files changed
+103 -6

No files matched your search

+81
View File
@@ -129,6 +129,8 @@
onNavigateAlbumsOfTheWeek?: () => void;
onNavigatePressAccolades?: () => void;
onNavigateQobuzPlaylists?: () => void;
onNavigateDailyQ?: () => void;
onNavigateWeeklyQ?: () => void;
}
let {
@@ -175,6 +177,8 @@
onNavigateAlbumsOfTheWeek,
onNavigatePressAccolades,
onNavigateQobuzPlaylists,
onNavigateDailyQ,
onNavigateWeeklyQ,
}: Props = $props();
// Home settings state
@@ -1447,6 +1451,39 @@
</HorizontalScrollRow>
{/if}
{/if}
{#if sectionId === 'yourMixes'}
<HorizontalScrollRow>
{#snippet header()}
<div class="section-header-group">
<h2 class="section-title">{$t('home.yourMixes')}</h2>
</div>
{/snippet}
{#snippet children()}
<button class="mix-card" onclick={() => onNavigateDailyQ?.()}
onkeydown={(e) => e.key === 'Enter' && onNavigateDailyQ?.()}>
<div class="mix-card-artwork">
<video src="/video/dailyq.mp4" autoplay loop muted playsinline></video>
</div>
<div class="mix-card-info">
<div class="mix-card-title">{$t('yourMixes.title')}</div>
<div class="mix-card-owner">Qobuz</div>
</div>
</button>
<button class="mix-card" onclick={() => onNavigateWeeklyQ?.()}
onkeydown={(e) => e.key === 'Enter' && onNavigateWeeklyQ?.()}>
<div class="mix-card-artwork">
<video src="/video/weeklyq.mp4" autoplay loop muted playsinline></video>
</div>
<div class="mix-card-info">
<div class="mix-card-title">{$t('weeklyMixes.title')}</div>
<div class="mix-card-owner">Qobuz</div>
</div>
</button>
<div class="spacer"></div>
{/snippet}
</HorizontalScrollRow>
{/if}
{/each}
<!-- Empty state: only show after all loading completes with no content -->
@@ -1815,4 +1852,48 @@
color: var(--text-primary);
}
.mix-card {
flex-shrink: 0;
width: 180px;
cursor: pointer;
background: none;
border: none;
padding: 0;
text-align: left;
color: inherit;
}
.mix-card-artwork {
width: 180px;
height: 180px;
border-radius: 8px;
overflow: hidden;
background: var(--bg-tertiary);
margin-bottom: 8px;
}
.mix-card-artwork video {
width: 100%;
height: 100%;
object-fit: cover;
}
.mix-card-title {
font-size: 14px;
font-weight: 500;
color: var(--text-primary);
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-bottom: 2px;
}
.mix-card-owner {
font-size: 12px;
font-weight: 400;
color: var(--text-muted);
line-height: 1.4;
}
</style>
+2 -1
View File
@@ -44,7 +44,8 @@
"artistPlays": "{count} Wiedergaben",
"moreFromFavorites": "Mehr aus Favoriten",
"startListening": "Beginne mit dem Hören",
"startListeningDescription": "Spiele Musik oder füge Favoriten hinzu, um Empfehlungen zu aktivieren."
"startListeningDescription": "Spiele Musik oder füge Favoriten hinzu, um Empfehlungen zu aktivieren.",
"yourMixes": "Deine Mixe"
},
"discover": {
"newReleases": "Neuerscheinungen",
+2 -1
View File
@@ -47,7 +47,8 @@
"artistPlays": "{count} plays",
"moreFromFavorites": "More From Favorites",
"startListening": "Start Listening",
"startListeningDescription": "Play music or add favorites to activate recommendations."
"startListeningDescription": "Play music or add favorites to activate recommendations.",
"yourMixes": "Your mixes"
},
"discover": {
"newReleases": "New Releases",
+2 -1
View File
@@ -47,7 +47,8 @@
"artistPlays": "{count} reproducciones",
"moreFromFavorites": "Más de Favoritos",
"startListening": "Empieza a Escuchar",
"startListeningDescription": "Reproduce música o agrega favoritos para activar recomendaciones."
"startListeningDescription": "Reproduce música o agrega favoritos para activar recomendaciones.",
"yourMixes": "Tus mixes"
},
"discover": {
"newReleases": "Novedades",
+2 -1
View File
@@ -44,7 +44,8 @@
"artistPlays": "{count} lectures",
"moreFromFavorites": "Plus de favoris",
"startListening": "Commencer l'écoute",
"startListeningDescription": "Lisez de la musique ou ajoutez des favoris pour activer les recommandations."
"startListeningDescription": "Lisez de la musique ou ajoutez des favoris pour activer les recommandations.",
"yourMixes": "Tes mix"
},
"discover": {
"newReleases": "Nouvelles sorties",
+3 -1
View File
@@ -17,7 +17,8 @@ export type HomeSectionId =
| 'recentAlbums'
| 'continueTracks'
| 'topArtists'
| 'favoriteAlbums';
| 'favoriteAlbums'
| 'yourMixes';
export interface HomeSection {
id: HomeSectionId;
@@ -53,6 +54,7 @@ const DEFAULT_SECTIONS: HomeSection[] = [
{ id: 'qobuzissimes', label: 'Qobuzissimes', visible: false, source: 'qobuz' },
{ id: 'editorPicks', label: "Editor's Picks", visible: false, source: 'qobuz' },
{ id: 'qobuzPlaylists', label: 'Qobuz Playlists', visible: true, source: 'qobuz' },
{ id: 'yourMixes', label: 'Your Mixes', visible: true, source: 'qobuz' },
{ id: 'essentialDiscography', label: 'Essential Discography', visible: true, source: 'qobuz' },
{ id: 'recentAlbums', label: 'Recently Played', visible: true, source: 'ml' },
{ id: 'continueTracks', label: 'Continue Listening', visible: true, source: 'ml' },
+3 -1
View File
@@ -31,7 +31,9 @@ export type ViewType =
| 'discover-press-accolades'
| 'discover-playlists'
| 'purchases'
| 'purchase-album';
| 'purchase-album'
| 'dailyq'
| 'weeklyq';
export type FavoritesTab = 'tracks' | 'albums' | 'artists' | 'playlists';
// Navigation state
+8
View File
@@ -324,6 +324,8 @@
import DiscoverPlaylistsBrowseView from '$lib/components/views/DiscoverPlaylistsBrowseView.svelte';
import PurchasesView from '$lib/components/views/PurchasesView.svelte';
import PurchaseAlbumDetailView from '$lib/components/views/PurchaseAlbumDetailView.svelte';
import DynamicSuggestView from '$lib/components/views/DynamicSuggestView.svelte';
import WeeklySuggestView from '$lib/components/views/WeeklySuggestView.svelte';
// Overlays
import QueuePanel from '$lib/components/QueuePanel.svelte';
@@ -3383,6 +3385,8 @@
onNavigateAlbumsOfTheWeek={() => navigateTo('discover-albums-of-the-week')}
onNavigatePressAccolades={() => navigateTo('discover-press-accolades')}
onNavigateQobuzPlaylists={() => navigateTo('discover-playlists')}
onNavigateDailyQ={() => navigateTo('dailyq')}
onNavigateWeeklyQ={() => navigateTo('weeklyq')}
/>
{/if}
{:else if activeView === 'search'}
@@ -3790,6 +3794,10 @@
onPlaylistCopyToLibrary={copyPlaylistToLibraryById}
onPlaylistShareQobuz={sharePlaylistQobuzLinkById}
/>
{:else if activeView === 'dailyq'}
<DynamicSuggestView />
{:else if activeView === 'weeklyq'}
<WeeklySuggestView />
{:else if activeView === 'purchases'}
<PurchasesView
onAlbumClick={handlePurchaseAlbumClick}