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');
|
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');
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
)
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -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;
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,3 +2,5 @@
|
|||||||
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,
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -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'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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