From c530d30a74bb521b1c22f0e2a468d9b69c6a2e8e Mon Sep 17 00:00:00 2001 From: João Augusto Costa Branco Marado Torres Date: Mon, 21 Jul 2025 00:37:34 -0300 Subject: feat: blog listing style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Augusto Costa Branco Marado Torres --- src/lib/collection/helpers.ts | 172 ++++++++++++++++++++++++++++++++++++++++++ src/lib/collection/schemas.ts | 9 ++- 2 files changed, 180 insertions(+), 1 deletion(-) (limited to 'src/lib/collection') diff --git a/src/lib/collection/helpers.ts b/src/lib/collection/helpers.ts index 587180b..0bfd3c8 100644 --- a/src/lib/collection/helpers.ts +++ b/src/lib/collection/helpers.ts @@ -13,6 +13,12 @@ import { createKeyFromArmor } from "../pgp/create.ts"; import { getUserIDsFromKey } from "../pgp/user.ts"; import type { UserIDPacket } from "openpgp"; import { getCollection } from "astro:content"; +import { + GetStaticPaths, + GetStaticPathsItem, + GetStaticPathsResult, +} from "astro"; +import { getEntry } from "astro:content"; export function getLastUpdate({ data }: CollectionEntry<"blog">): Date { return data.dateUpdated ?? data.dateCreated; @@ -108,3 +114,169 @@ export const isTranslation = ( export const isMicro = ( entry: CollectionEntry<"blog">, ): entry is MicroEntry => entry.data.kind === "micro"; + +export async function getTranslationOriginal( + translation: TranslationEntry, +): Promise { + if (!isTranslation(translation)) { + throw new Error(); + } + return await getEntry(translation.data.translationOf); +} + +export const datePaths = (async (): Promise< + { + params: { date?: string }; + props: { + posts: OriginalEntry[]; + next?: string; + previous?: string; + years: number[]; + months: number[]; + days?: number[]; + }; + }[] +> => { + const posts = await fromPosts(isEntry, identity); + + const archive = { + years: new Set(), + monthsByYear: new Map>(), + daysByMonth: new Map>(), + postsByDate: new Map(), + sortedDates: [] as string[], + }; + + const getYMD = (date: Date) => { + const y = date.getFullYear(); + const m = date.getMonth() + 1; + const d = date.getDate(); + return { y, m, d }; + }; + + for (const post of posts) { + const { y, m, d } = getYMD(post.data.dateCreated); + + archive.years.add(y); + + const months = archive.monthsByYear.get(y.toString()); + if (months === undefined) { + archive.monthsByYear.set(y.toString(), new Set([m])); + } else { + months.add(m); + } + + const ym = `${y}/${String(m).padStart(2, "0")}`; + const days = archive.daysByMonth.get(ym); + if (days === undefined) { + archive.daysByMonth.set(ym, new Set([d])); + } else { + days.add(d); + } + + const ymd = `${ym}/${String(d).padStart(2, "0")}`; + const posts = archive.postsByDate.get(ymd); + if (posts === undefined) { + archive.postsByDate.set(ymd, [post]); + } else { + posts.push(post); + } + } + + archive.sortedDates = Array.from(archive.postsByDate.keys()).sort(); + + const paths: { + params: { date?: string }; + props: { + posts: OriginalEntry[]; + next?: string; + previous?: string; + years: number[]; + months: number[]; + days?: number[]; + }; + }[] = [] satisfies GetStaticPathsItem[]; + + const sortedYears = Array.from(archive.years).sort(); + + const lastYear = Math.max(...sortedYears.map(Number)); + paths.push({ + params: { date: undefined }, + props: { + posts: posts.filter((p) => p.data.dateCreated.getFullYear() === lastYear), + next: undefined, + previous: sortedYears?.[sortedYears.length - 2]?.toString(), + years: sortedYears, + months: Array.from(archive.monthsByYear.get(lastYear.toString()) ?? []), + }, + }); + + for (const y of sortedYears) { + const yearPosts = posts.filter((p) => + p.data.dateCreated.getFullYear() === Number(y) + ); + const idx = sortedYears.indexOf(y); + paths.push({ + params: { date: y.toString() }, + props: { + posts: yearPosts, + next: sortedYears?.[idx + 1]?.toString(), + previous: sortedYears?.[idx - 1]?.toString(), + years: sortedYears, + months: Array.from(archive.monthsByYear.get(y.toString()) ?? []), + }, + }); + } + + const allMonths = Array.from(archive.monthsByYear.entries()) + .flatMap(([year, mset]) => + Array.from(mset).map((m) => `${year}/${String(m).padStart(2, "0")}`) + ) + .sort(); + + for (const [y, months] of archive.monthsByYear) { + const sortedMonths = Array.from(months).sort(); + for (const m of sortedMonths) { + const monthPosts = posts.filter((p) => { + const d = p.data.dateCreated; + return ( + d.getFullYear() === Number(y) && + d.getMonth() + 1 === m + ); + }); + + const ym = `${y}/${String(m).padStart(2, "0")}`; + const idx = allMonths.indexOf(ym); + + paths.push({ + params: { date: ym }, + props: { + posts: monthPosts, + next: allMonths?.[idx + 1], + previous: allMonths?.[idx - 1], + years: sortedYears, + months: Array.from(months).sort(), + days: Array.from(archive.daysByMonth.get(ym) ?? []).sort(), + }, + }); + } + } + + for (let i = 0; i < archive.sortedDates.length; i++) { + const ymd = archive.sortedDates[i]; + const [y, m] = ymd.split("/"); + paths.push({ + params: { date: ymd }, + props: { + posts: archive.postsByDate.get(ymd) ?? [], + next: archive.sortedDates?.[i + 1], + previous: archive.sortedDates?.[i - 1], + years: sortedYears, + months: Array.from(archive.monthsByYear.get(y) ?? []).sort(), + days: Array.from(archive.daysByMonth.get(`${y}/${m}`) ?? []).sort(), + }, + }); + } + + return paths; +}) satisfies GetStaticPaths; diff --git a/src/lib/collection/schemas.ts b/src/lib/collection/schemas.ts index eca996f..f8a021d 100644 --- a/src/lib/collection/schemas.ts +++ b/src/lib/collection/schemas.ts @@ -3,7 +3,14 @@ import { isValidLocale } from "../../utils/lang.ts"; import { get } from "../../utils/anonymous.ts"; import type { CollectionEntry } from "astro:content"; -export const KEYWORDS = ["Portugal", "democracy", "test"] as const; +export const KEYWORDS = [ + "Portugal", + "democracy", + "test", + "health", + "gym", + "diet", +] as const; export const KeywordsEnum = z.enum(KEYWORDS); export const ENTITY_TYPES = ["author", "co-author", "translator"] as const; -- cgit v1.2.3