2026-03-06 17:46:38 +01:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reactive.Linq;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using CommunityToolkit.Mvvm.Messaging;
|
|
|
|
|
using CommunityToolkit.WinUI.Controls;
|
|
|
|
|
using EmailValidation;
|
|
|
|
|
using Microsoft.UI.Xaml;
|
|
|
|
|
using Microsoft.UI.Xaml.Controls;
|
|
|
|
|
using Microsoft.UI.Xaml.Navigation;
|
|
|
|
|
using Windows.Foundation;
|
|
|
|
|
using Wino.Messaging.Client.Shell;
|
|
|
|
|
using Wino.Calendar.ViewModels.Data;
|
|
|
|
|
using Wino.Mail.WinUI.Views.Abstract;
|
|
|
|
|
|
|
|
|
|
namespace Wino.Calendar.Views;
|
|
|
|
|
|
|
|
|
|
public sealed partial class CalendarEventComposePage : CalendarEventComposePageAbstract,
|
|
|
|
|
IRecipient<ApplicationThemeChanged>
|
|
|
|
|
{
|
|
|
|
|
private readonly List<IDisposable> _disposables = [];
|
|
|
|
|
|
|
|
|
|
public CalendarEventComposePage()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override async void OnNavigatedTo(NavigationEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnNavigatedTo(e);
|
|
|
|
|
|
|
|
|
|
_disposables.Add(GetSuggestionBoxDisposable(AttendeeBox));
|
|
|
|
|
_disposables.Add(NotesEditor);
|
|
|
|
|
|
|
|
|
|
ViewModel.GetHtmlNotesAsync = async () => await NotesEditor.GetHtmlBodyAsync() ?? string.Empty;
|
|
|
|
|
await NotesEditor.RenderHtmlAsync(" ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnNavigatingFrom(e);
|
|
|
|
|
|
|
|
|
|
foreach (var disposable in _disposables)
|
|
|
|
|
{
|
|
|
|
|
disposable.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_disposables.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IDisposable GetSuggestionBoxDisposable(TokenizingTextBox box)
|
|
|
|
|
{
|
|
|
|
|
return Observable.FromEventPattern<TypedEventHandler<AutoSuggestBox, AutoSuggestBoxTextChangedEventArgs>, AutoSuggestBoxTextChangedEventArgs>(
|
|
|
|
|
handler => box.TextChanged += handler,
|
|
|
|
|
handler => box.TextChanged -= handler)
|
|
|
|
|
.Throttle(TimeSpan.FromMilliseconds(120))
|
|
|
|
|
.ObserveOn(SynchronizationContext.Current!)
|
|
|
|
|
.Subscribe(async eventPattern =>
|
|
|
|
|
{
|
|
|
|
|
if (eventPattern.EventArgs.Reason != AutoSuggestionBoxTextChangeReason.UserInput)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (eventPattern.Sender is not AutoSuggestBox senderBox || senderBox.Text.Length < 2)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var addresses = await ViewModel.SearchContactsAsync(senderBox.Text).ConfigureAwait(false);
|
|
|
|
|
await ViewModel.ExecuteUIThread(() => senderBox.ItemsSource = addresses);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void TokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
|
|
|
|
|
{
|
|
|
|
|
if (!EmailValidator.Validate(args.TokenText))
|
|
|
|
|
{
|
|
|
|
|
args.Cancel = true;
|
|
|
|
|
ViewModel.NotifyInvalidEmail(args.TokenText);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var deferral = args.GetDeferral();
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var attendee = await ViewModel.GetAttendeeAsync(args.TokenText);
|
|
|
|
|
if (attendee == null)
|
|
|
|
|
{
|
|
|
|
|
args.Cancel = true;
|
|
|
|
|
ViewModel.NotifyAddressExists();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args.Item = attendee;
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
deferral.Complete();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void AddressBoxLostFocus(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (sender is not TokenizingTextBox tokenizingTextBox)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (tokenizingTextBox.Items.LastOrDefault() is not ITokenStringContainer info)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var currentText = info.Text;
|
|
|
|
|
if (string.IsNullOrWhiteSpace(currentText) || !EmailValidator.Validate(currentText))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var attendee = await ViewModel.GetAttendeeAsync(currentText);
|
|
|
|
|
if (attendee == null)
|
|
|
|
|
{
|
|
|
|
|
tokenizingTextBox.Text = string.Empty;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ViewModel.AddAttendee(attendee);
|
|
|
|
|
tokenizingTextBox.Text = string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RemoveAttendeeClicked(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (sender is Button { Tag: CalendarComposeAttendeeViewModel attendee })
|
|
|
|
|
{
|
|
|
|
|
ViewModel.RemoveAttendeeCommand.Execute(attendee);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RemoveAttachmentClicked(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (sender is Button { Tag: CalendarComposeAttachmentViewModel attachment })
|
|
|
|
|
{
|
|
|
|
|
ViewModel.RemoveAttachmentCommand.Execute(attachment);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-07 01:46:07 +01:00
|
|
|
private void ComposeCalendarClicked(object sender, ItemClickEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (e.ClickedItem is AccountCalendarViewModel calendar)
|
|
|
|
|
{
|
|
|
|
|
ViewModel.SelectedCalendar = calendar;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-06 17:46:38 +01:00
|
|
|
public void Receive(ApplicationThemeChanged message)
|
|
|
|
|
{
|
|
|
|
|
ViewModel.IsDarkWebviewRenderer = message.IsUnderlyingThemeDark;
|
|
|
|
|
NotesEditor.IsEditorDarkMode = message.IsUnderlyingThemeDark;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void RegisterRecipients()
|
|
|
|
|
{
|
|
|
|
|
base.RegisterRecipients();
|
|
|
|
|
WeakReferenceMessenger.Default.Register<ApplicationThemeChanged>(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void UnregisterRecipients()
|
|
|
|
|
{
|
|
|
|
|
base.UnregisterRecipients();
|
|
|
|
|
WeakReferenceMessenger.Default.Unregister<ApplicationThemeChanged>(this);
|
|
|
|
|
}
|
|
|
|
|
}
|