feat(web): add search query helpers (summary + 3 facet infinite queries)
createSearchQuery hits /api/search with limit=10 (the main /search page summary). createSearchArtistsInfiniteQuery / createSearchAlbumsInfiniteQuery / createSearchTracksInfiniteQuery each hit /api/search with limit=50 and project to their own facet, used by the per-facet overflow pages. All four are gated by q.length > 0 so empty queries don't fire.
This commit is contained in:
@@ -15,3 +15,18 @@ describe('qk key generators', () => {
|
||||
expect(qk.album('xyz')).toEqual(['album', 'xyz']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('qk search keys', () => {
|
||||
test('search summary key includes q', () => {
|
||||
expect(qk.search('foo')).toEqual(['search', { q: 'foo' }]);
|
||||
});
|
||||
test('searchArtists key includes q', () => {
|
||||
expect(qk.searchArtists('foo')).toEqual(['searchArtists', { q: 'foo' }]);
|
||||
});
|
||||
test('searchAlbums key includes q', () => {
|
||||
expect(qk.searchAlbums('foo')).toEqual(['searchAlbums', { q: 'foo' }]);
|
||||
});
|
||||
test('searchTracks key includes q', () => {
|
||||
expect(qk.searchTracks('foo')).toEqual(['searchTracks', { q: 'foo' }]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import { createQuery, createInfiniteQuery } from '@tanstack/svelte-query';
|
||||
import { api } from './client';
|
||||
import type { ArtistRef, ArtistDetail, AlbumDetail, Page } from './types';
|
||||
import type { ArtistRef, ArtistDetail, AlbumDetail, Page, SearchResponse } from './types';
|
||||
|
||||
export type ArtistSort = 'alpha' | 'newest';
|
||||
export const ARTIST_PAGE_SIZE = 50;
|
||||
export const SEARCH_SUMMARY_LIMIT = 10;
|
||||
export const SEARCH_FACET_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,
|
||||
search: (q: string) => ['search', { q }] as const,
|
||||
searchArtists: (q: string) => ['searchArtists', { q }] as const,
|
||||
searchAlbums: (q: string) => ['searchAlbums', { q }] as const,
|
||||
searchTracks: (q: string) => ['searchTracks', { q }] as const,
|
||||
};
|
||||
|
||||
export function createArtistsQuery(sort: ArtistSort) {
|
||||
@@ -39,3 +45,62 @@ export function createAlbumQuery(id: string) {
|
||||
queryFn: () => api.get<AlbumDetail>(`/api/albums/${id}`),
|
||||
});
|
||||
}
|
||||
|
||||
export function createSearchQuery(q: string) {
|
||||
return createQuery({
|
||||
queryKey: qk.search(q),
|
||||
queryFn: () =>
|
||||
api.get<SearchResponse>(
|
||||
`/api/search?q=${encodeURIComponent(q)}&limit=${SEARCH_SUMMARY_LIMIT}`
|
||||
),
|
||||
enabled: q.length > 0
|
||||
});
|
||||
}
|
||||
|
||||
export function createSearchArtistsInfiniteQuery(q: string) {
|
||||
return createInfiniteQuery({
|
||||
queryKey: qk.searchArtists(q),
|
||||
queryFn: ({ pageParam = 0 }) =>
|
||||
api.get<SearchResponse>(
|
||||
`/api/search?q=${encodeURIComponent(q)}&limit=${SEARCH_FACET_PAGE_SIZE}&offset=${pageParam}`
|
||||
).then((r) => r.artists),
|
||||
initialPageParam: 0,
|
||||
getNextPageParam: (last) => {
|
||||
const loaded = last.offset + last.items.length;
|
||||
return loaded >= last.total ? undefined : loaded;
|
||||
},
|
||||
enabled: q.length > 0
|
||||
});
|
||||
}
|
||||
|
||||
export function createSearchAlbumsInfiniteQuery(q: string) {
|
||||
return createInfiniteQuery({
|
||||
queryKey: qk.searchAlbums(q),
|
||||
queryFn: ({ pageParam = 0 }) =>
|
||||
api.get<SearchResponse>(
|
||||
`/api/search?q=${encodeURIComponent(q)}&limit=${SEARCH_FACET_PAGE_SIZE}&offset=${pageParam}`
|
||||
).then((r) => r.albums),
|
||||
initialPageParam: 0,
|
||||
getNextPageParam: (last) => {
|
||||
const loaded = last.offset + last.items.length;
|
||||
return loaded >= last.total ? undefined : loaded;
|
||||
},
|
||||
enabled: q.length > 0
|
||||
});
|
||||
}
|
||||
|
||||
export function createSearchTracksInfiniteQuery(q: string) {
|
||||
return createInfiniteQuery({
|
||||
queryKey: qk.searchTracks(q),
|
||||
queryFn: ({ pageParam = 0 }) =>
|
||||
api.get<SearchResponse>(
|
||||
`/api/search?q=${encodeURIComponent(q)}&limit=${SEARCH_FACET_PAGE_SIZE}&offset=${pageParam}`
|
||||
).then((r) => r.tracks),
|
||||
initialPageParam: 0,
|
||||
getNextPageParam: (last) => {
|
||||
const loaded = last.offset + last.items.length;
|
||||
return loaded >= last.total ? undefined : loaded;
|
||||
},
|
||||
enabled: q.length > 0
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user