Files
Wino-Mail/Wino.Core.UWP/Services/NotificationBuilder.cs
Maicol Battistini 256fd1cce2 feat: Enhanced sender avatars with gravatar and favicons integration (#685)
* feat: Enhanced sender avatars with gravatar and favicons integration

* chore: Remove unused known companies thumbnails

* feat(thumbnail): add IThumbnailService and refactor usage

- Introduced a new interface `IThumbnailService` for handling thumbnail-related functionalities.
- Registered `IThumbnailService` with its implementation `ThumbnailService` in the service container.
- Updated `NotificationBuilder` to use an instance of `IThumbnailService` instead of static methods.
- Refactored `ThumbnailService` from a static class to a regular class with instance methods and variables.
- Modified `ImagePreviewControl` to utilize the new `IThumbnailService` instance.
- Completed integration of `IThumbnailService` in the application by registering it in `App.xaml.cs`.

* style: Show favicons as squares

- Changed `hintCrop` in `NotificationBuilder` to `None` for app logo display.
- Added `FaviconSquircle`, `FaviconImage`, and `isFavicon` to `ImagePreviewControl` for favicon handling.
- Updated `UpdateInformation` method to manage favicon visibility.
- Introduced `GetBitmapImageAsync` for converting Base64 to Bitmap images.
- Enhanced XAML to include `FaviconSquircle` for improved UI appearance.

* refactor thumbnail service

* Removed old code and added clear method

* added prefetch function

* Change key from host to email

* Remove redundant code

* Test event

* Fixed an issue with the thumbnail updated event.

* Fix cutted favicons

* exclude some domain from favicons

* add yandex.ru

* fix buttons in settings

* remove prefetch method

* Added thumbnails propagation to mailRenderingPage

* Revert MailItemViewModel to object

* Remove redundant code

* spaces

* await load parameter added

* fix spaces

* fix case sensativity for mail list thumbnails

* change duckdns to google

* Some cleanup.

---------

Co-authored-by: Aleh Khantsevich <aleh.khantsevich@gmail.com>
Co-authored-by: Burak Kaan Köse <bkaankose@outlook.com>
2025-06-21 01:40:25 +02:00

234 lines
9.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.WinUI.Notifications;
using Serilog;
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.MailItem;
using System.IO;
namespace Wino.Core.UWP.Services;
// TODO: Refactor this thing. It's garbage.
public class NotificationBuilder : INotificationBuilder
{
private readonly IUnderlyingThemeService _underlyingThemeService;
private readonly IAccountService _accountService;
private readonly IFolderService _folderService;
private readonly IMailService _mailService;
private readonly IThumbnailService _thumbnailService;
public NotificationBuilder(IUnderlyingThemeService underlyingThemeService,
IAccountService accountService,
IFolderService folderService,
IMailService mailService,
IThumbnailService thumbnailService)
{
_underlyingThemeService = underlyingThemeService;
_accountService = accountService;
_folderService = folderService;
_mailService = mailService;
_thumbnailService = thumbnailService;
}
public async Task CreateNotificationsAsync(Guid inboxFolderId, IEnumerable<IMailItem> downloadedMailItems)
{
var mailCount = downloadedMailItems.Count();
try
{
// If there are more than 3 mails, just display 1 general toast.
if (mailCount > 3)
{
var builder = new ToastContentBuilder();
builder.SetToastScenario(ToastScenario.Default);
builder.AddText(Translator.Notifications_MultipleNotificationsTitle);
builder.AddText(string.Format(Translator.Notifications_MultipleNotificationsMessage, mailCount));
builder.AddButton(GetDismissButton());
builder.AddAudio(new ToastAudio()
{
Src = new Uri("ms-winsoundevent:Notification.Mail")
});
builder.Show();
}
else
{
var validItems = new List<IMailItem>();
// Fetch mails again to fill up assigned folder data and latest statuses.
// They've been marked as read by executing synchronizer tasks until inital sync finishes.
foreach (var item in downloadedMailItems)
{
var mailItem = await _mailService.GetSingleMailItemAsync(item.UniqueId);
if (mailItem != null && mailItem.AssignedFolder != null)
{
validItems.Add(mailItem);
}
}
foreach (var mailItem in validItems)
{
if (mailItem.IsRead)
continue;
var builder = new ToastContentBuilder();
builder.SetToastScenario(ToastScenario.Default);
var avatarThumbnail = await _thumbnailService.GetThumbnailAsync(mailItem.FromAddress, awaitLoad: true);
if (!string.IsNullOrEmpty(avatarThumbnail))
{
var tempFile = await Windows.Storage.ApplicationData.Current.TemporaryFolder.CreateFileAsync($"{Guid.NewGuid()}.png", Windows.Storage.CreationCollisionOption.ReplaceExisting);
await using (var stream = await tempFile.OpenStreamForWriteAsync())
{
var bytes = Convert.FromBase64String(avatarThumbnail);
await stream.WriteAsync(bytes);
}
builder.AddAppLogoOverride(new Uri($"ms-appdata:///temp/{tempFile.Name}"), hintCrop: ToastGenericAppLogoCrop.Default);
}
// Override system notification timetamp with received date of the mail.
// It may create confusion for some users, but still it's the truth...
builder.AddCustomTimeStamp(mailItem.CreationDate.ToLocalTime());
builder.AddText(mailItem.FromName);
builder.AddText(mailItem.Subject);
builder.AddText(mailItem.PreviewText);
builder.AddArgument(Constants.ToastMailUniqueIdKey, mailItem.UniqueId.ToString());
builder.AddArgument(Constants.ToastActionKey, MailOperation.Navigate);
builder.AddButton(GetMarkAsReadButton(mailItem.UniqueId));
builder.AddButton(GetDeleteButton(mailItem.UniqueId));
builder.AddButton(GetArchiveButton(mailItem.UniqueId));
builder.AddAudio(new ToastAudio()
{
Src = new Uri("ms-winsoundevent:Notification.Mail")
});
builder.Show();
}
await UpdateTaskbarIconBadgeAsync();
}
}
catch (Exception ex)
{
Log.Error(ex, "Failed to create notifications.");
}
}
private ToastButton GetDismissButton()
=> new ToastButton()
.SetDismissActivation()
.SetImageUri(new Uri("ms-appx:///Assets/NotificationIcons/dismiss.png"));
private static ToastButton GetArchiveButton(Guid mailUniqueId)
=> new ToastButton()
.SetContent(Translator.MailOperation_Archive)
.SetImageUri(new Uri("ms-appx:///Assets/NotificationIcons/archive.png"))
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
.AddArgument(Constants.ToastActionKey, MailOperation.Archive)
.SetBackgroundActivation();
private ToastButton GetDeleteButton(Guid mailUniqueId)
=> new ToastButton()
.SetContent(Translator.MailOperation_Delete)
.SetImageUri(new Uri("ms-appx:///Assets/NotificationIcons/delete.png"))
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
.AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete)
.SetBackgroundActivation();
private static ToastButton GetMarkAsReadButton(Guid mailUniqueId)
=> new ToastButton()
.SetContent(Translator.MailOperation_MarkAsRead)
.SetImageUri(new System.Uri("ms-appx:///Assets/NotificationIcons/markread.png"))
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
.AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead)
.SetBackgroundActivation();
public async Task UpdateTaskbarIconBadgeAsync()
{
int totalUnreadCount = 0;
try
{
var badgeUpdater = BadgeUpdateManager.CreateBadgeUpdaterForApplication();
var accounts = await _accountService.GetAccountsAsync();
foreach (var account in accounts)
{
if (!account.Preferences.IsTaskbarBadgeEnabled) continue;
var accountInbox = await _folderService.GetSpecialFolderByAccountIdAsync(account.Id, SpecialFolderType.Inbox);
if (accountInbox == null)
continue;
var inboxUnreadCount = await _folderService.GetFolderNotificationBadgeAsync(accountInbox.Id);
totalUnreadCount += inboxUnreadCount;
}
if (totalUnreadCount > 0)
{
// Get the blank badge XML payload for a badge number
XmlDocument badgeXml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber);
// Set the value of the badge in the XML to our number
XmlElement badgeElement = badgeXml.SelectSingleNode("/badge") as XmlElement;
badgeElement.SetAttribute("value", totalUnreadCount.ToString());
// Create the badge notification
BadgeNotification badge = new BadgeNotification(badgeXml);
// And update the badge
badgeUpdater.Update(badge);
}
else
badgeUpdater.Clear();
}
catch (Exception ex)
{
Log.Error(ex, "Error while updating taskbar badge.");
}
}
public async Task CreateTestNotificationAsync(string title, string message)
{
// with args test.
await CreateNotificationsAsync(Guid.Parse("28c3c39b-7147-4de3-b209-949bd19eede6"), new List<IMailItem>()
{
new MailCopy()
{
Subject = "test subject",
PreviewText = "preview html",
CreationDate = DateTime.UtcNow,
FromAddress = "bkaankose@outlook.com",
Id = "AAkALgAAAAAAHYQDEapmEc2byACqAC-EWg0AnMdP0zg8wkS_Ib2Eeh80LAAGq91I3QAA",
}
});
//var builder = new ToastContentBuilder();
//builder.SetToastScenario(ToastScenario.Default);
//builder.AddText(title);
//builder.AddText(message);
//builder.Show();
//await Task.CompletedTask;
}
}