Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

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

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