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:
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>
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
Reference in new issue
Block a user