From 76e3b7289e682f6700fb6f629c56afc92b42160c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Sun, 1 Mar 2026 09:47:05 +0100 Subject: [PATCH] Some issues with changing the app mode and notifications have been fixed. --- Wino.Mail.WinUI/App.xaml.cs | 43 ++++++++++++--- Wino.Mail.WinUI/CoreUWPContainerSetup.cs | 2 + Wino.Mail.WinUI/Dispatcher.cs | 30 ++++++++++- Wino.Mail.WinUI/Services/NavigationService.cs | 52 ++++++++++++++++--- .../Views/Mail/MailListPage.xaml.cs | 7 ++- 5 files changed, 117 insertions(+), 17 deletions(-) diff --git a/Wino.Mail.WinUI/App.xaml.cs b/Wino.Mail.WinUI/App.xaml.cs index b303ec45..3f7286bb 100644 --- a/Wino.Mail.WinUI/App.xaml.cs +++ b/Wino.Mail.WinUI/App.xaml.cs @@ -31,6 +31,7 @@ using Wino.Messaging.Client.Accounts; using Wino.Messaging.Server; using Wino.Messaging.UI; using Wino.Services; +using WinUIEx; namespace Wino.Mail.WinUI; public partial class App : WinoApplication, @@ -186,8 +187,13 @@ public partial class App : WinoApplication, } } - private async void AppNotificationInvoked(AppNotificationManager sender, AppNotificationActivatedEventArgs args) - => await HandleToastActivationAsync(args); + private void AppNotificationInvoked(AppNotificationManager sender, AppNotificationActivatedEventArgs args) + { + if (MainWindow?.DispatcherQueue?.TryEnqueue(() => _ = HandleToastActivationAsync(args)) == true) + return; + + _ = HandleToastActivationAsync(args); + } private void TryRegisterAppNotifications() { @@ -247,7 +253,7 @@ public partial class App : WinoApplication, var calendarService = Services.GetRequiredService(); var navigationService = Services.GetRequiredService(); - var calendarItem = await calendarService.GetCalendarItemAsync(calendarItemId).ConfigureAwait(false); + var calendarItem = await calendarService.GetCalendarItemAsync(calendarItemId); if (calendarItem == null) return; @@ -259,8 +265,7 @@ public partial class App : WinoApplication, } else { - MainWindow?.BringToFront(); - MainWindow?.Activate(); + EnsureMainWindowVisibleAndForeground(); } navigationService.ChangeApplicationMode(Core.Domain.Enums.WinoApplicationMode.Calendar); @@ -276,10 +281,10 @@ public partial class App : WinoApplication, var mailService = Services.GetRequiredService(); var navigationService = Services.GetRequiredService(); - var account = await mailService.GetMailAccountByUniqueIdAsync(mailItemUniqueId).ConfigureAwait(false); + var account = await mailService.GetMailAccountByUniqueIdAsync(mailItemUniqueId); if (account == null) return; - var mailItem = await mailService.GetSingleMailItemAsync(mailItemUniqueId).ConfigureAwait(false); + var mailItem = await mailService.GetSingleMailItemAsync(mailItemUniqueId); if (mailItem == null) return; var message = new AccountMenuItemExtended(mailItem.AssignedFolder.Id, mailItem); @@ -300,7 +305,7 @@ public partial class App : WinoApplication, // App is already running - send message and bring window to front. navigationService.ChangeApplicationMode(Core.Domain.Enums.WinoApplicationMode.Mail); WeakReferenceMessenger.Default.Send(message); - MainWindow?.BringToFront(); + EnsureMainWindowVisibleAndForeground(); } } @@ -418,6 +423,7 @@ public partial class App : WinoApplication, LogActivation("Creating main window."); MainWindow = new ShellWindow(); + InitializeNavigationDispatcher(); var nativeAppService = Services.GetRequiredService(); nativeAppService.GetCoreWindowHwnd = () => WinRT.Interop.WindowNative.GetWindowHandle(MainWindow); @@ -450,6 +456,27 @@ public partial class App : WinoApplication, shellWindow.HandleAppActivation(args?.Arguments, GetCurrentLaunchTileId(), Environment.CommandLine); } + private void InitializeNavigationDispatcher() + { + if (MainWindow == null) + return; + + if (Services.GetService() is WinUIDispatcher dispatcher) + { + dispatcher.Initialize(MainWindow.DispatcherQueue); + } + } + + private void EnsureMainWindowVisibleAndForeground() + { + if (MainWindow == null) + return; + + MainWindow.Show(); + MainWindow.BringToFront(); + MainWindow.Activate(); + } + private void RegisterRecipients() { WeakReferenceMessenger.Default.Register(this); diff --git a/Wino.Mail.WinUI/CoreUWPContainerSetup.cs b/Wino.Mail.WinUI/CoreUWPContainerSetup.cs index 7c7ca562..6cbae79b 100644 --- a/Wino.Mail.WinUI/CoreUWPContainerSetup.cs +++ b/Wino.Mail.WinUI/CoreUWPContainerSetup.cs @@ -14,6 +14,8 @@ public static class CoreUWPContainerSetup public static void RegisterCoreUWPServices(this IServiceCollection services) { services.AddSingleton, ApplicationResourceManager>(); + services.AddSingleton(); + services.AddSingleton(provider => provider.GetRequiredService()); services.AddSingleton(); services.AddSingleton(); diff --git a/Wino.Mail.WinUI/Dispatcher.cs b/Wino.Mail.WinUI/Dispatcher.cs index 1bd5848f..82532d6d 100644 --- a/Wino.Mail.WinUI/Dispatcher.cs +++ b/Wino.Mail.WinUI/Dispatcher.cs @@ -8,12 +8,38 @@ namespace Wino.Mail.WinUI; public class WinUIDispatcher : IDispatcher { - private readonly DispatcherQueue _coreDispatcher; + private DispatcherQueue? _coreDispatcher; + + public WinUIDispatcher() + { + } public WinUIDispatcher(DispatcherQueue coreDispatcher) { _coreDispatcher = coreDispatcher; } - public Task ExecuteOnUIThread(Action action) => _coreDispatcher.EnqueueAsync(action, DispatcherQueuePriority.Normal); + public bool HasThreadAccess => _coreDispatcher?.HasThreadAccess == true; + + public void Initialize(DispatcherQueue coreDispatcher) + { + _coreDispatcher ??= coreDispatcher ?? throw new ArgumentNullException(nameof(coreDispatcher)); + } + + public Task ExecuteOnUIThread(Action action) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + + if (_coreDispatcher == null) + throw new InvalidOperationException("UI dispatcher is not initialized."); + + if (_coreDispatcher.HasThreadAccess) + { + action(); + return Task.CompletedTask; + } + + return _coreDispatcher.EnqueueAsync(action, DispatcherQueuePriority.Normal); + } } diff --git a/Wino.Mail.WinUI/Services/NavigationService.cs b/Wino.Mail.WinUI/Services/NavigationService.cs index 0597ab32..d804245d 100644 --- a/Wino.Mail.WinUI/Services/NavigationService.cs +++ b/Wino.Mail.WinUI/Services/NavigationService.cs @@ -28,6 +28,7 @@ namespace Wino.Services; public class NavigationService : NavigationServiceBase, INavigationService { private readonly IStatePersistanceService _statePersistanceService; + private readonly IDispatcher _dispatcher; private WinoPage[] _renderingPageTypes = new WinoPage[] { @@ -50,9 +51,34 @@ public class NavigationService : NavigationServiceBase, INavigationService WinoPage.EventDetailsPage ]; - public NavigationService(IStatePersistanceService statePersistanceService) + public NavigationService(IStatePersistanceService statePersistanceService, IDispatcher dispatcher) { _statePersistanceService = statePersistanceService; + _dispatcher = dispatcher; + } + + private bool IsOnNavigationThread() + => _dispatcher is WinUIDispatcher winUiDispatcher && winUiDispatcher.HasThreadAccess; + + private T ExecuteOnNavigationThread(Func action) + { + if (IsOnNavigationThread()) + return action(); + + T result = default!; + _dispatcher.ExecuteOnUIThread(() => result = action()).GetAwaiter().GetResult(); + return result; + } + + private void ExecuteOnNavigationThread(Action action) + { + if (IsOnNavigationThread()) + { + action(); + return; + } + + _dispatcher.ExecuteOnUIThread(action).GetAwaiter().GetResult(); } public Type? GetPageType(WinoPage winoPage) @@ -94,6 +120,9 @@ public class NavigationService : NavigationServiceBase, INavigationService } public Frame GetCoreFrame(NavigationReferenceFrame frameType) + => ExecuteOnNavigationThread(() => GetCoreFrameInternal(frameType)); + + private Frame GetCoreFrameInternal(NavigationReferenceFrame frameType) { if (WinoApplication.MainWindow is not IWinoShellWindow shellWindow) throw new ArgumentException("MainWindow must implement IWinoShellWindow"); if (shellWindow.GetMainFrame() is not Frame mainFrame) throw new ArgumentException("MainFrame cannot be null."); @@ -107,8 +136,11 @@ public class NavigationService : NavigationServiceBase, INavigationService } public bool ChangeApplicationMode(WinoApplicationMode mode) + => ExecuteOnNavigationThread(() => ChangeApplicationModeInternal(mode)); + + private bool ChangeApplicationModeInternal(WinoApplicationMode mode) { - var coreFrame = GetCoreFrame(NavigationReferenceFrame.ShellFrame); + var coreFrame = GetCoreFrameInternal(NavigationReferenceFrame.ShellFrame); if (coreFrame == null) return false; @@ -154,6 +186,12 @@ public class NavigationService : NavigationServiceBase, INavigationService object? parameter = null, NavigationReferenceFrame frame = NavigationReferenceFrame.InnerShellFrame, NavigationTransitionType transition = NavigationTransitionType.None) + => ExecuteOnNavigationThread(() => NavigateInternal(page, parameter, frame, transition)); + + private bool NavigateInternal(WinoPage page, + object? parameter = null, + NavigationReferenceFrame frame = NavigationReferenceFrame.InnerShellFrame, + NavigationTransitionType transition = NavigationTransitionType.None) { var pageType = GetPageType(page); if (pageType == null) return false; @@ -173,7 +211,7 @@ public class NavigationService : NavigationServiceBase, INavigationService _statePersistanceService.IsReadingMail = _renderingPageTypes.Contains(page); _statePersistanceService.IsEventDetailsVisible = page == WinoPage.EventDetailsPage; - Frame innerShellFrame = GetCoreFrame(NavigationReferenceFrame.InnerShellFrame); + Frame innerShellFrame = GetCoreFrameInternal(NavigationReferenceFrame.InnerShellFrame); if (innerShellFrame != null) { @@ -227,8 +265,7 @@ public class NavigationService : NavigationServiceBase, INavigationService // This page must be opened in the Frame placed in MailListingPage. if (isMailListingPageActive && frame == NavigationReferenceFrame.RenderingFrame) { - var listingFrame = GetCoreFrame(NavigationReferenceFrame.RenderingFrame); - + var listingFrame = GetCoreFrameInternal(NavigationReferenceFrame.RenderingFrame); if (listingFrame == null) return false; // Active page is mail list page and we are opening a mail item. @@ -294,6 +331,9 @@ public class NavigationService : NavigationServiceBase, INavigationService } public void GoBack(Core.Domain.Enums.NavigationTransitionEffect slideEffect = Core.Domain.Enums.NavigationTransitionEffect.FromRight) + => ExecuteOnNavigationThread(() => GoBackInternal(slideEffect)); + + private void GoBackInternal(Core.Domain.Enums.NavigationTransitionEffect slideEffect = Core.Domain.Enums.NavigationTransitionEffect.FromRight) { // Check if we're navigating within ManageAccountsPage (applies to both modes) // Check if we're navigating within SettingsPage (applies to both modes) @@ -304,7 +344,7 @@ public class NavigationService : NavigationServiceBase, INavigationService return; } - var innerShellFrame = GetCoreFrame(NavigationReferenceFrame.InnerShellFrame); + var innerShellFrame = GetCoreFrameInternal(NavigationReferenceFrame.InnerShellFrame); if (_statePersistanceService.ApplicationMode == WinoApplicationMode.Calendar && innerShellFrame?.CanGoBack == true) { diff --git a/Wino.Mail.WinUI/Views/Mail/MailListPage.xaml.cs b/Wino.Mail.WinUI/Views/Mail/MailListPage.xaml.cs index 8f403bba..34d21163 100644 --- a/Wino.Mail.WinUI/Views/Mail/MailListPage.xaml.cs +++ b/Wino.Mail.WinUI/Views/Mail/MailListPage.xaml.cs @@ -123,7 +123,7 @@ public sealed partial class MailListPage : MailListPageAbstract, ViewModel.ExecuteHoverActionCommand.Execute(e); } - private void FolderPivotChanged(object sender, SelectionChangedEventArgs e) + private async void FolderPivotChanged(object sender, SelectionChangedEventArgs e) { foreach (var addedItem in e.AddedItems) { @@ -144,6 +144,11 @@ public sealed partial class MailListPage : MailListPageAbstract, SelectAllCheckbox.IsChecked = false; SelectionModeToggle.IsChecked = false; + if (ViewModel.MailCollection.SelectedItemsCount > 0) + { + await ViewModel.MailCollection.UnselectAllAsync(); + } + UpdateSelectAllButtonStatus(); ViewModel.SelectedPivotChangedCommand.Execute(null); }