aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Shillingford <jgs272@cornell.edu>2020-07-15 14:46:58 -0400
committerGitHub <noreply@github.com>2020-07-15 14:46:58 -0400
commitd0a72c6e258def8e5f5ac99477114da8edfc2fcf (patch)
treebc104c687aa1a79a306bb791166328fb4be46741
parent3547018e9f803a5ff747126e16b9ef559c3f95cf (diff)
[TMA-96] Verification Page Logic (#19)
* Removed header from view * Setup basic layout of Verification page Also created new SubmitButton component * Some light code cleanup * Implemented SubmitButton component on Login * Added basic verification field * Styled Verification CodeField * Quick typo fix * Some lint cleaning * Removed header from view * Setup basic layout of Verification page Also created new SubmitButton component * Some light code cleanup * Implemented SubmitButton component on Login * Added basic verification field * Styled Verification CodeField * Quick typo fix * Some lint cleaning * Verification isn't that exciting lol * Removed header from view * Setup basic layout of Verification page Also created new SubmitButton component * Some light code cleanup * Implemented SubmitButton component on Login * Added basic verification field * Styled Verification CodeField * Quick typo fix * Some lint cleaning * Light lint cleaning * Still not that exciting lol * Removed misplaced accessibility labels * Added documentation to SubmitButton component * Implemented KeyboardAvoidingView * Fixed wizard position consistency * Updated Verification CodeField to take 6 digits * Removed marginVertical prop from SubmitButton * Added basic implementation of send-otp request * Added indicator to indicate progress during fetch * Handled verification logic * Fixed Verification Screen Routing naming * Passed username and email to verification * Some lint cleaning * Resend Code button is now fully functional * Some lint cleaning * Handling TypeScript type checking errors * Removed header from view * Setup basic layout of Verification page Also created new SubmitButton component * Some light code cleanup * Implemented SubmitButton component on Login * Added basic verification field * Styled Verification CodeField * Some lint cleaning * Setup basic layout of Verification page Also created new SubmitButton component * Some light code cleanup * Implemented SubmitButton component on Login * Added basic verification field * Styled Verification CodeField * Removed header from view * Setup basic layout of Verification page Also created new SubmitButton component * Some light code cleanup * Implemented SubmitButton component on Login * Added basic verification field * Styled Verification CodeField * Some lint cleaning * Removed misplaced accessibility labels * Added documentation to SubmitButton component * Implemented KeyboardAvoidingView * Fixed wizard position consistency * Updated Verification CodeField to take 6 digits * Removed marginVertical prop from SubmitButton * Added basic implementation of send-otp request * Added indicator to indicate progress during fetch * Handled verification logic * Fixed Verification Screen Routing naming * Passed username and email to verification * Some lint cleaning * Resend Code button is now fully functional * Some lint cleaning * Handling TypeScript type checking errors * Lint cleaning * Fixed a merge conflict resolution stowaway * Final lint cleaning before PR * Clear CodeField upon code resend * Baby lint * Navigate to Profile page upon verification * Improved invalid code message * Added documentation for new functions * Baby baby lint * Updated Wizard to have 4 steps * Statuses aren't verifications lmao
-rw-r--r--package.json3
-rw-r--r--src/components/onboarding/RegistrationWizard.tsx6
-rw-r--r--src/constants/api.ts3
-rw-r--r--src/routes/Routes.tsx8
-rw-r--r--src/screens/onboarding/Login.tsx2
-rw-r--r--src/screens/onboarding/Registration.tsx43
-rw-r--r--src/screens/onboarding/Verification.tsx97
-rw-r--r--yarn.lock5
8 files changed, 147 insertions, 20 deletions
diff --git a/package.json b/package.json
index baea7d2f..142bde68 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,8 @@
"react-native-linear-gradient": "^2.5.6",
"react-native-reanimated": "^1.9.0",
"react-native-safe-area-context": "^3.0.6",
- "react-native-screens": "^2.9.0"
+ "react-native-screens": "^2.9.0",
+ "react-promise-tracker": "^2.1.0"
},
"devDependencies": {
"@babel/core": "^7.6.2",
diff --git a/src/components/onboarding/RegistrationWizard.tsx b/src/components/onboarding/RegistrationWizard.tsx
index 5d7e6ee2..1157455f 100644
--- a/src/components/onboarding/RegistrationWizard.tsx
+++ b/src/components/onboarding/RegistrationWizard.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import {View, StyleSheet, ViewProps} from 'react-native';
interface RegistrationWizardProps extends ViewProps {
- step: 'one' | 'two' | 'three';
+ step: 'one' | 'two' | 'three' | 'four' | 'five';
}
const RegistrationWizard = (props: RegistrationWizardProps) => {
@@ -16,6 +16,8 @@ const RegistrationWizard = (props: RegistrationWizardProps) => {
<View style={props.step === 'two' ? stepActiveStyle : stepStyle} />
<View style={styles.progress} />
<View style={props.step === 'three' ? stepActiveStyle : stepStyle} />
+ <View style={styles.progress} />
+ <View style={props.step === 'four' ? stepActiveStyle : stepStyle} />
</View>
</View>
);
@@ -38,7 +40,7 @@ const styles = StyleSheet.create({
backgroundColor: '#e1f0ff',
},
progress: {
- width: '30%',
+ width: '20%',
height: 2,
backgroundColor: '#e1f0ff',
},
diff --git a/src/constants/api.ts b/src/constants/api.ts
index 0944eb16..657adf03 100644
--- a/src/constants/api.ts
+++ b/src/constants/api.ts
@@ -2,3 +2,6 @@ export const API_ENDPOINT: string = 'http://127.0.0.1:8000/api/';
export const LOGIN_ENDPOINT: string = 'http://127.0.0.1:8000/api/login/';
export const LOGOUT_ENDPOINT: string = 'http://127.0.0.1:8000/api/logout/';
export const REGISTER_ENDPOINT: string = 'http://127.0.0.1:8000/api/register/';
+export const SEND_OTP_ENDPOINT: string = 'http://127.0.0.1:8000/api/send-otp/';
+export const VERIFY_OTP_ENDPOINT: string =
+ 'http://127.0.0.1:8000/api/verify-otp/';
diff --git a/src/routes/Routes.tsx b/src/routes/Routes.tsx
index 98aeff8d..c6365613 100644
--- a/src/routes/Routes.tsx
+++ b/src/routes/Routes.tsx
@@ -12,7 +12,7 @@ import {
export type RootStackParamList = {
Login: undefined;
Registration: undefined;
- Verification: undefined;
+ Verification: {username: string; email: string} | undefined;
Profile: undefined;
Camera: undefined;
};
@@ -44,7 +44,11 @@ const Routes: React.FC<RoutesProps> = ({}) => {
component={Profile}
options={{headerShown: false}}
/>
- <RootStack.Screen name="Camera" component={Camera} />
+ <RootStack.Screen
+ name="Camera"
+ component={Camera}
+ options={{headerShown: false}}
+ />
</RootStack.Navigator>
);
};
diff --git a/src/screens/onboarding/Login.tsx b/src/screens/onboarding/Login.tsx
index e8500eec..8e406522 100644
--- a/src/screens/onboarding/Login.tsx
+++ b/src/screens/onboarding/Login.tsx
@@ -43,7 +43,7 @@ const Login: React.FC<LoginProps> = ({navigation}: LoginProps) => {
});
/**
- * Updates the state of username. Also verifies the input of the username field by ensuring proper length and characters.
+ * Updates the state of username. Also verifies the input of the username field by ensuring proper length and appropriate characters.
*/
const handleUsernameUpdate = (val: string) => {
let validLength: boolean = val.length >= 6;
diff --git a/src/screens/onboarding/Registration.tsx b/src/screens/onboarding/Registration.tsx
index 29a2b3f3..8d564cd0 100644
--- a/src/screens/onboarding/Registration.tsx
+++ b/src/screens/onboarding/Registration.tsx
@@ -10,7 +10,9 @@ import {
Platform,
TouchableOpacity,
KeyboardAvoidingView,
+ ActivityIndicator,
} from 'react-native';
+import {usePromiseTracker, trackPromise} from 'react-promise-tracker';
import {RootStackParamList} from '../../routes';
import {
@@ -25,6 +27,7 @@ import {
passwordRegex,
usernameRegex,
REGISTER_ENDPOINT,
+ SEND_OTP_ENDPOINT,
} from '../../constants';
type RegistrationScreenRouteProp = RouteProp<
@@ -200,7 +203,7 @@ const Registration: React.FC<RegistrationProps> = ({navigation}) => {
form.passwordsMatch
) {
if (form.tcAccepted) {
- let response = await fetch(REGISTER_ENDPOINT, {
+ let registerResponse = await fetch(REGISTER_ENDPOINT, {
method: 'POST',
body: JSON.stringify({
first_name: form.fname,
@@ -210,14 +213,25 @@ const Registration: React.FC<RegistrationProps> = ({navigation}) => {
password: form.password,
}),
});
- let statusCode = response.status;
- let data = await response.json();
+ let statusCode = registerResponse.status;
+ let data = await registerResponse.json();
if (statusCode === 201) {
- navigation.navigate('Verification');
- Alert.alert(
- "You've successfully registered!🥳",
- `Welcome, ${form.username}`,
+ let sendOtpResponse = await trackPromise(
+ fetch(SEND_OTP_ENDPOINT, {
+ method: 'POST',
+ body: JSON.stringify({
+ username: form.username,
+ email: form.email,
+ }),
+ }),
);
+ let otpStatusCode = sendOtpResponse.status;
+ if (otpStatusCode === 200) {
+ navigation.navigate('Verification', {
+ username: form.username,
+ email: form.email,
+ });
+ }
} else if (statusCode === 409) {
Alert.alert('Registration failed 😔', `${data}`);
} else {
@@ -274,6 +288,18 @@ const Registration: React.FC<RegistrationProps> = ({navigation}) => {
</View>
);
+ /**
+ * An activity indicator to indicate that the app is working during the send_otp request.
+ */
+ const LoadingIndicator = () => {
+ const {promiseInProgress} = usePromiseTracker();
+ return promiseInProgress ? (
+ <ActivityIndicator size="large" color="#fff" />
+ ) : (
+ <></>
+ );
+ };
+
return (
<Background style={styles.container}>
<StatusBar barStyle="light-content" />
@@ -392,6 +418,7 @@ const Registration: React.FC<RegistrationProps> = ({navigation}) => {
accepted={form.tcAccepted}
onChange={handleTcUpdate}
/>
+ <LoadingIndicator />
</KeyboardAvoidingView>
<Footer />
</Background>
@@ -421,7 +448,7 @@ const styles = StyleSheet.create({
marginBottom: '5%',
},
tc: {
- marginTop: '5%',
+ marginVertical: '5%',
},
footer: {
width: '100%',
diff --git a/src/screens/onboarding/Verification.tsx b/src/screens/onboarding/Verification.tsx
index 827f65e1..1c456e01 100644
--- a/src/screens/onboarding/Verification.tsx
+++ b/src/screens/onboarding/Verification.tsx
@@ -4,6 +4,7 @@ import {RootStackParamList} from '../../routes';
import {RouteProp} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
import {Background, RegistrationWizard, SubmitButton} from '../../components';
+import {VERIFY_OTP_ENDPOINT, SEND_OTP_ENDPOINT} from '../../constants';
import {Text} from 'react-native-animatable';
import {
CodeField,
@@ -16,28 +17,107 @@ import {
View,
TouchableOpacity,
KeyboardAvoidingView,
+ Alert,
+ ActivityIndicator,
} from 'react-native';
-type LoginScreenRouteProp = RouteProp<RootStackParamList, 'Login'>;
-type LoginScreenNavigationProp = StackNavigationProp<
+import {usePromiseTracker, trackPromise} from 'react-promise-tracker';
+
+type VerificationScreenRouteProp = RouteProp<
+ RootStackParamList,
+ 'Verification'
+>;
+type VerificationScreenNavigationProp = StackNavigationProp<
RootStackParamList,
- 'Login'
+ 'Verification'
>;
interface VerificationProps {
route: VerificationScreenRouteProp;
navigation: VerificationScreenNavigationProp;
}
-const Verification: React.FC<VerificationProps> = ({}) => {
+const Verification: React.FC<VerificationProps> = ({route, navigation}) => {
const [value, setValue] = React.useState('');
const ref = useBlurOnFulfill({value, cellCount: 6});
const [valueProps, getCellOnLayoutHandler] = useClearByFocusCell({
value,
setValue,
});
+ const registrationVals = route.params;
+ const username: string = registrationVals!.username;
+ const email: string = registrationVals!.email;
+
+ /**
+ * Sends the verify_otp request upon tapping the Verify button.
+ * If successful, it navigates to the Profile page.
+ */
+ const handleVerification = async () => {
+ try {
+ let verifyOtpResponse = await fetch(VERIFY_OTP_ENDPOINT, {
+ method: 'POST',
+ body: JSON.stringify({
+ username: username,
+ otp: value,
+ }),
+ });
+ let statusCode = verifyOtpResponse.status;
+ if (statusCode === 200) {
+ navigation.navigate('Profile');
+ } else {
+ Alert.alert(
+ 'Invalid verification code 🤔',
+ 'Try again. Tap the resend code button if you need a new code.',
+ );
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ };
+
+ /**
+ * Sends the send_otp request so to provide a new verification code upon tapping the Resend Code button.
+ */
+ const handleResend = async () => {
+ try {
+ let sendOtpResponse = await trackPromise(
+ fetch(SEND_OTP_ENDPOINT, {
+ method: 'POST',
+ body: JSON.stringify({
+ username: username,
+ email: email,
+ }),
+ }),
+ );
+ let sendOtpStatusCode = sendOtpResponse.status;
+ if (sendOtpStatusCode === 200) {
+ setValue('');
+ Alert.alert('New verification code sent!', 'Check your email for your code.');
+ } else {
+ Alert.alert('Something went wrong!');
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ };
+
+ /**
+ * 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.loadingIndicator}
+ size="large"
+ color="#fff"
+ />
+ ) : (
+ <></>
+ );
+ };
return (
<Background centered style={styles.container}>
- <RegistrationWizard style={styles.wizard} step="one" />
+ <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}>
@@ -69,10 +149,12 @@ const Verification: React.FC<VerificationProps> = ({}) => {
style={styles.button}
accessibilityLabel="Verify"
accessibilityHint="Select this after entering your email verification code"
+ onPress={handleVerification}
/>
- <TouchableOpacity>
+ <TouchableOpacity onPress={handleResend}>
<Text style={styles.resend}>Resend Code</Text>
</TouchableOpacity>
+ <LoadingIndicator />
</KeyboardAvoidingView>
</Background>
);
@@ -139,5 +221,8 @@ const styles = StyleSheet.create({
button: {
marginVertical: '5%',
},
+ loadingIndicator: {
+ marginVertical: '5%',
+ },
});
export default Verification;
diff --git a/yarn.lock b/yarn.lock
index d5d8a6e0..a0e56cf7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5178,6 +5178,11 @@ react-native@0.62.2:
use-subscription "^1.0.0"
whatwg-fetch "^3.0.0"
+react-promise-tracker@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/react-promise-tracker/-/react-promise-tracker-2.1.0.tgz#a8ac2ca5322ca60f83fa7a00ad9d99bfbbdb8434"
+ integrity sha512-1LQSGAnFF4o++iPFgeBrMpS+ZHQg8ZGl9Zuf70v+OoLxuOnUeLCP7ugma+InBCUZzPnSLyWTLt/jyW+FAy5ELQ==
+
react-refresh@^0.4.0:
version "0.4.3"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53"