From 4831308983b33231406bdaaa87ccf372d1c7de4b Mon Sep 17 00:00:00 2001 From: veganbeef Date: Mon, 16 Jun 2025 12:59:50 -0700 Subject: [PATCH] refactor: update frame to mini app and fix dark mode --- README.md | 2 +- bin/init.js | 14 +++---- package.json | 2 +- scripts/build.js | 30 +++++++-------- scripts/deploy.js | 52 +++++++++++++------------- scripts/dev.js | 10 ++--- src/app/api/send-notification/route.ts | 6 +-- src/app/api/webhook/route.ts | 13 ++++--- src/app/page.tsx | 4 +- src/app/share/[fid]/page.tsx | 4 +- src/components/Demo.tsx | 4 +- src/lib/constants.ts | 10 ++--- src/lib/neynar.ts | 6 +-- src/lib/notifs.ts | 6 +-- src/lib/utils.ts | 24 ++++++------ tailwind.config.ts | 2 +- 16 files changed, 95 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index fb244a3..191dc12 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Farcaster Mini Apps (formerly Frames) Quickstart by Neynar 🪐 +# Farcaster Mini Apps (formerly Frames v2) Quickstart by Neynar 🪐 A Farcaster Mini Apps quickstart npx script. diff --git a/bin/init.js b/bin/init.js index 34d862e..1206eda 100644 --- a/bin/init.js +++ b/bin/init.js @@ -166,14 +166,14 @@ export async function init() { break; } - const defaultFrameName = (neynarAppName && !neynarAppName.toLowerCase().includes('demo')) ? neynarAppName : undefined; + const defaultMiniAppName = (neynarAppName && !neynarAppName.toLowerCase().includes('demo')) ? neynarAppName : undefined; const answers = await inquirer.prompt([ { type: 'input', name: 'projectName', message: 'What is the name of your mini app?', - default: defaultFrameName, + default: defaultMiniAppName, validate: (input) => { if (input.trim() === '') { return 'Project name cannot be empty'; @@ -400,11 +400,11 @@ export async function init() { fs.writeFileSync(envPath, envExampleContent); // Append all remaining environment variables - fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_NAME="${answers.projectName}"`); - fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_DESCRIPTION="${answers.description}"`); - fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_PRIMARY_CATEGORY="${answers.primaryCategory}"`); - fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_TAGS="${answers.tags.join(',')}"`); - fs.appendFileSync(envPath, `\nNEXT_PUBLIC_FRAME_BUTTON_TEXT="${answers.buttonText}"`); + fs.appendFileSync(envPath, `\nNEXT_PUBLIC_MINI_APP_NAME="${answers.projectName}"`); + fs.appendFileSync(envPath, `\nNEXT_PUBLIC_MINI_APP_DESCRIPTION="${answers.description}"`); + fs.appendFileSync(envPath, `\nNEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY="${answers.primaryCategory}"`); + fs.appendFileSync(envPath, `\nNEXT_PUBLIC_MINI_APP_TAGS="${answers.tags.join(',')}"`); + fs.appendFileSync(envPath, `\nNEXT_PUBLIC_MINI_APP_BUTTON_TEXT="${answers.buttonText}"`); fs.appendFileSync(envPath, `\nNEXT_PUBLIC_ANALYTICS_ENABLED="${answers.enableAnalytics}"`); fs.appendFileSync(envPath, `\nNEXT_PUBLIC_USE_WALLET="${answers.useWallet}"`); diff --git a/package.json b/package.json index 146f279..7a14132 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@neynar/create-farcaster-mini-app", - "version": "1.3.2", + "version": "1.4.0", "type": "module", "private": false, "access": "public", diff --git a/scripts/build.js b/scripts/build.js index aece3cc..b680399 100755 --- a/scripts/build.js +++ b/scripts/build.js @@ -161,7 +161,7 @@ async function generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase }); const encodedSignature = Buffer.from(signature, 'utf-8').toString('base64url'); - const tags = process.env.NEXT_PUBLIC_FRAME_TAGS?.split(','); + const tags = process.env.NEXT_PUBLIC_MINI_APP_TAGS?.split(','); return { accountAssociation: { @@ -171,16 +171,16 @@ async function generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase }, frame: { version: "1", - name: process.env.NEXT_PUBLIC_FRAME_NAME, + name: process.env.NEXT_PUBLIC_MINI_APP_NAME, iconUrl: `https://${domain}/icon.png`, homeUrl: `https://${domain}`, imageUrl: `https://${domain}/api/opengraph-image`, - buttonTitle: process.env.NEXT_PUBLIC_FRAME_BUTTON_TEXT, + buttonTitle: process.env.NEXT_PUBLIC_MINI_APP_BUTTON_TEXT, splashImageUrl: `https://${domain}/splash.png`, splashBackgroundColor: "#f7f7f7", webhookUrl, - description: process.env.NEXT_PUBLIC_FRAME_DESCRIPTION, - primaryCategory: process.env.NEXT_PUBLIC_FRAME_PRIMARY_CATEGORY, + description: process.env.NEXT_PUBLIC_MINI_APP_DESCRIPTION, + primaryCategory: process.env.NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY, tags, }, }; @@ -217,7 +217,7 @@ async function main() { type: 'input', name: 'frameName', message: 'Enter the name for your mini app (e.g., My Cool Mini App):', - default: process.env.NEXT_PUBLIC_FRAME_NAME, + default: process.env.NEXT_PUBLIC_MINI_APP_NAME, validate: (input) => { if (input.trim() === '') { return 'Mini app name cannot be empty'; @@ -233,7 +233,7 @@ async function main() { type: 'input', name: 'buttonText', message: 'Enter the text for your mini app button:', - default: process.env.NEXT_PUBLIC_FRAME_BUTTON_TEXT || 'Launch Mini App', + default: process.env.NEXT_PUBLIC_MINI_APP_BUTTON_TEXT || 'Launch Mini App', validate: (input) => { if (input.trim() === '') { return 'Button text cannot be empty'; @@ -355,12 +355,12 @@ async function main() { // Base URL `NEXT_PUBLIC_URL=https://${domain}`, - // Frame metadata - `NEXT_PUBLIC_FRAME_NAME="${frameName}"`, - `NEXT_PUBLIC_FRAME_DESCRIPTION="${process.env.NEXT_PUBLIC_FRAME_DESCRIPTION || ''}"`, - `NEXT_PUBLIC_FRAME_PRIMARY_CATEGORY="${process.env.NEXT_PUBLIC_FRAME_PRIMARY_CATEGORY || ''}"`, - `NEXT_PUBLIC_FRAME_TAGS="${process.env.NEXT_PUBLIC_FRAME_TAGS || ''}"`, - `NEXT_PUBLIC_FRAME_BUTTON_TEXT="${buttonText}"`, + // Mini app metadata + `NEXT_PUBLIC_MINI_APP_NAME="${frameName}"`, + `NEXT_PUBLIC_MINI_APP_DESCRIPTION="${process.env.NEXT_PUBLIC_MINI_APP_DESCRIPTION || ''}"`, + `NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY="${process.env.NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY || ''}"`, + `NEXT_PUBLIC_MINI_APP_TAGS="${process.env.NEXT_PUBLIC_MINI_APP_TAGS || ''}"`, + `NEXT_PUBLIC_MINI_APP_BUTTON_TEXT="${buttonText}"`, // Analytics `NEXT_PUBLIC_ANALYTICS_ENABLED="${process.env.NEXT_PUBLIC_ANALYTICS_ENABLED || 'false'}"`, @@ -379,8 +379,8 @@ async function main() { `NEXTAUTH_SECRET="${process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString('hex')}"`, `NEXTAUTH_URL="https://${domain}"`, - // Frame manifest with signature - `FRAME_METADATA=${JSON.stringify(metadata)}`, + // Mini app manifest with signature + `MINI_APP_METADATA=${JSON.stringify(metadata)}`, ]; // Filter out empty values and join with newlines diff --git a/scripts/deploy.js b/scripts/deploy.js index 4ebaab8..ddedbcf 100755 --- a/scripts/deploy.js +++ b/scripts/deploy.js @@ -72,7 +72,7 @@ async function generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase }); const encodedSignature = Buffer.from(signature, 'utf-8').toString('base64url'); - const tags = process.env.NEXT_PUBLIC_FRAME_TAGS?.split(','); + const tags = process.env.NEXT_PUBLIC_MINI_APP_TAGS?.split(','); return { accountAssociation: { @@ -82,16 +82,16 @@ async function generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase }, frame: { version: "1", - name: process.env.NEXT_PUBLIC_FRAME_NAME, + name: process.env.NEXT_PUBLIC_MINI_APP_NAME, iconUrl: `https://${trimmedDomain}/icon.png`, homeUrl: `https://${trimmedDomain}`, imageUrl: `https://${trimmedDomain}/api/opengraph-image`, - buttonTitle: process.env.NEXT_PUBLIC_FRAME_BUTTON_TEXT, + buttonTitle: process.env.NEXT_PUBLIC_MINI_APP_BUTTON_TEXT, splashImageUrl: `https://${trimmedDomain}/splash.png`, splashBackgroundColor: "#f7f7f7", webhookUrl: webhookUrl?.trim(), - description: process.env.NEXT_PUBLIC_FRAME_DESCRIPTION, - primaryCategory: process.env.NEXT_PUBLIC_FRAME_PRIMARY_CATEGORY, + description: process.env.NEXT_PUBLIC_MINI_APP_DESCRIPTION, + primaryCategory: process.env.NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY, tags, }, }; @@ -116,11 +116,11 @@ async function loadEnvLocal() { // Define allowed variables to load from .env.local const allowedVars = [ 'SEED_PHRASE', - 'NEXT_PUBLIC_FRAME_NAME', - 'NEXT_PUBLIC_FRAME_DESCRIPTION', - 'NEXT_PUBLIC_FRAME_PRIMARY_CATEGORY', - 'NEXT_PUBLIC_FRAME_TAGS', - 'NEXT_PUBLIC_FRAME_BUTTON_TEXT', + 'NEXT_PUBLIC_MINI_APP_NAME', + 'NEXT_PUBLIC_MINI_APP_DESCRIPTION', + 'NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY', + 'NEXT_PUBLIC_MINI_APP_TAGS', + 'NEXT_PUBLIC_MINI_APP_BUTTON_TEXT', 'NEXT_PUBLIC_ANALYTICS_ENABLED', 'NEYNAR_API_KEY', 'NEYNAR_CLIENT_ID' @@ -161,15 +161,15 @@ async function checkRequiredEnvVars() { const requiredVars = [ { - name: 'NEXT_PUBLIC_FRAME_NAME', + name: 'NEXT_PUBLIC_MINI_APP_NAME', message: 'Enter the name for your frame (e.g., My Cool Mini App):', - default: process.env.NEXT_PUBLIC_FRAME_NAME, + default: process.env.NEXT_PUBLIC_MINI_APP_NAME, validate: input => input.trim() !== '' || 'Mini app name cannot be empty' }, { - name: 'NEXT_PUBLIC_FRAME_BUTTON_TEXT', + name: 'NEXT_PUBLIC_MINI_APP_BUTTON_TEXT', message: 'Enter the text for your frame button:', - default: process.env.NEXT_PUBLIC_FRAME_BUTTON_TEXT ?? 'Launch Mini App', + default: process.env.NEXT_PUBLIC_MINI_APP_BUTTON_TEXT ?? 'Launch Mini App', validate: input => input.trim() !== '' || 'Button text cannot be empty' } ]; @@ -340,7 +340,7 @@ async function setVercelEnvVar(key, value, projectRoot) { // Ignore errors from removal (var might not exist) } - // For complex objects like frameMetadata, use a temporary file approach + // For complex objects like miniAppMetadata, use a temporary file approach if (typeof value === 'object') { const tempFilePath = path.join(projectRoot, `${key}_temp.json`); // Write the value to a temporary file with proper JSON formatting @@ -432,11 +432,11 @@ async function deployToVercel(useGitHub = false) { } } - // Generate frame metadata if we have a seed phrase - let frameMetadata; + // Generate mini app metadata if we have a seed phrase + let miniAppMetadata; let fid; if (process.env.SEED_PHRASE) { - console.log('\nšŸ”Ø Generating frame metadata...'); + console.log('\nšŸ”Ø Generating mini app metadata...'); const accountAddress = await validateSeedPhrase(process.env.SEED_PHRASE); fid = await lookupFidByCustodyAddress(accountAddress, process.env.NEYNAR_API_KEY ?? 'FARCASTER_V2_FRAMES_DEMO'); @@ -445,8 +445,8 @@ async function deployToVercel(useGitHub = false) { ? `https://api.neynar.com/f/app/${process.env.NEYNAR_CLIENT_ID}/event` : `https://${domain}/api/webhook`; - frameMetadata = await generateFarcasterMetadata(domain, fid, accountAddress, process.env.SEED_PHRASE, webhookUrl); - console.log('āœ… Frame metadata generated and signed'); + miniAppMetadata = await generateFarcasterMetadata(domain, fid, accountAddress, process.env.SEED_PHRASE, webhookUrl); + console.log('āœ… Mini app metadata generated and signed'); } // Prepare environment variables @@ -462,8 +462,8 @@ async function deployToVercel(useGitHub = false) { ...(process.env.NEYNAR_API_KEY && { NEYNAR_API_KEY: process.env.NEYNAR_API_KEY }), ...(process.env.NEYNAR_CLIENT_ID && { NEYNAR_CLIENT_ID: process.env.NEYNAR_CLIENT_ID }), - // Frame metadata - don't stringify here - ...(frameMetadata && { FRAME_METADATA: frameMetadata }), + // Mini app metadata - don't stringify here + ...(miniAppMetadata && { MINI_APP_METADATA: miniAppMetadata }), // Public vars ...Object.fromEntries( @@ -539,10 +539,10 @@ async function deployToVercel(useGitHub = false) { ? `https://api.neynar.com/f/app/${process.env.NEYNAR_CLIENT_ID}/event` : `https://${actualDomain}/api/webhook`; - if (frameMetadata) { - frameMetadata = await generateFarcasterMetadata(actualDomain, fid, await validateSeedPhrase(process.env.SEED_PHRASE), process.env.SEED_PHRASE, webhookUrl); - // Update FRAME_METADATA env var using the new function - await setVercelEnvVar('FRAME_METADATA', frameMetadata, projectRoot); + if (miniAppMetadata) { + miniAppMetadata = await generateFarcasterMetadata(actualDomain, fid, await validateSeedPhrase(process.env.SEED_PHRASE), process.env.SEED_PHRASE, webhookUrl); + // Update MINI_APP_METADATA env var using the new function + await setVercelEnvVar('MINI_APP_METADATA', miniAppMetadata, projectRoot); } // Update NEXTAUTH_URL diff --git a/scripts/dev.js b/scripts/dev.js index ab49b8c..23c2a86 100755 --- a/scripts/dev.js +++ b/scripts/dev.js @@ -81,7 +81,7 @@ async function startDev() { } const useTunnel = process.env.USE_TUNNEL === 'true'; - let frameUrl; + let miniAppUrl; if (useTunnel) { // Start localtunnel and get URL @@ -93,7 +93,7 @@ async function startDev() { console.error('Error getting IP address:', error); } - frameUrl = tunnel.url; + miniAppUrl = tunnel.url; console.log(` 🌐 Local tunnel URL: ${tunnel.url} @@ -117,12 +117,12 @@ async function startDev() { 5. Click "Preview" (note that it may take ~10 seconds to load) `); } else { - frameUrl = 'http://localhost:3000'; + miniAppUrl = 'http://localhost:3000'; console.log(` šŸ’» To test your mini app: 1. Open the Warpcast Mini App Developer Tools: https://warpcast.com/~/developers 2. Scroll down to the "Preview Mini App" tool - 3. Enter this URL: ${frameUrl} + 3. Enter this URL: ${miniAppUrl} 4. Click "Preview" to test your mini app (note that it may take ~5 seconds to load the first time) `); } @@ -132,7 +132,7 @@ async function startDev() { nextDev = spawn(nextBin, ['dev'], { stdio: 'inherit', - env: { ...process.env, NEXT_PUBLIC_URL: frameUrl, NEXTAUTH_URL: frameUrl }, + env: { ...process.env, NEXT_PUBLIC_URL: miniAppUrl, NEXTAUTH_URL: miniAppUrl }, cwd: projectRoot, shell: process.platform === 'win32' // Add shell option for Windows }); diff --git a/src/app/api/send-notification/route.ts b/src/app/api/send-notification/route.ts index 3dda0f3..69d746e 100644 --- a/src/app/api/send-notification/route.ts +++ b/src/app/api/send-notification/route.ts @@ -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", diff --git a/src/app/api/webhook/route.ts b/src/app/api/webhook/route.ts index af6973c..a8c2ece 100644 --- a/src/app/api/webhook/route.ts +++ b/src/app/api/webhook/route.ts @@ -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; diff --git a/src/app/page.tsx b/src/app/page.tsx index 56d085b..4e11816 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -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 { images: [APP_OG_IMAGE_URL], }, other: { - "fc:frame": JSON.stringify(getFrameEmbedMetadata()), + "fc:frame": JSON.stringify(getMiniAppEmbedMetadata()), }, }; } diff --git a/src/app/share/[fid]/page.tsx b/src/app/share/[fid]/page.tsx index b7e06cc..861c3cf 100644 --- a/src/app/share/[fid]/page.tsx +++ b/src/app/share/[fid]/page.tsx @@ -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)), }, }; } diff --git a/src/components/Demo.tsx b/src/components/Demo.tsx index 4c697fe..0ae0372 100644 --- a/src/components/Demo.tsx +++ b/src/components/Demo.tsx @@ -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 ( diff --git a/src/lib/constants.ts b/src/lib/constants.ts index b603f63..c5c2c3e 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -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`; diff --git a/src/lib/neynar.ts b/src/lib/neynar.ts index ee40213..f0b6e23 100644 --- a/src/lib/neynar.ts +++ b/src/lib/neynar.ts @@ -31,7 +31,7 @@ export async function getNeynarUser(fid: number): Promise { } } -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 { +}): Promise { try { const client = getNeynarClient(); const targetFids = [fid]; diff --git a/src/lib/notifs.ts b/src/lib/notifs.ts index a1dffda..a04b24e 100644 --- a/src/lib/notifs.ts +++ b/src/lib/notifs.ts @@ -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 { +}): Promise { const notificationDetails = await getUserNotificationDetails(fid); if (!notificationDetails) { return { state: "no_token" }; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index d0383c0..85168a0 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -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 { - // First check for FRAME_METADATA in .env and use that if it exists - if (process.env.FRAME_METADATA) { +export async function getFarcasterMetadata(): Promise { + // 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 { 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, diff --git a/tailwind.config.ts b/tailwind.config.ts index 1e31bf5..3ee2214 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,7 +1,7 @@ import type { Config } from "tailwindcss"; export default { - darkMode: ["class"], + darkMode: "media", content: [ "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", "./src/components/**/*.{js,ts,jsx,tsx,mdx}",