feat(home): integrate genre filter into HomeView
- Add GenreFilterButton to home header - Pass genre_id to fetchFeaturedAlbums API calls - Reload home content when genre filter changes - Featured sections now filter by selected genre
This commit is contained in:
1 parent
0d0da7c5fc
commit
8428fafaa5
1 file changed
+34
-11
@@ -7,6 +7,7 @@
|
|||||||
import AlbumCard from '../AlbumCard.svelte';
|
import AlbumCard from '../AlbumCard.svelte';
|
||||||
import TrackRow from '../TrackRow.svelte';
|
import TrackRow from '../TrackRow.svelte';
|
||||||
import HomeSettingsModal from '../HomeSettingsModal.svelte';
|
import HomeSettingsModal from '../HomeSettingsModal.svelte';
|
||||||
|
import GenreFilterButton from '../GenreFilterButton.svelte';
|
||||||
import { formatDuration, formatQuality, getQobuzImage } from '$lib/adapters/qobuzAdapters';
|
import { formatDuration, formatQuality, getQobuzImage } from '$lib/adapters/qobuzAdapters';
|
||||||
import {
|
import {
|
||||||
subscribe as subscribeHomeSettings,
|
subscribe as subscribeHomeSettings,
|
||||||
@@ -15,6 +16,10 @@
|
|||||||
type HomeSettings,
|
type HomeSettings,
|
||||||
type HomeSectionId
|
type HomeSectionId
|
||||||
} from '$lib/stores/homeSettingsStore';
|
} from '$lib/stores/homeSettingsStore';
|
||||||
|
import {
|
||||||
|
getSelectedGenreId,
|
||||||
|
hasActiveFilter as hasGenreFilter
|
||||||
|
} from '$lib/stores/genreFilterStore';
|
||||||
import { setPlaybackContext } from '$lib/stores/playbackContextStore';
|
import { setPlaybackContext } from '$lib/stores/playbackContextStore';
|
||||||
import type { QobuzAlbum, QobuzArtist, QobuzTrack, DisplayTrack } from '$lib/types';
|
import type { QobuzAlbum, QobuzArtist, QobuzTrack, DisplayTrack } from '$lib/types';
|
||||||
|
|
||||||
@@ -317,11 +322,12 @@
|
|||||||
total: number;
|
total: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchFeaturedAlbums(featuredType: string, limit: number): Promise<AlbumCardData[]> {
|
async function fetchFeaturedAlbums(featuredType: string, limit: number, genreId?: number): Promise<AlbumCardData[]> {
|
||||||
try {
|
try {
|
||||||
const response = await invoke<FeaturedAlbumsResponse>('get_featured_albums', {
|
const response = await invoke<FeaturedAlbumsResponse>('get_featured_albums', {
|
||||||
featuredType,
|
featuredType,
|
||||||
limit
|
limit,
|
||||||
|
genreId: genreId ?? null
|
||||||
});
|
});
|
||||||
return response.items.map(toAlbumCard);
|
return response.items.map(toAlbumCard);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -434,6 +440,11 @@
|
|||||||
.slice(0, homeLimits.topArtists);
|
.slice(0, homeLimits.topArtists);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleGenreFilterChange() {
|
||||||
|
// Reload home page with new genre filter
|
||||||
|
loadHome();
|
||||||
|
}
|
||||||
|
|
||||||
async function loadHome() {
|
async function loadHome() {
|
||||||
isInitializing = true;
|
isInitializing = true;
|
||||||
isOverlayVisible = true;
|
isOverlayVisible = true;
|
||||||
@@ -469,9 +480,12 @@
|
|||||||
limitFavorites: Math.max(homeLimits.favoriteAlbums, homeLimits.favoriteTracks)
|
limitFavorites: Math.max(homeLimits.favoriteAlbums, homeLimits.favoriteTracks)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Get current genre filter
|
||||||
|
const genreId = getSelectedGenreId();
|
||||||
|
|
||||||
// Start Qobuz API calls in parallel (don't await)
|
// Start Qobuz API calls in parallel (don't await)
|
||||||
if (isSectionVisible('newReleases')) {
|
if (isSectionVisible('newReleases')) {
|
||||||
fetchFeaturedAlbums('new-releases', homeLimits.featuredAlbums).then(async albums => {
|
fetchFeaturedAlbums('new-releases', homeLimits.featuredAlbums, genreId).then(async albums => {
|
||||||
newReleases = albums;
|
newReleases = albums;
|
||||||
await loadAllAlbumDownloadStatuses(albums);
|
await loadAllAlbumDownloadStatuses(albums);
|
||||||
loadingNewReleases = false;
|
loadingNewReleases = false;
|
||||||
@@ -482,7 +496,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isSectionVisible('pressAwards')) {
|
if (isSectionVisible('pressAwards')) {
|
||||||
fetchFeaturedAlbums('press-awards', homeLimits.featuredAlbums).then(async albums => {
|
fetchFeaturedAlbums('press-awards', homeLimits.featuredAlbums, genreId).then(async albums => {
|
||||||
pressAwards = albums;
|
pressAwards = albums;
|
||||||
await loadAllAlbumDownloadStatuses(albums);
|
await loadAllAlbumDownloadStatuses(albums);
|
||||||
loadingPressAwards = false;
|
loadingPressAwards = false;
|
||||||
@@ -493,7 +507,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isSectionVisible('mostStreamed')) {
|
if (isSectionVisible('mostStreamed')) {
|
||||||
fetchFeaturedAlbums('most-streamed', homeLimits.featuredAlbums).then(async albums => {
|
fetchFeaturedAlbums('most-streamed', homeLimits.featuredAlbums, genreId).then(async albums => {
|
||||||
mostStreamed = albums;
|
mostStreamed = albums;
|
||||||
await loadAllAlbumDownloadStatuses(albums);
|
await loadAllAlbumDownloadStatuses(albums);
|
||||||
loadingMostStreamed = false;
|
loadingMostStreamed = false;
|
||||||
@@ -504,7 +518,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isSectionVisible('qobuzissimes')) {
|
if (isSectionVisible('qobuzissimes')) {
|
||||||
fetchFeaturedAlbums('qobuzissimes', homeLimits.featuredAlbums).then(async albums => {
|
fetchFeaturedAlbums('qobuzissimes', homeLimits.featuredAlbums, genreId).then(async albums => {
|
||||||
qobuzissimes = albums;
|
qobuzissimes = albums;
|
||||||
await loadAllAlbumDownloadStatuses(albums);
|
await loadAllAlbumDownloadStatuses(albums);
|
||||||
loadingQobuzissimes = false;
|
loadingQobuzissimes = false;
|
||||||
@@ -515,7 +529,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isSectionVisible('editorPicks')) {
|
if (isSectionVisible('editorPicks')) {
|
||||||
fetchFeaturedAlbums('editor-picks', homeLimits.featuredAlbums).then(async albums => {
|
fetchFeaturedAlbums('editor-picks', homeLimits.featuredAlbums, genreId).then(async albums => {
|
||||||
editorPicks = albums;
|
editorPicks = albums;
|
||||||
await loadAllAlbumDownloadStatuses(albums);
|
await loadAllAlbumDownloadStatuses(albums);
|
||||||
loadingEditorPicks = false;
|
loadingEditorPicks = false;
|
||||||
@@ -610,16 +624,19 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Header with greeting and settings -->
|
<!-- Header with greeting, filter and settings -->
|
||||||
<div class="home-header">
|
<div class="home-header">
|
||||||
{#if homeSettings.greeting.enabled}
|
{#if homeSettings.greeting.enabled}
|
||||||
<h2 class="greeting">{greetingText}</h2>
|
<h2 class="greeting">{greetingText}</h2>
|
||||||
{:else}
|
{:else}
|
||||||
<div></div>
|
<div></div>
|
||||||
{/if}
|
{/if}
|
||||||
<button class="settings-btn" onclick={() => isSettingsModalOpen = true} title={$t('home.customizeHome')}>
|
<div class="header-actions">
|
||||||
<img src="/home-gear.svg" alt="Settings" class="settings-icon" />
|
<GenreFilterButton onFilterChange={handleGenreFilterChange} />
|
||||||
</button>
|
<button class="settings-btn" onclick={() => isSettingsModalOpen = true} title={$t('home.customizeHome')}>
|
||||||
|
<img src="/home-gear.svg" alt="Settings" class="settings-icon" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if isInitializing && !isOverlayVisible}
|
{#if isInitializing && !isOverlayVisible}
|
||||||
@@ -1057,6 +1074,12 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-btn {
|
.settings-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
Reference in new issue
Block a user