master #81

Closed
malo wants to merge 322 commits from master into feat/bloc_layout/new-package
19 changed files with 331 additions and 424 deletions
Showing only changes of commit 3faceeebb6 - Show all commits

View File

@ -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(),
), ),
), ),
]),
]; ];
} }

View File

@ -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;
}, },
); );

View File

@ -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);
}
} }

View File

@ -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'),
),
],
), ),
), ),
); );

View File

@ -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);
} }
} }

View File

@ -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.

View File

@ -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
} }

View File

@ -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,

View File

@ -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();
} }
} }

View File

@ -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();
} }
} }

View File

@ -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();
} }
} }

View File

@ -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>,

View File

@ -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,

View File

@ -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);

View File

@ -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,

View File

@ -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();
} }
} }

View File

@ -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});

View File

@ -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,

View File

@ -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);