feat(detail): mark read chapters and count new ones on the manga page
On the manga detail page, dim chapters at or before the reader's last-read chapter (with an accent check + screen-reader "Read." label) and show a "N new since last read" badge counting chapters that have landed since. All client-side from the already-loaded chapter list and per-manga read progress — no extra requests. The count reflects the loaded (paginated) chapter list. Logic lives in a pure, unit-tested chapterProgress helper; e2e covers the mid-series and never-opened cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
33
frontend/src/lib/chapterProgress.test.ts
Normal file
33
frontend/src/lib/chapterProgress.test.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { isChapterRead, countNewChapters } from './chapterProgress';
|
||||
|
||||
describe('isChapterRead', () => {
|
||||
it('marks chapters at or before the last-read number as read', () => {
|
||||
expect(isChapterRead(1, 3)).toBe(true);
|
||||
expect(isChapterRead(3, 3)).toBe(true);
|
||||
});
|
||||
|
||||
it('leaves later chapters unread', () => {
|
||||
expect(isChapterRead(4, 3)).toBe(false);
|
||||
});
|
||||
|
||||
it('marks nothing when the last-read position is unknown', () => {
|
||||
expect(isChapterRead(1, null)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('countNewChapters', () => {
|
||||
const chapters = [{ number: 1 }, { number: 2 }, { number: 3 }];
|
||||
|
||||
it('counts chapters past the last-read number', () => {
|
||||
expect(countNewChapters(chapters, 1)).toBe(2);
|
||||
});
|
||||
|
||||
it('is zero when caught up to the latest', () => {
|
||||
expect(countNewChapters(chapters, 3)).toBe(0);
|
||||
});
|
||||
|
||||
it('is zero when the last-read position is unknown', () => {
|
||||
expect(countNewChapters(chapters, null)).toBe(0);
|
||||
});
|
||||
});
|
||||
28
frontend/src/lib/chapterProgress.ts
Normal file
28
frontend/src/lib/chapterProgress.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
// Pure helpers for reflecting a reader's personal progress on a manga's
|
||||
// chapter list. Kept UI-free so the read/unread and "new since last read"
|
||||
// logic is unit-testable without rendering the detail page.
|
||||
|
||||
export type NumberedChapter = { number: number };
|
||||
|
||||
/**
|
||||
* A chapter counts as read once its number is at or before the reader's
|
||||
* last-read chapter number. An unknown last-read position (guest, never
|
||||
* opened, or a deleted chapter) marks nothing — we never guess.
|
||||
*/
|
||||
export function isChapterRead(chapterNumber: number, lastReadNumber: number | null): boolean {
|
||||
return lastReadNumber != null && chapterNumber <= lastReadNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* How many of the given chapters sit past the reader's last-read chapter
|
||||
* (by number) — the personal "new since you last read" count. `0` when the
|
||||
* last-read position is unknown. Counts over the provided (loaded) list, so
|
||||
* for a paginated chapter list it reflects what's currently shown.
|
||||
*/
|
||||
export function countNewChapters(
|
||||
chapters: NumberedChapter[],
|
||||
lastReadNumber: number | null
|
||||
): number {
|
||||
if (lastReadNumber == null) return 0;
|
||||
return chapters.reduce((n, c) => (c.number > lastReadNumber ? n + 1 : n), 0);
|
||||
}
|
||||
Reference in New Issue
Block a user