From 1c79d14260247aabd67051c9f8f24e0d1992cf50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Wed, 1 Jan 2025 17:28:29 +0100 Subject: [PATCH] Finalizing quick event dialog. --- Wino.Calendar.ViewModels/AppShellViewModel.cs | 13 +- .../CalendarPageViewModel.cs | 176 +++++++++++++++++- .../Controls/CalendarItemControl.xaml | 3 + Wino.Calendar/Controls/WinoCalendarControl.cs | 2 +- Wino.Calendar/Package.appxmanifest | 2 +- Wino.Calendar/Views/AppShell.xaml | 4 +- Wino.Calendar/Views/AppShell.xaml.cs | 23 +-- Wino.Calendar/Views/CalendarPage.xaml | 163 +++++++++++++--- Wino.Calendar/Views/CalendarPage.xaml.cs | 39 ++-- Wino.Calendar/Wino.Calendar.csproj | 3 +- .../Models/Calendar/CalendarSettings.cs | 36 +++- .../Translations/en_US/resources.json | 7 +- Wino.Core.UWP/Helpers/XamlHelpers.cs | 9 +- Wino.Core.ViewModels/CalendarBaseViewModel.cs | 6 +- .../CalendarDisplayTypeChangedMessage.cs | 10 + .../Client/Calendar/CalendarEventAdded.cs | 9 - Wino.Services/CalendarService.cs | 12 +- 17 files changed, 424 insertions(+), 93 deletions(-) create mode 100644 Wino.Messages/Client/Calendar/CalendarDisplayTypeChangedMessage.cs delete mode 100644 Wino.Messages/Client/Calendar/CalendarEventAdded.cs diff --git a/Wino.Calendar.ViewModels/AppShellViewModel.cs b/Wino.Calendar.ViewModels/AppShellViewModel.cs index e778c37a..c782bf9d 100644 --- a/Wino.Calendar.ViewModels/AppShellViewModel.cs +++ b/Wino.Calendar.ViewModels/AppShellViewModel.cs @@ -26,9 +26,9 @@ namespace Wino.Calendar.ViewModels public partial class AppShellViewModel : CalendarBaseViewModel, IRecipient, IRecipient, - IRecipient + IRecipient, + IRecipient { - public event EventHandler DisplayTypeChanged; public IPreferencesService PreferencesService { get; } public IStatePersistanceService StatePersistenceService { get; } public IAccountCalendarStateService AccountCalendarStateService { get; } @@ -97,8 +97,8 @@ namespace Wino.Calendar.ViewModels { if (e == nameof(StatePersistenceService.CalendarDisplayType)) { - DisplayTypeChanged?.Invoke(this, StatePersistenceService.CalendarDisplayType); - OnPropertyChanged(nameof(IsVerticalCalendar)); + Messenger.Send(new CalendarDisplayTypeChangedMessage(StatePersistenceService.CalendarDisplayType)); + // Change the calendar. DateClicked(new CalendarViewDayClickedEventArgs(GetDisplayTypeSwitchDate())); @@ -334,5 +334,10 @@ namespace Wino.Calendar.ViewModels => await ExecuteUIThread(() => IsCalendarEnabled = message.IsEnabled); public void Receive(NavigateManageAccountsRequested message) => SelectedMenuItemIndex = 1; + + public void Receive(CalendarDisplayTypeChangedMessage message) + { + OnPropertyChanged(nameof(IsVerticalCalendar)); + } } } diff --git a/Wino.Calendar.ViewModels/CalendarPageViewModel.cs b/Wino.Calendar.ViewModels/CalendarPageViewModel.cs index adaf118a..af7a483a 100644 --- a/Wino.Calendar.ViewModels/CalendarPageViewModel.cs +++ b/Wino.Calendar.ViewModels/CalendarPageViewModel.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using CommunityToolkit.Diagnostics; using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; using MoreLinq; using Serilog; @@ -27,17 +28,14 @@ namespace Wino.Calendar.ViewModels IRecipient, IRecipient { - [ObservableProperty] - private ObservableRangeCollection _dayRanges = []; + #region Quick Event Creation [ObservableProperty] - private int _selectedDateRangeIndex; - - [ObservableProperty] - private DayRangeRenderModel _selectedDayRange; + private bool _isQuickEventDialogOpen; [ObservableProperty] [NotifyPropertyChangedFor(nameof(SelectedQuickEventAccountCalendarName))] + [NotifyCanExecuteChangedFor(nameof(SaveQuickEventCommand))] private AccountCalendarViewModel _selectedQuickEventAccountCalendar; public string SelectedQuickEventAccountCalendarName @@ -48,6 +46,54 @@ namespace Wino.Calendar.ViewModels } } + [ObservableProperty] + private List _hourSelectionStrings; + + // To be able to revert the values when the user enters an invalid time. + private string _previousSelectedStartTimeString; + private string _previousSelectedEndTimeString; + + [ObservableProperty] + private DateTime? _selectedQuickEventDate; + + [ObservableProperty] + private bool _isAllDay; + + [ObservableProperty] + [NotifyCanExecuteChangedFor(nameof(SaveQuickEventCommand))] + private string _selectedStartTimeString; + + [ObservableProperty] + [NotifyCanExecuteChangedFor(nameof(SaveQuickEventCommand))] + private string _selectedEndTimeString; + + [ObservableProperty] + private string _location; + + [ObservableProperty] + [NotifyCanExecuteChangedFor(nameof(SaveQuickEventCommand))] + private string _eventName; + + public DateTime QuickEventStartTime => SelectedQuickEventDate.Value.Date.Add(_currentSettings.GetTimeSpan(SelectedStartTimeString).Value); + public DateTime QuickEventEndTime => SelectedQuickEventDate.Value.Date.Add(_currentSettings.GetTimeSpan(SelectedEndTimeString).Value); + + public bool CanSaveQuickEvent => SelectedQuickEventAccountCalendar != null && + !string.IsNullOrWhiteSpace(EventName) && + !string.IsNullOrWhiteSpace(SelectedStartTimeString) && + !string.IsNullOrWhiteSpace(SelectedEndTimeString) && + QuickEventEndTime > QuickEventStartTime; + + #endregion + + [ObservableProperty] + private ObservableRangeCollection _dayRanges = []; + + [ObservableProperty] + private int _selectedDateRangeIndex; + + [ObservableProperty] + private DayRangeRenderModel _selectedDayRange; + [ObservableProperty] private bool _isCalendarEnabled = true; @@ -96,6 +142,8 @@ namespace Wino.Calendar.ViewModels days.ForEach(a => a.EventsCollection.FilterByCalendars(AccountCalendarStateService.ActiveCalendars.Select(a => a.Id))); } + + // TODO: Replace when calendar settings are updated. // Should be a field ideally. private BaseCalendarTypeDrawingStrategy GetDrawingStrategy(CalendarDisplayType displayType) @@ -118,7 +166,74 @@ namespace Wino.Calendar.ViewModels { base.OnNavigatedTo(mode, parameters); + RefreshSettings(); + + // Automatically select the first primary calendar for quick event dialog. + SelectedQuickEventAccountCalendar = AccountCalendarStateService.ActiveCalendars.FirstOrDefault(a => a.IsPrimary); + } + + [RelayCommand(AllowConcurrentExecutions = false, CanExecute = nameof(CanSaveQuickEvent))] + private async Task SaveQuickEventAsync() + { + var durationSeconds = (QuickEventEndTime - QuickEventStartTime).TotalSeconds; + + var testCalendarItem = new CalendarItem + { + CalendarId = Guid.Parse("40aa0bf0-9ea7-40d8-b426-9c78281723c9"), + StartDate = QuickEventStartTime, + DurationInSeconds = durationSeconds, + CreatedAt = DateTime.UtcNow, + Description = string.Empty, + Location = Location, + Title = EventName, + Id = Guid.NewGuid() + }; + + IsQuickEventDialogOpen = false; + await _calendarService.CreateNewCalendarItemAsync(testCalendarItem, null); + + // TODO: Create the request with the synchronizer. + } + + [RelayCommand] + private void MoreDetails() + { + // TODO: Navigate to advanced event creation page with existing parameters. + } + + public void SelectQuickEventTimeRange(TimeSpan startTime, TimeSpan endTime) + { + IsAllDay = false; + + SelectedStartTimeString = _currentSettings.GetTimeString(startTime); + SelectedEndTimeString = _currentSettings.GetTimeString(endTime); + } + + private void RefreshSettings() + { _currentSettings = _preferencesService.GetCurrentCalendarSettings(); + + // Populate the hour selection strings. + var timeStrings = new List(); + + for (int hour = 0; hour < 24; hour++) + { + for (int minute = 0; minute < 60; minute += 30) + { + var time = new DateTime(1, 1, 1, hour, minute, 0); + + if (_currentSettings.DayHeaderDisplayType == DayHeaderDisplayType.TwentyFourHour) + { + timeStrings.Add(time.ToString("HH:mm")); + } + else + { + timeStrings.Add(time.ToString("h:mm tt")); + } + } + } + + HourSelectionStrings = timeStrings; } partial void OnIsCalendarEnabledChanging(bool oldValue, bool newValue) => Messenger.Send(new CalendarEnableStatusChangedMessage(newValue)); @@ -160,7 +275,6 @@ namespace Wino.Calendar.ViewModels else if (ShouldScrollToItem(message)) { // Scroll to the selected date. - Messenger.Send(new ScrollToDateMessage(message.DisplayDate)); Debug.WriteLine("Scrolling to selected date."); return; @@ -404,9 +518,9 @@ namespace Wino.Calendar.ViewModels } - protected override async void OnCalendarEventAdded(CalendarItem calendarItem) + protected override async void OnCalendarItemAdded(CalendarItem calendarItem) { - base.OnCalendarEventAdded(calendarItem); + base.OnCalendarItemAdded(calendarItem); // test var calendar = await _calendarService.GetAccountCalendarAsync(Guid.Parse("40aa0bf0-9ea7-40d8-b426-9c78281723c9")); @@ -520,6 +634,48 @@ namespace Wino.Calendar.ViewModels return selectedDate >= initializedDateRange.StartDate && selectedDate <= initializedDateRange.EndDate; } + partial void OnIsAllDayChanged(bool value) + { + if (value) + { + SelectedStartTimeString = HourSelectionStrings.FirstOrDefault(); + SelectedEndTimeString = HourSelectionStrings.FirstOrDefault(); + } + else + { + SelectedStartTimeString = _previousSelectedStartTimeString; + SelectedEndTimeString = _previousSelectedEndTimeString; + } + } + + partial void OnSelectedStartTimeStringChanged(string newValue) + { + var parsedTime = _currentSettings.GetTimeSpan(newValue); + + if (parsedTime == null) + { + SelectedStartTimeString = _previousSelectedStartTimeString; + } + else if (IsAllDay) + { + _previousSelectedStartTimeString = newValue; + } + } + + partial void OnSelectedEndTimeStringChanged(string newValue) + { + var parsedTime = _currentSettings.GetTimeSpan(newValue); + + if (parsedTime == null) + { + SelectedEndTimeString = _previousSelectedStartTimeString; + } + else if (IsAllDay) + { + _previousSelectedEndTimeString = newValue; + } + } + partial void OnSelectedDayRangeChanged(DayRangeRenderModel value) { if (DayRanges.Count == 0 || SelectedDateRangeIndex < 0) return; @@ -565,7 +721,7 @@ namespace Wino.Calendar.ViewModels public void Receive(CalendarSettingsUpdatedMessage message) { - _currentSettings = _preferencesService.GetCurrentCalendarSettings(); + RefreshSettings(); // TODO: This might need throttling due to slider in the settings page for hour height. // or make sure the slider does not update on each tick but on focus lost. diff --git a/Wino.Calendar/Controls/CalendarItemControl.xaml b/Wino.Calendar/Controls/CalendarItemControl.xaml index 7f4c99e3..02721749 100644 --- a/Wino.Calendar/Controls/CalendarItemControl.xaml +++ b/Wino.Calendar/Controls/CalendarItemControl.xaml @@ -21,6 +21,9 @@ Margin="2,0" HorizontalAlignment="Center" VerticalAlignment="Center" + CharacterSpacing="8" + FontSize="13" + FontWeight="SemiBold" Foreground="{x:Bind helpers:XamlHelpers.GetReadableTextColor(CalendarItem.AssignedCalendar.BackgroundColorHex), Mode=OneWay}" Text="{x:Bind CalendarItem.Title}" TextWrapping="Wrap" /> diff --git a/Wino.Calendar/Controls/WinoCalendarControl.cs b/Wino.Calendar/Controls/WinoCalendarControl.cs index 735ddeeb..2bf9bbad 100644 --- a/Wino.Calendar/Controls/WinoCalendarControl.cs +++ b/Wino.Calendar/Controls/WinoCalendarControl.cs @@ -144,7 +144,7 @@ namespace Wino.Calendar.Controls private void UpdateIdleState() { - InternalFlipView.Visibility = IsFlipIdle ? Visibility.Collapsed : Visibility.Visible; + InternalFlipView.Opacity = IsFlipIdle ? 0 : 1; IdleGrid.Visibility = IsFlipIdle ? Visibility.Visible : Visibility.Collapsed; } diff --git a/Wino.Calendar/Package.appxmanifest b/Wino.Calendar/Package.appxmanifest index 185e2005..4385dc74 100644 --- a/Wino.Calendar/Package.appxmanifest +++ b/Wino.Calendar/Package.appxmanifest @@ -20,7 +20,7 @@ + Version="1.0.10.0" /> diff --git a/Wino.Calendar/Views/AppShell.xaml b/Wino.Calendar/Views/AppShell.xaml index 127ce31d..e3ad6f3e 100644 --- a/Wino.Calendar/Views/AppShell.xaml +++ b/Wino.Calendar/Views/AppShell.xaml @@ -55,7 +55,6 @@ + IsNavigationStackEnabled="False"> diff --git a/Wino.Calendar/Views/AppShell.xaml.cs b/Wino.Calendar/Views/AppShell.xaml.cs index c5c343f3..1bc4ce64 100644 --- a/Wino.Calendar/Views/AppShell.xaml.cs +++ b/Wino.Calendar/Views/AppShell.xaml.cs @@ -6,7 +6,8 @@ using Wino.Messaging.Client.Calendar; namespace Wino.Calendar.Views { - public sealed partial class AppShell : AppShellAbstract + public sealed partial class AppShell : AppShellAbstract, + IRecipient { private const string STATE_HorizontalCalendar = "HorizontalCalendar"; private const string STATE_VerticalCalendar = "VerticalCalendar"; @@ -17,11 +18,10 @@ namespace Wino.Calendar.Views InitializeComponent(); Window.Current.SetTitleBar(DragArea); - - ViewModel.DisplayTypeChanged += CalendarDisplayTypeChanged; } - private void CalendarDisplayTypeChanged(object sender, Core.Domain.Enums.CalendarDisplayType e) + + private void ManageCalendarDisplayType() { // Go to different states based on the display type. if (ViewModel.IsVerticalCalendar) @@ -34,18 +34,13 @@ namespace Wino.Calendar.Views } } - private void ShellFrameContentNavigated(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e) - { - - } - - private void BackButtonClicked(Core.UWP.Controls.WinoAppTitleBar sender, Windows.UI.Xaml.RoutedEventArgs args) - { - - } - private void PreviousDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoPreviousDateRequestedMessage()); private void NextDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoNextDateRequestedMessage()); + + public void Receive(CalendarDisplayTypeChangedMessage message) + { + ManageCalendarDisplayType(); + } } } diff --git a/Wino.Calendar/Views/CalendarPage.xaml b/Wino.Calendar/Views/CalendarPage.xaml index d1282d37..088d5494 100644 --- a/Wino.Calendar/Views/CalendarPage.xaml +++ b/Wino.Calendar/Views/CalendarPage.xaml @@ -5,6 +5,8 @@ xmlns:abstract="using:Wino.Calendar.Views.Abstract" xmlns:calendarControls="using:Wino.Calendar.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:data="using:Wino.Calendar.ViewModels.Data" + xmlns:domain="using:Wino.Core.Domain" xmlns:helpers="using:Wino.Helpers" xmlns:local="using:Wino.Calendar.Views" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" @@ -18,7 +20,7 @@ 900 @@ -51,8 +53,8 @@ HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Closed="CreateEventTipClosed" - IsOpen="False" - PreferredPlacement="Right" + IsOpen="{x:Bind ViewModel.IsQuickEventDialogOpen, Mode=TwoWay}" + PreferredPlacement="{x:Bind helpers:XamlHelpers.GetPlaccementModeForCalendarType(ViewModel.StatePersistanceService.CalendarDisplayType), Mode=OneWay}" Target="{x:Bind TeachingTipPositionerGrid}"> @@ -114,47 +116,162 @@ + + + + + + + + - + - - - - - - + Padding="0,6,6,6" + RowSpacing="12"> + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +