Fix interactive HTML media display for Arweave iframe content#2293
Conversation
Two bugs caused interactive HTML drops (text/html via Arweave) to glitch and fail to render: 1. Gateway fallback timeout fired while iframe was off-screen — the 8-second timeout in InteractiveHtmlMediaDisplay started on mount, but SandboxedExternalIframe defers rendering until the IntersectionObserver fires. Off-screen cards exhausted all gateway URLs before the user scrolled to them. Fixed by adding an onVisible callback so the timeout only starts once the iframe is actually in the viewport. 2. CSP blocked gateway.ar.io redirect target — gateway.ar.io redirects interactive content to <txid>.ar.io, but frame-src only allowed *.gateway.ar.io. Added ar.io to CSP sources (without adding it to the fallback gateway list) via a separate ADDITIONAL_CSP_HOSTS array. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdded an optional Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
components/common/SandboxedExternalIframe.tsx (1)
77-98: MissingonVisiblein the effect's dependency array.
onVisibleis referenced inside the effect but omitted from deps on Line 98. This will tripreact-hooks/exhaustive-depsand, if a caller passes a non-memoized callback whose closure matters, could fire a stale reference. Functionally fine today because the effect is single-shot (short-circuits onisVisible) and callers only use it to flip a boolean, but add it to deps for correctness and to keep the lint rule clean.Proposed fix
- }, [canonicalSrc, isVisible]); + }, [canonicalSrc, isVisible, onVisible]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/common/SandboxedExternalIframe.tsx` around lines 77 - 98, The useEffect that observes the iframe references onVisible but does not include it in the dependency array; update the effect's deps to include onVisible (i.e., change the dependency array from [canonicalSrc, isVisible] to [canonicalSrc, isVisible, onVisible]) so the hook honors the latest callback reference and satisfies react-hooks/exhaustive-deps while leaving the existing short-circuit logic in place.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/media/arweave-gateways.ts`:
- Around line 9-11: The ADDITIONAL_CSP_HOSTS constant currently broadens CSP to
all ar.io subdomains; narrow it by either (A) replacing the single "ar.io" entry
with a more specific pattern used only where necessary (e.g., only add
"https://*.ar.io" into FRAME_CSP_HOSTS and remove it from CONNECT/MEDIA lists)
or (B) keep ADDITIONAL_CSP_HOSTS as "ar.io" but change callers to only include
it in frame-src (not connect-src/media-src); update references to
ADDITIONAL_CSP_HOSTS or the CSP construction logic so that frame-related CSPs
allow ar.io redirects while connect/media CSPs do not include the broad ar.io
wildcard.
---
Nitpick comments:
In `@components/common/SandboxedExternalIframe.tsx`:
- Around line 77-98: The useEffect that observes the iframe references onVisible
but does not include it in the dependency array; update the effect's deps to
include onVisible (i.e., change the dependency array from [canonicalSrc,
isVisible] to [canonicalSrc, isVisible, onVisible]) so the hook honors the
latest callback reference and satisfies react-hooks/exhaustive-deps while
leaving the existing short-circuit logic in place.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e8576819-6c64-487f-827f-e32772b24aef
📒 Files selected for processing (3)
components/common/SandboxedExternalIframe.tsxcomponents/drops/view/item/content/media/MediaDisplay.tsxlib/media/arweave-gateways.ts
…teway-timeout-and-csp
|



Summary
Gateway fallback timeout fires while iframe is off-screen: The 8-second fallback timeout in
InteractiveHtmlMediaDisplaystarted on component mount, butSandboxedExternalIframedefers iframe rendering until itsIntersectionObserverfires. Off-screen cards (e.g. below the fold in a list) exhausted all gateway URLs before the user scrolled to them, causing the iframe to land on a broken/blocked gateway. Added anonVisiblecallback fromSandboxedExternalIframeso the timeout only starts once the iframe is actually in the viewport.CSP blocks
gateway.ar.ioredirect target:gateway.ar.ioredirects interactive content to<txid>.ar.io(not<txid>.gateway.ar.io), butframe-srconly allowed*.gateway.ar.io. Addedar.ioto CSP sources via a separateADDITIONAL_CSP_HOSTSarray — this keeps it out of the fallback gateway retry list while covering the redirect target in the Content Security Policy.Reproduction
Any
text/htmldrop hosted on Arweave (e.g. interactive meme card submissions) would glitch and error when:gateway.ar.io) redirected to*.ar.iowhich was blocked by CSPFiles changed
lib/media/arweave-gateways.ts— newADDITIONAL_CSP_HOSTSarray, included inARWEAVE_GATEWAY_CSP_SOURCEScomponents/common/SandboxedExternalIframe.tsx— new optionalonVisiblecallback prop, fired whenIntersectionObservertriggerscomponents/drops/view/item/content/media/MediaDisplay.tsx—InteractiveHtmlMediaDisplaydefers fallback timeout untilonVisiblefiresTest plan
*.ar.ioshould no longer be blocked🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Chores