Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: redirect #4

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 7 additions & 13 deletions examples/_dev/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,17 @@ app.frame('/action', ({ buttonValue, inputText }) => {
}
})

app.frame('/buttons', ({ buttonValue }) => {
app.frame('/buttons', (context) => {
const { buttonValue } = context
return {
image: (
<div
style={{
backgroundColor: '#2D2D2D',
display: 'flex',
fontSize: 60,
width: '100%',
height: '100%',
}}
>
{buttonValue ?? ''}
</div>
<div style={{ backgroundColor: '#2D2D2D' }}>{buttonValue ?? ''}</div>
),
intents: [
<Button action="post_redirect" value="apples">
<Button
action="post_redirect"
value="https://github.com/wevm/viem/blob/main/src/clients/createClient.ts"
Copy link
Member Author

@tmm tmm Feb 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting value to URL to redirect to. Reason this is in .frame is so you can use dynamic content from context.

>
Redirect
</Button>,
<Button.Link href="https://www.example.com">Link</Button.Link>,
Expand Down
1 change: 1 addition & 0 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function ButtonRoot({
<meta
property={`fc:frame:button:${index}`}
content={children}
data-type={action === 'post_redirect' ? 'redirect' : undefined}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if it makes sense to add a Button.Redirect component instead of overloading Button?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense to me

data-value={value}
/>,
<meta property={`fc:frame:button:${index}:action`} content={action} />,
Expand Down
23 changes: 17 additions & 6 deletions src/dev/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ function Button(props: ButtonProps) {
index: '${index}',
open: false,
target: ${target ? `'${target}'` : undefined},
url: ${type === 'link' && target ? `'${target}'` : undefined},
}`,
}
const leavingAppPrompt = (
Expand All @@ -187,11 +188,10 @@ function Button(props: ButtonProps) {
{...{
'@click.outside': 'open = false',
'@keyup.escape': 'open = false',
'x-trap.noscroll': 'open',
}}
>
<h1 class="font-bold text-base">Leaving Warpcast</h1>
<div class="text-fg2 text-sm font-mono">{target}</div>
<div class="line-clamp-2 text-fg2 text-sm font-mono" x-text="url" />
<p class="text-base leading-snug">
If you connect your wallet and the site is malicious, you may lose
funds.
Expand All @@ -208,7 +208,7 @@ function Button(props: ButtonProps) {
class="bg-er border-er rounded-md w-full text-sm text-white font-bold py-1"
target="_blank"
type="button"
x-on:click={`open = false; window.open(target, '_blank');`}
x-on:click={`open = false; window.open(url, '_blank');`}
>
<div style={{ marginTop: '1px' }}>I Understand</div>
</button>
Expand All @@ -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({
Expand All @@ -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))
`}
>
<div style={{ marginTop: '2px' }}>{innerHtml}</div>
Expand Down Expand Up @@ -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 <style dangerouslySetInnerHTML={{ __html: styles }} />
}
8 changes: 2 additions & 6 deletions src/dev/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ export function routes<
inputText,
})

console.log(postUrl)
const response = await fetch(postUrl, {
method: 'POST',
body: JSON.stringify({
Expand All @@ -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,
})
},
)
Expand Down
6 changes: 6 additions & 0 deletions src/farc-base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}`,
Expand Down
3 changes: 2 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ export type FrameContext<path extends string = string, state = unknown> = {
/**
* 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']
Expand Down
12 changes: 7 additions & 5 deletions src/utils/getFrameContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ export async function getFrameContext<state>(
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'
})()
Expand Down
17 changes: 14 additions & 3 deletions src/utils/getIntentState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}