Merge configurable mail notification actions
@@ -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.
|
- 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.
|
- 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)`).
|
- 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`.
|
- 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(...)`.
|
- 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.
|
- 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.
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public static class Constants
|
|||||||
public const string ToastModeKey = nameof(ToastModeKey);
|
public const string ToastModeKey = nameof(ToastModeKey);
|
||||||
public const string ToastModeMail = nameof(ToastModeMail);
|
public const string ToastModeMail = nameof(ToastModeMail);
|
||||||
public const string ToastModeCalendar = nameof(ToastModeCalendar);
|
public const string ToastModeCalendar = nameof(ToastModeCalendar);
|
||||||
|
public const string ToastDismissActionKey = nameof(ToastDismissActionKey);
|
||||||
public const string ToastStoreUpdateActionKey = nameof(ToastStoreUpdateActionKey);
|
public const string ToastStoreUpdateActionKey = nameof(ToastStoreUpdateActionKey);
|
||||||
public const string ToastStoreUpdateActionInstall = nameof(ToastStoreUpdateActionInstall);
|
public const string ToastStoreUpdateActionInstall = nameof(ToastStoreUpdateActionInstall);
|
||||||
public const string ClientLogFile = "Client_.log";
|
public const string ClientLogFile = "Client_.log";
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public enum WinoPage
|
|||||||
AboutPage,
|
AboutPage,
|
||||||
PersonalizationPage,
|
PersonalizationPage,
|
||||||
MessageListPage,
|
MessageListPage,
|
||||||
|
MailNotificationSettingsPage,
|
||||||
MailListPage,
|
MailListPage,
|
||||||
ReadComposePanePage,
|
ReadComposePanePage,
|
||||||
AppPreferencesPage,
|
AppPreferencesPage,
|
||||||
|
|||||||
@@ -192,6 +192,16 @@ public interface IPreferencesService : INotifyPropertyChanged
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Guid? StartupEntityId { get; set; }
|
Guid? StartupEntityId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Setting: First action button displayed on mail toast notifications.
|
||||||
|
/// </summary>
|
||||||
|
MailOperation FirstMailNotificationAction { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Setting: Second action button displayed on mail toast notifications.
|
||||||
|
/// </summary>
|
||||||
|
MailOperation SecondMailNotificationAction { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -68,6 +68,11 @@ public static class SettingsNavigationInfoProvider
|
|||||||
Translator.SettingsMessageList_Description,
|
Translator.SettingsMessageList_Description,
|
||||||
"\uE8C4",
|
"\uE8C4",
|
||||||
searchKeywords: Translator.SettingsSearch_MessageList_Keywords),
|
searchKeywords: Translator.SettingsSearch_MessageList_Keywords),
|
||||||
|
new(WinoPage.MailNotificationSettingsPage,
|
||||||
|
Translator.SettingsMailNotifications_Title,
|
||||||
|
Translator.SettingsMailNotifications_Description,
|
||||||
|
"\uE7F4",
|
||||||
|
searchKeywords: Translator.SettingsSearch_MailNotifications_Keywords),
|
||||||
new(WinoPage.ReadComposePanePage,
|
new(WinoPage.ReadComposePanePage,
|
||||||
Translator.SettingsReadComposePane_Title,
|
Translator.SettingsReadComposePane_Title,
|
||||||
Translator.SettingsReadComposePane_Description,
|
Translator.SettingsReadComposePane_Description,
|
||||||
@@ -149,6 +154,7 @@ public static class SettingsNavigationInfoProvider
|
|||||||
WinoPage.PersonalizationPage => Translator.SettingsPersonalization_Title,
|
WinoPage.PersonalizationPage => Translator.SettingsPersonalization_Title,
|
||||||
WinoPage.AboutPage => Translator.SettingsAbout_Title,
|
WinoPage.AboutPage => Translator.SettingsAbout_Title,
|
||||||
WinoPage.MessageListPage => Translator.SettingsMessageList_Title,
|
WinoPage.MessageListPage => Translator.SettingsMessageList_Title,
|
||||||
|
WinoPage.MailNotificationSettingsPage => Translator.SettingsMailNotifications_Title,
|
||||||
WinoPage.ReadComposePanePage => Translator.SettingsReadComposePane_Title,
|
WinoPage.ReadComposePanePage => Translator.SettingsReadComposePane_Title,
|
||||||
WinoPage.AppPreferencesPage => Translator.SettingsAppPreferences_Title,
|
WinoPage.AppPreferencesPage => Translator.SettingsAppPreferences_Title,
|
||||||
WinoPage.CalendarSettingsPage => Translator.CalendarSettings_Preferences_Title,
|
WinoPage.CalendarSettingsPage => Translator.CalendarSettings_Preferences_Title,
|
||||||
|
|||||||
@@ -84,6 +84,7 @@
|
|||||||
"Buttons_Delete": "Delete",
|
"Buttons_Delete": "Delete",
|
||||||
"Buttons_Deny": "Deny",
|
"Buttons_Deny": "Deny",
|
||||||
"Buttons_Discard": "Discard",
|
"Buttons_Discard": "Discard",
|
||||||
|
"Buttons_Dismiss": "Dismiss",
|
||||||
"Buttons_Edit": "Edit",
|
"Buttons_Edit": "Edit",
|
||||||
"Buttons_EnableImageRendering": "Enable",
|
"Buttons_EnableImageRendering": "Enable",
|
||||||
"Buttons_Multiselect": "Select Multiple",
|
"Buttons_Multiselect": "Select Multiple",
|
||||||
@@ -910,6 +911,14 @@
|
|||||||
"SettingsMarkAsRead_WhenSelected": "When selected",
|
"SettingsMarkAsRead_WhenSelected": "When selected",
|
||||||
"SettingsMessageList_Description": "Change how your messages should be organized in mail list.",
|
"SettingsMessageList_Description": "Change how your messages should be organized in mail list.",
|
||||||
"SettingsMessageList_Title": "Message 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.",
|
"SettingsNoAccountSetupMessage": "You didn't setup any accounts yet.",
|
||||||
"SettingsNotifications_Description": "Turn on or off notifications for this account.",
|
"SettingsNotifications_Description": "Turn on or off notifications for this account.",
|
||||||
"SettingsNotifications_Title": "Notifications",
|
"SettingsNotifications_Title": "Notifications",
|
||||||
@@ -946,6 +955,7 @@
|
|||||||
"SettingsSearch_About_Keywords": "about;version;website;privacy;github;donate;store;support",
|
"SettingsSearch_About_Keywords": "about;version;website;privacy;github;donate;store;support",
|
||||||
"SettingsSearch_KeyboardShortcuts_Keywords": "shortcut;shortcuts;hotkey;hotkeys;keyboard;keys",
|
"SettingsSearch_KeyboardShortcuts_Keywords": "shortcut;shortcuts;hotkey;hotkeys;keyboard;keys",
|
||||||
"SettingsSearch_MessageList_Keywords": "message;messages;list;threading;threads;avatar;preview;sender",
|
"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_ReadComposePane_Keywords": "reader;compose;composer;font;fonts;external content;display;reading",
|
||||||
"SettingsSearch_SignatureAndEncryption_Keywords": "signature;signatures;encryption;certificate;certificates;s mime;smime;security",
|
"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",
|
"SettingsSearch_Storage_Keywords": "storage;cache;caching;mime;disk;space;cleanup;clean up;local data",
|
||||||
|
|||||||
@@ -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<MailNotificationActionOption> 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;
|
||||||
|
}
|
||||||
@@ -41,9 +41,14 @@ internal static class ToastActivationResolver
|
|||||||
return calendarAction == Constants.ToastCalendarNavigateAction;
|
return calendarAction == Constants.ToastCalendarNavigateAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (toastArguments.TryGetValue(Constants.ToastDismissActionKey, out string _))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (toastArguments.TryGetValue(Constants.ToastActionKey, out MailOperation mailAction))
|
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;
|
return true;
|
||||||
@@ -52,5 +57,6 @@ internal static class ToastActivationResolver
|
|||||||
private static bool ContainsKnownToastKey(NotificationArguments toastArguments)
|
private static bool ContainsKnownToastKey(NotificationArguments toastArguments)
|
||||||
=> toastArguments.TryGetValue(Constants.ToastStoreUpdateActionKey, out string _) ||
|
=> toastArguments.TryGetValue(Constants.ToastStoreUpdateActionKey, out string _) ||
|
||||||
toastArguments.TryGetValue(Constants.ToastCalendarActionKey, out string _) ||
|
toastArguments.TryGetValue(Constants.ToastCalendarActionKey, out string _) ||
|
||||||
|
toastArguments.TryGetValue(Constants.ToastDismissActionKey, out string _) ||
|
||||||
toastArguments.TryGetValue(Constants.ToastActionKey, out string _);
|
toastArguments.TryGetValue(Constants.ToastActionKey, out string _);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -358,6 +358,7 @@ public partial class App : WinoApplication,
|
|||||||
services.AddTransient(typeof(AccountDetailsPageViewModel));
|
services.AddTransient(typeof(AccountDetailsPageViewModel));
|
||||||
services.AddTransient(typeof(SignatureManagementPageViewModel));
|
services.AddTransient(typeof(SignatureManagementPageViewModel));
|
||||||
services.AddTransient(typeof(MessageListPageViewModel));
|
services.AddTransient(typeof(MessageListPageViewModel));
|
||||||
|
services.AddTransient(typeof(MailNotificationSettingsPageViewModel));
|
||||||
services.AddTransient(typeof(ReadComposePanePageViewModel));
|
services.AddTransient(typeof(ReadComposePanePageViewModel));
|
||||||
services.AddTransient(typeof(MergedAccountDetailsPageViewModel));
|
services.AddTransient(typeof(MergedAccountDetailsPageViewModel));
|
||||||
services.AddTransient(typeof(AppPreferencesPageViewModel));
|
services.AddTransient(typeof(AppPreferencesPageViewModel));
|
||||||
@@ -599,6 +600,12 @@ public partial class App : WinoApplication,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (toastArguments.TryGetValue(Constants.ToastDismissActionKey, out string _))
|
||||||
|
{
|
||||||
|
LogActivation("Handling notification dismiss action.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check calendar reminder toast activation first.
|
// Check calendar reminder toast activation first.
|
||||||
if (toastArguments.TryGetValue(Constants.ToastCalendarActionKey, out string calendarAction) &&
|
if (toastArguments.TryGetValue(Constants.ToastCalendarActionKey, out string calendarAction) &&
|
||||||
toastArguments.TryGetValue(Constants.ToastCalendarItemIdKey, out string calendarItemIdString) &&
|
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.
|
// User clicked notification - create window if needed and navigate.
|
||||||
await HandleToastNavigationAsync(mailItemUniqueId);
|
await HandleToastNavigationAsync(mailItemUniqueId);
|
||||||
}
|
}
|
||||||
|
else if (IsComposeToastAction(action))
|
||||||
|
{
|
||||||
|
await HandleToastComposeActionAsync(action, mailItemUniqueId);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// User clicked action button (Mark as Read, Delete, etc.)
|
// 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<IMailService>();
|
||||||
|
var folderService = Services.GetRequiredService<IFolderService>();
|
||||||
|
var mimeFileService = Services.GetRequiredService<IMimeFileService>();
|
||||||
|
var navigationService = Services.GetRequiredService<INavigationService>();
|
||||||
|
var requestDelegator = Services.GetRequiredService<IWinoRequestDelegator>();
|
||||||
|
var mailShellViewModel = Services.GetRequiredService<MailAppShellViewModel>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the main window and activates it.
|
/// Creates the main window and activates it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
|
After Width: | Height: | Size: 426 B |
|
After Width: | Height: | Size: 420 B |
|
After Width: | Height: | Size: 287 B |
|
After Width: | Height: | Size: 327 B |
|
After Width: | Height: | Size: 506 B |
|
After Width: | Height: | Size: 518 B |
|
After Width: | Height: | Size: 337 B |
|
After Width: | Height: | Size: 399 B |
|
After Width: | Height: | Size: 608 B |
|
After Width: | Height: | Size: 601 B |
|
After Width: | Height: | Size: 365 B |
|
After Width: | Height: | Size: 455 B |
|
After Width: | Height: | Size: 776 B |
|
After Width: | Height: | Size: 759 B |
|
After Width: | Height: | Size: 481 B |
|
After Width: | Height: | Size: 597 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 767 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 380 B |
|
After Width: | Height: | Size: 386 B |
|
After Width: | Height: | Size: 271 B |
|
After Width: | Height: | Size: 305 B |
|
After Width: | Height: | Size: 473 B |
|
After Width: | Height: | Size: 481 B |
|
After Width: | Height: | Size: 318 B |
|
After Width: | Height: | Size: 363 B |
|
After Width: | Height: | Size: 558 B |
|
After Width: | Height: | Size: 550 B |
|
After Width: | Height: | Size: 347 B |
|
After Width: | Height: | Size: 424 B |
|
After Width: | Height: | Size: 691 B |
|
After Width: | Height: | Size: 697 B |
|
After Width: | Height: | Size: 452 B |
|
After Width: | Height: | Size: 552 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 705 B |
|
After Width: | Height: | Size: 974 B |
@@ -79,6 +79,7 @@ public class NavigationService : NavigationServiceBase, INavigationService
|
|||||||
WinoPage.AboutPage,
|
WinoPage.AboutPage,
|
||||||
WinoPage.PersonalizationPage,
|
WinoPage.PersonalizationPage,
|
||||||
WinoPage.MessageListPage,
|
WinoPage.MessageListPage,
|
||||||
|
WinoPage.MailNotificationSettingsPage,
|
||||||
WinoPage.ReadComposePanePage,
|
WinoPage.ReadComposePanePage,
|
||||||
WinoPage.AppPreferencesPage,
|
WinoPage.AppPreferencesPage,
|
||||||
WinoPage.AliasManagementPage,
|
WinoPage.AliasManagementPage,
|
||||||
@@ -142,6 +143,7 @@ public class NavigationService : NavigationServiceBase, INavigationService
|
|||||||
WinoPage.AboutPage => typeof(AboutPage),
|
WinoPage.AboutPage => typeof(AboutPage),
|
||||||
WinoPage.PersonalizationPage => typeof(PersonalizationPage),
|
WinoPage.PersonalizationPage => typeof(PersonalizationPage),
|
||||||
WinoPage.MessageListPage => typeof(MessageListPage),
|
WinoPage.MessageListPage => typeof(MessageListPage),
|
||||||
|
WinoPage.MailNotificationSettingsPage => typeof(MailNotificationSettingsPage),
|
||||||
WinoPage.ReadComposePanePage => typeof(ReadComposePanePage),
|
WinoPage.ReadComposePanePage => typeof(ReadComposePanePage),
|
||||||
WinoPage.MailRenderingPage => typeof(MailRenderingPage),
|
WinoPage.MailRenderingPage => typeof(MailRenderingPage),
|
||||||
WinoPage.ComposePage => typeof(ComposePage),
|
WinoPage.ComposePage => typeof(ComposePage),
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ using Wino.Core.Domain.Entities.Shared;
|
|||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
using Wino.Core.Domain.Extensions;
|
using Wino.Core.Domain.Extensions;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
using Wino.Helpers;
|
||||||
using Wino.Mail.WinUI.Activation;
|
using Wino.Mail.WinUI.Activation;
|
||||||
using Wino.Messaging.UI;
|
using Wino.Messaging.UI;
|
||||||
|
|
||||||
@@ -26,6 +27,16 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
{
|
{
|
||||||
private const string NotificationIconRootUri = "ms-appx:///Assets/NotificationIcons/";
|
private const string NotificationIconRootUri = "ms-appx:///Assets/NotificationIcons/";
|
||||||
private static int _calendarTaskbarBadgeCount;
|
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 IAccountService _accountService;
|
||||||
private readonly IFolderService _folderService;
|
private readonly IFolderService _folderService;
|
||||||
@@ -76,6 +87,7 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
builder.AddText(Translator.Notifications_MultipleNotificationsTitle);
|
builder.AddText(Translator.Notifications_MultipleNotificationsTitle);
|
||||||
builder.AddText(string.Format(Translator.Notifications_MultipleNotificationsMessage, mailCount));
|
builder.AddText(string.Format(Translator.Notifications_MultipleNotificationsMessage, mailCount));
|
||||||
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||||
|
builder.AddButton(CreateDismissButton());
|
||||||
builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Mail"));
|
builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Mail"));
|
||||||
|
|
||||||
ShowNotification(builder);
|
ShowNotification(builder);
|
||||||
@@ -167,6 +179,7 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
builder.AddButton(new AppNotificationButton(Translator.Buttons_FixAccount)
|
builder.AddButton(new AppNotificationButton(Translator.Buttons_FixAccount)
|
||||||
.AddArgument(Constants.ToastMailAccountIdKey, account.Id.ToString())
|
.AddArgument(Constants.ToastMailAccountIdKey, account.Id.ToString())
|
||||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail));
|
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail));
|
||||||
|
builder.AddButton(CreateDismissButton());
|
||||||
|
|
||||||
ShowNotification(builder);
|
ShowNotification(builder);
|
||||||
}
|
}
|
||||||
@@ -177,6 +190,7 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
builder.AddText(Translator.Exception_WebView2RuntimeMissing_Title);
|
builder.AddText(Translator.Exception_WebView2RuntimeMissing_Title);
|
||||||
builder.AddText(Translator.Exception_WebView2RuntimeMissing_Message);
|
builder.AddText(Translator.Exception_WebView2RuntimeMissing_Message);
|
||||||
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||||
|
builder.AddButton(CreateDismissButton());
|
||||||
|
|
||||||
ShowNotification(builder);
|
ShowNotification(builder);
|
||||||
}
|
}
|
||||||
@@ -188,6 +202,7 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
builder.AddText(Translator.Notifications_StoreUpdateAvailableMessage);
|
builder.AddText(Translator.Notifications_StoreUpdateAvailableMessage);
|
||||||
builder.AddArgument(Constants.ToastStoreUpdateActionKey, Constants.ToastStoreUpdateActionInstall);
|
builder.AddArgument(Constants.ToastStoreUpdateActionKey, Constants.ToastStoreUpdateActionInstall);
|
||||||
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||||
|
builder.AddButton(CreateDismissButton());
|
||||||
|
|
||||||
ShowNotification(builder, "store-update-available");
|
ShowNotification(builder, "store-update-available");
|
||||||
}
|
}
|
||||||
@@ -255,6 +270,8 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar));
|
.AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builder.AddButton(CreateDismissButton());
|
||||||
|
|
||||||
var tag = $"calendar-reminder-{calendarItem.Id:N}-{reminderDurationInSeconds}";
|
var tag = $"calendar-reminder-{calendarItem.Id:N}-{reminderDurationInSeconds}";
|
||||||
ShowNotification(builder, tag);
|
ShowNotification(builder, tag);
|
||||||
|
|
||||||
@@ -288,9 +305,11 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
builder.AddArgument(Constants.ToastMailUniqueIdKey, mailItem.UniqueId.ToString());
|
builder.AddArgument(Constants.ToastMailUniqueIdKey, mailItem.UniqueId.ToString());
|
||||||
builder.AddArgument(Constants.ToastActionKey, MailOperation.Navigate.ToString());
|
builder.AddArgument(Constants.ToastActionKey, MailOperation.Navigate.ToString());
|
||||||
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||||
builder.AddButton(GetMarkAsReadButton(mailItem.UniqueId));
|
|
||||||
builder.AddButton(GetDeleteButton(mailItem.UniqueId));
|
var (firstAction, secondAction) = GetConfiguredMailNotificationActions();
|
||||||
builder.AddButton(GetArchiveButton(mailItem.UniqueId));
|
builder.AddButton(CreateMailNotificationActionButton(firstAction, mailItem.UniqueId));
|
||||||
|
builder.AddButton(CreateMailNotificationActionButton(secondAction, mailItem.UniqueId));
|
||||||
|
builder.AddButton(CreateDismissButton());
|
||||||
builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Mail"));
|
builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Mail"));
|
||||||
|
|
||||||
ShowNotification(builder, mailItem.UniqueId.ToString());
|
ShowNotification(builder, mailItem.UniqueId.ToString());
|
||||||
@@ -347,26 +366,55 @@ public class NotificationBuilder : INotificationBuilder
|
|||||||
return string.Format(Translator.CalendarReminder_StartedMinutesAgo, minutesAgo);
|
return string.Format(Translator.CalendarReminder_StartedMinutesAgo, minutesAgo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AppNotificationButton GetArchiveButton(Guid mailUniqueId)
|
private (MailOperation FirstAction, MailOperation SecondAction) GetConfiguredMailNotificationActions()
|
||||||
=> new AppNotificationButton(Translator.MailOperation_Archive)
|
{
|
||||||
.SetIcon(GetNotificationIconUri("mail-archive"))
|
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.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
||||||
.AddArgument(Constants.ToastActionKey, MailOperation.Archive.ToString())
|
.AddArgument(Constants.ToastActionKey, action.ToString())
|
||||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
||||||
|
|
||||||
private AppNotificationButton GetDeleteButton(Guid mailUniqueId)
|
var iconUri = GetMailActionIconUri(action);
|
||||||
=> new AppNotificationButton(Translator.MailOperation_Delete)
|
if (iconUri != null)
|
||||||
.SetIcon(GetNotificationIconUri("mail-delete"))
|
{
|
||||||
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
button.SetIcon(iconUri);
|
||||||
.AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete.ToString())
|
}
|
||||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
|
||||||
|
|
||||||
private AppNotificationButton GetMarkAsReadButton(Guid mailUniqueId)
|
return button;
|
||||||
=> new AppNotificationButton(Translator.MailOperation_MarkAsRead)
|
}
|
||||||
.SetIcon(GetNotificationIconUri("mail-markread"))
|
|
||||||
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
private static Uri? GetMailActionIconUri(MailOperation action)
|
||||||
.AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead.ToString())
|
=> action switch
|
||||||
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
|
{
|
||||||
|
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)
|
private static AppNotificationBuilder CreateBuilder(AppNotificationScenario scenario = AppNotificationScenario.Default)
|
||||||
=> new AppNotificationBuilder().SetScenario(scenario);
|
=> new AppNotificationBuilder().SetScenario(scenario);
|
||||||
|
|||||||
@@ -237,6 +237,18 @@ public class PreferencesService(IConfigurationService configurationService) : Ob
|
|||||||
set => SaveProperty(propertyName: nameof(StartupEntityId), value);
|
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
|
public AppLanguage CurrentLanguage
|
||||||
{
|
{
|
||||||
get => _configurationService.Get(nameof(CurrentLanguage), TranslationService.DefaultAppLanguage);
|
get => _configurationService.Get(nameof(CurrentLanguage), TranslationService.DefaultAppLanguage);
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using Wino.Mail.ViewModels;
|
||||||
|
|
||||||
|
namespace Wino.Views.Abstract;
|
||||||
|
|
||||||
|
public abstract class MailNotificationSettingsPageAbstract : SettingsPageBase<MailNotificationSettingsPageViewModel> { }
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<abstract:MailNotificationSettingsPageAbstract
|
||||||
|
x:Class="Wino.Views.Settings.MailNotificationSettingsPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:abstract="using:Wino.Views.Abstract"
|
||||||
|
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:domain="using:Wino.Core.Domain"
|
||||||
|
xmlns:mailViewModels="using:Wino.Mail.ViewModels"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<ScrollViewer>
|
||||||
|
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
|
<controls:SettingsExpander
|
||||||
|
Description="{x:Bind domain:Translator.SettingsMailNotifications_Actions_Description}"
|
||||||
|
Header="{x:Bind domain:Translator.SettingsMailNotifications_Actions_Title}"
|
||||||
|
IsExpanded="True">
|
||||||
|
<controls:SettingsExpander.HeaderIcon>
|
||||||
|
<PathIcon Data="F1 M 6.347656 16.25 L 3.701172 16.25 C 3.375651 16.25 3.064779 16.18327 2.768555 16.049805 C 2.472331 15.916342 2.211914 15.737305 1.987305 15.512695 C 1.762695 15.288086 1.583659 15.02767 1.450195 14.731445 C 1.316732 14.435222 1.25 14.12435 1.25 13.798828 L 1.25 3.701172 C 1.25 3.375652 1.316732 3.064779 1.450195 2.768555 C 1.583659 2.472332 1.762695 2.211914 1.987305 1.987305 C 2.211914 1.762695 2.472331 1.58366 2.768555 1.450195 C 3.064779 1.316732 3.375651 1.25 3.701172 1.25 L 16.298828 1.25 C 16.624348 1.25 16.935221 1.316732 17.231445 1.450195 C 17.527668 1.58366 17.788086 1.762695 18.012695 1.987305 C 18.237305 2.211914 18.41634 2.472332 18.549805 2.768555 C 18.683268 3.064779 18.75 3.375652 18.75 3.701172 L 18.75 13.798828 C 18.75 14.13737 18.681641 14.454753 18.544922 14.750977 C 18.408203 15.047201 18.22591 15.30599 17.998047 15.527344 C 17.770182 15.748698 17.504883 15.924479 17.202148 16.054688 C 16.899414 16.184896 16.582031 16.25 16.25 16.25 L 13.652344 16.25 L 10.46875 19.794922 C 10.345052 19.931641 10.188802 20 10 20 C 9.811197 20 9.654947 19.931641 9.53125 19.794922 Z M 15.625 7.5 C 15.79427 7.5 15.940754 7.438151 16.064453 7.314453 C 16.18815 7.190756 16.25 7.044271 16.25 6.875 C 16.25 6.705729 16.18815 6.559245 16.064453 6.435547 C 15.940754 6.31185 15.79427 6.25 15.625 6.25 L 4.375 6.25 C 4.205729 6.25 4.059245 6.31185 3.935547 6.435547 C 3.811849 6.559245 3.75 6.705729 3.75 6.875 C 3.75 7.044271 3.811849 7.190756 3.935547 7.314453 C 4.059245 7.438151 4.205729 7.5 4.375 7.5 Z M 15.625 11.25 C 15.79427 11.25 15.940754 11.188151 16.064453 11.064453 C 16.18815 10.940756 16.25 10.794271 16.25 10.625 C 16.25 10.455729 16.18815 10.309245 16.064453 10.185547 C 15.940754 10.06185 15.79427 10 15.625 10 L 4.375 10 C 4.205729 10 4.059245 10.06185 3.935547 10.185547 C 3.811849 10.309245 3.75 10.455729 3.75 10.625 C 3.75 10.794271 3.811849 10.940756 3.935547 11.064453 C 4.059245 11.188151 4.205729 11.25 4.375 11.25 Z " />
|
||||||
|
</controls:SettingsExpander.HeaderIcon>
|
||||||
|
<controls:SettingsExpander.Items>
|
||||||
|
<controls:SettingsCard Description="{x:Bind domain:Translator.SettingsMailNotifications_FirstAction_Description}" Header="{x:Bind domain:Translator.SettingsMailNotifications_FirstAction_Title}">
|
||||||
|
|
||||||
|
<ComboBox ItemsSource="{x:Bind ViewModel.AvailableNotificationActions, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedFirstAction, Mode=TwoWay}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="mailViewModels:MailNotificationActionOption">
|
||||||
|
<TextBlock Text="{x:Bind DisplayText}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</controls:SettingsCard>
|
||||||
|
|
||||||
|
<controls:SettingsCard Description="{x:Bind domain:Translator.SettingsMailNotifications_SecondAction_Description}" Header="{x:Bind domain:Translator.SettingsMailNotifications_SecondAction_Title}">
|
||||||
|
|
||||||
|
<ComboBox ItemsSource="{x:Bind ViewModel.AvailableNotificationActions, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedSecondAction, Mode=TwoWay}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="mailViewModels:MailNotificationActionOption">
|
||||||
|
<TextBlock Text="{x:Bind DisplayText}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</controls:SettingsCard>
|
||||||
|
</controls:SettingsExpander.Items>
|
||||||
|
</controls:SettingsExpander>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</abstract:MailNotificationSettingsPageAbstract>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Wino.Views.Abstract;
|
||||||
|
|
||||||
|
namespace Wino.Views.Settings;
|
||||||
|
|
||||||
|
public sealed partial class MailNotificationSettingsPage : MailNotificationSettingsPageAbstract
|
||||||
|
{
|
||||||
|
public MailNotificationSettingsPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||