feat(web/m7-352): kind-aware createPlaylistsQuery + system-playlists-status helper

This commit is contained in:
2026-05-04 18:30:43 -04:00
parent 56f0998f84
commit 886903ae27
4 changed files with 57 additions and 7 deletions
+21
View File
@@ -0,0 +1,21 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { getSystemPlaylistsStatus } from './me';
vi.mock('./client', () => ({
api: { get: vi.fn() }
}));
import { api } from './client';
describe('me API', () => {
beforeEach(() => vi.clearAllMocks());
it('getSystemPlaylistsStatus GETs the correct path', async () => {
(api.get as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
in_flight: false, last_run_at: null, last_error: null
});
const got = await getSystemPlaylistsStatus();
expect(api.get).toHaveBeenCalledWith('/api/me/system-playlists-status');
expect(got.in_flight).toBe(false);
});
});
+24
View File
@@ -0,0 +1,24 @@
import { createQuery } from '@tanstack/svelte-query';
import { api } from './client';
import { qk } from './queries';
export type SystemPlaylistsStatus = {
in_flight: boolean;
last_run_at: string | null;
last_error: string | null;
};
export async function getSystemPlaylistsStatus(): Promise<SystemPlaylistsStatus> {
return api.get<SystemPlaylistsStatus>('/api/me/system-playlists-status');
}
// Refresh aggressively while a build is in_flight so the building → built
// transition appears live on the home page. Stale-time is short; manual
// invalidations on the server's POST endpoints are out of scope.
export function createSystemPlaylistsStatusQuery() {
return createQuery({
queryKey: qk.systemPlaylistsStatus(),
queryFn: getSystemPlaylistsStatus,
staleTime: 10_000
});
}
+8 -5
View File
@@ -3,13 +3,16 @@ import { apiFetch } from './client';
import { qk } from './queries';
import type { Playlist, PlaylistDetail } from './types';
export type PlaylistKind = 'user' | 'system' | 'all';
export type ListPlaylistsResponse = {
owned: Playlist[];
public: Playlist[];
};
export async function listPlaylists(): Promise<ListPlaylistsResponse> {
return (await apiFetch('/api/playlists', { method: 'GET' })) as ListPlaylistsResponse;
export async function listPlaylists(kind?: PlaylistKind): Promise<ListPlaylistsResponse> {
const k = kind ?? 'user';
return (await apiFetch(`/api/playlists?kind=${k}`, { method: 'GET' })) as ListPlaylistsResponse;
}
export async function getPlaylist(id: string): Promise<PlaylistDetail> {
@@ -73,10 +76,10 @@ export async function reorderPlaylist(playlistID: string, orderedPositions: numb
})) as PlaylistDetail;
}
export function createPlaylistsQuery() {
export function createPlaylistsQuery(kind?: PlaylistKind) {
return createQuery({
queryKey: qk.playlists(),
queryFn: listPlaylists,
queryKey: qk.playlists(kind),
queryFn: () => listPlaylists(kind),
staleTime: 30_000
});
}
+4 -2
View File
@@ -39,8 +39,10 @@ export const qk = {
home: () => ['home'] as const,
albumsAlpha: () => ['albumsAlpha'] as const,
artistTracks: (artistId: string) => ['artistTracks', artistId] as const,
playlists: () => ['playlists'] as const,
playlist: (id: string) => ['playlist', id] as const,
playlists: (kind?: 'user' | 'system' | 'all') =>
['playlists', { kind: kind ?? 'user' }] as const,
playlist: (id: string) => ['playlist', id] as const,
systemPlaylistsStatus: () => ['systemPlaylistsStatus'] as const,
};
export function createArtistsQuery(sort: ArtistSort) {