Skip to content

Commit 2dbf5aa

Browse files
committed
TF-2431 Handle switch active account
Signed-off-by: dab246 <[email protected]>
1 parent a3e247f commit 2dbf5aa

28 files changed

+416
-97
lines changed

lib/features/base/base_controller.dart

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_mixin.da
2525
import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixin.dart';
2626
import 'package:tmail_ui_user/features/caching/caching_manager.dart';
2727
import 'package:tmail_ui_user/features/email/presentation/bindings/mdn_interactor_bindings.dart';
28+
import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart';
2829
import 'package:tmail_ui_user/features/login/data/network/config/authorization_interceptors.dart';
2930
import 'package:tmail_ui_user/features/login/domain/state/logout_basic_auth_state.dart';
3031
import 'package:tmail_ui_user/features/login/domain/state/logout_oidc_state.dart';
3132
import 'package:tmail_ui_user/features/login/domain/state/logout_state.dart';
3233
import 'package:tmail_ui_user/features/login/domain/usecases/logout_interactor.dart';
34+
import 'package:tmail_ui_user/features/login/domain/usecases/set_current_account_active_interactor.dart';
3335
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
3436
import 'package:tmail_ui_user/features/login/presentation/model/login_arguments.dart';
3537
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/contact_autocomplete_bindings.dart';
@@ -75,6 +77,7 @@ abstract class BaseController extends GetxController
7577
final ResponsiveUtils responsiveUtils = Get.find<ResponsiveUtils>();
7678
final Uuid uuid = Get.find<Uuid>();
7779
final AppStore appStore = Get.find<AppStore>();
80+
final SetCurrentAccountActiveInteractor _setCurrentAccountActiveInteractor = Get.find<SetCurrentAccountActiveInteractor>();
7881

7982
final _fcmReceiver = FcmReceiver.instance;
8083
bool _isFcmEnabled = false;
@@ -383,4 +386,38 @@ abstract class BaseController extends GetxController
383386
logError('BaseController::clearAllData: Exception: $e | Stack: $s');
384387
}
385388
}
389+
390+
void setUpInterceptors(PersonalAccount personalAccount) {
391+
dynamicUrlInterceptors.setJmapUrl(personalAccount.baseUrl);
392+
dynamicUrlInterceptors.changeBaseUrl(personalAccount.baseUrl);
393+
394+
switch(personalAccount.authType) {
395+
case AuthenticationType.oidc:
396+
authorizationInterceptors.setTokenAndAuthorityOidc(
397+
newToken: personalAccount.tokenOidc,
398+
newConfig: personalAccount.tokenOidc!.oidcConfiguration
399+
);
400+
authorizationIsolateInterceptors.setTokenAndAuthorityOidc(
401+
newToken: personalAccount.tokenOidc,
402+
newConfig: personalAccount.tokenOidc!.oidcConfiguration
403+
);
404+
break;
405+
case AuthenticationType.basic:
406+
authorizationInterceptors.setBasicAuthorization(
407+
personalAccount.basicAuth!.userName,
408+
personalAccount.basicAuth!.password,
409+
);
410+
authorizationIsolateInterceptors.setBasicAuthorization(
411+
personalAccount.basicAuth!.userName,
412+
personalAccount.basicAuth!.password,
413+
);
414+
break;
415+
default:
416+
break;
417+
}
418+
}
419+
420+
void setCurrentAccountActive(PersonalAccount activeAccount) {
421+
consumeState(_setCurrentAccountActiveInteractor.execute(activeAccount));
422+
}
386423
}

lib/features/base/reloadable/reloadable_controller.dart

Lines changed: 19 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,24 @@ import 'package:get/get.dart';
55
import 'package:jmap_dart_client/jmap/account_id.dart';
66
import 'package:jmap_dart_client/jmap/core/session/session.dart';
77
import 'package:jmap_dart_client/jmap/core/user_name.dart';
8-
import 'package:model/account/authentication_type.dart';
9-
import 'package:model/account/personal_account.dart';
108
import 'package:model/extensions/session_extension.dart';
119
import 'package:tmail_ui_user/features/base/base_controller.dart';
1210
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
1311
import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart';
1412
import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart';
15-
import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart';
1613
import 'package:tmail_ui_user/features/login/domain/state/get_authenticated_account_state.dart';
14+
import 'package:tmail_ui_user/features/login/domain/usecases/add_account_id_to_active_account_interactor.dart';
1715
import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart';
18-
import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart';
1916
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
2017
import 'package:tmail_ui_user/features/login/presentation/model/login_arguments.dart';
2118
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
2219
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
2320
import 'package:tmail_ui_user/main/utils/message_toast_utils.dart';
2421

