diff --git a/Directory.Packages.props b/Directory.Packages.props index 15bf141d..7224d5b6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,64 +1,65 @@ - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Core.Domain/Models/DomainModelsJsonContext.cs b/Wino.Core.Domain/Models/DomainModelsJsonContext.cs index 39ce4827..7e9c5fa8 100644 --- a/Wino.Core.Domain/Models/DomainModelsJsonContext.cs +++ b/Wino.Core.Domain/Models/DomainModelsJsonContext.cs @@ -1,4 +1,5 @@ -using System.Text.Json.Serialization; +using System.Collections.Generic; +using System.Text.Json.Serialization; using Wino.Core.Domain.Models.AutoDiscovery; using Wino.Core.Domain.Models.Personalization; using Wino.Core.Domain.Models.Reader; @@ -8,4 +9,5 @@ namespace Wino.Core.Domain.Models; [JsonSerializable(typeof(AutoDiscoverySettings))] [JsonSerializable(typeof(CustomThemeMetadata))] [JsonSerializable(typeof(WebViewMessage))] +[JsonSerializable(typeof(List))] public partial class DomainModelsJsonContext: JsonSerializerContext; diff --git a/Wino.Core.UWP/Extensions/WebViewExtensions.cs b/Wino.Core.UWP/Extensions/WebViewExtensions.cs index c411351e..1edbe6fb 100644 --- a/Wino.Core.UWP/Extensions/WebViewExtensions.cs +++ b/Wino.Core.UWP/Extensions/WebViewExtensions.cs @@ -29,4 +29,17 @@ public static async Task ExecuteScriptFunctionSafeAsync(this Microsoft.U return string.Empty; } + + public static async Task ExecuteScriptSafeAsync(this Microsoft.UI.Xaml.Controls.WebView2 Chromium, string script) + { + if (Chromium == null) return string.Empty; + + try + { + return await Chromium.ExecuteScriptAsync(script); + } + catch { } + + return string.Empty; + } } diff --git a/Wino.Mail/App.xaml b/Wino.Mail/App.xaml index 965f4756..d044da41 100644 --- a/Wino.Mail/App.xaml +++ b/Wino.Mail/App.xaml @@ -15,6 +15,7 @@ + diff --git a/Wino.Mail/Controls/WebViewEditorControl.cs b/Wino.Mail/Controls/WebViewEditorControl.cs new file mode 100644 index 00000000..df12414f --- /dev/null +++ b/Wino.Mail/Controls/WebViewEditorControl.cs @@ -0,0 +1,362 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text.Json; +using System.Threading.Tasks; +using CommunityToolkit.WinUI; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.UI.Xaml.Controls; +using Microsoft.Web.WebView2.Core; +using Windows.UI.ViewManagement.Core; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Markup; +using Windows.UI.Xaml.Media; +using Wino.Core.Domain; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models; +using Wino.Core.Domain.Models.Reader; +using Wino.Core.UWP.Extensions; + +namespace Wino.Mail.Controls; +public sealed partial class WebViewEditorControl : Control, IDisposable +{ + private readonly INativeAppService _nativeAppService = App.Current.Services.GetService(); + private readonly IFontService _fontService = App.Current.Services.GetService(); + private readonly IPreferencesService _preferencesService = App.Current.Services.GetService(); + private readonly IUnderlyingThemeService _underlyingThemeService = App.Current.Services.GetService(); + + [GeneratedDependencyProperty] + public partial bool IsEditorDarkMode { get; set; } + async partial void OnIsEditorDarkModeChanged(bool newValue) + { + await UpdateEditorThemeAsync(); + } + + [GeneratedDependencyProperty] + public partial bool IsEditorBold { get; set; } + private bool _isEditorBoldInternal; + async partial void OnIsEditorBoldChanged(bool newValue) + { + if (newValue != _isEditorBoldInternal) + { + await _chromium.ExecuteScriptFunctionSafeAsync("editor.execCommand", JsonSerializer.Serialize("bold", BasicTypesJsonContext.Default.String)); + } + } + + [GeneratedDependencyProperty] + public partial bool IsEditorItalic { get; set; } + private bool _isEditorItalicInternal; + async partial void OnIsEditorItalicChanged(bool newValue) + { + if (newValue != _isEditorItalicInternal) + { + await _chromium.ExecuteScriptFunctionSafeAsync("editor.execCommand", JsonSerializer.Serialize("italic", BasicTypesJsonContext.Default.String)); + } + } + + [GeneratedDependencyProperty] + public partial bool IsEditorUnderline { get; set; } + private bool _isEditorUnderlineInternal; + async partial void OnIsEditorUnderlineChanged(bool newValue) + { + if (newValue != _isEditorUnderlineInternal) + { + await _chromium.ExecuteScriptFunctionSafeAsync("editor.execCommand", JsonSerializer.Serialize("underline", BasicTypesJsonContext.Default.String)); + } + } + + [GeneratedDependencyProperty] + public partial bool IsEditorStrikethrough { get; set; } + private bool _isEditorStrikethroughInternal; + async partial void OnIsEditorStrikethroughChanged(bool newValue) + { + if (newValue != _isEditorStrikethroughInternal) + { + await _chromium.ExecuteScriptFunctionSafeAsync("editor.execCommand", JsonSerializer.Serialize("strikethrough", BasicTypesJsonContext.Default.String)); + } + } + + [GeneratedDependencyProperty] + public partial bool IsEditorOl { get; set; } + private bool _isEditorOlInternal; + async partial void OnIsEditorOlChanged(bool newValue) + { + if (newValue != _isEditorOlInternal) + { + await _chromium.ExecuteScriptFunctionSafeAsync("editor.execCommand", JsonSerializer.Serialize("insertorderedlist", BasicTypesJsonContext.Default.String)); + } + } + + [GeneratedDependencyProperty] + public partial bool IsEditorUl { get; set; } + private bool _isEditorUlInternal; + async partial void OnIsEditorUlChanged(bool newValue) + { + if (newValue != _isEditorUlInternal) + { + await _chromium.ExecuteScriptFunctionSafeAsync("editor.execCommand", JsonSerializer.Serialize("insertunorderedlist", BasicTypesJsonContext.Default.String)); + } + } + + [GeneratedDependencyProperty] + public partial bool IsEditorIndentEnabled { get; private set; } + + [GeneratedDependencyProperty] + public partial bool IsEditorOutdentEnabled { get; private set; } + + [GeneratedDependencyProperty] + public partial int EditorAlignmentSelectedIndex { get; set; } + private int _editorAlignmentSelectedIndexInternal; + async partial void OnEditorAlignmentSelectedIndexChanged(int newValue) + { + if (newValue != _editorAlignmentSelectedIndexInternal) + { + var alignmentAction = newValue switch + { + 0 => "justifyleft", + 1 => "justifycenter", + 2 => "justifyright", + 3 => "justifyfull", + _ => throw new ArgumentOutOfRangeException(nameof(newValue)) + }; + + await _chromium.ExecuteScriptFunctionSafeAsync("editor.execCommand", JsonSerializer.Serialize(alignmentAction, BasicTypesJsonContext.Default.String)); + } + } + + [GeneratedDependencyProperty] + public partial bool IsEditorWebViewEditor { get; set; } + + async partial void OnIsEditorWebViewEditorChanged(bool newValue) + { + await _chromium.ExecuteScriptFunctionSafeAsync("toggleToolbar", JsonSerializer.Serialize(newValue, BasicTypesJsonContext.Default.Boolean)); + } + + private const string PART_WebView = "WebView"; + private WebView2 _chromium; + private bool _disposedValue; + private readonly TaskCompletionSource _domLoadedTask = new(); + + public WebViewEditorControl() + { + this.DefaultStyleKey = typeof(WebViewEditorControl); + + IsEditorIndentEnabled = true; + + IsEditorDarkMode = _underlyingThemeService.IsUnderlyingThemeDark(); + } + + protected override async void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _chromium = GetTemplateChild(PART_WebView) as WebView2; + + await InitializeComponent(); + } + + private async Task InitializeComponent() + { + Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF"); + Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation"); + _chromium.CoreWebView2Initialized += ChromiumInitialized; + + await _chromium.EnsureCoreWebView2Async(); + } + + public async void EditorIndentAsync() + { + await _chromium.ExecuteScriptFunctionSafeAsync("editor.execCommand", JsonSerializer.Serialize("indent", BasicTypesJsonContext.Default.String)); + } + + public async void EditorOutdentAsync() + { + await _chromium.ExecuteScriptFunctionSafeAsync("editor.execCommand", JsonSerializer.Serialize("outdent", BasicTypesJsonContext.Default.String)); + } + + public void ToggleEditorTheme() + { + IsEditorDarkMode = !IsEditorDarkMode; + } + + public async Task GetHtmlBodyAsync() + { + var editorContent = await _chromium.ExecuteScriptFunctionSafeAsync("GetHTMLContent"); + + return JsonSerializer.Deserialize(editorContent, BasicTypesJsonContext.Default.String); + } + + public async void ShowImagePicker() + { + await _chromium.ExecuteScriptFunctionSafeAsync("imageInput.click"); + } + + public async Task InsertImagesAsync(List images) + { + await _chromium.ExecuteScriptFunctionSafeAsync("insertImages", JsonSerializer.Serialize(images, DomainModelsJsonContext.Default.ListImageInfo)); + } + + public async void ShowEmojiPicker() + { + CoreInputView.GetForCurrentView().TryShow(CoreInputViewKind.Emoji); + + await FocusEditorAsync(focusControlAsWell: true); + } + + public WebView2 GetUnderlyingWebView() => _chromium; + + public async Task RenderHtmlAsync(string htmlBody) + { + await _domLoadedTask.Task; + + await UpdateEditorThemeAsync(); + await InitializeEditorAsync(); + + await _chromium.ExecuteScriptFunctionAsync("RenderHTML", parameters: JsonSerializer.Serialize(string.IsNullOrEmpty(htmlBody) ? " " : htmlBody, BasicTypesJsonContext.Default.String)); + } + + private async Task InitializeEditorAsync() + { + var fonts = _fontService.GetFonts(); + var composerFont = _preferencesService.ComposerFont; + int composerFontSize = _preferencesService.ComposerFontSize; + var readerFont = _preferencesService.ReaderFont; + int readerFontSize = _preferencesService.ReaderFontSize; + return await _chromium.ExecuteScriptFunctionAsync("initializeJodit", false, + JsonSerializer.Serialize(fonts, BasicTypesJsonContext.Default.ListString), + JsonSerializer.Serialize(composerFont, BasicTypesJsonContext.Default.String), + JsonSerializer.Serialize(composerFontSize, BasicTypesJsonContext.Default.Int32), + JsonSerializer.Serialize(readerFont, BasicTypesJsonContext.Default.String), + JsonSerializer.Serialize(readerFontSize, BasicTypesJsonContext.Default.Int32)); + } + + private async void ChromiumInitialized(WebView2 sender, CoreWebView2InitializedEventArgs args) + { + var editorBundlePath = (await _nativeAppService.GetEditorBundlePathAsync()).Replace("editor.html", string.Empty); + + _chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.editor", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow); + _chromium.Source = new Uri("https://app.editor/editor.html"); + + _chromium.CoreWebView2.DOMContentLoaded += DomLoaded; + + _chromium.CoreWebView2.WebMessageReceived += ScriptMessageReceived; + } + + public async Task UpdateEditorThemeAsync() + { + await _domLoadedTask.Task; + + if (IsEditorDarkMode) + { + _chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Dark; + await _chromium.ExecuteScriptFunctionSafeAsync("SetDarkEditor"); + } + else + { + _chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Light; + await _chromium.ExecuteScriptFunctionSafeAsync("SetLightEditor"); + } + } + + /// + /// Places the cursor in the composer. + /// + /// Whether control itself should be focused as well or not. + public async Task FocusEditorAsync(bool focusControlAsWell) + { + await _chromium.ExecuteScriptSafeAsync("editor.selection.setCursorIn(editor.editor.firstChild, true)"); + + if (focusControlAsWell) + { + _chromium.Focus(FocusState.Keyboard); + _chromium.Focus(FocusState.Programmatic); + } + } + + private void ScriptMessageReceived(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args) + { + var change = JsonSerializer.Deserialize(args.WebMessageAsJson, DomainModelsJsonContext.Default.WebViewMessage); + + if (change.Type == "bold") + { + _isEditorBoldInternal = change.Value == "true"; + IsEditorBold = _isEditorBoldInternal; + } + else if (change.Type == "italic") + { + _isEditorItalicInternal = change.Value == "true"; + IsEditorItalic = _isEditorItalicInternal; + } + else if (change.Type == "underline") + { + _isEditorUnderlineInternal = change.Value == "true"; + IsEditorUnderline = _isEditorUnderlineInternal; + } + else if (change.Type == "strikethrough") + { + _isEditorStrikethroughInternal = change.Value == "true"; + IsEditorStrikethrough = _isEditorStrikethroughInternal; + } + else if (change.Type == "ol") + { + _isEditorOlInternal = change.Value == "true"; + IsEditorOl = _isEditorOlInternal; + } + else if (change.Type == "ul") + { + _isEditorUlInternal = change.Value == "true"; + IsEditorUl = _isEditorUlInternal; + } + else if (change.Type == "indent") + { + IsEditorIndentEnabled = change.Value != "disabled"; + } + else if (change.Type == "outdent") + { + IsEditorOutdentEnabled = change.Value != "disabled"; + } + else if (change.Type == "alignment") + { + var parsedValue = change.Value switch + { + "jodit-icon_left" => 0, + "jodit-icon_center" => 1, + "jodit-icon_right" => 2, + "jodit-icon_justify" => 3, + _ => 0 + }; + _editorAlignmentSelectedIndexInternal = parsedValue; + EditorAlignmentSelectedIndex = _editorAlignmentSelectedIndexInternal; + } + } + + private void DomLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) => _domLoadedTask.TrySetResult(true); + + private void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing && _chromium != null) + { + _chromium.CoreWebView2Initialized -= ChromiumInitialized; + + if (_chromium.CoreWebView2 != null) + { + _chromium.CoreWebView2.DOMContentLoaded -= DomLoaded; + _chromium.CoreWebView2.WebMessageReceived -= ScriptMessageReceived; + } + + _chromium.Close(); + } + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } +} diff --git a/Wino.Mail/Dialogs/SignatureEditorDialog.xaml b/Wino.Mail/Dialogs/SignatureEditorDialog.xaml index a10a2149..1e0893fa 100644 --- a/Wino.Mail/Dialogs/SignatureEditorDialog.xaml +++ b/Wino.Mail/Dialogs/SignatureEditorDialog.xaml @@ -5,6 +5,7 @@ xmlns:accounts="using:Wino.Core.Domain.Models.Accounts" xmlns:controls="using:Wino.Controls" xmlns:controls1="using:CommunityToolkit.WinUI.Controls" + xmlns:controls2="using:Wino.Mail.Controls" xmlns:coreControls="using:Wino.Core.UWP.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:domain="using:Wino.Core.Domain" @@ -55,10 +56,10 @@ IsOpen="False"> + Visibility="{x:Bind WebViewEditor.IsEditorDarkMode, Mode=OneWay}"> @@ -66,10 +67,10 @@ + Visibility="{x:Bind WebViewEditor.IsEditorDarkMode, Mode=OneWay, Converter={StaticResource ReverseBooleanToVisibilityConverter}}"> @@ -78,9 +79,8 @@ @@ -88,9 +88,8 @@ @@ -98,9 +97,8 @@ @@ -108,9 +106,8 @@ @@ -119,9 +116,8 @@ @@ -129,9 +125,8 @@ @@ -142,9 +137,9 @@ @@ -152,9 +147,9 @@ @@ -164,10 +159,9 @@ + SelectedIndex="{x:Bind WebViewEditor.EditorAlignmentSelectedIndex, Mode=TwoWay}"> @@ -207,9 +201,8 @@ @@ -225,9 +218,8 @@ @@ -236,9 +228,8 @@ @@ -252,11 +243,7 @@ BorderBrush="{StaticResource CardStrokeColorDefaultBrush}" BorderThickness="1" CornerRadius="3"> - - - - - + diff --git a/Wino.Mail/Dialogs/SignatureEditorDialog.xaml.cs b/Wino.Mail/Dialogs/SignatureEditorDialog.xaml.cs index f4ae2ad9..29400f48 100644 --- a/Wino.Mail/Dialogs/SignatureEditorDialog.xaml.cs +++ b/Wino.Mail/Dialogs/SignatureEditorDialog.xaml.cs @@ -1,48 +1,20 @@ using System; -using System.Text.Json; using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Web.WebView2.Core; -using Windows.UI.ViewManagement.Core; -using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Wino.Core.Domain; using Wino.Core.Domain.Entities.Mail; -using Wino.Core.Domain.Interfaces; -using Wino.Core.Domain.Models; -using Wino.Core.Domain.Models.Reader; -using Wino.Core.UWP.Extensions; -using Wino.Views.Settings; namespace Wino.Dialogs; public sealed partial class SignatureEditorDialog : ContentDialog { - private Func> _getHTMLBodyFunction; - private readonly TaskCompletionSource _domLoadedTask = new TaskCompletionSource(); - - private readonly INativeAppService _nativeAppService = App.Current.Services.GetService(); - private readonly IFontService _fontService = App.Current.Services.GetService(); - private readonly IPreferencesService _preferencesService = App.Current.Services.GetService(); - public AccountSignature Result; - public bool IsComposerDarkMode - { - get { return (bool)GetValue(IsComposerDarkModeProperty); } - set { SetValue(IsComposerDarkModeProperty, value); } - } - - public static readonly DependencyProperty IsComposerDarkModeProperty = DependencyProperty.Register(nameof(IsComposerDarkMode), typeof(bool), typeof(SignatureManagementPage), new PropertyMetadata(false, OnIsComposerDarkModeChanged)); - public SignatureEditorDialog() { InitializeComponent(); SignatureNameTextBox.Header = Translator.SignatureEditorDialog_SignatureName_TitleNew; - Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF"); - Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation,FontAccess"); // TODO: Should be added additional logic to enable/disable primary button when webview content changed. IsPrimaryButtonEnabled = true; @@ -62,8 +34,6 @@ public SignatureEditorDialog(AccountSignature signatureModel) MailAccountId = signatureModel.MailAccountId, HtmlBody = signatureModel.HtmlBody }; - Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF"); - Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation"); // TODO: Should be added additional logic to enable/disable primary button when webview content changed. IsPrimaryButtonEnabled = true; @@ -71,39 +41,17 @@ public SignatureEditorDialog(AccountSignature signatureModel) private async void SignatureDialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args) { - Chromium.CoreWebView2Initialized -= ChromiumInitialized; - Chromium.CoreWebView2Initialized += ChromiumInitialized; - - await Chromium.EnsureCoreWebView2Async(); - - _getHTMLBodyFunction = new Func>(async () => - { - var editorContent = await Chromium.ExecuteScriptFunctionSafeAsync("GetHTMLContent"); - - return JsonSerializer.Deserialize(editorContent, BasicTypesJsonContext.Default.String); - }); - - var underlyingThemeService = App.Current.Services.GetService(); - - IsComposerDarkMode = underlyingThemeService.IsUnderlyingThemeDark(); - - await RenderInternalAsync(Result?.HtmlBody ?? string.Empty); + await WebViewEditor.RenderHtmlAsync(Result?.HtmlBody ?? string.Empty); } private void DialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) { - Chromium.CoreWebView2Initialized -= ChromiumInitialized; - - if (Chromium.CoreWebView2 != null) - { - Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded; - Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageReceived; - } + WebViewEditor.Dispose(); } private async void SaveClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args) { - var newSignature = Regex.Unescape(await _getHTMLBodyFunction()); + var newSignature = Regex.Unescape(await WebViewEditor.GetHtmlBodyAsync()); if (Result == null) { @@ -128,249 +76,5 @@ private void CancelClicked(ContentDialog sender, ContentDialogButtonClickEventAr Hide(); } - private async void BoldButtonClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('bold')"); - } - - private async void ItalicButtonClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('italic')"); - } - - private async void UnderlineButtonClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('underline')"); - } - - private async void StrokeButtonClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('strikethrough')"); - } - - private async void BulletListButtonClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('insertunorderedlist')"); - } - - private async void OrderedListButtonClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('insertorderedlist')"); - } - - private async void IncreaseIndentClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('indent')"); - } - - private async void DecreaseIndentClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('outdent')"); - } - - private async void AlignmentChanged(object sender, SelectionChangedEventArgs e) - { - var selectedItem = AlignmentListView.SelectedItem as ComboBoxItem; - var alignment = selectedItem.Tag.ToString(); - - switch (alignment) - { - case "left": - await InvokeScriptSafeAsync("editor.execCommand('justifyleft')"); - break; - case "center": - await InvokeScriptSafeAsync("editor.execCommand('justifycenter')"); - break; - case "right": - await InvokeScriptSafeAsync("editor.execCommand('justifyright')"); - break; - case "justify": - await InvokeScriptSafeAsync("editor.execCommand('justifyfull')"); - break; - } - } - - private async Task InvokeScriptSafeAsync(string function) - { - if (Chromium == null) return string.Empty; - - try - { - - return await Chromium.ExecuteScriptAsync(function); - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - - return string.Empty; - } - - private async void AddImageClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("imageInput.click();"); - } - - private async Task FocusEditorAsync() - { - await InvokeScriptSafeAsync("editor.selection.focus();"); - - Chromium.Focus(FocusState.Keyboard); - Chromium.Focus(FocusState.Programmatic); - } - - private async void EmojiButtonClicked(object sender, RoutedEventArgs e) - { - CoreInputView.GetForCurrentView().TryShow(CoreInputViewKind.Emoji); - - await FocusEditorAsync(); - } - - private async Task TryGetSelectedTextAsync() - { - try - { - return await Chromium.ExecuteScriptAsync("getSelectedText();"); - } - catch { } - - return string.Empty; - } - - private async void WebViewToggleButtonClicked(object sender, RoutedEventArgs e) - { - var enable = WebviewToolBarButton.IsChecked == true ? "true" : "false"; - await InvokeScriptSafeAsync($"toggleToolbar('{enable}');"); - } - - private async Task UpdateEditorThemeAsync() - { - await _domLoadedTask.Task; - - if (IsComposerDarkMode) - { - Chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Dark; - await InvokeScriptSafeAsync("SetDarkEditor();"); - } - else - { - Chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Light; - await InvokeScriptSafeAsync("SetLightEditor();"); - } - } - - private async Task RenderInternalAsync(string htmlBody) - { - await _domLoadedTask.Task; - - await UpdateEditorThemeAsync(); - await InitializeEditorAsync(); - - if (string.IsNullOrEmpty(htmlBody)) - { - await Chromium.ExecuteScriptFunctionAsync("RenderHTML", parameters: JsonSerializer.Serialize(" ", BasicTypesJsonContext.Default.String)); - } - else - { - await Chromium.ExecuteScriptFunctionAsync("RenderHTML", parameters: JsonSerializer.Serialize(htmlBody, BasicTypesJsonContext.Default.String)); - - await FocusEditorAsync(); - } - } - - private async Task InitializeEditorAsync() - { - var fonts = _fontService.GetFonts(); - var composerFont = _preferencesService.ComposerFont; - int composerFontSize = _preferencesService.ComposerFontSize; - var readerFont = _preferencesService.ReaderFont; - int readerFontSize = _preferencesService.ReaderFontSize; - return await Chromium.ExecuteScriptFunctionAsync("initializeJodit", false, - JsonSerializer.Serialize(fonts, BasicTypesJsonContext.Default.ListString), - JsonSerializer.Serialize(composerFont, BasicTypesJsonContext.Default.String), - JsonSerializer.Serialize(composerFontSize, BasicTypesJsonContext.Default.Int32), - JsonSerializer.Serialize(readerFont, BasicTypesJsonContext.Default.String), - JsonSerializer.Serialize(readerFontSize, BasicTypesJsonContext.Default.Int32)); - } - - private async void ChromiumInitialized(Microsoft.UI.Xaml.Controls.WebView2 sender, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs args) - { - var editorBundlePath = (await _nativeAppService.GetEditorBundlePathAsync()).Replace("editor.html", string.Empty); - - Chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.editor", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow); - Chromium.Source = new Uri("https://app.editor/editor.html"); - - Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded; - Chromium.CoreWebView2.DOMContentLoaded += DOMLoaded; - - Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageReceived; - Chromium.CoreWebView2.WebMessageReceived += ScriptMessageReceived; - } - - private static async void OnIsComposerDarkModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) - { - if (obj is SignatureEditorDialog dialog) - { - await dialog.UpdateEditorThemeAsync(); - } - } - - private void ScriptMessageReceived(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args) - { - var change = JsonSerializer.Deserialize(args.WebMessageAsJson, DomainModelsJsonContext.Default.WebViewMessage); - - if (change.Type == "bold") - { - BoldButton.IsChecked = change.Value == "true"; - } - else if (change.Type == "italic") - { - ItalicButton.IsChecked = change.Value == "true"; - } - else if (change.Type == "underline") - { - UnderlineButton.IsChecked = change.Value == "true"; - } - else if (change.Type == "strikethrough") - { - StrokeButton.IsChecked = change.Value == "true"; - } - else if (change.Type == "ol") - { - OrderedListButton.IsChecked = change.Value == "true"; - } - else if (change.Type == "ul") - { - BulletListButton.IsChecked = change.Value == "true"; - } - else if (change.Type == "indent") - { - IncreaseIndentButton.IsEnabled = change.Value == "disabled" ? false : true; - } - else if (change.Type == "outdent") - { - DecreaseIndentButton.IsEnabled = change.Value == "disabled" ? false : true; - } - else if (change.Type == "alignment") - { - var parsedValue = change.Value switch - { - "jodit-icon_left" => 0, - "jodit-icon_center" => 1, - "jodit-icon_right" => 2, - "jodit-icon_justify" => 3, - _ => 0 - }; - AlignmentListView.SelectionChanged -= AlignmentChanged; - AlignmentListView.SelectedIndex = parsedValue; - AlignmentListView.SelectionChanged += AlignmentChanged; - } - } - - private void DOMLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) => _domLoadedTask.TrySetResult(true); - private void SignatureNameTextBoxTextChanged(object sender, TextChangedEventArgs e) => IsPrimaryButtonEnabled = !string.IsNullOrWhiteSpace(SignatureNameTextBox.Text); - - private void InvertComposerThemeClicked(object sender, RoutedEventArgs e) => IsComposerDarkMode = !IsComposerDarkMode; } diff --git a/Wino.Mail/JS/editor.js b/Wino.Mail/JS/editor.js index a9fb22e0..f2e55a7a 100644 --- a/Wino.Mail/JS/editor.js +++ b/Wino.Mail/JS/editor.js @@ -115,7 +115,7 @@ function SetDarkEditor() { function toggleToolbar(enable) { const toolbar = document.querySelector('.jodit-toolbar__box'); - if (enable == 'true') { + if (enable) { toolbar.style.display = 'flex'; } else { diff --git a/Wino.Mail/Styles/WebViewEditorControl.xaml b/Wino.Mail/Styles/WebViewEditorControl.xaml new file mode 100644 index 00000000..f76cdc54 --- /dev/null +++ b/Wino.Mail/Styles/WebViewEditorControl.xaml @@ -0,0 +1,21 @@ + + + + diff --git a/Wino.Mail/Views/ComposePage.xaml b/Wino.Mail/Views/ComposePage.xaml index 084f54ab..43a419e8 100644 --- a/Wino.Mail/Views/ComposePage.xaml +++ b/Wino.Mail/Views/ComposePage.xaml @@ -4,6 +4,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:abstract="using:Wino.Views.Abstract" xmlns:controls="using:Wino.Controls" + xmlns:controls1="using:Wino.Mail.Controls" xmlns:coreControls="using:Wino.Core.UWP.Controls" xmlns:customcontrols="using:Wino.Core.UWP.Controls.CustomControls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" @@ -189,20 +190,20 @@ IsDynamicOverflowEnabled="True" OverflowButtonAlignment="Left"> + Visibility="{x:Bind WebViewEditor.IsEditorDarkMode, Mode=OneWay}"> + Visibility="{x:Bind WebViewEditor.IsEditorDarkMode, Mode=OneWay, Converter={StaticResource ReverseBooleanToVisibilityConverter}}"> @@ -222,34 +223,22 @@ - + - + - + - + @@ -257,19 +246,13 @@ - + - + @@ -278,8 +261,8 @@ @@ -289,8 +272,8 @@ @@ -305,7 +288,7 @@ VerticalAlignment="Center" Background="Transparent" BorderBrush="Transparent" - SelectionChanged="AlignmentChanged"> + SelectedIndex="{x:Bind WebViewEditor.EditorAlignmentSelectedIndex, Mode=TwoWay}"> @@ -347,8 +330,7 @@ @@ -368,19 +350,13 @@ - + - + @@ -630,9 +606,7 @@ BorderThickness="1" CornerRadius="7"> - - - + , IRecipient { - public bool IsComposerDarkMode - { - get { return (bool)GetValue(IsComposerDarkModeProperty); } - set { SetValue(IsComposerDarkModeProperty, value); } - } - - public static readonly DependencyProperty IsComposerDarkModeProperty = DependencyProperty.Register(nameof(IsComposerDarkMode), typeof(bool), typeof(ComposePage), new PropertyMetadata(false, OnIsComposerDarkModeChanged)); - public WebView2 GetWebView() => Chromium; + public WebView2 GetWebView() => WebViewEditor.GetUnderlyingWebView(); - private readonly TaskCompletionSource _domLoadedTask = new TaskCompletionSource(); - - private readonly List _disposables = new List(); + private readonly List _disposables = []; private readonly SystemNavigationManagerPreview _navManagerPreview = SystemNavigationManagerPreview.GetForCurrentView(); public ComposePage() { InitializeComponent(); _navManagerPreview.CloseRequested += OnClose; - - Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF"); - Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation"); } private async void GlobalFocusManagerGotFocus(object sender, FocusManagerGotFocusEventArgs e) @@ -71,17 +50,9 @@ private async void GlobalFocusManagerGotFocus(object sender, FocusManagerGotFocu // This is not done on the WebView2 handlers, because somehow it is // repeatedly focusing itself, even though when it has the focus already. - if (e.NewFocusedElement == Chromium) - { - await FocusEditorAsync(false); - } - } - - private static async void OnIsComposerDarkModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) - { - if (obj is ComposePage page) + if (e.NewFocusedElement == WebViewEditor) { - await page.UpdateEditorThemeAsync(); + await WebViewEditor.FocusEditorAsync(false); } } @@ -216,7 +187,7 @@ private async void OnImageDropGridImageDropped(object sender, DragEventArgs e) } } - await InvokeScriptSafeAsync($"insertImages({JsonSerializer.Serialize(imagesInformation, ComposerPageJsonContext.Default.ListImageInfo)});"); + await WebViewEditor.InsertImagesAsync(imagesInformation); } } // State should be reset even when an exception occurs, otherwise the UI will be stuck in a dragging state. @@ -245,299 +216,41 @@ private async Task AttachFiles(IEnumerable files) } } - private bool IsValidImageFile(StorageFile file) + private static bool IsValidImageFile(StorageFile file) { - string[] allowedTypes = new string[] { ".jpg", ".jpeg", ".png" }; + string[] allowedTypes = [".jpg", ".jpeg", ".png"]; var fileType = file.FileType.ToLower(); return allowedTypes.Contains(fileType); } - private async void BoldButtonClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('bold')"); - } - - private async void ItalicButtonClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('italic')"); - } - - private async void UnderlineButtonClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('underline')"); - } - - private async void StrokeButtonClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('strikethrough')"); - } - - private async void BulletListButtonClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('insertunorderedlist')"); - } - - private async void OrderedListButtonClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('insertorderedlist')"); - } - - private async void IncreaseIndentClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('indent')"); - } - - private async void DecreaseIndentClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("editor.execCommand('outdent')"); - } - - private async void AlignmentChanged(object sender, SelectionChangedEventArgs e) - { - var selectedItem = AlignmentListView.SelectedItem as ComboBoxItem; - var alignment = selectedItem.Tag.ToString(); - - switch (alignment) - { - case "left": - await InvokeScriptSafeAsync("editor.execCommand('justifyleft')"); - break; - case "center": - await InvokeScriptSafeAsync("editor.execCommand('justifycenter')"); - break; - case "right": - await InvokeScriptSafeAsync("editor.execCommand('justifyright')"); - break; - case "justify": - await InvokeScriptSafeAsync("editor.execCommand('justifyfull')"); - break; - } - } - - private async void WebViewToggleButtonClicked(object sender, RoutedEventArgs e) - { - var enable = WebviewToolBarButton.IsChecked == true ? "true" : "false"; - await InvokeScriptSafeAsync($"toggleToolbar('{enable}');"); - } - - private async Task InvokeScriptSafeAsync(string function) - { - if (Chromium == null) return string.Empty; - - try - { - return await Chromium.ExecuteScriptAsync(function); - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - - return string.Empty; - } - - private async void AddImageClicked(object sender, RoutedEventArgs e) - { - await InvokeScriptSafeAsync("imageInput.click();"); - } - - /// - /// Places the cursor in the composer. - /// - /// Whether control itself should be focused as well or not. - private async Task FocusEditorAsync(bool focusControlAsWell) - { - await InvokeScriptSafeAsync("editor.selection.setCursorIn(editor.editor.firstChild, true)"); - - if (focusControlAsWell) - { - Chromium.Focus(FocusState.Keyboard); - Chromium.Focus(FocusState.Programmatic); - } - } - - private async void EmojiButtonClicked(object sender, RoutedEventArgs e) - { - CoreInputView.GetForCurrentView().TryShow(CoreInputViewKind.Emoji); - - await FocusEditorAsync(focusControlAsWell: true); - } - - public async Task UpdateEditorThemeAsync() - { - await _domLoadedTask.Task; - - if (IsComposerDarkMode) - { - Chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Dark; - await InvokeScriptSafeAsync("SetDarkEditor();"); - } - else - { - Chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Light; - await InvokeScriptSafeAsync("SetLightEditor();"); - } - } - - private async Task RenderInternalAsync(string htmlBody) - { - await _domLoadedTask.Task; - - await UpdateEditorThemeAsync(); - await InitializeEditorAsync(); - - if (string.IsNullOrEmpty(htmlBody)) - { - await Chromium.ExecuteScriptFunctionAsync("RenderHTML", parameters: JsonSerializer.Serialize(" ", BasicTypesJsonContext.Default.String)); - } - else - { - await Chromium.ExecuteScriptFunctionAsync("RenderHTML", parameters: JsonSerializer.Serialize(htmlBody, BasicTypesJsonContext.Default.String)); - } - } - - private async Task InitializeEditorAsync() - { - var fonts = ViewModel.FontService.GetFonts(); - var composerFont = ViewModel.PreferencesService.ComposerFont; - int composerFontSize = ViewModel.PreferencesService.ComposerFontSize; - var readerFont = ViewModel.PreferencesService.ReaderFont; - int readerFontSize = ViewModel.PreferencesService.ReaderFontSize; - return await Chromium.ExecuteScriptFunctionAsync("initializeJodit", - false, - JsonSerializer.Serialize(fonts, BasicTypesJsonContext.Default.ListString), - JsonSerializer.Serialize(composerFont, BasicTypesJsonContext.Default.String), - JsonSerializer.Serialize(composerFontSize, BasicTypesJsonContext.Default.Int32), - JsonSerializer.Serialize(readerFont, BasicTypesJsonContext.Default.String), - JsonSerializer.Serialize(readerFontSize, BasicTypesJsonContext.Default.Int32)); - } - - private void DisposeWebView2() - { - if (Chromium == null) return; - - Chromium.CoreWebView2Initialized -= ChromiumInitialized; - - if (Chromium.CoreWebView2 != null) - { - Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded; - Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageReceived; - } - - Chromium.Close(); - GC.Collect(); - } - private void DisposeDisposables() { - if (_disposables.Any()) + if (_disposables.Count != 0) _disposables.ForEach(a => a.Dispose()); } - protected override async void OnNavigatedTo(NavigationEventArgs e) + protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); FocusManager.GotFocus += GlobalFocusManagerGotFocus; - var anim = ConnectedAnimationService.GetForCurrentView().GetAnimation("WebViewConnectedAnimation"); - anim?.TryStart(Chromium); - - DisposeDisposables(); + // TODO: disabled animation for now, since it's still not working properly. + //var anim = ConnectedAnimationService.GetForCurrentView().GetAnimation("WebViewConnectedAnimation"); + //anim?.TryStart(GetWebView()); _disposables.Add(GetSuggestionBoxDisposable(ToBox)); _disposables.Add(GetSuggestionBoxDisposable(CCBox)); _disposables.Add(GetSuggestionBoxDisposable(BccBox)); + _disposables.Add(WebViewEditor); - Chromium.CoreWebView2Initialized -= ChromiumInitialized; - Chromium.CoreWebView2Initialized += ChromiumInitialized; - - await Chromium.EnsureCoreWebView2Async(); - - ViewModel.GetHTMLBodyFunction = new Func>(async () => - { - var editorContent = await InvokeScriptSafeAsync("GetHTMLContent();"); - - return JsonSerializer.Deserialize(editorContent, BasicTypesJsonContext.Default.String); - }); - - var underlyingThemeService = App.Current.Services.GetService(); - - IsComposerDarkMode = underlyingThemeService.IsUnderlyingThemeDark(); - } - - private async void ChromiumInitialized(Microsoft.UI.Xaml.Controls.WebView2 sender, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs args) - { - var editorBundlePath = (await ViewModel.NativeAppService.GetEditorBundlePathAsync()).Replace("editor.html", string.Empty); - - Chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.editor", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow); - Chromium.Source = new Uri("https://app.editor/editor.html"); - - Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded; - Chromium.CoreWebView2.DOMContentLoaded += DOMLoaded; - - Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageReceived; - Chromium.CoreWebView2.WebMessageReceived += ScriptMessageReceived; - } - - private void ScriptMessageReceived(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args) - { - var change = JsonSerializer.Deserialize(args.WebMessageAsJson, DomainModelsJsonContext.Default.WebViewMessage); - - if (change.Type == "bold") - { - BoldButton.IsChecked = change.Value == "true"; - } - else if (change.Type == "italic") - { - ItalicButton.IsChecked = change.Value == "true"; - } - else if (change.Type == "underline") - { - UnderlineButton.IsChecked = change.Value == "true"; - } - else if (change.Type == "strikethrough") - { - StrokeButton.IsChecked = change.Value == "true"; - } - else if (change.Type == "ol") - { - OrderedListButton.IsChecked = change.Value == "true"; - } - else if (change.Type == "ul") - { - BulletListButton.IsChecked = change.Value == "true"; - } - else if (change.Type == "indent") - { - IncreaseIndentButton.IsEnabled = change.Value == "disabled" ? false : true; - } - else if (change.Type == "outdent") - { - DecreaseIndentButton.IsEnabled = change.Value == "disabled" ? false : true; - } - else if (change.Type == "alignment") - { - var parsedValue = change.Value switch - { - "jodit-icon_left" => 0, - "jodit-icon_center" => 1, - "jodit-icon_right" => 2, - "jodit-icon_justify" => 3, - _ => 0 - }; - AlignmentListView.SelectionChanged -= AlignmentChanged; - AlignmentListView.SelectedIndex = parsedValue; - AlignmentListView.SelectionChanged += AlignmentChanged; - } + ViewModel.GetHTMLBodyFunction = WebViewEditor.GetHtmlBodyAsync; } - private void DOMLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) => _domLoadedTask.TrySetResult(true); - async void IRecipient.Receive(CreateNewComposeMailRequested message) { - await RenderInternalAsync(message.RenderModel.RenderHtml); + await WebViewEditor.RenderHtmlAsync(message.RenderModel.RenderHtml); } private void ShowCCBCCClicked(object sender, RoutedEventArgs e) @@ -548,7 +261,6 @@ private void ShowCCBCCClicked(object sender, RoutedEventArgs e) private async void TokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args) { // Check is valid email. - if (!EmailValidator.Validate(args.TokenText)) { args.Cancel = true; @@ -557,18 +269,15 @@ private async void TokenItemAdding(TokenizingTextBox sender, TokenItemAddingEven return; } - var deferal = args.GetDeferral(); - - AccountContact addedItem = null; + var deferral = args.GetDeferral(); - var boxTag = sender.Tag?.ToString(); - - if (boxTag == "ToBox") - addedItem = await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.ToItems); - else if (boxTag == "CCBox") - addedItem = await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.CCItems); - else if (boxTag == "BCCBox") - addedItem = await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.BCCItems); + var addedItem = (sender.Tag?.ToString()) switch + { + "ToBox" => await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.ToItems), + "CCBox" => await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.CCItems), + "BCCBox" => await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.BCCItems), + _ => null + }; if (addedItem == null) { @@ -580,17 +289,12 @@ private async void TokenItemAdding(TokenizingTextBox sender, TokenItemAddingEven args.Item = addedItem; } - deferal.Complete(); + deferral.Complete(); } void IRecipient.Receive(ApplicationThemeChanged message) { - IsComposerDarkMode = message.IsUnderlyingThemeDark; - } - - private void InvertComposerThemeClicked(object sender, RoutedEventArgs e) - { - IsComposerDarkMode = !IsComposerDarkMode; + WebViewEditor.IsEditorDarkMode = message.IsUnderlyingThemeDark; } private void ImportanceClicked(object sender, RoutedEventArgs e) @@ -600,9 +304,7 @@ private void ImportanceClicked(object sender, RoutedEventArgs e) if (sender is Button senderButton) { - var selectedImportance = (MessageImportance)senderButton.Tag; - - ViewModel.SelectedMessageImportance = selectedImportance; + ViewModel.SelectedMessageImportance = (MessageImportance)senderButton.Tag; ((ImportanceSplitButton.Content as Viewbox).Child as SymbolIcon).Symbol = (senderButton.Content as SymbolIcon).Symbol; } } @@ -613,23 +315,21 @@ private async void AddressBoxLostFocus(object sender, RoutedEventArgs e) if (sender is TokenizingTextBox tokenizingTextBox) { - if (!(tokenizingTextBox.Items.LastOrDefault() is ITokenStringContainer info)) return; + if (tokenizingTextBox.Items.LastOrDefault() is not ITokenStringContainer info) return; var currentText = info.Text; if (!string.IsNullOrEmpty(currentText) && EmailValidator.Validate(currentText)) { - var boxTag = tokenizingTextBox.Tag?.ToString(); + var addressCollection = tokenizingTextBox.Tag?.ToString() switch + { + "ToBox" => ViewModel.ToItems, + "CCBox" => ViewModel.CCItems, + "BCCBox" => ViewModel.BCCItems, + _ => null + }; AccountContact addedItem = null; - ObservableCollection addressCollection = null; - - if (boxTag == "ToBox") - addressCollection = ViewModel.ToItems; - else if (boxTag == "CCBox") - addressCollection = ViewModel.CCItems; - else if (boxTag == "BCCBox") - addressCollection = ViewModel.BCCItems; if (addressCollection != null) addedItem = await ViewModel.GetAddressInformationAsync(currentText, addressCollection); @@ -676,7 +376,6 @@ protected override async void OnNavigatingFrom(NavigatingCancelEventArgs e) await ViewModel.UpdateMimeChangesAsync(); DisposeDisposables(); - DisposeWebView2(); } private async void OnClose(object sender, SystemNavigationCloseRequestedPreviewEventArgs e) { @@ -689,6 +388,3 @@ private async void OnClose(object sender, SystemNavigationCloseRequestedPreviewE finally { deferral.Complete(); } } } - -[JsonSerializable(typeof(List))] -public partial class ComposerPageJsonContext : JsonSerializerContext; diff --git a/Wino.Mail/Wino.Mail.csproj b/Wino.Mail/Wino.Mail.csproj index 957f80c8..0b963189 100644 --- a/Wino.Mail/Wino.Mail.csproj +++ b/Wino.Mail/Wino.Mail.csproj @@ -79,6 +79,7 @@ +