fix(history): count distinct new chapter numbers, authoritative on detail

new_chapters_count over-counted when scanlations share a chapter number
((manga_id, number) is non-unique, migration 0013): COUNT(*) tallied rows,
so duplicate-numbered chapters inflated the badge. Switch both the list and
per-manga read-progress queries to COUNT(DISTINCT number), and drop the dead
COALESCE (an empty set counts as 0, and the NULL-number case is handled by
the predicate). Fix the misleading comment.

Expose new_chapters_count on GET /me/read-progress/:manga_id and drive the
detail page's "N new" badge from it, instead of recomputing over the
paginated chapter list — which under-counted series past 50 chapters and
disagreed with the homepage shelf. Read markers stay client-side over the
loaded chapters and are documented as by-number (all we persist).

Adds duplicate-scanlation tests at both endpoints.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-04 21:48:36 +02:00
parent f1349100d7
commit 3824eaafb2
12 changed files with 153 additions and 61 deletions

View File

@@ -13,7 +13,7 @@
} from '$lib/api/mangas';
import { resyncManga } from '$lib/api/admin';
import { chapterLabel } from '$lib/api/chapters';
import { isChapterRead, countNewChapters } from '$lib/chapterProgress';
import { isChapterRead } from '$lib/chapterProgress';
import { formatBytes } from '$lib/upload-validation';
import { listTags, type Tag } from '$lib/api/tags';
import type { ContentWarning } from '$lib/api/page_tags';
@@ -70,11 +70,16 @@
: null
);
/** The reader's last-read chapter number, used to mark chapters as read
* and count what's landed since. `null` for guests / never-read. */
/** The reader's last-read chapter number, used to mark chapters read.
* `null` for guests / never-read. Read-state is by chapter *number*
* (all we persist), so re-reading one scanlation marks same-numbered
* scanlations read too — intended, since "chapter N" is read. */
const lastReadNumber = $derived(readProgress?.chapter_number ?? null);
/** New chapters since last read, over the loaded chapter list. */
const newChapterCount = $derived(countNewChapters(chapters, lastReadNumber));
/** New chapters since last read. Authoritative backend count over ALL
* chapters (distinct numbers) — not recomputed over the paginated
* `chapters` list, which would under-count long series and disagree
* with the homepage shelf. `0` for guests / never-read. */
const newChapterCount = $derived(readProgress?.new_chapters_count ?? 0);
const authors = $derived<AuthorRef[]>(manga.authors);
const genres = $derived<GenreRef[]>(manga.genres);