TF-3278 Handle open app via deep link at MailboxDashboard screen
This commit is contained in:
@@ -1,19 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:core/core.dart';
|
||||
import 'package:flutter/services.dart' as services;
|
||||
import 'package:contact/contact/model/capability_contact.dart';
|
||||
import 'package:core/data/network/config/dynamic_url_interceptors.dart';
|
||||
import 'package:core/domain/exceptions/platform_exception.dart';
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:core/presentation/state/failure.dart';
|
||||
import 'package:core/presentation/state/success.dart';
|
||||
import 'package:core/presentation/utils/app_toast.dart';
|
||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
||||
import 'package:core/presentation/views/toast/tmail_toast.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:core/utils/application_manager.dart';
|
||||
import 'package:core/utils/fps_manager.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:fcm/model/firebase_capability.dart';
|
||||
import 'package:fcm/model/firebase_registration_id.dart';
|
||||
@@ -34,7 +22,9 @@ import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixi
|
||||
import 'package:tmail_ui_user/features/caching/caching_manager.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/bindings/mdn_interactor_bindings.dart';
|
||||
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/exceptions/logout_exception.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
|
||||
@@ -203,6 +193,8 @@ abstract class BaseController extends GetxController
|
||||
clearDataAndGoToLoginPage();
|
||||
}
|
||||
|
||||
void onCancelReconnectWhenSessionExpired() {}
|
||||
|
||||
void _handleConnectionErrorException() {
|
||||
if (currentOverlayContext != null && currentContext != null) {
|
||||
appToast.showToastErrorMessage(
|
||||
@@ -241,7 +233,9 @@ abstract class BaseController extends GetxController
|
||||
outsideDismissible: false,
|
||||
titleActionButtonMaxLines: 1,
|
||||
icon: SvgPicture.asset(imagePaths.icTMailLogo, width: 64, height: 64),
|
||||
onConfirmAction: _executeBeforeReconnectAndLogOut);
|
||||
onConfirmAction: _executeBeforeReconnectAndLogOut,
|
||||
onCancelAction: onCancelReconnectWhenSessionExpired
|
||||
);
|
||||
} else if (PlatformInfo.isMobile) {
|
||||
if (currentContext == null) {
|
||||
clearDataAndGoToLoginPage();
|
||||
@@ -257,7 +251,9 @@ abstract class BaseController extends GetxController
|
||||
outsideDismissible: false,
|
||||
titleActionButtonMaxLines: 1,
|
||||
icon: SvgPicture.asset(imagePaths.icTMailLogo, width: 64, height: 64),
|
||||
onConfirmAction: clearDataAndGoToLoginPage);
|
||||
onConfirmAction: clearDataAndGoToLoginPage,
|
||||
onCancelAction: onCancelReconnectWhenSessionExpired
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,20 +473,22 @@ abstract class BaseController extends GetxController
|
||||
required Session session,
|
||||
required AccountId accountId,
|
||||
required Function onSuccessCallback,
|
||||
required Function onFailureCallback,
|
||||
required Function({Object? exception}) onFailureCallback,
|
||||
}) async {
|
||||
try {
|
||||
_isFcmEnabled = _isFcmActivated(session, accountId);
|
||||
|
||||
if (isAuthenticatedWithOidc) {
|
||||
final logoutViewState = await logoutOidcInteractor.execute().last;
|
||||
|
||||
logoutViewState.fold(
|
||||
(failure) => onFailureCallback(),
|
||||
(success) async {
|
||||
if (success is LogoutOidcSuccess) {
|
||||
logoutViewState.foldSuccess<LogoutOidcSuccess>(
|
||||
onSuccess: (success) async {
|
||||
await _handleDeleteFCMAndClearData();
|
||||
onSuccessCallback();
|
||||
},
|
||||
onFailure: (failure) async {
|
||||
if (failure is LogoutOidcFailure && _validateUserCancelledLogoutOidcFlow(failure.exception)) {
|
||||
await _handleDeleteFCMAndClearData();
|
||||
onSuccessCallback();
|
||||
onFailureCallback(exception: UserCancelledLogoutOIDCFlowException());
|
||||
} else {
|
||||
onFailureCallback();
|
||||
}
|
||||
@@ -506,6 +504,11 @@ abstract class BaseController extends GetxController
|
||||
}
|
||||
}
|
||||
|
||||
bool _validateUserCancelledLogoutOidcFlow(dynamic exception) {
|
||||
return exception is services.PlatformException &&
|
||||
exception.code == OIDCConstant.endSessionFailedCode;
|
||||
}
|
||||
|
||||
Future<void> _handleDeleteFCMAndClearData() async {
|
||||
await Future.wait([
|
||||
if (_isFcmEnabled)
|
||||
@@ -519,14 +522,12 @@ abstract class BaseController extends GetxController
|
||||
_getStoredFirebaseRegistrationInteractor = getBinding<GetStoredFirebaseRegistrationInteractor>();
|
||||
final fcmRegistration = await _getStoredFirebaseRegistrationInteractor?.execute().last;
|
||||
|
||||
fcmRegistration?.fold(
|
||||
(failure) => null,
|
||||
(success) async {
|
||||
if (success is GetStoredFirebaseRegistrationSuccess) {
|
||||
_destroyFirebaseRegistrationInteractor = getBinding<DestroyFirebaseRegistrationInteractor>();
|
||||
await _destroyFirebaseRegistrationInteractor?.execute(success.firebaseRegistration.id!).last;
|
||||
}
|
||||
fcmRegistration?.foldSuccess<GetStoredFirebaseRegistrationSuccess>(
|
||||
onSuccess: (success) async {
|
||||
_destroyFirebaseRegistrationInteractor = getBinding<DestroyFirebaseRegistrationInteractor>();
|
||||
await _destroyFirebaseRegistrationInteractor?.execute(success.firebaseRegistration.id!).last;
|
||||
},
|
||||
onFailure: (failure) {},
|
||||
);
|
||||
} catch (e) {
|
||||
logError('BaseController::_handleDeleteFCMRegistration:Exception = $e');
|
||||
|
||||
@@ -34,7 +34,7 @@ abstract class ReloadableController extends BaseController {
|
||||
goToLogin();
|
||||
} else if (failure is GetSessionFailure) {
|
||||
logError('$runtimeType::handleFailureViewState():Failure = $failure');
|
||||
handleGetSessionFailure(failure.exception);
|
||||
handleGetSessionFailure(failure);
|
||||
} else if (failure is UpdateAccountCacheFailure) {
|
||||
logError('$runtimeType::handleFailureViewState():Failure = $failure');
|
||||
_handleUpdateAccountCacheCompleted(
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:tmail_ui_user/features/caching/config/hive_cache_client.dart';
|
||||
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/model/oidc_configuration_cache.dart';
|
||||
|
||||
class OidcConfigurationCacheClient extends HiveCacheClient<OidcConfigurationCache> {
|
||||
|
||||
@override
|
||||
String get tableName => CachingConstants.oidcConfigurationCacheBoxName;
|
||||
|
||||
@override
|
||||
bool get encryption => true;
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import 'package:tmail_ui_user/features/login/data/local/encryption_key_cache_man
|
||||
import 'package:tmail_ui_user/features/login/data/model/account_cache.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/model/authentication_info_cache.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/model/encryption_key_cache.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/model/oidc_configuration_cache.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/model/recent_login_url_cache.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/model/recent_login_username_cache.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/model/token_oidc_cache.dart';
|
||||
@@ -185,6 +186,10 @@ class HiveCacheConfig {
|
||||
SessionHiveObjAdapter(),
|
||||
CachingConstants.SESSION_HIVE_CACHE_ID
|
||||
);
|
||||
registerCacheAdapter<OidcConfigurationCache>(
|
||||
OidcConfigurationCacheAdapter(),
|
||||
CachingConstants.OIDC_CONFIGURATION_CACHE_ID,
|
||||
);
|
||||
}
|
||||
|
||||
void registerCacheAdapter<T>(TypeAdapter<T> typeAdapter, int typeId) {
|
||||
|
||||
@@ -19,6 +19,7 @@ class CachingConstants {
|
||||
static const int DETAILED_EMAIL_HIVE_CACHE_ID = 17;
|
||||
static const int SENDING_EMAIL_HIVE_CACHE_ID = 18;
|
||||
static const int SESSION_HIVE_CACHE_ID = 19;
|
||||
static const int OIDC_CONFIGURATION_CACHE_ID = 20;
|
||||
|
||||
static const String fcmCacheBoxName = 'fcm_cache_box';
|
||||
static const String newEmailCacheBoxName = 'new_email_cache_box';
|
||||
@@ -26,6 +27,9 @@ class CachingConstants {
|
||||
static const String sendingEmailCacheBoxName = 'sending_email_cache_box';
|
||||
static const String sessionCacheBoxName = 'session_cache_box';
|
||||
static const String firebaseRegistrationCacheBoxName = 'firebase_registration_cache_box';
|
||||
static const String oidcConfigurationCacheBoxName = 'oidc_configuration_cache_box';
|
||||
|
||||
static const String oidcConfigurationCacheKeyName = 'oidc_configuration_cache_key';
|
||||
|
||||
static const String newEmailsContentFolderName = 'new_emails';
|
||||
static const String openedEmailContentFolderName = 'opened_email';
|
||||
|
||||
@@ -32,7 +32,7 @@ class AutoSignInViaDeepLinkInteractor {
|
||||
await Future.wait([
|
||||
_credentialRepository.saveBaseUrl(baseUri),
|
||||
_authenticationOIDCRepository.persistTokenOIDC(tokenOIDC),
|
||||
_authenticationOIDCRepository.persistAuthorityOidc(oidcConfiguration.authority),
|
||||
_authenticationOIDCRepository.persistOidcConfiguration(oidcConfiguration),
|
||||
]);
|
||||
|
||||
await _accountRepository.setCurrentAccount(
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:core/utils/app_logger.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||
import 'package:model/account/personal_account.dart';
|
||||
import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart';
|
||||
@@ -21,9 +22,12 @@ import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_lo
|
||||
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_search_cache_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/home/domain/state/auto_sign_in_via_deep_link_state.dart';
|
||||
import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/exceptions/logout_exception.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/main/deep_links/deep_link_data.dart';
|
||||
import 'package:tmail_ui_user/features/login/presentation/model/login_navigate_arguments.dart';
|
||||
import 'package:tmail_ui_user/features/login/presentation/model/login_navigate_type.dart';
|
||||
import 'package:tmail_ui_user/main/deep_links/open_app_deep_link_data.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
@@ -65,7 +69,7 @@ class HomeController extends ReloadableController {
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
_cleanupCache();
|
||||
_handleNavigateToScreen();
|
||||
super.onReady();
|
||||
}
|
||||
|
||||
@@ -76,6 +80,11 @@ class HomeController extends ReloadableController {
|
||||
arguments: session);
|
||||
}
|
||||
|
||||
@override
|
||||
void onCancelReconnectWhenSessionExpired() {
|
||||
clearDataAndGoToLoginPage();
|
||||
}
|
||||
|
||||
void _initFlutterDownloader() {
|
||||
FlutterDownloader
|
||||
.initialize(debug: kDebugMode)
|
||||
@@ -84,6 +93,15 @@ class HomeController extends ReloadableController {
|
||||
|
||||
static void downloadCallback(String id, DownloadTaskStatus status, int progress) {}
|
||||
|
||||
void _handleNavigateToScreen() {
|
||||
final arguments = Get.arguments;
|
||||
if (arguments is LoginNavigateArguments) {
|
||||
_handleLoginNavigateArguments(arguments);
|
||||
} else {
|
||||
_cleanupCache();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _cleanupCache() async {
|
||||
await HiveCacheConfig.instance.onUpgradeDatabase(cachingManager);
|
||||
|
||||
@@ -95,6 +113,17 @@ class HomeController extends ReloadableController {
|
||||
], eagerError: true).then((_) => getAuthenticatedAccountAction());
|
||||
}
|
||||
|
||||
void _handleLoginNavigateArguments(LoginNavigateArguments arguments) {
|
||||
switch (arguments.navigateType) {
|
||||
case LoginNavigateType.autoSignIn:
|
||||
_handleAutoSignInViaDeepLinkSuccess(arguments.autoSignInViaDeepLinkSuccess!);
|
||||
break;
|
||||
default:
|
||||
_cleanupCache();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _registerReceivingFileSharing() {
|
||||
_emailReceiveManager.registerReceivingFileSharingStreamWhileAppClosed();
|
||||
}
|
||||
@@ -124,7 +153,7 @@ class HomeController extends ReloadableController {
|
||||
void _handleLogOutAndSignInNewAccount({
|
||||
required Success authenticationViewStateSuccess,
|
||||
required PersonalAccount personalAccount,
|
||||
required DeepLinkData deepLinkData,
|
||||
required OpenAppDeepLinkData openAppDeepLinkData,
|
||||
}) {
|
||||
if (authenticationViewStateSuccess is GetCredentialViewState) {
|
||||
setDataToInterceptors(
|
||||
@@ -143,14 +172,14 @@ class HomeController extends ReloadableController {
|
||||
_getSessionActionToLogOut(
|
||||
authenticationViewStateSuccess: authenticationViewStateSuccess,
|
||||
personalAccount: personalAccount,
|
||||
deepLinkData: deepLinkData,
|
||||
openAppDeepLinkData: openAppDeepLinkData,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _getSessionActionToLogOut({
|
||||
required Success authenticationViewStateSuccess,
|
||||
required PersonalAccount personalAccount,
|
||||
required DeepLinkData deepLinkData,
|
||||
required OpenAppDeepLinkData openAppDeepLinkData,
|
||||
}) async {
|
||||
try {
|
||||
final sessionViewState = await getSessionInteractor.execute().last;
|
||||
@@ -161,7 +190,7 @@ class HomeController extends ReloadableController {
|
||||
sessionViewStateSuccess: success,
|
||||
authenticationViewStateSuccess: authenticationViewStateSuccess,
|
||||
personalAccount: personalAccount,
|
||||
deepLinkData: deepLinkData,
|
||||
openAppDeepLinkData: openAppDeepLinkData,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
@@ -174,18 +203,27 @@ class HomeController extends ReloadableController {
|
||||
required Success sessionViewStateSuccess,
|
||||
required Success authenticationViewStateSuccess,
|
||||
required PersonalAccount personalAccount,
|
||||
required DeepLinkData deepLinkData,
|
||||
required OpenAppDeepLinkData openAppDeepLinkData,
|
||||
}) {
|
||||
if (sessionViewStateSuccess is GetSessionSuccess) {
|
||||
logoutToSignInNewAccount(
|
||||
session: sessionViewStateSuccess.session,
|
||||
accountId: personalAccount.accountId!,
|
||||
onFailureCallback: () =>
|
||||
_continueUsingTheApp(authenticationViewStateSuccess),
|
||||
onFailureCallback: ({exception}) {
|
||||
if (exception is UserCancelledLogoutOIDCFlowException) {
|
||||
_deepLinksManager?.autoSignInViaDeepLink(
|
||||
openAppDeepLinkData: openAppDeepLinkData,
|
||||
onFailureCallback: () => _continueUsingTheApp(authenticationViewStateSuccess),
|
||||
onAutoSignInSuccessCallback: _handleAutoSignInViaDeepLinkSuccess,
|
||||
);
|
||||
} else {
|
||||
_continueUsingTheApp(authenticationViewStateSuccess);
|
||||
}
|
||||
},
|
||||
onSuccessCallback: () => _deepLinksManager?.autoSignInViaDeepLink(
|
||||
deepLinkData: deepLinkData,
|
||||
openAppDeepLinkData: openAppDeepLinkData,
|
||||
onFailureCallback: () => _continueUsingTheApp(authenticationViewStateSuccess),
|
||||
onSuccessCallback: _handleAutoSignInViaDeepLinkSuccess,
|
||||
onAutoSignInSuccessCallback: _handleAutoSignInViaDeepLinkSuccess,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
@@ -234,8 +272,19 @@ class HomeController extends ReloadableController {
|
||||
@override
|
||||
void handleFailureViewState(Failure failure) {
|
||||
if (_validateToHandleDeepLinksNotSignedIn(failure)) {
|
||||
_deepLinksManager!.handleDeepLinksWhenAppTerminatedNotSignedIn(
|
||||
onSuccessCallback: _handleAutoSignInViaDeepLinkSuccess,
|
||||
_deepLinksManager!.handleDeepLinksWhenAppTerminated(
|
||||
onSuccessCallback: (deepLinkData) {
|
||||
if (deepLinkData is OpenAppDeepLinkData) {
|
||||
_deepLinksManager!.handleOpenAppDeepLinks(
|
||||
openAppDeepLinkData: deepLinkData,
|
||||
isSignedIn: false,
|
||||
onFailureCallback: goToLogin,
|
||||
onAutoSignInSuccessCallback: _handleAutoSignInViaDeepLinkSuccess
|
||||
);
|
||||
} else {
|
||||
goToLogin();
|
||||
}
|
||||
},
|
||||
onFailureCallback: goToLogin,
|
||||
);
|
||||
} else {
|
||||
@@ -248,14 +297,25 @@ class HomeController extends ReloadableController {
|
||||
if (_validateToHandleDeepLinksSignedIn(success)) {
|
||||
final personalAccount = _getPersonalAccountFromViewStateSuccess(success);
|
||||
|
||||
_deepLinksManager!.handleDeepLinksWhenAppTerminatedSignedIn(
|
||||
username: personalAccount?.userName?.value,
|
||||
_deepLinksManager!.handleDeepLinksWhenAppTerminated(
|
||||
onSuccessCallback: (deepLinkData) {
|
||||
if (deepLinkData is OpenAppDeepLinkData) {
|
||||
_deepLinksManager!.handleOpenAppDeepLinks(
|
||||
openAppDeepLinkData: deepLinkData,
|
||||
isSignedIn: true,
|
||||
username: personalAccount?.userName,
|
||||
onConfirmLogoutCallback: (openAppDeepLinkData) => _handleLogOutAndSignInNewAccount(
|
||||
authenticationViewStateSuccess: success,
|
||||
personalAccount: personalAccount!,
|
||||
openAppDeepLinkData: openAppDeepLinkData,
|
||||
),
|
||||
onFailureCallback: () => _continueUsingTheApp(success),
|
||||
);
|
||||
} else {
|
||||
_continueUsingTheApp(success);
|
||||
}
|
||||
},
|
||||
onFailureCallback: () => _continueUsingTheApp(success),
|
||||
onConfirmCallback: (deepLinkData) => _handleLogOutAndSignInNewAccount(
|
||||
authenticationViewStateSuccess: success,
|
||||
personalAccount: personalAccount!,
|
||||
deepLinkData: deepLinkData,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
super.handleSuccessViewState(success);
|
||||
|
||||
@@ -16,9 +16,9 @@ abstract class AuthenticationOIDCDataSource {
|
||||
|
||||
Future<TokenOIDC> getStoredTokenOIDC(String tokenIdHash);
|
||||
|
||||
Future<void> persistAuthorityOidc(String authority);
|
||||
Future<void> persistOidcConfiguration(OIDCConfiguration oidcConfiguration);
|
||||
|
||||
Future<void> deleteAuthorityOidc();
|
||||
Future<void> deleteOidcConfiguration();
|
||||
|
||||
Future<OIDCConfiguration> getStoredOidcConfiguration();
|
||||
|
||||
|
||||
@@ -74,9 +74,9 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> persistAuthorityOidc(String authority) {
|
||||
Future<void> persistOidcConfiguration(OIDCConfiguration oidcConfiguration) {
|
||||
return Future.sync(() async {
|
||||
return await _oidcConfigurationCacheManager.persistAuthorityOidc(authority);
|
||||
return await _oidcConfigurationCacheManager.persistOidcConfiguration(oidcConfiguration);
|
||||
}).catchError(_cacheExceptionThrower.throwException);
|
||||
}
|
||||
|
||||
@@ -106,9 +106,9 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAuthorityOidc() {
|
||||
Future<void> deleteOidcConfiguration() {
|
||||
return Future.sync(() async {
|
||||
return await _oidcConfigurationCacheManager.deleteAuthorityOidc();
|
||||
return await _oidcConfigurationCacheManager.deleteOidcConfiguration();
|
||||
}).catchError(_exceptionThrower.throwException);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
import 'package:model/oidc/oidc_configuration.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/model/oidc_configuration_cache.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart';
|
||||
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||
|
||||
extension OidcConfigutationCacheExtension on OidcConfigurationCache {
|
||||
OIDCConfiguration toOIDCConfiguration() {
|
||||
return OIDCConfiguration(
|
||||
authority: authority,
|
||||
isTWP: isTWP,
|
||||
clientId: OIDCConstant.clientId,
|
||||
scopes: AppConfig.oidcScopes,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,54 @@
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:model/oidc/oidc_configuration.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:tmail_ui_user/features/caching/clients/oidc_configuration_cache_client.dart';
|
||||
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/extensions/oidc_configutation_cache_extension.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/network/oidc_error.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/extensions/oidc_configuration_extensions.dart';
|
||||
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||
|
||||
class OidcConfigurationCacheManager {
|
||||
final SharedPreferences _sharedPreferences;
|
||||
final OidcConfigurationCacheClient _oidcConfigurationCacheClient;
|
||||
|
||||
OidcConfigurationCacheManager(this._sharedPreferences);
|
||||
OidcConfigurationCacheManager(this._sharedPreferences, this._oidcConfigurationCacheClient);
|
||||
|
||||
Future<OIDCConfiguration> getOidcConfiguration() async {
|
||||
final authority = _sharedPreferences.getString(OIDCConstant.keyAuthorityOidc);
|
||||
if (authority == null || authority.isEmpty) {
|
||||
throw CanNotFoundOIDCAuthority();
|
||||
} else {
|
||||
final oidcConfigurationCache = await _oidcConfigurationCacheClient.getItem(CachingConstants.oidcConfigurationCacheKeyName);
|
||||
if (oidcConfigurationCache == null) {
|
||||
final authority = _sharedPreferences.getString(OIDCConstant.keyAuthorityOidc);
|
||||
|
||||
if (authority == null || authority.isEmpty) {
|
||||
throw CanNotFoundOIDCAuthority();
|
||||
}
|
||||
|
||||
return OIDCConfiguration(
|
||||
authority: authority,
|
||||
clientId: OIDCConstant.clientId,
|
||||
scopes: AppConfig.oidcScopes);
|
||||
authority: authority,
|
||||
clientId: OIDCConstant.clientId,
|
||||
scopes: AppConfig.oidcScopes,
|
||||
);
|
||||
} else {
|
||||
return oidcConfigurationCache.toOIDCConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> persistAuthorityOidc(String authority) async {
|
||||
log('OidcConfigurationCacheManager::persistAuthorityOidc(): $authority');
|
||||
await _sharedPreferences.setString(OIDCConstant.keyAuthorityOidc, authority);
|
||||
Future<void> persistOidcConfiguration(OIDCConfiguration oidcConfiguration) async {
|
||||
log('OidcConfigurationCacheManager::persistOidcConfiguration(): $oidcConfiguration');
|
||||
await _oidcConfigurationCacheClient.insertItem(
|
||||
CachingConstants.oidcConfigurationCacheKeyName,
|
||||
oidcConfiguration.toOidcConfigurationCache(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> deleteAuthorityOidc() async {
|
||||
log('OidcConfigurationCacheManager::deleteAuthorityOidc()');
|
||||
await _sharedPreferences.remove(OIDCConstant.keyAuthorityOidc);
|
||||
Future<void> deleteOidcConfiguration() async {
|
||||
log('OidcConfigurationCacheManager::deleteOidcConfiguration()');
|
||||
await Future.wait([
|
||||
_oidcConfigurationCacheClient.deleteItem(
|
||||
CachingConstants.oidcConfigurationCacheKeyName,
|
||||
),
|
||||
_sharedPreferences.remove(OIDCConstant.keyAuthorityOidc),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
|
||||
|
||||
part 'oidc_configuration_cache.g.dart';
|
||||
|
||||
@HiveType(typeId: CachingConstants.OIDC_CONFIGURATION_CACHE_ID)
|
||||
class OidcConfigurationCache extends HiveObject with EquatableMixin {
|
||||
|
||||
@HiveField(0)
|
||||
final String authority;
|
||||
|
||||
@HiveField(1)
|
||||
final bool isTWP;
|
||||
|
||||
OidcConfigurationCache(this.authority, this.isTWP);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [authority, isTWP];
|
||||
}
|
||||
@@ -4,13 +4,17 @@ import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||
class OIDCConstant {
|
||||
static String get mobileOidcClientId => 'teammail-mobile';
|
||||
static List<String> get oidcScope => ['openid', 'profile', 'email', 'offline_access'];
|
||||
static const keyAuthorityOidc = 'KEY_AUTHORITY_OIDC';
|
||||
static const authResponseKey = "auth_info";
|
||||
static const String keyAuthorityOidc = 'KEY_AUTHORITY_OIDC';
|
||||
static const String authResponseKey = "auth_info";
|
||||
static const String twakeWorkplaceUrlScheme = 'twakemail.mobile';
|
||||
static const String twakeWorkplaceRedirectUrl = '$twakeWorkplaceUrlScheme://redirect';
|
||||
static const String appParameter = 'tmail';
|
||||
static const String postRegisteredRedirectUrlPathParams = 'post_registered_redirect_url';
|
||||
static const String postLoginRedirectUrlPathParams = 'post_login_redirect_url';
|
||||
static const String endSessionFailedCode = 'end_session_failed';
|
||||
static const String redirectOidcMobile = 'teammail.mobile://oauthredirect';
|
||||
static const String loginRedirectOidcWeb = 'login-callback.html';
|
||||
static const String logoutRedirectOidcWeb = 'logout-callback.html';
|
||||
|
||||
static String get clientId => PlatformInfo.isWeb ? AppConfig.webOidcClientId : mobileOidcClientId;
|
||||
}
|
||||
@@ -52,11 +52,14 @@ class OIDCHttpClient {
|
||||
if (oidcResponse.links.isEmpty) {
|
||||
throw CanNotFoundOIDCAuthority();
|
||||
}
|
||||
log('OIDCHttpClient::getOIDCConfiguration(): href: ${oidcResponse.links[0].href}');
|
||||
final authority = oidcResponse.links[0].href.toString();
|
||||
final isTWP = authority == AppConfig.saasRegistrationUrl;
|
||||
log('OIDCHttpClient::getOIDCConfiguration():authority: $authority');
|
||||
return OIDCConfiguration(
|
||||
authority: oidcResponse.links[0].href.toString(),
|
||||
authority: authority,
|
||||
clientId: OIDCConstant.clientId,
|
||||
scopes: AppConfig.oidcScopes
|
||||
scopes: AppConfig.oidcScopes,
|
||||
isTWP: isTWP,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ class AuthenticationOIDCRepositoryImpl extends AuthenticationOIDCRepository {
|
||||
return _oidcDataSource.getOIDCConfiguration(oidcResponse);
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Future<OIDCDiscoveryResponse> discoverOIDC(OIDCConfiguration oidcConfiguration) {
|
||||
return _oidcDataSource.discoverOIDC(oidcConfiguration);
|
||||
@@ -49,8 +48,8 @@ class AuthenticationOIDCRepositoryImpl extends AuthenticationOIDCRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> persistAuthorityOidc(String authority) {
|
||||
return _oidcDataSource.persistAuthorityOidc(authority);
|
||||
Future<void> persistOidcConfiguration(OIDCConfiguration oidcConfiguration) {
|
||||
return _oidcDataSource.persistOidcConfiguration(oidcConfiguration);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -75,8 +74,8 @@ class AuthenticationOIDCRepositoryImpl extends AuthenticationOIDCRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAuthorityOidc() {
|
||||
return _oidcDataSource.deleteAuthorityOidc();
|
||||
Future<void> deleteOidcConfiguration() {
|
||||
return _oidcDataSource.deleteOidcConfiguration();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
class UserCancelledLogoutOIDCFlowException implements Exception {}
|
||||
@@ -4,6 +4,7 @@ import 'package:core/data/network/config/service_path.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:model/oidc/oidc_configuration.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/extensions/service_path_extension.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/model/oidc_configuration_cache.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart';
|
||||
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||
|
||||
@@ -12,32 +13,29 @@ extension OidcConfigurationExtensions on OIDCConfiguration {
|
||||
String get redirectUrl {
|
||||
if (PlatformInfo.isWeb) {
|
||||
return AppConfig.domainRedirectUrl.endsWith('/')
|
||||
? AppConfig.domainRedirectUrl + loginRedirectOidcWeb
|
||||
: '${AppConfig.domainRedirectUrl}/$loginRedirectOidcWeb';
|
||||
? AppConfig.domainRedirectUrl + OIDCConstant.loginRedirectOidcWeb
|
||||
: '${AppConfig.domainRedirectUrl}/${OIDCConstant.loginRedirectOidcWeb}';
|
||||
} else {
|
||||
return _isSaasAuthority(authority)
|
||||
return isTWP
|
||||
? OIDCConstant.twakeWorkplaceRedirectUrl
|
||||
: redirectOidcMobile;
|
||||
: OIDCConstant.redirectOidcMobile;
|
||||
}
|
||||
}
|
||||
|
||||
String get logoutRedirectUrl {
|
||||
if (PlatformInfo.isWeb) {
|
||||
if (AppConfig.domainRedirectUrl.endsWith('/')) {
|
||||
return AppConfig.domainRedirectUrl + logoutRedirectOidcWeb;
|
||||
return AppConfig.domainRedirectUrl + OIDCConstant.logoutRedirectOidcWeb;
|
||||
} else {
|
||||
return '${AppConfig.domainRedirectUrl}/$logoutRedirectOidcWeb';
|
||||
return '${AppConfig.domainRedirectUrl}/${OIDCConstant.logoutRedirectOidcWeb}';
|
||||
}
|
||||
} else {
|
||||
return _isSaasAuthority(authority)
|
||||
return isTWP
|
||||
? OIDCConstant.twakeWorkplaceRedirectUrl
|
||||
: redirectOidcMobile;
|
||||
: OIDCConstant.redirectOidcMobile;
|
||||
}
|
||||
}
|
||||
|
||||
bool _isSaasAuthority(String authority) =>
|
||||
authority == AppConfig.saasRegistrationUrl;
|
||||
|
||||
String get signInTWPUrl => ServicePath(authority)
|
||||
.withQueryParameters([
|
||||
StringQueryParameter(
|
||||
@@ -57,4 +55,8 @@ extension OidcConfigurationExtensions on OIDCConfiguration {
|
||||
StringQueryParameter('app', OIDCConstant.appParameter),
|
||||
])
|
||||
.generateEndpointPath();
|
||||
|
||||
OidcConfigurationCache toOidcConfigurationCache() {
|
||||
return OidcConfigurationCache(authority, isTWP);
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,9 @@ abstract class AuthenticationOIDCRepository {
|
||||
|
||||
Future<TokenOIDC> getStoredTokenOIDC(String tokenIdHash);
|
||||
|
||||
Future<void> persistAuthorityOidc(String authority);
|
||||
Future<void> persistOidcConfiguration(OIDCConfiguration oidcConfiguration);
|
||||
|
||||
Future<void> deleteAuthorityOidc();
|
||||
Future<void> deleteOidcConfiguration();
|
||||
|
||||
Future<OIDCConfiguration> getStoredOidcConfiguration();
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class DeleteAuthorityOidcInteractor {
|
||||
try {
|
||||
await Future.wait([
|
||||
_credentialRepository.removeBaseUrl(),
|
||||
_authenticationOIDCRepository.deleteAuthorityOidc(),
|
||||
_authenticationOIDCRepository.deleteOidcConfiguration(),
|
||||
_authenticationOIDCRepository.deleteTokenOIDC(),
|
||||
]);
|
||||
return Right(DeleteAuthorityOidcSuccess());
|
||||
|
||||
@@ -15,7 +15,7 @@ class GetOIDCConfigurationInteractor {
|
||||
try {
|
||||
yield Right<Failure, Success>(GetOIDCConfigurationLoading());
|
||||
final oidcConfiguration = await _oidcRepository.getOIDCConfiguration(oidcResponse);
|
||||
await _oidcRepository.persistAuthorityOidc(oidcConfiguration.authority);
|
||||
await _oidcRepository.persistOidcConfiguration(oidcConfiguration);
|
||||
yield Right<Failure, Success>(GetOIDCConfigurationSuccess(oidcConfiguration));
|
||||
} catch (e) {
|
||||
log('GetOIDCConfigurationInteractor::execute(): ERROR: $e');
|
||||
|
||||
@@ -35,7 +35,7 @@ class GetTokenOIDCInteractor {
|
||||
await Future.wait([
|
||||
_credentialRepository.saveBaseUrl(baseUrl),
|
||||
authenticationOIDCRepository.persistTokenOIDC(tokenOIDC),
|
||||
authenticationOIDCRepository.persistAuthorityOidc(config.authority),
|
||||
authenticationOIDCRepository.persistOidcConfiguration(config),
|
||||
]);
|
||||
|
||||
await _accountRepository.setCurrentAccount(
|
||||
|
||||
@@ -58,6 +58,7 @@ import 'package:tmail_ui_user/features/starting_page/domain/state/sign_in_twake_
|
||||
import 'package:tmail_ui_user/features/starting_page/domain/usecase/sign_in_twake_workplace_interactor.dart';
|
||||
import 'package:tmail_ui_user/main/deep_links/deep_link_data.dart';
|
||||
import 'package:tmail_ui_user/main/deep_links/deep_links_manager.dart';
|
||||
import 'package:tmail_ui_user/main/deep_links/open_app_deep_link_data.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
@@ -163,6 +164,7 @@ class LoginController extends ReloadableController {
|
||||
} else if (failure is GetAuthenticatedAccountFailure) {
|
||||
_checkOIDCIsAvailable();
|
||||
} else if (failure is GetSessionFailure) {
|
||||
SmartDialog.dismiss();
|
||||
clearAllData();
|
||||
} else if (failure is DNSLookupToGetJmapUrlFailure) {
|
||||
_username = null;
|
||||
@@ -218,6 +220,7 @@ class LoginController extends ReloadableController {
|
||||
_handleNoSuitableBrowserOIDC(failure)
|
||||
.map((stillFailed) => _handleCommonOIDCFailure());
|
||||
} else if (failure is GetSessionFailure) {
|
||||
SmartDialog.dismiss();
|
||||
clearAllData();
|
||||
} else {
|
||||
super.handleUrgentException(failure: failure, exception: exception);
|
||||
@@ -234,12 +237,6 @@ class LoginController extends ReloadableController {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleGetSessionFailure(GetSessionFailure failure) {
|
||||
SmartDialog.dismiss();
|
||||
super.handleGetSessionFailure(failure);
|
||||
}
|
||||
|
||||
void _registerDeepLinks() {
|
||||
_deepLinksManager = getBinding<DeepLinksManager>();
|
||||
_deepLinksManager?.clearPendingDeepLinkData();
|
||||
@@ -250,16 +247,21 @@ class LoginController extends ReloadableController {
|
||||
|
||||
void _handlePendingDeepLinkDataStream(DeepLinkData? deepLinkData) {
|
||||
log('LoginController::_handlePendingDeepLinkDataStream:DeepLinkData = $deepLinkData');
|
||||
if (deepLinkData == null) return;
|
||||
|
||||
if (currentContext != null) {
|
||||
SmartDialog.showLoading(msg: AppLocalizations.of(currentContext!).loadingPleaseWait);
|
||||
}
|
||||
|
||||
_deepLinksManager?.handleDeepLinksWhenAppOnForegroundNotSignedIn(
|
||||
_deepLinksManager?.handleDeepLinksWhenAppRunning(
|
||||
deepLinkData: deepLinkData,
|
||||
onSuccessCallback: _handleAutoSignInViaDeepLinkSuccess,
|
||||
onFailureCallback: SmartDialog.dismiss,
|
||||
onSuccessCallback: (deepLinkData) {
|
||||
if (deepLinkData is! OpenAppDeepLinkData) return;
|
||||
|
||||
if (currentContext != null) {
|
||||
SmartDialog.showLoading(msg: AppLocalizations.of(currentContext!).loadingPleaseWait);
|
||||
}
|
||||
|
||||
_deepLinksManager?.autoSignInViaDeepLink(
|
||||
openAppDeepLinkData: deepLinkData,
|
||||
onAutoSignInSuccessCallback: _handleAutoSignInViaDeepLinkSuccess,
|
||||
onFailureCallback: SmartDialog.dismiss,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:tmail_ui_user/features/home/domain/state/auto_sign_in_via_deep_link_state.dart';
|
||||
import 'package:tmail_ui_user/features/login/presentation/model/login_navigate_type.dart';
|
||||
import 'package:tmail_ui_user/main/routes/router_arguments.dart';
|
||||
|
||||
class LoginNavigateArguments extends RouterArguments {
|
||||
|
||||
final LoginNavigateType navigateType;
|
||||
final AutoSignInViaDeepLinkSuccess? autoSignInViaDeepLinkSuccess;
|
||||
|
||||
LoginNavigateArguments({
|
||||
required this.navigateType,
|
||||
this.autoSignInViaDeepLinkSuccess,
|
||||
});
|
||||
|
||||
factory LoginNavigateArguments.autoSignIn({
|
||||
required AutoSignInViaDeepLinkSuccess autoSignInViaDeepLinkSuccess,
|
||||
}) {
|
||||
return LoginNavigateArguments(
|
||||
navigateType: LoginNavigateType.autoSignIn,
|
||||
autoSignInViaDeepLinkSuccess: autoSignInViaDeepLinkSuccess,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
navigateType,
|
||||
autoSignInViaDeepLinkSuccess,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
enum LoginNavigateType {
|
||||
autoSignIn
|
||||
}
|
||||
+80
@@ -8,6 +8,7 @@ import 'package:email_recovery/email_recovery/email_recovery_action.dart';
|
||||
import 'package:email_recovery/email_recovery/email_recovery_action_id.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||
@@ -74,9 +75,12 @@ import 'package:tmail_ui_user/features/email/presentation/model/composer_argumen
|
||||
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
|
||||
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_arguments.dart';
|
||||
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
||||
import 'package:tmail_ui_user/features/home/domain/state/auto_sign_in_via_deep_link_state.dart';
|
||||
import 'package:tmail_ui_user/features/home/domain/usecases/store_session_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/identity_creator/domain/state/get_identity_cache_on_web_state.dart';
|
||||
import 'package:tmail_ui_user/features/identity_creator/domain/usecase/get_identity_cache_on_web_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/exceptions/logout_exception.dart';
|
||||
import 'package:tmail_ui_user/features/login/presentation/model/login_navigate_arguments.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/domain/exceptions/mailbox_exception.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart';
|
||||
@@ -157,6 +161,9 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_e
|
||||
import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_mailbox_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart';
|
||||
import 'package:tmail_ui_user/main/deep_links/deep_link_data.dart';
|
||||
import 'package:tmail_ui_user/main/deep_links/deep_links_manager.dart';
|
||||
import 'package:tmail_ui_user/main/deep_links/open_app_deep_link_data.dart';
|
||||
import 'package:tmail_ui_user/main/exceptions/remote_exception.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
@@ -254,6 +261,8 @@ class MailboxDashBoardController extends ReloadableController
|
||||
StreamSubscription? _receivingFileSharingStreamSubscription;
|
||||
StreamSubscription? _currentEmailIdInNotificationIOSStreamSubscription;
|
||||
bool _isFirstSessionLoad = false;
|
||||
DeepLinksManager? _deepLinksManager;
|
||||
StreamSubscription<DeepLinkData?>? _deepLinkDataStreamSubscription;
|
||||
|
||||
final StreamController<Either<Failure, Success>> _progressStateController =
|
||||
StreamController<Either<Failure, Success>>.broadcast();
|
||||
@@ -301,6 +310,7 @@ class MailboxDashBoardController extends ReloadableController
|
||||
void onInit() {
|
||||
if (PlatformInfo.isMobile) {
|
||||
_registerReceivingFileSharingStream();
|
||||
_registerDeepLinks();
|
||||
}
|
||||
_registerStreamListener();
|
||||
BackButtonInterceptor.add(_onBackButtonInterceptor, name: AppRoutes.dashboard);
|
||||
@@ -467,6 +477,8 @@ class MailboxDashBoardController extends ReloadableController
|
||||
|
||||
@override
|
||||
void handleUrgentExceptionOnMobile({Failure? failure, Exception? exception}) {
|
||||
SmartDialog.dismiss();
|
||||
|
||||
if (failure is SendEmailFailure && exception is NoNetworkError) {
|
||||
_storeSendingEmailInCaseOfSendingFailureInMobile(failure);
|
||||
} else {
|
||||
@@ -563,6 +575,71 @@ class MailboxDashBoardController extends ReloadableController
|
||||
);
|
||||
}
|
||||
|
||||
void _registerDeepLinks() {
|
||||
_deepLinksManager = getBinding<DeepLinksManager>();
|
||||
_deepLinksManager?.clearPendingDeepLinkData();
|
||||
_deepLinkDataStreamSubscription = _deepLinksManager
|
||||
?.pendingDeepLinkData.stream
|
||||
.listen(_handlePendingDeepLinkDataStream);
|
||||
}
|
||||
|
||||
void _handlePendingDeepLinkDataStream(DeepLinkData? deepLinkData) {
|
||||
log('MailboxDashBoardController::_handlePendingDeepLinkDataStream:DeepLinkData = $deepLinkData');
|
||||
_deepLinksManager?.handleDeepLinksWhenAppRunning(
|
||||
deepLinkData: deepLinkData,
|
||||
onSuccessCallback: (deepLinkData) {
|
||||
if (deepLinkData is! OpenAppDeepLinkData) return;
|
||||
|
||||
_deepLinksManager?.handleOpenAppDeepLinks(
|
||||
openAppDeepLinkData: deepLinkData,
|
||||
isSignedIn: true,
|
||||
username: sessionCurrent?.username,
|
||||
onConfirmLogoutCallback: _handleLogOutAndSignInNewAccount,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _handleLogOutAndSignInNewAccount(OpenAppDeepLinkData openAppDeepLinkData) {
|
||||
if (sessionCurrent == null || accountId.value == null) return;
|
||||
|
||||
if (currentContext != null) {
|
||||
SmartDialog.showLoading(msg: AppLocalizations.of(currentContext!).loadingPleaseWait);
|
||||
}
|
||||
|
||||
logoutToSignInNewAccount(
|
||||
session: sessionCurrent!,
|
||||
accountId: accountId.value!,
|
||||
onFailureCallback: ({exception}) {
|
||||
if (exception is UserCancelledLogoutOIDCFlowException) {
|
||||
_deepLinksManager?.autoSignInViaDeepLink(
|
||||
openAppDeepLinkData: openAppDeepLinkData,
|
||||
onFailureCallback: SmartDialog.dismiss,
|
||||
onAutoSignInSuccessCallback: _handleAutoSignInViaDeepLinkSuccess,
|
||||
);
|
||||
} else {
|
||||
SmartDialog.dismiss();
|
||||
}
|
||||
},
|
||||
onSuccessCallback: () => _deepLinksManager?.autoSignInViaDeepLink(
|
||||
openAppDeepLinkData: openAppDeepLinkData,
|
||||
onFailureCallback: SmartDialog.dismiss,
|
||||
onAutoSignInSuccessCallback: _handleAutoSignInViaDeepLinkSuccess,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleAutoSignInViaDeepLinkSuccess(AutoSignInViaDeepLinkSuccess success) {
|
||||
SmartDialog.dismiss();
|
||||
|
||||
pushAndPopAll(
|
||||
AppRoutes.home,
|
||||
arguments: LoginNavigateArguments.autoSignIn(
|
||||
autoSignInViaDeepLinkSuccess: success,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _registerPendingCurrentEmailIdInNotification() {
|
||||
_iosNotificationManager = getBinding<IOSNotificationManager>();
|
||||
_currentEmailIdInNotificationIOSStreamSubscription = _iosNotificationManager
|
||||
@@ -1625,6 +1702,8 @@ class MailboxDashBoardController extends ReloadableController
|
||||
@override
|
||||
void handleReloaded(Session session) {
|
||||
log('MailboxDashBoardController::handleReloaded():');
|
||||
SmartDialog.dismiss();
|
||||
|
||||
_getRouteParameters();
|
||||
_setUpComponentsFromSession(session);
|
||||
if (PlatformInfo.isWeb) {
|
||||
@@ -3144,6 +3223,7 @@ class MailboxDashBoardController extends ReloadableController
|
||||
_pendingSharedFileInfoSubscription?.cancel();
|
||||
_receivingFileSharingStreamSubscription?.cancel();
|
||||
_emailReceiveManager.closeEmailReceiveManagerStream();
|
||||
_deepLinkDataStreamSubscription?.cancel();
|
||||
}
|
||||
_progressStateController.close();
|
||||
_refreshActionEventController.close();
|
||||
|
||||
@@ -17,6 +17,7 @@ extension KeychainSharingSessionExtension on KeychainSharingSession {
|
||||
tokenEndpoint: tokenEndpoint,
|
||||
oidcScopes: oidcScopes,
|
||||
mailboxIdsBlockNotification: mailboxIdsBlockNotification ?? this.mailboxIdsBlockNotification,
|
||||
isTWP: isTWP,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ class KeychainSharingSession with EquatableMixin {
|
||||
String? tokenEndpoint;
|
||||
List<String>? oidcScopes;
|
||||
List<MailboxId>? mailboxIdsBlockNotification;
|
||||
bool isTWP;
|
||||
|
||||
KeychainSharingSession({
|
||||
required this.accountId,
|
||||
@@ -40,6 +41,7 @@ class KeychainSharingSession with EquatableMixin {
|
||||
this.tokenEndpoint,
|
||||
this.oidcScopes,
|
||||
this.mailboxIdsBlockNotification,
|
||||
this.isTWP = false,
|
||||
});
|
||||
|
||||
factory KeychainSharingSession.fromJson(Map<String, dynamic> json) => _$KeychainSharingSessionFromJson(json);
|
||||
@@ -59,5 +61,6 @@ class KeychainSharingSession with EquatableMixin {
|
||||
tokenEndpoint,
|
||||
oidcScopes,
|
||||
mailboxIdsBlockNotification,
|
||||
isTWP,
|
||||
];
|
||||
}
|
||||
@@ -36,7 +36,7 @@ class SignInTwakeWorkplaceInteractor {
|
||||
await Future.wait([
|
||||
_credentialRepository.saveBaseUrl(baseUri),
|
||||
_authenticationOIDCRepository.persistTokenOIDC(tokenOIDC),
|
||||
_authenticationOIDCRepository.persistAuthorityOidc(oidcConfiguration.authority),
|
||||
_authenticationOIDCRepository.persistOidcConfiguration(oidcConfiguration),
|
||||
]);
|
||||
|
||||
await _accountRepository.setCurrentAccount(
|
||||
|
||||
@@ -36,7 +36,7 @@ class SignUpTwakeWorkplaceInteractor {
|
||||
await Future.wait([
|
||||
_credentialRepository.saveBaseUrl(baseUri),
|
||||
_authenticationOIDCRepository.persistTokenOIDC(tokenOIDC),
|
||||
_authenticationOIDCRepository.persistAuthorityOidc(oidcConfiguration.authority),
|
||||
_authenticationOIDCRepository.persistOidcConfiguration(oidcConfiguration),
|
||||
]);
|
||||
|
||||
await _accountRepository.setCurrentAccount(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tmail_ui_user/features/base/base_bindings.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart';
|
||||
@@ -38,9 +37,6 @@ class TwakeWelcomeBindings extends BaseBindings {
|
||||
Get.find<AccountRepository>(),
|
||||
Get.find<CredentialRepository>(),
|
||||
));
|
||||
if (PlatformInfo.isMobile) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
+25
-11
@@ -23,6 +23,7 @@ import 'package:tmail_ui_user/features/starting_page/domain/usecase/sign_in_twak
|
||||
import 'package:tmail_ui_user/features/starting_page/domain/usecase/sign_up_twake_workplace_interactor.dart';
|
||||
import 'package:tmail_ui_user/main/deep_links/deep_link_data.dart';
|
||||
import 'package:tmail_ui_user/main/deep_links/deep_links_manager.dart';
|
||||
import 'package:tmail_ui_user/main/deep_links/open_app_deep_link_data.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
@@ -61,16 +62,21 @@ class TwakeWelcomeController extends ReloadableController {
|
||||
|
||||
void _handlePendingDeepLinkDataStream(DeepLinkData? deepLinkData) {
|
||||
log('TwakeWelcomeController::_handlePendingDeepLinkDataStream:DeepLinkData = $deepLinkData');
|
||||
if (deepLinkData == null) return;
|
||||
|
||||
if (currentContext != null) {
|
||||
SmartDialog.showLoading(msg: AppLocalizations.of(currentContext!).loadingPleaseWait);
|
||||
}
|
||||
|
||||
_deepLinksManager?.handleDeepLinksWhenAppOnForegroundNotSignedIn(
|
||||
_deepLinksManager?.handleDeepLinksWhenAppRunning(
|
||||
deepLinkData: deepLinkData,
|
||||
onSuccessCallback: _handleAutoSignInViaDeepLinkSuccess,
|
||||
onFailureCallback: SmartDialog.dismiss,
|
||||
onSuccessCallback: (deepLinkData) {
|
||||
if (deepLinkData is! OpenAppDeepLinkData) return;
|
||||
|
||||
if (currentContext != null) {
|
||||
SmartDialog.showLoading(msg: AppLocalizations.of(currentContext!).loadingPleaseWait);
|
||||
}
|
||||
|
||||
_deepLinksManager?.autoSignInViaDeepLink(
|
||||
openAppDeepLinkData: deepLinkData,
|
||||
onAutoSignInSuccessCallback: _handleAutoSignInViaDeepLinkSuccess,
|
||||
onFailureCallback: SmartDialog.dismiss,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -107,7 +113,8 @@ class TwakeWelcomeController extends ReloadableController {
|
||||
oidcConfiguration: OIDCConfiguration(
|
||||
authority: AppConfig.saasRegistrationUrl,
|
||||
clientId: OIDCConstant.clientId,
|
||||
scopes: AppConfig.oidcScopes
|
||||
scopes: AppConfig.oidcScopes,
|
||||
isTWP: true,
|
||||
)
|
||||
));
|
||||
}
|
||||
@@ -127,7 +134,8 @@ class TwakeWelcomeController extends ReloadableController {
|
||||
oidcConfiguration: OIDCConfiguration(
|
||||
authority: AppConfig.saasRegistrationUrl,
|
||||
clientId: OIDCConstant.clientId,
|
||||
scopes: AppConfig.oidcScopes
|
||||
scopes: AppConfig.oidcScopes,
|
||||
isTWP: true,
|
||||
)
|
||||
));
|
||||
}
|
||||
@@ -178,6 +186,12 @@ class TwakeWelcomeController extends ReloadableController {
|
||||
toastManager.showMessageFailure(failure);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleUrgentExceptionOnMobile({Failure? failure, Exception? exception}) {
|
||||
SmartDialog.dismiss();
|
||||
super.handleUrgentExceptionOnMobile(failure: failure, exception: exception);
|
||||
}
|
||||
|
||||
void _synchronizeTokenAndGetSession({
|
||||
required Uri baseUri,
|
||||
required TokenOIDC tokenOIDC,
|
||||
|
||||
Reference in New Issue
Block a user