Fix online search dedupe and pane layout scrolling

This commit is contained in:
Burak Kaan Köse
2026-04-12 15:56:27 +02:00
parent 4d04595d0a
commit d922dd2f2e
9 changed files with 318 additions and 35 deletions
+8 -2
View File
@@ -1390,6 +1390,12 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
static bool IsArchiveFolder(IMailItemFolder folder)
=> folder?.SpecialFolderType == SpecialFolderType.Archive || folder?.RemoteFolderId == ServiceConstants.ARCHIVE_LABEL_ID;
var distinctFolders = folders?
.Where(folder => folder != null)
.GroupBy(folder => folder.Id)
.Select(group => group.First())
.ToList();
var messageIds = new HashSet<string>(StringComparer.Ordinal);
async Task CollectMessageIdsAsync(UsersResource.MessagesResource.ListRequest request)
@@ -1421,7 +1427,7 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
bool hasScopedQuery = queryText.StartsWith("label:", StringComparison.OrdinalIgnoreCase) ||
queryText.StartsWith("in:", StringComparison.OrdinalIgnoreCase);
if (hasScopedQuery || folders?.Count == 0)
if (hasScopedQuery || distinctFolders?.Count == 0)
{
var request = _gmailService.Users.Messages.List("me");
request.Q = queryText;
@@ -1431,7 +1437,7 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
}
else
{
foreach (var folder in folders)
foreach (var folder in distinctFolders)
{
cancellationToken.ThrowIfCancellationRequested();
+8 -3
View File
@@ -1053,10 +1053,15 @@ public class ImapSynchronizer : WinoSynchronizer<ImapRequest, ImapMessageCreatio
{
client = await _clientPool.GetClientAsync().ConfigureAwait(false);
List<MailCopy> searchResults = [];
List<string> searchResultFolderMailUids = [];
var distinctFolders = folders?
.Where(folder => folder != null)
.GroupBy(folder => folder.Id)
.Select(group => group.First())
.ToList() ?? [];
foreach (var folder in folders)
HashSet<string> searchResultFolderMailUids = new(StringComparer.Ordinal);
foreach (var folder in distinctFolders)
{
if (folder is not MailItemFolder localFolder)
continue;
+22 -5
View File
@@ -267,18 +267,32 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
return MailSynchronizationResult.CompletedWithFolderResults(unreadNewItems, folderResults);
}
public async Task DownloadSearchResultMessageAsync(string messageId, MailItemFolder assignedFolder, CancellationToken cancellationToken = default)
public Task DownloadSearchResultMessageAsync(string messageId, MailItemFolder assignedFolder, CancellationToken cancellationToken = default)
=> DownloadSearchResultMessageAsync(messageId, assignedFolder, existingMessageIds: null, cancellationToken);
private async Task DownloadSearchResultMessageAsync(string messageId,
MailItemFolder assignedFolder,
ISet<string> existingMessageIds,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(messageId) || assignedFolder == null) return;
// Online search can return the same message across repeated invocations/races.
// Guard before network+MIME download and before database insert.
var existing = await _outlookChangeProcessor.AreMailsExistsAsync([messageId]).ConfigureAwait(false);
if (existing.Contains(messageId))
if (existingMessageIds?.Contains(messageId) == true)
{
return;
}
if (existingMessageIds == null)
{
var existing = await _outlookChangeProcessor.AreMailsExistsAsync([messageId]).ConfigureAwait(false);
if (existing.Contains(messageId))
{
return;
}
}
Log.Information("Downloading search result message {messageId} for {Name} - {FolderName}", messageId, Account.Name, assignedFolder.FolderName);
// Outlook message handling was a little strange.
@@ -314,6 +328,8 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
// Use safe upsert path to avoid duplicate rows when message already exists.
await _outlookChangeProcessor.CreateMailAsync(Account.Id, package).ConfigureAwait(false);
}
existingMessageIds?.Add(messageId);
}
private async Task<IEnumerable<string>> SynchronizeFolderAsync(MailItemFolder folder, CancellationToken cancellationToken = default)
@@ -2226,10 +2242,11 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
if (messageIdsWithKnownFolder.Count == 0) return [];
var locallyExistingMails = await _outlookChangeProcessor.AreMailsExistsAsync(messageIdsWithKnownFolder).ConfigureAwait(false);
var existingMessageIds = new HashSet<string>(locallyExistingMails, StringComparer.Ordinal);
// Find messages that are not downloaded yet.
List<Message> messagesToDownload = [];
foreach (var id in messageIdsWithKnownFolder.Except(locallyExistingMails, StringComparer.Ordinal))
foreach (var id in messageIdsWithKnownFolder.Except(existingMessageIds, StringComparer.Ordinal))
{
if (messagesById.TryGetValue(id, out var message))
{
@@ -2239,7 +2256,7 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
foreach (var message in messagesToDownload)
{
await DownloadSearchResultMessageAsync(message.Id, localFolders[message.ParentFolderId], cancellationToken).ConfigureAwait(false);
await DownloadSearchResultMessageAsync(message.Id, localFolders[message.ParentFolderId], existingMessageIds, cancellationToken).ConfigureAwait(false);
}
// Get results from database and return.