diff --git a/mini-apps/mini-app-validation/README.md b/mini-apps/mini-app-validation/README.md new file mode 100644 index 000000000..a9a3b2d1c --- /dev/null +++ b/mini-apps/mini-app-validation/README.md @@ -0,0 +1,32 @@ +# Base Mini App Compatibility Validator + +A read-only validation tool that scans mini app codebases to identify patterns that are not supported in Base app. + +## What it does + +This validator searches your mini app code for specific patterns that work in other environments (like Farcaster frames) but are not supported in Base app, helping ensure compatibility before deployment. + +## What it checks for + +- **Environment Detection**: `isInMiniApp()` calls +- **Haptics**: Haptic feedback API usage +- **Warpcast Composer URLs**: Direct links to Warpcast compose +- **Token Operations**: `swapToken`, `viewToken`, `sendToken` actions +- **Direct HTML Links**: `` tags and external links +- **Location Context**: Context location properties +- **SDK Ready Call**: Required `sdk.actions.ready()` implementation + +## How it works + +1. Scans application code in `/app/`, `/src/`, `/components/`, `/pages/` directories +2. Excludes third-party code (`/node_modules/`, `/.next/`, etc.) +3. Reports exact file locations and line numbers for unsupported patterns +4. Provides compatibility notes for each issue found + +## Output + +The validator provides a simple report listing any compatibility issues found, or confirms when no unsupported patterns are detected. + +--- + +*This is a read-only validator - it only reports findings and does not modify code or suggest fixes.* diff --git a/minikit/mini-app-help/validate.txt b/mini-apps/mini-app-validation/validate.txt similarity index 100% rename from minikit/mini-app-help/validate.txt rename to mini-apps/mini-app-validation/validate.txt diff --git a/minikit/mini-app-full-demo/.gitignore b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/.gitignore similarity index 100% rename from minikit/mini-app-full-demo/.gitignore rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/.gitignore diff --git a/minikit/mini-app-full-demo/README.md b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/README.md similarity index 100% rename from minikit/mini-app-full-demo/README.md rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/README.md diff --git a/minikit/mini-app-full-demo/components.json b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/components.json similarity index 100% rename from minikit/mini-app-full-demo/components.json rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/components.json diff --git a/minikit/mini-app-full-demo/next.config.ts b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/next.config.ts similarity index 100% rename from minikit/mini-app-full-demo/next.config.ts rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/next.config.ts diff --git a/minikit/mini-app-full-demo/package.json b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/package.json similarity index 100% rename from minikit/mini-app-full-demo/package.json rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/package.json diff --git a/minikit/mini-app-full-demo/postcss.config.mjs b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/postcss.config.mjs similarity index 100% rename from minikit/mini-app-full-demo/postcss.config.mjs rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/postcss.config.mjs diff --git a/minikit/mini-app-full-demo/public/banner.png b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/public/banner.png similarity index 100% rename from minikit/mini-app-full-demo/public/banner.png rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/public/banner.png diff --git a/minikit/mini-app-full-demo/public/base-logo.png b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/public/base-logo.png similarity index 100% rename from minikit/mini-app-full-demo/public/base-logo.png rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/public/base-logo.png diff --git a/minikit/mini-app-full-demo/public/base-square.png b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/public/base-square.png similarity index 100% rename from minikit/mini-app-full-demo/public/base-square.png rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/public/base-square.png diff --git a/minikit/mini-app-full-demo/public/icon.png b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/public/icon.png similarity index 100% rename from minikit/mini-app-full-demo/public/icon.png rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/public/icon.png diff --git a/minikit/mini-app-full-demo/src/app/.well-known/farcaster.json/route.ts b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/app/.well-known/farcaster.json/route.ts similarity index 100% rename from minikit/mini-app-full-demo/src/app/.well-known/farcaster.json/route.ts rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/app/.well-known/farcaster.json/route.ts diff --git a/minikit/mini-app-full-demo/src/app/app.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/app/app.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/app/app.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/app/app.tsx diff --git a/minikit/mini-app-full-demo/src/app/favicon.ico b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/app/favicon.ico similarity index 100% rename from minikit/mini-app-full-demo/src/app/favicon.ico rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/app/favicon.ico diff --git a/minikit/mini-app-full-demo/src/app/globals.css b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/app/globals.css similarity index 100% rename from minikit/mini-app-full-demo/src/app/globals.css rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/app/globals.css diff --git a/minikit/mini-app-full-demo/src/app/layout.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/app/layout.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/app/layout.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/app/layout.tsx diff --git a/minikit/mini-app-full-demo/src/app/page.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/app/page.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/app/page.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/app/page.tsx diff --git a/minikit/mini-app-full-demo/src/app/providers.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/app/providers.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/app/providers.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/app/providers.tsx diff --git a/minikit/mini-app-full-demo/src/components/Demo.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/Demo.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/Demo.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/Demo.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/add-miniapp.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/add-miniapp.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/add-miniapp.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/add-miniapp.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/close-miniapp.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/close-miniapp.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/close-miniapp.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/close-miniapp.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/compose-cast.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/compose-cast.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/compose-cast.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/compose-cast.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/get-capabilities.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/get-capabilities.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/get-capabilities.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/get-capabilities.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/get-chains.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/get-chains.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/get-chains.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/get-chains.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/haptics.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/haptics.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/haptics.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/haptics.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/open-miniapp.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/open-miniapp.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/open-miniapp.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/open-miniapp.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/openurl.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/openurl.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/openurl.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/openurl.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/quick-auth.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/quick-auth.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/quick-auth.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/quick-auth.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/request-camera-microphone.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/request-camera-microphone.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/request-camera-microphone.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/request-camera-microphone.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/send-token.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/send-token.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/send-token.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/send-token.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/set-primary-button.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/set-primary-button.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/set-primary-button.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/set-primary-button.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/signin.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/signin.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/signin.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/signin.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/swap-token.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/swap-token.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/swap-token.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/swap-token.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/view-cast.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/view-cast.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/view-cast.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/view-cast.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/view-profile.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/view-profile.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/view-profile.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/view-profile.tsx diff --git a/minikit/mini-app-full-demo/src/components/actions/view-token.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/view-token.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/actions/view-token.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/actions/view-token.tsx diff --git a/minikit/mini-app-full-demo/src/components/providers/ErudaProvider.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/providers/ErudaProvider.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/providers/ErudaProvider.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/providers/ErudaProvider.tsx diff --git a/minikit/mini-app-full-demo/src/components/providers/FrameProvider.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/providers/FrameProvider.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/providers/FrameProvider.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/providers/FrameProvider.tsx diff --git a/minikit/mini-app-full-demo/src/components/providers/WagmiProvider.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/providers/WagmiProvider.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/providers/WagmiProvider.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/providers/WagmiProvider.tsx diff --git a/minikit/mini-app-full-demo/src/components/ui/Button.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/ui/Button.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/ui/Button.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/ui/Button.tsx diff --git a/minikit/mini-app-full-demo/src/components/ui/input.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/ui/input.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/ui/input.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/ui/input.tsx diff --git a/minikit/mini-app-full-demo/src/components/ui/label.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/ui/label.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/ui/label.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/ui/label.tsx diff --git a/minikit/mini-app-full-demo/src/components/wallet/BasePay.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/wallet/BasePay.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/wallet/BasePay.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/wallet/BasePay.tsx diff --git a/minikit/mini-app-full-demo/src/components/wallet/WalletActions.tsx b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/wallet/WalletActions.tsx similarity index 100% rename from minikit/mini-app-full-demo/src/components/wallet/WalletActions.tsx rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/components/wallet/WalletActions.tsx diff --git a/minikit/mini-app-full-demo/src/lib/truncateAddress.ts b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/lib/truncateAddress.ts similarity index 100% rename from minikit/mini-app-full-demo/src/lib/truncateAddress.ts rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/lib/truncateAddress.ts diff --git a/minikit/mini-app-full-demo/src/lib/utils.ts b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/lib/utils.ts similarity index 100% rename from minikit/mini-app-full-demo/src/lib/utils.ts rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/src/lib/utils.ts diff --git a/minikit/mini-app-full-demo/tailwind.config.ts b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/tailwind.config.ts similarity index 100% rename from minikit/mini-app-full-demo/tailwind.config.ts rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/tailwind.config.ts diff --git a/minikit/mini-app-full-demo/tsconfig.json b/mini-apps/templates/farcaster-sdk/mini-app-full-demo/tsconfig.json similarity index 100% rename from minikit/mini-app-full-demo/tsconfig.json rename to mini-apps/templates/farcaster-sdk/mini-app-full-demo/tsconfig.json diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/.eslintrc.json b/mini-apps/templates/minikit/mini-app-full-demo-minikit/.eslintrc.json new file mode 100644 index 000000000..6b10a5b73 --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": [ + "next/core-web-vitals", + "next/typescript" + ] +} diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/.gitignore b/mini-apps/templates/minikit/mini-app-full-demo-minikit/.gitignore new file mode 100644 index 000000000..3256e8383 --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts \ No newline at end of file diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/README.md b/mini-apps/templates/minikit/mini-app-full-demo-minikit/README.md new file mode 100644 index 000000000..61a8cc78a --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/README.md @@ -0,0 +1,27 @@ +# Base Mini App Demo + +A demo [mini app](https://miniapps.farcaster.xyz) to demonstrate the functionality available for mini app developers in [Base App](https://base.app). + +Note: this Base Mini App Demo used to live [here](https://github.com/Vicolee/frames-v2-demo) and was recently moved to this `base/demos` repo so it can be more easily found + +## Getting Started + +This is a [NextJS](https://nextjs.org/) + TypeScript + React app. + +To install dependencies: + +```bash +$ yarn +``` + +To run the app: + +```bash +$ yarn dev +``` + +To test your mini app in Farcaster's playground or in Base App, you'll want to use a tunneling tool like [ngrok](https://ngrok.com/) + +## Relevant Links +- [Mini Apps in Base App Docs](https://docs.base.org/base-app/introduction/mini-apps) +- [MiniKit Docs](https://docs.base.org/base-app/build-with-minikit/overview) \ No newline at end of file diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/components.json b/mini-apps/templates/minikit/mini-app-full-demo-minikit/components.json new file mode 100644 index 000000000..21018f339 --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/app/globals.css", + "baseColor": "neutral", + "cssVariables": false, + "prefix": "" + }, + "aliases": { + "components": "~/components", + "utils": "~/lib/utils", + "ui": "~/components/ui", + "lib": "~/lib", + "hooks": "~/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/next.config.ts b/mini-apps/templates/minikit/mini-app-full-demo-minikit/next.config.ts new file mode 100644 index 000000000..9881415e6 --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/next.config.ts @@ -0,0 +1,28 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + devIndicators: false, + async headers() { + return [ + { + source: "/:path*", + headers: [ + { + key: "Content-Security-Policy", + value: "frame-ancestors *" + }, + { + key: "X-Frame-Options", + value: "SAMEORIGIN" + }, + { + key: "Access-Control-Allow-Origin", + value: "*" + } + ] + } + ]; + } +}; + +export default nextConfig; diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/package.json b/mini-apps/templates/minikit/mini-app-full-demo-minikit/package.json new file mode 100644 index 000000000..783ea9048 --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/package.json @@ -0,0 +1,48 @@ +{ + "name": "mini-app-full-demo", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --turbo", + "build": "next build --turbo", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@base-org/account": "^2.0.2", + "@base-org/account-ui": "^1.0.1", + "@coinbase/onchainkit": "^1.0.3", + "@farcaster/auth-kit": "^0.8.1", + "@farcaster/miniapp-core": "^0.3.8", + "@farcaster/miniapp-node": "^0.1.8", + "@farcaster/miniapp-sdk": "^0.1.9", + "@farcaster/miniapp-wagmi-connector": "^1.0.0", + "@farcaster/quick-auth": "^0.0.8", + "@radix-ui/react-label": "^2.1.7", + "@tanstack/react-query": "^5.85.9", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.542.0", + "next": "15.5.2", + "ox": "^0.9.4", + "react": "19.1.1", + "react-dom": "19.1.1", + "siwe": "^3.0.0", + "tailwind-merge": "^3.3.1", + "tailwindcss-animate": "^1.0.7", + "viem": "^2.37.1", + "wagmi": "^2.16.9" + }, + "devDependencies": { + "@types/node": "^24.3.0", + "@types/react": "^19.1.12", + "@types/react-dom": "^19.1.9", + "eruda": "^3.4.3", + "eslint": "^9", + "eslint-config-next": "15.5.2", + "postcss": "^8", + "tailwindcss": "^3.4.1", + "typescript": "^5" + }, + "packageManager": "npm@11.5.2" +} diff --git a/minikit/mini-app-route/postcss.config.mjs b/mini-apps/templates/minikit/mini-app-full-demo-minikit/postcss.config.mjs similarity index 100% rename from minikit/mini-app-route/postcss.config.mjs rename to mini-apps/templates/minikit/mini-app-full-demo-minikit/postcss.config.mjs diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/public/banner.png b/mini-apps/templates/minikit/mini-app-full-demo-minikit/public/banner.png new file mode 100644 index 000000000..29a8649e5 Binary files /dev/null and b/mini-apps/templates/minikit/mini-app-full-demo-minikit/public/banner.png differ diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/public/base-logo.png b/mini-apps/templates/minikit/mini-app-full-demo-minikit/public/base-logo.png new file mode 100644 index 000000000..14edafd7c Binary files /dev/null and b/mini-apps/templates/minikit/mini-app-full-demo-minikit/public/base-logo.png differ diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/public/base-square.png b/mini-apps/templates/minikit/mini-app-full-demo-minikit/public/base-square.png new file mode 100644 index 000000000..0ffcd7ee5 Binary files /dev/null and b/mini-apps/templates/minikit/mini-app-full-demo-minikit/public/base-square.png differ diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/public/icon.png b/mini-apps/templates/minikit/mini-app-full-demo-minikit/public/icon.png new file mode 100644 index 000000000..74fdcec6c Binary files /dev/null and b/mini-apps/templates/minikit/mini-app-full-demo-minikit/public/icon.png differ diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/.well-known/farcaster.json/route.ts b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/.well-known/farcaster.json/route.ts new file mode 100644 index 000000000..5bcfe846c --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/.well-known/farcaster.json/route.ts @@ -0,0 +1,53 @@ +import { METADATA } from "../../../lib/utils"; + +export async function GET() { + const config = { + accountAssociation: { + header: + "eyJmaWQiOjEyMTQyLCJ0eXBlIjoiY3VzdG9keSIsImtleSI6IjB4MDRlNkYxMTFlQmY2RkQyNTU3NmQ0ODA0ODA5NjI0MzVEYzNhYThEOCJ9", + payload: "eyJkb21haW4iOiJmcmFtZXMtdjItZGVtby1saWxhYy52ZXJjZWwuYXBwIn0", + signature: + "MHg5MGI1YzA0Zjc3MGY1M2I4M2I3OGQzOTMwNTNjMmJjZjUwNmE3ZThjNDViYmEwNDk2OTcwZTM1ZTQ0YzU2MGU1Nzc4Y2Y1ZTJkNDY2YzE1MWQxNGMzYmFjNzM3ZDcxZGEwZDVjYWJmMGMzZTdhYTc2YzRjMmQ5MmE5NDJhYjkyODFj", + }, + "frame": { + "version": "1", + "name": METADATA.name, + "iconUrl": METADATA.iconImageUrl, + "homeUrl": METADATA.homeUrl, + "imageUrl": METADATA.bannerImageUrl, + "splashImageUrl": METADATA.iconImageUrl, + "splashBackgroundColor": METADATA.splashBackgroundColor, + "description": METADATA.description, + "ogTitle": METADATA.name, + "ogDescription": METADATA.description, + "ogImageUrl": METADATA.bannerImageUrl, + "requiredCapabilities": [ + "actions.ready", + "actions.signIn", + "actions.openMiniApp", + "actions.openUrl", + "actions.sendToken", + "actions.viewToken", + "actions.composeCast", + "actions.viewProfile", + "actions.setPrimaryButton", + "actions.swapToken", + "actions.close", + "actions.viewCast", + "wallet.getEthereumProvider" + ], + "requiredChains": [ + "eip155:8453", + "eip155:10" + ], + "canonicalDomain": "https://fulldemo-minikit.vercel.app", + "noindex": false, + "tags": ["base", "baseapp", "miniapp", "demo", "basepay"] + }, + "baseBuilder": { + "allowedAddresses": ["0x8342A48694A74044116F330db5050a267b28dD85"], + } + }; + + return Response.json(config); +} diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/api/auth/route.ts b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/api/auth/route.ts new file mode 100644 index 000000000..fc539656a --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/api/auth/route.ts @@ -0,0 +1,82 @@ +import { Errors, createClient } from "@farcaster/quick-auth"; +import { NextRequest, NextResponse } from "next/server"; + +const client = createClient(); + +// Helper function to determine the correct domain for JWT verification +function getUrlHost(request: NextRequest): string { + // First try to get the origin from the Origin header (most reliable for CORS requests) + const origin = request.headers.get("origin"); + if (origin) { + try { + const url = new URL(origin); + return url.host; + } catch (error) { + console.warn("Invalid origin header:", origin, error); + } + } + + // Fallback to Host header + const host = request.headers.get("host"); + if (host) { + return host; + } + + // Final fallback to environment variables (your original logic) + let urlValue: string; + if (process.env.VERCEL_ENV === "production") { + urlValue = process.env.NEXT_PUBLIC_URL!; + } else if (process.env.VERCEL_URL) { + urlValue = `https://${process.env.VERCEL_URL}`; + } else { + urlValue = "http://localhost:3000"; + } + + const url = new URL(urlValue); + return url.host; +} + +export async function GET(request: NextRequest) { + // Because we're fetching this endpoint via `sdk.quickAuth.fetch`, + // if we're in a mini app, the request will include the necessary `Authorization` header. + const authorization = request.headers.get("Authorization"); + + // Here we ensure that we have a valid token. + if (!authorization || !authorization.startsWith("Bearer ")) { + return NextResponse.json({ message: "Missing token" }, { status: 401 }); + } + + try { + // Now we verify the token. `domain` must match the domain of the request. + // In our case, we're using the `getUrlHost` function to get the domain of the request + // based on the Vercel environment. This will vary depending on your hosting provider. + const payload = await client.verifyJwt({ + token: authorization.split(" ")[1] as string, + domain: getUrlHost(request), + }); + + console.log("payload", payload); + + // If the token was valid, `payload.sub` will be the user's Farcaster ID. + const userFid = payload.sub; + + // Return user information for your waitlist application + return NextResponse.json({ + success: true, + user: { + fid: userFid, + issuedAt: payload.iat, + expiresAt: payload.exp, + }, + }); + + } catch (e) { + if (e instanceof Errors.InvalidTokenError) { + return NextResponse.json({ message: "Invalid token" }, { status: 401 }); + } + if (e instanceof Error) { + return NextResponse.json({ message: e.message }, { status: 500 }); + } + throw e; + } +} diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/app.tsx b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/app.tsx new file mode 100644 index 000000000..6ab52495c --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/app.tsx @@ -0,0 +1,42 @@ +"use client"; + +import dynamic from "next/dynamic"; +import { useMiniKit, useQuickAuth } from '@coinbase/onchainkit/minikit'; +import { useEffect } from 'react'; + +const Demo = dynamic(() => import("~/components/Demo"), { + ssr: false, +}); + +interface AuthResponse { + success: boolean; + user?: { + fid: number; // FID is the unique identifier for the user + issuedAt?: number; + expiresAt?: number; + }; + message?: string; // Error messages come as 'message' not 'error' +} + +export default function App() { + const { isFrameReady, setFrameReady, context } = useMiniKit(); + + // Initialize the miniapp + useEffect(() => { + if (!isFrameReady) { + setFrameReady(); + } + }, [setFrameReady, isFrameReady]); + + // If you need to verify the user's identity, you can use the useQuickAuth hook. + // This hook will verify the user's signature and return the user's FID. You can update + // this to meet your needs. See the /app/api/auth/route.ts file for more details. + // Note: If you don't need to verify the user's identity, you can get their FID and other user data + // via `context.user.fid`. + const { data: authData, isLoading: isAuthLoading, error: authError } = useQuickAuth( + "/api/auth", + { method: "GET" } + ); + + return ; +} diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/favicon.ico b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/favicon.ico new file mode 100644 index 000000000..9ac91c4c7 Binary files /dev/null and b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/favicon.ico differ diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/globals.css b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/globals.css new file mode 100644 index 000000000..af0a0b4f1 --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/globals.css @@ -0,0 +1,82 @@ +@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap'); + +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + /* Base Docs inspired color palette */ + --background: #ffffff; + --foreground: #0f172a; + --card: #ffffff; + --card-foreground: #0f172a; + --popover: #ffffff; + --popover-foreground: #0f172a; + --primary: #0052ff; + --primary-foreground: #ffffff; + --secondary: #f1f5f9; + --secondary-foreground: #0f172a; + --muted: #f8fafc; + --muted-foreground: #64748b; + --accent: #f1f5f9; + --accent-foreground: #0f172a; + --destructive: #ef4444; + --destructive-foreground: #ffffff; + --border: #e2e8f0; + --input: #e2e8f0; + --ring: #0052ff; + --radius: 0.5rem; +} + +@media (prefers-color-scheme: dark) { + :root { + /* Keep white background even in dark mode */ + --background: #ffffff; + --foreground: #0f172a; + --card: #ffffff; + --card-foreground: #0f172a; + --popover: #ffffff; + --popover-foreground: #0f172a; + --primary: #0052ff; + --primary-foreground: #ffffff; + --secondary: #f1f5f9; + --secondary-foreground: #0f172a; + --muted: #f8fafc; + --muted-foreground: #64748b; + --accent: #f1f5f9; + --accent-foreground: #0f172a; + --destructive: #ef4444; + --destructive-foreground: #ffffff; + --border: #e2e8f0; + --input: #e2e8f0; + --ring: #0052ff; + } +} + +body { + color: var(--foreground); + background: var(--background); + font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + font-feature-settings: 'rlig' 1, 'calt' 1; +} + +* { + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE and Edge */ +} + +*::-webkit-scrollbar { + display: none; +} + +/* Smooth scrolling for horizontal navigation */ +.overflow-x-auto { + scroll-behavior: smooth; + -webkit-overflow-scrolling: touch; +} + +@layer base { + :root { + --radius: 0.5rem; + } +} diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/layout.tsx b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/layout.tsx new file mode 100644 index 000000000..1b794bbc6 --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/layout.tsx @@ -0,0 +1,31 @@ +import type { Metadata } from "next"; + + +import "~/app/globals.css"; +import { Providers } from "~/app/providers"; +import { METADATA } from "~/lib/utils"; + +export const metadata: Metadata = { + title: METADATA.name, + openGraph: { + title: METADATA.name, + description: METADATA.description, + images: [METADATA.bannerImageUrl], + url: METADATA.homeUrl, + siteName: METADATA.name + }, +}; + +export default async function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/page.tsx b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/page.tsx new file mode 100644 index 000000000..ac03b744b --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/page.tsx @@ -0,0 +1,42 @@ +import { Metadata } from "next"; +import App from "./app"; +import { METADATA } from "~/lib/utils"; + +const frame = { + version: "next", + imageUrl: METADATA.bannerImageUrl, + button: { + title: "Open", + action: { + type: "launch_frame", + name: METADATA.name, + url: METADATA.homeUrl, + splashImageUrl: METADATA.iconImageUrl, + splashBackgroundColor: METADATA.splashBackgroundColor + } + } +}; + +export const revalidate = 300; + +export async function generateMetadata(): Promise { + return { + title: METADATA.name, + openGraph: { + title: METADATA.name, + description: METADATA.description, + images: [METADATA.bannerImageUrl], + url: METADATA.homeUrl, + siteName: METADATA.name + }, + other: { + "fc:frame": JSON.stringify(frame), + "fc:miniapp": JSON.stringify(frame), + } + }; +} + +export default function Home() { + + return (); +} diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/providers.tsx b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/providers.tsx new file mode 100644 index 000000000..1df44c639 --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/app/providers.tsx @@ -0,0 +1,22 @@ +"use client"; + +import dynamic from "next/dynamic"; + +import Provider from '../components/providers/WagmiProvider'; +import { MiniKitProvider } from '@coinbase/onchainkit/minikit'; +import { ReactNode } from 'react'; + +export function Providers({ children }: { children: ReactNode }) { + return ( + + + + {children} + + + ); +} \ No newline at end of file diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/components/Demo.tsx b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/components/Demo.tsx new file mode 100644 index 000000000..213e336cb --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/components/Demo.tsx @@ -0,0 +1,418 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +"use client"; + +import React, { useState, useEffect } from "react"; +import { useFrameContext } from "~/components/providers/FrameProvider"; +import { sdk } from "@farcaster/miniapp-sdk"; +import { useAccount } from "wagmi"; +import { SignInAction } from "~/components/actions/signin"; +import { QuickAuthAction } from "~/components/actions/quick-auth"; +import { OpenMiniAppAction } from "~/components/actions/open-miniapp"; +import { OpenUrlAction } from "~/components/actions/openurl"; +import { ViewProfileAction } from "~/components/actions/view-profile"; +import { ViewTokenAction } from "~/components/actions/view-token"; +import { SwapTokenAction } from "~/components/actions/swap-token"; +import { SendTokenAction } from "~/components/actions/send-token"; +import { ViewCastAction } from "~/components/actions/view-cast"; +import { ComposeCastAction } from "~/components/actions/compose-cast"; +import { SetPrimaryButtonAction } from "~/components/actions/set-primary-button"; +import { AddMiniAppAction } from "~/components/actions/add-miniapp"; +import { CloseMiniAppAction } from "~/components/actions/close-miniapp"; +import { WalletConnect, SignMessage, SignSiweMessage, SendEth, SignTypedData, SwitchChain, SendTransaction } from "~/components/wallet/WalletActions"; +import { BasePay } from "~/components/wallet/BasePay"; +import { GetChainsAction } from "~/components/actions/get-chains"; +import { GetCapabilitiesAction } from "~/components/actions/get-capabilities"; +import { RequestCameraMicrophoneAction } from "~/components/actions/request-camera-microphone"; +import { HapticsAction } from "~/components/actions/haptics"; +import { useMiniKit } from "@coinbase/onchainkit/minikit"; + + +type TabType = "actions" | "context" | "wallet"; +type ActionPageType = "list" | "signin" | "quickauth" | "openurl" | "openminiapp" | "farcaster" | "viewprofile" | "viewtoken" | "swaptoken" | "sendtoken" | "viewcast" | "composecast" | "setprimarybutton" | "addminiapp" | "closeminiapp" | "runtime" | "requestcameramicrophone" | "haptics"; +type WalletPageType = "list" | "basepay" | "wallet"; + +interface ActionDefinition { + id: ActionPageType; + name: string; + description: string; + component: React.ComponentType; +} + +interface WalletActionDefinition { + id: WalletPageType; + name: string; + description: string; + component: React.ComponentType; +} + +export default function Demo() { + const frameContext = useFrameContext(); + const { isConnected } = useAccount(); + const [activeTab, setActiveTab] = useState("actions"); + const [currentActionPage, setCurrentActionPage] = useState("list"); + const [currentWalletPage, setCurrentWalletPage] = useState("list"); + const [isFullObjectOpen, setIsFullObjectOpen] = useState(false); + const [capabilities, setCapabilities] = useState(null); + + const toggleFullObject = (): void => { + setIsFullObjectOpen(prev => !prev); + }; + + + // Check capabilities on mount + useEffect(() => { + const getCapabilities = async () => { + try { + const caps = await sdk.getCapabilities(); + setCapabilities(caps); + } catch (error) { + console.error('Failed to get capabilities:', error); + } + }; + getCapabilities(); + }, []); + + // Camera/microphone feature is available if the action is supported in capabilities + const hasCamMicFeature = Boolean( + capabilities?.includes('actions.requestCameraAndMicrophoneAccess') + ); + + const actionDefinitions: ActionDefinition[] = [ + { id: "signin", name: "Sign In", description: "Authenticate with Farcaster", component: SignInAction }, + { id: "quickauth", name: "Quick Auth", description: "Quick authentication flow", component: QuickAuthAction }, + { id: "openminiapp", name: "Open Mini App", description: "Launch another mini app", component: OpenMiniAppAction }, + { id: "openurl", name: "Open URL", description: "Open external URLs", component: OpenUrlAction }, + { id: "viewprofile", name: "View Profile", description: "View user profiles", component: ViewProfileAction }, + { id: "viewtoken", name: "View Token", description: "Display token information", component: ViewTokenAction }, + { id: "swaptoken", name: "Swap Token", description: "Token swapping functionality", component: SwapTokenAction }, + { id: "sendtoken", name: "Send Token", description: "Send tokens to users", component: SendTokenAction }, + { id: "viewcast", name: "View Cast", description: "Display Farcaster casts", component: ViewCastAction }, + { id: "composecast", name: "Compose Cast", description: "Create new casts", component: ComposeCastAction }, + { id: "setprimarybutton", name: "Set Primary Button", description: "Configure primary button", component: SetPrimaryButtonAction }, + { id: "haptics", name: "Haptics", description: "Trigger haptic feedback", component: HapticsAction }, + ...(hasCamMicFeature + ? [{ id: "requestcameramicrophone", name: "Camera & Microphone", description: "Request access and demo camera/mic", component: RequestCameraMicrophoneAction } satisfies ActionDefinition] + : []), + { id: "addminiapp", name: "Add Mini App", description: "Add this Mini App to your Farcaster client", component: AddMiniAppAction }, + { id: "closeminiapp", name: "Close Mini App", description: "Close the current Mini App", component: CloseMiniAppAction }, + { id: "runtime", name: "Runtime Detection", description: "Get chains and capabilities", component: () => null }, + ]; + + const WalletActionsComponent = () => ( +
+ + + + + + +
+ ); + + const walletActionDefinitions: WalletActionDefinition[] = [ + { id: "basepay", name: "Base Pay", description: "Debug Base Pay", component: BasePay }, + { id: "wallet", name: "Wallet", description: "Debug wallet interactions", component: WalletActionsComponent }, + ]; + + const handleTabChange = async (tab: TabType) => { + if (capabilities?.includes('haptics.selectionChanged')) { + await sdk.haptics.selectionChanged(); + } + + setActiveTab(tab); + if (tab === "actions") { + setCurrentActionPage("list"); + } else if (tab === "wallet") { + setCurrentWalletPage("list"); + } + }; + + const handleActionSelect = async (actionId: ActionPageType) => { + // Add haptic feedback for action selection + try { + await sdk.haptics.selectionChanged(); + } catch (error) { + console.log('Haptics not supported:', error); + } + + setCurrentActionPage(actionId); + }; + + const handleBackToActionList = async () => { + // Add haptic feedback for back navigation + try { + await sdk.haptics.impactOccurred('light'); + } catch (error) { + console.log('Haptics not supported:', error); + } + + setCurrentActionPage("list"); + }; + + const handleWalletActionSelect = async (walletActionId: WalletPageType) => { + // Add haptic feedback for wallet action selection + try { + await sdk.haptics.selectionChanged(); + } catch (error) { + console.log('Haptics not supported:', error); + } + + setCurrentWalletPage(walletActionId); + }; + + const handleBackToWalletList = async () => { + // Add haptic feedback for back navigation + try { + await sdk.haptics.impactOccurred('light'); + } catch (error) { + console.log('Haptics not supported:', error); + } + + setCurrentWalletPage("list"); + }; + + return ( +
+
+
+ {/* eslint-disable-next-line @next/next/no-img-element */} + Base + + {/* Profile picture - only show if context data is available */} + {frameContext?.context && (frameContext.context as any)?.user?.pfpUrl && ( + + )} +
+ +
+
+ + + + +
+
+ + {activeTab === "actions" && ( +
+ {currentActionPage === "list" ? ( +
+ {actionDefinitions.map((action) => ( + + ))} +
+ ) : ( +
+
+ +

+ {actionDefinitions.find(a => a.id === currentActionPage)?.name} +

+
+
+ {currentActionPage === "runtime" ? ( +
+ + +
+ ) : ( + (() => { + const ActionComponent = actionDefinitions.find(a => a.id === currentActionPage)?.component; + return ActionComponent ? : null; + })() + )} +
+
+ )} +
+ )} + + {activeTab === "context" && ( +
+
+ + + {isFullObjectOpen && ( +
+
+                    {frameContext?.context ? JSON.stringify(frameContext.context, null, 2) : 'null'}
+                  
+
+ )} +
+
+

isInMiniApp

+
+ + {frameContext ? (frameContext.isInMiniApp ?? false).toString() : 'false'} + +
+
+ + {frameContext?.context && ( +
+ {Object.entries(frameContext.context as Record).map(([key, value]) => ( +
+

{key}

+
+
+                        {typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value)}
+                      
+
+
+ ))} +
+ )} + + {!frameContext?.context && ( +
+ + ⚠️ No context data available + +
+ )} +
+ )} + + {activeTab === "wallet" && ( +
+ {!isConnected ? ( +
+
+

Connect Your Wallet

+

+ Connect your wallet to access all features +

+
+ +
+ ) : ( +
+ {currentWalletPage === "list" ? ( +
+ {/* Connection Status */} + + + {/* Wallet Action Cells */} +
+ {walletActionDefinitions.map((walletAction) => ( + + ))} +
+
+ ) : ( +
+
+ +

+ {walletActionDefinitions.find(a => a.id === currentWalletPage)?.name} +

+
+
+ {(() => { + const walletAction = walletActionDefinitions.find(a => a.id === currentWalletPage); + if (walletAction) { + const Component = walletAction.component; + return ; + } + return null; + })()} +
+
+ )} +
+ )} +
+ )} + + +
+
+ ); +} diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/components/actions/add-miniapp.tsx b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/components/actions/add-miniapp.tsx new file mode 100644 index 000000000..bcad3d3a7 --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/components/actions/add-miniapp.tsx @@ -0,0 +1,62 @@ +"use client"; + +import { useCallback, useState } from "react"; +import { Button } from "~/components/ui/Button"; +import { useAddFrame } from "@coinbase/onchainkit/minikit"; + +export function AddMiniAppAction() { + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [status, setStatus] = useState(null); + + // Use MiniKit's useAddFrame hook + const addFrame = useAddFrame(); + + const handleAddFrame = useCallback(async (): Promise => { + setLoading(true); + setError(null); + setStatus(null); + + try { + const notificationDetails = await addFrame(); + + if (notificationDetails) { + setStatus("Mini App added successfully! Notifications enabled."); + } else { + setError("Failed to add Mini App - user may have cancelled or an error occurred"); + } + } catch (err) { + setError(`Failed to add Mini App: ${err instanceof Error ? err.message : 'Unknown error'}`); + } finally { + setLoading(false); + } + }, [addFrame]); + + return ( +
+
+
useAddFrame()
+
+ + {error && ( +
+ {error} +
+ )} + + {status && ( +
+ {status} +
+ )} + + +
+ ); +} diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/components/actions/close-miniapp.tsx b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/components/actions/close-miniapp.tsx new file mode 100644 index 000000000..2211e2005 --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/components/actions/close-miniapp.tsx @@ -0,0 +1,23 @@ +"use client"; + +import { useCallback } from "react"; +import { Button } from "~/components/ui/Button"; +import { useClose } from "@coinbase/onchainkit/minikit"; + +export function CloseMiniAppAction() { + // Call the hook at component level + const close = useClose(); + + const handleClose = useCallback((): void => { + close(); + }, [close]); + + return ( +
+
+
useClose()
+
+ +
+ ); +} \ No newline at end of file diff --git a/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/components/actions/compose-cast.tsx b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/components/actions/compose-cast.tsx new file mode 100644 index 000000000..1b69ffe81 --- /dev/null +++ b/mini-apps/templates/minikit/mini-app-full-demo-minikit/src/components/actions/compose-cast.tsx @@ -0,0 +1,165 @@ +"use client"; + +import { useState, useCallback } from "react"; +import { Button } from "~/components/ui/Button"; +import { useComposeCast } from "@coinbase/onchainkit/minikit"; + +export function ComposeCastAction() { + // Use MiniKit's useComposeCast hook (React Query powered) + const { composeCast, isPending, error, data } = useComposeCast(); + + // Form state + const [text, setText] = useState("I just learned how to compose a cast"); + const [embed1, setEmbed1] = useState("https://miniapps.farcaster.xyz/docs/sdk/actions/compose-cast"); + const [embed2, setEmbed2] = useState(""); + const [channelKey, setChannelKey] = useState(""); + const [close, setClose] = useState(false); + const [parentHash, setParentHash] = useState(""); + + const handleComposeCast = useCallback((): void => { + // Build embeds array - SDK expects [] | [string] | [string, string] + let embeds: [] | [string] | [string, string] | undefined; + const embed1Trimmed = embed1.trim(); + const embed2Trimmed = embed2.trim(); + + if (embed1Trimmed && embed2Trimmed) { + embeds = [embed1Trimmed, embed2Trimmed]; + } else if (embed1Trimmed) { + embeds = [embed1Trimmed]; + } else if (embed2Trimmed) { + embeds = [embed2Trimmed]; + } else { + embeds = undefined; + } + + // Build parameters object + const params = { + ...(text.trim() && { text: text.trim() }), + ...(embeds && { embeds }), + ...(channelKey.trim() && { channelKey: channelKey.trim() }), + ...(parentHash.trim() && { parent: { type: 'cast' as const, hash: parentHash.trim() } }) + }; + + // Note: MiniKit's current type system doesn't support close: true properly in this hook + // The close functionality would need to be handled at the app level + composeCast(params); + }, [composeCast, text, embed1, embed2, channelKey, parentHash]); + + return ( +
+
+
useComposeCast()
+
+ + {/* Form Fields */} +
+
+ +