diff options
Diffstat (limited to 'src/pages')
-rw-r--r-- | src/pages/blog/[...year].astro | 30 | ||||
-rw-r--r-- | src/pages/blog/micro/[page].astro | 32 | ||||
-rw-r--r-- | src/pages/blog/read/[...slug].astro | 74 | ||||
-rw-r--r-- | src/pages/index.astro | 104 | ||||
-rw-r--r-- | src/pages/robots.txt.ts | 4 | ||||
-rw-r--r-- | src/pages/rss.xml.js | 16 | ||||
-rw-r--r-- | src/pages/rss.xml.ts | 32 |
7 files changed, 214 insertions, 78 deletions
diff --git a/src/pages/blog/[...year].astro b/src/pages/blog/[...year].astro index f148a76..1742baa 100644 --- a/src/pages/blog/[...year].astro +++ b/src/pages/blog/[...year].astro @@ -1,20 +1,16 @@ --- +import type { + GetStaticPaths, + InferGetStaticParamsType, + InferGetStaticPropsType, +} from "astro"; import { getCollection } from "astro:content"; -import type { CollectionEntry } from "astro:content"; import Base from "@layouts/Base.astro"; import DateSelector from "@components/DateSelector.astro"; import BlogCard from "@components/BlogCard.astro"; +import { sortLastCreated } from "@lib/collection/helpers"; -type Props = { - posts: CollectionEntry<"blog">[]; - next: string; - previous: string; - years: number[]; - months: number[]; - days?: number[]; -}; - -export async function getStaticPaths() { +export const getStaticPaths = (async () => { const posts = await getCollection("blog"); const archive = { @@ -128,7 +124,7 @@ export async function getStaticPaths() { paths.push({ params: { year: ymd }, props: { - posts: archive.postsByDate.get(ymd), + posts: archive.postsByDate.get(ymd) ?? [], next: archive.sortedDates?.[i + 1], previous: archive.sortedDates?.[i - 1], years: sortedYears, @@ -139,16 +135,16 @@ export async function getStaticPaths() { } return paths; -} +}) satisfies GetStaticPaths; + +export type Params = InferGetStaticParamsType<typeof getStaticPaths>; +export type Props = InferGetStaticPropsType<typeof getStaticPaths>; const title = "Blog"; const description = "Latest articles."; let { posts, previous, next, years, months, days } = Astro.props; -posts = posts.sort((a, b) => - new Date(b.data.dateCreated).valueOf() - - new Date(a.data.dateCreated).valueOf() -); +posts = posts.sort(sortLastCreated); const date = posts[0].data.dateCreated as Date; --- diff --git a/src/pages/blog/micro/[page].astro b/src/pages/blog/micro/[page].astro new file mode 100644 index 0000000..9fb04f1 --- /dev/null +++ b/src/pages/blog/micro/[page].astro @@ -0,0 +1,32 @@ +--- +import MicroBlog from "@components/templates/MicroBlog.astro"; +import Base from "@layouts/Base.astro"; +import { fromPosts, isMicro } from "@lib/collection/helpers"; +import { identity } from "@utils/anonymous"; +import type { + GetStaticPaths, + InferGetStaticParamsType, + InferGetStaticPropsType, +} from "astro"; + +export const getStaticPaths = (async ({ paginate }) => { + const micros = await fromPosts(isMicro, identity); + + return paginate(micros, { pageSize: 20 }); +}) satisfies GetStaticPaths; + +export type Params = InferGetStaticParamsType<typeof getStaticPaths>; +export type Props = InferGetStaticPropsType<typeof getStaticPaths>; + +const { page } = Astro.props; +--- +<Base title="Micro Blogue"> + <h1>Page {page.currentPage}</h1> + <ul> + {page.data.map((micro) => <li><MicroBlog {...micro} /></li>)} + </ul> + {page.url.first ? <a href={page.url.first}>First</a> : null} + {page.url.prev ? <a href={page.url.prev}>Previous</a> : null} + {page.url.next ? <a href={page.url.next}>Next</a> : null} + {page.url.last ? <a href={page.url.last}>Last</a> : null} +</Base> diff --git a/src/pages/blog/read/[...slug].astro b/src/pages/blog/read/[...slug].astro index 05d68e8..348a976 100644 --- a/src/pages/blog/read/[...slug].astro +++ b/src/pages/blog/read/[...slug].astro @@ -9,9 +9,9 @@ import Keywords from "@components/Keywords.astro"; import Citations from "@components/Citations.astro"; import Signature from "@components/signature/Signature.astro"; import CopyrightNotice from "@components/CopyrightNotice.astro"; -import { getEntries } from "astro:content"; import { verifier as verifierPrototype } from "@lib/pgp/verify"; -import { defined, get } from "@utils/anonymous"; +import { getSigners } from "@lib/collection/helpers"; +import { get } from "@utils/anonymous"; import Authors from "@components/signature/Authors.astro"; import { getEntry } from "astro:content"; @@ -27,8 +27,9 @@ type Props = CollectionEntry<"blog">; const post = Astro.props; -if (defined(post.data.translationOf)) { - const original = await getEntry( +let original: CollectionEntry<"blog">; +if (post.data.kind === "translation") { + original = await getEntry( post.data.translationOf as CollectionEntry<"blog">, ); @@ -40,15 +41,15 @@ if (defined(post.data.translationOf)) { (s) => s.role === "author", ).map((s) => s.entity.id)?.[0]; const originalCoAuthors = new Set( - (original.data.signer ?? []).filter( + (original.data.signers ?? []).filter( (s) => s.role === "co-author", ).map((s) => s.entity.id), ); - const translationAuthor = (post.data.signer ?? []).filter( + const translationAuthor = (post.data.signers ?? []).filter( (s) => s.role === "author", ).map((s) => s.entity.id)?.[0]; const translationCoAuthors = new Set( - (post.data.signer ?? []).filter( + (post.data.signers ?? []).filter( (s) => s.role === "co-author", ).map((s) => s.entity.id), ); @@ -63,7 +64,7 @@ if (defined(post.data.translationOf)) { ); } - const translators = (post.data.signer ?? []).filter( + const translators = (post.data.signers ?? []).filter( (s) => s.role === "translator", ).map((s) => s.entity.id); @@ -77,7 +78,8 @@ if (defined(post.data.translationOf)) { } } } else { - if (post.data.signer?.some((x) => x.role === "translator")) { + original = post; + if (post.data.signers?.some((x) => x.role === "translator")) { throw new Error( `Post ${post.id} is not a translation but has translators defined`, ); @@ -89,8 +91,8 @@ const translationsSet = new Set( (await getCollection( "blog", (x) => - x.data.translationOf?.id === - (post.data.translationOf !== undefined + (x.data.kind === "translation") && x.data.translationOf.id === + (post.data.kind === "translation" ? post.data.translationOf.id : post.id), ) ?? []).map(({ id }) => id), @@ -102,16 +104,7 @@ const translations = [...translationsSet.values()].map((id) => ({ id, })); -const signers = await getEntries( - post.data.signer?.map(get("entity")) ?? [], -).then((x) => x.filter(defined)) - .then((x) => - x.map((x) => ({ - entity: x, - role: post.data.signer?.find((y) => y.entity.id === x.id)?.role, - })) - ) - .then((x) => x.filter((x) => x.role !== undefined)); +const signers = await getSigners(post); const verifier = await verifierPrototype.then((x) => x.clone()); @@ -137,7 +130,12 @@ const commit = await verification?.commit; <html lang="pt-PT"> <head> - <BaseHead title={post.data.title} description={post.data.description} /> + <BaseHead + title={post.data.title} + description={"description" in post.data + ? post.data.description + : post.data.title} + /> </head> <body> @@ -151,7 +149,7 @@ const commit = await verification?.commit; <hgroup> <h1 itemprop="headline">{post.data.title}</h1> { - post.data.subtitle && ( + "subtitle" in post.data && ( <p itemprop="alternativeHeadline" class="subtitle"> {post.data.subtitle} </p> @@ -159,7 +157,8 @@ const commit = await verification?.commit; } </hgroup> { - post.data.description && ( + "description" in post.data && post.data.description && + ( <section itemprop="abstract"> <h2>Resumo</h2> { @@ -181,7 +180,7 @@ const commit = await verification?.commit; <Authors verifications={verification.verifications} expectedSigners={signers} - commitSignerKey={commit?.signature?.keyFingerPrint} + commitSignerKey={commit?.signature?.signer} /> ) } @@ -201,7 +200,7 @@ const commit = await verification?.commit; post.data.dateUpdated && ( <dt>Última atualização</dt><dd> <time - itemprop="dateUpdated" + itemprop="dateModified" datetime={toIso8601Full(post.data.dateUpdated)} >{ new Intl.DateTimeFormat([lang], {}).format( @@ -212,7 +211,8 @@ const commit = await verification?.commit; ) } { - post.data.locationCreated && ( + "locationCreated" in post.data && + post.data.locationCreated && ( <dt itemprop="locationCreated" itemscope @@ -230,15 +230,23 @@ const commit = await verification?.commit; <hr /> <div itemprop="articleBody text"><Content /></div> <hr /> - <Keywords keywords={post.data.keywords} /> - <Citations citations={post.data.relatedPosts} /> + { + "keywords" in original.data && ( + <Keywords keywords={original.data.keywords} /> + ) + } + { + "relatedPosts" in original.data && ( + <Citations citations={original.data.relatedPosts} /> + ) + } <CopyrightNotice - author={signers[0]?.entity.data.website?.[0] ?? "Anonymous"} - website={signers[0]?.entity.data.website?.[0]} - email={signers[0]?.entity.data.website?.[0]} + author={signers[0]?.entity.data.websites?.[0] ?? "Anonymous"} + website={signers[0]?.entity.data.websites?.[0]} + email={signers[0]?.entity.data.websites?.[0]} title={post.data.title} dateCreated={post.data.dateCreated} - license={post.data.license as License} + license={post.data.license} /> </article> </main> diff --git a/src/pages/index.astro b/src/pages/index.astro index eea5205..7e506bd 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,28 +1,112 @@ --- +import MicroBlog from "@components/templates/MicroBlog.astro"; +import SimplePostList from "@components/templates/SimplePostList.astro"; import Base from "@layouts/Base.astro"; -import { SITE_TITLE } from "src/consts"; +import { + fromPosts, + isMicro, + isOriginal, + sortLastUpdated, +} from "@lib/collection/helpers"; +import { env } from "@lib/env"; + +const { PUBLIC_SITE_TITLE } = env; + +const originals = await fromPosts( + isOriginal, + (originals) => originals.sort(sortLastUpdated).slice(0, 10), +); +const micro = await fromPosts( + isMicro, + (originals) => originals.sort(sortLastUpdated)?.[0], +); --- -<Base title={SITE_TITLE} showSearch={true} showNav={true}> +<Base title={PUBLIC_SITE_TITLE}> <main> <article> <h2>Viva abril!</h2> <figure> <blockquote lang="es-VE" translate="no"> - «Los que le cierran el camino a la revolución - pacífica le abren al mismo tiempo el camino a la - revolución violenta». + <i>«Los que le cierran el camino a la revolución pacífica le abren al + mismo tiempo el camino a la revolución violenta.»</i> </blockquote> <figcaption> - — Hugo Chávez. + — Hugo Chávez. <p> - Tradução: “Aqueles que fecham o caminho para a - revolução pacífica abrem, ao mesmo tempo, o - caminho para a revolução violenta.” + <small>Tradução: “Aqueles que fecham o caminho para a + revolução pacífica abrem, ao mesmo tempo, o caminho para a + revolução violenta”.</small> </p> </figcaption> </figure> - <p><em>Portugal <em>fez</em> diferente!</em></p> + <p class="lead"><em>Portugal <em>fez</em> diferente!</em></p> </article> + { + (originals.length > 0 || micro) && ( + <section id="posts"> + <h2>Últimas aplicações atualizadas</h2> + {micro && <div id="last-micro"><MicroBlog {...micro} /></div>} + <div id="last-originals"><SimplePostList posts={originals} /></div> + </section> + ) + } </main> </Base> + +<style> + figure:has(blockquote) { + border-inline-start: 2px solid var(--color-active); + padding-inline-start: calc(var(--size-7) * 1em); + + & > blockquote { + margin-block-start: calc(var(--size-7) * 1em); + margin-inline-start: 0; + border-inline-start: 2px solid var(--color-light); + padding-inline-start: calc(var(--size-7) * 1em); + } + } + + #posts { + position: relative; + + & > h2 { + float: inline-start; + } + } + + #last-micro { + clear: inline-start; + max-width: 40ch; + margin-inline: auto; + } + + #last-originals { + clear: inline-start; + } + + @media (width >= 30rem) { + #posts { + & > h2 { + float: inline-start; + max-width: calc( + 100svw + - calc( + 50svw + + calc( + calc(2 * calc(var(--size-4) * 1em)) + calc(var(--size-2) * 1em) + ) + ) + ); + } + } + + #last-micro { + clear: none; + float: inline-end; + width: 50svw; + margin-inline-start: calc(var(--size-2) * 1em); + margin-block-end: calc(var(--size-2) * 1em); + } + } +</style> diff --git a/src/pages/robots.txt.ts b/src/pages/robots.txt.ts index 4edef8b..78c9fdf 100644 --- a/src/pages/robots.txt.ts +++ b/src/pages/robots.txt.ts @@ -1,4 +1,4 @@ -import type { APIRoute } from "astro"; +import type { APIContext, APIRoute } from "astro"; const getRobotsTxt = (sitemapURL: URL) => ` User-agent: * @@ -7,7 +7,7 @@ Allow: / Sitemap: ${sitemapURL.href} `; -export const GET: APIRoute = ({ site }) => { +export const GET: APIRoute = ({ site }: APIContext): Response => { const sitemapURL = new URL("sitemap-index.xml", site); return new Response(getRobotsTxt(sitemapURL)); }; diff --git a/src/pages/rss.xml.js b/src/pages/rss.xml.js deleted file mode 100644 index de5685b..0000000 --- a/src/pages/rss.xml.js +++ /dev/null @@ -1,16 +0,0 @@ -import rss from "@astrojs/rss"; -import { getCollection } from "astro:content"; -import { SITE_DESCRIPTION, SITE_TITLE } from "../consts"; - -export async function GET(context) { - const posts = await getCollection("blog"); - return rss({ - title: SITE_TITLE, - description: SITE_DESCRIPTION, - site: context.site, - items: posts.map((post) => ({ - ...post.data, - link: `/blog/${post.id}/`, - })), - }); -} diff --git a/src/pages/rss.xml.ts b/src/pages/rss.xml.ts new file mode 100644 index 0000000..c07f3bd --- /dev/null +++ b/src/pages/rss.xml.ts @@ -0,0 +1,32 @@ +import rss, { type RSSFeedItem } from "@astrojs/rss"; +import { getCollection } from "astro:content"; +import type { APIContext, APIRoute } from "astro"; +import { Blog } from "../lib/collection/schemas.ts"; +import { getFirstAuthorEmail } from "../lib/collection/helpers.ts"; +import { env } from "../lib/env.ts"; + +const { PUBLIC_SITE_TITLE, PUBLIC_SITE_DESCRIPTION, PUBLIC_SITE_URL } = env; + +export const GET: APIRoute = async (context: APIContext): Promise<Response> => { + const posts = await getCollection("blog"); + return rss({ + title: PUBLIC_SITE_TITLE, + description: PUBLIC_SITE_DESCRIPTION, + site: context.site ?? PUBLIC_SITE_URL, + items: await Promise.all(posts.map(async (post): Promise<RSSFeedItem> => { + const { id, rendered } = post; + const blog = Blog.parse(post.data); + + const { title, dateUpdated, dateCreated } = blog; + return { + description: "description" in blog ? blog.description : undefined, + title, + author: await getFirstAuthorEmail(post), + content: rendered?.html, + pubDate: dateUpdated ?? dateCreated, + categories: "keywords" in blog ? blog.keywords : undefined, + link: `/blog/read/${id}/`, + }; + })), + }); +}; |