Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MA-PART-3] TF-2431 Implement multiple account on mobile #2435

Open
wants to merge 17 commits into
base: starting-page
Choose a base branch
from
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()
dab246 marked this conversation as resolved.
Show resolved Hide resolved
};
}
}
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