feat(authentication): add custom routine, and documentation
This commit is contained in:
		
							parent
							
								
									4dc0b26e95
								
							
						
					
					
						commit
						692c255a19
					
				| @ -72,9 +72,9 @@ class AppRouter { | |||||||
|               state, |               state, | ||||||
|               const HomePage(), |               const HomePage(), | ||||||
|             ), |             ), | ||||||
|     ), |         routes: [ | ||||||
|           GoRoute( |           GoRoute( | ||||||
|       path: '/home/sub', |             path: 'sub', | ||||||
|             name: SubPage.pageName, |             name: SubPage.pageName, | ||||||
|             pageBuilder: (context, state) => defaultTransition( |             pageBuilder: (context, state) => defaultTransition( | ||||||
|               context, |               context, | ||||||
| @ -82,5 +82,6 @@ class AppRouter { | |||||||
|               const SubPage(), |               const SubPage(), | ||||||
|             ), |             ), | ||||||
|           ), |           ), | ||||||
|  |         ]), | ||||||
|   ]; |   ]; | ||||||
| } | } | ||||||
|  | |||||||
| @ -30,19 +30,6 @@ import 'package:go_router/go_router.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'; | ||||||
| 
 | 
 | ||||||
| // FutureOrResult<int?> onAccountChanges( |  | ||||||
| //   AuthenticationRepository<int> repo, |  | ||||||
| //   AuthChangeEvent? authEvent, |  | ||||||
| // ) async { |  | ||||||
| //   final id = Random().nextInt(1000); |  | ||||||
| //   final token = |  | ||||||
| //       await repo.getIdentityToken().fold((value) => value, (error) => 'null'); |  | ||||||
| 
 |  | ||||||
| //   debugPrint( |  | ||||||
| //       'onAccountChanges: ${authEvent?.account}, type: ${authEvent.runtimeType}, token: $token, generatedId: $id'); |  | ||||||
| //   return Ok<int, AppException>(id); |  | ||||||
| // } |  | ||||||
| 
 |  | ||||||
