import 'dart:async'; import 'package:collection/collection.dart'; import 'package:core/utils/app_logger.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/method/error_method_response.dart'; import 'package:jmap_dart_client/jmap/core/error/set_error.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/core/patch_object.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/state.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/mailbox/changes/changes_mailbox_method.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/changes/changes_mailbox_response.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/get/get_mailbox_method.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/get/get_mailbox_response.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/set/set_mailbox_method.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/set/set_mailbox_response.dart'; import 'package:model/error_type_handler/set_method_error_handler_mixin.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/mixin/handle_error_mixin.dart'; import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_change_response.dart'; import 'package:tmail_ui_user/features/mailbox/domain/extensions/list_mailbox_id_extension.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_response.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_subscribe_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/move_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/rename_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/subscribe_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/subscribe_multiple_mailbox_request.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; class MailboxAPI with HandleSetErrorMixin { final HttpClient httpClient; MailboxAPI(this.httpClient); Future getAllMailbox(Session session, AccountId accountId, {Properties? properties}) async { final processingInvocation = ProcessingInvocation(); final jmapRequestBuilder = JmapRequestBuilder(httpClient, processingInvocation); final getMailboxCreated = GetMailboxMethod(accountId); final queryInvocation = jmapRequestBuilder.invocation(getMailboxCreated); final capabilities = getMailboxCreated.requiredCapabilities .toCapabilitiesSupportTeamMailboxes(session, accountId); final result = await (jmapRequestBuilder ..usings(capabilities)) .build() .execute(); final resultCreated = result.parse( queryInvocation.methodCallId, GetMailboxResponse.deserialize); return MailboxResponse(mailboxes: resultCreated?.list, state: resultCreated?.state); } Future getChanges(Session session, AccountId accountId, State sinceState) async { final processingInvocation = ProcessingInvocation(); final jmapRequestBuilder = JmapRequestBuilder(httpClient, processingInvocation); final changesMailboxMethod = ChangesMailboxMethod(accountId, sinceState, maxChanges: UnsignedInt(128)); final changesMailboxInvocation = jmapRequestBuilder.invocation(changesMailboxMethod); final getMailboxUpdated = GetMailboxMethod(accountId) ..addReferenceIds(processingInvocation.createResultReference( changesMailboxInvocation.methodCallId, ReferencePath.updatedPath)) ..addReferenceProperties(processingInvocation.createResultReference( changesMailboxInvocation.methodCallId, ReferencePath.updatedPropertiesPath)); final getMailboxCreated = GetMailboxMethod(accountId) ..addReferenceIds(processingInvocation.createResultReference( changesMailboxInvocation.methodCallId, ReferencePath.createdPath)); final getMailboxUpdatedInvocation = jmapRequestBuilder.invocation(getMailboxUpdated); final getMailboxCreatedInvocation = jmapRequestBuilder.invocation(getMailboxCreated); final capabilities = getMailboxUpdated.requiredCapabilities .toCapabilitiesSupportTeamMailboxes(session, accountId); final result = await (jmapRequestBuilder ..usings(capabilities)) .build() .execute(); final resultChanges = result.parse( changesMailboxInvocation.methodCallId, ChangesMailboxResponse.deserialize); final resultUpdated = result.parse( getMailboxUpdatedInvocation.methodCallId, GetMailboxResponse.deserialize); final resultCreated = result.parse( getMailboxCreatedInvocation.methodCallId, GetMailboxResponse.deserialize); final listMailboxIdDestroyed = resultChanges?.destroyed.map((id) => MailboxId(id)).toList() ?? []; return MailboxChangeResponse( updated: resultUpdated?.list, created: resultCreated?.list, destroyed: listMailboxIdDestroyed, newStateMailbox: resultUpdated?.state, newStateChanges: resultChanges?.newState, hasMoreChanges: resultChanges?.hasMoreChanges ?? false, updatedProperties: resultChanges?.updatedProperties); } Future createNewMailbox(Session session, AccountId accountId, CreateNewMailboxRequest request) async { final setMailboxMethod = SetMailboxMethod(accountId) ..addCreate( request.creationId, Mailbox( name: request.newName, isSubscribed: IsSubscribed(request.isSubscribed), parentId: request.parentId ) ); final requestBuilder = JmapRequestBuilder(httpClient, ProcessingInvocation()); final setMailboxInvocation = requestBuilder.invocation(setMailboxMethod); final capabilities = setMailboxMethod.requiredCapabilities .toCapabilitiesSupportTeamMailboxes(session, accountId); final response = await (requestBuilder ..usings(capabilities)) .build() .execute(); final setMailboxResponse = response.parse( setMailboxInvocation.methodCallId, SetMailboxResponse.deserialize); final mapMailboxCreated = setMailboxResponse?.created; if (mapMailboxCreated != null && mapMailboxCreated.containsKey(request.creationId)) { final mailboxCreated = mapMailboxCreated[request.creationId]!; final newMailboxCreated = mailboxCreated.toMailbox( request.newName, parentId: request.parentId); return newMailboxCreated; } else { throw _parseErrorForSetMailboxResponse(setMailboxResponse, request.creationId); } } _parseErrorForSetMailboxResponse(SetMailboxResponse? response, Id requestId) { final mapError = response?.notCreated ?? response?.notUpdated ?? response?.notDestroyed; if (mapError != null && mapError.containsKey(requestId)) { final setError = mapError[requestId]; log('MailboxAPI::_parseErrorForSetMailboxResponse():setError: $setError'); if (setError?.type == ErrorMethodResponse.invalidArguments) { throw InvalidArgumentsMethodResponse(description: setError?.description); } else if (setError?.type == ErrorMethodResponse.invalidResultReference) { throw InvalidResultReferenceMethodResponse(description: setError?.description); } else { throw UnknownMethodResponse(description: setError?.description); } } else { throw UnknownMethodResponse(); } } Future> deleteMultipleMailbox(Session session, AccountId accountId, List mailboxIds) async { final coreCapability = session.getCapabilityProperties( accountId, CapabilityIdentifier.jmapCore); final maxMethodCount = coreCapability.maxCallsInRequest?.value.toInt() ?? 0; final Map finalDeletedMailboxErrors = {}; var start = 0; var end = 0; while (end < mailboxIds.length) { start = end; if (mailboxIds.length - start >= maxMethodCount) { end = maxMethodCount; } else { end = mailboxIds.length; } log('MailboxAPI::deleteMultipleMailbox(): delete from $start to $end / ${mailboxIds.length}'); final currentExecuteList = mailboxIds.sublist(start, end); final requestBuilder = JmapRequestBuilder(httpClient, ProcessingInvocation()); final currentSetMailboxInvocations = currentExecuteList.map((mailboxId) { return SetMailboxMethod(accountId) ..addDestroy({mailboxId.id}) ..addOnDestroyRemoveEmails(true); }) .map(requestBuilder.invocation) .toList(); final capabilities = {CapabilityIdentifier.jmapCore, CapabilityIdentifier.jmapMail} .toCapabilitiesSupportTeamMailboxes(session, accountId); final response = await (requestBuilder ..usings(capabilities)) .build() .execute(); final deleteMailboxErrors = currentSetMailboxInvocations .map((currentInvocation) => response.parse(currentInvocation.methodCallId, SetMailboxResponse.deserialize)) .map(_handleDeleteMailboxResponse) .expand((entries) => entries); finalDeletedMailboxErrors.addAll(Map.fromEntries(deleteMailboxErrors)); } return finalDeletedMailboxErrors; } List> _handleDeleteMailboxResponse(SetMailboxResponse? response) { final List> remainedErrors = []; if (response != null) { handleSetErrors( notDestroyedError: response.notDestroyed, notUpdatedError: response.notUpdated, notCreatedError: response.notCreated, notDestroyedHandlers: { (entry) => entry.value.type == SetError.notFound, }, unCatchErrorHandler: (setErrorEntry) { remainedErrors.add(setErrorEntry); return false; } ); } return remainedErrors; } Future renameMailbox(Session session, AccountId accountId, RenameMailboxRequest request) async { final setMailboxMethod = SetMailboxMethod(accountId) ..addUpdates({request.mailboxId.id : PatchObject({'name' : request.newName.name})}); final requestBuilder = JmapRequestBuilder(httpClient, ProcessingInvocation()); final setMailboxInvocation = requestBuilder.invocation(setMailboxMethod); final capabilities = setMailboxMethod.requiredCapabilities .toCapabilitiesSupportTeamMailboxes(session, accountId); final response = await (requestBuilder ..usings(capabilities)) .build() .execute(); final setMailboxResponse = response.parse( setMailboxInvocation.methodCallId, SetMailboxResponse.deserialize); return Future.sync(() async { return setMailboxResponse?.updated?.isNotEmpty == true; }).catchError((error) { throw error; }); } Future moveMailbox(Session session, AccountId accountId, MoveMailboxRequest request) async { final setMailboxMethod = SetMailboxMethod(accountId) ..addUpdates({ request.mailboxId.id : PatchObject({ 'parentId': request.destinationMailboxId?.id.value }) }); final requestBuilder = JmapRequestBuilder(httpClient, ProcessingInvocation()); final setMailboxInvocation = requestBuilder.invocation(setMailboxMethod); final capabilities = setMailboxMethod.requiredCapabilities .toCapabilitiesSupportTeamMailboxes(session, accountId); final response = await (requestBuilder ..usings(capabilities)) .build() .execute(); final setMailboxResponse = response.parse( setMailboxInvocation.methodCallId, SetMailboxResponse.deserialize); return Future.sync(() async { return setMailboxResponse?.updated?.isNotEmpty == true; }).catchError((error) { throw error; }); } Future subscribeMailbox(Session session, AccountId accountId, SubscribeMailboxRequest request) async { final setMailboxMethod = SetMailboxMethod(accountId) ..addUpdates({ request.mailboxId.id : PatchObject({ MailboxProperty.isSubscribed: request.subscribeState == MailboxSubscribeState.enabled }) }); final requestBuilder = JmapRequestBuilder(httpClient, ProcessingInvocation()); final setMailboxInvocation = requestBuilder.invocation(setMailboxMethod); final capabilities = setMailboxMethod.requiredCapabilities .toCapabilitiesSupportTeamMailboxes(session, accountId); final response = await (requestBuilder ..usings(capabilities)) .build() .execute(); final setMailboxResponse = response.parse( setMailboxInvocation.methodCallId, SetMailboxResponse.deserialize); return Future.sync(() async { return setMailboxResponse?.updated?.containsKey(request.mailboxId.id) ?? false; }).catchError((error) { throw error; }); } Future> subscribeMultipleMailbox( Session session, AccountId accountId, SubscribeMultipleMailboxRequest subscribeRequest ) async { final mapMailboxUpdated = subscribeRequest.mailboxIdsSubscribe .generateMapUpdateObjectSubscribeMailbox(subscribeRequest.subscribeState); final setMailboxMethod = SetMailboxMethod(accountId) ..addUpdates(mapMailboxUpdated); final requestBuilder = JmapRequestBuilder(httpClient, ProcessingInvocation()); final setMailboxInvocation = requestBuilder.invocation(setMailboxMethod); final capabilities = setMailboxMethod.requiredCapabilities .toCapabilitiesSupportTeamMailboxes(session, accountId); final response = await (requestBuilder ..usings(capabilities)) .build() .execute(); final setMailboxResponse = response.parse( setMailboxInvocation.methodCallId, SetMailboxResponse.deserialize ); final listMailboxIdSubscribe = setMailboxResponse?.updated?.keys .whereNotNull() .map((id) => MailboxId(id)) .toList(); log('MailboxAPI::subscribeMultipleMailbox():listMailboxIdSubscribe: $listMailboxIdSubscribe'); return listMailboxIdSubscribe ?? []; } }