fix(cnb): skip local email cache and add safe email/state cache clearing

(cherry picked from commit 0e7f589df58240badc1d2340e9856882aa075b63)
This commit is contained in:
dab246
2025-12-02 11:52:48 +07:00
committed by Dat H. Pham
parent 9d4f6e73d2
commit b3e5ab0df4
21 changed files with 141 additions and 294 deletions
+6 -10
View File
@@ -31,8 +31,8 @@ class SentryManager {
if (!_isSentryAvailable) { if (!_isSentryAvailable) {
await fallBackRunner(); await fallBackRunner();
} }
} catch (e, st) { } catch (e) {
logError('[SentryManager] Init failed', exception: e, stackTrace: st); logWarning('[SentryManager] Init failed. Exception $e');
await fallBackRunner(); await fallBackRunner();
} }
} }
@@ -90,8 +90,8 @@ class SentryManager {
try { try {
await Sentry.configureScope((scope) => scope.setUser(user)); await Sentry.configureScope((scope) => scope.setUser(user));
log('[SentryManager] User set: ${user.email}'); log('[SentryManager] User set: ${user.email}');
} catch (e, st) { } catch (e) {
logError('[SentryManager] Set user failed', exception: e, stackTrace: st); logWarning('[SentryManager] Set user failed. Exception: $e');
} }
} }
@@ -101,12 +101,8 @@ class SentryManager {
try { try {
await Sentry.configureScope((scope) => scope.setUser(null)); await Sentry.configureScope((scope) => scope.setUser(null));
log('[SentryManager] User cleared'); log('[SentryManager] User cleared');
} catch (e, st) { } catch (e) {
logError( logWarning('[SentryManager] Clear user failed. Exception: $e');
'[SentryManager] Clear user failed',
exception: e,
stackTrace: st,
);
} }
} }
} }
+17 -51
View File
@@ -448,7 +448,7 @@ abstract class BaseController extends GetxController
return; return;
} }
await clearAllMailDataCached(); await cachingManager.clearMailDataCached();
_isFcmEnabled = _isFcmActivated(session, accountId); _isFcmEnabled = _isFcmActivated(session, accountId);
if (isAuthenticatedWithOidc) { if (isAuthenticatedWithOidc) {
@@ -503,11 +503,10 @@ abstract class BaseController extends GetxController
} }
Future<void> _handleDeleteFCMAndClearData() async { Future<void> _handleDeleteFCMAndClearData() async {
await Future.wait([ if (_isFcmEnabled) {
if (_isFcmEnabled) await _handleDeleteFCMRegistration();
_handleDeleteFCMRegistration(), }
clearAllData(), clearAllData();
]);
} }
Future<void> _handleDeleteFCMRegistration() async { Future<void> _handleDeleteFCMRegistration() async {
@@ -560,57 +559,24 @@ abstract class BaseController extends GetxController
removeAllPageAndGoToLogin(); removeAllPageAndGoToLogin();
} }
Future<void> clearAllMailDataCached() async {
try {
await cachingManager.clearMailDataCached();
if (PlatformInfo.isMobile) {
await cachingManager.clearAllFileInStorage();
}
} catch (e) {
logError('BaseController::clearAllMailDataCached:Exception = $e');
}
}
Future<void> clearAllData() async { Future<void> clearAllData() async {
try { try {
if (isAuthenticatedWithOidc) { await Future.wait([
await _clearOidcAuthData(); if (isAuthenticatedWithOidc)
} else { deleteCredentialInteractor.execute()
await _clearBasicAuthData(); else
} deleteAuthorityOidcInteractor.execute(),
cachingManager.clearAll(),
languageCacheManager.removeLanguage(),
]);
authorizationInterceptors.clear();
authorizationIsolateInterceptors.clear();
await cachingManager.closeHive();
} catch (e) { } catch (e) {
logWarning('BaseController::clearAllData:Exception = $e'); logWarning('BaseController::clearAllData: Cannot clear all data: $e');
} }
} }
Future<void> _clearBasicAuthData() async {
await Future.wait([
deleteCredentialInteractor.execute(),
cachingManager.clearAll(),
languageCacheManager.removeLanguage(),
]);
if (PlatformInfo.isMobile) {
await cachingManager.clearAllFileInStorage();
}
authorizationInterceptors.clear();
authorizationIsolateInterceptors.clear();
await cachingManager.closeHive();
}
Future<void> _clearOidcAuthData() async {
await Future.wait([
deleteAuthorityOidcInteractor.execute(),
cachingManager.clearAll(),
languageCacheManager.removeLanguage(),
]);
if (PlatformInfo.isMobile) {
await cachingManager.clearAllFileInStorage();
}
authorizationIsolateInterceptors.clear();
authorizationInterceptors.clear();
await cachingManager.closeHive();
}
int getMinInputLengthAutocomplete({ int getMinInputLengthAutocomplete({
required Session session, required Session session,
required AccountId accountId, required AccountId accountId,
@@ -11,7 +11,7 @@ class UpgradeHiveDatabaseStepsV13 extends UpgradeDatabaseSteps {
@override @override
Future<void> onUpgrade(int oldVersion, int newVersion) async { Future<void> onUpgrade(int oldVersion, int newVersion) async {
if (oldVersion > 0 && oldVersion < newVersion && newVersion == 13) { if (oldVersion > 0 && oldVersion < newVersion && newVersion == 13) {
await _cachingManager.clearMailboxCache(); await _cachingManager.clearMailboxAndStateCache();
} }
} }
} }
+91 -135
View File
@@ -1,14 +1,9 @@
import 'package:core/utils/app_logger.dart'; import 'package:core/utils/app_logger.dart';
import 'package:core/utils/file_utils.dart'; import 'package:core/utils/file_utils.dart';
import 'package:core/utils/platform_info.dart'; import 'package:core/utils/platform_info.dart';
import 'package:fcm/model/type_name.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:model/extensions/account_id_extensions.dart';
import 'package:tmail_ui_user/features/caching/clients/hive_cache_version_client.dart'; import 'package:tmail_ui_user/features/caching/clients/hive_cache_version_client.dart';
import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart'; import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart';
import 'package:tmail_ui_user/features/caching/manager/session_cache_manger.dart'; import 'package:tmail_ui_user/features/caching/manager/session_cache_manger.dart';
import 'package:tmail_ui_user/features/caching/utils/cache_utils.dart';
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart'; import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
import 'package:tmail_ui_user/features/cleanup/data/local/recent_login_url_cache_manager.dart'; import 'package:tmail_ui_user/features/cleanup/data/local/recent_login_url_cache_manager.dart';
import 'package:tmail_ui_user/features/cleanup/data/local/recent_login_username_cache_manager.dart'; import 'package:tmail_ui_user/features/cleanup/data/local/recent_login_username_cache_manager.dart';
@@ -20,13 +15,11 @@ import 'package:tmail_ui_user/features/login/data/local/oidc_configuration_cache
import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart';
import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart';
import 'package:tmail_ui_user/features/mailbox/data/local/state_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox/data/local/state_cache_manager.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart';
import 'package:tmail_ui_user/features/offline_mode/manager/new_email_cache_manager.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/new_email_cache_manager.dart';
import 'package:tmail_ui_user/features/offline_mode/manager/opened_email_cache_manager.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/opened_email_cache_manager.dart';
import 'package:tmail_ui_user/features/offline_mode/manager/sending_email_cache_manager.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/sending_email_cache_manager.dart';
import 'package:tmail_ui_user/features/push_notification/data/keychain/keychain_sharing_manager.dart'; import 'package:tmail_ui_user/features/push_notification/data/keychain/keychain_sharing_manager.dart';
import 'package:tmail_ui_user/features/push_notification/data/local/fcm_cache_manager.dart'; import 'package:tmail_ui_user/features/push_notification/data/local/fcm_cache_manager.dart';
import 'package:tmail_ui_user/features/push_notification/presentation/extensions/type_name_extension.dart';
import 'package:tmail_ui_user/features/thread/data/local/email_cache_manager.dart'; import 'package:tmail_ui_user/features/thread/data/local/email_cache_manager.dart';
class CachingManager { class CachingManager {
@@ -73,76 +66,108 @@ class CachingManager {
); );
Future<void> clearAll() async { Future<void> clearAll() async {
await Future.wait([ try {
clearMailDataCached(), await Future.wait([
_fcmCacheManager.clear(), clearMailDataCached(),
_accountCacheManager.clear(), clearAccountDataCached(),
if (PlatformInfo.isIOS) ], eagerError: true);
_keychainSharingManager.delete() } catch (e) {
], eagerError: true); logWarning('CachingManager::clearAll: Cannot clear all cache: $e');
}
} }
Future<void> clearMailDataCached() async { Future<void> clearMailDataCached() async {
await Future.wait([ try {
_stateCacheManager.clear(), await Future.wait([
_mailboxCacheManager.clear(), _stateCacheManager.clear(),
_emailCacheManager.clear(), _mailboxCacheManager.clear(),
if (PlatformInfo.isMobile) _emailCacheManager.clear(),
...[ _clearFCMStateCache(),
_sessionCacheManager.clear(), if (PlatformInfo.isMobile)
_newEmailCacheManager.clear(), ...[
_openedEmailCacheManager.clear(), clearDetailedEmailCache(),
_sendingEmailCacheManager.clearAllSendingEmails(), _sendingEmailCacheManager.clear(),
], _sessionCacheManager.clear(),
], eagerError: true); ],
if (PlatformInfo.isIOS)
_keychainSharingManager.delete(),
], eagerError: true);
} catch (e) {
logWarning('CachingManager::clearMailDataCached: Cannot clear mail data cache: $e');
}
} }
Future<void> clearEmailAndStateCache({AccountId? accountId, UserName? userName}) { Future<void> clearAccountDataCached() async {
log('CachingManager::clearEmailAndStateCache:userName=$userName'); try {
await Future.wait([
final hasUserContext = accountId != null && userName != null; _accountCacheManager.clear(),
_oidcConfigurationCacheManager.clear(),
return Future.wait([ _tokenOidcCacheManager.clear(),
if (hasUserContext) _authenticationInfoCacheManager.clear(),
..._clearEmailAndStateWithUser(accountId, userName) ], eagerError: true);
else } catch (e) {
..._clearEmailAndStateWithoutUser(), logWarning('CachingManager::clearAccountDataCached: Cannot clear account data cache: $e');
}
if (PlatformInfo.isMobile)
clearFCMEmailStateCache(accountId: accountId, userName: userName),
]);
} }
Future<void> clearDetailedEmailCache({AccountId? accountId, UserName? userName}) { Future<void> clearAllEmailAndStateCache() async {
log('CachingManager::clearDetailedEmailCache:userName=$userName'); try {
await Future.wait([
final hasUserContext = accountId != null && userName != null; _emailCacheManager.clear(),
_stateCacheManager.clear(),
return Future.wait([ _clearFCMStateCache(),
if (hasUserContext) if (PlatformInfo.isMobile)
..._clearDetailedEmailWithUser(accountId, userName) clearDetailedEmailCache(),
else ]);
..._clearDetailedEmailWithoutUser(), } catch (e) {
]); logWarning('CachingManager::_clearEmailAndStateCache: Cannot clear email and state cache: $e');
}
} }
Future<void> clearAllEmailAndStateCache({AccountId? accountId, UserName? userName}) { Future<void> clearDetailedEmailCache() async {
log('CachingManager::clearAllEmailAndStateCache:userName=$userName'); try {
await Future.wait([
return Future.wait([ _newEmailCacheManager.clear(),
clearEmailAndStateCache(accountId: accountId, userName: userName), _openedEmailCacheManager.clear(),
if (PlatformInfo.isMobile) _clearEmailContentFileInStorage(),
clearDetailedEmailCache(accountId: accountId, userName: userName), ]);
]); } catch (e) {
logWarning('CachingManager::clearDetailedEmailCache: Cannot clear detailed email cache: $e');
}
} }
Future<void> clearMailboxCache() { Future<void> _clearEmailContentFileInStorage() async {
return Future.wait([ try {
_stateCacheManager.deleteContainKey( await _fileUtils.removeFolder(
StateType.mailbox.getTupleKeyStoredWithoutAccount(), CachingConstants.openedEmailContentFolderName,
), );
_mailboxCacheManager.clear(), await _fileUtils.removeFolder(
], eagerError: true); CachingConstants.newEmailsContentFolderName,
);
} catch (e) {
logWarning(
'CachingManager::_clearEmailContentFileInStorage: Cannot clear file in storage: $e',
);
}
}
Future<void> _clearFCMStateCache() async {
try {
await _fcmCacheManager.clear();
} catch (e) {
logWarning('CachingManager::_clearFCMStateCache: Cannot clear fcm state cache: $e');
}
}
Future<void> clearMailboxAndStateCache() async {
try {
await Future.wait([
_mailboxCacheManager.clear(),
_stateCacheManager.clear(),
], eagerError: true);
} catch (e) {
logWarning('CachingManager::clearMailboxCache: Cannot clear mailbox cache: $e');
}
} }
Future<bool> storeCacheVersion(int newVersion) async { Future<bool> storeCacheVersion(int newVersion) async {
@@ -157,75 +182,6 @@ class CachingManager {
Future<void> closeHive({bool isolated = true}) => Future<void> closeHive({bool isolated = true}) =>
HiveCacheConfig.instance.closeHive(isolated: isolated); HiveCacheConfig.instance.closeHive(isolated: isolated);
Future<void> clearAllFileInStorage() async {
await _fileUtils.removeFolder(CachingConstants.openedEmailContentFolderName);
await _fileUtils.removeFolder(CachingConstants.newEmailsContentFolderName);
}
Future<void> clearFCMEmailStateCache({
AccountId? accountId,
UserName? userName,
}) async {
final hasUserContext = accountId != null && userName != null;
if (hasUserContext) {
await _fcmCacheManager.deleteByKey(
TypeName.emailDelivery.getTupleKeyStored(accountId, userName),
);
await _fcmCacheManager.deleteByKey(
TypeName.emailType.getTupleKeyStored(accountId, userName),
);
} else {
await _fcmCacheManager.deleteContainKey(
TypeName.emailDelivery.getTupleKeyStoredWithoutAccount(),
);
await _fcmCacheManager.deleteContainKey(
TypeName.emailType.getTupleKeyStoredWithoutAccount(),
);
}
}
List<Future<void>> _clearEmailAndStateWithUser(
AccountId accountId,
UserName userName,
) {
final emailKey = TupleKey(accountId.asString, userName.value).encodeKey;
final stateKey = StateType.email.getTupleKeyStored(accountId, userName);
return [
_emailCacheManager.deleteContainKey(emailKey),
_stateCacheManager.deleteByKey(stateKey),
];
}
List<Future<void>> _clearEmailAndStateWithoutUser() {
final stateKey = StateType.email.getTupleKeyStoredWithoutAccount();
return [
_emailCacheManager.clear(),
_stateCacheManager.deleteContainKey(stateKey),
];
}
List<Future<void>> _clearDetailedEmailWithUser(
AccountId accountId,
UserName userName,
) {
final emailKey = TupleKey(accountId.asString, userName.value).encodeKey;
return [
_newEmailCacheManager.deleteContainKey(emailKey),
_openedEmailCacheManager.deleteContainKey(emailKey),
clearAllFileInStorage(),
];
}
List<Future<void>> _clearDetailedEmailWithoutUser() {
return [
_newEmailCacheManager.clear(),
_openedEmailCacheManager.clear(),
clearAllFileInStorage(),
];
}
Future<void> clearLoginRecentData() async { Future<void> clearLoginRecentData() async {
await Future.wait([ await Future.wait([
_recentLoginUrlCacheManager.clear(), _recentLoginUrlCacheManager.clear(),
@@ -131,7 +131,7 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource {
@override @override
Future<void> deleteOidcConfiguration() { Future<void> deleteOidcConfiguration() {
return Future.sync(() async { return Future.sync(() async {
return await _oidcConfigurationCacheManager.deleteOidcConfiguration(); return await _oidcConfigurationCacheManager.clear();
}).catchError(_cacheExceptionThrower.throwException); }).catchError(_cacheExceptionThrower.throwException);
} }
@@ -163,7 +163,7 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource {
@override @override
Future<void> deleteTokenOIDC() { Future<void> deleteTokenOIDC() {
return Future.sync(() async { return Future.sync(() async {
return await _tokenOidcCacheManager.deleteTokenOidc(); return await _tokenOidcCacheManager.clear();
}).catchError(_cacheExceptionThrower.throwException); }).catchError(_cacheExceptionThrower.throwException);
} }
@@ -24,8 +24,8 @@ class AuthenticationInfoCacheManager extends CacheManagerInteraction {
} }
} }
Future<void> removeAuthenticationInfo() { Future<void> clear() {
return _authenticationInfoCacheClient.deleteItem(AuthenticationInfoCache.keyCacheValue); return _authenticationInfoCacheClient.clearAllData();
} }
@override @override
@@ -43,12 +43,9 @@ class OidcConfigurationCacheManager extends CacheManagerInteraction {
); );
} }
Future<void> deleteOidcConfiguration() async { Future<void> clear() async {
log('OidcConfigurationCacheManager::deleteOidcConfiguration()');
await Future.wait([ await Future.wait([
_oidcConfigurationCacheClient.deleteItem( _oidcConfigurationCacheClient.clearAllData(),
CachingConstants.oidcConfigurationCacheKeyName,
),
_sharedPreferences.remove(OIDCConstant.keyAuthorityOidc), _sharedPreferences.remove(OIDCConstant.keyAuthorityOidc),
]); ]);
} }
@@ -24,7 +24,7 @@ class TokenOidcCacheManager extends CacheManagerInteraction {
Future<void> persistOneTokenOidc(TokenOIDC tokenOIDC) async { Future<void> persistOneTokenOidc(TokenOIDC tokenOIDC) async {
log('TokenOidcCacheManager::persistOneTokenOidc(): $tokenOIDC'); log('TokenOidcCacheManager::persistOneTokenOidc(): $tokenOIDC');
await _tokenOidcCacheClient.clearAllData(); await clear();
log('TokenOidcCacheManager::persistOneTokenOidc(): key: ${tokenOIDC.tokenId.uuid}'); log('TokenOidcCacheManager::persistOneTokenOidc(): key: ${tokenOIDC.tokenId.uuid}');
log('TokenOidcCacheManager::persistOneTokenOidc(): key\'s hash: ${tokenOIDC.tokenIdHash}'); log('TokenOidcCacheManager::persistOneTokenOidc(): key\'s hash: ${tokenOIDC.tokenIdHash}');
log('TokenOidcCacheManager::persistOneTokenOidc(): token: ${tokenOIDC.token}'); log('TokenOidcCacheManager::persistOneTokenOidc(): token: ${tokenOIDC.token}');
@@ -32,7 +32,7 @@ class TokenOidcCacheManager extends CacheManagerInteraction {
log('TokenOidcCacheManager::persistOneTokenOidc(): done'); log('TokenOidcCacheManager::persistOneTokenOidc(): done');
} }
Future<void> deleteTokenOidc() async { Future<void> clear() async {
await _tokenOidcCacheClient.clearAllData(); await _tokenOidcCacheClient.clearAllData();
} }
@@ -42,6 +42,6 @@ class CredentialRepositoryImpl extends CredentialRepository {
@override @override
Future<void> removeAuthenticationInfo() { Future<void> removeAuthenticationInfo() {
return _authenticationInfoCacheManager.removeAuthenticationInfo(); return _authenticationInfoCacheManager.clear();
} }
} }
@@ -14,7 +14,6 @@ import 'package:get/get.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/user_name.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:model/account/password.dart'; import 'package:model/account/password.dart';
import 'package:model/extensions/session_extension.dart';
import 'package:model/oidc/oidc_configuration.dart'; import 'package:model/oidc/oidc_configuration.dart';
import 'package:model/oidc/request/oidc_request.dart'; import 'package:model/oidc/request/oidc_request.dart';
import 'package:model/oidc/response/oidc_response.dart'; import 'package:model/oidc/response/oidc_response.dart';
@@ -255,10 +254,7 @@ class LoginController extends ReloadableController {
SmartDialog.dismiss(); SmartDialog.dismiss();
if (PlatformInfo.isWeb && AppConfig.isForceEmailQueryEnabled) { if (PlatformInfo.isWeb && AppConfig.isForceEmailQueryEnabled) {
await cachingManager.clearAllEmailAndStateCache( await cachingManager.clearAllEmailAndStateCache();
accountId: session.safeAccountId,
userName: session.username
);
} }
popAndPush( popAndPush(
@@ -612,10 +612,7 @@ class MailboxDashBoardController extends ReloadableController
Future<void> onBeforeUnloadBrowserListener(html.Event event) async { Future<void> onBeforeUnloadBrowserListener(html.Event event) async {
log('MailboxDashBoardController::onBeforeUnloadBrowserListener:event = ${event.runtimeType} | hasComposer = ${twakeAppManager.hasComposer} | isExecutingBeforeReconnect = ${twakeAppManager.isExecutingBeforeReconnect}'); log('MailboxDashBoardController::onBeforeUnloadBrowserListener:event = ${event.runtimeType} | hasComposer = ${twakeAppManager.hasComposer} | isExecutingBeforeReconnect = ${twakeAppManager.isExecutingBeforeReconnect}');
if (PlatformInfo.isWeb) { if (PlatformInfo.isWeb) {
await cachingManager.clearAllEmailAndStateCache( await cachingManager.clearAllEmailAndStateCache();
accountId: accountId.value,
userName: sessionCurrent?.username,
);
} }
if (event is html.BeforeUnloadEvent && if (event is html.BeforeUnloadEvent &&
@@ -140,10 +140,7 @@ class ManageAccountDashBoardController extends ReloadableController
@override @override
Future<void> onBeforeUnloadBrowserListener(html.Event event) async { Future<void> onBeforeUnloadBrowserListener(html.Event event) async {
if (PlatformInfo.isWeb) { if (PlatformInfo.isWeb) {
await cachingManager.clearAllEmailAndStateCache( await cachingManager.clearAllEmailAndStateCache();
accountId: accountId.value,
userName: sessionCurrent?.username,
);
} }
} }
@@ -53,7 +53,7 @@ class SendingEmailCacheManager extends CacheManagerInteraction {
return sendingEmailsCache; return sendingEmailsCache;
} }
Future<void> clearAllSendingEmails() => _hiveCacheClient.clearAllData(); Future<void> clear() => _hiveCacheClient.clearAllData();
Future<SendingEmailHiveCache> updateSendingEmail( Future<SendingEmailHiveCache> updateSendingEmail(
AccountId accountId, AccountId accountId,
@@ -89,11 +89,6 @@ class FCMCacheManager extends CacheManagerInteraction {
]); ]);
} }
Future<void> deleteByKey(String key) => _fcmCacheClient.deleteItem(key);
Future<void> deleteContainKey(String key) =>
_fcmCacheClient.clearAllDataContainKey(key);
@override @override
Future<void> migrateHiveToIsolatedHive() async { Future<void> migrateHiveToIsolatedHive() async {
await Future.wait([ await Future.wait([
@@ -77,5 +77,5 @@ abstract class ThreadDataSource {
Future<PresentationEmail> getEmailById(Session session, AccountId accountId, EmailId emailId, {Properties? properties}); Future<PresentationEmail> getEmailById(Session session, AccountId accountId, EmailId emailId, {Properties? properties});
Future<void> clearEmailCacheAndStateCache(AccountId accountId, Session session); Future<void> clearEmailCacheAndStateCache();
} }
@@ -133,12 +133,9 @@ class LocalThreadDataSourceImpl extends ThreadDataSource {
} }
@override @override
Future<void> clearEmailCacheAndStateCache(AccountId accountId, Session session) { Future<void> clearEmailCacheAndStateCache() {
return Future.sync(() async { return Future.sync(() async {
return await _cachingManager.clearAllEmailAndStateCache( return await _cachingManager.clearAllEmailAndStateCache();
accountId: accountId,
userName: session.username,
);
}).catchError(_exceptionThrower.throwException); }).catchError(_exceptionThrower.throwException);
} }
} }
@@ -142,7 +142,7 @@ class ThreadDataSourceImpl extends ThreadDataSource {
} }
@override @override
Future<void> clearEmailCacheAndStateCache(AccountId accountId, Session session) { Future<void> clearEmailCacheAndStateCache() {
throw UnimplementedError(); throw UnimplementedError();
} }
} }
@@ -148,37 +148,8 @@ class ThreadRepositoryImpl extends ThreadRepository {
EmailFilter? emailFilter, EmailFilter? emailFilter,
Properties? propertiesCreated, Properties? propertiesCreated,
}) async* { }) async* {
final localDataSource = mapDataSource[DataSourceType.local];
final networkDataSource = mapDataSource[DataSourceType.network];
if (localDataSource == null || networkDataSource == null) {
logError(
'ThreadRepositoryImpl::forceQueryAllEmailsForWeb(): '
'Missing required data sources (local or network).',
);
return;
}
List<Email> cachedList = const [];
jmap.State? cachedState; jmap.State? cachedState;
// Load cached emails
try {
cachedList = await localDataSource.getAllEmailCache(
accountId,
session.username,
inMailboxId: emailFilter?.mailboxId,
sort: sort,
limit: limit,
filterOption: emailFilter?.filterOption,
);
} catch (_) {
logError(
'ThreadRepositoryImpl::forceQueryAllEmailsForWeb(): '
'Failed to load cached emails',
);
}
// Load cached state // Load cached state
try { try {
cachedState = await stateDataSource.getState( cachedState = await stateDataSource.getState(
@@ -187,29 +158,19 @@ class ThreadRepositoryImpl extends ThreadRepository {
StateType.email, StateType.email,
); );
} catch (_) { } catch (_) {
logError( logWarning(
'ThreadRepositoryImpl::forceQueryAllEmailsForWeb(): ' 'ThreadRepositoryImpl::forceQueryAllEmailsForWeb(): '
'Failed to load cached email state', 'Failed to load cached email state',
); );
} }
final localResponse = EmailsResponse(
emailList: cachedList,
state: cachedState,
);
log( log(
'ThreadRepositoryImpl::forceQueryAllEmailsForWeb(): ' 'ThreadRepositoryImpl::forceQueryAllEmailsForWeb(): '
'Local cache count = ${cachedList.length}; ' 'State cache = ${cachedState?.value}',
'State = ${cachedState?.value}',
); );
if (localResponse.hasEmails()) {
yield localResponse;
}
// Query fresh emails from server // Query fresh emails from server
final serverResponse = await networkDataSource.getAllEmail( final serverResponse = await mapDataSource[DataSourceType.network]!.getAllEmail(
session, session,
accountId, accountId,
limit: limit, limit: limit,
@@ -588,13 +549,8 @@ class ThreadRepositoryImpl extends ThreadRepository {
} }
@override @override
Future<void> clearEmailCacheAndStateCache( Future<void> clearEmailCacheAndStateCache() =>
AccountId accountId, mapDataSource[DataSourceType.local]!.clearEmailCacheAndStateCache();
Session session,
) => mapDataSource[DataSourceType.local]!.clearEmailCacheAndStateCache(
accountId,
session,
);
@override @override
Stream<EmailsResponse> loadAllEmailInFolderWithoutCache({ Stream<EmailsResponse> loadAllEmailInFolderWithoutCache({
@@ -102,5 +102,5 @@ abstract class ThreadRepository {
StreamController<dartz.Either<Failure, Success>> onProgressController StreamController<dartz.Either<Failure, Success>> onProgressController
); );
Future<void> clearEmailCacheAndStateCache(AccountId accountId, Session session); Future<void> clearEmailCacheAndStateCache();
} }
@@ -36,7 +36,7 @@ class CleanAndGetEmailsInMailboxInteractor {
try { try {
yield Right<Failure, Success>(CleanAndGetAllEmailLoading()); yield Right<Failure, Success>(CleanAndGetAllEmailLoading());
await _threadRepository.clearEmailCacheAndStateCache(accountId, session); await _threadRepository.clearEmailCacheAndStateCache();
yield* _getEmailsInMailboxInteractor.execute( yield* _getEmailsInMailboxInteractor.execute(
session, session,
@@ -480,10 +480,7 @@ class ThreadController extends BaseController with EmailActionController {
void _handleErrorGetAllOrRefreshChangesEmail(Object error, StackTrace stackTrace) async { void _handleErrorGetAllOrRefreshChangesEmail(Object error, StackTrace stackTrace) async {
logWarning('ThreadController::_handleErrorGetAllOrRefreshChangesEmail():Error: $error'); logWarning('ThreadController::_handleErrorGetAllOrRefreshChangesEmail():Error: $error');
if (error is CannotCalculateChangesMethodResponseException) { if (error is CannotCalculateChangesMethodResponseException) {
await cachingManager.clearAllEmailAndStateCache( await cachingManager.clearAllEmailAndStateCache();
accountId: _accountId,
userName: _session?.username,
);
getAllEmailAction(); getAllEmailAction();
} else if (error is MethodLevelErrors) { } else if (error is MethodLevelErrors) {
if (currentOverlayContext != null && error.message != null) { if (currentOverlayContext != null && error.message != null) {
@@ -609,10 +606,7 @@ class ThreadController extends BaseController with EmailActionController {
Future<void> refreshAllEmail({bool forceEmailQuery = false}) async { Future<void> refreshAllEmail({bool forceEmailQuery = false}) async {
if (forceEmailQuery) { if (forceEmailQuery) {
await cachingManager.clearAllEmailAndStateCache( await cachingManager.clearAllEmailAndStateCache();
accountId: mailboxDashBoardController.accountId.value,
userName: mailboxDashBoardController.sessionCurrent?.username,
);
} }
if (searchController.isSearchEmailRunning) { if (searchController.isSearchEmailRunning) {