-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(authentication): Migrate to bloc - authentication
- Loading branch information
Showing
38 changed files
with
747 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import 'package:equatable/equatable.dart'; | ||
import 'package:flutter_bloc/flutter_bloc.dart'; | ||
import 'package:tsdm_client/exceptions/exceptions.dart'; | ||
import 'package:tsdm_client/features/authentication/repository/authentication_repository.dart'; | ||
import 'package:tsdm_client/features/authentication/repository/exceptions/exceptions.dart'; | ||
import 'package:tsdm_client/features/authentication/repository/models/hash.dart'; | ||
import 'package:tsdm_client/features/authentication/repository/models/user_credential.dart'; | ||
import 'package:tsdm_client/utils/debug.dart'; | ||
|
||
part 'authentication_event.dart'; | ||
part 'authentication_state.dart'; | ||
|
||
typedef AuthenticationEmitter = Emitter<AuthenticationState>; | ||
|
||
class AuthenticationBloc | ||
extends Bloc<AuthenticationEvent, AuthenticationState> { | ||
AuthenticationBloc( | ||
{required AuthenticationRepository authenticationRepository}) | ||
: _authenticationRepository = authenticationRepository, | ||
super(const AuthenticationState()) { | ||
on<AuthenticationFetchLoginHashRequested>( | ||
_onAuthenticationFetchLoginHashRequested); | ||
on<AuthenticationLoginRequested>(_onAuthenticationLoginRequested); | ||
} | ||
|
||
final AuthenticationRepository _authenticationRepository; | ||
|
||
Future<void> _onAuthenticationFetchLoginHashRequested( | ||
AuthenticationFetchLoginHashRequested event, | ||
AuthenticationEmitter emit, | ||
) async { | ||
emit(state.copyWith(status: AuthenticationStatus.fetchingHash)); | ||
try { | ||
final loginHash = await _authenticationRepository.fetchHash(); | ||
emit(state.copyWith( | ||
status: AuthenticationStatus.gotHash, loginHash: loginHash)); | ||
} on HttpRequestFailedException catch (e) { | ||
debug('failed to fetch login hash: $e'); | ||
emit(state.copyWith(status: AuthenticationStatus.failed)); | ||
} on LoginException catch (e) { | ||
debug('failed to fetch login hash: $e'); | ||
emit(state.copyWith( | ||
status: AuthenticationStatus.failed, loginException: e)); | ||
} | ||
} | ||
|
||
Future<void> _onAuthenticationLoginRequested( | ||
AuthenticationLoginRequested event, | ||
AuthenticationEmitter emit, | ||
) async { | ||
emit(state.copyWith(status: AuthenticationStatus.loggingIn)); | ||
try { | ||
await _authenticationRepository.loginWithPassword(event.userCredential); | ||
emit(state.copyWith(status: AuthenticationStatus.success)); | ||
} on HttpRequestFailedException catch (e) { | ||
debug('failed to login: $e'); | ||
emit(state.copyWith(status: AuthenticationStatus.failed)); | ||
} on LoginException catch (e) { | ||
debug('failed to login: $e'); | ||
emit(state.copyWith( | ||
status: AuthenticationStatus.failed, loginException: e)); | ||
} | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
lib/features/authentication/bloc/authentication_event.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
part of 'authentication_bloc.dart'; | ||
|
||
sealed class AuthenticationEvent extends Equatable { | ||
const AuthenticationEvent(); | ||
|
||
@override | ||
List<Object?> get props => []; | ||
} | ||
|
||
/// Call this event to fetch hash data required in login process before login. | ||
final class AuthenticationFetchLoginHashRequested extends AuthenticationEvent {} | ||
|
||
final class AuthenticationLoginRequested extends AuthenticationEvent { | ||
const AuthenticationLoginRequested(this.userCredential) : super(); | ||
final UserCredential userCredential; | ||
} |
49 changes: 49 additions & 0 deletions
49
lib/features/authentication/bloc/authentication_state.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
part of 'authentication_bloc.dart'; | ||
|
||
enum AuthenticationStatus { | ||
initial, | ||
|
||
/// Fetching hash data that need to use in login process. | ||
fetchingHash, | ||
|
||
// | ||
gotHash, | ||
|
||
/// Polling login request. | ||
loggingIn, | ||
|
||
/// Login success. | ||
success, | ||
|
||
/// Login failed. | ||
failed, | ||
} | ||
|
||
final class AuthenticationState extends Equatable { | ||
const AuthenticationState({ | ||
this.status = AuthenticationStatus.initial, | ||
this.loginHash, | ||
this.loginException, | ||
}); | ||
|
||
final AuthenticationStatus status; | ||
|
||
final LoginHash? loginHash; | ||
|
||
final LoginException? loginException; | ||
|
||
AuthenticationState copyWith({ | ||
AuthenticationStatus? status, | ||
LoginHash? loginHash, | ||
LoginException? loginException, | ||
}) { | ||
return AuthenticationState( | ||
status: status ?? this.status, | ||
loginHash: loginHash ?? this.loginHash, | ||
loginException: loginException ?? this.loginException, | ||
); | ||
} | ||
|
||
@override | ||
List<Object?> get props => [status]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
...ion_repository/exceptions/exceptions.dart → ...ion/repository/exceptions/exceptions.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import 'package:equatable/equatable.dart'; | ||
|
||
class LoginHash extends Equatable { | ||
const LoginHash({ | ||
required this.formHash, | ||
required this.loginHash, | ||
}); | ||
|
||
final String formHash; | ||
final String loginHash; | ||
|
||
@override | ||
List<Object?> get props => [formHash, loginHash]; | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_bloc/flutter_bloc.dart'; | ||
import 'package:go_router/go_router.dart'; | ||
import 'package:tsdm_client/features/authentication/bloc/authentication_bloc.dart'; | ||
import 'package:tsdm_client/features/authentication/repository/exceptions/exceptions.dart'; | ||
import 'package:tsdm_client/features/authentication/widgets/login_form.dart'; | ||
import 'package:tsdm_client/generated/i18n/strings.g.dart'; | ||
|
||
class LoginPage extends StatefulWidget { | ||
const LoginPage({this.redirectBackState, super.key}); | ||
|
||
final GoRouterState? redirectBackState; | ||
|
||
@override | ||
State<LoginPage> createState() => _LoginPageState(); | ||
} | ||
|
||
class _LoginPageState extends State<LoginPage> { | ||
@override | ||
Widget build(BuildContext context) { | ||
return Scaffold( | ||
appBar: AppBar( | ||
title: Text(context.t.loginPage.title), | ||
), | ||
body: BlocProvider( | ||
create: (context) => AuthenticationBloc( | ||
authenticationRepository: RepositoryProvider.of(context)) | ||
..add(AuthenticationFetchLoginHashRequested()), | ||
child: BlocListener<AuthenticationBloc, AuthenticationState>( | ||
listener: (context, state) { | ||
if (state.status == AuthenticationStatus.failed) { | ||
final errorText = switch (state.loginException) { | ||
LoginFormHashNotFoundException() => | ||
context.t.loginPage.hashValueNotFound, | ||
LoginInvalidFormHashException() => | ||
context.t.loginPage.failedToGetFormHash, | ||
LoginMessageNotFoundException() => | ||
context.t.loginPage.failedToLoginMessageNodeNotFound, | ||
LoginIncorrectCaptchaException() => | ||
context.t.loginPage.loginResultIncorrectCaptcha, | ||
LoginInvalidCredentialException() => | ||
context.t.loginPage.loginResultIncorrectUsernameOrPassword, | ||
LoginIncorrectSecurityQuestionException() => | ||
context.t.loginPage.loginResultIncorrectQuestionOrAnswer, | ||
LoginAttemptLimitException() => | ||
context.t.loginPage.loginResultTooManyLoginAttempts, | ||
LoginUserInfoNotFoundException() => | ||
context.t.loginPage.loginFailed, | ||
LoginOtherErrorException() => | ||
context.t.loginPage.loginResultOtherErrors, | ||
null => context.t.general.failedToLoad, | ||
}; | ||
ScaffoldMessenger.of(context) | ||
.showSnackBar(SnackBar(content: Text(errorText))); | ||
|
||
context | ||
.read<AuthenticationBloc>() | ||
.add(AuthenticationFetchLoginHashRequested()); | ||
} | ||
}, | ||
child: Padding( | ||
padding: const EdgeInsets.all(15), | ||
child: Center( | ||
child: ConstrainedBox( | ||
constraints: const BoxConstraints( | ||
maxHeight: 500, | ||
maxWidth: 500, | ||
), | ||
child: LoginForm( | ||
redirectPath: widget.redirectBackState?.fullPath, | ||
redirectPathParameters: | ||
widget.redirectBackState?.pathParameters, | ||
redirectExtra: widget.redirectBackState?.extra, | ||
), | ||
), | ||
), | ||
), | ||
), | ||
), | ||
); | ||
} | ||
} |
Oops, something went wrong.