From f123e537aefcbaa0c032742c44d5e4f37931cb21 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Sat, 3 Dec 2022 18:41:04 -0500 Subject: [PATCH] fix(authentication): allow email/password validators customization (closes #57) --- .../lib/core/utils/custom_password.dart | 30 ++++++++++++++ .../lib/presentation/features/app/app.dart | 4 +- .../sign_in/widgets/sign_in_form.dart | 10 +++-- .../sign_up/widgets/sign_up_form.dart | 6 ++- .../authentication_repository_impl.dart | 22 +++++++++-- .../features/sign_in/cubit/sign_in_cubit.dart | 39 ++++++++++++++++++- .../features/sign_in/cubit/sign_in_state.dart | 6 ++- .../features/sign_up/cubit/sign_up_cubit.dart | 37 ++++++++++++++++++ .../features/sign_up/cubit/sign_up_state.dart | 6 ++- 9 files changed, 145 insertions(+), 15 deletions(-) create mode 100644 packages/wyatt_authentication_bloc/example/lib/core/utils/custom_password.dart diff --git a/packages/wyatt_authentication_bloc/example/lib/core/utils/custom_password.dart b/packages/wyatt_authentication_bloc/example/lib/core/utils/custom_password.dart new file mode 100644 index 00000000..bdbb3552 --- /dev/null +++ b/packages/wyatt_authentication_bloc/example/lib/core/utils/custom_password.dart @@ -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 . + +import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; + +class CustomPassword extends RegexValidator { + 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,}$'); +} diff --git a/packages/wyatt_authentication_bloc/example/lib/presentation/features/app/app.dart b/packages/wyatt_authentication_bloc/example/lib/presentation/features/app/app.dart index c73ab7b8..44f9522f 100644 --- a/packages/wyatt_authentication_bloc/example/lib/presentation/features/app/app.dart +++ b/packages/wyatt_authentication_bloc/example/lib/presentation/features/app/app.dart @@ -3,7 +3,7 @@ // ----- // File: app.dart // Created Date: 19/08/2022 12:05:38 -// Last Modified: Wed Nov 23 2022 +// Last Modified: Sat Dec 03 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/dependency_injection/get_it.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_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; @@ -47,6 +48,7 @@ class App extends StatelessWidget { authenticationRemoteDataSource: getIt(), onSignUpSuccess: onSignUpSuccess, onAuthChange: onAccountChanges, + customPasswordValidator: const CustomPassword.pure(), extraSignUpInputs: [ FormInput( AppFormField.confirmedPassword, diff --git a/packages/wyatt_authentication_bloc/example/lib/presentation/features/sign_in/widgets/sign_in_form.dart b/packages/wyatt_authentication_bloc/example/lib/presentation/features/sign_in/widgets/sign_in_form.dart index c52d561c..45a45c4a 100644 --- a/packages/wyatt_authentication_bloc/example/lib/presentation/features/sign_in/widgets/sign_in_form.dart +++ b/packages/wyatt_authentication_bloc/example/lib/presentation/features/sign_in/widgets/sign_in_form.dart @@ -3,10 +3,11 @@ // ----- // File: sign_in_form.dart // Created Date: 19/08/2022 15:24:37 -// Last Modified: Wed Nov 16 2022 +// Last Modified: Sat Dec 03 2022 // ----- // Copyright (c) 2022 +import 'package:example_router/core/utils/custom_password.dart'; import 'package:flutter/material.dart'; import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; @@ -38,7 +39,8 @@ class _PasswordInput extends StatelessWidget { field: AuthFormField.password, builder: ((context, cubit, state, field, input) { return TextField( - onChanged: (pwd) => cubit.passwordChanged(pwd), + onChanged: (pwd) => cubit + .passwordCustomChanged(CustomPassword.dirty(pwd)), obscureText: true, decoration: InputDecoration( labelText: 'Password', @@ -59,7 +61,9 @@ class _SignInButton extends StatelessWidget { return status.isSubmissionInProgress ? const CircularProgressIndicator() : ElevatedButton( - onPressed: status.isValidated ? () => cubit.signInWithEmailAndPassword() : null, + onPressed: status.isValidated + ? () => cubit.signInWithEmailAndPassword() + : null, child: const Text('Sign in with credentials'), ); }), diff --git a/packages/wyatt_authentication_bloc/example/lib/presentation/features/sign_up/widgets/sign_up_form.dart b/packages/wyatt_authentication_bloc/example/lib/presentation/features/sign_up/widgets/sign_up_form.dart index 3859c0c2..0a512fb8 100644 --- a/packages/wyatt_authentication_bloc/example/lib/presentation/features/sign_up/widgets/sign_up_form.dart +++ b/packages/wyatt_authentication_bloc/example/lib/presentation/features/sign_up/widgets/sign_up_form.dart @@ -3,11 +3,12 @@ // ----- // File: sign_up_form.dart // Created Date: 19/08/2022 14:41:08 -// Last Modified: Wed Nov 16 2022 +// Last Modified: Sat Dec 03 2022 // ----- // Copyright (c) 2022 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:wyatt_authentication_bloc/wyatt_authentication_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) { return TextField( onChanged: (pwd) { - cubit.passwordChanged(pwd); + cubit.passwordCustomChanged( + CustomPassword.dirty(pwd)); cubit.dataChanged( AppFormField.confirmedPassword, ConfirmedPassword.dirty( diff --git a/packages/wyatt_authentication_bloc/lib/src/data/repositories/authentication_repository_impl.dart b/packages/wyatt_authentication_bloc/lib/src/data/repositories/authentication_repository_impl.dart index 1d628fed..309b31b3 100644 --- a/packages/wyatt_authentication_bloc/lib/src/data/repositories/authentication_repository_impl.dart +++ b/packages/wyatt_authentication_bloc/lib/src/data/repositories/authentication_repository_impl.dart @@ -57,6 +57,8 @@ class AuthenticationRepositoryImpl FormRepository? formRepository, // ignore: strict_raw_type List? extraSignUpInputs, + FormInputValidator? customEmailValidator, + FormInputValidator? customPasswordValidator, OnSignUpSuccess? onSignUpSuccess, OnAuthChange? onAuthChange, }) : _authenticationLocalDataSource = authenticationCacheDataSource, @@ -71,8 +73,14 @@ class AuthenticationRepositoryImpl ..registerForm( WyattFormImpl( [ - FormInput(AuthFormField.email, const Email.pure()), - FormInput(AuthFormField.password, const Password.pure()) + FormInput( + AuthFormField.email, + customEmailValidator ?? const Email.pure(), + ), + FormInput( + AuthFormField.password, + customPasswordValidator ?? const Password.pure(), + ) ], name: AuthFormName.signInForm, ), @@ -80,8 +88,14 @@ class AuthenticationRepositoryImpl ..registerForm( WyattFormImpl( [ - FormInput(AuthFormField.email, const Email.pure()), - FormInput(AuthFormField.password, const Password.pure()), + FormInput( + AuthFormField.email, + customEmailValidator ?? const Email.pure(), + ), + FormInput( + AuthFormField.password, + customPasswordValidator ?? const Password.pure(), + ), ...extraSignUpInputs ?? [] ], name: AuthFormName.signUpForm, diff --git a/packages/wyatt_authentication_bloc/lib/src/features/sign_in/cubit/sign_in_cubit.dart b/packages/wyatt_authentication_bloc/lib/src/features/sign_in/cubit/sign_in_cubit.dart index 911e899a..5db99b5f 100644 --- a/packages/wyatt_authentication_bloc/lib/src/features/sign_in/cubit/sign_in_cubit.dart +++ b/packages/wyatt_authentication_bloc/lib/src/features/sign_in/cubit/sign_in_cubit.dart @@ -41,15 +41,52 @@ class SignInCubit extends FormDataCubit { String get formName => AuthFormName.signInForm; 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); dataChanged(AuthFormField.email, email); } 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); dataChanged(AuthFormField.password, password); } + /// Same as [emailChanged] but with a custom [Validator]. + /// + /// Sort of short hand for [dataChanged]. + void emailCustomChanged< + Validator extends FormInputValidator>( + 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>( + Validator validator, + ) { + dataChanged(AuthFormField.password, validator); + } + @override FutureOr dataChanged( String key, @@ -114,7 +151,7 @@ class SignInCubit extends FormDataCubit { if (state.status.isSubmissionInProgress) { return; } - + if (!state.status.isValidated) { return; } diff --git a/packages/wyatt_authentication_bloc/lib/src/features/sign_in/cubit/sign_in_state.dart b/packages/wyatt_authentication_bloc/lib/src/features/sign_in/cubit/sign_in_state.dart index 85e4edb1..8c65fe0f 100644 --- a/packages/wyatt_authentication_bloc/lib/src/features/sign_in/cubit/sign_in_state.dart +++ b/packages/wyatt_authentication_bloc/lib/src/features/sign_in/cubit/sign_in_state.dart @@ -17,8 +17,10 @@ part of 'sign_in_cubit.dart'; class SignInState extends FormDataState { - Email get email => form.validatorOf(AuthFormField.email); - Password get password => form.validatorOf(AuthFormField.password); + FormInputValidator get email => + form.validatorOf(AuthFormField.email); + FormInputValidator get password => + form.validatorOf(AuthFormField.password); const SignInState({ required super.form, diff --git a/packages/wyatt_authentication_bloc/lib/src/features/sign_up/cubit/sign_up_cubit.dart b/packages/wyatt_authentication_bloc/lib/src/features/sign_up/cubit/sign_up_cubit.dart index 9abdf146..38e719a1 100644 --- a/packages/wyatt_authentication_bloc/lib/src/features/sign_up/cubit/sign_up_cubit.dart +++ b/packages/wyatt_authentication_bloc/lib/src/features/sign_up/cubit/sign_up_cubit.dart @@ -43,15 +43,52 @@ class SignUpCubit extends FormDataCubit { String get formName => AuthFormName.signUpForm; 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); dataChanged(AuthFormField.email, email); } 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); dataChanged(AuthFormField.password, password); } + /// Same as [emailChanged] but with a custom [Validator]. + /// + /// Sort of short hand for [dataChanged]. + void emailCustomChanged< + Validator extends FormInputValidator>( + 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>( + Validator validator, + ) { + dataChanged(AuthFormField.password, validator); + } + @override FutureOr dataChanged( String key, diff --git a/packages/wyatt_authentication_bloc/lib/src/features/sign_up/cubit/sign_up_state.dart b/packages/wyatt_authentication_bloc/lib/src/features/sign_up/cubit/sign_up_state.dart index d6ba26c3..8203c293 100644 --- a/packages/wyatt_authentication_bloc/lib/src/features/sign_up/cubit/sign_up_state.dart +++ b/packages/wyatt_authentication_bloc/lib/src/features/sign_up/cubit/sign_up_state.dart @@ -17,8 +17,10 @@ part of 'sign_up_cubit.dart'; class SignUpState extends FormDataState { - Email get email => form.validatorOf(AuthFormField.email); - Password get password => form.validatorOf(AuthFormField.password); + FormInputValidator get email => + form.validatorOf(AuthFormField.email); + FormInputValidator get password => + form.validatorOf(AuthFormField.password); const SignUpState({ required super.form, -- 2.47.2