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

Configure Feed

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

at remove_new_user_progress_guide 339 lines 10 kB view raw
1import React, {useState} from 'react' 2import { 3 ActivityIndicator, 4 SafeAreaView, 5 StyleSheet, 6 TouchableOpacity, 7 View, 8} from 'react-native' 9import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 10import {msg, Trans} from '@lingui/macro' 11import {useLingui} from '@lingui/react' 12import * as EmailValidator from 'email-validator' 13 14import {logger} from '#/logger' 15import {useModalControls} from '#/state/modals' 16import {useAgent, useSession} from '#/state/session' 17import {usePalette} from 'lib/hooks/usePalette' 18import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 19import {cleanError, isNetworkError} from 'lib/strings/errors' 20import {checkAndFormatResetCode} from 'lib/strings/password' 21import {colors, s} from 'lib/styles' 22import {isAndroid, isWeb} from 'platform/detection' 23import {ErrorMessage} from '../util/error/ErrorMessage' 24import {Button} from '../util/forms/Button' 25import {Text} from '../util/text/Text' 26import {ScrollView} from './util' 27import {TextInput} from './util' 28 29enum Stages { 30 RequestCode, 31 ChangePassword, 32 Done, 33} 34 35export const snapPoints = isAndroid ? ['90%'] : ['45%'] 36 37export function Component() { 38 const pal = usePalette('default') 39 const {currentAccount} = useSession() 40 const agent = useAgent() 41 const {_} = useLingui() 42 const [stage, setStage] = useState<Stages>(Stages.RequestCode) 43 const [isProcessing, setIsProcessing] = useState<boolean>(false) 44 const [resetCode, setResetCode] = useState<string>('') 45 const [newPassword, setNewPassword] = useState<string>('') 46 const [error, setError] = useState<string>('') 47 const {isMobile} = useWebMediaQueries() 48 const {closeModal} = useModalControls() 49 50 const onRequestCode = async () => { 51 if ( 52 !currentAccount?.email || 53 !EmailValidator.validate(currentAccount.email) 54 ) { 55 return setError(_(msg`Your email appears to be invalid.`)) 56 } 57 58 setError('') 59 setIsProcessing(true) 60 try { 61 await agent.com.atproto.server.requestPasswordReset({ 62 email: currentAccount.email, 63 }) 64 setStage(Stages.ChangePassword) 65 } catch (e: any) { 66 const errMsg = e.toString() 67 logger.warn('Failed to request password reset', {error: e}) 68 if (isNetworkError(e)) { 69 setError( 70 _( 71 msg`Unable to contact your service. Please check your Internet connection.`, 72 ), 73 ) 74 } else { 75 setError(cleanError(errMsg)) 76 } 77 } finally { 78 setIsProcessing(false) 79 } 80 } 81 82 const onChangePassword = async () => { 83 const formattedCode = checkAndFormatResetCode(resetCode) 84 // TODO Better password strength check 85 if (!formattedCode || !newPassword) { 86 setError( 87 _( 88 msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`, 89 ), 90 ) 91 return 92 } 93 94 setError('') 95 setIsProcessing(true) 96 try { 97 await agent.com.atproto.server.resetPassword({ 98 token: formattedCode, 99 password: newPassword, 100 }) 101 setStage(Stages.Done) 102 } catch (e: any) { 103 const errMsg = e.toString() 104 logger.warn('Failed to set new password', {error: e}) 105 if (isNetworkError(e)) { 106 setError( 107 'Unable to contact your service. Please check your Internet connection.', 108 ) 109 } else { 110 setError(cleanError(errMsg)) 111 } 112 } finally { 113 setIsProcessing(false) 114 } 115 } 116 117 const onBlur = () => { 118 const formattedCode = checkAndFormatResetCode(resetCode) 119 if (!formattedCode) { 120 setError( 121 _( 122 msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`, 123 ), 124 ) 125 return 126 } 127 setResetCode(formattedCode) 128 } 129 130 return ( 131 <SafeAreaView style={[pal.view, s.flex1]}> 132 <ScrollView 133 contentContainerStyle={[ 134 styles.container, 135 isMobile && styles.containerMobile, 136 ]} 137 keyboardShouldPersistTaps="handled"> 138 <View> 139 <View style={styles.titleSection}> 140 <Text type="title-lg" style={[pal.text, styles.title]}> 141 {stage !== Stages.Done 142 ? _(msg`Change Password`) 143 : _(msg`Password Changed`)} 144 </Text> 145 </View> 146 147 <Text type="lg" style={[pal.textLight, {marginBottom: 10}]}> 148 {stage === Stages.RequestCode ? ( 149 <Trans> 150 If you want to change your password, we will send you a code to 151 verify that this is your account. 152 </Trans> 153 ) : stage === Stages.ChangePassword ? ( 154 <Trans> 155 Enter the code you received to change your password. 156 </Trans> 157 ) : ( 158 <Trans>Your password has been changed successfully!</Trans> 159 )} 160 </Text> 161 162 {stage === Stages.RequestCode && ( 163 <View style={[s.flexRow, s.justifyCenter, s.mt10]}> 164 <TouchableOpacity 165 testID="skipSendEmailButton" 166 onPress={() => setStage(Stages.ChangePassword)} 167 accessibilityRole="button" 168 accessibilityLabel={_(msg`Go to next`)} 169 accessibilityHint={_(msg`Navigates to the next screen`)}> 170 <Text type="xl" style={[pal.link, s.pr5]}> 171 <Trans>Already have a code?</Trans> 172 </Text> 173 </TouchableOpacity> 174 </View> 175 )} 176 {stage === Stages.ChangePassword && ( 177 <View style={[pal.border, styles.group]}> 178 <View style={[styles.groupContent]}> 179 <FontAwesomeIcon 180 icon="ticket" 181 style={[pal.textLight, styles.groupContentIcon]} 182 /> 183 <TextInput 184 testID="codeInput" 185 style={[pal.text, styles.textInput]} 186 placeholder={_(msg`Reset code`)} 187 placeholderTextColor={pal.colors.textLight} 188 value={resetCode} 189 onChangeText={setResetCode} 190 onFocus={() => setError('')} 191 onBlur={onBlur} 192 accessible={true} 193 accessibilityLabel={_(msg`Reset Code`)} 194 accessibilityHint="" 195 autoCapitalize="none" 196 autoCorrect={false} 197 autoComplete="off" 198 /> 199 </View> 200 <View 201 style={[ 202 pal.borderDark, 203 styles.groupContent, 204 styles.groupBottom, 205 ]}> 206 <FontAwesomeIcon 207 icon="lock" 208 style={[pal.textLight, styles.groupContentIcon]} 209 /> 210 <TextInput 211 testID="codeInput" 212 style={[pal.text, styles.textInput]} 213 placeholder={_(msg`New password`)} 214 placeholderTextColor={pal.colors.textLight} 215 onChangeText={setNewPassword} 216 secureTextEntry 217 accessible={true} 218 accessibilityLabel={_(msg`New Password`)} 219 accessibilityHint="" 220 autoCapitalize="none" 221 autoComplete="new-password" 222 /> 223 </View> 224 </View> 225 )} 226 {error ? ( 227 <ErrorMessage message={error} style={styles.error} /> 228 ) : undefined} 229 </View> 230 <View style={[styles.btnContainer]}> 231 {isProcessing ? ( 232 <View style={styles.btn}> 233 <ActivityIndicator color="#fff" /> 234 </View> 235 ) : ( 236 <View style={{gap: 6}}> 237 {stage === Stages.RequestCode && ( 238 <Button 239 testID="requestChangeBtn" 240 type="primary" 241 onPress={onRequestCode} 242 accessibilityLabel={_(msg`Request Code`)} 243 accessibilityHint="" 244 label={_(msg`Request Code`)} 245 labelContainerStyle={{justifyContent: 'center', padding: 4}} 246 labelStyle={[s.f18]} 247 /> 248 )} 249 {stage === Stages.ChangePassword && ( 250 <Button 251 testID="confirmBtn" 252 type="primary" 253 onPress={onChangePassword} 254 accessibilityLabel={_(msg`Next`)} 255 accessibilityHint="" 256 label={_(msg`Next`)} 257 labelContainerStyle={{justifyContent: 'center', padding: 4}} 258 labelStyle={[s.f18]} 259 /> 260 )} 261 <Button 262 testID="cancelBtn" 263 type={stage !== Stages.Done ? 'default' : 'primary'} 264 onPress={() => { 265 closeModal() 266 }} 267 accessibilityLabel={ 268 stage !== Stages.Done ? _(msg`Cancel`) : _(msg`Close`) 269 } 270 accessibilityHint="" 271 label={stage !== Stages.Done ? _(msg`Cancel`) : _(msg`Close`)} 272 labelContainerStyle={{justifyContent: 'center', padding: 4}} 273 labelStyle={[s.f18]} 274 /> 275 </View> 276 )} 277 </View> 278 </ScrollView> 279 </SafeAreaView> 280 ) 281} 282 283const styles = StyleSheet.create({ 284 container: { 285 justifyContent: 'space-between', 286 }, 287 containerMobile: { 288 paddingHorizontal: 18, 289 paddingBottom: 35, 290 }, 291 titleSection: { 292 paddingTop: isWeb ? 0 : 4, 293 paddingBottom: isWeb ? 14 : 10, 294 }, 295 title: { 296 textAlign: 'center', 297 fontWeight: '600', 298 marginBottom: 5, 299 }, 300 error: { 301 borderRadius: 6, 302 }, 303 textInput: { 304 width: '100%', 305 paddingHorizontal: 14, 306 paddingVertical: 10, 307 fontSize: 16, 308 }, 309 btn: { 310 flexDirection: 'row', 311 alignItems: 'center', 312 justifyContent: 'center', 313 borderRadius: 32, 314 padding: 14, 315 backgroundColor: colors.blue3, 316 }, 317 btnContainer: { 318 paddingTop: 20, 319 }, 320 group: { 321 borderWidth: 1, 322 borderRadius: 10, 323 marginVertical: 20, 324 }, 325 groupLabel: { 326 paddingHorizontal: 20, 327 paddingBottom: 5, 328 }, 329 groupContent: { 330 flexDirection: 'row', 331 alignItems: 'center', 332 }, 333 groupBottom: { 334 borderTopWidth: 1, 335 }, 336 groupContentIcon: { 337 marginLeft: 10, 338 }, 339})