-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add dynamic categories and tags pages with enhanced header inte…
…raction #18 (#23) 1. Enable dynamic categories pages based on user-configured categories (links, titles, icons) in config.yml. 2. Generate dynamic tags pages based on post tags, with Chinese tags converted to Pinyin and underscores in URLs. 3. Move type interfaces from getConfig to global types for better type management. 4. Implement CategoryLinks.tsx and TagLinks.tsx to link categories and tags in posts, making thumbnails, titles, and categories clickable. 5. Display categories and tags at the top of each post for better navigation. 6. Improve header accessibility and desktop hover interaction for the "Posts" menu to show user-defined categories, with indentation for mobile displays. 7. Refactor services folder structure, add subfolders, and update imports accordingly.
- Loading branch information
Showing
30 changed files
with
648 additions
and
212 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,23 @@ background: '/images/background.jpg' | |
# Slogan displayed on your homepage, under your avatar. | ||
slogan: "As long as the code or the developer is able to run, it's all good." | ||
|
||
####################### | ||
# HEADER SETTINGS | ||
####################### | ||
# Post Categories: Add your post categories here. | ||
# Use icons from link below (e.g., 'FaHouse' for 'fa6 FaHouse'). | ||
# https://react-icons.github.io/react-icons/icons/fa6/ | ||
postCategories: | ||
- name: '前端' # Category name, same ad frontmatter category, will show in frontend. | ||
slug: 'frontend' # (Optional) Category slug, if not set, will be same as name. | ||
icon: 'FaHtml5' # (Optional) Font Awesome 6 icon name. If not set, no icon. | ||
- name: '全栈' | ||
slug: 'fullstack' | ||
icon: 'FaCode' | ||
- name: '教程' | ||
slug: 'tutorial' | ||
icon: 'FaGraduationCap' | ||
|
||
####################### | ||
# SOCIAL MEDIA SETTINGS | ||
####################### | ||
|
@@ -34,7 +51,7 @@ socialMedia: | |
bilibili: 'https://space.bilibili.com/29018759' # Bilibili profile URL. | ||
zhihu: 'https://www.zhihu.com/people/zl-asica' # Zhihu profile URL. | ||
email: '[email protected]' # Your email address (do NOT include the mailto: prefix). | ||
rss: '' # Optional: RSS feed URL (). | ||
rss: '' # RSS feed URL (can be relative url). | ||
|
||
####################### | ||
# PAGES (ABOUT, FRIENDS) SETTINGS | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { getAllPosts } from '@/services/content/posts'; | ||
import { getConfig } from '@/services/config/getConfig'; | ||
import { notFound } from 'next/navigation'; | ||
import PostListLayout from '@/components/layout/PostListLayout'; | ||
|
||
export async function generateStaticParams() { | ||
const config = getConfig(); | ||
return config.postCategories.map((category) => ({ | ||
categorySlug: category.slug, | ||
})); | ||
} | ||
|
||
export default async function CategoryPage({ | ||
params, | ||
}: { | ||
params: { categorySlug: string }; | ||
}) { | ||
const posts = await getAllPosts(); | ||
const config = getConfig(); | ||
|
||
// Find the category based on the slug from params | ||
const category = config.postCategories.find( | ||
(cat) => cat.slug === params.categorySlug, | ||
); | ||
|
||
if (!category) { | ||
// If the category doesn't exist, show 404 | ||
notFound(); | ||
} | ||
|
||
// Filter posts by the category name | ||
const filteredPosts = posts.filter((post) => | ||
post.frontmatter.categories?.includes(category.name), | ||
); | ||
|
||
return ( | ||
<div className='container mx-auto p-4'> | ||
<h1 className='mb-6 text-center text-4xl font-bold'>{category.name}</h1> | ||
|
||
<PostListLayout posts={filteredPosts} /> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,85 +1,15 @@ | ||
import { getAllPosts } from '@/services/posts'; | ||
import Link from 'next/link'; | ||
import PostListLayout from '@/components/layout/PostListLayout'; | ||
import { getAllPosts } from '@/services/content/posts'; | ||
import { PostData } from '@/types'; | ||
import Image from 'next/image'; | ||
import { FaFolder, FaRegClock, FaEye } from 'react-icons/fa6'; | ||
|
||
export default async function PostsPage() { | ||
const posts: PostData[] = await getAllPosts(); | ||
|
||
// TODO: Replace with actual read count | ||
const readCount: number = 29; | ||
|
||
posts.forEach((post) => { | ||
const plainText = post.contentHtml | ||
.replace(/<!--more-->/g, '[[MORE_PLACEHOLDER]]') | ||
.replace(/<[^>]*>/g, ''); | ||
|
||
const moreIndex = plainText.indexOf('[[MORE_PLACEHOLDER]]'); | ||
|
||
post.contentHtml = | ||
moreIndex > 0 | ||
? plainText.slice(0, moreIndex).replace('[[MORE_PLACEHOLDER]]', '') | ||
: plainText.slice(0, 150); | ||
}); | ||
|
||
return ( | ||
<div className='container mx-auto p-4'> | ||
<h1 className='mb-6 text-center text-4xl font-bold'>All Posts</h1> | ||
|
||
<div className='grid grid-cols-1 gap-6'> | ||
{posts.map((post, index) => ( | ||
<Link | ||
key={post.slug} | ||
href={`/posts/${post.slug}`} | ||
className={`flex flex-col overflow-hidden rounded-lg shadow-lg transition-shadow duration-300 hover:shadow-xl md:flex-row ${index % 2 !== 0 ? 'md:flex-row-reverse' : ''} mx-auto`} | ||
style={{ maxWidth: '780px', maxHeight: '400px' }} // Set max width and height | ||
> | ||
{/* Thumbnail */} | ||
<div className='max-h-[400px] w-full md:w-1/2'> | ||
{post.frontmatter.thumbnail && ( | ||
<Image | ||
src={post.frontmatter.thumbnail} | ||
alt={post.frontmatter.title} | ||
width={780} | ||
height={400} | ||
className='h-full w-full object-cover' | ||
/> | ||
)} | ||
</div> | ||
|
||
{/* Content */} | ||
<div className='flex flex-col justify-between p-4 md:w-1/2'> | ||
<div> | ||
{/* Date of Publish */} | ||
<div className='text-gray-450 mb-2 flex items-center text-sm'> | ||
<FaRegClock className='mr-2' /> | ||
<span>{post.frontmatter.date.split(' ')[0]}</span> | ||
</div> | ||
{/* Title in Frontmatter */} | ||
<h2 className='mb-2 text-2xl font-bold'> | ||
{post.frontmatter.title} | ||
</h2> | ||
<p className='text-sm text-gray-300'>{post.contentHtml}</p> | ||
</div> | ||
<div className='mt-4'> | ||
<div className='text-gray-450 flex items-center justify-between text-sm'> | ||
{/* Read Count */} | ||
<span className='flex items-center'> | ||
<FaEye className='mr-1' /> | ||
{readCount} 热度 | ||
</span> | ||
{/* Category */} | ||
<span className='flex items-center'> | ||
<FaFolder className='mr-1' /> | ||
{post.frontmatter.categories?.join(', ') || '未分类'} | ||
</span> | ||
</div> | ||
</div> | ||
</div> | ||
</Link> | ||
))} | ||
</div> | ||
<PostListLayout posts={posts} /> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { getAllPosts } from '@/services/content/posts'; | ||
import { notFound } from 'next/navigation'; | ||
import PostListLayout from '@/components/layout/PostListLayout'; | ||
import { getUniqueTags, convertToPinyin } from '@/services/parsing/tagLinks'; | ||
|
||
// Generate static paths for all unique tags | ||
export async function generateStaticParams() { | ||
const uniqueTags = await getUniqueTags(); | ||
return uniqueTags.map((tag) => ({ | ||
// Convert only Chinese tags to pinyin slug | ||
tagSlug: convertToPinyin(tag), | ||
})); | ||
} | ||
|
||
export default async function TagPage({ | ||
params, | ||
}: { | ||
params: { tagSlug: string }; | ||
}) { | ||
const posts = await getAllPosts(); | ||
|
||
// Retrieve all unique tags from the posts | ||
const uniqueTags = await getUniqueTags(); | ||
|
||
// Find the tag based on the slug from params | ||
const tag = uniqueTags.find((tag) => convertToPinyin(tag) === params.tagSlug); | ||
|
||
if (!tag) { | ||
// If the tag doesn't exist, show 404 | ||
notFound(); | ||
} | ||
|
||
// Filter posts by the tag name | ||
const filteredPosts = posts.filter((post) => | ||
post.frontmatter.tags?.includes(tag), | ||
); | ||
|
||
return ( | ||
<div className='container mx-auto p-4'> | ||
<h1 className='mb-6 text-center text-4xl font-bold'>{tag}</h1> | ||
|
||
<PostListLayout posts={filteredPosts} /> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.