| class App extends StatelessWidget { | class App extends StatelessWidget { | ||||||
|   final AuthenticationRepository<int> authenticationRepository = |   final AuthenticationRepository<int> authenticationRepository = | ||||||
|       AuthenticationRepositoryImpl( |       AuthenticationRepositoryImpl( | ||||||
| @ -64,7 +51,9 @@ class App extends StatelessWidget { | |||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     AuthenticationState<int>? previous; |     AuthenticationState<int>? previous; | ||||||
| 
 | 
 | ||||||
|     final AuthenticationCubit<int> authenticationCubit = ExampleAuthenticationCubit(authenticationRepository: authenticationRepository); |     final AuthenticationCubit<int> authenticationCubit = | ||||||
|  |         ExampleAuthenticationCubit( | ||||||
|  |             authenticationRepository: authenticationRepository); | ||||||
| 
 | 
 | ||||||
|     final GoRouter router = GoRouter( |     final GoRouter router = GoRouter( | ||||||
|       initialLocation: '/', |       initialLocation: '/', | ||||||
| @ -91,7 +80,7 @@ class App extends StatelessWidget { | |||||||
|             return '/home'; |             return '/home'; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|         return null; |         return state.name; | ||||||
|       }, |       }, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -49,4 +49,11 @@ class ExampleAuthenticationCubit extends AuthenticationCubit<int> { | |||||||
| 
 | 
 | ||||||
|     return const Ok(null); |     return const Ok(null); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   FutureOrResult<void> onDelete() { | ||||||
|  |     print('onDelete'); | ||||||
|  | 
 | ||||||
|  |     return const Ok(null); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -35,14 +35,20 @@ class SubPage extends StatelessWidget { | |||||||
|               icon: const Icon(Icons.logout_rounded)), |               icon: const Icon(Icons.logout_rounded)), | ||||||
|           IconButton( |           IconButton( | ||||||
|               onPressed: () => |               onPressed: () => | ||||||
|                   context.read<AuthenticationRepository<int>>().refresh(), |                   context.read<AuthenticationCubit<int>>().refresh(), | ||||||
|               icon: const Icon(Icons.refresh)) |               icon: const Icon(Icons.refresh)) | ||||||
|         ], |         ], | ||||||
|       ), |       ), | ||||||
|       body: const Padding( |       body: Padding( | ||||||
|         padding: EdgeInsets.all(8), |         padding: const EdgeInsets.all(8), | ||||||
|         child: SingleChildScrollView( |         child: ListView( | ||||||
|           child: Text('Another page'), |           children: [ | ||||||
|  |             const Text('Another page'), | ||||||
|  |             ElevatedButton( | ||||||
|  |               onPressed: () => context.read<AuthenticationCubit<int>>().delete(), | ||||||
|  |               child: const Text('Delete account'), | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|         ), |         ), | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|  | |||||||
| @ -35,7 +35,7 @@ class CustomRoutine<R, Data> { | |||||||
|   final FutureOr<Result<Data?, AppException>> Function( |   final FutureOr<Result<Data?, AppException>> Function( | ||||||
|     Result<R, AppException> routineResult, |     Result<R, AppException> routineResult, | ||||||
|   ) attachedLogic; |   ) attachedLogic; | ||||||
|   final void Function(AppException? exception) onError; |   final void Function(AppException exception) onError; | ||||||
|   final void Function(R result, Data? data) onSuccess; |   final void Function(R result, Data? data) onSuccess; | ||||||
| 
 | 
 | ||||||
|   FutureOr<void> call() async { |   FutureOr<void> call() async { | ||||||
| @ -46,17 +46,17 @@ class CustomRoutine<R, Data> { | |||||||
| 
 | 
 | ||||||
|     // Check for errors |     // Check for errors | ||||||
|     if (result.isErr) { |     if (result.isErr) { | ||||||
|       onError.call(result.err); |       onError.call(result.err!); | ||||||
| 
 | 
 | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     if (customRoutineResult.isErr) { |     if (customRoutineResult.isErr) { | ||||||
|       onError.call(customRoutineResult.err); |       onError.call(customRoutineResult.err!); | ||||||
| 
 | 
 | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // If no error |     // If no error | ||||||
|     return onSuccess.call(result.ok as Input, customRoutineResult.ok); |     return onSuccess.call(result.ok as R, customRoutineResult.ok); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -42,7 +42,7 @@ abstract class AuthenticationCubit<Data> | |||||||
|   } |   } | ||||||
|   final AuthenticationRepository<Data> authenticationRepository; |   final AuthenticationRepository<Data> authenticationRepository; | ||||||
| 
 | 
 | ||||||
|   SessionWrapper<Data>? latestSession; |   SessionWrapper<Data>? _latestSession; | ||||||
| 
 | 
 | ||||||
|   void _listenForAuthenticationChanges() { |   void _listenForAuthenticationChanges() { | ||||||
|     authenticationRepository.sessionStream().asyncMap((wrapper) async { |     authenticationRepository.sessionStream().asyncMap((wrapper) async { | ||||||
| @ -67,7 +67,7 @@ abstract class AuthenticationCubit<Data> | |||||||
|       } |       } | ||||||
|       return wrapper; |       return wrapper; | ||||||
|     }).listen((wrapper) async { |     }).listen((wrapper) async { | ||||||
|       latestSession = wrapper; |       _latestSession = wrapper; | ||||||
|       final session = wrapper.session; |       final session = wrapper.session; | ||||||
|       if (session != null) { |       if (session != null) { | ||||||
|         emit(AuthenticationState<Data>.authenticated(wrapper)); |         emit(AuthenticationState<Data>.authenticated(wrapper)); | ||||||
| @ -79,143 +79,59 @@ abstract class AuthenticationCubit<Data> | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// {@macro refresh} |   /// {@macro refresh} | ||||||
|   FutureOr<void> refresh() async { |   FutureOr<void> refresh() async => CustomRoutine<Account, Data?>( | ||||||
|     final refreshedAccount = await authenticationRepository.refresh(); |       routine: authenticationRepository.refresh, | ||||||
|     final customRoutineResult = await onRefresh(refreshedAccount); |       attachedLogic: onRefresh, | ||||||
| 
 |       onError: addError, | ||||||
|     if (refreshedAccount.isOk && customRoutineResult.isOk) { |       onSuccess: (result, data) => authenticationRepository.addSession( | ||||||
|       final account = refreshedAccount.ok!; |         SessionWrapper( | ||||||
|       final sessionData = customRoutineResult.ok; |           event: RefreshedEvent(account: result), | ||||||
| 
 |  | ||||||
|       final refreshedSession = SessionWrapper( |  | ||||||
|         event: RefreshedEvent(account: account), |  | ||||||
|           session: Session<Data>( |           session: Session<Data>( | ||||||
|           account: account, |             account: result, | ||||||
|           data: sessionData, |             data: data, | ||||||
|           ), |           ), | ||||||
|       ); |         ), | ||||||
| 
 |       ), | ||||||
|       authenticationRepository.addSession(refreshedSession); |     ).call(); | ||||||
| 
 |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (refreshedAccount.isErr) { |  | ||||||
|       addError(refreshedAccount.err!); |  | ||||||
| 
 |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (customRoutineResult.isErr) { |  | ||||||
|       addError(customRoutineResult.err!); |  | ||||||
| 
 |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   /// {@macro reauthenticate} |   /// {@macro reauthenticate} | ||||||
|   FutureOr<void> reauthenticate() async { |   FutureOr<void> reauthenticate() async => CustomRoutine<Account, Data?>( | ||||||
|     final reauthenticatedAccount = |         routine: authenticationRepository.reauthenticate, | ||||||
|         await authenticationRepository.reauthenticate(); |         attachedLogic: onReauthenticate, | ||||||
|     final customRoutineResult = await onReauthenticate(reauthenticatedAccount); |         onError: addError, | ||||||
| 
 |         onSuccess: (result, data) => authenticationRepository.addSession( | ||||||
|     if (reauthenticatedAccount.isOk && customRoutineResult.isOk) { |           SessionWrapper( | ||||||
|       final account = reauthenticatedAccount.ok!; |             event: ReauthenticatedEvent(account: result), | ||||||
|       final sessionData = customRoutineResult.ok; |  | ||||||
| 
 |  | ||||||
|       final reauthenticatedSession = SessionWrapper( |  | ||||||
|         event: ReauthenticatedEvent(account: account), |  | ||||||
|             session: Session<Data>( |             session: Session<Data>( | ||||||
|           account: account, |               account: result, | ||||||
|           data: sessionData, |               data: data, | ||||||
|             ), |             ), | ||||||
|       ); |           ), | ||||||
| 
 |         ), | ||||||
|       authenticationRepository.addSession(reauthenticatedSession); |       ).call(); | ||||||
| 
 |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (reauthenticatedAccount.isErr) { |  | ||||||
|       addError(reauthenticatedAccount.err!); |  | ||||||
| 
 |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (customRoutineResult.isErr) { |  | ||||||
|       addError(customRoutineResult.err!); |  | ||||||
| 
 |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   /// {@macro signout} |   /// {@macro signout} | ||||||
|   FutureOr<void> signOut() async { |   FutureOr<void> signOut() async => CustomRoutine<void, void>( | ||||||
|     final customRoutine = CustomRoutine<void, void>( |         routine: authenticationRepository.signOut, | ||||||
|       // ignore: unnecessary_lambdas |  | ||||||
|       routine: () => authenticationRepository.signOut(), |  | ||||||
|         attachedLogic: (routineResult) => onSignOut(), |         attachedLogic: (routineResult) => onSignOut(), | ||||||
|       onError: (error) => addError(error!), |         onError: addError, | ||||||
|         onSuccess: (result, data) => authenticationRepository |         onSuccess: (result, data) => authenticationRepository | ||||||
|           .addSession(const SessionWrapper(event: SignedOutEvent())), |             .addSession(SessionWrapper<Data>(event: const SignedOutEvent())), | ||||||
|     ); |       ).call(); | ||||||
| 
 |  | ||||||
|     customRoutine.call(); |  | ||||||
| 
 |  | ||||||
|     final signOut = await authenticationRepository.signOut(); |  | ||||||
| 
 |  | ||||||
|     final customRoutineResult = await onSignOut(); |  | ||||||
| 
 |  | ||||||
|     if (signOut.isOk && customRoutineResult.isOk) { |  | ||||||
|       authenticationRepository |  | ||||||
|           .addSession(const SessionWrapper(event: SignedOutEvent())); |  | ||||||
| 
 |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (signOut.isErr) { |  | ||||||
|       addError(signOut.err!); |  | ||||||
| 
 |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (customRoutineResult.isErr) { |  | ||||||
|       addError(customRoutineResult.err!); |  | ||||||
| 
 |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   /// {@macro delete} |   /// {@macro delete} | ||||||
|   FutureOr<void> delete() async { |   FutureOr<void> delete() async => CustomRoutine<void, void>( | ||||||
|     final signOut = await authenticationRepository.delete(); |         routine: authenticationRepository.delete, | ||||||
| 
 |         attachedLogic: (routineResult) => onDelete(), | ||||||
|     final customRoutineResult = await onDelete(); |         onError: addError, | ||||||
| 
 |         onSuccess: (result, data) => authenticationRepository | ||||||
|     if (signOut.isOk && customRoutineResult.isOk) { |             .addSession(SessionWrapper<Data>(event: const DeletedEvent())), | ||||||
|       authenticationRepository |       ).call(); | ||||||
|           .addSession(const SessionWrapper(event: DeletedEvent())); |  | ||||||
| 
 |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (signOut.isErr) { |  | ||||||
|       addError(signOut.err!); |  | ||||||
| 
 |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (customRoutineResult.isErr) { |  | ||||||
|       addError(customRoutineResult.err!); |  | ||||||
| 
 |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   /// Returns latest session wrapper. |   /// Returns latest session wrapper. | ||||||
|   /// |   /// | ||||||
|   /// Contains latest event and latest session data (account + extra data) |   /// Contains latest event and latest session data (account + extra data) | ||||||
|   SessionWrapper<Data>? currentSession() => latestSession; |   SessionWrapper<Data>? currentSession() => _latestSession; | ||||||
| 
 | 
 | ||||||
|   /// This callback is triggered when the user is automaticcaly logged in from |   /// This callback is triggered when the user is automaticcaly logged in from | ||||||
|   /// the cache. |   /// the cache. | ||||||
|  | |||||||
| @ -24,28 +24,22 @@ import 'package:wyatt_type_utils/wyatt_type_utils.dart'; | |||||||
| 
 | 
 | ||||||
| part 'password_reset_state.dart'; | part 'password_reset_state.dart'; | ||||||
| 
 | 
 | ||||||
|  | /// Cubit that allows user to reset his password | ||||||
| class PasswordResetCubit<Extra> extends FormDataCubit<PasswordResetState> { | class PasswordResetCubit<Extra> extends FormDataCubit<PasswordResetState> { | ||||||
|   PasswordResetCubit({ |   PasswordResetCubit({ | ||||||
|     required this.authenticationRepository, |     required this.authenticationRepository, | ||||||
|   })  :  |   }) : super( | ||||||
|         super( |  | ||||||
|           PasswordResetState( |           PasswordResetState( | ||||||
|             form: authenticationRepository.formRepository |             form: authenticationRepository.formRepository | ||||||
|                 .accessForm(AuthFormName.passwordResetForm), |                 .accessForm(AuthFormName.passwordResetForm), | ||||||
|           ), |           ), | ||||||
|         ); |         ); | ||||||
|   final AuthenticationRepository<Extra> authenticationRepository; |   final AuthenticationRepository<Extra> authenticationRepository; | ||||||
|   FormRepository get formRepository => |   FormRepository get formRepository => authenticationRepository.formRepository; | ||||||
|       authenticationRepository.formRepository; |  | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String get formName => AuthFormName.passwordResetForm; |   String get formName => AuthFormName.passwordResetForm; | ||||||
| 
 | 
 | ||||||
|   void emailChanged(String value) { |  | ||||||
|     final Email email = Email.dirty(value); |  | ||||||
|     dataChanged(AuthFormField.email, email); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |   @override | ||||||
|   FutureOr<void> dataChanged<Value>( |   FutureOr<void> dataChanged<Value>( | ||||||
|     String key, |     String key, | ||||||
| @ -74,6 +68,33 @@ class PasswordResetCubit<Extra> extends FormDataCubit<PasswordResetState> { | |||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   @override | ||||||
|  |   FutureOr<void> update( | ||||||
|  |     WyattForm form, { | ||||||
|  |     SetOperation operation = SetOperation.replace, | ||||||
|  |   }) { | ||||||
|  |     final WyattForm current = formRepository.accessForm(formName).clone(); | ||||||
|  |     final WyattForm newForm = operation.operation.call(current, form); | ||||||
|  |     formRepository.updateForm(newForm); | ||||||
|  | 
 | ||||||
|  |     emit( | ||||||
|  |       state.copyWith( | ||||||
|  |         form: newForm, | ||||||
|  |         status: newForm.validate(), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   FutureOr<void> validate() { | ||||||
|  |     emit( | ||||||
|  |       state.copyWith( | ||||||
|  |         status: formRepository.accessForm(formName).validate(), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Sends a password reset email to the user | ||||||
|   @override |   @override | ||||||
|   FutureOr<void> submit() async { |   FutureOr<void> submit() async { | ||||||
|     if (!state.status.isValidated) { |     if (!state.status.isValidated) { | ||||||
| @ -109,29 +130,29 @@ class PasswordResetCubit<Extra> extends FormDataCubit<PasswordResetState> { | |||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   void emailChanged(String value) { | ||||||
|   FutureOr<void> update( |     final emailValidatorType = formRepository | ||||||
|     WyattForm form, { |         .accessForm(formName) | ||||||
|     SetOperation operation = SetOperation.replace, |         .validatorOf(AuthFormField.email) | ||||||
|   }) { |         .runtimeType; | ||||||
|     final WyattForm current = formRepository.accessForm(formName).clone(); |     assert( | ||||||
|     final WyattForm newForm = operation.operation.call(current, form); |       emailValidatorType == Email, | ||||||
|     formRepository.updateForm(newForm); |       'Use emailCustomChanged(...) with validator $emailValidatorType', | ||||||
| 
 |  | ||||||
|     emit( |  | ||||||
|       state.copyWith( |  | ||||||
|         form: newForm, |  | ||||||
|         status: newForm.validate(), |  | ||||||
|       ), |  | ||||||
|     ); |     ); | ||||||
|  | 
 | ||||||
|  |     final Email email = Email.dirty(value); | ||||||
|  |     dataChanged(AuthFormField.email, email); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   /// Same as [emailChanged] but with a custom [Validator]. | ||||||
|   FutureOr<void> validate() { |   /// | ||||||
|     emit( |   /// Sort of short hand for [dataChanged]. | ||||||
|       state.copyWith( |   void emailCustomChanged< | ||||||
|         status: formRepository.accessForm(formName).validate(), |       Validator extends FormInputValidator<String?, ValidationError>>( | ||||||
|       ), |     Validator validator, | ||||||
|     ); |   ) { | ||||||
|  |     dataChanged(AuthFormField.email, validator); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   // TODO(wyatt): create base_password_reset_cubit and create mixins | ||||||
| } | } | ||||||
|  | |||||||
| @ -16,6 +16,8 @@ | |||||||
| 
 | 
 | ||||||
| part of 'sign_in_cubit.dart'; | part of 'sign_in_cubit.dart'; | ||||||
| 
 | 
 | ||||||
|  | /// Abstract sign in cubit useful for implementing a cubit with fine  | ||||||
|  | /// granularity by adding only the required mixins. | ||||||
| abstract class BaseSignInCubit<Data> extends FormDataCubit<SignInState> { | abstract class BaseSignInCubit<Data> extends FormDataCubit<SignInState> { | ||||||
|   BaseSignInCubit({ |   BaseSignInCubit({ | ||||||
|     required this.authenticationRepository, |     required this.authenticationRepository, | ||||||
|  | |||||||
| @ -17,17 +17,26 @@ | |||||||
| import 'dart:async'; | import 'dart:async'; | ||||||
| 
 | 
 | ||||||
| import 'package:wyatt_architecture/wyatt_architecture.dart'; | import 'package:wyatt_architecture/wyatt_architecture.dart'; | ||||||
|  | import 'package:wyatt_authentication_bloc/src/core/utils/custom_routine.dart'; | ||||||
| import 'package:wyatt_authentication_bloc/src/domain/entities/entities.dart'; | import 'package:wyatt_authentication_bloc/src/domain/entities/entities.dart'; | ||||||
| import 'package:wyatt_authentication_bloc/src/features/sign_in/cubit/sign_in_cubit.dart'; | import 'package:wyatt_authentication_bloc/src/features/sign_in/cubit/sign_in_cubit.dart'; | ||||||
| import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | ||||||
| import 'package:wyatt_type_utils/wyatt_type_utils.dart'; | import 'package:wyatt_type_utils/wyatt_type_utils.dart'; | ||||||
| 
 | 
 | ||||||
|  | /// Sign in mixin. | ||||||
|  | /// | ||||||
|  | /// Allows the user to sign in anonymously | ||||||
|  | /// | ||||||
|  | /// Gives access to the `signInAnonymously` method and | ||||||
|  | /// `onSignInAnonymously` callback. | ||||||
| mixin SignInAnonymously<Data> on BaseSignInCubit<Data> { | mixin SignInAnonymously<Data> on BaseSignInCubit<Data> { | ||||||
|  |   /// This callback is triggered when a user signs in anonymously. | ||||||
|   FutureOrResult<Data?> onSignInAnonymously( |   FutureOrResult<Data?> onSignInAnonymously( | ||||||
|     Result<Account, AppException> result, |     Result<Account, AppException> result, | ||||||
|     WyattForm form, |     WyattForm form, | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|  |   /// {@macro signin_anom} | ||||||
|   FutureOr<void> signInAnonymously() async { |   FutureOr<void> signInAnonymously() async { | ||||||
|     if (state.status.isSubmissionInProgress) { |     if (state.status.isSubmissionInProgress) { | ||||||
|       return; |       return; | ||||||
| @ -36,12 +45,13 @@ mixin SignInAnonymously<Data> on BaseSignInCubit<Data> { | |||||||
|     final form = formRepository.accessForm(formName); |     final form = formRepository.accessForm(formName); | ||||||
|     emit(SignInState(form: form, status: FormStatus.submissionInProgress)); |     emit(SignInState(form: form, status: FormStatus.submissionInProgress)); | ||||||
| 
 | 
 | ||||||
|     final result = await authenticationRepository.signInAnonymously(); |     return CustomRoutine<Account, Data?>( | ||||||
| 
 |       routine: authenticationRepository.signInAnonymously, | ||||||
|     // Custom routine |       attachedLogic: (routineResult) => onSignInAnonymously( | ||||||
|     final customRoutineResult = await onSignInAnonymously(result, form); |         routineResult, | ||||||
|     if (customRoutineResult.isErr) { |         form, | ||||||
|       final error = customRoutineResult.err!; |       ), | ||||||
|  |       onError: (error) { | ||||||
|         emit( |         emit( | ||||||
|           SignInState( |           SignInState( | ||||||
|             form: form, |             form: form, | ||||||
| @ -50,43 +60,24 @@ mixin SignInAnonymously<Data> on BaseSignInCubit<Data> { | |||||||
|           ), |           ), | ||||||
|         ); |         ); | ||||||
|         addError(error); |         addError(error); | ||||||
|     } |       }, | ||||||
| 
 |       onSuccess: (account, data) { | ||||||
|     // Check result |         authenticationRepository.addSession( | ||||||
|     if (result.isErr) { |           SessionWrapper( | ||||||
|       final error = result.err!; |  | ||||||
|       emit( |  | ||||||
|         SignInState( |  | ||||||
|           form: form, |  | ||||||
|           errorMessage: error.message, |  | ||||||
|           status: FormStatus.submissionFailure, |  | ||||||
|         ), |  | ||||||
|       ); |  | ||||||
|       addError(error); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     final account = result.ok!; |  | ||||||
| 
 |  | ||||||
|     final signedInSession = SessionWrapper( |  | ||||||
|             event: SignedInEvent(account: account), |             event: SignedInEvent(account: account), | ||||||
|             session: Session<Data>( |             session: Session<Data>( | ||||||
|               account: account, |               account: account, | ||||||
|         data: customRoutineResult.ok, |               data: data, | ||||||
|  |             ), | ||||||
|           ), |           ), | ||||||
|         ); |         ); | ||||||
| 
 |  | ||||||
|     authenticationRepository.addSession(signedInSession); |  | ||||||
| 
 |  | ||||||
|         emit( |         emit( | ||||||
|       result.fold( |           SignInState( | ||||||
|         (value) => |  | ||||||
|             SignInState(form: form, status: FormStatus.submissionSuccess), |  | ||||||
|         (error) => SignInState( |  | ||||||
|             form: form, |             form: form, | ||||||
|           errorMessage: error.message, |             status: FormStatus.submissionSuccess, | ||||||
|           status: FormStatus.submissionFailure, |  | ||||||
|         ), |  | ||||||
|           ), |           ), | ||||||
|         ); |         ); | ||||||
|  |       }, | ||||||
|  |     ).call(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,12 +18,20 @@ import 'dart:async'; | |||||||
| 
 | 
 | ||||||
| import 'package:wyatt_architecture/wyatt_architecture.dart'; | import 'package:wyatt_architecture/wyatt_architecture.dart'; | ||||||
| import 'package:wyatt_authentication_bloc/src/core/constants/form_field.dart'; | import 'package:wyatt_authentication_bloc/src/core/constants/form_field.dart'; | ||||||
|  | import 'package:wyatt_authentication_bloc/src/core/utils/custom_routine.dart'; | ||||||
| import 'package:wyatt_authentication_bloc/src/domain/domain.dart'; | import 'package:wyatt_authentication_bloc/src/domain/domain.dart'; | ||||||
| import 'package:wyatt_authentication_bloc/src/features/sign_in/cubit/sign_in_cubit.dart'; | import 'package:wyatt_authentication_bloc/src/features/sign_in/cubit/sign_in_cubit.dart'; | ||||||
| import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | ||||||
| import 'package:wyatt_type_utils/wyatt_type_utils.dart'; | import 'package:wyatt_type_utils/wyatt_type_utils.dart'; | ||||||
| 
 | 
 | ||||||
|  | /// Sign in mixin. | ||||||
|  | /// | ||||||
|  | /// Allows the user to sign in with email and password | ||||||
|  | /// | ||||||
|  | /// Gives access to the `signInWithEmailAndPassword` method and | ||||||
|  | /// `onSignInWithEmailAndPassword` callback. | ||||||
| mixin SignInWithEmailPassword<Data> on BaseSignInCubit<Data> { | mixin SignInWithEmailPassword<Data> on BaseSignInCubit<Data> { | ||||||
|  |   /// This callback is triggered when a user signs in with email and password. | ||||||
|   FutureOrResult<Data?> onSignInWithEmailAndPassword( |   FutureOrResult<Data?> onSignInWithEmailAndPassword( | ||||||
|     Result<Account, AppException> result, |     Result<Account, AppException> result, | ||||||
|     WyattForm form, |     WyattForm form, | ||||||
| @ -76,6 +84,7 @@ mixin SignInWithEmailPassword<Data> on BaseSignInCubit<Data> { | |||||||
|     dataChanged(AuthFormField.password, validator); |     dataChanged(AuthFormField.password, validator); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /// {@macro signin_pwd} | ||||||
|   FutureOr<void> signInWithEmailAndPassword() async { |   FutureOr<void> signInWithEmailAndPassword() async { | ||||||
|     if (state.status.isSubmissionInProgress) { |     if (state.status.isSubmissionInProgress) { | ||||||
|       return; |       return; | ||||||
| @ -106,18 +115,16 @@ mixin SignInWithEmailPassword<Data> on BaseSignInCubit<Data> { | |||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     final result = await authenticationRepository.signInWithEmailAndPassword( |     return CustomRoutine<Account, Data?>( | ||||||
|  |       routine: () => authenticationRepository.signInWithEmailAndPassword( | ||||||
|         email: email!, |         email: email!, | ||||||
|         password: password!, |         password: password!, | ||||||
|     ); |       ), | ||||||
| 
 |       attachedLogic: (routineResult) => onSignInWithEmailAndPassword( | ||||||
|     // Custom routine |         routineResult, | ||||||
|     final customRoutineResult = await onSignInWithEmailAndPassword( |  | ||||||
|       result, |  | ||||||
|         form, |         form, | ||||||
|     ); |       ), | ||||||
|     if (customRoutineResult.isErr) { |       onError: (error) { | ||||||
|       final error = customRoutineResult.err!; |  | ||||||
|         emit( |         emit( | ||||||
|           SignInState( |           SignInState( | ||||||
|             form: form, |             form: form, | ||||||
| @ -126,43 +133,24 @@ mixin SignInWithEmailPassword<Data> on BaseSignInCubit<Data> { | |||||||
|           ), |           ), | ||||||
|         ); |         ); | ||||||
|         addError(error); |         addError(error); | ||||||
|     } |       }, | ||||||
| 
 |       onSuccess: (account, data) { | ||||||
|     // Check result |         authenticationRepository.addSession( | ||||||
|     if (result.isErr) { |           SessionWrapper( | ||||||
|       final error = result.err!; |  | ||||||
|       emit( |  | ||||||
|         SignInState( |  | ||||||
|           form: form, |  | ||||||
|           errorMessage: error.message, |  | ||||||
|           status: FormStatus.submissionFailure, |  | ||||||
|         ), |  | ||||||
|       ); |  | ||||||
|       addError(error); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     final account = result.ok!; |  | ||||||
| 
 |  | ||||||
|     final signedInSession = SessionWrapper( |  | ||||||
|             event: SignedInEvent(account: account), |             event: SignedInEvent(account: account), | ||||||
|             session: Session<Data>( |             session: Session<Data>( | ||||||
|               account: account, |               account: account, | ||||||
|         data: customRoutineResult.ok, |               data: data, | ||||||
|  |             ), | ||||||
|           ), |           ), | ||||||
|         ); |         ); | ||||||
| 
 |  | ||||||
|     authenticationRepository.addSession(signedInSession); |  | ||||||
| 
 |  | ||||||
|         emit( |         emit( | ||||||
|       result.fold( |           SignInState( | ||||||
|         (value) => |  | ||||||
|             SignInState(form: form, status: FormStatus.submissionSuccess), |  | ||||||
|         (error) => SignInState( |  | ||||||
|             form: form, |             form: form, | ||||||
|           errorMessage: error.message, |             status: FormStatus.submissionSuccess, | ||||||
|           status: FormStatus.submissionFailure, |  | ||||||
|         ), |  | ||||||
|           ), |           ), | ||||||
|         ); |         ); | ||||||
|  |       }, | ||||||
|  |     ).call(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -17,17 +17,26 @@ | |||||||
| import 'dart:async'; | import 'dart:async'; | ||||||
| 
 | 
 | ||||||
| import 'package:wyatt_architecture/wyatt_architecture.dart'; | import 'package:wyatt_architecture/wyatt_architecture.dart'; | ||||||
|  | import 'package:wyatt_authentication_bloc/src/core/utils/custom_routine.dart'; | ||||||
| import 'package:wyatt_authentication_bloc/src/domain/entities/entities.dart'; | import 'package:wyatt_authentication_bloc/src/domain/entities/entities.dart'; | ||||||
| import 'package:wyatt_authentication_bloc/src/features/sign_in/cubit/sign_in_cubit.dart'; | import 'package:wyatt_authentication_bloc/src/features/sign_in/cubit/sign_in_cubit.dart'; | ||||||
| import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | ||||||
| import 'package:wyatt_type_utils/wyatt_type_utils.dart'; | import 'package:wyatt_type_utils/wyatt_type_utils.dart'; | ||||||
| 
 | 
 | ||||||
|  | /// Sign in mixin. | ||||||
|  | /// | ||||||
|  | /// Allows the user to sign in with google | ||||||
|  | /// | ||||||
|  | /// Gives access to the `signInWithGoogle` method and | ||||||
|  | /// `onSignInWithGoogle` callback. | ||||||
| mixin SignInWithGoogle<Data> on BaseSignInCubit<Data> { | mixin SignInWithGoogle<Data> on BaseSignInCubit<Data> { | ||||||
|  |   /// This callback is triggered when a user signs in with google. | ||||||
|   FutureOrResult<Data?> onSignInWithGoogle( |   FutureOrResult<Data?> onSignInWithGoogle( | ||||||
|     Result<Account, AppException> result, |     Result<Account, AppException> result, | ||||||
|     WyattForm form, |     WyattForm form, | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|  |   /// {@macro signin_google} | ||||||
|   FutureOr<void> signInWithGoogle() async { |   FutureOr<void> signInWithGoogle() async { | ||||||
|     if (state.status.isSubmissionInProgress) { |     if (state.status.isSubmissionInProgress) { | ||||||
|       return; |       return; | ||||||
| @ -35,15 +44,13 @@ mixin SignInWithGoogle<Data> on BaseSignInCubit<Data> { | |||||||
|     final form = formRepository.accessForm(formName); |     final form = formRepository.accessForm(formName); | ||||||
|     emit(SignInState(form: form, status: FormStatus.submissionInProgress)); |     emit(SignInState(form: form, status: FormStatus.submissionInProgress)); | ||||||
| 
 | 
 | ||||||
|     final result = await authenticationRepository.signInWithGoogle(); |     return CustomRoutine<Account, Data?>( | ||||||
| 
 |       routine: authenticationRepository.signInWithGoogle, | ||||||
|     // Custom routine |       attachedLogic: (routineResult) => onSignInWithGoogle( | ||||||
|     final customRoutineResult = await onSignInWithGoogle( |         routineResult, | ||||||
|       result, |  | ||||||
|         form, |         form, | ||||||
|     ); |       ), | ||||||
|     if (customRoutineResult.isErr) { |       onError: (error) { | ||||||
|       final error = customRoutineResult.err!; |  | ||||||
|         emit( |         emit( | ||||||
|           SignInState( |           SignInState( | ||||||
|             form: form, |             form: form, | ||||||
| @ -52,43 +59,24 @@ mixin SignInWithGoogle<Data> on BaseSignInCubit<Data> { | |||||||
|           ), |           ), | ||||||
|         ); |         ); | ||||||
|         addError(error); |         addError(error); | ||||||
|     } |       }, | ||||||
| 
 |       onSuccess: (account, data) { | ||||||
|     // Check result |         authenticationRepository.addSession( | ||||||
|     if (result.isErr) { |           SessionWrapper( | ||||||
|       final error = result.err!; |  | ||||||
|       emit( |  | ||||||
|         SignInState( |  | ||||||
|           form: form, |  | ||||||
|           errorMessage: error.message, |  | ||||||
|           status: FormStatus.submissionFailure, |  | ||||||
|         ), |  | ||||||
|       ); |  | ||||||
|       addError(error); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     final account = result.ok!; |  | ||||||
| 
 |  | ||||||
|     final signedInSession = SessionWrapper( |  | ||||||
|             event: SignedInEvent(account: account), |             event: SignedInEvent(account: account), | ||||||
|             session: Session<Data>( |             session: Session<Data>( | ||||||
|               account: account, |               account: account, | ||||||
|         data: customRoutineResult.ok, |               data: data, | ||||||
|  |             ), | ||||||
|           ), |           ), | ||||||
|         ); |         ); | ||||||
| 
 |  | ||||||
|     authenticationRepository.addSession(signedInSession); |  | ||||||
| 
 |  | ||||||
|         emit( |         emit( | ||||||
|       result.fold( |           SignInState( | ||||||
|         (value) => |  | ||||||
|             SignInState(form: form, status: FormStatus.submissionSuccess), |  | ||||||
|         (error) => SignInState( |  | ||||||
|             form: form, |             form: form, | ||||||
|           errorMessage: error.message, |             status: FormStatus.submissionSuccess, | ||||||
|           status: FormStatus.submissionFailure, |  | ||||||
|         ), |  | ||||||
|           ), |           ), | ||||||
|         ); |         ); | ||||||
|  |       }, | ||||||
|  |     ).call(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -32,6 +32,8 @@ part 'base_sign_in_cubit.dart'; | |||||||
| part 'sign_in_state.dart'; | part 'sign_in_state.dart'; | ||||||
| 
 | 
 | ||||||
| /// Fully featured sign in cubit. | /// Fully featured sign in cubit. | ||||||
|  | ///  | ||||||
|  | /// Sufficient in most cases. (Where fine granularity is not required.) | ||||||
| class SignInCubit<Data> extends BaseSignInCubit<Data> | class SignInCubit<Data> extends BaseSignInCubit<Data> | ||||||
|     with |     with | ||||||
|         SignInAnonymously<Data>, |         SignInAnonymously<Data>, | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ | |||||||
| 
 | 
 | ||||||
| part of 'sign_in_cubit.dart'; | part of 'sign_in_cubit.dart'; | ||||||
| 
 | 
 | ||||||
|  | /// Sign in cubit state to manage the form. | ||||||
| class SignInState extends FormDataState { | class SignInState extends FormDataState { | ||||||
|   const SignInState({ |   const SignInState({ | ||||||
|     required super.form, |     required super.form, | ||||||
|  | |||||||
| @ -19,7 +19,9 @@ import 'package:flutter_bloc/flutter_bloc.dart'; | |||||||
| import 'package:wyatt_authentication_bloc/src/features/sign_in/sign_in.dart'; | import 'package:wyatt_authentication_bloc/src/features/sign_in/sign_in.dart'; | ||||||
| import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | ||||||
| 
 | 
 | ||||||
| class SignInListener<Extra> extends StatelessWidget { | /// Widget that listens and builds a child based on the state of  | ||||||
|  | /// the sign in cubit | ||||||
|  | class SignInListener<Data> extends StatelessWidget { | ||||||
|   const SignInListener({ |   const SignInListener({ | ||||||
|     required this.child, |     required this.child, | ||||||
|     this.onProgress, |     this.onProgress, | ||||||
| @ -41,7 +43,7 @@ class SignInListener<Extra> extends StatelessWidget { | |||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) => |   Widget build(BuildContext context) => | ||||||
|       BlocListener<SignInCubit<Extra>, SignInState>( |       BlocListener<SignInCubit<Data>, SignInState>( | ||||||
|         listener: (context, state) { |         listener: (context, state) { | ||||||
|           if (customBuilder != null) { |           if (customBuilder != null) { | ||||||
|             return customBuilder!(context, state); |             return customBuilder!(context, state); | ||||||
|  | |||||||
| @ -16,6 +16,8 @@ | |||||||
| 
 | 
 | ||||||
| part of 'sign_up_cubit.dart'; | part of 'sign_up_cubit.dart'; | ||||||
| 
 | 
 | ||||||
|  | /// Abstract sign up cubit useful for implementing a cubit with fine  | ||||||
|  | /// granularity by adding only the required mixins. | ||||||
| abstract class BaseSignUpCubit<Data> extends FormDataCubit<SignUpState> { | abstract class BaseSignUpCubit<Data> extends FormDataCubit<SignUpState> { | ||||||
|   BaseSignUpCubit({ |   BaseSignUpCubit({ | ||||||
|     required this.authenticationRepository, |     required this.authenticationRepository, | ||||||
|  | |||||||
| @ -17,13 +17,20 @@ | |||||||
| import 'dart:async'; | import 'dart:async'; | ||||||
| 
 | 
 | ||||||
| import 'package:wyatt_architecture/wyatt_architecture.dart'; | import 'package:wyatt_architecture/wyatt_architecture.dart'; | ||||||
| import 'package:wyatt_authentication_bloc/src/core/constants/form_field.dart'; | import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; | ||||||
| import 'package:wyatt_authentication_bloc/src/domain/entities/entities.dart'; |  | ||||||
| import 'package:wyatt_authentication_bloc/src/features/sign_up/cubit/sign_up_cubit.dart'; |  | ||||||
| import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | ||||||
| import 'package:wyatt_type_utils/wyatt_type_utils.dart'; | import 'package:wyatt_type_utils/wyatt_type_utils.dart'; | ||||||
| 
 | 
 | ||||||
|  | /// Sign up mixin. | ||||||
|  | /// | ||||||
|  | /// Allows the user to register with an email and a password. | ||||||
|  | /// | ||||||
|  | /// Gives access to the `signUpWithEmailPassword` method and | ||||||
|  | /// `onSignUpWithEmailAndPassword` callback. | ||||||
| mixin SignUpWithEmailPassword<Data> on BaseSignUpCubit<Data> { | mixin SignUpWithEmailPassword<Data> on BaseSignUpCubit<Data> { | ||||||
|  |   /// This callback is triggered when a user creates an account. | ||||||
|  |   /// | ||||||
|  |   /// For example: when a user sign up in firebase. | ||||||
|   FutureOrResult<Data?> onSignUpWithEmailAndPassword( |   FutureOrResult<Data?> onSignUpWithEmailAndPassword( | ||||||
|     Result<Account, AppException> result, |     Result<Account, AppException> result, | ||||||
|     WyattForm form, |     WyattForm form, | ||||||
| @ -76,6 +83,7 @@ mixin SignUpWithEmailPassword<Data> on BaseSignUpCubit<Data> { | |||||||
|     dataChanged(AuthFormField.password, validator); |     dataChanged(AuthFormField.password, validator); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /// {@macro signup_pwd} | ||||||
|   FutureOr<void> signUpWithEmailPassword() async { |   FutureOr<void> signUpWithEmailPassword() async { | ||||||
|     if (!state.status.isValidated) { |     if (!state.status.isValidated) { | ||||||
|       return; |       return; | ||||||
| @ -97,18 +105,16 @@ mixin SignUpWithEmailPassword<Data> on BaseSignUpCubit<Data> { | |||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     final result = await authenticationRepository.signUpWithEmailAndPassword( |     return CustomRoutine<Account, Data?>( | ||||||
|  |       routine: () => authenticationRepository.signUpWithEmailAndPassword( | ||||||
|         email: email!, |         email: email!, | ||||||
|         password: password!, |         password: password!, | ||||||
|     ); |       ), | ||||||
| 
 |       attachedLogic: (routineResult) => onSignUpWithEmailAndPassword( | ||||||
|     // Custom routine |         routineResult, | ||||||
|     final customRoutineResult = await onSignUpWithEmailAndPassword( |  | ||||||
|       result, |  | ||||||
|         form, |         form, | ||||||
|     ); |       ), | ||||||
|     if (customRoutineResult.isErr) { |       onError: (error) { | ||||||
|       final error = customRoutineResult.err!; |  | ||||||
|         emit( |         emit( | ||||||
|           SignUpState( |           SignUpState( | ||||||
|             form: form, |             form: form, | ||||||
| @ -117,45 +123,24 @@ mixin SignUpWithEmailPassword<Data> on BaseSignUpCubit<Data> { | |||||||
|           ), |           ), | ||||||
|         ); |         ); | ||||||
|         addError(error); |         addError(error); | ||||||
|     } |       }, | ||||||
| 
 |       onSuccess: (account, data) { | ||||||
|     // Check result |         authenticationRepository.addSession( | ||||||
|     if (result.isErr) { |           SessionWrapper( | ||||||
|       final error = result.err!; |  | ||||||
|       emit( |  | ||||||
|         SignUpState( |  | ||||||
|           form: form, |  | ||||||
|           errorMessage: error.message, |  | ||||||
|           status: FormStatus.submissionFailure, |  | ||||||
|         ), |  | ||||||
|       ); |  | ||||||
|       addError(error); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     final account = result.ok!; |  | ||||||
| 
 |  | ||||||
|     final signedUpSession = SessionWrapper( |  | ||||||
|             event: SignedUpEvent(account: account), |             event: SignedUpEvent(account: account), | ||||||
|             session: Session<Data>( |             session: Session<Data>( | ||||||
|               account: account, |               account: account, | ||||||
|         data: customRoutineResult.ok, |               data: data, | ||||||
|  |             ), | ||||||
|           ), |           ), | ||||||
|         ); |         ); | ||||||
| 
 |  | ||||||
|     authenticationRepository.addSession(signedUpSession); |  | ||||||
| 
 |  | ||||||
|         emit( |         emit( | ||||||
|       result.fold( |           SignUpState( | ||||||
|         (value) => SignUpState( |  | ||||||
|             form: form, |             form: form, | ||||||
|             status: FormStatus.submissionSuccess, |             status: FormStatus.submissionSuccess, | ||||||
|           ), |           ), | ||||||
|         (error) => SignUpState( |  | ||||||
|           form: form, |  | ||||||
|           errorMessage: error.message, |  | ||||||
|           status: FormStatus.submissionFailure, |  | ||||||
|         ), |  | ||||||
|       ), |  | ||||||
|         ); |         ); | ||||||
|  |       }, | ||||||
|  |     ).call(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -28,6 +28,9 @@ import 'package:wyatt_type_utils/wyatt_type_utils.dart'; | |||||||
| part 'base_sign_up_cubit.dart'; | part 'base_sign_up_cubit.dart'; | ||||||
| part 'sign_up_state.dart'; | part 'sign_up_state.dart'; | ||||||
| 
 | 
 | ||||||
|  | /// Fully featured sign up cubit. | ||||||
|  | ///  | ||||||
|  | /// Sufficient in most cases. (Where fine granularity is not required.) | ||||||
| class SignUpCubit<Data> extends BaseSignUpCubit<Data> | class SignUpCubit<Data> extends BaseSignUpCubit<Data> | ||||||
|     with SignUpWithEmailPassword<Data> { |     with SignUpWithEmailPassword<Data> { | ||||||
|   SignUpCubit({required super.authenticationRepository}); |   SignUpCubit({required super.authenticationRepository}); | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ | |||||||
| 
 | 
 | ||||||
| part of 'sign_up_cubit.dart'; | part of 'sign_up_cubit.dart'; | ||||||
| 
 | 
 | ||||||
|  | /// Sign up cubit state to manage the form. | ||||||
| class SignUpState extends FormDataState { | class SignUpState extends FormDataState { | ||||||
|   const SignUpState({ |   const SignUpState({ | ||||||
|     required super.form, |     required super.form, | ||||||
|  | |||||||
| @ -19,7 +19,9 @@ import 'package:flutter_bloc/flutter_bloc.dart'; | |||||||
| import 'package:wyatt_authentication_bloc/src/features/sign_up/cubit/sign_up_cubit.dart'; | import 'package:wyatt_authentication_bloc/src/features/sign_up/cubit/sign_up_cubit.dart'; | ||||||
| import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | ||||||
| 
 | 
 | ||||||
| class SignUpListener<Extra> extends StatelessWidget { | /// Widget that listens and builds a child based on the state of  | ||||||
|  | /// the sign up cubit | ||||||
|  | class SignUpListener<Data> extends StatelessWidget { | ||||||
|   const SignUpListener({ |   const SignUpListener({ | ||||||
|     required this.child, |     required this.child, | ||||||
|     this.onProgress, |     this.onProgress, | ||||||
| @ -41,7 +43,7 @@ class SignUpListener<Extra> extends StatelessWidget { | |||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) => |   Widget build(BuildContext context) => | ||||||
|       BlocListener<SignUpCubit<Extra>, SignUpState>( |       BlocListener<SignUpCubit<Data>, SignUpState>( | ||||||
|         listener: (context, state) { |         listener: (context, state) { | ||||||
|           if (customBuilder != null) { |           if (customBuilder != null) { | ||||||
|             return customBuilder!(context, state); |             return customBuilder!(context, state); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user