refactor(web/api): offsetGetNextPageParam helper; paging sites migrated (W4)

Final task of PR2 DRY pass. Extracts the verbatim
`offset + items.length >= total` math repeated across 8
createInfiniteQuery sites into a single Page<T>-shape helper.

The task spec described a bare-array helper signature
(lastPage: T[], pageSize-based stop), but no call site in this
repo uses that shape — every paged endpoint returns a Page<T>
envelope. The helper is named pageGetNextPageParam and matches
the actual canonical shape so the 8 copies could collapse.

Migrated:
- likes.ts: 3 sites (TrackRef, AlbumRef, ArtistRef)
- albums.ts: 1 site (AlbumRef)
- queries.ts: 4 sites (artists + 3 search facets)

history.ts left alone — uses has_more/total, different shape.
This commit is contained in:
2026-05-08 07:33:07 -04:00
parent c41bc5a99d
commit 6d16aa2de4
5 changed files with 86 additions and 33 deletions
+14 -17
View File
@@ -1,6 +1,15 @@
import { createQuery, createInfiniteQuery } from '@tanstack/svelte-query';
import { api } from './client';
import type { ArtistRef, ArtistDetail, AlbumDetail, Page, SearchResponse } from './types';
import { pageGetNextPageParam } from './paging';
import type {
ArtistRef,
ArtistDetail,
AlbumRef,
AlbumDetail,
Page,
SearchResponse,
TrackRef
} from './types';
export type ArtistSort = 'alpha' | 'newest';
export const ARTIST_PAGE_SIZE = 50;
@@ -60,10 +69,7 @@ export function createArtistsQuery(sort: ArtistSort) {
`/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;
},
getNextPageParam: pageGetNextPageParam<ArtistRef>(),
});
}
@@ -100,10 +106,7 @@ export function createSearchArtistsInfiniteQuery(q: string) {
`/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;
},
getNextPageParam: pageGetNextPageParam<ArtistRef>(),
enabled: q.length > 0
});
}
@@ -116,10 +119,7 @@ export function createSearchAlbumsInfiniteQuery(q: string) {
`/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;
},
getNextPageParam: pageGetNextPageParam<AlbumRef>(),
enabled: q.length > 0
});
}
@@ -132,10 +132,7 @@ export function createSearchTracksInfiniteQuery(q: string) {
`/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;
},
getNextPageParam: pageGetNextPageParam<TrackRef>(),
enabled: q.length > 0
});
}