diff --git a/scripts/build.js b/scripts/build.js index 8dc054a..aece3cc 100755 --- a/scripts/build.js +++ b/scripts/build.js @@ -27,7 +27,7 @@ async function lookupFidByCustodyAddress(custodyAddress, apiKey) { { headers: { 'accept': 'application/json', - 'x-api-key': apiKey + 'x-api-key': 'FARCASTER_V2_FRAMES_DEMO' } } ); @@ -269,10 +269,17 @@ async function main() { } // Try to get client ID from API - const appInfo = await queryNeynarApp(neynarApiKey); - if (appInfo) { - neynarClientId = appInfo.app_uuid; - console.log('✅ Fetched Neynar app client ID'); + if (!neynarClientId) { + const appInfo = await queryNeynarApp(neynarApiKey); + if (appInfo) { + neynarClientId = appInfo.app_uuid; + console.log('✅ Fetched Neynar app client ID'); + break; + } + } + + // We have a client ID (either from .env or fetched from API), so we can break out of the loop + if (neynarClientId) { break; } @@ -366,6 +373,7 @@ async function main() { // FID (if it exists in current env) ...(process.env.FID ? [`FID="${process.env.FID}"`] : []), + `NEXT_PUBLIC_USE_WALLET="${process.env.NEXT_PUBLIC_USE_WALLET || 'false'}"`, // NextAuth configuration `NEXTAUTH_SECRET="${process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString('hex')}"`, diff --git a/src/components/Demo.tsx b/src/components/Demo.tsx index 199e226..4c697fe 100644 --- a/src/components/Demo.tsx +++ b/src/components/Demo.tsx @@ -2,7 +2,6 @@ "use client"; import { useCallback, useEffect, useMemo, useState } from "react"; -import { Input } from "../components/ui/input"; import { signIn, signOut, getCsrfToken } from "next-auth/react"; import sdk, { SignIn as SignInCore, @@ -35,7 +34,7 @@ import { useMiniApp } from "@neynar/react"; import { PublicKey, SystemProgram, Transaction } from '@solana/web3.js'; import { Header } from "~/components/ui/Header"; import { Footer } from "~/components/ui/Footer"; -import { USE_WALLET } from "~/lib/constants"; +import { USE_WALLET, APP_NAME } from "~/lib/constants"; export type Tab = 'home' | 'actions' | 'context' | 'wallet'; @@ -191,7 +190,7 @@ export default function Demo( const signTyped = useCallback(() => { signTypedData({ domain: { - name: "Frames v2 Demo", + name: APP_NAME, version: "1", chainId, }, @@ -199,7 +198,7 @@ export default function Demo( Message: [{ name: "content", type: "string" }], }, message: { - content: "Hello from Frames v2!", + content: `Hello from ${APP_NAME}!`, }, primaryType: "Message", }); @@ -243,7 +242,7 @@ export default function Demo( cast={{ text: "Check out this awesome frame @1 @2 @3! 🚀🪐", bestFriends: true, - embeds: [`${APP_URL}/share?fid=${context?.user?.fid || 'unknown'}`] + embeds: [`${process.env.NEXT_PUBLIC_URL}/share/${context?.user?.fid || ''}`] }} className="w-full" /> @@ -252,10 +251,10 @@ export default function Demo( - + {sendNotificationResult && ( diff --git a/src/components/ui/Share.tsx b/src/components/ui/Share.tsx index 1c0ace3..8cb1b0e 100644 --- a/src/components/ui/Share.tsx +++ b/src/components/ui/Share.tsx @@ -26,17 +26,18 @@ interface ShareButtonProps { export function ShareButton({ buttonText, cast, className = '', isLoading = false }: ShareButtonProps) { const [isProcessing, setIsProcessing] = useState(false); const [bestFriends, setBestFriends] = useState<{ fid: number; username: string; }[] | null>(null); + const [isLoadingBestFriends, setIsLoadingBestFriends] = useState(false); const { context, actions } = useMiniApp(); // Fetch best friends if needed useEffect(() => { if (cast.bestFriends && context?.user?.fid) { - setIsProcessing(true); + setIsLoadingBestFriends(true); fetch(`/api/best-friends?fid=${context.user.fid}`) .then(res => res.json()) .then(data => setBestFriends(data.bestFriends)) .catch(err => console.error('Failed to fetch best friends:', err)) - .finally(() => setIsProcessing(false)); + .finally(() => setIsLoadingBestFriends(false)); } }, [cast.bestFriends, context?.user?.fid]); @@ -47,16 +48,21 @@ export function ShareButton({ buttonText, cast, className = '', isLoading = fals let finalText = cast.text || ''; // Process best friends if enabled and data is loaded - if (cast.bestFriends && bestFriends) { - // Replace @N with usernames - finalText = finalText.replace(/@\d+/g, (match) => { - const friendIndex = parseInt(match.slice(1)) - 1; - const friend = bestFriends[friendIndex]; - if (friend) { - return `@${friend.username}`; - } - return match; - }); + if (cast.bestFriends) { + if (bestFriends) { + // Replace @N with usernames, or remove if no matching friend + finalText = finalText.replace(/@\d+/g, (match) => { + const friendIndex = parseInt(match.slice(1)) - 1; + const friend = bestFriends[friendIndex]; + if (friend) { + return `@${friend.username}`; + } + return ''; // Remove @N if no matching friend + }); + } else { + // If bestFriends is not loaded but bestFriends is enabled, remove @N patterns + finalText = finalText.replace(/@\d+/g, ''); + } } // Process embeds @@ -99,14 +105,12 @@ export function ShareButton({ buttonText, cast, className = '', isLoading = fals } }, [cast, bestFriends, context?.user?.fid, actions]); - const isButtonDisabled = cast.bestFriends && !bestFriends; - return (