Merged feature/vNext. Initial commit for Wino Mail 2.0
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user