From 3a3926612189773e448a50975bad5db60f974e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Wed, 25 Feb 2026 01:41:48 +0100 Subject: [PATCH] Simplified compoper and rendering logic through messages. --- Wino.Mail.ViewModels/ComposePageViewModel.cs | 10 ++- .../Data/AccountContactViewModel.cs | 26 ++++++- Wino.Mail.ViewModels/MailAppShellViewModel.cs | 14 +++- .../MailRenderingPageViewModel.cs | 16 +++- .../NewComposeDraftItemRequestedEvent.cs | 10 --- .../NewMailItemRenderingRequestedEvent.cs | 10 --- .../ReaderItemRefreshRequestedEvent.cs | 10 +++ .../Controls/ImagePreviewControl.cs | 75 +++++++++---------- Wino.Mail.WinUI/Services/NavigationService.cs | 4 +- Wino.Mail.WinUI/Views/Mail/ComposePage.xaml | 14 +--- .../Views/Mail/ComposePage.xaml.cs | 10 ++- .../Views/Mail/MailListPage.xaml.cs | 45 ++++++++--- .../Views/Mail/MailRenderingPage.xaml | 9 +-- .../Views/Mail/MailRenderingPage.xaml.cs | 12 +-- 14 files changed, 157 insertions(+), 108 deletions(-) delete mode 100644 Wino.Mail.ViewModels/Messages/NewComposeDraftItemRequestedEvent.cs delete mode 100644 Wino.Mail.ViewModels/Messages/NewMailItemRenderingRequestedEvent.cs create mode 100644 Wino.Mail.ViewModels/Messages/ReaderItemRefreshRequestedEvent.cs diff --git a/Wino.Mail.ViewModels/ComposePageViewModel.cs b/Wino.Mail.ViewModels/ComposePageViewModel.cs index 04f66c93..60582279 100644 --- a/Wino.Mail.ViewModels/ComposePageViewModel.cs +++ b/Wino.Mail.ViewModels/ComposePageViewModel.cs @@ -29,7 +29,7 @@ using Wino.Messaging.UI; namespace Wino.Mail.ViewModels; public partial class ComposePageViewModel : MailBaseViewModel, - IRecipient, + IRecipient, IRecipient, IRecipient, IRecipient @@ -511,8 +511,10 @@ public partial class ComposePageViewModel : MailBaseViewModel, } } - public async void Receive(NewComposeDraftItemRequestedEvent message) + public async void Receive(ReaderItemRefreshRequestedEvent message) { + if (message.MailItemViewModel == null || !message.MailItemViewModel.IsDraft) return; + // Save current draft before switching. await UpdateMimeChangesAsync(); @@ -555,7 +557,7 @@ public partial class ComposePageViewModel : MailBaseViewModel, { base.RegisterRecipients(); - Messenger.Register(this); + Messenger.Register(this); Messenger.Register(this); Messenger.Register(this); Messenger.Register(this); @@ -565,7 +567,7 @@ public partial class ComposePageViewModel : MailBaseViewModel, { base.UnregisterRecipients(); - Messenger.Unregister(this); + Messenger.Unregister(this); Messenger.Unregister(this); Messenger.Unregister(this); Messenger.Unregister(this); diff --git a/Wino.Mail.ViewModels/Data/AccountContactViewModel.cs b/Wino.Mail.ViewModels/Data/AccountContactViewModel.cs index cc9ff979..41731b0c 100644 --- a/Wino.Mail.ViewModels/Data/AccountContactViewModel.cs +++ b/Wino.Mail.ViewModels/Data/AccountContactViewModel.cs @@ -1,11 +1,12 @@ -using System; +using System; using CommunityToolkit.Mvvm.ComponentModel; using Wino.Core.Domain; using Wino.Core.Domain.Entities.Shared; +using Wino.Core.Domain.Interfaces; namespace Wino.Mail.ViewModels.Data; -public partial class AccountContactViewModel : ObservableObject +public partial class AccountContactViewModel : ObservableObject, IMailItemDisplayInformation { public string Address { get; set; } public string Name { get; set; } @@ -49,4 +50,25 @@ public partial class AccountContactViewModel : ObservableObject [ObservableProperty] public partial bool ThumbnailUpdatedEvent { get; set; } + + // IMailItemDisplayInformation implementation for avatar-only rendering. + public string Subject => string.Empty; + public string FromName => Name ?? string.Empty; + public string FromAddress => Address ?? string.Empty; + public string PreviewText => string.Empty; + public bool IsRead => true; + public bool IsDraft => false; + public bool HasAttachments => false; + public bool IsCalendarEvent => false; + public bool IsFlagged => false; + public DateTime CreationDate => default; + public bool IsBusy => false; + public bool IsThreadExpanded => false; + public AccountContact SenderContact => new() + { + Address = Address, + Name = Name, + Base64ContactPicture = Base64ContactPicture, + IsRootContact = IsRootContact + }; } diff --git a/Wino.Mail.ViewModels/MailAppShellViewModel.cs b/Wino.Mail.ViewModels/MailAppShellViewModel.cs index 6482a979..55af6fa0 100644 --- a/Wino.Mail.ViewModels/MailAppShellViewModel.cs +++ b/Wino.Mail.ViewModels/MailAppShellViewModel.cs @@ -226,9 +226,17 @@ public partial class MailAppShellViewModel : MailBaseViewModel, if (mode == NavigationMode.Back) { - // Account list may have changed while this shell was inactive. - await RecreateMenuItemsAsync(); - await RestoreSelectedAccountAfterMenuRefreshAsync(false); + // Preserve current mail/folder selection and active rendering page when + // switching back from Calendar mode. Recreating menu/folder state here + // causes a folder reload, which clears selection and disposes reader page. + // Rehydrate only if menu state is unexpectedly empty. + if (MenuItems?.Any() != true || FooterItems?.Any() != true) + { + await CreateFooterItemsAsync(); + await RecreateMenuItemsAsync(); + await RestoreSelectedAccountAfterMenuRefreshAsync(false); + } + return; } diff --git a/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs b/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs index 95177a3a..99e3644a 100644 --- a/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs +++ b/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs @@ -34,7 +34,7 @@ using IMailService = Wino.Core.Domain.Interfaces.IMailService; namespace Wino.Mail.ViewModels; public partial class MailRenderingPageViewModel : MailBaseViewModel, - IRecipient, + IRecipient, IRecipient, ITransferProgress // For listening IMAP message download progress. { @@ -127,6 +127,9 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel, [ObservableProperty] public partial string ContactPicture { get; set; } + [ObservableProperty] + public partial IMailItemDisplayInformation CurrentMailItemDisplayInformation { get; set; } + [ObservableProperty] public partial DateTime CreationDate { get; set; } public ObservableCollection ToItems { get; set; } = []; @@ -361,6 +364,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel, initializedMailItemViewModel = null; initializedMimeMessageInformation = null; + CurrentMailItemDisplayInformation = null; // Dispose existing content first. Messenger.Send(new CancelRenderingContentRequested()); @@ -447,6 +451,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel, } initializedMailItemViewModel = mailItemViewModel; + await ExecuteUIThread(() => { CurrentMailItemDisplayInformation = mailItemViewModel; }); await RenderAsync(mimeMessageInformation); } @@ -570,6 +575,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel, initializedMailItemViewModel = null; initializedMimeMessageInformation = null; + CurrentMailItemDisplayInformation = null; forceImageLoading = false; @@ -798,8 +804,10 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel, // For upload. void ITransferProgress.Report(long bytesTransferred) { } - public async void Receive(NewMailItemRenderingRequestedEvent message) + public async void Receive(ReaderItemRefreshRequestedEvent message) { + if (message.MailItemViewModel == null || message.MailItemViewModel.IsDraft) return; + try { await RenderAsync(message.MailItemViewModel, renderCancellationTokenSource.Token); @@ -909,7 +917,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel, { base.RegisterRecipients(); - Messenger.Register(this); + Messenger.Register(this); Messenger.Register(this); } @@ -917,7 +925,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel, { base.UnregisterRecipients(); - Messenger.Unregister(this); + Messenger.Unregister(this); Messenger.Unregister(this); } } diff --git a/Wino.Mail.ViewModels/Messages/NewComposeDraftItemRequestedEvent.cs b/Wino.Mail.ViewModels/Messages/NewComposeDraftItemRequestedEvent.cs deleted file mode 100644 index 1b94c725..00000000 --- a/Wino.Mail.ViewModels/Messages/NewComposeDraftItemRequestedEvent.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Wino.Mail.ViewModels.Data; - -namespace Wino.Mail.ViewModels.Messages; - -/// -/// When the compose page is already active, but a different draft item is selected. -/// To not trigger navigation again and re-use existing WebView2 editor. -/// -/// The new draft mail item to compose. -public record NewComposeDraftItemRequestedEvent(MailItemViewModel MailItemViewModel); diff --git a/Wino.Mail.ViewModels/Messages/NewMailItemRenderingRequestedEvent.cs b/Wino.Mail.ViewModels/Messages/NewMailItemRenderingRequestedEvent.cs deleted file mode 100644 index 095b1670..00000000 --- a/Wino.Mail.ViewModels/Messages/NewMailItemRenderingRequestedEvent.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Wino.Mail.ViewModels.Data; - -namespace Wino.Mail.ViewModels.Messages; - -/// -/// When the rendering page is active, but new item is requested to be rendered. -/// To not trigger navigation again and re-use existing Chromium. -/// -/// -public record NewMailItemRenderingRequestedEvent(MailItemViewModel MailItemViewModel); diff --git a/Wino.Mail.ViewModels/Messages/ReaderItemRefreshRequestedEvent.cs b/Wino.Mail.ViewModels/Messages/ReaderItemRefreshRequestedEvent.cs new file mode 100644 index 00000000..eab15937 --- /dev/null +++ b/Wino.Mail.ViewModels/Messages/ReaderItemRefreshRequestedEvent.cs @@ -0,0 +1,10 @@ +using Wino.Mail.ViewModels.Data; + +namespace Wino.Mail.ViewModels.Messages; + +/// +/// Requests refreshing the currently active reader page (mail rendering or compose) +/// with a different selected mail item without re-navigation. +/// +/// The selected mail item to refresh with. +public record ReaderItemRefreshRequestedEvent(MailItemViewModel MailItemViewModel); diff --git a/Wino.Mail.WinUI/Controls/ImagePreviewControl.cs b/Wino.Mail.WinUI/Controls/ImagePreviewControl.cs index c0f7ecb8..0e1abe04 100644 --- a/Wino.Mail.WinUI/Controls/ImagePreviewControl.cs +++ b/Wino.Mail.WinUI/Controls/ImagePreviewControl.cs @@ -1,6 +1,6 @@ using System; using System.IO; -using System.Net.Mail; +using System.ComponentModel; using System.Threading; using System.Threading.Tasks; using CommunityToolkit.WinUI; @@ -30,20 +30,9 @@ public sealed partial class ImagePreviewControl : PersonPicture [GeneratedDependencyProperty] public partial IMailItemDisplayInformation? MailItemInformation { get; set; } - [GeneratedDependencyProperty] - public partial string? FromName { get; set; } - - [GeneratedDependencyProperty] - public partial string? FromAddress { get; set; } - - [GeneratedDependencyProperty] - public partial string? SenderContactPicture { get; set; } - - [GeneratedDependencyProperty(DefaultValue = false)] - public partial bool ThumbnailUpdatedEvent { get; set; } - private readonly IThumbnailService? _thumbnailService; private readonly IPreferencesService? _preferencesService; + private INotifyPropertyChanged? _mailItemInformationPropertySource; private CancellationTokenSource? _refreshCancellationTokenSource; private CancellationTokenSource? _scheduledRefreshCancellationTokenSource; private long _refreshVersion; @@ -68,17 +57,21 @@ public sealed partial class ImagePreviewControl : PersonPicture partial void OnMailItemInformationPropertyChanged(DependencyPropertyChangedEventArgs e) { + if (_mailItemInformationPropertySource != null) + { + _mailItemInformationPropertySource.PropertyChanged -= MailItemInformationPropertyChanged; + _mailItemInformationPropertySource = null; + } + + if (e.NewValue is INotifyPropertyChanged observableMailItemInformation) + { + _mailItemInformationPropertySource = observableMailItemInformation; + _mailItemInformationPropertySource.PropertyChanged += MailItemInformationPropertyChanged; + } + RequestRefresh(); } - partial void OnFromNameChanged(string? newValue) => RequestRefresh(); - - partial void OnFromAddressChanged(string? newValue) => RequestRefresh(); - - partial void OnSenderContactPictureChanged(string? newValue) => RequestRefresh(); - - partial void OnThumbnailUpdatedEventChanged(bool newValue) => RequestRefresh(); - private void OnLoaded(object sender, RoutedEventArgs e) { RequestRefresh(); @@ -86,10 +79,28 @@ public sealed partial class ImagePreviewControl : PersonPicture private void OnUnloaded(object sender, RoutedEventArgs e) { + if (_mailItemInformationPropertySource != null) + { + _mailItemInformationPropertySource.PropertyChanged -= MailItemInformationPropertyChanged; + _mailItemInformationPropertySource = null; + } + CancelScheduledRefresh(); CancelActiveRefresh(); } + private void MailItemInformationPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + // Refresh only for fields that affect avatar image or initials. + if (string.IsNullOrEmpty(e.PropertyName) + || e.PropertyName == nameof(IMailItemDisplayInformation.Base64ContactPicture) + || e.PropertyName == nameof(IMailItemDisplayInformation.SenderContact) + || e.PropertyName == nameof(IMailItemDisplayInformation.ThumbnailUpdatedEvent)) + { + RequestRefresh(); + } + } + private void RequestRefresh() { if (DispatcherQueue == null || DispatcherQueue.HasThreadAccess) @@ -236,6 +247,9 @@ public sealed partial class ImagePreviewControl : PersonPicture private string ResolveAddress() { + if (MailItemInformation == null) + return string.Empty; + var contactAddress = MailItemInformation?.SenderContact?.Address; if (!string.IsNullOrWhiteSpace(contactAddress)) return contactAddress.Trim(); @@ -243,7 +257,7 @@ public sealed partial class ImagePreviewControl : PersonPicture if (!string.IsNullOrWhiteSpace(MailItemInformation?.FromAddress)) return MailItemInformation.FromAddress.Trim(); - return FromAddress?.Trim() ?? string.Empty; + return string.Empty; } private string ResolveDisplayName(string resolvedAddress) @@ -255,9 +269,6 @@ public sealed partial class ImagePreviewControl : PersonPicture if (!string.IsNullOrWhiteSpace(MailItemInformation?.FromName)) return MailItemInformation.FromName.Trim(); - if (!string.IsNullOrWhiteSpace(FromName)) - return FromName.Trim(); - return resolvedAddress.Trim(); } @@ -269,7 +280,7 @@ public sealed partial class ImagePreviewControl : PersonPicture if (!string.IsNullOrWhiteSpace(MailItemInformation?.Base64ContactPicture)) return MailItemInformation.Base64ContactPicture; - return SenderContactPicture ?? string.Empty; + return string.Empty; } private async Task ApplyInitialVisualStateAsync(string displayName, long refreshVersion, CancellationToken cancellationToken) @@ -387,16 +398,4 @@ public sealed partial class ImagePreviewControl : PersonPicture }).ConfigureAwait(false); } - private static bool IsValidEmail(string email) - { - try - { - _ = new MailAddress(email); - return true; - } - catch - { - return false; - } - } } diff --git a/Wino.Mail.WinUI/Services/NavigationService.cs b/Wino.Mail.WinUI/Services/NavigationService.cs index 95b4c845..a55214f7 100644 --- a/Wino.Mail.WinUI/Services/NavigationService.cs +++ b/Wino.Mail.WinUI/Services/NavigationService.cs @@ -212,7 +212,7 @@ public class NavigationService : NavigationServiceBase, INavigationService && parameter is MailItemViewModel mailItemViewModel && page != WinoPage.ComposePage) { - WeakReferenceMessenger.Default.Send(new NewMailItemRenderingRequestedEvent(mailItemViewModel)); + WeakReferenceMessenger.Default.Send(new ReaderItemRefreshRequestedEvent(mailItemViewModel)); } else if (listingFrame.Content != null && listingFrame.Content.GetType() == GetPageType(WinoPage.ComposePage) @@ -221,7 +221,7 @@ public class NavigationService : NavigationServiceBase, INavigationService { // ComposePage is already active and we're switching to another draft. // Reuse existing ComposePage and WebView2 instead of navigating. - WeakReferenceMessenger.Default.Send(new NewComposeDraftItemRequestedEvent(composeDraftViewModel)); + WeakReferenceMessenger.Default.Send(new ReaderItemRefreshRequestedEvent(composeDraftViewModel)); } else if (listingFrame.Content != null && listingFrame.Content.GetType() == GetPageType(WinoPage.IdlePage) diff --git a/Wino.Mail.WinUI/Views/Mail/ComposePage.xaml b/Wino.Mail.WinUI/Views/Mail/ComposePage.xaml index 7b87f701..611c6794 100644 --- a/Wino.Mail.WinUI/Views/Mail/ComposePage.xaml +++ b/Wino.Mail.WinUI/Views/Mail/ComposePage.xaml @@ -43,10 +43,7 @@ --> - + - + @@ -485,8 +479,8 @@ - - + + diff --git a/Wino.Mail.WinUI/Views/Mail/ComposePage.xaml.cs b/Wino.Mail.WinUI/Views/Mail/ComposePage.xaml.cs index 2e78f3be..155ce559 100644 --- a/Wino.Mail.WinUI/Views/Mail/ComposePage.xaml.cs +++ b/Wino.Mail.WinUI/Views/Mail/ComposePage.xaml.cs @@ -32,7 +32,7 @@ namespace Wino.Views.Mail; public sealed partial class ComposePage : ComposePageAbstract, IRecipient, IRecipient, - IRecipient + IRecipient { public WebView2 GetWebView() => WebViewEditor.GetUnderlyingWebView(); @@ -302,8 +302,10 @@ public sealed partial class ComposePage : ComposePageAbstract, WebViewEditor.IsEditorDarkMode = message.IsUnderlyingThemeDark; } - void IRecipient.Receive(NewComposeDraftItemRequestedEvent message) + void IRecipient.Receive(ReaderItemRefreshRequestedEvent message) { + if (message.MailItemViewModel == null || !message.MailItemViewModel.IsDraft) return; + // Reset the initial focus flag so ToBox gets focus for the new draft. isInitialFocusHandled = false; } @@ -423,7 +425,7 @@ public sealed partial class ComposePage : ComposePageAbstract, WeakReferenceMessenger.Default.Register(this); WeakReferenceMessenger.Default.Register(this); - WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); } protected override void UnregisterRecipients() @@ -432,7 +434,7 @@ public sealed partial class ComposePage : ComposePageAbstract, WeakReferenceMessenger.Default.Unregister(this); WeakReferenceMessenger.Default.Unregister(this); - WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); } // TODO: Save mime on closing the app. diff --git a/Wino.Mail.WinUI/Views/Mail/MailListPage.xaml.cs b/Wino.Mail.WinUI/Views/Mail/MailListPage.xaml.cs index c01df587..8f403bba 100644 --- a/Wino.Mail.WinUI/Views/Mail/MailListPage.xaml.cs +++ b/Wino.Mail.WinUI/Views/Mail/MailListPage.xaml.cs @@ -47,6 +47,8 @@ public sealed partial class MailListPage : MailListPageAbstract, IRecipient { private const double RENDERING_COLUMN_MIN_WIDTH = 375; + private const int IDLE_NAVIGATION_DELAY_MS = 120; + private int _idleNavigationRequestVersion = 0; private IStatePersistanceService StatePersistenceService { get; } = WinoApplication.Current.Services.GetService() ?? throw new Exception($"Can't resolve {nameof(KeyPressService)}"); private IKeyPressService KeyPressService { get; } = WinoApplication.Current.Services.GetService() ?? throw new Exception($"Can't resolve {nameof(KeyPressService)}"); @@ -80,6 +82,8 @@ public sealed partial class MailListPage : MailListPageAbstract, { base.OnNavigatedFrom(e); + InvalidatePendingIdleNavigation(); + this.Bindings.StopTracking(); ViewModel.MailCollection.ItemSelectionChanged -= WinoMailCollectionSelectionChanged; @@ -280,17 +284,12 @@ public sealed partial class MailListPage : MailListPageAbstract, // No active mail item. Go to empty page. if (message.SelectedMailItemViewModel == null) { - if (IsRenderingPageActive()) - { - WeakReferenceMessenger.Default.Send(new CancelRenderingContentRequested()); - } - - // Ensure rendering frame actually navigates away from Compose/Rendering pages. - // Otherwise those pages keep their messenger registrations alive. - ViewModel.NavigationService.Navigate(WinoPage.IdlePage, null, NavigationReferenceFrame.RenderingFrame, NavigationTransitionType.DrillIn); + _ = NavigateIdleWhenSelectionSettlesAsync(); } else { + InvalidatePendingIdleNavigation(); + // Navigate to composing page. if (message.SelectedMailItemViewModel.IsDraft) { @@ -309,7 +308,7 @@ public sealed partial class MailListPage : MailListPageAbstract, { // Composer is already active. Skip connected animation since the page // will be reused in-place (no navigation occurs). - // NavigationService will send NewComposeDraftItemRequestedEvent instead. + // NavigationService will send ReaderItemRefreshRequestedEvent instead. } else composerPageTransition = NavigationTransitionType.DrillIn; @@ -337,6 +336,34 @@ public sealed partial class MailListPage : MailListPageAbstract, private bool IsRenderingPageActive() => RenderingFrame.Content is MailRenderingPage; private bool IsComposingPageActive() => RenderingFrame.Content is ComposePage; + private void InvalidatePendingIdleNavigation() + { + unchecked + { + _idleNavigationRequestVersion++; + } + } + + private async Task NavigateIdleWhenSelectionSettlesAsync() + { + int requestVersion = ++_idleNavigationRequestVersion; + + await Task.Delay(IDLE_NAVIGATION_DELAY_MS); + + if (requestVersion != _idleNavigationRequestVersion) return; + if (ViewModel.MailCollection.SelectedItemsCount != 0) return; + + if (IsRenderingPageActive()) + { + WeakReferenceMessenger.Default.Send(new CancelRenderingContentRequested()); + } + + // Ensure rendering frame actually navigates away from Compose/Rendering pages. + // Otherwise those pages keep their messenger registrations alive. + ViewModel.NavigationService.Navigate(WinoPage.IdlePage, null, NavigationReferenceFrame.RenderingFrame, NavigationTransitionType.DrillIn); + UpdateAdaptiveness(); + } + private void PrepareComposePageWebViewTransition() { var webView = GetComposerPageWebView(); diff --git a/Wino.Mail.WinUI/Views/Mail/MailRenderingPage.xaml b/Wino.Mail.WinUI/Views/Mail/MailRenderingPage.xaml index c6b9a0c5..4e4b521f 100644 --- a/Wino.Mail.WinUI/Views/Mail/MailRenderingPage.xaml +++ b/Wino.Mail.WinUI/Views/Mail/MailRenderingPage.xaml @@ -46,10 +46,7 @@ Grid.RowSpan="2" Width="36" Height="36" - FromAddress="{x:Bind Address}" - FromName="{x:Bind Name}" - SenderContactPicture="{x:Bind Base64ContactPicture}" - ThumbnailUpdatedEvent="{x:Bind ThumbnailUpdatedEvent, Mode=OneWay}" /> + MailItemInformation="{x:Bind}" /> @@ -291,9 +288,7 @@ Width="36" Height="36" FontSize="16" - FromAddress="{x:Bind ViewModel.FromAddress, Mode=OneWay}" - FromName="{x:Bind ViewModel.FromName, Mode=OneWay}" - SenderContactPicture="{x:Bind ViewModel.ContactPicture, Mode=OneWay}" /> + MailItemInformation="{x:Bind ViewModel.CurrentMailItemDisplayInformation, Mode=OneWay}" /> (TaskCreationOptions.RunContinuationsAsynchronously); + Chromium.CoreWebView2Initialized -= CoreWebViewInitialized; + Chromium.CoreWebView2Initialized += CoreWebViewInitialized; + _ = Chromium.EnsureCoreWebView2Async(); + base.OnNavigatedTo(e); var anim = ConnectedAnimationService.GetForCurrentView().GetAnimation("WebViewConnectedAnimation"); anim?.TryStart(Chromium); - Chromium.CoreWebView2Initialized -= CoreWebViewInitialized; - Chromium.CoreWebView2Initialized += CoreWebViewInitialized; - - _ = Chromium.EnsureCoreWebView2Async(); - // We don't have shell initialized here. It's only standalone EML viewing. // Shift command bar from top to adjust the design.