Compare commits
1 Commits
b8a8cc474b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21539772ef |
309
src/utils/blog-utils.ts
Normal file
309
src/utils/blog-utils.ts
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
/**
|
||||||
|
* Blog utility functions
|
||||||
|
* This file contains common functions used across blog components
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { type BlogPost, type Lang } from '@/types';
|
||||||
|
import { defaultLang } from '@/i18n/ui';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get blog posts based on language
|
||||||
|
* @param lang - Current language
|
||||||
|
* @param postsGlob - Object containing imported posts from glob
|
||||||
|
* @returns Processed blog posts array
|
||||||
|
*/
|
||||||
|
export async function getBlogPosts(lang: Lang, postsGlob: Record<string, any>): Promise<BlogPost[]> {
|
||||||
|
// Get posts based on language
|
||||||
|
let allPosts = [];
|
||||||
|
|
||||||
|
if (lang === 'zh') {
|
||||||
|
allPosts = postsGlob.zh;
|
||||||
|
} else {
|
||||||
|
allPosts = postsGlob.en;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process blog post data
|
||||||
|
const posts: BlogPost[] = allPosts.map((post: any) => {
|
||||||
|
const slug = post.url?.split('/').filter(Boolean).pop() || '';
|
||||||
|
|
||||||
|
// Default image if not specified in frontmatter
|
||||||
|
const defaultImage = "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=250&fit=crop&crop=center";
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: post.frontmatter.title,
|
||||||
|
description: post.frontmatter.description || '',
|
||||||
|
image: post.frontmatter.image || defaultImage,
|
||||||
|
slug: slug,
|
||||||
|
tags: post.frontmatter.tags || [],
|
||||||
|
tagId: post.frontmatter.tagId || [],
|
||||||
|
category: Array.isArray(post.frontmatter.category) ? post.frontmatter.category : post.frontmatter.category ? [post.frontmatter.category] : [],
|
||||||
|
categoryId: Array.isArray(post.frontmatter.categoryId) ? post.frontmatter.categoryId : post.frontmatter.categoryId ? [post.frontmatter.categoryId] : [],
|
||||||
|
date: post.frontmatter.date || post.frontmatter.pubDate || '',
|
||||||
|
readTime: post.frontmatter.readTime || post.frontmatter.readingTime || '5 min read',
|
||||||
|
url: post.url || '',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return posts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort blog posts by date (newest first)
|
||||||
|
* @param posts - Array of blog posts
|
||||||
|
* @returns Sorted array of blog posts
|
||||||
|
*/
|
||||||
|
export function sortPostsByDate(posts: BlogPost[]): BlogPost[] {
|
||||||
|
return posts
|
||||||
|
.filter(post => post.date) // Filter out posts without dates
|
||||||
|
.sort((a, b) => {
|
||||||
|
const dateA = new Date(a.date).getTime();
|
||||||
|
const dateB = new Date(b.date).getTime();
|
||||||
|
return dateB - dateA; // Descending order, newest first
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter posts by category
|
||||||
|
* @param posts - Array of blog posts
|
||||||
|
* @param category - Category to filter by
|
||||||
|
* @returns Filtered array of blog posts
|
||||||
|
*/
|
||||||
|
export function filterPostsByCategory(posts: BlogPost[], category: string): BlogPost[] {
|
||||||
|
if (!category) return posts;
|
||||||
|
|
||||||
|
return posts.filter(post => {
|
||||||
|
// First check if post contains the current category ID
|
||||||
|
if (post.categoryId && Array.isArray(post.categoryId)) {
|
||||||
|
return post.categoryId.some(catId =>
|
||||||
|
catId.toLowerCase() === category.toLowerCase()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// If no categoryId, check category
|
||||||
|
else if (post.category && Array.isArray(post.category)) {
|
||||||
|
return post.category.some(cat =>
|
||||||
|
cat.toLowerCase() === category.toLowerCase()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter posts by tag
|
||||||
|
* @param posts - Array of blog posts
|
||||||
|
* @param tag - Tag to filter by
|
||||||
|
* @returns Filtered array of blog posts
|
||||||
|
*/
|
||||||
|
export function filterPostsByTag(posts: BlogPost[], tag: string): BlogPost[] {
|
||||||
|
if (!tag) return posts;
|
||||||
|
|
||||||
|
return posts.filter(post => {
|
||||||
|
// First check if post contains the current tag ID
|
||||||
|
if (post.tagId && Array.isArray(post.tagId)) {
|
||||||
|
return post.tagId.some(tagId =>
|
||||||
|
tagId.toLowerCase() === tag.toLowerCase()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// If no tagId, check tags
|
||||||
|
else if (post.tags && Array.isArray(post.tags)) {
|
||||||
|
return post.tags.some(postTag =>
|
||||||
|
postTag.toLowerCase() === tag.toLowerCase()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract categories from posts
|
||||||
|
* @param posts - Array of blog posts
|
||||||
|
* @returns Map of category names to category IDs
|
||||||
|
*/
|
||||||
|
export function extractCategories(posts: any[]): Map<string, string> {
|
||||||
|
const allCategories = new Set<string>();
|
||||||
|
const categoryMap = new Map<string, string>(); // Map of category name to ID
|
||||||
|
|
||||||
|
posts.forEach(post => {
|
||||||
|
// Process categories
|
||||||
|
if (post.frontmatter?.category) {
|
||||||
|
const categories = Array.isArray(post.frontmatter.category)
|
||||||
|
? post.frontmatter.category
|
||||||
|
: [post.frontmatter.category];
|
||||||
|
|
||||||
|
const categoryIds = Array.isArray(post.frontmatter.categoryId)
|
||||||
|
? post.frontmatter.categoryId
|
||||||
|
: post.frontmatter.categoryId ? [post.frontmatter.categoryId] : [];
|
||||||
|
|
||||||
|
// If categoryId exists, use mapping between categoryId and category
|
||||||
|
if (categoryIds.length > 0 && categoryIds.length === categories.length) {
|
||||||
|
categories.forEach((cat: string, index: number) => {
|
||||||
|
if (cat && categoryIds[index]) {
|
||||||
|
allCategories.add(cat);
|
||||||
|
categoryMap.set(cat, categoryIds[index]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// If no categoryId or lengths don't match, use category name as ID
|
||||||
|
categories.forEach((cat: string) => {
|
||||||
|
if (cat) {
|
||||||
|
allCategories.add(cat);
|
||||||
|
categoryMap.set(cat, cat.toLowerCase());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return categoryMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract tags from posts
|
||||||
|
* @param posts - Array of blog posts
|
||||||
|
* @returns Map of tag names to tag IDs
|
||||||
|
*/
|
||||||
|
export function extractTags(posts: any[]): Map<string, string> {
|
||||||
|
const allTags = new Set<string>();
|
||||||
|
const tagMap = new Map<string, string>(); // Map of tag name to ID
|
||||||
|
|
||||||
|
posts.forEach(post => {
|
||||||
|
// Process tags
|
||||||
|
if (post.frontmatter?.tags && Array.isArray(post.frontmatter.tags)) {
|
||||||
|
const tags = post.frontmatter.tags;
|
||||||
|
const tagIds = Array.isArray(post.frontmatter.tagId)
|
||||||
|
? post.frontmatter.tagId
|
||||||
|
: post.frontmatter.tagId ? [post.frontmatter.tagId] : [];
|
||||||
|
|
||||||
|
// If tagId exists, use mapping between tagId and tag
|
||||||
|
if (tagIds.length > 0 && tagIds.length === tags.length) {
|
||||||
|
tags.forEach((tag: string, index: number) => {
|
||||||
|
if (tag && tagIds[index]) {
|
||||||
|
allTags.add(tag);
|
||||||
|
tagMap.set(tag, tagIds[index]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// If no tagId or lengths don't match, use tag name as ID
|
||||||
|
tags.forEach((tag: string) => {
|
||||||
|
if (tag) {
|
||||||
|
allTags.add(tag);
|
||||||
|
tagMap.set(tag, tag.toLowerCase());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return tagMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get base URL for blog based on language
|
||||||
|
* @param lang - Current language
|
||||||
|
* @returns Base URL string
|
||||||
|
*/
|
||||||
|
export function getBlogBaseUrl(lang: Lang): string {
|
||||||
|
return lang === defaultLang ? '/blog' : `/${lang}/blog`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process raw blog post data from import.meta.glob
|
||||||
|
* @param allPosts - Raw posts data from import.meta.glob
|
||||||
|
* @param lang - Current language
|
||||||
|
* @returns Processed blog posts array
|
||||||
|
*/
|
||||||
|
export function processRawBlogPosts(allPosts: Record<string, any>[] | any[], lang: Lang): BlogPost[] {
|
||||||
|
// Process blog post data
|
||||||
|
const posts: BlogPost[] = allPosts.map((post: any) => {
|
||||||
|
const slug = post.url?.split('/').filter(Boolean).pop() || '';
|
||||||
|
|
||||||
|
// Default image if not specified in frontmatter
|
||||||
|
const defaultImage = "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=250&fit=crop&crop=center";
|
||||||
|
|
||||||
|
// Default read time text based on language
|
||||||
|
const defaultReadTime = lang === 'zh' ? '5 分钟阅读' : '5 min read';
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: post.frontmatter.title,
|
||||||
|
description: post.frontmatter.description || '',
|
||||||
|
image: post.frontmatter.image || defaultImage,
|
||||||
|
slug: slug,
|
||||||
|
tags: post.frontmatter.tags || [],
|
||||||
|
tagId: post.frontmatter.tagId || [],
|
||||||
|
category: Array.isArray(post.frontmatter.category) ? post.frontmatter.category : post.frontmatter.category ? [post.frontmatter.category] : [],
|
||||||
|
categoryId: Array.isArray(post.frontmatter.categoryId) ? post.frontmatter.categoryId : post.frontmatter.categoryId ? [post.frontmatter.categoryId] : [],
|
||||||
|
date: post.frontmatter.date || post.frontmatter.pubDate || '',
|
||||||
|
readTime: post.frontmatter.readTime || post.frontmatter.readingTime || defaultReadTime,
|
||||||
|
url: post.url || '',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return posts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract post metadata for categories and tags
|
||||||
|
* @param posts - Array of blog posts
|
||||||
|
* @returns Array of post metadata
|
||||||
|
*/
|
||||||
|
export function extractPostMetadata(posts: any[]) {
|
||||||
|
return posts.map((post: any) => ({
|
||||||
|
category: post.frontmatter?.category || [],
|
||||||
|
categoryId: post.frontmatter?.categoryId || [],
|
||||||
|
tags: post.frontmatter?.tags || [],
|
||||||
|
tagId: post.frontmatter?.tagId || []
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all blog posts with processing for a taxonomy page (category or tag)
|
||||||
|
* @param lang - Current language
|
||||||
|
* @param options - Additional options for processing
|
||||||
|
* @returns Object containing processed posts and metadata
|
||||||
|
*/
|
||||||
|
export async function getTaxonomyPageData(lang: Lang, options?: {
|
||||||
|
category?: string;
|
||||||
|
tag?: string;
|
||||||
|
}) {
|
||||||
|
// Read all blog posts
|
||||||
|
// Note: import.meta.glob only accepts literal strings, not variables
|
||||||
|
let allPosts;
|
||||||
|
|
||||||
|
// Use specific glob patterns based on language
|
||||||
|
if (lang === 'zh') {
|
||||||
|
allPosts = await import.meta.glob('/src/pages/zh/blog/posts/*.{md,mdx}', { eager: true });
|
||||||
|
} else {
|
||||||
|
allPosts = await import.meta.glob('/src/pages/blog/posts/*.{md,mdx}', { eager: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process blog post data
|
||||||
|
const blogPosts = processRawBlogPosts(Object.values(allPosts), lang);
|
||||||
|
|
||||||
|
// Apply filters if specified
|
||||||
|
let filteredPosts = blogPosts;
|
||||||
|
|
||||||
|
if (options?.category) {
|
||||||
|
filteredPosts = filterPostsByCategory(filteredPosts, options.category);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options?.tag) {
|
||||||
|
filteredPosts = filterPostsByTag(filteredPosts, options.tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort posts by date
|
||||||
|
const sortedBlogPosts = sortPostsByDate(filteredPosts);
|
||||||
|
|
||||||
|
// Extract categories and tags from all posts for sidebar
|
||||||
|
const allPostsMetadata = extractPostMetadata(Object.values(allPosts));
|
||||||
|
|
||||||
|
// Get categories and tags for sidebar
|
||||||
|
const categories = extractCategories(allPostsMetadata);
|
||||||
|
const tags = extractTags(allPostsMetadata);
|
||||||
|
|
||||||
|
return {
|
||||||
|
posts: sortedBlogPosts,
|
||||||
|
categories,
|
||||||
|
tags,
|
||||||
|
allPosts
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user