import AsyncStorage from '@react-native-community/async-storage'; import {useNavigation} from '@react-navigation/core'; import {RouteProp} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; import React, {useEffect, useState} from 'react'; import { Alert, KeyboardAvoidingView, Platform, StatusBar, StyleSheet, Text, TouchableOpacity, } from 'react-native'; import {normalize} from 'react-native-elements'; import Animated, {EasingNode, useValue} from 'react-native-reanimated'; import { ArrowButton, Background, LoadingIndicator, TaggInput, TaggSquareButton, TermsConditions, } from '../../components'; import { emailRegex, nameRegex, passwordRegex, phoneRegex, TAGG_LIGHT_BLUE_2, usernameRegex, } from '../../constants'; import { ERROR_NEXT_PAGE, ERROR_PHONE_IN_USE, ERROR_REGISTRATION, ERROR_SOMETHING_WENT_WRONG_REFRESH, ERROR_TWILIO_SERVER_ERROR, ERROR_T_AND_C_NOT_ACCEPTED, } from '../../constants/strings'; import {OnboardingStackParams} from '../../routes'; import { sendOtpStatusCode, sendRegister, verifyExistingInformation, } from '../../services'; import {BackgroundGradientType} from '../../types'; import {HeaderHeight, SCREEN_HEIGHT, SCREEN_WIDTH} from '../../utils'; type BasicInfoOnboardingRouteProp = RouteProp< OnboardingStackParams, 'BasicInfoOnboarding' >; type BasicInfoOnboardingNavigationProp = StackNavigationProp< OnboardingStackParams, 'BasicInfoOnboarding' >; interface BasicInfoOnboardingProps { route: BasicInfoOnboardingRouteProp; navigation: BasicInfoOnboardingNavigationProp; } const BasicInfoOnboarding: React.FC = ({route}) => { const {isPhoneVerified} = route.params; const navigation = useNavigation(); const [attemptedSubmit, setAttemptedSubmit] = useState(false); const [valid, setValid] = useState(false); const [currentStep, setCurrentStep] = useState(0); const [tcAccepted, setTCAccepted] = useState(false); const [passVisibility, setPassVisibility] = useState(false); const [invalidWithError, setInvalidWithError] = useState( 'Please enter a valid ', ); const [autoCapitalize, setAutoCap] = useState< 'none' | 'sentences' | 'words' | 'characters' | undefined >('none'); const [fadeValue, setFadeValue] = useState>( new Animated.Value(0), ); useEffect(() => { setValid(false); }, [invalidWithError]); const fadeButtonValue = useValue(0); const [form, setForm] = useState({ fname: '', lname: '', username: '', phone: '', email: '', password: '', }); const fadeFormIn = () => { setFadeValue(new Animated.Value(0)); }; const fadeButtonTo = (target: number) => { Animated.timing(fadeButtonValue, { toValue: target, duration: 100, easing: EasingNode.linear, }).start(); }; useEffect(() => { const fade = async () => { Animated.timing(fadeValue, { toValue: 1, duration: 1000, easing: EasingNode.linear, }).start(); }; fade(); }, [fadeValue]); useEffect(() => { if (valid) { fadeButtonTo(1); } else { fadeButtonTo(0); } }, [valid]); const goToPhoneVerification = async () => { if (!attemptedSubmit) { setAttemptedSubmit(true); } try { if (valid) { const {phone} = form; const code = await sendOtpStatusCode(phone); if (code) { switch (code) { case 200: const {fname, lname} = form; navigation.navigate('PhoneVerification', { firstName: fname, lastName: lname, phone, }); break; case 409: Alert.alert(ERROR_PHONE_IN_USE); break; default: Alert.alert(ERROR_TWILIO_SERVER_ERROR); } } else { setAttemptedSubmit(false); setTimeout(() => { setAttemptedSubmit(true); }); } } } catch (error) { Alert.alert(ERROR_NEXT_PAGE); return { name: 'Navigation error', description: error, }; } }; // 0 = first name, 1 = last name, 2 = username, 3 = phone # const handleNameUpdate = (name: string, nameType: number) => { name = name.trim(); let isValidName: boolean = nameRegex.test(name); switch (nameType) { case 0: setForm({ ...form, fname: name, }); setAutoCap('words'); setValid(isValidName); break; case 1: setForm({ ...form, lname: name, }); setAutoCap('words'); setValid(isValidName); break; case 2: setForm({ ...form, username: name, }); setValid(usernameRegex.test(name)); setAutoCap('none'); break; } }; const handlePhoneUpdate = (phone: string) => { phone = phone.trim(); setForm({ ...form, phone, }); setAutoCap('none'); setValid(phoneRegex.test(phone)); }; const handleEmailUpdate = (email: string) => { email = email.trim(); setForm({ ...form, email, }); setAutoCap('none'); setValid(emailRegex.test(email)); }; const handlePasswordUpdate = (password: string) => { setForm({ ...form, password, }); setAutoCap('none'); setValid(passwordRegex.test(password)); }; const formSteps: { placeholder: string; onChangeText: (text: string) => void; value: string; }[] = [ { placeholder: 'First Name', onChangeText: (text) => handleNameUpdate(text, 0), value: form.fname, }, { placeholder: 'Last Name', onChangeText: (text) => handleNameUpdate(text, 1), value: form.lname, }, { placeholder: 'Phone', onChangeText: handlePhoneUpdate, value: form.phone, }, { placeholder: 'School Email', onChangeText: handleEmailUpdate, value: form.email, }, { placeholder: 'Username', onChangeText: (text) => handleNameUpdate(text, 2), value: form.username, }, { placeholder: 'Password', onChangeText: handlePasswordUpdate, value: form.password, }, ]; const resetForm = (formStep: String) => { setValid(false); switch (formStep) { case 'First Name': setForm({ ...form, fname: '', }); break; case 'Last Name': setForm({ ...form, lname: '', }); break; case 'Email': setForm({ ...form, email: '', }); break; case 'Password': setForm({ ...form, password: '', }); break; case 'School Email': setForm({ ...form, email: '', }); break; case 'Username': setForm({ ...form, username: '', }); break; } }; const step = formSteps[currentStep]; useEffect(() => { setInvalidWithError( 'Please enter a valid ' + step.placeholder.toLowerCase(), ); }, [currentStep]); const advance = async () => { setAttemptedSubmit(true); if (valid) { if (step.placeholder === 'School Email') { const verifiedInfo = await verifyExistingInformation( form.email, undefined, ); if (!verifiedInfo) { setInvalidWithError('Email is taken'); return; } } else if (step.placeholder === 'Username') { const verifiedInfo = await verifyExistingInformation( undefined, form.username, ); if (!verifiedInfo) { setInvalidWithError('Username is taken'); return; } } setCurrentStep(currentStep + 1); setAttemptedSubmit(false); setValid(false); fadeFormIn(); } }; const advanceRegistration = async () => { setAttemptedSubmit(true); if (!valid) { return; } if (!tcAccepted) { Alert.alert('Terms and conditions', ERROR_T_AND_C_NOT_ACCEPTED); return; } const {fname, lname, phone, email, username, password} = form; const response = await sendRegister( fname, lname, phone, email, username, password, ); if (response) { const data = await response.json(); const {token, UserID} = data; switch (response.status) { case 201: await AsyncStorage.setItem('token', token); navigation.navigate('ProfileInfoOnboarding', { userId: UserID, username: username, }); break; case 400: Alert.alert(ERROR_REGISTRATION(data.toLowerCase())); break; default: Alert.alert(ERROR_SOMETHING_WENT_WRONG_REFRESH); break; } } else { Alert.alert(ERROR_SOMETHING_WENT_WRONG_REFRESH); } }; useEffect(() => { if (isPhoneVerified) { advance(); } }, [isPhoneVerified]); return ( {currentStep !== 0 && currentStep !== 3 && ( { // if I go back do I want to reset the previous form? setCurrentStep(currentStep - 1); resetForm(step.placeholder); setAttemptedSubmit(false); setFadeValue(new Animated.Value(0)); }} /> )} {step.placeholder === 'Phone' && !isPhoneVerified ? ( <> PHONE NUMBER ) : ( <> {step.placeholder !== 'Password' ? ( <> SIGN UP ) : ( <> SIGN UP setPassVisibility(!passVisibility)}> Show Password )} )} ); }; const styles = StyleSheet.create({ container: { height: SCREEN_HEIGHT, width: SCREEN_WIDTH, alignItems: 'center', justifyContent: 'center', }, formContainer: { marginTop: '20%', height: SCREEN_HEIGHT * 0.2, width: '80%', justifyContent: 'space-between', alignItems: 'center', }, arrow: { color: TAGG_LIGHT_BLUE_2, }, showPassContainer: { marginVertical: '1%', borderBottomWidth: 1, paddingBottom: '1%', alignSelf: 'flex-start', borderBottomColor: 'white', marginBottom: '8%', }, showPass: { color: 'white', }, passWarning: { fontSize: 14, marginTop: 5, color: '#FF8989', maxWidth: 350, alignSelf: 'flex-start', }, input: { minWidth: '100%', height: 40, fontSize: 16, fontWeight: '600', color: '#fff', borderBottomWidth: 1, borderBottomColor: '#fff', }, invalidColor: { color: '#FF8989', }, errorInput: { minWidth: '60%', height: 40, fontSize: 16, fontWeight: '600', color: '#FF8989', borderBottomWidth: 1, borderBottomColor: '#fff', }, button: { alignItems: 'center', width: 40, aspectRatio: 10, }, formHeader: { color: '#fff', fontSize: 30, fontWeight: '600', position: 'absolute', top: SCREEN_HEIGHT * 0.15, }, load: { top: '5%', }, tc: { marginVertical: '5%', }, backArrow: { width: normalize(29), height: normalize(25), position: 'absolute', top: HeaderHeight * 1.5, left: 20, }, }); export default BasicInfoOnboarding;