From ff358c8927086a69f6732b6e7e1abb85a9e3cc84 Mon Sep 17 00:00:00 2001 From: Justin Shillingford Date: Mon, 6 Jul 2020 18:48:58 -0400 Subject: [TMA63/4] - Presenting alert with result of login attempt (#12) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added 'Packages added' section to PR Template * Added a checklist item about rebasing before PR * Misspelled 'succinct' lol 😅 * Implemented POST request for login Presents alert based on response code * Made the alert messages more robust * Updated terminology and function documentation * Consolidated lines about rebasing Helps to keep the checklist short and concise * A redundant logo image somehow made it through lol * Moved API endpoints to a separate constants file * Refactored login to use async/await * [TMA-62] Basic Login Input Validation (#11) * Updated createRef() to useRef() * Animated invalid input hint Also removed useless focusPasswordInput prop * Users can no longer submit without typing * Added basic input validation for Username * Fixed username input validation 😅 * Removed autocapitalize from keyboard * Trim username input as early as possible Also removed trim from password * Adjusted styling to accomodate longer hint message * Lint cleaning * Updated documentation of update methods * Forgot to include periods in the error message 😅 * Modified styling to accomodate longer hint * Implemented POST request for login Presents alert based on response code * Made the alert messages more robust * Updated terminology and function documentation * A redundant logo image somehow made it through lol * Moved API endpoints to a separate constants file * Refactored login to use async/await * Removed artifact from merge conflict resolution Co-authored-by: Husam Salhab <47015061+hsalhab@users.noreply.github.com> --- src/assets/sign_in_logo.png | Bin 28865 -> 0 bytes src/constants/index.ts | 5 +++ src/screens/Login.tsx | 77 ++++++++++++++++++++++++++++---------------- 3 files changed, 54 insertions(+), 28 deletions(-) delete mode 100644 src/assets/sign_in_logo.png create mode 100644 src/constants/index.ts (limited to 'src') diff --git a/src/assets/sign_in_logo.png b/src/assets/sign_in_logo.png deleted file mode 100644 index 27e43268..00000000 Binary files a/src/assets/sign_in_logo.png and /dev/null differ diff --git a/src/constants/index.ts b/src/constants/index.ts new file mode 100644 index 00000000..0667a187 --- /dev/null +++ b/src/constants/index.ts @@ -0,0 +1,5 @@ +// Backend API constants +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/'; diff --git a/src/screens/Login.tsx b/src/screens/Login.tsx index 2a4ec060..5291b643 100644 --- a/src/screens/Login.tsx +++ b/src/screens/Login.tsx @@ -1,4 +1,4 @@ -import React, {useRef} from 'react'; +import React from 'react'; import {RouteProp} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; import { @@ -18,6 +18,8 @@ import LinearGradient from 'react-native-linear-gradient'; import LoginInput from '../components/common/LoginInput'; +import * as Constants from '../constants'; + type LoginScreenRouteProp = RouteProp; type LoginScreenNavigationProp = StackNavigationProp; @@ -27,23 +29,21 @@ interface LoginProps { } const Login = ({navigation}: LoginProps) => { - const input_ref = useRef(); + const input_ref = React.createRef(); const [data, setData] = React.useState({ username: '', password: '', - isValidUser: false, - isValidPassword: false, - attemptSubmit: false, + isValidUser: true, + isValidPassword: true, }); /* - 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. */ const handleUsernameUpdate = (val: string) => { - let validLength: boolean = val.length >= 6; - let validChars: boolean = !/[^A-Za-z0-9_.]/g.test(val); + let validLength: boolean = val.trim().length >= 6; - if (validLength && validChars) { + if (validLength) { setData({ ...data, username: val, @@ -59,10 +59,10 @@ const Login = ({navigation}: LoginProps) => { }; /* - Updates the state of password. Also verifies the input of the Password field by ensuring proper length. + Updates the state of password. Also verifies the input of the Password field. */ const handlePasswordUpdate = (val: string) => { - let validLength: boolean = val.length >= 8; + let validLength: boolean = val.trim().length >= 8; if (validLength) { setData({ @@ -81,18 +81,43 @@ const Login = ({navigation}: LoginProps) => { /* 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 = () => { - if (!data.attemptSubmit) { - setData({ - ...data, - attemptSubmit: true, - }); - } - if (data.isValidUser && data.isValidPassword) { + const handleLogin = async () => { + try { + if (data.isValidUser && data.isValidPassword) { + let response = await fetch(Constants.LOGIN_ENDPOINT, { + method: 'POST', + body: JSON.stringify({ + username: data.username, + password: data.password, + }), + }); + + let statusCode = response.status; + if (statusCode === 200) { + Alert.alert('Successfully logged in! 🥳', `Welcome ${data.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?", + ); + } + } + } catch (error) { Alert.alert( - `My favorite Girl Scout Cookies are taggalongs! What are yours ${data.username}?`, + 'Looks like our servers are down. 😓', + "Try again in a couple minutes. We're sorry for the inconvenience.", ); + return { + name: 'Login error', + description: error, + }; } }; @@ -128,13 +153,10 @@ const Login = ({navigation}: LoginProps) => { handleUsernameUpdate(user.trim())} + onChangeText={(user) => handleUsernameUpdate(user)} onSubmitEditing={() => handleUsernameSubmit()} isValid={data.isValidUser} - validationWarning={ - 'Username must be at least 6 characters and can only contain letters, numbers, periods, and underscores.' - } - attempt_submit={data.attemptSubmit} + validationWarning={'Username must be at least 6 characters long.'} /> { isValid={data.isValidPassword} validationWarning={'Password must be at least 8 characters long.'} input_ref={input_ref} - attempt_submit={data.attemptSubmit} />