diff --git a/src/app/[user]/opengraph-image.tsx b/src/app/[user]/opengraph-image.tsx
new file mode 100644
index 0000000..2e2d2fa
--- /dev/null
+++ b/src/app/[user]/opengraph-image.tsx
@@ -0,0 +1,124 @@
+import { ImageResponse } from "next/og";
+
+export const runtime = "edge";
+export const alt = "TwelveCash user profile";
+export const size = {
+ width: 1200,
+ height: 630,
+};
+export const contentType = "image/png";
+
+const DEFAULT_DOMAIN = "twelve.cash";
+
+function parseUserParam(userParam: string) {
+ const decoded = decodeURIComponent(userParam);
+ const [user, domain = DEFAULT_DOMAIN] = decoded.split("@");
+ return { user, domain };
+}
+
+export default async function Image({
+ params,
+}: {
+ params: Promise<{ user: string }>;
+}) {
+ const { user: userParam } = await params;
+ const { user, domain } = parseUserParam(userParam);
+ const displayName = `${user}@${domain}`;
+
+ return new ImageResponse(
+ (
+
+
+
TwelveCash
+
Simple Bitcoin Usernames
+
+
+
+
+ Pay me in bitcoin
+
+
+ {displayName}
+
+
+
+
+
+
BOLT 12 offers • Silent payments • more
+
twelve.cash
+
+
+ ₿
+
+
+
+ ),
+ {
+ ...size,
+ },
+ );
+}
diff --git a/src/app/[user]/page.tsx b/src/app/[user]/page.tsx
index eb5d47d..564487f 100644
--- a/src/app/[user]/page.tsx
+++ b/src/app/[user]/page.tsx
@@ -1,15 +1,52 @@
-import Bip353Box from "../components/Bip353Box";
-import CopyUserLinkButton from "../features/CopyUserLinkButton";
-import CopyBip353Button from "../features/CopyBip353Button";
-import PaymentDetail from "../components/PaymentDetail";
+import type { Metadata } from "next";
import Button from "../components/Button";
import { ArrowLeftIcon } from "@bitcoin-design/bitcoin-icons-react/filled";
import UserDetails from "../features/UserDetails";
+const DEFAULT_DOMAIN = "twelve.cash";
+
+function parseUserParam(userParam: string) {
+ const decoded = decodeURIComponent(userParam);
+ const [user, domain = DEFAULT_DOMAIN] = decoded.split("@");
+ return { user, domain };
+}
+
+export async function generateMetadata({
+ params,
+}: {
+ params: Promise<{ user: string }>;
+}): Promise {
+ const { user: userParam } = await params;
+ const { user, domain } = parseUserParam(userParam);
+ const displayName = `${user}@${domain}`;
+ const description = `Send bitcoin to ${displayName} with TwelveCash.`;
+
+ return {
+ title: `${displayName} | TwelveCash`,
+ description,
+ openGraph: {
+ title: `${displayName} | TwelveCash`,
+ description,
+ images: [{ url: `/${encodeURIComponent(displayName)}/opengraph-image` }],
+ },
+ twitter: {
+ card: "summary_large_image",
+ title: `${displayName} | TwelveCash`,
+ description,
+ images: [`/${encodeURIComponent(displayName)}/opengraph-image`],
+ },
+ alternates: {
+ canonical: `/${encodeURIComponent(displayName)}`,
+ },
+ other: {
+ "og:image:alt": `${displayName} on TwelveCash`,
+ },
+ };
+}
+
export default async function User({ params }: { params: Promise<{ user: string }> }) {
const { user: userParam } = await params;
- const decoded = decodeURIComponent(userParam);
- const [user, domain] = decoded.split("@");
+ const { user, domain } = parseUserParam(userParam);
return (
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 959fbe7..4db34de 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -9,26 +9,29 @@ import Header from "./components/Header";
const urbanist = Urbanist({ subsets: ["latin"] });
-const title = "TwelveCash | Simple Bitcoin Usernames"
-const description = "Get your own TwelveCash address. Supports BOLT 12 offers, Silent payments, and more!"
-const poster = "https://twelve.cash/twelve-cash-poster.png"
+const title = "TwelveCash | Simple Bitcoin Usernames";
+const description =
+ "Get your own TwelveCash address. Supports BOLT 12 offers, Silent payments, and more!";
+const poster = "/twelve-cash-poster.png";
export const metadata: Metadata = {
- title: title,
- description: description,
- icons: [{ url: '/favicon.ico', rel: 'icon' }],
+ metadataBase: new URL(process.env.NEXT_PUBLIC_URL || "https://twelve.cash"),
+ title,
+ description,
+ icons: [{ url: "/favicon.ico", rel: "icon" }],
openGraph: {
- title: title,
- description: description,
- images: [{ url: poster }],
+ title,
+ description,
+ images: [{ url: poster }],
},
twitter: {
- site: "TwelveCash",
- description: description,
- title: title,
- images: poster
- }
-}
+ card: "summary_large_image",
+ site: "@twelvecash",
+ title,
+ description,
+ images: [poster],
+ },
+};
export default function RootLayout({
children,