diff options
author | João Augusto Costa Branco Marado Torres <torres.dev@disroot.org> | 2025-07-27 10:58:32 -0300 |
---|---|---|
committer | João Augusto Costa Branco Marado Torres <torres.dev@disroot.org> | 2025-07-27 10:58:32 -0300 |
commit | 9f3b746f72bc7895d3f659a7201969349f5e5298 (patch) | |
tree | 094f189733c06570e351d837996e663192e960d8 | |
parent | 3159d2fd4bf98571c6131153229aa5bf96d7a56e (diff) |
refactor: cleaner code
Signed-off-by: João Augusto Costa Branco Marado Torres <torres.dev@disroot.org>
-rw-r--r-- | src/components/templates/PrevNextNav.astro | 56 | ||||
-rw-r--r-- | src/layouts/PrevNext.astro | 13 | ||||
-rw-r--r-- | src/lib/collection/helpers.ts | 162 | ||||
-rw-r--r-- | src/pages/blog/[...date].astro | 259 |
4 files changed, 250 insertions, 240 deletions
diff --git a/src/components/templates/PrevNextNav.astro b/src/components/templates/PrevNextNav.astro new file mode 100644 index 0000000..fe8bd8d --- /dev/null +++ b/src/components/templates/PrevNextNav.astro @@ -0,0 +1,56 @@ +--- +interface Props { + previous?: string | URL; + next?: string | URL; + label?: string; +} + +const { next, previous, label } = Astro.props; +--- + +{ + (next || previous) && ( + <nav> + { + previous && ( + <p> + < <a rel="prev" href={`/blog/${Astro.props.previous}`} + >Anterior</a> + </p> + ) + } + {label && <span class="small" set:html={label} />} + { + next && ( + <p> + <a rel="next" href={`/blog/${Astro.props.next}`}>Próximo</a> > + </p> + ) + } + </nav> + ) +} + +<style> + nav { + display: flex; + align-items: center; + padding-block: calc(var(--size-2) * 1em); + gap: calc(var(--size-2) * 1em); + justify-content: center; + & > p { + border-radius: calc(var(--size-0) * 1em); + border-width: 1px; + box-shadow: 0 1px 2px var(--color-light); + font-size: calc(var(--size-3) * 1rem); + font-weight: 500; + padding-inline: calc(var(--size-3) * 1em); + padding-block: calc(var(--size-2) * 1em); + display: inline-flex; + gap: calc(var(--size-2) * 1em); + align-items: center; + justify-content: center; + height: 2ch; + } + } +</style> diff --git a/src/layouts/PrevNext.astro b/src/layouts/PrevNext.astro new file mode 100644 index 0000000..64db40b --- /dev/null +++ b/src/layouts/PrevNext.astro @@ -0,0 +1,13 @@ +--- +import PrevNextNav from "@components/templates/PrevNextNav.astro"; + +interface Props { + previous?: string | URL; + next?: string | URL; + label?: string; +} +--- + +<PrevNextNav {...Astro.props} /> +<slot /> +<PrevNextNav {...Astro.props} /> diff --git a/src/lib/collection/helpers.ts b/src/lib/collection/helpers.ts index 0bfd3c8..f65e7c0 100644 --- a/src/lib/collection/helpers.ts +++ b/src/lib/collection/helpers.ts @@ -13,11 +13,6 @@ 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 { @@ -123,160 +118,3 @@ export async function getTranslationOriginal( } 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<number>(), - monthsByYear: new Map<string, Set<number>>(), - daysByMonth: new Map<string, Set<number>>(), - postsByDate: new Map<string, typeof posts>(), - 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/pages/blog/[...date].astro b/src/pages/blog/[...date].astro index d66ac8e..a0764be 100644 --- a/src/pages/blog/[...date].astro +++ b/src/pages/blog/[...date].astro @@ -1,14 +1,176 @@ --- import type { + GetStaticPaths, + GetStaticPathsItem, InferGetStaticParamsType, InferGetStaticPropsType, } from "astro"; import Base from "@layouts/Base.astro"; +import PrevNext from "@layouts/PrevNext.astro"; import DateSelector from "@components/DateSelector.astro"; import SimplePostList from "@components/templates/SimplePostList.astro"; -import { datePaths, sortLastCreated } from "@lib/collection/helpers"; +import { fromPosts, isEntry, sortLastCreated } from "@lib/collection/helpers"; +import type { Entry } from "@lib/collection/schemas"; +import { identity } from "@utils/anonymous"; -export const getStaticPaths = datePaths; +export const getStaticPaths = (async (): Promise< + { + params: { date?: string }; + props: { + posts: Entry[]; + next?: string; + previous?: string; + years: number[]; + months: number[]; + days?: number[]; + }; + }[] +> => { + const posts = await fromPosts(isEntry, identity); + + const archive = { + years: new Set<number>(), + monthsByYear: new Map<string, Set<number>>(), + daysByMonth: new Map<string, Set<number>>(), + postsByDate: new Map<string, typeof posts>(), + 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: Entry[]; + 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; export type Params = InferGetStaticParamsType<typeof getStaticPaths>; export type Props = InferGetStaticPropsType<typeof getStaticPaths>; @@ -29,7 +191,8 @@ const format = date === undefined month: dateParts?.[1] === undefined ? undefined : "long", day: dateParts?.[2] === undefined ? undefined : "numeric", }).format(date); -const title = "Publicações" + (format !== undefined ? ` -- ${format}` : ""); +const title = "Publicações" + + (format !== undefined ? ` – ${format}` : ""); const description = "Ultímas publicações" + (format !== undefined ? ` do dia ${format}` : "") + "."; --- @@ -46,83 +209,23 @@ const description = "Ultímas publicações" + itemscope itemtype="http://schema.org/Blog" > - <h2 itemprop="name description">{title}</h2> + <h2 itemprop="name description" set:html={title} /> <DateSelector {date} {years} {months} {days} /> - { - (next || previous) && ( - <nav> - { - previous && ( - <p> - < <a href={`/blog/${Astro.props.previous}`}>Anterior</a> - </p> - ) - } - <span class="small">{format}</span> - { - next && ( - <p><a href={`/blog/${Astro.props.next}`}>Próximo</a> ></p> - ) - } - </nav> - ) - } - <SimplePostList - {posts} - dateOptions={{ - weekday: "long", - year: "numeric", - month: "long", - day: "numeric", - hour: "2-digit", - minute: "2-digit", - timeZoneName: "long", - }} - /> - { - (next || previous) && ( - <nav> - { - previous && ( - <p> - < <a href={`/blog/${Astro.props.previous}`}>Anterior</a> - </p> - ) - } - <span class="small">{format}</span> - { - next && ( - <p><a href={`/blog/${Astro.props.next}`}>Próximo</a> ></p> - ) - } - </nav> - ) - } + <PrevNext {previous} {next} label={format}> + <SimplePostList + {posts} + dateOptions={{ + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + timeZoneName: "long", + }} + /> + </PrevNext> <DateSelector {date} {years} {months} {days} /> </section> </main> </Base> - -<style> - nav { - display: flex; - align-items: center; - padding-block: calc(var(--size-2) * 1em); - gap: calc(var(--size-2) * 1em); - justify-content: center; - & > p { - border-radius: calc(var(--size-0) * 1em); - border-width: 1px; - box-shadow: 0 1px 2px var(--color-light); - font-size: calc(var(--size-3) * 1rem); - font-weight: 500; - padding-inline: calc(var(--size-3) * 1em); - padding-block: calc(var(--size-2) * 1em); - display: inline-flex; - gap: calc(var(--size-2) * 1em); - align-items: center; - justify-content: center; - height: 2ch; - } - } -</style> |