master #81

Closed
malo wants to merge 322 commits from master into feat/bloc_layout/new-package
4 changed files with 161 additions and 88 deletions
Showing only changes of commit adcd98f47b - Show all commits

View File

@ -16,7 +16,6 @@
import 'dart:async';
import 'package:rxdart/rxdart.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_name.dart';
@ -25,37 +24,25 @@ import 'package:wyatt_authentication_bloc/src/domain/data_sources/local/authenti
import 'package:wyatt_authentication_bloc/src/domain/data_sources/remote/authentication_remote_data_source.dart';
import 'package:wyatt_authentication_bloc/src/domain/entities/account.dart';
import 'package:wyatt_authentication_bloc/src/domain/entities/account_wrapper.dart';
import 'package:wyatt_authentication_bloc/src/domain/entities/auth_change_event.dart';
import 'package:wyatt_authentication_bloc/src/domain/repositories/authentication_repository.dart';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
typedef OnSignUpSuccess<T> = FutureOrResult<T?> Function(
AuthenticationRepository<T> repo,
Account? account,
WyattForm form,
);
typedef OnAuthChange<T> = FutureOrResult<T?> Function(
AuthenticationRepository<T> repo,
Account? account,
);
class AuthenticationRepositoryImpl<T extends Object>
extends AuthenticationRepository<T> {
AuthenticationRepositoryImpl({
required AuthenticationCacheDataSource<T> authenticationCacheDataSource,
required AuthenticationRemoteDataSource authenticationRemoteDataSource,
required this.authenticationCacheDataSource,
required this.authenticationRemoteDataSource,
FormRepository? formRepository,
// ignore: strict_raw_type
List<FormInput>? extraSignUpInputs,
FormInputValidator<String?, ValidationError>? customEmailValidator,
FormInputValidator<String?, ValidationError>? customPasswordValidator,
OnSignUpSuccess<T>? onSignUpSuccess,
OnAuthChange<T>? onAuthChange,
}) : _authenticationLocalDataSource = authenticationCacheDataSource,
_authenticationRemoteDataSource = authenticationRemoteDataSource,
_onSignUpSuccess = onSignUpSuccess,
_onAccountChanges = onAuthChange {
AuthChangeListener<T>? onAuthChange,
AccountStreamTransformer<T>? accountStreamTransformer,
}) : _authChangeListener = onAuthChange,
_accountStreamTransformer = accountStreamTransformer {
_formRepository = formRepository ?? FormRepositoryImpl();
if (formRepository != null) {
return;
@ -103,23 +90,34 @@ class AuthenticationRepositoryImpl<T extends Object>
name: AuthFormName.passwordResetForm,
),
);
_accountStreamTransformer ??= (input) => input
.where((event) => event is! SignUpAuthChangeEvent)
.map<FutureOrResult<AccountWrapper<T>>>((event) async {
if (listener == null) {
return Ok(AccountWrapperModel<T>(event.account, null));
}
// Handle sign in, sign out and refresh
final dataResult = await listener!.call(this, event);
return dataResult.map((data) {
authenticationCacheDataSource.storeData(data);
return AccountWrapperModel(event.account, data);
});
});
}
final AuthenticationCacheDataSource<T> _authenticationLocalDataSource;
final AuthenticationRemoteDataSource _authenticationRemoteDataSource;
final AuthenticationCacheDataSource<T> authenticationCacheDataSource;
final AuthenticationRemoteDataSource authenticationRemoteDataSource;
late FormRepository _formRepository;
final OnSignUpSuccess<T>? _onSignUpSuccess;
final OnAuthChange<T>? _onAccountChanges;
final StreamController<FutureOrResult<AccountWrapper<T>>> _signUpStream =
StreamController();
bool _pause = false;
AuthChangeListener<T>? _authChangeListener;
AccountStreamTransformer<T>? _accountStreamTransformer;
@override
FormRepository get formRepository => _formRepository;
@override
AuthChangeListener<T>? get listener => _authChangeListener;
@override
FutureOrResult<Account> signInWithEmailAndPassword({
required String email,
@ -128,11 +126,11 @@ class AuthenticationRepositoryImpl<T extends Object>
Result.tryCatchAsync<Account, AppException, AppException>(
() async {
final account =
await _authenticationRemoteDataSource.signInWithEmailAndPassword(
await authenticationRemoteDataSource.signInWithEmailAndPassword(
email: email,
password: password,
);
await _authenticationLocalDataSource.storeAccount(account);
await authenticationCacheDataSource.storeAccount(account);
return account;
},
(error) => error,
@ -142,8 +140,8 @@ class AuthenticationRepositoryImpl<T extends Object>
FutureOrResult<void> signOut() =>
Result.tryCatchAsync<void, AppException, AppException>(
() async {
await _authenticationRemoteDataSource.signOut();
await _authenticationLocalDataSource.destroy();
await authenticationRemoteDataSource.signOut();
await authenticationCacheDataSource.destroy();
},
(error) => error,
);
@ -155,54 +153,34 @@ class AuthenticationRepositoryImpl<T extends Object>
}) =>
Result.tryCatchAsync<Account, AppException, AppException>(
() async {
_pause = true;
final account = await _authenticationRemoteDataSource.signUp(
final account = await authenticationRemoteDataSource.signUp(
email: email,
password: password,
);
await _authenticationLocalDataSource.storeAccount(account);
if (_onSignUpSuccess.isNotNull) {
final dataResult = await _onSignUpSuccess!.call(
this,
account,
_formRepository.accessForm(AuthFormName.signUpForm).clone(),
);
await dataResult.foldAsync(
(data) async {
await _authenticationLocalDataSource.storeData(data);
_signUpStream
.add(Future.value(Ok(AccountWrapperModel(account, data))));
},
(error) async => error,
);
}
_pause = false;
await authenticationCacheDataSource.storeAccount(account);
return account;
},
(error) {
_pause = false;
return error;
},
(error) => error,
);
@override
FutureOrResult<void> destroyCache() =>
Result.tryCatchAsync<void, AppException, AppException>(
_authenticationLocalDataSource.destroy,
authenticationCacheDataSource.destroy,
(error) => error,
);
@override
FutureOrResult<AccountWrapper<T>> getCache() =>
Result.tryCatchAsync<AccountWrapper<T>, AppException, AppException>(
_authenticationLocalDataSource.load,
authenticationCacheDataSource.load,
(error) => error,
);
@override
FutureOrResult<Account> getAccount() =>
Result.tryCatchAsync<Account, AppException, AppException>(
_authenticationLocalDataSource.loadAccount,
authenticationCacheDataSource.loadAccount,
(error) => error,
);
@ -212,7 +190,7 @@ class AuthenticationRepositoryImpl<T extends Object>
) =>
Result.tryCatchAsync<void, AppException, AppException>(
() async {
await _authenticationLocalDataSource.storeAccount(account);
await authenticationCacheDataSource.storeAccount(account);
},
(error) => error,
);
@ -220,7 +198,7 @@ class AuthenticationRepositoryImpl<T extends Object>
@override
FutureOrResult<T> getData() =>
Result.tryCatchAsync<T, AppException, AppException>(
_authenticationLocalDataSource.loadData,
authenticationCacheDataSource.loadData,
(error) => error,
);
@ -230,7 +208,7 @@ class AuthenticationRepositoryImpl<T extends Object>
) =>
Result.tryCatchAsync<void, AppException, AppException>(
() async {
await _authenticationLocalDataSource.storeData(data);
await authenticationCacheDataSource.storeData(data);
},
(error) => error,
);
@ -238,26 +216,13 @@ class AuthenticationRepositoryImpl<T extends Object>
@override
FutureOrResult<String> getIdentityToken() =>
Result.tryCatchAsync<String, AppException, AppException>(
_authenticationRemoteDataSource.getIdentityToken,
authenticationRemoteDataSource.getIdentityToken,
(error) => error,
);
@override
Stream<FutureOrResult<AccountWrapper<T>>> streamAccount() => MergeStream([
_signUpStream.stream.asBroadcastStream(),
_authenticationRemoteDataSource.streamAccount().map((account) async {
if (_onAccountChanges.isNotNull && !_pause) {
final dataResult = await _onAccountChanges!.call(this, account);
return dataResult.map((data) {
_authenticationLocalDataSource.storeData(data);
return AccountWrapperModel(account, data);
});
}
return Ok<AccountWrapperModel<T>, AppException>(
AccountWrapperModel<T>(account, null),
);
})
]);
Stream<FutureOrResult<AccountWrapper<T>>> streamAccount() =>
_accountStreamTransformer!.call(changes());
@override
FutureOrResult<void> confirmPasswordReset({
@ -266,7 +231,7 @@ class AuthenticationRepositoryImpl<T extends Object>
}) =>
Result.tryCatchAsync<void, AppException, AppException>(
() async {
await _authenticationRemoteDataSource.confirmPasswordReset(
await authenticationRemoteDataSource.confirmPasswordReset(
code: code,
newPassword: newPassword,
);
@ -278,7 +243,7 @@ class AuthenticationRepositoryImpl<T extends Object>
FutureOrResult<void> sendEmailVerification() =>
Result.tryCatchAsync<void, AppException, AppException>(
() async {
await _authenticationRemoteDataSource.sendEmailVerification();
await authenticationRemoteDataSource.sendEmailVerification();
},
(error) => error,
);
@ -287,7 +252,7 @@ class AuthenticationRepositoryImpl<T extends Object>
FutureOrResult<void> sendPasswordResetEmail({required String email}) =>
Result.tryCatchAsync<void, AppException, AppException>(
() async {
await _authenticationRemoteDataSource.sendPasswordResetEmail(
await authenticationRemoteDataSource.sendPasswordResetEmail(
email: email,
);
},
@ -299,7 +264,7 @@ class AuthenticationRepositoryImpl<T extends Object>
Result.tryCatchAsync<Account, AppException, AppException>(
() async {
final account =
await _authenticationRemoteDataSource.signInAnonymously();
await authenticationRemoteDataSource.signInAnonymously();
return account;
},
(error) => error,
@ -310,7 +275,7 @@ class AuthenticationRepositoryImpl<T extends Object>
Result.tryCatchAsync<Account, AppException, AppException>(
() async {
final account =
await _authenticationRemoteDataSource.signInWithGoogle();
await authenticationRemoteDataSource.signInWithGoogle();
return account;
},
(error) => error,
@ -320,7 +285,7 @@ class AuthenticationRepositoryImpl<T extends Object>
FutureOrResult<bool> verifyPasswordResetCode({required String code}) =>
Result.tryCatchAsync<bool, AppException, AppException>(
() async {
final response = await _authenticationRemoteDataSource
final response = await authenticationRemoteDataSource
.verifyPasswordResetCode(code: code);
return response;
},
@ -331,7 +296,7 @@ class AuthenticationRepositoryImpl<T extends Object>
FutureOrResult<void> refresh() =>
Result.tryCatchAsync<void, AppException, AppException>(
() async {
await _authenticationRemoteDataSource.refresh();
await authenticationRemoteDataSource.refresh();
},
(error) => error,
);
@ -340,7 +305,7 @@ class AuthenticationRepositoryImpl<T extends Object>
FutureOrResult<Account> reauthenticateWithCredential() =>
Result.tryCatchAsync<Account, AppException, AppException>(
() async {
final account = await _authenticationRemoteDataSource
final account = await authenticationRemoteDataSource
.reauthenticateWithCredential();
return account;
},
@ -352,7 +317,7 @@ class AuthenticationRepositoryImpl<T extends Object>
Result.tryCatchAsync<Account, AppException, AppException>(
() async {
final account =
await _authenticationRemoteDataSource.updateEmail(email: email);
await authenticationRemoteDataSource.updateEmail(email: email);
return account;
},
(error) => error,
@ -362,11 +327,52 @@ class AuthenticationRepositoryImpl<T extends Object>
FutureOrResult<Account> updatePassword({required String password}) =>
Result.tryCatchAsync<Account, AppException, AppException>(
() async {
final account = await _authenticationRemoteDataSource.updatePassword(
final account = await authenticationRemoteDataSource.updatePassword(
password: password,
);
return account;
},
(error) => error,
);
Account? _lastChange;
@override
Stream<AuthChangeEvent> changes() => authenticationRemoteDataSource
.streamAccount()
.map<AuthChangeEvent>((account) {
if (_lastChange != null && account == null) {
_lastChange = null;
return SignOutAuthChangeEvent();
}
if (_lastChange == null && account != null) {
_lastChange = account;
if (account.isNewUser ?? false) {
return SignUpAuthChangeEvent(account);
}
return SignInAuthChangeEvent(account);
}
if (_lastChange != null && account != null) {
_lastChange = account;
return RefreshAuthChangeEvent(account);
}
if (_lastChange == null && account == null) {
_lastChange = account;
return StillUnauthenticatedAuthChangeEvent();
}
_lastChange = account;
return RefreshAuthChangeEvent(account);
});
@override
FutureOrResult<void> addAuthChangeListener(AuthChangeListener<T> listener) {
_authChangeListener = listener;
return const Ok(null);
}
@override
FutureOrResult<void> removeAuthChangeListener() {
_authChangeListener = null;
return const Ok(null);
}
}

View File

@ -0,0 +1,43 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_authentication_bloc/src/domain/entities/account.dart';
abstract class AuthChangeEvent {
AuthChangeEvent(this.account);
final Account? account;
}
class SignInAuthChangeEvent extends AuthChangeEvent {
SignInAuthChangeEvent(super.account);
}
class SignUpAuthChangeEvent extends AuthChangeEvent {
SignUpAuthChangeEvent(super.account);
}
class RefreshAuthChangeEvent extends AuthChangeEvent {
RefreshAuthChangeEvent(super.account);
}
class StillUnauthenticatedAuthChangeEvent extends AuthChangeEvent {
StillUnauthenticatedAuthChangeEvent() : super(null);
}
class SignOutAuthChangeEvent extends AuthChangeEvent {
SignOutAuthChangeEvent() : super(null);
}

View File

@ -16,3 +16,4 @@
export 'account.dart';
export 'account_wrapper.dart';
export 'auth_change_event.dart';

View File

@ -17,10 +17,28 @@
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_authentication_bloc/src/domain/entities/account.dart';
import 'package:wyatt_authentication_bloc/src/domain/entities/account_wrapper.dart';
import 'package:wyatt_authentication_bloc/src/domain/entities/auth_change_event.dart';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
typedef SignUpCallback<T> = FutureOrResult<T?> Function(
AuthenticationRepository<T> repo,
Account? account,
WyattForm form,
);
typedef AuthChangeListener<T> = FutureOrResult<T?> Function(
AuthenticationRepository<T> repo,
AuthChangeEvent? authEvent,
);
typedef AccountStreamTransformer<T extends Object>
= Stream<FutureOrResult<AccountWrapper<T>>> Function(
Stream<AuthChangeEvent> input,
);
abstract class AuthenticationRepository<T> extends BaseRepository {
FormRepository get formRepository;
AuthChangeListener<T>? get listener;
/// {@template signup}
/// Creates a new user with the provided [email] and [password].
@ -141,6 +159,8 @@ abstract class AuthenticationRepository<T> extends BaseRepository {
/// {@endtemplate}
Stream<FutureOrResult<AccountWrapper<T>>> streamAccount();
Stream<AuthChangeEvent> changes();
FutureOrResult<String> getIdentityToken();
FutureOrResult<Account> getAccount();
@ -151,4 +171,7 @@ abstract class AuthenticationRepository<T> extends BaseRepository {
FutureOrResult<AccountWrapper<T>> getCache();
FutureOrResult<void> destroyCache();
FutureOrResult<void> addAuthChangeListener(AuthChangeListener<T> listener);
FutureOrResult<void> removeAuthChangeListener();
}