Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions packages/website/public/sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,22 @@
<xhtml:link rel="alternate" hreflang="en" href="https://tokenizen.nl/en/notes" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://tokenizen.nl/notes" />
</url>
<url>
<loc>https://tokenizen.nl/blog</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
<xhtml:link rel="alternate" hreflang="nl" href="https://tokenizen.nl/blog" />
<xhtml:link rel="alternate" hreflang="en" href="https://tokenizen.nl/en/blog" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://tokenizen.nl/blog" />
</url>
<url>
<loc>https://tokenizen.nl/en/blog</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
<xhtml:link rel="alternate" hreflang="nl" href="https://tokenizen.nl/blog" />
<xhtml:link rel="alternate" hreflang="en" href="https://tokenizen.nl/en/blog" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://tokenizen.nl/blog" />
</url>
<url>
<loc>https://tokenizen.nl/en/notes/check-pay-prove</loc>
<changefreq>monthly</changefreq>
Expand Down
6 changes: 3 additions & 3 deletions packages/website/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { NineWaysArticle } from "@/components/pages/nine-ways-article";
import { HierarchyOfAgentTrustArticle } from "@/components/pages/hierarchy-of-agent-trust-article";
import { TrustRadarPage } from "@/components/pages/trust-radar-page";
import { SelfCheckPage } from "@/components/pages/self-check-page";
import { NotesIndex } from "@/components/pages/notes-index";
import { BlogIndex, NotesIndex } from "@/components/pages/notes-index";
import { ZeroOf600Article } from "@/components/pages/zero-of-600-article";
import { ReceiptCannotNameArticle } from "@/components/pages/receipt-cannot-name-article";
import { RevocationsArticle } from "@/components/pages/revocations-article";
Expand All @@ -27,8 +27,8 @@ import { findPostByPath } from "@/data/blog-posts";
// homepage in plaats van het artikel te tonen.
const ARTICLES: Record<string, () => JSX.Element> = {
"/notes": NotesIndex,
// The blog and the notes are one place. /blog is the name people will guess.
"/blog": NotesIndex,
// The blog and the notes are two places. A blog is only what the owner has called one.
"/blog": BlogIndex,
"/notes/a-signed-receipt-that-cannot-name-its-own-payment": ReceiptCannotNameArticle,
"/notes/733203-attestations-were-taken-back": RevocationsArticle,
"/notes/zero-of-600-x402-services-sign-a-receipt": ZeroOf600Article,
Expand Down
25 changes: 14 additions & 11 deletions packages/website/src/components/blog/blog-post-page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";

import { BlogPostPage } from "@/components/blog/blog-post-page";
import { NotesIndex } from "@/components/pages/notes-index";
import { BlogIndex } from "@/components/pages/notes-index";
import { BLOG_POSTS, MISSION } from "@/data/blog-posts";
import { LocaleProvider, type Locale } from "@/i18n/context";

Expand All @@ -18,7 +18,7 @@ const renderPost = (locale: Locale) =>
const renderIndex = (locale: Locale) =>
renderToStaticMarkup(
<LocaleProvider locale={locale}>
<NotesIndex />
<BlogIndex />
</LocaleProvider>,
);

Expand Down Expand Up @@ -102,24 +102,27 @@ describe("BlogPostPage", () => {
it("keeps internal links in the reader's language", () => {
expect(renderPost("en")).toContain('href="/en/notes/zero-of-600-x402-services-sign-a-receipt"');
expect(renderPost("nl")).toContain('href="/notes/zero-of-600-x402-services-sign-a-receipt"');
expect(renderPost("en")).toContain('href="/en/notes"');
expect(renderPost("nl")).toContain('href="/notes"');
// The post is a blog, so its way back leads to the blog and not to the notes.
expect(renderPost("en")).toContain('href="/en/blog"');
expect(renderPost("nl")).toContain('href="/blog"');
});
});

describe("NotesIndex as the blog", () => {
it("features the newest post and shows the mission", () => {
describe("BlogIndex", () => {
it("features the pinned blog and shows no mission", () => {
const html = renderIndex("en");
expect(html).toContain(post.title);
expect(html).toContain("/en/notes/check-pay-prove");
expect(html).toContain(MISSION.headline);
expect(html).toContain("Start here");
expect(html).not.toContain(MISSION.headline);
expect(html).toContain("Pinned");
});

it("still lists the earlier notes", () => {
it("does not list the notes", () => {
const html = renderIndex("en");
expect(html).toContain("733,203 attestations were taken back");
expect(html).toContain("The hierarchy of agent trust");
expect(html).not.toContain("733,203 attestations were taken back");
expect(html).not.toContain("The hierarchy of agent trust");
expect(html).not.toContain("Official sources and payment addresses");
expect(html).not.toContain("We measured our scan");
});

it("is called Blog in both languages", () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/website/src/components/blog/blog-post-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,11 @@ export function BlogPostPage({ post }: { post: BlogPost }) {
<article className="border-t border-border py-16 md:py-24">
<div className="container max-w-3xl">
<a
href={`${prefix}/notes`}
href={`${prefix}${post.kind === "blog" ? "/blog" : "/notes"}`}
className="inline-flex items-center gap-2 text-sm text-muted-foreground transition-colors hover:text-foreground"
>
<ArrowLeft className="size-4" aria-hidden="true" />
Blog
{post.kind === "blog" ? "Blog" : "Notes"}
</a>

<p className="mt-10 flex flex-wrap items-center gap-x-3 gap-y-1 font-mono text-[11px] uppercase tracking-[0.2em] text-muted-foreground">
Expand Down
75 changes: 75 additions & 0 deletions packages/website/src/components/pages/notes-index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";

import { BlogIndex, NotesIndex, entriesOfKind } from "@/components/pages/notes-index";
import { BLOG_POSTS, MISSION, postPath } from "@/data/blog-posts";
import { LocaleProvider, type Locale } from "@/i18n/context";

const render = (locale: Locale, page: "blog" | "notes") =>
renderToStaticMarkup(<LocaleProvider locale={locale}>{page === "blog" ? <BlogIndex /> : <NotesIndex />}</LocaleProvider>);

const LOCALES: Locale[] = ["nl", "en"];

describe("the blog and the notes are two places", () => {
for (const locale of LOCALES) {
it(`[${locale}] the blog lists blogs only, with no mission block`, () => {
const html = render(locale, "blog");
for (const post of BLOG_POSTS) {
const listed = html.includes(postPath(post.slug));
expect(listed, post.slug).toBe(post.kind === "blog");
}
expect(html).not.toContain(MISSION.eyebrow);
expect(html).not.toContain(MISSION.headline);
// The seven older and newer notes must not be in the blog either.
expect(html).not.toContain("/notes/733203-attestations-were-taken-back");
expect(html).not.toContain("/notes/the-hierarchy-of-agent-trust");
});

it(`[${locale}] the notes list every note and no blog, and no mission block either`, () => {
const html = render(locale, "notes");
for (const post of BLOG_POSTS) {
expect(html.includes(postPath(post.slug)), post.slug).toBe(post.kind === "note");
}
expect(html).toContain("/notes/733203-attestations-were-taken-back");
expect(html).toContain("/notes/nine-ways-to-fake-a-delivery-claim");
expect(html).not.toContain(MISSION.eyebrow);
});
}

it("shows the pinned blog first, marked as pinned, and keeps it there whatever its date", () => {
const blogs = entriesOfKind("blog");
expect(blogs[0]?.pinned).toBe(true);
expect(blogs[0]?.slug).toBe("/notes/check-pay-prove");
const html = render("en", "blog");
expect(html).toContain("Pinned");
expect(html.indexOf("Check. Pay. Prove.")).toBeGreaterThan(-1);
expect(render("nl", "blog")).toContain("Vastgepind");
});

it("sorts a pinned entry above a newer one", () => {
const entries = entriesOfKind("blog");
const pinnedIndex = entries.findIndex((e) => e.pinned);
const newer = entries.findIndex((e) => !e.pinned && e.date > (entries[pinnedIndex]?.date ?? ""));
if (newer !== -1) expect(pinnedIndex).toBeLessThan(newer);
expect(pinnedIndex).toBe(0);
});

it("points each page at the other", () => {
expect(render("en", "blog")).toContain('href="/en/notes"');
expect(render("en", "notes")).toContain('href="/en/blog"');
expect(render("nl", "blog")).toContain('href="/notes"');
expect(render("nl", "notes")).toContain('href="/blog"');
});

it("keeps post urls where they were, so links that are already out there still work", () => {
expect(postPath("check-pay-prove")).toBe("/notes/check-pay-prove");
expect(postPath("official-sources")).toBe("/notes/official-sources");
expect(postPath("we-measured-our-scan")).toBe("/notes/we-measured-our-scan");
});

it("uses no long dashes in either index", () => {
for (const page of ["blog", "notes"] as const) {
for (const locale of LOCALES) expect(render(locale, page)).not.toMatch(/[–—]/);
}
});
});
Loading
Loading