From 9a6591fa59c4da737d22e27ee9cf8314222dc697 Mon Sep 17 00:00:00 2001 From: Jonathan Finley Date: Fri, 13 Mar 2026 23:39:53 -0400 Subject: [PATCH] fix: preserve keyset totals when count is skipped --- client/components/app/LazyBookshelf.vue | 5 ++- .../tests/components/app/LazyBookshelf.cy.js | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/client/components/app/LazyBookshelf.vue b/client/components/app/LazyBookshelf.vue index bcc8c4878..062b54b46 100644 --- a/client/components/app/LazyBookshelf.vue +++ b/client/components/app/LazyBookshelf.vue @@ -350,7 +350,10 @@ export default { this.pageCursors = { 0: null } }, updateVisibleBrowseRange(totalEntities = this.serverTotalEntities) { - this.serverTotalEntities = Number(totalEntities) || 0 + const hasExplicitTotal = totalEntities !== null && totalEntities !== undefined + if (hasExplicitTotal) { + this.serverTotalEntities = Number(totalEntities) || 0 + } const visibleTotalEntities = this.deepScrollBlocked ? Math.min(this.serverTotalEntities, this.maxOffsetPages * this.booksPerFetch) diff --git a/client/cypress/tests/components/app/LazyBookshelf.cy.js b/client/cypress/tests/components/app/LazyBookshelf.cy.js index 16fb7e0a2..c3b35327a 100644 --- a/client/cypress/tests/components/app/LazyBookshelf.cy.js +++ b/client/cypress/tests/components/app/LazyBookshelf.cy.js @@ -166,6 +166,44 @@ describe('LazyBookshelf', () => { }) }) + it('preserves visible totals when keyset follow-up responses omit total', () => { + const requestUrls = [] + const getStub = cy.stub().callsFake((url) => { + requestUrls.push(url) + + if (requestUrls.length === 1) { + return Promise.resolve({ + results: [{ id: 'item-1', mediaType: 'book', media: { metadata: { title: 'Alpha' } } }], + total: 4, + nextCursor: 'cursor-1', + paginationMode: 'keyset', + isCountDeferred: true + }) + } + + return Promise.resolve({ + results: [{ id: 'item-2', mediaType: 'book', media: { metadata: { title: 'Beta' } } }], + total: null, + nextCursor: null, + paginationMode: 'keyset', + isCountDeferred: true + }) + }) + + mountBookshelf(getStub, { initialPagesToLoad: 1 }).then(({ wrapper }) => { + cy.wrap(null) + .then(() => wrapper.vm.loadPage(1)) + .then(() => { + expect(wrapper.vm.serverTotalEntities).to.equal(4) + expect(wrapper.vm.totalEntities).to.equal(4) + expect(wrapper.vm.totalShelves).to.equal(4) + expect(wrapper.vm.entities).to.have.length(4) + }) + }) + + cy.get('[id^="shelf-"]').should('have.length', 4) + }) + it('keeps offset fallback mode explicit and caps deep endless scroll', () => { const requestUrls = [] const getStub = cy.stub().callsFake((url) => {