new Date(val)),
date_modified: z.date().optional(),
+ data_created: z.date().optional(),
+ category: z.string().optional().nullable(),
}),
});
@@ -28,9 +30,10 @@ const note = defineCollection({
schema: baseSchema.extend({
description: z.string().optional().nullable(),
date: z.union([z.string(), z.date()]).transform((val) => new Date(val)),
+ date_modified: z.date().optional(),
+ data_created: z.date().optional(),
tags: z.array(z.string()).default([]).transform(removeDupsAndLowerCase),
}),
});
-
export const collections = { post, note };
diff --git a/src/data/post.ts b/src/data/post.ts
index b76d262..c878a80 100644
--- a/src/data/post.ts
+++ b/src/data/post.ts
@@ -55,3 +55,25 @@ export function getUniqueTagsWithCount(posts: CollectionEntry<"post">[]): [strin
),
].sort((a, b) => b[1] - a[1]);
}
+
+/** Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. */
+export function getAllCategories(posts: Array>): string[] {
+ return posts.map((post) => post.data.category ?? "未分类");
+}
+
+/** Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. */
+export function getUniqueCategories(posts: Array>): string[] {
+ return [...new Set(getAllCategories(posts))];
+}
+
+/** Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. */
+export function getUniqueCategoriesWithCount(
+ posts: Array>,
+): Array<[string, number]> {
+ return [
+ ...getAllCategories(posts).reduce(
+ (acc, t) => acc.set(t, (acc.get(t) || 0) + 1),
+ new Map(),
+ ),
+ ].sort((a, b) => b[1] - a[1]);
+}
diff --git a/src/layouts/BlogPost.astro b/src/layouts/BlogPost.astro
index cf23bd2..ae170dd 100644
--- a/src/layouts/BlogPost.astro
+++ b/src/layouts/BlogPost.astro
@@ -19,6 +19,7 @@ const {
date_modified: updatedDate,
date: publishDate,
tags,
+ category,
} = post.data;
const socialImage = ogImage ?? `/og-image/${post.id}.png`;
const articleDate = updatedDate?.toISOString() ?? publishDate.toISOString();
@@ -32,7 +33,7 @@ const readingTime: string = remarkPluginFrontmatter.readingTime;
description: description ?? "",
ogImage: socialImage,
title,
- tags: tags.join(", "),
+ tags: [category, ...tags].join(", "),
}}
>
diff --git a/src/pages/categories/[category]/[...page].astro b/src/pages/categories/[category]/[...page].astro
new file mode 100644
index 0000000..46b4458
--- /dev/null
+++ b/src/pages/categories/[category]/[...page].astro
@@ -0,0 +1,74 @@
+---
+import type { CollectionEntry } from "astro:content";
+import Pagination from "@/components/Paginator.astro";
+import PostPreview from "@/components/blog/PostPreview.astro";
+import { getAllPosts, getUniqueCategories } from "@/data/post";
+import PageLayout from "@/layouts/Base.astro";
+import { collectionDateSort } from "@/utils/date";
+import type { GetStaticPaths, Page } from "astro";
+
+export const getStaticPaths: GetStaticPaths = async ({ paginate }) => {
+ const allPosts = await getAllPosts();
+ const sortedPosts = allPosts.sort(collectionDateSort);
+ const uniqueCategories = getUniqueCategories(sortedPosts);
+
+ return uniqueCategories.flatMap((category) => {
+ const filterPosts = sortedPosts.filter((post) => post.data.category === category);
+ return paginate(filterPosts, {
+ pageSize: 10,
+ params: { category },
+ });
+ });
+};
+
+interface Props {
+ page: Page>;
+}
+
+const { page } = Astro.props;
+const { category } = Astro.params;
+
+const meta = {
+ description: `View all posts with the category - ${category}`,
+ title: `Category: ${category}`,
+};
+
+const paginationProps = {
+ ...(page.url.prev && {
+ prevUrl: {
+ text: "← Previous Categories",
+ url: page.url.prev,
+ },
+ }),
+ ...(page.url.next && {
+ nextUrl: {
+ text: "Next Categories →",
+ url: page.url.next,
+ },
+ }),
+};
+---
+
+
+
+
+
+
diff --git a/src/pages/categories/index.astro b/src/pages/categories/index.astro
new file mode 100644
index 0000000..885a65f
--- /dev/null
+++ b/src/pages/categories/index.astro
@@ -0,0 +1,35 @@
+---
+import { getAllPosts, getUniqueCategoriesWithCount } from "@/data/post";
+import PageLayout from "@/layouts/Base.astro";
+
+const allPosts = await getAllPosts();
+const allCategories = getUniqueCategoriesWithCount(allPosts);
+
+const meta = {
+ description: "A list of all the categories I've written about in my posts",
+ title: "All Categories",
+};
+---
+
+
+
diff --git a/src/pages/posts/[...page].astro b/src/pages/posts/[...page].astro
index 9e7ea6f..9e030e1 100644
--- a/src/pages/posts/[...page].astro
+++ b/src/pages/posts/[...page].astro
@@ -2,7 +2,7 @@
import type { CollectionEntry } from "astro:content";
import Pagination from "@/components/Paginator.astro";
import PostPreview from "@/components/blog/PostPreview.astro";
-import { getAllPosts, getUniqueTags, groupPostsByYear } from "@/data/post";
+import { getAllPosts, getUniqueCategories, getUniqueTags, groupPostsByYear } from "@/data/post";
import PageLayout from "@/layouts/Base.astro";
import { collectionDateSort } from "@/utils/date";
import type { GetStaticPaths, Page } from "astro";
@@ -11,20 +11,23 @@ import { Icon } from "astro-icon/components";
export const getStaticPaths = (async ({ paginate }) => {
const MAX_POSTS_PER_PAGE = 10;
const MAX_TAGS = 7;
+ const MAX_CATEGORIES = 7;
const allPosts = await getAllPosts();
const uniqueTags = getUniqueTags(allPosts).slice(0, MAX_TAGS);
+ const uniqueCategories = getUniqueCategories(allPosts).slice(0, MAX_CATEGORIES);
return paginate(allPosts.sort(collectionDateSort), {
pageSize: MAX_POSTS_PER_PAGE,
- props: { uniqueTags },
+ props: { uniqueTags, uniqueCategories },
});
}) satisfies GetStaticPaths;
interface Props {
page: Page>;
uniqueTags: string[];
+ uniqueCategories: string[];
}
-const { page, uniqueTags } = Astro.props;
+const { page, uniqueTags, uniqueCategories } = Astro.props;
const meta = {
description: "Read my collection of posts and the things that interest me",
@@ -79,47 +82,96 @@ const descYearKeys = Object.keys(groupedByYear).sort((a, b) => +b - +a);
}
- {
- !!uniqueTags.length && (
-
- )
- }
+
+
+
+ Posts with the category {category}
+ All {" "}Categories + + +Post List
+-
+ {
+ page.data.map((p) => (
+
-
+
+
+ ))
+ }
+
Categories
+-
+ {
+ allCategories.map(([item, val]) => (
+
- + + #{item} + + + - {val} Post{val > 1 && "s"} + + + )) + } +