Some experiments.

This commit is contained in:
Burak Kaan Köse
2026-04-14 15:02:57 +02:00
parent feff929333
commit bba3523ec6
9 changed files with 238 additions and 158 deletions
@@ -178,6 +178,18 @@ public partial class CalendarAppShellViewModel : CalendarBaseViewModel,
await InitializeAccountCalendarsAsync(); await InitializeAccountCalendarsAsync();
ValidateConfiguredNewEventCalendar(); ValidateConfiguredNewEventCalendar();
if (activationContext?.Parameter is CalendarItemTarget calendarItemTarget)
{
NavigationService.Navigate(WinoPage.EventDetailsPage, calendarItemTarget);
return;
}
if (activationContext?.Parameter is CalendarPageNavigationArgs calendarPageNavigationArgs)
{
NavigationService.Navigate(WinoPage.CalendarPage, calendarPageNavigationArgs);
return;
}
TodayClicked(); TodayClicked();
} }
+1
View File
@@ -21,6 +21,7 @@ public static class Constants
public const string ToastCalendarJoinOnlineAction = nameof(ToastCalendarJoinOnlineAction); public const string ToastCalendarJoinOnlineAction = nameof(ToastCalendarJoinOnlineAction);
public const string ToastCalendarSnoozeAction = nameof(ToastCalendarSnoozeAction); public const string ToastCalendarSnoozeAction = nameof(ToastCalendarSnoozeAction);
public const string ToastCalendarSnoozeDurationInputId = nameof(ToastCalendarSnoozeDurationInputId); public const string ToastCalendarSnoozeDurationInputId = nameof(ToastCalendarSnoozeDurationInputId);
public const string ToastCalendarSnoozeDurationMinutesKey = nameof(ToastCalendarSnoozeDurationMinutesKey);
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);
@@ -1,4 +1,4 @@
using Microsoft.Windows.AppNotifications; using System;
using Wino.Core.Domain; using Wino.Core.Domain;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
@@ -49,8 +49,31 @@ internal static class ToastActivationResolver
return true; return true;
} }
public static bool TryResolveMode(NotificationArguments toastArguments, out WinoApplicationMode mode)
{
mode = WinoApplicationMode.Mail;
if (!toastArguments.TryGetValue(Constants.ToastModeKey, out string toastMode))
return false;
if (string.Equals(toastMode, Constants.ToastModeCalendar, StringComparison.OrdinalIgnoreCase))
{
mode = WinoApplicationMode.Calendar;
return true;
}
if (string.Equals(toastMode, Constants.ToastModeMail, StringComparison.OrdinalIgnoreCase))
{
mode = WinoApplicationMode.Mail;
return true;
}
return false;
}
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.ToastModeKey, out string _) ||
toastArguments.TryGetValue(Constants.ToastCalendarActionKey, out string _) || toastArguments.TryGetValue(Constants.ToastCalendarActionKey, out string _) ||
toastArguments.TryGetValue(Constants.ToastActionKey, out string _); toastArguments.TryGetValue(Constants.ToastActionKey, out string _);
} }
+108 -40
View File
@@ -4,8 +4,8 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Dispatching; using Microsoft.UI.Dispatching;
@@ -16,7 +16,6 @@ using Microsoft.Windows.AppNotifications;
using MimeKit.Cryptography; using MimeKit.Cryptography;
using Windows.ApplicationModel.Activation; using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.DataTransfer.ShareTarget;
using Windows.Storage; using Windows.Storage;
using Wino.Calendar.ViewModels; using Wino.Calendar.ViewModels;
using Wino.Calendar.ViewModels.Interfaces; using Wino.Calendar.ViewModels.Interfaces;
@@ -543,23 +542,20 @@ public partial class App : WinoApplication,
{ {
var activationArgs = AppInstance.GetCurrent().GetActivatedEventArgs(); var activationArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
if (activationArgs.Kind != ExtendedActivationKind.AppNotification || if (activationArgs.Kind == ExtendedActivationKind.Launch &&
activationArgs.Data is not AppNotificationActivatedEventArgs toastArgs || activationArgs.Data is ILaunchActivatedEventArgs launchArgs &&
!TryMarkInitialNotificationActivationHandled()) ToastActivationResolver.TryParse(launchArgs.Arguments, out var launchToastArguments) &&
TryMarkInitialNotificationActivationHandled())
{ {
return; LogActivation($"Processing initial toast launch activation from application startup. Arguments: {launchArgs.Arguments}");
}
LogActivation($"Processing initial notification activation from application startup. Arguments: {toastArgs.Argument}");
await EnsureActivationInfrastructureAsync(); await EnsureActivationInfrastructureAsync();
await HandleToastActivationAsync(toastArgs.Argument, toastArgs.UserInput); await HandleToastActivationAsync(launchToastArguments);
}
} }
private void AppNotificationInvoked(AppNotificationManager sender, AppNotificationActivatedEventArgs args) private void AppNotificationInvoked(AppNotificationManager sender, AppNotificationActivatedEventArgs args)
{ {
// AppNotification callbacks are not guaranteed to run on the UI thread.
// Marshal toast handling to the window dispatcher before touching window APIs.
if (MainWindow?.DispatcherQueue?.TryEnqueue(() => _ = HandleToastActivationAsync(args.Argument, args.UserInput)) == true) if (MainWindow?.DispatcherQueue?.TryEnqueue(() => _ = HandleToastActivationAsync(args.Argument, args.UserInput)) == true)
return; return;
@@ -569,19 +565,7 @@ public partial class App : WinoApplication,
private void TryRegisterAppNotifications() private void TryRegisterAppNotifications()
{ {
var notificationManager = AppNotificationManager.Default; // Classic targeted toasts use normal launch activation instead of COM toast activators.
notificationManager.NotificationInvoked -= AppNotificationInvoked;
notificationManager.NotificationInvoked += AppNotificationInvoked;
try
{
notificationManager.Register();
}
catch (Exception ex)
{
LogActivation($"App notification registration failed: {ex.GetType().Name} - {ex.Message}");
}
} }
/// <summary> /// <summary>
@@ -611,7 +595,7 @@ public partial class App : WinoApplication,
if (calendarAction == Constants.ToastCalendarSnoozeAction) if (calendarAction == Constants.ToastCalendarSnoozeAction)
{ {
await HandleCalendarToastSnoozeAsync(userInput, calendarItemId); await HandleCalendarToastSnoozeAsync(toastArguments, userInput, calendarItemId);
return; return;
} }
@@ -656,6 +640,26 @@ public partial class App : WinoApplication,
return HandleToastActivationAsync(toastArguments, userInput); return HandleToastActivationAsync(toastArguments, userInput);
} }
private static int? GetToastSnoozeDurationMinutes(NotificationArguments toastArguments, IDictionary<string, string>? userInput)
{
if (toastArguments.TryGetValue(Constants.ToastCalendarSnoozeDurationMinutesKey, out var snoozeDurationValue) &&
int.TryParse(snoozeDurationValue, out var snoozeDurationMinutes) &&
snoozeDurationMinutes > 0)
{
return snoozeDurationMinutes;
}
if (userInput != null &&
userInput.TryGetValue(Constants.ToastCalendarSnoozeDurationInputId, out var selectedValue) &&
int.TryParse(selectedValue, out snoozeDurationMinutes) &&
snoozeDurationMinutes > 0)
{
return snoozeDurationMinutes;
}
return null;
}
private async Task<bool> HandleShareTargetActivationAsync(AppActivationArguments activationArgs, bool activateWindow) private async Task<bool> HandleShareTargetActivationAsync(AppActivationArguments activationArgs, bool activateWindow)
{ {
if (activationArgs.Kind != ExtendedActivationKind.ShareTarget || if (activationArgs.Kind != ExtendedActivationKind.ShareTarget ||
@@ -810,9 +814,59 @@ public partial class App : WinoApplication,
navigationService.Navigate(WinoPage.EventDetailsPage, target); navigationService.Navigate(WinoPage.EventDetailsPage, target);
} }
private async Task HandleCalendarToastSnoozeAsync(IDictionary<string, string>? userInput, Guid calendarItemId) private async Task<object?> TryCreateToastNavigationParameterAsync(NotificationArguments toastArguments)
{ {
if (!TryGetSnoozeDurationMinutes(userInput, out var snoozeDurationMinutes)) if (toastArguments.TryGetValue(Constants.ToastCalendarActionKey, out string calendarAction) &&
calendarAction == Constants.ToastCalendarNavigateAction &&
toastArguments.TryGetValue(Constants.ToastCalendarItemIdKey, out string calendarItemIdString) &&
Guid.TryParse(calendarItemIdString, out Guid calendarItemId))
{
var calendarService = Services.GetRequiredService<ICalendarService>();
var calendarItem = await calendarService.GetCalendarItemAsync(calendarItemId);
if (calendarItem != null)
{
return new CalendarItemTarget(calendarItem, CalendarEventTargetType.Single);
}
}
return null;
}
private async Task<(WinoApplicationMode Mode, object? Parameter)?> TryResolveToastActivationTargetAsync(AppActivationArguments activationArgs)
{
NotificationArguments? toastArguments = null;
if (activationArgs.Kind == ExtendedActivationKind.Launch &&
activationArgs.Data is ILaunchActivatedEventArgs launchArgs &&
ToastActivationResolver.TryParse(launchArgs.Arguments, out var launchToastArguments))
{
toastArguments = launchToastArguments;
}
else if (activationArgs.Kind == ExtendedActivationKind.AppNotification &&
activationArgs.Data is AppNotificationActivatedEventArgs appNotificationArgs &&
ToastActivationResolver.TryParse(appNotificationArgs.Argument, out var appNotificationToastArguments))
{
toastArguments = appNotificationToastArguments;
}
else if (activationArgs.Data is ToastNotificationActivatedEventArgs classicToastArgs &&
ToastActivationResolver.TryParse(classicToastArgs.Argument, out var classicToastArguments))
{
toastArguments = classicToastArguments;
}
if (toastArguments == null ||
!ToastActivationResolver.TryResolveMode(toastArguments, out var mode))
{
return null;
}
return (mode, await TryCreateToastNavigationParameterAsync(toastArguments));
}
private async Task HandleCalendarToastSnoozeAsync(NotificationArguments toastArguments, IDictionary<string, string>? userInput, Guid calendarItemId)
{
if (!TryGetSnoozeDurationMinutes(toastArguments, userInput, out var snoozeDurationMinutes))
return; return;
var calendarService = Services.GetRequiredService<ICalendarService>(); var calendarService = Services.GetRequiredService<ICalendarService>();
@@ -836,22 +890,15 @@ public partial class App : WinoApplication,
await nativeAppService.LaunchUriAsync(joinUri); await nativeAppService.LaunchUriAsync(joinUri);
} }
private bool TryGetSnoozeDurationMinutes(IDictionary<string, string>? userInput, out int snoozeDurationMinutes) private bool TryGetSnoozeDurationMinutes(NotificationArguments toastArguments, IDictionary<string, string>? userInput, out int snoozeDurationMinutes)
{ {
snoozeDurationMinutes = _preferencesService?.DefaultSnoozeDurationInMinutes ?? 0; snoozeDurationMinutes = GetToastSnoozeDurationMinutes(toastArguments, userInput)
?? _preferencesService?.DefaultSnoozeDurationInMinutes
?? 0;
if (userInput == null ||
!userInput.TryGetValue(Constants.ToastCalendarSnoozeDurationInputId, out var selectedValue) ||
selectedValue == null)
{
return snoozeDurationMinutes > 0; return snoozeDurationMinutes > 0;
} }
var selectedText = selectedValue.ToString();
return int.TryParse(selectedText, out snoozeDurationMinutes) && snoozeDurationMinutes > 0;
}
/// <summary> /// <summary>
/// Handles toast notification click for navigation. /// Handles toast notification click for navigation.
/// Creates window if not running, sets up navigation parameter. /// Creates window if not running, sets up navigation parameter.
@@ -1583,10 +1630,25 @@ public partial class App : WinoApplication,
} }
} }
else if (TryResolveActivationMode(args, _preferencesService?.DefaultApplicationMode ?? WinoApplicationMode.Mail, out var redirectedMode)) else if (TryResolveActivationMode(args, _preferencesService?.DefaultApplicationMode ?? WinoApplicationMode.Mail, out var redirectedMode))
{
var navigationService = Services.GetRequiredService<INavigationService>();
var toastActivationTarget = await TryResolveToastActivationTargetAsync(args);
if (toastActivationTarget is { Parameter: CalendarItemTarget calendarTarget })
{
navigationService.ChangeApplicationMode(toastActivationTarget.Value.Mode, new ShellModeActivationContext
{
SuppressStartupFlows = true,
Parameter = calendarTarget
});
navigationService.Navigate(WinoPage.EventDetailsPage, calendarTarget);
}
else
{ {
shellWindow.HandleAppActivation(AppEntryConstants.GetModeLaunchArgument(redirectedMode)); shellWindow.HandleAppActivation(AppEntryConstants.GetModeLaunchArgument(redirectedMode));
} }
} }
}
// Redirected launches can target a shell window that is currently hidden in the tray. // Redirected launches can target a shell window that is currently hidden in the tray.
// Restore it through the window manager so Show/BringToFront/Activate happen together. // Restore it through the window manager so Show/BringToFront/Activate happen together.
@@ -1597,7 +1659,6 @@ public partial class App : WinoApplication,
} }
} }
// Dispatch to UI thread since this is called from Program.OnActivated.
if (MainWindow?.DispatcherQueue.TryEnqueue(() => _ = HandleRedirectedActivationAsync()) == true) if (MainWindow?.DispatcherQueue.TryEnqueue(() => _ = HandleRedirectedActivationAsync()) == true)
return; return;
@@ -1642,6 +1703,13 @@ public partial class App : WinoApplication,
return true; return true;
} }
if (activationArgs.Data is ToastNotificationActivatedEventArgs classicToastArgs &&
ToastActivationResolver.TryParse(classicToastArgs.Argument, out var classicToastArguments) &&
ToastActivationResolver.TryResolveMode(classicToastArguments, out mode))
{
return true;
}
if (activationArgs.Kind == ExtendedActivationKind.File && if (activationArgs.Kind == ExtendedActivationKind.File &&
activationArgs.Data is IFileActivatedEventArgs fileArgs) activationArgs.Data is IFileActivatedEventArgs fileArgs)
{ {
+3 -31
View File
@@ -6,10 +6,8 @@
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10" xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap uap10 rescap com desktop"> IgnorableNamespaces="uap uap10 rescap">
<!-- Publisher Cache Folders --> <!-- Publisher Cache Folders -->
<Extensions> <Extensions>
@@ -49,14 +47,13 @@
uap10:Parameters="--wino-mail"> uap10:Parameters="--wino-mail">
<uap:VisualElements <uap:VisualElements
DisplayName="Wino Mail" DisplayName="Wino Mail"
Description="Wino.Mail.WinUI" Description="Wino Mail"
BackgroundColor="transparent" BackgroundColor="transparent"
Square150x150Logo="Assets\AppEntries\MailAssets\Square150x150Logo.png" Square150x150Logo="Assets\AppEntries\MailAssets\Square150x150Logo.png"
Square44x44Logo="Assets\AppEntries\MailAssets\Square44x44Logo.png"> Square44x44Logo="Assets\AppEntries\MailAssets\Square44x44Logo.png">
<uap:DefaultTile Wide310x150Logo="Assets\AppEntries\MailAssets\Wide310x150Logo.png" Square71x71Logo="Assets\AppEntries\MailAssets\SmallTile.png" Square310x310Logo="Assets\AppEntries\MailAssets\LargeTile.png"/> <uap:DefaultTile Wide310x150Logo="Assets\AppEntries\MailAssets\Wide310x150Logo.png" Square71x71Logo="Assets\AppEntries\MailAssets\SmallTile.png" Square310x310Logo="Assets\AppEntries\MailAssets\LargeTile.png"/>
<uap:SplashScreen Image="Assets\AppEntries\MailAssets\SplashScreen.png" /> <uap:SplashScreen Image="Assets\AppEntries\MailAssets\SplashScreen.png" />
</uap:VisualElements> </uap:VisualElements>
<Extensions> <Extensions>
<uap5:Extension Category="windows.startupTask"> <uap5:Extension Category="windows.startupTask">
<uap5:StartupTask <uap5:StartupTask
@@ -65,19 +62,6 @@
DisplayName="Wino Startup Service" /> DisplayName="Wino Startup Service" />
</uap5:Extension> </uap5:Extension>
<!-- App notification activation -->
<desktop:Extension Category="windows.toastNotificationActivation">
<desktop:ToastNotificationActivation ToastActivatorCLSID="72c6d2d0-2538-44fe-a1b1-499f47bb1181" />
</desktop:Extension>
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer Executable="Wino.Mail.WinUI.exe" Arguments="----AppNotificationActivated:" DisplayName="Toast activator">
<com:Class Id="72c6d2d0-2538-44fe-a1b1-499f47bb1181" DisplayName="Toast activator"/>
</com:ExeServer>
</com:ComServer>
</com:Extension>
<!-- Protocol activation: mailto --> <!-- Protocol activation: mailto -->
<uap:Extension Category="windows.protocol"> <uap:Extension Category="windows.protocol">
<uap:Protocol Name="mailto" /> <uap:Protocol Name="mailto" />
@@ -124,7 +108,7 @@
uap10:Parameters="--wino-calendar"> uap10:Parameters="--wino-calendar">
<uap:VisualElements <uap:VisualElements
DisplayName="Wino Calendar" DisplayName="Wino Calendar"
Description="Wino.Mail.WinUI.Calendar" Description="Wino Calendar"
BackgroundColor="transparent" BackgroundColor="transparent"
Square150x150Logo="Assets\AppEntries\CalendarAssets\Square150x150Logo.png" Square150x150Logo="Assets\AppEntries\CalendarAssets\Square150x150Logo.png"
Square44x44Logo="Assets\AppEntries\CalendarAssets\Square44x44Logo.png"> Square44x44Logo="Assets\AppEntries\CalendarAssets\Square44x44Logo.png">
@@ -133,18 +117,6 @@
</uap:VisualElements> </uap:VisualElements>
<Extensions> <Extensions>
<desktop:Extension Category="windows.toastNotificationActivation">
<desktop:ToastNotificationActivation ToastActivatorCLSID="44c05d2b-aa1d-4e59-9d7d-8b4c8607cb8d" />
</desktop:Extension>
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer Executable="Wino.Mail.WinUI.exe" Arguments="----AppNotificationActivated:" DisplayName="Calendar toast activator">
<com:Class Id="44c05d2b-aa1d-4e59-9d7d-8b4c8607cb8d" DisplayName="Calendar toast activator"/>
</com:ExeServer>
</com:ComServer>
</com:Extension>
<uap:Extension Category="windows.protocol"> <uap:Extension Category="windows.protocol">
<uap:Protocol Name="webcal"> <uap:Protocol Name="webcal">
<uap:DisplayName>Calendar Protocol</uap:DisplayName> <uap:DisplayName>Calendar Protocol</uap:DisplayName>
+7
View File
@@ -207,6 +207,13 @@ public class Program
: true; : true;
} }
if (args.Data is Windows.ApplicationModel.Activation.ToastNotificationActivatedEventArgs classicToastArgs)
{
return ToastActivationResolver.TryParse(classicToastArgs.Argument, out var toastArguments)
? ToastActivationResolver.ShouldBringToForeground(toastArguments)
: true;
}
if (args.Kind == ExtendedActivationKind.Launch && if (args.Kind == ExtendedActivationKind.Launch &&
args.Data is Windows.ApplicationModel.Activation.ILaunchActivatedEventArgs launchArgs && args.Data is Windows.ApplicationModel.Activation.ILaunchActivatedEventArgs launchArgs &&
ToastActivationResolver.TryParse(launchArgs.Arguments, out var launchToastArguments)) ToastActivationResolver.TryParse(launchArgs.Arguments, out var launchToastArguments))
+43 -47
View File
@@ -5,8 +5,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Windows.AppNotifications; using CommunityToolkit.WinUI.Notifications;
using Microsoft.Windows.AppNotifications.Builder;
using Serilog; using Serilog;
using Windows.Data.Xml.Dom; using Windows.Data.Xml.Dom;
using Windows.UI.Notifications; using Windows.UI.Notifications;
@@ -76,9 +75,9 @@ 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.SetAudioUri(new Uri("ms-winsoundevent:Notification.Mail")); builder.AddAudio(new Uri("ms-winsoundevent:Notification.Mail"));
ShowNotification(builder); ShowNotification(builder, WinoApplicationMode.Mail);
} }
else else
{ {
@@ -146,9 +145,9 @@ public class NotificationBuilder : INotificationBuilder
{ {
try try
{ {
AppNotificationManager.Default.RemoveByTagAsync(mailUniqueId.ToString()).AsTask().GetAwaiter().GetResult(); ToastNotificationManager.History.Remove(mailUniqueId.ToString());
} }
catch (ArgumentException) catch (Exception ex) when (ex is ArgumentException or InvalidOperationException)
{ {
} }
catch (Exception ex) catch (Exception ex)
@@ -164,11 +163,12 @@ public class NotificationBuilder : INotificationBuilder
builder.AddText(string.Format(Translator.Exception_AccountNeedsAttention_Message, account.Name)); builder.AddText(string.Format(Translator.Exception_AccountNeedsAttention_Message, account.Name));
builder.AddArgument(Constants.ToastMailAccountIdKey, account.Id.ToString()); builder.AddArgument(Constants.ToastMailAccountIdKey, account.Id.ToString());
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
builder.AddButton(new AppNotificationButton(Translator.Buttons_FixAccount) builder.AddButton(new ToastButton()
.SetContent(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));
ShowNotification(builder); ShowNotification(builder, WinoApplicationMode.Mail);
} }
public void CreateWebView2RuntimeMissingNotification() public void CreateWebView2RuntimeMissingNotification()
@@ -178,7 +178,7 @@ public class NotificationBuilder : INotificationBuilder
builder.AddText(Translator.Exception_WebView2RuntimeMissing_Message); builder.AddText(Translator.Exception_WebView2RuntimeMissing_Message);
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
ShowNotification(builder); ShowNotification(builder, WinoApplicationMode.Mail);
} }
public void CreateStoreUpdateNotification() public void CreateStoreUpdateNotification()
@@ -189,7 +189,7 @@ public class NotificationBuilder : INotificationBuilder
builder.AddArgument(Constants.ToastStoreUpdateActionKey, Constants.ToastStoreUpdateActionInstall); builder.AddArgument(Constants.ToastStoreUpdateActionKey, Constants.ToastStoreUpdateActionInstall);
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
ShowNotification(builder, "store-update-available"); ShowNotification(builder, WinoApplicationMode.Mail, "store-update-available");
} }
public Task CreateCalendarReminderNotificationAsync(CalendarItem calendarItem, long reminderDurationInSeconds) public Task CreateCalendarReminderNotificationAsync(CalendarItem calendarItem, long reminderDurationInSeconds)
@@ -197,7 +197,7 @@ public class NotificationBuilder : INotificationBuilder
if (calendarItem == null) if (calendarItem == null)
return Task.CompletedTask; return Task.CompletedTask;
var builder = CreateBuilder(AppNotificationScenario.Reminder); var builder = CreateBuilder(ToastScenario.Reminder);
var localStart = calendarItem.GetLocalStartDate(); var localStart = calendarItem.GetLocalStartDate();
var reminderContext = GetCalendarReminderContext(localStart, DateTime.Now); var reminderContext = GetCalendarReminderContext(localStart, DateTime.Now);
@@ -210,7 +210,7 @@ public class NotificationBuilder : INotificationBuilder
builder.AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarNavigateAction); builder.AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarNavigateAction);
builder.AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString()); builder.AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString());
builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar); builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar);
builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Reminder")); builder.AddAudio(new Uri("ms-winsoundevent:Notification.Reminder"));
var allowedSnoozeMinutes = CalendarReminderSnoozeOptions.GetAllowedSnoozeMinutes( var allowedSnoozeMinutes = CalendarReminderSnoozeOptions.GetAllowedSnoozeMinutes(
reminderDurationInSeconds, reminderDurationInSeconds,
@@ -223,40 +223,33 @@ public class NotificationBuilder : INotificationBuilder
? preferredSnoozeMinutes ? preferredSnoozeMinutes
: allowedSnoozeMinutes[0]; : allowedSnoozeMinutes[0];
var selectionBox = new AppNotificationComboBox(Constants.ToastCalendarSnoozeDurationInputId) builder.AddButton(new ToastButton()
.SetSelectedItem(defaultSnoozeMinutes.ToString()); .SetContent(Translator.CalendarReminder_SnoozeAction)
.SetImageUri(GetNotificationIconUri("calendar-snooze"))
foreach (var snoozeMinutes in allowedSnoozeMinutes)
{
selectionBox.AddItem(
snoozeMinutes.ToString(),
string.Format(Translator.CalendarReminder_SnoozeMinutesOption, snoozeMinutes));
}
builder.AddComboBox(selectionBox);
builder.AddButton(new AppNotificationButton(Translator.CalendarReminder_SnoozeAction)
.SetIcon(GetNotificationIconUri("calendar-snooze"))
.AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarSnoozeAction) .AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarSnoozeAction)
.AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString()) .AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString())
.AddArgument(Constants.ToastCalendarSnoozeDurationMinutesKey, defaultSnoozeMinutes.ToString())
.AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar)); .AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar));
} }
builder.AddButton(new AppNotificationButton(Translator.Buttons_Open) builder.AddButton(new ToastButton()
.SetContent(Translator.Buttons_Open)
.AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarNavigateAction) .AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarNavigateAction)
.AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString()) .AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString())
.AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar)); .AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar));
if (Uri.TryCreate(calendarItem.HtmlLink, UriKind.Absolute, out _)) if (Uri.TryCreate(calendarItem.HtmlLink, UriKind.Absolute, out _))
{ {
builder.AddButton(new AppNotificationButton(Translator.CalendarEventDetails_JoinOnline) builder.AddButton(new ToastButton()
.SetIcon(GetNotificationIconUri("calendar-join")) .SetContent(Translator.CalendarEventDetails_JoinOnline)
.SetImageUri(GetNotificationIconUri("calendar-join"))
.AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarJoinOnlineAction) .AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarJoinOnlineAction)
.AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString()) .AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString())
.AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar)); .AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar));
} }
var tag = $"calendar-reminder-{calendarItem.Id:N}-{reminderDurationInSeconds}"; var tag = $"calendar-reminder-{calendarItem.Id:N}-{reminderDurationInSeconds}";
ShowNotification(builder, tag); ShowNotification(builder, WinoApplicationMode.Calendar, tag);
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -278,10 +271,10 @@ public class NotificationBuilder : INotificationBuilder
await stream.WriteAsync(bytes); await stream.WriteAsync(bytes);
} }
builder.SetAppLogoOverride(new Uri($"ms-appdata:///temp/{tempFile.Name}"), AppNotificationImageCrop.Default); builder.AddAppLogoOverride(new Uri($"ms-appdata:///temp/{tempFile.Name}"), ToastGenericAppLogoCrop.Default);
} }
builder.SetTimeStamp(mailItem.CreationDate.ToLocalTime()); builder.AddCustomTimeStamp(mailItem.CreationDate.ToLocalTime());
builder.AddText(mailItem.FromName); builder.AddText(mailItem.FromName);
builder.AddText(mailItem.Subject); builder.AddText(mailItem.Subject);
builder.AddText(mailItem.PreviewText); builder.AddText(mailItem.PreviewText);
@@ -291,9 +284,9 @@ public class NotificationBuilder : INotificationBuilder
builder.AddButton(GetMarkAsReadButton(mailItem.UniqueId)); builder.AddButton(GetMarkAsReadButton(mailItem.UniqueId));
builder.AddButton(GetDeleteButton(mailItem.UniqueId)); builder.AddButton(GetDeleteButton(mailItem.UniqueId));
builder.AddButton(GetArchiveButton(mailItem.UniqueId)); builder.AddButton(GetArchiveButton(mailItem.UniqueId));
builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Mail")); builder.AddAudio(new Uri("ms-winsoundevent:Notification.Mail"));
ShowNotification(builder, mailItem.UniqueId.ToString()); ShowNotification(builder, WinoApplicationMode.Mail, mailItem.UniqueId.ToString());
} }
private void UpdateBadge(string applicationId, int? badgeCount) private void UpdateBadge(string applicationId, int? badgeCount)
@@ -347,40 +340,43 @@ public class NotificationBuilder : INotificationBuilder
return string.Format(Translator.CalendarReminder_StartedMinutesAgo, minutesAgo); return string.Format(Translator.CalendarReminder_StartedMinutesAgo, minutesAgo);
} }
private AppNotificationButton GetArchiveButton(Guid mailUniqueId) private ToastButton GetArchiveButton(Guid mailUniqueId)
=> new AppNotificationButton(Translator.MailOperation_Archive) => new ToastButton()
.SetIcon(GetNotificationIconUri("mail-archive")) .SetContent(Translator.MailOperation_Archive)
.SetImageUri(GetNotificationIconUri("mail-archive"))
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString()) .AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
.AddArgument(Constants.ToastActionKey, MailOperation.Archive.ToString()) .AddArgument(Constants.ToastActionKey, MailOperation.Archive.ToString())
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); .AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
private AppNotificationButton GetDeleteButton(Guid mailUniqueId) private ToastButton GetDeleteButton(Guid mailUniqueId)
=> new AppNotificationButton(Translator.MailOperation_Delete) => new ToastButton()
.SetIcon(GetNotificationIconUri("mail-delete")) .SetContent(Translator.MailOperation_Delete)
.SetImageUri(GetNotificationIconUri("mail-delete"))
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString()) .AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
.AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete.ToString()) .AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete.ToString())
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); .AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
private AppNotificationButton GetMarkAsReadButton(Guid mailUniqueId) private ToastButton GetMarkAsReadButton(Guid mailUniqueId)
=> new AppNotificationButton(Translator.MailOperation_MarkAsRead) => new ToastButton()
.SetIcon(GetNotificationIconUri("mail-markread")) .SetContent(Translator.MailOperation_MarkAsRead)
.SetImageUri(GetNotificationIconUri("mail-markread"))
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString()) .AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
.AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead.ToString()) .AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead.ToString())
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); .AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
private static AppNotificationBuilder CreateBuilder(AppNotificationScenario scenario = AppNotificationScenario.Default) private static ToastContentBuilder CreateBuilder(ToastScenario scenario = ToastScenario.Default)
=> new AppNotificationBuilder().SetScenario(scenario); => new ToastContentBuilder().SetToastScenario(scenario);
private static void ShowNotification(AppNotificationBuilder builder, string? tag = null) private static void ShowNotification(ToastContentBuilder builder, WinoApplicationMode mode, string? tag = null)
{ {
var notification = builder.BuildNotification(); var notification = new ToastNotification(builder.GetXml());
if (!string.IsNullOrWhiteSpace(tag)) if (!string.IsNullOrWhiteSpace(tag))
{ {
notification.Tag = tag; notification.Tag = tag;
} }
AppNotificationManager.Default.Show(notification); ToastNotificationManager.CreateToastNotifier(AppEntryConstants.GetAppUserModelId(mode)).Show(notification);
} }
private static Uri GetNotificationIconUri(string iconName) private static Uri GetNotificationIconUri(string iconName)
@@ -210,12 +210,12 @@
Background="{ThemeResource DividerStrokeColorDefaultBrush}" /> Background="{ThemeResource DividerStrokeColorDefaultBrush}" />
<!-- Test Notification --> <!-- Test Notification -->
<!--<Button Command="{x:Bind ViewModel.CreateTestNotificationCommand}" Style="{StaticResource TransparentActionButtonStyle}"> <Button Command="{x:Bind ViewModel.CreateTestNotificationCommand}" Style="{StaticResource TransparentActionButtonStyle}">
<StackPanel Orientation="Horizontal" Spacing="8"> <StackPanel Orientation="Horizontal" Spacing="8">
<coreControls:WinoFontIcon FontSize="16" Icon="Reminder" /> <coreControls:WinoFontIcon FontSize="16" Icon="Reminder" />
<TextBlock VerticalAlignment="Center" Text="Test notification" /> <TextBlock VerticalAlignment="Center" Text="Test notification" />
</StackPanel> </StackPanel>
</Button>--> </Button>
<!-- Edit Series --> <!-- Edit Series -->
<Border <Border
+1
View File
@@ -185,6 +185,7 @@
<PackageReference Include="CommunityToolkit.WinUI.Controls.TokenizingTextBox" /> <PackageReference Include="CommunityToolkit.WinUI.Controls.TokenizingTextBox" />
<PackageReference Include="CommunityToolkit.WinUI.Extensions" /> <PackageReference Include="CommunityToolkit.WinUI.Extensions" />
<PackageReference Include="CommunityToolkit.WinUI.Lottie" /> <PackageReference Include="CommunityToolkit.WinUI.Lottie" />
<PackageReference Include="CommunityToolkit.WinUI.Notifications" />
<PackageReference Include="Microsoft.Graphics.Win2D" /> <PackageReference Include="Microsoft.Graphics.Win2D" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" /> <PackageReference Include="Microsoft.Windows.SDK.BuildTools" />