using System; using System.Text; using CommunityToolkit.Mvvm.Messaging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Toolkit.Uwp.Notifications; using Microsoft.Windows.AppLifecycle; using Wino.Core.Domain; using Wino.Core.Domain.Enums; using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Models.MailItem; using Wino.Core.WinUI; using Wino.Core.WinUI.Interfaces; using Wino.Mail.Services; using Wino.Mail.ViewModels; using Wino.Messaging.Client.Accounts; using Wino.Messaging.Server; using Wino.Services; namespace Wino.Mail.WinUI; public partial class App : WinoApplication, IRecipient { private ISynchronizationManager? _synchronizationManager; public App() { InitializeComponent(); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); ToastNotificationManagerCompat.OnActivated += ToastActivationHandler; RegisterRecipients(); } private async void ToastActivationHandler(ToastNotificationActivatedEventArgsCompat e) { // If we weren't launched by an app, launch our window like normal. // Otherwise if launched by a toast, our OnActivated callback will be triggered. var toastArgs = ToastArguments.Parse(e.Argument); var mailService = Services.GetRequiredService(); var accountService = Services.GetRequiredService(); var actionKey = toastArgs.Contains(Constants.ToastActionKey) ? toastArgs[Constants.ToastActionKey] : string.Empty; if (Guid.TryParse(toastArgs[Constants.ToastMailUniqueIdKey], out Guid mailItemUniqueId)) { // Action triggered. if (toastArgs.TryGetValue(Constants.ToastActionKey, out MailOperation action)) { // Check if the app is running. if (IsAppRunning()) { // Get the synchronizer and queue the action for the given item. var processor = Services.GetRequiredService(); var delegator = Services.GetRequiredService(); var mailItem = await mailService.GetSingleMailItemAsync(mailItemUniqueId); if (mailItem != null) { var package = new MailOperationPreperationRequest(action, mailItem); await delegator.ExecuteAsync(package); } } else { } } else { // Notification clicked. Handle navigation. var account = await mailService.GetMailAccountByUniqueIdAsync(mailItemUniqueId).ConfigureAwait(false); if (account == null) return; var mailItem = await mailService.GetSingleMailItemAsync(mailItemUniqueId).ConfigureAwait(false); if (mailItem == null) return; var message = new AccountMenuItemExtended(mailItem.AssignedFolder.Id, mailItem); // Delegate this event to LaunchProtocolService so app shell can pick it up on launch if app doesn't work. var launchProtocolService = Services.GetRequiredService(); launchProtocolService.LaunchParameter = message; // Send the messsage anyways. Launch protocol service will be ignored if the message is picked up by subscriber shell. WeakReferenceMessenger.Default.Send(message); } } if (ToastNotificationManagerCompat.WasCurrentProcessToastActivated()) { MainWindow.BringToFront(); } } #region Dependency Injection private void RegisterUWPServices(IServiceCollection services) { services.AddSingleton(); services.AddSingleton(); services.AddTransient(); services.AddTransient(); services.AddSingleton(); } private void RegisterViewModels(IServiceCollection services) { services.AddSingleton(typeof(AppShellViewModel)); services.AddTransient(typeof(MailListPageViewModel)); services.AddTransient(typeof(MailRenderingPageViewModel)); services.AddTransient(typeof(AccountManagementViewModel)); services.AddTransient(typeof(WelcomePageViewModel)); services.AddTransient(typeof(ComposePageViewModel)); services.AddTransient(typeof(IdlePageViewModel)); services.AddTransient(typeof(EditAccountDetailsPageViewModel)); services.AddTransient(typeof(AccountDetailsPageViewModel)); services.AddTransient(typeof(SignatureManagementPageViewModel)); services.AddTransient(typeof(MessageListPageViewModel)); services.AddTransient(typeof(ReadComposePanePageViewModel)); services.AddTransient(typeof(MergedAccountDetailsPageViewModel)); services.AddTransient(typeof(LanguageTimePageViewModel)); services.AddTransient(typeof(AppPreferencesPageViewModel)); services.AddTransient(typeof(AliasManagementPageViewModel)); services.AddTransient(typeof(ContactsPageViewModel)); } #endregion public override IServiceProvider ConfigureServices() { var services = new ServiceCollection(); services.RegisterViewModelService(); services.RegisterSharedServices(); services.RegisterCoreUWPServices(); services.RegisterCoreViewModels(); RegisterUWPServices(services); RegisterViewModels(services); return services.BuildServiceProvider(); } private bool IsStartupTaskLaunch() => AppInstance.GetCurrent().GetActivatedEventArgs()?.Kind == ExtendedActivationKind.StartupTask; public bool IsAppRunning() => MainWindow != null; protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { // If it's toast activation, compat will handle it. if (IsAppRunning()) return; // TODO: Check app relaunch mutex before loading anything. // Initialize NewThemeService first to get backdrop settings before creating window var newThemeService = Services.GetRequiredService(); var configService = Services.GetRequiredService(); var nativeAppService = Services.GetRequiredService(); _synchronizationManager = Services.GetRequiredService(); // Load saved backdrop type before creating window var savedBackdropType = (WindowBackdropType)configService.Get("WindowBackdropTypeKey", (int)WindowBackdropType.Mica); MainWindow = new ShellWindow(); nativeAppService.GetCoreWindowHwnd = () => WinRT.Interop.WindowNative.GetWindowHandle(MainWindow); await InitializeServicesAsync(); if (MainWindow is not IWinoShellWindow shellWindow) throw new ArgumentException("MainWindow must implement IWinoShellWindow"); bool isStartupTaskLaunch = IsStartupTaskLaunch(); shellWindow.HandleAppActivation(args); // Do not actiavate window if launched from startup task. Keep running in the system tray. if (!isStartupTaskLaunch) { MainWindow.Activate(); } } private void RegisterRecipients() { WeakReferenceMessenger.Default.Register(this); } public void Receive(NewMailSynchronizationRequested message) { _synchronizationManager?.SynchronizeMailAsync(message.Options); } }