Bluesky app fork with some witchin' additions 馃挮
at post-text-option 108 lines 2.7 kB view raw
1import {useEffect, useMemo, useRef} from 'react' 2import {WebView, type WebViewNavigation} from 'react-native-webview' 3import {type ShouldStartLoadRequest} from 'react-native-webview/lib/WebViewTypes' 4 5import {type SignupState} from '#/screens/Signup/state' 6 7const ALLOWED_HOSTS = [ 8 'bsky.social', 9 'bsky.app', 10 'staging.bsky.app', 11 'staging.bsky.dev', 12 'app.staging.bsky.dev', 13 'js.hcaptcha.com', 14 'newassets.hcaptcha.com', 15 'api2.hcaptcha.com', 16] 17 18const MIN_DELAY = 3_500 19 20export function CaptchaWebView({ 21 url, 22 stateParam, 23 state, 24 onComplete, 25 onSuccess, 26 onError, 27}: { 28 url: string 29 stateParam: string 30 state?: SignupState 31 onComplete: () => void 32 onSuccess: (code: string) => void 33 onError: (error: unknown) => void 34}) { 35 const startedAt = useRef(Date.now()) 36 const successTo = useRef<NodeJS.Timeout>(undefined) 37 38 useEffect(() => { 39 return () => { 40 if (successTo.current) { 41 clearTimeout(successTo.current) 42 } 43 } 44 }, []) 45 46 const redirectHost = useMemo(() => { 47 if (!state?.serviceUrl) return 'bsky.app' 48 49 return state?.serviceUrl && 50 new URL(state?.serviceUrl).host === 'staging.bsky.dev' 51 ? 'app.staging.bsky.dev' 52 : 'bsky.app' 53 }, [state?.serviceUrl]) 54 55 const wasSuccessful = useRef(false) 56 57 const onShouldStartLoadWithRequest = (event: ShouldStartLoadRequest) => { 58 const urlp = new URL(event.url) 59 return ALLOWED_HOSTS.includes(urlp.host) 60 } 61 62 const onNavigationStateChange = (e: WebViewNavigation) => { 63 if (wasSuccessful.current) return 64 65 const urlp = new URL(e.url) 66 if (urlp.host !== redirectHost || urlp.pathname === '/gate/signup') return 67 68 const code = urlp.searchParams.get('code') 69 if (urlp.searchParams.get('state') !== stateParam || !code) { 70 onError({error: 'Invalid state or code'}) 71 return 72 } 73 74 // We want to delay the completion of this screen ever so slightly so that it doesn't appear to be a glitch if it completes too fast 75 wasSuccessful.current = true 76 onComplete() 77 const now = Date.now() 78 const timeTaken = now - startedAt.current 79 if (timeTaken < MIN_DELAY) { 80 successTo.current = setTimeout(() => { 81 onSuccess(code) 82 }, MIN_DELAY - timeTaken) 83 } else { 84 onSuccess(code) 85 } 86 } 87 88 return ( 89 <WebView 90 source={{uri: url}} 91 javaScriptEnabled 92 style={{ 93 flex: 1, 94 backgroundColor: 'transparent', 95 borderRadius: 10, 96 }} 97 onShouldStartLoadWithRequest={onShouldStartLoadWithRequest} 98 onNavigationStateChange={onNavigationStateChange} 99 scrollEnabled={false} 100 onError={e => { 101 onError(e.nativeEvent) 102 }} 103 onHttpError={e => { 104 onError(e.nativeEvent) 105 }} 106 /> 107 ) 108}