Printing mails. (#471)

* Implemented printing functionality.

* Implemented icon for printing.

* Remove debug code.
This commit is contained in:
Burak Kaan Köse
2024-11-09 19:18:06 +01:00
committed by GitHub
parent 5245feb739
commit b49e1b3a97
18 changed files with 422 additions and 38 deletions

View File

@@ -0,0 +1,10 @@
namespace Wino.Core.Domain.Enums
{
public enum PrintingResult
{
Abandoned,
Canceled,
Failed,
Submitted
}
}

View File

@@ -17,5 +17,11 @@
/// Publisher shared folder path. /// Publisher shared folder path.
/// </summary> /// </summary>
string PublisherSharedFolderPath { get; set; } string PublisherSharedFolderPath { get; set; }
/// <summary>
/// Temp folder path of the application.
/// Files here are short-lived and can be deleted by system.
/// </summary>
string ApplicationTempFolderPath { get; set; }
} }
} }

View File

@@ -0,0 +1,10 @@
using System.Threading.Tasks;
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Interfaces
{
public interface IPrintService
{
Task<PrintingResult> PrintPdfFileAsync(string pdfFilePath, string printTitle);
}
}

View File

@@ -0,0 +1,16 @@
using System;
namespace Wino.Core.Domain.Models.Printing
{
public class PrintInformation
{
public PrintInformation(string pDFFilePath, string pDFTitle)
{
PDFFilePath = pDFFilePath ?? throw new ArgumentNullException(nameof(pDFFilePath));
PDFTitle = pDFTitle ?? throw new ArgumentNullException(nameof(pDFTitle));
}
public string PDFFilePath { get; }
public string PDFTitle { get; }
}
}

View File

@@ -100,6 +100,10 @@
"DialogMessage_ComposerValidationFailedTitle": "Validation Failed", "DialogMessage_ComposerValidationFailedTitle": "Validation Failed",
"DialogMessage_CreateLinkedAccountMessage": "Give this new link a name. Accounts will be merged under this name.", "DialogMessage_CreateLinkedAccountMessage": "Give this new link a name. Accounts will be merged under this name.",
"DialogMessage_CreateLinkedAccountTitle": "Account Link Name", "DialogMessage_CreateLinkedAccountTitle": "Account Link Name",
"DialogMessage_PrintingFailedMessage": "Failed to print this mail. Result: {0}",
"DialogMessage_PrintingFailedTitle": "Failed",
"DialogMessage_PrintingSuccessTitle": "Success",
"DialogMessage_PrintingSuccessMessage": "Mail is sent to printer.",
"DialogMessage_DeleteAccountConfirmationMessage": "Delete {0}?", "DialogMessage_DeleteAccountConfirmationMessage": "Delete {0}?",
"DialogMessage_DeleteAccountConfirmationTitle": "All data associated with this account will be deleted from disk permanently.", "DialogMessage_DeleteAccountConfirmationTitle": "All data associated with this account will be deleted from disk permanently.",
"DialogMessage_DiscardDraftConfirmationMessage": "This draft will be discarded. Do you want to continue?", "DialogMessage_DiscardDraftConfirmationMessage": "This draft will be discarded. Do you want to continue?",

View File

@@ -28,6 +28,7 @@ namespace Wino.Core.UWP
services.AddTransient<INotificationBuilder, NotificationBuilder>(); services.AddTransient<INotificationBuilder, NotificationBuilder>();
services.AddTransient<IClipboardService, ClipboardService>(); services.AddTransient<IClipboardService, ClipboardService>();
services.AddTransient<IStartupBehaviorService, StartupBehaviorService>(); services.AddTransient<IStartupBehaviorService, StartupBehaviorService>();
services.AddSingleton<IPrintService, PrintService>();
} }
} }
} }

View File

