Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@
- \#1792 Add option 'Empty Trash' in folder menu

### Changed
- \#1755 Use TupleKey('ObjectId\|AccountId\|UserName') store data to cache
- \#1755 Use TupleKey('AccountId\|UserName\|ObjectId') store data to cache

### Fixed
- \#1385 Fix \[Email rule\] Icon edit and delete might be seen as disable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 27. Use TupleKey store data cache
# 27. Use TupleKey store data to hive database

Date: 2023-04-27

Expand All @@ -9,13 +9,14 @@ Accepted
## Context

- Multiple accounts login at the same time in the same browser. The accounts will use the same database (`IndexDatabase`).
- To support multiple accounts

## Decision

- Use unique parameters (`AccountId`, `UserName`, `ObjectId(MailboxId/EmailId/StateType`) to form a unique `key` for storage (called `TupleKey`).
- TupleKey has the format: `ObjectId | AccountId | User`;
- Use unique parameters (`AccountId`, `UserName`, `ObjectId(MailboxId/EmailId/StateType/...)`) to form a unique `key` for storage (called `TupleKey`).
- TupleKey has the format: `AccountId | UserName | [ObjectId]`;
- `HiveDatabase` includes many `Box`. Each box is a `Map<Key, Object>` with `key=TupleKey`.

## Consequences

- The correct `mailbox` and `email` lists are obtained for each account
- Each account will manage its own data storage boxes
6 changes: 3 additions & 3 deletions lib/features/caching/caching_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:core/utils/app_logger.dart';
import 'package:core/utils/file_utils.dart';
import 'package:core/utils/platform_info.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:tmail_ui_user/features/caching/clients/account_cache_client.dart';
import 'package:tmail_ui_user/features/caching/clients/email_cache_client.dart';
import 'package:tmail_ui_user/features/caching/clients/fcm_cache_client.dart';
Expand Down Expand Up @@ -97,9 +97,9 @@ class CachingManager {
], eagerError: true);
}

