diff options
author | Ashm Walia <40498934+ashmgarv@users.noreply.github.com> | 2020-10-06 22:06:06 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-07 01:06:06 -0400 |
commit | e86478f52e191c52fea20980278174af46f50953 (patch) | |
tree | 195cacdf4326d199294034c0712b626bf7ebcfda | |
parent | 8aafec40501b2236f127cf9175e8a21eb31ee9b0 (diff) |
TMA 207 : Updated the Onboarding Process (#42)
-rw-r--r-- | src/routes/onboarding/Onboarding.tsx | 5 | ||||
-rw-r--r-- | src/routes/onboarding/OnboardingStack.tsx | 7 | ||||
-rw-r--r-- | src/screens/onboarding/Checkpoint.tsx | 6 | ||||
-rw-r--r-- | src/screens/onboarding/RegistrationOne.tsx | 160 | ||||
-rw-r--r-- | src/screens/onboarding/RegistrationThree.tsx | 362 | ||||
-rw-r--r-- | src/screens/onboarding/RegistrationTwo.tsx | 260 | ||||
-rw-r--r-- | src/screens/onboarding/Verification.tsx | 47 | ||||
-rw-r--r-- | src/screens/onboarding/index.ts | 1 |
8 files changed, 529 insertions, 319 deletions
diff --git a/src/routes/onboarding/Onboarding.tsx b/src/routes/onboarding/Onboarding.tsx index b14bd85c..7b2eb85d 100644 --- a/src/routes/onboarding/Onboarding.tsx +++ b/src/routes/onboarding/Onboarding.tsx @@ -4,6 +4,7 @@ import { Login, RegistrationOne, RegistrationTwo, + RegistrationThree, Verification, ProfileOnboarding, Checkpoint, @@ -39,6 +40,10 @@ const Onboarding: React.FC = () => { name="RegistrationTwo" component={RegistrationTwo} /> + <OnboardingStack.Screen + name="RegistrationThree" + component={RegistrationThree} + /> <OnboardingStack.Screen name="Checkpoint" component={Checkpoint} /> <OnboardingStack.Screen name="Verification" component={Verification} /> <OnboardingStack.Screen diff --git a/src/routes/onboarding/OnboardingStack.tsx b/src/routes/onboarding/OnboardingStack.tsx index 554260c8..1f6214fd 100644 --- a/src/routes/onboarding/OnboardingStack.tsx +++ b/src/routes/onboarding/OnboardingStack.tsx @@ -4,11 +4,10 @@ export type OnboardingStackParams = { Splash: undefined; Login: undefined; RegistrationOne: undefined; - RegistrationTwo: - | {firstName: string; lastName: string; email: string} - | undefined; + RegistrationTwo: {email: string}; + RegistrationThree: {firstName: string; lastName: string; email: string}; Checkpoint: {username: string; userId: string}; - Verification: {username: string; email: string; userId: string}; + Verification: {email: string}; ProfileOnboarding: {username: string; userId: string}; }; diff --git a/src/screens/onboarding/Checkpoint.tsx b/src/screens/onboarding/Checkpoint.tsx index 4a58548e..83f330f1 100644 --- a/src/screens/onboarding/Checkpoint.tsx +++ b/src/screens/onboarding/Checkpoint.tsx @@ -24,7 +24,7 @@ interface CheckpointProps { navigation: CheckpointNavigationProp; } /** - * Registration screen 2 for email, username, password, and terms and conditions + * Checkpoint to ask user if profile setup should be done * @param navigation react-navigation navigation object */ const Checkpoint: React.FC<CheckpointProps> = ({route, navigation}) => { @@ -50,10 +50,10 @@ const Checkpoint: React.FC<CheckpointProps> = ({route, navigation}) => { return ( <Background style={styles.container}> <StatusBar barStyle="light-content" /> - <RegistrationWizard style={styles.wizard} step="four" /> + <RegistrationWizard style={styles.wizard} step="five" /> <View style={styles.textContainer}> - <Text style={styles.header}>Email verified!</Text> + <Text style={styles.header}>You are registered!</Text> <Text style={styles.subtext}> We're almost there. Would you like to setup your profile now? </Text> diff --git a/src/screens/onboarding/RegistrationOne.tsx b/src/screens/onboarding/RegistrationOne.tsx index 720fcaed..9e9cabf5 100644 --- a/src/screens/onboarding/RegistrationOne.tsx +++ b/src/screens/onboarding/RegistrationOne.tsx @@ -10,16 +10,23 @@ import { Platform, TouchableOpacity, KeyboardAvoidingView, + ActivityIndicator, } from 'react-native'; import {OnboardingStackParams} from '../../routes'; + import { ArrowButton, RegistrationWizard, TaggInput, Background, } from '../../components'; -import {nameRegex, emailRegex} from '../../constants'; + +import {usePromiseTracker, trackPromise} from 'react-promise-tracker'; + +import {SEND_OTP_ENDPOINT} from '../../constants'; + +import {emailRegex} from '../../constants'; type RegistrationScreenOneRouteProp = RouteProp< OnboardingStackParams, @@ -34,67 +41,21 @@ interface RegistrationOneProps { navigation: RegistrationScreenOneNavigationProp; } /** - * Registration screen 1 for First Name, Last Name, and email + * Registration screen 1 for Email * @param navigation react-navigation navigation object */ const RegistrationOne: React.FC<RegistrationOneProps> = ({navigation}) => { - // refs for changing focus - const lnameRef = useRef(); + // // refs for changing focus const emailRef = useRef(); - /** - * Handles focus change to the next input field. - * @param field key for field to move focus onto - */ - const handleFocusChange = (field: string): void => { - switch (field) { - case 'lname': - const lnameField: any = lnameRef.current; - lnameField.focus(); - break; - case 'email': - const emailField: any = emailRef.current; - emailField.focus(); - break; - default: - return; - } - }; // registration form state const [form, setForm] = useState({ - fname: '', - lname: '', email: '', - isValidFname: false, - isValidLname: false, isValidEmail: false, attemptedSubmit: false, }); /* - * Handles changes to the first name field value and verifies the input by updating state and running a validation function. - */ - const handleFnameUpdate = (fname: string) => { - let isValidFname: boolean = nameRegex.test(fname); - setForm({ - ...form, - fname, - isValidFname, - }); - }; - /* - * Handles changes to the last name field value and verifies the input by updating state and running a validation function. - */ - const handleLnameUpdate = (lname: string) => { - let isValidLname: boolean = nameRegex.test(lname); - setForm({ - ...form, - lname, - isValidLname, - }); - }; - - /* * Handles changes to the email field value and verifies the input by updating state and running a validation function. */ const handleEmailUpdate = (email: string) => { @@ -107,9 +68,9 @@ const RegistrationOne: React.FC<RegistrationOneProps> = ({navigation}) => { }; /** - * Handles a click on the "next" arrow button by navigating to RegistrationTwo if First Name and Last Name are filled + * Handles a click on the "next" arrow button by navigating to Verification if email is filled correctly */ - const goToRegisterTwo = async () => { + const goToVerification = async () => { if (!form.attemptedSubmit) { setForm({ ...form, @@ -117,12 +78,30 @@ const RegistrationOne: React.FC<RegistrationOneProps> = ({navigation}) => { }); } try { - if (form.isValidFname && form.isValidLname && form.isValidEmail) { - navigation.navigate('RegistrationTwo', { - firstName: form.fname, - lastName: form.lname, - email: form.email, - }); + if (form.isValidEmail) { + let sendOtpResponse = await trackPromise( + fetch(SEND_OTP_ENDPOINT, { + method: 'POST', + body: JSON.stringify({ + email: form.email, + }), + }), + ); + let otpStatusCode = sendOtpResponse.status; + if (otpStatusCode === 200) { + navigation.navigate('Verification', { + email: form.email, + }); + } else if (otpStatusCode === 409) { + Alert.alert( + 'This email is already registered with us, please use another email.', + ); + } else { + Alert.alert( + "Looks like Our email servers might be down π'", + "Try again in a couple minutes. We're sorry for the inconvenience.", + ); + } } else { setForm({...form, attemptedSubmit: false}); setTimeout(() => setForm({...form, attemptedSubmit: true})); @@ -133,7 +112,7 @@ const RegistrationOne: React.FC<RegistrationOneProps> = ({navigation}) => { "Try again in a couple minutes. We're sorry for the inconvenience.", ); return { - name: 'Registration error', + name: 'Send OTP error', description: error, }; } @@ -145,24 +124,28 @@ const RegistrationOne: React.FC<RegistrationOneProps> = ({navigation}) => { direction="backward" onPress={() => navigation.navigate('Login')} /> - <TouchableOpacity onPress={goToRegisterTwo}> + <TouchableOpacity onPress={goToVerification}> <ArrowButton direction="forward" - disabled={ - !(form.isValidFname && form.isValidLname && form.isValidEmail) - } - onPress={() => - navigation.navigate('RegistrationTwo', { - firstName: form.fname, - lastName: form.lname, - email: form.email, - }) - } + disabled={!form.isValidEmail} + onPress={goToVerification} /> </TouchableOpacity> </View> ); + /** + * An activity indicator to indicate that the app is working during the send_otp request. + */ + const LoadingIndicator = () => { + const {promiseInProgress} = usePromiseTracker(); + return promiseInProgress ? ( + <ActivityIndicator style={styles.load} size="large" color="#fff" /> + ) : ( + <></> + ); + }; + return ( <Background style={styles.container}> <StatusBar barStyle="light-content" /> @@ -171,40 +154,9 @@ const RegistrationOne: React.FC<RegistrationOneProps> = ({navigation}) => { behavior={Platform.OS === 'ios' ? 'padding' : 'height'} style={styles.container}> <View> - <Text style={styles.formHeader}>SIGN UP</Text> + <Text style={styles.formHeader}>ENTER EMAIL</Text> </View> <TaggInput - accessibilityHint="Enter your first name." - accessibilityLabel="First name input field." - placeholder="First Name" - autoCompleteType="name" - textContentType="name" - returnKeyType="next" - onChangeText={handleFnameUpdate} - onSubmitEditing={() => handleFocusChange('lname')} - blurOnSubmit={false} - valid={form.isValidFname} - invalidWarning="Please enter a valid first name." - attemptedSubmit={form.attemptedSubmit} - width={280} - /> - <TaggInput - accessibilityHint="Enter your last name." - accessibilityLabel="Last name input field." - placeholder="Last Name" - autoCompleteType="name" - textContentType="name" - returnKeyType="next" - onChangeText={handleLnameUpdate} - onSubmitEditing={() => handleFocusChange('email')} - blurOnSubmit={false} - ref={lnameRef} - valid={form.isValidLname} - invalidWarning="Please enter a valid last name." - attemptedSubmit={form.attemptedSubmit} - width={280} - /> - <TaggInput accessibilityHint="Enter your email." accessibilityLabel="Email input field." placeholder="Email" @@ -220,8 +172,9 @@ const RegistrationOne: React.FC<RegistrationOneProps> = ({navigation}) => { invalidWarning={'Please enter a valid email address.'} attemptedSubmit={form.attemptedSubmit} width={280} - onSubmitEditing={goToRegisterTwo} + onSubmitEditing={goToVerification} /> + <LoadingIndicator /> </KeyboardAvoidingView> <Footer /> </Background> @@ -250,6 +203,9 @@ const styles = StyleSheet.create({ fontWeight: '600', marginBottom: '16%', }, + load: { + top: '5%', + }, footer: { width: '100%', flexDirection: 'row', diff --git a/src/screens/onboarding/RegistrationThree.tsx b/src/screens/onboarding/RegistrationThree.tsx new file mode 100644 index 00000000..f8daaf71 --- /dev/null +++ b/src/screens/onboarding/RegistrationThree.tsx @@ -0,0 +1,362 @@ +/** + * Author : Ashm Walia + * Purpose : Add a new screen to allow the user to enter first and last name + */ + +import React, {useState, useRef} from 'react'; +import {RouteProp} from '@react-navigation/native'; +import {StackNavigationProp} from '@react-navigation/stack'; +import { + View, + Text, + StyleSheet, + StatusBar, + Alert, + Platform, + TouchableOpacity, + KeyboardAvoidingView, + ActivityIndicator, +} from 'react-native'; +import {usePromiseTracker} from 'react-promise-tracker'; + +import {OnboardingStackParams} from '../../routes'; +import { + ArrowButton, + RegistrationWizard, + TaggInput, + TermsConditions, + Background, +} from '../../components'; +import {passwordRegex, usernameRegex, REGISTER_ENDPOINT} from '../../constants'; + +type RegistrationScreenThreeRouteProp = RouteProp< + OnboardingStackParams, + 'RegistrationThree' +>; +type RegistrationScreenThreeNavigationProp = StackNavigationProp< + OnboardingStackParams, + 'RegistrationThree' +>; +interface RegistrationThreeProps { + route: RegistrationScreenThreeRouteProp; + navigation: RegistrationScreenThreeNavigationProp; +} +/** + * Registration screen 3 for username, password, and terms and conditions + * @param navigation react-navigation navigation object + */ +const RegistrationThree: React.FC<RegistrationThreeProps> = ({ + route, + navigation, +}) => { + // refs for changing focus + const usernameRef = useRef(); + const passwordRef = useRef(); + const confirmRef = useRef(); + + const registrationName = route.params; + const fname: string = registrationName!.firstName; + const lname: string = registrationName!.lastName; + const email: string = registrationName!.email; + /** + * Handles focus change to the next input field. + * @param field key for field to move focus onto + */ + const handleFocusChange = (field: string): void => { + switch (field) { + case 'username': + const usernameField: any = usernameRef.current; + usernameField.focus(); + break; + case 'password': + const passwordField: any = passwordRef.current; + passwordField.focus(); + break; + case 'confirm': + const confirmField: any = confirmRef.current; + confirmField.focus(); + break; + default: + return; + } + }; + + // registration form state + const [form, setForm] = useState({ + email: '', + username: '', + password: '', + confirm: '', + isValidEmail: false, + isValidUsername: false, + isValidPassword: false, + passwordsMatch: false, + tcAccepted: false, + attemptedSubmit: false, + }); + + /* + * Handles changes to the username field value and verifies the input by updating state and running a validation function. + */ + const handleUsernameUpdate = (username: string) => { + let isValidUsername: boolean = usernameRegex.test(username); + setForm({ + ...form, + username, + isValidUsername, + }); + }; + /* + * Handles changes to the password field value and verifies the input by updating state and running a validation function. + */ + const handlePasswordUpdate = (password: string) => { + let isValidPassword: boolean = passwordRegex.test(password); + let passwordsMatch: boolean = form.password === form.confirm; + setForm({ + ...form, + password, + isValidPassword, + passwordsMatch, + }); + }; + + /* + * Handles changes to the confirm password field value and verifies the input by updating state and running a validation function. + */ + const handleConfirmUpdate = (confirm: string) => { + let passwordsMatch: boolean = form.password === confirm; + setForm({ + ...form, + confirm, + passwordsMatch, + }); + }; + + /** + * Handles changes to the terms and conditions accepted boolean. + * @param tcAccepted the boolean to set the terms and conditions value to + */ + const handleTcUpdate = (tcAccepted: boolean) => { + setForm({ + ...form, + tcAccepted, + }); + }; + + /** + * Handles a click on the "next" arrow button by sending an API request to the backend and displaying the appropriate response. + */ + const handleRegister = async () => { + if (!form.attemptedSubmit) { + setForm({ + ...form, + attemptedSubmit: true, + }); + } + try { + if (form.isValidUsername && form.isValidPassword && form.passwordsMatch) { + if (form.tcAccepted) { + let registerResponse = await fetch(REGISTER_ENDPOINT, { + method: 'POST', + body: JSON.stringify({ + first_name: fname, + last_name: lname, + email: email, + username: form.username, + password: form.password, + }), + }); + let statusCode = registerResponse.status; + let data = await registerResponse.json(); + const userId: string = data.UserID; + if (statusCode === 201) { + navigation.navigate('Checkpoint', { + userId: userId, + username: form.username, + }); + } else if (statusCode === 409) { + Alert.alert('Registration failed π', `${data}`); + } else { + Alert.alert( + 'Something went wrong! π', + "Would you believe me if I told you that I don't know what happened?", + ); + } + } else { + Alert.alert( + 'Terms and conditions', + 'You must first agree to the terms and conditions.', + ); + } + } else { + setForm({...form, attemptedSubmit: false}); + setTimeout(() => setForm({...form, attemptedSubmit: true})); + } + } catch (error) { + Alert.alert( + 'Registration failed π', + 'Please double-check your network connection and retry.', + ); + return { + name: 'Registration error', + description: error, + }; + } + }; + + const Footer = () => ( + <View style={styles.footer}> + <ArrowButton + direction="backward" + onPress={() => navigation.navigate('RegistrationOne')} + /> + <TouchableOpacity onPress={handleRegister}> + <ArrowButton + direction="forward" + disabled={ + !( + form.isValidUsername && + form.isValidPassword && + form.passwordsMatch && + form.tcAccepted + ) + } + onPress={handleRegister} + /> + </TouchableOpacity> + </View> + ); + + /** + * An activity indicator to indicate that the app is working during the send_otp request. + */ + const LoadingIndicator = () => { + const {promiseInProgress} = usePromiseTracker(); + return promiseInProgress ? ( + <ActivityIndicator style={styles.load} size="large" color="#fff" /> + ) : ( + <></> + ); + }; + + return ( + <Background style={styles.container}> + <StatusBar barStyle="light-content" /> + <RegistrationWizard style={styles.wizard} step="four" /> + <KeyboardAvoidingView + behavior={Platform.OS === 'ios' ? 'padding' : 'height'} + style={styles.container}> + <View> + <Text style={styles.formHeader}>SIGN UP</Text> + </View> + <TaggInput + accessibilityHint="Enter a username." + accessibilityLabel="Username input field." + placeholder="Username" + autoCompleteType="username" + textContentType="username" + autoCapitalize="none" + returnKeyType="next" + onChangeText={handleUsernameUpdate} + onSubmitEditing={() => handleFocusChange('password')} + blurOnSubmit={false} + ref={usernameRef} + valid={form.isValidUsername} + invalidWarning={ + 'Username must beΒ at least 6 characters and contain only alphanumerics.' + } + attemptedSubmit={form.attemptedSubmit} + width={280} + /> + <TaggInput + accessibilityHint="Enter a password." + accessibilityLabel="Password input field." + placeholder="Password" + autoCompleteType="password" + textContentType="newPassword" + returnKeyType="next" + onChangeText={handlePasswordUpdate} + onSubmitEditing={() => handleFocusChange('confirm')} + blurOnSubmit={false} + secureTextEntry + ref={passwordRef} + valid={form.isValidPassword} + invalidWarning={ + 'Password must be at least 8 characters & contain at least one of a-z, A-Z, 0-9, and a special character.' + } + attemptedSubmit={form.attemptedSubmit} + width={280} + /> + <TaggInput + accessibilityHint={'Re-enter your password.'} + accessibilityLabel={'Password confirmation input field.'} + placeholder={'Confirm Password'} + autoCompleteType="password" + textContentType="password" + returnKeyType={form.tcAccepted ? 'go' : 'default'} + onChangeText={handleConfirmUpdate} + onSubmitEditing={handleRegister} + secureTextEntry + ref={confirmRef} + valid={form.passwordsMatch} + invalidWarning={'Passwords must match.'} + attemptedSubmit={form.attemptedSubmit} + width={280} + /> + <LoadingIndicator /> + <TermsConditions + style={styles.tc} + accepted={form.tcAccepted} + onChange={handleTcUpdate} + /> + </KeyboardAvoidingView> + <Footer /> + </Background> + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + wizard: { + ...Platform.select({ + ios: { + top: 50, + }, + android: { + bottom: 40, + }, + }), + }, + formHeader: { + color: '#fff', + fontSize: 30, + fontWeight: '600', + marginBottom: '16%', + }, + tc: { + marginVertical: '5%', + top: '8%', + }, + load: { + top: '5%', + }, + footer: { + width: '100%', + flexDirection: 'row', + justifyContent: 'space-around', + ...Platform.select({ + ios: { + bottom: '20%', + }, + android: { + bottom: '10%', + }, + }), + }, +}); + +export default RegistrationThree; diff --git a/src/screens/onboarding/RegistrationTwo.tsx b/src/screens/onboarding/RegistrationTwo.tsx index b67c2403..0ce4f410 100644 --- a/src/screens/onboarding/RegistrationTwo.tsx +++ b/src/screens/onboarding/RegistrationTwo.tsx @@ -10,24 +10,18 @@ import { Platform, TouchableOpacity, KeyboardAvoidingView, - ActivityIndicator, } from 'react-native'; -import {usePromiseTracker, trackPromise} from 'react-promise-tracker'; import {OnboardingStackParams} from '../../routes'; + import { ArrowButton, RegistrationWizard, TaggInput, - TermsConditions, Background, } from '../../components'; -import { - passwordRegex, - usernameRegex, - REGISTER_ENDPOINT, - SEND_OTP_ENDPOINT, -} from '../../constants'; + +import {nameRegex} from '../../constants'; type RegistrationScreenTwoRouteProp = RouteProp< OnboardingStackParams, @@ -42,7 +36,7 @@ interface RegistrationTwoProps { navigation: RegistrationScreenTwoNavigationProp; } /** - * Registration screen 2 for email, username, password, and terms and conditions + * Registration screen 2 for First Name and Last Name * @param navigation react-navigation navigation object */ const RegistrationTwo: React.FC<RegistrationTwoProps> = ({ @@ -50,31 +44,19 @@ const RegistrationTwo: React.FC<RegistrationTwoProps> = ({ navigation, }) => { // refs for changing focus - const usernameRef = useRef(); - const passwordRef = useRef(); - const confirmRef = useRef(); + const lnameRef = useRef(); - const registrationName = route.params; - const fname: string = registrationName!.firstName; - const lname: string = registrationName!.lastName; - const email: string = registrationName!.email; + const params = route.params; + const email: string = params!.email; /** * Handles focus change to the next input field. * @param field key for field to move focus onto */ const handleFocusChange = (field: string): void => { switch (field) { - case 'username': - const usernameField: any = usernameRef.current; - usernameField.focus(); - break; - case 'password': - const passwordField: any = passwordRef.current; - passwordField.focus(); - break; - case 'confirm': - const confirmField: any = confirmRef.current; - confirmField.focus(); + case 'lname': + const lnameField: any = lnameRef.current; + lnameField.focus(); break; default: return; @@ -83,70 +65,41 @@ const RegistrationTwo: React.FC<RegistrationTwoProps> = ({ // registration form state const [form, setForm] = useState({ - email: '', - username: '', - password: '', - confirm: '', - isValidEmail: false, - isValidUsername: false, - isValidPassword: false, - passwordsMatch: false, - tcAccepted: false, + fname: '', + lname: '', + isValidFname: false, + isValidLname: false, attemptedSubmit: false, }); /* - * Handles changes to the username field value and verifies the input by updating state and running a validation function. - */ - const handleUsernameUpdate = (username: string) => { - let isValidUsername: boolean = usernameRegex.test(username); - setForm({ - ...form, - username, - isValidUsername, - }); - }; - /* - * Handles changes to the password field value and verifies the input by updating state and running a validation function. + * Handles changes to the first name field value and verifies the input by updating state and running a validation function. */ - const handlePasswordUpdate = (password: string) => { - let isValidPassword: boolean = passwordRegex.test(password); - let passwordsMatch: boolean = form.password === form.confirm; + const handleFnameUpdate = (fname: string) => { + let isValidFname: boolean = nameRegex.test(fname); setForm({ ...form, - password, - isValidPassword, - passwordsMatch, + fname, + isValidFname, }); }; /* - * Handles changes to the confirm password field value and verifies the input by updating state and running a validation function. - */ - const handleConfirmUpdate = (confirm: string) => { - let passwordsMatch: boolean = form.password === confirm; - setForm({ - ...form, - confirm, - passwordsMatch, - }); - }; - - /** - * Handles changes to the terms and conditions accepted boolean. - * @param tcAccepted the boolean to set the terms and conditions value to + * Handles changes to the last name field value and verifies the input by updating state and running a validation function. */ - const handleTcUpdate = (tcAccepted: boolean) => { + const handleLnameUpdate = (lname: string) => { + let isValidLname: boolean = nameRegex.test(lname); setForm({ ...form, - tcAccepted, + lname, + isValidLname, }); }; /** - * Handles a click on the "next" arrow button by sending an API request to the backend and displaying the appropriate response. + * Handles a click on the "next" arrow button by navigating to RegistrationThree if First Name and Last Name are filled */ - const handleRegister = async () => { + const goToRegisterThree = async () => { if (!form.attemptedSubmit) { setForm({ ...form, @@ -154,64 +107,23 @@ const RegistrationTwo: React.FC<RegistrationTwoProps> = ({ }); } try { - if (form.isValidUsername && form.isValidPassword && form.passwordsMatch) { - if (form.tcAccepted) { - let registerResponse = await fetch(REGISTER_ENDPOINT, { - method: 'POST', - body: JSON.stringify({ - first_name: fname, - last_name: lname, - email: email, - username: form.username, - password: form.password, - }), - }); - let statusCode = registerResponse.status; - let data = await registerResponse.json(); - if (statusCode === 201) { - let sendOtpResponse = await trackPromise( - fetch(SEND_OTP_ENDPOINT, { - method: 'POST', - body: JSON.stringify({ - username: form.username, - email: email, - }), - }), - ); - let otpStatusCode = sendOtpResponse.status; - if (otpStatusCode === 200) { - const userId: string = data.UserID; - navigation.navigate('Verification', { - username: form.username, - email: email, - userId: userId, - }); - } - } else if (statusCode === 409) { - Alert.alert('Registration failed π', `${data}`); - } else { - Alert.alert( - 'Something went wrong! π', - "Would you believe me if I told you that I don't know what happened?", - ); - } - } else { - Alert.alert( - 'Terms and conditions', - 'You must first agree to the terms and conditions.', - ); - } + if (form.isValidFname && form.isValidLname) { + navigation.navigate('RegistrationThree', { + firstName: form.fname, + lastName: form.lname, + email: email, + }); } else { setForm({...form, attemptedSubmit: false}); setTimeout(() => setForm({...form, attemptedSubmit: true})); } } catch (error) { Alert.alert( - 'Registration failed π', - 'Please double-check your network connection and retry.', + 'There was a problem while loading the next page π', + "Try again in a couple minutes. We're sorry for the inconvenience.", ); return { - name: 'Registration error', + name: 'Navigation error', description: error, }; } @@ -221,106 +133,58 @@ const RegistrationTwo: React.FC<RegistrationTwoProps> = ({ <View style={styles.footer}> <ArrowButton direction="backward" - onPress={() => navigation.navigate('RegistrationOne')} + onPress={() => navigation.navigate('Login')} /> - <TouchableOpacity onPress={handleRegister}> + <TouchableOpacity onPress={goToRegisterThree}> <ArrowButton direction="forward" - disabled={ - !( - form.isValidUsername && - form.isValidPassword && - form.passwordsMatch && - form.tcAccepted - ) - } - onPress={handleRegister} + disabled={!(form.isValidFname && form.isValidLname)} + onPress={goToRegisterThree} /> </TouchableOpacity> </View> ); - /** - * An activity indicator to indicate that the app is working during the send_otp request. - */ - const LoadingIndicator = () => { - const {promiseInProgress} = usePromiseTracker(); - return promiseInProgress ? ( - <ActivityIndicator style={styles.load} size="large" color="#fff" /> - ) : ( - <></> - ); - }; - return ( <Background style={styles.container}> <StatusBar barStyle="light-content" /> - <RegistrationWizard style={styles.wizard} step="two" /> + <RegistrationWizard style={styles.wizard} step="three" /> <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : 'height'} style={styles.container}> <View> - <Text style={styles.formHeader}>SIGN UP</Text> + <Text style={styles.formHeader}>ENTER NAME</Text> </View> <TaggInput - accessibilityHint="Enter a username." - accessibilityLabel="Username input field." - placeholder="Username" - autoCompleteType="username" - textContentType="username" - autoCapitalize="none" + accessibilityHint="Enter your first name." + accessibilityLabel="First name input field." + placeholder="First Name" + autoCompleteType="name" + textContentType="name" returnKeyType="next" - onChangeText={handleUsernameUpdate} - onSubmitEditing={() => handleFocusChange('password')} + onChangeText={handleFnameUpdate} + onSubmitEditing={() => handleFocusChange('lname')} blurOnSubmit={false} - ref={usernameRef} - valid={form.isValidUsername} - invalidWarning={ - 'Username must beΒ at least 6 characters and contain only alphanumerics.' - } + valid={form.isValidFname} + invalidWarning="Please enter a valid first name." attemptedSubmit={form.attemptedSubmit} width={280} /> <TaggInput - accessibilityHint="Enter a password." - accessibilityLabel="Password input field." - placeholder="Password" - autoCompleteType="password" - textContentType="newPassword" + accessibilityHint="Enter your last name." + accessibilityLabel="Last name input field." + placeholder="Last Name" + autoCompleteType="name" + textContentType="name" returnKeyType="next" - onChangeText={handlePasswordUpdate} - onSubmitEditing={() => handleFocusChange('confirm')} + onChangeText={handleLnameUpdate} blurOnSubmit={false} - secureTextEntry - ref={passwordRef} - valid={form.isValidPassword} - invalidWarning={ - 'Password must be at least 8 characters & contain at least one of a-z, A-Z, 0-9, and a special character.' - } + ref={lnameRef} + valid={form.isValidLname} + invalidWarning="Please enter a valid last name." attemptedSubmit={form.attemptedSubmit} width={280} - /> - <TaggInput - accessibilityHint={'Re-enter your password.'} - accessibilityLabel={'Password confirmation input field.'} - placeholder={'Confirm Password'} - autoCompleteType="password" - textContentType="password" - returnKeyType={form.tcAccepted ? 'go' : 'default'} - onChangeText={handleConfirmUpdate} - onSubmitEditing={handleRegister} - secureTextEntry - ref={confirmRef} - valid={form.passwordsMatch} - invalidWarning={'Passwords must match.'} - attemptedSubmit={form.attemptedSubmit} - width={280} - /> - <LoadingIndicator /> - <TermsConditions - style={styles.tc} - accepted={form.tcAccepted} - onChange={handleTcUpdate} + onSubmitEditing={goToRegisterThree} /> </KeyboardAvoidingView> <Footer /> @@ -350,10 +214,6 @@ const styles = StyleSheet.create({ fontWeight: '600', marginBottom: '16%', }, - tc: { - marginVertical: '5%', - top: '8%', - }, load: { top: '5%', }, diff --git a/src/screens/onboarding/Verification.tsx b/src/screens/onboarding/Verification.tsx index 7c74324a..89f79ac8 100644 --- a/src/screens/onboarding/Verification.tsx +++ b/src/screens/onboarding/Verification.tsx @@ -3,7 +3,12 @@ import React from 'react'; import {OnboardingStackParams} from '../../routes'; import {RouteProp} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; -import {Background, RegistrationWizard, SubmitButton} from '../../components'; +import { + Background, + RegistrationWizard, + SubmitButton, + ArrowButton, +} from '../../components'; import {VERIFY_OTP_ENDPOINT, SEND_OTP_ENDPOINT} from '../../constants'; import {Text} from 'react-native-animatable'; import { @@ -19,6 +24,7 @@ import { KeyboardAvoidingView, Alert, ActivityIndicator, + Platform, } from 'react-native'; import {usePromiseTracker, trackPromise} from 'react-promise-tracker'; @@ -42,7 +48,7 @@ const Verification: React.FC<VerificationProps> = ({route, navigation}) => { value, setValue, }); - const {username, email, userId} = route.params; + const {email} = route.params; /** * Sends the verify_otp request upon tapping the Verify button. @@ -53,15 +59,14 @@ const Verification: React.FC<VerificationProps> = ({route, navigation}) => { let verifyOtpResponse = await fetch(VERIFY_OTP_ENDPOINT, { method: 'POST', body: JSON.stringify({ - username: username, + email: email, otp: value, }), }); let statusCode = verifyOtpResponse.status; - if (statusCode === 200) { - navigation.navigate('Checkpoint', { - userId: userId, - username: username, + if (statusCode == 200) { + navigation.navigate('RegistrationTwo', { + email: email, }); } else { Alert.alert( @@ -90,7 +95,6 @@ const Verification: React.FC<VerificationProps> = ({route, navigation}) => { fetch(SEND_OTP_ENDPOINT, { method: 'POST', body: JSON.stringify({ - username: username, email: email, }), }), @@ -111,7 +115,7 @@ const Verification: React.FC<VerificationProps> = ({route, navigation}) => { }; /** - * An activity indicator to indicate that the app is working during the send_otp request. + * An activity indicator to indicate that the app is working during the verify_otp request. */ const LoadingIndicator = () => { const {promiseInProgress} = usePromiseTracker(); @@ -126,9 +130,18 @@ const Verification: React.FC<VerificationProps> = ({route, navigation}) => { ); }; + const Footer = () => ( + <View style={styles.footer}> + <ArrowButton + direction="backward" + onPress={() => navigation.navigate('RegistrationOne')} + /> + </View> + ); + return ( <Background centered style={styles.container}> - <RegistrationWizard style={styles.wizard} step="three" /> + <RegistrationWizard style={styles.wizard} step="two" /> <KeyboardAvoidingView behavior="padding" style={styles.form}> <Text style={styles.formHeader}>Enter 6 digit code</Text> <Text style={styles.description}> @@ -167,6 +180,7 @@ const Verification: React.FC<VerificationProps> = ({route, navigation}) => { </TouchableOpacity> <LoadingIndicator /> </KeyboardAvoidingView> + <Footer /> </Background> ); }; @@ -235,5 +249,18 @@ const styles = StyleSheet.create({ loadingIndicator: { marginVertical: '5%', }, + footer: { + width: '100%', + flexDirection: 'row', + justifyContent: 'space-around', + ...Platform.select({ + ios: { + bottom: '20%', + }, + android: { + bottom: '10%', + }, + }), + }, }); export default Verification; diff --git a/src/screens/onboarding/index.ts b/src/screens/onboarding/index.ts index 094d1e7b..041864b7 100644 --- a/src/screens/onboarding/index.ts +++ b/src/screens/onboarding/index.ts @@ -1,6 +1,7 @@ export {default as Login} from './Login'; export {default as RegistrationOne} from './RegistrationOne'; export {default as RegistrationTwo} from './RegistrationTwo'; +export {default as RegistrationThree} from './RegistrationThree'; export {default as Verification} from './Verification'; export {default as Checkpoint} from './Checkpoint'; export {default as ProfileOnboarding} from './ProfileOnboarding'; |