Skip to content

Commit 7d1cafe

Browse files
fix: render package errors
1 parent 63b9a71 commit 7d1cafe

12 files changed

+272
-191
lines changed

packages/render/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@
7979
"peerDependencies": {
8080
"next": "^14.1.0",
8181
"react": "^18.2.0",
82-
"react-dom": "^18.2.0"
82+
"react-dom": "^18.2.0",
83+
"viem": "^2.7.8"
8384
},
8485
"dependencies": {
8586
"@farcaster/core": "^0.14.7",

packages/render/src/farcaster/frames.tsx

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
import {
1+
import type {
22
CastId,
3+
FrameActionMessage
4+
} from "@farcaster/core";
5+
import {
36
NobleEd25519Signer,
47
makeFrameAction,
58
FarcasterNetwork,
69
Message,
7-
FrameActionBody,
8-
FrameActionMessage,
10+
FrameActionBody
911
} from "@farcaster/core";
10-
import { FrameButton } from "frames.js";
11-
import { FrameActionBodyPayload, FrameContext } from "../types.js";
12-
import { FarcasterSignerState } from "./signers.js";
1312
import { hexToBytes } from "viem";
13+
import type { FrameButton } from "frames.js";
14+
import type { FrameContext } from "../types";
15+
import type { FarcasterSignerState } from "./signers";
1416

15-
export interface FarcasterFrameActionBodyPayload
16-
extends FrameActionBodyPayload {}
1717

1818
export type FarcasterFrameContext = {
1919
/** Connected address of user, only sent with transaction data request */
@@ -80,16 +80,16 @@ export const signFrameAction = async ({
8080
}
8181

8282
const searchParams = new URLSearchParams({
83-
postType: transactionId ? "post" : frameButton?.action || "post",
83+
postType: transactionId ? "post" : frameButton.action,
8484
postUrl: target ?? "",
8585
});
8686

8787
return {
88-
searchParams: searchParams,
88+
searchParams,
8989
body: {
9090
untrustedData: {
9191
fid: signer.fid,
92-
url: url,
92+
url,
9393
messageHash: `0x${Buffer.from(message.hash).toString("hex")}`,
9494
timestamp: message.data.timestamp,
9595
network: 1,
@@ -169,5 +169,5 @@ export async function createFrameActionMessageWithSignerKey(
169169
Message.encode(message._unsafeUnwrap()).finish()
170170
).toString("hex");
171171

172-
return { message: message.unwrapOr(null), trustedBytes: trustedBytes };
172+
return { message: message.unwrapOr(null), trustedBytes };
173173
}
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export * from "./frames.js";
2-
export * from "./signers.js";
1+
export * from "./frames";
2+
export * from "./signers";

packages/render/src/farcaster/signers.tsx

+4-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
import { SignerStateInstance } from "../types.js";
2-
import { FarcasterFrameActionBodyPayload } from "./frames.js";
1+
import type { SignerStateInstance } from "..";
32

4-
export interface FarcasterSignerState
5-
extends SignerStateInstance<
6-
FarcasterSigner | null,
7-
FarcasterFrameActionBodyPayload
8-
> {}
3+
export type FarcasterSignerState = SignerStateInstance<
4+
FarcasterSigner | null
5+
>
96

107
export interface FarcasterSigner {
118
/* the Farcaster signer private key */

packages/render/src/frame-ui.tsx

+28-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { FrameTheme, FrameState } from "./types.js";
2-
import React, { ImgHTMLAttributes, useEffect } from "react";
3-
import { FrameButton } from "frames.js";
1+
import type { ImgHTMLAttributes } from "react";
2+
import React, { useEffect, useState } from "react";
3+
import type { FrameButton } from "frames.js";
4+
import type { FrameTheme, FrameState } from "./types";
45

56
export const defaultTheme: Required<FrameTheme> = {
67
buttonBg: "#fff",
@@ -11,7 +12,7 @@ export const defaultTheme: Required<FrameTheme> = {
1112
bg: "#efefef",
1213
};
1314

14-
const getThemeWithDefaults = (theme: FrameTheme) => {
15+
const getThemeWithDefaults = (theme: FrameTheme): FrameTheme => {
1516
return {
1617
...defaultTheme,
1718
...theme,
@@ -25,8 +26,12 @@ export type FrameUIProps = {
2526
};
2627

2728
/** A UI component only, that should be easy for any app to integrate */
28-
export function FrameUI({ frameState, theme, FrameImage }: FrameUIProps) {
29-
const [isImageLoading, setIsImageLoading] = React.useState(true);
29+
export function FrameUI({
30+
frameState,
31+
theme,
32+
FrameImage,
33+
}: FrameUIProps): React.JSX.Element | null {
34+
const [isImageLoading, setIsImageLoading] = useState(true);
3035

3136
const isLoading = !!frameState.isLoading || isImageLoading;
3237

@@ -53,7 +58,7 @@ export function FrameUI({ frameState, theme, FrameImage }: FrameUIProps) {
5358
<ImageEl
5459
src={frameState.frame.image}
5560
alt="Frame image"
56-
width={"100%"}
61+
width="100%"
5762
style={{
5863
filter: isLoading ? "blur(4px)" : undefined,
5964
borderTopLeftRadius: `${resolvedTheme.buttonRadius}px`,
@@ -69,9 +74,11 @@ export function FrameUI({ frameState, theme, FrameImage }: FrameUIProps) {
6974
onLoad={() => {
7075
setIsImageLoading(false);
7176
}}
72-
onError={() => setIsImageLoading(false)}
77+
onError={() => {
78+
setIsImageLoading(false);
79+
}}
7380
/>
74-
{frameState.frame.inputText && (
81+
{frameState.frame.inputText ? (
7582
<input
7683
className="p-[6px] mx-2 border box-border"
7784
style={{
@@ -81,9 +88,11 @@ export function FrameUI({ frameState, theme, FrameImage }: FrameUIProps) {
8188
value={frameState.inputText}
8289
type="text"
8390
placeholder={frameState.frame.inputText}
84-
onChange={(e) => frameState.setInputText(e.target.value)}
91+
onChange={(e) => {
92+
frameState.setInputText(e.target.value);
93+
}}
8594
/>
86-
)}
95+
) : null}
8796
<div
8897
style={{
8998
display: "flex",
@@ -108,7 +117,13 @@ export function FrameUI({ frameState, theme, FrameImage }: FrameUIProps) {
108117
color: resolvedTheme.buttonColor,
109118
cursor: isLoading ? undefined : "pointer",
110119
}}
111-
onClick={() => frameState.onButtonPress(frameButton, index)}
120+
onClick={() => {
121+
Promise.resolve(frameState.onButtonPress(frameButton, index)).catch((e: unknown) => {
122+
// eslint-disable-next-line no-console -- provide feedback to the user
123+
console.error(e);
124+
});
125+
}}
126+
// eslint-disable-next-line react/no-array-index-key -- this is fine
112127
key={index}
113128
>
114129
{frameButton.action === "mint" ? `⬗ ` : ""}
@@ -124,7 +139,7 @@ export function FrameUI({ frameState, theme, FrameImage }: FrameUIProps) {
124139
height="12"
125140
fill="currentColor"
126141
>
127-
<path d="M9.504.43a1.516 1.516 0 0 1 2.437 1.713L10.415 5.5h2.123c1.57 0 2.346 1.909 1.22 3.004l-7.34 7.142a1.249 1.249 0 0 1-.871.354h-.302a1.25 1.25 0 0 1-1.157-1.723L5.633 10.5H3.462c-1.57 0-2.346-1.909-1.22-3.004L9.503.429Zm1.047 1.074L3.286 8.571A.25.25 0 0 0 3.462 9H6.75a.75.75 0 0 1 .694 1.034l-1.713 4.188 6.982-6.793A.25.25 0 0 0 12.538 7H9.25a.75.75 0 0 1-.683-1.06l2.008-4.418.003-.006a.036.036 0 0 0-.004-.009l-.006-.006-.008-.001c-.003 0-.006.002-.009.004Z"></path>
142+
<path d="M9.504.43a1.516 1.516 0 0 1 2.437 1.713L10.415 5.5h2.123c1.57 0 2.346 1.909 1.22 3.004l-7.34 7.142a1.249 1.249 0 0 1-.871.354h-.302a1.25 1.25 0 0 1-1.157-1.723L5.633 10.5H3.462c-1.57 0-2.346-1.909-1.22-3.004L9.503.429Zm1.047 1.074L3.286 8.571A.25.25 0 0 0 3.462 9H6.75a.75.75 0 0 1 .694 1.034l-1.713 4.188 6.982-6.793A.25.25 0 0 0 12.538 7H9.25a.75.75 0 0 1-.683-1.06l2.008-4.418.003-.006a.036.036 0 0 0-.004-.009l-.006-.006-.008-.001c-.003 0-.006.002-.009.004Z" />
128143
</svg>
129144
) : (
130145
""

packages/render/src/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FrameContext } from "./types.js";
1+
import type { FrameContext } from "./types";
22

33
export const fallbackFrameContext: FrameContext = {
44
castId: {
@@ -10,6 +10,6 @@ export const fallbackFrameContext: FrameContext = {
1010

1111
export * from "./frame-ui.js";
1212
export * from "./types.js";
13-
export * from "./farcaster/index.js";
13+
export * from "./farcaster";
1414

1515
/** don't export use-frame from here, as it's a client component */

packages/render/src/next/FrameImage.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import Image from "next/image";
2-
import React, { ImgHTMLAttributes } from "react";
2+
import type { ImgHTMLAttributes } from "react";
3+
import React from "react";
34

45
export function FrameImageNext(
56
props: ImgHTMLAttributes<HTMLImageElement> & { src: string }
6-
) {
7+
): React.JSX.Element {
78
return (
89
<Image
910
{...props}

packages/render/src/next/GET.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { getFrame } from "frames.js";
2-
import { NextRequest, NextResponse as NextResponseBase } from "next/server";
2+
import type { NextRequest } from "next/server";
3+
import { NextResponse as NextResponseBase } from "next/server";
34

45
// this is ugly hack to go around the issue https://github.com/vercel/next.js/pull/61721
56
const NextResponse = (
@@ -21,6 +22,7 @@ export async function GET(request: NextRequest): Promise<NextResponseBase> {
2122

2223
return NextResponse.json({ frame, errors });
2324
} catch (err) {
25+
// eslint-disable-next-line no-console -- provide feedback to the developer
2426
console.error(err);
2527
return NextResponse.json({ message: err }, { status: 500 });
2628
}

packages/render/src/next/POST.tsx

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1+
import type { FrameActionPayload} from "frames.js";
12
import { getFrame } from "frames.js";
2-
import { NextRequest } from "next/server";
3+
import type { NextRequest } from "next/server";
34

45
/** Proxies frame actions to avoid CORS issues and preserve user IP privacy */
5-
export async function POST(req: NextRequest) {
6-
const body = await req.json();
6+
export async function POST(req: NextRequest): Promise<Response> {
7+
const body = await req.json() as FrameActionPayload;
78
const isPostRedirect =
89
req.nextUrl.searchParams.get("postType") === "post_redirect";
910
const isTransactionRequest =
1011
req.nextUrl.searchParams.get("postType") === "tx";
11-
const postUrl = req.nextUrl.searchParams.get("postUrl")!;
12+
const postUrl = req.nextUrl.searchParams.get("postUrl");
13+
14+
if (!postUrl) {
15+
return Response.error();
16+
}
1217

1318
try {
1419
const r = await fetch(postUrl, {
@@ -31,7 +36,7 @@ export async function POST(req: NextRequest) {
3136
}
3237

3338
if (isTransactionRequest) {
34-
const transaction = await r.json();
39+
const transaction = await r.json() as JSON;
3540
return Response.json(transaction);
3641
}
3742

@@ -44,6 +49,7 @@ export async function POST(req: NextRequest) {
4449

4550
return Response.json({ frame, errors });
4651
} catch (err) {
52+
// eslint-disable-next-line no-console -- provide feedback to the user
4753
console.error(err);
4854
return Response.error();
4955
}

packages/render/src/next/index.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export * from "./GET.js";
2-
export * from "./POST.js";
3-
export * from "./FrameImage.js";
1+
export * from "./GET";
2+
export * from "./POST";
3+
export * from "./FrameImage";

packages/render/src/types.ts

+13-11
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { Frame, FrameButton, TransactionTargetResponse } from "frames.js";
2-
import { FarcasterFrameContext } from "./farcaster/index.js";
2+
import type { FarcasterFrameContext } from "./farcaster/frames";
33

44
export type OnTransactionFunc = (
5-
t: onTransactionArgs
5+
t: OnTransactionArgs
66
) => Promise<`0x${string}` | null>;
77

88
export type UseFrameReturn<
@@ -22,7 +22,7 @@ export type UseFrameReturn<
2222
/** the initial frame. if not specified will fetch it from the url prop */
2323
frame?: Frame;
2424
/** a function to handle mint buttons */
25-
onMint?: (t: onMintArgs) => void;
25+
onMint?: (t: OnMintArgs) => void;
2626
/** a function to handle transaction buttons, returns the transaction hash or null */
2727
onTransaction?: OnTransactionFunc;
2828
/** the context of this frame, used for generating Frame Action payloads */
@@ -65,7 +65,6 @@ export interface SignerStateInstance<
6565
export type FrameRequest =
6666
| {
6767
method: "GET";
68-
request: {};
6968
url: string;
7069
}
7170
| {
@@ -97,10 +96,10 @@ export type FrameStackError = FrameStackBase & {
9796
requestError: unknown;
9897
};
9998

100-
export type FramesStack = Array<FrameStackSuccess | FrameStackError>;
99+
export type FramesStack = (FrameStackSuccess | FrameStackError)[];
101100

102101
export type FrameState = {
103-
fetchFrame: (request: FrameRequest) => void;
102+
fetchFrame: (request: FrameRequest) => void | Promise<void>;
104103
clearFrameStack: () => void;
105104
/** The frame at the top of the stack (at index 0) */
106105
frame: Frame | null;
@@ -110,21 +109,24 @@ export type FrameState = {
110109
isLoading?: null | FrameStackPending;
111110
inputText: string;
112111
setInputText: (s: string) => void;
113-
onButtonPress: (frameButton: FrameButton, index: number) => void;
112+
onButtonPress: (
113+
frameButton: FrameButton,
114+
index: number
115+
) => void | Promise<void>;
114116
/** Whether the frame at the top of the stack has any frame validation errors. Undefined when the frame is not loaded or set */
115117
isFrameValid: boolean | undefined;
116118
frameValidationErrors: Record<string, string[]> | undefined | null;
117-
error: null | unknown;
119+
error: unknown;
118120
homeframeUrl: string | null;
119121
};
120122

121-
export type onMintArgs = {
123+
export type OnMintArgs = {
122124
target: string;
123125
frameButton: FrameButton;
124126
frame: Frame;
125127
};
126128

127-
export type onTransactionArgs = {
129+
export type OnTransactionArgs = {
128130
transactionData: TransactionTargetResponse;
129131
frameButton: FrameButton;
130132
frame: Frame;
@@ -141,6 +143,6 @@ export const themeParams = [
141143

142144
export type FrameTheme = Partial<Record<(typeof themeParams)[number], string>>;
143145

144-
export interface FrameActionBodyPayload {}
146+
export type FrameActionBodyPayload = Record<string, unknown>;
145147

146148
export type FrameContext = FarcasterFrameContext;

0 commit comments

Comments
 (0)