diff --git a/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx b/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx index 50918c4c..caa0aa28 100644 --- a/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx +++ b/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx @@ -26,7 +26,7 @@ export function CaptchaWebView({ stateParam: string state?: SignupState onSuccess: (code: string) => void - onError: () => void + onError: (error: unknown) => void }) { const redirectHost = React.useMemo(() => { if (!state?.serviceUrl) return 'bsky.app' @@ -56,7 +56,7 @@ export function CaptchaWebView({ const code = urlp.searchParams.get('code') if (urlp.searchParams.get('state') !== stateParam || !code) { - onError() + onError({error: 'Invalid state or code'}) return } @@ -74,6 +74,12 @@ export function CaptchaWebView({ onShouldStartLoadWithRequest={onShouldStartLoadWithRequest} onNavigationStateChange={onNavigationStateChange} scrollEnabled={false} + onError={e => { + onError(e.nativeEvent) + }} + onHttpError={e => { + onError(e.nativeEvent) + }} /> ) } diff --git a/src/screens/Signup/StepCaptcha/CaptchaWebView.web.tsx b/src/screens/Signup/StepCaptcha/CaptchaWebView.web.tsx index 7791a58d..8faaf90a 100644 --- a/src/screens/Signup/StepCaptcha/CaptchaWebView.web.tsx +++ b/src/screens/Signup/StepCaptcha/CaptchaWebView.web.tsx @@ -13,8 +13,20 @@ export function CaptchaWebView({ url: string stateParam: string onSuccess: (code: string) => void - onError: () => void + onError: (error: unknown) => void }) { + React.useEffect(() => { + const timeout = setTimeout(() => { + onError({ + errorMessage: 'User did not complete the captcha within 30 seconds', + }) + }, 30e3) + + return () => { + clearTimeout(timeout) + } + }, [onError]) + const onLoad = React.useCallback(() => { // @ts-ignore web const frame: HTMLIFrameElement = document.getElementById( @@ -32,12 +44,14 @@ export function CaptchaWebView({ const code = urlp.searchParams.get('code') if (urlp.searchParams.get('state') !== stateParam || !code) { - onError() + onError({error: 'Invalid state or code'}) return } onSuccess(code) - } catch (e) { - // We don't need to handle this + } catch (e: unknown) { + // We don't actually want to record an error here, because this will happen quite a bit. We will only be able to + // get hte 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]) diff --git a/src/screens/Signup/StepCaptcha/index.tsx b/src/screens/Signup/StepCaptcha/index.tsx index 2429b0c5..d0fc4e93 100644 --- a/src/screens/Signup/StepCaptcha/index.tsx +++ b/src/screens/Signup/StepCaptcha/index.tsx @@ -5,6 +5,7 @@ import {useLingui} from '@lingui/react' import {nanoid} from 'nanoid/non-secure' import {createFullHandle} from '#/lib/strings/handles' +import {logger} from '#/logger' import {ScreenTransition} from '#/screens/Login/ScreenTransition' import {useSignupContext, useSubmitSignup} from '#/screens/Signup/state' import {CaptchaWebView} from '#/screens/Signup/StepCaptcha/CaptchaWebView' @@ -43,12 +44,19 @@ export function StepCaptcha() { [submit], ) - const onError = React.useCallback(() => { - dispatch({ - type: 'setError', - value: _(msg`Error receiving captcha response.`), - }) - }, [_, dispatch]) + const onError = React.useCallback( + (error?: unknown) => { + dispatch({ + type: 'setError', + value: _(msg`Error receiving captcha response.`), + }) + logger.error('Signup Flow Error', { + registrationHandle: state.handle, + error, + }) + }, + [_, dispatch, state.handle], + ) return ( diff --git a/src/screens/Signup/index.tsx b/src/screens/Signup/index.tsx index 6758f7fa..5e2596d8 100644 --- a/src/screens/Signup/index.tsx +++ b/src/screens/Signup/index.tsx @@ -8,6 +8,7 @@ import {useAnalytics} from '#/lib/analytics/analytics' import {FEEDBACK_FORM_URL} from '#/lib/constants' import {logEvent} from '#/lib/statsig/statsig' import {createFullHandle} from '#/lib/strings/handles' +import {logger} from '#/logger' import {useServiceQuery} from '#/state/queries/service' import {useAgent} from '#/state/session' import {LoggedOutLayout} from '#/view/com/util/layouts/LoggedOutLayout' @@ -119,11 +120,19 @@ export function Signup({onPressBack}: {onPressBack: () => void}) { const onBackPress = React.useCallback(() => { if (state.activeStep !== SignupStep.INFO) { + if (state.activeStep === SignupStep.CAPTCHA) { + logger.error('Signup Flow Error', { + errorMessage: + 'User went back from captcha step. Possibly encountered an error.', + registrationHandle: state.handle, + }) + } + dispatch({type: 'prev'}) } else { onPressBack() } - }, [onPressBack, state.activeStep]) + }, [onPressBack, state.activeStep, state.handle]) return ( diff --git a/src/screens/Signup/state.ts b/src/screens/Signup/state.ts index 86a14436..d6cf9c44 100644 --- a/src/screens/Signup/state.ts +++ b/src/screens/Signup/state.ts @@ -246,6 +246,10 @@ export function useSubmitSignup({ !verificationCode ) { dispatch({type: 'setStep', value: SignupStep.CAPTCHA}) + logger.error('Signup Flow Error', { + errorMessage: 'Verification captcha code was not set.', + registrationHandle: state.handle, + }) return dispatch({ type: 'setError', value: _(msg`Please complete the verification captcha.`), @@ -282,20 +286,17 @@ export function useSubmitSignup({ return } - if ([400, 429].includes(e.status)) { - logger.warn('Failed to create account', {message: e}) - } else { - logger.error(`Failed to create account (${e.status} status)`, { - message: e, - }) - } - const error = cleanError(errMsg) const isHandleError = error.toLowerCase().includes('handle') dispatch({type: 'setIsLoading', value: false}) - dispatch({type: 'setError', value: cleanError(errMsg)}) + dispatch({type: 'setError', value: error}) dispatch({type: 'setStep', value: isHandleError ? 2 : 1}) + + logger.error('Signup Flow Error', { + errorMessage: error, + registrationHandle: state.handle, + }) } finally { dispatch({type: 'setIsLoading', value: false}) }