Skip to content

Commit

Permalink
Improved outlook online search even more and removed redundant method…
Browse files Browse the repository at this point in the history
…s from ChangeProcessor (#586)
  • Loading branch information
Tiktack authored Feb 24, 2025
1 parent cf8f1ec commit 832b363
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 49 deletions.
17 changes: 0 additions & 17 deletions Wino.Core/Integration/Processors/DefaultChangeProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,6 @@ public interface IDefaultChangeProcessor
Task<bool> MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId);
Task UpdateFolderLastSyncDateAsync(Guid folderId);
Task UpdateRemoteAliasInformationAsync(MailAccount account, List<RemoteAccountAlias> remoteAccountAliases);

/// <summary>
/// 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.
/// </summary>
/// <param name="messageId">MailCopyId of the message.</param>
/// <returns>Whether mail exists or not.</returns>
Task<bool> IsMailExistsAsync(string messageId);

// Calendar
Task<List<AccountCalendar>> GetAccountCalendarsAsync(Guid accountId);

Expand All @@ -60,7 +50,6 @@ public interface IDefaultChangeProcessor
Task UpdateAccountCalendarAsync(AccountCalendar accountCalendar);

Task UpdateCalendarDeltaSynchronizationToken(Guid calendarId, string deltaToken);
Task<MailCopy> GetMailCopyAsync(string mailCopyId);
Task<List<MailCopy>> GetMailCopiesAsync(IEnumerable<string> mailCopyIds);
Task CreateMailRawAsync(MailAccount account, MailItemFolder mailItemFolder, NewMailItemPackage package);
Task DeleteUserMailCacheAsync(Guid accountId);
Expand Down Expand Up @@ -138,15 +127,9 @@ public Task<string> UpdateAccountDeltaSynchronizationIdentifierAsync(Guid accoun
public Task ChangeFlagStatusAsync(string mailCopyId, bool isFlagged)
=> MailService.ChangeFlagStatusAsync(mailCopyId, isFlagged);

public Task<bool> IsMailExistsAsync(string messageId)
=> MailService.IsMailExistsAsync(messageId);

public Task<List<string>> AreMailsExistsAsync(IEnumerable<string> mailCopyIds)
=> MailService.AreMailsExistsAsync(mailCopyIds);

public Task<MailCopy> GetMailCopyAsync(string mailCopyId)
=> MailService.GetSingleMailItemAsync(mailCopyId);

public Task<List<MailCopy>> GetMailCopiesAsync(IEnumerable<string> mailCopyIds)
=> MailService.GetMailItemsAsync(mailCopyIds);

Expand Down
63 changes: 31 additions & 32 deletions Wino.Core/Synchronizers/OutlookSynchronizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -967,12 +967,10 @@ public override async Task ExecuteNativeRequestsAsync(List<IRequestBundle<Reques

public override async Task<List<MailCopy>> OnlineSearchAsync(string queryText, List<IMailItemFolder> folders, CancellationToken cancellationToken = default)
{
bool isFoldersIncluded = folders?.Any() ?? false;

var messagesToDownload = new List<Message>();
List<Message> messagesReturnedByApi = [];

// Perform search for each folder separately.
if (isFoldersIncluded)
if (folders?.Count > 0)
{
var folderIds = folders.Select(a => a.RemoteFolderId);

Expand All @@ -990,9 +988,9 @@ public override async Task<List<MailCopy>> OnlineSearchAsync(string queryText, L

if (result?.Value != null)
{
lock (messagesToDownload)
lock (messagesReturnedByApi)
{
messagesToDownload.AddRange(result.Value);
messagesReturnedByApi.AddRange(result.Value);
}
}
});
Expand All @@ -1008,59 +1006,60 @@ public override async Task<List<MailCopy>> OnlineSearchAsync(string queryText, L
requestConfig.QueryParameters.Search = $"\"{queryText}\"";
requestConfig.QueryParameters.Select = ["Id, ParentFolderId"];
requestConfig.QueryParameters.Top = 1000;
});
}, cancellationToken);

var result = await mailQuery;

if (result?.Value != null)
{
lock (messagesToDownload)
{
messagesToDownload.AddRange(result.Value);
}
messagesReturnedByApi.AddRange(result.Value);
}
}

// Do not download messages that exists, but return them for listing.
if (messagesReturnedByApi.Count == 0) return [];

var localFolders = await _outlookChangeProcessor.GetLocalFoldersAsync(Account.Id).ConfigureAwait(false);
var localFolders = (await _outlookChangeProcessor.GetLocalFoldersAsync(Account.Id).ConfigureAwait(false))
.ToDictionary(x => x.RemoteFolderId);

var existingMessageIds = new List<string>();
var messagesDictionary = messagesReturnedByApi.ToDictionary(a => a.Id);

//Download missing messages.
foreach (var message in messagesToDownload)
{
var messageId = message.Id;
var parentFolderId = message.ParentFolderId;
// Contains a list of message ids that potentially can be downloaded.
List<string> messageIdsWithKnownFolder = [];

if (!localFolders.Any(a => a.RemoteFolderId == parentFolderId))
// Validate that all messages are in a known folder.
foreach (var message in messagesReturnedByApi)
{
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);

var exists = await _outlookChangeProcessor.IsMailExistsAsync(messageId).ConfigureAwait(false);
messageIdsWithKnownFolder.Add(message.Id);
}

if (!exists)
{
// Check if folder exists. We can't download a mail without existing folder.
var locallyExistingMails = await _outlookChangeProcessor.AreMailsExistsAsync(messageIdsWithKnownFolder).ConfigureAwait(false);

var localFolder = localFolders.Find(a => a.RemoteFolderId == parentFolderId);
// Find messages that are not downloaded yet.
List<Message> messagesToDownload = [];
foreach (var id in messagesDictionary.Keys.Except(locallyExistingMails))
{
messagesToDownload.Add(messagesDictionary[id]);
}

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<MimeMessage> 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<List<NewMailItemPackage>> CreateNewMailPackagesAsync(Message message, MailItemFolder assignedFolder, CancellationToken cancellationToken = default)
Expand Down

0 comments on commit 832b363

Please sign in to comment.