TF-2271 Implement dns lookup to get jmap url

Signed-off-by: dab246 <tdvu@linagora.com>
(cherry picked from commit 7b0c0015a15a243b6b4aef3d78e7653d556ad109)
This commit is contained in:
dab246
2023-11-10 17:06:50 +07:00
committed by Dat Vu
parent c0bef69435
commit 9b661b0688
33 changed files with 773 additions and 483 deletions
+1 -1
View File
@@ -105,7 +105,7 @@ class FileUtils {
log('FileUtils::removeFolder():folderPath: $folderPath'); log('FileUtils::removeFolder():folderPath: $folderPath');
final dir = Directory(folderPath); final dir = Directory(folderPath);
if (await dir.exists()) { if (await dir.exists()) {
await dir.delete(recursive: true); await dir.delete(recursive: true);
} }
} catch (e) { } catch (e) {
logError('FileUtils::removeFolder():EXCEPTION: $e'); logError('FileUtils::removeFolder():EXCEPTION: $e');
+27 -21
View File
@@ -141,21 +141,7 @@ abstract class BaseController extends GetxController
); );
} }
return error; return error;
} else if (error is BadCredentialsException) { } else if (error is BadCredentialsException || error is ConnectionError) {
if (currentOverlayContext != null && currentContext != null) {
appToast.showToastErrorMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).badCredentials
);
}
return error;
} else if (error is ConnectionError) {
if (authorizationInterceptors.isAppRunning && currentOverlayContext != null && currentContext != null) {
appToast.showToastErrorMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).connectionError
);
}
return error; return error;
} }
@@ -166,6 +152,22 @@ abstract class BaseController extends GetxController
void handleExceptionAction({Failure? failure, Exception? exception}) { void handleExceptionAction({Failure? failure, Exception? exception}) {
logError('BaseController::handleExceptionAction():failure: $failure | exception: $exception'); logError('BaseController::handleExceptionAction():failure: $failure | exception: $exception');
if (exception is BadCredentialsException) {
if (currentOverlayContext != null && currentContext != null) {
appToast.showToastErrorMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).badCredentials
);
}
} else if (exception is ConnectionError) {
if (currentOverlayContext != null && currentContext != null) {
appToast.showToastErrorMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).connectionError
);
}
}
if (!authorizationInterceptors.isAppRunning) { if (!authorizationInterceptors.isAppRunning) {
return; return;
} }
@@ -182,7 +184,7 @@ abstract class BaseController extends GetxController
} else { } else {
await clearDataAndGoToLoginPage(); await clearDataAndGoToLoginPage();
} }
} else if (failure is GetFCMSubscriptionLocalFailure || } else if (failure is GetFCMSubscriptionLocalFailure ||
failure is DestroySubscriptionFailure) { failure is DestroySubscriptionFailure) {
await clearDataAndGoToLoginPage(); await clearDataAndGoToLoginPage();
} }
@@ -326,9 +328,9 @@ abstract class BaseController extends GetxController
log('BaseController::clearDataAndGoToLoginPage:'); log('BaseController::clearDataAndGoToLoginPage:');
await clearAllData(); await clearAllData();
goToLogin(arguments: LoginArguments( goToLogin(arguments: LoginArguments(
isAuthenticatedWithOidc PlatformInfo.isWeb
? LoginFormType.ssoForm ? LoginFormType.none
: LoginFormType.credentialForm : LoginFormType.dnsLookupForm
)); ));
} }
@@ -346,7 +348,9 @@ abstract class BaseController extends GetxController
cachingManager.clearAll(), cachingManager.clearAll(),
languageCacheManager.removeLanguage(), languageCacheManager.removeLanguage(),
]); ]);
await cachingManager.clearAllFileInStorage(); if (PlatformInfo.isMobile) {
await cachingManager.clearAllFileInStorage();
}
authorizationInterceptors.clear(); authorizationInterceptors.clear();
authorizationIsolateInterceptors.clear(); authorizationIsolateInterceptors.clear();
if (_isFcmEnabled) { if (_isFcmEnabled) {
@@ -361,7 +365,9 @@ abstract class BaseController extends GetxController
cachingManager.clearAll(), cachingManager.clearAll(),
languageCacheManager.removeLanguage(), languageCacheManager.removeLanguage(),
]); ]);
await cachingManager.clearAllFileInStorage(); if (PlatformInfo.isMobile) {
await cachingManager.clearAllFileInStorage();
}
authorizationIsolateInterceptors.clear(); authorizationIsolateInterceptors.clear();
authorizationInterceptors.clear(); authorizationInterceptors.clear();
if (_isFcmEnabled) { if (_isFcmEnabled) {
@@ -1,7 +1,7 @@
import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/state/success.dart';
import 'package:core/utils/app_logger.dart'; import 'package:core/utils/app_logger.dart';
import 'package:flutter/cupertino.dart'; import 'package:core/utils/platform_info.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart';
@@ -10,11 +10,9 @@ import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:model/extensions/session_extension.dart'; import 'package:model/extensions/session_extension.dart';
import 'package:model/oidc/token_oidc.dart'; import 'package:model/oidc/token_oidc.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart'; import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart'; import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart';
import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart'; import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_authenticated_account_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/get_authenticated_account_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_credential_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/get_credential_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_stored_token_oidc_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/get_stored_token_oidc_state.dart';
@@ -24,9 +22,9 @@ import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
import 'package:tmail_ui_user/features/login/presentation/model/login_arguments.dart'; import 'package:tmail_ui_user/features/login/presentation/model/login_arguments.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_interactors_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_interactors_bindings.dart';
import 'package:tmail_ui_user/main/error/capability_validator.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart';
import 'package:tmail_ui_user/main/exceptions/remote_exception.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart';
import 'package:tmail_ui_user/main/utils/message_toast_utils.dart';
abstract class ReloadableController extends BaseController { abstract class ReloadableController extends BaseController {
final GetSessionInteractor _getSessionInteractor = Get.find<GetSessionInteractor>(); final GetSessionInteractor _getSessionInteractor = Get.find<GetSessionInteractor>();
@@ -37,16 +35,19 @@ abstract class ReloadableController extends BaseController {
void handleFailureViewState(Failure failure) { void handleFailureViewState(Failure failure) {
super.handleFailureViewState(failure); super.handleFailureViewState(failure);
if (failure is GetCredentialFailure) { if (failure is GetCredentialFailure) {
goToLogin(arguments: LoginArguments(LoginFormType.credentialForm)); goToLogin(arguments: LoginArguments(LoginFormType.dnsLookupForm));
} else if (failure is GetSessionFailure) { } else if (failure is GetSessionFailure) {
_handleGetSessionFailure( _handleGetSessionFailure(failure.exception);
sessionCacheException: failure.exception,
sessionRemoteException: failure.remoteException
);
} else if (failure is GetStoredTokenOidcFailure) { } else if (failure is GetStoredTokenOidcFailure) {
goToLogin(arguments: LoginArguments(LoginFormType.none)); goToLogin(arguments: LoginArguments(LoginFormType.none));
} else if (failure is GetAuthenticatedAccountFailure) { } else if (failure is GetAuthenticatedAccountFailure) {
goToLogin(arguments: LoginArguments(LoginFormType.credentialForm)); goToLogin(
arguments: LoginArguments(
PlatformInfo.isMobile
? LoginFormType.dnsLookupForm
: LoginFormType.none
)
);
} }
} }
@@ -95,56 +96,16 @@ abstract class ReloadableController extends BaseController {
consumeState(_getSessionInteractor.execute()); consumeState(_getSessionInteractor.execute());
} }
void _handleGetSessionFailure({ void _handleGetSessionFailure(dynamic exception) {
dynamic sessionRemoteException, if (currentContext != null || currentOverlayContext != null) {
dynamic sessionCacheException appToast.showToastErrorMessage(
}) { currentOverlayContext!,
showToastErrorMessageSessionFailure( MessageToastUtils.getMessageByException(currentContext!, exception) ?? AppLocalizations.of(currentContext!).unknownError
sessionRemoteException: sessionRemoteException, );
sessionCacheException: sessionCacheException, }
);
clearDataAndGoToLoginPage(); clearDataAndGoToLoginPage();
} }
void showToastErrorMessageSessionFailure({
dynamic sessionRemoteException,
dynamic sessionCacheException
}) {
if (currentContext == null || currentOverlayContext == null) {
return;
}
appToast.showToastErrorMessage(
currentOverlayContext!,
getErrorMessageFromSessionFailure(
context: currentContext!,
sessionRemoteException: sessionRemoteException,
sessionCacheException: sessionCacheException,
)
);
}
String getErrorMessageFromSessionFailure({
required BuildContext context,
dynamic sessionRemoteException,
dynamic sessionCacheException,
}) {
if (sessionRemoteException is ConnectionTimeout ||
sessionRemoteException is BadGateway ||
sessionRemoteException is SocketError) {
return AppLocalizations.of(context).wrongUrlMessage;
} else if (sessionRemoteException is BadCredentialsException) {
return AppLocalizations.of(context).badCredentials;
} else if (sessionRemoteException is ConnectionError) {
return AppLocalizations.of(context).connectionError;
} else if (sessionRemoteException is UnknownError && sessionRemoteException.message != null) {
return '[${sessionRemoteException.code ?? ''}] ${sessionRemoteException.message}';
} else if (sessionCacheException is NotFoundSessionException) {
return AppLocalizations.of(context).notFoundSession;
} else {
return AppLocalizations.of(context).unknownError;
}
}
void _handleGetSessionSuccess(GetSessionSuccess success) { void _handleGetSessionSuccess(GetSessionSuccess success) {
final session = success.session; final session = success.session;
final personalAccount = session.personalAccount; final personalAccount = session.personalAccount;
@@ -2,6 +2,7 @@
import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/resources/image_paths.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/login/domain/model/recent_login_url.dart'; import 'package:tmail_ui_user/features/login/domain/model/recent_login_url.dart';
import 'package:tmail_ui_user/features/login/domain/model/recent_login_username.dart'; import 'package:tmail_ui_user/features/login/domain/model/recent_login_username.dart';
@@ -9,13 +10,15 @@ class RecentItemTileWidget<T> extends StatelessWidget {
final T item; final T item;
final EdgeInsets? contentPadding; final EdgeInsets? contentPadding;
final ImagePaths imagePath; final _imagePath = Get.find<ImagePaths>();
const RecentItemTileWidget(this.item, { RecentItemTileWidget(
required this.imagePath, this.item,
{
Key? key, Key? key,
this.contentPadding, this.contentPadding,
}) : super(key: key); }
) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -23,14 +26,17 @@ class RecentItemTileWidget<T> extends StatelessWidget {
padding: contentPadding ?? const EdgeInsets.all(12), padding: contentPadding ?? const EdgeInsets.all(12),
child: Row( child: Row(
children: [ children: [
SvgPicture.asset(imagePath.icClockSB), SvgPicture.asset(_imagePath.icClockSB),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded( Expanded(
child: Text(_getTitle(item), child: Text(
style: const TextStyle( _getTitle(item),
fontSize: 15, style: const TextStyle(
fontWeight: FontWeight.normal, fontSize: 15,
color: Colors.black)), fontWeight: FontWeight.normal,
color: Colors.black
)
),
), ),
], ],
), ),
+4 -6
View File
@@ -129,12 +129,10 @@ class CachingManager {
} }
Future<void> clearAllFileInStorage() async { Future<void> clearAllFileInStorage() async {
if (PlatformInfo.isMobile) { await Future.wait([
await Future.wait([ _fileUtils.removeFolder(CachingConstants.newEmailsContentFolderName),
_fileUtils.removeFolder(CachingConstants.newEmailsContentFolderName), _fileUtils.removeFolder(CachingConstants.openedEmailContentFolderName),
_fileUtils.removeFolder(CachingConstants.openedEmailContentFolderName), ]);
]);
}
} }
Future<void> _clearSendingEmailCache() async { Future<void> _clearSendingEmailCache() async {
@@ -14,7 +14,6 @@ class GetSessionSuccess extends UIState {
} }
class GetSessionFailure extends FeatureFailure { class GetSessionFailure extends FeatureFailure {
final dynamic remoteException;
GetSessionFailure({dynamic exception, this.remoteException}) : super(exception: exception); GetSessionFailure(dynamic exception) : super(exception: exception);
} }
@@ -20,22 +20,19 @@ class GetSessionInteractor {
if (PlatformInfo.isMobile) { if (PlatformInfo.isMobile) {
yield* _getStoredSessionFromCache(remoteException: e); yield* _getStoredSessionFromCache(remoteException: e);
} else { } else {
yield Left<Failure, Success>(GetSessionFailure(exception: e)); yield Left<Failure, Success>(GetSessionFailure(e));
} }
} }
} }
Stream<Either<Failure, Success>> _getStoredSessionFromCache({dynamic remoteException}) async* { Stream<Either<Failure, Success>> _getStoredSessionFromCache({dynamic remoteException}) async* {
try { try {
log('GetSessionInteractor::_getStoredSessionFromCache:'); log('GetSessionInteractor::_getStoredSessionFromCache:remoteException: $remoteException');
yield Right<Failure, Success>(GetSessionLoading()); yield Right<Failure, Success>(GetSessionLoading());
final session = await sessionRepository.getStoredSession(); final session = await sessionRepository.getStoredSession();
yield Right<Failure, Success>(GetSessionSuccess(session)); yield Right<Failure, Success>(GetSessionSuccess(session));
} catch (e) { } catch (e) {
yield Left<Failure, Success>(GetSessionFailure( yield Left<Failure, Success>(GetSessionFailure(remoteException ?? e));
exception: e,
remoteException: remoteException
));
} }
} }
} }
@@ -46,6 +46,9 @@ class DNSService {
} }
final url = _removeDotAtEndOfString(listFieldData.last); final url = _removeDotAtEndOfString(listFieldData.last);
log('DNSService::_parsingUrlFromDataResourceRecord:url: $url'); log('DNSService::_parsingUrlFromDataResourceRecord:url: $url');
if (url.trim().isEmpty) {
throw NotFoundUrlException();
}
return url; return url;
} }
@@ -1,13 +1,11 @@
import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/state/success.dart';
import 'package:core/utils/app_logger.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:model/account/authentication_type.dart'; import 'package:model/account/authentication_type.dart';
import 'package:model/account/password.dart'; import 'package:model/account/password.dart';
import 'package:model/account/personal_account.dart'; import 'package:model/account/personal_account.dart';
import 'package:tmail_ui_user/features/login/data/model/authentication_info_cache.dart'; import 'package:tmail_ui_user/features/login/data/model/authentication_info_cache.dart';
import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart';
import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/authentication_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
@@ -24,33 +22,25 @@ class AuthenticationInteractor {
this._accountRepository this._accountRepository
); );
Stream<Either<Failure, Success>> execute({Uri? baseUrl, UserName? userName, Password? password}) async* { Stream<Either<Failure, Success>> execute({
required Uri baseUrl,
required UserName userName,
required Password password
}) async* {
try { try {
yield Right(AuthenticationUserLoading()); yield Right(AuthenticationUserLoading());
final user = await authenticationRepository.authenticationUser(baseUrl, userName, password);
if (baseUrl != null && userName != null && password != null) { await Future.wait([
final user = await authenticationRepository.authenticationUser(baseUrl, userName, password); credentialRepository.saveBaseUrl(baseUrl),
await Future.wait([ credentialRepository.storeAuthenticationInfo(AuthenticationInfoCache(userName.value, password.value)),
credentialRepository.saveBaseUrl(baseUrl), _accountRepository.setCurrentAccount(PersonalAccount(
credentialRepository.storeAuthenticationInfo(AuthenticationInfoCache(userName.value, password.value)), userName.value,
_accountRepository.setCurrentAccount(PersonalAccount( AuthenticationType.basic,
userName.value, isSelected: true
AuthenticationType.basic, ))
isSelected: true ]);
)) yield Right(AuthenticationUserSuccess(user));
]);
yield Right(AuthenticationUserSuccess(user));
} else if (baseUrl == null) {
yield Left(AuthenticationUserFailure(CanNotFoundBaseUrl()));
} else if (userName == null) {
yield Left(AuthenticationUserFailure(CanNotFoundUserName()));
} else if (password == null) {
yield Left(AuthenticationUserFailure(CanNotFoundPassword()));
} else {
yield Left(AuthenticationUserFailure(null));
}
} catch (e) { } catch (e) {
logError('AuthenticationInteractor::execute(): $e');
yield Left(AuthenticationUserFailure(e)); yield Left(AuthenticationUserFailure(e));
} }
} }
@@ -1,24 +1,11 @@
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/views/text/type_ahead_form_field_builder.dart'; import 'package:core/presentation/views/text/type_ahead_form_field_builder.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/widget/recent_item_tile_widget.dart'; import 'package:tmail_ui_user/features/base/widget/recent_item_tile_widget.dart';
import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart';
import 'package:tmail_ui_user/features/login/data/network/oidc_error.dart';
import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart';
import 'package:tmail_ui_user/features/login/domain/model/recent_login_username.dart'; import 'package:tmail_ui_user/features/login/domain/model/recent_login_username.dart';
import 'package:tmail_ui_user/features/login/domain/state/authenticate_oidc_on_browser_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/authentication_user_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/check_oidc_is_available_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_oidc_configuration_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_oidc_is_available_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_token_oidc_state.dart';
import 'package:tmail_ui_user/features/login/presentation/login_controller.dart'; import 'package:tmail_ui_user/features/login/presentation/login_controller.dart';
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
import 'package:tmail_ui_user/features/login/presentation/widgets/login_input_decoration_builder.dart'; import 'package:tmail_ui_user/features/login/presentation/widgets/login_input_decoration_builder.dart';
import 'package:tmail_ui_user/features/login/presentation/widgets/login_text_input_builder.dart'; import 'package:tmail_ui_user/features/login/presentation/widgets/login_text_input_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -26,85 +13,20 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
abstract class BaseLoginView extends GetWidget<LoginController> { abstract class BaseLoginView extends GetWidget<LoginController> {
const BaseLoginView({Key? key}) : super(key: key); const BaseLoginView({Key? key}) : super(key: key);
Widget buildLoginMessage(BuildContext context, Either<Failure, Success> viewState) {
return Padding(
padding: const EdgeInsets.only(top: 11, bottom: 36, left: 58, right: 58),
child: SizedBox(
width: controller.responsiveUtils.getWidthLoginTextField(context),
child: Text(
viewState.fold(
(failure) {
if (failure is CheckOIDCIsAvailableFailure) {
return _getMessageFailure(context, failure.exception);
} else if (failure is AuthenticationUserFailure) {
return _getMessageFailure(context, failure.exception);
} else if (failure is GetOIDCIsAvailableFailure) {
return _getMessageFailure(context, failure.exception);
} else if (failure is GetTokenOIDCFailure) {
return _getMessageFailure(context, failure.exception);
} else if (failure is AuthenticateOidcOnBrowserFailure) {
return _getMessageFailure(context, failure.exception);
} else if (failure is GetOIDCConfigurationFailure) {
return AppLocalizations.of(context).canNotVerifySSOConfiguration;
} else if (failure is GetSessionFailure) {
return controller.getErrorMessageFromSessionFailure(
context: context,
sessionRemoteException: failure.remoteException,
sessionCacheException: failure.exception
);
} else {
return AppLocalizations.of(context).unknownError;
}
},
(success) {
if (controller.loginFormType.value == LoginFormType.credentialForm) {
return AppLocalizations.of(context).loginInputCredentialMessage;
} else if (controller.loginFormType.value == LoginFormType.ssoForm) {
return AppLocalizations.of(context).loginInputSSOMessage;
}
return AppLocalizations.of(context).loginInputUrlMessage;
}),
key: const Key('login_message'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w400,
color: viewState.fold(
(failure) => AppColor.textFieldErrorBorderColor,
(success) => AppColor.colorNameEmail)),
))
);
}
String _getMessageFailure(BuildContext context, dynamic exception) {
if (exception is CanNotFoundBaseUrl) {
return AppLocalizations.of(context).requiredUrl;
} else if (exception is CanNotFoundUserName) {
return AppLocalizations.of(context).requiredEmail;
} else if (exception is CanNotFoundPassword) {
return AppLocalizations.of(context).requiredPassword;
} else if (exception is CanNotFoundOIDCLinks) {
return AppLocalizations.of(context).ssoNotAvailable;
} else if (exception is CanNotFoundToken) {
return AppLocalizations.of(context).canNotGetToken;
} else {
return '';
}
}
Widget buildLoginButton(BuildContext context) { Widget buildLoginButton(BuildContext context) {
return Container( return Container(
margin: const EdgeInsets.only(bottom: 16, left: 24, right: 24), margin: const EdgeInsetsDirectional.only(bottom: 16, start: 24, end: 24),
width: controller.responsiveUtils.getDeviceWidth(context),height: 48, width: controller.responsiveUtils.getDeviceWidth(context),
height: 48,
child: ElevatedButton( child: ElevatedButton(
key: const Key('loginSubmitForm'), key: const Key('loginSubmitForm'),
style: ButtonStyle( style: ElevatedButton.styleFrom(
foregroundColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) => Colors.white), foregroundColor: Colors.white,
backgroundColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) => AppColor.primaryColor), backgroundColor: AppColor.primaryColor,
shape: MaterialStateProperty.all(RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
side: const BorderSide(width: 0, color: AppColor.primaryColor) side: const BorderSide(width: 0, color: AppColor.primaryColor)
)) )
), ),
onPressed: controller.handleLoginPressed, onPressed: controller.handleLoginPressed,
child: Text( child: Text(
@@ -135,7 +57,8 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
return TypeAheadFormFieldBuilder<RecentLoginUsername>( return TypeAheadFormFieldBuilder<RecentLoginUsername>(
key: const Key('login_username_input'), key: const Key('login_username_input'),
controller: controller.usernameInputController, controller: controller.usernameInputController,
onTextChange: controller.setUserNameText, onTextChange: controller.onUsernameChange,
focusNode: controller.userNameFocusNode,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
autocorrect: false, autocorrect: false,
autofillHints: const [AutofillHints.email], autofillHints: const [AutofillHints.email],
@@ -146,11 +69,8 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
.build(), .build(),
debounceDuration: const Duration(milliseconds: 300), debounceDuration: const Duration(milliseconds: 300),
suggestionsCallback: controller.getAllRecentLoginUsernameAction, suggestionsCallback: controller.getAllRecentLoginUsernameAction,
itemBuilder: (context, loginUsername) => RecentItemTileWidget(loginUsername, imagePath: controller.imagePaths), itemBuilder: (_, loginUsername) => RecentItemTileWidget(loginUsername),
onSuggestionSelected: (recentUsername) { onSuggestionSelected: controller.selectUsernameFromSuggestion,
controller.setUsername(recentUsername.username);
controller.passFocusNode.requestFocus();
},
suggestionsBoxDecoration: const SuggestionsBoxDecoration(borderRadius: BorderRadius.all(Radius.circular(14))), suggestionsBoxDecoration: const SuggestionsBoxDecoration(borderRadius: BorderRadius.all(Radius.circular(14))),
noItemsFoundBuilder: (context) => const SizedBox(), noItemsFoundBuilder: (context) => const SizedBox(),
hideOnEmpty: true, hideOnEmpty: true,
@@ -167,8 +87,8 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
textInputAction: TextInputAction.done, textInputAction: TextInputAction.done,
hintText: AppLocalizations.of(context).password, hintText: AppLocalizations.of(context).password,
focusNode: controller.passFocusNode, focusNode: controller.passFocusNode,
onTextChange: controller.setPasswordText, onTextChange: controller.onPasswordChange,
onSubmitted: (value) => controller.handleLoginPressed(), onSubmitted: (_) => controller.handleLoginPressed(),
); );
} }
@@ -1,21 +1,21 @@
import 'package:core/data/model/source_type/data_source_type.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/base_bindings.dart'; import 'package:tmail_ui_user/features/base/base_bindings.dart';
import 'package:tmail_ui_user/features/caching/clients/recent_login_url_cache_client.dart'; import 'package:tmail_ui_user/features/caching/clients/recent_login_url_cache_client.dart';
import 'package:tmail_ui_user/features/caching/clients/recent_login_username_cache_client.dart'; import 'package:tmail_ui_user/features/caching/clients/recent_login_username_cache_client.dart';
import 'package:tmail_ui_user/features/login/data/datasource/login_url_datasource.dart'; import 'package:tmail_ui_user/features/login/data/datasource/login_datasource.dart';
import 'package:tmail_ui_user/features/login/data/datasource/login_username_datasource.dart'; import 'package:tmail_ui_user/features/login/data/datasource_impl/hive_login_datasource_impl.dart';
import 'package:tmail_ui_user/features/login/data/datasource_impl/login_url_datasource_impl.dart'; import 'package:tmail_ui_user/features/login/data/datasource_impl/login_datasource_impl.dart';
import 'package:tmail_ui_user/features/login/data/datasource_impl/login_username_datasource_impl.dart'; import 'package:tmail_ui_user/features/login/data/network/dns_service.dart';
import 'package:tmail_ui_user/features/login/data/repository/login_url_repository_impl.dart'; import 'package:tmail_ui_user/features/login/data/repository/login_repository_impl.dart';
import 'package:tmail_ui_user/features/login/data/repository/login_username_repository_impl.dart';
import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/login_url_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/login_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/login_username_repository.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/authenticate_oidc_on_browser_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authenticate_oidc_on_browser_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/check_oidc_is_available_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/check_oidc_is_available_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/dns_lookup_to_get_jmap_url_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_url_on_mobile_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_url_on_mobile_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_username_on_mobile_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_username_on_mobile_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_authentication_info_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_authentication_info_interactor.dart';
@@ -27,6 +27,7 @@ import 'package:tmail_ui_user/features/login/domain/usecases/save_login_url_on_m
import 'package:tmail_ui_user/features/login/domain/usecases/save_login_username_on_mobile_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/save_login_username_on_mobile_interactor.dart';
import 'package:tmail_ui_user/features/login/presentation/login_controller.dart'; import 'package:tmail_ui_user/features/login/presentation/login_controller.dart';
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
class LoginBindings extends BaseBindings { class LoginBindings extends BaseBindings {
@@ -45,24 +46,26 @@ class LoginBindings extends BaseBindings {
Get.find<GetAllRecentLoginUrlOnMobileInteractor>(), Get.find<GetAllRecentLoginUrlOnMobileInteractor>(),
Get.find<SaveLoginUsernameOnMobileInteractor>(), Get.find<SaveLoginUsernameOnMobileInteractor>(),
Get.find<GetAllRecentLoginUsernameOnMobileInteractor>(), Get.find<GetAllRecentLoginUsernameOnMobileInteractor>(),
Get.find<DNSLookupToGetJmapUrlInteractor>(),
)); ));
} }
@override @override
void bindingsDataSource() { void bindingsDataSource() {
Get.lazyPut<LoginUrlDataSource>(() => Get.find<LoginUrlDataSourceImpl>()); Get.lazyPut<LoginDataSource>(() => Get.find<LoginDataSourceImpl>());
Get.lazyPut<LoginUsernameDataSource>(() => Get.find<LoginUsernameDataSourceImpl>());
} }
@override @override
void bindingsDataSourceImpl() { void bindingsDataSourceImpl() {
Get.lazyPut(() => LoginUrlDataSourceImpl( Get.lazyPut(() => HiveLoginDataSourceImpl(
Get.find<RecentLoginUrlCacheClient>(), Get.find<RecentLoginUrlCacheClient>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => LoginUsernameDataSourceImpl(
Get.find<RecentLoginUsernameCacheClient>(), Get.find<RecentLoginUsernameCacheClient>(),
Get.find<CacheExceptionThrower>())); Get.find<CacheExceptionThrower>()
));
Get.lazyPut(() => LoginDataSourceImpl(
Get.find<DNSService>(),
Get.find<RemoteExceptionThrower>(),
));
} }
@override @override
@@ -90,28 +93,25 @@ class LoginBindings extends BaseBindings {
Get.lazyPut(() => GetStoredOidcConfigurationInteractor( Get.lazyPut(() => GetStoredOidcConfigurationInteractor(
Get.find<AuthenticationOIDCRepository>(), Get.find<AuthenticationOIDCRepository>(),
)); ));
Get.lazyPut(() => SaveLoginUrlOnMobileInteractor( Get.lazyPut(() => SaveLoginUrlOnMobileInteractor(Get.find<LoginRepository>(),));
Get.find<LoginUrlRepository>(), Get.lazyPut(() => GetAllRecentLoginUrlOnMobileInteractor(Get.find<LoginRepository>()));
)); Get.lazyPut(() => SaveLoginUsernameOnMobileInteractor(Get.find<LoginRepository>(),));
Get.lazyPut(() => GetAllRecentLoginUrlOnMobileInteractor(Get.find<LoginUrlRepository>())); Get.lazyPut(() => GetAllRecentLoginUsernameOnMobileInteractor(Get.find<LoginRepository>()));
Get.lazyPut(() => SaveLoginUsernameOnMobileInteractor( Get.lazyPut(() => DNSLookupToGetJmapUrlInteractor(Get.find<LoginRepository>()));
Get.find<LoginUsernameRepository>(),
));
Get.lazyPut(() => GetAllRecentLoginUsernameOnMobileInteractor(
Get.find<LoginUsernameRepository>()
));
} }
@override @override
void bindingsRepository() { void bindingsRepository() {
Get.lazyPut<LoginUrlRepository>(() => Get.find<LoginUrlRepositoryImpl>()); Get.lazyPut<LoginRepository>(() => Get.find<LoginRepositoryImpl>());
Get.lazyPut<LoginUsernameRepository>(() => Get.find<LoginUsernameRepositoryImpl>());
} }
@override @override
void bindingsRepositoryImpl() { void bindingsRepositoryImpl() {
Get.lazyPut(() => LoginUrlRepositoryImpl(Get.find<LoginUrlDataSource>())); Get.lazyPut(() => LoginRepositoryImpl(
Get.lazyPut(() => LoginUsernameRepositoryImpl(Get.find<LoginUsernameDataSource>())); {
DataSourceType.hiveCache: Get.find<HiveLoginDataSourceImpl>(),
DataSourceType.network: Get.find<LoginDataSource>(),
}
));
} }
} }
@@ -24,6 +24,7 @@ import 'package:tmail_ui_user/features/login/domain/model/recent_login_username.
import 'package:tmail_ui_user/features/login/domain/state/authenticate_oidc_on_browser_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/authenticate_oidc_on_browser_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/authentication_user_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/authentication_user_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/check_oidc_is_available_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/check_oidc_is_available_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/dns_lookup_to_get_jmap_url_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_all_recent_login_url_latest_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/get_all_recent_login_url_latest_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_all_recent_login_username_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/get_all_recent_login_username_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_authenticated_account_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/get_authenticated_account_state.dart';
@@ -35,6 +36,7 @@ import 'package:tmail_ui_user/features/login/domain/state/get_token_oidc_state.d
import 'package:tmail_ui_user/features/login/domain/usecases/authenticate_oidc_on_browser_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authenticate_oidc_on_browser_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/check_oidc_is_available_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/check_oidc_is_available_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/dns_lookup_to_get_jmap_url_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_url_on_mobile_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_url_on_mobile_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_username_on_mobile_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_username_on_mobile_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_authentication_info_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_authentication_info_interactor.dart';
@@ -67,12 +69,22 @@ class LoginController extends ReloadableController {
final GetAllRecentLoginUrlOnMobileInteractor _getAllRecentLoginUrlOnMobileInteractor; final GetAllRecentLoginUrlOnMobileInteractor _getAllRecentLoginUrlOnMobileInteractor;
final SaveLoginUsernameOnMobileInteractor _saveLoginUsernameOnMobileInteractor; final SaveLoginUsernameOnMobileInteractor _saveLoginUsernameOnMobileInteractor;
final GetAllRecentLoginUsernameOnMobileInteractor _getAllRecentLoginUsernameOnMobileInteractor; final GetAllRecentLoginUsernameOnMobileInteractor _getAllRecentLoginUsernameOnMobileInteractor;
final DNSLookupToGetJmapUrlInteractor _dnsLookupToGetJmapUrlInteractor;
final TextEditingController urlInputController = TextEditingController(); final TextEditingController urlInputController = TextEditingController();
final TextEditingController usernameInputController = TextEditingController(); final TextEditingController usernameInputController = TextEditingController();
final TextEditingController passwordInputController = TextEditingController(); final TextEditingController passwordInputController = TextEditingController();
final FocusNode baseUrlFocusNode = FocusNode();
final FocusNode userNameFocusNode = FocusNode();
final FocusNode passFocusNode = FocusNode(); final FocusNode passFocusNode = FocusNode();
final loginFormType = LoginFormType.none.obs;
OIDCResponse? _oidcResponse;
UserName? _username;
Password? _password;
Uri? _baseUri;
LoginController( LoginController(
this._authenticationInteractor, this._authenticationInteractor,
this._checkOIDCIsAvailableInteractor, this._checkOIDCIsAvailableInteractor,
@@ -86,42 +98,26 @@ class LoginController extends ReloadableController {
this._getAllRecentLoginUrlOnMobileInteractor, this._getAllRecentLoginUrlOnMobileInteractor,
this._saveLoginUsernameOnMobileInteractor, this._saveLoginUsernameOnMobileInteractor,
this._getAllRecentLoginUsernameOnMobileInteractor, this._getAllRecentLoginUsernameOnMobileInteractor,
this._dnsLookupToGetJmapUrlInteractor,
); );
final loginFormType = LoginFormType.baseUrlForm.obs;
String? _urlText;
String? _userNameText;
String? _passwordText;
OIDCResponse? _oidcResponse;
void setUrlText(String url) => _urlText = url.trim().formatURLValid();
void setUserNameText(String userName) => _userNameText = userName;
void setPasswordText(String password) => _passwordText = password;
Uri? _parseUri(String? url) => url != null && url.trim().isNotEmpty
? Uri.parse(url.trim())
: null;
UserName? _parseUserName(String? userName) => userName != null && userName.trim().isNotEmpty
? UserName(userName.trim())
: null;
Password? _parsePassword(String? password) => password != null && password.trim().isNotEmpty
? Password(password.trim())
: null;
@override @override
void onReady() { void onReady() {
super.onReady(); super.onReady();
if (PlatformInfo.isWeb) { final arguments = Get.arguments;
final arguments = Get.arguments; if (arguments is LoginArguments) {
if (arguments is LoginArguments) { if (arguments.loginFormType == LoginFormType.passwordForm) {
loginFormType.value = arguments.loginFormType; loginFormType.value = LoginFormType.dnsLookupForm;
_checkOIDCIsAvailable();
} else { } else {
loginFormType.value = arguments.loginFormType;
}
if (PlatformInfo.isWeb) {
_checkOIDCIsAvailable();
} else if (arguments.baseUrl?.isNotEmpty == true) {
onBaseUrlChange(arguments.baseUrl!);
}
} else {
if (PlatformInfo.isWeb) {
_getAuthenticationInfo(); _getAuthenticationInfo();
} }
} }
@@ -134,12 +130,21 @@ class LoginController extends ReloadableController {
} else if (failure is CheckOIDCIsAvailableFailure || } else if (failure is CheckOIDCIsAvailableFailure ||
failure is GetStoredOidcConfigurationFailure || failure is GetStoredOidcConfigurationFailure ||
failure is GetOIDCIsAvailableFailure || failure is GetOIDCIsAvailableFailure ||
failure is GetOIDCConfigurationFailure ||
failure is GetTokenOIDCFailure) { failure is GetTokenOIDCFailure) {
_showFormLoginWithCredentialAction(); if (PlatformInfo.isMobile && loginFormType.value == LoginFormType.dnsLookupForm) {
_showPasswordForm();
} else {
_showCredentialForm();
}
} else if (failure is GetAuthenticatedAccountFailure) { } else if (failure is GetAuthenticatedAccountFailure) {
_checkOIDCIsAvailable(); _checkOIDCIsAvailable();
} else if (failure is GetSessionFailure) { } else if (failure is GetSessionFailure) {
clearAllData(); clearAllData();
} else if (failure is DNSLookupToGetJmapUrlFailure) {
_username = null;
_clearTextInputField();
_showBaseUrlForm();
} else { } else {
super.handleFailureViewState(failure); super.handleFailureViewState(failure);
} }
@@ -162,6 +167,8 @@ class LoginController extends ReloadableController {
_getTokenOIDCSuccess(success); _getTokenOIDCSuccess(success);
} else if (success is AuthenticationUserSuccess) { } else if (success is AuthenticationUserSuccess) {
_loginSuccessAction(success); _loginSuccessAction(success);
} else if (success is DNSLookupToGetJmapUrlSuccess) {
_handleDNSLookupToGetJmapUrlSuccess(success);
} else { } else {
super.handleSuccessViewState(success); super.handleSuccessViewState(success);
} }
@@ -169,14 +176,21 @@ class LoginController extends ReloadableController {
@override @override
void handleExceptionAction({Failure? failure, Exception? exception}) { void handleExceptionAction({Failure? failure, Exception? exception}) {
super.handleExceptionAction(failure: failure, exception: exception); logError('LoginController::handleExceptionAction:exception: $exception | failure: ${failure.runtimeType}');
if (failure is CheckOIDCIsAvailableFailure || if (failure is CheckOIDCIsAvailableFailure ||
failure is GetStoredOidcConfigurationFailure || failure is GetStoredOidcConfigurationFailure ||
failure is GetOIDCConfigurationFailure ||
failure is GetOIDCIsAvailableFailure || failure is GetOIDCIsAvailableFailure ||
failure is GetTokenOIDCFailure) { failure is GetTokenOIDCFailure) {
_showFormLoginWithCredentialAction(); if (PlatformInfo.isMobile && loginFormType.value == LoginFormType.dnsLookupForm) {
_showPasswordForm();
} else {
_showCredentialForm();
}
} else if (failure is GetSessionFailure) {
clearAllData();
} else { } else {
clearState(); super.handleExceptionAction(failure: failure, exception: exception);
} }
} }
@@ -197,19 +211,20 @@ class LoginController extends ReloadableController {
} }
void handleNextInUrlInputFormPress() { void handleNextInUrlInputFormPress() {
_saveRecentLoginUrl(); if (PlatformInfo.isMobile && _currentBaseUrl != null) {
_storeBaseUrlToCache(_currentBaseUrl!);
}
_checkOIDCIsAvailable(); _checkOIDCIsAvailable();
} }
void _checkOIDCIsAvailable() { void _checkOIDCIsAvailable() {
final baseUrl = _getBaseUrl(); if (_currentBaseUrl == null) {
if (baseUrl == null) {
dispatchState(Left(CheckOIDCIsAvailableFailure(CanNotFoundBaseUrl()))); dispatchState(Left(CheckOIDCIsAvailableFailure(CanNotFoundBaseUrl())));
} else { } else {
consumeState(_checkOIDCIsAvailableInteractor.execute( consumeState(_checkOIDCIsAvailableInteractor.execute(
OIDCRequest( OIDCRequest(
baseUrl: baseUrl.toString(), baseUrl: _currentBaseUrl!.toString(),
resourceUrl: baseUrl.origin resourceUrl: _currentBaseUrl!.origin
) )
)); ));
} }
@@ -217,46 +232,60 @@ class LoginController extends ReloadableController {
void _redirectToSSOLoginScreen(CheckOIDCIsAvailableSuccess success) { void _redirectToSSOLoginScreen(CheckOIDCIsAvailableSuccess success) {
_oidcResponse = success.oidcResponse; _oidcResponse = success.oidcResponse;
handleSSOPressed(); consumeState(_getOIDCIsAvailableInteractor.execute(
OIDCRequest(
baseUrl: _currentBaseUrl!.toString(),
resourceUrl: _currentBaseUrl!.origin
)
));
} }
void handleBackInCredentialForm() { void handleBackButtonAction() {
clearState(); clearState();
loginFormType.value = LoginFormType.baseUrlForm; if (loginFormType.value == LoginFormType.credentialForm) {
} _password = null;
_username = null;
void _showFormLoginWithCredentialAction() { usernameInputController.clear();
clearState(); passwordInputController.clear();
loginFormType.value = LoginFormType.credentialForm; loginFormType.value = LoginFormType.baseUrlForm;
} } else if (loginFormType.value == LoginFormType.passwordForm) {
_password = null;
void handleLoginPressed() { _baseUri = null;
TextInput.finishAutofillContext(); urlInputController.clear();
passwordInputController.clear();
_saveRecentLoginUsername(); loginFormType.value = LoginFormType.dnsLookupForm;
log('LoginController::handleLoginPressed(): ${loginFormType.value}');
if (loginFormType.value == LoginFormType.ssoForm) {
_getOIDCConfiguration();
} else {
final baseUrl = _getBaseUrl();
final userName = _parseUserName(_userNameText);
final password = _parsePassword(_passwordText);
_loginAction(baseUrl: baseUrl, userName: userName, password: password);
} }
} }
void handleSSOPressed() { void _showCredentialForm() {
final baseUrl = _getBaseUrl(); clearState();
if (baseUrl != null) { loginFormType.value = LoginFormType.credentialForm;
consumeState(_getOIDCIsAvailableInteractor.execute( userNameFocusNode.requestFocus();
OIDCRequest( }
baseUrl: baseUrl.toString(),
resourceUrl: baseUrl.origin void handleLoginPressed() {
) log('LoginController::handleLoginPressed:_currentBaseUrl: $_currentBaseUrl | _username: $_username | _password: $_password');
)); if (_currentBaseUrl == null) {
consumeState(Stream.value(Left(AuthenticationUserFailure(CanNotFoundBaseUrl()))));
} else if (_username == null) {
consumeState(Stream.value(Left(AuthenticationUserFailure(CanNotFoundUserName()))));
} else if (_password == null) {
consumeState(Stream.value(Left(AuthenticationUserFailure(CanNotFoundPassword()))));
} else { } else {
dispatchState(Left(GetOIDCIsAvailableFailure(CanNotFoundBaseUrl()))); if (PlatformInfo.isMobile && loginFormType.value == LoginFormType.credentialForm) {
TextInput.finishAutofillContext();
if (_username!.value.isEmail) {
_storeUsernameToCache(_username!.value);
}
}
consumeState(
_authenticationInteractor.execute(
baseUrl: _currentBaseUrl!,
userName: _username!,
password: _password!
)
);
} }
} }
@@ -264,12 +293,11 @@ class LoginController extends ReloadableController {
if (_oidcResponse != null) { if (_oidcResponse != null) {
consumeState(_getOIDCConfigurationInteractor.execute(_oidcResponse!)); consumeState(_getOIDCConfigurationInteractor.execute(_oidcResponse!));
} else { } else {
dispatchState(Left(GetOIDCIsAvailableFailure(CanNotFoundOIDCLinks()))); dispatchState(Left(GetOIDCConfigurationFailure(CanNotFoundOIDCLinks())));
} }
} }
void _getOIDCConfigurationSuccess(GetOIDCConfigurationSuccess success) { void _getOIDCConfigurationSuccess(GetOIDCConfigurationSuccess success) {
log('LoginController::_getOIDCConfigurationSuccess():success: $success');
if (PlatformInfo.isWeb) { if (PlatformInfo.isWeb) {
_authenticateOidcOnBrowserAction(success.oidcConfiguration); _authenticateOidcOnBrowserAction(success.oidcConfiguration);
} else { } else {
@@ -277,10 +305,9 @@ class LoginController extends ReloadableController {
} }
} }
void _getTokenOIDCAction(OIDCConfiguration config) async { void _getTokenOIDCAction(OIDCConfiguration config) {
final baseUri = _getBaseUrl(); if (_currentBaseUrl != null) {
if (baseUri != null) { consumeState(_getTokenOIDCInteractor.execute(_currentBaseUrl!, config));
consumeState(_getTokenOIDCInteractor.execute(baseUri, config));
} else { } else {
dispatchState(Left(GetTokenOIDCFailure(CanNotFoundBaseUrl()))); dispatchState(Left(GetTokenOIDCFailure(CanNotFoundBaseUrl())));
} }
@@ -289,9 +316,8 @@ class LoginController extends ReloadableController {
void _authenticateOidcOnBrowserAction(OIDCConfiguration config) async { void _authenticateOidcOnBrowserAction(OIDCConfiguration config) async {
_removeAuthDestinationUrlInSessionStorage(); _removeAuthDestinationUrlInSessionStorage();
final baseUri = _parseUri(AppConfig.baseUrl); if (_currentBaseUrl != null) {
if (baseUri != null) { consumeState(_authenticateOidcOnBrowserInteractor.execute(_currentBaseUrl!, config));
consumeState(_authenticateOidcOnBrowserInteractor.execute(baseUri, config));
} else { } else {
dispatchState(Left(AuthenticateOidcOnBrowserFailure(CanNotFoundBaseUrl()))); dispatchState(Left(AuthenticateOidcOnBrowserFailure(CanNotFoundBaseUrl())));
} }
@@ -305,9 +331,8 @@ class LoginController extends ReloadableController {
} }
void _getTokenOIDCSuccess(GetTokenOIDCSuccess success) { void _getTokenOIDCSuccess(GetTokenOIDCSuccess success) {
log('LoginController::_getTokenOIDCSuccess(): ${success.tokenOIDC.toString()}'); dynamicUrlInterceptors.setJmapUrl(_currentBaseUrl?.toString());
dynamicUrlInterceptors.setJmapUrl(_getBaseUrl().toString()); dynamicUrlInterceptors.changeBaseUrl(_currentBaseUrl?.toString());
dynamicUrlInterceptors.changeBaseUrl(_getBaseUrl().toString());
authorizationInterceptors.setTokenAndAuthorityOidc( authorizationInterceptors.setTokenAndAuthorityOidc(
newToken: success.tokenOIDC.toToken(), newToken: success.tokenOIDC.toToken(),
newConfig: success.configuration); newConfig: success.configuration);
@@ -317,36 +342,24 @@ class LoginController extends ReloadableController {
getSessionAction(); getSessionAction();
} }
void _loginAction({Uri? baseUrl, UserName? userName, Password? password}) {
consumeState(_authenticationInteractor.execute(
baseUrl: baseUrl,
userName: userName,
password: password
));
}
void _loginSuccessAction(AuthenticationUserSuccess success) { void _loginSuccessAction(AuthenticationUserSuccess success) {
dynamicUrlInterceptors.setJmapUrl(_getBaseUrl().toString()); dynamicUrlInterceptors.setJmapUrl(_currentBaseUrl?.toString());
dynamicUrlInterceptors.changeBaseUrl(_getBaseUrl().toString()); dynamicUrlInterceptors.changeBaseUrl(_currentBaseUrl?.toString());
authorizationInterceptors.setBasicAuthorization(_userNameText, _passwordText); authorizationInterceptors.setBasicAuthorization(_username!, _password!);
authorizationIsolateInterceptors.setBasicAuthorization(_userNameText, _passwordText); authorizationIsolateInterceptors.setBasicAuthorization(_username!, _password!);
getSessionAction(); getSessionAction();
} }
void formatUrl(String url) { void selectBaseUrlFromSuggestion(String url) {
log('LoginController::formatUrl(): $url'); final validUrl = url.isValid() ? url.removePrefix() : url;
if (url.isValid()) { log('LoginController::selectBaseUrlFromSuggestion:validUrl: $validUrl');
urlInputController.text = url.removePrefix(); urlInputController.text = validUrl;
} onBaseUrlChange(validUrl);
setUrlText(urlInputController.text);
} }
void _saveRecentLoginUrl() { void _storeBaseUrlToCache(Uri uri) {
if (_urlText?.isNotEmpty == true && PlatformInfo.isMobile) { log('LoginController::_storeBaseUrlToCache:uri: $uri');
final recentLoginUrl = RecentLoginUrl.now(_urlText!); _saveLoginUrlOnMobileInteractor.execute(RecentLoginUrl.now(uri.toString()));
log('LoginController::_saveRecentLoginUrl(): $recentLoginUrl');
_saveLoginUrlOnMobileInteractor.execute(recentLoginUrl);
}
} }
Future<List<RecentLoginUrl>> getAllRecentLoginUrlAction(String pattern) async { Future<List<RecentLoginUrl>> getAllRecentLoginUrlAction(String pattern) async {
@@ -360,19 +373,19 @@ class LoginController extends ReloadableController {
)); ));
} }
void setUsername(String username) { void selectUsernameFromSuggestion(RecentLoginUsername recentLoginUsername) {
log('LoginController::formatUsername(): $username'); log('LoginController::selectUsernameFromSuggestion():recentLoginUsername: $recentLoginUsername');
usernameInputController.text = username; usernameInputController.text = recentLoginUsername.username;
setUserNameText(usernameInputController.text); _username = UserName(recentLoginUsername.username);
if (loginFormType.value == LoginFormType.credentialForm) {
passFocusNode.requestFocus();
}
} }
void _saveRecentLoginUsername() { void _storeUsernameToCache(String userName) {
if(PlatformInfo.isWeb || _userNameText == null || _userNameText!.isEmpty || !_userNameText!.isEmail) { log('LoginController::_storeUsername():userName: $userName');
return ; _saveLoginUsernameOnMobileInteractor.execute(RecentLoginUsername.now(userName));
}
final recentLoginUsername = RecentLoginUsername.now(_userNameText!);
log('LoginController::_saveRecentLoginUsername(): $recentLoginUsername');
_saveLoginUsernameOnMobileInteractor.execute(recentLoginUsername);
} }
Future<List<RecentLoginUsername>> getAllRecentLoginUsernameAction(String pattern) async { Future<List<RecentLoginUsername>> getAllRecentLoginUsernameAction(String pattern) async {
@@ -386,11 +399,82 @@ class LoginController extends ReloadableController {
)); ));
} }
Uri? _getBaseUrl() => PlatformInfo.isWeb ? _parseUri(AppConfig.baseUrl) : _parseUri(_urlText); Uri? get _currentBaseUrl => PlatformInfo.isWeb
? Uri.tryParse(AppConfig.baseUrl)
: _baseUri;
void invokeDNSLookupToGetJmapUrl() {
log('LoginController::invokeDNSLookupToGetJmapUrl:_username $_username');
if (_username == null) {
consumeState(Stream.value(Left(AuthenticationUserFailure(CanNotFoundUserName()))));
} else {
if (_username!.value.isEmail) {
_storeUsernameToCache(_username!.value);
consumeState(_dnsLookupToGetJmapUrlInteractor.execute(_username!.value));
} else {
_username = null;
_clearTextInputField();
_showBaseUrlForm();
}
}
}
void _handleDNSLookupToGetJmapUrlSuccess(DNSLookupToGetJmapUrlSuccess success) {
onBaseUrlChange(success.jmapUrl);
_checkOIDCIsAvailable();
}
void _showBaseUrlForm() {
clearState();
loginFormType.value = LoginFormType.baseUrlForm;
baseUrlFocusNode.requestFocus();
}
void _showPasswordForm() {
clearState();
loginFormType.value = LoginFormType.passwordForm;
passFocusNode.requestFocus();
}
void onUsernameChange(String value) {
if (value.isEmpty) {
_username = null;
} else {
_username = UserName(value);
}
}
void onPasswordChange(String value) {
if (value.isEmpty) {
_password = null;
} else {
_password = Password(value);
}
}
void onBaseUrlChange(String value) {
if (value.isEmpty) {
_baseUri = null;
} else {
if (value.isValid()) {
log('LoginController::onBaseUrlChange:value: $value');
urlInputController.text = value.removePrefix();
}
_baseUri = Uri.tryParse(value.formatURLValid());
}
}
void _clearTextInputField() {
urlInputController.clear();
usernameInputController.clear();
passwordInputController.clear();
}
@override @override
void onClose() { void onClose() {
passFocusNode.dispose(); passFocusNode.dispose();
baseUrlFocusNode.dispose();
userNameFocusNode.dispose();
urlInputController.dispose(); urlInputController.dispose();
usernameInputController.dispose(); usernameInputController.dispose();
passwordInputController.dispose(); passwordInputController.dispose();
@@ -1,3 +1,7 @@
enum LoginFormType { enum LoginFormType {
none, baseUrlForm, credentialForm, ssoForm none,
baseUrlForm,
credentialForm,
passwordForm,
dnsLookupForm;
} }
+58 -45
View File
@@ -3,7 +3,6 @@ import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/utils/theme_utils.dart'; import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/presentation/views/text/type_ahead_form_field_builder.dart'; import 'package:core/presentation/views/text/type_ahead_form_field_builder.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/widget/recent_item_tile_widget.dart'; import 'package:tmail_ui_user/features/base/widget/recent_item_tile_widget.dart';
@@ -11,7 +10,12 @@ import 'package:tmail_ui_user/features/login/domain/model/recent_login_url.dart'
import 'package:tmail_ui_user/features/login/presentation/base_login_view.dart'; import 'package:tmail_ui_user/features/login/presentation/base_login_view.dart';
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart'; import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
import 'package:tmail_ui_user/features/login/presentation/privacy_link_widget.dart'; import 'package:tmail_ui_user/features/login/presentation/privacy_link_widget.dart';
import 'package:tmail_ui_user/features/login/presentation/widgets/dns_lookup_input_form.dart';
import 'package:tmail_ui_user/features/login/presentation/widgets/horizontal_progress_loading_button.dart';
import 'package:tmail_ui_user/features/login/presentation/widgets/login_back_button.dart';
import 'package:tmail_ui_user/features/login/presentation/widgets/login_input_decoration_builder.dart'; import 'package:tmail_ui_user/features/login/presentation/widgets/login_input_decoration_builder.dart';
import 'package:tmail_ui_user/features/login/presentation/widgets/login_message_widget.dart';
import 'package:tmail_ui_user/features/login/presentation/widgets/password_input_form.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class LoginView extends BaseLoginView { class LoginView extends BaseLoginView {
@@ -31,13 +35,16 @@ class LoginView extends BaseLoginView {
child: SafeArea( child: SafeArea(
child: _supportScrollForm(context) child: _supportScrollForm(context)
? Stack(children: [ ? Stack(children: [
Center(child: SingleChildScrollView( Center(
child: SingleChildScrollView(
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
child: _buildCenterForm(context))), child: _buildCenterForm(context)
)
),
Obx(() { Obx(() {
if (controller.loginFormType.value == LoginFormType.credentialForm if (controller.loginFormType.value == LoginFormType.passwordForm ||
|| controller.loginFormType.value == LoginFormType.ssoForm) { controller.loginFormType.value == LoginFormType.credentialForm) {
return _buildBackButton(context); return LoginBackButton(onBackAction: controller.handleBackButtonAction);
} }
return const SizedBox.shrink(); return const SizedBox.shrink();
}) })
@@ -45,9 +52,9 @@ class LoginView extends BaseLoginView {
: Stack(children: [ : Stack(children: [
_buildCenterForm(context), _buildCenterForm(context),
Obx(() { Obx(() {
if (controller.loginFormType.value == LoginFormType.credentialForm if (controller.loginFormType.value == LoginFormType.passwordForm ||
|| controller.loginFormType.value == LoginFormType.ssoForm) { controller.loginFormType.value == LoginFormType.credentialForm) {
return _buildBackButton(context); return LoginBackButton(onBackAction: controller.handleBackButtonAction);
} }
return const SizedBox.shrink(); return const SizedBox.shrink();
}) })
@@ -75,9 +82,28 @@ class LoginView extends BaseLoginView {
style: const TextStyle(fontSize: 32, color: AppColor.colorNameEmail, fontWeight: FontWeight.w900) style: const TextStyle(fontSize: 32, color: AppColor.colorNameEmail, fontWeight: FontWeight.w900)
) )
), ),
Obx(() => buildLoginMessage(context, controller.viewState.value)), Obx(() => LoginMessageWidget(
formType: controller.loginFormType.value,
viewState: controller.viewState.value,
)),
Obx(() { Obx(() {
switch (controller.loginFormType.value) { switch (controller.loginFormType.value) {
case LoginFormType.dnsLookupForm:
return DNSLookupInputForm(
textEditingController: controller.usernameInputController,
onTextChange: controller.onUsernameChange,
onTextSubmitted: (_) => controller.invokeDNSLookupToGetJmapUrl(),
suggestionsCallback: controller.getAllRecentLoginUsernameAction,
onSuggestionSelected: controller.selectUsernameFromSuggestion
);
case LoginFormType.passwordForm:
return PasswordInputForm(
key: const Key('password_input_form'),
textEditingController: controller.passwordInputController,
focusNode: controller.passFocusNode,
onTextChange: controller.onPasswordChange,
onTextSubmitted: (_) => controller.handleLoginPressed(),
);
case LoginFormType.baseUrlForm: case LoginFormType.baseUrlForm:
return _buildUrlInput(context); return _buildUrlInput(context);
case LoginFormType.credentialForm: case LoginFormType.credentialForm:
@@ -97,22 +123,6 @@ class LoginView extends BaseLoginView {
); );
} }
Widget _buildBackButton(BuildContext context) {
return Positioned(
left: 12,
top: 12,
child: IconButton(
key: const Key('login_arrow_back_button'),
onPressed: () => controller.handleBackInCredentialForm(),
icon: SvgPicture.asset(
controller.imagePaths.icBack,
alignment: Alignment.center,
colorFilter: AppColor.primaryColor.asFilter()
)
),
);
}
Widget _buildUrlInput(BuildContext context) { Widget _buildUrlInput(BuildContext context) {
return Padding( return Padding(
padding: const EdgeInsets.only(right: 24, left: 24, bottom: 24), padding: const EdgeInsets.only(right: 24, left: 24, bottom: 24),
@@ -120,19 +130,17 @@ class LoginView extends BaseLoginView {
controller: controller.urlInputController, controller: controller.urlInputController,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
keyboardType: TextInputType.url, keyboardType: TextInputType.url,
focusNode: controller.baseUrlFocusNode,
onTextChange: controller.onBaseUrlChange,
onTextSubmitted: (value) => controller.handleNextInUrlInputFormPress(), onTextSubmitted: (value) => controller.handleNextInUrlInputFormPress(),
onTextChange: controller.formatUrl,
decoration: (LoginInputDecorationBuilder() decoration: (LoginInputDecorationBuilder()
..setLabelText(AppLocalizations.of(context).prefix_https) ..setLabelText(AppLocalizations.of(context).prefix_https)
..setPrefixText(AppLocalizations.of(context).prefix_https)) ..setPrefixText(AppLocalizations.of(context).prefix_https))
.build(), .build(),
debounceDuration: const Duration(milliseconds: 300), debounceDuration: const Duration(milliseconds: 300),
suggestionsCallback: (pattern) async { suggestionsCallback: controller.getAllRecentLoginUrlAction,
controller.formatUrl(pattern); itemBuilder: (_, loginUrl) => RecentItemTileWidget(loginUrl),
return controller.getAllRecentLoginUrlAction(pattern); onSuggestionSelected: (loginUrl) => controller.selectBaseUrlFromSuggestion(loginUrl.url),
},
itemBuilder: (context, loginUrl) => RecentItemTileWidget(loginUrl, imagePath: controller.imagePaths),
onSuggestionSelected: (loginUrl) => controller.formatUrl(loginUrl.url),
suggestionsBoxDecoration: const SuggestionsBoxDecoration(borderRadius: BorderRadius.all(Radius.circular(14))), suggestionsBoxDecoration: const SuggestionsBoxDecoration(borderRadius: BorderRadius.all(Radius.circular(14))),
noItemsFoundBuilder: (context) => const SizedBox(), noItemsFoundBuilder: (context) => const SizedBox(),
hideOnEmpty: true, hideOnEmpty: true,
@@ -156,20 +164,23 @@ class LoginView extends BaseLoginView {
width: controller.responsiveUtils.getDeviceWidth(context),height: 48, width: controller.responsiveUtils.getDeviceWidth(context),height: 48,
child: ElevatedButton( child: ElevatedButton(
key: const Key('nextToCredentialForm'), key: const Key('nextToCredentialForm'),
style: ButtonStyle( style: ElevatedButton.styleFrom(
foregroundColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) => Colors.white), foregroundColor: Colors.white,
backgroundColor: MaterialStateProperty.resolveWith<Color>( backgroundColor: AppColor.primaryColor,
(Set<MaterialState> states) => AppColor.primaryColor), shape: RoundedRectangleBorder(
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
side: const BorderSide(width: 0, color: AppColor.primaryColor) side: const BorderSide(width: 0, color: AppColor.primaryColor)
)) )
), ),
child: Text(AppLocalizations.of(context).next, child: Text(AppLocalizations.of(context).next,
style: const TextStyle(fontSize: 16, color: Colors.white) style: const TextStyle(fontSize: 16, color: Colors.white)
), ),
onPressed: () { onPressed: () {
controller.handleNextInUrlInputFormPress(); if (controller.loginFormType.value == LoginFormType.dnsLookupForm) {
controller.invokeDNSLookupToGetJmapUrl();
} else {
controller.handleNextInUrlInputFormPress();
}
} }
) )
); );
@@ -195,27 +206,29 @@ class LoginView extends BaseLoginView {
return Obx(() => controller.viewState.value.fold( return Obx(() => controller.viewState.value.fold(
(failure) { (failure) {
switch (controller.loginFormType.value) { switch (controller.loginFormType.value) {
case LoginFormType.dnsLookupForm:
case LoginFormType.baseUrlForm: case LoginFormType.baseUrlForm:
return _buildNextButtonInContext(context); return _buildNextButtonInContext(context);
case LoginFormType.passwordForm:
case LoginFormType.credentialForm: case LoginFormType.credentialForm:
return _buildLoginButtonInContext(context); return _buildLoginButtonInContext(context);
case LoginFormType.ssoForm:
return _buildLoginButtonInContext(context);
default: default:
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
}, },
(success) { (success) {
if (success is LoadingState) { if (success is LoadingState) {
return buildLoadingCircularProgress(); return _supportScrollForm(context)
? const HorizontalProgressLoadingButton()
: _buildExpandedButton(context, const HorizontalProgressLoadingButton());
} else { } else {
switch (controller.loginFormType.value) { switch (controller.loginFormType.value) {
case LoginFormType.dnsLookupForm:
case LoginFormType.baseUrlForm: case LoginFormType.baseUrlForm:
return _buildNextButtonInContext(context); return _buildNextButtonInContext(context);
case LoginFormType.passwordForm:
case LoginFormType.credentialForm: case LoginFormType.credentialForm:
return _buildLoginButtonInContext(context); return _buildLoginButtonInContext(context);
case LoginFormType.ssoForm:
return _buildLoginButtonInContext(context);
default: default:
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
@@ -8,6 +8,7 @@ import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
import 'package:tmail_ui_user/features/login/presentation/base_login_view.dart'; import 'package:tmail_ui_user/features/login/presentation/base_login_view.dart';
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart'; import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
import 'package:tmail_ui_user/features/login/presentation/privacy_link_widget.dart'; import 'package:tmail_ui_user/features/login/presentation/privacy_link_widget.dart';
import 'package:tmail_ui_user/features/login/presentation/widgets/login_message_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class LoginView extends BaseLoginView { class LoginView extends BaseLoginView {
@@ -47,13 +48,14 @@ class LoginView extends BaseLoginView {
style: const TextStyle(fontSize: 32, color: AppColor.colorNameEmail, fontWeight: FontWeight.w900) style: const TextStyle(fontSize: 32, color: AppColor.colorNameEmail, fontWeight: FontWeight.w900)
) )
), ),
Obx(() => buildLoginMessage(context, controller.viewState.value)), Obx(() => LoginMessageWidget(
formType: controller.loginFormType.value,
viewState: controller.viewState.value,
)),
Obx(() { Obx(() {
switch (controller.loginFormType.value) { switch (controller.loginFormType.value) {
case LoginFormType.credentialForm: case LoginFormType.credentialForm:
return buildInputCredentialForm(context); return buildInputCredentialForm(context);
case LoginFormType.ssoForm:
return const SizedBox(height: 150);
default: default:
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
@@ -179,13 +181,14 @@ class LoginView extends BaseLoginView {
style: const TextStyle(fontSize: 32, color: AppColor.colorNameEmail, fontWeight: FontWeight.w900) style: const TextStyle(fontSize: 32, color: AppColor.colorNameEmail, fontWeight: FontWeight.w900)
) )
), ),
Obx(() => buildLoginMessage(context, controller.viewState.value)), Obx(() => LoginMessageWidget(
formType: controller.loginFormType.value,
viewState: controller.viewState.value,
)),
Obx(() { Obx(() {
switch (controller.loginFormType.value) { switch (controller.loginFormType.value) {
case LoginFormType.credentialForm: case LoginFormType.credentialForm:
return buildInputCredentialForm(context); return buildInputCredentialForm(context);
case LoginFormType.ssoForm:
return const SizedBox(height: 150);
default: default:
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
@@ -232,41 +235,12 @@ class LoginView extends BaseLoginView {
); );
} }
Widget _buildSSOButton(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 16, left: 24, right: 24),
width: controller.responsiveUtils.getDeviceWidth(context),height: 48,
child: ElevatedButton(
key: const Key('ssoSubmitForm'),
style: ButtonStyle(
foregroundColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) => Colors.white),
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) => AppColor.primaryColor),
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: const BorderSide(width: 0, color: AppColor.primaryColor)
))
),
child: Text(AppLocalizations.of(context).singleSignOn,
style: const TextStyle(fontSize: 16, color: Colors.white)
),
onPressed: () {
controller.handleSSOPressed();
}
)
);
}
Widget _buildLoadingProgress(BuildContext context) { Widget _buildLoadingProgress(BuildContext context) {
return Obx(() => controller.viewState.value.fold( return Obx(() => controller.viewState.value.fold(
(failure) { (failure) {
switch (controller.loginFormType.value) { switch (controller.loginFormType.value) {
case LoginFormType.baseUrlForm:
return const SizedBox.shrink();
case LoginFormType.credentialForm: case LoginFormType.credentialForm:
return buildLoginButton(context); return buildLoginButton(context);
case LoginFormType.ssoForm:
return _buildSSOButton(context);
default: default:
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
@@ -276,12 +250,8 @@ class LoginView extends BaseLoginView {
return buildLoadingCircularProgress(); return buildLoadingCircularProgress();
} else { } else {
switch (controller.loginFormType.value) { switch (controller.loginFormType.value) {
case LoginFormType.baseUrlForm:
return const SizedBox.shrink();
case LoginFormType.credentialForm: case LoginFormType.credentialForm:
return buildLoginButton(context); return buildLoginButton(context);
case LoginFormType.ssoForm:
return _buildSSOButton(context);
default: default:
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
@@ -5,9 +5,13 @@ import 'package:tmail_ui_user/main/routes/router_arguments.dart';
class LoginArguments extends RouterArguments { class LoginArguments extends RouterArguments {
final LoginFormType loginFormType; final LoginFormType loginFormType;
final String? baseUrl;
LoginArguments(this.loginFormType); LoginArguments(this.loginFormType, {this.baseUrl});
@override @override
List<Object?> get props => [loginFormType]; List<Object?> get props => [
loginFormType,
baseUrl
];
} }
@@ -0,0 +1,57 @@
import 'package:core/presentation/views/text/type_ahead_form_field_builder.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:tmail_ui_user/features/base/widget/recent_item_tile_widget.dart';
import 'package:tmail_ui_user/features/login/domain/model/recent_login_username.dart';
import 'package:tmail_ui_user/features/login/presentation/widgets/login_input_decoration_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class DNSLookupInputForm extends StatelessWidget {
final TextEditingController textEditingController;
final ValueChanged<String> onTextChange;
final ValueChanged<String> onTextSubmitted;
final SuggestionsCallback<RecentLoginUsername> suggestionsCallback;
final SuggestionSelectionCallback<RecentLoginUsername> onSuggestionSelected;
const DNSLookupInputForm({
super.key,
required this.textEditingController,
required this.onTextChange,
required this.onTextSubmitted,
required this.suggestionsCallback,
required this.onSuggestionSelected
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsetsDirectional.only(bottom: 16, start: 24, end: 24),
child: TypeAheadFormFieldBuilder<RecentLoginUsername>(
controller: textEditingController,
onTextChange: onTextChange,
onTextSubmitted: onTextSubmitted,
textInputAction: TextInputAction.next,
autocorrect: false,
autofillHints: const [AutofillHints.email],
keyboardType: TextInputType.emailAddress,
decoration: (LoginInputDecorationBuilder()
..setLabelText(AppLocalizations.of(context).email)
..setHintText(AppLocalizations.of(context).email)
).build(),
debounceDuration: const Duration(milliseconds: 300),
suggestionsCallback: suggestionsCallback,
itemBuilder: (_, loginUsername) => RecentItemTileWidget(loginUsername),
onSuggestionSelected: onSuggestionSelected,
suggestionsBoxDecoration: const SuggestionsBoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(14))
),
noItemsFoundBuilder: (context) => const SizedBox(),
hideOnEmpty: true,
hideOnError: true,
hideOnLoading: true,
),
);
}
}
@@ -0,0 +1,30 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class HorizontalProgressLoadingButton extends StatelessWidget {
const HorizontalProgressLoadingButton({super.key});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsetsDirectional.only(bottom: 16, start: 24, end: 24),
width: context.width,
height: 48,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.primaryColor,
disabledBackgroundColor: AppColor.primaryColor,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
)
),
onPressed: null,
child: const LinearProgressIndicator(
color: Colors.white,
backgroundColor: AppColor.primaryColor
)
)
);
}
}
@@ -0,0 +1,30 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
class LoginBackButton extends StatelessWidget {
final _imagePath = Get.find<ImagePaths>();
final VoidCallback onBackAction;
LoginBackButton({super.key, required this.onBackAction});
@override
Widget build(BuildContext context) {
return PositionedDirectional(
start: 12,
top: 12,
child: IconButton(
onPressed: onBackAction,
icon: SvgPicture.asset(
_imagePath.icBack,
alignment: Alignment.center,
colorFilter: AppColor.primaryColor.asFilter()
)
),
);
}
}
@@ -0,0 +1,83 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/login/domain/state/dns_lookup_to_get_jmap_url_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_oidc_configuration_state.dart';
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/utils/message_toast_utils.dart';
class LoginMessageWidget extends StatelessWidget {
static const double _loginTextFieldWidthSmallScreen = 280.0;
static const double _loginTextFieldWidthLargeScreen = 320.0;
final LoginFormType formType;
final Either<Failure, Success> viewState;
const LoginMessageWidget({
super.key,
required this.formType,
required this.viewState
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsetsDirectional.only(
top: 11,
bottom: 36,
start: 58,
end: 58
),
child: SizedBox(
width: context.width < ResponsiveUtils.minTabletWidth
? _loginTextFieldWidthSmallScreen
: _loginTextFieldWidthLargeScreen,
child: Text(
viewState.fold(
(failure) {
if (failure is GetOIDCConfigurationFailure) {
return AppLocalizations.of(context).canNotVerifySSOConfiguration;
} else if (failure is DNSLookupToGetJmapUrlFailure) {
return AppLocalizations.of(context).dnsLookupLoginMessage;
} else if (failure is FeatureFailure) {
final errorMessage = MessageToastUtils.getMessageByException(context, failure.exception);
return errorMessage ?? AppLocalizations.of(context).unknownError;
} else {
return AppLocalizations.of(context).unknownError;
}
},
(success) {
if (formType == LoginFormType.credentialForm) {
return AppLocalizations.of(context).loginInputCredentialMessage;
} else if (formType == LoginFormType.dnsLookupForm) {
return AppLocalizations.of(context).dnsLookupLoginMessage;
} else if (formType == LoginFormType.passwordForm) {
return AppLocalizations.of(context).enterYourPasswordToSignIn;
} else if (formType == LoginFormType.baseUrlForm) {
return AppLocalizations.of(context).loginInputUrlMessage;
} else {
return '';
}
}
),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w400,
color: viewState.fold(
(failure) => AppColor.textFieldErrorBorderColor,
(success) => AppColor.colorNameEmail
)
),
)
)
);
}
}
@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/login/presentation/widgets/login_text_input_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class PasswordInputForm extends StatelessWidget {
final TextEditingController textEditingController;
final FocusNode focusNode;
final ValueChanged<String> onTextChange;
final ValueChanged<String> onTextSubmitted;
const PasswordInputForm({
super.key,
required this.textEditingController,
required this.focusNode,
required this.onTextChange,
required this.onTextSubmitted
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsetsDirectional.only(bottom: 16, start: 24, end: 24),
child: LoginTextInputBuilder(
controller: textEditingController,
autofillHints: const [AutofillHints.password],
textInputAction: TextInputAction.done,
hintText: AppLocalizations.of(context).password,
focusNode: focusNode,
onTextChange: onTextChange,
onSubmitted: onTextSubmitted,
),
);
}
}
@@ -16,8 +16,10 @@ class CreateDefaultMailboxAllSuccess extends UIActionState {
} }
class CreateDefaultMailboxFailure extends FeatureFailure { class CreateDefaultMailboxFailure extends FeatureFailure {
CreateDefaultMailboxFailure(dynamic exception) : super(exception: exception); final jmap.State? currentMailboxState;
CreateDefaultMailboxFailure(this.currentMailboxState, dynamic exception) : super(exception: exception);
@override @override
List<Object?> get props => [exception]; List<Object?> get props => [currentMailboxState, exception];
} }
@@ -1,9 +1,10 @@
import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/state/success.dart';
import 'package:core/utils/app_logger.dart'; import 'package:core/utils/app_logger.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart' as dartz;
import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/core/state.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart';
import 'package:tmail_ui_user/features/mailbox/domain/state/create_default_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/create_default_mailbox_state.dart';
@@ -13,30 +14,37 @@ class CreateDefaultMailboxInteractor {
CreateDefaultMailboxInteractor(this._mailboxRepository); CreateDefaultMailboxInteractor(this._mailboxRepository);
Stream<Either<Failure, Success>> execute( Stream<dartz.Either<Failure, Success>> execute(
Session session, Session session,
AccountId accountId, AccountId accountId,
List<Role> listRole List<Role> listRole
) async* { ) async* {
final currentMailboxState = await _getCurrentMailboxState(session, accountId);
try { try {
yield Right<Failure, Success>(CreateDefaultMailboxLoading()); yield dartz.Right<Failure, Success>(CreateDefaultMailboxLoading());
final currentMailboxState = await _mailboxRepository.getMailboxState(session, accountId);
final listMailboxCreated = await _mailboxRepository.createDefaultMailbox( final listMailboxCreated = await _mailboxRepository.createDefaultMailbox(
session, session,
accountId, accountId,
listRole listRole
); );
log('CreateDefaultMailboxInteractor::execute:listMailboxCreated: ${listMailboxCreated.length}');
await _mailboxRepository.setRoleDefaultMailbox( await _mailboxRepository.setRoleDefaultMailbox(
session, session,
accountId, accountId,
listMailboxCreated listMailboxCreated
); );
yield Right<Failure, Success>(CreateDefaultMailboxAllSuccess(currentMailboxState: currentMailboxState)); yield dartz.Right<Failure, Success>(CreateDefaultMailboxAllSuccess(currentMailboxState: currentMailboxState));
} catch (e) { } catch (e) {
yield Left<Failure, Success>(CreateDefaultMailboxFailure(e)); yield dartz.Left<Failure, Success>(CreateDefaultMailboxFailure(currentMailboxState, e));
}
}
Future<State?> _getCurrentMailboxState(Session session, AccountId accountId) async {
try {
final currentMailboxState = await _mailboxRepository.getMailboxState(session, accountId);
log('CreateDefaultMailboxInteractor::_getCurrentMailboxState:currentMailboxState: $currentMailboxState');
return currentMailboxState;
} catch (e) {
return null;
} }
} }
} }
@@ -193,7 +193,7 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
} else if (failure is RefreshChangesAllMailboxFailure) { } else if (failure is RefreshChangesAllMailboxFailure) {
_clearNewFolderId(); _clearNewFolderId();
} else if (failure is CreateDefaultMailboxFailure) { } else if (failure is CreateDefaultMailboxFailure) {
refreshAllMailbox(); _refreshMailboxChanges(currentMailboxState: failure.currentMailboxState);
} }
} }
@@ -127,9 +127,7 @@ class MailboxTree with EquatableMixin {
} }
Map<Role, PresentationMailbox> get mapPresentationMailboxByRole { Map<Role, PresentationMailbox> get mapPresentationMailboxByRole {
if (root.childrenItems?.isEmpty == true) { if (root.childrenItems?.isNotEmpty == true) {
return {};
} else {
final listPresentationMailboxHasRole = root.childrenItems! final listPresentationMailboxHasRole = root.childrenItems!
.where((node) => node.item.role != null) .where((node) => node.item.role != null)
.map((node) => node.item) .map((node) => node.item)
@@ -139,6 +137,8 @@ class MailboxTree with EquatableMixin {
for (var mailbox in listPresentationMailboxHasRole) for (var mailbox in listPresentationMailboxHasRole)
mailbox.role!: mailbox mailbox.role!: mailbox
}; };
} else {
return {};
} }
} }
@@ -8,6 +8,7 @@ import 'package:jmap_dart_client/jmap/mail/mailbox/get/get_mailbox_method.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/get/get_mailbox_response.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/get/get_mailbox_response.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox_filter_condition.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox_filter_condition.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/query/query_mailbox_method.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/query/query_mailbox_method.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/exceptions/spam_report_exception.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/unread_spam_emails_response.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/unread_spam_emails_response.dart';
import 'package:tmail_ui_user/main/error/capability_validator.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart';
@@ -50,11 +51,10 @@ class SpamReportApi {
final mailboxResponse = result final mailboxResponse = result
.parse<GetMailboxResponse>(getMailboxInvocation.methodCallId, GetMailboxResponse.deserialize); .parse<GetMailboxResponse>(getMailboxInvocation.methodCallId, GetMailboxResponse.deserialize);
return Future.sync(() async { if (mailboxResponse?.list.isNotEmpty == true) {
final unreadSpamMailbox = mailboxResponse?.list.first; return UnreadSpamEmailsResponse(unreadSpamMailbox: mailboxResponse!.list.first);
return UnreadSpamEmailsResponse(unreadSpamMailbox: unreadSpamMailbox); } else {
}).catchError((error) { throw NotFoundSpamMailboxException();
throw error; }
});
} }
} }
@@ -1,4 +1,6 @@
class NotFoundLastTimeDismissedSpamReportException implements Exception {} class NotFoundLastTimeDismissedSpamReportException implements Exception {}
class NotFoundSpamMailboxCachedException implements Exception {} class NotFoundSpamMailboxCachedException implements Exception {}
class NotFoundSpamMailboxException implements Exception {}
@@ -0,0 +1,20 @@
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
import 'package:tmail_ui_user/main/routes/router_arguments.dart';
class SessionArguments extends RouterArguments {
final LoginFormType loginFormType;
final String baseUrl;
SessionArguments(
this.loginFormType,
this.baseUrl
);
@override
List<Object?> get props => [
loginFormType,
baseUrl,
];
}
+13 -1
View File
@@ -1,5 +1,5 @@
{ {
"@@last_modified": "2023-11-14T16:28:55.718679", "@@last_modified": "2023-11-10T16:53:43.713688",
"initializing_data": "Initializing data...", "initializing_data": "Initializing data...",
"@initializing_data": { "@initializing_data": {
"type": "text", "type": "text",
@@ -3481,5 +3481,17 @@
"type": "text", "type": "text",
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
},
"dnsLookupLoginMessage": "To login and access your message please enter your email",
"@dnsLookupLoginMessage": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"enterYourPasswordToSignIn": "Enter your password to sign in",
"@enterYourPasswordToSignIn": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
} }
} }
@@ -18,6 +18,7 @@ import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager
import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart'; import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart';
import 'package:tmail_ui_user/features/login/data/network/config/authorization_interceptors.dart'; import 'package:tmail_ui_user/features/login/data/network/config/authorization_interceptors.dart';
import 'package:tmail_ui_user/features/login/data/network/config/time_out_interceptors.dart'; import 'package:tmail_ui_user/features/login/data/network/config/time_out_interceptors.dart';
import 'package:tmail_ui_user/features/login/data/network/dns_service.dart';
import 'package:tmail_ui_user/features/login/data/network/oidc_http_client.dart'; import 'package:tmail_ui_user/features/login/data/network/oidc_http_client.dart';
import 'package:tmail_ui_user/features/login/data/utils/library_platform/app_auth_plugin/app_auth_plugin.dart'; import 'package:tmail_ui_user/features/login/data/utils/library_platform/app_auth_plugin/app_auth_plugin.dart';
import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_api.dart'; import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_api.dart';
@@ -41,6 +42,7 @@ class NetworkBindings extends Bindings {
_bindingDio(); _bindingDio();
_bindingApi(); _bindingApi();
_bindingTransformer(); _bindingTransformer();
_bindingServices();
_bindingException(); _bindingException();
} }
@@ -119,4 +121,8 @@ class NetworkBindings extends Bindings {
Get.put(HtmlTransform(Get.find<DioClient>(), Get.find<HtmlEscape>())); Get.put(HtmlTransform(Get.find<DioClient>(), Get.find<HtmlEscape>()));
Get.put(HtmlAnalyzer(Get.find<HtmlTransform>())); Get.put(HtmlAnalyzer(Get.find<HtmlTransform>()));
} }
void _bindingServices() {
Get.put(DNSService());
}
} }
@@ -3610,4 +3610,18 @@ class AppLocalizations {
name: 'notFoundSession', name: 'notFoundSession',
); );
} }
String get dnsLookupLoginMessage {
return Intl.message(
'To login and access your message please enter your email',
name: 'dnsLookupLoginMessage'
);
}
String get enterYourPasswordToSignIn {
return Intl.message(
'Enter your password to sign in',
name: 'enterYourPasswordToSignIn'
);
}
} }
+36
View File
@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
import 'package:tmail_ui_user/features/login/data/network/oidc_error.dart';
import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart';
import 'package:tmail_ui_user/main/exceptions/remote_exception.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class MessageToastUtils {
static String? getMessageByException(BuildContext context, dynamic exception) {
if (exception is CanNotFoundBaseUrl) {
return AppLocalizations.of(context).requiredUrl;
} else if (exception is CanNotFoundUserName) {
return AppLocalizations.of(context).requiredEmail;
} else if (exception is CanNotFoundPassword) {
return AppLocalizations.of(context).requiredPassword;
} else if (exception is CanNotFoundOIDCLinks) {
return AppLocalizations.of(context).ssoNotAvailable;
} else if (exception is CanNotFoundToken) {
return AppLocalizations.of(context).canNotGetToken;
} else if (exception is ConnectionTimeout || exception is BadGateway || exception is SocketError) {
return AppLocalizations.of(context).wrongUrlMessage;
} else if (exception is BadCredentialsException) {
return AppLocalizations.of(context).badCredentials;
} else if (exception is ConnectionError) {
return AppLocalizations.of(context).connectionError;
} else if (exception is UnknownError && exception.message != null) {
return '[${exception.code ?? ''}] ${exception.message}';
} else if (exception is NotFoundSessionException) {
return AppLocalizations.of(context).notFoundSession;
} else {
return null;
}
}
}