mirror of
https://github.com/neynarxyz/create-farcaster-mini-app.git
synced 2025-12-08 02:12:34 -05:00
refactor: update frame to mini app and fix dark mode
This commit is contained in:
@@ -2,8 +2,8 @@ import { notificationDetailsSchema } from "@farcaster/frame-sdk";
|
||||
import { NextRequest } from "next/server";
|
||||
import { z } from "zod";
|
||||
import { setUserNotificationDetails } from "~/lib/kv";
|
||||
import { sendFrameNotification } from "~/lib/notifs";
|
||||
import { sendNeynarFrameNotification } from "~/lib/neynar";
|
||||
import { sendMiniAppNotification } from "~/lib/notifs";
|
||||
import { sendNeynarMiniAppNotification } from "~/lib/neynar";
|
||||
|
||||
const requestSchema = z.object({
|
||||
fid: z.number(),
|
||||
@@ -34,7 +34,7 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
// Use appropriate notification function based on Neynar status
|
||||
const sendNotification = neynarEnabled ? sendNeynarFrameNotification : sendFrameNotification;
|
||||
const sendNotification = neynarEnabled ? sendNeynarMiniAppNotification : sendMiniAppNotification;
|
||||
const sendResult = await sendNotification({
|
||||
fid: Number(requestBody.data.fid),
|
||||
title: "Test notification",
|
||||
|
||||
@@ -4,11 +4,12 @@ import {
|
||||
verifyAppKeyWithNeynar,
|
||||
} from "@farcaster/frame-node";
|
||||
import { NextRequest } from "next/server";
|
||||
import { APP_NAME } from "~/lib/constants";
|
||||
import {
|
||||
deleteUserNotificationDetails,
|
||||
setUserNotificationDetails,
|
||||
} from "~/lib/kv";
|
||||
import { sendFrameNotification } from "~/lib/notifs";
|
||||
import { sendMiniAppNotification } from "~/lib/notifs";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
// If Neynar is enabled, we don't need to handle webhooks here
|
||||
@@ -58,10 +59,10 @@ export async function POST(request: NextRequest) {
|
||||
case "frame_added":
|
||||
if (event.notificationDetails) {
|
||||
await setUserNotificationDetails(fid, event.notificationDetails);
|
||||
await sendFrameNotification({
|
||||
await sendMiniAppNotification({
|
||||
fid,
|
||||
title: "Welcome to Frames v2",
|
||||
body: "Frame is now added to your client",
|
||||
title: `Welcome to ${APP_NAME}`,
|
||||
body: "Mini app is now added to your client",
|
||||
});
|
||||
} else {
|
||||
await deleteUserNotificationDetails(fid);
|
||||
@@ -74,9 +75,9 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
case "notifications_enabled":
|
||||
await setUserNotificationDetails(fid, event.notificationDetails);
|
||||
await sendFrameNotification({
|
||||
await sendMiniAppNotification({
|
||||
fid,
|
||||
title: "Ding ding ding",
|
||||
title: `Welcome to ${APP_NAME}`,
|
||||
body: "Notifications are now enabled",
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Metadata } from "next";
|
||||
import App from "./app";
|
||||
import { APP_NAME, APP_DESCRIPTION, APP_OG_IMAGE_URL } from "~/lib/constants";
|
||||
import { getFrameEmbedMetadata } from "~/lib/utils";
|
||||
import { getMiniAppEmbedMetadata } from "~/lib/utils";
|
||||
|
||||
export const revalidate = 300;
|
||||
|
||||
@@ -14,7 +14,7 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||
images: [APP_OG_IMAGE_URL],
|
||||
},
|
||||
other: {
|
||||
"fc:frame": JSON.stringify(getFrameEmbedMetadata()),
|
||||
"fc:frame": JSON.stringify(getMiniAppEmbedMetadata()),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Metadata } from "next";
|
||||
import { redirect } from "next/navigation";
|
||||
import { APP_URL, APP_NAME, APP_DESCRIPTION } from "~/lib/constants";
|
||||
import { getFrameEmbedMetadata } from "~/lib/utils";
|
||||
import { getMiniAppEmbedMetadata } from "~/lib/utils";
|
||||
export const revalidate = 300;
|
||||
|
||||
// This is an example of how to generate a dynamically generated share page based on fid:
|
||||
@@ -23,7 +23,7 @@ export async function generateMetadata({
|
||||
images: [imageUrl],
|
||||
},
|
||||
other: {
|
||||
"fc:frame": JSON.stringify(getFrameEmbedMetadata(imageUrl)),
|
||||
"fc:frame": JSON.stringify(getMiniAppEmbedMetadata(imageUrl)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ interface NeynarUser {
|
||||
}
|
||||
|
||||
export default function Demo(
|
||||
{ title }: { title?: string } = { title: "Frames v2 Demo" }
|
||||
{ title }: { title?: string } = { title: "Neynar Starter Kit" }
|
||||
) {
|
||||
const {
|
||||
isSDKLoaded,
|
||||
@@ -539,7 +539,7 @@ function SignEvmMessage() {
|
||||
});
|
||||
}
|
||||
|
||||
signMessage({ message: "Hello from Frames v2!" });
|
||||
signMessage({ message: `Hello from ${APP_NAME}!` });
|
||||
}, [connectAsync, isConnected, signMessage]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
export const APP_URL = process.env.NEXT_PUBLIC_URL!;
|
||||
export const APP_NAME = process.env.NEXT_PUBLIC_FRAME_NAME;
|
||||
export const APP_DESCRIPTION = process.env.NEXT_PUBLIC_FRAME_DESCRIPTION;
|
||||
export const APP_PRIMARY_CATEGORY = process.env.NEXT_PUBLIC_FRAME_PRIMARY_CATEGORY;
|
||||
export const APP_TAGS = process.env.NEXT_PUBLIC_FRAME_TAGS?.split(',');
|
||||
export const APP_NAME = process.env.NEXT_PUBLIC_MINI_APP_NAME;
|
||||
export const APP_DESCRIPTION = process.env.NEXT_PUBLIC_MINI_APP_DESCRIPTION;
|
||||
export const APP_PRIMARY_CATEGORY = process.env.NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY;
|
||||
export const APP_TAGS = process.env.NEXT_PUBLIC_MINI_APP_TAGS?.split(',');
|
||||
export const APP_ICON_URL = `${APP_URL}/icon.png`;
|
||||
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_BUTTON_TEXT = process.env.NEXT_PUBLIC_MINI_APP_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`;
|
||||
|
||||
@@ -31,7 +31,7 @@ export async function getNeynarUser(fid: number): Promise<User | null> {
|
||||
}
|
||||
}
|
||||
|
||||
type SendFrameNotificationResult =
|
||||
type SendMiniAppNotificationResult =
|
||||
| {
|
||||
state: "error";
|
||||
error: unknown;
|
||||
@@ -40,7 +40,7 @@ type SendFrameNotificationResult =
|
||||
| { state: "rate_limit" }
|
||||
| { state: "success" };
|
||||
|
||||
export async function sendNeynarFrameNotification({
|
||||
export async function sendNeynarMiniAppNotification({
|
||||
fid,
|
||||
title,
|
||||
body,
|
||||
@@ -48,7 +48,7 @@ export async function sendNeynarFrameNotification({
|
||||
fid: number;
|
||||
title: string;
|
||||
body: string;
|
||||
}): Promise<SendFrameNotificationResult> {
|
||||
}): Promise<SendMiniAppNotificationResult> {
|
||||
try {
|
||||
const client = getNeynarClient();
|
||||
const targetFids = [fid];
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
import { getUserNotificationDetails } from "~/lib/kv";
|
||||
import { APP_URL } from "./constants";
|
||||
|
||||
type SendFrameNotificationResult =
|
||||
type SendMiniAppNotificationResult =
|
||||
| {
|
||||
state: "error";
|
||||
error: unknown;
|
||||
@@ -14,7 +14,7 @@ type SendFrameNotificationResult =
|
||||
| { state: "rate_limit" }
|
||||
| { state: "success" };
|
||||
|
||||
export async function sendFrameNotification({
|
||||
export async function sendMiniAppNotification({
|
||||
fid,
|
||||
title,
|
||||
body,
|
||||
@@ -22,7 +22,7 @@ export async function sendFrameNotification({
|
||||
fid: number;
|
||||
title: string;
|
||||
body: string;
|
||||
}): Promise<SendFrameNotificationResult> {
|
||||
}): Promise<SendMiniAppNotificationResult> {
|
||||
const notificationDetails = await getUserNotificationDetails(fid);
|
||||
if (!notificationDetails) {
|
||||
return { state: "no_token" };
|
||||
|
||||
@@ -4,7 +4,7 @@ import { mnemonicToAccount } from 'viem/accounts';
|
||||
import { APP_BUTTON_TEXT, APP_DESCRIPTION, APP_ICON_URL, APP_NAME, APP_OG_IMAGE_URL, APP_PRIMARY_CATEGORY, APP_SPLASH_BACKGROUND_COLOR, APP_TAGS, APP_URL, APP_WEBHOOK_URL } from './constants';
|
||||
import { APP_SPLASH_URL } from './constants';
|
||||
|
||||
interface FrameMetadata {
|
||||
interface MiniAppMetadata {
|
||||
version: string;
|
||||
name: string;
|
||||
iconUrl: string;
|
||||
@@ -19,13 +19,13 @@ interface FrameMetadata {
|
||||
tags?: string[];
|
||||
};
|
||||
|
||||
interface FrameManifest {
|
||||
interface MiniAppManifest {
|
||||
accountAssociation?: {
|
||||
header: string;
|
||||
payload: string;
|
||||
signature: string;
|
||||
};
|
||||
frame: FrameMetadata;
|
||||
frame: MiniAppMetadata;
|
||||
}
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
@@ -43,7 +43,7 @@ export function getSecretEnvVars() {
|
||||
return { seedPhrase, fid };
|
||||
}
|
||||
|
||||
export function getFrameEmbedMetadata(ogImageUrl?: string) {
|
||||
export function getMiniAppEmbedMetadata(ogImageUrl?: string) {
|
||||
return {
|
||||
version: "next",
|
||||
imageUrl: ogImageUrl ?? APP_OG_IMAGE_URL,
|
||||
@@ -64,15 +64,15 @@ export function getFrameEmbedMetadata(ogImageUrl?: string) {
|
||||
};
|
||||
}
|
||||
|
||||
export async function getFarcasterMetadata(): Promise<FrameManifest> {
|
||||
// First check for FRAME_METADATA in .env and use that if it exists
|
||||
if (process.env.FRAME_METADATA) {
|
||||
export async function getFarcasterMetadata(): Promise<MiniAppManifest> {
|
||||
// First check for MINI_APP_METADATA in .env and use that if it exists
|
||||
if (process.env.MINI_APP_METADATA) {
|
||||
try {
|
||||
const metadata = JSON.parse(process.env.FRAME_METADATA);
|
||||
console.log('Using pre-signed frame metadata from environment');
|
||||
const metadata = JSON.parse(process.env.MINI_APP_METADATA);
|
||||
console.log('Using pre-signed mini app metadata from environment');
|
||||
return metadata;
|
||||
} catch (error) {
|
||||
console.warn('Failed to parse FRAME_METADATA from environment:', error);
|
||||
console.warn('Failed to parse MINI_APP_METADATA from environment:', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,11 +123,11 @@ export async function getFarcasterMetadata(): Promise<FrameManifest> {
|
||||
accountAssociation,
|
||||
frame: {
|
||||
version: "1",
|
||||
name: APP_NAME ?? "Frames v2 Demo",
|
||||
name: APP_NAME ?? "Neynar Starter Kit",
|
||||
iconUrl: APP_ICON_URL,
|
||||
homeUrl: APP_URL,
|
||||
imageUrl: APP_OG_IMAGE_URL,
|
||||
buttonTitle: APP_BUTTON_TEXT ?? "Launch Frame",
|
||||
buttonTitle: APP_BUTTON_TEXT ?? "Launch Mini App",
|
||||
splashImageUrl: APP_SPLASH_URL,
|
||||
splashBackgroundColor: APP_SPLASH_BACKGROUND_COLOR,
|
||||
webhookUrl: APP_WEBHOOK_URL,
|
||||
|
||||
Reference in New Issue
Block a user