Future<void> clearEmailCacheAndStateCacheByTupleKey(AccountId accountId, Session session) {
Future<void> clearEmailCacheAndStateCacheByTupleKey(AccountId accountId, UserName userName) {
return Future.wait([
_stateCacheClient.deleteItem(StateType.email.getTupleKeyStored(accountId, session.username)),
_stateCacheClient.deleteItem(StateType.email.getTupleKeyStored(accountId, userName)),
_emailCacheClient.clearAllData(),
], eagerError: true);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/features/caching/utils/cache_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ class TupleKey {

TupleKey(
String key1,
String key2,
[
String? key2,
String? key3,
String? key4,
]
) : parts = [
key1,
if (key2 != null) key2,
key2,
if (key3 != null) key3,
if (key4 != null) key4,
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ extension ListMailboxExtension on List<Mailbox> {
Map<String, MailboxCache> toMapCache(AccountId accountId, UserName userName) {
return {
for (var mailbox in this)
TupleKey(mailbox.id!.asString, accountId.asString, userName.value).encodeKey : mailbox.toMailboxCache()
TupleKey(accountId.asString, userName.value, mailbox.id!.asString).encodeKey : mailbox.toMailboxCache()
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ import 'package:tmail_ui_user/features/caching/utils/cache_utils.dart';

extension ListMailboxIdExtension on List<MailboxId> {
List<String> toCacheKeyList(AccountId accountId, UserName userName) =>
map((id) => TupleKey(id.asString, accountId.asString, userName.value).encodeKey).toList();
map((id) => TupleKey(accountId.asString, userName.value, id.asString).encodeKey).toList();
}
4 changes: 2 additions & 2 deletions lib/features/mailbox/data/local/state_cache_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ class StateCacheManager {
StateCacheManager(this._stateCacheClient);

Future<State?> getState(AccountId accountId, UserName userName, StateType stateType) async {
final stateKey = TupleKey(stateType.name, accountId.asString, userName.value).encodeKey;
final stateKey = TupleKey(accountId.asString, userName.value, stateType.name).encodeKey;
final stateCache = await _stateCacheClient.getItem(stateKey);
return stateCache?.toState();
}

Future<void> saveState(AccountId accountId, UserName userName, StateCache stateCache) async {
final stateCacheExist = await _stateCacheClient.isExistTable();
final stateKey = TupleKey(stateCache.type.name, accountId.asString, userName.value).encodeKey;
final stateKey = TupleKey(accountId.asString, userName.value, stateCache.type.name).encodeKey;
if (stateCacheExist) {
return await _stateCacheClient.updateItem(stateKey, stateCache);
} else {
Expand Down
2 changes: 1 addition & 1 deletion lib/features/mailbox/data/model/state_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ enum StateType {
email;

String getTupleKeyStored(AccountId accountId, UserName userName) {
return TupleKey(name, accountId.asString, userName.value).encodeKey;
return TupleKey(accountId.asString, userName.value, name).encodeKey;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class NewEmailCacheManager {
UserName userName,
DetailedEmailHiveCache detailedEmailCache
) {
final keyCache = TupleKey(detailedEmailCache.emailId, accountId.asString, userName.value).encodeKey;
final keyCache = TupleKey(accountId.asString, userName.value, detailedEmailCache.emailId).encodeKey;
return _cacheClient.insertItem(keyCache, detailedEmailCache);
}

Expand All @@ -55,7 +55,7 @@ class NewEmailCacheManager {
UserName userName,
String emailId
) {
final keyCache = TupleKey(emailId, accountId.asString, userName.value).encodeKey;
final keyCache = TupleKey(accountId.asString, userName.value, emailId).encodeKey;
return _cacheClient.deleteItem(keyCache);
}

Expand All @@ -74,7 +74,7 @@ class NewEmailCacheManager {
UserName userName,
EmailId emailId
) async {
final keyCache = TupleKey(emailId.asString, accountId.asString, userName.value).encodeKey;
final keyCache = TupleKey(accountId.asString, userName.value, emailId.asString).encodeKey;
final detailedEmailCache = await _cacheClient.getItem(keyCache, needToReopen: true);
if (detailedEmailCache != null) {
return detailedEmailCache;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class OpenedEmailCacheManager {
UserName userName,
DetailedEmailHiveCache detailedEmailCache
) {
final keyCache = TupleKey(detailedEmailCache.emailId, accountId.asString, userName.value).encodeKey;
final keyCache = TupleKey(accountId.asString, userName.value, detailedEmailCache.emailId).encodeKey;
log('OpenedEmailCacheManager::insertDetailedEmail(): $keyCache');
return _cacheClient.insertItem(keyCache, detailedEmailCache);
}
Expand All @@ -34,7 +34,7 @@ class OpenedEmailCacheManager {
UserName userName,
String emailId
) {
final keyCache = TupleKey(emailId, accountId.asString, userName.value).encodeKey;
final keyCache = TupleKey(accountId.asString, userName.value, emailId).encodeKey;
log('OpenedEmailCacheManager::removeDetailedEmail(): $keyCache');
return _cacheClient.deleteItem(keyCache);
}
Expand Down Expand Up @@ -72,7 +72,7 @@ class OpenedEmailCacheManager {
UserName userName,
EmailId emailId
) async {
final keyCache = TupleKey(emailId.asString, accountId.asString, userName.value).encodeKey;
final keyCache = TupleKey(accountId.asString, userName.value, emailId.asString).encodeKey;
final detailedEmailCache = await _cacheClient.getItem(keyCache, needToReopen: true);
if (detailedEmailCache != null) {
return detailedEmailCache;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class SendingEmailCacheManager {
UserName userName,
SendingEmailHiveCache sendingEmailHiveCache
) async {
final keyCache = TupleKey(sendingEmailHiveCache.sendingId, accountId.asString, userName.value).encodeKey;
final keyCache = TupleKey(accountId.asString, userName.value, sendingEmailHiveCache.sendingId).encodeKey;
await _hiveCacheClient.insertItem(keyCache, sendingEmailHiveCache);
final newSendingEmailHiveCache = await _hiveCacheClient.getItem(keyCache);
if (newSendingEmailHiveCache != null) {
Expand All @@ -36,7 +36,7 @@ class SendingEmailCacheManager {
}

Future<void> deleteSendingEmail(AccountId accountId, UserName userName, String sendingId) async {
final keyCache = TupleKey(sendingId, accountId.asString, userName.value).encodeKey;
final keyCache = TupleKey(accountId.asString, userName.value, sendingId).encodeKey;
await _hiveCacheClient.deleteItem(keyCache);
final storedSendingEmail = await _hiveCacheClient.getItem(keyCache);
if (storedSendingEmail != null) {
Expand All @@ -57,7 +57,7 @@ class SendingEmailCacheManager {
UserName userName,
SendingEmailHiveCache sendingEmailHiveCache
) async {
final keyCache = TupleKey(sendingEmailHiveCache.sendingId, accountId.asString, userName.value).encodeKey;
final keyCache = TupleKey(accountId.asString, userName.value, sendingEmailHiveCache.sendingId).encodeKey;
await _hiveCacheClient.updateItem(keyCache, sendingEmailHiveCache);
final newSendingEmailHiveCache = await _hiveCacheClient.getItem(keyCache);
if (newSendingEmailHiveCache != null) {
Expand All @@ -74,15 +74,15 @@ class SendingEmailCacheManager {
) async {
final mapSendingEmailCache = {
for (var sendingEmailCache in listSendingEmailHiveCache)
TupleKey(sendingEmailCache.sendingId, accountId.asString, userName.value).encodeKey: sendingEmailCache
TupleKey(accountId.asString, userName.value, sendingEmailCache.sendingId).encodeKey: sendingEmailCache
};
await _hiveCacheClient.updateMultipleItem(mapSendingEmailCache);
final newListSendingEmailCache = await _hiveCacheClient.getValuesByListKey(mapSendingEmailCache.keys.toList());
return newListSendingEmailCache;
}

Future<void> deleteMultipleSendingEmail(AccountId accountId, UserName userName, List<String> sendingIds) async {
final listTupleKey = sendingIds.map((sendingId) => TupleKey(sendingId, accountId.asString, userName.value).encodeKey).toList();
final listTupleKey = sendingIds.map((sendingId) => TupleKey(accountId.asString, userName.value, sendingId).encodeKey).toList();
await _hiveCacheClient.deleteMultipleItem(listTupleKey);
final newListSendingEmailCache = await _hiveCacheClient.getValuesByListKey(listTupleKey);
if (newListSendingEmailCache.isNotEmpty) {
Expand All @@ -91,7 +91,7 @@ class SendingEmailCacheManager {
}

Future<SendingEmailHiveCache> getStoredSendingEmail(AccountId accountId, UserName userName, String sendingId) async {
final keyCache = TupleKey(sendingId, accountId.asString, userName.value).encodeKey;
final keyCache = TupleKey(accountId.asString, userName.value, sendingId).encodeKey;
final storedSendingEmail = await _hiveCacheClient.getItem(keyCache);
if (storedSendingEmail != null) {
return storedSendingEmail;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ class FCMCacheManager {
FCMCacheManager(this._fcmCacheClient,this._firebaseRegistrationCacheClient);

Future<void> storeStateToRefresh(AccountId accountId, UserName userName, TypeName typeName, jmap.State newState) {
final stateKeyCache = TupleKey(typeName.value, accountId.asString, userName.value).encodeKey;
final stateKeyCache = TupleKey(accountId.asString, userName.value, typeName.value).encodeKey;
return _fcmCacheClient.insertItem(stateKeyCache, newState.value);
}

Future<jmap.State> getStateToRefresh(AccountId accountId, UserName userName, TypeName typeName) async {
final stateKeyCache = TupleKey(typeName.value, accountId.asString, userName.value).encodeKey;
final stateKeyCache = TupleKey(accountId.asString, userName.value, typeName.value).encodeKey;
final stateValue = await _fcmCacheClient.getItem(stateKeyCache);
if (stateValue != null) {
return jmap.State(stateValue);
Expand All @@ -35,7 +35,7 @@ class FCMCacheManager {
}

Future<void> deleteStateToRefresh(AccountId accountId, UserName userName, TypeName typeName) {
final stateKeyCache = TupleKey(typeName.value, accountId.asString, userName.value).encodeKey;
final stateKeyCache = TupleKey(accountId.asString, userName.value, typeName.value).encodeKey;
return _fcmCacheClient.deleteItem(stateKeyCache);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ extension ListEmailExtension on List<Email> {
Map<String, EmailCache> toMapCache(AccountId accountId, UserName userName) {
return {
for (var email in this)
TupleKey(email.id!.asString, accountId.asString, userName.value).encodeKey : email.toEmailCache()
TupleKey(accountId.asString, userName.value, email.id!.asString).encodeKey : email.toEmailCache()
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ import 'package:tmail_ui_user/features/caching/utils/cache_utils.dart';

extension ListEmailIdExtension on List<EmailId> {
List<String> toCacheKeyList(AccountId accountId, UserName userName) =>
map((id) => TupleKey(id.asString, accountId.asString, userName.value).encodeKey).toList();
map((id) => TupleKey(accountId.asString, userName.value, id.asString).encodeKey).toList();
}
8 changes: 4 additions & 4 deletions lib/features/thread/data/local/email_cache_manager.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:model/model.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/caching/clients/email_cache_client.dart';
import 'package:tmail_ui_user/features/caching/utils/cache_utils.dart';
import 'package:tmail_ui_user/features/cleanup/domain/model/email_cleanup_rule.dart';
Expand All @@ -13,7 +14,6 @@ import 'package:tmail_ui_user/features/thread/data/extensions/email_extension.da
import 'package:tmail_ui_user/features/thread/data/extensions/list_email_cache_extension.dart';
import 'package:tmail_ui_user/features/thread/data/extensions/list_email_extension.dart';
import 'package:tmail_ui_user/features/thread/data/extensions/list_email_id_extension.dart';
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
import 'package:tmail_ui_user/features/thread/data/model/email_cache.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';

Expand Down Expand Up @@ -94,12 +94,12 @@ class EmailCacheManager {
}

Future<void> storeEmail(AccountId accountId, UserName userName, EmailCache emailCache) {
final keyCache = TupleKey(emailCache.id, accountId.asString, userName.value).encodeKey;
final keyCache = TupleKey(accountId.asString, userName.value, emailCache.id).encodeKey;
return _emailCacheClient.insertItem(keyCache, emailCache);
}

Future<EmailCache> getStoredEmail(AccountId accountId, UserName userName, EmailId emailId) async {
final keyCache = TupleKey(emailId.asString, accountId.asString, userName.value).encodeKey;
final keyCache = TupleKey(accountId.asString, userName.value, emailId.asString).encodeKey;
final emailCache = await _emailCacheClient.getItem(keyCache, needToReopen: true);
if (emailCache != null) {
return emailCache;
Expand Down
2 changes: 1 addition & 1 deletion lib/features/thread/presentation/thread_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ class ThreadController extends BaseController with EmailActionController {
logError('ThreadController::_handleErrorGetAllOrRefreshChangesEmail():Error: $error');
if (error is CannotCalculateChangesMethodResponseException) {
if (_accountId != null && _session != null) {
await cachingManager.clearEmailCacheAndStateCacheByTupleKey(_accountId!, _session!);
await cachingManager.clearEmailCacheAndStateCacheByTupleKey(_accountId!, _session!.username);
} else {
await cachingManager.clearEmailCacheAndAllStateCache();
}
Expand Down