Reading progress
- New ReadingProgressButton client component that reads/writes
localStorage key "sunnymh:last-read:{slug}"
- PageReader writes the currently-visible chapter as the user scrolls
through continuous chapters
- Manga detail CTA now reads: "开始阅读" on first visit, or
"继续阅读 · #{N} {title}" when a prior chapter is stored (with
spacing and truncation for long titles)
Multiple genres
- lib/genres.ts with parseGenres() and collectGenres() helpers to
split "冒险, 恋爱, 魔幻" into a list and aggregate across a collection
- Manga detail renders one pill per genre
- GenreTabs filters via parseGenres(...).includes(activeGenre) so a
multi-genre manga appears under each of its genres
- Home / Genre pages compute the tab list from collectGenres(signedManga)
- Card captions (GenreTabs + TrendingCarousel) show "冒险 · 恋爱 · 魔幻"
with truncation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
32 lines
799 B
TypeScript
32 lines
799 B
TypeScript
/**
|
|
* A manga's `genre` field may hold multiple comma-separated genres
|
|
* (e.g. "冒险, 恋爱, 魔幻"). This normalizes the raw string into a
|
|
* deduped, trimmed list.
|
|
*/
|
|
export function parseGenres(raw: string): string[] {
|
|
if (!raw) return [];
|
|
const seen = new Set<string>();
|
|
const out: string[] = [];
|
|
for (const part of raw.split(",")) {
|
|
const g = part.trim();
|
|
if (!g) continue;
|
|
if (seen.has(g)) continue;
|
|
seen.add(g);
|
|
out.push(g);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* Flatten all genres across a collection of manga into a sorted unique list.
|
|
*/
|
|
export function collectGenres(
|
|
mangas: { genre: string }[]
|
|
): string[] {
|
|
const seen = new Set<string>();
|
|
for (const m of mangas) {
|
|
for (const g of parseGenres(m.genre)) seen.add(g);
|
|
}
|
|
return [...seen].sort();
|
|
}
|