diff --git a/package.json b/package.json index 88e56ce..7df6749 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@neynar/create-farcaster-mini-app", - "version": "1.2.22", + "version": "1.2.23", "type": "module", "private": false, "access": "public", diff --git a/src/app/api/opengraph-image/route.tsx b/src/app/api/opengraph-image/route.tsx index 37aa1e8..87d59a6 100644 --- a/src/app/api/opengraph-image/route.tsx +++ b/src/app/api/opengraph-image/route.tsx @@ -8,7 +8,7 @@ export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url); const fid = searchParams.get('fid'); - const user = await getNeynarUser(Number(fid)); + const user = fid ? await getNeynarUser(Number(fid)) : null; return new ImageResponse( ( diff --git a/src/app/opengraph-image.tsx b/src/app/opengraph-image.tsx deleted file mode 100644 index 86b07cc..0000000 --- a/src/app/opengraph-image.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { ImageResponse } from "next/og"; -import { APP_NAME } from "~/lib/constants"; - -export const alt = APP_NAME; -export const size = { - width: 600, - height: 400, -}; - -export const contentType = "image/png"; - -// dynamically generated OG image for frame preview -export default async function Image() { - return new ImageResponse( - ( -
-

{alt}

-
- ), - { - ...size, - } - ); -} diff --git a/src/app/page.tsx b/src/app/page.tsx index 5214b54..56d085b 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,22 +1,7 @@ import { Metadata } from "next"; import App from "./app"; -import { APP_URL, APP_NAME, APP_DESCRIPTION, APP_OG_IMAGE_URL, APP_ICON_URL, APP_SPLASH_URL, APP_SPLASH_BACKGROUND_COLOR, APP_BUTTON_TEXT } from "~/lib/constants"; - -const framePreviewMetadata = { - version: "next", - imageUrl: APP_OG_IMAGE_URL, - button: { - title: APP_BUTTON_TEXT, - action: { - type: "launch_frame", - name: APP_NAME, - url: APP_URL, - splashImageUrl: APP_SPLASH_URL, - iconUrl: APP_ICON_URL, - splashBackgroundColor: APP_SPLASH_BACKGROUND_COLOR, - }, - }, -}; +import { APP_NAME, APP_DESCRIPTION, APP_OG_IMAGE_URL } from "~/lib/constants"; +import { getFrameEmbedMetadata } from "~/lib/utils"; export const revalidate = 300; @@ -26,9 +11,10 @@ export async function generateMetadata(): Promise { openGraph: { title: APP_NAME, description: APP_DESCRIPTION, + images: [APP_OG_IMAGE_URL], }, other: { - "fc:frame": JSON.stringify(framePreviewMetadata), + "fc:frame": JSON.stringify(getFrameEmbedMetadata()), }, }; } diff --git a/src/app/share/[fid]/page.tsx b/src/app/share/[fid]/page.tsx new file mode 100644 index 0000000..bb2e823 --- /dev/null +++ b/src/app/share/[fid]/page.tsx @@ -0,0 +1,30 @@ +import { Metadata } from "next"; +import { redirect } from "next/navigation"; +import { APP_URL, APP_NAME, APP_DESCRIPTION } from "~/lib/constants"; +import { getFrameEmbedMetadata } from "~/lib/utils"; +export const revalidate = 300; + +// This is an example of how to generate a dynamically generated share page based on fid: +// Sharing this route e.g. exmaple.com/share/123 will generate a share page for fid 123, +// with the image dynamically generated by the opengraph-image API route. +export async function generateMetadata({ params }: { params: { fid: string } }): Promise { + const fid = params.fid; + const imageUrl = `${APP_URL}/api/opengraph-image?fid=${fid}`; + + return { + title: `${APP_NAME} - Share`, + openGraph: { + title: APP_NAME, + description: APP_DESCRIPTION, + images: [imageUrl], + }, + other: { + "fc:frame": JSON.stringify(getFrameEmbedMetadata(imageUrl)), + }, + }; +} + +export default function SharePage() { + // redirect to home page + redirect("/"); +} \ No newline at end of file diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 09fbd6a..d4d0168 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -6,3 +6,6 @@ export const APP_OG_IMAGE_URL = `${APP_URL}/api/opengraph-image`; export const APP_SPLASH_URL = `${APP_URL}/splash.png`; export const APP_SPLASH_BACKGROUND_COLOR = "#f7f7f7"; export const APP_BUTTON_TEXT = process.env.NEXT_PUBLIC_FRAME_BUTTON_TEXT; +export const APP_WEBHOOK_URL = process.env.NEYNAR_API_KEY && process.env.NEYNAR_CLIENT_ID + ? `https://api.neynar.com/f/app/${process.env.NEYNAR_CLIENT_ID}/event` + : `${APP_URL}/api/webhook`; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 392cd65..3bb6c65 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,7 +1,7 @@ import { type ClassValue, clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; import { mnemonicToAccount } from 'viem/accounts'; -import { APP_BUTTON_TEXT, APP_ICON_URL, APP_NAME, APP_OG_IMAGE_URL, APP_SPLASH_BACKGROUND_COLOR, APP_URL } from './constants'; +import { APP_BUTTON_TEXT, APP_ICON_URL, APP_NAME, APP_OG_IMAGE_URL, APP_SPLASH_BACKGROUND_COLOR, APP_URL, APP_WEBHOOK_URL } from './constants'; import { APP_SPLASH_URL } from './constants'; interface FrameMetadata { @@ -38,6 +38,24 @@ export function getSecretEnvVars() { return { seedPhrase, fid }; } +export function getFrameEmbedMetadata(ogImageUrl?: string) { + return { + version: "next", + imageUrl: ogImageUrl ?? APP_OG_IMAGE_URL, + button: { + title: APP_BUTTON_TEXT, + action: { + type: "launch_frame", + name: APP_NAME, + url: APP_URL, + splashImageUrl: APP_SPLASH_URL, + iconUrl: APP_ICON_URL, + splashBackgroundColor: APP_SPLASH_BACKGROUND_COLOR, + }, + }, + }; +} + export async function getFarcasterMetadata(): Promise { // First check for FRAME_METADATA in .env and use that if it exists if (process.env.FRAME_METADATA) { @@ -93,13 +111,6 @@ export async function getFarcasterMetadata(): Promise { }; } - // Determine webhook URL based on whether Neynar is enabled - const neynarApiKey = process.env.NEYNAR_API_KEY; - const neynarClientId = process.env.NEYNAR_CLIENT_ID; - const webhookUrl = neynarApiKey && neynarClientId - ? `https://api.neynar.com/f/app/${neynarClientId}/event` - : `${APP_URL}/api/webhook`; - return { accountAssociation, frame: { @@ -111,7 +122,7 @@ export async function getFarcasterMetadata(): Promise { buttonTitle: APP_BUTTON_TEXT ?? "Launch Frame", splashImageUrl: APP_SPLASH_URL, splashBackgroundColor: APP_SPLASH_BACKGROUND_COLOR, - webhookUrl, + webhookUrl: APP_WEBHOOK_URL, }, }; }