From 7ade6a0bba6e18e868e1b4aa365d5f2832fee896 Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Fri, 9 Feb 2024 07:07:20 +1100 Subject: [PATCH] feat: previousContext --- example/src/index.tsx | 6 ++-- src/index.tsx | 77 +++++++++++++++++++++++++++++++------------ 2 files changed, 60 insertions(+), 23 deletions(-) diff --git a/example/src/index.tsx b/example/src/index.tsx index 46f5a9ca..02f35539 100644 --- a/example/src/index.tsx +++ b/example/src/index.tsx @@ -6,8 +6,10 @@ import { Button, Framework, TextInput } from 'farc' const app = new Framework() -app.frame('/', (props) => { - const { status, untrustedData } = props +app.frame('/', (context, previousContext) => { + console.log(context, previousContext) + + const { status, untrustedData } = context const fruit = untrustedData?.inputText return { image: ( diff --git a/src/index.tsx b/src/index.tsx index b82355f4..198813f8 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -44,31 +44,48 @@ export class Framework extends Hono { frame( path: string, handler: ( - c: FrameContext, + context: FrameContext, + previousContext: FrameContext, ) => FrameHandlerReturnType | Promise, ) { // Frame Route (implements GET & POST). this.use(path, async (c) => { + const query = c.req.query() + const previousContext = deserializeJson( + query.previousContext, + ) + const context = await getFrameContext(c) - const { intents } = await handler(context) - const serializedContext = encodeURIComponent(JSON.stringify(context)) + const { intents } = await handler(context, previousContext) + + const serializedContext = serializeJson(context) + return c.render( + - {intents ? parseIntents(intents) : null} , @@ -76,18 +93,19 @@ export class Framework extends Hono { }) // OG Image Route - this.get(`${parseUrl(path)}/image`, async (c) => { - const { context } = c.req.query() - const parsedContext = JSON.parse( - decodeURIComponent(context), - ) as FrameContext - const { image } = await handler(parsedContext) + this.get(`${toBaseUrl(path)}/image`, async (c) => { + const query = c.req.query() + const parsedContext = deserializeJson(query.context) + const parsedPreviousContext = deserializeJson( + query.previousContext, + ) + const { image } = await handler(parsedContext, parsedPreviousContext) return new ImageResponse(image) }) // Frame Preview Routes this.use( - `${parseUrl(path)}/preview`, + `${toBaseUrl(path)}/preview`, jsxRenderer( ({ children }) => { return ( @@ -191,7 +209,7 @@ export class Framework extends Hono { ) const message = frameActionMessage._unsafeUnwrap() - const response = await fetch(baseUrl, { + const response = await fetch(formData.get('action') as string, { method: 'POST', body: JSON.stringify({ untrustedData: { @@ -236,12 +254,19 @@ export class Framework extends Hono { export type ButtonProps = { children: string index?: number + value?: string } // TODO: `fc:frame:button:$idx:action` and `fc:frame:button:$idx:target` Button.__type = 'button' -export function Button({ children, index = 0 }: ButtonProps) { - return +export function Button({ children, index = 0, value }: ButtonProps) { + return ( + + ) } export type TextInputProps = { @@ -431,7 +456,7 @@ async function getFrameContext(ctx: Context): Promise { status: req.method === 'POST' ? 'response' : 'initial', trustedData, untrustedData, - url: req.url, + url: toBaseUrl(req.url), } } @@ -465,8 +490,18 @@ function parseIntent(node_: JSXNode, counter: Counter) { return (typeof node.tag === 'function' ? node.tag(props) : node) as JSXNode } -function parseUrl(path: string) { - return path.endsWith('/') ? path.slice(0, -1) : path +function toBaseUrl(path_: string) { + let path = path_.split('?')[0] + if (path.endsWith('/')) path = path.slice(0, -1) + return path +} + +function deserializeJson(data = '{}'): returnType { + return JSON.parse(decodeURIComponent(data)) +} + +function serializeJson(data: unknown = {}) { + return encodeURIComponent(JSON.stringify(data)) } function htmlToFrame(html: string) {