Application Android et IOS pour l'amicale des élèves https://play.google.com/store/apps/details?id=fr.amicaleinsat.application
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

LoginForm.tsx 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import React, { useRef, useState } from 'react';
  2. import {
  3. Image,
  4. StyleSheet,
  5. View,
  6. TextInput as RNTextInput,
  7. } from 'react-native';
  8. import {
  9. Button,
  10. Card,
  11. HelperText,
  12. TextInput,
  13. useTheme,
  14. } from 'react-native-paper';
  15. import i18n from 'i18n-js';
  16. import GENERAL_STYLES from '../../../constants/Styles';
  17. type Props = {
  18. loading: boolean;
  19. onSubmit: (email: string, password: string) => void;
  20. onHelpPress: () => void;
  21. onResetPasswordPress: () => void;
  22. };
  23. const ICON_AMICALE = require('../../../assets/amicale.png');
  24. const styles = StyleSheet.create({
  25. card: {
  26. marginTop: 'auto',
  27. marginBottom: 'auto',
  28. },
  29. header: {
  30. fontSize: 36,
  31. marginBottom: 48,
  32. },
  33. text: {
  34. color: '#ffffff',
  35. },
  36. buttonContainer: {
  37. flexWrap: 'wrap',
  38. },
  39. lockButton: {
  40. marginRight: 'auto',
  41. marginBottom: 20,
  42. },
  43. sendButton: {
  44. marginLeft: 'auto',
  45. },
  46. });
  47. const emailRegex = /^.+@.+\..+$/;
  48. /**
  49. * Checks if the entered email is valid (matches the regex)
  50. *
  51. * @returns {boolean}
  52. */
  53. function isEmailValid(email: string): boolean {
  54. return emailRegex.test(email);
  55. }
  56. /**
  57. * Checks if the user has entered a password
  58. *
  59. * @returns {boolean}
  60. */
  61. function isPasswordValid(password: string): boolean {
  62. return password !== '';
  63. }
  64. export default function LoginForm(props: Props) {
  65. const theme = useTheme();
  66. const [email, setEmail] = useState('');
  67. const [password, setPassword] = useState('');
  68. const [isEmailValidated, setIsEmailValidated] = useState(false);
  69. const [isPasswordValidated, setIsPasswordValidated] = useState(false);
  70. const passwordRef = useRef<RNTextInput>(null);
  71. /**
  72. * Checks if we should tell the user his email is invalid.
  73. * We should only show this if his email is invalid and has been checked when un-focusing the input
  74. *
  75. * @returns {boolean|boolean}
  76. */
  77. const shouldShowEmailError = () => {
  78. return isEmailValidated && !isEmailValid(email);
  79. };
  80. /**
  81. * Checks if we should tell the user his password is invalid.
  82. * We should only show this if his password is invalid and has been checked when un-focusing the input
  83. *
  84. * @returns {boolean|boolean}
  85. */
  86. const shouldShowPasswordError = () => {
  87. return isPasswordValidated && !isPasswordValid(password);
  88. };
  89. const onEmailSubmit = () => {
  90. if (passwordRef.current) {
  91. passwordRef.current.focus();
  92. }
  93. };
  94. /**
  95. * The user has unfocused the input, his email is ready to be validated
  96. */
  97. const validateEmail = () => setIsEmailValidated(true);
  98. /**
  99. * The user has unfocused the input, his password is ready to be validated
  100. */
  101. const validatePassword = () => setIsPasswordValidated(true);
  102. const onEmailChange = (value: string) => {
  103. if (isEmailValidated) {
  104. setIsEmailValidated(false);
  105. }
  106. setEmail(value);
  107. };
  108. const onPasswordChange = (value: string) => {
  109. if (isPasswordValidated) {
  110. setIsPasswordValidated(false);
  111. }
  112. setPassword(value);
  113. };
  114. const shouldEnableLogin = () => {
  115. return isEmailValid(email) && isPasswordValid(password) && !props.loading;
  116. };
  117. const onSubmit = () => {
  118. if (shouldEnableLogin()) {
  119. props.onSubmit(email, password);
  120. }
  121. };
  122. return (
  123. <View style={styles.card}>
  124. <Card.Title
  125. title={i18n.t('screens.login.title')}
  126. titleStyle={styles.text}
  127. subtitle={i18n.t('screens.login.subtitle')}
  128. subtitleStyle={styles.text}
  129. left={({ size }) => (
  130. <Image
  131. source={ICON_AMICALE}
  132. style={{
  133. width: size,
  134. height: size,
  135. }}
  136. />
  137. )}
  138. />
  139. <Card.Content>
  140. <View>
  141. <TextInput
  142. label={i18n.t('screens.login.email')}
  143. mode={'outlined'}
  144. value={email}
  145. onChangeText={onEmailChange}
  146. onBlur={validateEmail}
  147. onSubmitEditing={onEmailSubmit}
  148. error={shouldShowEmailError()}
  149. textContentType={'emailAddress'}
  150. autoCapitalize={'none'}
  151. autoCompleteType={'email'}
  152. autoCorrect={false}
  153. keyboardType={'email-address'}
  154. returnKeyType={'next'}
  155. secureTextEntry={false}
  156. />
  157. <HelperText type={'error'} visible={shouldShowEmailError()}>
  158. {i18n.t('screens.login.emailError')}
  159. </HelperText>
  160. <TextInput
  161. ref={passwordRef}
  162. label={i18n.t('screens.login.password')}
  163. mode={'outlined'}
  164. value={password}
  165. onChangeText={onPasswordChange}
  166. onBlur={validatePassword}
  167. onSubmitEditing={onSubmit}
  168. error={shouldShowPasswordError()}
  169. textContentType={'password'}
  170. autoCapitalize={'none'}
  171. autoCompleteType={'password'}
  172. autoCorrect={false}
  173. keyboardType={'default'}
  174. returnKeyType={'done'}
  175. secureTextEntry={true}
  176. />
  177. <HelperText type={'error'} visible={shouldShowPasswordError()}>
  178. {i18n.t('screens.login.passwordError')}
  179. </HelperText>
  180. </View>
  181. <Card.Actions style={styles.buttonContainer}>
  182. <Button
  183. icon="lock-question"
  184. mode="contained"
  185. onPress={props.onResetPasswordPress}
  186. color={theme.colors.warning}
  187. style={styles.lockButton}
  188. >
  189. {i18n.t('screens.login.resetPassword')}
  190. </Button>
  191. <Button
  192. icon="send"
  193. mode="contained"
  194. disabled={!shouldEnableLogin()}
  195. loading={props.loading}
  196. onPress={onSubmit}
  197. style={styles.sendButton}
  198. >
  199. {i18n.t('screens.login.title')}
  200. </Button>
  201. </Card.Actions>
  202. <Card.Actions>
  203. <Button
  204. icon="help-circle"
  205. mode="contained"
  206. onPress={props.onHelpPress}
  207. style={GENERAL_STYLES.centerHorizontal}
  208. >
  209. {i18n.t('screens.login.mascotDialog.title')}
  210. </Button>
  211. </Card.Actions>
  212. </Card.Content>
  213. </View>
  214. );
  215. }