@@ -0,0 +1,266 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Threading.Tasks;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Printing;
using Windows.Data.Pdf;
using Windows.Graphics.Printing;
using Windows.Graphics.Printing.OptionDetails;
using Windows.Storage.Streams;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Printing;
namespace Wino.Core.UWP.Services
{
/// <summary>
/// Printer service that uses WinRT APIs to print PDF files.
/// Used modified version of the code here:
/// https://github.com/microsoft/Win2D-Samples/blob/reunion_master/ExampleGallery/PrintingExample.xaml.cs
/// HTML file is saved as PDF to temporary location.
/// Then PDF is loaded as PdfDocument and printed using CanvasBitmap for each page.
/// </summary>
public class PrintService : IPrintService
{
private TaskCompletionSource<PrintingResult> _taskCompletionSource;
private CanvasPrintDocument printDocument;
private PrintTask printTask;
private PdfDocument pdfDocument;
private List<CanvasBitmap> bitmaps = new();
private Vector2 largestBitmap;
private Vector2 pageSize;
private Vector2 imagePadding = new Vector2(64, 64);
private Vector2 cellSize;
private int bitmapCount;
private int columns;
private int rows;
private int bitmapsPerPage;
private int pageCount = -1;
private PrintInformation _currentPrintInformation;
public async Task<PrintingResult> PrintPdfFileAsync(string pdfFilePath, string printTitle)
{
if (_taskCompletionSource != null)
{
_taskCompletionSource.TrySetResult(PrintingResult.Abandoned);
_taskCompletionSource = new TaskCompletionSource<PrintingResult>();
}
// Load the PDF file
var file = await Windows.Storage.StorageFile.GetFileFromPathAsync(pdfFilePath);
pdfDocument = await PdfDocument.LoadFromFileAsync(file);
_taskCompletionSource ??= new TaskCompletionSource<PrintingResult>();
_currentPrintInformation = new PrintInformation(pdfFilePath, printTitle);
printDocument = new CanvasPrintDocument();
printDocument.PrintTaskOptionsChanged += OnDocumentTaskOptionsChanged;
printDocument.Preview += OnDocumentPreview;
printDocument.Print += OnDocumentPrint;
var printManager = PrintManager.GetForCurrentView();
printManager.PrintTaskRequested += PrintingExample_PrintTaskRequested;
try
{
await PrintManager.ShowPrintUIAsync();
var result = await _taskCompletionSource.Task;
return result;
}
finally
{
// Dispose everything.
UnregisterPrintManager(printManager);
ClearBitmaps();
UnregisterTask();
DisposePDFDocument();
_taskCompletionSource = null;
}
}
private void DisposePDFDocument()
{
if (pdfDocument != null)
{
pdfDocument = null;
}
}
private void UnregisterTask()
{
if (printTask != null)
{
printTask.Completed -= TaskCompleted;
printTask = null;
}
}
private void UnregisterPrintManager(PrintManager manager)
{
manager.PrintTaskRequested -= PrintingExample_PrintTaskRequested;
}
private void ClearBitmaps()
{
foreach (var bitmap in bitmaps)
{
bitmap.Dispose();
}
bitmaps.Clear();
}
private void PrintingExample_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
{
if (_currentPrintInformation == null) return;
printTask = args.Request.CreatePrintTask(_currentPrintInformation.PDFTitle, (createPrintTaskArgs) =>
{
createPrintTaskArgs.SetSource(printDocument);
});
printTask.Completed += TaskCompleted;
}
private void TaskCompleted(PrintTask sender, PrintTaskCompletedEventArgs args)
=> _taskCompletionSource?.TrySetResult((PrintingResult)args.Completion);
private async void OnDocumentTaskOptionsChanged(CanvasPrintDocument sender, CanvasPrintTaskOptionsChangedEventArgs args)
{
var deferral = args.GetDeferral();
try
{
await LoadPDFPageBitmapsAsync(sender);
var pageDesc = args.PrintTaskOptions.GetPageDescription(1);
var newPageSize = pageDesc.PageSize.ToVector2();
if (pageSize == newPageSize && pageCount != -1)
{
// We've already figured out the pages and the page size hasn't changed, so there's nothing left for us to do here.
return;
}
pageSize = newPageSize;
sender.InvalidatePreview();
// Figure out the bitmap index at the top of the current preview page. We'll request that the preview defaults to showing
// the page that still has this bitmap on it in the new layout.
int indexOnCurrentPage = 0;
if (pageCount != -1)
{
indexOnCurrentPage = (int)(args.CurrentPreviewPageNumber - 1) * bitmapsPerPage;
}
// Calculate the new layout
var printablePageSize = pageSize * 0.9f;
cellSize = largestBitmap + imagePadding;
var cellsPerPage = printablePageSize / cellSize;
columns = Math.Max(1, (int)Math.Floor(cellsPerPage.X));
rows = Math.Max(1, (int)Math.Floor(cellsPerPage.Y));
bitmapsPerPage = columns * rows;
// Calculate the page count
bitmapCount = bitmaps.Count;
pageCount = (int)Math.Ceiling(bitmapCount / (double)bitmapsPerPage);
sender.SetPageCount((uint)pageCount);
// Set the preview page to the one that has the item that was currently displayed in the last preview
args.NewPreviewPageNumber = (uint)(indexOnCurrentPage / bitmapsPerPage) + 1;
}
finally
{
deferral.Complete();
}
}
private async Task LoadPDFPageBitmapsAsync(CanvasPrintDocument sender)
{
ClearBitmaps();
bitmaps ??= new List<CanvasBitmap>();
for (int i = 0; i < pdfDocument.PageCount; i++)
{
var page = pdfDocument.GetPage((uint)i);
var stream = new InMemoryRandomAccessStream();
await page.RenderToStreamAsync(stream);
var bitmap = await CanvasBitmap.LoadAsync(sender, stream);
bitmaps.Add(bitmap);
}
largestBitmap = Vector2.Zero;
foreach (var bitmap in bitmaps)
{
largestBitmap.X = Math.Max(largestBitmap.X, (float)bitmap.Size.Width);
largestBitmap.Y = Math.Max(largestBitmap.Y, (float)bitmap.Size.Height);
}
}
private void OnDocumentPreview(CanvasPrintDocument sender, CanvasPreviewEventArgs args)
{
var ds = args.DrawingSession;
var pageNumber = args.PageNumber;
DrawPdfPage(sender, ds, pageNumber);
}
private void OnDocumentPrint(CanvasPrintDocument sender, CanvasPrintEventArgs args)
{
var detailedOptions = PrintTaskOptionDetails.GetFromPrintTaskOptions(args.PrintTaskOptions);
int pageCountToPrint = (int)pdfDocument.PageCount;
for (uint i = 1; i <= pageCountToPrint; ++i)
{
using var ds = args.CreateDrawingSession();
var imageableRect = args.PrintTaskOptions.GetPageDescription(i).ImageableRect;
DrawPdfPage(sender, ds, i);
}
}
private void DrawPdfPage(CanvasPrintDocument sender, CanvasDrawingSession ds, uint pageNumber)
{
if (bitmaps?.Count == 0) return;
var cellAcross = new Vector2(cellSize.X, 0);
var cellDown = new Vector2(0, cellSize.Y);
var totalSize = cellAcross * columns + cellDown * rows;
Vector2 topLeft = (pageSize - totalSize) / 2;
int bitmapIndex = ((int)pageNumber - 1) * bitmapsPerPage;
for (int row = 0; row < rows; ++row)
{
for (int column = 0; column < columns; ++column)
{
var cellTopLeft = topLeft + cellAcross * column + cellDown * row;
var bitmapInfo = bitmaps[bitmapIndex % bitmaps.Count];
var bitmapPos = cellTopLeft + (cellSize - bitmapInfo.Size.ToVector2()) / 2;
ds.DrawImage(bitmapInfo, bitmapPos);
bitmapIndex++;
}
}
}
}
}

