feat(web): add home/albums/artists API clients for M6a
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
api: { get: vi.fn() }
|
||||
}));
|
||||
|
||||
import { listAlbumsAlpha } from './albums';
|
||||
import { qk } from './queries';
|
||||
import { api } from './client';
|
||||
|
||||
afterEach(() => vi.clearAllMocks());
|
||||
|
||||
describe('albums client', () => {
|
||||
test('listAlbumsAlpha hits /api/library/albums with paging', async () => {
|
||||
(api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
||||
items: [], total: 0, limit: 50, offset: 0
|
||||
});
|
||||
await listAlbumsAlpha(50, 0);
|
||||
expect(api.get).toHaveBeenCalledWith('/api/library/albums?limit=50&offset=0');
|
||||
});
|
||||
|
||||
test('listAlbumsAlpha honors custom limit and offset', async () => {
|
||||
(api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
||||
items: [], total: 0, limit: 25, offset: 100
|
||||
});
|
||||
await listAlbumsAlpha(25, 100);
|
||||
expect(api.get).toHaveBeenCalledWith('/api/library/albums?limit=25&offset=100');
|
||||
});
|
||||
|
||||
test('qk.albumsAlpha key is stable', () => {
|
||||
expect(qk.albumsAlpha()).toEqual(['albumsAlpha']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import { createInfiniteQuery } from '@tanstack/svelte-query';
|
||||
import { api } from './client';
|
||||
import { qk } from './queries';
|
||||
import type { AlbumRef, Page } from './types';
|
||||
|
||||
export const ALBUM_PAGE_SIZE = 50;
|
||||
|
||||
export async function listAlbumsAlpha(limit: number, offset: number): Promise<Page<AlbumRef>> {
|
||||
return api.get<Page<AlbumRef>>(`/api/library/albums?limit=${limit}&offset=${offset}`);
|
||||
}
|
||||
|
||||
export function createAlbumsAlphaInfiniteQuery() {
|
||||
return createInfiniteQuery({
|
||||
queryKey: qk.albumsAlpha(),
|
||||
queryFn: ({ pageParam = 0 }) => listAlbumsAlpha(ALBUM_PAGE_SIZE, pageParam),
|
||||
initialPageParam: 0,
|
||||
getNextPageParam: (last) => {
|
||||
const loaded = last.offset + last.items.length;
|
||||
return loaded >= last.total ? undefined : loaded;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
api: { get: vi.fn() }
|
||||
}));
|
||||
|
||||
import { listArtistTracks } from './artists';
|
||||
import { qk } from './queries';
|
||||
import { api } from './client';
|
||||
|
||||
afterEach(() => vi.clearAllMocks());
|
||||
|
||||
describe('artists client', () => {
|
||||
test('listArtistTracks hits /api/artists/:id/tracks', async () => {
|
||||
(api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce([]);
|
||||
await listArtistTracks('art-1');
|
||||
expect(api.get).toHaveBeenCalledWith('/api/artists/art-1/tracks');
|
||||
});
|
||||
|
||||
test('qk.artistTracks key includes the artist id', () => {
|
||||
expect(qk.artistTracks('art-1')).toEqual(['artistTracks', 'art-1']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createQuery } from '@tanstack/svelte-query';
|
||||
import { api } from './client';
|
||||
import { qk } from './queries';
|
||||
import type { TrackRef } from './types';
|
||||
|
||||
export async function listArtistTracks(artistId: string): Promise<TrackRef[]> {
|
||||
return api.get<TrackRef[]>(`/api/artists/${artistId}/tracks`);
|
||||
}
|
||||
|
||||
export function createArtistTracksQuery(artistId: string) {
|
||||
return createQuery({
|
||||
queryKey: qk.artistTracks(artistId),
|
||||
queryFn: () => listArtistTracks(artistId),
|
||||
enabled: artistId.length > 0
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
api: { get: vi.fn() }
|
||||
}));
|
||||
|
||||
import { listHome } from './home';
|
||||
import { qk } from './queries';
|
||||
import { api } from './client';
|
||||
import type { HomePayload } from './types';
|
||||
|
||||
afterEach(() => vi.clearAllMocks());
|
||||
|
||||
describe('home client', () => {
|
||||
test('listHome hits /api/home', async () => {
|
||||
const fixture: HomePayload = {
|
||||
recently_added_albums: [],
|
||||
rediscover_albums: [],
|
||||
rediscover_artists: [],
|
||||
most_played_tracks: [],
|
||||
last_played_artists: []
|
||||
};
|
||||
(api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce(fixture);
|
||||
const got = await listHome();
|
||||
expect(api.get).toHaveBeenCalledWith('/api/home');
|
||||
expect(got).toEqual(fixture);
|
||||
});
|
||||
|
||||
test('qk.home key is stable', () => {
|
||||
expect(qk.home()).toEqual(['home']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import { createQuery } from '@tanstack/svelte-query';
|
||||
import { api } from './client';
|
||||
import { qk } from './queries';
|
||||
import type { HomePayload } from './types';
|
||||
|
||||
export async function listHome(): Promise<HomePayload> {
|
||||
return api.get<HomePayload>('/api/home');
|
||||
}
|
||||
|
||||
// staleTime = 60s — repeat home-page mounts within a minute reuse cached
|
||||
// data. Like-state changes, new plays, freshly-added albums surface on
|
||||
// the next refetch.
|
||||
export function createHomeQuery() {
|
||||
return createQuery({
|
||||
queryKey: qk.home(),
|
||||
queryFn: listHome,
|
||||
staleTime: 60_000
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user