Some experiments.

This commit is contained in:
Burak Kaan Köse
2026-01-27 20:37:18 +01:00
parent 31097e42a9
commit b343152f14
7 changed files with 123 additions and 11 deletions
+9 -1
View File
@@ -1309,6 +1309,14 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
public override async Task ExecuteNativeRequestsAsync(List<IRequestBundle<IClientServiceRequest>> batchedRequests,
CancellationToken cancellationToken = default)
{
// First apply all UI changes immediately before any batching.
// This ensures UI reflects changes right away, regardless of batch processing.
foreach (var bundle in batchedRequests)
{
bundle.UIChangeRequest?.ApplyUIChanges();
}
// Now batch and execute the network requests.
var batchedBundles = batchedRequests.Batch((int)MaximumAllowedBatchRequestSize);
var bundleCount = batchedBundles.Count();
@@ -1325,7 +1333,7 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
for (int k = 0; k < bundleRequestCount; k++)
{
var requestBundle = bundle.ElementAt(k);
requestBundle.UIChangeRequest?.ApplyUIChanges();
// UI changes are already applied above before batching.
nativeBatchRequest.Queue<object>(requestBundle.NativeRequest, (content, error, index, message)
=> bundleTasks.Add(ProcessSingleNativeRequestResponseAsync(requestBundle, error, message, cancellationToken)));
@@ -1506,6 +1506,14 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
public override async Task ExecuteNativeRequestsAsync(List<IRequestBundle<RequestInformation>> batchedRequests, CancellationToken cancellationToken = default)
{
// First apply all UI changes immediately before any batching.
// This ensures UI reflects changes right away, regardless of batch processing.
foreach (var bundle in batchedRequests)
{
bundle.UIChangeRequest?.ApplyUIChanges();
}
// Now batch and execute the network requests.
var batchedGroups = batchedRequests.Batch((int)MaximumAllowedBatchRequestSize);
foreach (var batch in batchedGroups)
@@ -1542,7 +1550,7 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
var bundle = batch.ElementAt(i);
requiresSerial |= bundle.UIChangeRequest is SendDraftRequest;
bundle.UIChangeRequest?.ApplyUIChanges();
// UI changes are already applied in ExecuteNativeRequestsAsync before batching.
var batchRequestId = await batchContent.AddBatchRequestStepAsync(bundle.NativeRequest);
bundle.BundleId = batchRequestId;
bundleIdMap[batchRequestId] = bundle;
@@ -444,7 +444,7 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
await ExecuteUIThread(() =>
{
existingItem.MailCopy = updatedItem;
existingItem.UpdateFrom(updatedItem);
});
UpdateUniqueIdHashes(existingItem, true);
@@ -574,7 +574,7 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
foreach (var (existing, updated) in itemsToUpdate)
{
UpdateUniqueIdHashes(existing, false);
existing.MailCopy = updated;
existing.UpdateFrom(updated);
UpdateUniqueIdHashes(existing, true);
}
});
@@ -720,7 +720,7 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
}
/// <summary>
/// Fins the item container that updated mail copy belongs to and updates it.
/// Finds the item container that updated mail copy belongs to and updates it.
/// </summary>
/// <param name="updatedMailCopy">Updated mail copy.</param>
/// <returns></returns>
@@ -739,8 +739,9 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
{
UpdateUniqueIdHashes(itemContainer.ItemViewModel, false);
// Update the MailCopy - this will automatically notify all dependent properties
itemContainer.ItemViewModel.MailCopy = updatedMailCopy;
// Update the MailCopy using UpdateFrom to properly notify all XAML bindings
// This maintains reference integrity and ensures PropertyChanged is raised for all properties
itemContainer.ItemViewModel.UpdateFrom(updatedMailCopy);
UpdateUniqueIdHashes(itemContainer.ItemViewModel, true);
}
@@ -748,7 +749,7 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
// Trigger thread property notifications if this item is in a thread
if (itemContainer.ThreadViewModel != null)
{
itemContainer.ThreadViewModel.ThreadEmails = itemContainer.ThreadViewModel.ThreadEmails;
itemContainer.ThreadViewModel.NotifyMailItemUpdated(itemContainer.ItemViewModel);
}
});
}
+1 -1
View File
@@ -643,7 +643,7 @@ public partial class ComposePageViewModel : MailBaseViewModel
{
await ExecuteUIThread(() =>
{
CurrentMailDraftItem.MailCopy = updatedMail;
CurrentMailDraftItem.UpdateFrom(updatedMail);
DiscardCommand.NotifyCanExecuteChanged();
SendCommand.NotifyCanExecuteChanged();
});
@@ -184,4 +184,67 @@ public partial class MailItemViewModel(MailCopy mailCopy) : ObservableRecipient,
yield return this;
}
}
/// <summary>
/// Updates the MailCopy with new data and notifies all bound properties.
/// This method copies values from the source to the existing MailCopy to maintain reference integrity,
/// then explicitly raises PropertyChanged for all dependent properties.
/// </summary>
/// <param name="source">The source MailCopy with updated values.</param>
public void UpdateFrom(MailCopy source)
{
if (source == null) return;
// Update the underlying MailCopy properties directly to maintain reference integrity
// This is important because other parts of the app may hold references to this MailCopy
// Note: UniqueId is the primary key and should match - we don't update it
MailCopy.Id = source.Id;
MailCopy.FolderId = source.FolderId;
MailCopy.ThreadId = source.ThreadId;
MailCopy.MessageId = source.MessageId;
MailCopy.References = source.References;
MailCopy.InReplyTo = source.InReplyTo;
MailCopy.IsDraft = source.IsDraft;
MailCopy.DraftId = source.DraftId;
MailCopy.CreationDate = source.CreationDate;
MailCopy.Subject = source.Subject;
MailCopy.PreviewText = source.PreviewText;
MailCopy.FromName = source.FromName;
MailCopy.FromAddress = source.FromAddress;
MailCopy.HasAttachments = source.HasAttachments;
MailCopy.Importance = source.Importance;
MailCopy.IsRead = source.IsRead;
MailCopy.IsFlagged = source.IsFlagged;
MailCopy.IsFocused = source.IsFocused;
MailCopy.FileId = source.FileId;
MailCopy.ItemType = source.ItemType;
MailCopy.SenderContact = source.SenderContact;
MailCopy.AssignedAccount = source.AssignedAccount;
MailCopy.AssignedFolder = source.AssignedFolder;
// Raise PropertyChanged for all properties that XAML may bind to
OnPropertyChanged(nameof(CreationDate));
OnPropertyChanged(nameof(IsFlagged));
OnPropertyChanged(nameof(FromName));
OnPropertyChanged(nameof(IsFocused));
OnPropertyChanged(nameof(IsRead));
OnPropertyChanged(nameof(IsDraft));
OnPropertyChanged(nameof(DraftId));
OnPropertyChanged(nameof(Id));
OnPropertyChanged(nameof(Subject));
OnPropertyChanged(nameof(PreviewText));
OnPropertyChanged(nameof(FromAddress));
OnPropertyChanged(nameof(HasAttachments));
OnPropertyChanged(nameof(Importance));
OnPropertyChanged(nameof(ThreadId));
OnPropertyChanged(nameof(MessageId));
OnPropertyChanged(nameof(References));
OnPropertyChanged(nameof(InReplyTo));
OnPropertyChanged(nameof(FileId));
OnPropertyChanged(nameof(FolderId));
OnPropertyChanged(nameof(UniqueId));
OnPropertyChanged(nameof(Base64ContactPicture));
OnPropertyChanged(nameof(SortingDate));
OnPropertyChanged(nameof(SortingName));
}
}
@@ -213,6 +213,40 @@ public partial class ThreadMailItemViewModel : ObservableRecipient, IMailListIte
}
}
/// <summary>
/// Notifies that a mail item within this thread has been updated.
/// This raises PropertyChanged for all thread-level computed properties that depend on child items.
/// </summary>
/// <param name="updatedMailItem">The mail item that was updated (can be null to refresh all).</param>
public void NotifyMailItemUpdated(MailItemViewModel updatedMailItem)
{
// Raise PropertyChanged for all computed properties that depend on ThreadEmails contents
OnPropertyChanged(nameof(Subject));
OnPropertyChanged(nameof(FromName));
OnPropertyChanged(nameof(CreationDate));
OnPropertyChanged(nameof(FromAddress));
OnPropertyChanged(nameof(PreviewText));
OnPropertyChanged(nameof(HasAttachments));
OnPropertyChanged(nameof(IsFlagged));
OnPropertyChanged(nameof(IsFocused));
OnPropertyChanged(nameof(IsRead));
OnPropertyChanged(nameof(IsDraft));
OnPropertyChanged(nameof(DraftId));
OnPropertyChanged(nameof(Id));
OnPropertyChanged(nameof(Importance));
OnPropertyChanged(nameof(ThreadId));
OnPropertyChanged(nameof(MessageId));
OnPropertyChanged(nameof(References));
OnPropertyChanged(nameof(InReplyTo));
OnPropertyChanged(nameof(FileId));
OnPropertyChanged(nameof(FolderId));
OnPropertyChanged(nameof(UniqueId));
OnPropertyChanged(nameof(Base64ContactPicture));
OnPropertyChanged(nameof(ThumbnailUpdatedEvent));
OnPropertyChanged(nameof(SortingDate));
OnPropertyChanged(nameof(SortingName));
}
/// <summary>
/// Checks if this thread contains an email with the specified unique ID
/// </summary>
@@ -668,8 +668,6 @@ public partial class MailListPageViewModel : MailBaseViewModel,
{
base.OnMailUpdated(updatedMail);
Debug.WriteLine($"Updating {updatedMail.Id}-> {updatedMail.UniqueId}");
await MailCollection.UpdateMailCopy(updatedMail);
await ExecuteUIThread(() => { SetupTopBarActions(); });