import { type CollectionEntry, getCollection } from "astro:content"; import { collectionDateSort } from "@/utils/date"; /** filter out draft posts based on the environment and optionally archived posts */ export async function getAllPosts(includeArchived = false): Promise[]> { const posts = await getCollection("post", ({ data }) => { const isDraftFilter = import.meta.env.PROD ? !data.draft : true; const isArchivedFilter = includeArchived || !data.tags.includes("archived"); return isDraftFilter && isArchivedFilter; }); // Fetch pleroma posts and cast them to post type since schemas are now compatible const pleromaPosts = await getCollection("pleroma").catch(() => []); const pleromaAsPost = pleromaPosts as unknown as CollectionEntry<"post">[]; return [...posts, ...pleromaAsPost]; } /** Get tag metadata by tag name */ export async function getTagMeta(tag: string): Promise | undefined> { const tagEntries = await getCollection("tag", (entry) => { return entry.id === tag; }); return tagEntries[0]; } /** 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.publishDate.getFullYear(); if (!acc[year]) { acc[year] = []; } acc[year]?.push(post); return acc; }, {}); } /** 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">[]) { 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">[]) { 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][] { return [ ...getAllTags(posts).reduce( (acc, t) => acc.set(t, (acc.get(t) ?? 0) + 1), new Map(), ), ].sort((a, b) => b[1] - a[1]); } export type PostCategory = "regular" | "microblog" | "archived"; /** Determine the category of a post based on its tags. "archived" takes precedence over "microblog". */ export function getPostCategory(post: CollectionEntry<"post">): PostCategory { const tags = post.data.tags; if (tags.includes("archived")) return "archived"; if (tags.includes("microblog")) return "microblog"; return "regular"; } export interface PostNavigation { prevPost: CollectionEntry<"post"> | null; nextPost: CollectionEntry<"post"> | null; } /** Get previous (newer) and next (older) posts within the same category. * Posts are sorted newest-first. prevPost = newer, nextPost = older. */ export function getPostNavigation( allPosts: CollectionEntry<"post">[], currentPost: CollectionEntry<"post">, ): PostNavigation { const category = getPostCategory(currentPost); const sameCategoryPosts = allPosts .filter((p) => getPostCategory(p) === category) .sort(collectionDateSort); const currentIndex = sameCategoryPosts.findIndex((p) => p.id === currentPost.id); return { prevPost: currentIndex > 0 ? (sameCategoryPosts[currentIndex - 1] ?? null) : null, nextPost: currentIndex < sameCategoryPosts.length - 1 ? (sameCategoryPosts[currentIndex + 1] ?? null) : null, }; }