Files
Wino-Mail/Wino.Mail.WinUI/Views/Calendar/CalendarPage.xaml.cs
T

343 lines
12 KiB
C#
Raw Normal View History

2026-03-21 00:58:01 +01:00
using System;
2026-03-25 09:45:49 +01:00
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
2025-12-26 20:46:48 +01:00
using CommunityToolkit.Mvvm.Messaging;
2026-03-25 09:45:49 +01:00
using Microsoft.Extensions.DependencyInjection;
2026-03-23 14:56:36 +01:00
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
2025-12-26 20:46:48 +01:00
using Microsoft.UI.Xaml.Navigation;
2026-03-23 14:56:36 +01:00
using Windows.Foundation;
2026-03-25 09:45:49 +01:00
using Wino.Core.Domain;
2026-03-25 15:49:14 +01:00
using Wino.Core.Domain.Enums;
2026-03-23 14:56:36 +01:00
using Wino.Calendar.Controls;
2025-12-26 20:46:48 +01:00
using Wino.Calendar.Views.Abstract;
2026-03-25 09:45:49 +01:00
using Wino.Core.Domain.Entities.Calendar;
2025-12-26 20:46:48 +01:00
using Wino.Core.Domain.Models.Calendar;
2026-03-25 09:45:49 +01:00
using Wino.Core.Domain.Interfaces;
using Wino.Mail.WinUI;
using Wino.Mail.WinUI.Interfaces;
using Wino.Mail.WinUI.Models;
2025-12-26 20:46:48 +01:00
using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.Views;
2026-03-25 09:45:49 +01:00
public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearchHost
2025-12-26 20:46:48 +01:00
{
2026-03-23 14:56:36 +01:00
private const int PopupDialogOffset = 12;
2026-03-25 09:45:49 +01:00
private ICalendarShellClient CalendarShellClient { get; } = WinoApplication.Current.Services.GetRequiredService<ICalendarShellClient>();
private CancellationTokenSource? _searchCancellationTokenSource;
private long _calendarTypeSelectorChangedToken;
2026-03-25 15:49:14 +01:00
private bool _suppressSelectionResetOnPopupClose;
2026-03-25 09:45:49 +01:00
public ObservableCollection<TitleBarSearchSuggestion> SearchSuggestions { get; } = [];
public string SearchText { get; set; } = string.Empty;
public string SearchPlaceholderText => Translator.SearchBarPlaceholder;
2026-03-23 14:56:36 +01:00
2025-12-26 20:46:48 +01:00
public CalendarPage()
{
InitializeComponent();
2026-03-25 09:45:49 +01:00
_calendarTypeSelectorChangedToken = CalendarToolbar.RegisterSelectedTypeChanged(CalendarTypeSelectorSelectedTypeChanged);
CalendarToolbar.PreviousDateRequested += CalendarToolbarPreviousDateRequested;
CalendarToolbar.NextDateRequested += CalendarToolbarNextDateRequested;
ViewModel.PropertyChanged += ViewModelPropertyChanged;
CalendarShellClient.PropertyChanged += CalendarShellClientPropertyChanged;
CalendarShellClient.StatePersistenceService.StatePropertyChanged += CalendarStatePersistenceServiceChanged;
Unloaded += CalendarPageUnloaded;
RefreshCalendarToolbar();
2025-12-26 20:46:48 +01:00
}
2026-03-11 19:26:37 +01:00
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
2026-03-25 15:49:14 +01:00
CloseQuickEventPopup(clearSelection: true);
2026-03-11 19:26:37 +01:00
base.OnNavigatingFrom(e);
}
2025-12-26 20:46:48 +01:00
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
2026-03-25 09:45:49 +01:00
RefreshCalendarToolbar();
2025-12-26 20:46:48 +01:00
2026-03-21 00:58:01 +01:00
if (e.NavigationMode == NavigationMode.Back && ViewModel.RestoreVisibleState())
2026-03-11 19:26:37 +01:00
{
return;
}
2025-12-26 20:46:48 +01:00
2026-03-21 00:58:01 +01:00
var anchorDate = DateOnly.FromDateTime(DateTime.Now.Date);
2025-12-26 20:46:48 +01:00
2026-03-21 00:58:01 +01:00
if (e.Parameter is CalendarPageNavigationArgs args && !args.RequestDefaultNavigation)
2025-12-26 20:46:48 +01:00
{
2026-03-21 00:58:01 +01:00
anchorDate = DateOnly.FromDateTime(args.NavigationDate.Date);
2025-12-26 20:46:48 +01:00
}
2026-03-21 00:58:01 +01:00
var request = new CalendarDisplayRequest(ViewModel.StatePersistanceService.CalendarDisplayType, anchorDate);
WeakReferenceMessenger.Default.Send(new LoadCalendarMessage(request));
2025-12-26 20:46:48 +01:00
}
2026-03-23 14:56:36 +01:00
2026-03-25 09:45:49 +01:00
public async Task OnTitleBarSearchTextChangedAsync()
{
_searchCancellationTokenSource?.Cancel();
_searchCancellationTokenSource?.Dispose();
_searchCancellationTokenSource = null;
SearchSuggestions.Clear();
if (string.IsNullOrWhiteSpace(SearchText))
return;
_searchCancellationTokenSource = new CancellationTokenSource();
var cancellationToken = _searchCancellationTokenSource.Token;
try
{
await Task.Delay(150, cancellationToken);
var results = await ViewModel.SearchCalendarItemsAsync(SearchText, 6, cancellationToken);
if (cancellationToken.IsCancellationRequested)
return;
foreach (var result in results)
{
var subtitleParts = new[]
{
result.AssignedCalendar?.MailAccount?.Name,
result.AssignedCalendar?.Name,
result.LocalStartDate.ToString("g")
}.Where(part => !string.IsNullOrWhiteSpace(part));
SearchSuggestions.Add(new TitleBarSearchSuggestion(result.Title, string.Join(" • ", subtitleParts), result));
}
}
catch (OperationCanceledException)
{
}
}
public void OnTitleBarSearchSuggestionChosen(TitleBarSearchSuggestion suggestion)
{
SearchText = suggestion.Title;
}
public async Task OnTitleBarSearchSubmittedAsync(string queryText, TitleBarSearchSuggestion? chosenSuggestion)
{
SearchText = queryText;
if (chosenSuggestion?.Tag is CalendarItem selectedItem)
{
ViewModel.OpenCalendarSearchResult(selectedItem);
return;
}
var result = (await ViewModel.SearchCalendarItemsAsync(queryText, 1, CancellationToken.None)).FirstOrDefault();
if (result != null)
{
ViewModel.OpenCalendarSearchResult(result);
}
}
2026-03-23 14:56:36 +01:00
private void CalendarSurfaceEmptySlotTapped(object sender, CalendarEmptySlotTappedEventArgs e)
{
if (ViewModel.DisplayDetailsCalendarItemViewModel != null)
{
ViewModel.DisplayDetailsCalendarItemViewModel = null;
return;
}
ViewModel.SelectedQuickEventDate = e.ClickedDate;
2026-03-25 15:49:14 +01:00
ViewModel.IsAllDay = ViewModel.CurrentVisibleRange?.DisplayType == CalendarDisplayType.Month;
2026-03-23 14:56:36 +01:00
var transform = CalendarSurface.TransformToVisual(CalendarOverlayCanvas);
2026-03-25 15:49:14 +01:00
var canvasPoint = transform.TransformPoint(e.AnchorPoint);
2026-03-23 14:56:36 +01:00
2026-03-25 15:49:14 +01:00
TeachingTipPositionerGrid.Width = 1;
TeachingTipPositionerGrid.Height = 1;
2026-03-23 14:56:36 +01:00
Canvas.SetLeft(TeachingTipPositionerGrid, canvasPoint.X);
Canvas.SetTop(TeachingTipPositionerGrid, canvasPoint.Y);
2026-03-25 15:49:14 +01:00
if (!ViewModel.IsAllDay)
{
var startTime = e.ClickedDate.TimeOfDay;
var endTime = startTime.Add(TimeSpan.FromMinutes(30));
ViewModel.SelectQuickEventTimeRange(startTime, endTime);
}
2026-03-23 14:56:36 +01:00
2026-03-25 15:49:14 +01:00
_suppressSelectionResetOnPopupClose = true;
QuickEventPopupDialog.IsOpen = false;
2026-03-23 14:56:36 +01:00
QuickEventPopupDialog.IsOpen = true;
2026-03-25 15:49:14 +01:00
_suppressSelectionResetOnPopupClose = false;
2026-03-23 14:56:36 +01:00
}
private async void CalendarSurfaceCalendarItemDropped(object sender, CalendarItemDroppedEventArgs e)
=> await ViewModel.MoveCalendarItemAsync(e.CalendarItemViewModel, e.TargetStart);
2026-03-23 14:56:36 +01:00
private void QuickEventAccountSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
=> QuickEventAccountSelectorFlyout.Hide();
private void QuickEventPopupClosed(object sender, object e)
{
2026-03-25 15:49:14 +01:00
if (!_suppressSelectionResetOnPopupClose)
{
ViewModel.SelectedQuickEventDate = null;
}
2026-03-23 14:56:36 +01:00
}
private void PopupPlacementChanged(object sender, object e)
{
if (sender is not Popup popup)
{
return;
}
popup.HorizontalOffset = 0;
popup.VerticalOffset = 0;
switch (popup.ActualPlacement)
{
case PopupPlacementMode.Top:
popup.VerticalOffset = PopupDialogOffset * -1;
break;
case PopupPlacementMode.Bottom:
popup.VerticalOffset = PopupDialogOffset;
break;
case PopupPlacementMode.Left:
popup.HorizontalOffset = PopupDialogOffset * -1;
break;
case PopupPlacementMode.Right:
popup.HorizontalOffset = PopupDialogOffset;
break;
}
}
private void StartTimeDurationSubmitted(ComboBox sender, ComboBoxTextSubmittedEventArgs args)
=> ViewModel.SelectedStartTimeString = args.Text;
private void EndTimeDurationSubmitted(ComboBox sender, ComboBoxTextSubmittedEventArgs args)
=> ViewModel.SelectedEndTimeString = args.Text;
2026-03-25 09:45:49 +01:00
private void CalendarTypeSelectorSelectedTypeChanged(DependencyObject sender, DependencyProperty dp)
{
var selectedType = CalendarToolbar.SelectedType;
if (CalendarShellClient.StatePersistenceService.CalendarDisplayType != selectedType)
{
CalendarShellClient.StatePersistenceService.CalendarDisplayType = selectedType;
}
}
private void CalendarToolbarPreviousDateRequested(object? sender, EventArgs e)
=> CalendarShellClient.PreviousDateRangeCommand.Execute(null);
private void CalendarToolbarNextDateRequested(object? sender, EventArgs e)
=> CalendarShellClient.NextDateRangeCommand.Execute(null);
private void ViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ViewModel.VisibleDateRangeText))
{
RefreshCalendarToolbar();
}
}
private void CalendarShellClientPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ICalendarShellClient.VisibleDateRangeText))
{
RefreshCalendarToolbar();
}
}
private void CalendarStatePersistenceServiceChanged(object? sender, string propertyName)
{
if (propertyName == nameof(IStatePersistanceService.CalendarDisplayType) ||
propertyName == nameof(IStatePersistanceService.DayDisplayCount))
{
RefreshCalendarToolbar();
}
}
private void RefreshCalendarToolbar()
{
CalendarToolbar.VisibleDateRangeText = CalendarShellClient.VisibleDateRangeText;
CalendarToolbar.TodayClickedCommand = CalendarShellClient.TodayClickedCommand;
CalendarToolbar.DisplayDayCount = CalendarShellClient.StatePersistenceService.DayDisplayCount;
if (CalendarToolbar.SelectedType != CalendarShellClient.StatePersistenceService.CalendarDisplayType)
{
CalendarToolbar.SelectedType = CalendarShellClient.StatePersistenceService.CalendarDisplayType;
}
}
private void CalendarPageUnloaded(object sender, RoutedEventArgs e)
{
2026-03-25 15:49:14 +01:00
CloseQuickEventPopup(clearSelection: true);
2026-03-25 09:45:49 +01:00
CalendarToolbar.UnregisterSelectedTypeChanged(_calendarTypeSelectorChangedToken);
CalendarToolbar.PreviousDateRequested -= CalendarToolbarPreviousDateRequested;
CalendarToolbar.NextDateRequested -= CalendarToolbarNextDateRequested;
ViewModel.PropertyChanged -= ViewModelPropertyChanged;
CalendarShellClient.PropertyChanged -= CalendarShellClientPropertyChanged;
CalendarShellClient.StatePersistenceService.StatePropertyChanged -= CalendarStatePersistenceServiceChanged;
_searchCancellationTokenSource?.Cancel();
_searchCancellationTokenSource?.Dispose();
Unloaded -= CalendarPageUnloaded;
}
2026-03-25 15:49:14 +01:00
private async void SaveQuickEventClicked(object sender, RoutedEventArgs e)
{
if (!ViewModel.SaveQuickEventCommand.CanExecute(null))
{
return;
}
_suppressSelectionResetOnPopupClose = true;
try
{
QuickEventPopupDialog.IsOpen = false;
await ViewModel.SaveQuickEventCommand.ExecuteAsync(null);
}
finally
{
_suppressSelectionResetOnPopupClose = false;
ViewModel.SelectedQuickEventDate = null;
}
}
private void MoreDetailsClicked(object sender, RoutedEventArgs e)
{
if (!ViewModel.GoToEventComposePageCommand.CanExecute(null))
{
return;
}
_suppressSelectionResetOnPopupClose = true;
try
{
QuickEventPopupDialog.IsOpen = false;
ViewModel.GoToEventComposePageCommand.Execute(null);
}
finally
{
_suppressSelectionResetOnPopupClose = false;
ViewModel.SelectedQuickEventDate = null;
}
}
private void CloseQuickEventPopup(bool clearSelection)
{
_suppressSelectionResetOnPopupClose = !clearSelection;
QuickEventPopupDialog.IsOpen = false;
_suppressSelectionResetOnPopupClose = false;
if (clearSelection)
{
ViewModel.SelectedQuickEventDate = null;
}
}
2025-12-26 20:46:48 +01:00
}