Files
feishin/src/renderer/store/app.store.ts
T
2026-02-13 14:59:15 -08:00

274 lines
9.9 KiB
TypeScript

import type { ItemListStateItem } from '/@/renderer/components/item-list/helpers/item-list-state';
import type { LibraryItem } from '/@/shared/types/domain-types';
import merge from 'lodash/merge';
import { devtools, persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import { createWithEqualityFn } from 'zustand/traditional';
import { AlbumListSort, SortOrder } from '/@/shared/types/domain-types';
import { Platform } from '/@/shared/types/types';
export interface AppSlice extends AppState {
actions: {
setAlbumArtistDetailGroupingType: (groupingType: 'all' | 'primary') => void;
setAlbumArtistDetailSort: (sortBy: AlbumListSort, sortOrder: SortOrder) => void;
setAlbumArtistIdsMode: (mode: 'and' | 'or') => void;
setAlbumArtistSelectMode: (mode: 'multi' | 'single') => void;
setAppStore: (data: Partial<AppSlice>) => void;
setArtistIdsMode: (mode: 'and' | 'or') => void;
setArtistSelectMode: (mode: 'multi' | 'single') => void;
setGenreIdsMode: (mode: 'and' | 'or') => void;
setGenreSelectMode: (mode: 'multi' | 'single') => void;
setGlobalExpanded: (value: GlobalExpandedState | null) => void;
setPageSidebar: (key: string, value: boolean) => void;
setPrivateMode: (enabled: boolean) => void;
setShowTimeRemaining: (enabled: boolean) => void;
setSideBar: (options: Partial<SidebarProps>) => void;
setTitleBar: (options: Partial<TitlebarProps>) => void;
};
}
export interface AppState {
albumArtistDetailSort: {
groupingType: 'all' | 'primary';
sortBy: AlbumListSort;
sortOrder: SortOrder;
};
albumArtistIdsMode: 'and' | 'or';
albumArtistSelectMode: 'multi' | 'single';
artistIdsMode: 'and' | 'or';
artistSelectMode: 'multi' | 'single';
commandPalette: CommandPaletteProps;
genreIdsMode: 'and' | 'or';
genreSelectMode: 'multi' | 'single';
globalExpanded: GlobalExpandedState | null;
isReorderingQueue: boolean;
pageSidebar: Record<string, boolean>;
platform: Platform;
privateMode: boolean;
showTimeRemaining: boolean;
sidebar: SidebarProps;
titlebar: TitlebarProps;
}
export interface GlobalExpandedState {
item: ItemListStateItem;
itemType: LibraryItem;
}
type CommandPaletteProps = {
close: () => void;
open: () => void;
opened: boolean;
toggle: () => void;
};
type SidebarProps = {
collapsed: boolean;
expanded: string[];
image: boolean;
leftWidth: string;
rightExpanded: boolean;
rightWidth: string;
};
type TitlebarProps = {
backgroundColor: string;
outOfView: boolean;
};
export const useAppStore = createWithEqualityFn<AppSlice>()(
persist(
devtools(
immer((set, get) => ({
actions: {
setAlbumArtistDetailGroupingType: (groupingType) => {
set((state) => {
state.albumArtistDetailSort.groupingType = groupingType;
});
},
setAlbumArtistDetailSort: (sortBy, sortOrder) => {
set((state) => {
state.albumArtistDetailSort = {
...state.albumArtistDetailSort,
sortBy,
sortOrder,
};
});
},
setAlbumArtistIdsMode: (mode) => {
set((state) => {
state.albumArtistIdsMode = mode;
});
},
setAlbumArtistSelectMode: (mode) => {
set((state) => {
state.albumArtistSelectMode = mode;
});
},
setAppStore: (data) => {
set({ ...get(), ...data });
},
setArtistIdsMode: (mode) => {
set((state) => {
state.artistIdsMode = mode;
});
},
setArtistSelectMode: (mode) => {
set((state) => {
state.artistSelectMode = mode;
});
},
setGenreIdsMode: (mode) => {
set((state) => {
state.genreIdsMode = mode;
});
},
setGenreSelectMode: (mode) => {
set((state) => {
state.genreSelectMode = mode;
});
},
setGlobalExpanded: (value) => {
set((state) => {
state.globalExpanded = value;
});
},
setPageSidebar: (key, value) => {
set((state) => {
state.pageSidebar[key] = value;
});
},
setPrivateMode: (privateMode) => {
set((state) => {
state.privateMode = privateMode;
});
},
setShowTimeRemaining: (showTimeRemaining) => {
set((state) => {
state.showTimeRemaining = showTimeRemaining;
});
},
setSideBar: (options) => {
set((state) => {
state.sidebar = { ...state.sidebar, ...options };
});
},
setTitleBar: (options) => {
set((state) => {
state.titlebar = { ...state.titlebar, ...options };
});
},
},
albumArtistDetailSort: {
groupingType: 'primary',
sortBy: AlbumListSort.RELEASE_DATE,
sortOrder: SortOrder.DESC,
},
albumArtistIdsMode: 'and',
albumArtistSelectMode: 'multi',
artistIdsMode: 'and',
artistSelectMode: 'multi',
commandPalette: {
close: () => {
set((state) => {
state.commandPalette.opened = false;
});
},
open: () => {
set((state) => {
state.commandPalette.opened = true;
});
},
opened: false,
toggle: () => {
set((state) => {
state.commandPalette.opened = !state.commandPalette.opened;
});
},
},
genreIdsMode: 'and',
genreSelectMode: 'multi',
globalExpanded: null,
isReorderingQueue: false,
pageSidebar: {
album: true,
song: true,
},
platform: Platform.WINDOWS,
privateMode: false,
showTimeRemaining: false,
sidebar: {
collapsed: false,
expanded: [],
image: false,
leftWidth: '400px',
rightExpanded: false,
rightWidth: '600px',
},
titlebar: {
backgroundColor: '#000000',
outOfView: false,
},
})),
{ name: 'store_app' },
),
{
merge: (persistedState, currentState) => {
return merge(currentState, persistedState);
},
migrate: (persistedState, version) => {
if (version <= 2) {
return {} as AppState;
}
return persistedState;
},
name: 'store_app',
partialize: (state) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- ignore non-persisted state
const { globalExpanded: _, ...rest } = state;
return rest;
},
version: 4,
},
),
);
export const useAppStoreActions = () => useAppStore((state) => state.actions);
export const useSidebarStore = () => useAppStore((state) => state.sidebar);
export const useSidebarRightExpanded = () => useAppStore((state) => state.sidebar.rightExpanded);
export const useSetTitlebar = () => useAppStore((state) => state.actions.setTitleBar);
export const useTitlebarStore = () => useAppStore((state) => state.titlebar);
export const useCommandPalette = () => useAppStore((state) => state.commandPalette);
export const usePageSidebar = (key: string): [boolean, (value: boolean) => void] => {
const isOpen = useAppStore((state) => state.pageSidebar[key] ?? false);
const setPageSidebar = useAppStore((state) => state.actions.setPageSidebar);
const setIsOpen = (value: boolean) => {
setPageSidebar(key, value);
};
return [isOpen, setIsOpen];
};
export const useGlobalExpanded = () => useAppStore((state) => state.globalExpanded);
export const useSetGlobalExpanded = () => useAppStore((state) => state.actions.setGlobalExpanded);
export const useGlobalExpandedState = () => {
const globalExpanded = useGlobalExpanded();
const setGlobalExpanded = useSetGlobalExpanded();
const clearGlobalExpanded = () => setGlobalExpanded(null);
return { clearGlobalExpanded, globalExpanded, setGlobalExpanded };
};