Files
bsky-social-app/src/screens/Signup/StepCaptcha/CaptchaWebView.web.tsx
T
Tomek Zawadzki 2d313d806f Unblock React Compiler for 18 components with value blocks inside try
React Compiler cannot lower a conditional expression - `&&`, `||`, `??`, `?.`,
a ternary - inside a try block. Three techniques, picked per site:

- split `if (a && b)` into nested ifs, where there is no `else` to break
- hoist the expression into a const above the try, where it does not depend on
  anything the try produces
- move it into a module-scope helper, where it does

Optional calls become `if (f) f()`, which keeps the arguments unevaluated when
the callback is absent, exactly as `f?.()` does.

Skipped components: 125 -> 107.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 16:57:00 +02:00

84 lines
2.2 KiB
TypeScript

import {useCallback, useEffect} from 'react'
import {StyleSheet} from 'react-native'
import {type CaptchaWebViewProps} from './CaptchaWebView.shared'
const REDIRECT_HOST = new URL(window.location.href).host
/**
* Module scope because React Compiler cannot lower an optional chain inside a
* `try`, and this one has to stay in the `try` - reading `location` on a
* cross-origin frame throws.
*/
function getFrameHref(frame: HTMLIFrameElement | null): string | undefined {
return frame?.contentWindow?.location.href
}
export function CaptchaWebView({
url,
stateParam,
onSuccess,
onError,
}: CaptchaWebViewProps) {
useEffect(() => {
const timeout = setTimeout(() => {
onError({
errorMessage: 'User did not complete the captcha within 30 seconds',
})
}, 30e3)
return () => {
clearTimeout(timeout)
}
}, [onError])
const onLoad = useCallback(() => {
const frame: HTMLIFrameElement = document.getElementById(
'captcha-iframe',
) as HTMLIFrameElement
try {
const href = getFrameHref(frame)
if (!href) return
const urlp = new URL(href)
// This shouldn't happen with CORS protections, but for good measure
if (urlp.host !== REDIRECT_HOST) return
const code = urlp.searchParams.get('code')
const stateMismatch = urlp.searchParams.get('state') !== stateParam
if (stateMismatch) {
onError({error: 'Invalid state or code'})
return
}
if (!code) {
onError({error: 'Invalid state or code'})
return
}
onSuccess(code)
} catch (e) {
// We don't actually want to record an error here, because this will happen quite a bit. We will only be able to
// get the href of the iframe if it's on our domain, so all the hcaptcha requests will throw here, although it's
// harmless. Our other indicators of time-to-complete and back press should be more reliable in catching issues.
}
}, [stateParam, onSuccess, onError])
return (
<iframe
src={url}
style={styles.iframe}
id="captcha-iframe"
onLoad={onLoad}
/>
)
}
const styles = StyleSheet.create({
iframe: {
flex: 1,
borderWidth: 0,
borderRadius: 10,
backgroundColor: 'transparent',
},
})