feat(frontend): rich OG metadata + dynamic page titles across all routes
Some checks failed
lint / lint (push) Has been cancelled
test / unit (push) Has been cancelled
test / integration (push) Has been cancelled
lint / lint (pull_request) Has been cancelled
test / unit (pull_request) Has been cancelled
test / integration (pull_request) Has been cancelled

This commit is contained in:
2026-05-15 01:38:59 +02:00
parent 71a0f55c93
commit a123c0b8cc
11 changed files with 206 additions and 2 deletions

View File

@@ -1,3 +1,4 @@
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { cookies } from "next/headers";
import { getMe, lookupRemoteActor, getRemoteActorPosts, Me } from "@/lib/api";
@@ -7,6 +8,43 @@ interface RemoteActorPageProps {
searchParams: Promise<{ handle?: string }>;
}
function stripHtml(html: string) {
return html.replace(/<[^>]*>/g, "").trim();
}
export async function generateMetadata({
searchParams,
}: RemoteActorPageProps): Promise<Metadata> {
const { handle } = await searchParams;
if (!handle) return { title: "Profile" };
const token = (await cookies()).get("auth_token")?.value ?? null;
const actor = await lookupRemoteActor(handle, token).catch(() => null);
if (!actor) return { title: handle };
const name = actor.displayName || actor.handle;
const description = actor.bio
? stripHtml(actor.bio).slice(0, 160)
: `${name} on the Fediverse. Follow from Thoughts.`;
return {
title: `${name} (${actor.handle})`,
description,
openGraph: {
type: "profile",
title: `${name} (${actor.handle})`,
description,
images: actor.avatarUrl ? [{ url: actor.avatarUrl }] : [],
},
twitter: {
card: "summary",
title: `${name} · Thoughts`,
description,
images: actor.avatarUrl ? [actor.avatarUrl] : [],
},
};
}
export default async function RemoteActorPage({
searchParams,
}: RemoteActorPageProps) {