From 832b363da7c762d55bbc6c3120485a2ebef1c9d1 Mon Sep 17 00:00:00 2001 From: Aleh Khantsevich Date: Mon, 24 Feb 2025 18:53:11 +0100 Subject: [PATCH] Improved outlook online search even more and removed redundant methods from ChangeProcessor (#586) --- .../Processors/DefaultChangeProcessor.cs | 17 ----- .../Synchronizers/OutlookSynchronizer.cs | 63 +++++++++---------- 2 files changed, 31 insertions(+), 49 deletions(-) diff --git a/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs b/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs index a18991a2..89c103f7 100644 --- a/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs +++ b/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs @@ -40,16 +40,6 @@ public interface IDefaultChangeProcessor Task MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId); Task UpdateFolderLastSyncDateAsync(Guid folderId); Task UpdateRemoteAliasInformationAsync(MailAccount account, List remoteAccountAliases); - - /// - /// Interrupted initial synchronization may cause downloaded mails to be saved in the database twice. - /// Since downloading mime is costly in Outlook, we need to check if the actual copy of the message has been saved before. - /// This is also used in online search to prevent duplicate mails. - /// - /// MailCopyId of the message. - /// Whether mail exists or not. - Task IsMailExistsAsync(string messageId); - // Calendar Task> GetAccountCalendarsAsync(Guid accountId); @@ -60,7 +50,6 @@ public interface IDefaultChangeProcessor Task UpdateAccountCalendarAsync(AccountCalendar accountCalendar); Task UpdateCalendarDeltaSynchronizationToken(Guid calendarId, string deltaToken); - Task GetMailCopyAsync(string mailCopyId); Task> GetMailCopiesAsync(IEnumerable mailCopyIds); Task CreateMailRawAsync(MailAccount account, MailItemFolder mailItemFolder, NewMailItemPackage package); Task DeleteUserMailCacheAsync(Guid accountId); @@ -138,15 +127,9 @@ public class DefaultChangeProcessor(IDatabaseService databaseService, public Task ChangeFlagStatusAsync(string mailCopyId, bool isFlagged) => MailService.ChangeFlagStatusAsync(mailCopyId, isFlagged); - public Task IsMailExistsAsync(string messageId) - => MailService.IsMailExistsAsync(messageId); - public Task> AreMailsExistsAsync(IEnumerable mailCopyIds) => MailService.AreMailsExistsAsync(mailCopyIds); - public Task GetMailCopyAsync(string mailCopyId) - => MailService.GetSingleMailItemAsync(mailCopyId); - public Task> GetMailCopiesAsync(IEnumerable mailCopyIds) => MailService.GetMailItemsAsync(mailCopyIds); diff --git a/Wino.Core/Synchronizers/OutlookSynchronizer.cs b/Wino.Core/Synchronizers/OutlookSynchronizer.cs index fd9c7b5a..68da65c9 100644 --- a/Wino.Core/Synchronizers/OutlookSynchronizer.cs +++ b/Wino.Core/Synchronizers/OutlookSynchronizer.cs @@ -967,12 +967,10 @@ public class OutlookSynchronizer : WinoSynchronizer> OnlineSearchAsync(string queryText, List folders, CancellationToken cancellationToken = default) { - bool isFoldersIncluded = folders?.Any() ?? false; - - var messagesToDownload = new List(); + List messagesReturnedByApi = []; // Perform search for each folder separately. - if (isFoldersIncluded) + if (folders?.Count > 0) { var folderIds = folders.Select(a => a.RemoteFolderId); @@ -990,9 +988,9 @@ public class OutlookSynchronizer : WinoSynchronizer x.RemoteFolderId); - var existingMessageIds = new List(); + var messagesDictionary = messagesReturnedByApi.ToDictionary(a => a.Id); - //Download missing messages. - foreach (var message in messagesToDownload) + // Contains a list of message ids that potentially can be downloaded. + List messageIdsWithKnownFolder = []; + + // Validate that all messages are in a known folder. + foreach (var message in messagesReturnedByApi) { - var messageId = message.Id; - var parentFolderId = message.ParentFolderId; - - if (!localFolders.Any(a => a.RemoteFolderId == parentFolderId)) + if (!localFolders.ContainsKey(message.ParentFolderId)) { - Log.Warning($"Search result returned a message from a folder that is not synchronized."); + Log.Warning("Search result returned a message from a folder that is not synchronized."); continue; } - existingMessageIds.Add(messageId); + messageIdsWithKnownFolder.Add(message.Id); + } - var exists = await _outlookChangeProcessor.IsMailExistsAsync(messageId).ConfigureAwait(false); + var locallyExistingMails = await _outlookChangeProcessor.AreMailsExistsAsync(messageIdsWithKnownFolder).ConfigureAwait(false); - if (!exists) - { - // Check if folder exists. We can't download a mail without existing folder. + // Find messages that are not downloaded yet. + List messagesToDownload = []; + foreach (var id in messagesDictionary.Keys.Except(locallyExistingMails)) + { + messagesToDownload.Add(messagesDictionary[id]); + } - var localFolder = localFolders.Find(a => a.RemoteFolderId == parentFolderId); - - await DownloadSearchResultMessageAsync(messageId, localFolder, cancellationToken).ConfigureAwait(false); - } + foreach (var message in messagesToDownload) + { + await DownloadSearchResultMessageAsync(message.Id, localFolders[message.ParentFolderId], cancellationToken).ConfigureAwait(false); } // Get results from database and return. - return await _outlookChangeProcessor.GetMailCopiesAsync(existingMessageIds); + return await _outlookChangeProcessor.GetMailCopiesAsync(messageIdsWithKnownFolder).ConfigureAwait(false); } private async Task DownloadMimeMessageAsync(string messageId, CancellationToken cancellationToken = default) { var mimeContentStream = await _graphClient.Me.Messages[messageId].Content.GetAsync(null, cancellationToken).ConfigureAwait(false); - return await MimeMessage.LoadAsync(mimeContentStream).ConfigureAwait(false); + return await MimeMessage.LoadAsync(mimeContentStream, cancellationToken).ConfigureAwait(false); } public override async Task> CreateNewMailPackagesAsync(Message message, MailItemFolder assignedFolder, CancellationToken cancellationToken = default)