Skip to content

Commit

Permalink
refactor: provide parse result and current specification to resolveAc…
Browse files Browse the repository at this point in the history
…tionContext
  • Loading branch information
michalkvasnicak committed Oct 23, 2024
1 parent b0f0b31 commit 3fad5ba
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 103 deletions.
4 changes: 3 additions & 1 deletion packages/render/src/collapsed-frame-ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ export function CollapsedFrameUI({
cursor: isLoading ? undefined : "pointer",
}}
>
{frame.buttons.length === 1 && frame.buttons[0].label.length < 12
{frame.buttons.length === 1 &&
!!frame.buttons[0] &&
frame.buttons[0].label.length < 12
? frame.buttons[0].label
: "View"}
</button>
Expand Down
47 changes: 33 additions & 14 deletions packages/render/src/frame-ui.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import type { ImgHTMLAttributes } from "react";
import React, { useState } from "react";
import type { Frame, FrameButton } from "frames.js";
import type {
Frame,
FrameButton,
SupportedParsingSpecification,
} from "frames.js";
import type {
ParseFramesWithReportsResult,
ParseResult,

Check failure on line 10 in packages/render/src/frame-ui.tsx

View workflow job for this annotation

GitHub Actions / Build and Test on Node.js 20

'ParseResult' is defined but never used. Allowed unused vars must match /^_/u
} from "frames.js/frame-parsers";
import type { FrameTheme, FrameState } from "./types";
import {
getErrorMessageFromFramesStackItem,
Expand Down Expand Up @@ -146,28 +154,35 @@ export function FrameUI({
}

let frame: Frame | Partial<Frame> | undefined;
let parseResult: ParseFramesWithReportsResult | undefined;
let specification: SupportedParsingSpecification | undefined;
let debugImage: string | undefined;

if (currentFrameStackItem.status === "done") {
const parseResult = getFrameParseResultFromStackItemBySpecifications(
const parseResultBySpec = getFrameParseResultFromStackItemBySpecifications(
currentFrameStackItem,
specifications
);

frame = parseResult.frame;
frame = parseResultBySpec.frame;
parseResult = currentFrameStackItem.parseResult;
specification = parseResultBySpec.specification;
debugImage = enableImageDebugging
? parseResult.framesDebugInfo?.image
? parseResultBySpec.framesDebugInfo?.image
: undefined;
} else if (
currentFrameStackItem.status === "message" ||
currentFrameStackItem.status === "doneRedirect"
) {
frame = currentFrameStackItem.request.sourceFrame;
parseResult = currentFrameStackItem.request.sourceParseResult;
specification = currentFrameStackItem.request.specification;
} else if (currentFrameStackItem.status === "requestError") {
frame =
"sourceFrame" in currentFrameStackItem.request
? currentFrameStackItem.request.sourceFrame
: undefined;
if ("sourceFrame" in currentFrameStackItem.request) {
frame = currentFrameStackItem.request.sourceFrame;
parseResult = currentFrameStackItem.request.sourceParseResult;
specification = currentFrameStackItem.request.specification;
}
}

const ImageEl = FrameImage ? FrameImage : "img";
Expand Down Expand Up @@ -233,7 +248,11 @@ export function FrameUI({
}}
/>
) : null}
{!!frame && !!frame.buttons && frame.buttons.length > 0 ? (
{!!parseResult &&
!!specification &&
!!frame &&
!!frame.buttons &&
frame.buttons.length > 0 ? (
<div className="flex gap-[8px] px-2 pb-2">
{frame.buttons.map((frameButton: FrameButton, index: number) => (
<button
Expand All @@ -252,12 +271,12 @@ export function FrameUI({
}}
onClick={() => {
Promise.resolve(
frameState.onButtonPress(
// Partial frame could have enough data to handle button press
frame as Frame,
frameState.onButtonPress({
parseResult,
frameButton,
index
)
index,
specification,
})
).catch((e: unknown) => {
// eslint-disable-next-line no-console -- provide feedback to the user
console.error(e);
Expand Down
35 changes: 30 additions & 5 deletions packages/render/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ export function createParseFramesWithReportsObject(
// this is open frame
return {
openframes: input,
farcaster: { status: "failure", frame: input.frame, reports: {} },
farcaster: {
status: "failure",
frame: input.frame,
reports: {},
specification: "farcaster",
},
};
}

Expand All @@ -81,18 +86,38 @@ export function createParseFramesWithReportsObject(
openframes:
"accepts" in input.frame || "accepts" in input.reports
? input
: { status: "failure", frame: input.frame, reports: {} },
: {
status: "failure",
frame: input.frame,
reports: {},
specification: "openframes",
},
};
}

return {
// always treat the frame as farcaster frame
farcaster: { status: "success", frame: input, reports: {} },
farcaster: {
status: "success",
frame: input,
reports: {},
specification: "farcaster",
},
openframes:
// detect if it is a valid openframe
!input.accepts || input.accepts.length === 0
? { status: "failure", frame: input, reports: {} }
: { status: "success", frame: input, reports: {} },
? {
status: "failure",
frame: input,
reports: {},
specification: "openframes",
}
: {
status: "success",
frame: input,
reports: {},
specification: "openframes",
},
};
}

Expand Down
60 changes: 42 additions & 18 deletions packages/render/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,8 @@ export type SignFrameActionFunc<
actionContext: SignerStateActionContext<TSignerStorageType, TFrameContextType>
) => Promise<SignedFrameAction<TFrameActionBodyType>>;

export type UseFetchFrameSignFrameActionFunction = (arg: {
actionContext: SignerStateActionContext;
/**
* @defaultValue false
*/
forceRealSigner?: boolean;
}) => Promise<SignedFrameAction>;

export type UseFetchFrameOptions = {
dangerousSkipSigning: boolean;
stackAPI: FrameStackAPI;
/**
* URL or path to the frame proxy handling GET requests.
Expand All @@ -102,7 +95,6 @@ export type UseFetchFrameOptions = {
* Extra payload to be sent with the POST request.
*/
extraButtonRequestPayload?: Record<string, unknown>;
signFrameAction: UseFetchFrameSignFrameActionFunction;
/**
* Called after transaction data has been returned from the server and user needs to approve the transaction.
*/
Expand Down Expand Up @@ -195,10 +187,25 @@ export type UseFetchFrameOptions = {
onTransactionProcessingError?: (error: Error) => void;
};

export type ResolveFrameActionContextArgumentAction =
| { type: "composer" }
| { type: "cast" }
| {
type: "frame";
/**
* Current parse result that is active
*/
parseResult: ParseFramesWithReportsResult;
/**
* Specification that is currently active
*/
specification: SupportedParsingSpecification;
};

export type ResolveFrameActionContextArgument = {
readonly dangerouslySkipSigning: boolean;
readonly frameStack: FramesStack;
readonly specifications: SupportedParsingSpecification[];
readonly action: ResolveFrameActionContextArgumentAction;
};

export type ResolveFrameActionContextResult = {
Expand Down Expand Up @@ -420,19 +427,25 @@ export type FramePOSTRequest =
source?: never;
frameButton: FrameButtonPost | FrameButtonTx;
signerStateActionContext: SignerStateActionContext;
signerState: SignerStateInstance<any, any, any>;
isDangerousSkipSigning: boolean;
/**
* The frame that was the source of the button press.
*/
sourceFrame: Frame;
sourceParseResult: ParseFramesWithReportsResult;
specification: SupportedParsingSpecification;
}
| {
method: "POST";
source: "cast-action" | "composer-action";
frameButton: FrameButtonPost | FrameButtonTx;
signerStateActionContext: SignerStateActionContext;
signerState: SignerStateInstance<any, any, any>;
isDangerousSkipSigning: boolean;
sourceFrame: undefined;
sourceParseResult: undefined;
specification: undefined;
};

export type FrameRequest = FrameGETRequest | FramePOSTRequest;
Expand Down Expand Up @@ -541,12 +554,13 @@ export type FrameReducerActions =
homeframeUrl: string;
};

export type ButtonPressFunction = (
frame: Frame,
frameButton: FrameButton,
index: number,
fetchFrameOverride?: FetchFrameFunction
) => void | Promise<void>;
export type ButtonPressFunction = (arg: {
frameButton: FrameButton;
parseResult: ParseFramesWithReportsResult;
specification: SupportedParsingSpecification;
index: number;
fetchFrameOverride?: FetchFrameFunction;
}) => void | Promise<void>;

type CastActionButtonPressFunctionArg = {
castAction: CastActionResponse & {
Expand Down Expand Up @@ -581,7 +595,12 @@ export type ComposerActionButtonPressFunction = (

export type CastActionRequest = Omit<
FramePOSTRequest,
"method" | "frameButton" | "sourceFrame" | "signerStateActionContext"
| "method"
| "frameButton"
| "specification"
| "sourceFrame"
| "sourceParseResult"
| "signerStateActionContext"
> & {
method: "CAST_ACTION";
action: CastActionResponse & {
Expand All @@ -595,7 +614,12 @@ export type CastActionRequest = Omit<

export type ComposerActionRequest = Omit<
FramePOSTRequest,
"method" | "frameButton" | "sourceFrame" | "signerStateActionContext"
| "method"
| "frameButton"
| "specification"
| "sourceFrame"
| "sourceParseResult"
| "signerStateActionContext"
> & {
method: "COMPOSER_ACTION";
action: CastActionResponse & {
Expand Down
22 changes: 16 additions & 6 deletions packages/render/src/ui/frame.base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export function BaseFrameUI<TStylingProps extends Record<string, unknown>>({
case "requestError": {
if (
"sourceFrame" in currentFrameStackItem.request &&
currentFrameStackItem.request.sourceFrame
currentFrameStackItem.request.sourceParseResult
) {
frameUiState = {
status: "complete",
Expand All @@ -159,6 +159,8 @@ export function BaseFrameUI<TStylingProps extends Record<string, unknown>>({
isImageLoading,
id: currentFrameStackItem.timestamp.getTime(),
frameState,
parseResult: currentFrameStackItem.request.sourceParseResult,
specification: currentFrameStackItem.request.specification,
};
} else {
return components.Error(
Expand Down Expand Up @@ -192,6 +194,8 @@ export function BaseFrameUI<TStylingProps extends Record<string, unknown>>({
isImageLoading,
id: currentFrameStackItem.timestamp.getTime(),
frameState,
parseResult: currentFrameStackItem.request.sourceParseResult,
specification: currentFrameStackItem.request.specification,
};

break;
Expand All @@ -211,6 +215,8 @@ export function BaseFrameUI<TStylingProps extends Record<string, unknown>>({
isImageLoading,
id: currentFrameStackItem.timestamp.getTime(),
frameState,
parseResult: currentFrameStackItem.request.sourceParseResult,
specification: currentFrameStackItem.request.specification,
};
}

Expand All @@ -233,6 +239,8 @@ export function BaseFrameUI<TStylingProps extends Record<string, unknown>>({
isImageLoading,
id: currentFrameStackItem.timestamp.getTime(),
frameState,
parseResult: currentFrameStackItem.parseResult,
specification: parseResult.specification,
};
} else if (isPartialFrameParseResult(parseResult) && allowPartialFrame) {
frameUiState = {
Expand All @@ -245,6 +253,8 @@ export function BaseFrameUI<TStylingProps extends Record<string, unknown>>({
isImageLoading,
id: currentFrameStackItem.timestamp.getTime(),
frameState,
parseResult: currentFrameStackItem.parseResult,
specification: parseResult.specification,
};
} else {
return components.Error(
Expand Down Expand Up @@ -284,12 +294,12 @@ export function BaseFrameUI<TStylingProps extends Record<string, unknown>>({
rootDimensionsRef.current = rootRef.current?.computeDimensions();

Promise.resolve(
frameState.onButtonPress(
// @todo change the type onButtonPress to accept partial frame as well because that can happen if partial frames are enabled
frameUiState.frame as Frame,
frameState.onButtonPress({
frameButton,
index
)
index,
parseResult: frameUiState.parseResult,
specification: frameUiState.specification,
})
).catch((error) => {
// eslint-disable-next-line no-console -- provide feedback to the user
console.error(error);
Expand Down
11 changes: 10 additions & 1 deletion packages/render/src/ui/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import type { Frame, FrameButton } from "frames.js";
import type {
Frame,
FrameButton,
SupportedParsingSpecification,
} from "frames.js";
import type { createElement, ReactElement } from "react";
import type { ParseFramesWithReportsResult } from "frames.js/frame-parsers";
import type { FrameState } from "../types";

/**
Expand Down Expand Up @@ -44,6 +49,8 @@ export type FrameUIState =
frameState: FrameState;
debugImage?: string;
isImageLoading: boolean;
parseResult: ParseFramesWithReportsResult;
specification: SupportedParsingSpecification;
}
| {
id: number;
Expand All @@ -53,6 +60,8 @@ export type FrameUIState =
frameState: FrameState;
debugImage?: string;
isImageLoading: boolean;
parseResult: ParseFramesWithReportsResult;
specification: SupportedParsingSpecification;
};

type FrameUIStateProps = {
Expand Down
Loading

0 comments on commit 3fad5ba

Please sign in to comment.