Bluesky app fork with some witchin' additions 馃挮
at readme-update 110 lines 2.8 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 'witchsky.app', 9 'witchsky.social', 10 'bsky.social', 11 'bsky.app', 12 'staging.bsky.app', 13 'staging.bsky.dev', 14 'app.staging.bsky.dev', 15 'js.hcaptcha.com', 16 'newassets.hcaptcha.com', 17 'api2.hcaptcha.com', 18] 19 20const MIN_DELAY = 3_500 21 22export function CaptchaWebView({ 23 url, 24 stateParam, 25 state, 26 onComplete, 27 onSuccess, 28 onError, 29}: { 30 url: string 31 stateParam: string 32 state?: SignupState 33 onComplete: () => void 34 onSuccess: (code: string) => void 35 onError: (error: unknown) => void 36}) { 37 const startedAt = useRef(Date.now()) 38 const successTo = useRef<NodeJS.Timeout>(undefined) 39 40 useEffect(() => { 41 return () => { 42 if (successTo.current) { 43 clearTimeout(successTo.current) 44 } 45 } 46 }, []) 47 48 const redirectHost = useMemo(() => { 49 if (!state?.serviceUrl) return 'bsky.app' 50 51 return state?.serviceUrl && 52 new URL(state?.serviceUrl).host === 'staging.bsky.dev' 53 ? 'app.staging.bsky.dev' 54 : 'witchsky.app' 55 }, [state?.serviceUrl]) 56 57 const wasSuccessful = useRef(false) 58 59 const onShouldStartLoadWithRequest = (event: ShouldStartLoadRequest) => { 60 const urlp = new URL(event.url) 61 return ALLOWED_HOSTS.includes(urlp.host) 62 } 63 64 const onNavigationStateChange = (e: WebViewNavigation) => { 65 if (wasSuccessful.current) return 66 67 const urlp = new URL(e.url) 68 if (urlp.host !== redirectHost || urlp.pathname === '/gate/signup') return 69 70 const code = urlp.searchParams.get('code') 71 if (urlp.searchParams.get('state') !== stateParam || !code) { 72 onError({error: 'Invalid state or code'}) 73 return 74 } 75 76 // 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 77 wasSuccessful.current = true 78 onComplete() 79 const now = Date.now() 80 const timeTaken = now - startedAt.current 81 if (timeTaken < MIN_DELAY) { 82 successTo.current = setTimeout(() => { 83 onSuccess(code) 84 }, MIN_DELAY - timeTaken) 85 } else { 86 onSuccess(code) 87 } 88 } 89 90 return ( 91 <WebView 92 source={{uri: url}} 93 javaScriptEnabled 94 style={{ 95 flex: 1, 96 backgroundColor: 'transparent', 97 borderRadius: 10, 98 }} 99 onShouldStartLoadWithRequest={onShouldStartLoadWithRequest} 100 onNavigationStateChange={onNavigationStateChange} 101 scrollEnabled={false} 102 onError={e => { 103 onError(e.nativeEvent) 104 }} 105 onHttpError={e => { 106 onError(e.nativeEvent) 107 }} 108 /> 109 ) 110}