import {type PropsWithChildren, useEffect, useReducer, useState} from 'react'
import {AppState, type AppStateStatus, View} from 'react-native'
import ReactNativeDeviceAttest from 'react-native-device-attest'
import {KeyboardAvoidingView} from 'react-native-keyboard-controller'
import Animated, {FadeIn, LayoutAnimationConfig} from 'react-native-reanimated'
import {tokens} from '@bsky.app/alf'
import {Trans, useLingui} from '@lingui/react/macro'
import {FEEDBACK_FORM_URL} from '#/lib/constants'
import {logger} from '#/logger'
import {useServiceQuery} from '#/state/queries/service'
import {useStarterPackQuery} from '#/state/queries/starter-packs'
import {useActiveStarterPack} from '#/state/shell/landing'
import {LoggedOutLayout} from '#/view/com/util/layouts/LoggedOutLayout'
import {
initialState,
reducer,
SignupContext,
SignupStep,
useSubmitSignup,
} from '#/screens/Signup/state'
import {StepCaptcha} from '#/screens/Signup/StepCaptcha'
import {StepHandle} from '#/screens/Signup/StepHandle'
import {StepInfo} from '#/screens/Signup/StepInfo'
import {atoms as a, native, useBreakpoints, useTheme} from '#/alf'
import {AppLanguageDropdown} from '#/components/AppLanguageDropdown'
import {Divider} from '#/components/Divider'
import {LinearGradientBackground} from '#/components/LinearGradientBackground'
import {InlineLinkText} from '#/components/Link'
import {ScreenTransition} from '#/components/ScreenTransition'
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
import {GCP_PROJECT_ID, IS_ANDROID} from '#/env'
import {app} from '#/lexicons'
import * as bsky from '#/types/bsky'
export function Signup({onPressBack}: {onPressBack: () => void}) {
const ax = useAnalytics()
const {t: l} = useLingui()
const t = useTheme()
const [state, dispatch] = useReducer(reducer, {
...initialState,
analytics: ax,
})
const {gtMobile} = useBreakpoints()
const submit = useSubmitSignup()
useEffect(() => {
dispatch({
type: 'setAnalytics',
value: ax,
})
}, [ax])
const activeStarterPack = useActiveStarterPack()
const {
data: starterPack,
isFetching: isFetchingStarterPack,
isError: isErrorStarterPack,
} = useStarterPackQuery({
uri: activeStarterPack?.uri,
})
// eslint-disable-next-line react/hook-use-state
const [isFetchedAtMount] = useState(starterPack != null)
const showStarterPackCard =
activeStarterPack?.uri && !isFetchingStarterPack && starterPack
const {
data: serviceInfo,
isFetching,
isError,
refetch,
} = useServiceQuery(state.serviceUrl)
useEffect(() => {
if (isFetching) {
dispatch({type: 'setIsLoading', value: true})
} else if (!isFetching) {
dispatch({type: 'setIsLoading', value: false})
}
}, [isFetching])
useEffect(() => {
if (isError) {
dispatch({type: 'setServiceDescription', value: undefined})
dispatch({
type: 'setError',
value: l`Unable to contact your service. Please check your Internet connection.`,
})
} else if (serviceInfo) {
dispatch({type: 'setServiceDescription', value: serviceInfo})
dispatch({type: 'setError', value: ''})
}
}, [l, serviceInfo, isError])
useEffect(() => {
if (state.pendingSubmit) {
if (!state.pendingSubmit.mutableProcessed) {
// OK to mutate assuming it's never read in render.
// eslint-disable-next-line react-hooks/immutability, react-compiler/react-compiler
state.pendingSubmit.mutableProcessed = true
void submit(state, dispatch)
}
}
}, [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()
}, [])
// On Android, warmup the Play Integrity API on the signup screen so it is ready by the time we get to the gate screen.
useEffect(() => {
if (!IS_ANDROID) {
return
}
ReactNativeDeviceAttest.warmupIntegrity(GCP_PROJECT_ID).catch(err =>
logger.error(err),
)
}, [])
return (
{showStarterPackCard &&
bsky.isType(app.bsky.graph.starterpack, starterPack.record) ? (
{starterPack.record.name}
{starterPack.feeds?.length ? (
You'll follow the suggested users and feeds once you
finish creating your account!
) : (
You'll follow the suggested users once you finish
creating your account!
)}
) : null}
Step {state.activeStep + 1} of{' '}
{state.serviceDescription &&
!state.serviceDescription.phoneVerificationRequired
? '2'
: '3'}
{state.activeStep === SignupStep.INFO ? (
Your account
) : state.activeStep === SignupStep.HANDLE ? (
Choose your username
) : (
Complete the challenge
)}
{state.activeStep === SignupStep.INFO ? (
void refetch()}
/>
) : state.activeStep === SignupStep.HANDLE ? (
) : (
)}
Having trouble?{' '}
Contact support
)
}
function SignupKeyboardAvoidingView({
children,
enabled,
}: PropsWithChildren<{enabled: boolean}>) {
if (!enabled) {
return {children}
}
return (
{children}
)
}