From 856e1613a0266a6e85a61aa502749befd3d8c45a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Thu, 29 Aug 2024 23:58:39 +0200 Subject: [PATCH] Fixing Gmail sent folder deletes. --- .../Processors/DefaultChangeProcessor.cs | 5 ++-- .../Processors/GmailChangeProcessor.cs | 6 ++++- Wino.Core/Services/MailService.cs | 26 ++++++++++++++----- Wino.Core/Synchronizers/GmailSynchronizer.cs | 14 ++++++++-- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs b/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs index e8cb678a..d9960d53 100644 --- a/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs +++ b/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs @@ -21,7 +21,6 @@ namespace Wino.Core.Integration.Processors { Task UpdateAccountAsync(MailAccount account); Task UpdateAccountDeltaSynchronizationIdentifierAsync(Guid accountId, string deltaSynchronizationIdentifier); - Task CreateAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId); Task DeleteAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId); Task ChangeMailReadStatusAsync(string mailCopyId, bool isRead); Task ChangeFlagStatusAsync(string mailCopyId, bool isFlagged); @@ -53,6 +52,7 @@ namespace Wino.Core.Integration.Processors public interface IGmailChangeProcessor : IDefaultChangeProcessor { Task MapLocalDraftAsync(string mailCopyId, string newDraftId, string newThreadId); + Task CreateAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId); } public interface IOutlookChangeProcessor : IDefaultChangeProcessor @@ -135,8 +135,7 @@ namespace Wino.Core.Integration.Processors public Task DeleteAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId) => MailService.DeleteAssignmentAsync(accountId, mailCopyId, remoteFolderId); - public Task CreateAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId) - => MailService.CreateAssignmentAsync(accountId, mailCopyId, remoteFolderId); + public Task DeleteMailAsync(Guid accountId, string mailId) => MailService.DeleteMailAsync(accountId, mailId); diff --git a/Wino.Core/Integration/Processors/GmailChangeProcessor.cs b/Wino.Core/Integration/Processors/GmailChangeProcessor.cs index 72eb1dee..d23a3653 100644 --- a/Wino.Core/Integration/Processors/GmailChangeProcessor.cs +++ b/Wino.Core/Integration/Processors/GmailChangeProcessor.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Wino.Core.Domain.Interfaces; using Wino.Core.Services; @@ -12,5 +13,8 @@ namespace Wino.Core.Integration.Processors public Task MapLocalDraftAsync(string mailCopyId, string newDraftId, string newThreadId) => MailService.MapLocalDraftAsync(mailCopyId, newDraftId, newThreadId); + + public Task CreateAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId) + => MailService.CreateAssignmentAsync(accountId, mailCopyId, remoteFolderId); } } diff --git a/Wino.Core/Services/MailService.cs b/Wino.Core/Services/MailService.cs index 4a5e16a7..5cea1088 100644 --- a/Wino.Core/Services/MailService.cs +++ b/Wino.Core/Services/MailService.cs @@ -409,12 +409,11 @@ namespace Wino.Core.Services foreach (var mailItem in allMails) { - await DeleteMailInternalAsync(mailItem).ConfigureAwait(false); - - // Delete mime file. + // Delete mime file as well. // Even though Gmail might have multiple copies for the same mail, we only have one MIME file for all. // Their FileId is inserted same. - await _mimeFileService.DeleteMimeMessageAsync(accountId, mailItem.FileId); + + await DeleteMailInternalAsync(mailItem, preserveMimeFile: false).ConfigureAwait(false); } } @@ -457,7 +456,7 @@ namespace Wino.Core.Services ReportUIChange(new MailUpdatedMessage(mailCopy)); } - private async Task DeleteMailInternalAsync(MailCopy mailCopy) + private async Task DeleteMailInternalAsync(MailCopy mailCopy, bool preserveMimeFile) { if (mailCopy == null) { @@ -473,7 +472,7 @@ namespace Wino.Core.Services // If there are no more copies exists of the same mail, delete the MIME file as well. var isMailExists = await IsMailExistsAsync(mailCopy.Id).ConfigureAwait(false); - if (!isMailExists) + if (!isMailExists && !preserveMimeFile) { await _mimeFileService.DeleteMimeMessageAsync(mailCopy.AssignedAccount.Id, mailCopy.FileId).ConfigureAwait(false); } @@ -554,6 +553,19 @@ namespace Wino.Core.Services return; } + if (mailCopy.AssignedFolder.SpecialFolderType == SpecialFolderType.Sent && + localFolder.SpecialFolderType == SpecialFolderType.Deleted) + { + // Sent item is deleted. + // Gmail does not delete the sent items, but moves them to the deleted folder. + // API doesn't allow removing Sent label. + // Here we intercept this behavior, removing the Sent copy of the mail and adding the Deleted copy. + // This way item will only be visible in Trash folder as in Gmail Web UI. + // Don't delete MIME file since if exists. + + await DeleteMailInternalAsync(mailCopy, preserveMimeFile: true).ConfigureAwait(false); + } + // Copy one of the mail copy and assign it to the new folder. // We don't need to create a new MIME pack. // Therefore FileId is not changed for the new MailCopy. @@ -585,7 +597,7 @@ namespace Wino.Core.Services return; } - await DeleteMailInternalAsync(mailItem).ConfigureAwait(false); + await DeleteMailInternalAsync(mailItem, preserveMimeFile: false).ConfigureAwait(false); } public async Task CreateMailAsync(Guid accountId, NewMailItemPackage package) diff --git a/Wino.Core/Synchronizers/GmailSynchronizer.cs b/Wino.Core/Synchronizers/GmailSynchronizer.cs index 4a15ecf6..31f969b9 100644 --- a/Wino.Core/Synchronizers/GmailSynchronizer.cs +++ b/Wino.Core/Synchronizers/GmailSynchronizer.cs @@ -595,13 +595,23 @@ namespace Wino.Core.Synchronizers { return CreateBatchedHttpBundleFromGroup(request, (items) => { + // Sent label can't be removed from mails for Gmail. + // They are automatically assigned by Gmail. + // When you delete sent mail from gmail web portal, it's moved to Trash + // but still has Sent label. It's just hidden from the user. + // Proper assignments will be done later on CreateAssignment call to mimic this behavior. var batchModifyRequest = new BatchModifyMessagesRequest { Ids = items.Select(a => a.Item.Id.ToString()).ToList(), - AddLabelIds = new[] { request.ToFolder.RemoteFolderId }, - RemoveLabelIds = new[] { request.FromFolder.RemoteFolderId } + AddLabelIds = [request.ToFolder.RemoteFolderId] }; + // Only add remove label ids if the source folder is not sent folder. + if (request.FromFolder.SpecialFolderType != SpecialFolderType.Sent) + { + batchModifyRequest.RemoveLabelIds = [request.FromFolder.RemoteFolderId]; + } + return _gmailService.Users.Messages.BatchModify(batchModifyRequest, "me"); }); }