fix(library): integrate navigation history for back/forward support

Mouse back/forward buttons now work in local library view.

Changes:
- Add 'library-album' ViewType to navigation store
- Add selectLocalAlbum(), clearLocalAlbum(), getSelectedLocalAlbumId()
- LocalLibraryView subscribes to navigation state changes
- Album clicks use selectLocalAlbum() for proper history tracking
- Back button uses navGoBack() to navigate properly
- Tab changes navigate back to 'library' when viewing album
- +page.svelte shows LocalLibraryView for both 'library' and 'library-album'

cc
This commit is contained in:
vicrodh committed 2026-01-14 08:14:13 -06:00
1 parent b8e3bca6f8
commit 25d0a13d6b
3 files changed
+120 -4

No files matched your search

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { invoke, convertFileSrc } from '@tauri-apps/api/core'; import { invoke, convertFileSrc } from '@tauri-apps/api/core';
import { open } from '@tauri-apps/plugin-dialog'; import { open } from '@tauri-apps/plugin-dialog';
import { onMount } from 'svelte'; import { onMount, onDestroy } from 'svelte';
import { import {
HardDrive, Music, Disc3, Mic2, FolderPlus, Trash2, RefreshCw, HardDrive, Music, Disc3, Mic2, FolderPlus, Trash2, RefreshCw,
Settings, X, Play, AlertCircle, ImageDown, Upload, Search, LayoutGrid, List Settings, X, Play, AlertCircle, ImageDown, Upload, Search, LayoutGrid, List
@@ -9,6 +9,15 @@
import AlbumCard from '../AlbumCard.svelte'; import AlbumCard from '../AlbumCard.svelte';
import TrackRow from '../TrackRow.svelte'; import TrackRow from '../TrackRow.svelte';
import AddToPlaylistModal from '../AddToPlaylistModal.svelte'; import AddToPlaylistModal from '../AddToPlaylistModal.svelte';
import {
subscribe as subscribeNav,
selectLocalAlbum,
clearLocalAlbum,
getSelectedLocalAlbumId,
goBack as navGoBack,
navigateTo,
getNavigationState
} from '$lib/stores/navigationStore';
// Backend types matching Rust models // Backend types matching Rust models
interface LocalTrack { interface LocalTrack {
@@ -163,12 +172,70 @@
showPlaylistModal = true; showPlaylistModal = true;
} }
let unsubscribeNav: (() => void) | null = null;
onMount(() => { onMount(() => {
loadLibraryData(); loadLibraryData();
loadFolders(); loadFolders();
checkDiscogsCredentials(); checkDiscogsCredentials();
// Subscribe to navigation changes for back/forward support
unsubscribeNav = subscribeNav(() => {
const navState = getNavigationState();
// When navigating to library-album, load the album if we have an ID
if (navState.activeView === 'library-album' && navState.selectedLocalAlbumId) {
const albumId = navState.selectedLocalAlbumId;
// Find album in current list or load it
const album = albums.find(a => a.id === albumId);
if (album && (!selectedAlbum || selectedAlbum.id !== albumId)) {
loadAlbumById(albumId);
}
}
// When navigating back to library (from library-album), clear album selection
if (navState.activeView === 'library' && selectedAlbum) {
selectedAlbum = null;
albumTracks = [];
}
});
// Check if we should show an album on initial load (forward navigation)
const initialNavState = getNavigationState();
if (initialNavState.activeView === 'library-album' && initialNavState.selectedLocalAlbumId) {
loadAlbumById(initialNavState.selectedLocalAlbumId);
}
}); });
onDestroy(() => {
if (unsubscribeNav) {
unsubscribeNav();
}
});
async function loadAlbumById(albumId: string) {
try {
// Find album in current list
let album = albums.find(a => a.id === albumId);
// If not found in loaded albums, we need to fetch album list first
if (!album) {
const allAlbums = await invoke<LocalAlbum[]>('library_get_albums', { limit: 1000, offset: 0 });
albums = allAlbums;
album = allAlbums.find(a => a.id === albumId);
}
if (album) {
selectedAlbum = album;
albumTracks = await invoke<LocalTrack[]>('library_get_album_tracks', {
albumGroupKey: album.id
});
}
} catch (err) {
console.error('Failed to load album:', err);
}
}
async function checkDiscogsCredentials() { async function checkDiscogsCredentials() {
try { try {
hasDiscogsCredentials = await invoke<boolean>('discogs_has_credentials'); hasDiscogsCredentials = await invoke<boolean>('discogs_has_credentials');
@@ -229,7 +296,17 @@
function handleTabChange(tab: TabType) { function handleTabChange(tab: TabType) {
activeTab = tab; activeTab = tab;
// If we're viewing an album, navigate back to library
const navState = getNavigationState();
if (navState.activeView === 'library-album') {
clearLocalAlbum();
navigateTo('library');
}
// Clear local state
selectedAlbum = null; selectedAlbum = null;
albumTracks = [];
if (tab === 'artists' && artists.length === 0) { if (tab === 'artists' && artists.length === 0) {
loadArtists(); loadArtists();
@@ -399,6 +476,9 @@
} }
async function handleAlbumClick(album: LocalAlbum) { async function handleAlbumClick(album: LocalAlbum) {
// Use navigation store for proper back/forward support
selectLocalAlbum(album.id);
// Also load album data immediately for responsive UI
selectedAlbum = album; selectedAlbum = album;
try { try {
albumTracks = await invoke<LocalTrack[]>('library_get_album_tracks', { albumTracks = await invoke<LocalTrack[]>('library_get_album_tracks', {
@@ -858,7 +938,7 @@
{@const showDiscHeaders = albumSections.length > 1} {@const showDiscHeaders = albumSections.length > 1}
<!-- Album Detail View --> <!-- Album Detail View -->
<div class="album-detail"> <div class="album-detail">
<button class="back-btn" onclick={() => (selectedAlbum = null)}> <button class="back-btn" onclick={() => { clearLocalAlbum(); navGoBack(); }}>
<X size={20} /> <X size={20} />
<span>Back to Library</span> <span>Back to Library</span>
</button> </button>
+37 -1
View File
@@ -6,7 +6,7 @@
* fetched data, but selectedPlaylistId is managed here as it's just an ID. * fetched data, but selectedPlaylistId is managed here as it's just an ID.
*/ */
export type ViewType = 'home' | 'search' | 'library' | 'settings' | 'album' | 'artist' | 'playlist' | 'playlist-manager' | 'favorites'; export type ViewType = 'home' | 'search' | 'library' | 'library-album' | 'settings' | 'album' | 'artist' | 'playlist' | 'playlist-manager' | 'favorites';
// Navigation state // Navigation state
let activeView: ViewType = 'home'; let activeView: ViewType = 'home';
@@ -16,6 +16,9 @@ let forwardHistory: ViewType[] = [];
// Selected playlist ID (album/artist are full data objects in +page.svelte) // Selected playlist ID (album/artist are full data objects in +page.svelte)
let selectedPlaylistId: number | null = null; let selectedPlaylistId: number | null = null;
// Selected local album ID (for library-album view)
let selectedLocalAlbumId: string | null = null;
// Listeners // Listeners
const listeners = new Set<() => void>(); const listeners = new Set<() => void>();
@@ -118,6 +121,37 @@ export function getSelectedPlaylistId(): number | null {
return selectedPlaylistId; return selectedPlaylistId;
} }
// ============ Local Album Selection ============
/**
* Navigate to local library album detail view
*/
export function selectLocalAlbum(albumId: string): void {
const previousId = selectedLocalAlbumId;
selectedLocalAlbumId = albumId;
// If already on library-album view, still notify so the component reloads with new ID
if (activeView === 'library-album' && previousId !== albumId) {
notifyListeners();
} else {
navigateTo('library-album');
}
}
/**
* Clear selected local album (called when navigating back to library)
*/
export function clearLocalAlbum(): void {
selectedLocalAlbumId = null;
}
/**
* Get selected local album ID
*/
export function getSelectedLocalAlbumId(): string | null {
return selectedLocalAlbumId;
}
// ============ Getters ============ // ============ Getters ============
export function getActiveView(): ViewType { export function getActiveView(): ViewType {
@@ -131,6 +165,7 @@ export interface NavigationState {
viewHistory: ViewType[]; viewHistory: ViewType[];
forwardHistory: ViewType[]; forwardHistory: ViewType[];
selectedPlaylistId: number | null; selectedPlaylistId: number | null;
selectedLocalAlbumId: string | null;
canGoBack: boolean; canGoBack: boolean;
canGoForward: boolean; canGoForward: boolean;
} }
@@ -141,6 +176,7 @@ export function getNavigationState(): NavigationState {
viewHistory: [...viewHistory], viewHistory: [...viewHistory],
forwardHistory: [...forwardHistory], forwardHistory: [...forwardHistory],
selectedPlaylistId, selectedPlaylistId,
selectedLocalAlbumId,
canGoBack: viewHistory.length > 1, canGoBack: viewHistory.length > 1,
canGoForward: forwardHistory.length > 0 canGoForward: forwardHistory.length > 0
}; };
+1 -1
View File
@@ -1765,7 +1765,7 @@
onTrackGoToArtist={handleArtistClick} onTrackGoToArtist={handleArtistClick}
onPlaylistClick={selectPlaylist} onPlaylistClick={selectPlaylist}
/> />
{:else if activeView === 'library'} {:else if activeView === 'library' || activeView === 'library-album'}
<LocalLibraryView <LocalLibraryView
onTrackPlay={handleLocalTrackPlay} onTrackPlay={handleLocalTrackPlay}
onTrackPlayNext={queueLocalTrackNext} onTrackPlayNext={queueLocalTrackNext}