Encapsulation of grouped account selection events and collective events.
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using Serilog;
|
||||||
using Wino.Calendar.ViewModels.Data;
|
using Wino.Calendar.ViewModels.Data;
|
||||||
using Wino.Calendar.ViewModels.Interfaces;
|
using Wino.Calendar.ViewModels.Interfaces;
|
||||||
using Wino.Core.Domain.Collections;
|
using Wino.Core.Domain.Collections;
|
||||||
@@ -65,6 +67,9 @@ namespace Wino.Calendar.ViewModels
|
|||||||
|
|
||||||
public bool IsVerticalCalendar => StatePersistenceService.CalendarDisplayType == CalendarDisplayType.Month;
|
public bool IsVerticalCalendar => StatePersistenceService.CalendarDisplayType == CalendarDisplayType.Month;
|
||||||
|
|
||||||
|
// For updating account calendars asynchronously.
|
||||||
|
private SemaphoreSlim _accountCalendarUpdateSemaphoreSlim = new(1);
|
||||||
|
|
||||||
public AppShellViewModel(IPreferencesService preferencesService,
|
public AppShellViewModel(IPreferencesService preferencesService,
|
||||||
IStatePersistanceService statePersistanceService,
|
IStatePersistanceService statePersistanceService,
|
||||||
IAccountService accountService,
|
IAccountService accountService,
|
||||||
@@ -77,6 +82,9 @@ namespace Wino.Calendar.ViewModels
|
|||||||
_calendarService = calendarService;
|
_calendarService = calendarService;
|
||||||
|
|
||||||
AccountCalendarStateService = accountCalendarStateService;
|
AccountCalendarStateService = accountCalendarStateService;
|
||||||
|
AccountCalendarStateService.AccountCalendarSelectionStateChanged += UpdateAccountCalendarRequested;
|
||||||
|
AccountCalendarStateService.CollectiveAccountGroupSelectionStateChanged += AccountCalendarStateCollectivelyChanged;
|
||||||
|
|
||||||
NavigationService = navigationService;
|
NavigationService = navigationService;
|
||||||
ServerConnectionManager = serverConnectionManager;
|
ServerConnectionManager = serverConnectionManager;
|
||||||
PreferencesService = preferencesService;
|
PreferencesService = preferencesService;
|
||||||
@@ -104,16 +112,33 @@ namespace Wino.Calendar.ViewModels
|
|||||||
UpdateDateNavigationHeaderItems();
|
UpdateDateNavigationHeaderItems();
|
||||||
|
|
||||||
await InitializeAccountCalendarsAsync();
|
await InitializeAccountCalendarsAsync();
|
||||||
|
|
||||||
|
TodayClicked();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddGroupedAccountCalendarViewModel(GroupedAccountCalendarViewModel groupedAccountCalendarViewModel)
|
private async void AccountCalendarStateCollectivelyChanged(object sender, GroupedAccountCalendarViewModel e)
|
||||||
{
|
{
|
||||||
foreach (var calendarViewModel in groupedAccountCalendarViewModel.AccountCalendars)
|
// When using three-state checkbox, multiple accounts will be selected/unselected at the same time.
|
||||||
{
|
// Reporting all these changes one by one to the UI is not efficient and may cause problems in the future.
|
||||||
calendarViewModel.CalendarSelectionStateChanged += UpdateAccountCalendarRequested;
|
|
||||||
}
|
|
||||||
|
|
||||||
AccountCalendarStateService.GroupedAccountCalendars.Add(groupedAccountCalendarViewModel);
|
// Update all calendar states at once.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _accountCalendarUpdateSemaphoreSlim.WaitAsync();
|
||||||
|
|
||||||
|
foreach (var calendar in e.AccountCalendars)
|
||||||
|
{
|
||||||
|
await _calendarService.UpdateAccountCalendarAsync(calendar.AccountCalendar).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Error while waiting for account calendar update semaphore.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_accountCalendarUpdateSemaphoreSlim.Release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void UpdateAccountCalendarRequested(object sender, AccountCalendarViewModel e)
|
private async void UpdateAccountCalendarRequested(object sender, AccountCalendarViewModel e)
|
||||||
@@ -121,7 +146,7 @@ namespace Wino.Calendar.ViewModels
|
|||||||
|
|
||||||
private async Task InitializeAccountCalendarsAsync()
|
private async Task InitializeAccountCalendarsAsync()
|
||||||
{
|
{
|
||||||
await Dispatcher.ExecuteOnUIThread(() => AccountCalendarStateService.GroupedAccountCalendars.Clear());
|
await Dispatcher.ExecuteOnUIThread(() => AccountCalendarStateService.ClearGroupedAccountCalendar());
|
||||||
|
|
||||||
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
|
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -141,7 +166,7 @@ namespace Wino.Calendar.ViewModels
|
|||||||
|
|
||||||
await Dispatcher.ExecuteOnUIThread(() =>
|
await Dispatcher.ExecuteOnUIThread(() =>
|
||||||
{
|
{
|
||||||
AddGroupedAccountCalendarViewModel(groupedAccountCalendarViewModel);
|
AccountCalendarStateService.AddGroupedAccountCalendar(groupedAccountCalendarViewModel);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,12 +258,12 @@ namespace Wino.Calendar.ViewModels
|
|||||||
private readonly IAccountService _accountService;
|
private readonly IAccountService _accountService;
|
||||||
private readonly ICalendarService _calendarService;
|
private readonly ICalendarService _calendarService;
|
||||||
|
|
||||||
public override void OnPageLoaded()
|
//public override void OnPageLoaded()
|
||||||
{
|
//{
|
||||||
base.OnPageLoaded();
|
// base.OnPageLoaded();
|
||||||
|
|
||||||
TodayClicked();
|
// TodayClicked();
|
||||||
}
|
//}
|
||||||
|
|
||||||
#region Commands
|
#region Commands
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
|||||||
using CommunityToolkit.Diagnostics;
|
using CommunityToolkit.Diagnostics;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using Wino.Calendar.ViewModels.Data;
|
||||||
using Wino.Calendar.ViewModels.Interfaces;
|
using Wino.Calendar.ViewModels.Interfaces;
|
||||||
using Wino.Core.Domain.Collections;
|
using Wino.Core.Domain.Collections;
|
||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
@@ -211,8 +212,6 @@ namespace Wino.Calendar.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Create day ranges for each flip item until we reach the total days to load.
|
// Create day ranges for each flip item until we reach the total days to load.
|
||||||
int totalFlipItemCount = (int)Math.Ceiling((double)flipLoadRange.TotalDays / eachFlipItemCount);
|
int totalFlipItemCount = (int)Math.Ceiling((double)flipLoadRange.TotalDays / eachFlipItemCount);
|
||||||
|
|
||||||
@@ -233,15 +232,7 @@ namespace Wino.Calendar.ViewModels
|
|||||||
|
|
||||||
foreach (var renderModel in renderModels)
|
foreach (var renderModel in renderModels)
|
||||||
{
|
{
|
||||||
foreach (var day in renderModel.CalendarDays)
|
await InitializeCalendarEventsAsync(renderModel).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)
|
|
||||||
{
|
|
||||||
day.EventsCollection.Add(calendarItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CalendarLoadDirection animationDirection = calendarLoadDirection;
|
CalendarLoadDirection animationDirection = calendarLoadDirection;
|
||||||
@@ -326,6 +317,42 @@ namespace Wino.Calendar.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task InitializeCalendarEventsAsync(DayRangeRenderModel dayRangeRenderModel)
|
||||||
|
{
|
||||||
|
// Load for each selected calendar from the state.
|
||||||
|
var checkedCalendarViewModels = _accountCalendarStateService.GroupedAccountCalendars
|
||||||
|
.SelectMany(a => a.AccountCalendars)
|
||||||
|
.Where(b => b.IsChecked);
|
||||||
|
|
||||||
|
foreach (var calendarViewModel in checkedCalendarViewModels)
|
||||||
|
{
|
||||||
|
// Check all the events for the given date range and calendar.
|
||||||
|
// Then find the day representation for all the events returned, and add to the collection.
|
||||||
|
|
||||||
|
var events = await _calendarService.GetCalendarEventsAsync(calendarViewModel,
|
||||||
|
dayRangeRenderModel.Period.Start,
|
||||||
|
dayRangeRenderModel.Period.End)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
foreach (var calendarItem in events)
|
||||||
|
{
|
||||||
|
var calendarDayModel = dayRangeRenderModel.CalendarDays.FirstOrDefault(a => a.RepresentingDate.Date == calendarItem.StartTime.Date);
|
||||||
|
|
||||||
|
if (calendarDayModel == null) continue;
|
||||||
|
|
||||||
|
var calendarItemViewModel = new CalendarItemViewModel(calendarItem);
|
||||||
|
|
||||||
|
await ExecuteUIThread(() =>
|
||||||
|
{
|
||||||
|
// TODO: EventsCollection should not take CalendarItem, but CalendarItemViewModel.
|
||||||
|
// Enforce it later on.
|
||||||
|
|
||||||
|
calendarDayModel.EventsCollection.Add(calendarItemViewModel);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task TryConsolidateItemsAsync()
|
private async Task TryConsolidateItemsAsync()
|
||||||
{
|
{
|
||||||
// Check if trimming is necessary
|
// Check if trimming is necessary
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ namespace Wino.Calendar.ViewModels.Data
|
|||||||
{
|
{
|
||||||
public partial class AccountCalendarViewModel : ObservableObject, IAccountCalendar
|
public partial class AccountCalendarViewModel : ObservableObject, IAccountCalendar
|
||||||
{
|
{
|
||||||
public event EventHandler<AccountCalendarViewModel> CalendarSelectionStateChanged;
|
|
||||||
|
|
||||||
public MailAccount Account { get; }
|
public MailAccount Account { get; }
|
||||||
public AccountCalendar AccountCalendar { get; }
|
public AccountCalendar AccountCalendar { get; }
|
||||||
|
|
||||||
@@ -24,11 +22,7 @@ namespace Wino.Calendar.ViewModels.Data
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _isChecked;
|
private bool _isChecked;
|
||||||
|
|
||||||
partial void OnIsCheckedChanged(bool value)
|
partial void OnIsCheckedChanged(bool value) => IsExtended = value;
|
||||||
{
|
|
||||||
IsExtended = value;
|
|
||||||
CalendarSelectionStateChanged?.Invoke(this, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Name
|
public string Name
|
||||||
{
|
{
|
||||||
@@ -71,5 +65,6 @@ namespace Wino.Calendar.ViewModels.Data
|
|||||||
get => AccountCalendar.RemoteCalendarId;
|
get => AccountCalendar.RemoteCalendarId;
|
||||||
set => SetProperty(AccountCalendar.RemoteCalendarId, value, AccountCalendar, (u, r) => u.RemoteCalendarId = r);
|
set => SetProperty(AccountCalendar.RemoteCalendarId, value, AccountCalendar, (u, r) => u.RemoteCalendarId = r);
|
||||||
}
|
}
|
||||||
|
public Guid Id { get => ((IAccountCalendar)AccountCalendar).Id; set => ((IAccountCalendar)AccountCalendar).Id = value; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Itenso.TimePeriod;
|
using Itenso.TimePeriod;
|
||||||
|
using Wino.Core.Domain.Entities.Calendar;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
|
||||||
namespace Wino.Calendar.ViewModels.Data
|
namespace Wino.Calendar.ViewModels.Data
|
||||||
{
|
{
|
||||||
public partial class CalendarItemViewModel : ObservableObject, ICalendarItem
|
public partial class CalendarItemViewModel : ObservableObject, ICalendarItem, ICalendarItemViewModel
|
||||||
{
|
{
|
||||||
public ICalendarItem CalendarItem { get; }
|
public CalendarItem CalendarItem { get; }
|
||||||
|
|
||||||
public string Title => CalendarItem.Title;
|
public string Title => CalendarItem.Title;
|
||||||
|
|
||||||
@@ -19,7 +20,9 @@ namespace Wino.Calendar.ViewModels.Data
|
|||||||
|
|
||||||
public TimeRange Period => CalendarItem.Period;
|
public TimeRange Period => CalendarItem.Period;
|
||||||
|
|
||||||
public CalendarItemViewModel(ICalendarItem calendarItem)
|
public IAccountCalendar AssignedCalendar => ((ICalendarItem)CalendarItem).AssignedCalendar;
|
||||||
|
|
||||||
|
public CalendarItemViewModel(CalendarItem calendarItem)
|
||||||
{
|
{
|
||||||
CalendarItem = calendarItem;
|
CalendarItem = calendarItem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -9,6 +10,9 @@ namespace Wino.Calendar.ViewModels.Data
|
|||||||
{
|
{
|
||||||
public partial class GroupedAccountCalendarViewModel : ObservableObject
|
public partial class GroupedAccountCalendarViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
|
public event EventHandler CollectiveSelectionStateChanged;
|
||||||
|
public event EventHandler<AccountCalendarViewModel> CalendarSelectionStateChanged;
|
||||||
|
|
||||||
public MailAccount Account { get; }
|
public MailAccount Account { get; }
|
||||||
public ObservableCollection<AccountCalendarViewModel> AccountCalendars { get; }
|
public ObservableCollection<AccountCalendarViewModel> AccountCalendars { get; }
|
||||||
|
|
||||||
@@ -59,6 +63,7 @@ namespace Wino.Calendar.ViewModels.Data
|
|||||||
if (e.PropertyName == nameof(AccountCalendarViewModel.IsChecked))
|
if (e.PropertyName == nameof(AccountCalendarViewModel.IsChecked))
|
||||||
{
|
{
|
||||||
ManageIsCheckedState();
|
ManageIsCheckedState();
|
||||||
|
UpdateCalendarCheckedState(viewModel, viewModel.IsChecked, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,6 +78,8 @@ namespace Wino.Calendar.ViewModels.Data
|
|||||||
|
|
||||||
private void ManageIsCheckedState()
|
private void ManageIsCheckedState()
|
||||||
{
|
{
|
||||||
|
if (_isExternalPropChangeBlocked) return;
|
||||||
|
|
||||||
_isExternalPropChangeBlocked = true;
|
_isExternalPropChangeBlocked = true;
|
||||||
|
|
||||||
if (AccountCalendars.All(c => c.IsChecked))
|
if (AccountCalendars.All(c => c.IsChecked))
|
||||||
@@ -95,22 +102,45 @@ namespace Wino.Calendar.ViewModels.Data
|
|||||||
{
|
{
|
||||||
if (_isExternalPropChangeBlocked) return;
|
if (_isExternalPropChangeBlocked) return;
|
||||||
|
|
||||||
|
// Update is triggered by user on the three-state checkbox.
|
||||||
|
// We should not report all changes one by one.
|
||||||
|
|
||||||
|
_isExternalPropChangeBlocked = true;
|
||||||
|
|
||||||
if (newValue == null)
|
if (newValue == null)
|
||||||
{
|
{
|
||||||
// Only primary calendars must be checked.
|
// Only primary calendars must be checked.
|
||||||
|
|
||||||
foreach (var calendar in AccountCalendars)
|
foreach (var calendar in AccountCalendars)
|
||||||
{
|
{
|
||||||
calendar.IsChecked = calendar.IsPrimary;
|
UpdateCalendarCheckedState(calendar, calendar.IsPrimary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var calendar in AccountCalendars)
|
foreach (var calendar in AccountCalendars)
|
||||||
{
|
{
|
||||||
calendar.IsChecked = newValue.GetValueOrDefault();
|
UpdateCalendarCheckedState(calendar, newValue.GetValueOrDefault());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isExternalPropChangeBlocked = false;
|
||||||
|
|
||||||
|
CollectiveSelectionStateChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCalendarCheckedState(AccountCalendarViewModel accountCalendarViewModel, bool newValue, bool ignoreValueCheck = false)
|
||||||
|
{
|
||||||
|
var currentValue = accountCalendarViewModel.IsChecked;
|
||||||
|
|
||||||
|
if (currentValue == newValue && !ignoreValueCheck) return;
|
||||||
|
|
||||||
|
accountCalendarViewModel.IsChecked = newValue;
|
||||||
|
|
||||||
|
// No need to report.
|
||||||
|
if (_isExternalPropChangeBlocked == true) return;
|
||||||
|
|
||||||
|
CalendarSelectionStateChanged?.Invoke(this, accountCalendarViewModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using Wino.Calendar.ViewModels.Data;
|
using Wino.Calendar.ViewModels.Data;
|
||||||
|
|
||||||
namespace Wino.Calendar.ViewModels.Interfaces
|
namespace Wino.Calendar.ViewModels.Interfaces
|
||||||
{
|
{
|
||||||
public interface IAccountCalendarStateService
|
public interface IAccountCalendarStateService
|
||||||
{
|
{
|
||||||
ObservableCollection<GroupedAccountCalendarViewModel> GroupedAccountCalendars { get; }
|
ReadOnlyObservableCollection<GroupedAccountCalendarViewModel> GroupedAccountCalendars { get; }
|
||||||
|
|
||||||
|
event EventHandler<GroupedAccountCalendarViewModel> CollectiveAccountGroupSelectionStateChanged;
|
||||||
|
event EventHandler<AccountCalendarViewModel> AccountCalendarSelectionStateChanged;
|
||||||
|
|
||||||
|
public void AddGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar);
|
||||||
|
public void RemoveGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar);
|
||||||
|
public void ClearGroupedAccountCalendar();
|
||||||
|
|
||||||
|
public void AddAccountCalendar(AccountCalendarViewModel accountCalendar);
|
||||||
|
public void RemoveAccountCalendar(AccountCalendarViewModel accountCalendar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,91 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Wino.Calendar.ViewModels.Data;
|
using Wino.Calendar.ViewModels.Data;
|
||||||
using Wino.Calendar.ViewModels.Interfaces;
|
using Wino.Calendar.ViewModels.Interfaces;
|
||||||
|
|
||||||
namespace Wino.Calendar.Services
|
namespace Wino.Calendar.Services
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Encapsulated state manager for collectively managing the state of account calendars.
|
||||||
|
/// Callers must react to the events to update their state only from this service.
|
||||||
|
/// </summary>
|
||||||
public partial class AccountCalendarStateService : ObservableObject, IAccountCalendarStateService
|
public partial class AccountCalendarStateService : ObservableObject, IAccountCalendarStateService
|
||||||
{
|
{
|
||||||
|
public event EventHandler<GroupedAccountCalendarViewModel> CollectiveAccountGroupSelectionStateChanged;
|
||||||
|
public event EventHandler<AccountCalendarViewModel> AccountCalendarSelectionStateChanged;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private ObservableCollection<GroupedAccountCalendarViewModel> _groupedAccountCalendars = new ObservableCollection<GroupedAccountCalendarViewModel>();
|
private ReadOnlyObservableCollection<GroupedAccountCalendarViewModel> groupedAccountCalendars;
|
||||||
|
|
||||||
|
private ObservableCollection<GroupedAccountCalendarViewModel> _internalGroupedAccountCalendars = new ObservableCollection<GroupedAccountCalendarViewModel>();
|
||||||
|
|
||||||
|
public AccountCalendarStateService()
|
||||||
|
{
|
||||||
|
GroupedAccountCalendars = new ReadOnlyObservableCollection<GroupedAccountCalendarViewModel>(_internalGroupedAccountCalendars);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SingleGroupCalendarCollectiveStateChanged(object sender, EventArgs e)
|
||||||
|
=> CollectiveAccountGroupSelectionStateChanged?.Invoke(this, sender as GroupedAccountCalendarViewModel);
|
||||||
|
|
||||||
|
private void SingleCalendarSelectionStateChanged(object sender, AccountCalendarViewModel e)
|
||||||
|
=> AccountCalendarSelectionStateChanged?.Invoke(this, e);
|
||||||
|
|
||||||
|
public void AddGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar)
|
||||||
|
{
|
||||||
|
groupedAccountCalendar.CalendarSelectionStateChanged += SingleCalendarSelectionStateChanged;
|
||||||
|
groupedAccountCalendar.CollectiveSelectionStateChanged += SingleGroupCalendarCollectiveStateChanged;
|
||||||
|
|
||||||
|
_internalGroupedAccountCalendars.Add(groupedAccountCalendar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveGroupedAccountCalendar(GroupedAccountCalendarViewModel groupedAccountCalendar)
|
||||||
|
{
|
||||||
|
groupedAccountCalendar.CalendarSelectionStateChanged -= SingleCalendarSelectionStateChanged;
|
||||||
|
groupedAccountCalendar.CollectiveSelectionStateChanged -= SingleGroupCalendarCollectiveStateChanged;
|
||||||
|
|
||||||
|
_internalGroupedAccountCalendars.Remove(groupedAccountCalendar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearGroupedAccountCalendar()
|
||||||
|
{
|
||||||
|
foreach (var groupedAccountCalendar in _internalGroupedAccountCalendars)
|
||||||
|
{
|
||||||
|
RemoveGroupedAccountCalendar(groupedAccountCalendar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddAccountCalendar(AccountCalendarViewModel accountCalendar)
|
||||||
|
{
|
||||||
|
// Find the group that this calendar belongs to.
|
||||||
|
var group = _internalGroupedAccountCalendars.FirstOrDefault(g => g.Account.Id == accountCalendar.Account.Id);
|
||||||
|
|
||||||
|
if (group == null)
|
||||||
|
{
|
||||||
|
// If the group doesn't exist, create it.
|
||||||
|
group = new GroupedAccountCalendarViewModel(accountCalendar.Account, new[] { accountCalendar });
|
||||||
|
AddGroupedAccountCalendar(group);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
group.AccountCalendars.Add(accountCalendar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAccountCalendar(AccountCalendarViewModel accountCalendar)
|
||||||
|
{
|
||||||
|
var group = _internalGroupedAccountCalendars.FirstOrDefault(g => g.Account.Id == accountCalendar.Account.Id);
|
||||||
|
|
||||||
|
// We don't expect but just in case.
|
||||||
|
if (group == null) return;
|
||||||
|
|
||||||
|
group.AccountCalendars.Remove(accountCalendar);
|
||||||
|
|
||||||
|
if (group.AccountCalendars.Count == 0)
|
||||||
|
{
|
||||||
|
RemoveGroupedAccountCalendar(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
|
||||||
namespace Wino.Core.Domain.Collections
|
namespace Wino.Core.Domain.Collections
|
||||||
{
|
{
|
||||||
|
// TODO: Could be read-only collection in the MVVM package.
|
||||||
public class CalendarEventCollection : ObservableRangeCollection<ICalendarItem>
|
public class CalendarEventCollection : ObservableRangeCollection<ICalendarItem>
|
||||||
{
|
{
|
||||||
public ObservableCollection<ICalendarItem> AllDayEvents { get; } = new ObservableCollection<ICalendarItem>();
|
public ObservableCollection<ICalendarItem> AllDayEvents { get; } = new ObservableCollection<ICalendarItem>();
|
||||||
public new void Add(ICalendarItem calendarItem)
|
public new void Add(ICalendarItem calendarItem)
|
||||||
{
|
{
|
||||||
|
if (calendarItem is not ICalendarItemViewModel)
|
||||||
|
throw new ArgumentException("CalendarItem must be of type ICalendarItemViewModel", nameof(calendarItem));
|
||||||
|
|
||||||
base.Add(calendarItem);
|
base.Add(calendarItem);
|
||||||
|
|
||||||
if (calendarItem.Period.Duration.TotalMinutes == 1440)
|
if (calendarItem.Period.Duration.TotalMinutes == 1440)
|
||||||
@@ -18,6 +23,9 @@ namespace Wino.Core.Domain.Collections
|
|||||||
|
|
||||||
public new void Remove(ICalendarItem calendarItem)
|
public new void Remove(ICalendarItem calendarItem)
|
||||||
{
|
{
|
||||||
|
if (calendarItem is not ICalendarItemViewModel)
|
||||||
|
throw new ArgumentException("CalendarItem must be of type ICalendarItemViewModel", nameof(calendarItem));
|
||||||
|
|
||||||
base.Remove(calendarItem);
|
base.Remove(calendarItem);
|
||||||
|
|
||||||
if (calendarItem.Period.Duration.TotalMinutes == 1440)
|
if (calendarItem.Period.Duration.TotalMinutes == 1440)
|
||||||
|
|||||||
@@ -24,5 +24,8 @@ namespace Wino.Core.Domain.Entities.Calendar
|
|||||||
|
|
||||||
[Ignore]
|
[Ignore]
|
||||||
public TimeRange Period => new TimeRange(StartTime.Date, StartTime.Date.AddMinutes(DurationInMinutes));
|
public TimeRange Period => new TimeRange(StartTime.Date, StartTime.Date.AddMinutes(DurationInMinutes));
|
||||||
|
|
||||||
|
[Ignore]
|
||||||
|
public IAccountCalendar AssignedCalendar { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,5 +11,6 @@ namespace Wino.Core.Domain.Interfaces
|
|||||||
Guid AccountId { get; set; }
|
Guid AccountId { get; set; }
|
||||||
string RemoteCalendarId { get; set; }
|
string RemoteCalendarId { get; set; }
|
||||||
bool IsExtended { get; set; }
|
bool IsExtended { get; set; }
|
||||||
|
Guid Id { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,5 +10,6 @@ namespace Wino.Core.Domain.Interfaces
|
|||||||
DateTimeOffset StartTime { get; }
|
DateTimeOffset StartTime { get; }
|
||||||
int DurationInMinutes { get; }
|
int DurationInMinutes { get; }
|
||||||
TimeRange Period { get; }
|
TimeRange Period { get; }
|
||||||
|
IAccountCalendar AssignedCalendar { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
Wino.Core.Domain/Interfaces/ICalendarItemViewModel.cs
Normal file
7
Wino.Core.Domain/Interfaces/ICalendarItemViewModel.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Wino.Core.Domain.Interfaces
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Temporarily to enforce CalendarItemViewModel. Used in CalendarEventCollection.
|
||||||
|
/// </summary>
|
||||||
|
public interface ICalendarItemViewModel { }
|
||||||
|
}
|
||||||
@@ -14,6 +14,6 @@ namespace Wino.Core.Domain.Interfaces
|
|||||||
Task InsertAccountCalendarAsync(AccountCalendar accountCalendar);
|
Task InsertAccountCalendarAsync(AccountCalendar accountCalendar);
|
||||||
Task UpdateAccountCalendarAsync(AccountCalendar accountCalendar);
|
Task UpdateAccountCalendarAsync(AccountCalendar accountCalendar);
|
||||||
Task CreateNewCalendarItemAsync(CalendarItem calendarItem, List<CalendarEventAttendee> attendees);
|
Task CreateNewCalendarItemAsync(CalendarItem calendarItem, List<CalendarEventAttendee> attendees);
|
||||||
Task<List<ICalendarItem>> GetCalendarEventsAsync(Guid calendarId, DateTime rangeStart, DateTime rangeEnd);
|
Task<List<CalendarItem>> GetCalendarEventsAsync(IAccountCalendar calendar, DateTime rangeStart, DateTime rangeEnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace Wino.Core.Domain.Models.Calendar
|
|||||||
{
|
{
|
||||||
public TimeRange Period { get; }
|
public TimeRange Period { get; }
|
||||||
public CalendarEventCollection EventsCollection { get; } = new CalendarEventCollection();
|
public CalendarEventCollection EventsCollection { get; } = new CalendarEventCollection();
|
||||||
|
|
||||||
public CalendarDayModel(DateTime representingDate, CalendarRenderOptions calendarRenderOptions)
|
public CalendarDayModel(DateTime representingDate, CalendarRenderOptions calendarRenderOptions)
|
||||||
{
|
{
|
||||||
RepresentingDate = representingDate;
|
RepresentingDate = representingDate;
|
||||||
|
|||||||
@@ -71,16 +71,18 @@ namespace Wino.Services
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<ICalendarItem>> GetCalendarEventsAsync(Guid calendarId, DateTime rangeStart, DateTime rangeEnd)
|
public async Task<List<CalendarItem>> GetCalendarEventsAsync(IAccountCalendar calendar, DateTime rangeStart, DateTime rangeEnd)
|
||||||
{
|
{
|
||||||
// TODO: We might need to implement caching here.
|
// TODO: We might need to implement caching here.
|
||||||
// I don't know how much of the events we'll have in total, but this logic scans all events every time.
|
// I don't know how much of the events we'll have in total, but this logic scans all events every time.
|
||||||
|
|
||||||
var accountEvents = await Connection.Table<CalendarItem>().Where(x => x.CalendarId == calendarId).ToListAsync();
|
var accountEvents = await Connection.Table<CalendarItem>().Where(x => x.CalendarId == calendar.Id).ToListAsync();
|
||||||
var result = new List<ICalendarItem>();
|
var result = new List<CalendarItem>();
|
||||||
|
|
||||||
foreach (var ev in accountEvents)
|
foreach (var ev in accountEvents)
|
||||||
{
|
{
|
||||||
|
ev.AssignedCalendar = calendar;
|
||||||
|
|
||||||
// Parse recurrence rules
|
// Parse recurrence rules
|
||||||
var calendarEvent = new Ical.Net.CalendarComponents.CalendarEvent
|
var calendarEvent = new Ical.Net.CalendarComponents.CalendarEvent
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,16 +41,17 @@ namespace Wino.Core.SourceGeneration.Translator
|
|||||||
predicate: static (node, _) => node is ClassDeclarationSyntax,
|
predicate: static (node, _) => node is ClassDeclarationSyntax,
|
||||||
transform: static (context, _) => (ClassDeclarationSyntax)context.TargetNode);
|
transform: static (context, _) => (ClassDeclarationSyntax)context.TargetNode);
|
||||||
|
|
||||||
// Get the JSON schema
|
// Get the JSON schema and track changes
|
||||||
var jsonSchema = context.AdditionalTextsProvider
|
var jsonSchema = context.AdditionalTextsProvider
|
||||||
.Where(static file => file.Path.EndsWith("en_US\\resources.json"))
|
.Where(static file => file.Path.EndsWith("en_US\\resources.json"))
|
||||||
.Select((text, _) => (text, text.GetText()))
|
.Select((text, _) => (text, text.GetText()))
|
||||||
.Collect();
|
.Collect()
|
||||||
|
.WithTrackingName("JsonSchema");
|
||||||
|
|
||||||
// Combine the JSON schema with the marked classes
|
// Combine the JSON schema with the marked classes
|
||||||
var combined = classDeclarations.Combine(jsonSchema);
|
var combined = classDeclarations.Combine(jsonSchema);
|
||||||
|
|
||||||
// Generate the source
|
// Generate the source only when the JSON schema changes
|
||||||
context.RegisterSourceOutput(combined,
|
context.RegisterSourceOutput(combined,
|
||||||
static (spc, source) => Execute(source.Left, source.Right, spc));
|
static (spc, source) => Execute(source.Left, source.Right, spc));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user