Skip to content

Commit

Permalink
feat(preview): input
Browse files Browse the repository at this point in the history
  • Loading branch information
tmm committed Feb 8, 2024
1 parent 5371211 commit ff8c075
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 41 deletions.
10 changes: 7 additions & 3 deletions example/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { Button, Framework, TextInput } from 'farc'

const app = new Framework()

app.frame('/', ({ status }) => {
app.frame('/', (props) => {
const { status, untrustedData } = props
const fruit = untrustedData?.inputText
return {
image: (
<div
Expand Down Expand Up @@ -35,11 +37,13 @@ app.frame('/', ({ status }) => {
whiteSpace: 'pre-wrap',
}}
>
{status === 'response' ? 'Nice choice.' : 'Welcome!'}
{status === 'response'
? `Nice choice.${fruit ? ` ${fruit.toUpperCase()}!!` : ''}`
: 'Welcome!'}
</div>
</div>
),
intents: status === 'initial' && [
intents: [
<Button>Apples</Button>,
<Button>Oranges</Button>,
<Button>Bananas</Button>,
Expand Down
103 changes: 79 additions & 24 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ export class Framework extends Hono {
? (formData.get('buttonIndex') as string)
: '',
)
// TODO: Sanitize input
const inputText = formData.get('inputText')
? Buffer.from(formData.get('inputText') as string)
: undefined
Expand Down Expand Up @@ -200,7 +201,9 @@ export class Framework extends Hono {
hash: `0x${bytesToHex(castId.hash)}`,
},
fid,
inputText,
inputText: inputText
? Buffer.from(inputText).toString('utf-8')
: undefined,
messageHash: `0x${bytesToHex(message.hash)}`,
network: 1,
timestamp: message.data.timestamp,
Expand Down Expand Up @@ -320,44 +323,72 @@ function Preview({ baseUrl, frame }: PreviewProps) {
</div>
<input name="action" type="hidden" value={frame.postUrl} />
</div>
{/* TODO: Text input */}
{frame.buttons && (

{Boolean(frame.input || frame.buttons?.length) && (
<div
style={{
borderBottomLeftRadius: '0.5rem',
borderBottomRightRadius: '0.5rem',
borderTopWidth: '0 !important',
borderWidth: '1px',
display: 'grid',
gap: '10px',
gridTemplateColumns: `repeat(${frame.buttons.length}, minmax(0,1fr))`,
display: 'flex',
flexDirection: 'column',
gap: '8px',
paddingBottom: '0.5rem',
paddingLeft: '1rem',
paddingRight: '1rem',
paddingTop: '0.5rem',
}}
>
{frame.buttons.map((button) => (
<button
key={button.index}
name="buttonIndex"
{frame.input && (
<input
aria-label={frame.input.text}
name="inputText"
placeholder={frame.input.text}
style={{
borderRadius: '0.5rem',
borderRadius: '0.25rem',
borderWidth: '1px',
cursor: 'pointer',
fontSize: '0.875rem',
height: '2.5rem',
paddingBottom: '0.5rem',
paddingLeft: '1rem',
paddingRight: '1rem',
paddingTop: '0.5rem',
lineHeight: '1.25rem',
paddingBottom: '9px',
paddingLeft: '12px',
paddingRight: '12px',
paddingTop: '10px',
width: '100%',
}}
/>
)}
{frame.buttons && (
<div
style={{
display: 'grid',
gap: '10px',
gridTemplateColumns: `repeat(${frame.buttons.length}, minmax(0,1fr))`,
}}
type="submit"
value={button.index}
>
{button.title}
</button>
))}
{frame.buttons.map((button) => (
<button
key={button.index}
name="buttonIndex"
style={{
borderRadius: '0.5rem',
borderWidth: '1px',
cursor: 'pointer',
fontSize: '0.875rem',
height: '2.5rem',
paddingBottom: '0.5rem',
paddingLeft: '1rem',
paddingRight: '1rem',
paddingTop: '0.5rem',
}}
type="submit"
value={button.index}
>
{button.title}
</button>
))}
</div>
)}
</div>
)}
</form>
Expand All @@ -370,8 +401,13 @@ type DevtoolsProps = {
}

async function Devtools({ frame }: DevtoolsProps) {
const { debug: _d, title: _t, ...rest } = frame
return (
<div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
<pre style={{ fontFamily: 'monospace' }}>
{JSON.stringify(rest, null, 2)}
</pre>

<pre style={{ fontFamily: 'monospace' }}>
{frame.debug?.htmlTags.map((x) => (
<code style={{ display: 'grid' }}>{x}</code>
Expand Down Expand Up @@ -508,13 +544,21 @@ function htmlToFrame(html: string) {
}
buttons = buttons.toSorted((a, b) => a.index - b.index)

const input = properties['fc:frame:input:text']
? {
text: properties['fc:frame:input:text'] ?? '',
}
: undefined

const fallbackImageToUrl = !imageUrl
const postUrlTooLong = postUrl.length > 2_048
// TODO: Validate
const inputTextTooLong = false
// TODO: Figure out how this is determined
// https://warpcast.com/~/developers/frames
const valid = true

const frame = { buttons, imageUrl, postUrl, version }
const frame = { buttons, imageUrl, input, postUrl, version }
return {
...frame,
debug: {
Expand All @@ -523,6 +567,7 @@ function htmlToFrame(html: string) {
fallbackImageToUrl,
htmlTags: metaTags.map((x) => x.outerHTML),
image,
inputTextTooLong,
invalidButtons,
postUrlTooLong,
valid,
Expand Down Expand Up @@ -582,6 +627,16 @@ function getGlobalStyles() {
background: var(--bn);
}
input {
background: var(--bg);
}
pre {
margin: 0;
}
/** Reset **/
button,
input {
font-family: inherit;
Expand Down
35 changes: 21 additions & 14 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type UntrustedData = {
buttonIndex?: FrameButton['index'] | undefined
castId: { fid: number; hash: string }
fid: number
inputText?: string
inputText?: string | undefined
messageHash: string
network: number
timestamp: number
Expand All @@ -19,31 +19,36 @@ export type Frame = {
buttons?: readonly FrameButton[] | undefined
debug?: FrameDebug | undefined
imageUrl: string
input?: FrameInput | undefined
postUrl: string
title: string
version: FrameVersion
}

export type FrameDebug = {
buttons?: readonly FrameButton[] | undefined
buttonsAreOutOfOrder: boolean
fallbackImageToUrl: boolean
htmlTags: readonly string[]
image: string
imageUrl: string
invalidButtons: readonly FrameButton['index'][]
postUrl: string
postUrlTooLong: boolean
valid: boolean
version: FrameVersion
}
export type FrameDebug = Pretty<
Omit<Frame, 'debug' | 'title'> & {
buttonsAreOutOfOrder: boolean
fallbackImageToUrl: boolean
htmlTags: readonly string[]
image: string
inputTextTooLong: boolean
invalidButtons: readonly FrameButton['index'][]
postUrl: string
postUrlTooLong: boolean
valid: boolean
}
>

export type FrameButton = {
index: 1 | 2 | 3 | 4
title: string
type: 'post' | 'post_redirect'
}

export type FrameInput = {
text: string
}

export type FrameVersion = 'vNext'

export type FrameMetaTagPropertyName =
Expand All @@ -56,3 +61,5 @@ export type FrameMetaTagPropertyName =
| `fc:frame:button:${FrameButton['index']}:action`
| `fc:frame:button:${FrameButton['index']}:target`
| `fc:frame:button:${FrameButton['index']}`

type Pretty<type> = { [key in keyof type]: type[key] } & unknown

0 comments on commit ff8c075

Please sign in to comment.