diff options
author | Justin Shillingford <jgs272@cornell.edu> | 2020-07-06 18:48:58 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-06 18:48:58 -0400 |
commit | ff358c8927086a69f6732b6e7e1abb85a9e3cc84 (patch) | |
tree | 818a8190280a7efab9c04195e9d683ecbf00ea53 | |
parent | d2a1005d200abb91f72938d152a1493cb845d970 (diff) |
[TMA63/4] - Presenting alert with result of login attempt (#12)
* 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>
-rw-r--r-- | src/assets/sign_in_logo.png | bin | 28865 -> 0 bytes | |||
-rw-r--r-- | src/constants/index.ts | 5 | ||||
-rw-r--r-- | src/screens/Login.tsx | 77 |
3 files changed, 54 insertions, 28 deletions
diff --git a/src/assets/sign_in_logo.png b/src/assets/sign_in_logo.png Binary files differdeleted file mode 100644 index 27e43268..00000000 --- a/src/assets/sign_in_logo.png +++ /dev/null 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<RootStackParams, 'Login'>; type LoginScreenNavigationProp = StackNavigationProp<RootStackParams, 'Login'>; @@ -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) => { <LoginInput type={data.username} isUsername={true} - onChangeText={(user) => 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.'} /> <LoginInput type={data.password} @@ -144,7 +166,6 @@ const Login = ({navigation}: LoginProps) => { isValid={data.isValidPassword} validationWarning={'Password must be at least 8 characters long.'} input_ref={input_ref} - attempt_submit={data.attemptSubmit} /> <TouchableOpacity accessibilityLabel="Forgot password button" @@ -196,7 +217,7 @@ const styles = StyleSheet.create({ height: 149, }, forgotPassword: { - top: 175, + top: 190, left: -60, }, forgotPasswordText: { @@ -205,7 +226,7 @@ const styles = StyleSheet.create({ textDecorationLine: 'underline', }, start: { - top: 180, + top: 195, width: 144, height: 36, justifyContent: 'center', |