Files
spsays-training/app/(app)/u/[handle]/page.tsx
T

60 lines
1.9 KiB
TypeScript

/**
* /u/[handle] — public profile, Posts tab (default).
*
* Server shell that exists for three reasons:
*
* 1. Export `generateMetadata` so direct shares of this URL
* (https://prabhupadasays.org/u/<handle>) get a rich OG card
* pointing at /api/og/profile/<handle>. The layout and the body
* below are client components; only this file is server.
* 2. Emit server-rendered ProfilePage/Person JSON-LD so crawlers + LLMs
* can resolve the profile (the layout is a client component and
* fetches the profile after hydration, too late for crawlers).
* 3. Mount the client tree (<PostsGrid />), which reads the
* ProfileContext provided by the parent layout.
*/
import type { Metadata } from "next";
import { bff } from "@/lib/bff";
import { JsonLd } from "@/components/seo/json-ld";
import { profileJsonLd } from "@/lib/jsonld";
import { PostsGrid } from "./posts-grid";
import { metadataForHandle } from "./profile-og";
type Props = { params: Promise<{ handle: string }> };
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { handle } = await params;
return metadataForHandle(handle);
}
export default async function PublicProfilePage({ params }: Props) {
const { handle } = await params;
let person: object | null = null;
try {
const profile = await bff<{
handle: string;
name: string;
avatar_url?: string | null;
}>(`/u/${encodeURIComponent(handle)}`, { revalidate: 60 });
if (profile) {
person = profileJsonLd({
path: `/u/${profile.handle}`,
name: profile.name,
handle: profile.handle,
image: profile.avatar_url ?? null,
});
}
} catch {
// Profile unavailable — skip JSON-LD rather than fail the page.
}
return (
<>
{person ? <JsonLd data={person} /> : null}
<PostsGrid params={params} />
</>
);
}