diff --git a/packages/wyatt_authentication_bloc/analysis_options.yaml b/packages/wyatt_authentication_bloc/analysis_options.yaml index 82177cd5..b0c6aced 100644 --- a/packages/wyatt_authentication_bloc/analysis_options.yaml +++ b/packages/wyatt_authentication_bloc/analysis_options.yaml @@ -1 +1,4 @@ -include: package:wyatt_analysis/analysis_options.flutter.yaml \ No newline at end of file +include: package:wyatt_analysis/analysis_options.flutter.yaml + +analyzer: + exclude: "!example/**" \ No newline at end of file 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 89cd4dc6..dd272666 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,7 +3,7 @@ // ----- // File: sign_in_form.dart // Created Date: 19/08/2022 15:24:37 -// Last Modified: Fri Nov 11 2022 +// Last Modified: Tue Nov 15 2022 // ----- // Copyright (c) 2022 @@ -59,8 +59,24 @@ class _SignInButton extends StatelessWidget { return status.isSubmissionInProgress ? const CircularProgressIndicator() : ElevatedButton( - onPressed: status.isValidated ? () => cubit.submit() : null, - child: const Text('Sign in'), + onPressed: status.isValidated ? () => cubit.signInWithEmailAndPassword() : null, + child: const Text('Sign in with credentials'), + ); + }), + ); + } +} + +class _SignInAnonymouslyButton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return SubmitBuilder>( + builder: ((context, cubit, status) { + return status.isSubmissionInProgress + ? const CircularProgressIndicator() + : ElevatedButton( + onPressed: () => cubit.signInAnonymously(), + child: const Text('Sign in anonymously'), ); }), ); @@ -80,12 +96,15 @@ class SignInForm extends StatelessWidget { ), child: SingleChildScrollView( child: Column( + crossAxisAlignment: CrossAxisAlignment.center, children: [ _EmailInput(), const SizedBox(height: 8), _PasswordInput(), const SizedBox(height: 16), _SignInButton(), + const SizedBox(height: 16), + _SignInAnonymouslyButton(), ], ), ), 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 b8cc1358..911e899a 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 @@ -16,9 +16,7 @@ import 'dart:async'; -import 'package:wyatt_authentication_bloc/src/core/constants/form_field.dart'; -import 'package:wyatt_authentication_bloc/src/core/constants/form_name.dart'; -import 'package:wyatt_authentication_bloc/src/domain/repositories/authentication_repository.dart'; +import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; import 'package:wyatt_type_utils/wyatt_type_utils.dart'; @@ -82,6 +80,41 @@ class SignInCubit extends FormDataCubit { @override FutureOr submit() async { + throw UnimplementedError('`submit()` is not implemented for SignInCubit, ' + 'please use `signInWithEmailAndPassword()` or `signInAnonymously()`'); + } + + @override + FutureOr 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 validate() { + emit( + state.copyWith( + status: _formRepository.accessForm(formName).validate(), + ), + ); + } + + FutureOr signInWithEmailAndPassword() async { + if (state.status.isSubmissionInProgress) { + return; + } + if (!state.status.isValidated) { return; } @@ -117,28 +150,22 @@ class SignInCubit extends FormDataCubit { ); } - @override - FutureOr 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); + FutureOr signInAnonymously() async { + if (state.status.isSubmissionInProgress) { + return; + } + + emit(state.copyWith(status: FormStatus.submissionInProgress)); + + final uid = await _authenticationRepository.signInAnonymously(); emit( - state.copyWith( - form: newForm, - status: newForm.validate(), - ), - ); - } - - @override - FutureOr validate() { - emit( - state.copyWith( - status: _formRepository.accessForm(formName).validate(), + uid.fold( + (value) => state.copyWith(status: FormStatus.submissionSuccess), + (error) => state.copyWith( + errorMessage: error.message, + status: FormStatus.submissionFailure, + ), ), ); } diff --git a/packages/wyatt_authentication_bloc/test/sign_in/sign_in_cubit_test.dart b/packages/wyatt_authentication_bloc/test/sign_in/sign_in_cubit_test.dart index 72b2834a..d45316ff 100644 --- a/packages/wyatt_authentication_bloc/test/sign_in/sign_in_cubit_test.dart +++ b/packages/wyatt_authentication_bloc/test/sign_in/sign_in_cubit_test.dart @@ -65,6 +65,10 @@ void main() { ), ).thenAnswer((_) async => Ok(account)); + when( + () => authenticationRepository.signInAnonymously(), + ).thenAnswer((_) async => Ok(account)); + when( () => authenticationRepository.formRepository, ).thenAnswer((_) => formRepository); @@ -258,13 +262,38 @@ void main() { ); }); - group('submit', () { + group('signInWithEmailAndPassword()', () { blocTest, SignInState>( 'does nothing when status is not validated', build: () => SignInCubit( authenticationRepository: authenticationRepository, ), - act: (cubit) => cubit.submit(), + act: (cubit) => cubit.signInWithEmailAndPassword(), + expect: () => const [], + ); + + blocTest, SignInState>( + 'does nothing when status is submitInProgress', + build: () => SignInCubit( + authenticationRepository: authenticationRepository, + ), + seed: () => SignInState( + form: WyattFormImpl( + [ + FormInput( + AuthFormField.email, + const Email.dirty(validEmailString), + ), + FormInput( + AuthFormField.password, + const Password.dirty(validPasswordString), + ) + ], + name: AuthFormName.signInForm, + ), + status: FormStatus.submissionInProgress, + ), + act: (cubit) => cubit.signInWithEmailAndPassword(), expect: () => const [], ); @@ -308,7 +337,7 @@ void main() { ), status: FormStatus.valid, ), - act: (cubit) => cubit.submit(), + act: (cubit) => cubit.signInWithEmailAndPassword(), verify: (_) { verify( () => authenticationRepository.signInWithEmailAndPassword( @@ -360,7 +389,7 @@ void main() { ), status: FormStatus.valid, ), - act: (cubit) => cubit.submit(), + act: (cubit) => cubit.signInWithEmailAndPassword(), expect: () => [ SignInState( form: WyattFormImpl( @@ -444,7 +473,170 @@ void main() { ), status: FormStatus.valid, ), - act: (cubit) => cubit.submit(), + act: (cubit) => cubit.signInWithEmailAndPassword(), + expect: () => [ + SignInState( + form: WyattFormImpl( + [ + FormInput( + AuthFormField.email, + const Email.dirty(validEmailString), + ), + FormInput( + AuthFormField.password, + const Password.dirty(validPasswordString), + ) + ], + name: AuthFormName.signInForm, + ), + status: FormStatus.submissionInProgress, + ), + SignInState( + form: WyattFormImpl( + [ + FormInput( + AuthFormField.email, + const Email.dirty(validEmailString), + ), + FormInput( + AuthFormField.password, + const Password.dirty(validPasswordString), + ) + ], + name: AuthFormName.signInForm, + ), + status: FormStatus.submissionFailure, + ) + ], + ); + }); + + group('signInAnonymously()', () { + blocTest, SignInState>( + 'does nothing when status is submitInProgress', + build: () => SignInCubit( + authenticationRepository: authenticationRepository, + ), + seed: () => SignInState( + form: WyattFormImpl( + [ + FormInput( + AuthFormField.email, + const Email.dirty(validEmailString), + ), + FormInput( + AuthFormField.password, + const Password.dirty(validPasswordString), + ) + ], + name: AuthFormName.signInForm, + ), + status: FormStatus.submissionInProgress, + ), + act: (cubit) => cubit.signInAnonymously(), + expect: () => const [], + ); + + blocTest, SignInState>( + 'calls signInAnonymously', + build: () => SignInCubit( + authenticationRepository: authenticationRepository, + ), + act: (cubit) => cubit.signInAnonymously(), + verify: (_) { + verify( + () => authenticationRepository.signInAnonymously(), + ).called(1); + }, + ); + + blocTest, SignInState>( + 'emits [submissionInProgress, submissionSuccess] ' + 'when signInAnonymously succeeds', + build: () => SignInCubit( + authenticationRepository: authenticationRepository, + ), + seed: () => SignInState( + form: WyattFormImpl( + [ + FormInput( + AuthFormField.email, + const Email.dirty(validEmailString), + ), + FormInput( + AuthFormField.password, + const Password.dirty(validPasswordString), + ) + ], + name: AuthFormName.signInForm, + ), + status: FormStatus.valid, + ), + act: (cubit) => cubit.signInAnonymously(), + expect: () => [ + SignInState( + form: WyattFormImpl( + [ + FormInput( + AuthFormField.email, + const Email.dirty(validEmailString), + ), + FormInput( + AuthFormField.password, + const Password.dirty(validPasswordString), + ) + ], + name: AuthFormName.signInForm, + ), + status: FormStatus.submissionInProgress, + ), + SignInState( + form: WyattFormImpl( + [ + FormInput( + AuthFormField.email, + const Email.dirty(validEmailString), + ), + FormInput( + AuthFormField.password, + const Password.dirty(validPasswordString), + ) + ], + name: AuthFormName.signInForm, + ), + status: FormStatus.submissionSuccess, + ) + ], + ); + + blocTest, SignInState>( + 'emits [submissionInProgress, submissionFailure] ' + 'when signInAnonymously fails', + setUp: () { + when( + () => authenticationRepository.signInAnonymously(), + ).thenAnswer((_) async => Err(ServerException())); + }, + build: () => SignInCubit( + authenticationRepository: authenticationRepository, + ), + seed: () => SignInState( + form: WyattFormImpl( + [ + FormInput( + AuthFormField.email, + const Email.dirty(validEmailString), + ), + FormInput( + AuthFormField.password, + const Password.dirty(validPasswordString), + ) + ], + name: AuthFormName.signInForm, + ), + status: FormStatus.valid, + ), + act: (cubit) => cubit.signInAnonymously(), expect: () => [ SignInState( form: WyattFormImpl(