feat: fetch the next page of books before the reader hits the bottom

The prefetch zone is a viewport deep rather than 200px, and the observer
re-observes after a page lands so a chain of loads is not cut short.
This commit is contained in:
2026-08-17 13:48:40 -04:00
parent 55e00ba960
commit 0220f2d970
2 changed files with 45 additions and 16 deletions
+27 -10
View File
@@ -28,6 +28,8 @@ export class BookCollectionState {
public moreBooks = $state(false);
private currentBookPage = $state(1);
public loading = $state(false);
/** A page append, as opposed to `loading`, which replaces the whole list. */
public loadingMore = $state(false);
/**
* Whether the reader has loaded past the first page. Anything that refetches
@@ -165,22 +167,37 @@ export class BookCollectionState {
});
this.books = [...result.items];
this.moreBooks = result.total > result.items.length;
this.currentBookPage = 1;
this.loading = false;
}
/**
* Prefetching means the trigger can fire again while a page is still in
* flight, so the guard is here rather than in the observer — every caller
* gets it, and a second call is dropped instead of duplicating a page.
*/
async loadMoreBooks() {
const result = await this.ops.listBooks({
currentPage: this.currentBookPage + 1,
pageSize: 50,
sortOrder: this.sortOrder,
orderBy: this.orderBy,
...this.filters
});
if (this.loadingMore || !this.moreBooks) return;
this.books = [...this.books, ...result.items];
this.moreBooks = result.total > this.books.length;
this.currentBookPage++;
this.loadingMore = true;
try {
const result = await this.ops.listBooks({
currentPage: this.currentBookPage + 1,
pageSize: 50,
sortOrder: this.sortOrder,
orderBy: this.orderBy,
...this.filters
});
this.books = [...this.books, ...result.items];
// An empty page means the count we were given was stale; stop asking
// rather than loop on a page that never grows the list.
this.moreBooks = result.items.length > 0 && result.total > this.books.length;
this.currentBookPage++;
} finally {
this.loadingMore = false;
}
}
updateBooks(books: PaginatedResponse<Book>) {