feat(library): genre sliders, artist links, and navigation utils
🏗️ Build and Test JellyTau / Run Tests (pull_request) Successful in 3m49s
Traceability Validation / Check Requirement Traces (pull_request) Successful in 19s
🏗️ Build and Test JellyTau / Build Android APK (pull_request) Successful in 18m24s

- music landing: diverse per-genre album sliders (online counts /
  offline wide-probe fallback) and home-screen library shortcuts
- add ArtistLinks component and shared navigation/genreDiversity utils
- player/playback-mode refinements across Rust and frontend
This commit is contained in:
2026-06-25 19:18:06 +02:00
parent 62874564ff
commit 1836615dc0
38 changed files with 1009 additions and 207 deletions
+49 -1
View File
@@ -4,11 +4,13 @@
import { platform } from "@tauri-apps/plugin-os";
import { auth, isAuthenticated } from "$lib/stores/auth";
import { home } from "$lib/stores/home";
import { library, libraries } from "$lib/stores/library";
import { isServerReachable } from "$lib/stores/connectivity";
import { currentMedia } from "$lib/stores/player";
import HeroBanner from "$lib/components/home/HeroBanner.svelte";
import Carousel from "$lib/components/home/Carousel.svelte";
import type { MediaItem } from "$lib/api/types";
import MediaCard from "$lib/components/library/MediaCard.svelte";
import type { MediaItem, Library } from "$lib/api/types";
// Track if we've done an initial load (plain variable, not reactive)
let hasLoadedOnce = false;
@@ -34,6 +36,9 @@
if ($isAuthenticated) {
await home.loadHomeSections();
if ($libraries.length === 0) {
await library.loadLibraries();
}
hasLoadedOnce = true;
}
});
@@ -68,6 +73,31 @@
}
}
// Playlist libraries live inside the Music landing page, not as top-level shortcuts.
const shortcutLibraries = $derived(
$libraries.filter((lib) => lib.collectionType !== "playlists")
);
function handleLibraryClick(lib: Library) {
// Mirror /library routing: dedicated landing pages need currentLibrary set.
library.setCurrentLibrary(lib);
switch (lib.collectionType) {
case "music":
goto("/library/music");
break;
case "tvshows":
goto("/library/tv");
break;
case "movies":
goto("/library/movies");
break;
default:
library.clearGenres();
goto("/library");
break;
}
}
const heroItems = $derived($home.heroItems);
const resumeItems = $derived($home.resumeItems.filter(
i => i.type === "Movie" || i.type === "Episode"
@@ -92,6 +122,24 @@
<HeroBanner items={heroItems} />
{/if}
<!-- Your Libraries: quick jump into each dedicated landing page -->
{#if shortcutLibraries.length > 0}
<div>
<h2 class="text-xl font-bold text-white mb-4 px-4">Your Libraries</h2>
<div class="flex gap-4 overflow-x-auto px-4 pb-2">
{#each shortcutLibraries as lib (lib.id)}
<div class="flex-shrink-0">
<MediaCard
item={lib}
size="medium"
onclick={() => handleLibraryClick(lib)}
/>
</div>
{/each}
</div>
</div>
{/if}
<!-- Next Movie -->
{#if resumeMovies.length > 0}
<Carousel