Merged feature/vNext. Initial commit for Wino Mail 2.0

This commit is contained in:
Burak Kaan Köse
2026-04-05 16:30:26 +02:00
1513 changed files with 93788 additions and 26896 deletions
+221 -118
View File
@@ -11,27 +11,30 @@ using CommunityToolkit.Mvvm.Messaging;
using MailKit;
using MimeKit;
using MimeKit.Cryptography;
using Serilog;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Exceptions;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Menus;
using Wino.Core.Domain.Models.Navigation;
using Wino.Core.Domain.Models.Printing;
using Wino.Core.Domain.Models.Reader;
using Wino.Core.Services;
using Wino.Mail.ViewModels.Data;
using Wino.Mail.ViewModels.Messages;
using Wino.Messaging.Client.Mails;
using Wino.Messaging.Server;
using Wino.Messaging.UI;
using IMailService = Wino.Core.Domain.Interfaces.IMailService;
namespace Wino.Mail.ViewModels;
public partial class MailRenderingPageViewModel : MailBaseViewModel,
IRecipient<NewMailItemRenderingRequestedEvent>,
IRecipient<ReaderItemRefreshRequestedEvent>,
IRecipient<ThumbnailAdded>,
ITransferProgress // For listening IMAP message download progress.
{
@@ -40,13 +43,13 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
private readonly IMimeFileService _mimeFileService;
private readonly Core.Domain.Interfaces.IMailService _mailService;
private readonly IFolderService _folderService;
private readonly IFileService _fileService;
private readonly IWinoRequestDelegator _requestDelegator;
private readonly IContactService _contactService;
private readonly IClipboardService _clipboardService;
private readonly IUnsubscriptionService _unsubscriptionService;
private readonly IApplicationConfiguration _applicationConfiguration;
private readonly IWinoServerConnectionManager _winoServerConnectionManager;
private bool forceImageLoading = false;
private MailItemViewModel initializedMailItemViewModel = null;
@@ -56,11 +59,17 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
// Used in 'Save as' and 'Print' functionality.
public Func<string, Task<bool>> SaveHTMLasPDFFunc { get; set; }
public Func<WebView2PrintSettingsModel, Task<PrintingResult>> DirectPrintFuncAsync { get; set; }
#region Properties
public bool ShouldDisplayDownloadProgress => IsIndetermineProgress || (CurrentDownloadPercentage > 0 && CurrentDownloadPercentage <= 100);
public bool CanUnsubscribe => CurrentRenderModel?.UnsubscribeInfo?.CanUnsubscribe ?? false;
public bool IsJunkMail => initializedMailItemViewModel?.AssignedFolder != null && initializedMailItemViewModel.AssignedFolder.SpecialFolderType == SpecialFolderType.Junk;
public bool IsSmimeSigned => (CurrentRenderModel?.Signatures?.Count ?? 0) > 0;
public bool IsSmimeEncrypted => CurrentRenderModel?.IsSmimeEncrypted ?? false;
public bool IsJunkMail => initializedMailItemViewModel?.MailCopy.AssignedFolder != null && initializedMailItemViewModel.MailCopy.AssignedFolder.SpecialFolderType == SpecialFolderType.Junk;
public bool SmimeSignaturesValid => CurrentRenderModel?.Signatures?.Any(x => x.Value) ?? false;
public bool SmimeSignaturesInvalid => !SmimeSignaturesValid;
public bool IsImageRenderingDisabled
{
@@ -100,6 +109,10 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanUnsubscribe))]
[NotifyPropertyChangedFor(nameof(IsSmimeSigned))]
[NotifyPropertyChangedFor(nameof(IsSmimeEncrypted))]
[NotifyPropertyChangedFor(nameof(SmimeSignaturesValid))]
[NotifyPropertyChangedFor(nameof(SmimeSignaturesInvalid))]
public partial MailRenderModel CurrentRenderModel { get; set; }
[ObservableProperty]
@@ -112,7 +125,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
public partial string FromName { get; set; }
[ObservableProperty]
public partial string ContactPicture { get; set; }
public partial IMailItemDisplayInformation CurrentMailItemDisplayInformation { get; set; }
[ObservableProperty]
public partial DateTime CreationDate { get; set; }
@@ -120,7 +133,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
public ObservableCollection<AccountContactViewModel> CcItems { get; set; } = [];
public ObservableCollection<AccountContactViewModel> BccItems { get; set; } = [];
public ObservableCollection<MailAttachmentViewModel> Attachments { get; set; } = [];
public ObservableCollection<MailOperationMenuItem> MenuItems { get; set; } = [];
public ObservableCollection<IMenuOperation> MenuItems { get; set; } = [];
#endregion
@@ -128,22 +141,24 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
public IStatePersistanceService StatePersistenceService { get; }
public IPreferencesService PreferencesService { get; }
public IPrintService PrintService { get; }
public Guid? CurrentMailAccountId => initializedMailItemViewModel?.MailCopy.AssignedAccount?.Id;
public Guid? CurrentMailFileId => initializedMailItemViewModel?.MailCopy.FileId;
public MailRenderingPageViewModel(IMailDialogService dialogService,
INativeAppService nativeAppService,
IUnderlyingThemeService underlyingThemeService,
IMimeFileService mimeFileService,
IMailService mailService,
IFileService fileService,
IWinoRequestDelegator requestDelegator,
IStatePersistanceService statePersistenceService,
IContactService contactService,
IClipboardService clipboardService,
IUnsubscriptionService unsubscriptionService,
IPreferencesService preferencesService,
IPrintService printService,
IApplicationConfiguration applicationConfiguration,
IWinoServerConnectionManager winoServerConnectionManager)
INativeAppService nativeAppService,
IUnderlyingThemeService underlyingThemeService,
IMimeFileService mimeFileService,
IMailService mailService,
IFolderService folderService,
IFileService fileService,
IWinoRequestDelegator requestDelegator,
IStatePersistanceService statePersistenceService,
IContactService contactService,
IClipboardService clipboardService,
IUnsubscriptionService unsubscriptionService,
IPreferencesService preferencesService,
IPrintService printService,
IApplicationConfiguration applicationConfiguration)
{
_dialogService = dialogService;
NativeAppService = nativeAppService;
@@ -152,12 +167,12 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
PreferencesService = preferencesService;
PrintService = printService;
_applicationConfiguration = applicationConfiguration;
_winoServerConnectionManager = winoServerConnectionManager;
_clipboardService = clipboardService;
_unsubscriptionService = unsubscriptionService;
_underlyingThemeService = underlyingThemeService;
_mimeFileService = mimeFileService;
_mailService = mailService;
_folderService = folderService;
_fileService = fileService;
_requestDelegator = requestDelegator;
}
@@ -240,63 +255,98 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
}
[RelayCommand]
private async Task OperationClicked(MailOperationMenuItem menuItem)
private async Task OperationClicked(IMenuOperation menuItem)
{
if (menuItem == null) return;
if (menuItem is not MailOperationMenuItem mailOperationMenuItem) return;
await HandleMailOperationAsync(menuItem.Operation);
await HandleMailOperationAsync(mailOperationMenuItem.Operation);
}
private async Task HandleMailOperationAsync(MailOperation operation)
{
// Toggle theme
if (operation == MailOperation.DarkEditor || operation == MailOperation.LightEditor)
IsDarkWebviewRenderer = !IsDarkWebviewRenderer;
else if (operation == MailOperation.SaveAs)
try
{
await SaveAsAsync();
}
else if (operation == MailOperation.Print)
{
await PrintAsync();
}
else if (operation == MailOperation.ViewMessageSource)
{
await _dialogService.ShowMessageSourceDialogAsync(initializedMimeMessageInformation.MimeMessage.ToString());
}
else if (operation == MailOperation.Reply || operation == MailOperation.ReplyAll || operation == MailOperation.Forward)
{
if (initializedMailItemViewModel == null) return;
// Create new draft.
var draftOptions = new DraftCreationOptions()
if (operation == MailOperation.SaveAs)
{
Reason = operation switch
await SaveAsAsync();
}
else if (operation == MailOperation.Print)
{
var settings = await _dialogService.ShowPrintDialogAsync();
if (settings == null) return;
var printingResult = await DirectPrintFuncAsync.Invoke(settings);
// TODO: More detailed printing result handling.
if (printingResult == PrintingResult.Submitted)
{
MailOperation.Reply => DraftCreationReason.Reply,
MailOperation.ReplyAll => DraftCreationReason.ReplyAll,
MailOperation.Forward => DraftCreationReason.Forward,
_ => DraftCreationReason.Empty
},
ReferencedMessage = new ReferencedMessage()
{
MimeMessage = initializedMimeMessageInformation.MimeMessage,
MailCopy = initializedMailItemViewModel.MailCopy
_dialogService.InfoBarMessage(Translator.DialogMessage_PrintingSuccessTitle, Translator.DialogMessage_PrintingSuccessMessage, InfoBarMessageType.Success);
}
else if (printingResult == PrintingResult.Failed)
{
_dialogService.InfoBarMessage(Translator.DialogMessage_PrintingFailedTitle, Translator.DialogMessage_PrintingFailedMessage, InfoBarMessageType.Error);
}
};
var (draftMailCopy, draftBase64MimeMessage) = await _mailService.CreateDraftAsync(initializedMailItemViewModel.AssignedAccount.Id, draftOptions).ConfigureAwait(false);
}
else if (operation == MailOperation.ViewMessageSource)
{
await _dialogService.ShowMessageSourceDialogAsync(initializedMimeMessageInformation.MimeMessage.ToString());
}
else if (operation == MailOperation.Reply || operation == MailOperation.ReplyAll || operation == MailOperation.Forward)
{
if (initializedMailItemViewModel == null) return;
var draftPreparationRequest = new DraftPreparationRequest(initializedMailItemViewModel.AssignedAccount, draftMailCopy, draftBase64MimeMessage, draftOptions.Reason, initializedMailItemViewModel.MailCopy);
// Create new draft.
var draftOptions = new DraftCreationOptions()
{
Reason = operation switch
{
MailOperation.Reply => DraftCreationReason.Reply,
MailOperation.ReplyAll => DraftCreationReason.ReplyAll,
MailOperation.Forward => DraftCreationReason.Forward,
_ => DraftCreationReason.Empty
},
ReferencedMessage = new ReferencedMessage()
{
MimeMessage = initializedMimeMessageInformation.MimeMessage,
MailCopy = initializedMailItemViewModel.MailCopy
}
};
await _requestDelegator.ExecuteAsync(draftPreparationRequest);
var (draftMailCopy, draftBase64MimeMessage) = await _mailService.CreateDraftAsync(initializedMailItemViewModel.MailCopy.AssignedAccount.Id, draftOptions).ConfigureAwait(false);
var draftPreparationRequest = new DraftPreparationRequest(initializedMailItemViewModel.MailCopy.AssignedAccount, draftMailCopy, draftBase64MimeMessage, draftOptions.Reason, initializedMailItemViewModel.MailCopy);
await _requestDelegator.ExecuteAsync(draftPreparationRequest);
}
else if (initializedMailItemViewModel != null)
{
// All other operations require a mail item.
var prepRequest = new MailOperationPreperationRequest(operation, initializedMailItemViewModel.MailCopy);
await _requestDelegator.ExecuteAsync(prepRequest);
}
}
else if (initializedMailItemViewModel != null)
catch (UnavailableSpecialFolderException unavailableSpecialFolderException)
{
// All other operations require a mail item.
var prepRequest = new MailOperationPreperationRequest(operation, initializedMailItemViewModel.MailCopy);
await _requestDelegator.ExecuteAsync(prepRequest);
_dialogService.InfoBarMessage(Translator.Info_MissingFolderTitle,
string.Format(Translator.Info_MissingFolderMessage, unavailableSpecialFolderException.SpecialFolderType),
InfoBarMessageType.Warning,
Translator.SettingConfigureSpecialFolders_Button,
() =>
{
_dialogService.HandleSystemFolderConfigurationDialogAsync(unavailableSpecialFolderException.AccountId, _folderService);
});
}
catch (NotImplementedException)
{
_dialogService.ShowNotSupportedMessage();
}
catch (Exception ex)
{
Log.Error(ex, "Mail operation execution failed. Operation: {Operation}", operation);
_dialogService.InfoBarMessage(Translator.Info_RequestCreationFailedTitle, ex.Message, InfoBarMessageType.Error);
}
}
@@ -310,6 +360,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
initializedMailItemViewModel = null;
initializedMimeMessageInformation = null;
CurrentMailItemDisplayInformation = null;
// Dispose existing content first.
Messenger.Send(new CancelRenderingContentRequested());
@@ -355,8 +406,10 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
// To show the progress on the UI.
CurrentDownloadPercentage = 1;
var package = new DownloadMissingMessageRequested(mailItemViewModel.AssignedAccount.Id, mailItemViewModel.MailCopy);
await _winoServerConnectionManager.GetResponseAsync<bool, DownloadMissingMessageRequested>(package);
// Download missing MIME message using SynchronizationManager
await SynchronizationManager.Instance.DownloadMimeMessageAsync(
mailItemViewModel.MailCopy,
mailItemViewModel.MailCopy.AssignedAccount.Id);
}
catch (OperationCanceledException)
{
@@ -375,7 +428,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
private async Task RenderAsync(MailItemViewModel mailItemViewModel, CancellationToken cancellationToken = default)
{
ResetProgress();
var isMimeExists = await _mimeFileService.IsMimeExistAsync(mailItemViewModel.AssignedAccount.Id, mailItemViewModel.MailCopy.FileId);
var isMimeExists = await _mimeFileService.IsMimeExistAsync(mailItemViewModel.MailCopy.AssignedAccount.Id, mailItemViewModel.MailCopy.FileId);
if (!isMimeExists)
{
@@ -384,7 +437,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
// Find the MIME for this item and render it.
var mimeMessageInformation = await _mimeFileService.GetMimeMessageInformationAsync(mailItemViewModel.MailCopy.FileId,
mailItemViewModel.AssignedAccount.Id,
mailItemViewModel.MailCopy.AssignedAccount.Id,
cancellationToken).ConfigureAwait(false);
if (mimeMessageInformation == null)
@@ -394,6 +447,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
}
initializedMailItemViewModel = mailItemViewModel;
await ExecuteUIThread(() => { CurrentMailItemDisplayInformation = mailItemViewModel; });
await RenderAsync(mimeMessageInformation);
}
@@ -435,13 +489,14 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
// TODO: FromName and FromAddress is probably not correct here for mail lists.
FromAddress = message.From.Mailboxes.FirstOrDefault()?.Address ?? Translator.UnknownAddress;
FromName = message.From.Mailboxes.FirstOrDefault()?.Name ?? Translator.UnknownSender;
CreationDate = message.Date.DateTime;
ContactPicture = initializedMailItemViewModel?.SenderContact?.Base64ContactPicture;
// Use the received date from MailCopy if available, otherwise fall back to the sent date from MIME message
CreationDate = initializedMailItemViewModel?.MailCopy.CreationDate ?? message.Date.DateTime;
// Automatically disable images for Junk folder to prevent pixel tracking.
// This can only work for selected mail item rendering, not for EML file rendering.
if (initializedMailItemViewModel != null &&
initializedMailItemViewModel.AssignedFolder.SpecialFolderType == SpecialFolderType.Junk)
initializedMailItemViewModel.MailCopy.AssignedFolder.SpecialFolderType == SpecialFolderType.Junk)
{
renderingOptions.LoadImages = false;
}
@@ -480,7 +535,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
var contactViewModel = new AccountContactViewModel(foundContact);
// Make sure that user account first in the list.
if (string.Equals(contactViewModel.Address, initializedMailItemViewModel?.AssignedAccount?.Address, StringComparison.OrdinalIgnoreCase))
if (string.Equals(contactViewModel.Address, initializedMailItemViewModel?.MailCopy.AssignedAccount?.Address, StringComparison.OrdinalIgnoreCase))
{
contactViewModel.IsMe = true;
accounts.Insert(0, contactViewModel);
@@ -506,11 +561,15 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
{
base.OnNavigatedFrom(mode, parameters);
renderCancellationTokenSource.Cancel();
renderCancellationTokenSource?.Cancel();
renderCancellationTokenSource?.Dispose();
renderCancellationTokenSource = null;
CurrentDownloadPercentage = 0d;
initializedMailItemViewModel = null;
initializedMimeMessageInformation = null;
CurrentMailItemDisplayInformation = null;
forceImageLoading = false;
@@ -527,12 +586,6 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
{
MenuItems.Clear();
// Add light/dark editor theme switch.
if (IsDarkWebviewRenderer)
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.LightEditor));
else
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.DarkEditor));
// Save As PDF
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.SaveAs, true, true));
@@ -563,7 +616,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
}
// Archive - Unarchive
if (initializedMailItemViewModel.AssignedFolder.SpecialFolderType == SpecialFolderType.Archive)
if (initializedMailItemViewModel.MailCopy.AssignedFolder.SpecialFolderType == SpecialFolderType.Archive)
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.UnArchive));
else
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.Archive));
@@ -586,15 +639,15 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.MarkAsRead, true, false));
}
protected override async void OnMailUpdated(MailCopy updatedMail)
protected override async void OnMailUpdated(MailCopy updatedMail, MailUpdateSource source, MailCopyChangeFlags changedProperties)
{
base.OnMailUpdated(updatedMail);
base.OnMailUpdated(updatedMail, source, changedProperties);
if (initializedMailItemViewModel == null) return;
// Check if the updated mail is the same mail item we are rendering.
// This is done with UniqueId to include FolderId into calculations.
if (initializedMailItemViewModel.UniqueId != updatedMail.UniqueId) return;
if (initializedMailItemViewModel.MailCopy.UniqueId != updatedMail.UniqueId) return;
await ExecuteUIThread(() => { InitializeCommandBarItems(); });
}
@@ -690,40 +743,6 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
}
}
private async Task PrintAsync()
{
// Printing:
// 1. Let WebView2 save the current HTML as PDF to temporary location.
// 2. Saving as PDF will divide pages correctly for Win2D CanvasBitmap.
// 3. Use Win2D CanvasBitmap as IPrintDocumentSource and WinRT APIs to print the PDF.
try
{
var printFilePath = Path.Combine(_applicationConfiguration.ApplicationTempFolderPath, "print.pdf");
if (File.Exists(printFilePath)) File.Delete(printFilePath);
await SaveHTMLasPDFFunc(printFilePath);
var result = await PrintService.PrintPdfFileAsync(printFilePath, Subject);
if (result == PrintingResult.Submitted)
{
_dialogService.InfoBarMessage(Translator.DialogMessage_PrintingSuccessTitle, Translator.DialogMessage_PrintingSuccessMessage, InfoBarMessageType.Success);
}
else if (result != PrintingResult.Canceled)
{
var message = string.Format(Translator.DialogMessage_PrintingFailedMessage, result);
_dialogService.InfoBarMessage(Translator.DialogMessage_PrintingFailedTitle, message, InfoBarMessageType.Warning);
}
}
catch (Exception ex)
{
Log.Error(ex, "Failed to print mail.");
_dialogService.InfoBarMessage(string.Empty, ex.Message, InfoBarMessageType.Error);
}
}
private async Task SaveAsAsync()
{
try
@@ -739,8 +758,8 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
if (isSaved)
{
_dialogService.InfoBarMessage(Translator.Info_PDFSaveSuccessTitle,
string.Format(Translator.Info_PDFSaveSuccessMessage, pdfFilePath),
InfoBarMessageType.Success);
string.Format(Translator.Info_PDFSaveSuccessMessage, pdfFilePath),
InfoBarMessageType.Success);
}
}
catch (Exception ex)
@@ -782,8 +801,10 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
// For upload.
void ITransferProgress.Report(long bytesTransferred) { }
public async void Receive(NewMailItemRenderingRequestedEvent message)
public async void Receive(ReaderItemRefreshRequestedEvent message)
{
if (message.MailItemViewModel == null || message.MailItemViewModel.IsDraft) return;
try
{
await RenderAsync(message.MailItemViewModel, renderCancellationTokenSource.Token);
@@ -822,4 +843,86 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
}
});
}
[RelayCommand]
private async Task ShowSmimeSigningCertificateInfoAsync()
{
if (IsSmimeSigned)
{
MimePart signaturePart;
if (initializedMimeMessageInformation?.MimeMessage?.Body is MultipartSigned signed && signed[1] is MimePart signaturePart1)
{
signaturePart = signaturePart1;
}
else if (initializedMimeMessageInformation?.MimeMessage?.Body is ApplicationPkcs7Mime pkcs7)
{
signaturePart = null;
}
else
{
//_dialogService.InfoBarMessage(Translator.Info_SmimeSignatureNotFoundTitle, Translator.Info_SmimeSignatureNotFoundMessage, InfoBarMessageType.Error);
return;
}
string info = $"{Translator.SmimeSignaturesInMessage}:\n";
foreach (var (signature, valid) in CurrentRenderModel.Signatures)
{
info += string.Format(Translator.SmimeSignatureEntry, valid ? "✅" : "❌", signature.SignerCertificate.Name, signature.SignerCertificate.Fingerprint, signature.SignerCertificate.CreationDate, signature.SignerCertificate.ExpirationDate);
}
await ShowSmimeCertificateInfoAsync(signaturePart, info, Translator.SmimeSigningCertificateInfoTitle);
}
}
private async Task ShowSmimeCertificateInfoAsync(MimePart certificateAttachment, string additionalInfo = "", string title = null)
{
{
if (certificateAttachment == null)
{
await _dialogService.ShowConfirmationDialogAsync(
$"{additionalInfo}\n{Translator.SmimeNoCertificateFileFound}", title ?? Translator.SmimeCertificateInfoTitle, Translator.Buttons_OK);
return;
}
var fileName = certificateAttachment.FileName ?? "smime.p7s";
var contentType = certificateAttachment.ContentType?.MimeType ?? "application/pkcs7-signature";
var size = certificateAttachment.Content?.Stream?.Length ?? 0;
var info = string.Format(Translator.SmimeCertificateFileInfo, fileName, contentType, size);
var result = await _dialogService.ShowConfirmationDialogAsync(
$"{additionalInfo}\n{info}", title ?? Translator.SmimeCertificateInfoTitle,
Translator.SmimeSaveCertificate);
if (result)
{
var pickedPath = await _dialogService.PickFilePathAsync(fileName);
if (!string.IsNullOrEmpty(pickedPath))
{
var pickedDirectory = Path.GetDirectoryName(pickedPath);
var pickedFileName = Path.GetFileName(pickedPath);
await using (var stream = await _fileService.GetFileStreamAsync(pickedDirectory, pickedFileName))
{
await certificateAttachment.Content!.DecodeToAsync(stream);
}
_dialogService.InfoBarMessage(Translator.SmimeCertificate, string.Format(Translator.SmimeCertificateSavedTo, pickedPath),
InfoBarMessageType.Success);
}
}
}
}
protected override void RegisterRecipients()
{
base.RegisterRecipients();
Messenger.Register<ReaderItemRefreshRequestedEvent>(this);
Messenger.Register<ThumbnailAdded>(this);
}
protected override void UnregisterRecipients()
{
base.UnregisterRecipients();
Messenger.Unregister<ReaderItemRefreshRequestedEvent>(this);
Messenger.Unregister<ThumbnailAdded>(this);
}
}