aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/routes/onboarding/Onboarding.tsx5
-rw-r--r--src/routes/onboarding/OnboardingStack.tsx7
-rw-r--r--src/screens/onboarding/Checkpoint.tsx6
-rw-r--r--src/screens/onboarding/RegistrationOne.tsx160
-rw-r--r--src/screens/onboarding/RegistrationThree.tsx362
-rw-r--r--src/screens/onboarding/RegistrationTwo.tsx260
-rw-r--r--src/screens/onboarding/Verification.tsx47
-rw-r--r--src/screens/onboarding/index.ts1
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';