2522
abstract class ReloadableController extends BaseController {
26-
final GetSessionInteractor _getSessionInteractor = Get.find<GetSessionInteractor>();
23+
final GetSessionInteractor getSessionInteractor = Get.find<GetSessionInteractor>();
2724
final GetAuthenticatedAccountInteractor _getAuthenticatedAccountInteractor = Get.find<GetAuthenticatedAccountInteractor>();
28-
final UpdateAuthenticationAccountInteractor _updateAuthenticationAccountInteractor = Get.find<UpdateAuthenticationAccountInteractor>();
25+
final AddAccountIdToActiveAccountInteractor _addAccountIdToActiveAccountInteractor = Get.find<AddAccountIdToActiveAccountInteractor>();
2926

3027
@override
3128
void handleFailureViewState(Failure failure) {
@@ -68,38 +65,8 @@ abstract class ReloadableController extends BaseController {
6865
consumeState(_getAuthenticatedAccountInteractor.execute());
6966
}
7067

71-
void setUpInterceptors(PersonalAccount personalAccount) {
72-
dynamicUrlInterceptors.setJmapUrl(personalAccount.baseUrl);
73-
dynamicUrlInterceptors.changeBaseUrl(personalAccount.baseUrl);
74-
75-
switch(personalAccount.authType) {
76-
case AuthenticationType.oidc:
77-
authorizationInterceptors.setTokenAndAuthorityOidc(
78-
newToken: personalAccount.tokenOidc,
79-
newConfig: personalAccount.tokenOidc!.oidcConfiguration
80-
);
81-
authorizationIsolateInterceptors.setTokenAndAuthorityOidc(
82-
newToken: personalAccount.tokenOidc,
83-
newConfig: personalAccount.tokenOidc!.oidcConfiguration
84-
);
85-
break;
86-
case AuthenticationType.basic:
87-
authorizationInterceptors.setBasicAuthorization(
88-
personalAccount.basicAuth!.userName,
89-
personalAccount.basicAuth!.password,
90-
);
91-
authorizationIsolateInterceptors.setBasicAuthorization(
92-
personalAccount.basicAuth!.userName,
93-
personalAccount.basicAuth!.password,
94-
);
95-
break;
96-
default:
97-
break;
98-
}
99-
}
100-
10168
void getSessionAction({AccountId? accountId, UserName? userName}) {
102-
consumeState(_getSessionInteractor.execute(
69+
consumeState(getSessionInteractor.execute(
10370
accountId: accountId,
10471
userName: userName
10572
));
@@ -121,7 +88,11 @@ abstract class ReloadableController extends BaseController {
12188
final apiUrl = session.getQualifiedApiUrl(baseUrl: dynamicUrlInterceptors.jmapUrl);
12289
if (apiUrl.isNotEmpty) {
12390
dynamicUrlInterceptors.changeBaseUrl(apiUrl);
124-
updateAuthenticationAccount(session, personalAccount.accountId, session.username);
91+
_addAccountIdToActiveAccount(
92+
personalAccount.accountId,
93+
session.username,
94+
apiUrl
95+
);
12596
handleReloaded(session);
12697
} else {
12798
clearDataAndGoToLoginPage();
@@ -130,10 +101,15 @@ abstract class ReloadableController extends BaseController {
130101

131102
void handleReloaded(Session session) {}
132103

133-
void updateAuthenticationAccount(Session session, AccountId accountId, UserName userName) {
134-
final apiUrl = session.getQualifiedApiUrl(baseUrl: dynamicUrlInterceptors.jmapUrl);
135-
if (apiUrl.isNotEmpty) {
136-
consumeState(_updateAuthenticationAccountInteractor.execute(accountId, apiUrl, userName));
137-
}
104+
void _addAccountIdToActiveAccount(
105+
AccountId accountId,
106+
UserName userName,
107+
String apiUrl,
108+
) {
109+
consumeState(_addAccountIdToActiveAccountInteractor.execute(
110+
accountId,
111+
apiUrl,
112+
userName
113+
));
138114
}
139115
}

lib/features/home/presentation/home_controller.dart

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import 'dart:async';
2+
3+
import 'package:core/utils/app_logger.dart';
14
import 'package:core/utils/platform_info.dart';
25
import 'package:flutter/foundation.dart';
36
import 'package:flutter_downloader/flutter_downloader.dart';
@@ -21,7 +24,12 @@ import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_email_cac
2124
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_login_url_cache_interactor.dart';
2225
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_login_username_interactor.dart';
2326
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_search_cache_interactor.dart';
27+
import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart';
28+
import 'package:tmail_ui_user/features/login/presentation/model/login_navigate_arguments.dart';
29+
import 'package:tmail_ui_user/features/login/presentation/model/login_navigate_type.dart';
2430
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/preview_email_arguments.dart';
31+
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/restore_active_account_arguments.dart';
32+
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/switch_active_account_arguments.dart';
2533
import 'package:tmail_ui_user/features/push_notification/presentation/services/fcm_receiver.dart';
2634
import 'package:tmail_ui_user/main/routes/app_routes.dart';
2735
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
@@ -44,8 +52,8 @@ class HomeController extends ReloadableController {
4452
this._cleanupRecentLoginUsernameCacheInteractor,
4553
);
4654

47-
PersonalAccount? currentAccount;
4855
EmailId? _emailIdPreview;
56+
StreamSubscription? _sessionStreamSubscription;
4957

5058
@override
5159
void onInit() {
@@ -65,6 +73,12 @@ class HomeController extends ReloadableController {
6573
super.onReady();
6674
}
6775

76+
@override
77+
void onClose() {
78+
_sessionStreamSubscription?.cancel();
79+
super.onClose();
80+
}
81+
6882
@override
6983
void handleReloaded(Session session) {
7084
if (_emailIdPreview != null) {
@@ -84,20 +98,26 @@ class HomeController extends ReloadableController {
8498
}
8599

86100
void _initFlutterDownloader() {
87-
FlutterDownloader
88-
.initialize(debug: kDebugMode)
89-
.then((_) => FlutterDownloader.registerCallback(downloadCallback));
101+
if (!FlutterDownloader.initialized) {
102+
FlutterDownloader
103+
.initialize(debug: kDebugMode)
104+
.then((_) => FlutterDownloader.registerCallback(downloadCallback));
105+
}
90106
}
91107

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

94110
void _handleNavigateToScreen() async {
95111
if (PlatformInfo.isMobile) {
96-
final firstTimeAppLaunch = await appStore.getItemBoolean(AppConfig.firstTimeAppLaunchKey);
97-
if (firstTimeAppLaunch) {
98-
await _cleanupCache();
112+
if (Get.arguments is LoginNavigateArguments) {
113+
_handleLoginNavigateArguments(Get.arguments);
99114
} else {
100-
_navigateToTwakeWelcomePage();
115+
final firstTimeAppLaunch = await appStore.getItemBoolean(AppConfig.firstTimeAppLaunchKey);
116+
if (firstTimeAppLaunch) {
117+
await _cleanupCache();
118+
} else {
119+
_navigateToTwakeWelcomePage();
120+
}
101121
}
102122
} else {
103123
await _cleanupCache();
@@ -148,4 +168,76 @@ class HomeController extends ReloadableController {
148168
}
149169
}
150170
}
171+
172+
void _handleLoginNavigateArguments(LoginNavigateArguments navigateArguments) async {
173+
if (navigateArguments.navigateType == LoginNavigateType.switchActiveAccount) {
174+
_switchActiveAccount(
175+
navigateArguments.currentAccount!,
176+
navigateArguments.sessionCurrentAccount!,
177+
navigateArguments.nextActiveAccount!);
178+
} else {
179+
await _cleanupCache();
180+
}
181+
}
182+
183+
void _switchActiveAccount(
184+
PersonalAccount currentActiveAccount,
185+
Session sessionCurrentAccount,
186+
PersonalAccount nextActiveAccount
187+
) {
188+
setUpInterceptors(nextActiveAccount);
189+
190+
_sessionStreamSubscription = getSessionInteractor.execute(
191+
accountId: nextActiveAccount.accountId,
192+
userName: nextActiveAccount.userName
193+
).listen(
194+
(viewState) {
195+
viewState.fold(
196+
(failure) => _handleGetSessionFailureWhenSwitchActiveAccount(
197+
currentActiveAccount: currentActiveAccount,
198+
session: sessionCurrentAccount,
199+
exception: failure),
200+
(success) => success is GetSessionSuccess
201+
? _handleGetSessionSuccessWhenSwitchActiveAccount(nextActiveAccount, success.session)
202+
: null,
203+
);
204+
},
205+
onError: (error, stack) {
206+
logError('HomeController::_switchActiveAccount:Exception: $error | Stack: $stack');
207+
_handleGetSessionFailureWhenSwitchActiveAccount(
208+
currentActiveAccount: currentActiveAccount,
209+
session: sessionCurrentAccount,
210+
exception: error);
211+
}
212+
);
213+
}
214+
215+
void _handleGetSessionSuccessWhenSwitchActiveAccount(
216+
PersonalAccount nextActiveAccount,
217+
Session sessionActiveAccount
218+
) async {
219+
log('HomeController::_handleGetSessionSuccessWhenSwitchActiveAccount:sessionActiveAccount: $sessionActiveAccount');
220+
await popAndPush(
221+
RouteUtils.generateNavigationRoute(AppRoutes.dashboard),
222+
arguments: SwitchActiveAccountArguments(
223+
session: sessionActiveAccount,
224+
nextActiveAccount: nextActiveAccount,
225+
)
226+
);
227+
}
228+
229+
void _handleGetSessionFailureWhenSwitchActiveAccount({
230+
required PersonalAccount currentActiveAccount,
231+
required Session session,
232+
dynamic exception
233+
}) async {
234+
logError('HomeController::_handleGetSessionFailureWhenSwitchActiveAccount:exception: $exception');
235+
await popAndPush(
236+
RouteUtils.generateNavigationRoute(AppRoutes.dashboard),
237+
arguments: RestoreActiveAccountArguments(
238+
currentAccount: currentActiveAccount,
239+
session: session
240+
)
241+
);
242+
}
151243
}

lib/features/login/data/datasource/account_datasource.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ abstract class AccountDatasource {
88
Future<void> deleteCurrentAccount(String accountId);
99

1010
Future<List<PersonalAccount>> getAllAccount();
11+
12+
Future<void> setCurrentAccountActive(PersonalAccount activeAccount);
1113
}

lib/features/login/data/datasource_impl/hive_account_datasource_impl.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,11 @@ class HiveAccountDatasourceImpl extends AccountDatasource {
4848
return await _accountCacheManager.getAllAccount();
4949
}).catchError(_exceptionThrower.throwException);
5050
}
51+
52+
@override
53+
Future<void> setCurrentAccountActive(PersonalAccount activeAccount) {
54+
return Future.sync(() async {
55+
return await _accountCacheManager.setCurrentAccountActive(activeAccount);
56+
}).catchError(_exceptionThrower.throwException);
57+
}
5158
}

lib/features/login/data/extensions/personal_account_extension.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extensio
88
import 'package:tmail_ui_user/features/login/data/model/account_cache.dart';
99

1010
extension PersonalAccountExtension on PersonalAccount {
11-
AccountCache toCache() {
11+
AccountCache toCache({bool? isSelected}) {
1212
return AccountCache(
1313
id: id,
1414
authType: authType.name,
15-
isSelected: isSelected,
15+
isSelected: isSelected ?? this.isSelected,
1616
baseUrl: baseUrl,
1717
accountId: accountId?.id.value,
1818
apiUrl: apiUrl,
@@ -36,7 +36,7 @@ extension PersonalAccountExtension on PersonalAccount {
3636
);
3737
}
3838

39-
PersonalAccount updateAccountId({
39+
PersonalAccount addAccountId({
4040
required AccountId accountId,
4141
required String apiUrl,
4242
required UserName userName,

lib/features/login/data/local/account_cache_manager.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,18 @@ class AccountCacheManager {
5757
throw NotFoundAuthenticatedAccountException();
5858
}
5959
}
60+
61+
Future<void> setCurrentAccountActive(PersonalAccount activeAccount) async {
62+
log('AccountCacheManager::setCurrentAccountActive(): $activeAccount');
63+
final newAccountCache = activeAccount.toCache(isSelected: true);
64+
final allAccounts = await _accountCacheClient.getAll();
65+
log('AccountCacheManager::setCurrentAccountActive::allAccounts(): $allAccounts');
66+
if (allAccounts.isNotEmpty) {
67+
final newAllAccounts = allAccounts.unselected().toList();
68+
if (newAllAccounts.isNotEmpty) {
69+
await _accountCacheClient.updateMultipleItem(newAllAccounts.toMap());
70+
}
71+
}
72+
return _accountCacheClient.insertItem(newAccountCache.id, newAccountCache);
73+
}
6074
}

lib/features/login/data/repository/account_repository_impl.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,9 @@ class AccountRepositoryImpl extends AccountRepository {
2727
Future<List<PersonalAccount>> getAllAccount() {
2828
return _accountDatasource.getAllAccount();
2929
}
30+
31+
@override
32+
Future<void> setCurrentAccountActive(PersonalAccount activeAccount) {
33+
return _accountDatasource.setCurrentAccountActive(activeAccount);
34+
}
3035
}

0 commit comments

Comments
 (0)