aboutsummaryrefslogtreecommitdiff
path: root/src/screens/onboarding/BasicInfoOnboarding.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/screens/onboarding/BasicInfoOnboarding.tsx')
-rw-r--r--src/screens/onboarding/BasicInfoOnboarding.tsx460
1 files changed, 460 insertions, 0 deletions
diff --git a/src/screens/onboarding/BasicInfoOnboarding.tsx b/src/screens/onboarding/BasicInfoOnboarding.tsx
new file mode 100644
index 00000000..25a5a6f8
--- /dev/null
+++ b/src/screens/onboarding/BasicInfoOnboarding.tsx
@@ -0,0 +1,460 @@
+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,
+ View,
+} from 'react-native';
+import {
+ ArrowButton,
+ Background,
+ LoadingIndicator,
+ TaggInput,
+ TaggSquareButton,
+ TermsConditions,
+} from '../../components';
+import {
+ emailRegex,
+ nameRegex,
+ passwordRegex,
+ phoneRegex,
+ 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} from '../../services';
+import {BackgroundGradientType} from '../../types';
+import {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<BasicInfoOnboardingProps> = ({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 [form, setForm] = useState({
+ fname: '',
+ lname: '',
+ username: '',
+ phone: '',
+ email: '',
+ password: '',
+ });
+
+ const goNext = 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,
+ });
+ setValid(isValidName);
+ break;
+ case 1:
+ setForm({
+ ...form,
+ lname: name,
+ });
+ setValid(isValidName);
+ break;
+ case 2:
+ setForm({
+ ...form,
+ username: name,
+ });
+ setValid(usernameRegex.test(name));
+ break;
+ }
+ };
+ const handlePhoneUpdate = (phone: string) => {
+ phone = phone.trim();
+ setForm({
+ ...form,
+ phone,
+ });
+ setValid(phoneRegex.test(phone));
+ };
+ const handleEmailUpdate = (email: string) => {
+ email = email.trim();
+ setForm({
+ ...form,
+ email,
+ });
+ setValid(emailRegex.test(email));
+ };
+ const handlePasswordUpdate = (password: string) => {
+ setForm({
+ ...form,
+ password,
+ });
+ setValid(passwordRegex.test(password));
+ };
+ const formSteps: {
+ placeholder: string;
+ onChangeText: (text: string) => void;
+ }[] = [
+ {
+ placeholder: 'First Name',
+ onChangeText: (text) => handleNameUpdate(text, 0),
+ },
+ {
+ placeholder: 'Last Name',
+ onChangeText: (text) => handleNameUpdate(text, 1),
+ },
+ {
+ placeholder: 'Phone',
+ onChangeText: handlePhoneUpdate,
+ },
+ {
+ placeholder: 'School Email',
+ onChangeText: handleEmailUpdate,
+ },
+ {
+ placeholder: 'Username',
+ onChangeText: (text) => handleNameUpdate(text, 2),
+ },
+ {
+ placeholder: 'Password',
+ onChangeText: handlePasswordUpdate,
+ },
+ ];
+ 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];
+ const advance = () => {
+ setAttemptedSubmit(true);
+ if (valid) {
+ setCurrentStep(currentStep + 1);
+ setAttemptedSubmit(false);
+ setValid(false);
+ }
+ };
+ 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 (
+ <Background
+ style={styles.container}
+ gradientType={BackgroundGradientType.Light}>
+ <StatusBar barStyle="light-content" />
+ {/* getting rid of registration progress in onboarding*/}
+ {/* <RegistrationWizard style={styles.wizard} step="one" /> */}
+ <KeyboardAvoidingView
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
+ style={styles.container}>
+ <View style={styles.footer}>
+ {currentStep !== 0 && currentStep !== 3 && (
+ <ArrowButton
+ direction="backward"
+ onPress={() => {
+ // if I go back do I want to reset the previous form?
+ setCurrentStep(currentStep - 1);
+ resetForm(step.placeholder);
+ setAttemptedSubmit(false);
+ }}
+ />
+ )}
+ </View>
+ {step.placeholder === 'Phone' && !isPhoneVerified ? (
+ <>
+ <TaggInput
+ maxLength={10} // currently only support US phone numbers
+ accessibilityHint="Enter your phone number."
+ accessibilityLabel="Phone number input field."
+ placeholder="Phone Number"
+ autoCompleteType="tel"
+ textContentType="telephoneNumber"
+ autoCapitalize="none"
+ keyboardType="number-pad"
+ onChangeText={handlePhoneUpdate}
+ autoFocus={true}
+ blurOnSubmit={false}
+ valid={valid}
+ invalidWarning={'Please enter a valid 10 digit number.'}
+ attemptedSubmit={attemptedSubmit}
+ onSubmitEditing={goNext}
+ />
+ <TaggSquareButton
+ onPress={goNext}
+ title={'Verify'}
+ buttonStyle={'normal'}
+ buttonColor={'white'}
+ labelColor={'blue'}
+ />
+ </>
+ ) : (
+ <>
+ {step.placeholder !== 'Password' ? (
+ <>
+ <Text style={styles.formHeader}>SIGN UP</Text>
+ <View>
+ <TaggInput
+ key={step.placeholder}
+ style={styles.input}
+ accessibilityHint={`Enter your ${step.placeholder.toLowerCase()}`}
+ accessibilityLabel={`${step.placeholder} input field.`}
+ placeholder={currentStep + ' ' + step.placeholder}
+ autoCompleteType="name"
+ autoCapitalize="none"
+ textContentType="name"
+ returnKeyType="done"
+ onChangeText={step.onChangeText}
+ onSubmitEditing={advance}
+ autoFocus={true}
+ blurOnSubmit={false}
+ valid={valid}
+ invalidWarning={`Please enter a valid ${step.placeholder.toLowerCase()}`}
+ attemptedSubmit={attemptedSubmit}
+ />
+ <TaggSquareButton
+ onPress={advance}
+ title={'Next'}
+ buttonStyle={'normal'}
+ buttonColor={'white'}
+ labelColor={'blue'}
+ />
+ </View>
+ </>
+ ) : (
+ <>
+ <TaggInput
+ accessibilityHint="Enter a password."
+ accessibilityLabel="Password input field."
+ placeholder="Password"
+ autoCompleteType="password"
+ textContentType="oneTimeCode"
+ returnKeyType="next"
+ onChangeText={handlePasswordUpdate}
+ onSubmitEditing={advanceRegistration}
+ blurOnSubmit={false}
+ secureTextEntry={!passVisibility}
+ valid={valid}
+ invalidWarning={
+ 'Password must be at least 8 characters & contain at least one of a-z, A-Z, 0-9, and a special character.'
+ }
+ attemptedSubmit={attemptedSubmit}
+ style={styles.input}
+ />
+ <TouchableOpacity
+ onPress={() => setPassVisibility(!passVisibility)}>
+ <Text style={[styles.tc, styles.tc]}>Show Password</Text>
+ </TouchableOpacity>
+ <LoadingIndicator />
+ <TermsConditions
+ style={styles.tc}
+ accepted={tcAccepted}
+ onChange={setTCAccepted}
+ />
+ </>
+ )}
+ </>
+ )}
+ </KeyboardAvoidingView>
+ </Background>
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ height: SCREEN_HEIGHT,
+ width: SCREEN_WIDTH,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ input: {
+ minWidth: '60%',
+ height: 40,
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#fff',
+ paddingLeft: 13,
+ borderBottomWidth: 1,
+ borderBottomColor: '#fff',
+ },
+ button: {
+ width: 40,
+ aspectRatio: 10,
+ },
+ formHeader: {
+ color: '#fff',
+ fontSize: 30,
+ fontWeight: '600',
+ position: 'absolute',
+ top: '20%',
+ },
+ load: {
+ top: '5%',
+ },
+ tc: {
+ marginVertical: '5%',
+ },
+ footer: {
+ width: '100%',
+ flexDirection: 'row',
+ justifyContent: 'space-around',
+ ...Platform.select({
+ ios: {
+ bottom: '20%',
+ },
+ android: {
+ bottom: '10%',
+ },
+ }),
+ },
+});
+export default BasicInfoOnboarding;