Skip to content

Commit 2d4eeb6

Browse files
committed
Move data-fetching operations for the library inside @/cms/
1 parent 253f92a commit 2d4eeb6

File tree

4 files changed

+45
-33
lines changed

4 files changed

+45
-33
lines changed

cms/models/Book.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import z from "zod";
2+
3+
const BookSchema = z.object({
4+
title: z.string(),
5+
author: z.string(),
6+
readAt: z.iso.date(),
7+
category: z.union([
8+
z.literal("foundational"),
9+
z.literal("craft"),
10+
z.literal("mental-models"),
11+
z.literal("worldview"),
12+
z.literal("exploration"),
13+
]),
14+
image: z.string(),
15+
status: z.union([z.literal("read")]),
16+
language: z.union([z.literal("en"), z.literal("es")]),
17+
});
18+
19+
export type Book = z.infer<typeof BookSchema>;
20+
21+
export const toBook = (data: unknown): Book => {
22+
return BookSchema.parse(data);
23+
};

cms/repositories/BookRepository.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { library } from "@/cms/content/library.json";
2+
import { Book, toBook } from "@/cms/models/Book";
3+
4+
export class BookRepository {
5+
public all(): Book[] {
6+
return library.map(toBook);
7+
}
8+
}

cms/repositories/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { MarkdownRepository } from "@/cms/lib/MarkdownRepository";
22
import { getConfig } from "@/config";
33

44
import { ArticleRepository } from "./ArticleRepository";
5+
import { BookRepository } from "./BookRepository";
56
import { CollectionRepository } from "./CollectionRepository";
67

78
export const getBlogpostRepository = (): ArticleRepository =>
@@ -10,3 +11,5 @@ export const getBlogpostRepository = (): ArticleRepository =>
1011
export const getNotesRepository = (): ArticleRepository => MarkdownRepository.fromDirectory(getConfig().writing.notes);
1112

1213
export const getCollectionRepository = (): CollectionRepository => new CollectionRepository();
14+
15+
export const getBookRepository = (): BookRepository => new BookRepository();

pages/library.tsx

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,18 @@
11
import { GetStaticProps } from "next";
22
import Image from "next/image";
33
import { useState } from "react";
4-
import z from "zod";
54

6-
import { library } from "@/cms/content/library.json";
5+
import { Book } from "@/cms/models/Book";
6+
import { getBookRepository } from "@/cms/repositories";
77
import { Heading } from "@/components/Heading";
88
import { Link } from "@/components/Link";
99
import { Text } from "@/components/Text";
1010
import { Application } from "@/layouts/Application";
1111

12-
const BookSchema = z.object({
13-
title: z.string(),
14-
author: z.string(),
15-
readAt: z.iso.date(),
16-
category: z.union([
17-
z.literal("foundational"),
18-
z.literal("craft"),
19-
z.literal("mental-models"),
20-
z.literal("worldview"),
21-
z.literal("exploration"),
22-
]),
23-
image: z.string(),
24-
status: z.union([z.literal("read")]),
25-
language: z.union([z.literal("en"), z.literal("es")]),
26-
});
27-
28-
type Book = z.infer<typeof BookSchema>;
29-
3012
type LibraryPageProps = {
3113
library: Record<Book["category"], Book[]>;
3214
};
3315

34-
const toBook = (data: unknown): Book => {
35-
return BookSchema.parse(data);
36-
};
37-
3816
export function LibrarySection(props: Readonly<React.PropsWithChildren>) {
3917
return <div className="LibrarySection">{props.children}</div>;
4018
}
@@ -50,6 +28,13 @@ export function LibrarySectionDescription(props: Readonly<React.PropsWithChildre
5028
export function LibrarySectionBooks(props: Readonly<{ books: Book[] }>) {
5129
const [now] = useState(() => Date.now());
5230

31+
const yearsAgo = (date: Date) => {
32+
return new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(
33+
-Math.floor((now - date.getTime()) / (1000 * 60 * 60 * 24 * 30 * 12)),
34+
"year",
35+
);
36+
};
37+
5338
return (
5439
<ul className="LibrarySection__Books">
5540
{props.books.map((book) => {
@@ -71,14 +56,7 @@ export function LibrarySectionBooks(props: Readonly<{ books: Book[] }>) {
7156
<dd className="LibrarySection__Book__Author">{book.author}</dd>
7257

7358
<dt className="sr-only">Read At</dt>
74-
<dd className="LibrarySection__Book__Date">
75-
{/* Displays relative time - e.g, 1 year ago */}
76-
last read{" "}
77-
{new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(
78-
-Math.floor((now - new Date(book.readAt).getTime()) / (1000 * 60 * 60 * 24 * 30 * 12)),
79-
"year",
80-
)}
81-
</dd>
59+
<dd className="LibrarySection__Book__Date">last read {yearsAgo(new Date(book.readAt))}</dd>
8260
</dl>
8361
</li>
8462
);
@@ -167,7 +145,7 @@ export default function LibraryPage(props: Readonly<LibraryPageProps>) {
167145
}
168146

169147
export const getStaticProps: GetStaticProps<LibraryPageProps> = async () => {
170-
const books = library.map(toBook);
148+
const books = getBookRepository().all();
171149

172150
return {
173151
props: {

0 commit comments

Comments
 (0)