Fix AppNotification multi-entry notifications

This commit is contained in:
Burak Kaan Köse
2026-04-11 13:02:41 +02:00
parent 5cb49efeb4
commit 24626d1c31
2 changed files with 67 additions and 102 deletions
+67 -101
View File
@@ -5,10 +5,10 @@ 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 CommunityToolkit.WinUI.Notifications; using Microsoft.Windows.AppNotifications;
using Microsoft.Windows.AppNotifications.Builder;
using Serilog; using Serilog;
using Windows.Data.Xml.Dom; using Windows.Data.Xml.Dom;
using Windows.Storage;
using Windows.UI.Notifications; using Windows.UI.Notifications;
using Wino.Core.Domain; using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Calendar; using Wino.Core.Domain.Entities.Calendar;
@@ -72,16 +72,13 @@ public class NotificationBuilder : INotificationBuilder
if (mailCount > 3) if (mailCount > 3)
{ {
var builder = new ToastContentBuilder() var builder = CreateBuilder();
.AddText(Translator.Notifications_MultipleNotificationsTitle) builder.AddText(Translator.Notifications_MultipleNotificationsTitle);
.AddText(string.Format(Translator.Notifications_MultipleNotificationsMessage, mailCount)) builder.AddText(string.Format(Translator.Notifications_MultipleNotificationsMessage, mailCount));
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail) builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
.AddAudio(new ToastAudio builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Mail"));
{
Src = new Uri("ms-winsoundevent:Notification.Mail")
});
ShowToast(builder, ToastTargetApp.Mail); ShowNotification(builder);
} }
else else
{ {
@@ -149,10 +146,7 @@ public class NotificationBuilder : INotificationBuilder
{ {
try try
{ {
ToastNotificationManager.History.Remove( AppNotificationManager.Default.RemoveByTagAsync(mailUniqueId.ToString()).AsTask().GetAwaiter().GetResult();
mailUniqueId.ToString(),
null,
AppEntryConstants.GetAppUserModelId(WinoApplicationMode.Mail));
} }
catch (ArgumentException) catch (ArgumentException)
{ {
@@ -165,38 +159,37 @@ public class NotificationBuilder : INotificationBuilder
public void CreateAttentionRequiredNotification(MailAccount account) public void CreateAttentionRequiredNotification(MailAccount account)
{ {
var builder = new ToastContentBuilder() var builder = CreateBuilder();
.AddText(Translator.Exception_AccountNeedsAttention_Title) builder.AddText(Translator.Exception_AccountNeedsAttention_Title);
.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.ToastModeKey, Constants.ToastModeMail);
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));
.AddButton(new ToastButton()
.SetContent(Translator.Buttons_FixAccount)
.AddArgument(Constants.ToastMailAccountIdKey, account.Id.ToString())
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail));
ShowToast(builder, ToastTargetApp.Mail); ShowNotification(builder);
} }
public void CreateWebView2RuntimeMissingNotification() public void CreateWebView2RuntimeMissingNotification()
{ {
var builder = new ToastContentBuilder() var builder = CreateBuilder();
.AddText(Translator.Exception_WebView2RuntimeMissing_Title) builder.AddText(Translator.Exception_WebView2RuntimeMissing_Title);
.AddText(Translator.Exception_WebView2RuntimeMissing_Message) builder.AddText(Translator.Exception_WebView2RuntimeMissing_Message);
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
ShowToast(builder, ToastTargetApp.Mail); ShowNotification(builder);
} }
public void CreateStoreUpdateNotification() public void CreateStoreUpdateNotification()
{ {
var builder = new ToastContentBuilder() var builder = CreateBuilder();
.AddText(Translator.Notifications_StoreUpdateAvailableTitle) builder.AddText(Translator.Notifications_StoreUpdateAvailableTitle);
.AddText(Translator.Notifications_StoreUpdateAvailableMessage) builder.AddText(Translator.Notifications_StoreUpdateAvailableMessage);
.AddArgument(Constants.ToastStoreUpdateActionKey, Constants.ToastStoreUpdateActionInstall) builder.AddArgument(Constants.ToastStoreUpdateActionKey, Constants.ToastStoreUpdateActionInstall);
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); builder.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
ShowToast(builder, ToastTargetApp.Mail, "store-update-available"); ShowNotification(builder, "store-update-available");
} }
public Task CreateCalendarReminderNotificationAsync(CalendarItem calendarItem, long reminderDurationInSeconds) public Task CreateCalendarReminderNotificationAsync(CalendarItem calendarItem, long reminderDurationInSeconds)
@@ -204,8 +197,7 @@ public class NotificationBuilder : INotificationBuilder
if (calendarItem == null) if (calendarItem == null)
return Task.CompletedTask; return Task.CompletedTask;
var builder = new ToastContentBuilder() var builder = CreateBuilder(AppNotificationScenario.Reminder);
.SetToastScenario(ToastScenario.Reminder);
var localStart = calendarItem.GetLocalStartDate(); var localStart = calendarItem.GetLocalStartDate();
var reminderContext = GetCalendarReminderContext(localStart, DateTime.Now); var reminderContext = GetCalendarReminderContext(localStart, DateTime.Now);
@@ -218,10 +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.AddAudio(new ToastAudio builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Reminder"));
{
Src = new Uri("ms-winsoundevent:Notification.Reminder")
});
var allowedSnoozeMinutes = CalendarReminderSnoozeOptions.GetAllowedSnoozeMinutes( var allowedSnoozeMinutes = CalendarReminderSnoozeOptions.GetAllowedSnoozeMinutes(
reminderDurationInSeconds, reminderDurationInSeconds,
@@ -234,52 +223,45 @@ public class NotificationBuilder : INotificationBuilder
? preferredSnoozeMinutes ? preferredSnoozeMinutes
: allowedSnoozeMinutes[0]; : allowedSnoozeMinutes[0];
var selectionBox = new ToastSelectionBox(Constants.ToastCalendarSnoozeDurationInputId) var selectionBox = new AppNotificationComboBox(Constants.ToastCalendarSnoozeDurationInputId)
{ .SetSelectedItem(defaultSnoozeMinutes.ToString());
DefaultSelectionBoxItemId = defaultSnoozeMinutes.ToString()
};
foreach (var snoozeMinutes in allowedSnoozeMinutes) foreach (var snoozeMinutes in allowedSnoozeMinutes)
{ {
selectionBox.Items.Add(new ToastSelectionBoxItem( selectionBox.AddItem(
snoozeMinutes.ToString(), snoozeMinutes.ToString(),
string.Format(Translator.CalendarReminder_SnoozeMinutesOption, snoozeMinutes))); string.Format(Translator.CalendarReminder_SnoozeMinutesOption, snoozeMinutes));
} }
builder.AddToastInput(selectionBox); builder.AddComboBox(selectionBox);
builder.AddButton(new ToastButton() builder.AddButton(new AppNotificationButton(Translator.CalendarReminder_SnoozeAction)
.SetContent(Translator.CalendarReminder_SnoozeAction) .SetIcon(GetNotificationIconUri("calendar-snooze"))
.SetImageUri(GetNotificationIconUri("calendar-snooze"))
.SetBackgroundActivation()
.AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarSnoozeAction) .AddArgument(Constants.ToastCalendarActionKey, Constants.ToastCalendarSnoozeAction)
.AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString()) .AddArgument(Constants.ToastCalendarItemIdKey, calendarItem.Id.ToString())
.AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar)); .AddArgument(Constants.ToastModeKey, Constants.ToastModeCalendar));
} }
builder.AddButton(new ToastButton() builder.AddButton(new AppNotificationButton(Translator.Buttons_Open)
.SetContent(Translator.Buttons_Open)
.SetBackgroundActivation()
.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 var joinUri)) if (Uri.TryCreate(calendarItem.HtmlLink, UriKind.Absolute, out var joinUri))
{ {
builder.AddButton(new ToastButton() builder.AddButton(new AppNotificationButton(Translator.CalendarEventDetails_JoinOnline)
.SetContent(Translator.CalendarEventDetails_JoinOnline) .SetIcon(GetNotificationIconUri("calendar-join"))
.SetImageUri(GetNotificationIconUri("calendar-join")) .SetInvokeUri(joinUri));
.SetProtocolActivation(joinUri));
} }
var tag = $"calendar-reminder-{calendarItem.Id:N}-{reminderDurationInSeconds}"; var tag = $"calendar-reminder-{calendarItem.Id:N}-{reminderDurationInSeconds}";
ShowToast(builder, ToastTargetApp.Calendar, tag); ShowNotification(builder, tag);
return Task.CompletedTask; return Task.CompletedTask;
} }
private async Task CreateSingleNotificationAsync(MailCopy mailItem) private async Task CreateSingleNotificationAsync(MailCopy mailItem)
{ {
var builder = new ToastContentBuilder(); var builder = CreateBuilder();
var avatarThumbnail = await _thumbnailService.GetThumbnailAsync(mailItem.FromAddress, awaitLoad: true); var avatarThumbnail = await _thumbnailService.GetThumbnailAsync(mailItem.FromAddress, awaitLoad: true);
if (!string.IsNullOrEmpty(avatarThumbnail)) if (!string.IsNullOrEmpty(avatarThumbnail))
@@ -294,25 +276,22 @@ public class NotificationBuilder : INotificationBuilder
await stream.WriteAsync(bytes); await stream.WriteAsync(bytes);
} }
builder.AddAppLogoOverride(new Uri($"ms-appdata:///temp/{tempFile.Name}"), ToastGenericAppLogoCrop.Default); builder.SetAppLogoOverride(new Uri($"ms-appdata:///temp/{tempFile.Name}"), AppNotificationImageCrop.Default);
} }
builder.AddCustomTimeStamp(mailItem.CreationDate.ToLocalTime()); builder.SetTimeStamp(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);
builder.AddArgument(Constants.ToastMailUniqueIdKey, mailItem.UniqueId.ToString()); builder.AddArgument(Constants.ToastMailUniqueIdKey, mailItem.UniqueId.ToString());
builder.AddArgument(Constants.ToastActionKey, MailOperation.Navigate); 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(GetMarkAsReadButton(mailItem.UniqueId));
builder.AddButton(GetDeleteButton(mailItem.UniqueId)); builder.AddButton(GetDeleteButton(mailItem.UniqueId));
builder.AddButton(GetArchiveButton(mailItem.UniqueId)); builder.AddButton(GetArchiveButton(mailItem.UniqueId));
builder.AddAudio(new ToastAudio builder.SetAudioUri(new Uri("ms-winsoundevent:Notification.Mail"));
{
Src = new Uri("ms-winsoundevent:Notification.Mail")
});
ShowToast(builder, ToastTargetApp.Mail, mailItem.UniqueId.ToString()); ShowNotification(builder, mailItem.UniqueId.ToString());
} }
private void UpdateBadge(string applicationId, int? badgeCount) private void UpdateBadge(string applicationId, int? badgeCount)
@@ -366,55 +345,42 @@ public class NotificationBuilder : INotificationBuilder
return string.Format(Translator.CalendarReminder_StartedMinutesAgo, minutesAgo); return string.Format(Translator.CalendarReminder_StartedMinutesAgo, minutesAgo);
} }
private ToastButton GetArchiveButton(Guid mailUniqueId) private AppNotificationButton GetArchiveButton(Guid mailUniqueId)
=> new ToastButton() => new AppNotificationButton(Translator.MailOperation_Archive)
.SetContent(Translator.MailOperation_Archive) .SetIcon(GetNotificationIconUri("mail-archive"))
.SetImageUri(GetNotificationIconUri("mail-archive"))
.SetBackgroundActivation()
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString()) .AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
.AddArgument(Constants.ToastActionKey, MailOperation.Archive) .AddArgument(Constants.ToastActionKey, MailOperation.Archive.ToString())
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); .AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
private ToastButton GetDeleteButton(Guid mailUniqueId) private AppNotificationButton GetDeleteButton(Guid mailUniqueId)
=> new ToastButton() => new AppNotificationButton(Translator.MailOperation_Delete)
.SetContent(Translator.MailOperation_Delete) .SetIcon(GetNotificationIconUri("mail-delete"))
.SetImageUri(GetNotificationIconUri("mail-delete"))
.SetBackgroundActivation()
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString()) .AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
.AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete) .AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete.ToString())
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); .AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
private ToastButton GetMarkAsReadButton(Guid mailUniqueId) private AppNotificationButton GetMarkAsReadButton(Guid mailUniqueId)
=> new ToastButton() => new AppNotificationButton(Translator.MailOperation_MarkAsRead)
.SetContent(Translator.MailOperation_MarkAsRead) .SetIcon(GetNotificationIconUri("mail-markread"))
.SetImageUri(GetNotificationIconUri("mail-markread"))
.SetBackgroundActivation()
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString()) .AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
.AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead) .AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead.ToString())
.AddArgument(Constants.ToastModeKey, Constants.ToastModeMail); .AddArgument(Constants.ToastModeKey, Constants.ToastModeMail);
private static void ShowToast(ToastContentBuilder builder, ToastTargetApp targetApp, string? tag = null) private static AppNotificationBuilder CreateBuilder(AppNotificationScenario scenario = AppNotificationScenario.Default)
=> new AppNotificationBuilder().SetScenario(scenario);
private static void ShowNotification(AppNotificationBuilder builder, string? tag = null)
{ {
var toastNotification = new ToastNotification(builder.GetToastContent().GetXml()); var notification = builder.BuildNotification();
if (!string.IsNullOrWhiteSpace(tag)) if (!string.IsNullOrWhiteSpace(tag))
{ {
toastNotification.Tag = tag; notification.Tag = tag;
} }
ToastNotificationManager AppNotificationManager.Default.Show(notification);
.CreateToastNotifier(AppEntryConstants.GetAppUserModelId(targetApp == ToastTargetApp.Calendar
? WinoApplicationMode.Calendar
: WinoApplicationMode.Mail))
.Show(toastNotification);
} }
private static Uri GetNotificationIconUri(string iconName) private static Uri GetNotificationIconUri(string iconName)
=> new($"{NotificationIconRootUri}{iconName}.png"); => new($"{NotificationIconRootUri}{iconName}.png");
private enum ToastTargetApp
{
Mail,
Calendar
}
} }
-1
View File
@@ -174,7 +174,6 @@
<PackageReference Include="CommunityToolkit.Diagnostics" /> <PackageReference Include="CommunityToolkit.Diagnostics" />
<PackageReference Include="CommunityToolkit.Mvvm" /> <PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="CommunityToolkit.Labs.WinUI.DependencyPropertyGenerator" /> <PackageReference Include="CommunityToolkit.Labs.WinUI.DependencyPropertyGenerator" />
<PackageReference Include="CommunityToolkit.WinUI.Notifications" />
<PackageReference Include="CommunityToolkit.WinUI.Animations" /> <PackageReference Include="CommunityToolkit.WinUI.Animations" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" /> <PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" /> <PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" />