feat(sidebar): add split Mix navigation mode with separate sidebar entries

Extract ALL_NAV_ITEMS to src/config/navItems.ts and add randomMix/randomAlbums
nav items. A new toggle in Settings switches between a single "Build a Mix" hub
and separate "Random Mix" / "Random Albums" sidebar entries (randomNavMode in
authStore). Fixes reorder crash caused by hidden items being overwritten with
undefined during the merge step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Psychotoxical
2026-04-15 22:27:53 +02:00
parent 189bb519eb
commit e23280c013
15 changed files with 108 additions and 89 deletions
+7
View File
@@ -212,6 +212,11 @@ interface AuthState {
setMixMinRatingArtist: (v: number) => void;
setMusicFolders: (folders: Array<{ id: string; name: string }>) => void;
setMusicLibraryFilter: (folderId: 'all' | string) => void;
/** Navigation style for Mix pages: single hub ('hub') or separate sidebar entries ('separate'). */
randomNavMode: 'hub' | 'separate';
setRandomNavMode: (v: 'hub' | 'separate') => void;
logout: () => void;
// Derived
@@ -304,6 +309,7 @@ export const useAuthStore = create<AuthState>()(
mixMinRatingSong: 0,
mixMinRatingAlbum: 0,
mixMinRatingArtist: 0,
randomNavMode: 'hub',
musicFolders: [],
musicLibraryFilterByServer: {},
musicLibraryFilterVersion: 0,
@@ -457,6 +463,7 @@ export const useAuthStore = create<AuthState>()(
setMixMinRatingSong: (v) => set({ mixMinRatingSong: clampMixFilterMinStars(v) }),
setMixMinRatingAlbum: (v) => set({ mixMinRatingAlbum: clampMixFilterMinStars(v) }),
setMixMinRatingArtist: (v) => set({ mixMinRatingArtist: clampMixFilterMinStars(v) }),
setRandomNavMode: (v) => set({ randomNavMode: v }),
setMusicFolders: (folders) => {
const sid = get().activeServerId;
+6 -4
View File
@@ -13,6 +13,8 @@ export const DEFAULT_SIDEBAR_ITEMS: SidebarItemConfig[] = [
{ id: 'newReleases', visible: true },
{ id: 'allAlbums', visible: true },
{ id: 'randomPicker', visible: true },
{ id: 'randomMix', visible: true },
{ id: 'randomAlbums', visible: true },
{ id: 'artists', visible: true },
{ id: 'genres', visible: true },
{ id: 'favorites', visible: true },
@@ -49,11 +51,11 @@ export const useSidebarStore = create<SidebarStore>()(
name: 'psysonic_sidebar',
onRehydrateStorage: () => (state) => {
if (!state) return;
const known = new Set(state.items.map(i => i.id));
// Sanitize: remove any null/corrupted entries that may have been persisted
const safe = (state.items ?? []).filter((i): i is SidebarItemConfig => i != null && typeof i.id === 'string');
const known = new Set(safe.map(i => i.id));
const missing = DEFAULT_SIDEBAR_ITEMS.filter(i => !known.has(i.id));
if (missing.length > 0) {
state.items = [...state.items, ...missing];
}
state.items = missing.length > 0 ? [...safe, ...missing] : safe;
},
}
)