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:
@@ -105,7 +105,7 @@ class FileUtils {
|
||||
log('FileUtils::removeFolder():folderPath: $folderPath');
|
||||
final dir = Directory(folderPath);
|
||||
if (await dir.exists()) {
|
||||
await dir.delete(recursive: true);
|
||||
await dir.delete(recursive: true);
|
||||
}
|
||||
} catch (e) {
|
||||
logError('FileUtils::removeFolder():EXCEPTION: $e');
|
||||
|
||||
@@ -141,21 +141,7 @@ abstract class BaseController extends GetxController
|
||||
);
|
||||
}
|
||||
return error;
|
||||
} else if (error is BadCredentialsException) {
|
||||
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
|
||||
);
|
||||
}
|
||||
} else if (error is BadCredentialsException || error is ConnectionError) {
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -166,6 +152,22 @@ abstract class BaseController extends GetxController
|
||||
|
||||
void 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) {
|
||||
return;
|
||||
}
|
||||
@@ -182,7 +184,7 @@ abstract class BaseController extends GetxController
|
||||
} else {
|
||||
await clearDataAndGoToLoginPage();
|
||||
}
|
||||
} else if (failure is GetFCMSubscriptionLocalFailure ||
|
||||
} else if (failure is GetFCMSubscriptionLocalFailure ||
|
||||
failure is DestroySubscriptionFailure) {
|
||||
await clearDataAndGoToLoginPage();
|
||||
}
|
||||
@@ -326,9 +328,9 @@ abstract class BaseController extends GetxController
|
||||
log('BaseController::clearDataAndGoToLoginPage:');
|
||||
await clearAllData();
|
||||
goToLogin(arguments: LoginArguments(
|
||||
isAuthenticatedWithOidc
|
||||
? LoginFormType.ssoForm
|
||||
: LoginFormType.credentialForm
|
||||
PlatformInfo.isWeb
|
||||
? LoginFormType.none
|
||||
: LoginFormType.dnsLookupForm
|
||||
));
|
||||
}
|
||||
|
||||
@@ -346,7 +348,9 @@ abstract class BaseController extends GetxController
|
||||
cachingManager.clearAll(),
|
||||
languageCacheManager.removeLanguage(),
|
||||
]);
|
||||
await cachingManager.clearAllFileInStorage();
|
||||
if (PlatformInfo.isMobile) {
|
||||
await cachingManager.clearAllFileInStorage();
|
||||
}
|
||||
authorizationInterceptors.clear();
|
||||
authorizationIsolateInterceptors.clear();
|
||||
if (_isFcmEnabled) {
|
||||
@@ -361,7 +365,9 @@ abstract class BaseController extends GetxController
|
||||
cachingManager.clearAll(),
|
||||
languageCacheManager.removeLanguage(),
|
||||
]);
|
||||
await cachingManager.clearAllFileInStorage();
|
||||
if (PlatformInfo.isMobile) {
|
||||
await cachingManager.clearAllFileInStorage();
|
||||
}
|
||||
authorizationIsolateInterceptors.clear();
|
||||
authorizationInterceptors.clear();
|
||||
if (_isFcmEnabled) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:core/presentation/state/failure.dart';
|
||||
import 'package:core/presentation/state/success.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:jmap_dart_client/jmap/account_id.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/oidc/token_oidc.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/state/get_session_state.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_credential_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/manage_account/presentation/vacation/vacation_interactors_bindings.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/routes/route_navigation.dart';
|
||||
import 'package:tmail_ui_user/main/utils/message_toast_utils.dart';
|
||||
|
||||
abstract class ReloadableController extends BaseController {
|
||||
final GetSessionInteractor _getSessionInteractor = Get.find<GetSessionInteractor>();
|
||||
@@ -37,16 +35,19 @@ abstract class ReloadableController extends BaseController {
|
||||
void handleFailureViewState(Failure failure) {
|
||||
super.handleFailureViewState(failure);
|
||||
if (failure is GetCredentialFailure) {
|
||||
goToLogin(arguments: LoginArguments(LoginFormType.credentialForm));
|
||||
goToLogin(arguments: LoginArguments(LoginFormType.dnsLookupForm));
|
||||
} else if (failure is GetSessionFailure) {
|
||||
_handleGetSessionFailure(
|
||||
sessionCacheException: failure.exception,
|
||||
sessionRemoteException: failure.remoteException
|
||||
);
|
||||
_handleGetSessionFailure(failure.exception);
|
||||
} else if (failure is GetStoredTokenOidcFailure) {
|
||||
goToLogin(arguments: LoginArguments(LoginFormType.none));
|
||||
} 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());
|
||||
}
|
||||
|
||||
void _handleGetSessionFailure({
|
||||
dynamic sessionRemoteException,
|
||||
dynamic sessionCacheException
|
||||
}) {
|
||||
showToastErrorMessageSessionFailure(
|
||||
sessionRemoteException: sessionRemoteException,
|
||||
sessionCacheException: sessionCacheException,
|
||||
);
|
||||
void _handleGetSessionFailure(dynamic exception) {
|
||||
if (currentContext != null || currentOverlayContext != null) {
|
||||
appToast.showToastErrorMessage(
|
||||
currentOverlayContext!,
|
||||
MessageToastUtils.getMessageByException(currentContext!, exception) ?? AppLocalizations.of(currentContext!).unknownError
|
||||
);
|
||||
}
|
||||
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) {
|
||||
final session = success.session;
|
||||
final personalAccount = session.personalAccount;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
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';
|
||||
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';
|
||||
|
||||
@@ -9,13 +10,15 @@ class RecentItemTileWidget<T> extends StatelessWidget {
|
||||
|
||||
final T item;
|
||||
final EdgeInsets? contentPadding;
|
||||
final ImagePaths imagePath;
|
||||
final _imagePath = Get.find<ImagePaths>();
|
||||
|
||||
const RecentItemTileWidget(this.item, {
|
||||
required this.imagePath,
|
||||
RecentItemTileWidget(
|
||||
this.item,
|
||||
{
|
||||
Key? key,
|
||||
this.contentPadding,
|
||||
}) : super(key: key);
|
||||
}
|
||||
) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -23,14 +26,17 @@ class RecentItemTileWidget<T> extends StatelessWidget {
|
||||
padding: contentPadding ?? const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset(imagePath.icClockSB),
|
||||
SvgPicture.asset(_imagePath.icClockSB),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(_getTitle(item),
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Colors.black)),
|
||||
child: Text(
|
||||
_getTitle(item),
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Colors.black
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -129,12 +129,10 @@ class CachingManager {
|
||||
}
|
||||
|
||||
Future<void> clearAllFileInStorage() async {
|
||||
if (PlatformInfo.isMobile) {
|
||||
await Future.wait([
|
||||
_fileUtils.removeFolder(CachingConstants.newEmailsContentFolderName),
|
||||
_fileUtils.removeFolder(CachingConstants.openedEmailContentFolderName),
|
||||
]);
|
||||
}
|
||||
await Future.wait([
|
||||
_fileUtils.removeFolder(CachingConstants.newEmailsContentFolderName),
|
||||
_fileUtils.removeFolder(CachingConstants.openedEmailContentFolderName),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> _clearSendingEmailCache() async {
|
||||
|
||||
@@ -14,7 +14,6 @@ class GetSessionSuccess extends UIState {
|
||||
}
|
||||
|
||||
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) {
|
||||
yield* _getStoredSessionFromCache(remoteException: e);
|
||||
} else {
|
||||
yield Left<Failure, Success>(GetSessionFailure(exception: e));
|
||||
yield Left<Failure, Success>(GetSessionFailure(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Stream<Either<Failure, Success>> _getStoredSessionFromCache({dynamic remoteException}) async* {
|
||||
try {
|
||||
log('GetSessionInteractor::_getStoredSessionFromCache:');
|
||||
log('GetSessionInteractor::_getStoredSessionFromCache:remoteException: $remoteException');
|
||||
yield Right<Failure, Success>(GetSessionLoading());
|
||||
final session = await sessionRepository.getStoredSession();
|
||||
yield Right<Failure, Success>(GetSessionSuccess(session));
|
||||
} catch (e) {
|
||||
yield Left<Failure, Success>(GetSessionFailure(
|
||||
exception: e,
|
||||
remoteException: remoteException
|
||||
));
|
||||
yield Left<Failure, Success>(GetSessionFailure(remoteException ?? e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,9 @@ class DNSService {
|
||||
}
|
||||
final url = _removeDotAtEndOfString(listFieldData.last);
|
||||
log('DNSService::_parsingUrlFromDataResourceRecord:url: $url');
|
||||
if (url.trim().isEmpty) {
|
||||
throw NotFoundUrlException();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import 'package:core/presentation/state/failure.dart';
|
||||
import 'package:core/presentation/state/success.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
||||
import 'package:model/account/authentication_type.dart';
|
||||
import 'package:model/account/password.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/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/authentication_repository.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
|
||||
@@ -24,33 +22,25 @@ class AuthenticationInteractor {
|
||||
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 {
|
||||
yield Right(AuthenticationUserLoading());
|
||||
|
||||
if (baseUrl != null && userName != null && password != null) {
|
||||
final user = await authenticationRepository.authenticationUser(baseUrl, userName, password);
|
||||
await Future.wait([
|
||||
credentialRepository.saveBaseUrl(baseUrl),
|
||||
credentialRepository.storeAuthenticationInfo(AuthenticationInfoCache(userName.value, password.value)),
|
||||
_accountRepository.setCurrentAccount(PersonalAccount(
|
||||
userName.value,
|
||||
AuthenticationType.basic,
|
||||
isSelected: true
|
||||
))
|
||||
]);
|
||||
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));
|
||||
}
|
||||
final user = await authenticationRepository.authenticationUser(baseUrl, userName, password);
|
||||
await Future.wait([
|
||||
credentialRepository.saveBaseUrl(baseUrl),
|
||||
credentialRepository.storeAuthenticationInfo(AuthenticationInfoCache(userName.value, password.value)),
|
||||
_accountRepository.setCurrentAccount(PersonalAccount(
|
||||
userName.value,
|
||||
AuthenticationType.basic,
|
||||
isSelected: true
|
||||
))
|
||||
]);
|
||||
yield Right(AuthenticationUserSuccess(user));
|
||||
} catch (e) {
|
||||
logError('AuthenticationInteractor::execute(): $e');
|
||||
yield Left(AuthenticationUserFailure(e));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,11 @@
|
||||
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:dartz/dartz.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.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/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/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_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_text_input_builder.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> {
|
||||
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) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 16, left: 24, right: 24),
|
||||
width: controller.responsiveUtils.getDeviceWidth(context),height: 48,
|
||||
margin: const EdgeInsetsDirectional.only(bottom: 16, start: 24, end: 24),
|
||||
width: controller.responsiveUtils.getDeviceWidth(context),
|
||||
height: 48,
|
||||
child: ElevatedButton(
|
||||
key: const Key('loginSubmitForm'),
|
||||
style: ButtonStyle(
|
||||
foregroundColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) => Colors.white),
|
||||
backgroundColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) => AppColor.primaryColor),
|
||||
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: AppColor.primaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
side: const BorderSide(width: 0, color: AppColor.primaryColor)
|
||||
))
|
||||
)
|
||||
),
|
||||
onPressed: controller.handleLoginPressed,
|
||||
child: Text(
|
||||
@@ -135,7 +57,8 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
|
||||
return TypeAheadFormFieldBuilder<RecentLoginUsername>(
|
||||
key: const Key('login_username_input'),
|
||||
controller: controller.usernameInputController,
|
||||
onTextChange: controller.setUserNameText,
|
||||
onTextChange: controller.onUsernameChange,
|
||||
focusNode: controller.userNameFocusNode,
|
||||
textInputAction: TextInputAction.next,
|
||||
autocorrect: false,
|
||||
autofillHints: const [AutofillHints.email],
|
||||
@@ -146,11 +69,8 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
|
||||
.build(),
|
||||
debounceDuration: const Duration(milliseconds: 300),
|
||||
suggestionsCallback: controller.getAllRecentLoginUsernameAction,
|
||||
itemBuilder: (context, loginUsername) => RecentItemTileWidget(loginUsername, imagePath: controller.imagePaths),
|
||||
onSuggestionSelected: (recentUsername) {
|
||||
controller.setUsername(recentUsername.username);
|
||||
controller.passFocusNode.requestFocus();
|
||||
},
|
||||
itemBuilder: (_, loginUsername) => RecentItemTileWidget(loginUsername),
|
||||
onSuggestionSelected: controller.selectUsernameFromSuggestion,
|
||||
suggestionsBoxDecoration: const SuggestionsBoxDecoration(borderRadius: BorderRadius.all(Radius.circular(14))),
|
||||
noItemsFoundBuilder: (context) => const SizedBox(),
|
||||
hideOnEmpty: true,
|
||||
@@ -167,8 +87,8 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
|
||||
textInputAction: TextInputAction.done,
|
||||
hintText: AppLocalizations.of(context).password,
|
||||
focusNode: controller.passFocusNode,
|
||||
onTextChange: controller.setPasswordText,
|
||||
onSubmitted: (value) => controller.handleLoginPressed(),
|
||||
onTextChange: controller.onPasswordChange,
|
||||
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: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_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_username_datasource.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_username_datasource_impl.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_username_repository_impl.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/datasource/login_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_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_repository_impl.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/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_username_repository.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/repository/login_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/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/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_username_on_mobile_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/presentation/login_controller.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 {
|
||||
|
||||
@@ -45,24 +46,26 @@ class LoginBindings extends BaseBindings {
|
||||
Get.find<GetAllRecentLoginUrlOnMobileInteractor>(),
|
||||
Get.find<SaveLoginUsernameOnMobileInteractor>(),
|
||||
Get.find<GetAllRecentLoginUsernameOnMobileInteractor>(),
|
||||
Get.find<DNSLookupToGetJmapUrlInteractor>(),
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
void bindingsDataSource() {
|
||||
Get.lazyPut<LoginUrlDataSource>(() => Get.find<LoginUrlDataSourceImpl>());
|
||||
Get.lazyPut<LoginUsernameDataSource>(() => Get.find<LoginUsernameDataSourceImpl>());
|
||||
|
||||
Get.lazyPut<LoginDataSource>(() => Get.find<LoginDataSourceImpl>());
|
||||
}
|
||||
|
||||
@override
|
||||
void bindingsDataSourceImpl() {
|
||||
Get.lazyPut(() => LoginUrlDataSourceImpl(
|
||||
Get.lazyPut(() => HiveLoginDataSourceImpl(
|
||||
Get.find<RecentLoginUrlCacheClient>(),
|
||||
Get.find<CacheExceptionThrower>()));
|
||||
Get.lazyPut(() => LoginUsernameDataSourceImpl(
|
||||
Get.find<RecentLoginUsernameCacheClient>(),
|
||||
Get.find<CacheExceptionThrower>()));
|
||||
Get.find<CacheExceptionThrower>()
|
||||
));
|
||||
Get.lazyPut(() => LoginDataSourceImpl(
|
||||
Get.find<DNSService>(),
|
||||
Get.find<RemoteExceptionThrower>(),
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -90,28 +93,25 @@ class LoginBindings extends BaseBindings {
|
||||
Get.lazyPut(() => GetStoredOidcConfigurationInteractor(
|
||||
Get.find<AuthenticationOIDCRepository>(),
|
||||
));
|
||||
Get.lazyPut(() => SaveLoginUrlOnMobileInteractor(
|
||||
Get.find<LoginUrlRepository>(),
|
||||
));
|
||||
Get.lazyPut(() => GetAllRecentLoginUrlOnMobileInteractor(Get.find<LoginUrlRepository>()));
|
||||
Get.lazyPut(() => SaveLoginUsernameOnMobileInteractor(
|
||||
Get.find<LoginUsernameRepository>(),
|
||||
));
|
||||
Get.lazyPut(() => GetAllRecentLoginUsernameOnMobileInteractor(
|
||||
Get.find<LoginUsernameRepository>()
|
||||
));
|
||||
Get.lazyPut(() => SaveLoginUrlOnMobileInteractor(Get.find<LoginRepository>(),));
|
||||
Get.lazyPut(() => GetAllRecentLoginUrlOnMobileInteractor(Get.find<LoginRepository>()));
|
||||
Get.lazyPut(() => SaveLoginUsernameOnMobileInteractor(Get.find<LoginRepository>(),));
|
||||
Get.lazyPut(() => GetAllRecentLoginUsernameOnMobileInteractor(Get.find<LoginRepository>()));
|
||||
Get.lazyPut(() => DNSLookupToGetJmapUrlInteractor(Get.find<LoginRepository>()));
|
||||
}
|
||||
|
||||
@override
|
||||
void bindingsRepository() {
|
||||
Get.lazyPut<LoginUrlRepository>(() => Get.find<LoginUrlRepositoryImpl>());
|
||||
Get.lazyPut<LoginUsernameRepository>(() => Get.find<LoginUsernameRepositoryImpl>());
|
||||
|
||||
Get.lazyPut<LoginRepository>(() => Get.find<LoginRepositoryImpl>());
|
||||
}
|
||||
|
||||
@override
|
||||
void bindingsRepositoryImpl() {
|
||||
Get.lazyPut(() => LoginUrlRepositoryImpl(Get.find<LoginUrlDataSource>()));
|
||||
Get.lazyPut(() => LoginUsernameRepositoryImpl(Get.find<LoginUsernameDataSource>()));
|
||||
Get.lazyPut(() => LoginRepositoryImpl(
|
||||
{
|
||||
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/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/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_username_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/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/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_username_on_mobile_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 SaveLoginUsernameOnMobileInteractor _saveLoginUsernameOnMobileInteractor;
|
||||
final GetAllRecentLoginUsernameOnMobileInteractor _getAllRecentLoginUsernameOnMobileInteractor;
|
||||
final DNSLookupToGetJmapUrlInteractor _dnsLookupToGetJmapUrlInteractor;
|
||||
|
||||
final TextEditingController urlInputController = TextEditingController();
|
||||
final TextEditingController usernameInputController = TextEditingController();
|
||||
final TextEditingController passwordInputController = TextEditingController();
|
||||
final FocusNode baseUrlFocusNode = FocusNode();
|
||||
final FocusNode userNameFocusNode = FocusNode();
|
||||
final FocusNode passFocusNode = FocusNode();
|
||||
|
||||
final loginFormType = LoginFormType.none.obs;
|
||||
|
||||
OIDCResponse? _oidcResponse;
|
||||
UserName? _username;
|
||||
Password? _password;
|
||||
Uri? _baseUri;
|
||||
|
||||
LoginController(
|
||||
this._authenticationInteractor,
|
||||
this._checkOIDCIsAvailableInteractor,
|
||||
@@ -86,42 +98,26 @@ class LoginController extends ReloadableController {
|
||||
this._getAllRecentLoginUrlOnMobileInteractor,
|
||||
this._saveLoginUsernameOnMobileInteractor,
|
||||
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
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
if (PlatformInfo.isWeb) {
|
||||
final arguments = Get.arguments;
|
||||
if (arguments is LoginArguments) {
|
||||
loginFormType.value = arguments.loginFormType;
|
||||
_checkOIDCIsAvailable();
|
||||
final arguments = Get.arguments;
|
||||
if (arguments is LoginArguments) {
|
||||
if (arguments.loginFormType == LoginFormType.passwordForm) {
|
||||
loginFormType.value = LoginFormType.dnsLookupForm;
|
||||
} else {
|
||||
loginFormType.value = arguments.loginFormType;
|
||||
}
|
||||
if (PlatformInfo.isWeb) {
|
||||
_checkOIDCIsAvailable();
|
||||
} else if (arguments.baseUrl?.isNotEmpty == true) {
|
||||
onBaseUrlChange(arguments.baseUrl!);
|
||||
}
|
||||
} else {
|
||||
if (PlatformInfo.isWeb) {
|
||||
_getAuthenticationInfo();
|
||||
}
|
||||
}
|
||||
@@ -134,12 +130,21 @@ class LoginController extends ReloadableController {
|
||||
} else if (failure is CheckOIDCIsAvailableFailure ||
|
||||
failure is GetStoredOidcConfigurationFailure ||
|
||||
failure is GetOIDCIsAvailableFailure ||
|
||||
failure is GetOIDCConfigurationFailure ||
|
||||
failure is GetTokenOIDCFailure) {
|
||||
_showFormLoginWithCredentialAction();
|
||||
if (PlatformInfo.isMobile && loginFormType.value == LoginFormType.dnsLookupForm) {
|
||||
_showPasswordForm();
|
||||
} else {
|
||||
_showCredentialForm();
|
||||
}
|
||||
} else if (failure is GetAuthenticatedAccountFailure) {
|
||||
_checkOIDCIsAvailable();
|
||||
} else if (failure is GetSessionFailure) {
|
||||
clearAllData();
|
||||
} else if (failure is DNSLookupToGetJmapUrlFailure) {
|
||||
_username = null;
|
||||
_clearTextInputField();
|
||||
_showBaseUrlForm();
|
||||
} else {
|
||||
super.handleFailureViewState(failure);
|
||||
}
|
||||
@@ -162,6 +167,8 @@ class LoginController extends ReloadableController {
|
||||
_getTokenOIDCSuccess(success);
|
||||
} else if (success is AuthenticationUserSuccess) {
|
||||
_loginSuccessAction(success);
|
||||
} else if (success is DNSLookupToGetJmapUrlSuccess) {
|
||||
_handleDNSLookupToGetJmapUrlSuccess(success);
|
||||
} else {
|
||||
super.handleSuccessViewState(success);
|
||||
}
|
||||
@@ -169,14 +176,21 @@ class LoginController extends ReloadableController {
|
||||
|
||||
@override
|
||||
void handleExceptionAction({Failure? failure, Exception? exception}) {
|
||||
super.handleExceptionAction(failure: failure, exception: exception);
|
||||
logError('LoginController::handleExceptionAction:exception: $exception | failure: ${failure.runtimeType}');
|
||||
if (failure is CheckOIDCIsAvailableFailure ||
|
||||
failure is GetStoredOidcConfigurationFailure ||
|
||||
failure is GetOIDCConfigurationFailure ||
|
||||
failure is GetOIDCIsAvailableFailure ||
|
||||
failure is GetTokenOIDCFailure) {
|
||||
_showFormLoginWithCredentialAction();
|
||||
if (PlatformInfo.isMobile && loginFormType.value == LoginFormType.dnsLookupForm) {
|
||||
_showPasswordForm();
|
||||
} else {
|
||||
_showCredentialForm();
|
||||
}
|
||||
} else if (failure is GetSessionFailure) {
|
||||
clearAllData();
|
||||
} else {
|
||||
clearState();
|
||||
super.handleExceptionAction(failure: failure, exception: exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,19 +211,20 @@ class LoginController extends ReloadableController {
|
||||
}
|
||||
|
||||
void handleNextInUrlInputFormPress() {
|
||||
_saveRecentLoginUrl();
|
||||
if (PlatformInfo.isMobile && _currentBaseUrl != null) {
|
||||
_storeBaseUrlToCache(_currentBaseUrl!);
|
||||
}
|
||||
_checkOIDCIsAvailable();
|
||||
}
|
||||
|
||||
void _checkOIDCIsAvailable() {
|
||||
final baseUrl = _getBaseUrl();
|
||||
if (baseUrl == null) {
|
||||
if (_currentBaseUrl == null) {
|
||||
dispatchState(Left(CheckOIDCIsAvailableFailure(CanNotFoundBaseUrl())));
|
||||
} else {
|
||||
consumeState(_checkOIDCIsAvailableInteractor.execute(
|
||||
OIDCRequest(
|
||||
baseUrl: baseUrl.toString(),
|
||||
resourceUrl: baseUrl.origin
|
||||
baseUrl: _currentBaseUrl!.toString(),
|
||||
resourceUrl: _currentBaseUrl!.origin
|
||||
)
|
||||
));
|
||||
}
|
||||
@@ -217,46 +232,60 @@ class LoginController extends ReloadableController {
|
||||
|
||||
void _redirectToSSOLoginScreen(CheckOIDCIsAvailableSuccess success) {
|
||||
_oidcResponse = success.oidcResponse;
|
||||
handleSSOPressed();
|
||||
consumeState(_getOIDCIsAvailableInteractor.execute(
|
||||
OIDCRequest(
|
||||
baseUrl: _currentBaseUrl!.toString(),
|
||||
resourceUrl: _currentBaseUrl!.origin
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
void handleBackInCredentialForm() {
|
||||
void handleBackButtonAction() {
|
||||
clearState();
|
||||
loginFormType.value = LoginFormType.baseUrlForm;
|
||||
}
|
||||
|
||||
void _showFormLoginWithCredentialAction() {
|
||||
clearState();
|
||||
loginFormType.value = LoginFormType.credentialForm;
|
||||
}
|
||||
|
||||
void handleLoginPressed() {
|
||||
TextInput.finishAutofillContext();
|
||||
|
||||
_saveRecentLoginUsername();
|
||||
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);
|
||||
if (loginFormType.value == LoginFormType.credentialForm) {
|
||||
_password = null;
|
||||
_username = null;
|
||||
usernameInputController.clear();
|
||||
passwordInputController.clear();
|
||||
loginFormType.value = LoginFormType.baseUrlForm;
|
||||
} else if (loginFormType.value == LoginFormType.passwordForm) {
|
||||
_password = null;
|
||||
_baseUri = null;
|
||||
urlInputController.clear();
|
||||
passwordInputController.clear();
|
||||
loginFormType.value = LoginFormType.dnsLookupForm;
|
||||
}
|
||||
}
|
||||
|
||||
void handleSSOPressed() {
|
||||
final baseUrl = _getBaseUrl();
|
||||
if (baseUrl != null) {
|
||||
consumeState(_getOIDCIsAvailableInteractor.execute(
|
||||
OIDCRequest(
|
||||
baseUrl: baseUrl.toString(),
|
||||
resourceUrl: baseUrl.origin
|
||||
)
|
||||
));
|
||||
void _showCredentialForm() {
|
||||
clearState();
|
||||
loginFormType.value = LoginFormType.credentialForm;
|
||||
userNameFocusNode.requestFocus();
|
||||
}
|
||||
|
||||
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 {
|
||||
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) {
|
||||
consumeState(_getOIDCConfigurationInteractor.execute(_oidcResponse!));
|
||||
} else {
|
||||
dispatchState(Left(GetOIDCIsAvailableFailure(CanNotFoundOIDCLinks())));
|
||||
dispatchState(Left(GetOIDCConfigurationFailure(CanNotFoundOIDCLinks())));
|
||||
}
|
||||
}
|
||||
|
||||
void _getOIDCConfigurationSuccess(GetOIDCConfigurationSuccess success) {
|
||||
log('LoginController::_getOIDCConfigurationSuccess():success: $success');
|
||||
if (PlatformInfo.isWeb) {
|
||||
_authenticateOidcOnBrowserAction(success.oidcConfiguration);
|
||||
} else {
|
||||
@@ -277,10 +305,9 @@ class LoginController extends ReloadableController {
|
||||
}
|
||||
}
|
||||
|
||||
void _getTokenOIDCAction(OIDCConfiguration config) async {
|
||||
final baseUri = _getBaseUrl();
|
||||
if (baseUri != null) {
|
||||
consumeState(_getTokenOIDCInteractor.execute(baseUri, config));
|
||||
void _getTokenOIDCAction(OIDCConfiguration config) {
|
||||
if (_currentBaseUrl != null) {
|
||||
consumeState(_getTokenOIDCInteractor.execute(_currentBaseUrl!, config));
|
||||
} else {
|
||||
dispatchState(Left(GetTokenOIDCFailure(CanNotFoundBaseUrl())));
|
||||
}
|
||||
@@ -289,9 +316,8 @@ class LoginController extends ReloadableController {
|
||||
void _authenticateOidcOnBrowserAction(OIDCConfiguration config) async {
|
||||
_removeAuthDestinationUrlInSessionStorage();
|
||||
|
||||
final baseUri = _parseUri(AppConfig.baseUrl);
|
||||
if (baseUri != null) {
|
||||
consumeState(_authenticateOidcOnBrowserInteractor.execute(baseUri, config));
|
||||
if (_currentBaseUrl != null) {
|
||||
consumeState(_authenticateOidcOnBrowserInteractor.execute(_currentBaseUrl!, config));
|
||||
} else {
|
||||
dispatchState(Left(AuthenticateOidcOnBrowserFailure(CanNotFoundBaseUrl())));
|
||||
}
|
||||
@@ -305,9 +331,8 @@ class LoginController extends ReloadableController {
|
||||
}
|
||||
|
||||
void _getTokenOIDCSuccess(GetTokenOIDCSuccess success) {
|
||||
log('LoginController::_getTokenOIDCSuccess(): ${success.tokenOIDC.toString()}');
|
||||
dynamicUrlInterceptors.setJmapUrl(_getBaseUrl().toString());
|
||||
dynamicUrlInterceptors.changeBaseUrl(_getBaseUrl().toString());
|
||||
dynamicUrlInterceptors.setJmapUrl(_currentBaseUrl?.toString());
|
||||
dynamicUrlInterceptors.changeBaseUrl(_currentBaseUrl?.toString());
|
||||
authorizationInterceptors.setTokenAndAuthorityOidc(
|
||||
newToken: success.tokenOIDC.toToken(),
|
||||
newConfig: success.configuration);
|
||||
@@ -317,36 +342,24 @@ class LoginController extends ReloadableController {
|
||||
getSessionAction();
|
||||
}
|
||||
|
||||
void _loginAction({Uri? baseUrl, UserName? userName, Password? password}) {
|
||||
consumeState(_authenticationInteractor.execute(
|
||||
baseUrl: baseUrl,
|
||||
userName: userName,
|
||||
password: password
|
||||
));
|
||||
}
|
||||
|
||||
void _loginSuccessAction(AuthenticationUserSuccess success) {
|
||||
dynamicUrlInterceptors.setJmapUrl(_getBaseUrl().toString());
|
||||
dynamicUrlInterceptors.changeBaseUrl(_getBaseUrl().toString());
|
||||
authorizationInterceptors.setBasicAuthorization(_userNameText, _passwordText);
|
||||
authorizationIsolateInterceptors.setBasicAuthorization(_userNameText, _passwordText);
|
||||
dynamicUrlInterceptors.setJmapUrl(_currentBaseUrl?.toString());
|
||||
dynamicUrlInterceptors.changeBaseUrl(_currentBaseUrl?.toString());
|
||||
authorizationInterceptors.setBasicAuthorization(_username!, _password!);
|
||||
authorizationIsolateInterceptors.setBasicAuthorization(_username!, _password!);
|
||||
getSessionAction();
|
||||
}
|
||||
|
||||
void formatUrl(String url) {
|
||||
log('LoginController::formatUrl(): $url');
|
||||
if (url.isValid()) {
|
||||
urlInputController.text = url.removePrefix();
|
||||
}
|
||||
setUrlText(urlInputController.text);
|
||||
void selectBaseUrlFromSuggestion(String url) {
|
||||
final validUrl = url.isValid() ? url.removePrefix() : url;
|
||||
log('LoginController::selectBaseUrlFromSuggestion:validUrl: $validUrl');
|
||||
urlInputController.text = validUrl;
|
||||
onBaseUrlChange(validUrl);
|
||||
}
|
||||
|
||||
void _saveRecentLoginUrl() {
|
||||
if (_urlText?.isNotEmpty == true && PlatformInfo.isMobile) {
|
||||
final recentLoginUrl = RecentLoginUrl.now(_urlText!);
|
||||
log('LoginController::_saveRecentLoginUrl(): $recentLoginUrl');
|
||||
_saveLoginUrlOnMobileInteractor.execute(recentLoginUrl);
|
||||
}
|
||||
void _storeBaseUrlToCache(Uri uri) {
|
||||
log('LoginController::_storeBaseUrlToCache:uri: $uri');
|
||||
_saveLoginUrlOnMobileInteractor.execute(RecentLoginUrl.now(uri.toString()));
|
||||
}
|
||||
|
||||
Future<List<RecentLoginUrl>> getAllRecentLoginUrlAction(String pattern) async {
|
||||
@@ -360,19 +373,19 @@ class LoginController extends ReloadableController {
|
||||
));
|
||||
}
|
||||
|
||||
void setUsername(String username) {
|
||||
log('LoginController::formatUsername(): $username');
|
||||
usernameInputController.text = username;
|
||||
setUserNameText(usernameInputController.text);
|
||||
void selectUsernameFromSuggestion(RecentLoginUsername recentLoginUsername) {
|
||||
log('LoginController::selectUsernameFromSuggestion():recentLoginUsername: $recentLoginUsername');
|
||||
usernameInputController.text = recentLoginUsername.username;
|
||||
_username = UserName(recentLoginUsername.username);
|
||||
|
||||
if (loginFormType.value == LoginFormType.credentialForm) {
|
||||
passFocusNode.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void _saveRecentLoginUsername() {
|
||||
if(PlatformInfo.isWeb || _userNameText == null || _userNameText!.isEmpty || !_userNameText!.isEmail) {
|
||||
return ;
|
||||
}
|
||||
final recentLoginUsername = RecentLoginUsername.now(_userNameText!);
|
||||
log('LoginController::_saveRecentLoginUsername(): $recentLoginUsername');
|
||||
_saveLoginUsernameOnMobileInteractor.execute(recentLoginUsername);
|
||||
void _storeUsernameToCache(String userName) {
|
||||
log('LoginController::_storeUsername():userName: $userName');
|
||||
_saveLoginUsernameOnMobileInteractor.execute(RecentLoginUsername.now(userName));
|
||||
}
|
||||
|
||||
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
|
||||
void onClose() {
|
||||
passFocusNode.dispose();
|
||||
baseUrlFocusNode.dispose();
|
||||
userNameFocusNode.dispose();
|
||||
urlInputController.dispose();
|
||||
usernameInputController.dispose();
|
||||
passwordInputController.dispose();
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
enum LoginFormType {
|
||||
none, baseUrlForm, credentialForm, ssoForm
|
||||
none,
|
||||
baseUrlForm,
|
||||
credentialForm,
|
||||
passwordForm,
|
||||
dnsLookupForm;
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import 'package:core/presentation/state/success.dart';
|
||||
import 'package:core/presentation/utils/theme_utils.dart';
|
||||
import 'package:core/presentation/views/text/type_ahead_form_field_builder.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||
import 'package:get/get.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/login_form_type.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_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';
|
||||
|
||||
class LoginView extends BaseLoginView {
|
||||
@@ -31,13 +35,16 @@ class LoginView extends BaseLoginView {
|
||||
child: SafeArea(
|
||||
child: _supportScrollForm(context)
|
||||
? Stack(children: [
|
||||
Center(child: SingleChildScrollView(
|
||||
Center(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: _buildCenterForm(context))),
|
||||
child: _buildCenterForm(context)
|
||||
)
|
||||
),
|
||||
Obx(() {
|
||||
if (controller.loginFormType.value == LoginFormType.credentialForm
|
||||
|| controller.loginFormType.value == LoginFormType.ssoForm) {
|
||||
return _buildBackButton(context);
|
||||
if (controller.loginFormType.value == LoginFormType.passwordForm ||
|
||||
controller.loginFormType.value == LoginFormType.credentialForm) {
|
||||
return LoginBackButton(onBackAction: controller.handleBackButtonAction);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
})
|
||||
@@ -45,9 +52,9 @@ class LoginView extends BaseLoginView {
|
||||
: Stack(children: [
|
||||
_buildCenterForm(context),
|
||||
Obx(() {
|
||||
if (controller.loginFormType.value == LoginFormType.credentialForm
|
||||
|| controller.loginFormType.value == LoginFormType.ssoForm) {
|
||||
return _buildBackButton(context);
|
||||
if (controller.loginFormType.value == LoginFormType.passwordForm ||
|
||||
controller.loginFormType.value == LoginFormType.credentialForm) {
|
||||
return LoginBackButton(onBackAction: controller.handleBackButtonAction);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
})
|
||||
@@ -75,9 +82,28 @@ class LoginView extends BaseLoginView {
|
||||
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(() {
|
||||
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:
|
||||
return _buildUrlInput(context);
|
||||
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) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 24, left: 24, bottom: 24),
|
||||
@@ -120,19 +130,17 @@ class LoginView extends BaseLoginView {
|
||||
controller: controller.urlInputController,
|
||||
textInputAction: TextInputAction.next,
|
||||
keyboardType: TextInputType.url,
|
||||
focusNode: controller.baseUrlFocusNode,
|
||||
onTextChange: controller.onBaseUrlChange,
|
||||
onTextSubmitted: (value) => controller.handleNextInUrlInputFormPress(),
|
||||
onTextChange: controller.formatUrl,
|
||||
decoration: (LoginInputDecorationBuilder()
|
||||
..setLabelText(AppLocalizations.of(context).prefix_https)
|
||||
..setPrefixText(AppLocalizations.of(context).prefix_https))
|
||||
.build(),
|
||||
debounceDuration: const Duration(milliseconds: 300),
|
||||
suggestionsCallback: (pattern) async {
|
||||
controller.formatUrl(pattern);
|
||||
return controller.getAllRecentLoginUrlAction(pattern);
|
||||
},
|
||||
itemBuilder: (context, loginUrl) => RecentItemTileWidget(loginUrl, imagePath: controller.imagePaths),
|
||||
onSuggestionSelected: (loginUrl) => controller.formatUrl(loginUrl.url),
|
||||
suggestionsCallback: controller.getAllRecentLoginUrlAction,
|
||||
itemBuilder: (_, loginUrl) => RecentItemTileWidget(loginUrl),
|
||||
onSuggestionSelected: (loginUrl) => controller.selectBaseUrlFromSuggestion(loginUrl.url),
|
||||
suggestionsBoxDecoration: const SuggestionsBoxDecoration(borderRadius: BorderRadius.all(Radius.circular(14))),
|
||||
noItemsFoundBuilder: (context) => const SizedBox(),
|
||||
hideOnEmpty: true,
|
||||
@@ -156,20 +164,23 @@ class LoginView extends BaseLoginView {
|
||||
width: controller.responsiveUtils.getDeviceWidth(context),height: 48,
|
||||
child: ElevatedButton(
|
||||
key: const Key('nextToCredentialForm'),
|
||||
style: ButtonStyle(
|
||||
foregroundColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) => Colors.white),
|
||||
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||
(Set<MaterialState> states) => AppColor.primaryColor),
|
||||
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: AppColor.primaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
side: const BorderSide(width: 0, color: AppColor.primaryColor)
|
||||
))
|
||||
)
|
||||
),
|
||||
child: Text(AppLocalizations.of(context).next,
|
||||
style: const TextStyle(fontSize: 16, color: Colors.white)
|
||||
),
|
||||
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(
|
||||
(failure) {
|
||||
switch (controller.loginFormType.value) {
|
||||
case LoginFormType.dnsLookupForm:
|
||||
case LoginFormType.baseUrlForm:
|
||||
return _buildNextButtonInContext(context);
|
||||
case LoginFormType.passwordForm:
|
||||
case LoginFormType.credentialForm:
|
||||
return _buildLoginButtonInContext(context);
|
||||
case LoginFormType.ssoForm:
|
||||
return _buildLoginButtonInContext(context);
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
},
|
||||
(success) {
|
||||
if (success is LoadingState) {
|
||||
return buildLoadingCircularProgress();
|
||||
return _supportScrollForm(context)
|
||||
? const HorizontalProgressLoadingButton()
|
||||
: _buildExpandedButton(context, const HorizontalProgressLoadingButton());
|
||||
} else {
|
||||
switch (controller.loginFormType.value) {
|
||||
case LoginFormType.dnsLookupForm:
|
||||
case LoginFormType.baseUrlForm:
|
||||
return _buildNextButtonInContext(context);
|
||||
case LoginFormType.passwordForm:
|
||||
case LoginFormType.credentialForm:
|
||||
return _buildLoginButtonInContext(context);
|
||||
case LoginFormType.ssoForm:
|
||||
return _buildLoginButtonInContext(context);
|
||||
default:
|
||||
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/login_form_type.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';
|
||||
|
||||
class LoginView extends BaseLoginView {
|
||||
@@ -47,13 +48,14 @@ class LoginView extends BaseLoginView {
|
||||
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(() {
|
||||
switch (controller.loginFormType.value) {
|
||||
case LoginFormType.credentialForm:
|
||||
return buildInputCredentialForm(context);
|
||||
case LoginFormType.ssoForm:
|
||||
return const SizedBox(height: 150);
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
@@ -179,13 +181,14 @@ class LoginView extends BaseLoginView {
|
||||
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(() {
|
||||
switch (controller.loginFormType.value) {
|
||||
case LoginFormType.credentialForm:
|
||||
return buildInputCredentialForm(context);
|
||||
case LoginFormType.ssoForm:
|
||||
return const SizedBox(height: 150);
|
||||
default:
|
||||
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) {
|
||||
return Obx(() => controller.viewState.value.fold(
|
||||
(failure) {
|
||||
switch (controller.loginFormType.value) {
|
||||
case LoginFormType.baseUrlForm:
|
||||
return const SizedBox.shrink();
|
||||
case LoginFormType.credentialForm:
|
||||
return buildLoginButton(context);
|
||||
case LoginFormType.ssoForm:
|
||||
return _buildSSOButton(context);
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
@@ -276,12 +250,8 @@ class LoginView extends BaseLoginView {
|
||||
return buildLoadingCircularProgress();
|
||||
} else {
|
||||
switch (controller.loginFormType.value) {
|
||||
case LoginFormType.baseUrlForm:
|
||||
return const SizedBox.shrink();
|
||||
case LoginFormType.credentialForm:
|
||||
return buildLoginButton(context);
|
||||
case LoginFormType.ssoForm:
|
||||
return _buildSSOButton(context);
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
@@ -5,9 +5,13 @@ import 'package:tmail_ui_user/main/routes/router_arguments.dart';
|
||||
class LoginArguments extends RouterArguments {
|
||||
|
||||
final LoginFormType loginFormType;
|
||||
final String? baseUrl;
|
||||
|
||||
LoginArguments(this.loginFormType);
|
||||
LoginArguments(this.loginFormType, {this.baseUrl});
|
||||
|
||||
@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 {
|
||||
CreateDefaultMailboxFailure(dynamic exception) : super(exception: exception);
|
||||
final jmap.State? currentMailboxState;
|
||||
|
||||
CreateDefaultMailboxFailure(this.currentMailboxState, dynamic exception) : super(exception: exception);
|
||||
|
||||
@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/success.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/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:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/domain/state/create_default_mailbox_state.dart';
|
||||
@@ -13,30 +14,37 @@ class CreateDefaultMailboxInteractor {
|
||||
|
||||
CreateDefaultMailboxInteractor(this._mailboxRepository);
|
||||
|
||||
Stream<Either<Failure, Success>> execute(
|
||||
Stream<dartz.Either<Failure, Success>> execute(
|
||||
Session session,
|
||||
AccountId accountId,
|
||||
List<Role> listRole
|
||||
) async* {
|
||||
final currentMailboxState = await _getCurrentMailboxState(session, accountId);
|
||||
try {
|
||||
yield Right<Failure, Success>(CreateDefaultMailboxLoading());
|
||||
|
||||
final currentMailboxState = await _mailboxRepository.getMailboxState(session, accountId);
|
||||
|
||||
yield dartz.Right<Failure, Success>(CreateDefaultMailboxLoading());
|
||||
final listMailboxCreated = await _mailboxRepository.createDefaultMailbox(
|
||||
session,
|
||||
accountId,
|
||||
listRole
|
||||
);
|
||||
log('CreateDefaultMailboxInteractor::execute:listMailboxCreated: ${listMailboxCreated.length}');
|
||||
await _mailboxRepository.setRoleDefaultMailbox(
|
||||
session,
|
||||
accountId,
|
||||
listMailboxCreated
|
||||
);
|
||||
yield Right<Failure, Success>(CreateDefaultMailboxAllSuccess(currentMailboxState: currentMailboxState));
|
||||
yield dartz.Right<Failure, Success>(CreateDefaultMailboxAllSuccess(currentMailboxState: currentMailboxState));
|
||||
} 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) {
|
||||
_clearNewFolderId();
|
||||
} else if (failure is CreateDefaultMailboxFailure) {
|
||||
refreshAllMailbox();
|
||||
_refreshMailboxChanges(currentMailboxState: failure.currentMailboxState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -127,9 +127,7 @@ class MailboxTree with EquatableMixin {
|
||||
}
|
||||
|
||||
Map<Role, PresentationMailbox> get mapPresentationMailboxByRole {
|
||||
if (root.childrenItems?.isEmpty == true) {
|
||||
return {};
|
||||
} else {
|
||||
if (root.childrenItems?.isNotEmpty == true) {
|
||||
final listPresentationMailboxHasRole = root.childrenItems!
|
||||
.where((node) => node.item.role != null)
|
||||
.map((node) => node.item)
|
||||
@@ -139,6 +137,8 @@ class MailboxTree with EquatableMixin {
|
||||
for (var mailbox in listPresentationMailboxHasRole)
|
||||
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/mailbox_filter_condition.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/main/error/capability_validator.dart';
|
||||
|
||||
@@ -50,11 +51,10 @@ class SpamReportApi {
|
||||
final mailboxResponse = result
|
||||
.parse<GetMailboxResponse>(getMailboxInvocation.methodCallId, GetMailboxResponse.deserialize);
|
||||
|
||||
return Future.sync(() async {
|
||||
final unreadSpamMailbox = mailboxResponse?.list.first;
|
||||
return UnreadSpamEmailsResponse(unreadSpamMailbox: unreadSpamMailbox);
|
||||
}).catchError((error) {
|
||||
throw error;
|
||||
});
|
||||
if (mailboxResponse?.list.isNotEmpty == true) {
|
||||
return UnreadSpamEmailsResponse(unreadSpamMailbox: mailboxResponse!.list.first);
|
||||
} else {
|
||||
throw NotFoundSpamMailboxException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
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,
|
||||
];
|
||||
}
|
||||
@@ -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": {
|
||||
"type": "text",
|
||||
@@ -3481,5 +3481,17 @@
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"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/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/dns_service.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/mailbox/data/network/mailbox_api.dart';
|
||||
@@ -41,6 +42,7 @@ class NetworkBindings extends Bindings {
|
||||
_bindingDio();
|
||||
_bindingApi();
|
||||
_bindingTransformer();
|
||||
_bindingServices();
|
||||
_bindingException();
|
||||
}
|
||||
|
||||
@@ -119,4 +121,8 @@ class NetworkBindings extends Bindings {
|
||||
Get.put(HtmlTransform(Get.find<DioClient>(), Get.find<HtmlEscape>()));
|
||||
Get.put(HtmlAnalyzer(Get.find<HtmlTransform>()));
|
||||
}
|
||||
|
||||
void _bindingServices() {
|
||||
Get.put(DNSService());
|
||||
}
|
||||
}
|
||||
@@ -3610,4 +3610,18 @@ class AppLocalizations {
|
||||
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'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user