View File

@@ -91,6 +91,7 @@
<Compile Include="Models\Personalization\SystemAppTheme.cs" /> <Compile Include="Models\Personalization\SystemAppTheme.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\PreferencesService.cs" /> <Compile Include="Services\PreferencesService.cs" />
<Compile Include="Services\PrintService.cs" />
<Compile Include="Services\StartupBehaviorService.cs" /> <Compile Include="Services\StartupBehaviorService.cs" />
<Compile Include="Services\StatePersistenceService.cs" /> <Compile Include="Services\StatePersistenceService.cs" />
<Compile Include="Services\WinoServerConnectionManager.cs" /> <Compile Include="Services\WinoServerConnectionManager.cs" />
@@ -120,6 +121,9 @@
<PackageReference Include="Microsoft.Toolkit.Uwp"> <PackageReference Include="Microsoft.Toolkit.Uwp">
<Version>7.1.3</Version> <Version>7.1.3</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Win2D.uwp">
<Version>1.28.0</Version>
</PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Wino.Core.Domain\Wino.Core.Domain.csproj"> <ProjectReference Include="..\Wino.Core.Domain\Wino.Core.Domain.csproj">
@@ -140,6 +144,9 @@
<Name>Windows Desktop Extensions for the UWP</Name> <Name>Windows Desktop Extensions for the UWP</Name>
</SDKReference> </SDKReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Models\Printing\" />
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' "> <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion> <VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup> </PropertyGroup>

