Files
Wino-Mail/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs

792 lines
31 KiB
C#
Raw Normal View History

2024-04-18 01:44:37 +02:00
using System;
using System.Collections.Generic;
2024-04-18 01:44:37 +02:00
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using MailKit;
2024-04-18 01:44:37 +02:00
using MimeKit;
using Serilog;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
2024-04-18 01:44:37 +02:00
using Wino.Core.Domain.Enums;
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.Reader;
using Wino.Mail.ViewModels.Data;
Folder operations, Gmail folder sync improvements and rework of menu items. (#273) * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * New rename folder dialog keys. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * Enable empty folder on base sync. * Move updates on event listeners. * Remove folder UI messages. * Reworked folder synchronization for gmail. * Loading folders on the fly as the selected account changed instead of relying on cached menu items. * Merged account folder items, re-navigating to existing rendering page. * - Reworked merged account menu system. - Reworked unread item count loadings. - Fixed back button visibility. - Instant rendering of mails if renderer is active. - Animation fixes. - Menu item re-load crash/hang fixes. * Handle folder renaming on the UI. * Empty folder for all synchronizers. * New execution delay mechanism and handling folder mark as read for all synchronizers. * Revert UI changes on failure for IMAP. * Remove duplicate translation keys. * Cleanup.
2024-07-09 01:05:16 +02:00
using Wino.Mail.ViewModels.Messages;
Full trust Wino Server implementation. (#295) * Separation of messages. Introducing Wino.Messages library. * Wino.Server and Wino.Packaging projects. Enabling full trust for UWP and app service connection manager basics. * Remove debug code. * Enable generating assembly info to deal with unsupported os platform warnings. * Fix server-client connection. * UIMessage communication. Single instancing for server and re-connection mechanism on suspension. * Removed IWinoSynchronizerFactory from UWP project. * Removal of background task service from core. * Delegating changes to UI and triggering new background synchronization. * Fix build error. * Moved core lib messages to Messaging project. * Better client-server communication. Handling of requests in the server. New synchronizer factory in the server. * WAM broker and MSAL token caching for OutlookAuthenticator. Handling account creation for Outlook. * WinoServerResponse basics. * Delegating protocol activation for Gmail authenticator. * Adding margin to searchbox to match action bar width. * Move libraries into lib folder. * Storing base64 encoded mime on draft creation instead of MimeMessage object. Fixes serialization/deserialization issue with S.T.Json * Scrollbar adjustments * WınoExpander for thread expander layout ıssue. * Handling synchronizer state changes. * Double init on background activation. * FIxing packaging issues and new Wino Mail launcher protocol for activation from full thrust process. * Remove debug deserialization. * Remove debug code. * Making sure the server connection is established when the app is launched. * Thrust -> Trust string replacement... * Rename package to Wino Mail * Enable translated values in the server. * Fixed an issue where toast activation can't find the clicked mail after the folder is initialized. * Revert debug code. * Change server background sync to every 3 minute and Inbox only synchronization. * Revert google auth changes. * App preferences page. * Changing tray icon visibility on preference change. * Start the server with invisible tray icon if set to invisible. * Reconnect button on the title bar. * Handling of toast actions. * Enable x86 build for server during packaging. * Get rid of old background tasks and v180 migration. * Terminate client when Exit clicked in server. * Introducing SynchronizationSource to prevent notifying UI after server tick synchronization. * Remove confirmAppClose restricted capability and unused debug code in manifest. * Closing the reconnect info popup when reconnect is clicked. * Custom RetryHandler for OutlookSynchronizer and separating client/server logs. * Running server on Windows startup. * Fix startup exe. * Fix for expander list view item paddings. * Force full sync on app launch instead of Inbox. * Fix draft creation. * Fix an issue with custom folder sync logic. * Reporting back account sync progress from server. * Fix sending drafts and missing notifications for imap. * Changing imap folder sync requirements. * Retain file count is set to 3. * Disabled swipe gestures temporarily due to native crash with SwipeControl * Save all attachments implementation. * Localization for save all attachments button. * Fix logging dates for logs. * Fixing ARM64 build. * Add ARM64 build config to packaging project. * Comment out OutOfProcPDB for ARM64. * Hnadling GONE response for Outlook folder synchronization.
2024-08-05 00:36:26 +02:00
using Wino.Messaging.Client.Mails;
using Wino.Messaging.Server;
using IMailService = Wino.Core.Domain.Interfaces.IMailService;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
namespace Wino.Mail.ViewModels;
public partial class MailRenderingPageViewModel : MailBaseViewModel,
IRecipient<NewMailItemRenderingRequestedEvent>,
ITransferProgress // For listening IMAP message download progress.
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
private readonly IMailDialogService _dialogService;
private readonly IUnderlyingThemeService _underlyingThemeService;
private readonly IMimeFileService _mimeFileService;
private readonly Core.Domain.Interfaces.IMailService _mailService;
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;
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
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 IsImageRenderingDisabled
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
get
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
if (IsJunkMail)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
return !forceImageLoading;
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
else
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
return !CurrentRenderModel?.MailRenderingOptions?.LoadImages ?? false;
2024-04-18 01:44:37 +02:00
}
}
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private bool isDarkWebviewRenderer;
public bool IsDarkWebviewRenderer
{
get => isDarkWebviewRenderer;
set
{
if (SetProperty(ref isDarkWebviewRenderer, value))
{
2025-02-16 11:54:23 +01:00
InitializeCommandBarItems();
}
2025-02-16 11:35:43 +01:00
}
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ShouldDisplayDownloadProgress))]
public partial bool IsIndetermineProgress { get; set; }
2025-02-16 11:54:23 +01:00
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ShouldDisplayDownloadProgress))]
public partial double CurrentDownloadPercentage { get; set; }
2025-02-16 11:54:23 +01:00
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanUnsubscribe))]
public partial MailRenderModel CurrentRenderModel { get; set; }
2025-02-16 11:54:23 +01:00
[ObservableProperty]
public partial string Subject { get; set; }
2025-02-16 11:54:23 +01:00
[ObservableProperty]
public partial string FromAddress { get; set; }
2025-02-16 11:54:23 +01:00
[ObservableProperty]
public partial string FromName { get; set; }
2025-02-16 11:54:23 +01:00
[ObservableProperty]
public partial string ContactPicture { get; set; }
2025-02-16 11:54:23 +01:00
[ObservableProperty]
public partial DateTime CreationDate { get; set; }
public ObservableCollection<AccountContactViewModel> ToItems { get; set; } = [];
public ObservableCollection<AccountContactViewModel> CcItems { get; set; } = [];
public ObservableCollection<AccountContactViewModel> BccItems { get; set; } = [];
2025-02-16 11:54:23 +01:00
public ObservableCollection<MailAttachmentViewModel> Attachments { get; set; } = [];
public ObservableCollection<MailOperationMenuItem> MenuItems { get; set; } = [];
#endregion
public INativeAppService NativeAppService { get; }
public IStatePersistanceService StatePersistenceService { get; }
public IPreferencesService PreferencesService { get; }
public IPrintService PrintService { get; }
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)
{
_dialogService = dialogService;
NativeAppService = nativeAppService;
StatePersistenceService = statePersistenceService;
_contactService = contactService;
PreferencesService = preferencesService;
PrintService = printService;
_applicationConfiguration = applicationConfiguration;
_winoServerConnectionManager = winoServerConnectionManager;
_clipboardService = clipboardService;
_unsubscriptionService = unsubscriptionService;
_underlyingThemeService = underlyingThemeService;
_mimeFileService = mimeFileService;
_mailService = mailService;
_fileService = fileService;
_requestDelegator = requestDelegator;
}
[RelayCommand]
private async Task CopyClipboard(string copyText)
{
try
{
2025-02-16 11:54:23 +01:00
await _clipboardService.CopyClipboardAsync(copyText);
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
_dialogService.InfoBarMessage(Translator.ClipboardTextCopied_Title, string.Format(Translator.ClipboardTextCopied_Message, copyText), InfoBarMessageType.Information);
}
2025-02-16 11:54:23 +01:00
catch (Exception)
2025-02-16 11:35:43 +01:00
{
2025-02-16 11:54:23 +01:00
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error, string.Format(Translator.ClipboardTextCopyFailed_Message, copyText), InfoBarMessageType.Error);
}
}
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
[RelayCommand]
private async Task ForceImageLoading()
{
if (initializedMailItemViewModel == null && initializedMimeMessageInformation == null) return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
await RenderAsync(initializedMimeMessageInformation, ignoreJunkFilter: true);
}
[RelayCommand]
private async Task UnsubscribeAsync()
{
if (!(CurrentRenderModel?.UnsubscribeInfo?.CanUnsubscribe ?? false)) return;
bool confirmed;
// Try to unsubscribe by http first.
if (CurrentRenderModel.UnsubscribeInfo.HttpLink is not null)
{
if (!Uri.IsWellFormedUriString(CurrentRenderModel.UnsubscribeInfo.HttpLink, UriKind.RelativeOrAbsolute))
{
2025-02-16 11:54:23 +01:00
_dialogService.InfoBarMessage(Translator.Info_UnsubscribeLinkInvalidTitle, Translator.Info_UnsubscribeLinkInvalidMessage, InfoBarMessageType.Error);
return;
}
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
// Support for List-Unsubscribe-Post header. It can be done without launching browser.
// https://datatracker.ietf.org/doc/html/rfc8058
if (CurrentRenderModel.UnsubscribeInfo.IsOneClick)
{
confirmed = await _dialogService.ShowConfirmationDialogAsync(string.Format(Translator.DialogMessage_UnsubscribeConfirmationOneClickMessage, FromName), Translator.DialogMessage_UnsubscribeConfirmationTitle, Translator.Unsubscribe);
if (!confirmed) return;
bool isOneClickUnsubscribed = await _unsubscriptionService.OneClickUnsubscribeAsync(CurrentRenderModel.UnsubscribeInfo);
if (isOneClickUnsubscribed)
{
2025-02-16 11:54:23 +01:00
_dialogService.InfoBarMessage(Translator.Unsubscribe, string.Format(Translator.Info_UnsubscribeSuccessMessage, FromName), InfoBarMessageType.Success);
}
else
{
2025-02-16 11:54:23 +01:00
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error, Translator.Info_UnsubscribeErrorMessage, InfoBarMessageType.Error);
}
}
2025-02-16 11:54:23 +01:00
else
{
2025-02-16 11:54:23 +01:00
confirmed = await _dialogService.ShowConfirmationDialogAsync(string.Format(Translator.DialogMessage_UnsubscribeConfirmationGoToWebsiteMessage, FromName), Translator.DialogMessage_UnsubscribeConfirmationTitle, Translator.DialogMessage_UnsubscribeConfirmationGoToWebsiteConfirmButton);
if (!confirmed) return;
2025-02-16 11:54:23 +01:00
await NativeAppService.LaunchUriAsync(new Uri(CurrentRenderModel.UnsubscribeInfo.HttpLink));
}
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
else if (CurrentRenderModel.UnsubscribeInfo.MailToLink is not null)
{
2025-02-16 11:54:23 +01:00
confirmed = await _dialogService.ShowConfirmationDialogAsync(string.Format(Translator.DialogMessage_UnsubscribeConfirmationMailtoMessage, FromName, new string(CurrentRenderModel.UnsubscribeInfo.MailToLink.Skip(7).ToArray())), Translator.DialogMessage_UnsubscribeConfirmationTitle, Translator.Unsubscribe);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (!confirmed) return;
// TODO: Implement automatic mail send after user confirms the action.
// Currently it will launch compose page and user should manually press send button.
await NativeAppService.LaunchUriAsync(new Uri(CurrentRenderModel.UnsubscribeInfo.MailToLink));
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
[RelayCommand]
private async Task OperationClicked(MailOperationMenuItem menuItem)
{
if (menuItem == null) return;
await HandleMailOperationAsync(menuItem.Operation);
}
private async Task HandleMailOperationAsync(MailOperation operation)
{
// Toggle theme
if (operation == MailOperation.DarkEditor || operation == MailOperation.LightEditor)
IsDarkWebviewRenderer = !IsDarkWebviewRenderer;
else if (operation == MailOperation.SaveAs)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
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;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Create new draft.
var draftOptions = new DraftCreationOptions()
{
Reason = operation switch
{
2025-02-16 11:54:23 +01:00
MailOperation.Reply => DraftCreationReason.Reply,
MailOperation.ReplyAll => DraftCreationReason.ReplyAll,
MailOperation.Forward => DraftCreationReason.Forward,
_ => DraftCreationReason.Empty
},
ReferencedMessage = new ReferencedMessage()
{
MimeMessage = initializedMimeMessageInformation.MimeMessage,
MailCopy = initializedMailItemViewModel.MailCopy
}
};
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var (draftMailCopy, draftBase64MimeMessage) = await _mailService.CreateDraftAsync(initializedMailItemViewModel.AssignedAccount.Id, draftOptions).ConfigureAwait(false);
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
var draftPreparationRequest = new DraftPreparationRequest(initializedMailItemViewModel.AssignedAccount, draftMailCopy, draftBase64MimeMessage, draftOptions.Reason, initializedMailItemViewModel.MailCopy);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
await _requestDelegator.ExecuteAsync(draftPreparationRequest);
}
else if (initializedMailItemViewModel != null)
{
2025-02-16 11:54:23 +01:00
// All other operations require a mail item.
var prepRequest = new MailOperationPreperationRequest(operation, initializedMailItemViewModel.MailCopy);
await _requestDelegator.ExecuteAsync(prepRequest);
}
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private CancellationTokenSource renderCancellationTokenSource = new CancellationTokenSource();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
{
base.OnNavigatedTo(mode, parameters);
2025-02-16 11:54:23 +01:00
renderCancellationTokenSource.Cancel();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
initializedMailItemViewModel = null;
initializedMimeMessageInformation = null;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Dispose existing content first.
Messenger.Send(new CancelRenderingContentRequested());
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// This page can be accessed for 2 purposes.
// 1. Rendering a mail item when the user selects.
// 2. Rendering an existing EML file with MimeMessage.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// MimeMessage rendering must be readonly and no command bar items must be shown except common
// items like dark/light editor, zoom, print etc.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Configure common rendering properties first.
IsDarkWebviewRenderer = _underlyingThemeService.IsUnderlyingThemeDark();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
renderCancellationTokenSource = new CancellationTokenSource();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Mime content might not be available for now and might require a download.
try
{
if (parameters is MailItemViewModel selectedMailItemViewModel)
await RenderAsync(selectedMailItemViewModel, renderCancellationTokenSource.Token);
else if (parameters is MimeMessageInformation mimeMessageInformation)
await RenderAsync(mimeMessageInformation);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
InitializeCommandBarItems();
}
catch (OperationCanceledException)
2025-02-16 11:35:43 +01:00
{
2025-02-16 11:54:23 +01:00
Log.Information("Canceled mail rendering.");
}
catch (Exception ex)
{
_dialogService.InfoBarMessage(Translator.Info_MailRenderingFailedTitle, string.Format(Translator.Info_MailRenderingFailedMessage, ex.Message), InfoBarMessageType.Error);
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
Log.Error(ex, "Failed to render mail.");
2025-02-16 11:35:43 +01:00
}
2025-02-16 11:54:23 +01:00
}
2025-02-16 11:54:23 +01:00
private async Task HandleSingleItemDownloadAsync(MailItemViewModel mailItemViewModel)
{
try
{
// To show the progress on the UI.
CurrentDownloadPercentage = 1;
var package = new DownloadMissingMessageRequested(mailItemViewModel.AssignedAccount.Id, mailItemViewModel.MailCopy);
await _winoServerConnectionManager.GetResponseAsync<bool, DownloadMissingMessageRequested>(package);
}
catch (OperationCanceledException)
{
Log.Information("MIME download is canceled.");
}
catch (Exception ex)
{
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error, ex.Message, InfoBarMessageType.Error);
}
finally
2024-04-18 01:44:37 +02:00
{
ResetProgress();
2025-02-16 11:54:23 +01:00
}
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private async Task RenderAsync(MailItemViewModel mailItemViewModel, CancellationToken cancellationToken = default)
{
ResetProgress();
var isMimeExists = await _mimeFileService.IsMimeExistAsync(mailItemViewModel.AssignedAccount.Id, mailItemViewModel.MailCopy.FileId);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (!isMimeExists)
{
await HandleSingleItemDownloadAsync(mailItemViewModel);
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Find the MIME for this item and render it.
var mimeMessageInformation = await _mimeFileService.GetMimeMessageInformationAsync(mailItemViewModel.MailCopy.FileId,
mailItemViewModel.AssignedAccount.Id,
cancellationToken).ConfigureAwait(false);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (mimeMessageInformation == null)
{
_dialogService.InfoBarMessage(Translator.Info_MessageCorruptedTitle, Translator.Info_MessageCorruptedMessage, InfoBarMessageType.Error);
return;
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
initializedMailItemViewModel = mailItemViewModel;
await RenderAsync(mimeMessageInformation);
}
private async Task RenderAsync(MimeMessageInformation mimeMessageInformation, bool ignoreJunkFilter = false)
{
forceImageLoading = ignoreJunkFilter;
2025-02-16 11:54:23 +01:00
var message = mimeMessageInformation.MimeMessage;
var messagePath = mimeMessageInformation.Path;
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
initializedMimeMessageInformation = mimeMessageInformation;
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
// TODO: Handle S/MIME decryption.
// initializedMimeMessageInformation.MimeMessage.Body is MultipartSigned
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
var renderingOptions = PreferencesService.GetRenderingOptions();
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
// Prepare account contacts info in advance, to avoid UI shifts after clearing collections.
var toAccountContacts = await GetAccountContacts(message.To);
var ccAccountContacts = await GetAccountContacts(message.Cc);
var bccAccountContacts = await GetAccountContacts(message.Bcc);
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
await ExecuteUIThread(() =>
{
Attachments.Clear();
ToItems.Clear();
CcItems.Clear();
BccItems.Clear();
foreach (var item in toAccountContacts)
ToItems.Add(item);
foreach (var item in ccAccountContacts)
CcItems.Add(item);
foreach (var item in bccAccountContacts)
BccItems.Add(item);
Subject = string.IsNullOrWhiteSpace(message.Subject) ? Translator.MailItemNoSubject : message.Subject;
// 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;
// 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)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
renderingOptions.LoadImages = false;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Load images if forced.
if (ignoreJunkFilter)
{
renderingOptions.LoadImages = true;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
CurrentRenderModel = _mimeFileService.GetMailRenderModel(message, messagePath, renderingOptions);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
Messenger.Send(new HtmlRenderingRequested(CurrentRenderModel.RenderHtml));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
foreach (var attachment in CurrentRenderModel.Attachments)
{
Attachments.Add(new MailAttachmentViewModel(attachment));
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
OnPropertyChanged(nameof(IsImageRenderingDisabled));
Folder operations, Gmail folder sync improvements and rework of menu items. (#273) * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * New rename folder dialog keys. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * Enable empty folder on base sync. * Move updates on event listeners. * Remove folder UI messages. * Reworked folder synchronization for gmail. * Loading folders on the fly as the selected account changed instead of relying on cached menu items. * Merged account folder items, re-navigating to existing rendering page. * - Reworked merged account menu system. - Reworked unread item count loadings. - Fixed back button visibility. - Instant rendering of mails if renderer is active. - Animation fixes. - Menu item re-load crash/hang fixes. * Handle folder renaming on the UI. * Empty folder for all synchronizers. * New execution delay mechanism and handling folder mark as read for all synchronizers. * Revert UI changes on failure for IMAP. * Remove duplicate translation keys. * Cleanup.
2024-07-09 01:05:16 +02:00
2025-02-16 11:54:23 +01:00
StatePersistenceService.IsReadingMail = true;
});
}
2024-04-18 01:44:37 +02:00
private async Task<List<AccountContactViewModel>> GetAccountContacts(InternetAddressList internetAddresses)
2025-02-16 11:54:23 +01:00
{
List<AccountContactViewModel> accounts = [];
2025-02-16 11:54:23 +01:00
foreach (var item in internetAddresses)
{
2025-02-16 11:54:23 +01:00
if (item is MailboxAddress mailboxAddress)
{
2025-02-16 11:54:23 +01:00
var foundContact = await _contactService.GetAddressInformationByAddressAsync(mailboxAddress.Address).ConfigureAwait(false)
?? new AccountContact() { Name = mailboxAddress.Name, Address = mailboxAddress.Address };
var contactViewModel = new AccountContactViewModel(foundContact);
2025-02-16 11:54:23 +01:00
// Make sure that user account first in the list.
if (string.Equals(contactViewModel.Address, initializedMailItemViewModel?.AssignedAccount?.Address, StringComparison.OrdinalIgnoreCase))
{
contactViewModel.IsMe = true;
accounts.Insert(0, contactViewModel);
}
2025-02-16 11:54:23 +01:00
else
{
accounts.Add(contactViewModel);
}
}
2025-02-16 11:54:23 +01:00
else if (item is GroupAddress groupAddress)
{
accounts.AddRange(await GetAccountContacts(groupAddress.Members));
}
}
2024-04-18 01:44:37 +02:00
if (accounts.Count > 0)
accounts[^1].IsSemicolon = false;
2025-02-16 11:54:23 +01:00
return accounts;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public override void OnNavigatedFrom(NavigationMode mode, object parameters)
{
base.OnNavigatedFrom(mode, parameters);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
renderCancellationTokenSource.Cancel();
CurrentDownloadPercentage = 0d;
2025-02-16 11:54:23 +01:00
initializedMailItemViewModel = null;
initializedMimeMessageInformation = null;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
forceImageLoading = false;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
StatePersistenceService.IsReadingMail = false;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private void ResetProgress()
{
CurrentDownloadPercentage = 0;
IsIndetermineProgress = false;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private void InitializeCommandBarItems()
{
MenuItems.Clear();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Add light/dark editor theme switch.
if (IsDarkWebviewRenderer)
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.LightEditor));
else
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.DarkEditor));
2025-02-16 11:54:23 +01:00
// Save As PDF
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.SaveAs, true, true));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Print
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.Print, true, true));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (initializedMailItemViewModel == null)
return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.Seperator));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// You can't do these to draft items.
if (!initializedMailItemViewModel.IsDraft)
{
// Reply
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.Reply));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Reply All
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.ReplyAll));
2025-02-16 11:54:23 +01:00
// Forward
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.Forward));
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (initializedMimeMessageInformation?.MimeMessage != null)
{
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.ViewMessageSource, true, true));
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Archive - Unarchive
if (initializedMailItemViewModel.AssignedFolder.SpecialFolderType == SpecialFolderType.Archive)
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.UnArchive));
else
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.Archive));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Delete
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.SoftDelete));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Flag - Clear Flag
if (initializedMailItemViewModel.IsFlagged)
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.ClearFlag));
else
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.SetFlag));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Secondary items.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Read - Unread
if (initializedMailItemViewModel.IsRead)
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.MarkAsUnread, true, false));
else
MenuItems.Add(MailOperationMenuItem.Create(MailOperation.MarkAsRead, true, false));
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
protected override async void OnMailUpdated(MailCopy updatedMail)
{
base.OnMailUpdated(updatedMail);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (initializedMailItemViewModel == null) return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// 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;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Mail operation might change the mail item like mark read/unread or change flag.
// So we need to update the mail item view model when this happens.
// Also command bar items must be re-initialized since the items loaded based on the mail item.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
await ExecuteUIThread(() => { InitializeCommandBarItems(); });
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
[RelayCommand]
private async Task OpenAttachmentAsync(MailAttachmentViewModel attachmentViewModel)
{
try
{
var fileFolderPath = Path.Combine(initializedMimeMessageInformation.Path, attachmentViewModel.FileName);
var directoryInfo = new DirectoryInfo(initializedMimeMessageInformation.Path);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var fileExists = File.Exists(fileFolderPath);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (!fileExists)
await SaveAttachmentInternalAsync(attachmentViewModel, initializedMimeMessageInformation.Path);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
await LaunchFileInternalAsync(fileFolderPath);
2025-02-16 11:35:43 +01:00
}
2025-02-16 11:54:23 +01:00
catch (Exception ex)
2025-02-16 11:35:43 +01:00
{
2025-02-16 11:54:23 +01:00
Log.Error(ex, "Failed to open attachment.");
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
_dialogService.InfoBarMessage(Translator.Info_AttachmentOpenFailedTitle, Translator.Info_AttachmentOpenFailedMessage, InfoBarMessageType.Error);
}
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
[RelayCommand]
private async Task SaveAttachmentAsync(MailAttachmentViewModel attachmentViewModel)
{
if (attachmentViewModel == null)
return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
try
{
attachmentViewModel.IsBusy = true;
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
var pickedPath = await _dialogService.PickWindowsFolderAsync();
2025-02-16 11:54:23 +01:00
if (string.IsNullOrEmpty(pickedPath)) return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
await SaveAttachmentInternalAsync(attachmentViewModel, pickedPath);
_dialogService.InfoBarMessage(Translator.Info_AttachmentSaveSuccessTitle, Translator.Info_AttachmentSaveSuccessMessage, InfoBarMessageType.Success);
2025-02-16 11:35:43 +01:00
}
2025-02-16 11:54:23 +01:00
catch (Exception ex)
{
Log.Error(ex, "Failed to save attachment.");
2025-02-16 11:54:23 +01:00
_dialogService.InfoBarMessage(Translator.Info_AttachmentSaveFailedTitle, Translator.Info_AttachmentSaveFailedMessage, InfoBarMessageType.Error);
}
finally
Full trust Wino Server implementation. (#295) * Separation of messages. Introducing Wino.Messages library. * Wino.Server and Wino.Packaging projects. Enabling full trust for UWP and app service connection manager basics. * Remove debug code. * Enable generating assembly info to deal with unsupported os platform warnings. * Fix server-client connection. * UIMessage communication. Single instancing for server and re-connection mechanism on suspension. * Removed IWinoSynchronizerFactory from UWP project. * Removal of background task service from core. * Delegating changes to UI and triggering new background synchronization. * Fix build error. * Moved core lib messages to Messaging project. * Better client-server communication. Handling of requests in the server. New synchronizer factory in the server. * WAM broker and MSAL token caching for OutlookAuthenticator. Handling account creation for Outlook. * WinoServerResponse basics. * Delegating protocol activation for Gmail authenticator. * Adding margin to searchbox to match action bar width. * Move libraries into lib folder. * Storing base64 encoded mime on draft creation instead of MimeMessage object. Fixes serialization/deserialization issue with S.T.Json * Scrollbar adjustments * WınoExpander for thread expander layout ıssue. * Handling synchronizer state changes. * Double init on background activation. * FIxing packaging issues and new Wino Mail launcher protocol for activation from full thrust process. * Remove debug deserialization. * Remove debug code. * Making sure the server connection is established when the app is launched. * Thrust -> Trust string replacement... * Rename package to Wino Mail * Enable translated values in the server. * Fixed an issue where toast activation can't find the clicked mail after the folder is initialized. * Revert debug code. * Change server background sync to every 3 minute and Inbox only synchronization. * Revert google auth changes. * App preferences page. * Changing tray icon visibility on preference change. * Start the server with invisible tray icon if set to invisible. * Reconnect button on the title bar. * Handling of toast actions. * Enable x86 build for server during packaging. * Get rid of old background tasks and v180 migration. * Terminate client when Exit clicked in server. * Introducing SynchronizationSource to prevent notifying UI after server tick synchronization. * Remove confirmAppClose restricted capability and unused debug code in manifest. * Closing the reconnect info popup when reconnect is clicked. * Custom RetryHandler for OutlookSynchronizer and separating client/server logs. * Running server on Windows startup. * Fix startup exe. * Fix for expander list view item paddings. * Force full sync on app launch instead of Inbox. * Fix draft creation. * Fix an issue with custom folder sync logic. * Reporting back account sync progress from server. * Fix sending drafts and missing notifications for imap. * Changing imap folder sync requirements. * Retain file count is set to 3. * Disabled swipe gestures temporarily due to native crash with SwipeControl * Save all attachments implementation. * Localization for save all attachments button. * Fix logging dates for logs. * Fixing ARM64 build. * Add ARM64 build config to packaging project. * Comment out OutOfProcPDB for ARM64. * Hnadling GONE response for Outlook folder synchronization.
2024-08-05 00:36:26 +02:00
{
2025-02-16 11:54:23 +01:00
attachmentViewModel.IsBusy = false;
}
}
Full trust Wino Server implementation. (#295) * Separation of messages. Introducing Wino.Messages library. * Wino.Server and Wino.Packaging projects. Enabling full trust for UWP and app service connection manager basics. * Remove debug code. * Enable generating assembly info to deal with unsupported os platform warnings. * Fix server-client connection. * UIMessage communication. Single instancing for server and re-connection mechanism on suspension. * Removed IWinoSynchronizerFactory from UWP project. * Removal of background task service from core. * Delegating changes to UI and triggering new background synchronization. * Fix build error. * Moved core lib messages to Messaging project. * Better client-server communication. Handling of requests in the server. New synchronizer factory in the server. * WAM broker and MSAL token caching for OutlookAuthenticator. Handling account creation for Outlook. * WinoServerResponse basics. * Delegating protocol activation for Gmail authenticator. * Adding margin to searchbox to match action bar width. * Move libraries into lib folder. * Storing base64 encoded mime on draft creation instead of MimeMessage object. Fixes serialization/deserialization issue with S.T.Json * Scrollbar adjustments * WınoExpander for thread expander layout ıssue. * Handling synchronizer state changes. * Double init on background activation. * FIxing packaging issues and new Wino Mail launcher protocol for activation from full thrust process. * Remove debug deserialization. * Remove debug code. * Making sure the server connection is established when the app is launched. * Thrust -> Trust string replacement... * Rename package to Wino Mail * Enable translated values in the server. * Fixed an issue where toast activation can't find the clicked mail after the folder is initialized. * Revert debug code. * Change server background sync to every 3 minute and Inbox only synchronization. * Revert google auth changes. * App preferences page. * Changing tray icon visibility on preference change. * Start the server with invisible tray icon if set to invisible. * Reconnect button on the title bar. * Handling of toast actions. * Enable x86 build for server during packaging. * Get rid of old background tasks and v180 migration. * Terminate client when Exit clicked in server. * Introducing SynchronizationSource to prevent notifying UI after server tick synchronization. * Remove confirmAppClose restricted capability and unused debug code in manifest. * Closing the reconnect info popup when reconnect is clicked. * Custom RetryHandler for OutlookSynchronizer and separating client/server logs. * Running server on Windows startup. * Fix startup exe. * Fix for expander list view item paddings. * Force full sync on app launch instead of Inbox. * Fix draft creation. * Fix an issue with custom folder sync logic. * Reporting back account sync progress from server. * Fix sending drafts and missing notifications for imap. * Changing imap folder sync requirements. * Retain file count is set to 3. * Disabled swipe gestures temporarily due to native crash with SwipeControl * Save all attachments implementation. * Localization for save all attachments button. * Fix logging dates for logs. * Fixing ARM64 build. * Add ARM64 build config to packaging project. * Comment out OutOfProcPDB for ARM64. * Hnadling GONE response for Outlook folder synchronization.
2024-08-05 00:36:26 +02:00
2025-02-16 11:54:23 +01:00
[RelayCommand]
private async Task SaveAllAttachmentsAsync()
{
var pickedPath = await _dialogService.PickWindowsFolderAsync();
Full trust Wino Server implementation. (#295) * Separation of messages. Introducing Wino.Messages library. * Wino.Server and Wino.Packaging projects. Enabling full trust for UWP and app service connection manager basics. * Remove debug code. * Enable generating assembly info to deal with unsupported os platform warnings. * Fix server-client connection. * UIMessage communication. Single instancing for server and re-connection mechanism on suspension. * Removed IWinoSynchronizerFactory from UWP project. * Removal of background task service from core. * Delegating changes to UI and triggering new background synchronization. * Fix build error. * Moved core lib messages to Messaging project. * Better client-server communication. Handling of requests in the server. New synchronizer factory in the server. * WAM broker and MSAL token caching for OutlookAuthenticator. Handling account creation for Outlook. * WinoServerResponse basics. * Delegating protocol activation for Gmail authenticator. * Adding margin to searchbox to match action bar width. * Move libraries into lib folder. * Storing base64 encoded mime on draft creation instead of MimeMessage object. Fixes serialization/deserialization issue with S.T.Json * Scrollbar adjustments * WınoExpander for thread expander layout ıssue. * Handling synchronizer state changes. * Double init on background activation. * FIxing packaging issues and new Wino Mail launcher protocol for activation from full thrust process. * Remove debug deserialization. * Remove debug code. * Making sure the server connection is established when the app is launched. * Thrust -> Trust string replacement... * Rename package to Wino Mail * Enable translated values in the server. * Fixed an issue where toast activation can't find the clicked mail after the folder is initialized. * Revert debug code. * Change server background sync to every 3 minute and Inbox only synchronization. * Revert google auth changes. * App preferences page. * Changing tray icon visibility on preference change. * Start the server with invisible tray icon if set to invisible. * Reconnect button on the title bar. * Handling of toast actions. * Enable x86 build for server during packaging. * Get rid of old background tasks and v180 migration. * Terminate client when Exit clicked in server. * Introducing SynchronizationSource to prevent notifying UI after server tick synchronization. * Remove confirmAppClose restricted capability and unused debug code in manifest. * Closing the reconnect info popup when reconnect is clicked. * Custom RetryHandler for OutlookSynchronizer and separating client/server logs. * Running server on Windows startup. * Fix startup exe. * Fix for expander list view item paddings. * Force full sync on app launch instead of Inbox. * Fix draft creation. * Fix an issue with custom folder sync logic. * Reporting back account sync progress from server. * Fix sending drafts and missing notifications for imap. * Changing imap folder sync requirements. * Retain file count is set to 3. * Disabled swipe gestures temporarily due to native crash with SwipeControl * Save all attachments implementation. * Localization for save all attachments button. * Fix logging dates for logs. * Fixing ARM64 build. * Add ARM64 build config to packaging project. * Comment out OutOfProcPDB for ARM64. * Hnadling GONE response for Outlook folder synchronization.
2024-08-05 00:36:26 +02:00
2025-02-16 11:54:23 +01:00
if (string.IsNullOrEmpty(pickedPath)) return;
Full trust Wino Server implementation. (#295) * Separation of messages. Introducing Wino.Messages library. * Wino.Server and Wino.Packaging projects. Enabling full trust for UWP and app service connection manager basics. * Remove debug code. * Enable generating assembly info to deal with unsupported os platform warnings. * Fix server-client connection. * UIMessage communication. Single instancing for server and re-connection mechanism on suspension. * Removed IWinoSynchronizerFactory from UWP project. * Removal of background task service from core. * Delegating changes to UI and triggering new background synchronization. * Fix build error. * Moved core lib messages to Messaging project. * Better client-server communication. Handling of requests in the server. New synchronizer factory in the server. * WAM broker and MSAL token caching for OutlookAuthenticator. Handling account creation for Outlook. * WinoServerResponse basics. * Delegating protocol activation for Gmail authenticator. * Adding margin to searchbox to match action bar width. * Move libraries into lib folder. * Storing base64 encoded mime on draft creation instead of MimeMessage object. Fixes serialization/deserialization issue with S.T.Json * Scrollbar adjustments * WınoExpander for thread expander layout ıssue. * Handling synchronizer state changes. * Double init on background activation. * FIxing packaging issues and new Wino Mail launcher protocol for activation from full thrust process. * Remove debug deserialization. * Remove debug code. * Making sure the server connection is established when the app is launched. * Thrust -> Trust string replacement... * Rename package to Wino Mail * Enable translated values in the server. * Fixed an issue where toast activation can't find the clicked mail after the folder is initialized. * Revert debug code. * Change server background sync to every 3 minute and Inbox only synchronization. * Revert google auth changes. * App preferences page. * Changing tray icon visibility on preference change. * Start the server with invisible tray icon if set to invisible. * Reconnect button on the title bar. * Handling of toast actions. * Enable x86 build for server during packaging. * Get rid of old background tasks and v180 migration. * Terminate client when Exit clicked in server. * Introducing SynchronizationSource to prevent notifying UI after server tick synchronization. * Remove confirmAppClose restricted capability and unused debug code in manifest. * Closing the reconnect info popup when reconnect is clicked. * Custom RetryHandler for OutlookSynchronizer and separating client/server logs. * Running server on Windows startup. * Fix startup exe. * Fix for expander list view item paddings. * Force full sync on app launch instead of Inbox. * Fix draft creation. * Fix an issue with custom folder sync logic. * Reporting back account sync progress from server. * Fix sending drafts and missing notifications for imap. * Changing imap folder sync requirements. * Retain file count is set to 3. * Disabled swipe gestures temporarily due to native crash with SwipeControl * Save all attachments implementation. * Localization for save all attachments button. * Fix logging dates for logs. * Fixing ARM64 build. * Add ARM64 build config to packaging project. * Comment out OutOfProcPDB for ARM64. * Hnadling GONE response for Outlook folder synchronization.
2024-08-05 00:36:26 +02:00
2025-02-16 11:54:23 +01:00
try
{
Full trust Wino Server implementation. (#295) * Separation of messages. Introducing Wino.Messages library. * Wino.Server and Wino.Packaging projects. Enabling full trust for UWP and app service connection manager basics. * Remove debug code. * Enable generating assembly info to deal with unsupported os platform warnings. * Fix server-client connection. * UIMessage communication. Single instancing for server and re-connection mechanism on suspension. * Removed IWinoSynchronizerFactory from UWP project. * Removal of background task service from core. * Delegating changes to UI and triggering new background synchronization. * Fix build error. * Moved core lib messages to Messaging project. * Better client-server communication. Handling of requests in the server. New synchronizer factory in the server. * WAM broker and MSAL token caching for OutlookAuthenticator. Handling account creation for Outlook. * WinoServerResponse basics. * Delegating protocol activation for Gmail authenticator. * Adding margin to searchbox to match action bar width. * Move libraries into lib folder. * Storing base64 encoded mime on draft creation instead of MimeMessage object. Fixes serialization/deserialization issue with S.T.Json * Scrollbar adjustments * WınoExpander for thread expander layout ıssue. * Handling synchronizer state changes. * Double init on background activation. * FIxing packaging issues and new Wino Mail launcher protocol for activation from full thrust process. * Remove debug deserialization. * Remove debug code. * Making sure the server connection is established when the app is launched. * Thrust -> Trust string replacement... * Rename package to Wino Mail * Enable translated values in the server. * Fixed an issue where toast activation can't find the clicked mail after the folder is initialized. * Revert debug code. * Change server background sync to every 3 minute and Inbox only synchronization. * Revert google auth changes. * App preferences page. * Changing tray icon visibility on preference change. * Start the server with invisible tray icon if set to invisible. * Reconnect button on the title bar. * Handling of toast actions. * Enable x86 build for server during packaging. * Get rid of old background tasks and v180 migration. * Terminate client when Exit clicked in server. * Introducing SynchronizationSource to prevent notifying UI after server tick synchronization. * Remove confirmAppClose restricted capability and unused debug code in manifest. * Closing the reconnect info popup when reconnect is clicked. * Custom RetryHandler for OutlookSynchronizer and separating client/server logs. * Running server on Windows startup. * Fix startup exe. * Fix for expander list view item paddings. * Force full sync on app launch instead of Inbox. * Fix draft creation. * Fix an issue with custom folder sync logic. * Reporting back account sync progress from server. * Fix sending drafts and missing notifications for imap. * Changing imap folder sync requirements. * Retain file count is set to 3. * Disabled swipe gestures temporarily due to native crash with SwipeControl * Save all attachments implementation. * Localization for save all attachments button. * Fix logging dates for logs. * Fixing ARM64 build. * Add ARM64 build config to packaging project. * Comment out OutOfProcPDB for ARM64. * Hnadling GONE response for Outlook folder synchronization.
2024-08-05 00:36:26 +02:00
2025-02-16 11:54:23 +01:00
foreach (var attachmentViewModel in Attachments)
{
2025-02-16 11:54:23 +01:00
await SaveAttachmentInternalAsync(attachmentViewModel, pickedPath);
}
2025-02-16 11:54:23 +01:00
_dialogService.InfoBarMessage(Translator.Info_AttachmentSaveSuccessTitle, Translator.Info_AttachmentSaveSuccessMessage, InfoBarMessageType.Success);
}
catch (Exception ex)
2025-02-16 11:35:43 +01:00
{
2025-02-16 11:54:23 +01:00
Log.Error(ex, "Failed to save attachment.");
2025-02-16 11:54:23 +01:00
_dialogService.InfoBarMessage(Translator.Info_AttachmentSaveFailedTitle, Translator.Info_AttachmentSaveFailedMessage, InfoBarMessageType.Error);
}
}
2025-02-16 11:54:23 +01:00
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.
2025-02-16 11:54:23 +01:00
try
{
var printFilePath = Path.Combine(_applicationConfiguration.ApplicationTempFolderPath, "print.pdf");
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
if (File.Exists(printFilePath)) File.Delete(printFilePath);
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
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)
{
2025-02-16 11:54:23 +01:00
var message = string.Format(Translator.DialogMessage_PrintingFailedMessage, result);
_dialogService.InfoBarMessage(Translator.DialogMessage_PrintingFailedTitle, message, InfoBarMessageType.Warning);
}
}
2025-02-16 11:54:23 +01:00
catch (Exception ex)
{
Log.Error(ex, "Failed to print mail.");
_dialogService.InfoBarMessage(string.Empty, ex.Message, InfoBarMessageType.Error);
}
}
2025-02-16 11:54:23 +01:00
private async Task SaveAsAsync()
{
try
{
2025-02-16 11:54:23 +01:00
var pickedFolder = await _dialogService.PickWindowsFolderAsync();
2025-02-16 11:54:23 +01:00
if (string.IsNullOrEmpty(pickedFolder)) return;
2025-02-16 11:54:23 +01:00
var pdfFilePath = Path.Combine(pickedFolder, $"{initializedMailItemViewModel.FromAddress}.pdf");
2025-02-16 11:54:23 +01:00
bool isSaved = await SaveHTMLasPDFFunc(pdfFilePath);
2025-02-16 11:54:23 +01:00
if (isSaved)
{
2025-02-16 11:54:23 +01:00
_dialogService.InfoBarMessage(Translator.Info_PDFSaveSuccessTitle,
string.Format(Translator.Info_PDFSaveSuccessMessage, pdfFilePath),
InfoBarMessageType.Success);
}
}
2025-02-16 11:54:23 +01:00
catch (Exception ex)
2025-02-16 11:35:43 +01:00
{
2025-02-16 11:54:23 +01:00
Log.Error(ex, "Failed to save as PDF.");
_dialogService.InfoBarMessage(Translator.Info_PDFSaveFailedTitle, ex.Message, InfoBarMessageType.Error);
}
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Returns created file path.
private async Task<string> SaveAttachmentInternalAsync(MailAttachmentViewModel attachmentViewModel, string saveFolderPath)
{
var fullFilePath = Path.Combine(saveFolderPath, attachmentViewModel.FileName);
var stream = await _fileService.GetFileStreamAsync(saveFolderPath, attachmentViewModel.FileName);
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
using (stream)
{
await attachmentViewModel.MimeContent.DecodeToAsync(stream);
2025-02-16 11:35:43 +01:00
}
2025-02-16 11:54:23 +01:00
return fullFilePath;
}
private async Task LaunchFileInternalAsync(string filePath)
{
try
2025-02-16 11:35:43 +01:00
{
2025-02-16 11:54:23 +01:00
await NativeAppService.LaunchFileAsync(filePath);
}
catch (Exception ex)
{
_dialogService.InfoBarMessage(Translator.Info_FileLaunchFailedTitle, ex.Message, InfoBarMessageType.Error);
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
void ITransferProgress.Report(long bytesTransferred, long totalSize)
=> _ = ExecuteUIThread(() => { CurrentDownloadPercentage = bytesTransferred * 100 / Math.Max(1, totalSize); });
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// For upload.
void ITransferProgress.Report(long bytesTransferred) { }
Folder operations, Gmail folder sync improvements and rework of menu items. (#273) * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Disable vertical scroll for composing page editor items. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * New rename folder dialog keys. * Insfra work for folder operations and rename folder code. * RenameFolder for Gmail. * Fixed input dialog to take custom take for primary button. * Missing rename for DS call. * Outlook to throw exception in case of error. * Implemented rename folder functionality for Outlook. * Remove default primary text from input dialog. * Fixed an issue where outlook folder rename does not work. * Fixing some issues with imap folder sync. * fix copy pasta * TODO folder update/removed overrides for shell. * New rename folder dialog keys. * New rename folder dialog keys. * New rename folder dialog keys. * Fixed an issue where redundant older updates causing pivots to be re-created. * New empty folder request * Enable empty folder on base sync. * Move updates on event listeners. * Remove folder UI messages. * Reworked folder synchronization for gmail. * Loading folders on the fly as the selected account changed instead of relying on cached menu items. * Merged account folder items, re-navigating to existing rendering page. * - Reworked merged account menu system. - Reworked unread item count loadings. - Fixed back button visibility. - Instant rendering of mails if renderer is active. - Animation fixes. - Menu item re-load crash/hang fixes. * Handle folder renaming on the UI. * Empty folder for all synchronizers. * New execution delay mechanism and handling folder mark as read for all synchronizers. * Revert UI changes on failure for IMAP. * Remove duplicate translation keys. * Cleanup.
2024-07-09 01:05:16 +02:00
2025-02-16 11:54:23 +01:00
public async void Receive(NewMailItemRenderingRequestedEvent message)
{
try
2025-02-16 11:35:43 +01:00
{
2025-02-16 11:54:23 +01:00
await RenderAsync(message.MailItemViewModel, renderCancellationTokenSource.Token);
}
catch (OperationCanceledException)
{
Log.Information("Canceled mail rendering.");
}
catch (Exception ex)
{
_dialogService.InfoBarMessage(Translator.Info_MailRenderingFailedTitle, string.Format(Translator.Info_MailRenderingFailedMessage, ex.Message), InfoBarMessageType.Error);
Full trust Wino Server implementation. (#295) * Separation of messages. Introducing Wino.Messages library. * Wino.Server and Wino.Packaging projects. Enabling full trust for UWP and app service connection manager basics. * Remove debug code. * Enable generating assembly info to deal with unsupported os platform warnings. * Fix server-client connection. * UIMessage communication. Single instancing for server and re-connection mechanism on suspension. * Removed IWinoSynchronizerFactory from UWP project. * Removal of background task service from core. * Delegating changes to UI and triggering new background synchronization. * Fix build error. * Moved core lib messages to Messaging project. * Better client-server communication. Handling of requests in the server. New synchronizer factory in the server. * WAM broker and MSAL token caching for OutlookAuthenticator. Handling account creation for Outlook. * WinoServerResponse basics. * Delegating protocol activation for Gmail authenticator. * Adding margin to searchbox to match action bar width. * Move libraries into lib folder. * Storing base64 encoded mime on draft creation instead of MimeMessage object. Fixes serialization/deserialization issue with S.T.Json * Scrollbar adjustments * WınoExpander for thread expander layout ıssue. * Handling synchronizer state changes. * Double init on background activation. * FIxing packaging issues and new Wino Mail launcher protocol for activation from full thrust process. * Remove debug deserialization. * Remove debug code. * Making sure the server connection is established when the app is launched. * Thrust -> Trust string replacement... * Rename package to Wino Mail * Enable translated values in the server. * Fixed an issue where toast activation can't find the clicked mail after the folder is initialized. * Revert debug code. * Change server background sync to every 3 minute and Inbox only synchronization. * Revert google auth changes. * App preferences page. * Changing tray icon visibility on preference change. * Start the server with invisible tray icon if set to invisible. * Reconnect button on the title bar. * Handling of toast actions. * Enable x86 build for server during packaging. * Get rid of old background tasks and v180 migration. * Terminate client when Exit clicked in server. * Introducing SynchronizationSource to prevent notifying UI after server tick synchronization. * Remove confirmAppClose restricted capability and unused debug code in manifest. * Closing the reconnect info popup when reconnect is clicked. * Custom RetryHandler for OutlookSynchronizer and separating client/server logs. * Running server on Windows startup. * Fix startup exe. * Fix for expander list view item paddings. * Force full sync on app launch instead of Inbox. * Fix draft creation. * Fix an issue with custom folder sync logic. * Reporting back account sync progress from server. * Fix sending drafts and missing notifications for imap. * Changing imap folder sync requirements. * Retain file count is set to 3. * Disabled swipe gestures temporarily due to native crash with SwipeControl * Save all attachments implementation. * Localization for save all attachments button. * Fix logging dates for logs. * Fixing ARM64 build. * Add ARM64 build config to packaging project. * Comment out OutOfProcPDB for ARM64. * Hnadling GONE response for Outlook folder synchronization.
2024-08-05 00:36:26 +02:00
2025-02-16 11:54:23 +01:00
Log.Error(ex, "Failed to render mail.");
Full trust Wino Server implementation. (#295) * Separation of messages. Introducing Wino.Messages library. * Wino.Server and Wino.Packaging projects. Enabling full trust for UWP and app service connection manager basics. * Remove debug code. * Enable generating assembly info to deal with unsupported os platform warnings. * Fix server-client connection. * UIMessage communication. Single instancing for server and re-connection mechanism on suspension. * Removed IWinoSynchronizerFactory from UWP project. * Removal of background task service from core. * Delegating changes to UI and triggering new background synchronization. * Fix build error. * Moved core lib messages to Messaging project. * Better client-server communication. Handling of requests in the server. New synchronizer factory in the server. * WAM broker and MSAL token caching for OutlookAuthenticator. Handling account creation for Outlook. * WinoServerResponse basics. * Delegating protocol activation for Gmail authenticator. * Adding margin to searchbox to match action bar width. * Move libraries into lib folder. * Storing base64 encoded mime on draft creation instead of MimeMessage object. Fixes serialization/deserialization issue with S.T.Json * Scrollbar adjustments * WınoExpander for thread expander layout ıssue. * Handling synchronizer state changes. * Double init on background activation. * FIxing packaging issues and new Wino Mail launcher protocol for activation from full thrust process. * Remove debug deserialization. * Remove debug code. * Making sure the server connection is established when the app is launched. * Thrust -> Trust string replacement... * Rename package to Wino Mail * Enable translated values in the server. * Fixed an issue where toast activation can't find the clicked mail after the folder is initialized. * Revert debug code. * Change server background sync to every 3 minute and Inbox only synchronization. * Revert google auth changes. * App preferences page. * Changing tray icon visibility on preference change. * Start the server with invisible tray icon if set to invisible. * Reconnect button on the title bar. * Handling of toast actions. * Enable x86 build for server during packaging. * Get rid of old background tasks and v180 migration. * Terminate client when Exit clicked in server. * Introducing SynchronizationSource to prevent notifying UI after server tick synchronization. * Remove confirmAppClose restricted capability and unused debug code in manifest. * Closing the reconnect info popup when reconnect is clicked. * Custom RetryHandler for OutlookSynchronizer and separating client/server logs. * Running server on Windows startup. * Fix startup exe. * Fix for expander list view item paddings. * Force full sync on app launch instead of Inbox. * Fix draft creation. * Fix an issue with custom folder sync logic. * Reporting back account sync progress from server. * Fix sending drafts and missing notifications for imap. * Changing imap folder sync requirements. * Retain file count is set to 3. * Disabled swipe gestures temporarily due to native crash with SwipeControl * Save all attachments implementation. * Localization for save all attachments button. * Fix logging dates for logs. * Fixing ARM64 build. * Add ARM64 build config to packaging project. * Comment out OutOfProcPDB for ARM64. * Hnadling GONE response for Outlook folder synchronization.
2024-08-05 00:36:26 +02:00
}
2024-04-18 01:44:37 +02:00
}
}