Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 8c3553cd66ad07ef8c8c4e760b495cf6ce08cc8d 246 lines 8.8 kB view raw
1import {useEffect, useReducer, useState} from 'react' 2import {AppState, type AppStateStatus, View} from 'react-native' 3import ReactNativeDeviceAttest from 'react-native-device-attest' 4import Animated, {FadeIn, LayoutAnimationConfig} from 'react-native-reanimated' 5import {AppBskyGraphStarterpack} from '@atproto/api' 6import {msg, Trans} from '@lingui/macro' 7import {useLingui} from '@lingui/react' 8 9import {FEEDBACK_FORM_URL} from '#/lib/constants' 10import {logger} from '#/logger' 11import {useServiceQuery} from '#/state/queries/service' 12import {useStarterPackQuery} from '#/state/queries/starter-packs' 13import {useActiveStarterPack} from '#/state/shell/starter-pack' 14import {LoggedOutLayout} from '#/view/com/util/layouts/LoggedOutLayout' 15import { 16 initialState, 17 reducer, 18 SignupContext, 19 SignupStep, 20 useSubmitSignup, 21} from '#/screens/Signup/state' 22import {StepCaptcha} from '#/screens/Signup/StepCaptcha' 23import {StepHandle} from '#/screens/Signup/StepHandle' 24import {StepInfo} from '#/screens/Signup/StepInfo' 25import {atoms as a, native, useBreakpoints, useTheme} from '#/alf' 26import {AppLanguageDropdown} from '#/components/AppLanguageDropdown' 27import {Divider} from '#/components/Divider' 28import {LinearGradientBackground} from '#/components/LinearGradientBackground' 29import {InlineLinkText} from '#/components/Link' 30import {ScreenTransition} from '#/components/ScreenTransition' 31import {Text} from '#/components/Typography' 32import {IS_ANDROID} from '#/env' 33import {GCP_PROJECT_ID} from '#/env' 34import * as bsky from '#/types/bsky' 35 36export function Signup({ 37 onPressBack, 38 onPressSignIn, 39}: { 40 onPressBack: () => void 41 onPressSignIn: () => void 42}) { 43 const {_} = useLingui() 44 const t = useTheme() 45 const [state, dispatch] = useReducer(reducer, initialState) 46 const {gtMobile} = useBreakpoints() 47 const submit = useSubmitSignup() 48 49 const activeStarterPack = useActiveStarterPack() 50 const { 51 data: starterPack, 52 isFetching: isFetchingStarterPack, 53 isError: isErrorStarterPack, 54 } = useStarterPackQuery({ 55 uri: activeStarterPack?.uri, 56 }) 57 58 const [isFetchedAtMount] = useState(starterPack != null) 59 const showStarterPackCard = 60 activeStarterPack?.uri && !isFetchingStarterPack && starterPack 61 62 const { 63 data: serviceInfo, 64 isFetching, 65 isError, 66 refetch, 67 } = useServiceQuery(state.serviceUrl) 68 69 useEffect(() => { 70 if (isFetching) { 71 dispatch({type: 'setIsLoading', value: true}) 72 } else if (!isFetching) { 73 dispatch({type: 'setIsLoading', value: false}) 74 } 75 }, [isFetching]) 76 77 useEffect(() => { 78 if (isError) { 79 dispatch({type: 'setServiceDescription', value: undefined}) 80 dispatch({ 81 type: 'setError', 82 value: _( 83 msg`Unable to contact your service. Please check your Internet connection.`, 84 ), 85 }) 86 } else if (serviceInfo) { 87 dispatch({type: 'setServiceDescription', value: serviceInfo}) 88 dispatch({type: 'setError', value: ''}) 89 } 90 }, [_, serviceInfo, isError]) 91 92 useEffect(() => { 93 if (state.pendingSubmit) { 94 if (!state.pendingSubmit.mutableProcessed) { 95 state.pendingSubmit.mutableProcessed = true 96 submit(state, dispatch) 97 } 98 } 99 }, [state, dispatch, submit]) 100 101 // Track app backgrounding during signup 102 useEffect(() => { 103 const subscription = AppState.addEventListener( 104 'change', 105 (nextAppState: AppStateStatus) => { 106 if (nextAppState === 'background') { 107 dispatch({type: 'incrementBackgroundCount'}) 108 } 109 }, 110 ) 111 112 return () => subscription.remove() 113 }, []) 114 115 // On Android, warmup the Play Integrity API on the signup screen so it is ready by the time we get to the gate screen. 116 useEffect(() => { 117 if (!IS_ANDROID) { 118 return 119 } 120 ReactNativeDeviceAttest.warmupIntegrity(GCP_PROJECT_ID).catch(err => 121 logger.error(err), 122 ) 123 }, []) 124 125 return ( 126 <Animated.View exiting={native(FadeIn.duration(90))} style={a.flex_1}> 127 <SignupContext.Provider value={{state, dispatch}}> 128 <LoggedOutLayout 129 leadin="" 130 title={_(msg`Create Account`)} 131 description={_(msg`Welcome to the ATmosphere!`)} 132 scrollable> 133 <View testID="createAccount" style={a.flex_1}> 134 {showStarterPackCard && 135 bsky.dangerousIsType<AppBskyGraphStarterpack.Record>( 136 starterPack.record, 137 AppBskyGraphStarterpack.isRecord, 138 ) ? ( 139 <Animated.View entering={!isFetchedAtMount ? FadeIn : undefined}> 140 <LinearGradientBackground 141 style={[a.mx_lg, a.p_lg, a.gap_sm, a.rounded_sm]}> 142 <Text style={[a.font_semi_bold, a.text_xl, {color: 'white'}]}> 143 {starterPack.record.name} 144 </Text> 145 <Text style={[{color: 'white'}]}> 146 {starterPack.feeds?.length ? ( 147 <Trans> 148 You'll follow the suggested users and feeds once you 149 finish creating your account! 150 </Trans> 151 ) : ( 152 <Trans> 153 You'll follow the suggested users once you finish 154 creating your account! 155 </Trans> 156 )} 157 </Text> 158 </LinearGradientBackground> 159 </Animated.View> 160 ) : null} 161 <LayoutAnimationConfig skipEntering> 162 <ScreenTransition 163 key={state.activeStep} 164 direction={state.screenTransitionDirection}> 165 <View 166 style={[ 167 a.flex_1, 168 a.px_xl, 169 a.pt_2xl, 170 !gtMobile && {paddingBottom: 100}, 171 ]}> 172 <View style={[a.gap_sm, a.pb_3xl]}> 173 <Text 174 style={[a.font_semi_bold, t.atoms.text_contrast_medium]}> 175 <Trans> 176 Step {state.activeStep + 1} of{' '} 177 {state.serviceDescription && 178 !state.serviceDescription.phoneVerificationRequired 179 ? '2' 180 : '3'} 181 </Trans> 182 </Text> 183 <Text style={[a.text_3xl, a.font_semi_bold]}> 184 {state.activeStep === SignupStep.INFO ? ( 185 <Trans>The ATmosphere </Trans> 186 ) : state.activeStep === SignupStep.HANDLE ? ( 187 <Trans>Choose your username</Trans> 188 ) : ( 189 <Trans>Complete the challenge</Trans> 190 )} 191 </Text> 192 </View> 193 194 <LayoutAnimationConfig skipEntering skipExiting> 195 {state.activeStep === SignupStep.INFO ? ( 196 <StepInfo 197 onPressBack={onPressBack} 198 onPressSignIn={onPressSignIn} 199 isLoadingStarterPack={ 200 isFetchingStarterPack && !isErrorStarterPack 201 } 202 isServerError={isError} 203 refetchServer={refetch} 204 /> 205 ) : state.activeStep === SignupStep.HANDLE ? ( 206 <StepHandle /> 207 ) : ( 208 <StepCaptcha /> 209 )} 210 </LayoutAnimationConfig> 211 212 <Divider /> 213 214 <View 215 style={[ 216 a.w_full, 217 a.py_lg, 218 a.flex_row, 219 a.gap_md, 220 a.align_center, 221 ]}> 222 <AppLanguageDropdown /> 223 <Text 224 style={[ 225 a.flex_1, 226 t.atoms.text_contrast_medium, 227 !gtMobile && a.text_md, 228 ]}> 229 <Trans>Having trouble?</Trans>{' '} 230 <InlineLinkText 231 label={_(msg`Contact support`)} 232 to={FEEDBACK_FORM_URL({email: state.email})} 233 style={[!gtMobile && a.text_md]}> 234 <Trans>Open a Tangled Issue</Trans> 235 </InlineLinkText> 236 </Text> 237 </View> 238 </View> 239 </ScreenTransition> 240 </LayoutAnimationConfig> 241 </View> 242 </LoggedOutLayout> 243 </SignupContext.Provider> 244 </Animated.View> 245 ) 246}