2026-03-11 01:39:32 +01:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using CommunityToolkit.Mvvm.Messaging;
|
|
|
|
|
using CommunityToolkit.WinUI;
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
using Microsoft.UI.Xaml;
|
|
|
|
|
using Microsoft.UI.Xaml.Controls;
|
|
|
|
|
using Microsoft.UI.Xaml.Controls.Primitives;
|
|
|
|
|
using Microsoft.UI.Xaml.Input;
|
|
|
|
|
using Microsoft.UI.Xaml.Navigation;
|
|
|
|
|
using Windows.Foundation;
|
2026-03-21 00:58:01 +01:00
|
|
|
using Windows.System;
|
2026-03-11 01:39:32 +01:00
|
|
|
using Wino.Core.Domain;
|
|
|
|
|
using Wino.Core.Domain.Entities.Mail;
|
|
|
|
|
using Wino.Core.Domain.Enums;
|
|
|
|
|
using Wino.Core.Domain.Interfaces;
|
2026-03-13 07:20:37 +01:00
|
|
|
using Wino.Core.Domain.MenuItems;
|
2026-03-11 01:39:32 +01:00
|
|
|
using Wino.Core.Domain.Models;
|
2026-03-21 00:58:01 +01:00
|
|
|
using Wino.Core.Domain.Models.Calendar;
|
2026-03-11 01:39:32 +01:00
|
|
|
using Wino.Core.Domain.Models.Folders;
|
|
|
|
|
using Wino.Core.Domain.Models.MailItem;
|
|
|
|
|
using Wino.Core.Domain.Models.Navigation;
|
2026-03-27 14:45:36 +01:00
|
|
|
using Wino.Calendar.ViewModels;
|
|
|
|
|
using Wino.Mail.ViewModels;
|
2026-03-11 01:39:32 +01:00
|
|
|
using Wino.Mail.ViewModels.Data;
|
2026-03-27 14:45:36 +01:00
|
|
|
using Wino.Mail.WinUI.ViewModels;
|
2026-03-11 01:39:32 +01:00
|
|
|
using Wino.Mail.WinUI.Controls;
|
2026-04-05 13:18:50 +02:00
|
|
|
using Wino.Mail.WinUI.Helpers;
|
2026-03-11 01:39:32 +01:00
|
|
|
using Wino.MenuFlyouts;
|
|
|
|
|
using Wino.MenuFlyouts.Context;
|
|
|
|
|
using Wino.Messaging.Client.Accounts;
|
|
|
|
|
using Wino.Messaging.Client.Calendar;
|
|
|
|
|
using Wino.Messaging.Client.Mails;
|
|
|
|
|
using Wino.Messaging.Client.Shell;
|
2026-03-14 14:14:58 +01:00
|
|
|
using Wino.Messaging.UI;
|
2026-03-11 19:26:37 +01:00
|
|
|
using Wino.Views;
|
2026-03-11 01:39:32 +01:00
|
|
|
|
|
|
|
|
namespace Wino.Mail.WinUI.Views;
|
|
|
|
|
|
|
|
|
|
public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
|
|
|
|
|
IShellHost,
|
|
|
|
|
IRecipient<AccountMenuItemExtended>,
|
|
|
|
|
IRecipient<NavigateMailFolderEvent>,
|
|
|
|
|
IRecipient<CreateNewMailWithMultipleAccountsRequested>,
|
2026-03-14 14:14:58 +01:00
|
|
|
IRecipient<CalendarDisplayTypeChangedMessage>,
|
|
|
|
|
IRecipient<AccountCreatedMessage>
|
2026-03-11 01:39:32 +01:00
|
|
|
{
|
|
|
|
|
private const string StateDefaultShellContent = "DefaultShellContentState";
|
|
|
|
|
private const string StateEventDetailsContent = "EventDetailsContentState";
|
|
|
|
|
private WinoApplicationMode? _activeMode;
|
2026-03-12 19:04:47 +01:00
|
|
|
private bool _isSyncingNavigationViewSelection;
|
2026-03-21 00:58:01 +01:00
|
|
|
private bool _isSynchronizingVisibleDateRangeCalendar;
|
2026-03-27 14:45:36 +01:00
|
|
|
private bool _isPreparedForWindowClose;
|
2026-03-11 01:39:32 +01:00
|
|
|
|
|
|
|
|
public WinoAppShell()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
|
2026-03-11 19:26:37 +01:00
|
|
|
var pageDispatcher = new WinUIDispatcher(DispatcherQueue);
|
|
|
|
|
ViewModel.MailClient.Dispatcher = pageDispatcher;
|
|
|
|
|
ViewModel.CalendarClient.Dispatcher = pageDispatcher;
|
|
|
|
|
ViewModel.GetClient(WinoApplicationMode.Contacts).Dispatcher = pageDispatcher;
|
2026-03-12 19:04:47 +01:00
|
|
|
ViewModel.GetClient(WinoApplicationMode.Settings).Dispatcher = pageDispatcher;
|
2026-03-11 19:26:37 +01:00
|
|
|
|
2026-03-11 01:39:32 +01:00
|
|
|
ViewModel.MailClient.PropertyChanged += MailClientPropertyChanged;
|
|
|
|
|
ViewModel.CalendarClient.PropertyChanged += CalendarClientPropertyChanged;
|
2026-03-12 19:04:47 +01:00
|
|
|
ViewModel.PropertyChanged += ViewModelPropertyChanged;
|
2026-03-21 18:25:54 +01:00
|
|
|
ViewModel.PreferencesService.PreferenceChanged += PreferencesServiceChanged;
|
2026-03-11 01:39:32 +01:00
|
|
|
ViewModel.StatePersistenceService.StatePropertyChanged += StatePersistenceServiceChanged;
|
|
|
|
|
|
|
|
|
|
InitializeCalendarControls();
|
|
|
|
|
UpdateEventDetailsVisualState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool HasShellContent => InnerShellFrame.Content != null;
|
|
|
|
|
|
|
|
|
|
public Frame GetShellFrame() => InnerShellFrame;
|
|
|
|
|
|
2026-03-11 19:26:37 +01:00
|
|
|
public void ActivateMode(WinoApplicationMode mode, ShellModeActivationContext activationContext)
|
2026-03-11 01:39:32 +01:00
|
|
|
{
|
|
|
|
|
if (_activeMode == mode && InnerShellFrame.Content != null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
DeactivateCurrentMode();
|
2026-03-11 19:26:37 +01:00
|
|
|
ResetShellModeNavigationState();
|
2026-03-11 01:39:32 +01:00
|
|
|
|
|
|
|
|
_activeMode = mode;
|
|
|
|
|
ViewModel.SetCurrentMode(mode);
|
|
|
|
|
|
|
|
|
|
RefreshNavigationViewBindings(syncMailSelection: mode != WinoApplicationMode.Mail);
|
|
|
|
|
|
|
|
|
|
ApplyModeLayout();
|
|
|
|
|
UpdateTitleBarSubtitle();
|
|
|
|
|
|
|
|
|
|
ViewModel.CurrentClient.Activate(activationContext);
|
2026-03-14 14:14:58 +01:00
|
|
|
ResetShellModeNavigationState();
|
2026-03-24 16:57:13 +01:00
|
|
|
NotifyTitleBarContentChanged();
|
2026-03-11 01:39:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnNavigatedFrom(e);
|
2026-03-27 14:45:36 +01:00
|
|
|
|
|
|
|
|
if (!_isPreparedForWindowClose)
|
|
|
|
|
{
|
|
|
|
|
DeactivateCurrentMode();
|
|
|
|
|
DetachLifetimeSubscriptions();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Bindings.StopTracking();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void PrepareForWindowClose()
|
|
|
|
|
{
|
|
|
|
|
if (_isPreparedForWindowClose)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
_isPreparedForWindowClose = true;
|
|
|
|
|
|
|
|
|
|
DeactivateAllShellClients();
|
|
|
|
|
WeakReferenceMessenger.Default.Unregister<LanguageChanged>(this);
|
|
|
|
|
UnregisterRecipients();
|
|
|
|
|
DetachLifetimeSubscriptions();
|
2026-03-11 01:39:32 +01:00
|
|
|
Bindings.StopTracking();
|
2026-03-27 14:45:36 +01:00
|
|
|
|
|
|
|
|
navigationView.MenuItemsSource = null;
|
|
|
|
|
CalendarHostListView.ItemsSource = null;
|
2026-04-05 13:18:50 +02:00
|
|
|
WindowCleanupHelper.CleanupFrame(InnerShellFrame);
|
2026-03-11 01:39:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnLoaded(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
UpdateNavigationPaneLayout(navigationView.DisplayMode);
|
|
|
|
|
RefreshNavigationViewBindings();
|
|
|
|
|
RefreshCalendarControls();
|
|
|
|
|
|
|
|
|
|
if (_activeMode == null)
|
|
|
|
|
{
|
2026-03-11 19:26:37 +01:00
|
|
|
ActivateMode(ViewModel.StatePersistenceService.ApplicationMode, new ShellModeActivationContext
|
|
|
|
|
{
|
|
|
|
|
IsInitialActivation = true
|
|
|
|
|
});
|
2026-03-11 01:39:32 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ApplyModeLayout()
|
|
|
|
|
{
|
|
|
|
|
RefreshCalendarControls();
|
|
|
|
|
UpdateEventDetailsVisualState();
|
|
|
|
|
UpdateTitleBarSubtitle();
|
|
|
|
|
UpdateNavigationPaneLayout(navigationView.DisplayMode);
|
2026-03-24 16:57:13 +01:00
|
|
|
NotifyTitleBarContentChanged();
|
2026-03-11 01:39:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DeactivateCurrentMode()
|
|
|
|
|
{
|
|
|
|
|
if (_activeMode == WinoApplicationMode.Mail)
|
|
|
|
|
{
|
2026-03-11 19:26:37 +01:00
|
|
|
WeakReferenceMessenger.Default.Send(new ClearMailSelectionsRequested());
|
|
|
|
|
WeakReferenceMessenger.Default.Send(new DisposeRenderingFrameRequested());
|
|
|
|
|
ViewModel.StatePersistenceService.IsReadingMail = false;
|
2026-03-11 01:39:32 +01:00
|
|
|
ViewModel.MailClient.Deactivate();
|
|
|
|
|
}
|
|
|
|
|
else if (_activeMode == WinoApplicationMode.Calendar)
|
|
|
|
|
{
|
2026-03-11 19:26:37 +01:00
|
|
|
ViewModel.StatePersistenceService.IsEventDetailsVisible = false;
|
2026-03-11 01:39:32 +01:00
|
|
|
ViewModel.CalendarClient.Deactivate();
|
|
|
|
|
}
|
|
|
|
|
else if (_activeMode == WinoApplicationMode.Contacts)
|
|
|
|
|
{
|
|
|
|
|
ViewModel.CurrentClient.Deactivate();
|
|
|
|
|
}
|
2026-03-12 19:04:47 +01:00
|
|
|
else if (_activeMode == WinoApplicationMode.Settings)
|
|
|
|
|
{
|
2026-03-14 14:14:58 +01:00
|
|
|
if (InnerShellFrame.Content is SettingsPage settingsPage)
|
|
|
|
|
{
|
|
|
|
|
settingsPage.ResetForModeSwitch();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-12 19:04:47 +01:00
|
|
|
ViewModel.CurrentClient.Deactivate();
|
|
|
|
|
}
|
2026-03-11 19:26:37 +01:00
|
|
|
}
|
|
|
|
|
|
2026-03-27 14:45:36 +01:00
|
|
|
private void DeactivateAllShellClients()
|
|
|
|
|
{
|
|
|
|
|
ViewModel.StatePersistenceService.IsReadingMail = false;
|
|
|
|
|
ViewModel.StatePersistenceService.IsEventDetailsVisible = false;
|
|
|
|
|
|
|
|
|
|
ViewModel.MailClient.Deactivate();
|
|
|
|
|
ViewModel.CalendarClient.Deactivate();
|
|
|
|
|
|
|
|
|
|
if (ViewModel.GetClient(WinoApplicationMode.Contacts) is ContactsShellClient contactsClient)
|
|
|
|
|
{
|
|
|
|
|
contactsClient.PrepareForShellShutdown();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ViewModel.GetClient(WinoApplicationMode.Settings) is SettingsShellClient settingsClient)
|
|
|
|
|
{
|
|
|
|
|
settingsClient.PrepareForShellShutdown();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ViewModel.MailClient is MailAppShellViewModel mailClient)
|
|
|
|
|
{
|
|
|
|
|
mailClient.PrepareForShellShutdown();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ViewModel.CalendarClient is CalendarAppShellViewModel calendarClient)
|
|
|
|
|
{
|
|
|
|
|
calendarClient.PrepareForShellShutdown();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DetachLifetimeSubscriptions()
|
|
|
|
|
{
|
|
|
|
|
ViewModel.MailClient.PropertyChanged -= MailClientPropertyChanged;
|
|
|
|
|
ViewModel.CalendarClient.PropertyChanged -= CalendarClientPropertyChanged;
|
|
|
|
|
ViewModel.PropertyChanged -= ViewModelPropertyChanged;
|
|
|
|
|
ViewModel.PreferencesService.PreferenceChanged -= PreferencesServiceChanged;
|
|
|
|
|
ViewModel.StatePersistenceService.StatePropertyChanged -= StatePersistenceServiceChanged;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-11 19:26:37 +01:00
|
|
|
private void ResetShellModeNavigationState()
|
|
|
|
|
{
|
|
|
|
|
InnerShellFrame.BackStack.Clear();
|
|
|
|
|
InnerShellFrame.ForwardStack.Clear();
|
2026-03-11 01:39:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateTitleBarSubtitle()
|
|
|
|
|
{
|
|
|
|
|
if (ViewModel.IsContactsMode)
|
|
|
|
|
{
|
|
|
|
|
ViewModel.StatePersistenceService.CoreWindowTitle = string.Empty;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ViewModel.IsCalendarMode)
|
|
|
|
|
{
|
2026-03-23 14:56:36 +01:00
|
|
|
ViewModel.StatePersistenceService.CoreWindowTitle = string.Empty;
|
2026-03-11 01:39:32 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ViewModel.StatePersistenceService.CoreWindowTitle = string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void InitializeCalendarControls()
|
|
|
|
|
{
|
|
|
|
|
CalendarHostListView.ItemsSource = ViewModel.CalendarClient.GroupedAccountCalendars;
|
|
|
|
|
|
|
|
|
|
RefreshCalendarControls();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RefreshCalendarControls()
|
|
|
|
|
{
|
|
|
|
|
CalendarHostListView.ItemsSource = ViewModel.CalendarClient.GroupedAccountCalendars;
|
2026-03-21 00:58:01 +01:00
|
|
|
SynchronizeVisibleDateRangeCalendar();
|
2026-03-11 01:39:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RefreshNavigationViewBindings(bool syncMailSelection = true)
|
|
|
|
|
{
|
|
|
|
|
navigationView.MenuItemsSource = ViewModel.CurrentMenuItems;
|
2026-03-12 19:04:47 +01:00
|
|
|
SetNavigationViewSelectedItem(ViewModel.CurrentClient.HandlesNavigationSelection && syncMailSelection
|
2026-03-11 01:39:32 +01:00
|
|
|
? ViewModel.SelectedMenuItem
|
2026-03-12 19:04:47 +01:00
|
|
|
: null);
|
2026-03-11 01:39:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateEventDetailsVisualState()
|
|
|
|
|
{
|
|
|
|
|
VisualStateManager.GoToState(this,
|
|
|
|
|
ViewModel.StatePersistenceService.IsEventDetailsVisible ? StateEventDetailsContent : StateDefaultShellContent,
|
|
|
|
|
false);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-13 07:20:37 +01:00
|
|
|
private async void NewCalendarEventNavigationItemTapped(object sender, TappedRoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
await InvokeNewCalendarEventAsync();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 20:23:20 +02:00
|
|
|
private async void AttentionIconClicked(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (sender is not FrameworkElement { DataContext: AccountMenuItem accountMenuItem })
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (ViewModel.MailClient is MailAppShellViewModel mailClient)
|
|
|
|
|
{
|
|
|
|
|
await mailClient.HandleAccountAttentionAsync(accountMenuItem.Parameter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-13 07:20:37 +01:00
|
|
|
private async void NewCalendarEventNavigationItemKeyDown(object sender, KeyRoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (e.Key is not (VirtualKey.Enter or VirtualKey.Space))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
await InvokeNewCalendarEventAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Task InvokeNewCalendarEventAsync()
|
|
|
|
|
=> ViewModel.CalendarClient.HandleNavigationItemInvokedAsync(new NewCalendarEventMenuItem());
|
|
|
|
|
|
2026-04-04 20:23:20 +02:00
|
|
|
private async void SynchronizeCalendarsNavigationItemTapped(object sender, TappedRoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
await InvokeCalendarSynchronizationAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void SynchronizeCalendarsNavigationItemKeyDown(object sender, KeyRoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (e.Key is not (VirtualKey.Enter or VirtualKey.Space))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
await InvokeCalendarSynchronizationAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Task InvokeCalendarSynchronizationAsync()
|
|
|
|
|
{
|
|
|
|
|
if (ViewModel.CalendarClient.SyncCommand.CanExecute(null))
|
|
|
|
|
{
|
|
|
|
|
ViewModel.CalendarClient.SyncCommand.Execute(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-24 16:57:13 +01:00
|
|
|
public void Receive(CalendarDisplayTypeChangedMessage message) => NotifyTitleBarContentChanged();
|
2026-03-11 01:39:32 +01:00
|
|
|
|
2026-03-14 14:14:58 +01:00
|
|
|
public void Receive(AccountCreatedMessage message)
|
|
|
|
|
{
|
|
|
|
|
_ = DispatcherQueue.EnqueueAsync(async () =>
|
|
|
|
|
{
|
|
|
|
|
ViewModel.NavigationService.ChangeApplicationMode(WinoApplicationMode.Mail);
|
|
|
|
|
await ViewModel.MailClient.HandleAccountCreatedAsync(message.Account);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-11 01:39:32 +01:00
|
|
|
private async void NavigationViewItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
|
|
|
|
|
{
|
2026-03-12 19:04:47 +01:00
|
|
|
if (_isSyncingNavigationViewSelection)
|
|
|
|
|
return;
|
|
|
|
|
|
2026-03-11 01:39:32 +01:00
|
|
|
if (ViewModel.IsCalendarMode)
|
|
|
|
|
{
|
|
|
|
|
if (args.InvokedItemContainer is FrameworkElement { DataContext: IMenuItem menuItem })
|
|
|
|
|
{
|
|
|
|
|
await ViewModel.CalendarClient.HandleNavigationItemInvokedAsync(menuItem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (args.InvokedItemContainer is WinoNavigationViewItem winoNavigationViewItem)
|
|
|
|
|
{
|
|
|
|
|
if (winoNavigationViewItem.SelectsOnInvoked)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
await ViewModel.CurrentClient.HandleNavigationItemInvokedAsync(winoNavigationViewItem.DataContext as IMenuItem);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void MenuSelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
|
|
|
|
|
{
|
2026-03-12 19:04:47 +01:00
|
|
|
if (_isSyncingNavigationViewSelection)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!ViewModel.CurrentClient.HandlesNavigationSelection)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (ReferenceEquals(args.SelectedItem, ViewModel.SelectedMenuItem))
|
2026-03-11 01:39:32 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (args.SelectedItem is IMenuItem invokedMenuItem)
|
|
|
|
|
{
|
|
|
|
|
await ViewModel.CurrentClient.HandleNavigationSelectionChangedAsync(invokedMenuItem);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Receive(AccountMenuItemExtended message)
|
|
|
|
|
{
|
|
|
|
|
if (!ViewModel.IsMailMode)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
_ = DispatcherQueue.EnqueueAsync(async () =>
|
|
|
|
|
{
|
|
|
|
|
if (message.FolderId == default)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (ViewModel.MailClient.MenuItems!.TryGetFolderMenuItem(message.FolderId, out IBaseFolderMenuItem foundMenuItem))
|
|
|
|
|
{
|
|
|
|
|
foundMenuItem.Expand();
|
|
|
|
|
await ViewModel.MailClient.NavigateFolderAsync(foundMenuItem);
|
2026-03-12 19:04:47 +01:00
|
|
|
SetNavigationViewSelectedItem(foundMenuItem);
|
2026-03-11 01:39:32 +01:00
|
|
|
|
|
|
|
|
if (message.NavigateMailItem != null)
|
|
|
|
|
{
|
|
|
|
|
WeakReferenceMessenger.Default.Send(new MailItemNavigationRequested(message.NavigateMailItem.UniqueId, ScrollToItem: true));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (message.NavigateMailItem == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (ViewModel.MailClient.MenuItems!.TryGetAccountMenuItem(message.NavigateMailItem.AssignedAccount.Id, out IAccountMenuItem accountMenuItem))
|
|
|
|
|
{
|
|
|
|
|
await ViewModel.MailClient.ChangeLoadedAccountAsync(accountMenuItem, navigateInbox: false);
|
|
|
|
|
|
|
|
|
|
if (ViewModel.MailClient.MenuItems!.TryGetFolderMenuItem(message.FolderId, out IBaseFolderMenuItem accountFolderMenuItem))
|
|
|
|
|
{
|
|
|
|
|
accountFolderMenuItem.Expand();
|
|
|
|
|
await ViewModel.MailClient.NavigateFolderAsync(accountFolderMenuItem);
|
2026-03-12 19:04:47 +01:00
|
|
|
SetNavigationViewSelectedItem(accountFolderMenuItem);
|
2026-03-11 01:39:32 +01:00
|
|
|
WeakReferenceMessenger.Default.Send(new MailItemNavigationRequested(message.NavigateMailItem.UniqueId, ScrollToItem: true));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Receive(NavigateMailFolderEvent message)
|
|
|
|
|
{
|
|
|
|
|
if (!ViewModel.IsMailMode || message.BaseFolderMenuItem == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (navigationView.SelectedItem != message.BaseFolderMenuItem)
|
|
|
|
|
{
|
|
|
|
|
var navigateFolderArgs = new NavigateMailFolderEventArgs(message.BaseFolderMenuItem, message.FolderInitLoadAwaitTask);
|
|
|
|
|
|
|
|
|
|
ViewModel.NavigationService.Navigate(WinoPage.MailListPage, navigateFolderArgs, NavigationReferenceFrame.InnerShellFrame);
|
|
|
|
|
|
2026-03-12 19:04:47 +01:00
|
|
|
SetNavigationViewSelectedItem(message.BaseFolderMenuItem);
|
2026-03-11 01:39:32 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
message.FolderInitLoadAwaitTask?.TrySetResult(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ShellFrameContentNavigated(object sender, NavigationEventArgs e)
|
|
|
|
|
{
|
2026-03-24 16:57:13 +01:00
|
|
|
NotifyTitleBarContentChanged();
|
2026-03-11 01:39:32 +01:00
|
|
|
|
|
|
|
|
if (ViewModel.IsMailMode)
|
|
|
|
|
{
|
|
|
|
|
RefreshNavigationViewBindings();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void MenuItemContextRequested(UIElement sender, ContextRequestedEventArgs args)
|
|
|
|
|
{
|
|
|
|
|
if (!ViewModel.IsMailMode)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (sender is WinoNavigationViewItem menuItem &&
|
|
|
|
|
menuItem.DataContext is IBaseFolderMenuItem baseFolderMenuItem &&
|
|
|
|
|
baseFolderMenuItem.IsMoveTarget &&
|
|
|
|
|
args.TryGetPosition(sender, out Point p))
|
|
|
|
|
{
|
|
|
|
|
args.Handled = true;
|
|
|
|
|
|
|
|
|
|
var source = new TaskCompletionSource<FolderOperationMenuItem>();
|
|
|
|
|
var actions = ViewModel.MailClient.GetFolderContextMenuActions(baseFolderMenuItem);
|
|
|
|
|
var flyout = new FolderOperationFlyout(actions, source);
|
|
|
|
|
|
|
|
|
|
flyout.ShowAt(menuItem, new FlyoutShowOptions
|
|
|
|
|
{
|
|
|
|
|
ShowMode = FlyoutShowMode.Standard,
|
|
|
|
|
Position = new Point(p.X + 30, p.Y - 20)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var operation = await source.Task;
|
|
|
|
|
flyout.Dispose();
|
|
|
|
|
|
|
|
|
|
if (operation != null)
|
|
|
|
|
{
|
|
|
|
|
await ViewModel.MailClient.PerformFolderOperationAsync(operation.Operation, baseFolderMenuItem);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Receive(CreateNewMailWithMultipleAccountsRequested message)
|
|
|
|
|
{
|
|
|
|
|
if (!ViewModel.IsMailMode)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var container = navigationView.ContainerFromMenuItem(ViewModel.MailClient.CreatePrimaryMenuItem);
|
|
|
|
|
var flyout = new AccountSelectorFlyout(message.AllAccounts, ViewModel.MailClient.CreateNewMailForAsync);
|
|
|
|
|
|
|
|
|
|
flyout.ShowAt(container, new FlyoutShowOptions
|
|
|
|
|
{
|
|
|
|
|
ShowMode = FlyoutShowMode.Auto,
|
|
|
|
|
Placement = FlyoutPlacementMode.Right
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void NavigationPaneOpening(NavigationView sender, object args)
|
|
|
|
|
{
|
|
|
|
|
if (!ViewModel.IsMailMode)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (sender.DisplayMode == NavigationViewDisplayMode.Minimal && sender.SelectedItem is IFolderMenuItem selectedFolderMenuItem)
|
|
|
|
|
{
|
|
|
|
|
selectedFolderMenuItem.Expand();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 09:00:26 +01:00
|
|
|
private void NavigationPaneOpened(NavigationView sender, object args)
|
|
|
|
|
=> UpdateNavigationPaneLayout(sender.DisplayMode);
|
|
|
|
|
|
|
|
|
|
private void NavigationPaneClosed(NavigationView sender, object args)
|
|
|
|
|
=> UpdateNavigationPaneLayout(sender.DisplayMode);
|
|
|
|
|
|
2026-03-11 01:39:32 +01:00
|
|
|
private void NavigationViewDisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
|
|
|
|
|
=> UpdateNavigationPaneLayout(args.DisplayMode);
|
|
|
|
|
|
|
|
|
|
private void MailClientPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (e.PropertyName == nameof(IShellClient.SelectedMenuItem) && ViewModel.IsMailMode)
|
|
|
|
|
{
|
2026-03-12 19:04:47 +01:00
|
|
|
SetNavigationViewSelectedItem(ViewModel.MailClient.SelectedMenuItem);
|
2026-03-11 01:39:32 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CalendarClientPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
|
|
|
|
{
|
2026-03-12 11:28:41 +01:00
|
|
|
if (!DispatcherQueue.HasThreadAccess)
|
|
|
|
|
{
|
|
|
|
|
var enqueued = DispatcherQueue.TryEnqueue(() => CalendarClientPropertyChanged(sender, e));
|
|
|
|
|
|
|
|
|
|
if (!enqueued)
|
|
|
|
|
throw new InvalidOperationException("Could not marshal calendar property changes onto the UI thread.");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 00:58:01 +01:00
|
|
|
if (e.PropertyName == nameof(ICalendarShellClient.CurrentVisibleRange) ||
|
|
|
|
|
e.PropertyName == nameof(ICalendarShellClient.VisibleDateRangeText))
|
2026-03-11 01:39:32 +01:00
|
|
|
{
|
2026-03-21 00:58:01 +01:00
|
|
|
SynchronizeVisibleDateRangeCalendar();
|
2026-03-11 01:39:32 +01:00
|
|
|
UpdateTitleBarSubtitle();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 00:58:01 +01:00
|
|
|
private void VisibleDateRangeCalendarViewSelectedDatesChanged(CalendarView sender, CalendarViewSelectedDatesChangedEventArgs args)
|
|
|
|
|
{
|
|
|
|
|
if (_isSynchronizingVisibleDateRangeCalendar)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
DateTimeOffset? interactedDate = null;
|
|
|
|
|
|
|
|
|
|
if (args.AddedDates.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
interactedDate = args.AddedDates[0];
|
|
|
|
|
}
|
|
|
|
|
else if (args.RemovedDates.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
interactedDate = args.RemovedDates[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (interactedDate is null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var clickedArgs = new CalendarViewDayClickedEventArgs(interactedDate.Value.DateTime);
|
|
|
|
|
|
|
|
|
|
if (ViewModel.CalendarClient.DateClickedCommand.CanExecute(clickedArgs))
|
|
|
|
|
{
|
|
|
|
|
ViewModel.CalendarClient.DateClickedCommand.Execute(clickedArgs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SynchronizeVisibleDateRangeCalendar()
|
|
|
|
|
{
|
|
|
|
|
if (!DispatcherQueue.HasThreadAccess)
|
|
|
|
|
{
|
|
|
|
|
var enqueued = DispatcherQueue.TryEnqueue(SynchronizeVisibleDateRangeCalendar);
|
|
|
|
|
|
|
|
|
|
if (!enqueued)
|
|
|
|
|
throw new InvalidOperationException("Could not marshal visible date range calendar synchronization onto the UI thread.");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_isSynchronizingVisibleDateRangeCalendar = true;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2026-03-21 18:25:54 +01:00
|
|
|
VisibleDateRangeCalendarView.FirstDayOfWeek = MapFirstDayOfWeek(ViewModel.PreferencesService.FirstDayOfWeek);
|
2026-03-21 00:58:01 +01:00
|
|
|
VisibleDateRangeCalendarView.SelectedDates.Clear();
|
|
|
|
|
|
|
|
|
|
var currentRange = ViewModel.CalendarClient.CurrentVisibleRange;
|
|
|
|
|
if (currentRange == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
foreach (var date in currentRange.Dates)
|
|
|
|
|
{
|
|
|
|
|
VisibleDateRangeCalendarView.SelectedDates.Add(new DateTimeOffset(date.ToDateTime(TimeOnly.MinValue)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VisibleDateRangeCalendarView.SetDisplayDate(new DateTimeOffset(currentRange.AnchorDate.ToDateTime(TimeOnly.MinValue)));
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
_isSynchronizingVisibleDateRangeCalendar = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 18:25:54 +01:00
|
|
|
private void PreferencesServiceChanged(object? sender, string propertyName)
|
|
|
|
|
{
|
|
|
|
|
if (propertyName == nameof(IPreferencesService.FirstDayOfWeek))
|
|
|
|
|
{
|
|
|
|
|
SynchronizeVisibleDateRangeCalendar();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static Windows.Globalization.DayOfWeek MapFirstDayOfWeek(DayOfWeek dayOfWeek)
|
|
|
|
|
=> dayOfWeek switch
|
|
|
|
|
{
|
|
|
|
|
DayOfWeek.Sunday => Windows.Globalization.DayOfWeek.Sunday,
|
|
|
|
|
DayOfWeek.Monday => Windows.Globalization.DayOfWeek.Monday,
|
|
|
|
|
DayOfWeek.Tuesday => Windows.Globalization.DayOfWeek.Tuesday,
|
|
|
|
|
DayOfWeek.Wednesday => Windows.Globalization.DayOfWeek.Wednesday,
|
|
|
|
|
DayOfWeek.Thursday => Windows.Globalization.DayOfWeek.Thursday,
|
|
|
|
|
DayOfWeek.Friday => Windows.Globalization.DayOfWeek.Friday,
|
|
|
|
|
DayOfWeek.Saturday => Windows.Globalization.DayOfWeek.Saturday,
|
|
|
|
|
_ => Windows.Globalization.DayOfWeek.Monday
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-12 19:04:47 +01:00
|
|
|
private void ViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (e.PropertyName != nameof(ViewModel.SelectedMenuItem) || !ViewModel.CurrentClient.HandlesNavigationSelection)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
SetNavigationViewSelectedItem(ViewModel.SelectedMenuItem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetNavigationViewSelectedItem(object? item)
|
|
|
|
|
{
|
|
|
|
|
if (ReferenceEquals(navigationView.SelectedItem, item))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
_isSyncingNavigationViewSelection = true;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
navigationView.SelectedItem = item;
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
_isSyncingNavigationViewSelection = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-11 01:39:32 +01:00
|
|
|
private void StatePersistenceServiceChanged(object? sender, string propertyName)
|
|
|
|
|
{
|
|
|
|
|
if (propertyName == nameof(IStatePersistanceService.CalendarDisplayType))
|
|
|
|
|
{
|
2026-03-24 16:57:13 +01:00
|
|
|
NotifyTitleBarContentChanged();
|
2026-03-11 01:39:32 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (propertyName == nameof(IStatePersistanceService.DayDisplayCount))
|
|
|
|
|
{
|
2026-03-24 16:57:13 +01:00
|
|
|
NotifyTitleBarContentChanged();
|
2026-03-11 01:39:32 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (propertyName == nameof(IStatePersistanceService.IsEventDetailsVisible))
|
|
|
|
|
{
|
|
|
|
|
UpdateEventDetailsVisualState();
|
2026-03-24 16:57:13 +01:00
|
|
|
NotifyTitleBarContentChanged();
|
2026-03-11 01:39:32 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-24 16:57:13 +01:00
|
|
|
private static void NotifyTitleBarContentChanged()
|
|
|
|
|
=> WeakReferenceMessenger.Default.Send(new TitleBarShellContentUpdated());
|
|
|
|
|
|
2026-03-11 01:39:32 +01:00
|
|
|
private void UpdateNavigationPaneLayout(NavigationViewDisplayMode displayMode)
|
|
|
|
|
{
|
2026-03-12 14:55:07 +01:00
|
|
|
if (displayMode == NavigationViewDisplayMode.Expanded && navigationView.IsPaneOpen)
|
2026-03-11 01:39:32 +01:00
|
|
|
{
|
2026-03-12 14:55:07 +01:00
|
|
|
if (ViewModel.IsCalendarMode)
|
|
|
|
|
{
|
|
|
|
|
PaneCustomContent.Visibility = Visibility.Visible;
|
|
|
|
|
CalendarPaneContent.Visibility = Visibility.Visible;
|
|
|
|
|
ContactsPaneContent.Visibility = Visibility.Collapsed;
|
|
|
|
|
InnerShellFrame.Margin = new Thickness(0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-03-11 01:39:32 +01:00
|
|
|
|
2026-03-12 14:55:07 +01:00
|
|
|
if (ViewModel.IsContactsMode)
|
|
|
|
|
{
|
|
|
|
|
PaneCustomContent.Visibility = Visibility.Visible;
|
|
|
|
|
CalendarPaneContent.Visibility = Visibility.Collapsed;
|
|
|
|
|
ContactsPaneContent.Visibility = Visibility.Visible;
|
|
|
|
|
InnerShellFrame.Margin = new Thickness(0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-03-11 01:39:32 +01:00
|
|
|
}
|
|
|
|
|
|
2026-03-12 14:55:07 +01:00
|
|
|
CalendarPaneContent.Visibility = Visibility.Collapsed;
|
|
|
|
|
ContactsPaneContent.Visibility = Visibility.Collapsed;
|
2026-03-11 01:39:32 +01:00
|
|
|
PaneCustomContent.Visibility = Visibility.Collapsed;
|
|
|
|
|
InnerShellFrame.Margin = displayMode == NavigationViewDisplayMode.Minimal
|
|
|
|
|
? new Thickness(7, 0, 0, 0)
|
|
|
|
|
: new Thickness(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void OnPreviewKeyDown(object sender, KeyRoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (e.KeyStatus.RepeatCount > 1 || ShouldIgnoreShortcut())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var key = NormalizeKey(e.Key);
|
|
|
|
|
if (string.IsNullOrEmpty(key))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var mode = ViewModel.CurrentMode;
|
|
|
|
|
var shortcutService = WinoApplication.Current.Services.GetRequiredService<IKeyboardShortcutService>();
|
|
|
|
|
var shortcut = await shortcutService.GetShortcutForKeyAsync(mode, key, GetCurrentModifierKeys());
|
|
|
|
|
|
|
|
|
|
if (shortcut == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var details = new KeyboardShortcutTriggerDetails
|
|
|
|
|
{
|
|
|
|
|
ShortcutId = shortcut.Id,
|
|
|
|
|
Mode = shortcut.Mode,
|
|
|
|
|
Action = shortcut.Action,
|
|
|
|
|
Key = shortcut.Key,
|
|
|
|
|
ModifierKeys = shortcut.ModifierKeys,
|
|
|
|
|
Sender = sender,
|
|
|
|
|
Origin = FocusManager.GetFocusedElement(XamlRoot)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
await ViewModel.CurrentClient.KeyboardShortcutHook(details);
|
|
|
|
|
|
|
|
|
|
if (InnerShellFrame.Content is BasePage activePage && activePage.AssociatedViewModel != null)
|
|
|
|
|
{
|
|
|
|
|
await activePage.AssociatedViewModel.KeyboardShortcutHook(details);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (details.Handled)
|
|
|
|
|
{
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ShouldIgnoreShortcut()
|
|
|
|
|
{
|
|
|
|
|
var focusedElement = FocusManager.GetFocusedElement(XamlRoot);
|
|
|
|
|
|
|
|
|
|
if (focusedElement is TextBox or AutoSuggestBox or PasswordBox or RichEditBox or ComboBox)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (focusedElement is FrameworkElement frameworkElement)
|
|
|
|
|
{
|
|
|
|
|
var typeName = frameworkElement.GetType().Name;
|
|
|
|
|
if (typeName.Contains("WebView", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void ItemDroppedOnFolder(object sender, DragEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (sender is WinoNavigationViewItem droppedContainer)
|
|
|
|
|
{
|
|
|
|
|
droppedContainer.IsDraggingItemOver = false;
|
|
|
|
|
|
|
|
|
|
if (CanContinueDragDrop(droppedContainer, e) && droppedContainer.DataContext is IBaseFolderMenuItem draggingFolder)
|
|
|
|
|
{
|
|
|
|
|
var dragPackage = e.DataView.Properties[nameof(MailDragPackage)] as MailDragPackage;
|
|
|
|
|
if (dragPackage == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
|
|
|
|
|
var mailCopies = ExtractMailCopies(dragPackage).ToList();
|
|
|
|
|
await ViewModel.MailClient.PerformMoveOperationAsync(mailCopies, draggingFolder);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ItemDragLeaveFromFolder(object sender, DragEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (sender is WinoNavigationViewItem leavingContainer)
|
|
|
|
|
{
|
|
|
|
|
leavingContainer.IsDraggingItemOver = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool CanContinueDragDrop(WinoNavigationViewItem interactingContainer, DragEventArgs args)
|
|
|
|
|
{
|
|
|
|
|
if (!ViewModel.IsMailMode || !args.DataView.Properties.ContainsKey(nameof(MailDragPackage)))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
var dragPackage = args.DataView.Properties[nameof(MailDragPackage)] as MailDragPackage;
|
|
|
|
|
if (dragPackage == null || !dragPackage.DraggingMails.Any())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (interactingContainer.IsSelected)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (interactingContainer.DataContext is not IBaseFolderMenuItem folderMenuItem || !folderMenuItem.IsMoveTarget)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
var draggedAccountIds = folderMenuItem.HandlingFolders.Select(a => a.MailAccountId);
|
|
|
|
|
var draggedMails = ExtractMailCopies(dragPackage).ToList();
|
|
|
|
|
|
|
|
|
|
return draggedMails.Any() && draggedMails.Any(a => draggedAccountIds.Contains(a.AssignedAccount.Id));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static IEnumerable<MailCopy> ExtractMailCopies(MailDragPackage dragPackage)
|
|
|
|
|
{
|
|
|
|
|
foreach (var item in dragPackage.DraggingMails)
|
|
|
|
|
{
|
|
|
|
|
if (item is MailCopy mailCopy)
|
|
|
|
|
{
|
|
|
|
|
yield return mailCopy;
|
|
|
|
|
}
|
|
|
|
|
else if (item is MailItemViewModel singleMailItemViewModel)
|
|
|
|
|
{
|
|
|
|
|
yield return singleMailItemViewModel.MailCopy;
|
|
|
|
|
}
|
|
|
|
|
else if (item is ThreadMailItemViewModel threadViewModel)
|
|
|
|
|
{
|
|
|
|
|
foreach (var threadMail in threadViewModel.ThreadEmails)
|
|
|
|
|
{
|
|
|
|
|
yield return threadMail.MailCopy;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ItemDragEnterOnFolder(object sender, DragEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (sender is WinoNavigationViewItem droppedContainer && CanContinueDragDrop(droppedContainer, e))
|
|
|
|
|
{
|
|
|
|
|
droppedContainer.IsDraggingItemOver = true;
|
|
|
|
|
|
|
|
|
|
if (droppedContainer.DataContext is IBaseFolderMenuItem draggingFolder)
|
|
|
|
|
{
|
|
|
|
|
e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
|
|
|
|
|
e.DragUIOverride.Caption = string.Format(Translator.DragMoveToFolderCaption, draggingFolder.FolderName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static ModifierKeys GetCurrentModifierKeys()
|
|
|
|
|
{
|
|
|
|
|
var modifiers = ModifierKeys.None;
|
|
|
|
|
|
|
|
|
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Control).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
|
|
|
|
modifiers |= ModifierKeys.Control;
|
|
|
|
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Menu).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
|
|
|
|
modifiers |= ModifierKeys.Alt;
|
|
|
|
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
|
|
|
|
modifiers |= ModifierKeys.Shift;
|
|
|
|
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.LeftWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down) ||
|
|
|
|
|
Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.RightWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
|
|
|
|
{
|
|
|
|
|
modifiers |= ModifierKeys.Windows;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return modifiers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string NormalizeKey(Windows.System.VirtualKey key)
|
|
|
|
|
{
|
|
|
|
|
return key switch
|
|
|
|
|
{
|
|
|
|
|
Windows.System.VirtualKey.Control or
|
|
|
|
|
Windows.System.VirtualKey.LeftControl or
|
|
|
|
|
Windows.System.VirtualKey.RightControl or
|
|
|
|
|
Windows.System.VirtualKey.Menu or
|
|
|
|
|
Windows.System.VirtualKey.LeftMenu or
|
|
|
|
|
Windows.System.VirtualKey.RightMenu or
|
|
|
|
|
Windows.System.VirtualKey.Shift or
|
|
|
|
|
Windows.System.VirtualKey.LeftShift or
|
|
|
|
|
Windows.System.VirtualKey.RightShift or
|
|
|
|
|
Windows.System.VirtualKey.LeftWindows or
|
|
|
|
|
Windows.System.VirtualKey.RightWindows => string.Empty,
|
|
|
|
|
_ => key.ToString()
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void RegisterRecipients()
|
|
|
|
|
{
|
|
|
|
|
base.RegisterRecipients();
|
|
|
|
|
|
|
|
|
|
WeakReferenceMessenger.Default.Register<AccountMenuItemExtended>(this);
|
|
|
|
|
WeakReferenceMessenger.Default.Register<CreateNewMailWithMultipleAccountsRequested>(this);
|
|
|
|
|
WeakReferenceMessenger.Default.Register<NavigateMailFolderEvent>(this);
|
|
|
|
|
WeakReferenceMessenger.Default.Register<CalendarDisplayTypeChangedMessage>(this);
|
2026-03-14 14:14:58 +01:00
|
|
|
WeakReferenceMessenger.Default.Register<AccountCreatedMessage>(this);
|
2026-03-11 01:39:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void UnregisterRecipients()
|
|
|
|
|
{
|
|
|
|
|
base.UnregisterRecipients();
|
|
|
|
|
|
|
|
|
|
WeakReferenceMessenger.Default.Unregister<AccountMenuItemExtended>(this);
|
|
|
|
|
WeakReferenceMessenger.Default.Unregister<CreateNewMailWithMultipleAccountsRequested>(this);
|
|
|
|
|
WeakReferenceMessenger.Default.Unregister<NavigateMailFolderEvent>(this);
|
|
|
|
|
WeakReferenceMessenger.Default.Unregister<CalendarDisplayTypeChangedMessage>(this);
|
2026-03-14 14:14:58 +01:00
|
|
|
WeakReferenceMessenger.Default.Unregister<AccountCreatedMessage>(this);
|
2026-03-11 01:39:32 +01:00
|
|
|
}
|
|
|
|
|
}
|