diff --git a/src/components/blog/Masthead.astro b/src/components/blog/Masthead.astro index de6757b..c9727c7 100644 --- a/src/components/blog/Masthead.astro +++ b/src/components/blog/Masthead.astro @@ -1,18 +1,18 @@ --- -import type { CollectionEntry } from "astro:content"; import FormattedDate from "@/components/FormattedDate.astro"; import Card from "../componentsBefore/Card.astro"; import { Icon } from "astro-icon/components"; import Label from "../componentsBefore/Label.astro"; +import type { PostItem } from "@/types"; interface Props { - content: CollectionEntry<"post">; + content: PostItem; readingTime: string; ogImage: string; } const { - content: { data }, + content: { data, dateToCmp }, readingTime, ogImage, } = Astro.props; @@ -27,13 +27,7 @@ const socialImageURL = new URL(ogImage ? ogImage : "/social-card.png", Astro.url { socialImageURL && (
- {socialImageURL} + {socialImageURL}
) } @@ -54,7 +48,7 @@ const socialImageURL = new URL(ogImage ? ogImage : "/social-card.png", Astro.url

- /{" "} + /{" "} {readingTime}

{ diff --git a/src/components/blog/PostPreview.astro b/src/components/blog/PostPreview.astro index fe81513..241a4d0 100644 --- a/src/components/blog/PostPreview.astro +++ b/src/components/blog/PostPreview.astro @@ -1,10 +1,10 @@ --- -import type { CollectionEntry } from "astro:content"; import FormattedDate from "@/components/FormattedDate.astro"; import type { HTMLTag, Polymorphic } from "astro/types"; +import type { AllItem } from "@/types"; type Props = Polymorphic<{ as: Tag }> & { - post: CollectionEntry<"post">; + post: AllItem; withDesc?: boolean; }; @@ -17,7 +17,11 @@ const { as: Tag = "div", post, withDesc = false } = Astro.props; /> {post.data.draft && (Draft) } - + {post.data.fixedToTop && *} {post.data.title} diff --git a/src/components/note/Note.astro b/src/components/note/Note.astro index 0fc192d..ec2b0e4 100644 --- a/src/components/note/Note.astro +++ b/src/components/note/Note.astro @@ -1,5 +1,5 @@ --- -import { type CollectionEntry, render } from "astro:content"; +import { render } from "astro:content"; import FormattedDate from "@/components/FormattedDate.astro"; import type { HTMLTag, Polymorphic } from "astro/types"; import GiscusComment from "@/components/componentsBefore/GiscusComment"; @@ -8,8 +8,9 @@ import ShareButtons from "../ShareButtons.astro"; import ContentFooter from "../ContentFooter.astro"; import { cn } from "@/utils/tailwind"; import Card from "../componentsBefore/Card.astro"; +import type { NoteItem } from "@/types"; type Props = Polymorphic<{ as: Tag }> & { - note: CollectionEntry<"note">; + note: NoteItem; isPreview?: boolean | undefined; index?: number; enableLineClamp?: boolean; @@ -78,17 +79,17 @@ if (modifiedDate && modifiedDate.toDateString() === date.toDateString()) { enableLineClamp && "line-clamp-4" )} > - { - !isPreview && note.data.description && note.data.description.trim().length > 0 && ( - -
{note.data.description}
-
- ) - } + { + !isPreview && note.data.description && note.data.description.trim().length > 0 && ( + +
{note.data.description}
+
+ ) + }
- + {!isPreview && } {!isPreview && } @@ -99,5 +100,4 @@ if (modifiedDate && modifiedDate.toDateString() === date.toDateString()) { ) } - diff --git a/src/content.config.ts b/src/content.config.ts index 04a51d1..9f069be 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -68,6 +68,7 @@ const note = defineCollection({ .optional() .transform((val) => (val ? processDate(val) : undefined)), tags: z.array(z.string()).default([]).transform(removeDupsAndLowerCase), + category: z.string().optional().nullable(), }), }); diff --git a/src/data/post.ts b/src/data/post.ts index 5f72bc1..147c156 100644 --- a/src/data/post.ts +++ b/src/data/post.ts @@ -1,22 +1,61 @@ +import type { AllItem, NoteItem, PostItem } from "@/types"; import { collectionDateSort } from "@/utils/date"; import { type CollectionEntry, getCollection } from "astro:content"; +/** + * @description: 添加分类作为前缀 + * @param {CollectionEntry} posts + */ +const addCategoryAsPrefix = (posts: CollectionEntry<"post" | "note">[]) => { + return posts.map((post) => { + return { + ...post, + data: { + ...post.data, + title: post.data?.category + ? `[${post.data?.category}] ${post.data.title}` + : post.data.title, + }, + }; + }); +}; + +export const getDateSortByCreateTime = (post: CollectionEntry<"post" | "note">) => { + return post.data.date ?? post.data.data_created ?? post.data.date_modified ?? new Date(); +}; + +export const getDateSortByUpdateTime = (post: CollectionEntry<"post" | "note">) => { + return post.data.date_modified ?? post.data.date ?? post.data.data_created ?? new Date(); +}; + /** filter out draft posts based on the environment */ -export async function getAllPosts(): Promise[]> { - return await getCollection("post", ({ data }) => { +export async function getAllPosts(): Promise { + const posts = await getCollection("post", ({ data }) => { return import.meta.env.PROD ? !data.draft : true; }); + return (addCategoryAsPrefix(posts) as CollectionEntry<"post">[]).map((item) => { + return { + ...item, + dateToCmp: getDateSortByCreateTime(item), + }; + }); } -export async function getAllFixedToTopPosts(): Promise[]> { - return await getCollection("post", ({ data }) => { - return import.meta.env.PROD ? data.fixedToTop : false; +export async function getAllFixedToTopPosts() { + const posts = await getAllPosts(); + return posts.filter((post) => post.data.fixedToTop); +} + +export async function getAllNotes(): Promise { + const notes = await getCollection("note"); + return (addCategoryAsPrefix(notes) as NoteItem[]).map((item) => { + return { ...item, dateToCmp: getDateSortByCreateTime(item) }; }); } export async function getAllCollectionPosts() { const posts = await getAllPosts(); - const notes = await getCollection("note"); + const notes = await getAllNotes(); const allPosts = [...posts, ...notes]; const allPostsSortedByDate = allPosts.sort(collectionDateSort); return allPostsSortedByDate; @@ -25,9 +64,9 @@ export async function getAllCollectionPosts() { /** groups posts by year (based on option siteConfig.sortPostsByUpdatedDate), using the year as the key * Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. */ -export function groupPostsByYear(posts: CollectionEntry<"post">[]) { - return posts.reduce[]>>((acc, post) => { - const year = post.data.date.getFullYear(); +export function groupPostsByYear(posts: AllItem[]) { + return posts.reduce>((acc, post) => { + const year = getDateSortByCreateTime(post).getFullYear(); if (!acc[year]) { acc[year] = []; } @@ -39,21 +78,21 @@ export function groupPostsByYear(posts: CollectionEntry<"post">[]) { /** returns all tags created from posts (inc duplicate tags) * Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. * */ -export function getAllTags(posts: CollectionEntry<"post">[]) { +export function getAllTags(posts: AllItem[]) { return posts.flatMap((post) => [...post.data.tags]); } /** returns all unique tags created from posts * Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. * */ -export function getUniqueTags(posts: CollectionEntry<"post">[]) { +export function getUniqueTags(posts: AllItem[]) { return [...new Set(getAllTags(posts))]; } /** returns a count of each unique tag - [[tagName, count], ...] * Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. * */ -export function getUniqueTagsWithCount(posts: CollectionEntry<"post">[]): [string, number][] { +export function getUniqueTagsWithCount(posts: AllItem[]): [string, number][] { return [ ...getAllTags(posts).reduce( (acc, t) => acc.set(t, (acc.get(t) ?? 0) + 1), @@ -63,19 +102,17 @@ export function getUniqueTagsWithCount(posts: CollectionEntry<"post">[]): [strin } /** Note: This function doesn't filter draft posts, pass it the result of getAllPosts above to do so. */ -export function getAllCategories(posts: Array>): string[] { +export function getAllCategories(posts: AllItem[]): 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[] { +export function getUniqueCategories(posts: AllItem[]): 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]> { +export function getUniqueCategoriesWithCount(posts: AllItem[]): Array<[string, number]> { return [ ...getAllCategories(posts).reduce( (acc, t) => acc.set(t, (acc.get(t) || 0) + 1), diff --git a/src/layouts/BlogPost.astro b/src/layouts/BlogPost.astro index 8418fe3..cc997a7 100644 --- a/src/layouts/BlogPost.astro +++ b/src/layouts/BlogPost.astro @@ -1,5 +1,5 @@ --- -import { type CollectionEntry, render } from "astro:content"; +import { render } from "astro:content"; import Masthead from "@/components/blog/Masthead.astro"; import TOC from "@/components/blog/TOC.astro"; @@ -9,23 +9,16 @@ import BaseLayout from "./Base.astro"; import ArticleContainer from "@/components/ArticleContainer.astro"; import ShareButtons from "@/components/ShareButtons.astro"; import ContentFooter from "@/components/ContentFooter.astro"; +import type { PostItem } from "@/types"; interface Props { - post: CollectionEntry<"post">; + post: PostItem; } const { post } = Astro.props; -const { - banner: ogImage, - title, - description, - date_modified: updatedDate, - date: publishDate, - tags, - category, -} = post.data; +const { banner: ogImage, title, description, tags, category } = post.data; const socialImage = ogImage ?? `/og-image/${post.id}.png`; -const articleDate = updatedDate?.toISOString() ?? publishDate.toISOString(); +const articleDate = post.dateToCmp.toISOString(); const { headings, remarkPluginFrontmatter } = await render(post); const readingTime: string = remarkPluginFrontmatter.readingTime; --- @@ -67,7 +60,6 @@ const readingTime: string = remarkPluginFrontmatter.readingTime;
- {!!headings.length && }
diff --git a/src/pages/archive/[...page].astro b/src/pages/archive/[...page].astro new file mode 100644 index 0000000..ac66baf --- /dev/null +++ b/src/pages/archive/[...page].astro @@ -0,0 +1,109 @@ +--- +import Pagination from "@/components/Paginator.astro"; +import PostPreview from "@/components/blog/PostPreview.astro"; +import { + getAllCollectionPosts, + getUniqueCategories, + getUniqueTags, + groupPostsByYear, +} from "@/data/post"; +import PageLayout from "@/layouts/Base.astro"; +import { collectionDateSort, getLatestUpdatedPost } from "@/utils/date"; +import type { GetStaticPaths, Page } from "astro"; +import { MAX_TAGS, MAX_CATEGORIES, MAX_POSTS_PER_PAGE, MAX_LATEST_POSTS } from "@/utils/constant"; +import type { AllItem } from "@/types"; + +export const getStaticPaths = (async ({ paginate }) => { + const allPosts = await getAllCollectionPosts(); + const uniqueTags = getUniqueTags(allPosts).slice(0, MAX_TAGS); + const uniqueCategories = getUniqueCategories(allPosts).slice(0, MAX_CATEGORIES); + const latestUpdatedPost = allPosts.sort(getLatestUpdatedPost).slice(0, MAX_LATEST_POSTS); + const postsCount = allPosts.length; + return paginate(allPosts.sort(collectionDateSort), { + pageSize: MAX_POSTS_PER_PAGE, + props: { uniqueTags, uniqueCategories, postsCount, latestUpdatedPost }, + }); +}) satisfies GetStaticPaths; + +interface Props { + page: Page; + uniqueTags: string[]; + uniqueCategories: string[]; + postsCount: number; + latestUpdatedPost: AllItem[]; +} + +const { page, postsCount, latestUpdatedPost } = Astro.props; + +const meta = { + description: "Read my collection of posts and the things that interest me", + title: "Posts", +}; + +const paginationProps = { + ...(page.url.prev && { + prevUrl: { + text: "← Previous Page", + url: page.url.prev, + }, + }), + ...(page.url.next && { + nextUrl: { + text: "Next Page →", + url: page.url.next, + }, + }), +}; + +const groupedByYear = groupPostsByYear(page.data); +const descYearKeys = Object.keys(groupedByYear).sort((a, b) => +b - +a); +--- + + +
+

Archive({postsCount})

+
+
+
+ { + descYearKeys.map((yearKey) => ( +
+

+ Posts in + {yearKey} +

+
    + {groupedByYear[yearKey]?.map((p) => ( +
  • + +
  • + ))} +
+
+ )) + } + +
+ +
+
diff --git a/src/pages/categories/index.astro b/src/pages/categories/index.astro index 885a65f..5ca17dd 100644 --- a/src/pages/categories/index.astro +++ b/src/pages/categories/index.astro @@ -18,7 +18,7 @@ const meta = { allCategories.map(([item, val]) => (
  • []; +const allPostsByDate = allPosts.sort(collectionDateSort).slice(0, MAX_POSTS); // Fixed to top Posts const allFixedToTopPosts = await getAllFixedToTopPosts(); -const allFixedToTopPostsByDate = allFixedToTopPosts - .sort(collectionDateSort) - .slice(0, MAX_POSTS) as CollectionEntry<"post">[]; - +const allFixedToTopPostsByDate = allFixedToTopPosts.sort(collectionDateSort).slice(0, MAX_POSTS); + // Notes const MAX_NOTES = 6; -const allNotes = await getCollection("note"); +const allNotes = await getAllNotes(); const latestNotes = allNotes.sort(collectionDateSort).slice(0, MAX_NOTES); --- @@ -36,15 +31,15 @@ const latestNotes = allNotes.sort(collectionDateSort).slice(0, MAX_NOTES); { allFixedToTopPostsByDate.length > 0 && (
    -

    置顶文章

    +

    + 置顶文章 +

      - { - allFixedToTopPostsByDate.map((p) => ( -
    • - -
    • - )) - } + {allFixedToTopPostsByDate.map((p) => ( +
    • + +
    • + ))}
    ) diff --git a/src/pages/notes/[...page].astro b/src/pages/notes/[...page].astro index 55f0712..4ccbc51 100644 --- a/src/pages/notes/[...page].astro +++ b/src/pages/notes/[...page].astro @@ -1,15 +1,16 @@ --- -import { type CollectionEntry, getCollection } from "astro:content"; import Pagination from "@/components/Paginator.astro"; import Note from "@/components/note/Note.astro"; import PageLayout from "@/layouts/Base.astro"; import { collectionDateSort } from "@/utils/date"; import type { GetStaticPaths, Page } from "astro"; import { Icon } from "astro-icon/components"; +import { getAllNotes } from "@/data/post"; +import type { NoteItem } from "@/types"; export const getStaticPaths = (async ({ paginate }) => { const MAX_NOTES_PER_PAGE = 10; - const allNotes = await getCollection("note"); + const allNotes = await getAllNotes(); const notesCount = allNotes.length; return paginate(allNotes.sort(collectionDateSort), { pageSize: MAX_NOTES_PER_PAGE, @@ -18,7 +19,7 @@ export const getStaticPaths = (async ({ paginate }) => { }) satisfies GetStaticPaths; interface Props { - page: Page>; + page: Page; uniqueTags: string[]; notesCount: number; } @@ -45,7 +46,7 @@ const paginationProps = { }), }; -function calculateIndex(index: number, page: Page>) { +function calculateIndex(index: number, page: Page) { return index + page.start; } --- @@ -60,11 +61,11 @@ function calculateIndex(index: number, page: Page>) {
    flowpreview -
    +
    { page.data.map((note, index) => ( >) { as="h2" isPreview index={calculateIndex(index, page)} - enableLineClamp={true} + enableLineClamp={false} /> )) } diff --git a/src/pages/notes/[...slug].astro b/src/pages/notes/[...slug].astro index 4cbcdc9..7acca7d 100644 --- a/src/pages/notes/[...slug].astro +++ b/src/pages/notes/[...slug].astro @@ -1,14 +1,13 @@ --- -import { getCollection } from "astro:content"; - import Note from "@/components/note/Note.astro"; import PageLayout from "@/layouts/Base.astro"; import type { GetStaticPaths, InferGetStaticPropsType } from "astro"; import { siteConfig } from "@/site.config"; +import { getAllNotes } from "@/data/post"; // if you're using an adaptor in SSR mode, getStaticPaths wont work -> https://docs.astro.build/en/guides/routing/#modifying-the-slug-example-for-ssr export const getStaticPaths = (async () => { - const allNotes = await getCollection("note"); + const allNotes = await getAllNotes(); return allNotes.map((note) => ({ params: { slug: note.id }, props: { note }, @@ -22,7 +21,7 @@ const { note } = Astro.props; const meta = { description: note.data.description || - `Read about my note posted on ${siteConfig.title} (${siteConfig.description}) at ${note.data.date.toLocaleDateString()} by ${siteConfig.author}`, + `Read about my note posted on ${siteConfig.title} (${siteConfig.description}) at ${note.dateToCmp.toLocaleDateString()} by ${siteConfig.author}`, title: note.data.title, tags: note.data.tags.join(", "), }; diff --git a/src/pages/notes/list/[...page].astro b/src/pages/notes/list/[...page].astro deleted file mode 100644 index 62ad758..0000000 --- a/src/pages/notes/list/[...page].astro +++ /dev/null @@ -1,82 +0,0 @@ ---- -import { type CollectionEntry, getCollection } from "astro:content"; -import Pagination from "@/components/Paginator.astro"; -import Note from "@/components/note/Note.astro"; -import PageLayout from "@/layouts/Base.astro"; -import { collectionDateSort } from "@/utils/date"; -import type { GetStaticPaths, Page } from "astro"; -import { Icon } from "astro-icon/components"; - -export const getStaticPaths = (async ({ paginate }) => { - const MAX_NOTES_PER_PAGE = 10; - const allNotes = await getCollection("note"); - const notesCount = allNotes.length; - return paginate(allNotes.sort(collectionDateSort), { - pageSize: MAX_NOTES_PER_PAGE, - props: { notesCount }, - }); -}) satisfies GetStaticPaths; - -interface Props { - page: Page>; - uniqueTags: string[]; - notesCount: number; -} - -const { page, notesCount } = Astro.props; - -const meta = { - description: "Read my collection of notes", - title: "Notes", -}; - -const paginationProps = { - ...(page.url.prev && { - prevUrl: { - text: "← Previous Page", - url: page.url.prev, - }, - }), - ...(page.url.next && { - nextUrl: { - text: "Next Page →", - url: page.url.next, - }, - }), -}; - -function calculateIndex(index: number, page: Page>) { - return index + page.start; -} ---- - - -
    -

    - Notes({notesCount}) - - RSS feed - -
    - preview -

    -
    - { - page.data.map((note, index) => ( - - )) - } -
    - -
    -
    diff --git a/src/pages/notes/rss.xml.ts b/src/pages/notes/rss.xml.ts index 7ef9b00..3b58bd1 100644 --- a/src/pages/notes/rss.xml.ts +++ b/src/pages/notes/rss.xml.ts @@ -1,12 +1,12 @@ +import { getAllNotes } from "@/data/post"; import { siteConfig } from "@/site.config"; import { collectionDateSort } from "@/utils/date"; import rss from "@astrojs/rss"; -import { getCollection } from "astro:content"; import MarkdownIt from "markdown-it"; import sanitizeHtml from "sanitize-html"; export const GET = async () => { - const notes = await getCollection("note"); + const notes = await getAllNotes(); const sortedNotes = notes.sort(collectionDateSort); const parser = new MarkdownIt(); @@ -25,7 +25,7 @@ export const GET = async () => { return { title: post.data.title, description: (post.data.description ?? "") + "\t" + tagStr, - pubDate: post.data.date, + pubDate: post.dateToCmp, link: `notes/${post.id}/`, content: post.body ? sanitizeHtml( diff --git a/src/pages/og-image/[...slug].png.ts b/src/pages/og-image/[...slug].png.ts index 6c3b5e6..3c78c75 100644 --- a/src/pages/og-image/[...slug].png.ts +++ b/src/pages/og-image/[...slug].png.ts @@ -15,7 +15,7 @@ const ogOptions: SatoriOptions = { name: "ZCOOLXiaoWei", style: "normal", weight: 400, - } + }, ], height: 630, width: 1200, @@ -76,7 +76,7 @@ export async function getStaticPaths() { .map((post) => ({ params: { slug: post.id }, props: { - pubDate: post.data.date ?? post.data.date_modified , + pubDate: post.dateToCmp, title: post.data.title, }, })); diff --git a/src/pages/posts/[...page].astro b/src/pages/posts/[...page].astro index 5c3c4d2..0c484ec 100644 --- a/src/pages/posts/[...page].astro +++ b/src/pages/posts/[...page].astro @@ -7,11 +7,10 @@ import PageLayout from "@/layouts/Base.astro"; import { collectionDateSort } from "@/utils/date"; import type { GetStaticPaths, Page } from "astro"; import { Icon } from "astro-icon/components"; +import { MAX_TAGS, MAX_CATEGORIES, MAX_POSTS_PER_PAGE } from "@/utils/constant"; +import type { PostItem } from "@/types"; export const getStaticPaths = (async ({ paginate }) => { - const MAX_POSTS_PER_PAGE = 20; - 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); @@ -23,7 +22,7 @@ export const getStaticPaths = (async ({ paginate }) => { }) satisfies GetStaticPaths; interface Props { - page: Page>; + page: Page; uniqueTags: string[]; uniqueCategories: string[]; postsCount: number; @@ -90,7 +89,7 @@ const descYearKeys = Object.keys(groupedByYear).sort((a, b) => +b - +a); !!uniqueTags.length && (

    - + Tags +b - +a);