mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
fork

Configure Feed

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

at utm-source 196 lines 6.4 kB view raw
1import React, {useRef} from 'react' 2import {View} from 'react-native' 3import {msg, Trans} from '@lingui/macro' 4import {useLingui} from '@lingui/react' 5 6import {logEvent} from '#/lib/statsig/statsig' 7import {createFullHandle, validateHandle} from '#/lib/strings/handles' 8import {useAgent} from '#/state/session' 9import {ScreenTransition} from '#/screens/Login/ScreenTransition' 10import {useSignupContext} from '#/screens/Signup/state' 11import {atoms as a, useTheme} from '#/alf' 12import * as TextField from '#/components/forms/TextField' 13import {useThrottledValue} from '#/components/hooks/useThrottledValue' 14import {At_Stroke2_Corner0_Rounded as At} from '#/components/icons/At' 15import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' 16import {TimesLarge_Stroke2_Corner0_Rounded as Times} from '#/components/icons/Times' 17import {Text} from '#/components/Typography' 18import {BackNextButtons} from './BackNextButtons' 19 20export function StepHandle() { 21 const {_} = useLingui() 22 const t = useTheme() 23 const {state, dispatch} = useSignupContext() 24 const agent = useAgent() 25 const handleValueRef = useRef<string>(state.handle) 26 const [draftValue, setDraftValue] = React.useState(state.handle) 27 const isLoading = useThrottledValue(state.isLoading, 500) 28 29 const onNextPress = React.useCallback(async () => { 30 const handle = handleValueRef.current.trim() 31 dispatch({ 32 type: 'setHandle', 33 value: handle, 34 }) 35 36 const newValidCheck = validateHandle(handle, state.userDomain) 37 if (!newValidCheck.overall) { 38 return 39 } 40 41 try { 42 dispatch({type: 'setIsLoading', value: true}) 43 44 const res = await agent.resolveHandle({ 45 handle: createFullHandle(handle, state.userDomain), 46 }) 47 48 if (res.data.did) { 49 dispatch({ 50 type: 'setError', 51 value: _(msg`That handle is already taken.`), 52 }) 53 return 54 } 55 } catch (e) { 56 // Don't have to handle 57 } finally { 58 dispatch({type: 'setIsLoading', value: false}) 59 } 60 61 logEvent('signup:nextPressed', { 62 activeStep: state.activeStep, 63 phoneVerificationRequired: 64 state.serviceDescription?.phoneVerificationRequired, 65 }) 66 // phoneVerificationRequired is actually whether a captcha is required 67 if (!state.serviceDescription?.phoneVerificationRequired) { 68 dispatch({ 69 type: 'submit', 70 task: {verificationCode: undefined, mutableProcessed: false}, 71 }) 72 return 73 } 74 dispatch({type: 'next'}) 75 }, [ 76 _, 77 dispatch, 78 state.activeStep, 79 state.serviceDescription?.phoneVerificationRequired, 80 state.userDomain, 81 agent, 82 ]) 83 84 const onBackPress = React.useCallback(() => { 85 const handle = handleValueRef.current.trim() 86 dispatch({ 87 type: 'setHandle', 88 value: handle, 89 }) 90 dispatch({type: 'prev'}) 91 logEvent('signup:backPressed', { 92 activeStep: state.activeStep, 93 }) 94 }, [dispatch, state.activeStep]) 95 96 const validCheck = validateHandle(draftValue, state.userDomain) 97 return ( 98 <ScreenTransition> 99 <View style={[a.gap_lg]}> 100 <View> 101 <TextField.Root> 102 <TextField.Icon icon={At} /> 103 <TextField.Input 104 testID="handleInput" 105 onChangeText={val => { 106 if (state.error) { 107 dispatch({type: 'setError', value: ''}) 108 } 109 110 // These need to always be in sync. 111 handleValueRef.current = val 112 setDraftValue(val) 113 }} 114 label={_(msg`Input your user handle`)} 115 defaultValue={draftValue} 116 autoCapitalize="none" 117 autoCorrect={false} 118 autoFocus 119 autoComplete="off" 120 /> 121 </TextField.Root> 122 </View> 123 {draftValue !== '' && ( 124 <Text style={[a.text_md]}> 125 <Trans>Your full handle will be</Trans>{' '} 126 <Text style={[a.text_md, a.font_bold]}> 127 @{createFullHandle(draftValue, state.userDomain)} 128 </Text> 129 </Text> 130 )} 131 132 {draftValue !== '' && ( 133 <View 134 style={[ 135 a.w_full, 136 a.rounded_sm, 137 a.border, 138 a.p_md, 139 a.gap_sm, 140 t.atoms.border_contrast_low, 141 ]}> 142 {state.error ? ( 143 <View style={[a.w_full, a.flex_row, a.align_center, a.gap_sm]}> 144 <IsValidIcon valid={false} /> 145 <Text style={[a.text_md, a.flex_1]}>{state.error}</Text> 146 </View> 147 ) : undefined} 148 {validCheck.hyphenStartOrEnd ? ( 149 <View style={[a.w_full, a.flex_row, a.align_center, a.gap_sm]}> 150 <IsValidIcon valid={validCheck.handleChars} /> 151 <Text style={[a.text_md, a.flex_1]}> 152 <Trans>Only contains letters, numbers, and hyphens</Trans> 153 </Text> 154 </View> 155 ) : ( 156 <View style={[a.w_full, a.flex_row, a.align_center, a.gap_sm]}> 157 <IsValidIcon valid={validCheck.hyphenStartOrEnd} /> 158 <Text style={[a.text_md, a.flex_1]}> 159 <Trans>Doesn't begin or end with a hyphen</Trans> 160 </Text> 161 </View> 162 )} 163 <View style={[a.w_full, a.flex_row, a.align_center, a.gap_sm]}> 164 <IsValidIcon 165 valid={validCheck.frontLength && validCheck.totalLength} 166 /> 167 {!validCheck.totalLength ? ( 168 <Text style={[a.text_md, a.flex_1]}> 169 <Trans>No longer than 253 characters</Trans> 170 </Text> 171 ) : ( 172 <Text style={[a.text_md, a.flex_1]}> 173 <Trans>At least 3 characters</Trans> 174 </Text> 175 )} 176 </View> 177 </View> 178 )} 179 </View> 180 <BackNextButtons 181 isLoading={isLoading} 182 isNextDisabled={!validCheck.overall} 183 onBackPress={onBackPress} 184 onNextPress={onNextPress} 185 /> 186 </ScreenTransition> 187 ) 188} 189 190function IsValidIcon({valid}: {valid: boolean}) { 191 const t = useTheme() 192 if (!valid) { 193 return <Times size="md" style={{color: t.palette.negative_500}} /> 194 } 195 return <Check size="md" style={{color: t.palette.positive_700}} /> 196}