Fixing UI thread issues with bulk operations and request queue refactoring.

This commit is contained in:
Burak Kaan Köse
2026-04-20 02:18:23 +02:00
parent 3bd0b69429
commit 54148716bb
38 changed files with 1644 additions and 206 deletions
+28 -5
View File
@@ -259,25 +259,48 @@ public class SynchronizationManager : ISynchronizationManager, IRecipient<Accoun
/// <param name="accountId">Account ID to queue the request for</param>
/// <param name="triggerSynchronization">Whether to automatically trigger synchronization after queuing the request</param>
public async Task QueueRequestAsync(IRequestBase request, Guid accountId, bool triggerSynchronization)
=> await QueueRequestsAsync([request], accountId, triggerSynchronization).ConfigureAwait(false);
public async Task QueueRequestsAsync(IEnumerable<IRequestBase> requests, Guid accountId, bool triggerSynchronization)
{
EnsureInitialized();
var requestList = requests?.Where(request => request != null).ToList() ?? [];
if (requestList.Count == 0)
return;
var synchronizer = await GetOrCreateSynchronizerAsync(accountId);
if (synchronizer == null)
{
_logger.Error("Could not find or create synchronizer for account {AccountId} to queue request", accountId);
_logger.Error("Could not find or create synchronizer for account {AccountId} to queue {RequestCount} request(s)", accountId, requestList.Count);
return;
}
_logger.Debug("Queuing request {RequestType} for account {AccountId}",
request.GetType().Name, accountId);
if (requestList.Count == 1)
{
_logger.Debug("Queuing request {RequestType} for account {AccountId}",
requestList[0].GetType().Name, accountId);
}
else
{
var requestSummary = string.Join(", ", requestList
.GroupBy(request => request.GetType().Name)
.OrderBy(group => group.Key)
.Select(group => $"{group.Key} x{group.Count()}"));
synchronizer.QueueRequest(request);
_logger.Debug("Queuing {RequestCount} requests for account {AccountId}: {RequestSummary}",
requestList.Count, accountId, requestSummary);
}
foreach (var request in requestList)
{
synchronizer.QueueRequest(request);
}
if (triggerSynchronization)
{
// Determine if this is a calendar or mail operation
bool isCalendarOperation = request is ICalendarActionRequest;
bool isCalendarOperation = requestList.All(request => request is ICalendarActionRequest);
if (isCalendarOperation)
{
+10 -10
View File
@@ -93,13 +93,11 @@ public class WinoRequestDelegator : IWinoRequestDelegator
// Queue requests for each account and start synchronization.
foreach (var accountGroup in accountIds)
{
foreach (var accountRequest in accountGroup)
{
await QueueRequestAsync(accountRequest, accountGroup.Key);
}
var groupedRequests = accountGroup.Cast<IRequestBase>().ToList();
await QueueRequestsAsync(groupedRequests, accountGroup.Key).ConfigureAwait(false);
var account = accountGroup.First().Item.AssignedAccount;
var actionItems = SynchronizationActionHelper.CreateActionItems(accountGroup, accountGroup.Key, account.Name);
var actionItems = SynchronizationActionHelper.CreateActionItems(groupedRequests, accountGroup.Key, account.Name);
if (actionItems.Count > 0)
WeakReferenceMessenger.Default.Send(new SynchronizationActionsAdded(accountGroup.Key, account.Name, actionItems));
@@ -214,10 +212,7 @@ public class WinoRequestDelegator : IWinoRequestDelegator
if (requestList.Count == 0)
return;
foreach (var request in requestList)
{
await QueueRequestAsync(request, accountId).ConfigureAwait(false);
}
await QueueRequestsAsync(requestList, accountId).ConfigureAwait(false);
await SendSyncActionsAddedAsync(requestList, accountId).ConfigureAwait(false);
await QueueSynchronizationAsync(accountId).ConfigureAwait(false);
@@ -274,7 +269,12 @@ public class WinoRequestDelegator : IWinoRequestDelegator
private async Task QueueRequestAsync(IRequestBase request, Guid accountId)
{
// Don't trigger synchronization for individual requests - we'll trigger it once for all requests
await SynchronizationManager.Instance.QueueRequestAsync(request, accountId, triggerSynchronization: false);
await SynchronizationManager.Instance.QueueRequestAsync(request, accountId, triggerSynchronization: false).ConfigureAwait(false);
}
private async Task QueueRequestsAsync(IEnumerable<IRequestBase> requests, Guid accountId)
{
await SynchronizationManager.Instance.QueueRequestsAsync(requests, accountId, triggerSynchronization: false).ConfigureAwait(false);
}
private Task QueueSynchronizationAsync(Guid accountId)
+7 -3
View File
@@ -54,6 +54,10 @@ public class WinoRequestProcessor : IWinoRequestProcessor
{
var action = preperationRequest.Action;
var moveTargetStructure = preperationRequest.MoveTargetFolder;
var mailItems = preperationRequest.MailItems?.Where(item => item != null).ToList() ?? [];
if (mailItems.Count == 0)
return [];
// Ask confirmation for permanent delete operation.
// Drafts are always hard deleted without any protection.
@@ -78,12 +82,12 @@ public class WinoRequestProcessor : IWinoRequestProcessor
// Handle the case when user is trying to move multiple mails that belong to different accounts.
// We can't handle this with only 1 picker dialog.
bool isInvalidMoveTarget = preperationRequest.MailItems.Select(a => a.AssignedAccount.Id).Distinct().Count() > 1;
bool isInvalidMoveTarget = mailItems.Select(a => a.AssignedAccount.Id).Distinct().Count() > 1;
if (isInvalidMoveTarget)
throw new InvalidMoveTargetException(InvalidMoveTargetReason.MultipleAccounts);
var accountId = preperationRequest.MailItems.FirstOrDefault().AssignedAccount.Id;
var accountId = mailItems[0].AssignedAccount.Id;
moveTargetStructure = await _dialogService.PickFolderAsync(accountId, PickFolderReason.Move, _folderService);
@@ -94,7 +98,7 @@ public class WinoRequestProcessor : IWinoRequestProcessor
var requests = new List<IMailActionRequest>();
// TODO: Fix: Collection was modified; enumeration operation may not execute
foreach (var item in preperationRequest.MailItems.ToList())
foreach (var item in mailItems)
{
var singleRequest = await GetSingleRequestAsync(item, action, moveTargetStructure, preperationRequest.ToggleExecution);