feat(web): add TanStack Query helpers for library endpoints

qk.{artists,artist,album} key generators and create*Query wrappers
with shared page size, sort-aware keys, and infinite-query plumbing
for /api/artists.
This commit is contained in:
2026-04-23 18:17:34 -04:00
parent 4cd67405c9
commit ce6b79ec95
2 changed files with 58 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
import { describe, expect, test } from 'vitest';
import { qk } from './queries';
describe('qk key generators', () => {
test('artists key includes sort discriminator', () => {
expect(qk.artists('alpha')).toEqual(['artists', { sort: 'alpha' }]);
expect(qk.artists('newest')).toEqual(['artists', { sort: 'newest' }]);
});
test('artist key tuple', () => {
expect(qk.artist('abc')).toEqual(['artist', 'abc']);
});
test('album key tuple', () => {
expect(qk.album('xyz')).toEqual(['album', 'xyz']);
});
});
+41
View File
@@ -0,0 +1,41 @@
import { createQuery, createInfiniteQuery } from '@tanstack/svelte-query';
import { api } from './client';
import type { ArtistRef, ArtistDetail, AlbumDetail, Page } from './types';
export type ArtistSort = 'alpha' | 'newest';
export const ARTIST_PAGE_SIZE = 50;
export const qk = {
artists: (sort: ArtistSort) => ['artists', { sort }] as const,
artist: (id: string) => ['artist', id] as const,
album: (id: string) => ['album', id] as const,
};
export function createArtistsQuery(sort: ArtistSort) {
return createInfiniteQuery({
queryKey: qk.artists(sort),
queryFn: ({ pageParam = 0 }) =>
api.get<Page<ArtistRef>>(
`/api/artists?sort=${sort}&limit=${ARTIST_PAGE_SIZE}&offset=${pageParam}`
),
initialPageParam: 0,
getNextPageParam: (last) => {
const loaded = last.offset + last.items.length;
return loaded >= last.total ? undefined : loaded;
},
});
}
export function createArtistQuery(id: string) {
return createQuery({
queryKey: qk.artist(id),
queryFn: () => api.get<ArtistDetail>(`/api/artists/${id}`),
});
}
export function createAlbumQuery(id: string) {
return createQuery({
queryKey: qk.album(id),
queryFn: () => api.get<AlbumDetail>(`/api/albums/${id}`),
});
}