feat: move frame code to FrameProvider.tsx

This commit is contained in:
lucas-neynar 2025-03-19 17:18:16 -07:00
parent bbf4978c6e
commit 80520c34a9
No known key found for this signature in database
3 changed files with 137 additions and 110 deletions

View File

@ -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 (
<SessionProvider session={session}>
<WagmiProvider>
{children}
<FrameProvider>
{children}
</FrameProvider>
</WagmiProvider>
</SessionProvider>
);

View File

@ -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<Context.FrameContext>();
const { isSDKLoaded, context, added, notificationDetails, lastEvent, addFrame, addFrameResult } = useFrame();
const [isContextOpen, setIsContextOpen] = useState(false);
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("");
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 <div className="text-red-500 text-xs mt-1">{error.message}</div>;
};

View 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>
);
}