fork
Configure Feed
Select the types of activity you want to include in your feed.
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.
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}