import React, {useRef, useState} 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 {OnboardingStackParams} from '../../routes/onboarding'; import {AuthContext} from '../../routes/authentication'; import {Background, TaggInput, SubmitButton} from '../../components'; import {usernameRegex, LOGIN_ENDPOINT} from '../../constants'; import AsyncStorage from '@react-native-community/async-storage'; import {UserType} from '../../types'; type VerificationScreenRouteProp = RouteProp; type VerificationScreenNavigationProp = StackNavigationProp< OnboardingStackParams, 'Login' >; interface LoginProps { route: VerificationScreenRouteProp; navigation: VerificationScreenNavigationProp; } /** * Login screen. * @param navigation react-navigation navigation object. */ const Login: React.FC = ({navigation}: LoginProps) => { // ref for focusing on input fields const inputRef = useRef(); const NO_USER: UserType = { userId: '', username: '', }; // login form state const [form, setForm] = React.useState({ username: '', password: '', isValidUser: false, isValidPassword: false, attemptedSubmit: false, token: '', }); // determines if user is logged in const {login} = React.useContext(AuthContext); const [user, setUser] = useState(NO_USER); /** * Updates the state of username. Also verifies the input of the username field by ensuring proper length and appropriate characters. */ const handleUsernameUpdate = (val: string) => { val = val.trim(); let validLength: boolean = val.length >= 3; 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. * Stores token received in the response, into client's AsynStorage */ const handleLogin = async () => { if (!form.attemptedSubmit) { setForm({ ...form, attemptedSubmit: true, }); } try { if (form.isValidUser && form.isValidPassword) { const {username, password} = form; let response = await fetch(LOGIN_ENDPOINT, { method: 'POST', body: JSON.stringify({ username, password, }), }); let statusCode = response.status; let data = await response.json(); if (statusCode === 200) { //Stores token received in the response into client's AsynStorage try { await AsyncStorage.setItem('token', data.token); await AsyncStorage.setItem('userId', data.UserID); await AsyncStorage.setItem('username', username); login(data.UserID, username); } catch (err) { setUser(NO_USER); console.log(data); Alert.alert('Auth token storage failed', 'Please login again!'); } } 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( 'Login failed 😓', 'Please double-check your network connection and retry.', ); return { name: 'Login error', description: error, }; } }; /* * Handles tap on "Get Started" text by resetting fields & navigating to the registration page. */ const goToRegistration = () => { navigation.navigate('InvitationCodeVerification'); 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 ( {/* Commenting this line because the Forgot Password has not been implemented for Alpha */} {/* */} ); }; 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;