mirror of
https://github.com/neynarxyz/create-farcaster-mini-app.git
synced 2025-11-16 08:08:56 -05:00
feat: add support for back button and haptics
This commit is contained in:
parent
f97a697f88
commit
e349724267
@ -347,7 +347,7 @@ export async function init() {
|
|||||||
"@farcaster/frame-sdk": ">=0.0.31 <1.0.0",
|
"@farcaster/frame-sdk": ">=0.0.31 <1.0.0",
|
||||||
"@farcaster/frame-wagmi-connector": ">=0.0.19 <1.0.0",
|
"@farcaster/frame-wagmi-connector": ">=0.0.19 <1.0.0",
|
||||||
"@farcaster/mini-app-solana": ">=0.0.17 <1.0.0",
|
"@farcaster/mini-app-solana": ">=0.0.17 <1.0.0",
|
||||||
"@neynar/react": "^1.2.2",
|
"@neynar/react": "^1.2.5",
|
||||||
"@radix-ui/react-label": "^2.1.1",
|
"@radix-ui/react-label": "^2.1.1",
|
||||||
"@solana/wallet-adapter-react": "^0.15.38",
|
"@solana/wallet-adapter-react": "^0.15.38",
|
||||||
"@tanstack/react-query": "^5.61.0",
|
"@tanstack/react-query": "^5.61.0",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@neynar/create-farcaster-mini-app",
|
"name": "@neynar/create-farcaster-mini-app",
|
||||||
"version": "1.4.0",
|
"version": "1.4.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": false,
|
"private": false,
|
||||||
"access": "public",
|
"access": "public",
|
||||||
|
|||||||
@ -18,7 +18,7 @@ export function Providers({ session, children }: { session: Session | null, chil
|
|||||||
return (
|
return (
|
||||||
<SessionProvider session={session}>
|
<SessionProvider session={session}>
|
||||||
<WagmiProvider>
|
<WagmiProvider>
|
||||||
<MiniAppProvider analyticsEnabled={true}>
|
<MiniAppProvider analyticsEnabled={true} backButtonEnabled={true}>
|
||||||
<SafeFarcasterSolanaProvider endpoint={solanaEndpoint}>
|
<SafeFarcasterSolanaProvider endpoint={solanaEndpoint}>
|
||||||
{children}
|
{children}
|
||||||
</SafeFarcasterSolanaProvider>
|
</SafeFarcasterSolanaProvider>
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { useCallback, useEffect, useMemo, useState } from "react";
|
|||||||
import { signIn, signOut, getCsrfToken } from "next-auth/react";
|
import { signIn, signOut, getCsrfToken } from "next-auth/react";
|
||||||
import sdk, {
|
import sdk, {
|
||||||
SignIn as SignInCore,
|
SignIn as SignInCore,
|
||||||
|
type Haptics,
|
||||||
} from "@farcaster/frame-sdk";
|
} from "@farcaster/frame-sdk";
|
||||||
import {
|
import {
|
||||||
useAccount,
|
useAccount,
|
||||||
@ -52,13 +53,17 @@ export default function Demo(
|
|||||||
added,
|
added,
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
actions,
|
actions,
|
||||||
|
setInitialTab,
|
||||||
|
setActiveTab,
|
||||||
|
currentTab,
|
||||||
|
haptics,
|
||||||
} = useMiniApp();
|
} = useMiniApp();
|
||||||
const [isContextOpen, setIsContextOpen] = useState(false);
|
const [isContextOpen, setIsContextOpen] = useState(false);
|
||||||
const [activeTab, setActiveTab] = useState<Tab>('home');
|
|
||||||
const [txHash, setTxHash] = useState<string | null>(null);
|
const [txHash, setTxHash] = useState<string | null>(null);
|
||||||
const [sendNotificationResult, setSendNotificationResult] = useState("");
|
const [sendNotificationResult, setSendNotificationResult] = useState("");
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
const [neynarUser, setNeynarUser] = useState<NeynarUser | null>(null);
|
const [neynarUser, setNeynarUser] = useState<NeynarUser | null>(null);
|
||||||
|
const [hapticIntensity, setHapticIntensity] = useState<Haptics.ImpactOccurredType>('medium');
|
||||||
|
|
||||||
const { address, isConnected } = useAccount();
|
const { address, isConnected } = useAccount();
|
||||||
const chainId = useChainId();
|
const chainId = useChainId();
|
||||||
@ -66,6 +71,13 @@ export default function Demo(
|
|||||||
const solanaWallet = useSolanaWallet();
|
const solanaWallet = useSolanaWallet();
|
||||||
const { publicKey: solanaPublicKey } = solanaWallet;
|
const { publicKey: solanaPublicKey } = solanaWallet;
|
||||||
|
|
||||||
|
// Set initial tab to home on page load
|
||||||
|
useEffect(() => {
|
||||||
|
if (isSDKLoaded) {
|
||||||
|
setInitialTab('home');
|
||||||
|
}
|
||||||
|
}, [isSDKLoaded, setInitialTab]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("isSDKLoaded", isSDKLoaded);
|
console.log("isSDKLoaded", isSDKLoaded);
|
||||||
console.log("context", context);
|
console.log("context", context);
|
||||||
@ -226,7 +238,7 @@ export default function Demo(
|
|||||||
|
|
||||||
<h1 className="text-2xl font-bold text-center mb-4">{title}</h1>
|
<h1 className="text-2xl font-bold text-center mb-4">{title}</h1>
|
||||||
|
|
||||||
{activeTab === 'home' && (
|
{currentTab === 'home' && (
|
||||||
<div className="flex items-center justify-center h-[calc(100vh-200px)] px-6">
|
<div className="flex items-center justify-center h-[calc(100vh-200px)] px-6">
|
||||||
<div className="text-center w-full max-w-md mx-auto">
|
<div className="text-center w-full max-w-md mx-auto">
|
||||||
<p className="text-lg mb-2">Put your content here!</p>
|
<p className="text-lg mb-2">Put your content here!</p>
|
||||||
@ -235,7 +247,7 @@ export default function Demo(
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === 'actions' && (
|
{currentTab === 'actions' && (
|
||||||
<div className="space-y-3 px-6 w-full max-w-md mx-auto">
|
<div className="space-y-3 px-6 w-full max-w-md mx-auto">
|
||||||
<ShareButton
|
<ShareButton
|
||||||
buttonText="Share Mini App"
|
buttonText="Share Mini App"
|
||||||
@ -251,8 +263,6 @@ export default function Demo(
|
|||||||
|
|
||||||
<Button onClick={() => actions.openUrl("https://www.youtube.com/watch?v=dQw4w9WgXcQ")} className="w-full">Open Link</Button>
|
<Button onClick={() => actions.openUrl("https://www.youtube.com/watch?v=dQw4w9WgXcQ")} className="w-full">Open Link</Button>
|
||||||
|
|
||||||
<Button onClick={actions.close} className="w-full">Close Mini App</Button>
|
|
||||||
|
|
||||||
<Button onClick={actions.addMiniApp} disabled={added} className="w-full">
|
<Button onClick={actions.addMiniApp} disabled={added} className="w-full">
|
||||||
Add Mini App to Client
|
Add Mini App to Client
|
||||||
</Button>
|
</Button>
|
||||||
@ -280,10 +290,39 @@ export default function Demo(
|
|||||||
>
|
>
|
||||||
{copied ? "Copied!" : "Copy share URL"}
|
{copied ? "Copied!" : "Copy share URL"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
|
Haptic Intensity
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={hapticIntensity}
|
||||||
|
onChange={(e) => setHapticIntensity(e.target.value as typeof hapticIntensity)}
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-purple-500"
|
||||||
|
>
|
||||||
|
<option value="light">Light</option>
|
||||||
|
<option value="medium">Medium</option>
|
||||||
|
<option value="heavy">Heavy</option>
|
||||||
|
<option value="soft">Soft</option>
|
||||||
|
<option value="rigid">Rigid</option>
|
||||||
|
</select>
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
try {
|
||||||
|
await haptics.impactOccurred(hapticIntensity);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Haptic feedback failed:', error);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
Trigger Haptic Feedback
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === 'context' && (
|
{currentTab === 'context' && (
|
||||||
<div className="mx-6">
|
<div className="mx-6">
|
||||||
<h2 className="text-lg font-semibold mb-2">Context</h2>
|
<h2 className="text-lg font-semibold mb-2">Context</h2>
|
||||||
<div className="p-4 bg-gray-100 dark:bg-gray-800 rounded-lg">
|
<div className="p-4 bg-gray-100 dark:bg-gray-800 rounded-lg">
|
||||||
@ -294,7 +333,7 @@ export default function Demo(
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === 'wallet' && USE_WALLET && (
|
{currentTab === 'wallet' && USE_WALLET && (
|
||||||
<div className="space-y-3 px-6 w-full max-w-md mx-auto">
|
<div className="space-y-3 px-6 w-full max-w-md mx-auto">
|
||||||
{address && (
|
{address && (
|
||||||
<div className="text-xs w-full">
|
<div className="text-xs w-full">
|
||||||
@ -389,7 +428,7 @@ export default function Demo(
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Footer activeTab={activeTab} setActiveTab={setActiveTab} showWallet={USE_WALLET} />
|
<Footer activeTab={currentTab as Tab} setActiveTab={setActiveTab} showWallet={USE_WALLET} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -97,7 +97,7 @@ export function ShareButton({ buttonText, cast, className = '', isLoading = fals
|
|||||||
parent: cast.parent,
|
parent: cast.parent,
|
||||||
channelKey: cast.channelKey,
|
channelKey: cast.channelKey,
|
||||||
close: cast.close,
|
close: cast.close,
|
||||||
}, 'share-button');
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to share:', error);
|
console.error('Failed to share:', error);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user