summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/organisms/KeywordsList.astro11
-rw-r--r--src/components/organisms/RelativeTime.astro19
-rw-r--r--src/components/templates/MicroBlog.astro66
-rw-r--r--src/components/templates/PrevNextNav.astro13
-rw-r--r--src/layouts/PrevNext.astro2
-rw-r--r--src/pages/blog/[...date].astro6
-rw-r--r--src/pages/blog/micro/[page].astro42
-rw-r--r--src/pages/index.astro2
-rw-r--r--src/utils/datetime.test.ts74
-rw-r--r--src/utils/datetime.ts25
10 files changed, 228 insertions, 32 deletions
diff --git a/src/components/organisms/KeywordsList.astro b/src/components/organisms/KeywordsList.astro
index d3b9e7f..44c5641 100644
--- a/src/components/organisms/KeywordsList.astro
+++ b/src/components/organisms/KeywordsList.astro
@@ -7,7 +7,13 @@ const { keywords } = Astro.props;
---
<p>
- {keywords.map((x) => <span>#<b itemprop="keywords">{x}</b></span>)}
+ {
+ keywords.map((x) => (
+ <span><a href={`/blog/keywords/${x}`}>#<b itemprop="keywords">{
+ x
+ }</b></a></span>
+ ))
+ }
</p>
<style>
@@ -16,8 +22,9 @@ const { keywords } = Astro.props;
flex-direction: row-reverse;
flex-wrap: wrap;
gap: calc(var(--size-0) * 1em);
+ margin-block: calc(var(--size-0) * 1em);
- & > * {
+ & > * > * {
border-radius: calc(infinity * 1px);
background-color: color-mix(
in srgb,
diff --git a/src/components/organisms/RelativeTime.astro b/src/components/organisms/RelativeTime.astro
new file mode 100644
index 0000000..13d531d
--- /dev/null
+++ b/src/components/organisms/RelativeTime.astro
@@ -0,0 +1,19 @@
+---
+import type { HTMLAttributes } from "astro/types";
+import { getRelativeTimeUnit } from "@utils/datetime";
+
+interface Props {
+ date: Date;
+ locales: Intl.LocalesArgument;
+ options: Intl.RelativeTimeFormatOptions;
+ itemprop: HTMLAttributes<"time">["itemprop"];
+}
+
+const { date, locales, options } = Astro.props;
+
+const format = new Intl.RelativeTimeFormat(locales, options).format(
+ ...getRelativeTimeUnit(date),
+);
+---
+
+{format}
diff --git a/src/components/templates/MicroBlog.astro b/src/components/templates/MicroBlog.astro
index 88321dc..713e7eb 100644
--- a/src/components/templates/MicroBlog.astro
+++ b/src/components/templates/MicroBlog.astro
@@ -1,12 +1,17 @@
---
import Date from "@components/organisms/Date.astro";
+import RelativeTime from "@components/organisms/RelativeTime.astro";
import KeywordsList from "@components/organisms/KeywordsList.astro";
import { getFirstUserID, getLastUpdate } from "@lib/collection/helpers";
-import type { Micro, MicroEntry } from "@lib/collection/schemas";
+import type { MicroEntry } from "@lib/collection/schemas";
+import { getRelativeTimeUnit } from "@utils/datetime";
-interface Props extends MicroEntry {}
+interface Props {
+ micro: MicroEntry;
+ seeAll?: boolean;
+}
-const micro = Astro.props;
+const { micro, seeAll = false } = Astro.props;
const { id, data, rendered } = micro;
const { title, lang, keywords } = data;
const date = getLastUpdate(micro);
@@ -20,6 +25,7 @@ const little = ((first?.[0] ?? "") + (last?.[0] ?? "")).slice(0, 2);
itemprop="blogPost"
itemscope
itemtype="https://schema.org/BlogPosting"
+ style={`--rows: ${seeAll ? 3 : 2};`}
>
<header>
<h3 class="title">
@@ -36,35 +42,49 @@ const little = ((first?.[0] ?? "") + (last?.[0] ?? "")).slice(0, 2);
itemscope
itemtype="https://schema.org/Person"
><span itemprop="alternateName">{first} {last}</span></span>
- <span class="small">· <Date
+ <span class="small"
+ >· <Date
{date}
locales={lang}
options={{ month: "short", day: "numeric" }}
itemprop="dateModified"
- /></span>
+ /> (<RelativeTime
+ {date}
+ locales={lang}
+ options={{
+ style: "short",
+ numeric: "auto",
+ }}
+ />)</span>
</div>
+ <span class="lang mute">{lang}</span>
</header>
<div class="content small">
<div {lang} itemprop="articleBody text">
<Fragment set:html={rendered?.html} />
</div>
<footer>
+ <a href={`/blog/read/${id}`}>Ver mais...</a>
<div class="keywords"><KeywordsList {keywords} /></div>
</footer>
</div>
- <aside>
- <small><a href="/blog/micro/1">Ver todos os microposts</a></small>
- </aside>
+ {
+ seeAll && (
+ <aside>
+ <small><a href="/blog/micro/1">Ver todos os microposts</a></small>
+ </aside>
+ )
+ }
</article>
<style>
article {
- border-radius: calc(var(--size-1) * 1em);
+ --rows: 2 border-radius: calc(var(--size-1) * 1em);
box-shadow: 0 0 calc(var(--size-1) * 1em) var(--color-light);
padding: calc(var(--size-4) * 1em);
display: grid;
- grid-template-rows: repeat(3, auto);
- grid-template-columns: calc(var(--size-9) * 1em) auto;
+ grid-template-rows: repeat(var(--rows), auto);
+ grid-template-columns: calc(var(--size-9) * 1em) repeat(2, auto);
gap: calc(var(--size-1) * 1em);
& > header {
@@ -73,7 +93,7 @@ const little = ((first?.[0] ?? "") + (last?.[0] ?? "")).slice(0, 2);
& > aside {
grid-row: 3 / 4;
- grid-column: 1 / 3;
+ grid-column: 1 / 4;
border-block-start: 1px solid #e7e7e7;
padding-block-start: calc(var(--size-1) * 1em);
}
@@ -93,7 +113,8 @@ const little = ((first?.[0] ?? "") + (last?.[0] ?? "")).slice(0, 2);
color: #fff;
font-weight: 950;
font-size: smaller;
- border-radius: calc(infinity * 1px);
+ /* border-radius: calc(infinity * 1px); */
+ border-radius: calc(var(--size-3) * 1em);
text-align: center;
text-transform: uppercase;
}
@@ -102,15 +123,32 @@ const little = ((first?.[0] ?? "") + (last?.[0] ?? "")).slice(0, 2);
.title {
display: none;
}
+
+ .lang {
+ grid-row: 1/2;
+ grid-column: 3 / 4;
+ display: flex;
+ justify-content: flex-end;
+ align-items: baseline;
+ }
+
.content {
grid-row: 2/3;
- grid-column: 2 / 3;
+ grid-column: 2 / 4;
& > [lang] > :global(*:first-child) {
margin-block-start: 0;
}
& > [lang] > :global(*:last-child) {
margin-block-end: 0;
}
+
+ & > footer {
+ display: flex;
+ gap: 1em;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ align-items: baseline;
+ }
}
.keywords {
diff --git a/src/components/templates/PrevNextNav.astro b/src/components/templates/PrevNextNav.astro
index fe8bd8d..f60a4cd 100644
--- a/src/components/templates/PrevNextNav.astro
+++ b/src/components/templates/PrevNextNav.astro
@@ -1,21 +1,23 @@
---
interface Props {
+ first?: string | URL;
previous?: string | URL;
next?: string | URL;
+ last?: string | URL;
label?: string;
}
-const { next, previous, label } = Astro.props;
+const { first, next, previous, last, label } = Astro.props;
---
{
- (next || previous) && (
+ (first || next || previous || last) && (
<nav>
+ {first && <p><a href={Astro.props.first}>Primeiro</a></p>}
{
previous && (
<p>
- &lt; <a rel="prev" href={`/blog/${Astro.props.previous}`}
- >Anterior</a>
+ &lt; <a rel="prev" href={Astro.props.previous}>Anterior</a>
</p>
)
}
@@ -23,10 +25,11 @@ const { next, previous, label } = Astro.props;
{
next && (
<p>
- <a rel="next" href={`/blog/${Astro.props.next}`}>Próximo</a> &gt;
+ <a rel="next" href={Astro.props.next}>Próximo</a> &gt;
</p>
)
}
+ {last && <p><a href={Astro.props.last}>Último</a></p>}
</nav>
)
}
diff --git a/src/layouts/PrevNext.astro b/src/layouts/PrevNext.astro
index 64db40b..58e3c2a 100644
--- a/src/layouts/PrevNext.astro
+++ b/src/layouts/PrevNext.astro
@@ -2,8 +2,10 @@
import PrevNextNav from "@components/templates/PrevNextNav.astro";
interface Props {
+ first?: string | URL;
previous?: string | URL;
next?: string | URL;
+ last?: string | URL;
label?: string;
}
---
diff --git a/src/pages/blog/[...date].astro b/src/pages/blog/[...date].astro
index a0764be..8346baf 100644
--- a/src/pages/blog/[...date].astro
+++ b/src/pages/blog/[...date].astro
@@ -211,7 +211,11 @@ const description = "Ultímas publicações" +
>
<h2 itemprop="name description" set:html={title} />
<DateSelector {date} {years} {months} {days} />
- <PrevNext {previous} {next} label={format}>
+ <PrevNext
+ previous={`/blog/${previous}`}
+ next={`/blog/${next}`}
+ label={format}
+ >
<SimplePostList
{posts}
dateOptions={{
diff --git a/src/pages/blog/micro/[page].astro b/src/pages/blog/micro/[page].astro
index 9fb04f1..72ccdcf 100644
--- a/src/pages/blog/micro/[page].astro
+++ b/src/pages/blog/micro/[page].astro
@@ -1,6 +1,7 @@
---
import MicroBlog from "@components/templates/MicroBlog.astro";
import Base from "@layouts/Base.astro";
+import PrevNext from "@layouts/PrevNext.astro";
import { fromPosts, isMicro } from "@lib/collection/helpers";
import { identity } from "@utils/anonymous";
import type {
@@ -19,14 +20,39 @@ export type Params = InferGetStaticParamsType<typeof getStaticPaths>;
export type Props = InferGetStaticPropsType<typeof getStaticPaths>;
const { page } = Astro.props;
+
+const { prev, next, first, last } = page.url;
---
<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}
+ <main
+ itemprop="mainContentOfPage"
+ itemscope
+ itemtype="https://schema.org/WebPageElement"
+ >
+ <section
+ id="posts"
+ itemprop="citation"
+ itemscope
+ itemtype="http://schema.org/Blog"
+ >
+ <h2 itemprop="name description">Página {page.currentPage}</h2>
+ <PrevNext previous={prev} {next} {first} {last}>
+ <ul>{page.data.map((micro) => <li><MicroBlog {micro} /></li>)}</ul>
+ </PrevNext>
+ </section>
+ </main>
</Base>
+<style>
+ ul {
+ max-width: 40ch;
+ margin-inline: auto;
+ margin-block: calc(var(--size-7) * 1em);
+ padding-inline: 0;
+ list-style-type: none;
+ & > li {
+ width: 100%;
+ margin-block: calc(var(--size-0) * 1em);
+ margin-inline: auto;
+ }
+ }
+</style>
diff --git a/src/pages/index.astro b/src/pages/index.astro
index bb4108c..baadbc7 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -55,7 +55,7 @@ const micro = await fromPosts(
itemtype="http://schema.org/Blog"
>
<h2 itemprop="name description">Últimas publicações atualizadas</h2>
- {micro && <div id="last-micro"><MicroBlog {...micro} /></div>}
+ {micro && <div id="last-micro"><MicroBlog {micro} seeAll /></div>}
<div id="last-originals">
<SimplePostList posts={originals} small />
</div>
diff --git a/src/utils/datetime.test.ts b/src/utils/datetime.test.ts
index 5f0749d..03e2771 100644
--- a/src/utils/datetime.test.ts
+++ b/src/utils/datetime.test.ts
@@ -1,6 +1,10 @@
import { assertEquals, assertMatch } from "@std/assert";
import { describe, it } from "@std/testing/bdd";
-import { toIso8601Full, toIso8601FullUTC } from "./datetime.ts";
+import {
+ getRelativeTimeUnit,
+ toIso8601Full,
+ toIso8601FullUTC,
+} from "./datetime.ts";
describe("toIso8601Full", () => {
it("formats current local time with offset", () => {
@@ -60,3 +64,71 @@ describe("toIso8601FullUTC", () => {
assertMatch(result, /^-\d{6}-03-15T12:00:00\.000Z$/);
});
});
+
+describe("getRelativeTimeUnit", () => {
+ const now = new Date();
+
+ it("returns seconds for differences under a minute", () => {
+ const future = new Date(now.getTime() + 45 * 1000);
+ const [value, unit] = getRelativeTimeUnit(future);
+
+ assertEquals(unit, "second");
+ assertEquals(value, 1);
+ });
+
+ it("returns minutes for differences under an hour but over a minute", () => {
+ const future = new Date(now.getTime() + 15 * 60 * 1000);
+ const [value, unit] = getRelativeTimeUnit(future);
+
+ assertEquals(unit, "minute");
+ assertEquals(value, 15);
+ });
+
+ it("returns hours for differences under a day but over an hour", () => {
+ const past = new Date(now.getTime() - 3.2 * 60 * 60 * 1000);
+ const [value, unit] = getRelativeTimeUnit(past);
+
+ assertEquals(unit, "hour");
+ assertEquals(value, -3);
+ });
+
+ it("returns days for differences under a week but over a day", () => {
+ const past = new Date(now.getTime() - 2.5 * 24 * 60 * 60 * 1000);
+ const [value, unit] = getRelativeTimeUnit(past);
+
+ assertEquals(unit, "day");
+ assertEquals(value, -3);
+ });
+
+ it("returns weeks for differences under a month but over a week", () => {
+ const future = new Date(now.getTime() + 2.3 * 7 * 24 * 60 * 60 * 1000);
+ const [value, unit] = getRelativeTimeUnit(future);
+
+ assertEquals(unit, "week");
+ assertEquals(value, 2);
+ });
+
+ it("returns months for differences under a quarter but over a month", () => {
+ const past = new Date(now.getTime() - 1.5 * 30 * 24 * 60 * 60 * 1000);
+ const [value, unit] = getRelativeTimeUnit(past);
+
+ assertEquals(unit, "month");
+ assertEquals(value, -2);
+ });
+
+ it("returns quarters for differences under a year but over a quarter", () => {
+ const future = new Date(now.getTime() + 5 * 30 * 24 * 60 * 60 * 1000);
+ const [value, unit] = getRelativeTimeUnit(future);
+
+ assertEquals(unit, "quarter");
+ assertEquals(value, 2);
+ });
+
+ it("returns years for differences over a year", () => {
+ const past = new Date(now.getTime() - 3.4 * 365 * 24 * 60 * 60 * 1000);
+ const [value, unit] = getRelativeTimeUnit(past);
+
+ assertEquals(unit, "year");
+ assertEquals(value, -3);
+ });
+});
diff --git a/src/utils/datetime.ts b/src/utils/datetime.ts
index 3a2cd25..c32fde0 100644
--- a/src/utils/datetime.ts
+++ b/src/utils/datetime.ts
@@ -41,3 +41,28 @@ export function toIso8601FullUTC(date: Date): string {
}
const pad = (num: number, len = 2) => String(Math.abs(num)).padStart(len, "0");
+
+export function getRelativeTimeUnit(
+ date: Date,
+ now: Date = new Date(),
+): Parameters<Intl.RelativeTimeFormat["format"]> {
+ const diffMs = date.getTime() - now.getTime();
+
+ const seconds = diffMs / 1000;
+ const minutes = seconds / 60;
+ const hours = minutes / 60;
+ const days = hours / 24;
+ const weeks = days / 7;
+ const months = days / 30;
+ const quarters = months / 3;
+ const years = days / 365;
+
+ if (Math.abs(years) >= 1) return [Math.round(years), "year"];
+ if (Math.abs(quarters) >= 1) return [Math.round(quarters), "quarter"];
+ if (Math.abs(months) >= 1) return [Math.round(months), "month"];
+ if (Math.abs(weeks) >= 1) return [Math.round(weeks), "week"];
+ if (Math.abs(days) >= 1) return [Math.round(days), "day"];
+ if (Math.abs(hours) >= 1) return [Math.round(hours), "hour"];
+ if (Math.abs(minutes) >= 1) return [Math.round(minutes), "minute"];
+ return [Math.round(seconds), "second"];
+}