Skip to content

Commit

Permalink
TF-2431 Handle switch active account
Browse files Browse the repository at this point in the history
Signed-off-by: dab246 <[email protected]>
  • Loading branch information
dab246 committed Jan 8, 2024
1 parent a3e247f commit 2dbf5aa
Show file tree
Hide file tree
Showing 28 changed files with 416 additions and 97 deletions.
37 changes: 37 additions & 0 deletions lib/features/base/base_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_mixin.da
import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixin.dart';
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/login/data/extensions/token_oidc_extension.dart';
import 'package:tmail_ui_user/features/login/data/network/config/authorization_interceptors.dart';
import 'package:tmail_ui_user/features/login/domain/state/logout_basic_auth_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/logout_oidc_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/logout_state.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/logout_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/set_current_account_active_interactor.dart';
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/mailbox_dashboard/presentation/bindings/contact_autocomplete_bindings.dart';
Expand Down Expand Up @@ -75,6 +77,7 @@ abstract class BaseController extends GetxController
final ResponsiveUtils responsiveUtils = Get.find<ResponsiveUtils>();
final Uuid uuid = Get.find<Uuid>();
final AppStore appStore = Get.find<AppStore>();
final SetCurrentAccountActiveInteractor _setCurrentAccountActiveInteractor = Get.find<SetCurrentAccountActiveInteractor>();

final _fcmReceiver = FcmReceiver.instance;
bool _isFcmEnabled = false;
Expand Down Expand Up @@ -383,4 +386,38 @@ abstract class BaseController extends GetxController
logError('BaseController::clearAllData: Exception: $e | Stack: $s');
}
}

void setUpInterceptors(PersonalAccount personalAccount) {
dynamicUrlInterceptors.setJmapUrl(personalAccount.baseUrl);
dynamicUrlInterceptors.changeBaseUrl(personalAccount.baseUrl);

switch(personalAccount.authType) {
case AuthenticationType.oidc:
authorizationInterceptors.setTokenAndAuthorityOidc(
newToken: personalAccount.tokenOidc,
newConfig: personalAccount.tokenOidc!.oidcConfiguration
);
authorizationIsolateInterceptors.setTokenAndAuthorityOidc(
newToken: personalAccount.tokenOidc,
newConfig: personalAccount.tokenOidc!.oidcConfiguration
);
break;
case AuthenticationType.basic:
authorizationInterceptors.setBasicAuthorization(
personalAccount.basicAuth!.userName,
personalAccount.basicAuth!.password,
);
authorizationIsolateInterceptors.setBasicAuthorization(
personalAccount.basicAuth!.userName,
personalAccount.basicAuth!.password,
);
break;
default:
break;
}
}

void setCurrentAccountActive(PersonalAccount activeAccount) {
consumeState(_setCurrentAccountActiveInteractor.execute(activeAccount));
}
}
62 changes: 19 additions & 43 deletions lib/features/base/reloadable/reloadable_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,24 @@ import 'package:get/get.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/user_name.dart';
import 'package:model/account/authentication_type.dart';
import 'package:model/account/personal_account.dart';
import 'package:model/extensions/session_extension.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart';
import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart';
import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart';
import 'package:tmail_ui_user/features/login/domain/state/get_authenticated_account_state.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/add_account_id_to_active_account_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart';
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/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
import 'package:tmail_ui_user/main/utils/message_toast_utils.dart';

