Commit b0884c1

HPCesia <me@hpcesia.com>
2025-02-10 12:54:46
refactor: get archive
`getTags` and `getCategories` now return a map of tag/category name to the number of posts with that tag/category.
1 parent e760d2e
Changed files (4)
src/pages/archives/categories/index.astro
@@ -1,6 +1,11 @@
 ---
 import type { BlogPostData } from '@/types/data';
-import { getCategories, getCategoryUrl, getSortedPosts } from '@/utils/content-utils';
+import {
+  getCategories,
+  getCategoryUrl,
+  getSortedPosts,
+  getTagUrl,
+} from '@/utils/content-utils';
 import ProfileCard from '@components/aside/ProfileCard.astro';
 import TOC from '@components/aside/TOC.astro';
 import Button from '@components/widgets/Button.astro';
@@ -10,14 +15,14 @@ import { i18n } from '@i18n/translation';
 import GridLayout from '@layouts/GridLayout.astro';
 import { Icon } from 'astro-icon/components';
 
-const categories = await getCategories();
+const categoriesMap = await getCategories();
 const allPosts = await getSortedPosts();
 
 const categoryPosts = new Map<string, { body: string; data: BlogPostData }[]>();
-categories.forEach((category) => {
+for (const [category] of categoriesMap) {
   const posts = allPosts.filter((post) => post.data.category === category).slice(0, 3);
   categoryPosts.set(category, posts);
-});
+}
 const uncategorizedPosts = allPosts.filter((post) => !post.data.category).slice(0, 3);
 if (uncategorizedPosts.length > 0)
   categoryPosts.set(i18n(I18nKey.uncategorized) as string, uncategorizedPosts);
@@ -39,8 +44,8 @@ if (uncategorizedPosts.length > 0)
         </div>
         <div>
           {
-            `${categories.length} ${i18n(
-              categories.length > 1 ? I18nKey.categoriesCount : I18nKey.categoryCount
+            `${categoriesMap.size} ${i18n(
+              categoriesMap.size > 1 ? I18nKey.categoriesCount : I18nKey.categoryCount
             )}`
           }
         </div>
@@ -65,6 +70,9 @@ if (uncategorizedPosts.length > 0)
               >
                 <h2 class="scroll-mt-20 text-2xl font-bold" id={`heading-${category}`}>
                   {category}
+                  <span class="text-base-content/60 ml-2 text-base">
+                    ({categoriesMap.get(category) || 0})
+                  </span>
                 </h2>
                 <Button href={getCategoryUrl(category)} title={category} class="pl-3">
                   {i18n(I18nKey.more)}
@@ -93,7 +101,7 @@ if (uncategorizedPosts.length > 0)
                             return {
                               icon: 'material-symbols:tag-rounded',
                               text: tag,
-                              link: getCategoryUrl(tag),
+                              link: getTagUrl(tag),
                             };
                           }) || []),
                         ].map((meta) => (
@@ -125,7 +133,7 @@ if (uncategorizedPosts.length > 0)
   </Fragment>
   <Fragment slot="aside-sticky">
     <TOC
-      headings={categories.map((category) => ({
+      headings={Array.from(categoriesMap.keys()).map((category) => ({
         text: category,
         slug: `heading-${category}`,
         depth: 2,
src/pages/archives/tags/index.astro
@@ -10,14 +10,14 @@ import { i18n } from '@i18n/translation';
 import GridLayout from '@layouts/GridLayout.astro';
 import { Icon } from 'astro-icon/components';
 
-const tags = await getTags();
+const tagsMap = await getTags();
 const allPosts = await getSortedPosts();
 
 const tagPosts = new Map<string, { body: string; data: BlogPostData }[]>();
-tags.forEach((tag) => {
+for (const [tag] of tagsMap) {
   const posts = allPosts.filter((post) => post.data.tags?.includes(tag)).slice(0, 3);
   tagPosts.set(tag, posts);
-});
+}
 const untaggedPosts = allPosts
   .filter((post) => !post.data.tags || post.data.tags.length === 0)
   .slice(0, 3);
@@ -40,7 +40,7 @@ if (untaggedPosts.length > 0) tagPosts.set(i18n(I18nKey.untagged) as string, unt
           }
         </div>
         <div>
-          {`${tags.length} ${i18n(tags.length > 1 ? I18nKey.tagsCount : I18nKey.tagCount)}`}
+          {`${tagsMap.size} ${i18n(tagsMap.size > 1 ? I18nKey.tagsCount : I18nKey.tagCount)}`}
         </div>
       </div>
     </div>
@@ -63,6 +63,9 @@ if (untaggedPosts.length > 0) tagPosts.set(i18n(I18nKey.untagged) as string, unt
               >
                 <h2 class="scroll-mt-20 text-2xl font-bold" id={`heading-${tag}`}>
                   {tag}
+                  <span class="text-base-content/60 ml-2 text-base">
+                    ({tagsMap.get(tag) || 0})
+                  </span>
                 </h2>
                 <Button href={getTagUrl(tag)} title={tag} class="pl-3">
                   {i18n(I18nKey.more)}
@@ -123,7 +126,7 @@ if (untaggedPosts.length > 0) tagPosts.set(i18n(I18nKey.untagged) as string, unt
   </Fragment>
   <Fragment slot="aside-sticky">
     <TOC
-      headings={tags.map((tag) => ({
+      headings={Array.from(tagsMap.keys()).map((tag) => ({
         text: tag,
         slug: `heading-${tag}`,
         depth: 2,
src/pages/[...page].astro
@@ -23,7 +23,7 @@ const categories = await getCategories();
 ---
 
 <GridLayout>
-  <CategoryBar categories={categories} />
+  <CategoryBar categories={Array.from(categories.keys())} />
   <PostPage
     posts={articles}
     currentPage={page}
src/utils/content-utils.ts
@@ -51,20 +51,30 @@ export async function getPostsCount(): Promise<number> {
   return allBlogPosts.length;
 }
 
-export async function getCategories(): Promise<string[]> {
+export async function getCategories(): Promise<Map<string, number>> {
   const allBlogPosts = await getSortedPosts();
-  const categories = [
-    ...new Set(
-      allBlogPosts.map((post) => post.data.category || (i18n(I18nKey.uncategorized) as string))
-    ),
-  ];
-  return categories;
+  const categoryMap = new Map<string, number>();
+
+  allBlogPosts.forEach((post) => {
+    const category = post.data.category || (i18n(I18nKey.uncategorized) as string);
+    categoryMap.set(category, (categoryMap.get(category) || 0) + 1);
+  });
+
+  return categoryMap;
 }
 
-export async function getTags(): Promise<string[]> {
+export async function getTags(): Promise<Map<string, number>> {
   const allBlogPosts = await getSortedPosts();
-  const tags = [...new Set(allBlogPosts.map((post) => post.data.tags || []).flat())];
-  return tags;
+  const tagMap = new Map<string, number>();
+
+  allBlogPosts.forEach((post) => {
+    const tags = post.data.tags || [];
+    tags.forEach((tag) => {
+      tagMap.set(tag, (tagMap.get(tag) || 0) + 1);
+    });
+  });
+
+  return tagMap;
 }
 
 export function getCategoryUrl(category: string | undefined) {