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 ruby-v 342 lines 11 kB view raw
1import React, {useState} from 'react' 2import { 3 ActivityIndicator, 4 Pressable, 5 SafeAreaView, 6 StyleSheet, 7 View, 8} from 'react-native' 9import {Circle, Path, Svg} from 'react-native-svg' 10import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 11import {msg, Trans} from '@lingui/macro' 12import {useLingui} from '@lingui/react' 13 14import {usePalette} from '#/lib/hooks/usePalette' 15import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 16import {cleanError} from '#/lib/strings/errors' 17import {colors, s} from '#/lib/styles' 18import {logger} from '#/logger' 19import {isWeb} from '#/platform/detection' 20import {useModalControls} from '#/state/modals' 21import {useAgent, useSession} from '#/state/session' 22import {ErrorMessage} from '../util/error/ErrorMessage' 23import {Button} from '../util/forms/Button' 24import {Text} from '../util/text/Text' 25import * as Toast from '../util/Toast' 26import {ScrollView, TextInput} from './util' 27 28export const snapPoints = ['90%'] 29 30enum Stages { 31 Reminder, 32 Email, 33 ConfirmCode, 34} 35 36export function Component({ 37 showReminder, 38 onSuccess, 39}: { 40 showReminder?: boolean 41 onSuccess?: () => void 42}) { 43 const pal = usePalette('default') 44 const agent = useAgent() 45 const {currentAccount} = useSession() 46 const {_} = useLingui() 47 const [stage, setStage] = useState<Stages>( 48 showReminder ? Stages.Reminder : Stages.Email, 49 ) 50 const [confirmationCode, setConfirmationCode] = useState<string>('') 51 const [isProcessing, setIsProcessing] = useState<boolean>(false) 52 const [error, setError] = useState<string>('') 53 const {isMobile} = useWebMediaQueries() 54 const {openModal, closeModal} = useModalControls() 55 56 React.useEffect(() => { 57 if (!currentAccount) { 58 logger.error(`VerifyEmail modal opened without currentAccount`) 59 closeModal() 60 } 61 }, [currentAccount, closeModal]) 62 63 const onSendEmail = async () => { 64 setError('') 65 setIsProcessing(true) 66 try { 67 await agent.com.atproto.server.requestEmailConfirmation() 68 setStage(Stages.ConfirmCode) 69 } catch (e) { 70 setError(cleanError(String(e))) 71 } finally { 72 setIsProcessing(false) 73 } 74 } 75 76 const onConfirm = async () => { 77 setError('') 78 setIsProcessing(true) 79 try { 80 await agent.com.atproto.server.confirmEmail({ 81 email: (currentAccount?.email || '').trim(), 82 token: confirmationCode.trim(), 83 }) 84 await agent.resumeSession(agent.session!) 85 Toast.show(_(msg`Email verified`)) 86 closeModal() 87 onSuccess?.() 88 } catch (e) { 89 setError(cleanError(String(e))) 90 } finally { 91 setIsProcessing(false) 92 } 93 } 94 95 const onEmailIncorrect = () => { 96 closeModal() 97 openModal({name: 'change-email'}) 98 } 99 100 return ( 101 <SafeAreaView style={[pal.view, s.flex1]}> 102 <ScrollView 103 testID="verifyEmailModal" 104 style={[s.flex1, isMobile && {paddingHorizontal: 18}]}> 105 {stage === Stages.Reminder && <ReminderIllustration />} 106 <View style={styles.titleSection}> 107 <Text type="title-lg" style={[pal.text, styles.title]}> 108 {stage === Stages.Reminder ? ( 109 <Trans>Please Verify Your Email</Trans> 110 ) : stage === Stages.Email ? ( 111 <Trans>Verify Your Email</Trans> 112 ) : stage === Stages.ConfirmCode ? ( 113 <Trans>Enter Confirmation Code</Trans> 114 ) : ( 115 '' 116 )} 117 </Text> 118 </View> 119 120 <Text type="lg" style={[pal.textLight, {marginBottom: 10}]}> 121 {stage === Stages.Reminder ? ( 122 <Trans> 123 Your email has not yet been verified. This is an important 124 security step which we recommend. 125 </Trans> 126 ) : stage === Stages.Email ? ( 127 <Trans> 128 This is important in case you ever need to change your email or 129 reset your password. 130 </Trans> 131 ) : stage === Stages.ConfirmCode ? ( 132 <Trans> 133 An email has been sent to {currentAccount?.email || '(no email)'}. 134 It includes a confirmation code which you can enter below. 135 </Trans> 136 ) : ( 137 '' 138 )} 139 </Text> 140 141 {stage === Stages.Email ? ( 142 <> 143 <View style={styles.emailContainer}> 144 <FontAwesomeIcon 145 icon="envelope" 146 color={pal.colors.text} 147 size={16} 148 /> 149 <Text type="xl-medium" style={[pal.text, s.flex1, {minWidth: 0}]}> 150 {currentAccount?.email || _(msg`(no email)`)} 151 </Text> 152 </View> 153 <Pressable 154 accessibilityRole="link" 155 accessibilityLabel={_(msg`Change my email`)} 156 accessibilityHint="" 157 onPress={onEmailIncorrect} 158 style={styles.changeEmailLink}> 159 <Text type="lg" style={pal.link}> 160 <Trans>Change</Trans> 161 </Text> 162 </Pressable> 163 </> 164 ) : stage === Stages.ConfirmCode ? ( 165 <TextInput 166 testID="confirmCodeInput" 167 style={[styles.textInput, pal.border, pal.text]} 168 placeholder="XXXXX-XXXXX" 169 placeholderTextColor={pal.colors.textLight} 170 value={confirmationCode} 171 onChangeText={setConfirmationCode} 172 accessible={true} 173 accessibilityLabel={_(msg`Confirmation code`)} 174 accessibilityHint="" 175 autoCapitalize="none" 176 autoComplete="one-time-code" 177 autoCorrect={false} 178 /> 179 ) : undefined} 180 181 {error ? ( 182 <ErrorMessage message={error} style={styles.error} /> 183 ) : undefined} 184 185 <View style={[styles.btnContainer]}> 186 {isProcessing ? ( 187 <View style={styles.btn}> 188 <ActivityIndicator color="#fff" /> 189 </View> 190 ) : ( 191 <View style={{gap: 6}}> 192 {stage === Stages.Reminder && ( 193 <Button 194 testID="getStartedBtn" 195 type="primary" 196 onPress={() => setStage(Stages.Email)} 197 accessibilityLabel={_(msg`Get Started`)} 198 accessibilityHint="" 199 label={_(msg`Get Started`)} 200 labelContainerStyle={{justifyContent: 'center', padding: 4}} 201 labelStyle={[s.f18]} 202 /> 203 )} 204 {stage === Stages.Email && ( 205 <> 206 <Button 207 testID="sendEmailBtn" 208 type="primary" 209 onPress={onSendEmail} 210 accessibilityLabel={_(msg`Send Confirmation Email`)} 211 accessibilityHint="" 212 label={_(msg`Send Confirmation Email`)} 213 labelContainerStyle={{ 214 justifyContent: 'center', 215 padding: 4, 216 }} 217 labelStyle={[s.f18]} 218 /> 219 <Button 220 testID="haveCodeBtn" 221 type="default" 222 accessibilityLabel={_(msg`I have a code`)} 223 accessibilityHint="" 224 label={_(msg`I have a confirmation code`)} 225 labelContainerStyle={{ 226 justifyContent: 'center', 227 padding: 4, 228 }} 229 labelStyle={[s.f18]} 230 onPress={() => setStage(Stages.ConfirmCode)} 231 /> 232 </> 233 )} 234 {stage === Stages.ConfirmCode && ( 235 <Button 236 testID="confirmBtn" 237 type="primary" 238 onPress={onConfirm} 239 accessibilityLabel={_(msg`Confirm`)} 240 accessibilityHint="" 241 label={_(msg`Confirm`)} 242 labelContainerStyle={{justifyContent: 'center', padding: 4}} 243 labelStyle={[s.f18]} 244 /> 245 )} 246 <Button 247 testID="cancelBtn" 248 type="default" 249 onPress={() => { 250 closeModal() 251 }} 252 accessibilityLabel={ 253 stage === Stages.Reminder 254 ? _(msg`Not right now`) 255 : _(msg`Cancel`) 256 } 257 accessibilityHint="" 258 label={ 259 stage === Stages.Reminder 260 ? _(msg`Not right now`) 261 : _(msg`Cancel`) 262 } 263 labelContainerStyle={{justifyContent: 'center', padding: 4}} 264 labelStyle={[s.f18]} 265 /> 266 </View> 267 )} 268 </View> 269 </ScrollView> 270 </SafeAreaView> 271 ) 272} 273 274function ReminderIllustration() { 275 const pal = usePalette('default') 276 const palInverted = usePalette('inverted') 277 return ( 278 <View style={[pal.viewLight, {borderRadius: 8, marginBottom: 20}]}> 279 <Svg viewBox="0 0 112 84" fill="none" height={200}> 280 <Path 281 fillRule="evenodd" 282 clipRule="evenodd" 283 d="M26 26.4264V55C26 60.5229 30.4772 65 36 65H76C81.5228 65 86 60.5229 86 55V27.4214L63.5685 49.8528C59.6633 53.7581 53.3316 53.7581 49.4264 49.8528L26 26.4264Z" 284 fill={palInverted.colors.background} 285 /> 286 <Path 287 fillRule="evenodd" 288 clipRule="evenodd" 289 d="M83.666 19.5784C85.47 21.7297 84.4897 24.7895 82.5044 26.7748L60.669 48.6102C58.3259 50.9533 54.5269 50.9533 52.1838 48.6102L29.9502 26.3766C27.8241 24.2505 26.8952 20.8876 29.0597 18.8005C30.8581 17.0665 33.3045 16 36 16H76C79.0782 16 81.8316 17.3908 83.666 19.5784Z" 290 fill={palInverted.colors.background} 291 /> 292 <Circle cx="82" cy="61" r="13" fill="#20BC07" /> 293 <Path d="M75 61L80 66L89 57" stroke="white" strokeWidth="2" /> 294 </Svg> 295 </View> 296 ) 297} 298 299const styles = StyleSheet.create({ 300 titleSection: { 301 paddingTop: isWeb ? 0 : 4, 302 paddingBottom: isWeb ? 14 : 10, 303 }, 304 title: { 305 textAlign: 'center', 306 fontWeight: '600', 307 marginBottom: 5, 308 }, 309 error: { 310 borderRadius: 6, 311 marginTop: 10, 312 }, 313 emailContainer: { 314 flexDirection: 'row', 315 alignItems: 'center', 316 gap: 6, 317 paddingHorizontal: 14, 318 marginTop: 10, 319 }, 320 changeEmailLink: { 321 marginHorizontal: 12, 322 marginBottom: 12, 323 }, 324 textInput: { 325 borderWidth: 1, 326 borderRadius: 6, 327 paddingHorizontal: 14, 328 paddingVertical: 10, 329 fontSize: 16, 330 }, 331 btn: { 332 flexDirection: 'row', 333 alignItems: 'center', 334 justifyContent: 'center', 335 borderRadius: 32, 336 padding: 14, 337 backgroundColor: colors.blue3, 338 }, 339 btnContainer: { 340 paddingTop: 20, 341 }, 342})