TF-4236 Apply BatchSetEmailProcessingMixin for some method use SetEmailMethod
This commit is contained in:
@@ -212,7 +212,10 @@ abstract class EmailDataSource {
|
|||||||
KeyWordIdentifier labelKeyword,
|
KeyWordIdentifier labelKeyword,
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<void> addLabelToThread(
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> addLabelToThread(
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
List<EmailId> emailIds,
|
List<EmailId> emailIds,
|
||||||
@@ -226,7 +229,10 @@ abstract class EmailDataSource {
|
|||||||
KeyWordIdentifier labelKeyword,
|
KeyWordIdentifier labelKeyword,
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<void> removeLabelFromThread(
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> removeLabelFromThread(
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
List<EmailId> emailIds,
|
List<EmailId> emailIds,
|
||||||
|
|||||||
@@ -572,7 +572,10 @@ class EmailDataSourceImpl extends EmailDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> addLabelToThread(
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> addLabelToThread(
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
List<EmailId> emailIds,
|
List<EmailId> emailIds,
|
||||||
@@ -606,7 +609,10 @@ class EmailDataSourceImpl extends EmailDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> removeLabelFromThread(
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> removeLabelFromThread(
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
List<EmailId> emailIds,
|
List<EmailId> emailIds,
|
||||||
|
|||||||
@@ -582,7 +582,10 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> addLabelToThread(Session session, AccountId accountId, List<EmailId> emailIds, KeyWordIdentifier labelKeyword) {
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> addLabelToThread(Session session, AccountId accountId, List<EmailId> emailIds, KeyWordIdentifier labelKeyword) {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -592,7 +595,10 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> removeLabelFromThread(Session session, AccountId accountId, List<EmailId> emailIds, KeyWordIdentifier labelKeyword) {
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> removeLabelFromThread(Session session, AccountId accountId, List<EmailId> emailIds, KeyWordIdentifier labelKeyword) {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -358,7 +358,10 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> addLabelToThread(Session session, AccountId accountId, List<EmailId> emailIds, KeyWordIdentifier labelKeyword) {
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> addLabelToThread(Session session, AccountId accountId, List<EmailId> emailIds, KeyWordIdentifier labelKeyword) {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,7 +371,10 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> removeLabelFromThread(Session session, AccountId accountId, List<EmailId> emailIds, KeyWordIdentifier labelKeyword) {
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> removeLabelFromThread(Session session, AccountId accountId, List<EmailId> emailIds, KeyWordIdentifier labelKeyword) {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,7 +272,10 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> addLabelToThread(Session session, AccountId accountId, List<EmailId> emailIds, KeyWordIdentifier labelKeyword) {
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> addLabelToThread(Session session, AccountId accountId, List<EmailId> emailIds, KeyWordIdentifier labelKeyword) {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,7 +285,10 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> removeLabelFromThread(Session session, AccountId accountId, List<EmailId> emailIds, KeyWordIdentifier labelKeyword) {
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> removeLabelFromThread(Session session, AccountId accountId, List<EmailId> emailIds, KeyWordIdentifier labelKeyword) {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,23 +53,27 @@ import 'package:model/extensions/list_email_id_extension.dart';
|
|||||||
import 'package:model/extensions/list_id_extension.dart';
|
import 'package:model/extensions/list_id_extension.dart';
|
||||||
import 'package:model/extensions/mailbox_id_extension.dart';
|
import 'package:model/extensions/mailbox_id_extension.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/mixin/batch_set_email_processing_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/base/mixin/handle_error_mixin.dart';
|
import 'package:tmail_ui_user/features/base/mixin/handle_error_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/base/mixin/mail_api_mixin.dart';
|
import 'package:tmail_ui_user/features/base/mixin/mail_api_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/domain/exceptions/set_method_exception.dart';
|
import 'package:tmail_ui_user/features/composer/domain/exceptions/set_method_exception.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';
|
||||||
import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
|
import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
|
||||||
|
import 'package:tmail_ui_user/features/download/domain/state/download_all_attachments_for_web_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/download/domain/state/download_attachment_for_web_state.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
|
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart';
|
import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart';
|
import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart';
|
||||||
import 'package:tmail_ui_user/features/download/domain/state/download_all_attachments_for_web_state.dart';
|
|
||||||
import 'package:tmail_ui_user/features/download/domain/state/download_attachment_for_web_state.dart';
|
|
||||||
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
|
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
|
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
|
||||||
import 'package:tmail_ui_user/main/error/capability_validator.dart';
|
import 'package:tmail_ui_user/main/error/capability_validator.dart';
|
||||||
import 'package:uri/uri.dart';
|
import 'package:uri/uri.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class EmailAPI with HandleSetErrorMixin, MailAPIMixin {
|
class EmailAPI with
|
||||||
|
HandleSetErrorMixin,
|
||||||
|
MailAPIMixin,
|
||||||
|
BatchSetEmailProcessingMixin {
|
||||||
|
|
||||||
final HttpClient _httpClient;
|
final HttpClient _httpClient;
|
||||||
final DownloadManager _downloadManager;
|
final DownloadManager _downloadManager;
|
||||||
@@ -246,51 +250,15 @@ class EmailAPI with HandleSetErrorMixin, MailAPIMixin {
|
|||||||
List<EmailId> emailIds,
|
List<EmailId> emailIds,
|
||||||
ReadActions readActions,
|
ReadActions readActions,
|
||||||
) async {
|
) async {
|
||||||
final maxObjects = getMaxObjectsInSetMethod(session, accountId);
|
return executeBatchSetEmail(
|
||||||
final totalEmails = emailIds.length;
|
session: session,
|
||||||
final maxBatches = min(totalEmails, maxObjects);
|
accountId: accountId,
|
||||||
|
emailIds: emailIds,
|
||||||
final List<EmailId> updatedEmailIds = List.empty(growable: true);
|
httpClient: _httpClient,
|
||||||
final Map<Id, SetError> mapErrors = <Id, SetError>{};
|
debugLabel: 'markAsRead',
|
||||||
|
onGenerateUpdates: (batchIds) =>
|
||||||
for (int start = 0; start < totalEmails; start += maxBatches) {
|
batchIds.generateMapUpdateObjectMarkAsRead(readActions),
|
||||||
int end = (start + maxBatches < totalEmails)
|
);
|
||||||
? start + maxBatches
|
|
||||||
: totalEmails;
|
|
||||||
log('EmailAPI::markAsRead:emails from ${start + 1} to $end');
|
|
||||||
|
|
||||||
final currentListEmailIds = emailIds.sublist(start, end);
|
|
||||||
|
|
||||||
final setEmailMethod = SetEmailMethod(accountId)
|
|
||||||
..addUpdates(
|
|
||||||
currentListEmailIds.generateMapUpdateObjectMarkAsRead(readActions)
|
|
||||||
);
|
|
||||||
|
|
||||||
final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
|
|
||||||
|
|
||||||
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
|
|
||||||
|
|
||||||
final capabilities = setEmailMethod.requiredCapabilities
|
|
||||||
.toCapabilitiesSupportTeamMailboxes(session, accountId);
|
|
||||||
|
|
||||||
final response = await (requestBuilder
|
|
||||||
..usings(capabilities))
|
|
||||||
.build()
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
final setEmailResponse = response.parse<SetEmailResponse>(
|
|
||||||
setEmailInvocation.methodCallId,
|
|
||||||
SetEmailResponse.deserialize,
|
|
||||||
);
|
|
||||||
|
|
||||||
final listEmailIds = setEmailResponse?.updated?.keys.toEmailIds() ?? [];
|
|
||||||
final mapErrors = handleSetResponse([setEmailResponse]);
|
|
||||||
|
|
||||||
updatedEmailIds.addAll(listEmailIds);
|
|
||||||
mapErrors.addAll(mapErrors);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (emailIdsSuccess: updatedEmailIds, mapErrors: mapErrors);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<DownloadedResponse> exportAttachment(
|
Future<DownloadedResponse> exportAttachment(
|
||||||
@@ -486,51 +454,15 @@ class EmailAPI with HandleSetErrorMixin, MailAPIMixin {
|
|||||||
List<EmailId> emailIds,
|
List<EmailId> emailIds,
|
||||||
MarkStarAction markStarAction
|
MarkStarAction markStarAction
|
||||||
) async {
|
) async {
|
||||||
final maxObjects = getMaxObjectsInSetMethod(session, accountId);
|
return executeBatchSetEmail(
|
||||||
final totalEmails = emailIds.length;
|
session: session,
|
||||||
final maxBatches = min(totalEmails, maxObjects);
|
accountId: accountId,
|
||||||
|
emailIds: emailIds,
|
||||||
final List<EmailId> updatedEmailIds = List.empty(growable: true);
|
httpClient: _httpClient,
|
||||||
final Map<Id, SetError> mapErrors = <Id, SetError>{};
|
debugLabel: 'markAsStar',
|
||||||
|
onGenerateUpdates: (batchIds) =>
|
||||||
for (int start = 0; start < totalEmails; start += maxBatches) {
|
batchIds.generateMapUpdateObjectMarkAsStar(markStarAction),
|
||||||
int end = (start + maxBatches < totalEmails)
|
);
|
||||||
? start + maxBatches
|
|
||||||
: totalEmails;
|
|
||||||
log('EmailAPI::markAsStar:emails from ${start + 1} to $end');
|
|
||||||
|
|
||||||
final currentListEmailIds = emailIds.sublist(start, end);
|
|
||||||
|
|
||||||
final setEmailMethod = SetEmailMethod(accountId)
|
|
||||||
..addUpdates(
|
|
||||||
currentListEmailIds.generateMapUpdateObjectMarkAsStar(markStarAction),
|
|
||||||
);
|
|
||||||
|
|
||||||
final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
|
|
||||||
|
|
||||||
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
|
|
||||||
|
|
||||||
final capabilities = setEmailMethod.requiredCapabilities
|
|
||||||
.toCapabilitiesSupportTeamMailboxes(session, accountId);
|
|
||||||
|
|
||||||
final response = await (requestBuilder
|
|
||||||
..usings(capabilities))
|
|
||||||
.build()
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
final setEmailResponse = response.parse<SetEmailResponse>(
|
|
||||||
setEmailInvocation.methodCallId,
|
|
||||||
SetEmailResponse.deserialize,
|
|
||||||
);
|
|
||||||
|
|
||||||
final listEmailIds = setEmailResponse?.updated?.keys.toEmailIds() ?? [];
|
|
||||||
final mapErrors = handleSetResponse([setEmailResponse]);
|
|
||||||
|
|
||||||
updatedEmailIds.addAll(listEmailIds);
|
|
||||||
mapErrors.addAll(mapErrors);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (emailIdsSuccess: updatedEmailIds, mapErrors: mapErrors);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Email> _emailSetCreateMethod(
|
Future<Email> _emailSetCreateMethod(
|
||||||
@@ -965,39 +897,24 @@ class EmailAPI with HandleSetErrorMixin, MailAPIMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addLabelToThread(
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> addLabelToThread(
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
List<EmailId> emailIds,
|
List<EmailId> emailIds,
|
||||||
KeyWordIdentifier labelKeyword,
|
KeyWordIdentifier labelKeyword,
|
||||||
) async {
|
) async {
|
||||||
if (emailIds.isEmpty) {
|
return executeBatchSetEmail(
|
||||||
throw ArgumentError.value(emailIds, 'emailIds', 'must not be empty');
|
session: session,
|
||||||
}
|
accountId: accountId,
|
||||||
|
emailIds: emailIds,
|
||||||
final method = SetEmailMethod(accountId)
|
httpClient: _httpClient,
|
||||||
..addUpdates(emailIds.generateMapUpdateObjectLabel(labelKeyword));
|
debugLabel: 'addLabelToThread',
|
||||||
|
onGenerateUpdates: (batchIds) =>
|
||||||
final builder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
|
batchIds.generateMapUpdateObjectLabel(labelKeyword),
|
||||||
final invocation = builder.invocation(method);
|
|
||||||
|
|
||||||
final capabilities = method.requiredCapabilities
|
|
||||||
.toCapabilitiesSupportTeamMailboxes(session, accountId);
|
|
||||||
final result = await (builder..usings(capabilities)).build().execute();
|
|
||||||
|
|
||||||
final response = result.parse<SetEmailResponse>(
|
|
||||||
invocation.methodCallId,
|
|
||||||
SetEmailResponse.deserialize,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final emailIdsUpdated = response?.updated?.keys ?? <Id>[];
|
|
||||||
final ids = emailIds.toSetIds();
|
|
||||||
final isUpdated = ids.every(emailIdsUpdated.contains) &&
|
|
||||||
emailIdsUpdated.length == ids.length;
|
|
||||||
|
|
||||||
if (emailIdsUpdated.isEmpty || !isUpdated) {
|
|
||||||
throw parseErrorForSetResponse(response, emailIds.first.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeLabelFromEmail(
|
Future<void> removeLabelFromEmail(
|
||||||
@@ -1030,41 +947,25 @@ class EmailAPI with HandleSetErrorMixin, MailAPIMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeLabelFromThread(
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> removeLabelFromThread(
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
List<EmailId> emailIds,
|
List<EmailId> emailIds,
|
||||||
KeyWordIdentifier labelKeyword,
|
KeyWordIdentifier labelKeyword,
|
||||||
) async {
|
) async {
|
||||||
if (emailIds.isEmpty) {
|
return executeBatchSetEmail(
|
||||||
throw ArgumentError.value(emailIds, 'emailIds', 'must not be empty');
|
session: session,
|
||||||
}
|
accountId: accountId,
|
||||||
|
emailIds: emailIds,
|
||||||
final method = SetEmailMethod(accountId)
|
httpClient: _httpClient,
|
||||||
..addUpdates(emailIds.generateMapUpdateObjectLabel(
|
debugLabel: 'removeLabelFromThread',
|
||||||
|
onGenerateUpdates: (batchIds) => batchIds.generateMapUpdateObjectLabel(
|
||||||
labelKeyword,
|
labelKeyword,
|
||||||
remove: true,
|
remove: true,
|
||||||
));
|
),
|
||||||
|
|
||||||
final builder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
|
|
||||||
final invocation = builder.invocation(method);
|
|
||||||
|
|
||||||
final capabilities = method.requiredCapabilities
|
|
||||||
.toCapabilitiesSupportTeamMailboxes(session, accountId);
|
|
||||||
final result = await (builder..usings(capabilities)).build().execute();
|
|
||||||
|
|
||||||
final response = result.parse<SetEmailResponse>(
|
|
||||||
invocation.methodCallId,
|
|
||||||
SetEmailResponse.deserialize,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final emailIdsUpdated = response?.updated?.keys ?? <Id>[];
|
|
||||||
final ids = emailIds.toSetIds();
|
|
||||||
final isUpdated = emailIdsUpdated.every(ids.contains) &&
|
|
||||||
emailIdsUpdated.length == ids.length;
|
|
||||||
|
|
||||||
if (emailIdsUpdated.isEmpty || !isUpdated) {
|
|
||||||
throw parseErrorForSetResponse(response, emailIds.first.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -497,7 +497,10 @@ class EmailRepositoryImpl extends EmailRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> addLabelToThread(
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> addLabelToThread(
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
List<EmailId> emailIds,
|
List<EmailId> emailIds,
|
||||||
@@ -527,7 +530,10 @@ class EmailRepositoryImpl extends EmailRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> removeLabelFromThread(
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> removeLabelFromThread(
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
List<EmailId> emailIds,
|
List<EmailId> emailIds,
|
||||||
|
|||||||
@@ -23,3 +23,5 @@ class NotParsableBlobIdToEmailException implements Exception {
|
|||||||
|
|
||||||
NotParsableBlobIdToEmailException({this.ids});
|
NotParsableBlobIdToEmailException({this.ids});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EmailIdListIsEmptyException implements Exception {}
|
||||||
@@ -169,7 +169,10 @@ abstract class EmailRepository {
|
|||||||
KeyWordIdentifier labelKeyword,
|
KeyWordIdentifier labelKeyword,
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<void> addLabelToThread(
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> addLabelToThread(
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
List<EmailId> emailIds,
|
List<EmailId> emailIds,
|
||||||
@@ -183,7 +186,10 @@ abstract class EmailRepository {
|
|||||||
KeyWordIdentifier labelKeyword,
|
KeyWordIdentifier labelKeyword,
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<void> removeLabelFromThread(
|
Future<({
|
||||||
|
List<EmailId> emailIdsSuccess,
|
||||||
|
Map<Id, SetError> mapErrors,
|
||||||
|
})> removeLabelFromThread(
|
||||||
Session session,
|
Session session,
|
||||||
AccountId accountId,
|
AccountId accountId,
|
||||||
List<EmailId> emailIds,
|
List<EmailId> emailIds,
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ class AddALabelToAThreadSuccess extends UIState {
|
|||||||
List<Object> get props => [emailIds, labelKeyword, labelDisplay];
|
List<Object> get props => [emailIds, labelKeyword, labelDisplay];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AddALabelToAThreadHasSomeFailure extends AddALabelToAThreadSuccess {
|
||||||
|
AddALabelToAThreadHasSomeFailure(
|
||||||
|
super.emailIds,
|
||||||
|
super.labelKeyword,
|
||||||
|
super.labelDisplay,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
class AddALabelToAThreadFailure extends FeatureFailure {
|
class AddALabelToAThreadFailure extends FeatureFailure {
|
||||||
final String labelDisplay;
|
final String labelDisplay;
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,15 @@ class RemoveALabelFromAThreadSuccess extends UIState {
|
|||||||
List<Object> get props => [emailIds, labelKeyword, labelDisplay];
|
List<Object> get props => [emailIds, labelKeyword, labelDisplay];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RemoveALabelFromAThreadHasSomeFailure
|
||||||
|
extends RemoveALabelFromAThreadSuccess {
|
||||||
|
RemoveALabelFromAThreadHasSomeFailure(
|
||||||
|
super.emailIds,
|
||||||
|
super.labelKeyword,
|
||||||
|
super.labelDisplay,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
class RemoveALabelFromAThreadFailure extends FeatureFailure {
|
class RemoveALabelFromAThreadFailure extends FeatureFailure {
|
||||||
final String labelDisplay;
|
final String labelDisplay;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ 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/session/session.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:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/state/add_a_label_to_a_thread_state.dart';
|
import 'package:tmail_ui_user/features/email/domain/state/add_a_label_to_a_thread_state.dart';
|
||||||
|
|
||||||
@@ -22,17 +23,30 @@ class AddALabelToAThreadInteractor {
|
|||||||
) async* {
|
) async* {
|
||||||
try {
|
try {
|
||||||
yield Right(AddingALabelToAThread());
|
yield Right(AddingALabelToAThread());
|
||||||
await _emailRepository.addLabelToThread(
|
final result = await _emailRepository.addLabelToThread(
|
||||||
session,
|
session,
|
||||||
accountId,
|
accountId,
|
||||||
emailIds,
|
emailIds,
|
||||||
labelKeyword,
|
labelKeyword,
|
||||||
);
|
);
|
||||||
yield Right(AddALabelToAThreadSuccess(
|
if (emailIds.length == result.emailIdsSuccess.length) {
|
||||||
emailIds,
|
yield Right(AddALabelToAThreadSuccess(
|
||||||
labelKeyword,
|
result.emailIdsSuccess,
|
||||||
labelDisplay,
|
labelKeyword,
|
||||||
));
|
labelDisplay,
|
||||||
|
));
|
||||||
|
} else if (result.emailIdsSuccess.isEmpty) {
|
||||||
|
yield Left(AddALabelToAThreadFailure(
|
||||||
|
exception: EmailIdListIsEmptyException(),
|
||||||
|
labelDisplay: labelDisplay,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
yield Right(AddALabelToAThreadHasSomeFailure(
|
||||||
|
result.emailIdsSuccess,
|
||||||
|
labelKeyword,
|
||||||
|
labelDisplay,
|
||||||
|
));
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
yield Left(AddALabelToAThreadFailure(
|
yield Left(AddALabelToAThreadFailure(
|
||||||
exception: e,
|
exception: e,
|
||||||
|
|||||||
+20
-6
@@ -5,6 +5,7 @@ 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/session/session.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:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||||
|
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
||||||
import 'package:tmail_ui_user/features/email/domain/state/labels/remove_a_label_from_a_thread_state.dart';
|
import 'package:tmail_ui_user/features/email/domain/state/labels/remove_a_label_from_a_thread_state.dart';
|
||||||
|
|
||||||
@@ -22,17 +23,30 @@ class RemoveALabelFromAThreadInteractor {
|
|||||||
) async* {
|
) async* {
|
||||||
try {
|
try {
|
||||||
yield Right(RemovingALabelFromAThread());
|
yield Right(RemovingALabelFromAThread());
|
||||||
await _emailRepository.removeLabelFromThread(
|
final result = await _emailRepository.removeLabelFromThread(
|
||||||
session,
|
session,
|
||||||
accountId,
|
accountId,
|
||||||
emailIds,
|
emailIds,
|
||||||
labelKeyword,
|
labelKeyword,
|
||||||
);
|
);
|
||||||
yield Right(RemoveALabelFromAThreadSuccess(
|
if (emailIds.length == result.emailIdsSuccess.length) {
|
||||||
emailIds,
|
yield Right(RemoveALabelFromAThreadSuccess(
|
||||||
labelKeyword,
|
result.emailIdsSuccess,
|
||||||
labelDisplay,
|
labelKeyword,
|
||||||
));
|
labelDisplay,
|
||||||
|
));
|
||||||
|
} else if (result.emailIdsSuccess.isEmpty) {
|
||||||
|
yield Left(RemoveALabelFromAThreadFailure(
|
||||||
|
exception: EmailIdListIsEmptyException(),
|
||||||
|
labelDisplay: labelDisplay,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
yield Right(RemoveALabelFromAThreadHasSomeFailure(
|
||||||
|
result.emailIdsSuccess,
|
||||||
|
labelKeyword,
|
||||||
|
labelDisplay,
|
||||||
|
));
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
yield Left(RemoveALabelFromAThreadFailure(
|
yield Left(RemoveALabelFromAThreadFailure(
|
||||||
exception: e,
|
exception: e,
|
||||||
|
|||||||
@@ -169,7 +169,10 @@ extension HandleLabelForEmailExtension on SingleEmailController {
|
|||||||
final selectedEmail = mailboxDashBoardController.selectedEmail.value;
|
final selectedEmail = mailboxDashBoardController.selectedEmail.value;
|
||||||
if (selectedEmail?.id != emailId) return;
|
if (selectedEmail?.id != emailId) return;
|
||||||
|
|
||||||
selectedEmail?.keywords?.toggleKeyword(labelKeyword, remove);
|
selectedEmail?.keywords?.toggleKeyword(
|
||||||
|
keyword: labelKeyword,
|
||||||
|
shouldRemove: remove,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateLabelOnThreadIfNeeded({
|
void _updateLabelOnThreadIfNeeded({
|
||||||
|
|||||||
@@ -32,7 +32,10 @@ extension MapKeywordsExtension on Map<KeyWordIdentifier, bool>? {
|
|||||||
this?.remove(keyword);
|
this?.remove(keyword);
|
||||||
}
|
}
|
||||||
|
|
||||||
void toggleKeyword(KeyWordIdentifier keyword, bool shouldRemove) {
|
void toggleKeyword({
|
||||||
|
required KeyWordIdentifier keyword,
|
||||||
|
required bool shouldRemove,
|
||||||
|
}) {
|
||||||
if (shouldRemove) {
|
if (shouldRemove) {
|
||||||
removeKeyword(keyword);
|
removeKeyword(keyword);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+7
-3
@@ -37,7 +37,11 @@ extension AddLabelToThreadExtension on ThreadDetailController {
|
|||||||
emailLabels: threadLabels,
|
emailLabels: threadLabels,
|
||||||
emailIds: emailIds,
|
emailIds: emailIds,
|
||||||
onAddLabelToEmailsCallback: (emailIds, label, isSelected) {
|
onAddLabelToEmailsCallback: (emailIds, label, isSelected) {
|
||||||
toggleLabelToThread(label, isSelected, currentEmailIds: emailIds);
|
toggleLabelToThread(
|
||||||
|
label,
|
||||||
|
isSelected: isSelected,
|
||||||
|
currentEmailIds: emailIds,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
dialogLabel: 'add-label-to-thread-modal',
|
dialogLabel: 'add-label-to-thread-modal',
|
||||||
@@ -45,8 +49,8 @@ extension AddLabelToThreadExtension on ThreadDetailController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void toggleLabelToThread(
|
void toggleLabelToThread(
|
||||||
Label label,
|
Label label,{
|
||||||
bool isSelected, {
|
required bool isSelected,
|
||||||
List<EmailId>? currentEmailIds,
|
List<EmailId>? currentEmailIds,
|
||||||
}) {
|
}) {
|
||||||
final accountId = mailboxDashBoardController.accountId.value;
|
final accountId = mailboxDashBoardController.accountId.value;
|
||||||
|
|||||||
+1
-1
@@ -188,7 +188,7 @@ extension OnThreadDetailActionClick on ThreadDetailController {
|
|||||||
emailInThreadDetailInfos: emailsInThreadDetailInfo,
|
emailInThreadDetailInfos: emailsInThreadDetailInfo,
|
||||||
labels: mailboxDashBoardController.labelController.labels,
|
labels: mailboxDashBoardController.labelController.labels,
|
||||||
onSelectLabelAction: (label, isSelected) {
|
onSelectLabelAction: (label, isSelected) {
|
||||||
toggleLabelToThread(label, isSelected);
|
toggleLabelToThread(label, isSelected: isSelected);
|
||||||
submenuController.hide();
|
submenuController.hide();
|
||||||
popBack();
|
popBack();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ void main() {
|
|||||||
final keyword = KeyWordIdentifier('\$seen');
|
final keyword = KeyWordIdentifier('\$seen');
|
||||||
final map = <KeyWordIdentifier, bool>{};
|
final map = <KeyWordIdentifier, bool>{};
|
||||||
|
|
||||||
map.toggleKeyword(keyword, false);
|
map.toggleKeyword(keyword: keyword, shouldRemove: false);
|
||||||
|
|
||||||
expect(map.length, 1);
|
expect(map.length, 1);
|
||||||
expect(map[keyword], true);
|
expect(map[keyword], true);
|
||||||
@@ -265,7 +265,7 @@ void main() {
|
|||||||
KeyWordIdentifier('\$flagged'): true,
|
KeyWordIdentifier('\$flagged'): true,
|
||||||
};
|
};
|
||||||
|
|
||||||
map.toggleKeyword(keyword, false);
|
map.toggleKeyword(keyword: keyword, shouldRemove: false);
|
||||||
|
|
||||||
expect(map.length, 2);
|
expect(map.length, 2);
|
||||||
expect(map[keyword], true);
|
expect(map[keyword], true);
|
||||||
@@ -277,7 +277,7 @@ void main() {
|
|||||||
keyword: false,
|
keyword: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
map.toggleKeyword(keyword, false);
|
map.toggleKeyword(keyword: keyword, shouldRemove: false);
|
||||||
|
|
||||||
expect(map.length, 1);
|
expect(map.length, 1);
|
||||||
expect(map[keyword], true);
|
expect(map[keyword], true);
|
||||||
@@ -290,7 +290,7 @@ void main() {
|
|||||||
KeyWordIdentifier('\$flagged'): true,
|
KeyWordIdentifier('\$flagged'): true,
|
||||||
};
|
};
|
||||||
|
|
||||||
map.toggleKeyword(keyword, true);
|
map.toggleKeyword(keyword: keyword, shouldRemove: true);
|
||||||
|
|
||||||
expect(map.length, 1);
|
expect(map.length, 1);
|
||||||
expect(map.containsKey(keyword), false);
|
expect(map.containsKey(keyword), false);
|
||||||
@@ -302,7 +302,7 @@ void main() {
|
|||||||
};
|
};
|
||||||
final keyword = KeyWordIdentifier('\$seen');
|
final keyword = KeyWordIdentifier('\$seen');
|
||||||
|
|
||||||
map.toggleKeyword(keyword, true);
|
map.toggleKeyword(keyword: keyword, shouldRemove: true);
|
||||||
|
|
||||||
expect(map.length, 1);
|
expect(map.length, 1);
|
||||||
expect(map.values.first, true);
|
expect(map.values.first, true);
|
||||||
@@ -312,7 +312,7 @@ void main() {
|
|||||||
final map = <KeyWordIdentifier, bool>{};
|
final map = <KeyWordIdentifier, bool>{};
|
||||||
final keyword = KeyWordIdentifier('\$seen');
|
final keyword = KeyWordIdentifier('\$seen');
|
||||||
|
|
||||||
expect(() => map.toggleKeyword(keyword, true), returnsNormally);
|
expect(() => map.toggleKeyword(keyword: keyword, shouldRemove: true), returnsNormally);
|
||||||
expect(map.isEmpty, true);
|
expect(map.isEmpty, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -320,7 +320,7 @@ void main() {
|
|||||||
final map = <KeyWordIdentifier, bool>{};
|
final map = <KeyWordIdentifier, bool>{};
|
||||||
final keyword = KeyWordIdentifier('\$seen');
|
final keyword = KeyWordIdentifier('\$seen');
|
||||||
|
|
||||||
expect(() => map.toggleKeyword(keyword, false), returnsNormally);
|
expect(() => map.toggleKeyword(keyword: keyword, shouldRemove: false), returnsNormally);
|
||||||
expect(map.length, 1);
|
expect(map.length, 1);
|
||||||
expect(map[keyword], true);
|
expect(map[keyword], true);
|
||||||
});
|
});
|
||||||
@@ -329,16 +329,16 @@ void main() {
|
|||||||
Map<KeyWordIdentifier, bool>? map;
|
Map<KeyWordIdentifier, bool>? map;
|
||||||
final keyword = KeyWordIdentifier('\$seen');
|
final keyword = KeyWordIdentifier('\$seen');
|
||||||
|
|
||||||
expect(() => map.toggleKeyword(keyword, true), returnsNormally);
|
expect(() => map.toggleKeyword(keyword: keyword, shouldRemove: true), returnsNormally);
|
||||||
expect(() => map.toggleKeyword(keyword, false), returnsNormally);
|
expect(() => map.toggleKeyword(keyword: keyword, shouldRemove: false), returnsNormally);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('calling toggleKeyword on null map has no side effects', () {
|
test('calling toggleKeyword on null map has no side effects', () {
|
||||||
Map<KeyWordIdentifier, bool>? map;
|
Map<KeyWordIdentifier, bool>? map;
|
||||||
final keyword = KeyWordIdentifier('\$seen');
|
final keyword = KeyWordIdentifier('\$seen');
|
||||||
|
|
||||||
map.toggleKeyword(keyword, false);
|
map.toggleKeyword(keyword: keyword, shouldRemove: false);
|
||||||
map.toggleKeyword(keyword, true);
|
map.toggleKeyword(keyword: keyword, shouldRemove: true);
|
||||||
|
|
||||||
expect(map, isNull);
|
expect(map, isNull);
|
||||||
});
|
});
|
||||||
@@ -351,7 +351,7 @@ void main() {
|
|||||||
|
|
||||||
final originalIdentity = identityHashCode(map);
|
final originalIdentity = identityHashCode(map);
|
||||||
|
|
||||||
map.toggleKeyword(keyword, true);
|
map.toggleKeyword(keyword: keyword, shouldRemove: true);
|
||||||
|
|
||||||
expect(identityHashCode(map), originalIdentity);
|
expect(identityHashCode(map), originalIdentity);
|
||||||
expect(map.isEmpty, true);
|
expect(map.isEmpty, true);
|
||||||
|
|||||||
Reference in New Issue
Block a user