fix(authentication): allow email/password validators customization (closes #57) #60
| @ -0,0 +1,30 @@ | |||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | //  | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | //  | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | //  | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | ||||||
|  | 
 | ||||||
|  | class CustomPassword extends RegexValidator<ValidationStandardError> { | ||||||
|  |   const CustomPassword.pure([super.value]) : super.pure(); | ||||||
|  |   const CustomPassword.dirty([super.value]) : super.dirty(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   ValidationStandardError get onEmpty => ValidationStandardError.empty; | ||||||
|  |   @override | ||||||
|  |   ValidationStandardError get onError => ValidationStandardError.invalid; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   RegExp get regex => RegExp(r'^(?=.*[A-Za-z\w\s])(?=.*\d).{6,}$'); | ||||||
|  | } | ||||||
| @ -3,7 +3,7 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: app.dart | // File: app.dart | ||||||
| // Created Date: 19/08/2022 12:05:38 | // Created Date: 19/08/2022 12:05:38 | ||||||
| // Last Modified: Wed Nov 23 2022 | // Last Modified: Sat Dec 03 2022 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
| @ -13,6 +13,7 @@ import 'dart:math'; | |||||||
| import 'package:example_router/core/constants/form_field.dart'; | import 'package:example_router/core/constants/form_field.dart'; | ||||||
| import 'package:example_router/core/dependency_injection/get_it.dart'; | import 'package:example_router/core/dependency_injection/get_it.dart'; | ||||||
| import 'package:example_router/core/routes/router.dart'; | import 'package:example_router/core/routes/router.dart'; | ||||||
|  | import 'package:example_router/core/utils/custom_password.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | import 'package:flutter_bloc/flutter_bloc.dart'; | ||||||
| import 'package:go_router/go_router.dart'; | import 'package:go_router/go_router.dart'; | ||||||
| @ -47,6 +48,7 @@ class App extends StatelessWidget { | |||||||
|     authenticationRemoteDataSource: getIt<AuthenticationRemoteDataSource>(), |     authenticationRemoteDataSource: getIt<AuthenticationRemoteDataSource>(), | ||||||
|     onSignUpSuccess: onSignUpSuccess, |     onSignUpSuccess: onSignUpSuccess, | ||||||
|     onAuthChange: onAccountChanges, |     onAuthChange: onAccountChanges, | ||||||
|  |     customPasswordValidator: const CustomPassword.pure(), | ||||||
|     extraSignUpInputs: [ |     extraSignUpInputs: [ | ||||||
|       FormInput( |       FormInput( | ||||||
|         AppFormField.confirmedPassword, |         AppFormField.confirmedPassword, | ||||||
|  | |||||||
| @ -3,10 +3,11 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: sign_in_form.dart | // File: sign_in_form.dart | ||||||
| // Created Date: 19/08/2022 15:24:37 | // Created Date: 19/08/2022 15:24:37 | ||||||
| // Last Modified: Wed Nov 16 2022 | // Last Modified: Sat Dec 03 2022 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
|  | import 'package:example_router/core/utils/custom_password.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; | import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; | ||||||
| import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | ||||||
| @ -38,7 +39,8 @@ class _PasswordInput extends StatelessWidget { | |||||||
|       field: AuthFormField.password, |       field: AuthFormField.password, | ||||||
|       builder: ((context, cubit, state, field, input) { |       builder: ((context, cubit, state, field, input) { | ||||||
|         return TextField( |         return TextField( | ||||||
|           onChanged: (pwd) => cubit.passwordChanged(pwd), |           onChanged: (pwd) => cubit | ||||||
|  |               .passwordCustomChanged<CustomPassword>(CustomPassword.dirty(pwd)), | ||||||
|           obscureText: true, |           obscureText: true, | ||||||
|           decoration: InputDecoration( |           decoration: InputDecoration( | ||||||
|             labelText: 'Password', |             labelText: 'Password', | ||||||
| @ -59,7 +61,9 @@ class _SignInButton extends StatelessWidget { | |||||||
|         return status.isSubmissionInProgress |         return status.isSubmissionInProgress | ||||||
|             ? const CircularProgressIndicator() |             ? const CircularProgressIndicator() | ||||||
|             : ElevatedButton( |             : ElevatedButton( | ||||||
|                 onPressed: status.isValidated ? () => cubit.signInWithEmailAndPassword() : null, |                 onPressed: status.isValidated | ||||||
|  |                     ? () => cubit.signInWithEmailAndPassword() | ||||||
|  |                     : null, | ||||||
|                 child: const Text('Sign in with credentials'), |                 child: const Text('Sign in with credentials'), | ||||||
|               ); |               ); | ||||||
|       }), |       }), | ||||||
|  | |||||||
| @ -3,11 +3,12 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: sign_up_form.dart | // File: sign_up_form.dart | ||||||
| // Created Date: 19/08/2022 14:41:08 | // Created Date: 19/08/2022 14:41:08 | ||||||
| // Last Modified: Wed Nov 16 2022 | // Last Modified: Sat Dec 03 2022 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
| import 'package:example_router/core/constants/form_field.dart'; | import 'package:example_router/core/constants/form_field.dart'; | ||||||
|  | import 'package:example_router/core/utils/custom_password.dart'; | ||||||
| import 'package:flutter/material.dart' hide FormField; | import 'package:flutter/material.dart' hide FormField; | ||||||
| import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; | import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; | ||||||
| import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | ||||||
| @ -40,7 +41,8 @@ class _PasswordInput extends StatelessWidget { | |||||||
|       builder: ((context, cubit, state, field, input) { |       builder: ((context, cubit, state, field, input) { | ||||||
|         return TextField( |         return TextField( | ||||||
|           onChanged: (pwd) { |           onChanged: (pwd) { | ||||||
|             cubit.passwordChanged(pwd); |             cubit.passwordCustomChanged<CustomPassword>( | ||||||
|  |                 CustomPassword.dirty(pwd)); | ||||||
|             cubit.dataChanged( |             cubit.dataChanged( | ||||||
|                 AppFormField.confirmedPassword, |                 AppFormField.confirmedPassword, | ||||||
|                 ConfirmedPassword.dirty( |                 ConfirmedPassword.dirty( | ||||||
|  | |||||||
| @ -57,6 +57,8 @@ class AuthenticationRepositoryImpl<T extends Object> | |||||||
|     FormRepository? formRepository, |     FormRepository? formRepository, | ||||||
|     // ignore: strict_raw_type |     // ignore: strict_raw_type | ||||||
|     List<FormInput>? extraSignUpInputs, |     List<FormInput>? extraSignUpInputs, | ||||||
|  |     FormInputValidator<String?, ValidationError>? customEmailValidator, | ||||||
|  |     FormInputValidator<String?, ValidationError>? customPasswordValidator, | ||||||
|     OnSignUpSuccess<T>? onSignUpSuccess, |     OnSignUpSuccess<T>? onSignUpSuccess, | ||||||
|     OnAuthChange<T>? onAuthChange, |     OnAuthChange<T>? onAuthChange, | ||||||
|   })  : _authenticationLocalDataSource = authenticationCacheDataSource, |   })  : _authenticationLocalDataSource = authenticationCacheDataSource, | ||||||
| @ -71,8 +73,14 @@ class AuthenticationRepositoryImpl<T extends Object> | |||||||
|       ..registerForm( |       ..registerForm( | ||||||
|         WyattFormImpl( |         WyattFormImpl( | ||||||
|           [ |           [ | ||||||
|             FormInput(AuthFormField.email, const Email.pure()), |             FormInput( | ||||||
|             FormInput(AuthFormField.password, const Password.pure()) |               AuthFormField.email, | ||||||
|  |               customEmailValidator ?? const Email.pure(), | ||||||
|  |             ), | ||||||
|  |             FormInput( | ||||||
|  |               AuthFormField.password, | ||||||
|  |               customPasswordValidator ?? const Password.pure(), | ||||||
|  |             ) | ||||||
|           ], |           ], | ||||||
|           name: AuthFormName.signInForm, |           name: AuthFormName.signInForm, | ||||||
|         ), |         ), | ||||||
| @ -80,8 +88,14 @@ class AuthenticationRepositoryImpl<T extends Object> | |||||||
|       ..registerForm( |       ..registerForm( | ||||||
|         WyattFormImpl( |         WyattFormImpl( | ||||||
|           [ |           [ | ||||||
|             FormInput(AuthFormField.email, const Email.pure()), |             FormInput( | ||||||
|             FormInput(AuthFormField.password, const Password.pure()), |               AuthFormField.email, | ||||||
|  |               customEmailValidator ?? const Email.pure(), | ||||||
|  |             ), | ||||||
|  |             FormInput( | ||||||
|  |               AuthFormField.password, | ||||||
|  |               customPasswordValidator ?? const Password.pure(), | ||||||
|  |             ), | ||||||
|             ...extraSignUpInputs ?? [] |             ...extraSignUpInputs ?? [] | ||||||
|           ], |           ], | ||||||
|           name: AuthFormName.signUpForm, |           name: AuthFormName.signUpForm, | ||||||
|  | |||||||
| @ -41,15 +41,52 @@ class SignInCubit<Extra> extends FormDataCubit<SignInState> { | |||||||
|   String get formName => AuthFormName.signInForm; |   String get formName => AuthFormName.signInForm; | ||||||
| 
 | 
 | ||||||
|   void emailChanged(String value) { |   void emailChanged(String value) { | ||||||
|  |     final emailValidatorType = _formRepository | ||||||
|  |         .accessForm(formName) | ||||||
|  |         .validatorOf(AuthFormField.email) | ||||||
|  |         .runtimeType; | ||||||
|  |     assert( | ||||||
|  |       emailValidatorType == Email, | ||||||
|  |       'Use emailCustomChanged(...) with validator $emailValidatorType', | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|     final Email email = Email.dirty(value); |     final Email email = Email.dirty(value); | ||||||
|     dataChanged(AuthFormField.email, email); |     dataChanged(AuthFormField.email, email); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void passwordChanged(String value) { |   void passwordChanged(String value) { | ||||||
|  |     final passwordValidatorType = _formRepository | ||||||
|  |         .accessForm(formName) | ||||||
|  |         .validatorOf(AuthFormField.password) | ||||||
|  |         .runtimeType; | ||||||
|  |     assert( | ||||||
|  |       passwordValidatorType == Password, | ||||||
|  |       'Use passwordCustomChanged(...) with validator $passwordValidatorType', | ||||||
|  |     ); | ||||||
|     final Password password = Password.dirty(value); |     final Password password = Password.dirty(value); | ||||||
|     dataChanged(AuthFormField.password, password); |     dataChanged(AuthFormField.password, password); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /// Same as [emailChanged] but with a custom [Validator]. | ||||||
|  |   /// | ||||||
|  |   /// Sort of short hand for [dataChanged]. | ||||||
|  |   void emailCustomChanged< | ||||||
|  |       Validator extends FormInputValidator<String?, ValidationError>>( | ||||||
|  |     Validator validator, | ||||||
|  |   ) { | ||||||
|  |     dataChanged(AuthFormField.email, validator); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Same as [passwordChanged] but with a custom [Validator]. | ||||||
|  |   /// | ||||||
|  |   /// Sort of short hand for [dataChanged]. | ||||||
|  |   void passwordCustomChanged< | ||||||
|  |       Validator extends FormInputValidator<String?, ValidationError>>( | ||||||
|  |     Validator validator, | ||||||
|  |   ) { | ||||||
|  |     dataChanged(AuthFormField.password, validator); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   @override |   @override | ||||||
|   FutureOr<void> dataChanged<Value>( |   FutureOr<void> dataChanged<Value>( | ||||||
|     String key, |     String key, | ||||||
| @ -114,7 +151,7 @@ class SignInCubit<Extra> extends FormDataCubit<SignInState> { | |||||||
|     if (state.status.isSubmissionInProgress) { |     if (state.status.isSubmissionInProgress) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|      | 
 | ||||||
|     if (!state.status.isValidated) { |     if (!state.status.isValidated) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -17,8 +17,10 @@ | |||||||
| part of 'sign_in_cubit.dart'; | part of 'sign_in_cubit.dart'; | ||||||
| 
 | 
 | ||||||
| class SignInState extends FormDataState { | class SignInState extends FormDataState { | ||||||
|   Email get email => form.validatorOf(AuthFormField.email); |   FormInputValidator<String?, ValidationError> get email => | ||||||
|   Password get password => form.validatorOf(AuthFormField.password); |       form.validatorOf(AuthFormField.email); | ||||||
|  |   FormInputValidator<String?, ValidationError> get password => | ||||||
|  |       form.validatorOf(AuthFormField.password); | ||||||
| 
 | 
 | ||||||
|   const SignInState({ |   const SignInState({ | ||||||
|     required super.form, |     required super.form, | ||||||
|  | |||||||
| @ -43,15 +43,52 @@ class SignUpCubit<Extra> extends FormDataCubit<SignUpState> { | |||||||
|   String get formName => AuthFormName.signUpForm; |   String get formName => AuthFormName.signUpForm; | ||||||
| 
 | 
 | ||||||
|   void emailChanged(String value) { |   void emailChanged(String value) { | ||||||
|  |     final emailValidatorType = _formRepository | ||||||
|  |         .accessForm(formName) | ||||||
|  |         .validatorOf(AuthFormField.email) | ||||||
|  |         .runtimeType; | ||||||
|  |     assert( | ||||||
|  |       emailValidatorType == Email, | ||||||
|  |       'Use emailCustomChanged(...) with validator $emailValidatorType', | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|     final Email email = Email.dirty(value); |     final Email email = Email.dirty(value); | ||||||
|     dataChanged(AuthFormField.email, email); |     dataChanged(AuthFormField.email, email); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void passwordChanged(String value) { |   void passwordChanged(String value) { | ||||||
|  |     final passwordValidatorType = _formRepository | ||||||
|  |         .accessForm(formName) | ||||||
|  |         .validatorOf(AuthFormField.password) | ||||||
|  |         .runtimeType; | ||||||
|  |     assert( | ||||||
|  |       passwordValidatorType == Password, | ||||||
|  |       'Use passwordCustomChanged(...) with validator $passwordValidatorType', | ||||||
|  |     ); | ||||||
|     final Password password = Password.dirty(value); |     final Password password = Password.dirty(value); | ||||||
|     dataChanged(AuthFormField.password, password); |     dataChanged(AuthFormField.password, password); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /// Same as [emailChanged] but with a custom [Validator]. | ||||||
|  |   /// | ||||||
|  |   /// Sort of short hand for [dataChanged]. | ||||||
|  |   void emailCustomChanged< | ||||||
|  |       Validator extends FormInputValidator<String?, ValidationError>>( | ||||||
|  |     Validator validator, | ||||||
|  |   ) { | ||||||
|  |     dataChanged(AuthFormField.email, validator); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Same as [passwordChanged] but with a custom [Validator]. | ||||||
|  |   /// | ||||||
|  |   /// Sort of short hand for [dataChanged]. | ||||||
|  |   void passwordCustomChanged< | ||||||
|  |       Validator extends FormInputValidator<String?, ValidationError>>( | ||||||
|  |     Validator validator, | ||||||
|  |   ) { | ||||||
|  |     dataChanged(AuthFormField.password, validator); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   @override |   @override | ||||||
|   FutureOr<void> dataChanged<Value>( |   FutureOr<void> dataChanged<Value>( | ||||||
|     String key, |     String key, | ||||||
|  | |||||||
| @ -17,8 +17,10 @@ | |||||||
| part of 'sign_up_cubit.dart'; | part of 'sign_up_cubit.dart'; | ||||||
| 
 | 
 | ||||||
| class SignUpState extends FormDataState { | class SignUpState extends FormDataState { | ||||||
|   Email get email => form.validatorOf(AuthFormField.email); |   FormInputValidator<String?, ValidationError> get email => | ||||||
|   Password get password => form.validatorOf(AuthFormField.password); |       form.validatorOf(AuthFormField.email); | ||||||
|  |   FormInputValidator<String?, ValidationError> get password => | ||||||
|  |       form.validatorOf(AuthFormField.password); | ||||||
| 
 | 
 | ||||||
|   const SignUpState({ |   const SignUpState({ | ||||||
|     required super.form, |     required super.form, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user