Instrument signup (#8037)
This commit is contained in:
+16
-1
@@ -51,6 +51,17 @@ export type MetricEvents = {
|
|||||||
}
|
}
|
||||||
'signup:captchaSuccess': {}
|
'signup:captchaSuccess': {}
|
||||||
'signup:captchaFailure': {}
|
'signup:captchaFailure': {}
|
||||||
|
'signup:fieldError': {
|
||||||
|
field: string
|
||||||
|
errorCount: number
|
||||||
|
errorMessage: string
|
||||||
|
activeStep: number
|
||||||
|
}
|
||||||
|
'signup:backgrounded': {
|
||||||
|
activeStep: number
|
||||||
|
backgroundCount: number
|
||||||
|
}
|
||||||
|
'signup:handleTaken': {}
|
||||||
'signin:hostingProviderPressed': {
|
'signin:hostingProviderPressed': {
|
||||||
hostingProviderDidChange: boolean
|
hostingProviderDidChange: boolean
|
||||||
}
|
}
|
||||||
@@ -135,7 +146,11 @@ export type MetricEvents = {
|
|||||||
|
|
||||||
// Data events
|
// Data events
|
||||||
'account:create:begin': {}
|
'account:create:begin': {}
|
||||||
'account:create:success': {}
|
'account:create:success': {
|
||||||
|
signupDuration: number
|
||||||
|
fieldErrorsTotal: number
|
||||||
|
backgroundCount: number
|
||||||
|
}
|
||||||
'post:create': {
|
'post:create': {
|
||||||
imageCount: number
|
imageCount: number
|
||||||
isReply: boolean
|
isReply: boolean
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {msg} from '@lingui/macro'
|
|||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {nanoid} from 'nanoid/non-secure'
|
import {nanoid} from 'nanoid/non-secure'
|
||||||
|
|
||||||
import {logEvent} from '#/lib/statsig/statsig'
|
|
||||||
import {createFullHandle} from '#/lib/strings/handles'
|
import {createFullHandle} from '#/lib/strings/handles'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {ScreenTransition} from '#/screens/Login/ScreenTransition'
|
import {ScreenTransition} from '#/screens/Login/ScreenTransition'
|
||||||
@@ -40,7 +39,7 @@ export function StepCaptcha() {
|
|||||||
const onSuccess = React.useCallback(
|
const onSuccess = React.useCallback(
|
||||||
(code: string) => {
|
(code: string) => {
|
||||||
setCompleted(true)
|
setCompleted(true)
|
||||||
logEvent('signup:captchaSuccess', {})
|
logger.metric('signup:captchaSuccess', {}, {statsig: true})
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
task: {verificationCode: code, mutableProcessed: false},
|
task: {verificationCode: code, mutableProcessed: false},
|
||||||
@@ -55,7 +54,7 @@ export function StepCaptcha() {
|
|||||||
type: 'setError',
|
type: 'setError',
|
||||||
value: _(msg`Error receiving captcha response.`),
|
value: _(msg`Error receiving captcha response.`),
|
||||||
})
|
})
|
||||||
logEvent('signup:captchaFailure', {})
|
logger.metric('signup:captchaFailure', {}, {statsig: true})
|
||||||
logger.error('Signup Flow Error', {
|
logger.error('Signup Flow Error', {
|
||||||
registrationHandle: state.handle,
|
registrationHandle: state.handle,
|
||||||
error,
|
error,
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import {View} from 'react-native'
|
|||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
import {logEvent} from '#/lib/statsig/statsig'
|
|
||||||
import {
|
import {
|
||||||
createFullHandle,
|
createFullHandle,
|
||||||
MAX_SERVICE_HANDLE_LENGTH,
|
MAX_SERVICE_HANDLE_LENGTH,
|
||||||
validateServiceHandle,
|
validateServiceHandle,
|
||||||
} from '#/lib/strings/handles'
|
} from '#/lib/strings/handles'
|
||||||
|
import {logger} from '#/logger'
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
import {ScreenTransition} from '#/screens/Login/ScreenTransition'
|
import {ScreenTransition} from '#/screens/Login/ScreenTransition'
|
||||||
import {useSignupContext} from '#/screens/Signup/state'
|
import {useSignupContext} from '#/screens/Signup/state'
|
||||||
@@ -53,7 +53,9 @@ export function StepHandle() {
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: 'setError',
|
type: 'setError',
|
||||||
value: _(msg`That handle is already taken.`),
|
value: _(msg`That handle is already taken.`),
|
||||||
|
field: 'handle',
|
||||||
})
|
})
|
||||||
|
logger.metric('signup:handleTaken', {})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -62,11 +64,15 @@ export function StepHandle() {
|
|||||||
dispatch({type: 'setIsLoading', value: false})
|
dispatch({type: 'setIsLoading', value: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
logEvent('signup:nextPressed', {
|
logger.metric(
|
||||||
|
'signup:nextPressed',
|
||||||
|
{
|
||||||
activeStep: state.activeStep,
|
activeStep: state.activeStep,
|
||||||
phoneVerificationRequired:
|
phoneVerificationRequired:
|
||||||
state.serviceDescription?.phoneVerificationRequired,
|
state.serviceDescription?.phoneVerificationRequired,
|
||||||
})
|
},
|
||||||
|
{statsig: true},
|
||||||
|
)
|
||||||
// phoneVerificationRequired is actually whether a captcha is required
|
// phoneVerificationRequired is actually whether a captcha is required
|
||||||
if (!state.serviceDescription?.phoneVerificationRequired) {
|
if (!state.serviceDescription?.phoneVerificationRequired) {
|
||||||
dispatch({
|
dispatch({
|
||||||
@@ -92,9 +98,11 @@ export function StepHandle() {
|
|||||||
value: handle,
|
value: handle,
|
||||||
})
|
})
|
||||||
dispatch({type: 'prev'})
|
dispatch({type: 'prev'})
|
||||||
logEvent('signup:backPressed', {
|
logger.metric(
|
||||||
activeStep: state.activeStep,
|
'signup:backPressed',
|
||||||
})
|
{activeStep: state.activeStep},
|
||||||
|
{statsig: true},
|
||||||
|
)
|
||||||
}, [dispatch, state.activeStep])
|
}, [dispatch, state.activeStep])
|
||||||
|
|
||||||
const validCheck = validateServiceHandle(draftValue, state.userDomain)
|
const validCheck = validateServiceHandle(draftValue, state.userDomain)
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import React, {useRef} from 'react'
|
import React, {useRef} from 'react'
|
||||||
import {TextInput, View} from 'react-native'
|
import {type TextInput, View} from 'react-native'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import * as EmailValidator from 'email-validator'
|
import * as EmailValidator from 'email-validator'
|
||||||
import type tldts from 'tldts'
|
import type tldts from 'tldts'
|
||||||
|
|
||||||
import {logEvent} from '#/lib/statsig/statsig'
|
|
||||||
import {isEmailMaybeInvalid} from '#/lib/strings/email'
|
import {isEmailMaybeInvalid} from '#/lib/strings/email'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {ScreenTransition} from '#/screens/Login/ScreenTransition'
|
import {ScreenTransition} from '#/screens/Login/ScreenTransition'
|
||||||
@@ -13,7 +12,7 @@ import {is13, is18, useSignupContext} from '#/screens/Signup/state'
|
|||||||
import {Policies} from '#/screens/Signup/StepInfo/Policies'
|
import {Policies} from '#/screens/Signup/StepInfo/Policies'
|
||||||
import {atoms as a, native} from '#/alf'
|
import {atoms as a, native} from '#/alf'
|
||||||
import * as DateField from '#/components/forms/DateField'
|
import * as DateField from '#/components/forms/DateField'
|
||||||
import {DateFieldRef} from '#/components/forms/DateField/types'
|
import {type DateFieldRef} from '#/components/forms/DateField/types'
|
||||||
import {FormError} from '#/components/forms/FormError'
|
import {FormError} from '#/components/forms/FormError'
|
||||||
import {HostingProvider} from '#/components/forms/HostingProvider'
|
import {HostingProvider} from '#/components/forms/HostingProvider'
|
||||||
import * as TextField from '#/components/forms/TextField'
|
import * as TextField from '#/components/forms/TextField'
|
||||||
@@ -134,9 +133,13 @@ export function StepInfo({
|
|||||||
dispatch({type: 'setEmail', value: email})
|
dispatch({type: 'setEmail', value: email})
|
||||||
dispatch({type: 'setPassword', value: password})
|
dispatch({type: 'setPassword', value: password})
|
||||||
dispatch({type: 'next'})
|
dispatch({type: 'next'})
|
||||||
logEvent('signup:nextPressed', {
|
logger.metric(
|
||||||
|
'signup:nextPressed',
|
||||||
|
{
|
||||||
activeStep: state.activeStep,
|
activeStep: state.activeStep,
|
||||||
})
|
},
|
||||||
|
{statsig: true},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import {useEffect, useReducer, useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {AppState, type AppStateStatus, View} from 'react-native'
|
||||||
import Animated, {FadeIn, LayoutAnimationConfig} from 'react-native-reanimated'
|
import Animated, {FadeIn, LayoutAnimationConfig} from 'react-native-reanimated'
|
||||||
import {AppBskyGraphStarterpack} from '@atproto/api'
|
import {AppBskyGraphStarterpack} from '@atproto/api'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
@@ -31,7 +31,7 @@ import * as bsky from '#/types/bsky'
|
|||||||
export function Signup({onPressBack}: {onPressBack: () => void}) {
|
export function Signup({onPressBack}: {onPressBack: () => void}) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const [state, dispatch] = React.useReducer(reducer, initialState)
|
const [state, dispatch] = useReducer(reducer, initialState)
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
const submit = useSubmitSignup()
|
const submit = useSubmitSignup()
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
|
|||||||
uri: activeStarterPack?.uri,
|
uri: activeStarterPack?.uri,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [isFetchedAtMount] = React.useState(starterPack != null)
|
const [isFetchedAtMount] = useState(starterPack != null)
|
||||||
const showStarterPackCard =
|
const showStarterPackCard =
|
||||||
activeStarterPack?.uri && !isFetchingStarterPack && starterPack
|
activeStarterPack?.uri && !isFetchingStarterPack && starterPack
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
|
|||||||
refetch,
|
refetch,
|
||||||
} = useServiceQuery(state.serviceUrl)
|
} = useServiceQuery(state.serviceUrl)
|
||||||
|
|
||||||
React.useEffect(() => {
|
useEffect(() => {
|
||||||
if (isFetching) {
|
if (isFetching) {
|
||||||
dispatch({type: 'setIsLoading', value: true})
|
dispatch({type: 'setIsLoading', value: true})
|
||||||
} else if (!isFetching) {
|
} else if (!isFetching) {
|
||||||
@@ -63,7 +63,7 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
|
|||||||
}
|
}
|
||||||
}, [isFetching])
|
}, [isFetching])
|
||||||
|
|
||||||
React.useEffect(() => {
|
useEffect(() => {
|
||||||
if (isError) {
|
if (isError) {
|
||||||
dispatch({type: 'setServiceDescription', value: undefined})
|
dispatch({type: 'setServiceDescription', value: undefined})
|
||||||
dispatch({
|
dispatch({
|
||||||
@@ -78,7 +78,7 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
|
|||||||
}
|
}
|
||||||
}, [_, serviceInfo, isError])
|
}, [_, serviceInfo, isError])
|
||||||
|
|
||||||
React.useEffect(() => {
|
useEffect(() => {
|
||||||
if (state.pendingSubmit) {
|
if (state.pendingSubmit) {
|
||||||
if (!state.pendingSubmit.mutableProcessed) {
|
if (!state.pendingSubmit.mutableProcessed) {
|
||||||
state.pendingSubmit.mutableProcessed = true
|
state.pendingSubmit.mutableProcessed = true
|
||||||
@@ -87,6 +87,20 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
|
|||||||
}
|
}
|
||||||
}, [state, dispatch, submit])
|
}, [state, dispatch, submit])
|
||||||
|
|
||||||
|
// Track app backgrounding during signup
|
||||||
|
useEffect(() => {
|
||||||
|
const subscription = AppState.addEventListener(
|
||||||
|
'change',
|
||||||
|
(nextAppState: AppStateStatus) => {
|
||||||
|
if (nextAppState === 'background') {
|
||||||
|
dispatch({type: 'incrementBackgroundCount'})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return () => subscription.remove()
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SignupContext.Provider value={{state, dispatch}}>
|
<SignupContext.Provider value={{state, dispatch}}>
|
||||||
<LoggedOutLayout
|
<LoggedOutLayout
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, {useCallback} from 'react'
|
|||||||
import {LayoutAnimation} from 'react-native'
|
import {LayoutAnimation} from 'react-native'
|
||||||
import {
|
import {
|
||||||
ComAtprotoServerCreateAccount,
|
ComAtprotoServerCreateAccount,
|
||||||
ComAtprotoServerDescribeServer,
|
type ComAtprotoServerDescribeServer,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
@@ -56,6 +56,11 @@ export type SignupState = {
|
|||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
|
|
||||||
pendingSubmit: null | SubmitTask
|
pendingSubmit: null | SubmitTask
|
||||||
|
|
||||||
|
// Tracking
|
||||||
|
signupStartTime: number
|
||||||
|
fieldErrors: Record<ErrorField, number>
|
||||||
|
backgroundCount: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SignupAction =
|
export type SignupAction =
|
||||||
@@ -74,6 +79,7 @@ export type SignupAction =
|
|||||||
| {type: 'clearError'}
|
| {type: 'clearError'}
|
||||||
| {type: 'setIsLoading'; value: boolean}
|
| {type: 'setIsLoading'; value: boolean}
|
||||||
| {type: 'submit'; task: SubmitTask}
|
| {type: 'submit'; task: SubmitTask}
|
||||||
|
| {type: 'incrementBackgroundCount'}
|
||||||
|
|
||||||
export const initialState: SignupState = {
|
export const initialState: SignupState = {
|
||||||
hasPrev: false,
|
hasPrev: false,
|
||||||
@@ -93,6 +99,17 @@ export const initialState: SignupState = {
|
|||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
|
||||||
pendingSubmit: null,
|
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) {
|
export function is13(date: Date) {
|
||||||
@@ -169,6 +186,23 @@ export function reducer(s: SignupState, a: SignupAction): SignupState {
|
|||||||
case 'setError': {
|
case 'setError': {
|
||||||
next.error = a.value
|
next.error = a.value
|
||||||
next.errorField = a.field
|
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
|
break
|
||||||
}
|
}
|
||||||
case 'clearError': {
|
case 'clearError': {
|
||||||
@@ -180,6 +214,20 @@ export function reducer(s: SignupState, a: SignupAction): SignupState {
|
|||||||
next.pendingSubmit = a.task
|
next.pendingSubmit = a.task
|
||||||
break
|
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
|
next.hasPrev = next.activeStep !== SignupStep.INFO
|
||||||
@@ -212,6 +260,7 @@ export function useSubmitSignup() {
|
|||||||
return dispatch({
|
return dispatch({
|
||||||
type: 'setError',
|
type: 'setError',
|
||||||
value: _(msg`Please enter your email.`),
|
value: _(msg`Please enter your email.`),
|
||||||
|
field: 'email',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (!EmailValidator.validate(state.email)) {
|
if (!EmailValidator.validate(state.email)) {
|
||||||
@@ -219,6 +268,7 @@ export function useSubmitSignup() {
|
|||||||
return dispatch({
|
return dispatch({
|
||||||
type: 'setError',
|
type: 'setError',
|
||||||
value: _(msg`Your email appears to be invalid.`),
|
value: _(msg`Your email appears to be invalid.`),
|
||||||
|
field: 'email',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (!state.password) {
|
if (!state.password) {
|
||||||
@@ -226,6 +276,7 @@ export function useSubmitSignup() {
|
|||||||
return dispatch({
|
return dispatch({
|
||||||
type: 'setError',
|
type: 'setError',
|
||||||
value: _(msg`Please choose your password.`),
|
value: _(msg`Please choose your password.`),
|
||||||
|
field: 'password',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (!state.handle) {
|
if (!state.handle) {
|
||||||
@@ -233,6 +284,7 @@ export function useSubmitSignup() {
|
|||||||
return dispatch({
|
return dispatch({
|
||||||
type: 'setError',
|
type: 'setError',
|
||||||
value: _(msg`Please choose your handle.`),
|
value: _(msg`Please choose your handle.`),
|
||||||
|
field: 'handle',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
@@ -253,7 +305,8 @@ export function useSubmitSignup() {
|
|||||||
dispatch({type: 'setIsLoading', value: true})
|
dispatch({type: 'setIsLoading', value: true})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await createAccount({
|
await createAccount(
|
||||||
|
{
|
||||||
service: state.serviceUrl,
|
service: state.serviceUrl,
|
||||||
email: state.email,
|
email: state.email,
|
||||||
handle: createFullHandle(state.handle, state.userDomain),
|
handle: createFullHandle(state.handle, state.userDomain),
|
||||||
@@ -261,7 +314,17 @@ export function useSubmitSignup() {
|
|||||||
birthDate: state.dateOfBirth,
|
birthDate: state.dateOfBirth,
|
||||||
inviteCode: state.inviteCode.trim(),
|
inviteCode: state.inviteCode.trim(),
|
||||||
verificationCode: state.pendingSubmit?.verificationCode,
|
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
|
* Must happen last so that if the user has multiple tabs open and
|
||||||
* createAccount fails, one tab is not stuck in onboarding — Eric
|
* createAccount fails, one tab is not stuck in onboarding — Eric
|
||||||
@@ -275,6 +338,7 @@ export function useSubmitSignup() {
|
|||||||
value: _(
|
value: _(
|
||||||
msg`Invite code not accepted. Check that you input it correctly and try again.`,
|
msg`Invite code not accepted. Check that you input it correctly and try again.`,
|
||||||
),
|
),
|
||||||
|
field: 'invite-code',
|
||||||
})
|
})
|
||||||
dispatch({type: 'setStep', value: SignupStep.INFO})
|
dispatch({type: 'setStep', value: SignupStep.INFO})
|
||||||
return
|
return
|
||||||
@@ -284,7 +348,11 @@ export function useSubmitSignup() {
|
|||||||
const isHandleError = error.toLowerCase().includes('handle')
|
const isHandleError = error.toLowerCase().includes('handle')
|
||||||
|
|
||||||
dispatch({type: 'setIsLoading', value: false})
|
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})
|
dispatch({type: 'setStep', value: isHandleError ? 2 : 1})
|
||||||
|
|
||||||
logger.error('Signup Flow Error', {
|
logger.error('Signup Flow Error', {
|
||||||
|
|||||||
+25
-10
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {AtpSessionEvent, BskyAgent} from '@atproto/api'
|
import {type AtpSessionEvent, type BskyAgent} from '@atproto/api'
|
||||||
|
|
||||||
import {logEvent} from '#/lib/statsig/statsig'
|
|
||||||
import {isWeb} from '#/platform/detection'
|
import {isWeb} from '#/platform/detection'
|
||||||
import * as persisted from '#/state/persisted'
|
import * as persisted from '#/state/persisted'
|
||||||
import {useCloseAllActiveElements} from '#/state/util'
|
import {useCloseAllActiveElements} from '#/state/util'
|
||||||
@@ -9,7 +8,7 @@ import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
|
|||||||
import {emitSessionDropped} from '../events'
|
import {emitSessionDropped} from '../events'
|
||||||
import {
|
import {
|
||||||
agentToSessionAccount,
|
agentToSessionAccount,
|
||||||
BskyAppAgent,
|
type BskyAppAgent,
|
||||||
createAgentAndCreateAccount,
|
createAgentAndCreateAccount,
|
||||||
createAgentAndLogin,
|
createAgentAndLogin,
|
||||||
createAgentAndResume,
|
createAgentAndResume,
|
||||||
@@ -20,7 +19,11 @@ import {getInitialState, reducer} from './reducer'
|
|||||||
export {isSignupQueued} from './util'
|
export {isSignupQueued} from './util'
|
||||||
import {addSessionDebugLog} from './logging'
|
import {addSessionDebugLog} from './logging'
|
||||||
export type {SessionAccount} from '#/state/session/types'
|
export type {SessionAccount} from '#/state/session/types'
|
||||||
import {SessionApiContext, SessionStateContext} from '#/state/session/types'
|
import {logger} from '#/logger'
|
||||||
|
import {
|
||||||
|
type SessionApiContext,
|
||||||
|
type SessionStateContext,
|
||||||
|
} from '#/state/session/types'
|
||||||
|
|
||||||
const StateContext = React.createContext<SessionStateContext>({
|
const StateContext = React.createContext<SessionStateContext>({
|
||||||
accounts: [],
|
accounts: [],
|
||||||
@@ -65,10 +68,10 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const createAccount = React.useCallback<SessionApiContext['createAccount']>(
|
const createAccount = React.useCallback<SessionApiContext['createAccount']>(
|
||||||
async params => {
|
async (params, metrics) => {
|
||||||
addSessionDebugLog({type: 'method:start', method: 'createAccount'})
|
addSessionDebugLog({type: 'method:start', method: 'createAccount'})
|
||||||
const signal = cancelPendingTask()
|
const signal = cancelPendingTask()
|
||||||
logEvent('account:create:begin', {})
|
logger.metric('account:create:begin', {}, {statsig: true})
|
||||||
const {agent, account} = await createAgentAndCreateAccount(
|
const {agent, account} = await createAgentAndCreateAccount(
|
||||||
params,
|
params,
|
||||||
onAgentSessionChange,
|
onAgentSessionChange,
|
||||||
@@ -82,7 +85,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
newAgent: agent,
|
newAgent: agent,
|
||||||
newAccount: account,
|
newAccount: account,
|
||||||
})
|
})
|
||||||
logEvent('account:create:success', {})
|
logger.metric('account:create:success', metrics, {statsig: true})
|
||||||
addSessionDebugLog({type: 'method:end', method: 'createAccount', account})
|
addSessionDebugLog({type: 'method:end', method: 'createAccount', account})
|
||||||
},
|
},
|
||||||
[onAgentSessionChange, cancelPendingTask],
|
[onAgentSessionChange, cancelPendingTask],
|
||||||
@@ -105,7 +108,11 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
newAgent: agent,
|
newAgent: agent,
|
||||||
newAccount: account,
|
newAccount: account,
|
||||||
})
|
})
|
||||||
logEvent('account:loggedIn', {logContext, withPassword: true})
|
logger.metric(
|
||||||
|
'account:loggedIn',
|
||||||
|
{logContext, withPassword: true},
|
||||||
|
{statsig: true},
|
||||||
|
)
|
||||||
addSessionDebugLog({type: 'method:end', method: 'login', account})
|
addSessionDebugLog({type: 'method:end', method: 'login', account})
|
||||||
},
|
},
|
||||||
[onAgentSessionChange, cancelPendingTask],
|
[onAgentSessionChange, cancelPendingTask],
|
||||||
@@ -120,7 +127,11 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: 'logged-out-current-account',
|
type: 'logged-out-current-account',
|
||||||
})
|
})
|
||||||
logEvent('account:loggedOut', {logContext, scope: 'current'})
|
logger.metric(
|
||||||
|
'account:loggedOut',
|
||||||
|
{logContext, scope: 'current'},
|
||||||
|
{statsig: true},
|
||||||
|
)
|
||||||
addSessionDebugLog({type: 'method:end', method: 'logout'})
|
addSessionDebugLog({type: 'method:end', method: 'logout'})
|
||||||
},
|
},
|
||||||
[cancelPendingTask],
|
[cancelPendingTask],
|
||||||
@@ -135,7 +146,11 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: 'logged-out-every-account',
|
type: 'logged-out-every-account',
|
||||||
})
|
})
|
||||||
logEvent('account:loggedOut', {logContext, scope: 'every'})
|
logger.metric(
|
||||||
|
'account:loggedOut',
|
||||||
|
{logContext, scope: 'every'},
|
||||||
|
{statsig: true},
|
||||||
|
)
|
||||||
addSessionDebugLog({type: 'method:end', method: 'logout'})
|
addSessionDebugLog({type: 'method:end', method: 'logout'})
|
||||||
},
|
},
|
||||||
[cancelPendingTask],
|
[cancelPendingTask],
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ export type SessionStateContext = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type SessionApiContext = {
|
export type SessionApiContext = {
|
||||||
createAccount: (props: {
|
createAccount: (
|
||||||
|
props: {
|
||||||
service: string
|
service: string
|
||||||
email: string
|
email: string
|
||||||
password: string
|
password: string
|
||||||
@@ -19,7 +20,9 @@ export type SessionApiContext = {
|
|||||||
inviteCode?: string
|
inviteCode?: string
|
||||||
verificationPhone?: string
|
verificationPhone?: string
|
||||||
verificationCode?: string
|
verificationCode?: string
|
||||||
}) => Promise<void>
|
},
|
||||||
|
metrics: LogEvents['account:create:success'],
|
||||||
|
) => Promise<void>
|
||||||
login: (
|
login: (
|
||||||
props: {
|
props: {
|
||||||
service: string
|
service: string
|
||||||
|
|||||||
Reference in New Issue
Block a user