refactor: extract navigation state into navigationStore

- Create navigationStore.ts for view navigation and history
- Implement navigateTo, goBack, goForward actions
- Add selectPlaylist for playlist detail navigation
- Update +page.svelte to use store subscription and functions
- Simplify navigation wrapper functions
This commit is contained in:
vicrodh committed 2026-01-10 04:32:44 -06:00
1 parent e98ea146e3
commit cef950b08b
2 files changed
+168 -29

No files matched your search

+140
View File
@@ -0,0 +1,140 @@
/**
* Navigation State Store
*
* Manages view navigation and history.
* Note: Selected album/artist data objects are kept in +page.svelte since they're
* fetched data, but selectedPlaylistId is managed here as it's just an ID.
*/
export type ViewType = 'home' | 'search' | 'library' | 'settings' | 'album' | 'artist' | 'playlist' | 'favorites';
// Navigation state
let activeView: ViewType = 'home';
let viewHistory: ViewType[] = ['home'];
let forwardHistory: ViewType[] = [];
// Selected playlist ID (album/artist are full data objects in +page.svelte)
let selectedPlaylistId: number | null = null;
// Listeners
const listeners = new Set<() => void>();
function notifyListeners(): void {
for (const listener of listeners) {
listener();
}
}
/**
* Subscribe to navigation state changes
*/
export function subscribe(listener: () => void): () => void {
listeners.add(listener);
listener(); // Immediately notify with current state
return () => listeners.delete(listener);
}
// ============ Navigation Actions ============
/**
* Navigate to a view
*/
export function navigateTo(view: ViewType): void {
if (view !== activeView) {
viewHistory = [...viewHistory, view];
forwardHistory = [];
activeView = view;
notifyListeners();
}
}
/**
* Go back in history
* @returns true if navigation happened
*/
export function goBack(): boolean {
if (viewHistory.length > 1) {
const lastView = viewHistory[viewHistory.length - 1];
viewHistory = viewHistory.slice(0, -1);
forwardHistory = [...forwardHistory, lastView];
activeView = viewHistory[viewHistory.length - 1];
notifyListeners();
return true;
}
return false;
}
/**
* Go forward in history
* @returns true if navigation happened
*/
export function goForward(): boolean {
if (forwardHistory.length > 0) {
const nextView = forwardHistory[forwardHistory.length - 1];
forwardHistory = forwardHistory.slice(0, -1);
viewHistory = [...viewHistory, nextView];
activeView = nextView;
notifyListeners();
return true;
}
return false;
}
/**
* Check if we can go back
*/
export function canGoBack(): boolean {
return viewHistory.length > 1;
}
/**
* Check if we can go forward
*/
export function canGoForward(): boolean {
return forwardHistory.length > 0;
}
// ============ Playlist Selection ============
/**
* Navigate to playlist detail view
*/
export function selectPlaylist(playlistId: number): void {
selectedPlaylistId = playlistId;
navigateTo('playlist');
}
/**
* Get selected playlist ID
*/
export function getSelectedPlaylistId(): number | null {
return selectedPlaylistId;
}
// ============ Getters ============
export function getActiveView(): ViewType {
return activeView;
}
// ============ State Getter ============
export interface NavigationState {
activeView: ViewType;
viewHistory: ViewType[];
forwardHistory: ViewType[];
selectedPlaylistId: number | null;
canGoBack: boolean;
canGoForward: boolean;
}
export function getNavigationState(): NavigationState {
return {
activeView,
viewHistory: [...viewHistory],
forwardHistory: [...forwardHistory],
selectedPlaylistId,
canGoBack: viewHistory.length > 1,
canGoForward: forwardHistory.length > 0
};
}
+28 -29
View File
@@ -53,6 +53,18 @@
type UserInfo
} from '$lib/stores/authStore';
// Navigation state management
import {
subscribe as subscribeNav,
navigateTo as navTo,
goBack as navGoBack,
goForward as navGoForward,
selectPlaylist,
getNavigationState,
type ViewType,
type NavigationState
} from '$lib/stores/navigationStore';
// Components
import Sidebar from '$lib/components/Sidebar.svelte';
import NowPlayingBar from '$lib/components/NowPlayingBar.svelte';
@@ -232,14 +244,12 @@
let isLoggedIn = $state(false);
let userInfo = $state<UserInfo | null>(null);
// View State
type ViewType = 'home' | 'search' | 'library' | 'settings' | 'album' | 'artist' | 'playlist' | 'favorites';
// View State (from navigationStore subscription)
let activeView = $state<ViewType>('home');
let viewHistory = $state<ViewType[]>(['home']);
let forwardHistory = $state<ViewType[]>([]);
let selectedPlaylistId = $state<number | null>(null);
// Album and Artist data are fetched, so kept local
let selectedAlbum = $state<AlbumDetail | null>(null);
let selectedArtist = $state<ArtistDetail | null>(null);
let selectedPlaylistId = $state<number | null>(null);
// Overlay States (from uiStore subscription)
let isQueueOpen = $state(false);
@@ -276,41 +286,22 @@
// Toast State (from store subscription)
let toast = $state<ToastData | null>(null);
// Navigation Functions
// Navigation Functions (using store)
function navigateTo(view: string) {
console.log('navigateTo called with:', view, 'current activeView:', activeView);
const typedView = view as ViewType;
if (typedView !== activeView) {
viewHistory = [...viewHistory, typedView];
forwardHistory = [];
activeView = typedView;
console.log('View changed to:', activeView);
} else {
console.log('View unchanged (already on this view)');
}
navTo(view as ViewType);
}
function goBack() {
if (viewHistory.length > 1) {
const lastView = viewHistory[viewHistory.length - 1];
viewHistory = viewHistory.slice(0, -1);
forwardHistory = [...forwardHistory, lastView];
activeView = viewHistory[viewHistory.length - 1];
}
navGoBack();
}
function goForward() {
if (forwardHistory.length > 0) {
const nextView = forwardHistory[forwardHistory.length - 1];
forwardHistory = forwardHistory.slice(0, -1);
viewHistory = [...viewHistory, nextView];
activeView = nextView;
}
navGoForward();
}
function handlePlaylistSelect(playlistId: number) {
selectedPlaylistId = playlistId;
navigateTo('playlist');
selectPlaylist(playlistId);
}
async function handleAlbumClick(albumId: string) {
@@ -1544,6 +1535,13 @@
userInfo = authState.userInfo;
});
// Subscribe to navigation state changes
const unsubscribeNav = subscribeNav(() => {
const navState = getNavigationState();
activeView = navState.activeView;
selectedPlaylistId = navState.selectedPlaylistId;
});
// Restore Last.fm session on app startup
(async () => {
try {
@@ -1577,6 +1575,7 @@
unsubscribeToast();
unsubscribeUI();
unsubscribeAuth();
unsubscribeNav();
};
});