TF-1923 Store list new email by ascending

(cherry picked from commit e2ad80e20bca3d72bf9799e957b995a24c0844c8)
This commit is contained in:
dab246
2023-06-21 03:23:30 +07:00
committed by Dat H. Pham
parent ad5136b117
commit 125f1a6f13
15 changed files with 109 additions and 80 deletions
@@ -8,6 +8,7 @@ import 'package:dartz/dartz.dart';
import 'package:dio/dio.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/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/account/account_request.dart';
@@ -78,7 +79,7 @@ abstract class EmailDataSource {
Future<void> storeDetailedNewEmail(Session session, AccountId accountId, DetailedEmail detailedEmail);
Future<Email> getDetailedEmailById(Session session, AccountId accountId, EmailId emailId);
Future<List<Email>> getListDetailedEmailById(Session session, AccountId accountId, Set<EmailId> emailIds, {Set<Comparator>? sort});
Future<void> storeEmail(Session session, AccountId accountId, Email email);
@@ -8,6 +8,7 @@ import 'package:dartz/dartz.dart';
import 'package:dio/dio.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/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/model.dart';
@@ -158,9 +159,9 @@ class EmailDataSourceImpl extends EmailDataSource {
}
@override
Future<Email> getDetailedEmailById(Session session, AccountId accountId, EmailId emailId) {
Future<List<Email>> getListDetailedEmailById(Session session, AccountId accountId, Set<EmailId> emailIds, {Set<Comparator>? sort}) {
return Future.sync(() async {
return await emailAPI.getDetailedEmailById(session, accountId, emailId);
return await emailAPI.getListDetailedEmailById(session, accountId, emailIds, sort: sort);
}).catchError(_exceptionThrower.throwException);
}
@@ -13,6 +13,7 @@ import 'package:dio/dio.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:jmap_dart_client/jmap/core/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/account/account_request.dart';
import 'package:model/download/download_task_id.dart';
@@ -159,7 +160,7 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
}
@override
Future<Email> getDetailedEmailById(Session session, AccountId accountId, EmailId emailId) {
Future<List<Email>> getListDetailedEmailById(Session session, AccountId accountId, Set<EmailId> emailIds, {Set<Comparator>? sort}) {
throw UnimplementedError();
}
+15 -3
View File
@@ -13,6 +13,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart';
import 'package:jmap_dart_client/jmap/core/capability/core_capability.dart';
import 'package:jmap_dart_client/jmap/core/error/set_error.dart';
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/method/response/set_response.dart';
import 'package:jmap_dart_client/jmap/core/patch_object.dart';
@@ -628,11 +629,16 @@ class EmailAPI with HandleSetErrorMixin {
return setEmailResponse?.destroyed?.contains(emailId.id) == true;
}
Future<Email> getDetailedEmailById(Session session, AccountId accountId, EmailId emailId) async {
Future<List<Email>> getListDetailedEmailById(
Session session,
AccountId accountId,
Set<EmailId> emailIds,
{Set<Comparator>? sort}
) async {
final jmapRequestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
final getEmailMethod = GetEmailMethod(accountId)
..addIds({emailId.id})
..addIds(emailIds.map((emailId) => emailId.id).toSet())
..addProperties(ThreadConstants.propertiesGetDetailedEmail)
..addFetchHTMLBodyValues(true);
@@ -649,8 +655,14 @@ class EmailAPI with HandleSetErrorMixin {
getEmailInvocation.methodCallId,
GetEmailResponse.deserialize);
if (sort != null && resultList != null) {
for (var comparator in sort) {
resultList.sortEmails(comparator);
}
}
if (resultList?.list.isNotEmpty == true) {
return resultList!.list.first;
return resultList!.list;
} else {
throw NotFoundEmailException();
}
@@ -10,6 +10,7 @@ import 'package:dartz/dartz.dart';
import 'package:dio/dio.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/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/core/state.dart' as jmap;
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/account/account_request.dart';
@@ -190,8 +191,8 @@ class EmailRepositoryImpl extends EmailRepository {
}
@override
Future<Email> getDetailedEmailById(Session session, AccountId accountId, EmailId emailId) {
return emailDataSource[DataSourceType.network]!.getDetailedEmailById(session, accountId, emailId);
Future<List<Email>> getListDetailedEmailById(Session session, AccountId accountId, Set<EmailId> emailIds, {Set<Comparator>? sort}) {
return emailDataSource[DataSourceType.network]!.getListDetailedEmailById(session, accountId, emailIds, sort: sort);
}
@override
@@ -5,6 +5,4 @@ class NotFoundStoredNewEmailException implements Exception {}
class NotFoundStoredEmailException implements Exception {}
class OpenedEmailAlreadyStoredException implements Exception {}
class NewEmailAlreadyStoredException implements Exception {}
class OpenedEmailAlreadyStoredException implements Exception {}
@@ -7,7 +7,7 @@ extension EmailExtension on Email {
DetailedEmail toDetailedEmail({String? htmlEmailContent}) {
return DetailedEmail(
emailId: id!,
createdTime: DateTime.now(),
createdTime: receivedAt?.value ?? DateTime.now(),
attachments: allAttachments,
headers: headers,
keywords: keywords,
@@ -8,6 +8,7 @@ import 'package:dartz/dartz.dart';
import 'package:dio/dio.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/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/core/state.dart' as jmap;
import 'package:model/account/account_request.dart';
@@ -90,7 +91,7 @@ abstract class EmailRepository {
Future<void> storeDetailedNewEmail(Session session, AccountId accountId, DetailedEmail detailedEmail);
Future<Email> getDetailedEmailById(Session session, AccountId accountId, EmailId emailId);
Future<List<Email>> getListDetailedEmailById(Session session, AccountId accountId, Set<EmailId> emailIds, {Set<Comparator>? sort});
Future<void> storeEmail(Session session, AccountId accountId, Email email);
@@ -9,15 +9,14 @@ class GetDetailedEmailByIdLoading extends UIState {}
class GetDetailedEmailByIdSuccess extends UIState {
final Email email;
final DetailedEmail detailedEmail;
final Map<Email, DetailedEmail> mapDetailedEmail;
final AccountId accountId;
final Session session;
GetDetailedEmailByIdSuccess(this.email, this.detailedEmail, this.accountId, this.session);
GetDetailedEmailByIdSuccess(this.mapDetailedEmail, this.accountId, this.session);
@override
List<Object?> get props => [email, detailedEmail, accountId, session];
List<Object?> get props => [mapDetailedEmail, accountId, session];
}
class GetDetailedEmailByIdFailure extends FeatureFailure {
@@ -2,7 +2,9 @@ import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/extensions/utc_date_extension.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/extensions/email_extension.dart';
import 'package:model/extensions/list_attachment_extension.dart';
@@ -12,27 +14,38 @@ import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart';
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_detailed_email_by_id_state.dart';
class GetDetailedEmailByIdInteractor {
class GetListDetailedEmailByIdInteractor {
final EmailRepository _emailRepository;
GetDetailedEmailByIdInteractor(this._emailRepository);
GetListDetailedEmailByIdInteractor(this._emailRepository);
Stream<Either<Failure, Success>> execute(
Session session,
AccountId accountId,
EmailId emailId,
String? baseDownloadUrl
Set<EmailId> emailIds,
String? baseDownloadUrl,
{Set<Comparator>? sort}
) async* {
try {
yield Right<Failure, Success>(GetDetailedEmailByIdLoading());
final email = await _emailRepository.getDetailedEmailById(session, accountId, emailId);
final listEmails = await _emailRepository.getListDetailedEmailById(session, accountId, emailIds, sort: sort);
final parsedEmail = await _parsingEmailToDetailedEmail(accountId, email, baseDownloadUrl);
final listTuple2Email = await Future.wait(
listEmails.map((email) => _parsingEmailToDetailedEmail(accountId, email, baseDownloadUrl)),
eagerError: true);
listTuple2Email.sort((detailedEmail1, detailedEmail2) {
return detailedEmail1.value1.receivedAt.compareToSort(detailedEmail1.value1.receivedAt, true);
});
final mapDetailedEmails = {
for (var tuple2 in listTuple2Email)
tuple2.value1 : tuple2.value2
};
yield Right<Failure, Success>(GetDetailedEmailByIdSuccess(
parsedEmail.value1,
parsedEmail.value2,
mapDetailedEmails,
accountId,
session,
));
@@ -5,43 +5,38 @@ import 'package:dartz/dartz.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/mail/email/email.dart';
import 'package:tmail_ui_user/features/email/domain/exceptions/email_cache_exceptions.dart';
import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart';
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
import 'package:tmail_ui_user/features/email/domain/state/store_new_email_state.dart';
class StoreNewEmailInteractor {
class StoreListNewEmailInteractor {
final EmailRepository _emailRepository;
StoreNewEmailInteractor(this._emailRepository);
StoreListNewEmailInteractor(this._emailRepository);
Stream<Either<Failure, Success>> execute(
Session session,
AccountId accountId,
Email email,
DetailedEmail detailedEmail
Map<Email, DetailedEmail> mapDetailedEmails
) async* {
try {
yield Right<Failure, Success>(StoreNewEmailLoading());
final isNewEmailExist = await _isNewEmailAlreadyStored(session, accountId, detailedEmail);
log('StoreNewEmailInteractor::execute():isNewEmailExist: $isNewEmailExist');
if (!isNewEmailExist) {
await Future.wait([
_emailRepository.storeEmail(session, accountId, email),
_emailRepository.storeDetailedNewEmail(session, accountId, detailedEmail),
], eagerError: true);
log('StoreNewEmailInteractor::execute():Store Success | EMAIL: ${detailedEmail.emailId} | TIME: ${detailedEmail.createdTime}');
yield Right<Failure, Success>(StoreNewEmailSuccess());
} else {
log('StoreNewEmailInteractor::execute():Store Failure | EMAIL: ${detailedEmail.emailId} | TIME: ${detailedEmail.createdTime}');
yield Left<Failure, Success>(StoreNewEmailFailure(NewEmailAlreadyStoredException()));
for (var email in mapDetailedEmails.keys) {
await _storeNewEmail(session, accountId, email, mapDetailedEmails[email]!);
}
yield Right<Failure, Success>(StoreNewEmailSuccess());
} catch (e) {
yield Left<Failure, Success>(StoreNewEmailFailure(e));
}
}
Future<bool> _isNewEmailAlreadyStored(Session session, AccountId accountId, DetailedEmail detailedEmail) async {
Future<bool> _isNewEmailAlreadyStored(
Session session,
AccountId accountId,
DetailedEmail detailedEmail
) async {
try {
await _emailRepository.getStoredNewEmail(session, accountId, detailedEmail.emailId);
return true;
@@ -50,4 +45,20 @@ class StoreNewEmailInteractor {
return false;
}
}
Future<void> _storeNewEmail(
Session session,
AccountId accountId,
Email email,
DetailedEmail detailedEmail
) async {
final isNewEmailExist = await _isNewEmailAlreadyStored(session, accountId, detailedEmail);
log('StoreNewEmailInteractor::execute():isNewEmailExist: $isNewEmailExist');
if (!isNewEmailExist) {
await Future.wait([
_emailRepository.storeEmail(session, accountId, email),
_emailRepository.storeDetailedNewEmail(session, accountId, detailedEmail),
], eagerError: true);
}
}
}
@@ -406,7 +406,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
if (PlatformInfo.isMobile) {
final detailedEmail = DetailedEmail(
emailId: currentEmail!.id!,
createdTime: DateTime.now(),
createdTime: currentEmail?.receivedAt?.value ?? DateTime.now(),
attachments: attachments,
headers: currentEmail?.emailHeader?.toSet(),
keywords: currentEmail?.keywords,
@@ -13,8 +13,8 @@ import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart';
import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
import 'package:tmail_ui_user/features/email/data/repository/email_repository_impl.dart';
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_detailed_email_by_id_interator.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/store_new_email_interator.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_list_detailed_email_by_id_interator.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/store_list_new_email_interator.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_impl/mailbox_cache_datasource_impl.dart';
@@ -136,8 +136,8 @@ class FcmInteractorBindings extends InteractorsBindings {
Get.lazyPut(() => GetNewReceiveEmailFromNotificationInteractor(
Get.find<FCMRepositoryImpl>(),
Get.find<EmailRepository>()));
Get.lazyPut(() => GetDetailedEmailByIdInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => StoreNewEmailInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => GetListDetailedEmailByIdInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => StoreListNewEmailInteractor(Get.find<EmailRepository>()));
}
@override
@@ -9,9 +9,12 @@ import 'package:core/utils/platform_info.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
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/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/core/state.dart' as jmap;
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_comparator.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_comparator_property.dart';
import 'package:model/email/email_property.dart';
import 'package:model/email/presentation_email.dart';
import 'package:model/extensions/list_presentation_email_extension.dart';
@@ -22,9 +25,9 @@ import 'package:tmail_ui_user/features/base/action/ui_action.dart';
import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_detailed_email_by_id_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_stored_state_email_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_detailed_email_by_id_interator.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_list_detailed_email_by_id_interator.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_stored_email_state_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/store_new_email_interator.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/store_list_new_email_interator.dart';
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/push_notification/domain/exceptions/fcm_exception.dart';
@@ -60,9 +63,9 @@ class EmailChangeListener extends ChangeListener {
GetMailboxesNotPutNotificationsInteractor? _getMailboxesNotPutNotificationsInteractor;
GetEmailChangesToRemoveNotificationInteractor? _getEmailChangesToRemoveNotificationInteractor;
GetNewReceiveEmailFromNotificationInteractor? _getNewReceiveEmailFromNotificationInteractor;
GetDetailedEmailByIdInteractor? _getDetailedEmailByIdInteractor;
GetListDetailedEmailByIdInteractor? _getListDetailedEmailByIdInteractor;
DynamicUrlInterceptors? _dynamicUrlInterceptors;
StoreNewEmailInteractor? _storeNewEmailInteractor;
StoreListNewEmailInteractor? _storeListNewEmailInteractor;
jmap.State? _newStateEmailDelivery;
AccountId? _accountId;
@@ -81,9 +84,9 @@ class EmailChangeListener extends ChangeListener {
_getMailboxesNotPutNotificationsInteractor = getBinding<GetMailboxesNotPutNotificationsInteractor>();
_getEmailChangesToRemoveNotificationInteractor = getBinding<GetEmailChangesToRemoveNotificationInteractor>();
_getNewReceiveEmailFromNotificationInteractor = getBinding<GetNewReceiveEmailFromNotificationInteractor>();
_getDetailedEmailByIdInteractor = getBinding<GetDetailedEmailByIdInteractor>();
_getListDetailedEmailByIdInteractor = getBinding<GetListDetailedEmailByIdInteractor>();
_dynamicUrlInterceptors = getBinding<DynamicUrlInterceptors>();
_storeNewEmailInteractor = getBinding<StoreNewEmailInteractor>();
_storeListNewEmailInteractor = getBinding<StoreListNewEmailInteractor>();
} catch (e) {
logError('EmailChangeListener::_internal(): IS NOT REGISTERED: ${e.toString()}');
}
@@ -252,8 +255,7 @@ class EmailChangeListener extends ChangeListener {
_storeNewEmailAction(
success.session,
success.accountId,
success.email,
success.detailedEmail);
success.mapDetailedEmail);
}
}
@@ -322,21 +324,18 @@ class EmailChangeListener extends ChangeListener {
void _getListDetailedEmailByIdAction(Session? session, AccountId accountId, Set<EmailId> emailIds) {
log('EmailChangeListener::_getListDetailedEmailByIdAction():emailIds: $emailIds');
for (var emailId in emailIds) {
_getDetailedEmailByIdAction(session, accountId, emailId);
}
}
void _getDetailedEmailByIdAction(Session? session, AccountId accountId, EmailId emailId) {
if (_getDetailedEmailByIdInteractor != null &&
if (_getListDetailedEmailByIdInteractor != null &&
_dynamicUrlInterceptors != null &&
session != null) {
final baseDownloadUrl = session.getDownloadUrl(jmapUrl: _dynamicUrlInterceptors!.jmapUrl);
consumeState(_getDetailedEmailByIdInteractor!.execute(
consumeState(_getListDetailedEmailByIdInteractor!.execute(
session,
accountId,
emailId,
baseDownloadUrl
emailIds,
baseDownloadUrl,
sort: <Comparator>{}
..add(EmailComparator(EmailComparatorProperty.receivedAt)
..setIsAscending(true))
));
}
}
@@ -344,16 +343,14 @@ class EmailChangeListener extends ChangeListener {
void _storeNewEmailAction(
Session session,
AccountId accountId,
Email email,
DetailedEmail detailedEmail
Map<Email, DetailedEmail> mapDetailedEmails
) {
log('EmailChangeListener::_handleGetDetailedEmailByIdActionSuccess():emailId: ${email.id}');
if (_storeNewEmailInteractor != null) {
consumeState(_storeNewEmailInteractor!.execute(
log('EmailChangeListener::_storeNewEmailAction():mapDetailedEmails: ${mapDetailedEmails.length}');
if (_storeListNewEmailInteractor != null) {
consumeState(_storeListNewEmailInteractor!.execute(
session,
accountId,
email,
detailedEmail
mapDetailedEmails
));
}
}
-6
View File
@@ -1,8 +1,6 @@
import 'package:core/domain/extensions/datetime_extension.dart';
import 'package:core/presentation/extensions/string_extension.dart';
import 'package:equatable/equatable.dart';
import 'package:intl/intl.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';
@@ -111,10 +109,6 @@ class PresentationEmail with EquatableMixin {
bool get pushNotificationActivated => !isDraft && !hasRead;
String getCreateTimeAt(String newLocale) {
return DateFormat(sentAt?.value.toPattern(), newLocale).format(sentAt?.value ?? DateTime.now());
}
List<EmailContent> get emailContentList {
final newHtmlBody = htmlBody
?.where((emailBody) => emailBody.partId != null && emailBody.type != null)