feat: add policy pages
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import Link from "next/link";
|
||||
|
||||
export default function ShareNotFound() {
|
||||
return (
|
||||
<main className="mx-auto flex min-h-[60vh] max-w-xl flex-col items-center justify-center px-4 py-16 text-center">
|
||||
<h1 className="text-2xl font-semibold">Link not found</h1>
|
||||
<p className="text-muted-foreground mt-3 text-sm">
|
||||
This short link may have expired or never existed.
|
||||
</p>
|
||||
<Link
|
||||
href="/"
|
||||
className="text-primary mt-6 text-sm underline underline-offset-4"
|
||||
>
|
||||
Return to Prabhupāda Says →
|
||||
</Link>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import type { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
import { resolveShortLink } from "@/lib/share";
|
||||
import { buildShareOgMeta } from "@/lib/share-og";
|
||||
import { ogDefault } from "@/lib/og";
|
||||
import { RedirectClient } from "./redirect-client";
|
||||
|
||||
type PageProps = { params: Promise<{ code: string }> };
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: PageProps): Promise<Metadata> {
|
||||
const { code } = await params;
|
||||
const fullPath = await resolveShortLink(code);
|
||||
if (!fullPath) {
|
||||
return {
|
||||
title: "Link expired — Prabhupāda Says",
|
||||
robots: { index: false, follow: false },
|
||||
...ogDefault(),
|
||||
};
|
||||
}
|
||||
return buildShareOgMeta(fullPath);
|
||||
}
|
||||
|
||||
export default async function SharePage({ params }: PageProps) {
|
||||
const { code } = await params;
|
||||
const fullPath = await resolveShortLink(code);
|
||||
if (!fullPath) notFound();
|
||||
|
||||
return (
|
||||
<main className="mx-auto flex min-h-[60vh] max-w-xl flex-col items-center justify-center px-4 py-16 text-center">
|
||||
<p className="text-muted-foreground text-sm">
|
||||
Redirecting to your highlight…
|
||||
</p>
|
||||
<noscript>
|
||||
<p className="mt-4 text-sm">
|
||||
JavaScript is disabled.{" "}
|
||||
<a className="underline" href={fullPath}>
|
||||
Continue to the page
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</noscript>
|
||||
<RedirectClient to={fullPath} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
// Client-side redirect: we deliberately don't use `redirect()` from
|
||||
// next/navigation on the server, because that returns a 307 before the OG
|
||||
// `<head>` is ever served. With `generateMetadata` + a 200 body, social-link
|
||||
// scrapers get the full OG card while real users still get redirected.
|
||||
export function RedirectClient({ to }: { to: string }) {
|
||||
const router = useRouter();
|
||||
useEffect(() => {
|
||||
router.replace(to);
|
||||
}, [router, to]);
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user