From 5dee1e585353b6d7407f521dfa9186dbf10e8226 Mon Sep 17 00:00:00 2001 From: meganhong <34787696+meganhong@users.noreply.github.com> Date: Mon, 13 Jul 2020 15:08:06 -0700 Subject: TMA123: Add Profile Pictures UI (#17) * rebasing * rebasing * remove debug code * fixed margins and added navigation from login * moved plist file into gitignore * moved index.ts to onboarding directory * install react native image crop picker * added permissions into Info.plist * rebasing * minor changes for Justins PR * change debug code back Co-authored-by: meganhong --- src/components/onboarding/SubmitButton.tsx | 1 + src/routes/Routes.tsx | 9 +- src/screens/Login.tsx | 317 --------------------- src/screens/Registration.tsx | 441 ----------------------------- src/screens/Verification.tsx | 143 ---------- src/screens/index.ts | 3 - src/screens/onboarding/Camera.tsx | 28 ++ src/screens/onboarding/Login.tsx | 317 +++++++++++++++++++++ src/screens/onboarding/Profile.tsx | 121 ++++++++ src/screens/onboarding/Registration.tsx | 441 +++++++++++++++++++++++++++++ src/screens/onboarding/Verification.tsx | 143 ++++++++++ src/screens/onboarding/index.ts | 5 + 12 files changed, 1064 insertions(+), 905 deletions(-) delete mode 100644 src/screens/Login.tsx delete mode 100644 src/screens/Registration.tsx delete mode 100644 src/screens/Verification.tsx delete mode 100644 src/screens/index.ts create mode 100644 src/screens/onboarding/Camera.tsx create mode 100644 src/screens/onboarding/Login.tsx create mode 100644 src/screens/onboarding/Profile.tsx create mode 100644 src/screens/onboarding/Registration.tsx create mode 100644 src/screens/onboarding/Verification.tsx create mode 100644 src/screens/onboarding/index.ts (limited to 'src') diff --git a/src/components/onboarding/SubmitButton.tsx b/src/components/onboarding/SubmitButton.tsx index f946d390..d6a0d8d5 100644 --- a/src/components/onboarding/SubmitButton.tsx +++ b/src/components/onboarding/SubmitButton.tsx @@ -21,6 +21,7 @@ const SubmitButton: React.FC = ( return ( (); @@ -31,6 +33,11 @@ const Routes: React.FC = ({}) => { component={Verification} options={{headerShown: false}} /> + + ); }; diff --git a/src/screens/Login.tsx b/src/screens/Login.tsx deleted file mode 100644 index 1ddf6e0a..00000000 --- a/src/screens/Login.tsx +++ /dev/null @@ -1,317 +0,0 @@ -import React, {useRef} from 'react'; -import {RouteProp} from '@react-navigation/native'; -import {StackNavigationProp} from '@react-navigation/stack'; -import { - View, - Text, - Alert, - StatusBar, - Image, - TouchableOpacity, - StyleSheet, - KeyboardAvoidingView, - Platform, -} from 'react-native'; - -import {RootStackParamList} from '../routes'; -import {Background, TaggInput, SubmitButton} from '../components'; -import {usernameRegex, LOGIN_ENDPOINT} from '../constants'; - -type LoginScreenRouteProp = RouteProp; -type LoginScreenNavigationProp = StackNavigationProp< - RootStackParamList, - 'Login' ->; -interface LoginProps { - route: LoginScreenRouteProp; - navigation: LoginScreenNavigationProp; -} -/** - * Login screen. - * @param navigation react-navigation navigation object. - */ -const Login: React.FC = ({navigation}: LoginProps) => { - // ref for focusing on input fields - const inputRef = useRef(); - // login form state - const [form, setForm] = React.useState({ - username: '', - password: '', - isValidUser: false, - isValidPassword: false, - attemptedSubmit: false, - }); - - /** - * Updates the state of username. Also verifies the input of the username field by ensuring proper length and characters. - */ - const handleUsernameUpdate = (val: string) => { - let validLength: boolean = val.length >= 6; - let validChars: boolean = usernameRegex.test(val); - - if (validLength && validChars) { - setForm({ - ...form, - username: val, - isValidUser: true, - }); - } else { - setForm({ - ...form, - username: val, - isValidUser: false, - }); - } - }; - - /** - * Updates the state of password. Also verifies the input of the password field by ensuring proper length. - */ - const handlePasswordUpdate = (val: string) => { - let validLength: boolean = val.trim().length >= 8; - - if (validLength) { - setForm({ - ...form, - password: val, - isValidPassword: true, - }); - } else { - setForm({ - ...form, - password: val, - isValidPassword: false, - }); - } - }; - - /* - * Handles tap on username keyboard's "Next" button by focusing on password field. - */ - const handleUsernameSubmit = () => { - const passwordField: any = inputRef.current; - if (passwordField) { - passwordField.focus(); - } - }; - - /** - * Handler for the Let's Start button or the Go button on the keyboard. - Makes a POST request to the Django login API and presents Alerts based on the status codes that the backend returns. - */ - const handleLogin = async () => { - if (!form.attemptedSubmit) { - setForm({ - ...form, - attemptedSubmit: true, - }); - } - try { - if (form.isValidUser && form.isValidPassword) { - let response = await fetch(LOGIN_ENDPOINT, { - method: 'POST', - body: JSON.stringify({ - username: form.username, - password: form.password, - }), - }); - - let statusCode = response.status; - if (statusCode === 200) { - Alert.alert('Successfully logged in! πŸ₯³', `Welcome ${form.username}`); - } else if (statusCode === 401) { - Alert.alert( - 'Login failed πŸ˜”', - 'Try re-entering your login information.', - ); - } else { - Alert.alert( - 'Something went wrong! 😭', - "Would you believe me if I told you that I don't know what happened?", - ); - } - } else { - setForm({...form, attemptedSubmit: false}); - setTimeout(() => setForm({...form, attemptedSubmit: true})); - } - } catch (error) { - Alert.alert( - 'Looks like our servers are down. πŸ˜“', - "Try again in a couple minutes. We're sorry for the inconvenience.", - ); - return { - name: 'Login error', - description: error, - }; - } - }; - - /* - * Handles tap on "Get Started" text by resetting fields & navigating to the registration page. - */ - const goToRegistration = () => { - navigation.navigate('Registration'); - setForm({...form, attemptedSubmit: false}); - }; - - /** - * Login screen forgot password button. - */ - const ForgotPassword = () => ( - Alert.alert("tagg! You're it!")}> - Forgot password - - ); - - /** - * Login screen login button. - */ - const LoginButton = () => ( - - ); - - /** - * Login screen registration prompt. - */ - const RegistrationPrompt = () => ( - - - New to tagg?{' '} - - - - Get started! - - - - ); - - return ( - - - - - - - - - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - }, - keyboardAvoidingView: { - alignItems: 'center', - }, - logo: { - width: 215, - height: 149, - marginBottom: '10%', - }, - forgotPassword: { - marginTop: 10, - marginBottom: 15, - }, - forgotPasswordText: { - fontSize: 14, - color: '#fff', - textDecorationLine: 'underline', - }, - start: { - width: 144, - height: 36, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: '#fff', - borderRadius: 18, - marginBottom: '15%', - }, - startDisabled: { - backgroundColor: '#ddd', - }, - startText: { - fontSize: 16, - color: '#78a0ef', - fontWeight: 'bold', - }, - newUserContainer: { - flexDirection: 'row', - color: '#fff', - }, - newUser: { - fontSize: 14, - color: '#f4ddff', - }, - getStarted: { - fontSize: 14, - color: '#fff', - textDecorationLine: 'underline', - }, - button: { - marginVertical: '10%' - } -}); - -export default Login; diff --git a/src/screens/Registration.tsx b/src/screens/Registration.tsx deleted file mode 100644 index 0471e42e..00000000 --- a/src/screens/Registration.tsx +++ /dev/null @@ -1,441 +0,0 @@ -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, -} from 'react-native'; - -import {RootStackParamList} from '../routes'; -import { - ArrowButton, - RegistrationWizard, - TaggInput, - TermsConditions, - Background, -} from '../components'; -import { - emailRegex, - passwordRegex, - usernameRegex, - REGISTER_ENDPOINT, -} from '../constants'; - -type RegistrationScreenRouteProp = RouteProp< - RootStackParamList, - 'Registration' ->; -type RegistrationScreenNavigationProp = StackNavigationProp< - RootStackParamList, - 'Registration' ->; -interface RegistrationProps { - route: RegistrationScreenRouteProp; - navigation: RegistrationScreenNavigationProp; -} -/** - * Registration screen. - * @param navigation react-navigation navigation object - */ -const Registration: React.FC = ({navigation}) => { - // refs for changing focus - const lnameRef = useRef(); - const emailRef = useRef(); - const usernameRef = useRef(); - const passwordRef = useRef(); - const confirmRef = 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; - 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({ - fname: '', - lname: '', - email: '', - username: '', - password: '', - confirm: '', - isValidFname: false, - isValidLname: false, - isValidEmail: false, - isValidUsername: false, - isValidPassword: false, - passwordsMatch: false, - tcAccepted: 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 = fname.length > 0; - 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 = lname.length > 0; - 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) => { - let isValidEmail: boolean = emailRegex.test(email); - setForm({ - ...form, - email, - isValidEmail, - }); - }; - - /* - * 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.isValidFname && - form.isValidLname && - form.isValidUsername && - form.isValidPassword && - form.passwordsMatch - ) { - if (form.tcAccepted) { - let response = await fetch(REGISTER_ENDPOINT, { - method: 'POST', - body: JSON.stringify({ - first_name: form.fname, - last_name: form.lname, - email: form.email, - username: form.username, - password: form.password, - }), - }); - let statusCode = response.status; - let data = await response.json(); - if (statusCode === 201) { - navigation.navigate('Verification'); - Alert.alert( - "You've successfully registered!πŸ₯³", - `Welcome, ${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( - 'Looks like our servers are down. πŸ˜“', - "Try again in a couple minutes. We're sorry for the inconvenience.", - ); - return { - name: 'Registration error', - description: error, - }; - } - }; - - const Footer = () => ( - - navigation.navigate('Login')} - /> - - - - - ); - - return ( - - - - - - Sign up. - - handleFocusChange('lname')} - blurOnSubmit={false} - valid={form.isValidFname} - invalidWarning="First name cannot be empty." - attemptedSubmit={form.attemptedSubmit} - width={280} - /> - handleFocusChange('email')} - blurOnSubmit={false} - ref={lnameRef} - valid={form.isValidLname} - invalidWarning="Last name cannot be empty." - attemptedSubmit={form.attemptedSubmit} - width={280} - /> - handleFocusChange('username')} - blurOnSubmit={false} - ref={emailRef} - valid={form.isValidEmail} - invalidWarning={'Please enter a valid email address.'} - attemptedSubmit={form.attemptedSubmit} - width={280} - /> - 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} - /> - 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} - /> - - - -