From 4ca26cb13143782e27ddcd2f76becdeaeccca7d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Wed, 15 Apr 2026 15:43:07 +0200 Subject: [PATCH] Add configurable mail notification actions --- AGENTS.md | 2 + Wino.Core.Domain/Constants.cs | 1 + Wino.Core.Domain/Enums/WinoPage.cs | 1 + .../Interfaces/IPreferencesService.cs | 10 ++ .../Settings/SettingsNavigationItemInfo.cs | 6 + .../Translations/en_US/resources.json | 10 ++ .../MailNotificationSettingsPageViewModel.cs | 135 ++++++++++++++++++ .../Activation/ToastActivationResolver.cs | 8 +- Wino.Mail.WinUI/App.xaml.cs | 92 ++++++++++++ .../theme-dark/scale-100/mail-forward.png | Bin 0 -> 426 bytes .../theme-dark/scale-100/mail-junk.png | Bin 0 -> 420 bytes .../theme-dark/scale-100/mail-reply.png | Bin 0 -> 287 bytes .../theme-dark/scale-100/mail-replyall.png | Bin 0 -> 327 bytes .../theme-dark/scale-125/mail-forward.png | Bin 0 -> 506 bytes .../theme-dark/scale-125/mail-junk.png | Bin 0 -> 518 bytes .../theme-dark/scale-125/mail-reply.png | Bin 0 -> 337 bytes .../theme-dark/scale-125/mail-replyall.png | Bin 0 -> 399 bytes .../theme-dark/scale-150/mail-forward.png | Bin 0 -> 608 bytes .../theme-dark/scale-150/mail-junk.png | Bin 0 -> 601 bytes .../theme-dark/scale-150/mail-reply.png | Bin 0 -> 365 bytes .../theme-dark/scale-150/mail-replyall.png | Bin 0 -> 455 bytes .../theme-dark/scale-200/mail-forward.png | Bin 0 -> 776 bytes .../theme-dark/scale-200/mail-junk.png | Bin 0 -> 759 bytes .../theme-dark/scale-200/mail-reply.png | Bin 0 -> 481 bytes .../theme-dark/scale-200/mail-replyall.png | Bin 0 -> 597 bytes .../theme-dark/scale-400/mail-forward.png | Bin 0 -> 1544 bytes .../theme-dark/scale-400/mail-junk.png | Bin 0 -> 1517 bytes .../theme-dark/scale-400/mail-reply.png | Bin 0 -> 767 bytes .../theme-dark/scale-400/mail-replyall.png | Bin 0 -> 1055 bytes .../theme-light/scale-100/mail-forward.png | Bin 0 -> 380 bytes .../theme-light/scale-100/mail-junk.png | Bin 0 -> 386 bytes .../theme-light/scale-100/mail-reply.png | Bin 0 -> 271 bytes .../theme-light/scale-100/mail-replyall.png | Bin 0 -> 305 bytes .../theme-light/scale-125/mail-forward.png | Bin 0 -> 473 bytes .../theme-light/scale-125/mail-junk.png | Bin 0 -> 481 bytes .../theme-light/scale-125/mail-reply.png | Bin 0 -> 318 bytes .../theme-light/scale-125/mail-replyall.png | Bin 0 -> 363 bytes .../theme-light/scale-150/mail-forward.png | Bin 0 -> 558 bytes .../theme-light/scale-150/mail-junk.png | Bin 0 -> 550 bytes .../theme-light/scale-150/mail-reply.png | Bin 0 -> 347 bytes .../theme-light/scale-150/mail-replyall.png | Bin 0 -> 424 bytes .../theme-light/scale-200/mail-forward.png | Bin 0 -> 691 bytes .../theme-light/scale-200/mail-junk.png | Bin 0 -> 697 bytes .../theme-light/scale-200/mail-reply.png | Bin 0 -> 452 bytes .../theme-light/scale-200/mail-replyall.png | Bin 0 -> 552 bytes .../theme-light/scale-400/mail-forward.png | Bin 0 -> 1392 bytes .../theme-light/scale-400/mail-junk.png | Bin 0 -> 1379 bytes .../theme-light/scale-400/mail-reply.png | Bin 0 -> 705 bytes .../theme-light/scale-400/mail-replyall.png | Bin 0 -> 974 bytes Wino.Mail.WinUI/Services/NavigationService.cs | 2 + .../Services/NotificationBuilder.cs | 86 ++++++++--- .../Services/PreferencesService.cs | 12 ++ .../MailNotificationSettingsPageAbstract.cs | 5 + .../MailNotificationSettingsPage.xaml | 49 +++++++ .../MailNotificationSettingsPage.xaml.cs | 11 ++ 55 files changed, 410 insertions(+), 20 deletions(-) create mode 100644 Wino.Mail.ViewModels/MailNotificationSettingsPageViewModel.cs create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-100/mail-forward.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-100/mail-junk.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-100/mail-reply.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-100/mail-replyall.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-125/mail-forward.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-125/mail-junk.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-125/mail-reply.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-125/mail-replyall.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-150/mail-forward.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-150/mail-junk.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-150/mail-reply.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-150/mail-replyall.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-200/mail-forward.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-200/mail-junk.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-200/mail-reply.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-200/mail-replyall.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-400/mail-forward.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-400/mail-junk.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-400/mail-reply.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-400/mail-replyall.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-100/mail-forward.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-100/mail-junk.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-100/mail-reply.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-100/mail-replyall.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-125/mail-forward.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-125/mail-junk.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-125/mail-reply.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-125/mail-replyall.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-150/mail-forward.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-150/mail-junk.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-150/mail-reply.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-150/mail-replyall.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-200/mail-forward.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-200/mail-junk.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-200/mail-reply.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-200/mail-replyall.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-400/mail-forward.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-400/mail-junk.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-400/mail-reply.png create mode 100644 Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-400/mail-replyall.png create mode 100644 Wino.Mail.WinUI/Views/Abstract/MailNotificationSettingsPageAbstract.cs create mode 100644 Wino.Mail.WinUI/Views/Settings/MailNotificationSettingsPage.xaml create mode 100644 Wino.Mail.WinUI/Views/Settings/MailNotificationSettingsPage.xaml.cs diff --git a/AGENTS.md b/AGENTS.md index b32a6ef6..4b451cce 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -150,6 +150,8 @@ private string searchQuery = string.Empty; - For dependency properties in WinUI code, always prefer `[GeneratedDependencyProperty]` from CommunityToolkit over manual `DependencyProperty.Register(...)` declarations. - When a `[RelayCommand]` needs enable/disable logic, prefer the command's `CanExecute` over binding `Button.IsEnabled` in XAML; use `[NotifyCanExecuteChangedFor]` on dependent properties and call `NotifyCanExecuteChanged()` explicitly when non-generated state affects the command. - In ViewModels, update all UI-bound properties/collections via `ExecuteUIThread(...)` (especially after awaited calls and any use of `ConfigureAwait(false)`). +- `ConfigureAwait(false)` continues execution on a background thread. Any UI-bound property change, `INotifyPropertyChanged` notification, collection mutation, or similar UI-facing state update after that point must be marshaled back with `ExecuteUIThread(...)` or the appropriate dispatcher call, otherwise the app can crash. +- Messenger messages are raised from a background thread by default, while UI control event handlers such as `Button.Click` start on the UI thread. Be deliberate when combining dispatcher usage with `ConfigureAwait(false)` so post-await UI updates always return to the UI thread. - ViewModels should only handle UI interaction/state and delegate business logic to services; account-management work belongs in `WinoAccountProfileService`, and preferences import/export/apply logic belongs in `PreferencesService`. - In `EventDetailsPageViewModel.LoadAttendeesAsync`, never mutate `CurrentEvent.Attendees` outside `ExecuteUIThread(...)`. - Never create pure C# controls or controls that heavily manipulate UI structure from `.cs` files. Define controls in XAML and keep UI composition in XAML. diff --git a/Wino.Core.Domain/Constants.cs b/Wino.Core.Domain/Constants.cs index e57f7696..dd1af841 100644 --- a/Wino.Core.Domain/Constants.cs +++ b/Wino.Core.Domain/Constants.cs @@ -24,6 +24,7 @@ public static class Constants public const string ToastModeKey = nameof(ToastModeKey); public const string ToastModeMail = nameof(ToastModeMail); public const string ToastModeCalendar = nameof(ToastModeCalendar); + public const string ToastDismissActionKey = nameof(ToastDismissActionKey); public const string ToastStoreUpdateActionKey = nameof(ToastStoreUpdateActionKey); public const string ToastStoreUpdateActionInstall = nameof(ToastStoreUpdateActionInstall); public const string ClientLogFile = "Client_.log"; diff --git a/Wino.Core.Domain/Enums/WinoPage.cs b/Wino.Core.Domain/Enums/WinoPage.cs index 5a23c97c..8f8e79bc 100644 --- a/Wino.Core.Domain/Enums/WinoPage.cs +++ b/Wino.Core.Domain/Enums/WinoPage.cs @@ -19,6 +19,7 @@ public enum WinoPage AboutPage, PersonalizationPage, MessageListPage, + MailNotificationSettingsPage, MailListPage, ReadComposePanePage, AppPreferencesPage, diff --git a/Wino.Core.Domain/Interfaces/IPreferencesService.cs b/Wino.Core.Domain/Interfaces/IPreferencesService.cs index c9f58ba3..cde1ca06 100644 --- a/Wino.Core.Domain/Interfaces/IPreferencesService.cs +++ b/Wino.Core.Domain/Interfaces/IPreferencesService.cs @@ -192,6 +192,16 @@ public interface IPreferencesService : INotifyPropertyChanged /// Guid? StartupEntityId { get; set; } + /// + /// Setting: First action button displayed on mail toast notifications. + /// + MailOperation FirstMailNotificationAction { get; set; } + + /// + /// Setting: Second action button displayed on mail toast notifications. + /// + MailOperation SecondMailNotificationAction { get; set; } + /// diff --git a/Wino.Core.Domain/Models/Settings/SettingsNavigationItemInfo.cs b/Wino.Core.Domain/Models/Settings/SettingsNavigationItemInfo.cs index 4a3581e4..274ce538 100644 --- a/Wino.Core.Domain/Models/Settings/SettingsNavigationItemInfo.cs +++ b/Wino.Core.Domain/Models/Settings/SettingsNavigationItemInfo.cs @@ -68,6 +68,11 @@ public static class SettingsNavigationInfoProvider Translator.SettingsMessageList_Description, "\uE8C4", searchKeywords: Translator.SettingsSearch_MessageList_Keywords), + new(WinoPage.MailNotificationSettingsPage, + Translator.SettingsMailNotifications_Title, + Translator.SettingsMailNotifications_Description, + "\uE7F4", + searchKeywords: Translator.SettingsSearch_MailNotifications_Keywords), new(WinoPage.ReadComposePanePage, Translator.SettingsReadComposePane_Title, Translator.SettingsReadComposePane_Description, @@ -149,6 +154,7 @@ public static class SettingsNavigationInfoProvider WinoPage.PersonalizationPage => Translator.SettingsPersonalization_Title, WinoPage.AboutPage => Translator.SettingsAbout_Title, WinoPage.MessageListPage => Translator.SettingsMessageList_Title, + WinoPage.MailNotificationSettingsPage => Translator.SettingsMailNotifications_Title, WinoPage.ReadComposePanePage => Translator.SettingsReadComposePane_Title, WinoPage.AppPreferencesPage => Translator.SettingsAppPreferences_Title, WinoPage.CalendarSettingsPage => Translator.CalendarSettings_Preferences_Title, diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json index b1fd54b5..fe103b57 100644 --- a/Wino.Core.Domain/Translations/en_US/resources.json +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -84,6 +84,7 @@ "Buttons_Delete": "Delete", "Buttons_Deny": "Deny", "Buttons_Discard": "Discard", + "Buttons_Dismiss": "Dismiss", "Buttons_Edit": "Edit", "Buttons_EnableImageRendering": "Enable", "Buttons_Multiselect": "Select Multiple", @@ -910,6 +911,14 @@ "SettingsMarkAsRead_WhenSelected": "When selected", "SettingsMessageList_Description": "Change how your messages should be organized in mail list.", "SettingsMessageList_Title": "Message List", + "SettingsMailNotifications_Title": "Notifications", + "SettingsMailNotifications_Description": "Notification settings and preferences for mails.", + "SettingsMailNotifications_Actions_Title": "App notification actions.", + "SettingsMailNotifications_Actions_Description": "Customize the button behaviors on the notifications as you like.", + "SettingsMailNotifications_FirstAction_Title": "First notification action", + "SettingsMailNotifications_FirstAction_Description": "Choose the first button shown on mail notifications.", + "SettingsMailNotifications_SecondAction_Title": "Second notification action", + "SettingsMailNotifications_SecondAction_Description": "Choose the second button shown on mail notifications.", "SettingsNoAccountSetupMessage": "You didn't setup any accounts yet.", "SettingsNotifications_Description": "Turn on or off notifications for this account.", "SettingsNotifications_Title": "Notifications", @@ -946,6 +955,7 @@ "SettingsSearch_About_Keywords": "about;version;website;privacy;github;donate;store;support", "SettingsSearch_KeyboardShortcuts_Keywords": "shortcut;shortcuts;hotkey;hotkeys;keyboard;keys", "SettingsSearch_MessageList_Keywords": "message;messages;list;threading;threads;avatar;preview;sender", + "SettingsSearch_MailNotifications_Keywords": "mail;notification;notifications;toast;action;actions;reply;reply all;forward;archive;delete;junk;read", "SettingsSearch_ReadComposePane_Keywords": "reader;compose;composer;font;fonts;external content;display;reading", "SettingsSearch_SignatureAndEncryption_Keywords": "signature;signatures;encryption;certificate;certificates;s mime;smime;security", "SettingsSearch_Storage_Keywords": "storage;cache;caching;mime;disk;space;cleanup;clean up;local data", diff --git a/Wino.Mail.ViewModels/MailNotificationSettingsPageViewModel.cs b/Wino.Mail.ViewModels/MailNotificationSettingsPageViewModel.cs new file mode 100644 index 00000000..19032544 --- /dev/null +++ b/Wino.Mail.ViewModels/MailNotificationSettingsPageViewModel.cs @@ -0,0 +1,135 @@ +using System.Collections.ObjectModel; +using System.Linq; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain; + +namespace Wino.Mail.ViewModels; + +public partial class MailNotificationSettingsPageViewModel : MailBaseViewModel +{ + private static readonly MailOperation[] SupportedMailNotificationActions = + [ + MailOperation.MarkAsRead, + MailOperation.SoftDelete, + MailOperation.MoveToJunk, + MailOperation.Archive, + MailOperation.Reply, + MailOperation.ReplyAll, + MailOperation.Forward + ]; + + private readonly IPreferencesService _preferencesService; + private bool _isUpdatingSelection; + private bool _isLoaded; + + public ObservableCollection AvailableNotificationActions { get; } = []; + + [ObservableProperty] + public partial MailNotificationActionOption SelectedFirstAction { get; set; } + + [ObservableProperty] + public partial MailNotificationActionOption SelectedSecondAction { get; set; } + + public MailNotificationSettingsPageViewModel(IPreferencesService preferencesService) + { + _preferencesService = preferencesService; + + foreach (var action in SupportedMailNotificationActions) + { + AvailableNotificationActions.Add(new MailNotificationActionOption(action, GetOperationDisplayText(action))); + } + + InitializeSelections(); + _isLoaded = true; + } + + partial void OnSelectedFirstActionChanged(MailNotificationActionOption value) + { + if (!_isLoaded || _isUpdatingSelection || value == null) + return; + + EnsureDistinctSelections(changedSelection: value, isFirstSelection: true); + _preferencesService.FirstMailNotificationAction = value.Operation; + } + + partial void OnSelectedSecondActionChanged(MailNotificationActionOption value) + { + if (!_isLoaded || _isUpdatingSelection || value == null) + return; + + EnsureDistinctSelections(changedSelection: value, isFirstSelection: false); + _preferencesService.SecondMailNotificationAction = value.Operation; + } + + private void InitializeSelections() + { + var firstAction = ResolveSupportedAction(_preferencesService.FirstMailNotificationAction, MailOperation.MarkAsRead); + var secondAction = ResolveSupportedAction(_preferencesService.SecondMailNotificationAction, MailOperation.SoftDelete); + + if (secondAction == firstAction) + { + secondAction = GetFallbackDistinctAction(firstAction); + } + + SelectedFirstAction = GetOption(firstAction); + SelectedSecondAction = GetOption(secondAction); + + _preferencesService.FirstMailNotificationAction = firstAction; + _preferencesService.SecondMailNotificationAction = secondAction; + } + + private void EnsureDistinctSelections(MailNotificationActionOption changedSelection, bool isFirstSelection) + { + var otherSelection = isFirstSelection ? SelectedSecondAction : SelectedFirstAction; + if (otherSelection?.Operation != changedSelection.Operation) + return; + + _isUpdatingSelection = true; + + var fallbackAction = GetFallbackDistinctAction(changedSelection.Operation); + var fallbackOption = GetOption(fallbackAction); + + if (isFirstSelection) + { + SelectedSecondAction = fallbackOption; + _preferencesService.SecondMailNotificationAction = fallbackAction; + } + else + { + SelectedFirstAction = fallbackOption; + _preferencesService.FirstMailNotificationAction = fallbackAction; + } + + _isUpdatingSelection = false; + } + + private MailNotificationActionOption GetOption(MailOperation action) + => AvailableNotificationActions.First(option => option.Operation == action); + + private static MailOperation ResolveSupportedAction(MailOperation action, MailOperation fallbackAction) + => SupportedMailNotificationActions.Contains(action) ? action : fallbackAction; + + private static MailOperation GetFallbackDistinctAction(MailOperation excludedAction) + => SupportedMailNotificationActions.First(action => action != excludedAction); + + private static string GetOperationDisplayText(MailOperation action) + => action switch + { + MailOperation.MarkAsRead => Translator.MailOperation_MarkAsRead, + MailOperation.SoftDelete => Translator.MailOperation_Delete, + MailOperation.MoveToJunk => Translator.MailOperation_MarkAsJunk, + MailOperation.Archive => Translator.MailOperation_Archive, + MailOperation.Reply => Translator.MailOperation_Reply, + MailOperation.ReplyAll => Translator.MailOperation_ReplyAll, + MailOperation.Forward => Translator.MailOperation_Forward, + _ => action.ToString() + }; +} + +public sealed class MailNotificationActionOption(MailOperation operation, string displayText) +{ + public MailOperation Operation { get; } = operation; + public string DisplayText { get; } = displayText; +} diff --git a/Wino.Mail.WinUI/Activation/ToastActivationResolver.cs b/Wino.Mail.WinUI/Activation/ToastActivationResolver.cs index bbbf829d..e3cb7426 100644 --- a/Wino.Mail.WinUI/Activation/ToastActivationResolver.cs +++ b/Wino.Mail.WinUI/Activation/ToastActivationResolver.cs @@ -41,9 +41,14 @@ internal static class ToastActivationResolver return calendarAction == Constants.ToastCalendarNavigateAction; } + if (toastArguments.TryGetValue(Constants.ToastDismissActionKey, out string _)) + { + return false; + } + if (toastArguments.TryGetValue(Constants.ToastActionKey, out MailOperation mailAction)) { - return mailAction == MailOperation.Navigate; + return mailAction is MailOperation.Navigate or MailOperation.Reply or MailOperation.ReplyAll or MailOperation.Forward; } return true; @@ -52,5 +57,6 @@ internal static class ToastActivationResolver private static bool ContainsKnownToastKey(NotificationArguments toastArguments) => toastArguments.TryGetValue(Constants.ToastStoreUpdateActionKey, out string _) || toastArguments.TryGetValue(Constants.ToastCalendarActionKey, out string _) || + toastArguments.TryGetValue(Constants.ToastDismissActionKey, out string _) || toastArguments.TryGetValue(Constants.ToastActionKey, out string _); } diff --git a/Wino.Mail.WinUI/App.xaml.cs b/Wino.Mail.WinUI/App.xaml.cs index b3b7189a..893990b5 100644 --- a/Wino.Mail.WinUI/App.xaml.cs +++ b/Wino.Mail.WinUI/App.xaml.cs @@ -358,6 +358,7 @@ public partial class App : WinoApplication, services.AddTransient(typeof(AccountDetailsPageViewModel)); services.AddTransient(typeof(SignatureManagementPageViewModel)); services.AddTransient(typeof(MessageListPageViewModel)); + services.AddTransient(typeof(MailNotificationSettingsPageViewModel)); services.AddTransient(typeof(ReadComposePanePageViewModel)); services.AddTransient(typeof(MergedAccountDetailsPageViewModel)); services.AddTransient(typeof(AppPreferencesPageViewModel)); @@ -599,6 +600,12 @@ public partial class App : WinoApplication, return; } + if (toastArguments.TryGetValue(Constants.ToastDismissActionKey, out string _)) + { + LogActivation("Handling notification dismiss action."); + return; + } + // Check calendar reminder toast activation first. if (toastArguments.TryGetValue(Constants.ToastCalendarActionKey, out string calendarAction) && toastArguments.TryGetValue(Constants.ToastCalendarItemIdKey, out string calendarItemIdString) && @@ -632,6 +639,10 @@ public partial class App : WinoApplication, // User clicked notification - create window if needed and navigate. await HandleToastNavigationAsync(mailItemUniqueId); } + else if (IsComposeToastAction(action)) + { + await HandleToastComposeActionAsync(action, mailItemUniqueId); + } else { // User clicked action button (Mark as Read, Delete, etc.) @@ -985,6 +996,87 @@ public partial class App : WinoApplication, } } + private async Task HandleToastComposeActionAsync(MailOperation action, Guid mailItemUniqueId) + { + LogActivation($"Handling compose toast action: {action} for mail {mailItemUniqueId}"); + + var mailService = Services.GetRequiredService(); + var folderService = Services.GetRequiredService(); + var mimeFileService = Services.GetRequiredService(); + var navigationService = Services.GetRequiredService(); + var requestDelegator = Services.GetRequiredService(); + var mailShellViewModel = Services.GetRequiredService(); + + var mailItem = await mailService.GetSingleMailItemAsync(mailItemUniqueId); + if (mailItem == null) + { + LogActivation($"Compose toast mail item was not found for {mailItemUniqueId}."); + return; + } + + var account = await mailService.GetMailAccountByUniqueIdAsync(mailItemUniqueId) ?? mailItem.AssignedAccount; + if (account == null) + { + LogActivation($"Compose toast account was not found for {mailItemUniqueId}."); + return; + } + + var draftFolder = await folderService.GetSpecialFolderByAccountIdAsync(account.Id, SpecialFolderType.Draft); + if (draftFolder == null) + { + LogActivation($"Compose toast draft folder is missing for account {account.Id}."); + return; + } + + var mimeInformation = await mimeFileService.GetMimeMessageInformationAsync(mailItem.FileId, account.Id); + if (mimeInformation?.MimeMessage == null) + { + LogActivation($"Compose toast MIME payload was not found for mail {mailItemUniqueId}."); + return; + } + + await EnsureShellWindowAsync(WinoApplicationMode.Mail, activateWindow: true); + navigationService.ChangeApplicationMode(WinoApplicationMode.Mail); + + if (mailShellViewModel.MenuItems.TryGetAccountMenuItem(account.Id, out IAccountMenuItem accountMenuItem)) + { + await mailShellViewModel.ChangeLoadedAccountAsync(accountMenuItem, navigateInbox: false); + } + + if (mailShellViewModel.MenuItems.TryGetSpecialFolderMenuItem(account.Id, SpecialFolderType.Draft, out var draftFolderMenuItem)) + { + await mailShellViewModel.NavigateFolderAsync(draftFolderMenuItem); + } + + var draftOptions = new DraftCreationOptions + { + Reason = action switch + { + MailOperation.Reply => DraftCreationReason.Reply, + MailOperation.ReplyAll => DraftCreationReason.ReplyAll, + MailOperation.Forward => DraftCreationReason.Forward, + _ => DraftCreationReason.Empty + }, + ReferencedMessage = new ReferencedMessage + { + MimeMessage = mimeInformation.MimeMessage, + MailCopy = mailItem + } + }; + + var (draftMailCopy, draftBase64MimeMessage) = await mailService.CreateDraftAsync(account.Id, draftOptions); + var draftPreparationRequest = new DraftPreparationRequest(account, draftMailCopy, draftBase64MimeMessage, draftOptions.Reason, mailItem); + + await requestDelegator.ExecuteAsync(draftPreparationRequest); + navigationService.Navigate(WinoPage.ComposePage, + new MailItemViewModel(draftMailCopy), + NavigationReferenceFrame.RenderingFrame, + NavigationTransitionType.DrillIn); + } + + private static bool IsComposeToastAction(MailOperation action) + => action is MailOperation.Reply or MailOperation.ReplyAll or MailOperation.Forward; + /// /// Creates the main window and activates it. /// diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-100/mail-forward.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-100/mail-forward.png new file mode 100644 index 0000000000000000000000000000000000000000..38c3e00a0881e9677c75f3fd613aca2bc3b9a7e8 GIT binary patch literal 426 zcmV;b0agBqP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0Y6DZK~y+Tz0yxh zl|dK<@T;^4ENBr%QBk-H1b4Oy+O%mA87=!m^bsPHXze0J>KkMrh#OZW{Xs%%*G}5C z6%wQ`AipDn<7M8vxCKE69(dD zE_PHyP>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0Xa!TK~y+Tz0$Eu zm0=VH@O#BU0{8HSL*V8jDFsOgwg@6RC_y0T>gv=`ORX)otDu7|;v#Anp`f7%3Tzb; z{5yU}-pl)4-h1KR1i=RmeD8Cf^PIyuU#)f@z!2uKf=Au?zQ9ZTiim@V*v40EcI%Sx z0Y-~xU<{X-E_6E)5m+dT(|Et}y@)-0iime*F+NAcESfDcB2pq1Oh!aKA_jBRv5)1V z_&k3Pr<$ax4E|uFJyHhVU<>c@rQjEF+InPzw3sHo;dLQQd%41O!9BshwA{*I04G`e z0>{~KDWC5Z4s(Mu{4ASP1}WJny3A$q1wQ5}$1#NoeCTUX0gt=6hFVdQ&MBb@0Ql>2Z2$lO literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-100/mail-replyall.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-100/mail-replyall.png new file mode 100644 index 0000000000000000000000000000000000000000..aa9644ad45d3952a9fcc062c997b7ce74bbeb0e6 GIT binary patch literal 327 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc!8@KVjv*HQcQ0Cdxj2fnJTwe+K6vNG9d<6I zrboZ`GCPVtWb(Pw{3Cbsvt^e`dn>0sNa^>Fp9!70? zdZ%Q+r+!hn?tA&pL$>f4t2f&gbS&RwF6MQ(tjSa7p%A~t`pveJWENz8*Iy{EQ-9U4 zH(Z8a?YD;St?Bc1`ER_sXZ}cJd-F=JoW!+P-EX{K?fcO3Nw`njxgN@xNAR|ANf literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-125/mail-forward.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-125/mail-forward.png new file mode 100644 index 0000000000000000000000000000000000000000..7b5f253265e468edf355aab6ce9be0708bf0fe69 GIT binary patch literal 506 zcmVPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0gp*UK~y+T&C@-o z&ruu(@SBw4vLNrX5h*N`$%t~3MT#spi!#bB7AbkX-ZD}a3zKBCm{=@gP+r19O!9ej zuJix*`=9?m-CG7vJ^jvke$V$g?_*>C1F*2L)#nUs#{;~>M?Arf);B9)A|2p6KGgR9 z)<1(J3z97D5{t16Uz@=9Sc}KCzkDDJC-54t8Ww$TC+G*xVKT{vTKfXWyJ36q3EQxy z%Os1EoT_2pun}8u44Z5HGhFHh-oTx{&;Unj;F%=rYr7oEYTU2=quqkj_?hHhl63=V zfA)3-eqpLJ+p?nPoo!gzFTe%d#pOEk$t3f-p*Yyv#}+uy^%nCrT&(S_c#4-vb~nDA zV!;eT3&W zZnfgbz#fv!Z?zt9b*Q%P;q-twOW+Ewx8lvAQD%uc!=WWl;0rdDH#rBESMxc^zJb7k wa$)gi#9RIwi+`%k>I36gH3yEj8UGx=0lZ+b(Qx1iq5uE@07*qoM6N<$f|uv&82|tP literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-125/mail-junk.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-125/mail-junk.png new file mode 100644 index 0000000000000000000000000000000000000000..7532e7c644b6b491970dc0e904c4f17568e5ef57 GIT binary patch literal 518 zcmV+h0{Q)kP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0h>ugK~y+T-PB8{ zO<^1b@avUFB4uFYIa8i7RFp6z4ySte+4YZ6+Fe0Bxf{vW_J%xG{r<|heOH})T@0$(SY+A)%h!8yF|!4nmS8(~BEkYrAh``B4G zca^(f3Fb9ey1t98jj)fHSzVvR#ZF+m-M);&9iai-#=dGjl;qP$SfyRUsd~X6zTf~R zcZ{F@_A*r*!Mj$g)d@>71>dl$UT_*Wa1(En>`5}dRx}n5tHCU&>9g<{*E?Ya<@s^2 zT5rH3yuywoqbq$buHr?KTewq(f-_0RH3QG%SVP-{XBDsEK{+?9z+UXevNGK=yn1cV zz!6{@E+ko+WK<8oOnY%t{AJ+pkYr*x0xaw@N(VUFCH4VKsLpgz(-{7n4hza_RG<8a zxEY_Z5o`M7+UiH~29vr1tI+*;j+cMHd*yR&W_2M+-~1=|4msL^R|HhWHUIzs07*qo IM6N<$f?MRNS%G}c0*}aI1_r*vAk26?e?($Hqs%qv(SLNk=kG=0V_q*l%&zmnx ze$IDs5kEfT^g7cS2X)`Ae()wb@K9dC_wdB<9eo|0Z=xGnV;;#`M;vxrdu^kL;N;48 z0T=o^4j9XQ2wQ#qQr!bV^@wwl_6J1#ykDfb_=M}ntgc++cBHOUH#Nroxyb>^xDAtH zWo4#Zxcf%R<%p%-#nbEf*seahnz<>vp?g#F(Kq`#J8l}KS?^w$@P7OL4t1HQ|0W(( g+*b5|#>JmZG55H)Ch0tG1NxZ3)78&qol`;+0G;xTJ^%m! literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-125/mail-replyall.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-125/mail-replyall.png new file mode 100644 index 0000000000000000000000000000000000000000..3029bcaafa8134ad2a80ddccd8c6e2107dfbdb30 GIT binary patch literal 399 zcmV;A0dW3_P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0VGL8K~y+T?a?ty zmr)$WaY<7GGgMr1m6mjf0+(=Ub!cdFZqm(np-``&Nhl+FB~jpJK}11=LrFAf(DOO* zzz@;eKmH-O`JL|lJ?Eb1oO?$`F1au;hwUUcYQKUft?s|TG(O^Uk}J5EBzVPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0rg2lK~zXf<Io`H8vd!@vWx7^N8rf{(QbxfRUVhJD*>-&S?TX$>&!QF|(Ms2H&&u z9#$2JTgXB`#WQS6-M6l+2pp~^Zb*Bbt_oLj(0L4&0>_(KnMALWwt(vqF{PB03#9cM z8w*?TFmKP^Qt-Hc5SNRE@??iHUpXNcNMe;b#=bn+gA|kh?Z(m6CFWyw5)?DCUBrSS zPrE=w%q=-4x;=*VmijWwIi6q_uq@^F>&-^VX3bC(mMKtO2#0000Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0q#jeK~zXf<j_FCWCYwvIWTdn^Vn22@Qg>9Hu$BY@+f%o`~XLu74 zf%6eDt&SfRa4^O8cElp056hE(T)M0ChJk*}s7FLh!lD!zs`Db^P@>^N1*8E1u_B9w`^x zi7o5xcDuOg3m!(qMO@F0iU}5Wi5GMV6t8!7qP@Iz+DD9seaRdq6#<`mRX@L$fH6rHJd=n9IAIEF{w+j3Y$}9id1Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0Rl-xK~zXf?a|9B zMPV4m@tqXO3`0^5Wq_0k_h4u$u0cvNcPOrafuU!dx{p>m-BKoT9 zDB<_m#xC~K+QAl9F`RQ+5)lJYEZ_}~n9jZqT;uC6oB9Fsc*O(evd>_$oZ=+A zmXwm}A#bv)qxuiGdEOSxVmZ(2z(qO5V0K*r>k)Ao5&hZq_{V$$!x&Le5s<@&00000 LNkvXXu0mjf#TT4s literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-150/mail-replyall.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-150/mail-replyall.png new file mode 100644 index 0000000000000000000000000000000000000000..5875c1c316afba720784f8f2b7151ec8be3215fa GIT binary patch literal 455 zcmV;&0XY7NP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0bEH$K~zXf?b1I> z)nORMaS0qM$CieQAXBV#3>ux=dIh$%wKSSULrFz{BoItjp;u4@ZM}_XYA8w@91h9n zj@xeG=ff}QM3bB^95~N)UtAZ@{d>m7MvTBCEOyRU@Gc^z4m$qPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0-Z@jK~z{r?UviC zO;HqwkCbalIY$y9d6m3UUc7h`AtZkQd2xF|$tBm4B-`aU61jxJ2@!b^N|KaIxr8Sk zD0UsU^$m@6#@yx}XHVBzd6QOYmT|*Y-#D^e*%~SYyvug+rW8X4KUto zOGQKm0|zAm@D*6+=am951i0gq27n*Y@1uTx(*W)DDgOXm07e6sBHIcdqapfaA~G~J zOf$JYWj(-K5g8{U{ef#{`y?X6eZCsNTHrPCU0+{f+*PfJZ1YJK-~(`9L>>UgfB_;h z5V&dn#XfJ2icRobM9u)G5@WULue-#*fR!TBFOD1j1H(+WFZSmEv&?zO`%(b+Tmku$ ziO4iyCr|-)>2jKWyXks1BXGdt$QZu?xbZ;5O>d@QZH!<;j{ zF9on63cB2EuOizCv#k#{W44c_qDmy_0LFNYM?@w7KXn4I&}^SW5TFV?isGv6y?(fS zEG^)Ob$F)IzfjO5h4Bjo zO;Q*iIBq?azAQ8WaM%iH^S&HluL=Ii4Hmd%_rsp&fC5an0xp*Q7Jx+-J3k$hgew;C z4S1~QK?&oH#eA0h3dV@YJm9Kc%YYXpjE8n&mikuvD2?A#?QYqw0J0wd0000Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0*pySK~z{r?UqZ3 zjbRwaCle-jp)@H&LLs?iB|>52R+DTfx2z<^cG6I6T$2UK%7R?VC1z$xHpt?Xvcg_j zxSi1NujllXg*$d2hrV3E-j^el6)U@E#ZweI+E#0qy}mQpa_$L=$Z5%?nJ8xs0s% z3rS;vK9AoPz3C19=mGXDhZ|0OIWdmz1w-?=dPe$0|^@(#{O pJ#ZGd+M=tkSS&7#b=v$h`~l*+CD{%9wp#!I002ovPDHLkV1nCZU;_XE literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-200/mail-reply.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-200/mail-reply.png new file mode 100644 index 0000000000000000000000000000000000000000..972a9bf1bf647a7e636cc3392bb1cc3caf0eac41 GIT binary patch literal 481 zcmV<70UrK|P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0d`45K~z{r?Uubt z13?spgQ%#5#vkGzT4-TqWt)c($rIRH+4u@J7CwZfrKN?1C>A0n{xvp&g4&2y;&)`= z28K)6&1yDU><81#J#){Q*_~`krGkQj3`i;g{eHeHNE!x?fg51R*K@_pPb3LAmo(@# zxzWOW+-q{fnO^`CUXvTnya80aCO4esYsd&JF-S0%#uBp zN~;k}fCZomYybyQkSAc(d!z!)Mj?)X0k25{SO)f^AnRU}3NQm)TZ2#10XMiip#HAzV70q$)5ny` X+(%MJ+eerN00000NkvXXu0mjftIWtm literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-200/mail-replyall.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-200/mail-replyall.png new file mode 100644 index 0000000000000000000000000000000000000000..2a94da2bde49f645cccf0642c1a8ad312faafb0c GIT binary patch literal 597 zcmV-b0;>IqP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0qRLaK~z{r?UlW5 z6G0S(%|FNpFg8C1sz^u_De?f3@(>6ffeKOb2GkV11)`-TQZV92j)@&eOj6K*5Cv2~ z1NsiS)*WjY?P?9XPAK+kET5V2IdeU8r_rdXsi^=-ha{ay^-7U+0eC5CRnp<0107%h zjDh2SuBr&&Jg_NAz-viIOqbO5@1}1^I+m_2jqh;iCbrX#>25x7eF&UO*Omr28=2T= zJ41hK6-w8Z2515O9P@S8w}EcDwltDX1Ftg7EmSa{&(7WaMTWWUJXB;Cxa#vK2eJ}? zqrh`Fc?O&`-A;Ef_pvX)d*BoB0eA;I0UiK1Lg>H7jkesRC#h+=2bQRU#2i`;> zTBf^M(AV-B53mS4ih|rq=M<6`!u3!gJFKVDwE^I62q9@D9WNv)dmN%`e}n7O!J%GmX%c_Ad(+z|F=IzAKr7>%M3lbryU?Fzcy%X4OCG=~mJZL3qyv%;X6Q}pps60PN_l`Ep||WQ-rz?5j$~fl jxnt*J-^^=j{%d{%oLCxNs&q;$00000NkvXXu0mjf!D;^} literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-400/mail-forward.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-400/mail-forward.png new file mode 100644 index 0000000000000000000000000000000000000000..81125184759197cae06a327ca1cbf78f7824515a GIT binary patch literal 1544 zcmV+j2KV`iP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1)WJmK~#8N?VD+= zT~ic*YpRNB5K~oxAch!9jg6wjREd9v5Q5;Bp+RC6q)2NXs)R)N(L`bh2|tk5<5y#< z2oV}X;x%{=t+AeWz9s9~`&nn*eeXTz+*{7$e98Iq_S$Q&z3$n2ueJ7hlO{DZG&D3c z)D{uhSVRs2&IN7+?i7(bfGdGhMPz5+=RX0k2e1hEL^=Tc1`Gff0#m%62?sD$e`VlT zKiO^o9|Na*of8J&VBjM^v9bVjo z3g9W-H}vfZ&jY}tx*1yALrn3#>A%vpcSS^|Wm&c&Z$@tbmqz`-#lAfSkOB9K$ewu% zy93Mx)&)I30o!<)wg4|XL*56j08Rvst@vyg^=m?7^{2?PY&9?^Z&`6Pkpk94eZYQR zCJAtsH3WF9-=QV&kR8JV3;}y3`pp0i48c=;jskY@vLfdGAB+<2II=$}O`$4z! zA2`{`9|ODw{F0De3G8U)vn*SZkUQMUBnck=-E=zM%k%@_ro4H7lV#ZpPWF5+69L`@ zws!KmXS9%dqgv3Y$#l^8e0wj`4}dfC=H)of$nC2ep%+lXFYhP6(90>nFtCM{O`_|> zbkd!3s+Z{pA%<_6m>O!cxK78g4{?E;y-cL=QU;DNo%9HL3E1Du^#kC&yqRNTz7yL; zWJ6$;mx%zU8C}==b_;OT_`sd84ltcH2w5Z|TYC9k04__6HDKDO0IR)BMC%87LIKbF zHcKd*&B%}5hLi&$A)GzW_o+%qg>!McZfdc8aBy%l5&6NkR{?l9KXM1$5?I%!O0&Gs zkx~HQ0;^aH9Kf}@>_Ys}6B-T!+xl_a;vAr{v?dnGphZ2K3P8%D`;6D!FMu=rcpY&L z(4b>l_`9AC%k%2TWWE6|5fQJdFu+#8hhBHD01NzBU2zW3O(ypXhYO095>|*F$ItJw z0MqpZ@OrBP-0sIHL^lU0`c?q9rE2#fUERg^DMCoUwWM7c;9KAvue&hL0U8LkI3N;Hk?-+B3>Xab~3 zjqVR^-Zl+y2Sns*5!p4-e;?gn{NFoL#Q}EFq;JCk z1`F&Isyvz%&Ivg}a&n>ms28bgOD{+7fU?O@*vwL?s77N8d_tmWEbxet(>+8hIhB@= zz6tR}(FnGz{XIJ%qJfg;?@^c0xoUKDR5L-{Nat(eBO&u_)v#Mcjy8Idn|Me1<$wrq zb}FGs#rPq&pXYUyA(74gyADu7%ztaDnxllH>vG*keBZM4+W|lJ!vPVk?25?K<1_zj z3=OBJd)caZ2b8vdeL5floCMqj9O~P8WkNV$ebP7}?|}05Hx6hV&^Ta1J78pFPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1%gRLK~#8N?V5L} z9aRvA6MHYHs8}&7s8O+E3l>a_*d@dgTTl`IkO&eYg2V#(L$MLmfT9Qr21HOK5iE#e zBcic_y(^X|F(&%(?Kd*M*~_`Vd*9o=yEoxJ*$);jd*;lXnLT^<%$z%6LPJACLqo&B z18iYty8-(Edz#rcD)YYsm;&4myaxOT`~_sd5b!JT4zLI~&CE8?{^J%in*qFGJpg=U zW>>1NaRsn9utc5f1)nWucBtwec!24^iY^X)0X*PO7nlkhX=cZp*`>h!-rl8;zbY4~ zje!L?9T*lTJ_arZHc|N$Guw1{cz8DON!mYjT%z&=$I#HwWMGX9{6RC@TxG|?%(e!; z7d^gU9<8zigPE-lESEvwqH<+0vxjvSg}%>ymZf8?0FR}5l&V2(Bc9ey zz%F$zpQ^6UH#Wde$sj*)8)~-*bsku4W@iC!=J&EJTLtW(eMuQ{(d0 zc}nQLOa?w(Z5PqSdAD&=w4E$%*J=MkeCni1>H>UcW*dg?rOEww)pikMIiF)@%aZoj z)3!o@yHW%F0C?JU`w9>IlH9*Z?TY7dCY)xmdkGOz);Z7vR*ECnI(aQkj%0&O5;N zz{;fGg(}m5CmBhHZ-9+drY^t{EpJBbOjDVhs?IyWe!d+}`c74u4m^QKI(+SGLcj4% z3GioP=X#ZK>NxKJvy#5Uz$BIFKmzbHu)E6i1Nlb3qV08^cYr68{+=zar!pO+0f>(N zy`JCiwE(y|vEf-;YaQs5^Lqh*Cps3YOjk4jIlvEQHqXqq(mqv)>Q5_s1D=R{<598O zv-1w{c&ev#m1wj8ipTFXQ|4JimU-EMk`JNp8{9q zd;2Pw*&d!Csct7WU{#zyOg?@7dQDcjQIP<6y=@|JPP%BNL>lX#&WWn448S#Abgpzq zRJI7;UjV;}y7?+MCK7;F_7-gy0q?czAHlcJz*e*0)m9-z0m$+DK7D~N+f9!EywppU=fvl>E3qbgDobtj8h{*NTi{0DJ+_Z&1mG!mPWtQ$ z-0L}qFQn2o&CC{eo8Bot6MVXBs_V6fO0%G90CHkMT3}|sw-2?&VlPxgaiwj=2A%$kk7ie()*>U8-Sd6x<1%e(o;m1Wy|YKVc_z7Uje+R z?WO2f0CHkaeqXvTSAbW6GqrE8xSd4j3))_aeg_~YrshL(dgGg`87f;9;ErVMgB5@r zGdnom;l7{Vj*9@SO_~5S0cZly1fU5(6M!ZF^#ZUiPKMSNz-`IcE9U~|=Sm`@>q{AY zUEo{oBmhgay%b)IOakDFW~s~|;zgA>yU53Vx5|_v>TRDTiu`!2=L+q*#A|L@N>4P)wPbB1_|IF;KL}$ZmHw%8(!>;Vut^5a*(b`m8!oD4Gj$q4HNzaJToxO TGcIV(00000NkvXXu0mjfh1bu1 literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-400/mail-reply.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-dark/scale-400/mail-reply.png new file mode 100644 index 0000000000000000000000000000000000000000..22b30d41b7db485a567926b0d4b85b37dcd267de GIT binary patch literal 767 zcmVPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0+dNaK~#8N?V8Q2 zO;H%euYs?LNFtY#jD(Syi6Y&bk1`-iqI|o!$V>*vUtnUuUmz(HqdPD_NhX)6x*6QM z`Ao;T?m53#tCl)Ft#cA*Ew#@3t3BEK*}c#EboN?nuYHDw9LI4S$8j9Tah(2QW{b^i zxt<>gfDzz5@Cmq}*Q*8?1!gS*%ni&hL7R^Odgdt@an)8wx8}|4q-v|9Tl2sf)mBHh=3A}Sm};w|Tk`|4 z&QCQD`~Y08aD>O{j{(ACb3naj1Gt

