massive UI/UX overhaul

This commit is contained in:
Boof2015
2026-07-03 23:56:34 -04:00
parent 9ce825b673
commit 61e6147d3e
71 changed files with 2806 additions and 953 deletions
+81 -3
View File
@@ -5,8 +5,10 @@ import {
getAlbums,
getAllTracks,
getRecentlyPlayedTracks,
getSetting,
getTrackCount,
markTrackPlayed,
setSetting,
} from '@/db/queries';
import { ensureArtworkThumbnails } from '@/library/artwork';
import { buildArtistList } from '@/library/artistGrouping';
@@ -18,7 +20,9 @@ import {
type ScanProgress,
type ScanResult,
} from '@/library/scanner';
import type { TrackSort } from '@/lib/trackSort';
import { ALBUM_SORT_LABELS, type AlbumSort } from '@/lib/albumSort';
import { ARTIST_SORT_LABELS, type ArtistSort } from '@/lib/artistSort';
import { TRACK_SORT_LABELS, type TrackSort } from '@/lib/trackSort';
import { usePlaylistStore } from './playlistStore';
import { useSettingsStore } from './settingsStore';
@@ -28,6 +32,38 @@ import { useSettingsStore } from './settingsStore';
*/
type ViewMode = 'tracks' | 'albums' | 'artists' | 'playlists' | 'folders';
const VIEW_MODE_KEY = 'library_view_mode';
const TRACK_SORT_KEY = 'library_track_sort';
const ALBUM_SORT_KEY = 'library_album_sort';
const ARTIST_SORT_KEY = 'library_artist_sort';
const VIEW_MODES: readonly ViewMode[] = ['tracks', 'albums', 'artists', 'playlists', 'folders'];
function parseViewMode(value: string | null): ViewMode | null {
return VIEW_MODES.includes(value as ViewMode) ? (value as ViewMode) : null;
}
function parseTrackSort(value: string | null): TrackSort | null {
return value !== null && value in TRACK_SORT_LABELS ? (value as TrackSort) : null;
}
function parseAlbumSort(value: string | null): AlbumSort | null {
return value !== null && value in ALBUM_SORT_LABELS ? (value as AlbumSort) : null;
}
function parseArtistSort(value: string | null): ArtistSort | null {
return value !== null && value in ARTIST_SORT_LABELS ? (value as ArtistSort) : null;
}
/** Fire-and-forget settings write so view/sort switching stays synchronous. */
function persistSetting(key: string, value: string) {
void openLibraryDb()
.then((db) => setSetting(db, key, value))
.catch(() => {
// Losing a view preference write is harmless; never surface it.
});
}
export type FolderWithCount = LibraryFolder & { track_count: number };
interface ScanProgressState {
@@ -49,6 +85,8 @@ interface LibraryStore {
totalTrackCount: number;
viewMode: ViewMode;
trackSort: TrackSort;
albumSort: AlbumSort;
artistSort: ArtistSort;
isScanning: boolean;
scanProgress: ScanProgressState;
scanError: string | null;
@@ -59,6 +97,8 @@ interface LibraryStore {
recomputeArtists: () => void;
setViewMode: (mode: ViewMode) => void;
setTrackSort: (sort: TrackSort) => void;
setAlbumSort: (sort: AlbumSort) => void;
setArtistSort: (sort: ArtistSort) => void;
addFolder: () => Promise<void>;
removeFolder: (folderId: number) => Promise<void>;
rescan: () => Promise<void>;
@@ -93,6 +133,8 @@ export const useLibraryStore = create<LibraryStore>((set, get) => {
totalTrackCount: 0,
viewMode: 'albums',
trackSort: 'artist',
albumSort: 'artist',
artistSort: 'name',
isScanning: false,
scanProgress: { ...IDLE_PROGRESS },
scanError: null,
@@ -107,6 +149,26 @@ export const useLibraryStore = create<LibraryStore>((set, get) => {
useSettingsStore.subscribe((state, prev) => {
if (state.artistGroupingMode !== prev.artistGroupingMode) get().recomputeArtists();
});
// Restore view preferences before the first render of the library screen.
const [savedViewMode, savedTrackSort, savedAlbumSort, savedArtistSort] =
await Promise.all([
getSetting(db, VIEW_MODE_KEY),
getSetting(db, TRACK_SORT_KEY),
getSetting(db, ALBUM_SORT_KEY),
getSetting(db, ARTIST_SORT_KEY),
]);
const viewMode = parseViewMode(savedViewMode);
const trackSort = parseTrackSort(savedTrackSort);
const albumSort = parseAlbumSort(savedAlbumSort);
const artistSort = parseArtistSort(savedArtistSort);
if (viewMode || trackSort || albumSort || artistSort) {
set({
...(viewMode ? { viewMode } : null),
...(trackSort ? { trackSort } : null),
...(albumSort ? { albumSort } : null),
...(artistSort ? { artistSort } : null),
});
}
await get().refresh();
set({ initialized: true });
// One-time recovery: the v3 migration marks tracks stale (mtime = -1)
@@ -161,9 +223,25 @@ export const useLibraryStore = create<LibraryStore>((set, get) => {
artists: buildArtistList(state.tracks, useSettingsStore.getState().artistGroupingMode),
})),
setViewMode: (viewMode) => set({ viewMode }),
setViewMode: (viewMode) => {
set({ viewMode });
persistSetting(VIEW_MODE_KEY, viewMode);
},
setTrackSort: (trackSort) => set({ trackSort }),
setTrackSort: (trackSort) => {
set({ trackSort });
persistSetting(TRACK_SORT_KEY, trackSort);
},
setAlbumSort: (albumSort) => {
set({ albumSort });
persistSetting(ALBUM_SORT_KEY, albumSort);
},
setArtistSort: (artistSort) => {
set({ artistSort });
persistSetting(ARTIST_SORT_KEY, artistSort);
},
addFolder: () => runScan(() => addFolderViaPicker({ onProgress })),