View File

@@ -7,7 +7,7 @@ namespace Wino.Core.Services
public const string SharedFolderName = "WinoShared"; public const string SharedFolderName = "WinoShared";
public string ApplicationDataFolderPath { get; set; } public string ApplicationDataFolderPath { get; set; }
public string PublisherSharedFolderPath { get; set; } public string PublisherSharedFolderPath { get; set; }
public string ApplicationTempFolderPath { get; set; }
} }
} }

View File

@@ -26,6 +26,7 @@ using Wino.Mail.ViewModels.Data;
using Wino.Mail.ViewModels.Messages; using Wino.Mail.ViewModels.Messages;
using Wino.Messaging.Client.Mails; using Wino.Messaging.Client.Mails;
using Wino.Messaging.Server; using Wino.Messaging.Server;
using IMailService = Wino.Core.Domain.Interfaces.IMailService;
namespace Wino.Mail.ViewModels namespace Wino.Mail.ViewModels
{ {
@@ -42,12 +43,17 @@ namespace Wino.Mail.ViewModels
private readonly IContactService _contactService; private readonly IContactService _contactService;
private readonly IClipboardService _clipboardService; private readonly IClipboardService _clipboardService;
private readonly IUnsubscriptionService _unsubscriptionService; private readonly IUnsubscriptionService _unsubscriptionService;
private readonly IApplicationConfiguration _applicationConfiguration;
private readonly IWinoServerConnectionManager _winoServerConnectionManager; private readonly IWinoServerConnectionManager _winoServerConnectionManager;
private bool forceImageLoading = false; private bool forceImageLoading = false;
private MailItemViewModel initializedMailItemViewModel = null; private MailItemViewModel initializedMailItemViewModel = null;
private MimeMessageInformation initializedMimeMessageInformation = null; private MimeMessageInformation initializedMimeMessageInformation = null;
// Func to get WebView2 to save current HTML as PDF to given location.
// Used in 'Save as' and 'Print' functionality.
public Func<string, Task<bool>> SaveHTMLasPDFFunc { get; set; }
#region Properties #region Properties
public bool ShouldDisplayDownloadProgress => IsIndetermineProgress || (CurrentDownloadPercentage > 0 && CurrentDownloadPercentage <= 100); public bool ShouldDisplayDownloadProgress => IsIndetermineProgress || (CurrentDownloadPercentage > 0 && CurrentDownloadPercentage <= 100);
@@ -121,12 +127,13 @@ namespace Wino.Mail.ViewModels
public INativeAppService NativeAppService { get; } public INativeAppService NativeAppService { get; }
public IStatePersistanceService StatePersistenceService { get; } public IStatePersistanceService StatePersistenceService { get; }
public IPreferencesService PreferencesService { get; } public IPreferencesService PreferencesService { get; }
public IPrintService PrintService { get; }
public MailRenderingPageViewModel(IDialogService dialogService, public MailRenderingPageViewModel(IDialogService dialogService,
INativeAppService nativeAppService, INativeAppService nativeAppService,
IUnderlyingThemeService underlyingThemeService, IUnderlyingThemeService underlyingThemeService,
IMimeFileService mimeFileService, IMimeFileService mimeFileService,
Core.Domain.Interfaces.IMailService mailService, IMailService mailService,
IFileService fileService, IFileService fileService,
IWinoRequestDelegator requestDelegator, IWinoRequestDelegator requestDelegator,
IStatePersistanceService statePersistenceService, IStatePersistanceService statePersistenceService,
@@ -134,12 +141,16 @@ namespace Wino.Mail.ViewModels
IClipboardService clipboardService, IClipboardService clipboardService,
IUnsubscriptionService unsubscriptionService, IUnsubscriptionService unsubscriptionService,
IPreferencesService preferencesService, IPreferencesService preferencesService,
IPrintService printService,
IApplicationConfiguration applicationConfiguration,
IWinoServerConnectionManager winoServerConnectionManager) : base(dialogService) IWinoServerConnectionManager winoServerConnectionManager) : base(dialogService)
{ {
NativeAppService = nativeAppService; NativeAppService = nativeAppService;
StatePersistenceService = statePersistenceService; StatePersistenceService = statePersistenceService;
_contactService = contactService; _contactService = contactService;
PreferencesService = preferencesService; PreferencesService = preferencesService;
PrintService = printService;
_applicationConfiguration = applicationConfiguration;
_winoServerConnectionManager = winoServerConnectionManager; _winoServerConnectionManager = winoServerConnectionManager;
_clipboardService = clipboardService; _clipboardService = clipboardService;
_unsubscriptionService = unsubscriptionService; _unsubscriptionService = unsubscriptionService;
@@ -150,7 +161,6 @@ namespace Wino.Mail.ViewModels
_requestDelegator = requestDelegator; _requestDelegator = requestDelegator;
} }
[RelayCommand] [RelayCommand]
private async Task CopyClipboard(string copyText) private async Task CopyClipboard(string copyText)
{ {
@@ -243,14 +253,11 @@ namespace Wino.Mail.ViewModels
IsDarkWebviewRenderer = !IsDarkWebviewRenderer; IsDarkWebviewRenderer = !IsDarkWebviewRenderer;
else if (operation == MailOperation.SaveAs) else if (operation == MailOperation.SaveAs)
{ {
// Save as PDF await SaveAsAsync();
var pickedFolder = await DialogService.PickWindowsFolderAsync(); }
else if (operation == MailOperation.Print)
if (!string.IsNullOrEmpty(pickedFolder)) {
{ await PrintAsync();
var fullPath = Path.Combine(pickedFolder, $"{initializedMailItemViewModel.FromAddress}.pdf");
Messenger.Send(new SaveAsPDFRequested(fullPath));
}
} }
else if (operation == MailOperation.Reply || operation == MailOperation.ReplyAll || operation == MailOperation.Forward) else if (operation == MailOperation.Reply || operation == MailOperation.ReplyAll || operation == MailOperation.Forward)
{ {
@@ -519,6 +526,9 @@ namespace Wino.Mail.ViewModels
// Save As PDF // Save As PDF
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.SaveAs, true, true)); MenuItems.Add(MailOperationMenuItem.Create(MailOperation.SaveAs, true, true));
// Print
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.Print, true, true));
if (initializedMailItemViewModel == null) if (initializedMailItemViewModel == null)
return; return;
@@ -659,6 +669,66 @@ namespace Wino.Mail.ViewModels
} }
} }
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
{
var message = string.Format(Translator.DialogMessage_PrintingFailedMessage, result);
DialogService.InfoBarMessage(Translator.DialogMessage_PrintingFailedTitle, message, InfoBarMessageType.Warning);
}
}
catch (Exception ex)
{
DialogService.InfoBarMessage(string.Empty, ex.Message, InfoBarMessageType.Error);
Crashes.TrackError(ex);
}
}
private async Task SaveAsAsync()
{
try
{
var pickedFolder = await DialogService.PickWindowsFolderAsync();
if (string.IsNullOrEmpty(pickedFolder)) return;
var pdfFilePath = Path.Combine(pickedFolder, $"{initializedMailItemViewModel.FromAddress}.pdf");
bool isSaved = await SaveHTMLasPDFFunc(pdfFilePath);
if (isSaved)
{
DialogService.InfoBarMessage(Translator.Info_PDFSaveSuccessTitle,
string.Format(Translator.Info_PDFSaveSuccessMessage, pdfFilePath),
InfoBarMessageType.Success);
}
}
catch (Exception ex)
{
DialogService.InfoBarMessage(Translator.Info_PDFSaveFailedTitle, ex.Message, InfoBarMessageType.Error);
Crashes.TrackError(ex);
}
}
// Returns created file path. // Returns created file path.
private async Task<string> SaveAttachmentInternalAsync(MailAttachmentViewModel attachmentViewModel, string saveFolderPath) private async Task<string> SaveAttachmentInternalAsync(MailAttachmentViewModel attachmentViewModel, string saveFolderPath)
{ {

View File

@@ -96,6 +96,7 @@ namespace Wino
// Make sure the paths are setup on app start. // Make sure the paths are setup on app start.
_applicationFolderConfiguration.ApplicationDataFolderPath = ApplicationData.Current.LocalFolder.Path; _applicationFolderConfiguration.ApplicationDataFolderPath = ApplicationData.Current.LocalFolder.Path;
_applicationFolderConfiguration.PublisherSharedFolderPath = ApplicationData.Current.GetPublisherCacheFolder(ApplicationConfiguration.SharedFolderName).Path; _applicationFolderConfiguration.PublisherSharedFolderPath = ApplicationData.Current.GetPublisherCacheFolder(ApplicationConfiguration.SharedFolderName).Path;
_applicationFolderConfiguration.ApplicationTempFolderPath = ApplicationData.Current.TemporaryFolder.Path;
_appServiceConnectionManager = Services.GetService<IWinoServerConnectionManager<AppServiceConnection>>(); _appServiceConnectionManager = Services.GetService<IWinoServerConnectionManager<AppServiceConnection>>();
_themeService = Services.GetService<IThemeService>(); _themeService = Services.GetService<IThemeService>();

Binary file not shown.

View File

@@ -62,7 +62,7 @@ namespace Wino.Controls
{ WinoIconGlyph.Mail, "\uF509" }, { WinoIconGlyph.Mail, "\uF509" },
{ WinoIconGlyph.More, "\uE824" }, { WinoIconGlyph.More, "\uE824" },
{ WinoIconGlyph.CustomServer, "\uF509" }, { WinoIconGlyph.CustomServer, "\uF509" },
{ WinoIconGlyph.Print, "\uE954" },
{ WinoIconGlyph.Attachment, "\uE723" }, { WinoIconGlyph.Attachment, "\uE723" },
{ WinoIconGlyph.SortTextDesc, "\U000F3606" }, { WinoIconGlyph.SortTextDesc, "\U000F3606" },
{ WinoIconGlyph.SortLinesDesc, "\U000F038A" }, { WinoIconGlyph.SortLinesDesc, "\U000F038A" },

View File

@@ -69,7 +69,8 @@ namespace Wino.Controls
Blocked, Blocked,
Message, Message,
New, New,
IMAP IMAP,
Print
} }
public class WinoFontIcon : FontIcon public class WinoFontIcon : FontIcon

