master #81
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -16,3 +16,4 @@
|
||||
|
||||
export 'account.dart';
|
||||
export 'account_wrapper.dart';
|
||||
export 'auth_change_event.dart';
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user