Files
Psychotoxical-psysonic/src/store/playlistStore.ts
T
Frank Stellmacher f7b2799d39 refactor(api): F.51 — extract playlists + play queue + radio + statistics (#616)
Four more domain-eng modules out of `api/subsonic.ts`:

- `subsonicPlaylists.ts` — `getPlaylists`/`getPlaylist`/`createPlaylist`
  /`updatePlaylist`/`updatePlaylistMeta`/`uploadPlaylistCoverArt`/
  `uploadArtistImage`/`deletePlaylist`. The two upload helpers use
  the Tauri-side CORS bypass (`upload_playlist_cover` /
  `upload_artist_image`).
- `subsonicPlayQueue.ts` — `getPlayQueue`/`savePlayQueue`.
- `subsonicRadio.ts` — Internet Radio CRUD (4 fns), Tauri-side cover
  art ops (3 fns), RadioBrowser search/top + `fetchUrlBytes`.
- `subsonicStatistics.ts` — `fetchStatisticsLibraryAggregates`/
  `fetchStatisticsOverview`/`fetchStatisticsFormatSample` with their
  three per-server-folder caches and the `statisticsPageCacheKey`
  helper. `STATS_CACHE_TTL` renamed from the misleadingly-shared
  `RATING_CACHE_TTL` constant.

Also: drop ~20 now-unused type/value imports from `subsonic.ts`, trim
three orphan jsdoc comments left behind by earlier slices, fix four
`vi.mock` targets (`queueSync`, `playerStore.persistence`) plus the
dynamic `await import('../api/subsonic')` calls in `ContextMenu.tsx`
to point at the new module paths.

Pure code-move. subsonic.ts: 561 → 144 LOC (−417). What's left:
ping/pingWithCredentials/probeInstantMix/scheduleInstantMixProbe +
internal `apiWithCredentials`/`restBaseFromUrl`.
2026-05-13 01:03:13 +02:00

62 lines
2.0 KiB
TypeScript

import { getPlaylists } from '../api/subsonicPlaylists';
import type { SubsonicPlaylist } from '../api/subsonicTypes';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { createPlaylist as apiCreatePlaylist } from '../api/subsonicPlaylists';
interface PlaylistStore {
recentIds: string[];
playlists: SubsonicPlaylist[];
playlistsLoading: boolean;
lastModified: Record<string, number>;
touchPlaylist: (id: string) => void;
removeId: (id: string) => void;
fetchPlaylists: () => Promise<void>;
createPlaylist: (name: string, songIds?: string[]) => Promise<SubsonicPlaylist | null>;
addPlaylist: (playlist: SubsonicPlaylist) => void;
}
export const usePlaylistStore = create<PlaylistStore>()(
persist(
(set, get) => ({
recentIds: [],
playlists: [],
playlistsLoading: false,
lastModified: {},
touchPlaylist: (id) =>
set((s) => ({
recentIds: [id, ...s.recentIds.filter((x) => x !== id)].slice(0, 50),
lastModified: { ...s.lastModified, [id]: Date.now() },
})),
removeId: (id) =>
set((s) => ({ recentIds: s.recentIds.filter((x) => x !== id) })),
fetchPlaylists: async () => {
set({ playlistsLoading: true });
try {
const playlists = await getPlaylists();
set({ playlists, playlistsLoading: false });
} catch {
set({ playlistsLoading: false });
}
},
createPlaylist: async (name: string, songIds?: string[]) => {
try {
const playlist = await apiCreatePlaylist(name, songIds);
set((s) => ({
playlists: [...s.playlists, playlist],
recentIds: [playlist.id, ...s.recentIds.filter((x) => x !== playlist.id)].slice(0, 50),
}));
return playlist;
} catch {
return null;
}
},
addPlaylist: (playlist) => {
set((s) => ({
playlists: [...s.playlists, playlist],
}));
},
}),
{ name: 'psysonic_playlists_recent' }
)
);