View File

@@ -144,6 +144,7 @@ namespace Wino.Helpers
MailOperation.ReplyAll => WinoIconGlyph.ReplyAll, MailOperation.ReplyAll => WinoIconGlyph.ReplyAll,
MailOperation.Zoom => WinoIconGlyph.Zoom, MailOperation.Zoom => WinoIconGlyph.Zoom,
MailOperation.SaveAs => WinoIconGlyph.Save, MailOperation.SaveAs => WinoIconGlyph.Save,
MailOperation.Print => WinoIconGlyph.Print,
MailOperation.Find => WinoIconGlyph.Find, MailOperation.Find => WinoIconGlyph.Find,
MailOperation.Forward => WinoIconGlyph.Forward, MailOperation.Forward => WinoIconGlyph.Forward,
MailOperation.DarkEditor => WinoIconGlyph.DarkEditor, MailOperation.DarkEditor => WinoIconGlyph.DarkEditor,

View File

@@ -3,7 +3,6 @@ using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.AppCenter.Crashes;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.Core;
@@ -12,8 +11,6 @@ using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation; using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
using Wino.Core.Domain;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Mail.ViewModels.Data; using Wino.Mail.ViewModels.Data;
using Wino.Messaging.Client.Mails; using Wino.Messaging.Client.Mails;
@@ -25,8 +22,7 @@ namespace Wino.Views
public sealed partial class MailRenderingPage : MailRenderingPageAbstract, public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
IRecipient<HtmlRenderingRequested>, IRecipient<HtmlRenderingRequested>,
IRecipient<CancelRenderingContentRequested>, IRecipient<CancelRenderingContentRequested>,
IRecipient<ApplicationThemeChanged>, IRecipient<ApplicationThemeChanged>
IRecipient<SaveAsPDFRequested>
{ {
private readonly IPreferencesService _preferencesService = App.Current.Services.GetService<IPreferencesService>(); private readonly IPreferencesService _preferencesService = App.Current.Services.GetService<IPreferencesService>();
private readonly IDialogService _dialogService = App.Current.Services.GetService<IDialogService>(); private readonly IDialogService _dialogService = App.Current.Services.GetService<IDialogService>();
@@ -44,6 +40,11 @@ namespace Wino.Views
Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF"); Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF");
Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation,msWebView2CodeCache"); Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation,msWebView2CodeCache");
ViewModel.SaveHTMLasPDFFunc = new Func<string, Task<bool>>((path) =>
{
return Chromium.CoreWebView2.PrintToPdfAsync(path, null).AsTask();
});
} }
public override async void OnEditorThemeChanged() public override async void OnEditorThemeChanged()
@@ -277,26 +278,6 @@ namespace Wino.Views
ViewModel.IsDarkWebviewRenderer = message.IsUnderlyingThemeDark; ViewModel.IsDarkWebviewRenderer = message.IsUnderlyingThemeDark;
} }
public async void Receive(SaveAsPDFRequested message)
{
try
{
bool isSaved = await Chromium.CoreWebView2.PrintToPdfAsync(message.FileSavePath, null);
if (isSaved)
{
_dialogService.InfoBarMessage(Translator.Info_PDFSaveSuccessTitle,
string.Format(Translator.Info_PDFSaveSuccessMessage, message.FileSavePath),
InfoBarMessageType.Success);
}
}
catch (Exception ex)
{
_dialogService.InfoBarMessage(Translator.Info_PDFSaveFailedTitle, ex.Message, InfoBarMessageType.Error);
Crashes.TrackError(ex);
}
}
private void InternetAddressClicked(object sender, RoutedEventArgs e) private void InternetAddressClicked(object sender, RoutedEventArgs e)
{ {
if (sender is HyperlinkButton hyperlinkButton) if (sender is HyperlinkButton hyperlinkButton)

View File

@@ -0,0 +1,9 @@
namespace Wino.Messaging.Client.Mails
{
/// <summary>
/// When print mail is requested.
/// </summary>
/// <param name="PDFFilePath">Path to PDF file that WebView2 saved the html content as PDF.</param>
/// <param name="PrintTitle">Printer title on the dialog.</param>
public record PrintMailRequested(string PDFFilePath, string PrintTitle);
}

View File

@@ -84,6 +84,7 @@ namespace Wino.Server
applicationFolderConfiguration.ApplicationDataFolderPath = ApplicationData.Current.LocalFolder.Path; applicationFolderConfiguration.ApplicationDataFolderPath = ApplicationData.Current.LocalFolder.Path;
applicationFolderConfiguration.PublisherSharedFolderPath = ApplicationData.Current.GetPublisherCacheFolder(ApplicationConfiguration.SharedFolderName).Path; applicationFolderConfiguration.PublisherSharedFolderPath = ApplicationData.Current.GetPublisherCacheFolder(ApplicationConfiguration.SharedFolderName).Path;
applicationFolderConfiguration.ApplicationTempFolderPath = ApplicationData.Current.TemporaryFolder.Path;
// Setup logger // Setup logger
var logInitializer = Services.GetService<ILogInitializer>(); var logInitializer = Services.GetService<ILogInitializer>();