~O!n}F@WQQ%Iy-F^y8Yu!$OhrlZJn}skiB@BE6cB-~tnAwuJwf4Zw zK5P8|FlA=j^xiB0ml6iTbeMg^16U2*3ZW-FG=aT(PZoNFuv@h`Xf~T0fF}tLVc&6w z>aze`Oc*E@LIAjt@bCp#ui7jCV+jMLLpYW2@Y2lYsWuD1`GkSeAzVv%7*}03;#8;x zN{8@B>VFK=v+A;uS}c_e!OT{M=U?iVeNhV=BAo`JiDDrDx25)^nW-!Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1GGs*K~#8N?V8PN zTvZr{3#C%2S`msAyQnC1plzGMP+fV*#E3_UQet1UL@-9IjXNbp?0_xC_hxBl>z*VP>bS0PvfcZBuUvFP8WZ z%m7*yW@-cq;r0PjW;Sv0ew z>N+LK52{;xv=4_w%91~(z5|?4xAruf&FCjtlKk5$I>3;+wI`L#N|X0lL*3frNop2> zRPqBVI>3kO)*hfQ^l>Gce5zQ$Idy9f&yUu-=9eSlDf4AI1J23l1U}MY>WUFfX~#eJ!bYQFjq__B$P@%HAZ*=egu96 z#=_;l{{s93dD;CX_$bndvLXyc{08u`nLX}(+mF8dnwgpLpL>Dl78Vu`04Kv&{#)R-#wilU zjLwV`zGRIwpsWb*NBpT&_lW^K8V18_z&(xU0Dl1o^}Uqn25ym^+|LYzU~00BMnFvLC*(+@7*CfleEIjo(8_wn9gcru9!qpUW7h%okY>* zf!EY+7vR%K%qcV5r*0*b%nqp{#JbNMa7f*DNsKc$%xtT=m9UB;oQ?QCEv|YH1I}wq zGy6on%18`!KIf;3FcR_oIluvR+XeVa;{6BgQm-;rQG{imT3BJWe1a4sJW~5d;$2a% zQWE{KtO%nK-`5E*tJ@Bqu*ACAv9Vx@#DftpLa(}x@A@MCB=C~DtqALc>0+!#qw#`z zm6KQo|CxF==%+&w-*=!p)oq0@pC!)59KPsNBH&i40FF1b=!f_0R<9MnWr=ZJosJPU zHGcrVx3f(3#``ikH2e#Fy&e4IN(%73IvpjP4{X=_DS&5z=kbfM=ut z<9@28-liq22WAU3__r(n&tNV1N;8}#PBfd%?t9?69{5RlzGMP*!lgVTP Z{{zC{Wob3zvV#Bs002ovPDHLkV1f{A>5~8e literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-100/mail-forward.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-100/mail-forward.png new file mode 100644 index 0000000000000000000000000000000000000000..48d584f31945d8ab4e459dc171b189066630ac97 GIT binary patch literal 380 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc4T7F7jv*HQdoS5)S0qZXeUO(FIi;ZFVzxBL z=l`Y%9w#^blkO9yr5u6cKQtJRr@I{OuTXO)GCGGF4eI$X&UZgGIs%1QU3Ky07~qie9L z+9!40M|)oQGrkSfaNLzN=LYYsMFCCGTig-_r$-#9;rza#xl3Y#LDN%tgTFDE_WPUt YwU#*v_vrS%0)`lar>mdKI;Vst08g-%cmMzZ literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-100/mail-junk.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-100/mail-junk.png new file mode 100644 index 0000000000000000000000000000000000000000..6ec71bc7fe6b6e915a01d39852f02bc8870bc182 GIT binary patch literal 386 zcmV-|0e$|7P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0T)R`K~y+Tz0t8N z?_m_j@fR7S>_P@*vxpl-QY=ck2De*Ex$9l;WME;nu%j4QWD%|uWgtbdD$4&s&$s8R z(>M7kO*CQxztCQNti>3vv4eG-;a}C1XhKJbHgw?uL&3L!7fc4N zXzvCWA-1rF#o$ZHyBNhu(2ImV)S)3qI&Wh-B%kJUbIwVgf?ND8FM)ZiVhR6){u4*x zUJNF+bdH&zr@1`gIcU9j%)>3H$6+=P;~*ED%Io`$z5Ky3t}34t1=3@hddYY;-(e+d z-S~kXEC=tq1xh-r0tR!(Z1Sa8J{QccL~&<|UthC~9)qSYl>|!Xh4@4;eKd9({KX literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-100/mail-replyall.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-100/mail-replyall.png new file mode 100644 index 0000000000000000000000000000000000000000..4eec5a28d0a8cbc8c31bdd224f138fc3b7363948 GIT binary patch literal 305 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc!DF5-jv*HQcQ1N-F$IVm`xrktOT(o6&Ymev z0tYutx6NU1(p|)AQ7FH5vFVc)rdJlsn$kHb#-jOl=)$(I?cDcwK0jFg?&D&VGux-GP>j#uh;iQOV{^REB>lhgrO$2J{bTIwHt%M=G$EzlT+ZO-AL-u` zsUL&<3M6)Qck6^7mDaj>cYTCA|LoL9{YLC_rYO!~Ho0^T=pzPCS3j3^P6Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0d7e|K~y+T&C@?f zR$&y!@edIMX)P6&+1!{BluLnAV+KjMM3A(IOVAhzwnQNP)2whbXmQag#LdM?mzKyu zFr!+Enj(@TM|gOAmA69Z3qS62&pqdNpL@@9_)plM`e)8#8mst-Ib5idnxz-N3w_60 zuCLa~o9x4(N(XQlUn|28=)i2=AF0i5<2~M0iumQi=m#EP0H?Bc3AakK9(+O}u>T^d@6l7)#TV>)G`g zT+C~9HaM6HZs2}Vw?QMrAjT_>$O=ZX>opU2SqHZ>++=SZC>U*28-XzW8=hjk4!&sg zmL`cEZu#UG{4Y42GufPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0d`45K~y+T-PAuw zR#6L8;ytUOAesJ#docr8!?(d$<|HdJV;yvEsAzI2wwQ?HE zxQP?!iohitDJNIij-5sAz&`v&XEDDo0zacEH|j8i?`zcUsFC~zJirg^#!Fnt&5QAr zcI+)Gt$G?~viT`&9?fxm2TwBJQ}pLT!3$iCaSHpHg(Ze@H@kKVi@1iZxe=U;t`6W^ z5w;D#a5U50!*fjFD=uSm=5NC5Xw3dh--Wk$mT48z{;$Wl8*lIl=dm&LJs86rCNULF zxsUoRtHLAPD6~`f5H*fhvDL%4f=lR#?xyCgVy(a`XYm*Zv7t1h+ij(|Uf_B(#w$2b z8fgWCrC0+E5!hN9=@k6U3KQuw$~#}72MaifV>QNS%G}c0*}aI1_r*vAk26?e?r_?yV?K4Psb~6?*t*raPpU|`h{#Hd*ByG=o?M?>{h;9Cp6C0X-?7*E zvz$vW@a#d8YacqEYs~Wu{MN9%a_ZfOa^9A6uBhdG>=nLJ5Fk~WlWck4!biAv&tum3 zr5`1)%kO!_WxZ1&`e5l4xg^tlAKu?MASd-Ps;l&NS%G}c0*}aI1_r*vAk26?e?<>p|MCw6> z?K+zxc*{4;^v(Il-L?O~l0P2*gaXZ`xJdGNu!-!nEoHNPxBYi-&Uahe=Qi_;=bf+Q z^ZhqjF=v7PBg>q$S(m;%<}%N4WR15t)0*nhr&rh)=Ht3~Z%jnny^NAZGoMGSw|8#f zIc#w((`4SU;vI93@*KM`Ph4!H(BiZ1H6kBv=Y8now@DIz9Ql{uYGd>Eg6qA;calVy zXB;T}M05N#F`njxg HN@xNAI1rR$ literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-150/mail-forward.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-150/mail-forward.png new file mode 100644 index 0000000000000000000000000000000000000000..a10134b02add48f7374d3f64ea2ea908b28fb9f7 GIT binary patch literal 558 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 Z{s7H40h}>iYY+ec002ovPDHLkV1mo(JCy(c literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-200/mail-junk.png b/Wino.Mail.WinUI/Assets/NotificationIcons/theme-light/scale-200/mail-junk.png new file mode 100644 index 0000000000000000000000000000000000000000..7f32d4bcce3dbfa116a8bc3b28f3d8a41b4fb03e GIT binary patch literal 697 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 literal 0 HcmV?d00001 diff --git a/Wino.Mail.WinUI/Services/NavigationService.cs b/Wino.Mail.WinUI/Services/NavigationService.cs index cdf45cca..233df6e3 100644 --- a/Wino.Mail.WinUI/Services/NavigationService.cs +++ b/Wino.Mail.WinUI/Services/NavigationService.cs @@ -79,6 +79,7 @@ public class NavigationService : NavigationServiceBase, INavigationService WinoPage.AboutPage, WinoPage.PersonalizationPage, WinoPage.MessageListPage, + WinoPage.MailNotificationSettingsPage, WinoPage.ReadComposePanePage, WinoPage.AppPreferencesPage, WinoPage.AliasManagementPage, @@ -142,6 +143,7 @@ public class NavigationService : NavigationServiceBase, INavigationService WinoPage.AboutPage => typeof(AboutPage), WinoPage.PersonalizationPage => typeof(PersonalizationPage), WinoPage.MessageListPage => typeof(MessageListPage), + WinoPage.MailNotificationSettingsPage => typeof(MailNotificationSettingsPage), WinoPage.ReadComposePanePage => typeof(ReadComposePanePage), WinoPage.MailRenderingPage => typeof(MailRenderingPage), WinoPage.ComposePage => typeof(ComposePage), diff --git a/Wino.Mail.WinUI/Services/NotificationBuilder.cs b/Wino.Mail.WinUI/Services/NotificationBuilder.cs index 3a134f50..63813f09 100644 --- a/Wino.Mail.WinUI/Services/NotificationBuilder.cs +++ b/Wino.Mail.WinUI/Services/NotificationBuilder.cs @@ -17,6 +17,7 @@ using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Enums; using Wino.Core.Domain.Extensions; using Wino.Core.Domain.Interfaces; +using Wino.Helpers; using Wino.Mail.WinUI.Activation; using Wino.Messaging.UI; @@ -26,6 +27,16 @@ public class NotificationBuilder : INotificationBuilder { private const string NotificationIconRootUri = "ms-appx:///Assets/NotificationIcons/"; private static int _calendarTaskbarBadgeCount; + private static readonly MailOperation[] SupportedMailNotificationActions = + [ + MailOperation.MarkAsRead, + MailOperation.SoftDelete, + MailOperation.MoveToJunk, + MailOperation.Archive, + MailOperation.Reply, + MailOperation.ReplyAll, + MailOperation.Forward + ]; private readonly IAccountService _accountService; private readonly IFolderService _folderService; @@ -76,6 +87,7 @@ public class NotificationBuilder : INotificationBuilder builder.AddText(Translator.Notifications_MultipleNotificationsTitle); builder.AddText(string.Format(Translator.Notifications_MultipleNotificationsMessage, mailCount)); builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); + builder.AddButton(CreateDismissButton()); builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Mail")); ShowNotification(builder); @@ -167,6 +179,7 @@ public class NotificationBuilder : INotificationBuilder builder.AddButton(new AppNotificationButton(Translator.Buttons_FixAccount) .AddArgument(Constants.ToastMailAccountIdKey, account.Id.ToString()) .AddArgument(Constants.ToastModeKey, Constants.ToastModeMail)); + builder.AddButton(CreateDismissButton()); ShowNotification(builder); } @@ -177,6 +190,7 @@ public class NotificationBuilder : INotificationBuilder builder.AddText(Translator.Exception_WebView2RuntimeMissing_Title); builder.AddText(Translator.Exception_WebView2RuntimeMissing_Message); builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); + builder.AddButton(CreateDismissButton()); ShowNotification(builder); } @@ -188,6 +202,7 @@ public class NotificationBuilder : INotificationBuilder builder.AddText(Translator.Notifications_StoreUpdateAvailableMessage); builder.AddArgument(Constants.ToastStoreUpdateActionKey, Constants.ToastStoreUpdateActionInstall); builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); + builder.AddButton(CreateDismissButton()); ShowNotification(builder, "store-update-available"); } @@ -255,6 +270,8 @@ public class NotificationBuilder : INotificationBuilder .AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar)); } + builder.AddButton(CreateDismissButton()); + var tag = $"calendar-reminder-{calendarItem.Id:N}-{reminderDurationInSeconds}"; ShowNotification(builder, tag); @@ -288,9 +305,11 @@ public class NotificationBuilder : INotificationBuilder builder.AddArgument(Constants.ToastMailUniqueIdKey, mailItem.UniqueId.ToString()); builder.AddArgument(Constants.ToastActionKey, MailOperation.Navigate.ToString()); builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); - builder.AddButton(GetMarkAsReadButton(mailItem.UniqueId)); - builder.AddButton(GetDeleteButton(mailItem.UniqueId)); - builder.AddButton(GetArchiveButton(mailItem.UniqueId)); + + var (firstAction, secondAction) = GetConfiguredMailNotificationActions(); + builder.AddButton(CreateMailNotificationActionButton(firstAction, mailItem.UniqueId)); + builder.AddButton(CreateMailNotificationActionButton(secondAction, mailItem.UniqueId)); + builder.AddButton(CreateDismissButton()); builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Mail")); ShowNotification(builder, mailItem.UniqueId.ToString()); @@ -347,26 +366,55 @@ public class NotificationBuilder : INotificationBuilder return string.Format(Translator.CalendarReminder_StartedMinutesAgo, minutesAgo); } - private AppNotificationButton GetArchiveButton(Guid mailUniqueId) - => new AppNotificationButton(Translator.MailOperation_Archive) - .SetIcon(GetNotificationIconUri("mail-archive")) + private (MailOperation FirstAction, MailOperation SecondAction) GetConfiguredMailNotificationActions() + { + var firstAction = ResolveMailNotificationAction(_preferencesService.FirstMailNotificationAction, MailOperation.MarkAsRead); + var secondAction = ResolveMailNotificationAction(_preferencesService.SecondMailNotificationAction, MailOperation.SoftDelete); + + if (secondAction == firstAction) + { + secondAction = SupportedMailNotificationActions.First(action => action != firstAction); + } + + return (firstAction, secondAction); + } + + private static MailOperation ResolveMailNotificationAction(MailOperation configuredAction, MailOperation fallbackAction) + => SupportedMailNotificationActions.Contains(configuredAction) ? configuredAction : fallbackAction; + + private AppNotificationButton CreateMailNotificationActionButton(MailOperation action, Guid mailUniqueId) + { + var button = new AppNotificationButton(XamlHelpers.GetOperationString(action)) .AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString()) - .AddArgument(Constants.ToastActionKey, MailOperation.Archive.ToString()) + .AddArgument(Constants.ToastActionKey, action.ToString()) .AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); - private AppNotificationButton GetDeleteButton(Guid mailUniqueId) - => new AppNotificationButton(Translator.MailOperation_Delete) - .SetIcon(GetNotificationIconUri("mail-delete")) - .AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString()) - .AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete.ToString()) - .AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); + var iconUri = GetMailActionIconUri(action); + if (iconUri != null) + { + button.SetIcon(iconUri); + } - private AppNotificationButton GetMarkAsReadButton(Guid mailUniqueId) - => new AppNotificationButton(Translator.MailOperation_MarkAsRead) - .SetIcon(GetNotificationIconUri("mail-markread")) - .AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString()) - .AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead.ToString()) - .AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); + return button; + } + + private static Uri? GetMailActionIconUri(MailOperation action) + => action switch + { + MailOperation.Archive => GetNotificationIconUri("mail-archive"), + MailOperation.SoftDelete => GetNotificationIconUri("mail-delete"), + MailOperation.MarkAsRead => GetNotificationIconUri("mail-markread"), + MailOperation.MoveToJunk => GetNotificationIconUri("mail-junk"), + MailOperation.Reply => GetNotificationIconUri("mail-reply"), + MailOperation.ReplyAll => GetNotificationIconUri("mail-replyall"), + MailOperation.Forward => GetNotificationIconUri("mail-forward"), + _ => null + }; + + private static AppNotificationButton CreateDismissButton() + => new AppNotificationButton(Translator.Buttons_Dismiss) + .SetIcon(GetNotificationIconUri("dismiss")) + .AddArgument(Constants.ToastDismissActionKey, bool.TrueString); private static AppNotificationBuilder CreateBuilder(AppNotificationScenario scenario = AppNotificationScenario.Default) => new AppNotificationBuilder().SetScenario(scenario); diff --git a/Wino.Mail.WinUI/Services/PreferencesService.cs b/Wino.Mail.WinUI/Services/PreferencesService.cs index cc1ed9bf..d7d3feb3 100644 --- a/Wino.Mail.WinUI/Services/PreferencesService.cs +++ b/Wino.Mail.WinUI/Services/PreferencesService.cs @@ -237,6 +237,18 @@ public class PreferencesService(IConfigurationService configurationService) : Ob set => SaveProperty(propertyName: nameof(StartupEntityId), value); } + public MailOperation FirstMailNotificationAction + { + get => _configurationService.Get(nameof(FirstMailNotificationAction), MailOperation.MarkAsRead); + set => SetPropertyAndSave(nameof(FirstMailNotificationAction), value); + } + + public MailOperation SecondMailNotificationAction + { + get => _configurationService.Get(nameof(SecondMailNotificationAction), MailOperation.SoftDelete); + set => SetPropertyAndSave(nameof(SecondMailNotificationAction), value); + } + public AppLanguage CurrentLanguage { get => _configurationService.Get(nameof(CurrentLanguage), TranslationService.DefaultAppLanguage); diff --git a/Wino.Mail.WinUI/Views/Abstract/MailNotificationSettingsPageAbstract.cs b/Wino.Mail.WinUI/Views/Abstract/MailNotificationSettingsPageAbstract.cs new file mode 100644 index 00000000..132714e3 --- /dev/null +++ b/Wino.Mail.WinUI/Views/Abstract/MailNotificationSettingsPageAbstract.cs @@ -0,0 +1,5 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract; + +public abstract class MailNotificationSettingsPageAbstract : SettingsPageBase { } diff --git a/Wino.Mail.WinUI/Views/Settings/MailNotificationSettingsPage.xaml b/Wino.Mail.WinUI/Views/Settings/MailNotificationSettingsPage.xaml new file mode 100644 index 00000000..034d5582 --- /dev/null +++ b/Wino.Mail.WinUI/Views/Settings/MailNotificationSettingsPage.xaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail.WinUI/Views/Settings/MailNotificationSettingsPage.xaml.cs b/Wino.Mail.WinUI/Views/Settings/MailNotificationSettingsPage.xaml.cs new file mode 100644 index 00000000..bb160fe5 --- /dev/null +++ b/Wino.Mail.WinUI/Views/Settings/MailNotificationSettingsPage.xaml.cs @@ -0,0 +1,11 @@ +using Wino.Views.Abstract; + +namespace Wino.Views.Settings; + +public sealed partial class MailNotificationSettingsPage : MailNotificationSettingsPageAbstract +{ + public MailNotificationSettingsPage() + { + InitializeComponent(); + } +}