-
-
Notifications
You must be signed in to change notification settings - Fork 96
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
fix: render initial frame image from image handler #271
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"frog": patch | ||
--- | ||
|
||
Dropped `frog:image` meta tag in favor of having the image handler to render the initial frame image itself. Fixed wrangler/edge deployment issues that were caused by requesting for `frog:image` tag at the frame handler from image handler where it is limited by wrangler/edge api. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,7 @@ | |
const metadata = await getFrameMetadata('https://frame.frog.fm/api') | ||
// biome-ignore lint/performance/noDelete: <explanation> | ||
delete metadata['frog:version'] | ||
expect(metadata).toMatchInlineSnapshot(` | ||
Check failure on line 8 in src/next/getFrameMetadata.test.ts GitHub Actions / Verify / Testsrc/next/getFrameMetadata.test.ts > default
Check failure on line 8 in src/next/getFrameMetadata.test.ts GitHub Actions / Verify / Testsrc/next/getFrameMetadata.test.ts > default
|
||
{ | ||
"fc:frame": "vNext", | ||
"fc:frame:button:1": "Features →", | ||
|
@@ -20,7 +20,6 @@ | |
"fc:frame:image": "https://frame.frog.fm/api/image", | ||
"fc:frame:image:aspect_ratio": "1.91:1", | ||
"fc:frame:post_url": "https://frame.frog.fm/api?initialPath=%252Fapi&previousButtonValues=%2523A_%252C_l%252C_l", | ||
"frog:image": "https://frame.frog.fm/og.png", | ||
"og:image": "https://frame.frog.fm/api/image", | ||
"og:title": "Frog Frame", | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,7 @@ | |
const metadata = await getFrameMetadata('https://frame.frog.fm/api').then( | ||
(m) => m.filter((m) => m.property !== 'frog:version'), | ||
) | ||
expect(metadata).toMatchInlineSnapshot(` | ||
Check failure on line 8 in src/utils/getFrameMetadata.test.ts GitHub Actions / Verify / Testsrc/utils/getFrameMetadata.test.ts > default
Check failure on line 8 in src/utils/getFrameMetadata.test.ts GitHub Actions / Verify / Testsrc/utils/getFrameMetadata.test.ts > default
|
||
[ | ||
{ | ||
"content": "vNext", | ||
|
@@ -66,11 +66,7 @@ | |
{ | ||
"content": "https://github.com/wevm/frog", | ||
"property": "fc:frame:button:3:target", | ||
}, | ||
{ | ||
"content": "https://frame.frog.fm/og.png", | ||
"property": "frog:image", | ||
}, | ||
} | ||
] | ||
`) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we go down this route, then this will cause a double render cycle on the initial request (one render cycle on the main endpoint, and one on this
/image
endpoint). This means it will become non-idempotent (ie. operations in the handler will be invoked twice which could cause db/store related bugs).I wonder if there is a way to do this without invoking the handler again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still need to play around with the initial image caching issue, but why would WC cache the initial image if
cache-control: max-age=0
on the frame endpoint? Shouldn’t it always serve fresh frame data and seems like a WC issue?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue is that WC caches an image by url, and if an image changes its url also changes because the compressed
image
query parameter is changing.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Therefore since every "refreshed" image has a unique link, wc will cache the initial one and would expect the initial one to be updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're absolutely right.
We need to think of a different solution then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://warpcast.com/horsefacts.eth/0x66aac3d2 IMO the cleaner way to handle this. It's a small step for the user but the code is even cleaner. So we could just revert and include instructions in the documentation of provide some sort of middleware to make it easier to use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jxom, if idempotency is our main issue in this PR, we could enforce frame image handler to always be invoked after the frame handler.
Currently, frog implements those to be independent from each other - meaning that given a frame response you're given a specific frame image URL that always renders "independently" from a frame. As in fact if you serialize the query parameters correctly, you could render any image even outside of the frame context.
However, all the app clients are fetching frames first, and retrieve image urls as a result, thus the frame image in fact depends on the frame response itself.
I propose to implement an initial frame image storage inside of frog which would enforce triggering only one of the handlers and returning a stores value in another one.
Here's the first example, where a Frame is casted for the first time (meaning that frame is not cached in the app client):
context.status === "initial"
, we put theimage
in a local storage where the key is some kind of uniquerequestId
(can we get it from somewhere?), which stays the same within a single full frame fetch operation..image
in local storage by the givenrequestId
, so we just return it.And here's the second example where the app client has the frame cached.
The example is also only applicable for requests where
context.status === "initial"
age
of the cached frame image request has surpassedmax-age
).requestId
(because frame handler was not invoked), so the image handler invokes frame handler just to retrieve the image, and doesn't store anything in the local storage since there will be no requests afterwards.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just figured out that all of that is applicable without this
requestId
, there's no need to preserve the "origin" of the request as the context is empty – GET requests have no payload in them.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
such a "storage" most likely won't be reliable in serverless environments where a worker would kill itself in between frame and frame image requests causing idempotency breakage
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
however, the delay between such request is so small that it's less likely that a pod would go to sleep