mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 19:01:08 +02:00
Combines LightWrite (beat/lyrics editor) and Mukke (iOS music player) into a single web-based music workspace app. Archives the old Mukke mobile app. - Rename: @lightwrite/* → @mukke/*, all branding, configs, Dockerfiles - New DB schemas: songs, playlists, playlist_songs + songId FK on projects - New backend modules: SongModule, PlaylistModule, LibraryModule - New web: app shell with sidebar, library (songs/albums/artists/genres), web player (queue/shuffle/repeat/MediaSession), playlists, search, upload, dashboard, album/artist/genre detail pages - Auth: add forgot-password + reset-password pages, extend auth store - Tests: 40 backend unit tests (song, playlist, library services) - Config: env generation, MinIO bucket, docker-compose prod, cloudflare - Docs: update CLAUDE.md, auth guidelines with SvelteKit checklist Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
95 lines
2.1 KiB
TypeScript
95 lines
2.1 KiB
TypeScript
import { create } from 'zustand';
|
|
|
|
import type { Album, Artist, Genre, LibraryTab, Song, SortDirection, SortField } from '~/types';
|
|
import * as libraryService from '~/services/libraryService';
|
|
|
|
interface LibraryState {
|
|
songs: Song[];
|
|
albums: Album[];
|
|
artists: Artist[];
|
|
genres: Genre[];
|
|
activeTab: LibraryTab;
|
|
sortField: SortField;
|
|
sortDirection: SortDirection;
|
|
isLoading: boolean;
|
|
songCount: number;
|
|
|
|
setActiveTab: (tab: LibraryTab) => void;
|
|
setSortField: (field: SortField) => void;
|
|
setSortDirection: (dir: SortDirection) => void;
|
|
loadSongs: () => Promise<void>;
|
|
loadAlbums: () => Promise<void>;
|
|
loadArtists: () => Promise<void>;
|
|
loadGenres: () => Promise<void>;
|
|
loadAll: () => Promise<void>;
|
|
toggleFavorite: (id: string) => Promise<void>;
|
|
}
|
|
|
|
export const useLibraryStore = create<LibraryState>((set, get) => ({
|
|
songs: [],
|
|
albums: [],
|
|
artists: [],
|
|
genres: [],
|
|
activeTab: 'songs',
|
|
sortField: 'title',
|
|
sortDirection: 'asc',
|
|
isLoading: false,
|
|
songCount: 0,
|
|
|
|
setActiveTab: (tab) => set({ activeTab: tab }),
|
|
|
|
setSortField: (field) => {
|
|
set({ sortField: field });
|
|
get().loadSongs();
|
|
},
|
|
|
|
setSortDirection: (dir) => {
|
|
set({ sortDirection: dir });
|
|
get().loadSongs();
|
|
},
|
|
|
|
loadSongs: async () => {
|
|
const { sortField, sortDirection } = get();
|
|
const songs = await libraryService.getAllSongs(
|
|
sortField,
|
|
sortDirection.toUpperCase() as 'ASC' | 'DESC'
|
|
);
|
|
set({ songs, songCount: songs.length });
|
|
},
|
|
|
|
loadAlbums: async () => {
|
|
const albums = await libraryService.getAlbums();
|
|
set({ albums });
|
|
},
|
|
|
|
loadArtists: async () => {
|
|
const artists = await libraryService.getArtists();
|
|
set({ artists });
|
|
},
|
|
|
|
loadGenres: async () => {
|
|
const genres = await libraryService.getGenres();
|
|
set({ genres });
|
|
},
|
|
|
|
loadAll: async () => {
|
|
set({ isLoading: true });
|
|
try {
|
|
await Promise.all([
|
|
get().loadSongs(),
|
|
get().loadAlbums(),
|
|
get().loadArtists(),
|
|
get().loadGenres(),
|
|
]);
|
|
} finally {
|
|
set({ isLoading: false });
|
|
}
|
|
},
|
|
|
|
toggleFavorite: async (id) => {
|
|
const newFav = await libraryService.toggleFavorite(id);
|
|
set((state) => ({
|
|
songs: state.songs.map((s) => (s.id === id ? { ...s, favorite: newFav } : s)),
|
|
}));
|
|
},
|
|
}));
|