Fixing outlook attachments, re-using compose page and some additional fixes on the mime headers for outlook.

This commit is contained in:
Burak Kaan Köse
2026-02-07 13:10:57 +01:00
parent 1ec8d5bbf2
commit d28de50ec6
10 changed files with 234 additions and 145 deletions
+34 -1
View File
@@ -21,11 +21,13 @@ using Wino.Core.Domain.Models.Navigation;
using Wino.Core.Extensions;
using Wino.Core.Services;
using Wino.Mail.ViewModels.Data;
using Wino.Mail.ViewModels.Messages;
using Wino.Messaging.Client.Mails;
namespace Wino.Mail.ViewModels;
public partial class ComposePageViewModel : MailBaseViewModel
public partial class ComposePageViewModel : MailBaseViewModel,
IRecipient<NewComposeDraftItemRequestedEvent>
{
public Func<Task<string>> GetHTMLBodyFunction;
@@ -432,6 +434,35 @@ public partial class ComposePageViewModel : MailBaseViewModel
}
}
public async void Receive(NewComposeDraftItemRequestedEvent message)
{
// Save current draft before switching.
await UpdateMimeChangesAsync();
// Reset state for the new draft.
isUpdatingMimeBlocked = false;
ComposingAccount = null;
IncludedAttachments.Clear();
// Set the new draft item and prepare it.
CurrentMailDraftItem = message.MailItemViewModel;
await TryPrepareComposeAsync(true);
}
protected override void RegisterRecipients()
{
base.RegisterRecipients();
Messenger.Register<NewComposeDraftItemRequestedEvent>(this);
}
protected override void UnregisterRecipients()
{
base.UnregisterRecipients();
Messenger.Unregister<NewComposeDraftItemRequestedEvent>(this);
}
private async Task<bool> InitializeComposerAccountAsync()
{
if (CurrentMailDraftItem == null) return false;
@@ -550,6 +581,8 @@ public partial class ComposePageViewModel : MailBaseViewModel
{
if (CurrentMimeMessage == null) return;
IncludedAttachments.Clear();
foreach (var attachment in CurrentMimeMessage.Attachments)
{
if (attachment.IsAttachment && attachment is MimePart attachmentPart)
@@ -635,6 +635,56 @@ public partial class MailListPageViewModel : MailBaseViewModel,
if (isFromDraftOrSentFolder)
{
// Fix for draft duplication: When a draft is created for reply/forward, it's first added as local draft.
// Then the server sync fetches it back. We should skip adding remote drafts if a local draft already exists
// with the same ThreadId. The mapping system (DraftMapped) will handle updating the existing local draft.
if (addedMail.IsDraft && !addedMail.IsLocalDraft && !string.IsNullOrEmpty(addedMail.ThreadId))
{
// Check if collection already has a local draft with the same ThreadId in the same folder
bool hasLocalDraftInSameThread = false;
foreach (var group in MailCollection.MailItems)
{
foreach (var item in group)
{
if (item is MailItemViewModel mailItem)
{
if (mailItem.IsDraft &&
mailItem.MailCopy.IsLocalDraft &&
mailItem.MailCopy.ThreadId == addedMail.ThreadId &&
mailItem.MailCopy.FolderId == addedMail.FolderId)
{
hasLocalDraftInSameThread = true;
break;
}
}
else if (item is ThreadMailItemViewModel threadItem)
{
foreach (var threadEmail in threadItem.ThreadEmails)
{
if (threadEmail.IsDraft &&
threadEmail.MailCopy.IsLocalDraft &&
threadEmail.MailCopy.ThreadId == addedMail.ThreadId &&
threadEmail.MailCopy.FolderId == addedMail.FolderId)
{
hasLocalDraftInSameThread = true;
break;
}
}
if (hasLocalDraftInSameThread) break;
}
}
if (hasLocalDraftInSameThread) break;
}
if (hasLocalDraftInSameThread)
{
// Local draft exists in the same thread - skip adding remote duplicate
// The mapping system will update the local draft with remote IDs when DraftMapped message is received
return;
}
}
// Only add if the ThreadId exists in the collection (can be threaded with existing items)
if (!ThreadIdExistsInCollection(addedMail)) return;
}
@@ -757,6 +807,17 @@ public partial class MailListPageViewModel : MailBaseViewModel,
}
}
protected override void OnDraftMapped(string localDraftCopyId, string remoteDraftCopyId)
{
base.OnDraftMapped(localDraftCopyId, remoteDraftCopyId);
// When a draft is mapped from local to remote, the database has been updated
// but the UI collection still references the MailCopy object with old IDs.
// The MailCollection.AddAsync method checks UniqueId (which doesn't change during mapping)
// so if mapping worked correctly, no duplicate should appear.
// This method is here for future enhancements if additional UI updates are needed.
}
private async Task<List<MailItemViewModel>> PrepareMailViewModelsAsync(IEnumerable<MailCopy> mailItems, CancellationToken cancellationToken = default)
{
// Run ViewModel creation on background thread to avoid blocking UI
@@ -0,0 +1,10 @@
using Wino.Mail.ViewModels.Data;
namespace Wino.Mail.ViewModels.Messages;
/// <summary>
/// When the compose page is already active, but a different draft item is selected.
/// To not trigger navigation again and re-use existing WebView2 editor.
/// </summary>
/// <param name="MailItemViewModel">The new draft mail item to compose.</param>
public record NewComposeDraftItemRequestedEvent(MailItemViewModel MailItemViewModel);