diff --git a/examples/_dev/src/index.tsx b/examples/_dev/src/index.tsx
index 8f07882d..f4f9f4b0 100644
--- a/examples/_dev/src/index.tsx
+++ b/examples/_dev/src/index.tsx
@@ -86,23 +86,17 @@ app.frame('/action', ({ buttonValue, inputText }) => {
}
})
-app.frame('/buttons', ({ buttonValue }) => {
+app.frame('/buttons', (context) => {
+ const { buttonValue } = context
return {
image: (
-
- {buttonValue ?? ''}
-
+ {buttonValue ?? ''}
),
intents: [
-
@@ -235,6 +235,7 @@ function Button(props: ButtonProps) {
class={buttonClass}
type="button"
x-on:click={`
+ if (open) return
fetch(baseUrl + '/dev/redirect', {
method: 'POST',
body: JSON.stringify({
@@ -246,12 +247,15 @@ function Button(props: ButtonProps) {
'Content-Type': 'application/json',
},
})
- .then(res => {
+ .then(async (res) => {
console.log(res)
- target = 'https://example.com'
+ const json = await res.json()
+ // TODO: show error
+ if (!json.success) return
+ url = json.redirectUrl
open = true
})
- .catch(error => console.log(error))
+ .catch((error) => console.log(error))
`}
>
{innerHtml}
@@ -709,6 +713,13 @@ export function Style() {
scrollbar-color: var(--br) transparent;
scrollbar-width: thin;
}
+
+ .line-clamp-2 {
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ }
`
return
}
diff --git a/src/dev/routes.tsx b/src/dev/routes.tsx
index 7ad4acdb..11d737a1 100644
--- a/src/dev/routes.tsx
+++ b/src/dev/routes.tsx
@@ -148,7 +148,6 @@ export function routes<
inputText,
})
- console.log(postUrl)
const response = await fetch(postUrl, {
method: 'POST',
body: JSON.stringify({
@@ -175,12 +174,9 @@ export function routes<
}),
})
- // TODO: Get redirect url
- console.log({ response })
-
return c.json({
- success: true,
- redirectUrl: '/',
+ success: response.redirected,
+ redirectUrl: response.url,
})
},
)
diff --git a/src/farc-base.tsx b/src/farc-base.tsx
index 2be36bc3..a3c07329 100644
--- a/src/farc-base.tsx
+++ b/src/farc-base.tsx
@@ -89,6 +89,12 @@ export class FarcBase<
request: c.req,
})
+ if (context.status === 'redirect') {
+ const location = context.buttonValue
+ if (!location) throw new Error('value required to redirect')
+ return c.redirect(location, 302)
+ }
+
if (context.url !== parsePath(c.req.url))
return c.redirect(
`${context.url}?previousContext=${query.previousContext}`,
diff --git a/src/types.ts b/src/types.ts
index e821273e..5c2465f3 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -14,9 +14,10 @@ export type FrameContext = {
/**
* Status of the frame in the frame lifecycle.
* - `initial` - The frame has not yet been interacted with.
+ * - `redirect` - The frame has request is a redirect (button of type `'post_redirect'`).
* - `response` - The frame has been interacted with (user presses button).
*/
- status: 'initial' | 'response'
+ status: 'initial' | 'redirect' | 'response'
trustedData?: TrustedData | undefined
untrustedData?: UntrustedData | undefined
url: Context['req']['url']
diff --git a/src/utils/getFrameContext.ts b/src/utils/getFrameContext.ts
index 427e765e..2a64b5ba 100644
--- a/src/utils/getFrameContext.ts
+++ b/src/utils/getFrameContext.ts
@@ -20,13 +20,15 @@ export async function getFrameContext(
const { context, previousContext, request } = options
const { trustedData, untrustedData } = context || {}
- const { buttonIndex, buttonValue, inputText, reset } = getIntentState(
- // TODO: derive from untrusted data.
- untrustedData,
- previousContext?.intents || [],
- )
+ const { buttonIndex, buttonValue, inputText, redirect, reset } =
+ getIntentState(
+ // TODO: derive from untrusted data.
+ untrustedData,
+ previousContext?.intents || [],
+ )
const status = (() => {
+ if (redirect) return 'redirect'
if (reset) return 'initial'
return context.status || 'initial'
})()
diff --git a/src/utils/getIntentState.ts b/src/utils/getIntentState.ts
index 41af098a..8f1b23ab 100644
--- a/src/utils/getIntentState.ts
+++ b/src/utils/getIntentState.ts
@@ -7,15 +7,26 @@ export function getIntentState(
intents: readonly JSXNode[] | null,
) {
const { buttonIndex, inputText } = frameData || {}
- const state = { buttonIndex, buttonValue: undefined, inputText, reset: false }
+ const state = {
+ buttonIndex,
+ buttonValue: undefined,
+ inputText,
+ redirect: false,
+ reset: false,
+ }
if (!intents) return state
if (buttonIndex) {
const buttonIntents = intents.filter((intent) =>
intent?.props.property.match(/fc:frame:button:\d$/),
)
const intent = buttonIntents[buttonIndex - 1]
- state.buttonValue = intent.props['data-value']
- if (intent.props['data-type'] === 'reset') state.reset = true
+ const type = intent.props['data-type']
+ const value = intent.props['data-value']
+
+ if (type === 'redirect') state.redirect = true
+ else if (type === 'reset') state.reset = true
+
+ state.buttonValue = value
}
return state
}