summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoão Augusto Costa Branco Marado Torres <torres.dev@disroot.org>2025-07-27 10:58:32 -0300
committerJoão Augusto Costa Branco Marado Torres <torres.dev@disroot.org>2025-07-27 10:58:32 -0300
commit9f3b746f72bc7895d3f659a7201969349f5e5298 (patch)
tree094f189733c06570e351d837996e663192e960d8
parent3159d2fd4bf98571c6131153229aa5bf96d7a56e (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.astro56
-rw-r--r--src/layouts/PrevNext.astro13
-rw-r--r--src/lib/collection/helpers.ts162
-rw-r--r--src/pages/blog/[...date].astro259
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>
+ &lt; <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> &gt;
+ </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 ? ` &ndash; ${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>
- &lt; <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> &gt;</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>
- &lt; <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> &gt;</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>