From d3b8c4cd01019da4f36a01f6be77fdcce4924d18 Mon Sep 17 00:00:00 2001 From: rays Date: Fri, 6 Nov 2026 23:44:51 +0000 Subject: [PATCH] A short Unread list read through no longer says more to load (#385) hasMore() compared nextOffset(), which on Unread counts only the loaded items still unread, with S.total from the answer: once they were read it was 0 against that total, so the page asked for a page that came back empty and took its total, 0, with the items still on screen. Whether more is left now comes from the last answer itself, its offset and items against its total; reading takes an item off the server's list and off nextOffset() alike, so what that answer said is left stays true. Co-Authored-By: Claude Opus 5.5 --- tests/page-unit.js | 10 +++++++++- web/CHANGELOG.md | 1 + web/src/items.ts | 5 ++++- web/src/types.ts | 2 ++ web/src/util.ts | 2 +- 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/page-unit.js b/tests/page-unit.js index 48c0956..0d7dc8c 100644 --- a/tests/page-unit.js +++ b/tests/page-unit.js @@ -268,10 +268,18 @@ test('the next page starts after the unread ones on the Unread tab, after all of run(`S.filter = 'unread'`); // Two read here are off the server's unread list: starting at 3 would skip two unread ones (#383). assert.equal(run('nextOffset()'), 1); - assert.equal(run('hasMore()'), true); run(`S.filter = 'all'`); assert.equal(run('nextOffset()'), 3); +}); + +test('a short Unread list read through says nothing more to load (#385)', () => { + run(`S.filter = 'unread'; S.offset = 0`); + run(`showList({total: 2, entries: [{guid:'a',read:false,enclosures:[]},{guid:'b',read:false,enclosures:[]}]}, false, 0)`); + run(`S.entries.forEach(e => e.read = true)`); + // nextOffset() is 0 now, under the total of 2, which once read as more to load. assert.equal(run('hasMore()'), false); + run(`S.offset = 50; showList({total: 120, entries: Array.from({length: 50}, (_, i) => ({guid: 'p' + i, read: false, enclosures: []}))}, true, 0)`); + assert.equal(run('hasMore()'), true); }); test('a reader is stuck only when a drag up or down had room to move it and did not (#384)', () => { diff --git a/web/CHANGELOG.md b/web/CHANGELOG.md index 7917d95..2058e3d 100644 --- a/web/CHANGELOG.md +++ b/web/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Swiping, or j, through more than 50 items no longer stops at the 50th until a reload: the next ones are fetched as you near the end, and Load more on the Unread tab no longer skips as many unread items as you had read (#383). +- On the Unread tab, a short list read through no longer asks the server for more that is not there, or shows a count of 0 items beside the items still on screen (#385). - ipx-web no longer logs a deprecation warning about its metrics setting at every start (#381). - A tap's time in the traces ends with the last answer to what it set off, not with a refresh made seconds later (#379). - Switching away from the page a minute after using it no longer stops it with "Maximum call stack size exceeded" (#378). diff --git a/web/src/items.ts b/web/src/items.ts index 0f29414..60b04d2 100644 --- a/web/src/items.ts +++ b/web/src/items.ts @@ -104,6 +104,9 @@ async function loadEntries(append?: boolean, quiet?: boolean){ /// A list's page into S and onto the screen. function showList(r: ListPage, append: boolean, asked: number){ S.total=r.total; + // Reading an item takes it off the server's Unread list and off nextOffset() alike, so what is + // left there stays as this answer says; S.total against nextOffset() said more once read (#385). + S.more=(append?S.offset:0)+r.entries.length!e.read).length : S.entries.length; } -const hasMore=()=>nextOffset()S.more; /// Turning near the end of what is loaded fetches the next page behind: only the list's Load more /// button did, and a swipe or j stopped dead at the 50th item until a reload (#383). let loadingAhead=false; diff --git a/web/src/types.ts b/web/src/types.ts index 39173f9..8fe7d6b 100644 --- a/web/src/types.ts +++ b/web/src/types.ts @@ -136,6 +136,8 @@ interface State { entries: Entry[]; total: number; offset: number; + /// Whether the server has more after the last page it sent, by that answer's own numbers. + more: boolean; filter: string; q: string; /// The selected item's guid. diff --git a/web/src/util.ts b/web/src/util.ts index 8158ebe..22fbf59 100644 --- a/web/src/util.ts +++ b/web/src/util.ts @@ -290,7 +290,7 @@ const S: State = { // Which feed (or place) and which tab were open last time, so a refresh lands back where // you were instead of jumping to the first feed alphabetically. feed:(()=>{ try{ return localStorage.getItem('ipx.feed'); }catch{ return null; } })(), - entries:[], total:0, offset:0, + entries:[], total:0, offset:0, more:false, filter:(()=>{ try{ return localStorage.getItem('ipx.filter'); }catch{ return null; } })()||'all', q:'', sel:null, me:null, // The item table's order, kept across visits. The server sorts: a list arrives fifty at a time.