about summary refs log tree commit diff
path: root/src/screens/Signup/state.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/screens/Signup/state.ts')
-rw-r--r--src/screens/Signup/state.ts90
1 files changed, 79 insertions, 11 deletions
diff --git a/src/screens/Signup/state.ts b/src/screens/Signup/state.ts
index 3daf36a9b..48ea4ccd9 100644
--- a/src/screens/Signup/state.ts
+++ b/src/screens/Signup/state.ts
@@ -2,7 +2,7 @@ import React, {useCallback} from 'react'
 import {LayoutAnimation} from 'react-native'
 import {
   ComAtprotoServerCreateAccount,
-  ComAtprotoServerDescribeServer,
+  type ComAtprotoServerDescribeServer,
 } from '@atproto/api'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
@@ -56,6 +56,11 @@ export type SignupState = {
   isLoading: boolean
 
   pendingSubmit: null | SubmitTask
+
+  // Tracking
+  signupStartTime: number
+  fieldErrors: Record<ErrorField, number>
+  backgroundCount: number
 }
 
 export type SignupAction =
@@ -74,6 +79,7 @@ export type SignupAction =
   | {type: 'clearError'}
   | {type: 'setIsLoading'; value: boolean}
   | {type: 'submit'; task: SubmitTask}
+  | {type: 'incrementBackgroundCount'}
 
 export const initialState: SignupState = {
   hasPrev: false,
@@ -93,6 +99,17 @@ export const initialState: SignupState = {
   isLoading: false,
 
   pendingSubmit: null,
+
+  // Tracking
+  signupStartTime: Date.now(),
+  fieldErrors: {
+    'invite-code': 0,
+    email: 0,
+    handle: 0,
+    password: 0,
+    'date-of-birth': 0,
+  },
+  backgroundCount: 0,
 }
 
 export function is13(date: Date) {
@@ -169,6 +186,23 @@ export function reducer(s: SignupState, a: SignupAction): SignupState {
     case 'setError': {
       next.error = a.value
       next.errorField = a.field
+
+      // Track field errors
+      if (a.field) {
+        next.fieldErrors[a.field] = (next.fieldErrors[a.field] || 0) + 1
+
+        // Log the field error
+        logger.metric(
+          'signup:fieldError',
+          {
+            field: a.field,
+            errorCount: next.fieldErrors[a.field],
+            errorMessage: a.value,
+            activeStep: next.activeStep,
+          },
+          {statsig: true},
+        )
+      }
       break
     }
     case 'clearError': {
@@ -180,6 +214,20 @@ export function reducer(s: SignupState, a: SignupAction): SignupState {
       next.pendingSubmit = a.task
       break
     }
+    case 'incrementBackgroundCount': {
+      next.backgroundCount = s.backgroundCount + 1
+
+      // Log background/foreground event during signup
+      logger.metric(
+        'signup:backgrounded',
+        {
+          activeStep: next.activeStep,
+          backgroundCount: next.backgroundCount,
+        },
+        {statsig: true},
+      )
+      break
+    }
   }
 
   next.hasPrev = next.activeStep !== SignupStep.INFO
@@ -212,6 +260,7 @@ export function useSubmitSignup() {
         return dispatch({
           type: 'setError',
           value: _(msg`Please enter your email.`),
+          field: 'email',
         })
       }
       if (!EmailValidator.validate(state.email)) {
@@ -219,6 +268,7 @@ export function useSubmitSignup() {
         return dispatch({
           type: 'setError',
           value: _(msg`Your email appears to be invalid.`),
+          field: 'email',
         })
       }
       if (!state.password) {
@@ -226,6 +276,7 @@ export function useSubmitSignup() {
         return dispatch({
           type: 'setError',
           value: _(msg`Please choose your password.`),
+          field: 'password',
         })
       }
       if (!state.handle) {
@@ -233,6 +284,7 @@ export function useSubmitSignup() {
         return dispatch({
           type: 'setError',
           value: _(msg`Please choose your handle.`),
+          field: 'handle',
         })
       }
       if (
@@ -253,15 +305,26 @@ export function useSubmitSignup() {
       dispatch({type: 'setIsLoading', value: true})
 
       try {
-        await createAccount({
-          service: state.serviceUrl,
-          email: state.email,
-          handle: createFullHandle(state.handle, state.userDomain),
-          password: state.password,
-          birthDate: state.dateOfBirth,
-          inviteCode: state.inviteCode.trim(),
-          verificationCode: state.pendingSubmit?.verificationCode,
-        })
+        await createAccount(
+          {
+            service: state.serviceUrl,
+            email: state.email,
+            handle: createFullHandle(state.handle, state.userDomain),
+            password: state.password,
+            birthDate: state.dateOfBirth,
+            inviteCode: state.inviteCode.trim(),
+            verificationCode: state.pendingSubmit?.verificationCode,
+          },
+          {
+            signupDuration: Date.now() - state.signupStartTime,
+            fieldErrorsTotal: Object.values(state.fieldErrors).reduce(
+              (a, b) => a + b,
+              0,
+            ),
+            backgroundCount: state.backgroundCount,
+          },
+        )
+
         /*
          * Must happen last so that if the user has multiple tabs open and
          * createAccount fails, one tab is not stuck in onboarding — Eric
@@ -275,6 +338,7 @@ export function useSubmitSignup() {
             value: _(
               msg`Invite code not accepted. Check that you input it correctly and try again.`,
             ),
+            field: 'invite-code',
           })
           dispatch({type: 'setStep', value: SignupStep.INFO})
           return
@@ -284,7 +348,11 @@ export function useSubmitSignup() {
         const isHandleError = error.toLowerCase().includes('handle')
 
         dispatch({type: 'setIsLoading', value: false})
-        dispatch({type: 'setError', value: error})
+        dispatch({
+          type: 'setError',
+          value: error,
+          field: isHandleError ? 'handle' : undefined,
+        })
         dispatch({type: 'setStep', value: isHandleError ? 2 : 1})
 
         logger.error('Signup Flow Error', {