Listing account calendars on the shell, some visual state updates and reacting to calendar setting changes properly.

This commit is contained in:
Burak Kaan Köse
2024-12-29 17:41:54 +01:00
parent 8d8d7d0f8c
commit eef2ee1baa
16 changed files with 484 additions and 83 deletions

View File

@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Wino.Calendar.ViewModels.Data;
using Wino.Calendar.ViewModels.Interfaces;
using Wino.Core.Domain.Collections;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
@@ -26,6 +29,7 @@ namespace Wino.Calendar.ViewModels
public event EventHandler<CalendarDisplayType> DisplayTypeChanged;
public IPreferencesService PreferencesService { get; }
public IStatePersistanceService StatePersistenceService { get; }
public IAccountCalendarStateService AccountCalendarStateService { get; }
public INavigationService NavigationService { get; }
public IWinoServerConnectionManager ServerConnectionManager { get; }
@@ -63,9 +67,16 @@ namespace Wino.Calendar.ViewModels
public AppShellViewModel(IPreferencesService preferencesService,
IStatePersistanceService statePersistanceService,
IAccountService accountService,
ICalendarService calendarService,
IAccountCalendarStateService accountCalendarStateService,
INavigationService navigationService,
IWinoServerConnectionManager serverConnectionManager)
{
_accountService = accountService;
_calendarService = calendarService;
AccountCalendarStateService = accountCalendarStateService;
NavigationService = navigationService;
ServerConnectionManager = serverConnectionManager;
PreferencesService = preferencesService;
@@ -86,11 +97,53 @@ namespace Wino.Calendar.ViewModels
}
}
public override void OnNavigatedTo(NavigationMode mode, object parameters)
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
{
base.OnNavigatedTo(mode, parameters);
UpdateDateNavigationHeaderItems();
await InitializeAccountCalendarsAsync();
}
private void AddGroupedAccountCalendarViewModel(GroupedAccountCalendarViewModel groupedAccountCalendarViewModel)
{
foreach (var calendarViewModel in groupedAccountCalendarViewModel.AccountCalendars)
{
calendarViewModel.CalendarSelectionStateChanged += UpdateAccountCalendarRequested;
}
AccountCalendarStateService.GroupedAccountCalendars.Add(groupedAccountCalendarViewModel);
}
private async void UpdateAccountCalendarRequested(object sender, AccountCalendarViewModel e)
=> await _calendarService.UpdateAccountCalendarAsync(e.AccountCalendar).ConfigureAwait(false);
private async Task InitializeAccountCalendarsAsync()
{
await Dispatcher.ExecuteOnUIThread(() => AccountCalendarStateService.GroupedAccountCalendars.Clear());
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
foreach (var account in accounts)
{
var accountCalendars = await _calendarService.GetAccountCalendarsAsync(account.Id).ConfigureAwait(false);
var calendarViewModels = new List<AccountCalendarViewModel>();
foreach (var calendar in accountCalendars)
{
var calendarViewModel = new AccountCalendarViewModel(account, calendar);
calendarViewModels.Add(calendarViewModel);
}
var groupedAccountCalendarViewModel = new GroupedAccountCalendarViewModel(account, calendarViewModels);
await Dispatcher.ExecuteOnUIThread(() =>
{
AddGroupedAccountCalendarViewModel(groupedAccountCalendarViewModel);
});
}
}
private void ForceNavigateCalendarDate()
@@ -177,6 +230,8 @@ namespace Wino.Calendar.ViewModels
}
private DateTime? _navigationDate;
private readonly IAccountService _accountService;
private readonly ICalendarService _calendarService;
public override void OnPageLoaded()
{

View File

@@ -7,18 +7,21 @@ using System.Threading.Tasks;
using CommunityToolkit.Diagnostics;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Wino.Calendar.ViewModels.Interfaces;
using Wino.Core.Domain.Collections;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Calendar;
using Wino.Core.Domain.Models.Calendar.CalendarTypeStrategies;
using Wino.Core.Domain.Models.Navigation;
using Wino.Core.ViewModels;
using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.ViewModels
{
public partial class CalendarPageViewModel : CalendarBaseViewModel,
IRecipient<LoadCalendarMessage>
IRecipient<LoadCalendarMessage>,
IRecipient<CalendarSettingsUpdatedMessage>
{
[ObservableProperty]
private ObservableRangeCollection<DayRangeRenderModel> _dayRanges = [];
@@ -32,9 +35,11 @@ namespace Wino.Calendar.ViewModels
[ObservableProperty]
private bool _isCalendarEnabled = true;
// Get rid of some of the items if we have too many.
// TODO: Get rid of some of the items if we have too many.
private const int maxDayRangeSize = 10;
private readonly ICalendarService _calendarService;
private readonly IAccountCalendarStateService _accountCalendarStateService;
private readonly IPreferencesService _preferencesService;
// Store latest rendered options.
@@ -49,13 +54,14 @@ namespace Wino.Calendar.ViewModels
public CalendarPageViewModel(IStatePersistanceService statePersistanceService,
ICalendarService calendarService,
IAccountCalendarStateService accountCalendarStateService,
IPreferencesService preferencesService)
{
StatePersistanceService = statePersistanceService;
_calendarService = calendarService;
_preferencesService = preferencesService;
_currentSettings = _preferencesService.GetCurrentCalendarSettings();
_calendarService = calendarService;
_accountCalendarStateService = accountCalendarStateService;
_preferencesService = preferencesService;
}
// TODO: Replace when calendar settings are updated.
@@ -70,10 +76,25 @@ namespace Wino.Calendar.ViewModels
};
}
public override void OnNavigatedFrom(NavigationMode mode, object parameters)
{
// Do not call base method because that will unregister messenger recipient.
// This is a singleton view model and should not be unregistered.
}
public override void OnNavigatedTo(NavigationMode mode, object parameters)
{
base.OnNavigatedTo(mode, parameters);
_currentSettings = _preferencesService.GetCurrentCalendarSettings();
}
partial void OnIsCalendarEnabledChanging(bool oldValue, bool newValue) => Messenger.Send(new CalendarEnableStatusChangedMessage(newValue));
private bool ShouldResetDayRanges(LoadCalendarMessage message)
{
if (message.ForceRedraw) return true;
// Never reset if the initiative is from the app.
if (message.CalendarInitInitiative == CalendarInitInitiative.App) return false;
@@ -168,14 +189,14 @@ namespace Wino.Calendar.ViewModels
DateRange flipLoadRange = null;
if (calendarInitInitiative == CalendarInitInitiative.User)
var initializedDateRange = GetLoadedDateRange();
if (calendarInitInitiative == CalendarInitInitiative.User || initializedDateRange == null)
{
flipLoadRange = strategy.GetRenderDateRange(displayDate, StatePersistanceService.DayDisplayCount);
}
else
{
var initializedDateRange = GetLoadedDateRange();
// App is trying to load.
// This should be based on direction. We'll load the next or previous range.
// DisplayDate is either the start or end date of the current visible range.
@@ -214,7 +235,7 @@ namespace Wino.Calendar.ViewModels
{
foreach (var day in renderModel.CalendarDays)
{
var events = await _calendarService.GetCalendarEventsAsync(Guid.Parse("13e8e385-a1bb-4764-95b4-757901cad35a"), day.Period.Start, day.Period.End).ConfigureAwait(false);
var events = await _calendarService.GetCalendarEventsAsync(Guid.Parse("9ead7613-dacb-4163-8d33-2e32e65008a1"), day.Period.Start, day.Period.End).ConfigureAwait(false);
foreach (var calendarItem in events)
{
@@ -343,6 +364,8 @@ namespace Wino.Calendar.ViewModels
var initializedDateRange = GetLoadedDateRange();
if (initializedDateRange == null) return false;
var selectedDate = message.DisplayDate;
return selectedDate >= initializedDateRange.StartDate && selectedDate <= initializedDateRange.EndDate;
@@ -391,67 +414,14 @@ namespace Wino.Calendar.ViewModels
}
}
protected override async void OnCalendarEventAdded(ICalendarItem calendarItem)
public void Receive(CalendarSettingsUpdatedMessage message)
{
base.OnCalendarEventAdded(calendarItem);
_currentSettings = _preferencesService.GetCurrentCalendarSettings();
// Test
//var eventDays = DayRanges.SelectMany(a => a.CalendarDays).Where(b => b.Period.Start.Date == calendarItem.StartTime.Date);
// 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.
//var beforeAllDay = new CalendarItem(calendarItem.StartTime.Date.AddHours(0), calendarItem.StartTime.Date.AddMinutes(30))
//{
// Title = "kj"
//};
//var allday = new CalendarItem(calendarItem.StartTime.Date.AddHours(1), calendarItem.StartTime.Date.AddHours(10).AddMinutes(59))
//{
// Title = "All day"
//};
//var test = new CalendarItem(calendarItem.StartTime.Date.AddHours(4), calendarItem.StartTime.Date.AddHours(4).AddMinutes(30))
//{
// Title = "test"
//};
//var hour = new CalendarItem(calendarItem.StartTime.Date.AddHours(7), calendarItem.StartTime.Date.AddHours(8))
//{
// Title = "1 h"
//};
//var hourandhalf = new CalendarItem(calendarItem.StartTime.Date.AddHours(7), calendarItem.StartTime.Date.AddHours(8).AddMinutes(30))
//{
// Title = "1.5 h"
//};
//var halfhour1 = new CalendarItem(calendarItem.StartTime.Date.AddHours(7), calendarItem.StartTime.Date.AddHours(7).AddMinutes(30))
//{
// Title = "30 min"
//};
//var halfhour2 = new CalendarItem(calendarItem.StartTime.Date.AddHours(7).AddMinutes(30), calendarItem.StartTime.Date.AddHours(8))
//{
// Title = "30 min"
//};
//var halfhour3 = new CalendarItem(calendarItem.StartTime.Date.AddHours(8), calendarItem.StartTime.Date.AddHours(8).AddMinutes(30))
//{
// Title = "30 min"
//};
//foreach (var day in eventDays)
//{
// await ExecuteUIThread(() =>
// {
// day.Events.Add(beforeAllDay);
// day.Events.Add(allday);
// day.Events.Add(hourandhalf);
// day.Events.Add(hour);
// day.Events.Add(halfhour1);
// day.Events.Add(halfhour2);
// day.Events.Add(halfhour3);
// day.Events.Add(test);
// });
//}
//return;
Messenger.Send(new LoadCalendarMessage(DateTime.UtcNow.Date, CalendarInitInitiative.App, true));
}
}
}

View File

@@ -0,0 +1,75 @@
using System;
using CommunityToolkit.Mvvm.ComponentModel;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Interfaces;
namespace Wino.Calendar.ViewModels.Data
{
public partial class AccountCalendarViewModel : ObservableObject, IAccountCalendar
{
public event EventHandler<AccountCalendarViewModel> CalendarSelectionStateChanged;
public MailAccount Account { get; }
public AccountCalendar AccountCalendar { get; }
public AccountCalendarViewModel(MailAccount account, AccountCalendar accountCalendar)
{
Account = account;
AccountCalendar = accountCalendar;
IsChecked = accountCalendar.IsExtended;
}
[ObservableProperty]
private bool _isChecked;
partial void OnIsCheckedChanged(bool value)
{
IsExtended = value;
CalendarSelectionStateChanged?.Invoke(this, this);
}
public string Name
{
get => AccountCalendar.Name;
set => SetProperty(AccountCalendar.Name, value, AccountCalendar, (u, n) => u.Name = n);
}
public string TextColorHex
{
get => AccountCalendar.TextColorHex;
set => SetProperty(AccountCalendar.TextColorHex, value, AccountCalendar, (u, t) => u.TextColorHex = t);
}
public string BackgroundColorHex
{
get => AccountCalendar.BackgroundColorHex;
set => SetProperty(AccountCalendar.BackgroundColorHex, value, AccountCalendar, (u, b) => u.BackgroundColorHex = b);
}
public bool IsExtended
{
get => AccountCalendar.IsExtended;
set => SetProperty(AccountCalendar.IsExtended, value, AccountCalendar, (u, i) => u.IsExtended = i);
}
public bool IsPrimary
{
get => AccountCalendar.IsPrimary;
set => SetProperty(AccountCalendar.IsPrimary, value, AccountCalendar, (u, i) => u.IsPrimary = i);
}
public Guid AccountId
{
get => AccountCalendar.AccountId;
set => SetProperty(AccountCalendar.AccountId, value, AccountCalendar, (u, a) => u.AccountId = a);
}
public string RemoteCalendarId
{
get => AccountCalendar.RemoteCalendarId;
set => SetProperty(AccountCalendar.RemoteCalendarId, value, AccountCalendar, (u, r) => u.RemoteCalendarId = r);
}
}
}

View File

@@ -0,0 +1,116 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using Wino.Core.Domain.Entities.Shared;
namespace Wino.Calendar.ViewModels.Data
{
public partial class GroupedAccountCalendarViewModel : ObservableObject
{
public MailAccount Account { get; }
public ObservableCollection<AccountCalendarViewModel> AccountCalendars { get; }
public GroupedAccountCalendarViewModel(MailAccount account, IEnumerable<AccountCalendarViewModel> calendarViewModels)
{
Account = account;
AccountCalendars = new ObservableCollection<AccountCalendarViewModel>(calendarViewModels);
ManageIsCheckedState();
foreach (var calendarViewModel in calendarViewModels)
{
calendarViewModel.PropertyChanged += CalendarPropertyChanged;
}
AccountCalendars.CollectionChanged += CalendarListUpdated;
}
private void CalendarListUpdated(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (AccountCalendarViewModel calendar in e.NewItems)
{
calendar.PropertyChanged += CalendarPropertyChanged;
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (AccountCalendarViewModel calendar in e.OldItems)
{
calendar.PropertyChanged -= CalendarPropertyChanged;
}
}
else if (e.Action == NotifyCollectionChangedAction.Reset)
{
foreach (AccountCalendarViewModel calendar in e.OldItems)
{
calendar.PropertyChanged -= CalendarPropertyChanged;
}
}
}
private void CalendarPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (sender is AccountCalendarViewModel viewModel)
{
if (e.PropertyName == nameof(AccountCalendarViewModel.IsChecked))
{
ManageIsCheckedState();
}
}
}
[ObservableProperty]
private bool _isExpanded = true;
[ObservableProperty]
private bool? isCheckedState = true;
private bool _isExternalPropChangeBlocked = false;
private void ManageIsCheckedState()
{
_isExternalPropChangeBlocked = true;
if (AccountCalendars.All(c => c.IsChecked))
{
IsCheckedState = true;
}
else if (AccountCalendars.All(c => !c.IsChecked))
{
IsCheckedState = false;
}
else
{
IsCheckedState = null;
}
_isExternalPropChangeBlocked = false;
}
partial void OnIsCheckedStateChanged(bool? newValue)
{
if (_isExternalPropChangeBlocked) return;
if (newValue == null)
{
// Only primary calendars must be checked.
foreach (var calendar in AccountCalendars)
{
calendar.IsChecked = calendar.IsPrimary;
}
}
else
{
foreach (var calendar in AccountCalendars)
{
calendar.IsChecked = newValue.GetValueOrDefault();
}
}
}
}
}

View File

@@ -0,0 +1,10 @@
using System.Collections.ObjectModel;
using Wino.Calendar.ViewModels.Data;
namespace Wino.Calendar.ViewModels.Interfaces
{
public interface IAccountCalendarStateService
{
ObservableCollection<GroupedAccountCalendarViewModel> GroupedAccountCalendars { get; }
}
}