Continue row: only manga with something left after the last chapter read

GET /api/library returns `left` per manga: chapters after the newest saved
position that are unfinished, unread, or were last read more than 7 days
before it (a reread from the start). The continue row lists manga with
left > 0, so caught-up series drop out until a new chapter arrives.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MaashkinoozheandClaude Opus 5.5 committed 2026-11-01 11:27:02 +01:00
1 parent dddf9bb4ac
commit 9b529d7709
5 files changed
+110 -6

No files matched your search

+13 -2
View File
@@ -465,6 +465,17 @@ function continueOf(m, prog, s) {
const next = m.chapters.slice(m.chapters.indexOf(last.ch) + 1).find(ok);
return next ? { c: next.id, at, text: 'Read next · ' + next.label } : { c: last.ch.id, v: last.p.v, at, text: 'Read again · ' + last.ch.label };
}
// A chapter after the head counts as read in this pass only if it is done and was saved within this
// gap before the head; older ones belong to an earlier pass, so the head is a reread.
const REREAD_GAP_MS = 7 * 24 * 3600 * 1000;
// Chapters still ahead of the head: never finished, or last read in an earlier pass. 0 = caught up.
function leftOf(m, prog, s) {
const last = lastOf(m, prog);
if (!last) return 0;
const after = m.chapters.slice(m.chapters.indexOf(last.ch) + 1).filter(c => !(s.skipExtras && c.extra));
return after.filter(c => { const p = prog[c.id]; return !p || !p.done || last.p.t - p.t > REREAD_GAP_MS; }).length
+ (last.p.done ? 0 : 1);
}
// What a reader tab compares itself with (stale reading sessions, docs/architecture.md).
function latestOf(m, prog, s) {
const last = lastOf(m, prog);
@@ -620,7 +631,7 @@ const routes = {
'GET /api/library': ({ u }) => {
const prog = db.progress[u.name] || {}, fol = db.follows[u.name] || {}, notes = (db.notes[u.name] || []).filter(n => !n.read);
const showStory = settingsOf(u).readingStory;
const s = settingsOf(u), showStory = s.readingStory;
const mangas = lib.mangas.map(m => {
const p = prog[m.id] || {}, meta = m.meta || {}, release = firstReleaseOf(meta);
return {
@@ -630,7 +641,7 @@ const routes = {
tags: Array.isArray(meta.tags) ? meta.tags : (meta.tags ? [String(meta.tags)] : []), status: meta.status || '', year: meta.year || '',
firstRelease: release.label, firstReleaseAt: release.time,
read: m.chapters.filter(c => p[c.id] && p[c.id].done).length,
lastRead: Object.values(p).reduce((t, x) => Math.max(t, x.t || 0), 0),
lastRead: Object.values(p).reduce((t, x) => Math.max(t, x.t || 0), 0), left: leftOf(m, p, s),
following: !!fol[m.id], unread: notes.filter(n => n.m === m.id).length,
reading: showStory ? mangaInsight(mangaReadingOf(u, m), m.chapters.map(ch => ch.id)) : null,
};