diff options
Diffstat (limited to 'src/screens/Signup/StepCaptcha/CaptchaWebView.tsx')
-rw-r--r-- | src/screens/Signup/StepCaptcha/CaptchaWebView.tsx | 88 |
1 files changed, 50 insertions, 38 deletions
diff --git a/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx b/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx index caa0aa28a..27951d4cc 100644 --- a/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx +++ b/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx @@ -1,20 +1,22 @@ -import React from 'react' -import {StyleSheet} from 'react-native' -import {WebView, WebViewNavigation} from 'react-native-webview' -import {ShouldStartLoadRequest} from 'react-native-webview/lib/WebViewTypes' +import {useEffect, useMemo, useRef} from 'react' +import {WebView, type WebViewNavigation} from 'react-native-webview' +import {type ShouldStartLoadRequest} from 'react-native-webview/lib/WebViewTypes' -import {SignupState} from '#/screens/Signup/state' +import {type SignupState} from '#/screens/Signup/state' const ALLOWED_HOSTS = [ 'bsky.social', 'bsky.app', 'staging.bsky.app', 'staging.bsky.dev', + 'app.staging.bsky.dev', 'js.hcaptcha.com', 'newassets.hcaptcha.com', 'api2.hcaptcha.com', ] +const MIN_DELAY = 3_500 + export function CaptchaWebView({ url, stateParam, @@ -28,49 +30,67 @@ export function CaptchaWebView({ onSuccess: (code: string) => void onError: (error: unknown) => void }) { - const redirectHost = React.useMemo(() => { + const startedAt = useRef(Date.now()) + const successTo = useRef<NodeJS.Timeout>() + + useEffect(() => { + return () => { + if (successTo.current) { + clearTimeout(successTo.current) + } + } + }, []) + + const redirectHost = useMemo(() => { if (!state?.serviceUrl) return 'bsky.app' return state?.serviceUrl && new URL(state?.serviceUrl).host === 'staging.bsky.dev' - ? 'staging.bsky.app' + ? 'app.staging.bsky.dev' : 'bsky.app' }, [state?.serviceUrl]) - const wasSuccessful = React.useRef(false) + const wasSuccessful = useRef(false) - const onShouldStartLoadWithRequest = React.useCallback( - (event: ShouldStartLoadRequest) => { - const urlp = new URL(event.url) - return ALLOWED_HOSTS.includes(urlp.host) - }, - [], - ) + const onShouldStartLoadWithRequest = (event: ShouldStartLoadRequest) => { + const urlp = new URL(event.url) + return ALLOWED_HOSTS.includes(urlp.host) + } - const onNavigationStateChange = React.useCallback( - (e: WebViewNavigation) => { - if (wasSuccessful.current) return + const onNavigationStateChange = (e: WebViewNavigation) => { + if (wasSuccessful.current) return - const urlp = new URL(e.url) - if (urlp.host !== redirectHost) return + const urlp = new URL(e.url) + if (urlp.host !== redirectHost || urlp.pathname === '/gate/signup') return - const code = urlp.searchParams.get('code') - if (urlp.searchParams.get('state') !== stateParam || !code) { - onError({error: 'Invalid state or code'}) - return - } + const code = urlp.searchParams.get('code') + if (urlp.searchParams.get('state') !== stateParam || !code) { + onError({error: 'Invalid state or code'}) + return + } - wasSuccessful.current = true + // 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 + wasSuccessful.current = true + const now = Date.now() + const timeTaken = now - startedAt.current + if (timeTaken < MIN_DELAY) { + successTo.current = setTimeout(() => { + onSuccess(code) + }, MIN_DELAY - timeTaken) + } else { onSuccess(code) - }, - [redirectHost, stateParam, onSuccess, onError], - ) + } + } return ( <WebView source={{uri: url}} javaScriptEnabled - style={styles.webview} + style={{ + flex: 1, + backgroundColor: 'transparent', + borderRadius: 10, + }} onShouldStartLoadWithRequest={onShouldStartLoadWithRequest} onNavigationStateChange={onNavigationStateChange} scrollEnabled={false} @@ -83,11 +103,3 @@ export function CaptchaWebView({ /> ) } - -const styles = StyleSheet.create({ - webview: { - flex: 1, - backgroundColor: 'transparent', - borderRadius: 10, - }, -}) |