diff options
author | Justin Shillingford <jgs272@cornell.edu> | 2020-07-15 14:46:58 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-15 14:46:58 -0400 |
commit | d0a72c6e258def8e5f5ac99477114da8edfc2fcf (patch) | |
tree | bc104c687aa1a79a306bb791166328fb4be46741 | |
parent | 3547018e9f803a5ff747126e16b9ef559c3f95cf (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.json | 3 | ||||
-rw-r--r-- | src/components/onboarding/RegistrationWizard.tsx | 6 | ||||
-rw-r--r-- | src/constants/api.ts | 3 | ||||
-rw-r--r-- | src/routes/Routes.tsx | 8 | ||||
-rw-r--r-- | src/screens/onboarding/Login.tsx | 2 | ||||
-rw-r--r-- | src/screens/onboarding/Registration.tsx | 43 | ||||
-rw-r--r-- | src/screens/onboarding/Verification.tsx | 97 | ||||
-rw-r--r-- | yarn.lock | 5 |
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; @@ -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" |