From f558d783e9f087a00e55feb04d853ccca4478993 Mon Sep 17 00:00:00 2001 From: Nikolai R Kristiansen Date: Tue, 12 Oct 2021 19:51:38 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Improve=20Login?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ♻ Extract FormErrors and FormInput into own components --- src/components/FormErrors.js | 26 ++++++ src/components/FormInput.js | 44 ++++++++++ src/modules/login/Login.js | 126 ++++++++++----------------- src/modules/login/useLogin.js | 22 +++-- src/modules/register/UserRegister.js | 79 ++--------------- src/reduxStore.js | 2 +- 6 files changed, 141 insertions(+), 158 deletions(-) create mode 100644 src/components/FormErrors.js create mode 100644 src/components/FormInput.js diff --git a/src/components/FormErrors.js b/src/components/FormErrors.js new file mode 100644 index 0000000..b10666d --- /dev/null +++ b/src/components/FormErrors.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { Alert, HStack, Text } from 'native-base'; + +const FormErrors = ({ errors }) => { + if (!Object.keys(errors).length || !['non_field_errors', 'detail'].some((errorField) => errorField in errors)) { + return null; + } + + let errorFormatted; + if ('non_field_errors' in errors) { + errorFormatted = errors.non_field_errors?.[0]; + } else if ('detail' in errors) { + errorFormatted = errors.detail; + } + if (!errorFormatted) errorFormatted = 'Kunne ikke sende inn skjemaet, prøv igjen...'; + + return ( + + + + {errorFormatted} + + + ); +}; +export default FormErrors; diff --git a/src/components/FormInput.js b/src/components/FormInput.js new file mode 100644 index 0000000..a3cc3d5 --- /dev/null +++ b/src/components/FormInput.js @@ -0,0 +1,44 @@ +import React from 'react'; +import { Controller } from 'react-hook-form'; + +import { Input, Text, WarningOutlineIcon, FormControl } from 'native-base'; + +const FormInput = ({ + control, + errors, + name, + label, + rules = { + required: 'kan ikke være tomt.', + }, + returnKeyType = 'next', + ...rest +}) => { + return ( + ( + + {label} + + {errors[name] && ( + }> + {errors[name].message} + + )} + + )} + name={name} + defaultValue="" + /> + ); +}; +export default FormInput; diff --git a/src/modules/login/Login.js b/src/modules/login/Login.js index 22bbf1b..ebd5e12 100644 --- a/src/modules/login/Login.js +++ b/src/modules/login/Login.js @@ -1,102 +1,70 @@ -import React, { useRef } from 'react'; +import React from 'react'; import { Linking, StyleSheet, View } from 'react-native'; -import { Container, Input, Spinner, Button, Text } from 'native-base'; +import { ScrollView, Spinner, Button, Text } from 'native-base'; import Config from 'react-native-config'; import theme from '../../theme'; import useLogin from './useLogin'; - -const FormErrors = ({ loginError }) => { - if (!loginError) { - return ; - } - const err = loginError.non_field_errors; - const errorFormatted = err ? err[0] : 'Feil brukernavn eller passord, prøv igjen...'; - // TODO: Format these and highlight error fields - return ( - - {errorFormatted} - - ); -}; +import FormInput from '../../components/FormInput'; +import FormErrors from '../../components/FormErrors'; const Login = ({ onLogin }) => { - const passwordInputRef = useRef(); - const { password, setPassword, email, setEmail, loginError, onLoginPress, isLoggingIn } = useLogin(onLogin); - - // FIXME: highlight fields with errors - // const usernameHasError = Boolean(loginError?.username); - // const passwordHasError = Boolean(loginError?.password); + const { isLoggingIn, errors, control, setFocus, onSubmit } = useLogin(onLogin); return ( - - - E-post / Brukernavn - { - passwordInputRef.current.focus(); - }} - /> - Passord - - - - {isLoggingIn && } - + + + + setFocus('password')} + /> + + + + {isLoggingIn && } + + ); }; export default Login; const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - }, - input: { - width: 320, - fontSize: 20, - height: 52, - }, loginButton: { - marginBottom: 16, + marginTop: 16, }, forgotPasswordButton: { alignSelf: 'center', marginTop: 32, marginBottom: 16, }, - errorBox: { - marginVertical: 10, - }, - errorMessage: { - textAlign: 'center', - color: 'red', - }, card: theme.card, }); diff --git a/src/modules/login/useLogin.js b/src/modules/login/useLogin.js index 6487d4a..573d51a 100644 --- a/src/modules/login/useLogin.js +++ b/src/modules/login/useLogin.js @@ -1,25 +1,31 @@ import { useNavigation } from '@react-navigation/native'; -import { useState, useEffect } from 'react'; +import { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import { useForm } from 'react-hook-form'; import { requestLogin } from '../../api'; const useLogin = () => { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); const navigation = useNavigation(); const dispatch = useDispatch(); - const onLoginPress = () => { - dispatch(requestLogin(email, password)); - }; - const { isAuthenticated, isLoggingIn, loginError } = useSelector((store) => ({ isAuthenticated: store.isAuthenticated, isLoggingIn: store.isLoggingIn, loginError: store.loginError, })); + const { + control, + handleSubmit, + formState: { errors }, + setFocus, + } = useForm(); + + const onSubmit = handleSubmit(({ email, password }) => { + dispatch(requestLogin(email, password)); + }); + useEffect(() => { if (isAuthenticated) { // navigation.goBack onLogin works since LoginScreen is allways navigated to from MembershipScreen @@ -27,7 +33,7 @@ const useLogin = () => { } }, [isAuthenticated]); - return { email, setEmail, password, setPassword, onLoginPress, isLoggingIn, loginError }; + return { onSubmit, isLoggingIn, control, setFocus, errors: { ...errors, ...loginError } }; }; export default useLogin; diff --git a/src/modules/register/UserRegister.js b/src/modules/register/UserRegister.js index a89c62a..329bd9e 100644 --- a/src/modules/register/UserRegister.js +++ b/src/modules/register/UserRegister.js @@ -1,77 +1,12 @@ import React from 'react'; -import { Input, Spinner, Button, Text, WarningOutlineIcon, FormControl, ScrollView } from 'native-base'; +import { Spinner, Button, Text, ScrollView } from 'native-base'; import { StyleSheet, View } from 'react-native'; import * as EmailValidator from 'email-validator'; -import { Controller } from 'react-hook-form'; import theme from '../../theme'; import useUserRegister from './useUserRegister'; - -const FormInput = ({ - control, - errors, - name, - label, - rules = { - required: 'kan ikke være tomt.', - }, - returnKeyType = 'next', - ...rest -}) => { - return ( - ( - - {label} - - {errors[name] && ( - }> - {errors[name].message} - - )} - - )} - name={name} - defaultValue="" - /> - ); -}; - -const Registerbutton = ({ isDisabled, onPress }) => ( - -); - -const NonFieldErrors = ({ errors }) => { - if (!Object.keys(errors).length || !['non_field_errors', 'detail'].some((errorField) => errorField in errors)) { - return null; - } - let errorFormatted; - if ('non_field_errors' in errors) { - errorFormatted = errors.non_field_errors?.[0]; - } else if ('detail' in errors) { - errorFormatted = errors.detail; - } - if (!errorFormatted) errorFormatted = 'Kunne ikke registrere bruker, prøv igjen...'; - - return ( - - }> - {errorFormatted} - - - ); -}; +import FormInput from '../../components/FormInput'; +import FormErrors from '../../components/FormErrors'; const UserRegister = () => { const { isRegisteringUser, onSubmit, control, isValid, errors, setFocus } = useUserRegister(); @@ -130,8 +65,12 @@ const UserRegister = () => { returnKeyType="send" onSubmitEditing={onSubmit} /> - - + + + + {isRegisteringUser && } diff --git a/src/reduxStore.js b/src/reduxStore.js index 4513771..d6e5682 100644 --- a/src/reduxStore.js +++ b/src/reduxStore.js @@ -8,7 +8,7 @@ import rootReducer from './reducers'; const persistConfig = { key: 'root', storage: AsyncStorage, - blacklist: ['isLoggingIn', 'isFetchingUserData', 'isRegisteringUser', 'isChargingMembership'], + blacklist: ['isLoggingIn', 'isFetchingUserData', 'isRegisteringUser', 'isChargingMembership', 'loginError'], }; const persistedReducer = persistReducer(persistConfig, rootReducer);