feat(authentication): remove session wrapper for AuthenticationSession
This commit is contained in:
parent
2e5aafdb5a
commit
b458f8c0c8
@ -37,7 +37,7 @@ class ExampleAuthenticationCubit extends AuthenticationCubit<int> {
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOrResult<int?> onSignInFromCache(SessionWrapper<int> wrapper) {
|
||||
FutureOrResult<int?> onSignInFromCache(AuthenticationSession<int> session) {
|
||||
print('onSignInFromCache');
|
||||
|
||||
return const Ok(1);
|
||||
|
@ -30,8 +30,7 @@ class HomePage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
'Home | ${context.account<AuthenticationCubit<int>, int>()?.email}'),
|
||||
title: Text('Home | ${context.watchAccount<int>()?.email}'),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () =>
|
||||
@ -44,8 +43,8 @@ class HomePage extends StatelessWidget {
|
||||
child: ListView(
|
||||
children: [
|
||||
AuthenticationBuilder<int>(
|
||||
authenticated: (context, sessionWrapper) => Text(
|
||||
'Logged as ${sessionWrapper.session?.account.email} | GeneratedId is ${sessionWrapper.session?.data}'),
|
||||
authenticated: (context, session) => Text(
|
||||
'Logged as ${session.account?.email} | GeneratedId is ${session.data}'),
|
||||
unauthenticated: (context) =>
|
||||
const Text('Not logged (unauthenticated)'),
|
||||
unknown: (context) => const Text('Not logged (unknown)'),
|
||||
|
@ -17,26 +17,82 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/account.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/session.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/session_wrapper.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/auth_session.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/authentication_change_event/authentication_change_event.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/features/authentication/cubit/authentication_cubit.dart';
|
||||
|
||||
/// Extension that helps to quickly access useful resources like wrapper,
|
||||
/// session, account or data.
|
||||
extension BuildContextExtension on BuildContext {
|
||||
/// Returns session wrapper
|
||||
SessionWrapper<Data>? wrapper<T extends AuthenticationCubit<Data>, Data>() =>
|
||||
watch<T>().currentSession();
|
||||
/// Read session in context from a specific AuthenticationCubit type [T]
|
||||
AuthenticationSession<Data>?
|
||||
readSessionFrom<T extends AuthenticationCubit<Data>, Data>() =>
|
||||
read<T>().currentSession();
|
||||
|
||||
/// Returns session
|
||||
Session<Data>? session<T extends AuthenticationCubit<Data>, Data>() =>
|
||||
watch<T>().currentSession()?.session;
|
||||
/// Watch session in context from a specific AuthenticationCubit type [T]
|
||||
AuthenticationSession<Data>?
|
||||
watchSessionFrom<T extends AuthenticationCubit<Data>, Data>() =>
|
||||
watch<T>().currentSession();
|
||||
|
||||
/// Returns account
|
||||
Account? account<T extends AuthenticationCubit<Data>, Data>() =>
|
||||
watch<T>().currentSession()?.session?.account;
|
||||
/// Read session in context from generic AuthenticationCubit type
|
||||
AuthenticationSession<Data>? readSession<Data>() =>
|
||||
read<AuthenticationCubit<Data>>().currentSession();
|
||||
|
||||
/// Returns associated data
|
||||
Data? data<T extends AuthenticationCubit<Data>, Data>() =>
|
||||
watch<T>().currentSession()?.session?.data;
|
||||
/// Watch session in context from generic AuthenticationCubit type
|
||||
AuthenticationSession<Data>? watchSession<Data>() =>
|
||||
watch<AuthenticationCubit<Data>>().currentSession();
|
||||
|
||||
/// Read event in context from a specific AuthenticationCubit type [T]
|
||||
AuthenticationChangeEvent?
|
||||
readEventFrom<T extends AuthenticationCubit<Data>, Data>() =>
|
||||
readSessionFrom<T, Data>()?.latestEvent;
|
||||
|
||||
/// Watch event in context from a specific AuthenticationCubit type [T]
|
||||
AuthenticationChangeEvent?
|
||||
watchEventFrom<T extends AuthenticationCubit<Data>, Data>() =>
|
||||
watchSessionFrom<T, Data>()?.latestEvent;
|
||||
|
||||
/// Read event in context from generic AuthenticationCubit type
|
||||
AuthenticationChangeEvent? readEvent<Data>() =>
|
||||
readSession<Data>()?.latestEvent;
|
||||
|
||||
/// Watch event in context from generic AuthenticationCubit type
|
||||
AuthenticationChangeEvent? watchEvent<Data>() =>
|
||||
watchSession<Data>()?.latestEvent;
|
||||
|
||||
/// Read account in context from a specific AuthenticationCubit type [T]
|
||||
Account? readAccountFrom<T extends AuthenticationCubit<Data>, Data>() =>
|
||||
readSessionFrom<T, Data>()?.account;
|
||||
|
||||
/// Watch account in context from a specific AuthenticationCubit type [T]
|
||||
Account? watchAccountFrom<T extends AuthenticationCubit<Data>, Data>() =>
|
||||
watchSessionFrom<T, Data>()?.account;
|
||||
|
||||
/// Read account in context from generic AuthenticationCubit type
|
||||
Account? readAccount<Data>() => readSession<Data>()?.account;
|
||||
|
||||
/// Watch account in context from generic AuthenticationCubit type
|
||||
Account? watchAccount<Data>() => watchSession<Data>()?.account;
|
||||
|
||||
/// Read data in context from a specific AuthenticationCubit type [T]
|
||||
Data? readDataFrom<T extends AuthenticationCubit<Data>, Data>() =>
|
||||
readSessionFrom<T, Data>()?.data;
|
||||
|
||||
/// Watch data in context from a specific AuthenticationCubit type [T]
|
||||
Data? watchDataFrom<T extends AuthenticationCubit<Data>, Data>() =>
|
||||
watchSessionFrom<T, Data>()?.data;
|
||||
|
||||
/// Read data in context from generic AuthenticationCubit type
|
||||
Data? readData<Data>() => readSession<Data>()?.data;
|
||||
|
||||
/// Watch data in context from generic AuthenticationCubit type
|
||||
Data? watchData<Data>() => watchSession<Data>()?.data;
|
||||
|
||||
/// Check if user is authenticated from a
|
||||
/// specific AuthenticationCubit type [T]
|
||||
bool isAuthenticatedFrom<T extends AuthenticationCubit<Data>, Data>() =>
|
||||
readEventFrom<T, Data>() is AuthenticatedChangeEvent;
|
||||
|
||||
/// Check if user is authenticated from generic AuthenticationCubit type
|
||||
bool isAuthenticated<Data>() => readEvent<Data>() is AuthenticatedChangeEvent;
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ import 'package:wyatt_authentication_bloc/src/core/exceptions/exceptions.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/data/models/models.dart';
|
||||
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/auth_session.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/authentication_change_event/authentication_change_event.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/session_wrapper.dart';
|
||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
|
||||
class AuthenticationFirebaseDataSourceImpl<Data>
|
||||
@ -41,7 +41,7 @@ class AuthenticationFirebaseDataSourceImpl<Data>
|
||||
_checkForCachedAccount();
|
||||
}
|
||||
|
||||
late StreamController<SessionWrapper<Data>> _sessionStream;
|
||||
late StreamController<AuthenticationSession<Data>> _sessionStream;
|
||||
late StreamController<UserCredential?> _latestCredentials;
|
||||
|
||||
final FirebaseAuth _firebaseAuth;
|
||||
@ -51,8 +51,11 @@ class AuthenticationFirebaseDataSourceImpl<Data>
|
||||
final currentUser = _firebaseAuth.currentUser;
|
||||
|
||||
if (currentUser == null) {
|
||||
_sessionStream
|
||||
.add(const SessionWrapper(event: UnknownAuthenticationEvent()));
|
||||
_sessionStream.add(
|
||||
const AuthenticationSession(
|
||||
latestEvent: UnknownAuthenticationEvent(),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -62,7 +65,9 @@ class AuthenticationFirebaseDataSourceImpl<Data>
|
||||
accessToken: jwt,
|
||||
);
|
||||
_sessionStream.add(
|
||||
SessionWrapper(event: SignedInFromCacheEvent(account: currentAccount)),
|
||||
AuthenticationSession.fromEvent(
|
||||
SignedInFromCacheEvent(account: currentAccount),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -78,19 +83,23 @@ class AuthenticationFirebaseDataSourceImpl<Data>
|
||||
return account;
|
||||
}
|
||||
|
||||
// Stream related methods ===================================================
|
||||
// Session related methods ===================================================
|
||||
|
||||
/// {@macro add_session}
|
||||
@override
|
||||
void addSession(SessionWrapper<Data> wrapper) {
|
||||
_sessionStream.add(wrapper);
|
||||
void addSession(AuthenticationSession<Data> session) {
|
||||
_sessionStream.add(session);
|
||||
}
|
||||
|
||||
/// {@macro session_stream}
|
||||
@override
|
||||
Stream<SessionWrapper<Data>> sessionStream() =>
|
||||
Stream<AuthenticationSession<Data>> sessionStream() =>
|
||||
_sessionStream.stream.asBroadcastStream();
|
||||
|
||||
/// {@macro current_session}
|
||||
@override
|
||||
Future<AuthenticationSession<Data>> currentSession() => sessionStream().last;
|
||||
|
||||
// SignUp/SignIn methods ====================================================
|
||||
|
||||
/// {@macro signup_pwd}
|
||||
|
@ -18,7 +18,7 @@ import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/core/utils/forms.dart';
|
||||
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/session_wrapper.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/auth_session.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';
|
||||
@ -73,18 +73,30 @@ class AuthenticationRepositoryImpl<Data extends Object>
|
||||
@override
|
||||
FormRepository get formRepository => _formRepository;
|
||||
|
||||
// Stream related methods ===================================================
|
||||
// Session related methods ===================================================
|
||||
|
||||
/// {@macro add_session}
|
||||
@override
|
||||
void addSession(SessionWrapper<Data> wrapper) =>
|
||||
authenticationRemoteDataSource.addSession(wrapper);
|
||||
void addSession(AuthenticationSession<Data> session) =>
|
||||
authenticationRemoteDataSource.addSession(session);
|
||||
|
||||
/// {@macro session_stream}
|
||||
@override
|
||||
Stream<SessionWrapper<Data>> sessionStream() =>
|
||||
Stream<AuthenticationSession<Data>> sessionStream() =>
|
||||
authenticationRemoteDataSource.sessionStream();
|
||||
|
||||
/// {@macro current_session}
|
||||
@override
|
||||
FutureOrResult<AuthenticationSession<Data>> currentSession() =>
|
||||
Result.tryCatchAsync<AuthenticationSession<Data>, AppException,
|
||||
AppException>(
|
||||
() async {
|
||||
final session = await authenticationRemoteDataSource.currentSession();
|
||||
return session;
|
||||
},
|
||||
(error) => error,
|
||||
);
|
||||
|
||||
// SignUp/SignIn methods ====================================================
|
||||
|
||||
/// {@macro signup_pwd}
|
||||
|
@ -16,16 +16,16 @@
|
||||
|
||||
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/session_wrapper.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/auth_session.dart';
|
||||
|
||||
/// Is responsible for abstracting the provenance of the data.
|
||||
abstract class AuthenticationRemoteDataSource<Data>
|
||||
extends BaseRemoteDataSource {
|
||||
// Stream related methods ===================================================
|
||||
// Session related methods ===================================================
|
||||
|
||||
void addSession(SessionWrapper<Data> wrapper);
|
||||
|
||||
Stream<SessionWrapper<Data>> sessionStream();
|
||||
void addSession(AuthenticationSession<Data> session);
|
||||
Stream<AuthenticationSession<Data>> sessionStream();
|
||||
Future<AuthenticationSession<Data>> currentSession();
|
||||
|
||||
// SignUp/SignIn methods ====================================================
|
||||
|
||||
|
@ -0,0 +1,60 @@
|
||||
// Copyright (C) 2023 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:equatable/equatable.dart';
|
||||
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
|
||||
|
||||
/// The [AuthenticationSession] object is used to transport and propagate
|
||||
/// the last event issued by an authentication state change, a user account
|
||||
/// if connected, and the associated data.
|
||||
class AuthenticationSession<Data> extends Equatable {
|
||||
const AuthenticationSession({
|
||||
required this.latestEvent,
|
||||
this.account,
|
||||
this.data,
|
||||
});
|
||||
|
||||
factory AuthenticationSession.fromEvent(
|
||||
AuthenticationChangeEvent latestEvent, {
|
||||
Data? data,
|
||||
}) {
|
||||
if (latestEvent is AuthenticatedChangeEvent) {
|
||||
return AuthenticationSession(
|
||||
latestEvent: latestEvent,
|
||||
account: latestEvent.account,
|
||||
data: data,
|
||||
);
|
||||
}
|
||||
return AuthenticationSession(
|
||||
latestEvent: latestEvent,
|
||||
data: data,
|
||||
);
|
||||
}
|
||||
|
||||
final AuthenticationChangeEvent latestEvent;
|
||||
final Account? account;
|
||||
final Data? data;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
latestEvent,
|
||||
account,
|
||||
data,
|
||||
];
|
||||
|
||||
@override
|
||||
bool? get stringify => true;
|
||||
}
|
@ -14,23 +14,15 @@
|
||||
// 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:equatable/equatable.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/account.dart';
|
||||
part of 'authentication_change_event.dart';
|
||||
|
||||
|
||||
/// Represents every event where user is authenticated.
|
||||
abstract class AuthenticatedChangeEvent extends AuthenticationChangeEvent {
|
||||
const AuthenticatedChangeEvent({required this.account});
|
||||
|
||||
/// The [Session] object is used to transport and propagate
|
||||
/// the connected user [Account] and personalized [Data] in the application.
|
||||
class Session<Data> extends Equatable {
|
||||
const Session({
|
||||
required this.account,
|
||||
this.data,
|
||||
});
|
||||
|
||||
final Account account;
|
||||
final Data? data;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account, data];
|
||||
|
||||
@override
|
||||
bool? get stringify => true;
|
||||
List<Object?> get props => [account];
|
||||
}
|
@ -18,15 +18,16 @@ import 'package:equatable/equatable.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/account.dart';
|
||||
|
||||
part 'authenticated_change_event.dart';
|
||||
part 'deleted_event.dart';
|
||||
part 'reauthenticated_event.dart';
|
||||
part 'refreshed_event.dart';
|
||||
part 'signed_in_event.dart';
|
||||
part 'signed_in_from_cache_event.dart';
|
||||
part 'signed_out_event.dart';
|
||||
part 'signed_up_event.dart';
|
||||
part 'refreshed_event.dart';
|
||||
part 'reauthenticated_event.dart';
|
||||
part 'updated_event.dart';
|
||||
part 'unknown_authentication_event.dart';
|
||||
part 'signed_in_from_cache_event.dart';
|
||||
part 'deleted_event.dart';
|
||||
part 'updated_event.dart';
|
||||
|
||||
/// Represents an event initiated by a change in
|
||||
/// the user's authentication status.
|
||||
|
@ -16,14 +16,9 @@
|
||||
|
||||
part of 'authentication_change_event.dart';
|
||||
|
||||
/// When a user re-authenticates (from the logged in state to the
|
||||
/// logged in state with a different and fresh access
|
||||
/// When a user re-authenticates (from the logged in state to the
|
||||
/// logged in state with a different and fresh access
|
||||
/// token and a different login time)
|
||||
class ReauthenticatedEvent extends AuthenticationChangeEvent {
|
||||
const ReauthenticatedEvent({required this.account});
|
||||
|
||||
final Account account;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account];
|
||||
class ReauthenticatedEvent extends AuthenticatedChangeEvent {
|
||||
const ReauthenticatedEvent({required super.account});
|
||||
}
|
||||
|
@ -16,13 +16,8 @@
|
||||
|
||||
part of 'authentication_change_event.dart';
|
||||
|
||||
/// When a user access token is refreshed (from the logged in state to the
|
||||
/// When a user access token is refreshed (from the logged in state to the
|
||||
/// logged in state with a different access token)
|
||||
class RefreshedEvent extends AuthenticationChangeEvent {
|
||||
const RefreshedEvent({required this.account});
|
||||
|
||||
final Account account;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account];
|
||||
class RefreshedEvent extends AuthenticatedChangeEvent {
|
||||
const RefreshedEvent({required super.account});
|
||||
}
|
||||
|
@ -17,11 +17,6 @@
|
||||
part of 'authentication_change_event.dart';
|
||||
|
||||
/// When a user authenticates (from not logged in to logged in).
|
||||
class SignedInEvent extends AuthenticationChangeEvent {
|
||||
const SignedInEvent({required this.account});
|
||||
|
||||
final Account account;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account];
|
||||
class SignedInEvent extends AuthenticatedChangeEvent {
|
||||
const SignedInEvent({required super.account});
|
||||
}
|
||||
|
@ -17,11 +17,6 @@
|
||||
part of 'authentication_change_event.dart';
|
||||
|
||||
/// When a user authenticates automatically (from not logged in to logged in).
|
||||
class SignedInFromCacheEvent extends AuthenticationChangeEvent {
|
||||
const SignedInFromCacheEvent({required this.account});
|
||||
|
||||
final Account account;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account];
|
||||
class SignedInFromCacheEvent extends AuthenticatedChangeEvent {
|
||||
const SignedInFromCacheEvent({required super.account});
|
||||
}
|
||||
|
@ -17,11 +17,6 @@
|
||||
part of 'authentication_change_event.dart';
|
||||
|
||||
/// When a user creates an account.
|
||||
class SignedUpEvent extends AuthenticationChangeEvent {
|
||||
const SignedUpEvent({required this.account});
|
||||
|
||||
final Account account;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account];
|
||||
class SignedUpEvent extends AuthenticatedChangeEvent {
|
||||
const SignedUpEvent({required super.account});
|
||||
}
|
||||
|
@ -17,11 +17,6 @@
|
||||
part of 'authentication_change_event.dart';
|
||||
|
||||
/// When the user's account has been updated.
|
||||
class UpdatedEvent extends AuthenticationChangeEvent {
|
||||
const UpdatedEvent({required this.account});
|
||||
|
||||
final Account account;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [account];
|
||||
class UpdatedEvent extends AuthenticatedChangeEvent {
|
||||
const UpdatedEvent({required super.account});
|
||||
}
|
||||
|
@ -15,6 +15,5 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
export 'account.dart';
|
||||
export 'auth_session.dart';
|
||||
export 'authentication_change_event/authentication_change_event.dart';
|
||||
export 'session.dart';
|
||||
export 'session_wrapper.dart';
|
||||
|
@ -1,38 +0,0 @@
|
||||
// Copyright (C) 2023 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:equatable/equatable.dart';
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/authentication_change_event/authentication_change_event.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/session.dart';
|
||||
|
||||
/// Contains the [AuthenticationChangeEvent] initiating the state
|
||||
/// change and the current [Session].
|
||||
class SessionWrapper<Data> extends Equatable implements Entity {
|
||||
const SessionWrapper({
|
||||
required this.event,
|
||||
this.session,
|
||||
});
|
||||
|
||||
final AuthenticationChangeEvent event;
|
||||
final Session<Data>? session;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [event, session];
|
||||
|
||||
@override
|
||||
bool get stringify => true;
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
|
||||
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/session_wrapper.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/auth_session.dart';
|
||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||
|
||||
abstract class AuthenticationRepository<Data> extends BaseRepository {
|
||||
@ -30,12 +30,17 @@ abstract class AuthenticationRepository<Data> extends BaseRepository {
|
||||
/// {@template add_session}
|
||||
/// Add a new authentication event.
|
||||
/// {@endtemplate}
|
||||
void addSession(SessionWrapper<Data> wrapper);
|
||||
void addSession(AuthenticationSession<Data> session);
|
||||
|
||||
/// {@template session_stream}
|
||||
/// Authentication state change event stream.
|
||||
/// {@endtemplate}
|
||||
Stream<SessionWrapper<Data>> sessionStream();
|
||||
Stream<AuthenticationSession<Data>> sessionStream();
|
||||
|
||||
/// {@template current_session}
|
||||
/// Latest session issued by the session stream.
|
||||
/// {@endtemplate}
|
||||
FutureOrResult<AuthenticationSession<Data>> currentSession();
|
||||
|
||||
// SignUp/SignIn methods ====================================================
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/core/enums/authentication_status.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/session_wrapper.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/domain/entities/auth_session.dart';
|
||||
import 'package:wyatt_authentication_bloc/src/features/authentication/cubit/authentication_cubit.dart';
|
||||
|
||||
class AuthenticationBuilder<Data> extends StatelessWidget {
|
||||
@ -30,7 +30,7 @@ class AuthenticationBuilder<Data> extends StatelessWidget {
|
||||
|
||||
final Widget Function(
|
||||
BuildContext context,
|
||||
SessionWrapper<Data> sessionWrapper,
|
||||
AuthenticationSession<Data> session,
|
||||
) authenticated;
|
||||
final Widget Function(BuildContext context) unauthenticated;
|
||||
final Widget Function(BuildContext context) unknown;
|
||||
@ -40,8 +40,8 @@ class AuthenticationBuilder<Data> extends StatelessWidget {
|
||||
BlocBuilder<AuthenticationCubit<Data>, AuthenticationState<Data>>(
|
||||
builder: (context, state) {
|
||||
if (state.status == AuthenticationStatus.authenticated) {
|
||||
if (state.wrapper != null) {
|
||||
return authenticated(context, state.wrapper!);
|
||||
if (state.session != null) {
|
||||
return authenticated(context, state.session!);
|
||||
} else {
|
||||
return unauthenticated(context);
|
||||
}
|
||||
|
@ -42,35 +42,31 @@ abstract class AuthenticationCubit<Data>
|
||||
}
|
||||
final AuthenticationRepository<Data> authenticationRepository;
|
||||
|
||||
SessionWrapper<Data>? _latestSession;
|
||||
AuthenticationSession<Data>? _latestSession;
|
||||
|
||||
void _listenForAuthenticationChanges() {
|
||||
authenticationRepository.sessionStream().asyncMap((wrapper) async {
|
||||
final event = wrapper.event;
|
||||
authenticationRepository.sessionStream().asyncMap((session) async {
|
||||
final event = session.latestEvent;
|
||||
if (event is SignedInFromCacheEvent) {
|
||||
final customRoutineResult = await onSignInFromCache(wrapper);
|
||||
final customRoutineResult = await onSignInFromCache(session);
|
||||
|
||||
if (customRoutineResult.isOk) {
|
||||
final account = event.account;
|
||||
final sessionData = customRoutineResult.ok;
|
||||
|
||||
final signedInSession = SessionWrapper(
|
||||
event: SignedInFromCacheEvent(account: account),
|
||||
session: Session<Data>(
|
||||
account: account,
|
||||
data: sessionData,
|
||||
),
|
||||
final signedInSession = AuthenticationSession.fromEvent(
|
||||
SignedInFromCacheEvent(account: account),
|
||||
data: sessionData,
|
||||
);
|
||||
|
||||
return signedInSession;
|
||||
}
|
||||
}
|
||||
return wrapper;
|
||||
}).listen((wrapper) async {
|
||||
_latestSession = wrapper;
|
||||
final session = wrapper.session;
|
||||
if (session != null) {
|
||||
emit(AuthenticationState<Data>.authenticated(wrapper));
|
||||
return session;
|
||||
}).listen((session) async {
|
||||
_latestSession = session;
|
||||
if (session.account != null) {
|
||||
emit(AuthenticationState<Data>.authenticated(session));
|
||||
return;
|
||||
}
|
||||
emit(AuthenticationState<Data>.unauthenticated());
|
||||
@ -80,19 +76,16 @@ abstract class AuthenticationCubit<Data>
|
||||
|
||||
/// {@macro refresh}
|
||||
FutureOr<void> refresh() async => CustomRoutine<Account, Data?>(
|
||||
routine: authenticationRepository.refresh,
|
||||
attachedLogic: onRefresh,
|
||||
onError: addError,
|
||||
onSuccess: (result, data) => authenticationRepository.addSession(
|
||||
SessionWrapper(
|
||||
event: RefreshedEvent(account: result),
|
||||
session: Session<Data>(
|
||||
account: result,
|
||||
routine: authenticationRepository.refresh,
|
||||
attachedLogic: onRefresh,
|
||||
onError: addError,
|
||||
onSuccess: (result, data) => authenticationRepository.addSession(
|
||||
AuthenticationSession.fromEvent(
|
||||
RefreshedEvent(account: result),
|
||||
data: data,
|
||||
),
|
||||
),
|
||||
),
|
||||
).call();
|
||||
).call();
|
||||
|
||||
/// {@macro reauthenticate}
|
||||
FutureOr<void> reauthenticate() async => CustomRoutine<Account, Data?>(
|
||||
@ -100,12 +93,9 @@ abstract class AuthenticationCubit<Data>
|
||||
attachedLogic: onReauthenticate,
|
||||
onError: addError,
|
||||
onSuccess: (result, data) => authenticationRepository.addSession(
|
||||
SessionWrapper(
|
||||
event: ReauthenticatedEvent(account: result),
|
||||
session: Session<Data>(
|
||||
account: result,
|
||||
data: data,
|
||||
),
|
||||
AuthenticationSession.fromEvent(
|
||||
ReauthenticatedEvent(account: result),
|
||||
data: data,
|
||||
),
|
||||
),
|
||||
).call();
|
||||
@ -115,8 +105,11 @@ abstract class AuthenticationCubit<Data>
|
||||
routine: authenticationRepository.signOut,
|
||||
attachedLogic: (routineResult) => onSignOut(),
|
||||
onError: addError,
|
||||
onSuccess: (result, data) => authenticationRepository
|
||||
.addSession(SessionWrapper<Data>(event: const SignedOutEvent())),
|
||||
onSuccess: (result, data) => authenticationRepository.addSession(
|
||||
const AuthenticationSession(
|
||||
latestEvent: SignedOutEvent(),
|
||||
),
|
||||
),
|
||||
).call();
|
||||
|
||||
/// {@macro delete}
|
||||
@ -124,20 +117,23 @@ abstract class AuthenticationCubit<Data>
|
||||
routine: authenticationRepository.delete,
|
||||
attachedLogic: (routineResult) => onDelete(),
|
||||
onError: addError,
|
||||
onSuccess: (result, data) => authenticationRepository
|
||||
.addSession(SessionWrapper<Data>(event: const DeletedEvent())),
|
||||
onSuccess: (result, data) => authenticationRepository.addSession(
|
||||
const AuthenticationSession(
|
||||
latestEvent: DeletedEvent(),
|
||||
),
|
||||
),
|
||||
).call();
|
||||
|
||||
/// Returns latest session wrapper.
|
||||
/// Returns latest session.
|
||||
///
|
||||
/// Contains latest event and latest session data (account + extra data)
|
||||
SessionWrapper<Data>? currentSession() => _latestSession;
|
||||
AuthenticationSession<Data>? currentSession() => _latestSession;
|
||||
|
||||
/// This callback is triggered when the user is automaticcaly logged in from
|
||||
/// the cache.
|
||||
///
|
||||
/// For example: when the user is sign in from the Firebase cache.
|
||||
FutureOrResult<Data?> onSignInFromCache(SessionWrapper<Data> wrapper);
|
||||
FutureOrResult<Data?> onSignInFromCache(AuthenticationSession<Data> session);
|
||||
|
||||
/// This callback is triggered when the account is refreshed.
|
||||
///
|
||||
|
@ -17,25 +17,25 @@
|
||||
part of 'authentication_cubit.dart';
|
||||
|
||||
class AuthenticationState<Data> extends Equatable {
|
||||
const AuthenticationState._(this.status, this.wrapper);
|
||||
const AuthenticationState._(this.status, this.session);
|
||||
|
||||
const AuthenticationState.unauthenticated()
|
||||
: this._(AuthenticationStatus.unauthenticated, null);
|
||||
|
||||
const AuthenticationState.authenticated(SessionWrapper<Data> sessionWrapper)
|
||||
const AuthenticationState.authenticated(AuthenticationSession<Data> session)
|
||||
: this._(
|
||||
AuthenticationStatus.authenticated,
|
||||
sessionWrapper,
|
||||
session,
|
||||
);
|
||||
|
||||
const AuthenticationState.unknown()
|
||||
: this._(AuthenticationStatus.unknown, null);
|
||||
|
||||
final AuthenticationStatus status;
|
||||
final SessionWrapper<Data>? wrapper;
|
||||
final AuthenticationSession<Data>? session;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [status, wrapper];
|
||||
List<Object?> get props => [status, session];
|
||||
|
||||
@override
|
||||
bool? get stringify => true;
|
||||
|
@ -111,12 +111,9 @@ mixin UpdateEmail<Data> on BaseEditAccountCubit<Data> {
|
||||
},
|
||||
onSuccess: (account, data) {
|
||||
authenticationRepository.addSession(
|
||||
SessionWrapper(
|
||||
event: UpdatedEvent(account: account),
|
||||
session: Session<Data>(
|
||||
account: account,
|
||||
data: data,
|
||||
),
|
||||
AuthenticationSession.fromEvent(
|
||||
UpdatedEvent(account: account),
|
||||
data: data,
|
||||
),
|
||||
);
|
||||
emit(
|
||||
|
@ -110,12 +110,9 @@ mixin UpdatePassword<Data> on BaseEditAccountCubit<Data> {
|
||||
},
|
||||
onSuccess: (account, data) {
|
||||
authenticationRepository.addSession(
|
||||
SessionWrapper(
|
||||
event: SignedInEvent(account: account),
|
||||
session: Session<Data>(
|
||||
account: account,
|
||||
data: data,
|
||||
),
|
||||
AuthenticationSession.fromEvent(
|
||||
UpdatedEvent(account: account),
|
||||
data: data,
|
||||
),
|
||||
);
|
||||
emit(
|
||||
|
@ -31,12 +31,13 @@ class EmailVerificationCubit<Data> extends Cubit<EmailVerificationState> {
|
||||
final AuthenticationRepository<Data> authenticationRepository;
|
||||
|
||||
FutureOr<void> sendEmailVerification() async {
|
||||
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
||||
emit(const EmailVerificationState(status: FormStatus.submissionInProgress));
|
||||
final response = await authenticationRepository.sendEmailVerification();
|
||||
emit(
|
||||
response.fold(
|
||||
(value) => state.copyWith(status: FormStatus.submissionSuccess),
|
||||
(error) => state.copyWith(
|
||||
(value) =>
|
||||
const EmailVerificationState(status: FormStatus.submissionSuccess),
|
||||
(error) => EmailVerificationState(
|
||||
errorMessage: error.message,
|
||||
status: FormStatus.submissionFailure,
|
||||
),
|
||||
@ -45,7 +46,7 @@ class EmailVerificationCubit<Data> extends Cubit<EmailVerificationState> {
|
||||
}
|
||||
|
||||
FutureOr<void> checkEmailVerification() async {
|
||||
emit(state.copyWith(status: FormStatus.submissionInProgress));
|
||||
emit(const EmailVerificationState(status: FormStatus.submissionInProgress));
|
||||
|
||||
final refresh = await authenticationRepository.refresh();
|
||||
if (refresh.isErr) {
|
||||
@ -59,11 +60,11 @@ class EmailVerificationCubit<Data> extends Cubit<EmailVerificationState> {
|
||||
return;
|
||||
}
|
||||
|
||||
final wrapper = await authenticationRepository.sessionStream().last;
|
||||
final currentAccount = wrapper.session?.account;
|
||||
final session = await authenticationRepository.currentSession();
|
||||
final currentAccount = session.ok?.account;
|
||||
if (currentAccount != null) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
EmailVerificationState(
|
||||
isVerified: currentAccount.emailVerified,
|
||||
status: FormStatus.submissionSuccess,
|
||||
),
|
||||
|
@ -1,4 +1,3 @@
|
||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
// Copyright (C) 2023 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
@ -18,16 +17,16 @@
|
||||
part of 'email_verification_cubit.dart';
|
||||
|
||||
class EmailVerificationState extends Equatable {
|
||||
final FormStatus status;
|
||||
final bool isVerified;
|
||||
final String? errorMessage;
|
||||
|
||||
const EmailVerificationState({
|
||||
this.isVerified = false,
|
||||
this.status = FormStatus.pure,
|
||||
this.errorMessage,
|
||||
});
|
||||
|
||||
final FormStatus status;
|
||||
final bool isVerified;
|
||||
final String? errorMessage;
|
||||
|
||||
EmailVerificationState copyWith({
|
||||
FormStatus? status,
|
||||
bool? isVerified,
|
||||
|
@ -63,12 +63,9 @@ mixin SignInAnonymously<Data> on BaseSignInCubit<Data> {
|
||||
},
|
||||
onSuccess: (account, data) {
|
||||
authenticationRepository.addSession(
|
||||
SessionWrapper(
|
||||
event: SignedInEvent(account: account),
|
||||
session: Session<Data>(
|
||||
account: account,
|
||||
data: data,
|
||||
),
|
||||
AuthenticationSession.fromEvent(
|
||||
SignedInEvent(account: account),
|
||||
data: data,
|
||||
),
|
||||
);
|
||||
emit(
|
||||
|
@ -136,12 +136,9 @@ mixin SignInWithEmailPassword<Data> on BaseSignInCubit<Data> {
|
||||
},
|
||||
onSuccess: (account, data) {
|
||||
authenticationRepository.addSession(
|
||||
SessionWrapper(
|
||||
event: SignedInEvent(account: account),
|
||||
session: Session<Data>(
|
||||
account: account,
|
||||
data: data,
|
||||
),
|
||||
AuthenticationSession.fromEvent(
|
||||
SignedInEvent(account: account),
|
||||
data: data,
|
||||
),
|
||||
);
|
||||
emit(
|
||||
|
@ -62,12 +62,9 @@ mixin SignInWithGoogle<Data> on BaseSignInCubit<Data> {
|
||||
},
|
||||
onSuccess: (account, data) {
|
||||
authenticationRepository.addSession(
|
||||
SessionWrapper(
|
||||
event: SignedInEvent(account: account),
|
||||
session: Session<Data>(
|
||||
account: account,
|
||||
data: data,
|
||||
),
|
||||
AuthenticationSession.fromEvent(
|
||||
SignedInEvent(account: account),
|
||||
data: data,
|
||||
),
|
||||
);
|
||||
emit(
|
||||
|
@ -126,12 +126,9 @@ mixin SignUpWithEmailPassword<Data> on BaseSignUpCubit<Data> {
|
||||
},
|
||||
onSuccess: (account, data) {
|
||||
authenticationRepository.addSession(
|
||||
SessionWrapper(
|
||||
event: SignedUpEvent(account: account),
|
||||
session: Session<Data>(
|
||||
account: account,
|
||||
data: data,
|
||||
),
|
||||
AuthenticationSession.fromEvent(
|
||||
SignedUpEvent(account: account),
|
||||
data: data,
|
||||
),
|
||||
);
|
||||
emit(
|
||||
|
@ -43,7 +43,9 @@ class TestAuthenticationCubit extends AuthenticationCubit<int> {
|
||||
const Ok(null);
|
||||
|
||||
@override
|
||||
FutureOrResult<int?> onSignInFromCache(SessionWrapper<int> wrapper) async =>
|
||||
FutureOrResult<int?> onSignInFromCache(
|
||||
AuthenticationSession<int> session,
|
||||
) async =>
|
||||
const Ok(null);
|
||||
|
||||
@override
|
||||
@ -53,9 +55,10 @@ class TestAuthenticationCubit extends AuthenticationCubit<int> {
|
||||
void main() {
|
||||
group('AuthenticationCubit<T>', () {
|
||||
final MockAccount account = MockAccount();
|
||||
final SessionWrapper<int> wrapper = SessionWrapper(
|
||||
event: const UnknownAuthenticationEvent(),
|
||||
session: Session(account: account, data: 10),
|
||||
final AuthenticationSession<int> session = AuthenticationSession<int>(
|
||||
latestEvent: const UnknownAuthenticationEvent(),
|
||||
account: account,
|
||||
data: 10,
|
||||
);
|
||||
late AuthenticationRepository<int> authenticationRepository;
|
||||
|
||||
@ -80,14 +83,14 @@ void main() {
|
||||
'emits authenticated when stream contains session',
|
||||
setUp: () {
|
||||
when(() => authenticationRepository.sessionStream()).thenAnswer(
|
||||
(_) => Stream.fromIterable([wrapper]),
|
||||
(_) => Stream.fromIterable([session]),
|
||||
);
|
||||
},
|
||||
build: () => TestAuthenticationCubit(
|
||||
authenticationRepository: authenticationRepository,
|
||||
),
|
||||
seed: () => const AuthenticationState.unknown(),
|
||||
expect: () => [AuthenticationState<int>.authenticated(wrapper)],
|
||||
expect: () => [AuthenticationState<int>.authenticated(session)],
|
||||
);
|
||||
|
||||
blocTest<AuthenticationCubit<int>, AuthenticationState<int>>(
|
||||
@ -95,7 +98,8 @@ void main() {
|
||||
setUp: () {
|
||||
when(() => authenticationRepository.sessionStream()).thenAnswer(
|
||||
(_) => Stream.fromIterable(
|
||||
[const SessionWrapper(event: SignedOutEvent())],),
|
||||
[const AuthenticationSession(latestEvent: SignedOutEvent())],
|
||||
),
|
||||
);
|
||||
},
|
||||
build: () => TestAuthenticationCubit(
|
||||
|
@ -27,7 +27,7 @@ void main() {
|
||||
const AuthenticationState<void> state =
|
||||
AuthenticationState.unauthenticated();
|
||||
expect(state.status, AuthenticationStatus.unauthenticated);
|
||||
expect(state.wrapper, null);
|
||||
expect(state.session, null);
|
||||
});
|
||||
});
|
||||
|
||||
@ -36,13 +36,12 @@ void main() {
|
||||
final MockAccount account = MockAccount();
|
||||
final AuthenticationState<void> state =
|
||||
AuthenticationState.authenticated(
|
||||
SessionWrapper<void>(
|
||||
event: SignedInEvent(account: account),
|
||||
session: Session(account: account),
|
||||
AuthenticationSession.fromEvent(
|
||||
SignedInEvent(account: account),
|
||||
),
|
||||
);
|
||||
expect(state.status, AuthenticationStatus.authenticated);
|
||||
expect(state.wrapper?.session?.account, account);
|
||||
expect(state.session?.account, account);
|
||||
});
|
||||
});
|
||||
|
||||
@ -52,14 +51,14 @@ void main() {
|
||||
const String extra = 'AwesomeExtraData';
|
||||
final AuthenticationState<String> state =
|
||||
AuthenticationState.authenticated(
|
||||
SessionWrapper<String>(
|
||||
event: SignedInEvent(account: account),
|
||||
session: Session(account: account, data: extra),
|
||||
AuthenticationSession.fromEvent(
|
||||
SignedInEvent(account: account),
|
||||
data: extra,
|
||||
),
|
||||
);
|
||||
expect(state.status, AuthenticationStatus.authenticated);
|
||||
expect(state.wrapper?.session?.account, account);
|
||||
expect(state.wrapper?.session?.data, extra);
|
||||
expect(state.session?.account, account);
|
||||
expect(state.session?.data, extra);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -43,13 +43,24 @@ void main() {
|
||||
|
||||
when(() => authenticationRepository.sessionStream()).thenAnswer(
|
||||
(_) => Stream.fromIterable([
|
||||
SessionWrapper<int>(
|
||||
event: SignedInFromCacheEvent(account: account),
|
||||
session: Session<int>(account: account, data: 10),
|
||||
AuthenticationSession.fromEvent(
|
||||
SignedInFromCacheEvent(account: account),
|
||||
data: 10,
|
||||
)
|
||||
]),
|
||||
);
|
||||
|
||||
when(
|
||||
() => authenticationRepository.currentSession(),
|
||||
).thenAnswer(
|
||||
(_) async => Ok(
|
||||
AuthenticationSession.fromEvent(
|
||||
SignedInFromCacheEvent(account: account),
|
||||
data: 10,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
when(
|
||||
() => authenticationRepository.refresh(),
|
||||
).thenAnswer((_) async => Ok(account));
|
||||
|
Loading…
x
Reference in New Issue
Block a user