Dispose mail webviews when closing the shell

This commit is contained in:
Burak Kaan Köse
2026-04-20 19:40:32 +02:00
parent d85812ed7b
commit 2b1676a4f7
7 changed files with 129 additions and 12 deletions
@@ -0,0 +1,8 @@
namespace Wino.Core.Domain.Enums;
public enum ThreeButtonDialogResult
{
Primary,
Secondary,
Cancel
}
@@ -19,6 +19,12 @@ public interface IMailDialogService : IDialogServiceBase
{
void ShowReadOnlyCalendarMessage();
Task<bool> ShowHardDeleteConfirmationAsync();
Task<ThreeButtonDialogResult> ShowThreeButtonDialogAsync(string title,
string description,
string primaryButtonText,
string secondaryButtonText,
string cancelButtonText,
WinoCustomMessageDialogIcon? icon = null);
Task HandleSystemFolderConfigurationDialogAsync(Guid accountId, IFolderService folderService);
// Custom dialogs
@@ -305,6 +305,8 @@
"DialogMessage_DeleteRecurringSeriesTitle": "Delete Recurring Series",
"DialogMessage_DiscardDraftConfirmationMessage": "This draft will be discarded. Do you want to continue?",
"DialogMessage_DiscardDraftConfirmationTitle": "Discard Draft",
"DialogMessage_CloseDraftWindowConfirmationMessage": "A draft is still open. Save it before closing the window?",
"DialogMessage_CloseDraftWindowConfirmationTitle": "Close Window",
"DialogMessage_EmptySubjectConfirmation": "Missing Subject",
"DialogMessage_EmptySubjectConfirmationMessage": "Message has no subject. Do you want to continue?",
"DialogMessage_EnableStartupLaunchDeniedMessage": "You can enable startup launch from Settings -> App Preferences.",
+21 -5
View File
@@ -488,6 +488,12 @@ public partial class ComposePageViewModel : MailBaseViewModel,
[RelayCommand(CanExecute = nameof(canSendMail))]
private async Task DiscardAsync()
=> await DiscardDraftAsync();
public Task SaveDraftAsync()
=> UpdateMimeChangesAsync();
public async Task DiscardDraftAsync(bool requireConfirmation = true)
{
if (ComposingAccount == null)
{
@@ -495,14 +501,19 @@ public partial class ComposePageViewModel : MailBaseViewModel,
return;
}
var confirmation = await _dialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_DiscardDraftConfirmationMessage,
Translator.DialogMessage_DiscardDraftConfirmationTitle,
Translator.Buttons_Yes);
var confirmation = !requireConfirmation || await _dialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_DiscardDraftConfirmationMessage,
Translator.DialogMessage_DiscardDraftConfirmationTitle,
Translator.Buttons_Yes);
if (confirmation)
if (!confirmation)
{
isUpdatingMimeBlocked = true;
return;
}
isUpdatingMimeBlocked = true;
try
{
// Don't send delete request for local drafts. Just delete the record and mime locally.
if (CurrentMailDraftItem.MailCopy.IsLocalDraft)
{
@@ -514,6 +525,11 @@ public partial class ComposePageViewModel : MailBaseViewModel,
await _worker.ExecuteAsync(deletePackage).ConfigureAwait(false);
}
}
catch
{
isUpdatingMimeBlocked = false;
throw;
}
}
//public override void OnNavigatedFrom(NavigationMode mode, object parameters)
+35
View File
@@ -137,6 +137,41 @@ public class DialogService : DialogServiceBase, IMailDialogService
WinoCustomMessageDialogIcon.Warning,
Translator.Buttons_No);
public async Task<ThreeButtonDialogResult> ShowThreeButtonDialogAsync(string title,
string description,
string primaryButtonText,
string secondaryButtonText,
string cancelButtonText,
WinoCustomMessageDialogIcon? icon = null)
{
var informationContainer = new CustomMessageDialogInformationContainer(
title,
description,
icon ?? WinoCustomMessageDialogIcon.Information,
false);
var dialog = new ContentDialog
{
Style = ApplicationResourceManager.GetResource<Style>("WinoDialogStyle"),
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme(),
DefaultButton = ContentDialogButton.Primary,
PrimaryButtonText = primaryButtonText,
SecondaryButtonText = secondaryButtonText,
CloseButtonText = cancelButtonText,
ContentTemplate = ApplicationResourceManager.GetResource<DataTemplate>("CustomWinoContentDialogContentTemplate"),
Content = informationContainer
};
var dialogResult = await HandleDialogPresentationAsync(dialog);
return dialogResult switch
{
ContentDialogResult.Primary => ThreeButtonDialogResult.Primary,
ContentDialogResult.Secondary => ThreeButtonDialogResult.Secondary,
_ => ThreeButtonDialogResult.Cancel
};
}
public async Task<MailAccount> ShowAccountPickerDialogAsync(List<MailAccount> availableAccounts)
{
var accountPicker = new AccountPickerDialog(availableAccounts)
+57 -1
View File
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.UI;
@@ -20,8 +21,10 @@ using Wino.Mail.WinUI.Helpers;
using Wino.Mail.WinUI.Interfaces;
using Wino.Mail.WinUI.Models;
using Wino.Mail.WinUI.Views;
using Wino.Messaging.Client.Mails;
using Wino.Messaging.Client.Shell;
using Wino.Messaging.UI;
using Wino.Views.Mail;
using WinUIEx;
namespace Wino.Mail.WinUI;
@@ -356,12 +359,16 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow,
SynchronizeTitleBarSearchBox();
}
private void OnAppWindowClosing(object sender, Microsoft.UI.Windowing.AppWindowClosingEventArgs e)
private async void OnAppWindowClosing(object sender, AppWindowClosingEventArgs e)
{
if (_allowClose || (Application.Current as App)?.IsExiting == true)
return;
e.Cancel = true;
if (!await PrepareMailModeForHideAsync())
return;
var windowManager = WinoApplication.Current.Services.GetService<IWinoWindowManager>();
windowManager?.HideWindow(this);
}
@@ -398,6 +405,55 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow,
UnregisterRecipients();
}
private async Task<bool> PrepareMailModeForHideAsync()
{
if (MainShellFrame.Content is not WinoAppShell shellPage)
return true;
if (shellPage.GetShellFrame().Content is not MailListPage mailListPage)
return true;
var renderingFrame = mailListPage.FindName("RenderingFrame") as Frame;
if (renderingFrame?.Content is ComposePage composePage)
{
var closeResult = await MailDialogService.ShowThreeButtonDialogAsync(
Translator.DialogMessage_CloseDraftWindowConfirmationTitle,
Translator.DialogMessage_CloseDraftWindowConfirmationMessage,
Translator.Buttons_Save,
Translator.Buttons_Discard,
Translator.Buttons_Cancel,
WinoCustomMessageDialogIcon.Warning);
if (closeResult == ThreeButtonDialogResult.Cancel)
{
return false;
}
try
{
if (closeResult == ThreeButtonDialogResult.Primary)
{
await composePage.ViewModel.SaveDraftAsync();
}
else
{
await composePage.ViewModel.DiscardDraftAsync(requireConfirmation: false);
}
}
catch (Exception ex)
{
MailDialogService.InfoBarMessage(Translator.GeneralTitle_Error, ex.Message, InfoBarMessageType.Error);
return false;
}
}
await mailListPage.ViewModel.MailCollection.UnselectAllAsync();
WeakReferenceMessenger.Default.Send(new DisposeRenderingFrameRequested());
return true;
}
private void RegisterRecipients()
{
WeakReferenceMessenger.Default.Register<TitleBarShellContentUpdated>(this);
@@ -56,7 +56,6 @@ public sealed partial class MailListPage : MailListPageAbstract,
{
private const double RENDERING_COLUMN_MIN_WIDTH = 375;
private const int SELECTION_SETTLE_DELAY_MS = 120;
private const int RENDERING_FRAME_RELEASE_DELAY_MS = 2000;
private int _idleNavigationRequestVersion = 0;
private int _mailActivationRequestVersion = 0;
private IPopoutClient? _activePopoutClient;
@@ -546,11 +545,6 @@ public sealed partial class MailListPage : MailListPageAbstract,
}
}
await Task.Delay(RENDERING_FRAME_RELEASE_DELAY_MS);
if (requestVersion != _idleNavigationRequestVersion) return;
if (ViewModel.MailCollection.SelectedItemsCount != 0) return;
// Ensure rendering frame actually navigates away from Compose/Rendering pages.
// Otherwise those pages keep their messenger registrations alive.
ViewModel.NavigationService.Navigate(WinoPage.IdlePage, null, NavigationReferenceFrame.RenderingFrame, NavigationTransitionType.DrillIn);