mirror of
https://github.com/neynarxyz/create-farcaster-mini-app.git
synced 2025-11-16 08:08:56 -05:00
feat: move frame code to FrameProvider.tsx
This commit is contained in:
parent
bbf4978c6e
commit
80520c34a9
@ -3,7 +3,7 @@
|
|||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import type { Session } from "next-auth"
|
import type { Session } from "next-auth"
|
||||||
import { SessionProvider } from "next-auth/react"
|
import { SessionProvider } from "next-auth/react"
|
||||||
|
import { FrameProvider } from "~/components/providers/FrameProvider";
|
||||||
|
|
||||||
const WagmiProvider = dynamic(
|
const WagmiProvider = dynamic(
|
||||||
() => import("~/components/providers/WagmiProvider"),
|
() => import("~/components/providers/WagmiProvider"),
|
||||||
@ -16,7 +16,9 @@ export function Providers({ session, children }: { session: Session | null, chil
|
|||||||
return (
|
return (
|
||||||
<SessionProvider session={session}>
|
<SessionProvider session={session}>
|
||||||
<WagmiProvider>
|
<WagmiProvider>
|
||||||
{children}
|
<FrameProvider>
|
||||||
|
{children}
|
||||||
|
</FrameProvider>
|
||||||
</WagmiProvider>
|
</WagmiProvider>
|
||||||
</SessionProvider>
|
</SessionProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,13 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useCallback, useState, useMemo } from "react";
|
import { useCallback, useMemo, useState } from "react";
|
||||||
import { Input } from "../components/ui/input";
|
import { Input } from "../components/ui/input";
|
||||||
import { signIn, signOut, getCsrfToken } from "next-auth/react";
|
import { signIn, signOut, getCsrfToken } from "next-auth/react";
|
||||||
import sdk, {
|
import sdk, {
|
||||||
AddFrame,
|
|
||||||
FrameNotificationDetails,
|
|
||||||
SignIn as SignInCore,
|
SignIn as SignInCore,
|
||||||
type Context,
|
|
||||||
} from "@farcaster/frame-sdk";
|
} from "@farcaster/frame-sdk";
|
||||||
import {
|
import {
|
||||||
useAccount,
|
useAccount,
|
||||||
@ -27,30 +24,18 @@ import { truncateAddress } from "~/lib/truncateAddress";
|
|||||||
import { base, degen, mainnet, optimism, unichain } from "wagmi/chains";
|
import { base, degen, mainnet, optimism, unichain } from "wagmi/chains";
|
||||||
import { BaseError, UserRejectedRequestError } from "viem";
|
import { BaseError, UserRejectedRequestError } from "viem";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { createStore } from "mipd";
|
|
||||||
import { Label } from "~/components/ui/label";
|
import { Label } from "~/components/ui/label";
|
||||||
|
import { useFrame } from "~/components/providers/FrameProvider";
|
||||||
|
|
||||||
export default function Demo(
|
export default function Demo(
|
||||||
{ title }: { title?: string } = { title: "Frames v2 Demo" }
|
{ title }: { title?: string } = { title: "Frames v2 Demo" }
|
||||||
) {
|
) {
|
||||||
const [isSDKLoaded, setIsSDKLoaded] = useState(false);
|
const { isSDKLoaded, context, added, notificationDetails, lastEvent, addFrame, addFrameResult } = useFrame();
|
||||||
const [context, setContext] = useState<Context.FrameContext>();
|
|
||||||
const [isContextOpen, setIsContextOpen] = useState(false);
|
const [isContextOpen, setIsContextOpen] = useState(false);
|
||||||
const [txHash, setTxHash] = useState<string | null>(null);
|
const [txHash, setTxHash] = useState<string | null>(null);
|
||||||
|
|
||||||
const [added, setAdded] = useState(false);
|
|
||||||
const [notificationDetails, setNotificationDetails] =
|
|
||||||
useState<FrameNotificationDetails | null>(null);
|
|
||||||
|
|
||||||
const [lastEvent, setLastEvent] = useState("");
|
|
||||||
|
|
||||||
const [addFrameResult, setAddFrameResult] = useState("");
|
|
||||||
const [sendNotificationResult, setSendNotificationResult] = useState("");
|
const [sendNotificationResult, setSendNotificationResult] = useState("");
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setNotificationDetails(context?.client.notificationDetails ?? null);
|
|
||||||
}, [context]);
|
|
||||||
|
|
||||||
const { address, isConnected } = useAccount();
|
const { address, isConnected } = useAccount();
|
||||||
const chainId = useChainId();
|
const chainId = useChainId();
|
||||||
|
|
||||||
@ -101,68 +86,6 @@ export default function Demo(
|
|||||||
switchChain({ chainId: nextChain.id });
|
switchChain({ chainId: nextChain.id });
|
||||||
}, [switchChain, nextChain.id]);
|
}, [switchChain, nextChain.id]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const load = async () => {
|
|
||||||
const context = await sdk.context;
|
|
||||||
setContext(context);
|
|
||||||
setAdded(context.client.added);
|
|
||||||
|
|
||||||
sdk.on("frameAdded", ({ notificationDetails }) => {
|
|
||||||
setLastEvent(
|
|
||||||
`frameAdded${!!notificationDetails ? ", notifications enabled" : ""}`
|
|
||||||
);
|
|
||||||
|
|
||||||
setAdded(true);
|
|
||||||
if (notificationDetails) {
|
|
||||||
setNotificationDetails(notificationDetails);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
sdk.on("frameAddRejected", ({ reason }) => {
|
|
||||||
setLastEvent(`frameAddRejected, reason ${reason}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
sdk.on("frameRemoved", () => {
|
|
||||||
setLastEvent("frameRemoved");
|
|
||||||
setAdded(false);
|
|
||||||
setNotificationDetails(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
sdk.on("notificationsEnabled", ({ notificationDetails }) => {
|
|
||||||
setLastEvent("notificationsEnabled");
|
|
||||||
setNotificationDetails(notificationDetails);
|
|
||||||
});
|
|
||||||
sdk.on("notificationsDisabled", () => {
|
|
||||||
setLastEvent("notificationsDisabled");
|
|
||||||
setNotificationDetails(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
sdk.on("primaryButtonClicked", () => {
|
|
||||||
console.log("primaryButtonClicked");
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("Calling ready");
|
|
||||||
sdk.actions.ready({});
|
|
||||||
|
|
||||||
// Set up a MIPD Store, and request Providers.
|
|
||||||
const store = createStore();
|
|
||||||
|
|
||||||
// Subscribe to the MIPD Store.
|
|
||||||
store.subscribe((providerDetails) => {
|
|
||||||
console.log("PROVIDER DETAILS", providerDetails);
|
|
||||||
// => [EIP6963ProviderDetail, EIP6963ProviderDetail, ...]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
if (sdk && !isSDKLoaded) {
|
|
||||||
console.log("Calling load");
|
|
||||||
setIsSDKLoaded(true);
|
|
||||||
load();
|
|
||||||
return () => {
|
|
||||||
sdk.removeAllListeners();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}, [isSDKLoaded]);
|
|
||||||
|
|
||||||
const openUrl = useCallback(() => {
|
const openUrl = useCallback(() => {
|
||||||
sdk.actions.openUrl("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
|
sdk.actions.openUrl("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
|
||||||
}, []);
|
}, []);
|
||||||
@ -175,33 +98,6 @@ export default function Demo(
|
|||||||
sdk.actions.close();
|
sdk.actions.close();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addFrame = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
setNotificationDetails(null);
|
|
||||||
|
|
||||||
const result = await sdk.actions.addFrame();
|
|
||||||
|
|
||||||
if (result.notificationDetails) {
|
|
||||||
setNotificationDetails(result.notificationDetails);
|
|
||||||
}
|
|
||||||
setAddFrameResult(
|
|
||||||
result.notificationDetails
|
|
||||||
? `Added, got notificaton token ${result.notificationDetails.token} and url ${result.notificationDetails.url}`
|
|
||||||
: "Added, got no notification details"
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof AddFrame.RejectedByUser) {
|
|
||||||
setAddFrameResult(`Not added: ${error.message}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error instanceof AddFrame.InvalidDomainManifest) {
|
|
||||||
setAddFrameResult(`Not added: ${error.message}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
setAddFrameResult(`Error: ${error}`);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const sendNotification = useCallback(async () => {
|
const sendNotification = useCallback(async () => {
|
||||||
setSendNotificationResult("");
|
setSendNotificationResult("");
|
||||||
if (!notificationDetails || !context) {
|
if (!notificationDetails || !context) {
|
||||||
@ -713,7 +609,6 @@ function ViewProfile() {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderError = (error: Error | null) => {
|
const renderError = (error: Error | null) => {
|
||||||
if (!error) return null;
|
if (!error) return null;
|
||||||
if (error instanceof BaseError) {
|
if (error instanceof BaseError) {
|
||||||
@ -728,3 +623,4 @@ const renderError = (error: Error | null) => {
|
|||||||
|
|
||||||
return <div className="text-red-500 text-xs mt-1">{error.message}</div>;
|
return <div className="text-red-500 text-xs mt-1">{error.message}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
129
src/components/providers/FrameProvider.tsx
Normal file
129
src/components/providers/FrameProvider.tsx
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState, useCallback } from "react";
|
||||||
|
import sdk, { type Context, type FrameNotificationDetails, AddFrame } from "@farcaster/frame-sdk";
|
||||||
|
import { createStore } from "mipd";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface FrameContextType {
|
||||||
|
isSDKLoaded: boolean;
|
||||||
|
context: Context.FrameContext | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FrameContext = React.createContext<FrameContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
export function useFrame() {
|
||||||
|
const [isSDKLoaded, setIsSDKLoaded] = useState(false);
|
||||||
|
const [context, setContext] = useState<Context.FrameContext>();
|
||||||
|
const [added, setAdded] = useState(false);
|
||||||
|
const [notificationDetails, setNotificationDetails] = useState<FrameNotificationDetails | null>(null);
|
||||||
|
const [lastEvent, setLastEvent] = useState("");
|
||||||
|
const [addFrameResult, setAddFrameResult] = useState("");
|
||||||
|
|
||||||
|
const addFrame = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
setNotificationDetails(null);
|
||||||
|
|
||||||
|
const result = await sdk.actions.addFrame();
|
||||||
|
|
||||||
|
if (result.notificationDetails) {
|
||||||
|
setNotificationDetails(result.notificationDetails);
|
||||||
|
}
|
||||||
|
setAddFrameResult(
|
||||||
|
result.notificationDetails
|
||||||
|
? `Added, got notificaton token ${result.notificationDetails.token} and url ${result.notificationDetails.url}`
|
||||||
|
: "Added, got no notification details"
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AddFrame.RejectedByUser) {
|
||||||
|
setAddFrameResult(`Not added: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof AddFrame.InvalidDomainManifest) {
|
||||||
|
setAddFrameResult(`Not added: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setAddFrameResult(`Error: ${error}`);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const load = async () => {
|
||||||
|
const context = await sdk.context;
|
||||||
|
setContext(context);
|
||||||
|
setIsSDKLoaded(true);
|
||||||
|
|
||||||
|
// Set up event listeners
|
||||||
|
sdk.on("frameAdded", ({ notificationDetails }) => {
|
||||||
|
console.log("Frame added", notificationDetails);
|
||||||
|
setAdded(true);
|
||||||
|
setNotificationDetails(notificationDetails ?? null);
|
||||||
|
setLastEvent("Frame added");
|
||||||
|
});
|
||||||
|
|
||||||
|
sdk.on("frameAddRejected", ({ reason }) => {
|
||||||
|
console.log("Frame add rejected", reason);
|
||||||
|
setAdded(false);
|
||||||
|
setLastEvent(`Frame add rejected: ${reason}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
sdk.on("frameRemoved", () => {
|
||||||
|
console.log("Frame removed");
|
||||||
|
setAdded(false);
|
||||||
|
setLastEvent("Frame removed");
|
||||||
|
});
|
||||||
|
|
||||||
|
sdk.on("notificationsEnabled", ({ notificationDetails }) => {
|
||||||
|
console.log("Notifications enabled", notificationDetails);
|
||||||
|
setNotificationDetails(notificationDetails ?? null);
|
||||||
|
setLastEvent("Notifications enabled");
|
||||||
|
});
|
||||||
|
|
||||||
|
sdk.on("notificationsDisabled", () => {
|
||||||
|
console.log("Notifications disabled");
|
||||||
|
setNotificationDetails(null);
|
||||||
|
setLastEvent("Notifications disabled");
|
||||||
|
});
|
||||||
|
|
||||||
|
sdk.on("primaryButtonClicked", () => {
|
||||||
|
console.log("Primary button clicked");
|
||||||
|
setLastEvent("Primary button clicked");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Call ready action
|
||||||
|
console.log("Calling ready");
|
||||||
|
sdk.actions.ready({});
|
||||||
|
|
||||||
|
// Set up MIPD Store
|
||||||
|
const store = createStore();
|
||||||
|
store.subscribe((providerDetails) => {
|
||||||
|
console.log("PROVIDER DETAILS", providerDetails);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (sdk && !isSDKLoaded) {
|
||||||
|
console.log("Calling load");
|
||||||
|
setIsSDKLoaded(true);
|
||||||
|
load();
|
||||||
|
return () => {
|
||||||
|
sdk.removeAllListeners();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [isSDKLoaded]);
|
||||||
|
|
||||||
|
return { isSDKLoaded, context, added, notificationDetails, lastEvent, addFrame, addFrameResult };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FrameProvider({ children }: { children: React.ReactNode }) {
|
||||||
|
const { isSDKLoaded, context } = useFrame();
|
||||||
|
|
||||||
|
if (!isSDKLoaded) {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FrameContext.Provider value={{ isSDKLoaded, context }}>
|
||||||
|
{children}
|
||||||
|
</FrameContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user