diff --git a/web/src/lib/api/me.test.ts b/web/src/lib/api/me.test.ts new file mode 100644 index 00000000..e37eacb5 --- /dev/null +++ b/web/src/lib/api/me.test.ts @@ -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).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); + }); +}); diff --git a/web/src/lib/api/me.ts b/web/src/lib/api/me.ts new file mode 100644 index 00000000..b6811722 --- /dev/null +++ b/web/src/lib/api/me.ts @@ -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 { + return api.get('/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 + }); +} diff --git a/web/src/lib/api/playlists.ts b/web/src/lib/api/playlists.ts index 9e0ba763..7ef9aae8 100644 --- a/web/src/lib/api/playlists.ts +++ b/web/src/lib/api/playlists.ts @@ -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 { - return (await apiFetch('/api/playlists', { method: 'GET' })) as ListPlaylistsResponse; +export async function listPlaylists(kind?: PlaylistKind): Promise { + const k = kind ?? 'user'; + return (await apiFetch(`/api/playlists?kind=${k}`, { method: 'GET' })) as ListPlaylistsResponse; } export async function getPlaylist(id: string): Promise { @@ -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 }); } diff --git a/web/src/lib/api/queries.ts b/web/src/lib/api/queries.ts index bbf5b421..7cc90b95 100644 --- a/web/src/lib/api/queries.ts +++ b/web/src/lib/api/queries.ts @@ -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) {