TF-145 Add data layer for getAllEmail in Caching
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
import 'package:tmail_ui_user/features/caching/config/hive_cache_client.dart';
|
import 'package:tmail_ui_user/features/caching/config/hive_cache_client.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/data/model/email_cache.dart';
|
import 'package:tmail_ui_user/features/thread/data/model/email_cache.dart';
|
||||||
|
|
||||||
@@ -118,4 +119,17 @@ class EmailCacheClient extends HiveCacheClient<EmailCache> {
|
|||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<EmailCache>> getListEmailCacheByMailboxId(MailboxId mailboxId) {
|
||||||
|
return Future.sync(() async {
|
||||||
|
final boxEmail = await openBox();
|
||||||
|
return boxEmail.values.where((emailCache) {
|
||||||
|
return emailCache.mailboxIds != null
|
||||||
|
&& emailCache.mailboxIds!.containsKey(mailboxId.id.value)
|
||||||
|
&& emailCache.mailboxIds![mailboxId.id.value] == true;
|
||||||
|
}).toList();
|
||||||
|
}).catchError((error) {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,15 @@ import 'package:jmap_dart_client/jmap/account_id.dart';
|
|||||||
import 'package:jmap_dart_client/jmap/core/filter/filter.dart';
|
import 'package:jmap_dart_client/jmap/core/filter/filter.dart';
|
||||||
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/core/sort/comparator.dart';
|
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/state.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
import 'package:jmap_dart_client/jmap/core/unsigned_int.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:tmail_ui_user/features/thread/data/model/email_change_response.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
|
||||||
|
|
||||||
abstract class ThreadDataSource {
|
abstract class ThreadDataSource {
|
||||||
Future<List<Email>> getAllEmail(
|
Future<EmailResponse> getAllEmail(
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
{
|
{
|
||||||
int? position,
|
int? position,
|
||||||
@@ -16,4 +20,17 @@ abstract class ThreadDataSource {
|
|||||||
Properties? properties
|
Properties? properties
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Future<EmailChangeResponse> getChanges(
|
||||||
|
AccountId accountId,
|
||||||
|
State sinceState,
|
||||||
|
{
|
||||||
|
Properties? propertiesCreated,
|
||||||
|
Properties? propertiesUpdated
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Future<List<Email>> getAllEmailCache({MailboxId? inMailboxId, Set<Comparator>? sort});
|
||||||
|
|
||||||
|
Future<void> update({List<Email>? updated, List<Email>? created, List<EmailId>? destroyed});
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/filter/filter.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/state.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/local/email_cache_manager.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
|
||||||
|
|
||||||
|
class LocalThreadDataSourceImpl extends ThreadDataSource {
|
||||||
|
|
||||||
|
final EmailCacheManager _emailCacheManager;
|
||||||
|
|
||||||
|
LocalThreadDataSourceImpl(this._emailCacheManager);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<EmailResponse> getAllEmail(
|
||||||
|
AccountId accountId,
|
||||||
|
{
|
||||||
|
int? position,
|
||||||
|
UnsignedInt? limit,
|
||||||
|
Set<Comparator>? sort,
|
||||||
|
Filter? filter,
|
||||||
|
Properties? properties
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<EmailChangeResponse> getChanges(
|
||||||
|
AccountId accountId,
|
||||||
|
State sinceState,
|
||||||
|
{
|
||||||
|
Properties? propertiesCreated,
|
||||||
|
Properties? propertiesUpdated
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<Email>> getAllEmailCache({MailboxId? inMailboxId, Set<Comparator>? sort}) {
|
||||||
|
return Future.sync(() async {
|
||||||
|
return await _emailCacheManager.getAllEmail(inMailboxId: inMailboxId, sort: sort);
|
||||||
|
}).catchError((error) {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> update({List<Email>? updated, List<Email>? created, List<EmailId>? destroyed}) {
|
||||||
|
return Future.sync(() async {
|
||||||
|
return await _emailCacheManager.update(updated: updated, created: created, destroyed: destroyed);
|
||||||
|
}).catchError((error) {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,13 @@ import 'package:jmap_dart_client/jmap/account_id.dart';
|
|||||||
import 'package:jmap_dart_client/jmap/core/filter/filter.dart';
|
import 'package:jmap_dart_client/jmap/core/filter/filter.dart';
|
||||||
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/core/sort/comparator.dart';
|
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/state.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
import 'package:jmap_dart_client/jmap/core/unsigned_int.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:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart';
|
import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart';
|
import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart';
|
||||||
|
|
||||||
class ThreadDataSourceImpl extends ThreadDataSource {
|
class ThreadDataSourceImpl extends ThreadDataSource {
|
||||||
@@ -14,14 +18,17 @@ class ThreadDataSourceImpl extends ThreadDataSource {
|
|||||||
ThreadDataSourceImpl(this.threadAPI);
|
ThreadDataSourceImpl(this.threadAPI);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Email>> getAllEmail(
|
Future<EmailResponse> getAllEmail(
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
{
|
{
|
||||||
int? position,
|
int? position,
|
||||||
UnsignedInt? limit,
|
UnsignedInt? limit,
|
||||||
Set<Comparator>? sort,
|
Set<Comparator>? sort,
|
||||||
Filter? filter,
|
Filter? filter,
|
||||||
Properties? properties
|
Properties? properties,
|
||||||
|
Properties? propertiesCreated,
|
||||||
|
Properties? propertiesUpdated,
|
||||||
|
MailboxId? inMailboxId
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
return Future.sync(() async {
|
return Future.sync(() async {
|
||||||
@@ -36,4 +43,34 @@ class ThreadDataSourceImpl extends ThreadDataSource {
|
|||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<EmailChangeResponse> getChanges(
|
||||||
|
AccountId accountId,
|
||||||
|
State sinceState,
|
||||||
|
{
|
||||||
|
Properties? propertiesCreated,
|
||||||
|
Properties? propertiesUpdated
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
return Future.sync(() async {
|
||||||
|
return await threadAPI.getChanges(
|
||||||
|
accountId,
|
||||||
|
sinceState,
|
||||||
|
propertiesCreated: propertiesCreated,
|
||||||
|
propertiesUpdated: propertiesUpdated);
|
||||||
|
}).catchError((error) {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<Email>> getAllEmailCache({MailboxId? inMailboxId, Set<Comparator>? sort}) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> update({List<Email>? updated, List<Email>? created, List<EmailId>? destroyed}) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/model/email_address_hive_cache.dart';
|
||||||
|
|
||||||
|
extension EmailAddressHiveCacheExtension on EmailAddressHiveCache {
|
||||||
|
|
||||||
|
EmailAddress toEmailAddress() => EmailAddress(name, email);
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/utc_date.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/model/email_cache.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/extensions/email_address_hive_cache_extension.dart';
|
||||||
|
|
||||||
|
extension EmailCacheExtension on EmailCache {
|
||||||
|
Email toEmail() {
|
||||||
|
return Email(
|
||||||
|
EmailId(Id(id)),
|
||||||
|
keywords: keywords != null
|
||||||
|
? Map.fromIterables(keywords!.keys.map((value) => KeyWordIdentifier(value)), keywords!.values)
|
||||||
|
: null,
|
||||||
|
size: size != null ? UnsignedInt(size!) : null,
|
||||||
|
receivedAt: receivedAt != null ? UTCDate(receivedAt!) : null,
|
||||||
|
hasAttachment: hasAttachment,
|
||||||
|
preview: preview,
|
||||||
|
subject: subject,
|
||||||
|
sentAt: sentAt != null ? UTCDate(sentAt!) : null,
|
||||||
|
from: from?.map((emailAddressCache) => emailAddressCache.toEmailAddress()).toSet(),
|
||||||
|
to: to?.map((emailAddressCache) => emailAddressCache.toEmailAddress()).toSet(),
|
||||||
|
cc: cc?.map((emailAddressCache) => emailAddressCache.toEmailAddress()).toSet(),
|
||||||
|
bcc: bcc?.map((emailAddressCache) => emailAddressCache.toEmailAddress()).toSet(),
|
||||||
|
replyTo: replyTo?.map((emailAddressCache) => emailAddressCache.toEmailAddress()).toSet(),
|
||||||
|
mailboxIds: mailboxIds != null
|
||||||
|
? Map.fromIterables(mailboxIds!.keys.map((value) => MailboxId(Id(value))), mailboxIds!.values)
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,12 +2,13 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
|||||||
import 'package:tmail_ui_user/features/thread/data/model/email_cache.dart';
|
import 'package:tmail_ui_user/features/thread/data/model/email_cache.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/data/extensions/map_keywords_extension.dart';
|
import 'package:tmail_ui_user/features/thread/data/extensions/map_keywords_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/data/extensions/email_address_extension.dart';
|
import 'package:tmail_ui_user/features/thread/data/extensions/email_address_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/extensions/map_mailbox_id_extension.dart';
|
||||||
|
|
||||||
extension EmailExtension on Email {
|
extension EmailExtension on Email {
|
||||||
|
|
||||||
EmailCache toEmailCache() {
|
EmailCache toEmailCache() {
|
||||||
return EmailCache(
|
return EmailCache(
|
||||||
id.toString(),
|
id.id.value,
|
||||||
keywords: keywords?.toMapString(),
|
keywords: keywords?.toMapString(),
|
||||||
size: size?.value.round(),
|
size: size?.value.round(),
|
||||||
receivedAt: receivedAt?.value,
|
receivedAt: receivedAt?.value,
|
||||||
@@ -20,6 +21,7 @@ extension EmailExtension on Email {
|
|||||||
cc: cc?.map((emailAddress) => emailAddress.toEmailAddressHiveCache()).toList(),
|
cc: cc?.map((emailAddress) => emailAddress.toEmailAddressHiveCache()).toList(),
|
||||||
bcc: bcc?.map((emailAddress) => emailAddress.toEmailAddressHiveCache()).toList(),
|
bcc: bcc?.map((emailAddress) => emailAddress.toEmailAddressHiveCache()).toList(),
|
||||||
replyTo: replyTo?.map((emailAddress) => emailAddress.toEmailAddressHiveCache()).toList(),
|
replyTo: replyTo?.map((emailAddress) => emailAddress.toEmailAddressHiveCache()).toList(),
|
||||||
|
mailboxIds: mailboxIds?.toMapString(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/model/email_cache.dart';
|
||||||
|
|
||||||
|
extension ListEmailCacheExtension on List<EmailCache> {
|
||||||
|
Map<String, EmailCache> toMap() {
|
||||||
|
return Map<String, EmailCache>.fromIterable(
|
||||||
|
this,
|
||||||
|
key: (emailCache) => emailCache.id,
|
||||||
|
value: (emailCache) => emailCache);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||||
|
|
||||||
|
extension MapMailboxIdExtension on Map<MailboxId, bool> {
|
||||||
|
|
||||||
|
Map<String, bool> toMapString() => Map.fromIterables(keys.map((mailboxId) => mailboxId.id.value), values);
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
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:tmail_ui_user/features/caching/email_cache_client.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/extensions/email_cache_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/extensions/email_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/extensions/list_email_cache_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/model/email_cache.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
|
||||||
|
|
||||||
|
class EmailCacheManager {
|
||||||
|
|
||||||
|
final EmailCacheClient _emailCacheClient;
|
||||||
|
|
||||||
|
EmailCacheManager(this._emailCacheClient);
|
||||||
|
|
||||||
|
Future<List<Email>> getAllEmail({MailboxId? inMailboxId, Set<Comparator>? sort}) async {
|
||||||
|
final emailCacheList = inMailboxId != null
|
||||||
|
? await _emailCacheClient.getListEmailCacheByMailboxId(inMailboxId)
|
||||||
|
: await _emailCacheClient.getAll();
|
||||||
|
final emailList = emailCacheList.map((emailCache) => emailCache.toEmail()).toList();
|
||||||
|
if (sort != null) {
|
||||||
|
sort.forEach((comparator) {
|
||||||
|
emailList.sortBy(comparator);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return emailList;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> update({List<Email>? updated, List<Email>? created, List<EmailId>? destroyed}) async {
|
||||||
|
final emailCacheExist = await _emailCacheClient.isExistTable();
|
||||||
|
if (emailCacheExist) {
|
||||||
|
final updatedCacheEmails = updated
|
||||||
|
?.map((email) => email.toEmailCache()).toList() ?? <EmailCache>[];
|
||||||
|
final createdCacheEmails = created
|
||||||
|
?.map((email) => email.toEmailCache()).toList() ?? <EmailCache>[];
|
||||||
|
final destroyedCacheEmails = destroyed
|
||||||
|
?.map((emailId) => emailId.id.value).toList() ?? <String>[];
|
||||||
|
await Future.wait([
|
||||||
|
_emailCacheClient.updateMultipleItem(updatedCacheEmails.toMap()),
|
||||||
|
_emailCacheClient.insertMultipleItem(createdCacheEmails.toMap()),
|
||||||
|
_emailCacheClient.deleteMultipleItem(destroyedCacheEmails)
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
final createdCacheEmails = created
|
||||||
|
?.map((email) => email.toEmailCache()).toList() ?? <EmailCache>[];
|
||||||
|
await _emailCacheClient.insertMultipleItem(createdCacheEmails.toMap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -48,6 +48,9 @@ class EmailCache extends HiveObject with EquatableMixin {
|
|||||||
@HiveField(12)
|
@HiveField(12)
|
||||||
final List<EmailAddressHiveCache>? replyTo;
|
final List<EmailAddressHiveCache>? replyTo;
|
||||||
|
|
||||||
|
@HiveField(13)
|
||||||
|
Map<String, bool>? mailboxIds;
|
||||||
|
|
||||||
EmailCache(
|
EmailCache(
|
||||||
this.id,
|
this.id,
|
||||||
{
|
{
|
||||||
@@ -63,6 +66,7 @@ class EmailCache extends HiveObject with EquatableMixin {
|
|||||||
this.cc,
|
this.cc,
|
||||||
this.bcc,
|
this.bcc,
|
||||||
this.replyTo,
|
this.replyTo,
|
||||||
|
this.mailboxIds,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -80,6 +84,7 @@ class EmailCache extends HiveObject with EquatableMixin {
|
|||||||
sentAt,
|
sentAt,
|
||||||
replyTo,
|
replyTo,
|
||||||
preview,
|
preview,
|
||||||
hasAttachment
|
hasAttachment,
|
||||||
|
mailboxIds,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -30,13 +30,14 @@ class EmailCacheAdapter extends TypeAdapter<EmailCache> {
|
|||||||
cc: (fields[10] as List?)?.cast<EmailAddressHiveCache>(),
|
cc: (fields[10] as List?)?.cast<EmailAddressHiveCache>(),
|
||||||
bcc: (fields[11] as List?)?.cast<EmailAddressHiveCache>(),
|
bcc: (fields[11] as List?)?.cast<EmailAddressHiveCache>(),
|
||||||
replyTo: (fields[12] as List?)?.cast<EmailAddressHiveCache>(),
|
replyTo: (fields[12] as List?)?.cast<EmailAddressHiveCache>(),
|
||||||
|
mailboxIds: (fields[13] as Map?)?.cast<String, bool>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, EmailCache obj) {
|
void write(BinaryWriter writer, EmailCache obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(13)
|
..writeByte(14)
|
||||||
..writeByte(0)
|
..writeByte(0)
|
||||||
..write(obj.id)
|
..write(obj.id)
|
||||||
..writeByte(1)
|
..writeByte(1)
|
||||||
@@ -62,7 +63,9 @@ class EmailCacheAdapter extends TypeAdapter<EmailCache> {
|
|||||||
..writeByte(11)
|
..writeByte(11)
|
||||||
..write(obj.bcc)
|
..write(obj.bcc)
|
||||||
..writeByte(12)
|
..writeByte(12)
|
||||||
..write(obj.replyTo);
|
..write(obj.replyTo)
|
||||||
|
..writeByte(13)
|
||||||
|
..write(obj.mailboxIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
import 'package:model/model.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/state.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
|
|
||||||
|
class EmailChangeResponse with EquatableMixin {
|
||||||
|
final List<Email>? updated;
|
||||||
|
final List<Email>? created;
|
||||||
|
final List<EmailId>? destroyed;
|
||||||
|
final State? newStateEmail;
|
||||||
|
final State? newStateChanges;
|
||||||
|
final bool hasMoreChanges;
|
||||||
|
final Properties? updatedProperties;
|
||||||
|
|
||||||
|
EmailChangeResponse({
|
||||||
|
this.updated,
|
||||||
|
this.created,
|
||||||
|
this.destroyed,
|
||||||
|
this.newStateEmail,
|
||||||
|
this.newStateChanges,
|
||||||
|
this.hasMoreChanges = false,
|
||||||
|
this.updatedProperties,
|
||||||
|
});
|
||||||
|
|
||||||
|
EmailChangeResponse union(EmailChangeResponse other) => EmailChangeResponse(
|
||||||
|
updated: updated.union(other.updated),
|
||||||
|
created: created.union(other.created),
|
||||||
|
destroyed: destroyed.union(other.destroyed),
|
||||||
|
newStateEmail: other.newStateEmail,
|
||||||
|
newStateChanges: other.newStateChanges,
|
||||||
|
hasMoreChanges: other.hasMoreChanges,
|
||||||
|
updatedProperties: other.updatedProperties,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [
|
||||||
|
updated,
|
||||||
|
created,
|
||||||
|
destroyed,
|
||||||
|
newStateEmail,
|
||||||
|
newStateChanges,
|
||||||
|
hasMoreChanges,
|
||||||
|
updatedProperties
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -6,12 +6,17 @@ import 'package:jmap_dart_client/jmap/core/filter/filter.dart';
|
|||||||
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/core/request/reference_path.dart';
|
import 'package:jmap_dart_client/jmap/core/request/reference_path.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
|
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/state.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
||||||
import 'package:jmap_dart_client/jmap/jmap_request.dart';
|
import 'package:jmap_dart_client/jmap/jmap_request.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/changes/changes_email_method.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/changes/changes_email_response.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/get/get_email_method.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/get/get_email_method.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/get/get_email_response.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/get/get_email_response.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/query/query_email_method.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/query/query_email_method.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
|
||||||
|
|
||||||
class ThreadAPI {
|
class ThreadAPI {
|
||||||
|
|
||||||
@@ -19,7 +24,7 @@ class ThreadAPI {
|
|||||||
|
|
||||||
ThreadAPI(this.httpClient);
|
ThreadAPI(this.httpClient);
|
||||||
|
|
||||||
Future<List<Email>> getAllEmail(
|
Future<EmailResponse> getAllEmail(
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
{
|
{
|
||||||
int? position,
|
int? position,
|
||||||
@@ -69,6 +74,72 @@ class ThreadAPI {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultList == null ? [] : resultList.list;
|
return EmailResponse(emailList: resultList?.list, state: resultList?.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<EmailChangeResponse> getChanges(
|
||||||
|
AccountId accountId,
|
||||||
|
State sinceState,
|
||||||
|
{
|
||||||
|
Properties? propertiesCreated,
|
||||||
|
Properties? propertiesUpdated
|
||||||
|
}
|
||||||
|
) async {
|
||||||
|
final processingInvocation = ProcessingInvocation();
|
||||||
|
|
||||||
|
final jmapRequestBuilder = JmapRequestBuilder(httpClient, processingInvocation);
|
||||||
|
|
||||||
|
final changesEmailMethod = ChangesEmailMethod(accountId, sinceState, maxChanges: UnsignedInt(128));
|
||||||
|
|
||||||
|
final changesEmailInvocation = jmapRequestBuilder.invocation(changesEmailMethod);
|
||||||
|
|
||||||
|
final getMailboxUpdated = GetEmailMethod(accountId)
|
||||||
|
..addReferenceIds(processingInvocation.createResultReference(
|
||||||
|
changesEmailInvocation.methodCallId,
|
||||||
|
ReferencePath.updatedPath));
|
||||||
|
|
||||||
|
if (propertiesUpdated != null) {
|
||||||
|
getMailboxUpdated..addProperties(propertiesUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
final getEmailCreated = GetEmailMethod(accountId)
|
||||||
|
..addReferenceIds(processingInvocation.createResultReference(
|
||||||
|
changesEmailInvocation.methodCallId,
|
||||||
|
ReferencePath.createdPath));
|
||||||
|
|
||||||
|
if (propertiesCreated != null) {
|
||||||
|
getEmailCreated..addProperties(propertiesCreated);
|
||||||
|
}
|
||||||
|
|
||||||
|
final getEmailUpdatedInvocation = jmapRequestBuilder.invocation(getMailboxUpdated);
|
||||||
|
final getEmailCreatedInvocation = jmapRequestBuilder.invocation(getEmailCreated);
|
||||||
|
|
||||||
|
final result = await (jmapRequestBuilder
|
||||||
|
..usings(getEmailCreated.requiredCapabilities))
|
||||||
|
.build()
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
final resultChanges = result.parse<ChangesEmailResponse>(
|
||||||
|
changesEmailInvocation.methodCallId,
|
||||||
|
ChangesEmailResponse.deserialize);
|
||||||
|
|
||||||
|
final resultUpdated = result.parse<GetEmailResponse>(
|
||||||
|
getEmailUpdatedInvocation.methodCallId,
|
||||||
|
GetEmailResponse.deserialize);
|
||||||
|
|
||||||
|
final resultCreated = result.parse<GetEmailResponse>(
|
||||||
|
getEmailCreatedInvocation.methodCallId,
|
||||||
|
GetEmailResponse.deserialize);
|
||||||
|
|
||||||
|
final listMailboxIdDestroyed = resultChanges?.destroyed.map((id) => EmailId(id)).toList() ?? <EmailId>[];
|
||||||
|
|
||||||
|
return EmailChangeResponse(
|
||||||
|
updated: resultUpdated?.list,
|
||||||
|
created: resultCreated?.list,
|
||||||
|
destroyed: listMailboxIdDestroyed,
|
||||||
|
newStateEmail: resultUpdated?.state,
|
||||||
|
newStateChanges: resultChanges?.newState,
|
||||||
|
hasMoreChanges: resultChanges?.hasMoreChanges ?? false,
|
||||||
|
updatedProperties: propertiesUpdated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,37 +1,163 @@
|
|||||||
|
|
||||||
|
import 'package:core/core.dart';
|
||||||
|
import 'package:model/model.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/filter/filter.dart';
|
import 'package:jmap_dart_client/jmap/core/filter/filter.dart';
|
||||||
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/core/sort/comparator.dart';
|
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/state.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
import 'package:jmap_dart_client/jmap/core/unsigned_int.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:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart';
|
import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
|
||||||
|
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart';
|
import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox/data/extensions/state_extension.dart';
|
||||||
|
|
||||||
class ThreadRepositoryImpl extends ThreadRepository {
|
class ThreadRepositoryImpl extends ThreadRepository {
|
||||||
|
|
||||||
final ThreadDataSource threadDataSource;
|
final Map<DataSourceType, ThreadDataSource> mapDataSource;
|
||||||
|
final StateDataSource stateDataSource;
|
||||||
|
|
||||||
ThreadRepositoryImpl(this.threadDataSource);
|
ThreadRepositoryImpl(this.mapDataSource, this.stateDataSource);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Email>> getAllEmail(
|
Stream<EmailResponse> getAllEmail(
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
{
|
{
|
||||||
int? position,
|
int? position,
|
||||||
UnsignedInt? limit,
|
UnsignedInt? limit,
|
||||||
Set<Comparator>? sort,
|
Set<Comparator>? sort,
|
||||||
Filter? filter,
|
Filter? filter,
|
||||||
Properties? properties
|
Properties? propertiesCreated,
|
||||||
|
Properties? propertiesUpdated,
|
||||||
|
MailboxId? inMailboxId
|
||||||
}
|
}
|
||||||
) {
|
) async* {
|
||||||
return threadDataSource.getAllEmail(
|
final localEmailResponse = await Future.wait([
|
||||||
accountId,
|
mapDataSource[DataSourceType.local]!.getAllEmailCache(inMailboxId: inMailboxId, sort: sort),
|
||||||
position: position,
|
stateDataSource.getState(StateType.email)
|
||||||
limit: limit,
|
]).then((List response) {
|
||||||
sort: sort,
|
return EmailResponse(emailList: response.first, state: response.last);
|
||||||
filter: filter,
|
});
|
||||||
properties: properties
|
|
||||||
);
|
EmailResponse? networkEmailResponse;
|
||||||
|
|
||||||
|
if (!localEmailResponse.hasEmails()) {
|
||||||
|
networkEmailResponse = await mapDataSource[DataSourceType.network]!.getAllEmail(
|
||||||
|
accountId,
|
||||||
|
position: position,
|
||||||
|
limit: limit,
|
||||||
|
sort: sort,
|
||||||
|
filter: filter,
|
||||||
|
properties: propertiesCreated);
|
||||||
|
|
||||||
|
yield networkEmailResponse;
|
||||||
|
} else {
|
||||||
|
yield localEmailResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localEmailResponse.hasState()) {
|
||||||
|
EmailChangeResponse? emailChangeResponse;
|
||||||
|
bool hasMoreChanges = true;
|
||||||
|
State? sinceState = localEmailResponse.state!;
|
||||||
|
|
||||||
|
while(hasMoreChanges && sinceState != null) {
|
||||||
|
final changesResponse = await mapDataSource[DataSourceType.network]!.getChanges(
|
||||||
|
accountId,
|
||||||
|
sinceState,
|
||||||
|
propertiesCreated: propertiesCreated,
|
||||||
|
propertiesUpdated: propertiesUpdated);
|
||||||
|
|
||||||
|
hasMoreChanges = changesResponse.hasMoreChanges;
|
||||||
|
sinceState = changesResponse.newStateChanges;
|
||||||
|
|
||||||
|
if (emailChangeResponse != null) {
|
||||||
|
emailChangeResponse.union(changesResponse);
|
||||||
|
} else {
|
||||||
|
emailChangeResponse = changesResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final totalEmails = networkEmailResponse != null
|
||||||
|
? networkEmailResponse.emailList
|
||||||
|
: localEmailResponse.emailList;
|
||||||
|
|
||||||
|
final newEmailUpdated = await _combineEmailCache(
|
||||||
|
emailUpdated: emailChangeResponse?.updated,
|
||||||
|
updatedProperties: emailChangeResponse?.updatedProperties,
|
||||||
|
emailCacheList: totalEmails);
|
||||||
|
|
||||||
|
final newEmailCreated = networkEmailResponse != null
|
||||||
|
? networkEmailResponse.emailList
|
||||||
|
: emailChangeResponse?.created;
|
||||||
|
|
||||||
|
await _updateEmailCache(
|
||||||
|
newCreated: newEmailCreated,
|
||||||
|
newUpdated: newEmailUpdated,
|
||||||
|
newDestroyed: emailChangeResponse?.destroyed);
|
||||||
|
|
||||||
|
if (emailChangeResponse != null && emailChangeResponse.newStateEmail != null) {
|
||||||
|
await _updateState(emailChangeResponse.newStateEmail!);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (networkEmailResponse != null) {
|
||||||
|
await _updateEmailCache(newCreated: networkEmailResponse.emailList);
|
||||||
|
if (networkEmailResponse.state != null) {
|
||||||
|
await _updateState(networkEmailResponse.state!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final newEmailResponse = await Future.wait([
|
||||||
|
mapDataSource[DataSourceType.local]!.getAllEmailCache(inMailboxId: inMailboxId, sort: sort),
|
||||||
|
stateDataSource.getState(StateType.email)
|
||||||
|
]).then((List response) {
|
||||||
|
return EmailResponse(emailList: response.first, state: response.last);
|
||||||
|
});
|
||||||
|
|
||||||
|
yield newEmailResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Email>?> _combineEmailCache({
|
||||||
|
List<Email>? emailUpdated,
|
||||||
|
Properties? updatedProperties,
|
||||||
|
List<Email>? emailCacheList
|
||||||
|
}) async {
|
||||||
|
if (emailUpdated != null && emailUpdated.isNotEmpty) {
|
||||||
|
final newEmailUpdated = emailUpdated.map((email) {
|
||||||
|
if (updatedProperties == null) {
|
||||||
|
return email;
|
||||||
|
} else {
|
||||||
|
final emailOld = emailCacheList?.findEmail(email.id);
|
||||||
|
if (emailOld != null) {
|
||||||
|
return emailOld.combineEmail(email, updatedProperties);
|
||||||
|
} else {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
return newEmailUpdated;
|
||||||
|
}
|
||||||
|
return emailUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _updateEmailCache({
|
||||||
|
List<Email>? newUpdated,
|
||||||
|
List<Email>? newCreated,
|
||||||
|
List<EmailId>? newDestroyed
|
||||||
|
}) async {
|
||||||
|
await mapDataSource[DataSourceType.local]!.update(
|
||||||
|
updated: newUpdated,
|
||||||
|
created: newCreated,
|
||||||
|
destroyed: newDestroyed);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _updateState(State newState) async {
|
||||||
|
await stateDataSource.saveState(newState.toStateCache(StateType.email));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,11 @@
|
|||||||
import 'package:core/core.dart';
|
import 'package:core/core.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:tmail_ui_user/features/caching/caching_manager.dart';
|
import 'package:tmail_ui_user/features/caching/caching_manager.dart';
|
||||||
|
import 'package:tmail_ui_user/features/caching/email_cache_client.dart';
|
||||||
import 'package:tmail_ui_user/features/caching/mailbox_cache_client.dart';
|
import 'package:tmail_ui_user/features/caching/mailbox_cache_client.dart';
|
||||||
import 'package:tmail_ui_user/features/caching/state_cache_client.dart';
|
import 'package:tmail_ui_user/features/caching/state_cache_client.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/thread/data/local/email_cache_manager.dart';
|
||||||
|
|
||||||
class LocalBindings extends Bindings {
|
class LocalBindings extends Bindings {
|
||||||
|
|
||||||
@@ -25,5 +27,7 @@ class LocalBindings extends Bindings {
|
|||||||
Get.put(CachingManager(
|
Get.put(CachingManager(
|
||||||
Get.find<MailboxCacheClient>(),
|
Get.find<MailboxCacheClient>(),
|
||||||
Get.find<StateCacheClient>()));
|
Get.find<StateCacheClient>()));
|
||||||
|
Get.put(EmailCacheClient());
|
||||||
|
Get.put(EmailCacheManager(Get.find<EmailCacheClient>()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user