305 lines
11 KiB
Dart
305 lines
11 KiB
Dart
import 'dart:async';
|
|
import 'dart:math' hide log;
|
|
|
|
import 'package:core/presentation/state/failure.dart';
|
|
import 'package:core/presentation/state/success.dart';
|
|
import 'package:core/utils/app_logger.dart';
|
|
import 'package:dartz/dartz.dart';
|
|
import 'package:jmap_dart_client/http/http_client.dart';
|
|
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/filter/filter.dart';
|
|
import 'package:jmap_dart_client/jmap/core/id.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/session/session.dart';
|
|
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
|
|
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
|
import 'package:jmap_dart_client/jmap/jmap_request.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:jmap_dart_client/jmap/mail/email/email_filter_condition.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/query/query_email_method.dart';
|
|
import 'package:jmap_dart_client/jmap/mail/email/query/query_email_response.dart';
|
|
import 'package:jmap_dart_client/jmap/mail/email/set/set_email_method.dart';
|
|
import 'package:jmap_dart_client/jmap/mail/email/set/set_email_response.dart';
|
|
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
|
import 'package:model/email/email_property.dart';
|
|
import 'package:model/extensions/list_email_extension.dart';
|
|
import 'package:model/extensions/list_email_id_extension.dart';
|
|
import 'package:model/extensions/list_id_extension.dart';
|
|
import 'package:model/extensions/session_extension.dart';
|
|
import 'package:tmail_ui_user/features/base/mixin/handle_error_mixin.dart';
|
|
import 'package:tmail_ui_user/features/email/domain/model/move_action.dart';
|
|
import 'package:tmail_ui_user/features/mailbox/domain/exceptions/mailbox_exception.dart';
|
|
import 'package:tmail_ui_user/features/mailbox/domain/state/move_folder_content_state.dart';
|
|
import 'package:tmail_ui_user/features/thread/data/extensions/list_email_extension.dart';
|
|
import 'package:tmail_ui_user/features/thread/data/extensions/list_email_id_extension.dart';
|
|
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
|
|
import 'package:tmail_ui_user/main/error/capability_validator.dart';
|
|
|
|
mixin MailAPIMixin on HandleSetErrorMixin {
|
|
int getMaxObjectsInSetMethod(Session session, AccountId accountId) {
|
|
final coreCapability = session.getCapabilityProperties<CoreCapability>(
|
|
accountId,
|
|
CapabilityIdentifier.jmapCore,
|
|
);
|
|
final maxObjectsInSetMethod =
|
|
coreCapability?.maxObjectsInSet?.value.toInt() ??
|
|
CapabilityIdentifierExtension.defaultMaxObjectsInSet;
|
|
|
|
final minOfMaxObjectsInSetMethod = min(
|
|
maxObjectsInSetMethod,
|
|
CapabilityIdentifierExtension.defaultMaxObjectsInSet,
|
|
);
|
|
log('$runtimeType::_getMaxObjectsInSetMethod:minOfMaxObjectsInSetMethod = $minOfMaxObjectsInSetMethod');
|
|
return minOfMaxObjectsInSetMethod;
|
|
}
|
|
|
|
Future<({List<EmailId> emailIdsSuccess, Map<Id, SetError> mapErrors})>
|
|
moveEmailsBetweenMailboxes({
|
|
required HttpClient httpClient,
|
|
required Session session,
|
|
required AccountId accountId,
|
|
required List<EmailId> emailIds,
|
|
required MailboxId currentMailboxId,
|
|
required MailboxId destinationMailboxId,
|
|
bool markAsRead = false,
|
|
}) async {
|
|
final maxObjects = getMaxObjectsInSetMethod(session, accountId);
|
|
final totalEmails = emailIds.length;
|
|
final maxBatches = min(totalEmails, maxObjects);
|
|
|
|
final List<EmailId> updatedEmailIds = List.empty(growable: true);
|
|
final Map<Id, SetError> mapErrors = <Id, SetError>{};
|
|
|
|
for (int start = 0; start < totalEmails; start += maxBatches) {
|
|
int end =
|
|
(start + maxBatches < totalEmails) ? start + maxBatches : totalEmails;
|
|
log('$runtimeType::moveEmailsBetweenMailboxes:emails from ${start + 1} to $end');
|
|
|
|
final currentEmailIds = emailIds.sublist(start, end);
|
|
|
|
final moveProperties = currentEmailIds.generateMapUpdateObjectMoveToMailbox(
|
|
currentMailboxId: currentMailboxId,
|
|
destinationMailboxId: destinationMailboxId,
|
|
markAsRead: markAsRead,
|
|
);
|
|
|
|
final setEmailMethod = SetEmailMethod(accountId)
|
|
..addUpdates(moveProperties);
|
|
|
|
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(
|
|
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<EmailsResponse> fetchAllEmail({
|
|
required HttpClient httpClient,
|
|
required Session session,
|
|
required AccountId accountId,
|
|
UnsignedInt? limit,
|
|
int? position,
|
|
Set<Comparator>? sort,
|
|
Filter? filter,
|
|
Properties? properties,
|
|
}) async {
|
|
final processingInvocation = ProcessingInvocation();
|
|
|
|
final jmapRequestBuilder = JmapRequestBuilder(
|
|
httpClient,
|
|
processingInvocation,
|
|
);
|
|
|
|
final queryEmailMethod = QueryEmailMethod(accountId);
|
|
|
|
if (limit != null) queryEmailMethod.addLimit(limit);
|
|
|
|
if (position != null) queryEmailMethod.addPosition(position);
|
|
|
|
if (sort != null) queryEmailMethod.addSorts(sort);
|
|
|
|
if (filter != null) queryEmailMethod.addFilters(filter);
|
|
|
|
final queryEmailInvocation =
|
|
jmapRequestBuilder.invocation(queryEmailMethod);
|
|
|
|
final getEmailMethod = GetEmailMethod(accountId);
|
|
|
|
if (properties != null) getEmailMethod.addProperties(properties);
|
|
|
|
getEmailMethod.addReferenceIds(
|
|
processingInvocation.createResultReference(
|
|
queryEmailInvocation.methodCallId,
|
|
ReferencePath.idsPath,
|
|
),
|
|
);
|
|
|
|
final getEmailInvocation = jmapRequestBuilder.invocation(getEmailMethod);
|
|
|
|
final capabilities = getEmailMethod.requiredCapabilities
|
|
.toCapabilitiesSupportTeamMailboxes(session, accountId);
|
|
|
|
final result =
|
|
await (jmapRequestBuilder..usings(capabilities)).build().execute();
|
|
|
|
final responseOfGetEmailMethod = result.parse<GetEmailResponse>(
|
|
getEmailInvocation.methodCallId,
|
|
GetEmailResponse.deserialize,
|
|
);
|
|
|
|
final responseOfQueryEmailMethod = result.parse<QueryEmailResponse>(
|
|
queryEmailInvocation.methodCallId,
|
|
QueryEmailResponse.deserialize,
|
|
);
|
|
|
|
final emailList = sortEmails(
|
|
getEmailResponse: responseOfGetEmailMethod,
|
|
queryEmailResponse: responseOfQueryEmailMethod,
|
|
);
|
|
|
|
final notFoundEmailIds =
|
|
responseOfGetEmailMethod?.notFound?.toEmailIds().toList();
|
|
log('$runtimeType::getAllEmail:notFoundEmailIds = ${notFoundEmailIds!.asListString.toString()} | NewState = ${responseOfGetEmailMethod?.state.value}');
|
|
return EmailsResponse(
|
|
emailList: emailList,
|
|
notFoundEmailIds: notFoundEmailIds,
|
|
state: responseOfGetEmailMethod?.state,
|
|
);
|
|
}
|
|
|
|
List<Email>? sortEmails({
|
|
GetEmailResponse? getEmailResponse,
|
|
QueryEmailResponse? queryEmailResponse,
|
|
}) {
|
|
final listEmails = getEmailResponse?.list;
|
|
final listIds = queryEmailResponse?.ids.toList();
|
|
|
|
if (listEmails?.isNotEmpty != true || listIds?.isNotEmpty != true) {
|
|
return listEmails;
|
|
}
|
|
|
|
final listSortedEmails = listEmails!.sortEmailsById(listIds!);
|
|
|
|
return listSortedEmails;
|
|
}
|
|
|
|
Future<void> moveAllEmailsBetweenFolders({
|
|
required HttpClient httpClient,
|
|
required Session session,
|
|
required AccountId accountId,
|
|
required MailboxId currentMailboxId,
|
|
required MailboxId destinationMailboxId,
|
|
required MoveAction moveAction,
|
|
int totalEmails = 0,
|
|
bool markAsRead = false,
|
|
StreamController<Either<Failure, Success>>? onProgressController,
|
|
}) async {
|
|
int countEmailsCompleted = 0;
|
|
bool hasEmails = true;
|
|
Email? lastEmail;
|
|
|
|
while (hasEmails) {
|
|
final listEmails = await getLatestEmails(
|
|
httpClient: httpClient,
|
|
session: session,
|
|
accountId: accountId,
|
|
mailboxId: currentMailboxId,
|
|
lastEmail: lastEmail,
|
|
);
|
|
log('$runtimeType::moveAllEmailsBetweenFolders(): Length of emails = ${listEmails.length}');
|
|
if (listEmails.isEmpty) {
|
|
hasEmails = false;
|
|
} else {
|
|
hasEmails = true;
|
|
lastEmail = listEmails.last;
|
|
|
|
final movedEmails = await moveEmailsBetweenMailboxes(
|
|
httpClient: httpClient,
|
|
session: session,
|
|
accountId: accountId,
|
|
emailIds: listEmails.listEmailIds,
|
|
currentMailboxId: currentMailboxId,
|
|
destinationMailboxId: destinationMailboxId,
|
|
markAsRead: markAsRead,
|
|
);
|
|
|
|
countEmailsCompleted += movedEmails.emailIdsSuccess.length;
|
|
|
|
onProgressController?.add(
|
|
Right<Failure, Success>(MoveFolderContentProgressState(
|
|
currentMailboxId,
|
|
countEmailsCompleted,
|
|
totalEmails,
|
|
)),
|
|
);
|
|
}
|
|
}
|
|
log('$runtimeType::moveAllEmailsBetweenFolders(): Total emails moved = $countEmailsCompleted');
|
|
if (moveAction == MoveAction.moving &&
|
|
countEmailsCompleted < totalEmails &&
|
|
totalEmails > 0) {
|
|
throw CannotMoveAllEmailException();
|
|
}
|
|
}
|
|
|
|
Future<List<Email>> getLatestEmails({
|
|
required HttpClient httpClient,
|
|
required Session session,
|
|
required AccountId accountId,
|
|
required MailboxId mailboxId,
|
|
Email? lastEmail,
|
|
}) async {
|
|
final response = await fetchAllEmail(
|
|
httpClient: httpClient,
|
|
session: session,
|
|
accountId: accountId,
|
|
sort: <Comparator>{}..add(
|
|
EmailComparator(
|
|
EmailComparatorProperty.receivedAt,
|
|
)..setIsAscending(false),
|
|
),
|
|
filter: EmailFilterCondition(
|
|
inMailbox: mailboxId,
|
|
before: lastEmail?.receivedAt,
|
|
),
|
|
properties: Properties({
|
|
EmailProperty.id,
|
|
EmailProperty.receivedAt,
|
|
}),
|
|
);
|
|
|
|
if (lastEmail?.id != null) {
|
|
return response.emailList?.withoutEmailWithId(lastEmail!.id!) ?? [];
|
|
} else {
|
|
return response.emailList ?? [];
|
|
}
|
|
}
|
|
}
|