Add Goodreads series and group them on the about page
This commit is contained in:
1 parent
737700a9a8
commit
2318cdeaac
11 files changed
+651
-12
No files matched your search
@@ -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 };
|
||||
}
|
||||
Reference in new issue
Block a user