diff --git a/src/app/providers.tsx b/src/app/providers.tsx
index 24be2ac..794a016 100644
--- a/src/app/providers.tsx
+++ b/src/app/providers.tsx
@@ -3,7 +3,7 @@
import dynamic from "next/dynamic";
import type { Session } from "next-auth"
import { SessionProvider } from "next-auth/react"
-
+import { FrameProvider } from "~/components/providers/FrameProvider";
const WagmiProvider = dynamic(
() => import("~/components/providers/WagmiProvider"),
@@ -16,7 +16,9 @@ export function Providers({ session, children }: { session: Session | null, chil
return (
- {children}
+
+ {children}
+
);
diff --git a/src/components/Demo.tsx b/src/components/Demo.tsx
index 7c1d673..13a4235 100644
--- a/src/components/Demo.tsx
+++ b/src/components/Demo.tsx
@@ -1,13 +1,10 @@
"use client";
-import { useEffect, useCallback, useState, useMemo } from "react";
+import { useCallback, useMemo, useState } from "react";
import { Input } from "../components/ui/input";
import { signIn, signOut, getCsrfToken } from "next-auth/react";
import sdk, {
- AddFrame,
- FrameNotificationDetails,
SignIn as SignInCore,
- type Context,
} from "@farcaster/frame-sdk";
import {
useAccount,
@@ -27,30 +24,18 @@ import { truncateAddress } from "~/lib/truncateAddress";
import { base, degen, mainnet, optimism, unichain } from "wagmi/chains";
import { BaseError, UserRejectedRequestError } from "viem";
import { useSession } from "next-auth/react";
-import { createStore } from "mipd";
import { Label } from "~/components/ui/label";
+import { useFrame } from "~/components/providers/FrameProvider";
export default function Demo(
{ title }: { title?: string } = { title: "Frames v2 Demo" }
) {
- const [isSDKLoaded, setIsSDKLoaded] = useState(false);
- const [context, setContext] = useState();
+ const { isSDKLoaded, context, added, notificationDetails, lastEvent, addFrame, addFrameResult } = useFrame();
const [isContextOpen, setIsContextOpen] = useState(false);
const [txHash, setTxHash] = useState(null);
- const [added, setAdded] = useState(false);
- const [notificationDetails, setNotificationDetails] =
- useState(null);
-
- const [lastEvent, setLastEvent] = useState("");
-
- const [addFrameResult, setAddFrameResult] = useState("");
const [sendNotificationResult, setSendNotificationResult] = useState("");
- useEffect(() => {
- setNotificationDetails(context?.client.notificationDetails ?? null);
- }, [context]);
-
const { address, isConnected } = useAccount();
const chainId = useChainId();
@@ -101,68 +86,6 @@ export default function Demo(
switchChain({ chainId: 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(() => {
sdk.actions.openUrl("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
}, []);
@@ -175,33 +98,6 @@ export default function Demo(
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 () => {
setSendNotificationResult("");
if (!notificationDetails || !context) {
@@ -713,7 +609,6 @@ function ViewProfile() {
>
);
}
-
const renderError = (error: Error | null) => {
if (!error) return null;
if (error instanceof BaseError) {
@@ -728,3 +623,4 @@ const renderError = (error: Error | null) => {
return {error.message}
;
};
+
diff --git a/src/components/providers/FrameProvider.tsx b/src/components/providers/FrameProvider.tsx
new file mode 100644
index 0000000..c18d512
--- /dev/null
+++ b/src/components/providers/FrameProvider.tsx
@@ -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(undefined);
+
+export function useFrame() {
+ const [isSDKLoaded, setIsSDKLoaded] = useState(false);
+ const [context, setContext] = useState();
+ const [added, setAdded] = useState(false);
+ const [notificationDetails, setNotificationDetails] = useState(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 Loading...
;
+ }
+
+ return (
+
+ {children}
+
+ );
+}
\ No newline at end of file