TF-3385 Update cache on set method

This commit is contained in:
DatDang
2025-01-02 17:23:18 +07:00
committed by Dat H. Pham
parent ea283207a7
commit 4bf4113c88
6 changed files with 296 additions and 48 deletions
@@ -18,11 +18,13 @@ import 'package:jmap_dart_client/jmap/core/properties/properties.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:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
import 'package:model/account/account_request.dart'; import 'package:model/account/account_request.dart';
import 'package:model/download/download_task_id.dart'; import 'package:model/download/download_task_id.dart';
import 'package:model/email/attachment.dart'; import 'package:model/email/attachment.dart';
import 'package:model/email/mark_star_action.dart'; import 'package:model/email/mark_star_action.dart';
import 'package:model/email/read_actions.dart'; import 'package:model/email/read_actions.dart';
import 'package:model/extensions/email_extension.dart';
import 'package:model/extensions/email_id_extensions.dart'; import 'package:model/extensions/email_id_extensions.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/composer/domain/model/email_request.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart';
@@ -79,8 +81,13 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
AccountId accountId, AccountId accountId,
EmailId emailId, EmailId emailId,
{CancelToken? cancelToken} {CancelToken? cancelToken}
) { ) async {
throw UnimplementedError(); await _emailCacheManager.update(
accountId,
session.username,
destroyed: [emailId],
);
return true;
} }
@override @override
@@ -91,8 +98,16 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
Session session, Session session,
AccountId accountId, AccountId accountId,
List<EmailId> emailIds, List<EmailId> emailIds,
) { ) async {
throw UnimplementedError(); await _emailCacheManager.update(
accountId,
session.username,
destroyed: emailIds,
);
return (
emailIdsSuccess: emailIds,
mapErrors: <Id, SetError>{}
);
} }
@override @override
@@ -137,8 +152,24 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
AccountId accountId, AccountId accountId,
List<EmailId> emailIds, List<EmailId> emailIds,
ReadActions readActions, ReadActions readActions,
) { ) async {
throw UnimplementedError(); final storedEmails = await Future.wait(emailIds.map(
(emailId) => getStoredEmail(session, accountId, emailId),
));
for (var email in storedEmails) {
if (readActions == ReadActions.markAsUnread) {
email.keywords?.remove(KeyWordIdentifier.emailSeen);
} else {
email.keywords?[KeyWordIdentifier.emailSeen] = true;
}
}
await Future.wait(storedEmails.map(
(email) => storeEmail(session, accountId, email),
));
return (
emailIdsSuccess: emailIds,
mapErrors: <Id, SetError>{}
);
} }
@override @override
@@ -150,8 +181,24 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
AccountId accountId, AccountId accountId,
List<EmailId> emailIds, List<EmailId> emailIds,
MarkStarAction markStarAction, MarkStarAction markStarAction,
) { ) async {
throw UnimplementedError(); final storedEmails = await Future.wait(emailIds.map(
(emailId) => getStoredEmail(session, accountId, emailId),
));
for (var email in storedEmails) {
if (markStarAction == MarkStarAction.unMarkStar) {
email.keywords?.remove(KeyWordIdentifier.emailFlagged);
} else {
email.keywords?[KeyWordIdentifier.emailFlagged] = true;
}
}
await Future.wait(storedEmails.map(
(email) => storeEmail(session, accountId, email),
));
return (
emailIdsSuccess: emailIds,
mapErrors: <Id, SetError>{}
);
} }
@override @override
@@ -162,8 +209,29 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
Session session, Session session,
AccountId accountId, AccountId accountId,
MoveToMailboxRequest moveRequest, MoveToMailboxRequest moveRequest,
) { ) async {
throw UnimplementedError(); final emailIds = moveRequest.currentMailboxes.entries.fold(
<EmailId>{},
(emailIds, entry) {
emailIds.addAll(entry.value);
return emailIds;
},
).toList();
final storedEmails = await Future.wait(emailIds.map(
(emailId) => getStoredEmail(session, accountId, emailId),
));
for (int i = 0; i < storedEmails.length; i++) {
storedEmails[i] = storedEmails[i].updatedEmail(
newMailboxIds: {moveRequest.destinationMailboxId: true},
);
}
await Future.wait(storedEmails.map(
(email) => storeEmail(session, accountId, email),
));
return (
emailIdsSuccess: emailIds,
mapErrors: <Id, SetError>{}
);
} }
@override @override
@@ -172,8 +240,13 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
AccountId accountId, AccountId accountId,
EmailId emailId, EmailId emailId,
{CancelToken? cancelToken} {CancelToken? cancelToken}
) { ) async {
throw UnimplementedError(); await _emailCacheManager.update(
accountId,
session.username,
destroyed: [emailId],
);
return true;
} }
@override @override
@@ -182,8 +255,9 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
AccountId accountId, AccountId accountId,
Email email, Email email,
{CancelToken? cancelToken} {CancelToken? cancelToken}
) { ) async {
throw UnimplementedError(); await _emailCacheManager.update(accountId, session.username, created: [email]);
return email;
} }
@override @override
@@ -232,8 +306,13 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
Email newEmail, Email newEmail,
EmailId oldEmailId, EmailId oldEmailId,
{CancelToken? cancelToken} {CancelToken? cancelToken}
) { ) async {
throw UnimplementedError(); await _emailCacheManager.update(
accountId,
session.username,
updated: [newEmail],
);
return newEmail;
} }
@override @override
@@ -93,13 +93,22 @@ class EmailRepositoryImpl extends EmailRepository {
AccountId accountId, AccountId accountId,
List<EmailId> emailIds, List<EmailId> emailIds,
ReadActions readActions, ReadActions readActions,
) { ) async {
return emailDataSource[DataSourceType.network]!.markAsRead( final result = await emailDataSource[DataSourceType.network]!.markAsRead(
session, session,
accountId, accountId,
emailIds, emailIds,
readActions, readActions,
); );
await emailDataSource[DataSourceType.hiveCache]!.markAsRead(
session,
accountId,
result.emailIdsSuccess,
readActions,
);
return result;
} }
@override @override
@@ -136,12 +145,34 @@ class EmailRepositoryImpl extends EmailRepository {
Session session, Session session,
AccountId accountId, AccountId accountId,
MoveToMailboxRequest moveRequest, MoveToMailboxRequest moveRequest,
) { ) async {
return emailDataSource[DataSourceType.network]!.moveToMailbox( final result = await emailDataSource[DataSourceType.network]
!.moveToMailbox(
session, session,
accountId, accountId,
moveRequest, moveRequest,
); );
final updatedCurrentMailboxes = moveRequest.currentMailboxes.map(
(key, value) => MapEntry(
key,
value.where(result.emailIdsSuccess.contains).toList(),
),
);
await emailDataSource[DataSourceType.hiveCache]
!.moveToMailbox(
session,
accountId,
MoveToMailboxRequest(
updatedCurrentMailboxes,
moveRequest.destinationMailboxId,
moveRequest.moveAction,
moveRequest.emailActionType,
destinationPath: moveRequest.destinationPath,
),
);
return result;
} }
@override @override
@@ -153,13 +184,20 @@ class EmailRepositoryImpl extends EmailRepository {
AccountId accountId, AccountId accountId,
List<EmailId> emailIds, List<EmailId> emailIds,
MarkStarAction markStarAction MarkStarAction markStarAction
) { ) async {
return emailDataSource[DataSourceType.network]!.markAsStar( final result = await emailDataSource[DataSourceType.network]!.markAsStar(
session, session,
accountId, accountId,
emailIds, emailIds,
markStarAction, markStarAction,
); );
await emailDataSource[DataSourceType.hiveCache]!.markAsStar(
session,
accountId,
result.emailIdsSuccess,
markStarAction
);
return result;
} }
@override @override
@@ -185,13 +223,20 @@ class EmailRepositoryImpl extends EmailRepository {
AccountId accountId, AccountId accountId,
Email email, Email email,
{CancelToken? cancelToken} {CancelToken? cancelToken}
) { ) async {
return emailDataSource[DataSourceType.network]!.saveEmailAsDrafts( final result = await emailDataSource[DataSourceType.network]!.saveEmailAsDrafts(
session, session,
accountId, accountId,
email, email,
cancelToken: cancelToken cancelToken: cancelToken
); );
await emailDataSource[DataSourceType.hiveCache]!.saveEmailAsDrafts(
session,
accountId,
result,
cancelToken: cancelToken
);
return result;
} }
@override @override
@@ -200,13 +245,20 @@ class EmailRepositoryImpl extends EmailRepository {
AccountId accountId, AccountId accountId,
EmailId emailId, EmailId emailId,
{CancelToken? cancelToken} {CancelToken? cancelToken}
) { ) async {
return emailDataSource[DataSourceType.network]!.removeEmailDrafts( final result = await emailDataSource[DataSourceType.network]!.removeEmailDrafts(
session, session,
accountId, accountId,
emailId, emailId,
cancelToken: cancelToken cancelToken: cancelToken
); );
await emailDataSource[DataSourceType.hiveCache]!.removeEmailDrafts(
session,
accountId,
emailId,
cancelToken: cancelToken
);
return result;
} }
@override @override
@@ -216,14 +268,21 @@ class EmailRepositoryImpl extends EmailRepository {
Email newEmail, Email newEmail,
EmailId oldEmailId, EmailId oldEmailId,
{CancelToken? cancelToken} {CancelToken? cancelToken}
) { ) async {
return emailDataSource[DataSourceType.network]!.updateEmailDrafts( final result = await emailDataSource[DataSourceType.network]!.updateEmailDrafts(
session, session,
accountId, accountId,
newEmail, newEmail,
oldEmailId, oldEmailId,
cancelToken: cancelToken cancelToken: cancelToken
); );
await emailDataSource[DataSourceType.hiveCache]!.updateEmailDrafts(
session,
accountId,
result,
oldEmailId,
);
return result;
} }
@override @override
@@ -254,12 +313,17 @@ class EmailRepositoryImpl extends EmailRepository {
Session session, Session session,
AccountId accountId, AccountId accountId,
List<EmailId> emailIds, List<EmailId> emailIds,
) { ) async {
return emailDataSource[DataSourceType.network]!.deleteMultipleEmailsPermanently( final result = await emailDataSource[DataSourceType.network]
!.deleteMultipleEmailsPermanently(
session, session,
accountId, accountId,
emailIds, emailIds,
); );
await emailDataSource[DataSourceType.hiveCache]
!.deleteMultipleEmailsPermanently(session, accountId, result.emailIdsSuccess);
return result;
} }
@override @override
@@ -268,13 +332,20 @@ class EmailRepositoryImpl extends EmailRepository {
AccountId accountId, AccountId accountId,
EmailId emailId, EmailId emailId,
{CancelToken? cancelToken} {CancelToken? cancelToken}
) { ) async {
return emailDataSource[DataSourceType.network]!.deleteEmailPermanently( final result = await emailDataSource[DataSourceType.network]!.deleteEmailPermanently(
session, session,
accountId, accountId,
emailId, emailId,
cancelToken: cancelToken cancelToken: cancelToken
); );
await emailDataSource[DataSourceType.hiveCache]!.deleteEmailPermanently(
session,
accountId,
emailId,
);
return result;
} }
@override @override
@@ -13,6 +13,8 @@ import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/extensions/list_mailbox_extension.dart';
import 'package:model/extensions/mailbox_extension.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.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/model/mailbox_change_response.dart'; import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_change_response.dart';
@@ -68,8 +70,26 @@ class MailboxCacheDataSourceImpl extends MailboxDataSource {
} }
@override @override
Future<bool> renameMailbox(Session session, AccountId accountId, RenameMailboxRequest request) { Future<bool> renameMailbox(
throw UnimplementedError(); Session session,
AccountId accountId,
RenameMailboxRequest request,
) async {
final cachedMailboxes = await getAllMailboxCache(accountId, session.username);
final updatedMailbox = cachedMailboxes.findMailbox(request.mailboxId);
if (updatedMailbox == null) return false;
final updatedMailboxIndex = cachedMailboxes.indexOf(updatedMailbox);
if (updatedMailboxIndex == -1) return false;
cachedMailboxes[updatedMailboxIndex] = updatedMailbox.copyWith(name: request.newName);
await update(
accountId,
session.username,
updated: cachedMailboxes,
);
return true;
} }
@override @override
@@ -79,12 +99,29 @@ class MailboxCacheDataSourceImpl extends MailboxDataSource {
@override @override
Future<List<EmailId>> markAsMailboxRead( Future<List<EmailId>> markAsMailboxRead(
Session session, Session session,
AccountId accountId, AccountId accountId,
MailboxId mailboxId, MailboxId mailboxId,
int totalEmailUnread, int totalEmailUnread,
StreamController<dartz.Either<Failure, Success>> onProgressController) { StreamController<dartz.Either<Failure, Success>> onProgressController,
throw UnimplementedError(); ) async {
final mailboxes = await getAllMailboxCache(accountId, session.username);
final updatedMailbox = mailboxes.findMailbox(mailboxId);
if (updatedMailbox == null) return [];
final updatedMailboxIndex = mailboxes.indexOf(updatedMailbox);
if (updatedMailboxIndex == -1) return [];
mailboxes[updatedMailboxIndex] = updatedMailbox.copyWith(
unreadEmails: UnreadEmails(UnsignedInt(totalEmailUnread)),
);
await _mailboxCacheManager.update(
accountId,
session.username,
updated: mailboxes,
);
return [];
} }
@override @override
@@ -15,8 +15,10 @@ import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/email/read_actions.dart';
import 'package:model/extensions/list_mailbox_extension.dart'; import 'package:model/extensions/list_mailbox_extension.dart';
import 'package:model/extensions/mailbox_extension.dart'; import 'package:model/extensions/mailbox_extension.dart';
import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart';
import 'package:tmail_ui_user/features/mailbox/data/extensions/state_extension.dart'; import 'package:tmail_ui_user/features/mailbox/data/extensions/state_extension.dart';
@@ -36,10 +38,12 @@ class MailboxRepositoryImpl extends MailboxRepository {
final Map<DataSourceType, MailboxDataSource> mapDataSource; final Map<DataSourceType, MailboxDataSource> mapDataSource;
final StateDataSource stateDataSource; final StateDataSource stateDataSource;
final EmailDataSource? emailDataSource;
MailboxRepositoryImpl( MailboxRepositoryImpl(
this.mapDataSource, this.mapDataSource,
this.stateDataSource, this.stateDataSource,
[this.emailDataSource,]
); );
@override @override
@@ -211,23 +215,44 @@ class MailboxRepositoryImpl extends MailboxRepository {
} }
@override @override
Future<bool> renameMailbox(Session session, AccountId accountId, RenameMailboxRequest request) { Future<bool> renameMailbox(Session session, AccountId accountId, RenameMailboxRequest request) async {
return mapDataSource[DataSourceType.network]!.renameMailbox(session, accountId, request); final result = await mapDataSource[DataSourceType.network]
!.renameMailbox(session, accountId, request);
await mapDataSource[DataSourceType.local]
!.renameMailbox(session, accountId, request);
return result;
} }
@override @override
Future<List<EmailId>> markAsMailboxRead( Future<List<EmailId>> markAsMailboxRead(
Session session, Session session,
AccountId accountId, AccountId accountId,
MailboxId mailboxId, MailboxId mailboxId,
int totalEmailUnread, int totalEmailUnread,
StreamController<dartz.Either<Failure, Success>> onProgressController) async { StreamController<dartz.Either<Failure, Success>> onProgressController,
return mapDataSource[DataSourceType.network]!.markAsMailboxRead( ) async {
final result = await mapDataSource[DataSourceType.network]!.markAsMailboxRead(
session, session,
accountId, accountId,
mailboxId, mailboxId,
totalEmailUnread, totalEmailUnread,
onProgressController); onProgressController);
await mapDataSource[DataSourceType.local]!.markAsMailboxRead(
session,
accountId,
mailboxId,
totalEmailUnread - result.length,
onProgressController,
);
await emailDataSource?.markAsRead(
session,
accountId,
result,
ReadActions.markAsRead,
);
return result;
} }
@override @override
@@ -3,6 +3,7 @@ import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/base_bindings.dart'; import 'package:tmail_ui_user/features/base/base_bindings.dart';
import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart'; import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart';
import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart';
@@ -122,6 +123,7 @@ class MailboxBindings extends BaseBindings {
DataSourceType.local: Get.find<MailboxCacheDataSourceImpl>() DataSourceType.local: Get.find<MailboxCacheDataSourceImpl>()
}, },
Get.find<StateDataSource>(), Get.find<StateDataSource>(),
Get.find<EmailHiveCacheDataSourceImpl>(),
)); ));
} }
} }
@@ -1,5 +1,7 @@
import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox_rights.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/namespace.dart';
import 'package:model/model.dart'; import 'package:model/model.dart';
extension MailboxExtension on Mailbox { extension MailboxExtension on Mailbox {
@@ -68,4 +70,36 @@ extension MailboxExtension on Mailbox {
namespace: namespace, namespace: namespace,
); );
} }
Mailbox copyWith({
MailboxId? id,
MailboxName? name,
MailboxId? parentId,
Role? role,
SortOrder? sortOrder,
TotalEmails? totalEmails,
UnreadEmails? unreadEmails,
TotalThreads? totalThreads,
UnreadThreads? unreadThreads,
MailboxRights? myRights,
IsSubscribed? isSubscribed,
Namespace? namespace,
Map<String, List<String>?>? rights,
}) {
return Mailbox(
id: id ?? this.id,
name: name ?? this.name,
parentId: parentId ?? this.parentId,
role: role ?? this.role,
sortOrder: sortOrder ?? this.sortOrder,
totalEmails: totalEmails ?? this.totalEmails,
unreadEmails: unreadEmails ?? this.unreadEmails,
totalThreads: totalThreads ?? this.totalThreads,
unreadThreads: unreadThreads ?? this.unreadThreads,
myRights: myRights ?? this.myRights,
isSubscribed: isSubscribed ?? this.isSubscribed,
namespace: namespace ?? this.namespace,
rights: rights ?? this.rights,
);
}
} }