From bade04b7856b38dca9f1c32e23332fe3a313e03b Mon Sep 17 00:00:00 2001 From: Shreyaschorge Date: Fri, 18 Jul 2025 22:55:10 +0530 Subject: [PATCH 1/5] fix-deploy-and-manifest-issue --- .eslintrc.json | 31 ++++++++++++++++++++++++++- package.json | 2 +- src/app/layout.tsx | 1 - src/app/providers.tsx | 29 +++++++++++++++++-------- src/components/ui/tabs/ActionsTab.tsx | 10 ++++----- src/lib/constants.ts | 26 +++++++++++----------- src/lib/kv.ts | 24 +++++++++++---------- src/lib/utils.ts | 26 +++++++++------------- 8 files changed, 92 insertions(+), 57 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 3722418..24a1239 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,3 +1,32 @@ { - "extends": ["next/core-web-vitals", "next/typescript"] + "extends": ["next/core-web-vitals", "next/typescript"], + "rules": { + // Disable img warnings since you're using them intentionally in specific contexts + "@next/next/no-img-element": "off", + + // Allow @ts-ignore comments (though @ts-expect-error is preferred) + "@typescript-eslint/ban-ts-comment": "off", + + // Allow explicit any types (sometimes necessary for dynamic imports and APIs) + "@typescript-eslint/no-explicit-any": "off", + + // Allow unused variables that start with underscore + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_", + "caughtErrorsIgnorePattern": "^_" + } + ], + + // Make display name warnings instead of errors for dynamic components + "react/display-name": "warn", + + // Allow module assignment for dynamic imports + "@next/next/no-assign-module-variable": "warn", + + // Make exhaustive deps a warning instead of error for complex hooks + "react-hooks/exhaustive-deps": "warn" + } } diff --git a/package.json b/package.json index 60cf744..e7340c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@neynar/create-farcaster-mini-app", - "version": "1.7.9", + "version": "1.7.10", "type": "module", "private": false, "access": "public", diff --git a/src/app/layout.tsx b/src/app/layout.tsx index c42d41a..30368ab 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -22,7 +22,6 @@ export default async function RootLayout({ let session = null; if (shouldUseSession) { try { - // @ts-ignore - auth module may not exist in all template variants const authModule = eval('require("~/auth")'); session = await authModule.getSession(); } catch (error) { diff --git a/src/app/providers.tsx b/src/app/providers.tsx index d9c19fa..429a147 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -12,14 +12,12 @@ const WagmiProvider = dynamic( } ); - - export function Providers({ session, children, shouldUseSession = false, }: { - session: any | null; + session: unknown | null; children: React.ReactNode; shouldUseSession?: boolean; }) { @@ -34,18 +32,31 @@ export function Providers({ return Promise.resolve().then(() => { // Use eval to avoid build-time module resolution try { - // @ts-ignore - These modules may not exist in all template variants const nextAuth = eval('require("next-auth/react")'); const authKit = eval('require("@farcaster/auth-kit")'); - - return ({ children }: { children: React.ReactNode }) => ( + + const AuthWrapper = ({ + children, + }: { + children: React.ReactNode; + }) => ( - {children} + + {children} + ); - } catch (error) { + AuthWrapper.displayName = 'AuthWrapper'; + return AuthWrapper; + } catch (_error) { // Fallback component when auth modules aren't available - return ({ children }: { children: React.ReactNode }) => <>{children}; + const FallbackWrapper = ({ + children, + }: { + children: React.ReactNode; + }) => <>{children}; + FallbackWrapper.displayName = 'FallbackWrapper'; + return FallbackWrapper; } }); }, diff --git a/src/components/ui/tabs/ActionsTab.tsx b/src/components/ui/tabs/ActionsTab.tsx index bc7880d..c633ed9 100644 --- a/src/components/ui/tabs/ActionsTab.tsx +++ b/src/components/ui/tabs/ActionsTab.tsx @@ -1,7 +1,7 @@ 'use client'; import dynamic from 'next/dynamic'; -import { useCallback, useState, type ComponentType } from 'react'; +import { useCallback, useState } from 'react'; import { useMiniApp } from '@neynar/react'; import { ShareButton } from '../Share'; import { Button } from '../Button'; @@ -14,10 +14,9 @@ const NeynarAuthButton = dynamic( () => { return Promise.resolve().then(() => { try { - // @ts-ignore - NeynarAuthButton may not exist in all template variants - const module = eval('require("../NeynarAuthButton/index")'); - return module.default || module.NeynarAuthButton; - } catch (error) { + const authModule = eval('require("../NeynarAuthButton/index")'); + return authModule.default || authModule.NeynarAuthButton; + } catch (_error) { // Return null component when module doesn't exist return () => null; } @@ -26,7 +25,6 @@ const NeynarAuthButton = dynamic( { ssr: false } ); - /** * ActionsTab component handles mini app actions like sharing, notifications, and haptic feedback. * diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 4938795..2666d8b 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1,4 +1,4 @@ -import { type AccountAssociation } from '@farcaster/miniapp-node'; +import { type AccountAssociation } from '@farcaster/miniapp-core/src/manifest'; /** * Application constants and configuration values. @@ -22,25 +22,25 @@ export const APP_URL: string = process.env.NEXT_PUBLIC_URL!; * The name of the mini app as displayed to users. * Used in titles, headers, and app store listings. */ -export const APP_NAME: string = 'Starter Kit'; +export const APP_NAME = 'idk2'; /** * A brief description of the mini app's functionality. * Used in app store listings and metadata. */ -export const APP_DESCRIPTION: string = 'A demo of the Neynar Starter Kit'; +export const APP_DESCRIPTION = 'A Farcaster mini app created with Neynar'; /** * The primary category for the mini app. * Used for app store categorization and discovery. */ -export const APP_PRIMARY_CATEGORY: string = 'developer-tools'; +export const APP_PRIMARY_CATEGORY = ''; /** * Tags associated with the mini app. * Used for search and discovery in app stores. */ -export const APP_TAGS: string[] = ['neynar', 'starter-kit', 'demo']; +export const APP_TAGS = ['neynar', 'starter-kit', 'demo']; // --- Asset URLs --- /** @@ -65,21 +65,22 @@ export const APP_SPLASH_URL: string = `${APP_URL}/splash.png`; * Background color for the splash screen. * Used as fallback when splash image is loading. */ -export const APP_SPLASH_BACKGROUND_COLOR: string = "#f7f7f7"; +export const APP_SPLASH_BACKGROUND_COLOR: string = '#f7f7f7'; /** * Account association for the mini app. * Used to associate the mini app with a Farcaster account. * If not provided, the mini app will be unsigned and have limited capabilities. */ -export const APP_ACCOUNT_ASSOCIATION: AccountAssociation | undefined = undefined; +export const APP_ACCOUNT_ASSOCIATION: AccountAssociation | undefined = + undefined; // --- UI Configuration --- /** * Text displayed on the main action button. * Used for the primary call-to-action in the mini app. */ -export const APP_BUTTON_TEXT: string = 'Launch NSK'; +export const APP_BUTTON_TEXT = 'Launch Mini App'; // --- Integration Configuration --- /** @@ -89,7 +90,8 @@ export const APP_BUTTON_TEXT: string = 'Launch NSK'; * Neynar webhook endpoint. Otherwise, falls back to a local webhook * endpoint for development and testing. */ -export const APP_WEBHOOK_URL: string = process.env.NEYNAR_API_KEY && process.env.NEYNAR_CLIENT_ID +export const APP_WEBHOOK_URL: string = + 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`; @@ -100,7 +102,7 @@ export const APP_WEBHOOK_URL: string = process.env.NEYNAR_API_KEY && process.env * When false, wallet functionality is completely hidden from the UI. * Useful for mini apps that don't require wallet integration. */ -export const USE_WALLET: boolean = true; +export const USE_WALLET = false; /** * Flag to enable/disable analytics tracking. @@ -109,7 +111,7 @@ export const USE_WALLET: boolean = true; * When false, analytics collection is disabled. * Useful for privacy-conscious users or development environments. */ -export const ANALYTICS_ENABLED: boolean = true; +export const ANALYTICS_ENABLED = true; /** * Required chains for the mini app. @@ -117,7 +119,7 @@ export const ANALYTICS_ENABLED: boolean = true; * Contains an array of CAIP-2 identifiers for blockchains that the mini app requires. * If the host does not support all chains listed here, it will not render the mini app. * If empty or undefined, the mini app will be rendered regardless of chain support. - * + * * Supported chains: eip155:1, eip155:137, eip155:42161, eip155:10, eip155:8453, * solana:mainnet, solana:devnet */ diff --git a/src/lib/kv.ts b/src/lib/kv.ts index eefc680..28f0982 100644 --- a/src/lib/kv.ts +++ b/src/lib/kv.ts @@ -1,16 +1,18 @@ -import { FrameNotificationDetails } from "@farcaster/miniapp-sdk"; -import { Redis } from "@upstash/redis"; -import { APP_NAME } from "./constants"; +import { MiniAppNotificationDetails } from '@farcaster/miniapp-sdk'; +import { Redis } from '@upstash/redis'; +import { APP_NAME } from './constants'; // In-memory fallback storage -const localStore = new Map(); +const localStore = new Map(); // Use Redis if KV env vars are present, otherwise use in-memory const useRedis = process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN; -const redis = useRedis ? new Redis({ - url: process.env.KV_REST_API_URL!, - token: process.env.KV_REST_API_TOKEN!, -}) : null; +const redis = useRedis + ? new Redis({ + url: process.env.KV_REST_API_URL!, + token: process.env.KV_REST_API_TOKEN!, + }) + : null; function getUserNotificationDetailsKey(fid: number): string { return `${APP_NAME}:user:${fid}`; @@ -18,17 +20,17 @@ function getUserNotificationDetailsKey(fid: number): string { export async function getUserNotificationDetails( fid: number -): Promise { +): Promise { const key = getUserNotificationDetailsKey(fid); if (redis) { - return await redis.get(key); + return await redis.get(key); } return localStore.get(key) || null; } export async function setUserNotificationDetails( fid: number, - notificationDetails: FrameNotificationDetails + notificationDetails: MiniAppNotificationDetails ): Promise { const key = getUserNotificationDetailsKey(fid); if (redis) { diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 89e8b8c..dffb165 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,6 +1,6 @@ import { type ClassValue, clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; -import { type Manifest } from '@farcaster/miniapp-node'; +import { Manifest } from '@farcaster/miniapp-core/src/manifest'; import { APP_BUTTON_TEXT, APP_DESCRIPTION, @@ -10,7 +10,8 @@ import { APP_PRIMARY_CATEGORY, APP_SPLASH_BACKGROUND_COLOR, APP_SPLASH_URL, - APP_TAGS, APP_URL, + APP_TAGS, + APP_URL, APP_WEBHOOK_URL, APP_ACCOUNT_ASSOCIATION, APP_REQUIRED_CHAINS, @@ -22,7 +23,7 @@ export function cn(...inputs: ClassValue[]) { export function getMiniAppEmbedMetadata(ogImageUrl?: string) { return { - version: "next", + version: 'next', imageUrl: ogImageUrl ?? APP_OG_IMAGE_URL, ogTitle: APP_NAME, ogDescription: APP_DESCRIPTION, @@ -30,7 +31,7 @@ export function getMiniAppEmbedMetadata(ogImageUrl?: string) { button: { title: APP_BUTTON_TEXT, action: { - type: "launch_frame", + type: 'launch_frame', name: APP_NAME, url: APP_URL, splashImageUrl: APP_SPLASH_URL, @@ -46,24 +47,17 @@ export function getMiniAppEmbedMetadata(ogImageUrl?: string) { export async function getFarcasterDomainManifest(): Promise { return { - accountAssociation: APP_ACCOUNT_ASSOCIATION, + accountAssociation: APP_ACCOUNT_ASSOCIATION!, miniapp: { - version: "1", - name: APP_NAME ?? "Neynar Starter Kit", - iconUrl: APP_ICON_URL, + version: '1', + name: APP_NAME ?? 'Neynar Starter Kit', homeUrl: APP_URL, + iconUrl: APP_ICON_URL, imageUrl: APP_OG_IMAGE_URL, - buttonTitle: APP_BUTTON_TEXT ?? "Launch Mini App", + buttonTitle: APP_BUTTON_TEXT ?? 'Launch Mini App', splashImageUrl: APP_SPLASH_URL, splashBackgroundColor: APP_SPLASH_BACKGROUND_COLOR, webhookUrl: APP_WEBHOOK_URL, - description: APP_DESCRIPTION, - primaryCategory: APP_PRIMARY_CATEGORY, - tags: APP_TAGS, - requiredChains: APP_REQUIRED_CHAINS.length > 0 ? APP_REQUIRED_CHAINS : undefined, - ogTitle: APP_NAME, - ogDescription: APP_DESCRIPTION, - ogImageUrl: APP_OG_IMAGE_URL, }, }; } From 882e4f166fd240957d4b48c2c16c57ddd08f416d Mon Sep 17 00:00:00 2001 From: Shreyaschorge Date: Sat, 19 Jul 2025 02:43:31 +0530 Subject: [PATCH 2/5] fix imports --- package.json | 2 +- src/app/layout.tsx | 4 +- src/app/providers.tsx | 150 ++++++++++++++++---------- src/components/ui/tabs/ActionsTab.tsx | 19 ++-- src/lib/constants.ts | 8 +- src/lib/utils.ts | 1 - 6 files changed, 107 insertions(+), 77 deletions(-) diff --git a/package.json b/package.json index e7340c5..a855ee6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@neynar/create-farcaster-mini-app", - "version": "1.7.10", + "version": "1.7.11", "type": "module", "private": false, "access": "public", diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 30368ab..6b3ad05 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -22,8 +22,8 @@ export default async function RootLayout({ let session = null; if (shouldUseSession) { try { - const authModule = eval('require("~/auth")'); - session = await authModule.getSession(); + const { getSession } = await import('~/auth'); + session = await getSession(); } catch (error) { console.warn('Failed to get session:', error); } diff --git a/src/app/providers.tsx b/src/app/providers.tsx index 429a147..63fb582 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -4,6 +4,7 @@ import dynamic from 'next/dynamic'; import { MiniAppProvider } from '@neynar/react'; import { SafeFarcasterSolanaProvider } from '~/components/providers/SafeFarcasterSolanaProvider'; import { ANALYTICS_ENABLED } from '~/lib/constants'; +import React, { useState, useEffect } from 'react'; const WagmiProvider = dynamic( () => import('~/components/providers/WagmiProvider'), @@ -12,72 +13,107 @@ const WagmiProvider = dynamic( } ); +// Helper component to conditionally render auth providers +function AuthProviders({ + children, + session, + shouldUseSession, +}: { + children: React.ReactNode; + session: any; + shouldUseSession: boolean; +}) { + const [authComponents, setAuthComponents] = useState<{ + SessionProvider: React.ComponentType | null; + AuthKitProvider: React.ComponentType | null; + loaded: boolean; + }>({ + SessionProvider: null, + AuthKitProvider: null, + loaded: false, + }); + + useEffect(() => { + if (!shouldUseSession) { + setAuthComponents({ + SessionProvider: null, + AuthKitProvider: null, + loaded: true, + }); + return; + } + + const loadAuthComponents = async () => { + try { + // Dynamic imports for auth modules + let SessionProvider = null; + let AuthKitProvider = null; + + try { + const nextAuth = await import('next-auth/react'); + SessionProvider = nextAuth.SessionProvider; + } catch (error) { + console.warn('NextAuth not available:', error); + } + + try { + const authKit = await import('@farcaster/auth-kit'); + AuthKitProvider = authKit.AuthKitProvider; + } catch (error) { + console.warn('Farcaster AuthKit not available:', error); + } + + setAuthComponents({ + SessionProvider, + AuthKitProvider, + loaded: true, + }); + } catch (error) { + console.error('Error loading auth components:', error); + setAuthComponents({ + SessionProvider: null, + AuthKitProvider: null, + loaded: true, + }); + } + }; + + loadAuthComponents(); + }, [shouldUseSession]); + + if (!authComponents.loaded) { + return
Loading...
; + } + + if (!shouldUseSession || !authComponents.SessionProvider) { + return <>{children}; + } + + const { SessionProvider, AuthKitProvider } = authComponents; + + if (AuthKitProvider) { + return ( + + {children} + + ); + } + + return {children}; +} + export function Providers({ session, children, shouldUseSession = false, }: { - session: unknown | null; + session: any | null; children: React.ReactNode; shouldUseSession?: boolean; }) { const solanaEndpoint = process.env.SOLANA_RPC_ENDPOINT || 'https://solana-rpc.publicnode.com'; - // Only wrap with SessionProvider if next auth is used - if (shouldUseSession) { - // Dynamic import for auth components - will work if modules exist, fallback if not - const AuthWrapper = dynamic( - () => { - return Promise.resolve().then(() => { - // Use eval to avoid build-time module resolution - try { - const nextAuth = eval('require("next-auth/react")'); - const authKit = eval('require("@farcaster/auth-kit")'); - - const AuthWrapper = ({ - children, - }: { - children: React.ReactNode; - }) => ( - - - {children} - - - ); - AuthWrapper.displayName = 'AuthWrapper'; - return AuthWrapper; - } catch (_error) { - // Fallback component when auth modules aren't available - const FallbackWrapper = ({ - children, - }: { - children: React.ReactNode; - }) => <>{children}; - FallbackWrapper.displayName = 'FallbackWrapper'; - return FallbackWrapper; - } - }); - }, - { ssr: false } - ); - - return ( - - - - {children} - - - - ); - } - - // Return without SessionProvider if no session return ( - {children} + + {children} + diff --git a/src/components/ui/tabs/ActionsTab.tsx b/src/components/ui/tabs/ActionsTab.tsx index c633ed9..8c418e9 100644 --- a/src/components/ui/tabs/ActionsTab.tsx +++ b/src/components/ui/tabs/ActionsTab.tsx @@ -9,19 +9,12 @@ import { SignIn } from '../wallet/SignIn'; import { type Haptics } from '@farcaster/miniapp-sdk'; import { APP_URL } from '~/lib/constants'; -// Optional import for NeynarAuthButton - may not exist in all templates +// Import NeynarAuthButton const NeynarAuthButton = dynamic( - () => { - return Promise.resolve().then(() => { - try { - const authModule = eval('require("../NeynarAuthButton/index")'); - return authModule.default || authModule.NeynarAuthButton; - } catch (_error) { - // Return null component when module doesn't exist - return () => null; - } - }); - }, + () => + import('../NeynarAuthButton').then((module) => ({ + default: module.NeynarAuthButton, + })), { ssr: false } ); @@ -156,7 +149,7 @@ export function ActionsTab() { {/* Neynar Authentication */} - {NeynarAuthButton && } + {/* Mini app actions */}