Add Goodreads series and group them on the about page

This commit is contained in:
SirBlob committed 2026-11-01 20:57:37 -05:00
1 parent 737700a9a8
commit 2318cdeaac
11 files changed
+651 -12

No files matched your search

+60
View File
@@ -0,0 +1,60 @@
import type { Book, BookShelf } from '#lib/content';
export interface BookSeriesGroup {
name: string;
url: string;
books: Book[];
counts: Record<BookShelf, number>;
}
export interface GroupedBooks {
series: BookSeriesGroup[];
standalone: Book[];
}
const positionGroups = { single: 0, range: 1, none: 2 };
function seriesOrder(first: Book, second: Book): number {
const [firstGroup, firstNumber] = positionKey(first);
const [secondGroup, secondNumber] = positionKey(second);
return firstGroup - secondGroup || firstNumber - secondNumber;
}
function positionKey(book: Book): [number, number] {
const position = book.series?.position ?? '';
const match = position.match(/\d+(?:\.\d+)?/);
if (!match) return [positionGroups.none, 0];
const isRange = /\d\s*[-–]\s*\d/.test(position);
return [isRange ? positionGroups.range : positionGroups.single, Number(match[0])];
}
function shelfCounts(books: Book[]): Record<BookShelf, number> {
return {
read: books.filter((book) => book.shelf === 'read').length,
reading: books.filter((book) => book.shelf === 'reading').length,
'want-to-read': books.filter((book) => book.shelf === 'want-to-read').length
};
}
export function groupBySeries(books: Book[]): GroupedBooks {
const groups = new Map<string, Book[]>();
const standalone: Book[] = [];
for (const book of books) {
if (!book.series) {
standalone.push(book);
continue;
}
const key = book.series.url || book.series.name;
groups.set(key, [...(groups.get(key) ?? []), book]);
}
const series = [...groups.values()]
.map((seriesBooks) => {
const ordered = [...seriesBooks].sort(seriesOrder);
return { name: ordered[0].series!.name, url: ordered[0].series!.url, books: ordered, counts: shelfCounts(ordered) };
})
.sort((first, second) => second.counts.read - first.counts.read || first.name.localeCompare(second.name));
return { series, standalone };
}