abstract class ReloadableController extends BaseController {
final GetSessionInteractor _getSessionInteractor = Get.find<GetSessionInteractor>();
final GetSessionInteractor getSessionInteractor = Get.find<GetSessionInteractor>();
final GetAuthenticatedAccountInteractor _getAuthenticatedAccountInteractor = Get.find<GetAuthenticatedAccountInteractor>();
final UpdateAuthenticationAccountInteractor _updateAuthenticationAccountInteractor = Get.find<UpdateAuthenticationAccountInteractor>();
final AddAccountIdToActiveAccountInteractor _addAccountIdToActiveAccountInteractor = Get.find<AddAccountIdToActiveAccountInteractor>();

@override
void handleFailureViewState(Failure failure) {
Expand Down Expand Up @@ -68,38 +65,8 @@ abstract class ReloadableController extends BaseController {
consumeState(_getAuthenticatedAccountInteractor.execute());
}

void setUpInterceptors(PersonalAccount personalAccount) {
dynamicUrlInterceptors.setJmapUrl(personalAccount.baseUrl);
dynamicUrlInterceptors.changeBaseUrl(personalAccount.baseUrl);

switch(personalAccount.authType) {
case AuthenticationType.oidc:
authorizationInterceptors.setTokenAndAuthorityOidc(
newToken: personalAccount.tokenOidc,
newConfig: personalAccount.tokenOidc!.oidcConfiguration
);
authorizationIsolateInterceptors.setTokenAndAuthorityOidc(
newToken: personalAccount.tokenOidc,
newConfig: personalAccount.tokenOidc!.oidcConfiguration
);
break;
case AuthenticationType.basic:
authorizationInterceptors.setBasicAuthorization(
personalAccount.basicAuth!.userName,
personalAccount.basicAuth!.password,
);
authorizationIsolateInterceptors.setBasicAuthorization(
personalAccount.basicAuth!.userName,
personalAccount.basicAuth!.password,
);
break;
default:
break;
}
}

void getSessionAction({AccountId? accountId, UserName? userName}) {
consumeState(_getSessionInteractor.execute(
consumeState(getSessionInteractor.execute(
accountId: accountId,
userName: userName
));
Expand All @@ -121,7 +88,11 @@ abstract class ReloadableController extends BaseController {
final apiUrl = session.getQualifiedApiUrl(baseUrl: dynamicUrlInterceptors.jmapUrl);
if (apiUrl.isNotEmpty) {
dynamicUrlInterceptors.changeBaseUrl(apiUrl);
updateAuthenticationAccount(session, personalAccount.accountId, session.username);
_addAccountIdToActiveAccount(
personalAccount.accountId,
session.username,
apiUrl
);
handleReloaded(session);
} else {
clearDataAndGoToLoginPage();
Expand All @@ -130,10 +101,15 @@ abstract class ReloadableController extends BaseController {

void handleReloaded(Session session) {}

void updateAuthenticationAccount(Session session, AccountId accountId, UserName userName) {
final apiUrl = session.getQualifiedApiUrl(baseUrl: dynamicUrlInterceptors.jmapUrl);
if (apiUrl.isNotEmpty) {
consumeState(_updateAuthenticationAccountInteractor.execute(accountId, apiUrl, userName));
}
void _addAccountIdToActiveAccount(
AccountId accountId,
UserName userName,
String apiUrl,
) {
consumeState(_addAccountIdToActiveAccountInteractor.execute(
accountId,
apiUrl,
userName
));
}
}
108 changes: 100 additions & 8 deletions lib/features/home/presentation/home_controller.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import 'dart:async';

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';
Expand All @@ -21,7 +24,12 @@ import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_email_cac
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_login_url_cache_interactor.dart';
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_login_username_interactor.dart';
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_search_cache_interactor.dart';
import 'package:tmail_ui_user/features/home/domain/state/get_session_state.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/features/mailbox_dashboard/presentation/model/preview_email_arguments.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/restore_active_account_arguments.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/switch_active_account_arguments.dart';
import 'package:tmail_ui_user/features/push_notification/presentation/services/fcm_receiver.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
Expand All @@ -44,8 +52,8 @@ class HomeController extends ReloadableController {
this._cleanupRecentLoginUsernameCacheInteractor,
);

PersonalAccount? currentAccount;
EmailId? _emailIdPreview;
StreamSubscription? _sessionStreamSubscription;

@override
void onInit() {
Expand All @@ -65,6 +73,12 @@ class HomeController extends ReloadableController {
super.onReady();
}

@override
void onClose() {
_sessionStreamSubscription?.cancel();
super.onClose();
}

@override
void handleReloaded(Session session) {
if (_emailIdPreview != null) {
Expand All @@ -84,20 +98,26 @@ class HomeController extends ReloadableController {
}

void _initFlutterDownloader() {
FlutterDownloader
.initialize(debug: kDebugMode)
.then((_) => FlutterDownloader.registerCallback(downloadCallback));
if (!FlutterDownloader.initialized) {
FlutterDownloader
.initialize(debug: kDebugMode)
.then((_) => FlutterDownloader.registerCallback(downloadCallback));
}
}

static void downloadCallback(String id, DownloadTaskStatus status, int progress) {}

void _handleNavigateToScreen() async {
if (PlatformInfo.isMobile) {
final firstTimeAppLaunch = await appStore.getItemBoolean(AppConfig.firstTimeAppLaunchKey);
if (firstTimeAppLaunch) {
await _cleanupCache();
if (Get.arguments is LoginNavigateArguments) {
_handleLoginNavigateArguments(Get.arguments);
} else {
_navigateToTwakeWelcomePage();
final firstTimeAppLaunch = await appStore.getItemBoolean(AppConfig.firstTimeAppLaunchKey);
if (firstTimeAppLaunch) {
await _cleanupCache();
} else {
_navigateToTwakeWelcomePage();
}
}
} else {
await _cleanupCache();
Expand Down Expand Up @@ -148,4 +168,76 @@ class HomeController extends ReloadableController {
}
}
}

void _handleLoginNavigateArguments(LoginNavigateArguments navigateArguments) async {
if (navigateArguments.navigateType == LoginNavigateType.switchActiveAccount) {
_switchActiveAccount(
navigateArguments.currentAccount!,
navigateArguments.sessionCurrentAccount!,
navigateArguments.nextActiveAccount!);
} else {
await _cleanupCache();
}
}

void _switchActiveAccount(
PersonalAccount currentActiveAccount,
Session sessionCurrentAccount,
PersonalAccount nextActiveAccount
) {
setUpInterceptors(nextActiveAccount);

_sessionStreamSubscription = getSessionInteractor.execute(
accountId: nextActiveAccount.accountId,
userName: nextActiveAccount.userName
).listen(
(viewState) {
viewState.fold(
(failure) => _handleGetSessionFailureWhenSwitchActiveAccount(
currentActiveAccount: currentActiveAccount,
session: sessionCurrentAccount,
exception: failure),
(success) => success is GetSessionSuccess
? _handleGetSessionSuccessWhenSwitchActiveAccount(nextActiveAccount, success.session)
: null,
);
},
onError: (error, stack) {
logError('HomeController::_switchActiveAccount:Exception: $error | Stack: $stack');
_handleGetSessionFailureWhenSwitchActiveAccount(
currentActiveAccount: currentActiveAccount,
session: sessionCurrentAccount,
exception: error);
}
);
}

void _handleGetSessionSuccessWhenSwitchActiveAccount(
PersonalAccount nextActiveAccount,
Session sessionActiveAccount
) async {
log('HomeController::_handleGetSessionSuccessWhenSwitchActiveAccount:sessionActiveAccount: $sessionActiveAccount');
await popAndPush(
RouteUtils.generateNavigationRoute(AppRoutes.dashboard),
arguments: SwitchActiveAccountArguments(
session: sessionActiveAccount,
nextActiveAccount: nextActiveAccount,
)
);
}

void _handleGetSessionFailureWhenSwitchActiveAccount({
required PersonalAccount currentActiveAccount,
required Session session,
dynamic exception
}) async {
logError('HomeController::_handleGetSessionFailureWhenSwitchActiveAccount:exception: $exception');
await popAndPush(
RouteUtils.generateNavigationRoute(AppRoutes.dashboard),
arguments: RestoreActiveAccountArguments(
currentAccount: currentActiveAccount,
session: session
)
);
}
}
2 changes: 2 additions & 0 deletions lib/features/login/data/datasource/account_datasource.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ abstract class AccountDatasource {
Future<void> deleteCurrentAccount(String accountId);

Future<List<PersonalAccount>> getAllAccount();

Future<void> setCurrentAccountActive(PersonalAccount activeAccount);
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,11 @@ class HiveAccountDatasourceImpl extends AccountDatasource {
return await _accountCacheManager.getAllAccount();
}).catchError(_exceptionThrower.throwException);
}

@override
Future<void> setCurrentAccountActive(PersonalAccount activeAccount) {
return Future.sync(() async {
return await _accountCacheManager.setCurrentAccountActive(activeAccount);
}).catchError(_exceptionThrower.throwException);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extensio
import 'package:tmail_ui_user/features/login/data/model/account_cache.dart';

extension PersonalAccountExtension on PersonalAccount {
AccountCache toCache() {
AccountCache toCache({bool? isSelected}) {
return AccountCache(
id: id,
authType: authType.name,
isSelected: isSelected,
isSelected: isSelected ?? this.isSelected,
baseUrl: baseUrl,
accountId: accountId?.id.value,
apiUrl: apiUrl,
Expand All @@ -36,7 +36,7 @@ extension PersonalAccountExtension on PersonalAccount {
);
}

PersonalAccount updateAccountId({
PersonalAccount addAccountId({
required AccountId accountId,
required String apiUrl,
required UserName userName,
Expand Down
14 changes: 14 additions & 0 deletions lib/features/login/data/local/account_cache_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,18 @@ class AccountCacheManager {
throw NotFoundAuthenticatedAccountException();
}
}

Future<void> setCurrentAccountActive(PersonalAccount activeAccount) async {
log('AccountCacheManager::setCurrentAccountActive(): $activeAccount');
final newAccountCache = activeAccount.toCache(isSelected: true);
final allAccounts = await _accountCacheClient.getAll();
log('AccountCacheManager::setCurrentAccountActive::allAccounts(): $allAccounts');
if (allAccounts.isNotEmpty) {
final newAllAccounts = allAccounts.unselected().toList();
if (newAllAccounts.isNotEmpty) {
await _accountCacheClient.updateMultipleItem(newAllAccounts.toMap());
}
}
return _accountCacheClient.insertItem(newAccountCache.id, newAccountCache);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@ class AccountRepositoryImpl extends AccountRepository {
Future<List<PersonalAccount>> getAllAccount() {
return _accountDatasource.getAllAccount();
}

@override
Future<void> setCurrentAccountActive(PersonalAccount activeAccount) {
return _accountDatasource.setCurrentAccountActive(activeAccount);
}
}
Loading

0 comments on commit 2dbf5aa

Please sign in to comment.