diff --git a/Wino.Core.Domain/Interfaces/INotificationBuilder.cs b/Wino.Core.Domain/Interfaces/INotificationBuilder.cs index 6b7fe872..aae5b2b2 100644 --- a/Wino.Core.Domain/Interfaces/INotificationBuilder.cs +++ b/Wino.Core.Domain/Interfaces/INotificationBuilder.cs @@ -31,6 +31,11 @@ public interface INotificationBuilder /// Account that needs attention. void CreateAttentionRequiredNotification(MailAccount account); + /// + /// Shows a notification when WebView2 runtime is unavailable. + /// + void CreateWebView2RuntimeMissingNotification(); + /// /// Creates a calendar reminder toast for the specified calendar item. /// diff --git a/Wino.Core.Domain/Interfaces/IWebView2RuntimeValidatorService.cs b/Wino.Core.Domain/Interfaces/IWebView2RuntimeValidatorService.cs new file mode 100644 index 00000000..aafa0669 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IWebView2RuntimeValidatorService.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace Wino.Core.Domain.Interfaces; + +public interface IWebView2RuntimeValidatorService +{ + /// + /// Validates whether WebView2 runtime is installed and available for use. + /// + Task IsRuntimeAvailableAsync(); +} + diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json index ec1838e5..15012aa4 100644 --- a/Wino.Core.Domain/Translations/en_US/resources.json +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -238,6 +238,8 @@ "Error_FailedToSetupSystemFolders_Title": "Failed to setup system folders", "Exception_AccountNeedsAttention_Title": "Account needs attention", "Exception_AccountNeedsAttention_Message": "'{0}' requires your attention to continue working.", + "Exception_WebView2RuntimeMissing_Message": "Wino Mail could not find Microsoft Edge WebView2 Runtime. Please install or repair the runtime to render message content correctly.", + "Exception_WebView2RuntimeMissing_Title": "WebView2 runtime is required", "Exception_AuthenticationCanceled": "Authentication canceled", "Exception_CustomThemeExists": "This theme already exists.", "Exception_CustomThemeMissingName": "You must provide a name.", diff --git a/Wino.Mail.ViewModels/MailAppShellViewModel.cs b/Wino.Mail.ViewModels/MailAppShellViewModel.cs index 68d8226a..6482a979 100644 --- a/Wino.Mail.ViewModels/MailAppShellViewModel.cs +++ b/Wino.Mail.ViewModels/MailAppShellViewModel.cs @@ -77,6 +77,7 @@ public partial class MailAppShellViewModel : MailBaseViewModel, private readonly IWinoRequestDelegator _winoRequestDelegator; private readonly IMailDialogService _dialogService; private readonly IMimeFileService _mimeFileService; + private readonly IWebView2RuntimeValidatorService _webView2RuntimeValidatorService; private readonly INativeAppService _nativeAppService; private readonly IMailService _mailService; @@ -98,7 +99,8 @@ public partial class MailAppShellViewModel : MailBaseViewModel, IFolderService folderService, IStatePersistanceService statePersistanceService, IConfigurationService configurationService, - IStartupBehaviorService startupBehaviorService) + IStartupBehaviorService startupBehaviorService, + IWebView2RuntimeValidatorService webView2RuntimeValidatorService) { StatePersistenceService = statePersistanceService; @@ -118,6 +120,7 @@ public partial class MailAppShellViewModel : MailBaseViewModel, _launchProtocolService = launchProtocolService; _notificationBuilder = notificationBuilder; _winoRequestDelegator = winoRequestDelegator; + _webView2RuntimeValidatorService = webView2RuntimeValidatorService; } protected override void OnDispatcherAssigned() @@ -233,6 +236,7 @@ public partial class MailAppShellViewModel : MailBaseViewModel, await RecreateMenuItemsAsync(); await ProcessLaunchOptionsAsync(); + await ValidateWebView2RuntimeAsync(); if (!Debugger.IsAttached) { @@ -242,6 +246,16 @@ public partial class MailAppShellViewModel : MailBaseViewModel, await MakeSureEnableStartupLaunchAsync(); } + private async Task ValidateWebView2RuntimeAsync() + { + var isRuntimeAvailable = await _webView2RuntimeValidatorService.IsRuntimeAvailableAsync(); + + if (!isRuntimeAvailable) + { + await ExecuteUIThread(() => _notificationBuilder.CreateWebView2RuntimeMissingNotification()); + } + } + private async Task MakeSureEnableStartupLaunchAsync() { if (!_configurationService.Get(IsActivateStartupLaunchAskedKey, false)) diff --git a/Wino.Mail.WinUI/CoreUWPContainerSetup.cs b/Wino.Mail.WinUI/CoreUWPContainerSetup.cs index fe804730..7c7ca562 100644 --- a/Wino.Mail.WinUI/CoreUWPContainerSetup.cs +++ b/Wino.Mail.WinUI/CoreUWPContainerSetup.cs @@ -29,6 +29,7 @@ public static class CoreUWPContainerSetup services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddSingleton(); services.AddTransient(); diff --git a/Wino.Mail.WinUI/Services/NotificationBuilder.cs b/Wino.Mail.WinUI/Services/NotificationBuilder.cs index dbf27526..c251a61e 100644 --- a/Wino.Mail.WinUI/Services/NotificationBuilder.cs +++ b/Wino.Mail.WinUI/Services/NotificationBuilder.cs @@ -252,6 +252,18 @@ public class NotificationBuilder : INotificationBuilder builder.Show(); } + public void CreateWebView2RuntimeMissingNotification() + { + var builder = new ToastContentBuilder(); + builder.SetToastScenario(ToastScenario.Default); + + builder.AddText(Translator.Exception_WebView2RuntimeMissing_Title); + builder.AddText(Translator.Exception_WebView2RuntimeMissing_Message); + + builder.AddButton(GetDismissButton()); + builder.Show(); + } + public Task CreateCalendarReminderNotificationAsync(CalendarItem calendarItem, long reminderDurationInSeconds) { if (calendarItem == null) diff --git a/Wino.Mail.WinUI/Services/WebView2RuntimeValidatorService.cs b/Wino.Mail.WinUI/Services/WebView2RuntimeValidatorService.cs new file mode 100644 index 00000000..5b8fb458 --- /dev/null +++ b/Wino.Mail.WinUI/Services/WebView2RuntimeValidatorService.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Web.WebView2.Core; +using Serilog; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Mail.WinUI.Services; + +public class WebView2RuntimeValidatorService : IWebView2RuntimeValidatorService +{ + public Task IsRuntimeAvailableAsync() + { + try + { + var version = CoreWebView2Environment.GetAvailableBrowserVersionString(); + var hasRuntime = !string.IsNullOrWhiteSpace(version); + + return Task.FromResult(hasRuntime); + } + catch (Exception ex) + { + Log.Error(ex, "WebView2 runtime validation failed."); + return Task.FromResult(false); + } + } +}