Proper handling of DateTimeOffset, support for Multi-Day events and reacting to adding/removing events for the days.
This commit is contained in:
@@ -258,13 +258,6 @@ namespace Wino.Calendar.ViewModels
|
|||||||
private readonly IAccountService _accountService;
|
private readonly IAccountService _accountService;
|
||||||
private readonly ICalendarService _calendarService;
|
private readonly ICalendarService _calendarService;
|
||||||
|
|
||||||
//public override void OnPageLoaded()
|
|
||||||
//{
|
|
||||||
// base.OnPageLoaded();
|
|
||||||
|
|
||||||
// TodayClicked();
|
|
||||||
//}
|
|
||||||
|
|
||||||
#region Commands
|
#region Commands
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
@@ -341,7 +334,5 @@ namespace Wino.Calendar.ViewModels
|
|||||||
=> await ExecuteUIThread(() => IsCalendarEnabled = message.IsEnabled);
|
=> await ExecuteUIThread(() => IsCalendarEnabled = message.IsEnabled);
|
||||||
|
|
||||||
public void Receive(NavigateManageAccountsRequested message) => SelectedMenuItemIndex = 1;
|
public void Receive(NavigateManageAccountsRequested message) => SelectedMenuItemIndex = 1;
|
||||||
|
|
||||||
//public void Receive(GoToCalendarDayMessage message) => SelectedMenuItemIndex = -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ 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 MoreLinq;
|
||||||
using Serilog;
|
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;
|
||||||
|
using Wino.Core.Domain.Entities.Calendar;
|
||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
using Wino.Core.Domain.Models.Calendar;
|
using Wino.Core.Domain.Models.Calendar;
|
||||||
@@ -70,20 +72,16 @@ namespace Wino.Calendar.ViewModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void AccountCalendarStateCollectivelyChanged(object sender, GroupedAccountCalendarViewModel e)
|
private void AccountCalendarStateCollectivelyChanged(object sender, GroupedAccountCalendarViewModel e)
|
||||||
{
|
=> FilterActiveCalendars(DayRanges);
|
||||||
// For all date ranges, update the events.
|
|
||||||
foreach (var dayRange in DayRanges)
|
|
||||||
{
|
|
||||||
_ = InitializeCalendarEventsForDayRangeAsync(dayRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAccountCalendarRequested(object sender, AccountCalendarViewModel e)
|
private void UpdateAccountCalendarRequested(object sender, AccountCalendarViewModel e)
|
||||||
|
=> FilterActiveCalendars(DayRanges);
|
||||||
|
|
||||||
|
private void FilterActiveCalendars(IEnumerable<DayRangeRenderModel> dayRangeRenderModels)
|
||||||
{
|
{
|
||||||
foreach (var range in DayRanges)
|
var days = dayRangeRenderModels.SelectMany(a => a.CalendarDays);
|
||||||
{
|
|
||||||
_ = InitializeCalendarEventsForDayRangeAsync(range);
|
days.ForEach(a => a.EventsCollection.FilterByCalendars(_accountCalendarStateService.ActiveCalendars.Select(a => a.Id)));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Replace when calendar settings are updated.
|
// TODO: Replace when calendar settings are updated.
|
||||||
@@ -297,6 +295,9 @@ namespace Wino.Calendar.ViewModels
|
|||||||
await InitializeCalendarEventsForDayRangeAsync(renderModel).ConfigureAwait(false);
|
await InitializeCalendarEventsForDayRangeAsync(renderModel).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter by active calendars. This is a quick operation, and things are not on the UI yet.
|
||||||
|
FilterActiveCalendars(renderModels);
|
||||||
|
|
||||||
CalendarLoadDirection animationDirection = calendarLoadDirection;
|
CalendarLoadDirection animationDirection = calendarLoadDirection;
|
||||||
|
|
||||||
bool removeCurrent = calendarLoadDirection == CalendarLoadDirection.Replace;
|
bool removeCurrent = calendarLoadDirection == CalendarLoadDirection.Replace;
|
||||||
@@ -372,22 +373,39 @@ namespace Wino.Calendar.ViewModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO...
|
// TODO...
|
||||||
private async void EventsUpdatedInDayHeader(object sender, CalendarDayModel e)
|
private void EventsUpdatedInDayHeader(object sender, CalendarDayModel e)
|
||||||
{
|
{
|
||||||
// Find the day range model that contains the day model.
|
|
||||||
// TODO: Maybe optimize by just updating the day?
|
|
||||||
|
|
||||||
if (sender is DayRangeRenderModel dayRangeRenderModel)
|
}
|
||||||
{
|
|
||||||
await InitializeCalendarEventsForDayRangeAsync(dayRangeRenderModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
protected override async void OnCalendarEventAdded(CalendarItem calendarItem)
|
||||||
|
{
|
||||||
|
base.OnCalendarEventAdded(calendarItem);
|
||||||
|
|
||||||
//var dayRange = DayRanges.FirstOrDefault(a => a.CalendarDays.Contains(e));
|
// test
|
||||||
|
var calendar = await _calendarService.GetAccountCalendarAsync(Guid.Parse("9ead7613-dacb-4163-8d33-2e32e65008a1"));
|
||||||
|
|
||||||
//if (dayRange == null) return;
|
calendarItem.AssignedCalendar = calendar;
|
||||||
|
// Check if event falls into the current date range.
|
||||||
|
|
||||||
//await InitializeCalendarEventsForDayRangeAsync(dayRange);
|
var loadedDateRange = GetLoadedDateRange();
|
||||||
|
|
||||||
|
if (loadedDateRange == null) return;
|
||||||
|
|
||||||
|
// Check whether this event falls into any of the loaded date ranges.
|
||||||
|
|
||||||
|
//if (calendarItem.Period.Start >= loadedDateRange.StartDate && calendarItem.Period.Start.Date <= loadedDateRange.EndDate)
|
||||||
|
//{
|
||||||
|
// // Find the day representation for the event.
|
||||||
|
// var dayModel = DayRanges.SelectMany(a => a.CalendarDays).FirstOrDefault(a => a.RepresentingDate.Date == calendarItem.Period.Start.Date);
|
||||||
|
// if (dayModel == null) return;
|
||||||
|
|
||||||
|
// var calendarItemViewModel = new CalendarItemViewModel(calendarItem);
|
||||||
|
// await ExecuteUIThread(() =>
|
||||||
|
// {
|
||||||
|
// dayModel.EventsCollection.AddCalendarItem(calendarItemViewModel);
|
||||||
|
// });
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task InitializeCalendarEventsForDayRangeAsync(DayRangeRenderModel dayRangeRenderModel)
|
private async Task InitializeCalendarEventsForDayRangeAsync(DayRangeRenderModel dayRangeRenderModel)
|
||||||
@@ -401,38 +419,32 @@ namespace Wino.Calendar.ViewModels
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load for each selected calendar from the state.
|
// Initialization is done for all calendars, regardless whether they are actively selected or not.
|
||||||
var checkedCalendarViewModels = _accountCalendarStateService.GroupedAccountCalendars
|
// This is because the filtering is cached internally of the calendar items in CalendarEventCollection.
|
||||||
.SelectMany(a => a.AccountCalendars)
|
var allCalendars = _accountCalendarStateService.GroupedAccountCalendars.SelectMany(a => a.AccountCalendars);
|
||||||
.Where(b => b.IsChecked);
|
|
||||||
|
|
||||||
foreach (var calendarViewModel in checkedCalendarViewModels)
|
foreach (var calendarViewModel in allCalendars)
|
||||||
{
|
{
|
||||||
// Check all the events for the given date range and calendar.
|
// 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.
|
// Then find the day representation for all the events returned, and add to the collection.
|
||||||
|
|
||||||
var events = await _calendarService.GetCalendarEventsAsync(calendarViewModel,
|
var events = await _calendarService.GetCalendarEventsAsync(calendarViewModel, dayRangeRenderModel).ConfigureAwait(false);
|
||||||
dayRangeRenderModel.Period.Start,
|
|
||||||
dayRangeRenderModel.Period.End)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
var groupedEvents = events.GroupBy(a => a.StartTime.Date);
|
foreach (var @event in events)
|
||||||
|
|
||||||
foreach (var group in groupedEvents)
|
|
||||||
{
|
{
|
||||||
var startDate = group.Key;
|
// Find the days that the event falls into.
|
||||||
|
// TODO: Multi-day events are not fully supported yet.
|
||||||
|
|
||||||
var calendarDayModel = dayRangeRenderModel.CalendarDays.FirstOrDefault(a => a.RepresentingDate.Date == startDate);
|
var allDaysForEvent = dayRangeRenderModel.CalendarDays.Where(a => a.Period.OverlapsWith(@event.Period));
|
||||||
|
|
||||||
if (calendarDayModel == null) continue;
|
foreach (var calendarDay in allDaysForEvent)
|
||||||
|
|
||||||
var calendarItemViewModels = group.Select(a => new CalendarItemViewModel(a));
|
|
||||||
|
|
||||||
await ExecuteUIThread(() =>
|
|
||||||
{
|
{
|
||||||
// Use range-based add for performance.
|
var calendarItemViewModel = new CalendarItemViewModel(@event);
|
||||||
calendarDayModel.EventsCollection.AddCalendarItemRange(calendarItemViewModels);
|
await ExecuteUIThread(() =>
|
||||||
});
|
{
|
||||||
|
calendarDay.EventsCollection.AddCalendarItem(calendarItemViewModel);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -532,7 +544,7 @@ namespace Wino.Calendar.ViewModels
|
|||||||
// TODO: This might need throttling due to slider in the settings page for hour height.
|
// 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.
|
// or make sure the slider does not update on each tick but on focus lost.
|
||||||
|
|
||||||
Messenger.Send(new LoadCalendarMessage(DateTime.UtcNow.Date, CalendarInitInitiative.App, true));
|
// Messenger.Send(new LoadCalendarMessage(DateTime.UtcNow.Date, CalendarInitInitiative.App, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,15 @@ namespace Wino.Calendar.ViewModels.Data
|
|||||||
|
|
||||||
public Guid Id => CalendarItem.Id;
|
public Guid Id => CalendarItem.Id;
|
||||||
|
|
||||||
public DateTimeOffset StartTime => CalendarItem.StartTime;
|
public IAccountCalendar AssignedCalendar => CalendarItem.AssignedCalendar;
|
||||||
|
|
||||||
public int DurationInMinutes => CalendarItem.DurationInMinutes;
|
public DateTime StartDate { get => CalendarItem.StartDate; set => CalendarItem.StartDate = value; }
|
||||||
|
|
||||||
public TimeRange Period => CalendarItem.Period;
|
public DateTime EndDate => CalendarItem.EndDate;
|
||||||
|
|
||||||
public IAccountCalendar AssignedCalendar => ((ICalendarItem)CalendarItem).AssignedCalendar;
|
public double DurationInSeconds { get => CalendarItem.DurationInSeconds; set => CalendarItem.DurationInSeconds = value; }
|
||||||
|
|
||||||
|
public ITimePeriod Period => CalendarItem.Period;
|
||||||
|
|
||||||
public CalendarItemViewModel(CalendarItem calendarItem)
|
public CalendarItemViewModel(CalendarItem calendarItem)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using Wino.Calendar.ViewModels.Data;
|
using Wino.Calendar.ViewModels.Data;
|
||||||
|
|
||||||
@@ -17,5 +18,10 @@ namespace Wino.Calendar.ViewModels.Interfaces
|
|||||||
|
|
||||||
public void AddAccountCalendar(AccountCalendarViewModel accountCalendar);
|
public void AddAccountCalendar(AccountCalendarViewModel accountCalendar);
|
||||||
public void RemoveAccountCalendar(AccountCalendarViewModel accountCalendar);
|
public void RemoveAccountCalendar(AccountCalendarViewModel accountCalendar);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enumeration of currently selected calendars.
|
||||||
|
/// </summary>
|
||||||
|
IEnumerable<AccountCalendarViewModel> ActiveCalendars { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using Windows.UI.Xaml;
|
||||||
using Windows.UI.Xaml;
|
|
||||||
using Windows.UI.Xaml.Controls;
|
using Windows.UI.Xaml.Controls;
|
||||||
using Wino.Core.Domain.Collections;
|
using Wino.Core.Domain.Collections;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
@@ -76,9 +75,6 @@ namespace Wino.Calendar.Controls
|
|||||||
collection.CalendarItemAdded += SingleEventUpdated;
|
collection.CalendarItemAdded += SingleEventUpdated;
|
||||||
collection.CalendarItemRemoved += SingleEventUpdated;
|
collection.CalendarItemRemoved += SingleEventUpdated;
|
||||||
|
|
||||||
collection.CalendarItemRangeAdded += CollectionOfEventsUpdated;
|
|
||||||
collection.CalendarItemRangeRemoved += CollectionOfEventsUpdated;
|
|
||||||
|
|
||||||
collection.CalendarItemsCleared += EventsCleared;
|
collection.CalendarItemsCleared += EventsCleared;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,14 +83,10 @@ namespace Wino.Calendar.Controls
|
|||||||
collection.CalendarItemAdded -= SingleEventUpdated;
|
collection.CalendarItemAdded -= SingleEventUpdated;
|
||||||
collection.CalendarItemRemoved -= SingleEventUpdated;
|
collection.CalendarItemRemoved -= SingleEventUpdated;
|
||||||
|
|
||||||
collection.CalendarItemRangeAdded -= CollectionOfEventsUpdated;
|
|
||||||
collection.CalendarItemRangeRemoved -= CollectionOfEventsUpdated;
|
|
||||||
|
|
||||||
collection.CalendarItemsCleared -= EventsCleared;
|
collection.CalendarItemsCleared -= EventsCleared;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SingleEventUpdated(object sender, ICalendarItem calendarItem) => UpdateCollectionVisuals();
|
private void SingleEventUpdated(object sender, ICalendarItem calendarItem) => UpdateCollectionVisuals();
|
||||||
private void CollectionOfEventsUpdated(object sender, List<ICalendarItem> calendarItems) => UpdateCollectionVisuals();
|
|
||||||
private void EventsCleared(object sender, System.EventArgs e) => UpdateCollectionVisuals();
|
private void EventsCleared(object sender, System.EventArgs e) => UpdateCollectionVisuals();
|
||||||
|
|
||||||
private void UpdateCollectionVisuals()
|
private void UpdateCollectionVisuals()
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Diagnostics;
|
|
||||||
using Windows.UI.Xaml;
|
using Windows.UI.Xaml;
|
||||||
using Windows.UI.Xaml.Controls;
|
using Windows.UI.Xaml.Controls;
|
||||||
using Wino.Calendar.Args;
|
using Wino.Calendar.Args;
|
||||||
@@ -95,8 +94,6 @@ namespace Wino.Calendar.Controls
|
|||||||
{
|
{
|
||||||
if (canvas == null) return;
|
if (canvas == null) return;
|
||||||
|
|
||||||
Debug.WriteLine("Deregister active canvas.");
|
|
||||||
|
|
||||||
canvas.SelectedDateTime = null;
|
canvas.SelectedDateTime = null;
|
||||||
canvas.TimelineCellSelected -= ActiveTimelineCellSelected;
|
canvas.TimelineCellSelected -= ActiveTimelineCellSelected;
|
||||||
canvas.TimelineCellUnselected -= ActiveTimelineCellUnselected;
|
canvas.TimelineCellUnselected -= ActiveTimelineCellUnselected;
|
||||||
@@ -106,8 +103,6 @@ namespace Wino.Calendar.Controls
|
|||||||
{
|
{
|
||||||
if (canvas == null) return;
|
if (canvas == null) return;
|
||||||
|
|
||||||
Debug.WriteLine("Register new canvas.");
|
|
||||||
|
|
||||||
canvas.SelectedDateTime = null;
|
canvas.SelectedDateTime = null;
|
||||||
canvas.TimelineCellSelected += ActiveTimelineCellSelected;
|
canvas.TimelineCellSelected += ActiveTimelineCellSelected;
|
||||||
canvas.TimelineCellUnselected += ActiveTimelineCellUnselected;
|
canvas.TimelineCellUnselected += ActiveTimelineCellUnselected;
|
||||||
@@ -127,13 +122,6 @@ namespace Wino.Calendar.Controls
|
|||||||
InternalFlipView = GetTemplateChild(PART_WinoFlipView) as WinoCalendarFlipView;
|
InternalFlipView = GetTemplateChild(PART_WinoFlipView) as WinoCalendarFlipView;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FlipViewsActiveTimelineCanvasChanged(object sender, WinoDayTimelineCanvas e)
|
|
||||||
{
|
|
||||||
ActiveCanvas = e;
|
|
||||||
|
|
||||||
SelectedFlipViewDayRange = InternalFlipView.SelectedItem as DayRangeRenderModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ActiveTimelineCellUnselected(object sender, TimelineCellUnselectedArgs e)
|
private void ActiveTimelineCellUnselected(object sender, TimelineCellUnselectedArgs e)
|
||||||
=> TimelineCellUnselected?.Invoke(this, e);
|
=> TimelineCellUnselected?.Invoke(this, e);
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Itenso.TimePeriod;
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
using Windows.UI.Xaml;
|
using Windows.UI.Xaml;
|
||||||
using Windows.UI.Xaml.Controls;
|
using Windows.UI.Xaml.Controls;
|
||||||
@@ -21,6 +22,14 @@ namespace Wino.Calendar.Controls
|
|||||||
|
|
||||||
public static readonly DependencyProperty EventItemMarginProperty = DependencyProperty.Register(nameof(EventItemMargin), typeof(Thickness), typeof(WinoCalendarPanel), new PropertyMetadata(new Thickness(0, 0, 0, 0)));
|
public static readonly DependencyProperty EventItemMarginProperty = DependencyProperty.Register(nameof(EventItemMargin), typeof(Thickness), typeof(WinoCalendarPanel), new PropertyMetadata(new Thickness(0, 0, 0, 0)));
|
||||||
public static readonly DependencyProperty HourHeightProperty = DependencyProperty.Register(nameof(HourHeight), typeof(double), typeof(WinoCalendarPanel), new PropertyMetadata(0d));
|
public static readonly DependencyProperty HourHeightProperty = DependencyProperty.Register(nameof(HourHeight), typeof(double), typeof(WinoCalendarPanel), new PropertyMetadata(0d));
|
||||||
|
public static readonly DependencyProperty PeriodProperty = DependencyProperty.Register(nameof(Period), typeof(ITimePeriod), typeof(WinoCalendarPanel), new PropertyMetadata(null));
|
||||||
|
|
||||||
|
|
||||||
|
public ITimePeriod Period
|
||||||
|
{
|
||||||
|
get { return (ITimePeriod)GetValue(PeriodProperty); }
|
||||||
|
set { SetValue(PeriodProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
public double HourHeight
|
public double HourHeight
|
||||||
{
|
{
|
||||||
@@ -41,11 +50,18 @@ namespace Wino.Calendar.Controls
|
|||||||
|
|
||||||
private double GetChildTopMargin(ICalendarItem calendarItemViewModel, double availableHeight)
|
private double GetChildTopMargin(ICalendarItem calendarItemViewModel, double availableHeight)
|
||||||
{
|
{
|
||||||
var childStart = calendarItemViewModel.StartTime;
|
var childStart = calendarItemViewModel.StartDate;
|
||||||
|
|
||||||
double totalMinutes = 1440;
|
if (childStart <= Period.Start)
|
||||||
double minutesFromStart = (childStart - childStart.DateTime.Date).TotalMinutes;
|
{
|
||||||
return (minutesFromStart / totalMinutes) * availableHeight;
|
// Event started before or exactly at the periods tart. This might be a multi-day event.
|
||||||
|
// We can simply consider event must not have a top margin.
|
||||||
|
|
||||||
|
return 0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
double minutesFromStart = (childStart - Period.Start).TotalMinutes;
|
||||||
|
return (minutesFromStart / 1440) * availableHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double GetChildWidth(CalendarItemMeasurement calendarItemMeasurement, double availableWidth)
|
private double GetChildWidth(CalendarItemMeasurement calendarItemMeasurement, double availableWidth)
|
||||||
@@ -56,16 +72,48 @@ namespace Wino.Calendar.Controls
|
|||||||
private double GetChildLeftMargin(CalendarItemMeasurement calendarItemMeasurement, double availableWidth)
|
private double GetChildLeftMargin(CalendarItemMeasurement calendarItemMeasurement, double availableWidth)
|
||||||
=> availableWidth * calendarItemMeasurement.Left;
|
=> availableWidth * calendarItemMeasurement.Left;
|
||||||
|
|
||||||
private double GetChildHeight(DateTimeOffset childStart, DateTimeOffset childEnd)
|
private double GetChildHeight(ICalendarItem child)
|
||||||
{
|
{
|
||||||
double totalMinutes = 1440;
|
double childDurationInMinutes = 0d;
|
||||||
|
|
||||||
double availableHeight = HourHeight * 24;
|
double availableHeight = HourHeight * 24;
|
||||||
double childDuration = (childEnd - childStart).TotalMinutes;
|
|
||||||
return (childDuration / totalMinutes) * availableHeight;
|
var childStart = child.Period.Start;
|
||||||
|
var childEnd = child.Period.End;
|
||||||
|
|
||||||
|
// Multi-day event.
|
||||||
|
|
||||||
|
if (childStart < Period.Start)
|
||||||
|
{
|
||||||
|
if (childEnd >= Period.End)
|
||||||
|
{
|
||||||
|
// Event spans the whole period.
|
||||||
|
return availableHeight;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Check how many of the event falls into the current period.
|
||||||
|
childDurationInMinutes = (childEnd - Period.Start).TotalMinutes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
childDurationInMinutes = (childEnd - childStart).TotalMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (childDurationInMinutes / 1440) * availableHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Size MeasureOverride(Size availableSize)
|
||||||
|
{
|
||||||
|
ResetMeasurements();
|
||||||
|
return base.MeasureOverride(availableSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Size ArrangeOverride(Size finalSize)
|
protected override Size ArrangeOverride(Size finalSize)
|
||||||
{
|
{
|
||||||
|
if (Period == null || HourHeight == 0d) return finalSize;
|
||||||
|
|
||||||
// Measure/arrange each child height and width.
|
// Measure/arrange each child height and width.
|
||||||
// This is a vertical calendar. Therefore the height of each child is the duration of the event.
|
// This is a vertical calendar. Therefore the height of each child is the duration of the event.
|
||||||
// Children weights for left and right will be saved if they don't exist.
|
// Children weights for left and right will be saved if they don't exist.
|
||||||
@@ -99,7 +147,7 @@ namespace Wino.Calendar.Controls
|
|||||||
|
|
||||||
var childMeasurement = _measurements[child.Id];
|
var childMeasurement = _measurements[child.Id];
|
||||||
|
|
||||||
double childHeight = Math.Max(0, GetChildHeight(child.StartTime, child.StartTime.AddMinutes(child.DurationInMinutes)));
|
double childHeight = Math.Max(0, GetChildHeight(child));
|
||||||
double childWidth = Math.Max(0, GetChildWidth(childMeasurement, finalSize.Width));
|
double childWidth = Math.Max(0, GetChildWidth(childMeasurement, finalSize.Width));
|
||||||
double childTop = Math.Max(0, GetChildTopMargin(child, availableHeight));
|
double childTop = Math.Max(0, GetChildTopMargin(child, availableHeight));
|
||||||
double childLeft = Math.Max(0, GetChildLeftMargin(childMeasurement, availableWidth));
|
double childLeft = Math.Max(0, GetChildLeftMargin(childMeasurement, availableWidth));
|
||||||
@@ -142,7 +190,7 @@ namespace Wino.Calendar.Controls
|
|||||||
var columns = new List<List<ICalendarItem>>();
|
var columns = new List<List<ICalendarItem>>();
|
||||||
DateTime? lastEventEnding = null;
|
DateTime? lastEventEnding = null;
|
||||||
|
|
||||||
foreach (var ev in events.OrderBy(ev => ev.Period.Start).ThenBy(ev => ev.Period.End))
|
foreach (var ev in events.OrderBy(ev => ev.StartDate).ThenBy(ev => ev.EndDate))
|
||||||
{
|
{
|
||||||
if (ev.Period.Start >= lastEventEnding)
|
if (ev.Period.Start >= lastEventEnding)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
@@ -21,6 +22,16 @@ namespace Wino.Calendar.Services
|
|||||||
|
|
||||||
private ObservableCollection<GroupedAccountCalendarViewModel> _internalGroupedAccountCalendars = new ObservableCollection<GroupedAccountCalendarViewModel>();
|
private ObservableCollection<GroupedAccountCalendarViewModel> _internalGroupedAccountCalendars = new ObservableCollection<GroupedAccountCalendarViewModel>();
|
||||||
|
|
||||||
|
public IEnumerable<AccountCalendarViewModel> ActiveCalendars
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GroupedAccountCalendars
|
||||||
|
.SelectMany(a => a.AccountCalendars)
|
||||||
|
.Where(b => b.IsChecked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public AccountCalendarStateService()
|
public AccountCalendarStateService()
|
||||||
{
|
{
|
||||||
GroupedAccountCalendars = new ReadOnlyObservableCollection<GroupedAccountCalendarViewModel>(_internalGroupedAccountCalendars);
|
GroupedAccountCalendars = new ReadOnlyObservableCollection<GroupedAccountCalendarViewModel>(_internalGroupedAccountCalendars);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<ResourceDictionary x:Key="Dark">
|
<ResourceDictionary x:Key="Dark">
|
||||||
|
|
||||||
<!-- CalendarControl -->
|
<!-- CalendarControl -->
|
||||||
<SolidColorBrush x:Key="CalendarSeperatorBrush">#000000</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarSeperatorBrush">#525252</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarFieldWorkingHoursBackgroundBrush">#32262626</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarFieldWorkingHoursBackgroundBrush">#32262626</SolidColorBrush>
|
||||||
<SolidColorBrush x:Key="CalendarFieldSelectedBackgroundBrush">#121212</SolidColorBrush>
|
<SolidColorBrush x:Key="CalendarFieldSelectedBackgroundBrush">#121212</SolidColorBrush>
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,11 @@
|
|||||||
|
|
||||||
<!-- Default Calendar Item View Model Template -->
|
<!-- Default Calendar Item View Model Template -->
|
||||||
<DataTemplate x:Key="CalendarItemViewModelItemTemplate" x:DataType="data:CalendarItemViewModel">
|
<DataTemplate x:Key="CalendarItemViewModelItemTemplate" x:DataType="data:CalendarItemViewModel">
|
||||||
<Grid Background="{x:Bind helpers:XamlHelpers.GetSolidColorBrushFromHex(AssignedCalendar.BackgroundColorHex), Mode=OneWay}" CornerRadius="4">
|
<Grid
|
||||||
|
Background="{x:Bind helpers:XamlHelpers.GetSolidColorBrushFromHex(AssignedCalendar.BackgroundColorHex), Mode=OneWay}"
|
||||||
|
BorderBrush="{ThemeResource CalendarSeperatorBrush}"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="6">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="2,0"
|
Margin="2,0"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
@@ -44,7 +48,7 @@
|
|||||||
<ItemsControl ItemTemplate="{StaticResource CalendarItemViewModelItemTemplate}" ItemsSource="{x:Bind EventsCollection.RegularEvents}">
|
<ItemsControl ItemTemplate="{StaticResource CalendarItemViewModelItemTemplate}" ItemsSource="{x:Bind EventsCollection.RegularEvents}">
|
||||||
<ItemsControl.ItemsPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<controls:WinoCalendarPanel HourHeight="{Binding Path=CalendarRenderOptions.CalendarSettings.HourHeight}" />
|
<controls:WinoCalendarPanel HourHeight="{Binding Path=CalendarRenderOptions.CalendarSettings.HourHeight}" Period="{Binding Path=Period}" />
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
</ItemsControl.ItemsPanel>
|
</ItemsControl.ItemsPanel>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
|||||||
@@ -44,11 +44,6 @@ namespace Wino.Calendar.Views
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//public void Receive(GoToCalendarDayMessage message)
|
|
||||||
//{
|
|
||||||
// CalendarView.GoToDay(message.DateTime);
|
|
||||||
//}
|
|
||||||
|
|
||||||
private void PreviousDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoPreviousDateRequestedMessage());
|
private void PreviousDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoPreviousDateRequestedMessage());
|
||||||
|
|
||||||
private void NextDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoNextDateRequestedMessage());
|
private void NextDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoNextDateRequestedMessage());
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using Itenso.TimePeriod;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Windows.UI.Xaml.Controls;
|
using Windows.UI.Xaml.Controls;
|
||||||
using Windows.UI.Xaml.Navigation;
|
using Windows.UI.Xaml.Navigation;
|
||||||
@@ -16,7 +17,7 @@ namespace Wino.Calendar.Views
|
|||||||
IRecipient<GoNextDateRequestedMessage>,
|
IRecipient<GoNextDateRequestedMessage>,
|
||||||
IRecipient<GoPreviousDateRequestedMessage>
|
IRecipient<GoPreviousDateRequestedMessage>
|
||||||
{
|
{
|
||||||
private DateTime? selectedDateTime;
|
private DateTimeOffset? selectedDateTime;
|
||||||
public CalendarPage()
|
public CalendarPage()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -50,7 +51,13 @@ namespace Wino.Calendar.Views
|
|||||||
|
|
||||||
private void CellSelected(object sender, TimelineCellSelectedArgs e)
|
private void CellSelected(object sender, TimelineCellSelectedArgs e)
|
||||||
{
|
{
|
||||||
|
// Selected date is in Local kind.
|
||||||
selectedDateTime = e.ClickedDate;
|
selectedDateTime = e.ClickedDate;
|
||||||
|
var utc = DateTime.SpecifyKind(e.ClickedDate, DateTimeKind.Utc);
|
||||||
|
var unspecified = DateTime.SpecifyKind(e.ClickedDate, DateTimeKind.Unspecified);
|
||||||
|
|
||||||
|
var putc = new TimeRange(utc, utc.AddMinutes(30));
|
||||||
|
var punspecified = new TimeRange(unspecified, unspecified.AddMinutes(30));
|
||||||
|
|
||||||
// TODO: Popup is not positioned well on daily view.
|
// TODO: Popup is not positioned well on daily view.
|
||||||
TeachingTipPositionerGrid.Width = e.CellSize.Width;
|
TeachingTipPositionerGrid.Width = e.CellSize.Width;
|
||||||
@@ -59,8 +66,19 @@ namespace Wino.Calendar.Views
|
|||||||
Canvas.SetLeft(TeachingTipPositionerGrid, e.PositionerPoint.X);
|
Canvas.SetLeft(TeachingTipPositionerGrid, e.PositionerPoint.X);
|
||||||
Canvas.SetTop(TeachingTipPositionerGrid, e.PositionerPoint.Y);
|
Canvas.SetTop(TeachingTipPositionerGrid, e.PositionerPoint.Y);
|
||||||
|
|
||||||
// TODO: End time can be from settings.
|
//var testCalendarItem = new CalendarItem
|
||||||
// WeakReferenceMessenger.Default.Send(new CalendarEventAdded(new CalendarItem(selectedDateTime.Value, selectedDateTime.Value.AddMinutes(30))));
|
//{
|
||||||
|
// CalendarId = Guid.Parse("9ead7613-dacb-4163-8d33-2e32e65008a1"),
|
||||||
|
// StartTime = selectedDateTime.Value, // All events are saved in UTC.
|
||||||
|
// DurationInMinutes = 30,
|
||||||
|
// CreatedAt = DateTime.UtcNow,
|
||||||
|
// Description = "Test Description",
|
||||||
|
// Location = "Poland",
|
||||||
|
// Title = "Test event",
|
||||||
|
// Id = Guid.NewGuid()
|
||||||
|
//};
|
||||||
|
|
||||||
|
//WeakReferenceMessenger.Default.Send(new CalendarEventAdded(testCalendarItem));
|
||||||
|
|
||||||
NewEventTip.IsOpen = true;
|
NewEventTip.IsOpen = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Itenso.TimePeriod;
|
||||||
using Wino.Core.Domain.Entities.Calendar;
|
using Wino.Core.Domain.Entities.Calendar;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
|
||||||
@@ -12,9 +13,6 @@ namespace Wino.Core.Domain.Collections
|
|||||||
public event EventHandler<ICalendarItem> CalendarItemAdded;
|
public event EventHandler<ICalendarItem> CalendarItemAdded;
|
||||||
public event EventHandler<ICalendarItem> CalendarItemRemoved;
|
public event EventHandler<ICalendarItem> CalendarItemRemoved;
|
||||||
|
|
||||||
public event EventHandler<List<ICalendarItem>> CalendarItemRangeAdded;
|
|
||||||
public event EventHandler<List<ICalendarItem>> CalendarItemRangeRemoved;
|
|
||||||
|
|
||||||
public event EventHandler CalendarItemsCleared;
|
public event EventHandler CalendarItemsCleared;
|
||||||
|
|
||||||
private ObservableRangeCollection<ICalendarItem> _internalRegularEvents = [];
|
private ObservableRangeCollection<ICalendarItem> _internalRegularEvents = [];
|
||||||
@@ -22,79 +20,97 @@ namespace Wino.Core.Domain.Collections
|
|||||||
|
|
||||||
public ReadOnlyObservableCollection<ICalendarItem> RegularEvents { get; }
|
public ReadOnlyObservableCollection<ICalendarItem> RegularEvents { get; }
|
||||||
public ReadOnlyObservableCollection<ICalendarItem> AllDayEvents { get; }
|
public ReadOnlyObservableCollection<ICalendarItem> AllDayEvents { get; }
|
||||||
|
public ITimePeriod Period { get; }
|
||||||
|
|
||||||
public CalendarEventCollection()
|
private readonly List<ICalendarItem> _allItems = new List<ICalendarItem>();
|
||||||
|
|
||||||
|
public CalendarEventCollection(ITimePeriod period)
|
||||||
{
|
{
|
||||||
RegularEvents = new ReadOnlyObservableCollection<ICalendarItem>(_internalRegularEvents);
|
RegularEvents = new ReadOnlyObservableCollection<ICalendarItem>(_internalRegularEvents);
|
||||||
AllDayEvents = new ReadOnlyObservableCollection<ICalendarItem>(_internalAllDayEvents);
|
AllDayEvents = new ReadOnlyObservableCollection<ICalendarItem>(_internalAllDayEvents);
|
||||||
|
Period = period;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasCalendarEvent(AccountCalendar accountCalendar)
|
public bool HasCalendarEvent(AccountCalendar accountCalendar)
|
||||||
|
=> _allItems.Any(x => x.AssignedCalendar.Id == accountCalendar.Id);
|
||||||
|
|
||||||
|
public void FilterByCalendars(IEnumerable<Guid> visibleCalendarIds)
|
||||||
{
|
{
|
||||||
return _internalAllDayEvents.Any(x => x.AssignedCalendar.Id == accountCalendar.Id) ||
|
foreach (var item in _allItems)
|
||||||
_internalRegularEvents.Any(x => x.AssignedCalendar.Id == accountCalendar.Id);
|
{
|
||||||
|
var collection = GetProperCollectionForCalendarItem(item);
|
||||||
|
|
||||||
|
if (!visibleCalendarIds.Contains(item.AssignedCalendar.Id) && collection.Contains(item))
|
||||||
|
{
|
||||||
|
RemoveCalendarItemInternal(collection, item, false);
|
||||||
|
}
|
||||||
|
else if (visibleCalendarIds.Contains(item.AssignedCalendar.Id) && !collection.Contains(item))
|
||||||
|
{
|
||||||
|
AddCalendarItemInternal(collection, item, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddCalendarItemRange(IEnumerable<ICalendarItem> calendarItems)
|
private ObservableRangeCollection<ICalendarItem> GetProperCollectionForCalendarItem(ICalendarItem calendarItem)
|
||||||
{
|
{
|
||||||
foreach (var calendarItem in calendarItems)
|
// Event duration is not simply enough to determine whether it's an all-day event or not.
|
||||||
{
|
// Event may start at 11:00 PM and end next day at 11:00 PM. It's not an all-day event.
|
||||||
AddCalendarItem(calendarItem);
|
// It's a multi-day event.
|
||||||
}
|
|
||||||
|
|
||||||
CalendarItemRangeAdded?.Invoke(this, new List<ICalendarItem>(calendarItems));
|
bool isAllDayEvent = calendarItem.Period.Duration.TotalDays == 1 && calendarItem.Period.Start.TimeOfDay == TimeSpan.Zero;
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveCalendarItemRange(IEnumerable<ICalendarItem> calendarItems)
|
return isAllDayEvent ? _internalAllDayEvents : _internalRegularEvents;
|
||||||
{
|
|
||||||
foreach (var calendarItem in calendarItems)
|
|
||||||
{
|
|
||||||
RemoveCalendarItem(calendarItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
CalendarItemRangeRemoved?.Invoke(this, new List<ICalendarItem>(calendarItems));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddCalendarItem(ICalendarItem calendarItem)
|
public void AddCalendarItem(ICalendarItem calendarItem)
|
||||||
|
{
|
||||||
|
var collection = GetProperCollectionForCalendarItem(calendarItem);
|
||||||
|
AddCalendarItemInternal(collection, calendarItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveCalendarItem(ICalendarItem calendarItem)
|
||||||
|
{
|
||||||
|
var collection = GetProperCollectionForCalendarItem(calendarItem);
|
||||||
|
RemoveCalendarItemInternal(collection, calendarItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddCalendarItemInternal(ObservableRangeCollection<ICalendarItem> collection, ICalendarItem calendarItem, bool create = true)
|
||||||
{
|
{
|
||||||
if (calendarItem is not ICalendarItemViewModel)
|
if (calendarItem is not ICalendarItemViewModel)
|
||||||
throw new ArgumentException("CalendarItem must be of type ICalendarItemViewModel", nameof(calendarItem));
|
throw new ArgumentException("CalendarItem must be of type ICalendarItemViewModel", nameof(calendarItem));
|
||||||
|
|
||||||
if (calendarItem.Period.Duration.TotalMinutes == 1440)
|
collection.Add(calendarItem);
|
||||||
|
|
||||||
|
if (create)
|
||||||
{
|
{
|
||||||
_internalAllDayEvents.Add(calendarItem);
|
_allItems.Add(calendarItem);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_internalRegularEvents.Add(calendarItem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CalendarItemAdded?.Invoke(this, calendarItem);
|
CalendarItemAdded?.Invoke(this, calendarItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RemoveCalendarItemInternal(ObservableRangeCollection<ICalendarItem> collection, ICalendarItem calendarItem, bool destroy = true)
|
||||||
|
{
|
||||||
|
if (calendarItem is not ICalendarItemViewModel)
|
||||||
|
throw new ArgumentException("CalendarItem must be of type ICalendarItemViewModel", nameof(calendarItem));
|
||||||
|
|
||||||
|
collection.Remove(calendarItem);
|
||||||
|
|
||||||
|
if (destroy)
|
||||||
|
{
|
||||||
|
_allItems.Remove(calendarItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
CalendarItemRemoved?.Invoke(this, calendarItem);
|
||||||
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
_internalAllDayEvents.Clear();
|
_internalAllDayEvents.Clear();
|
||||||
_internalRegularEvents.Clear();
|
_internalRegularEvents.Clear();
|
||||||
|
_allItems.Clear();
|
||||||
|
|
||||||
CalendarItemsCleared?.Invoke(this, EventArgs.Empty);
|
CalendarItemsCleared?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveCalendarItem(ICalendarItem calendarItem)
|
|
||||||
{
|
|
||||||
if (calendarItem is not ICalendarItemViewModel)
|
|
||||||
throw new ArgumentException("CalendarItem must be of type ICalendarItemViewModel", nameof(calendarItem));
|
|
||||||
|
|
||||||
if (calendarItem.Period.Duration.TotalMinutes == 1440)
|
|
||||||
{
|
|
||||||
_internalAllDayEvents.Remove(calendarItem);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_internalRegularEvents.Remove(calendarItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
CalendarItemRemoved?.Invoke(this, calendarItem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,18 +13,43 @@ namespace Wino.Core.Domain.Entities.Calendar
|
|||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string Location { get; set; }
|
public string Location { get; set; }
|
||||||
public DateTimeOffset StartTime { get; set; }
|
|
||||||
public int DurationInMinutes { get; set; }
|
public DateTime StartDate { get; set; }
|
||||||
|
|
||||||
|
public DateTime EndDate
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return StartDate.AddSeconds(DurationInSeconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimeSpan StartDateOffset { get; set; }
|
||||||
|
public TimeSpan EndDateOffset { get; set; }
|
||||||
|
|
||||||
|
private ITimePeriod _period;
|
||||||
|
public ITimePeriod Period
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
_period ??= new TimeRange(StartDate, EndDate);
|
||||||
|
|
||||||
|
return _period;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double DurationInSeconds { get; set; }
|
||||||
public string Recurrence { get; set; }
|
public string Recurrence { get; set; }
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
public string CustomEventColorHex { get; set; }
|
||||||
|
public string HtmlLink { get; set; }
|
||||||
public CalendarItemStatus Status { get; set; }
|
public CalendarItemStatus Status { get; set; }
|
||||||
public CalendarItemVisibility Visibility { get; set; }
|
public CalendarItemVisibility Visibility { get; set; }
|
||||||
public DateTimeOffset CreatedAt { get; set; }
|
public DateTimeOffset CreatedAt { get; set; }
|
||||||
public DateTimeOffset UpdatedAt { get; set; }
|
public DateTimeOffset UpdatedAt { get; set; }
|
||||||
public Guid CalendarId { get; set; }
|
public Guid CalendarId { get; set; }
|
||||||
|
|
||||||
[Ignore]
|
|
||||||
public TimeRange Period => new TimeRange(StartTime.Date, StartTime.Date.AddMinutes(DurationInMinutes));
|
|
||||||
|
|
||||||
[Ignore]
|
[Ignore]
|
||||||
public IAccountCalendar AssignedCalendar { get; set; }
|
public IAccountCalendar AssignedCalendar { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ namespace Wino.Core.Domain.Interfaces
|
|||||||
{
|
{
|
||||||
string Title { get; }
|
string Title { get; }
|
||||||
Guid Id { get; }
|
Guid Id { get; }
|
||||||
DateTimeOffset StartTime { get; }
|
|
||||||
int DurationInMinutes { get; }
|
|
||||||
TimeRange Period { get; }
|
|
||||||
IAccountCalendar AssignedCalendar { get; }
|
IAccountCalendar AssignedCalendar { get; }
|
||||||
|
DateTime StartDate { get; set; }
|
||||||
|
DateTime EndDate { get; }
|
||||||
|
double DurationInSeconds { get; set; }
|
||||||
|
ITimePeriod Period { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,20 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Wino.Core.Domain.Entities.Calendar;
|
using Wino.Core.Domain.Entities.Calendar;
|
||||||
|
using Wino.Core.Domain.Models.Calendar;
|
||||||
|
|
||||||
namespace Wino.Core.Domain.Interfaces
|
namespace Wino.Core.Domain.Interfaces
|
||||||
{
|
{
|
||||||
public interface ICalendarService
|
public interface ICalendarService
|
||||||
{
|
{
|
||||||
Task<List<AccountCalendar>> GetAccountCalendarsAsync(Guid accountId);
|
Task<List<AccountCalendar>> GetAccountCalendarsAsync(Guid accountId);
|
||||||
|
Task<AccountCalendar> GetAccountCalendarAsync(Guid accountCalendarId);
|
||||||
Task DeleteCalendarItemAsync(Guid calendarItemId);
|
Task DeleteCalendarItemAsync(Guid calendarItemId);
|
||||||
|
|
||||||
Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar);
|
Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar);
|
||||||
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<CalendarItem>> GetCalendarEventsAsync(IAccountCalendar calendar, DateTime rangeStart, DateTime rangeEnd);
|
Task<List<CalendarItem>> GetCalendarEventsAsync(IAccountCalendar calendar, DayRangeRenderModel dayRangeRenderModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,15 @@ namespace Wino.Core.Domain.Models.Calendar
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class CalendarDayModel
|
public class CalendarDayModel
|
||||||
{
|
{
|
||||||
public TimeRange Period { get; }
|
public ITimePeriod Period { get; }
|
||||||
public CalendarEventCollection EventsCollection { get; } = new CalendarEventCollection();
|
public CalendarEventCollection EventsCollection { get; }
|
||||||
|
|
||||||
public CalendarDayModel(DateTime representingDate, CalendarRenderOptions calendarRenderOptions)
|
public CalendarDayModel(DateTime representingDate, CalendarRenderOptions calendarRenderOptions)
|
||||||
{
|
{
|
||||||
RepresentingDate = representingDate;
|
RepresentingDate = representingDate;
|
||||||
Period = new TimeRange(representingDate, representingDate.AddDays(1));
|
Period = new TimeRange(representingDate, representingDate.AddDays(1));
|
||||||
CalendarRenderOptions = calendarRenderOptions;
|
CalendarRenderOptions = calendarRenderOptions;
|
||||||
|
EventsCollection = new CalendarEventCollection(Period);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DateTime RepresentingDate { get; }
|
public DateTime RepresentingDate { get; }
|
||||||
|
|||||||
@@ -56,25 +56,16 @@ namespace Wino.Core.Domain.Models.Calendar
|
|||||||
private void RegisterCalendarDayEvents(CalendarDayModel calendarDayModel)
|
private void RegisterCalendarDayEvents(CalendarDayModel calendarDayModel)
|
||||||
{
|
{
|
||||||
calendarDayModel.EventsCollection.CalendarItemAdded += CalendarItemAdded;
|
calendarDayModel.EventsCollection.CalendarItemAdded += CalendarItemAdded;
|
||||||
calendarDayModel.EventsCollection.CalendarItemRangeRemoved += CalendarItemRangeRemoved;
|
|
||||||
calendarDayModel.EventsCollection.CalendarItemRemoved += CalendarItemRemoved;
|
calendarDayModel.EventsCollection.CalendarItemRemoved += CalendarItemRemoved;
|
||||||
calendarDayModel.EventsCollection.CalendarItemRangeAdded += CalendarItemRangeAdded;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: These handlers have incorrect senders. They should be the CalendarDayModel.
|
// TODO: These handlers have incorrect senders. They should be the CalendarDayModel.
|
||||||
|
|
||||||
private void CalendarItemRangeAdded(object sender, List<ICalendarItem> e)
|
|
||||||
=> CalendarDayEventCollectionUpdated?.Invoke(this, sender as CalendarDayModel);
|
|
||||||
|
|
||||||
private void CalendarItemRemoved(object sender, ICalendarItem e)
|
private void CalendarItemRemoved(object sender, ICalendarItem e)
|
||||||
=> CalendarDayEventCollectionUpdated?.Invoke(this, sender as CalendarDayModel);
|
=> CalendarDayEventCollectionUpdated?.Invoke(this, sender as CalendarDayModel);
|
||||||
|
|
||||||
private void CalendarItemAdded(object sender, ICalendarItem e)
|
private void CalendarItemAdded(object sender, ICalendarItem e)
|
||||||
=> CalendarDayEventCollectionUpdated?.Invoke(this, sender as CalendarDayModel);
|
=> CalendarDayEventCollectionUpdated?.Invoke(this, sender as CalendarDayModel);
|
||||||
|
|
||||||
private void CalendarItemRangeRemoved(object sender, List<ICalendarItem> e)
|
|
||||||
=> CalendarDayEventCollectionUpdated?.Invoke(this, sender as CalendarDayModel);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unregisters all calendar item change listeners to draw the UI for calendar events.
|
/// Unregisters all calendar item change listeners to draw the UI for calendar events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -82,9 +73,7 @@ namespace Wino.Core.Domain.Models.Calendar
|
|||||||
{
|
{
|
||||||
foreach (var day in CalendarDays)
|
foreach (var day in CalendarDays)
|
||||||
{
|
{
|
||||||
day.EventsCollection.CalendarItemRangeRemoved -= CalendarItemRangeRemoved;
|
|
||||||
day.EventsCollection.CalendarItemRemoved -= CalendarItemRemoved;
|
day.EventsCollection.CalendarItemRemoved -= CalendarItemRemoved;
|
||||||
day.EventsCollection.CalendarItemRangeAdded -= CalendarItemRangeAdded;
|
|
||||||
day.EventsCollection.CalendarItemAdded -= CalendarItemAdded;
|
day.EventsCollection.CalendarItemAdded -= CalendarItemAdded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Entities.Calendar;
|
||||||
using Wino.Messaging.Client.Calendar;
|
using Wino.Messaging.Client.Calendar;
|
||||||
|
|
||||||
namespace Wino.Core.ViewModels
|
namespace Wino.Core.ViewModels
|
||||||
@@ -8,6 +8,6 @@ namespace Wino.Core.ViewModels
|
|||||||
{
|
{
|
||||||
public void Receive(CalendarEventAdded message) => OnCalendarEventAdded(message.CalendarItem);
|
public void Receive(CalendarEventAdded message) => OnCalendarEventAdded(message.CalendarItem);
|
||||||
|
|
||||||
protected virtual void OnCalendarEventAdded(ICalendarItem calendarItem) { }
|
protected virtual void OnCalendarEventAdded(CalendarItem calendarItem) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,88 +209,74 @@ namespace Wino.Core.Extensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="calendarEvent">The Google Calendar Event object.</param>
|
/// <param name="calendarEvent">The Google Calendar Event object.</param>
|
||||||
/// <returns>The start DateTimeOffset of the event, or null if it cannot be determined.</returns>
|
/// <returns>The start DateTimeOffset of the event, or null if it cannot be determined.</returns>
|
||||||
public static DateTimeOffset? GetEventStartDateTimeOffset(this Event calendarEvent)
|
//public static DateTimeOffset GetEventStartDateTimeOffset(this Event calendarEvent)
|
||||||
{
|
//{
|
||||||
if (calendarEvent == null)
|
// return GetEventDateTimeOffset(calendarEvent.Start);
|
||||||
{
|
//}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (calendarEvent.Start != null)
|
public static DateTimeOffset GetEventDateTimeOffset(EventDateTime calendarEvent)
|
||||||
|
{
|
||||||
|
if (calendarEvent != null)
|
||||||
{
|
{
|
||||||
if (calendarEvent.Start.DateTimeDateTimeOffset != null)
|
if (calendarEvent.DateTimeDateTimeOffset != null)
|
||||||
{
|
{
|
||||||
return calendarEvent.Start.DateTimeDateTimeOffset; // Use the direct DateTimeOffset property!
|
return calendarEvent.DateTimeDateTimeOffset.Value;
|
||||||
}
|
}
|
||||||
else if (calendarEvent.Start.Date != null)
|
else if (calendarEvent.Date != null)
|
||||||
{
|
{
|
||||||
if (DateTime.TryParse(calendarEvent.Start.Date, out DateTime startDate))
|
if (DateTime.TryParse(calendarEvent.Date, out DateTime eventDateTime))
|
||||||
{
|
{
|
||||||
// Date-only events are treated as UTC midnight
|
// Date-only events are treated as UTC midnight
|
||||||
return new DateTimeOffset(startDate, TimeSpan.Zero);
|
return new DateTimeOffset(eventDateTime, TimeSpan.Zero);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return null;
|
throw new Exception("Invalid date format in Google Calendar event date.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null; // Start time not found
|
throw new Exception("Google Calendar event has no date.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the duration of a Google Calendar Event in minutes.
|
/// Calculates the duration of a Google Calendar Event in seconds.
|
||||||
/// Handles date-only and date-time events, but *does not* handle recurring events correctly.
|
/// Handles date-only and date-time events, but *does not* handle recurring events correctly.
|
||||||
/// For recurring events, this method will return the duration of the *first* instance.
|
/// For recurring events, this method will return the duration of the *first* instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="calendarEvent">The Google Calendar Event object.</param>
|
/// <param name="calendarEvent">The Google Calendar Event object.</param>
|
||||||
/// <returns>The duration of the event in minutes, or null if it cannot be determined.</returns>
|
/// <returns>The duration of the event in minutes, or null if it cannot be determined.</returns>
|
||||||
public static int? GetEventDurationInMinutes(this Event calendarEvent)
|
//public static int GetEventDurationInSeconds(this Event calendarEvent)
|
||||||
{
|
//{
|
||||||
if (calendarEvent == null)
|
// var start = calendarEvent.GetEventStartDateTimeOffset();
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
DateTimeOffset? start = calendarEvent.GetEventStartDateTimeOffset();
|
// DateTimeOffset? end = null;
|
||||||
if (start == null)
|
// if (calendarEvent.End != null)
|
||||||
{
|
// {
|
||||||
return null;
|
// if (calendarEvent.End.DateTimeDateTimeOffset != null)
|
||||||
}
|
// {
|
||||||
|
// end = calendarEvent.End.DateTimeDateTimeOffset;
|
||||||
|
// }
|
||||||
|
// else if (calendarEvent.End.Date != null)
|
||||||
|
// {
|
||||||
|
// if (DateTime.TryParse(calendarEvent.End.Date, out DateTime endDate))
|
||||||
|
// {
|
||||||
|
// end = new DateTimeOffset(endDate, TimeSpan.Zero);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// throw new Exception("Invalid date format in Google Calendar event end date.");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
DateTimeOffset? end = null;
|
// if (end == null) throw new Exception("Google Calendar event has no end date.");
|
||||||
if (calendarEvent.End != null)
|
|
||||||
{
|
|
||||||
if (calendarEvent.End.DateTimeDateTimeOffset != null)
|
|
||||||
{
|
|
||||||
end = calendarEvent.End.DateTimeDateTimeOffset;
|
|
||||||
}
|
|
||||||
else if (calendarEvent.End.Date != null)
|
|
||||||
{
|
|
||||||
if (DateTime.TryParse(calendarEvent.End.Date, out DateTime endDate))
|
|
||||||
{
|
|
||||||
end = new DateTimeOffset(endDate, TimeSpan.Zero);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int)(end.Value - start.Value).TotalMinutes;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// return (int)(end.Value - start).TotalSeconds;
|
||||||
|
//}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// RRULE, EXRULE, RDATE and EXDATE lines for a recurring event, as specified in RFC5545.
|
/// RRULE, EXRULE, RDATE and EXDATE lines for a recurring event, as specified in RFC5545.
|
||||||
///
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>___ separated lines.</returns>
|
/// <returns>___ separated lines.</returns>
|
||||||
public static string GetRecurrenceString(this Event calendarEvent)
|
public static string GetRecurrenceString(this Event calendarEvent)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Google.Apis.Calendar.v3.Data;
|
using Google.Apis.Calendar.v3.Data;
|
||||||
using Wino.Core.Domain.Entities.Calendar;
|
using Wino.Core.Domain.Entities.Calendar;
|
||||||
@@ -33,22 +34,32 @@ namespace Wino.Core.Integration.Processors
|
|||||||
|
|
||||||
public async Task<CalendarItem> CreateCalendarItemAsync(Event calendarEvent, AccountCalendar assignedCalendar, MailAccount organizerAccount)
|
public async Task<CalendarItem> CreateCalendarItemAsync(Event calendarEvent, AccountCalendar assignedCalendar, MailAccount organizerAccount)
|
||||||
{
|
{
|
||||||
|
var eventStartDateTimeOffset = GoogleIntegratorExtensions.GetEventDateTimeOffset(calendarEvent.Start);
|
||||||
|
var eventEndDateTimeOffset = GoogleIntegratorExtensions.GetEventDateTimeOffset(calendarEvent.End);
|
||||||
|
|
||||||
|
var totalDurationInSeconds = (eventEndDateTimeOffset - eventStartDateTimeOffset).TotalSeconds;
|
||||||
|
|
||||||
var calendarItem = new CalendarItem()
|
var calendarItem = new CalendarItem()
|
||||||
{
|
{
|
||||||
CalendarId = assignedCalendar.Id,
|
CalendarId = assignedCalendar.Id,
|
||||||
CreatedAt = DateTimeOffset.UtcNow,
|
CreatedAt = DateTimeOffset.UtcNow,
|
||||||
Description = calendarEvent.Description,
|
Description = calendarEvent.Description,
|
||||||
StartTime = GoogleIntegratorExtensions.GetEventStartDateTimeOffset(calendarEvent) ?? throw new Exception("Event without a start time."),
|
|
||||||
DurationInMinutes = GoogleIntegratorExtensions.GetEventDurationInMinutes(calendarEvent) ?? throw new Exception("Event without a duration."),
|
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
|
StartDate = eventStartDateTimeOffset.DateTime,
|
||||||
|
StartDateOffset = eventStartDateTimeOffset.Offset,
|
||||||
|
EndDateOffset = eventEndDateTimeOffset.Offset,
|
||||||
|
DurationInSeconds = totalDurationInSeconds,
|
||||||
Location = calendarEvent.Location,
|
Location = calendarEvent.Location,
|
||||||
Recurrence = GoogleIntegratorExtensions.GetRecurrenceString(calendarEvent),
|
Recurrence = GoogleIntegratorExtensions.GetRecurrenceString(calendarEvent),
|
||||||
Status = GetStatus(calendarEvent.Status),
|
Status = GetStatus(calendarEvent.Status),
|
||||||
Title = calendarEvent.Summary,
|
Title = calendarEvent.Summary,
|
||||||
UpdatedAt = DateTimeOffset.UtcNow,
|
UpdatedAt = DateTimeOffset.UtcNow,
|
||||||
Visibility = GetVisibility(calendarEvent.Visibility),
|
Visibility = GetVisibility(calendarEvent.Visibility),
|
||||||
|
HtmlLink = calendarEvent.HtmlLink
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Debug.WriteLine($"({assignedCalendar.Name}) {calendarItem.Title}, Start: {calendarItem.StartDate.ToString("f")}, End: {calendarItem.EndDate.ToString("f")}");
|
||||||
|
|
||||||
// TODO: There are some edge cases with cancellation here.
|
// TODO: There are some edge cases with cancellation here.
|
||||||
CalendarItemStatus GetStatus(string status)
|
CalendarItemStatus GetStatus(string status)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Entities.Calendar;
|
||||||
|
|
||||||
namespace Wino.Messaging.Client.Calendar
|
namespace Wino.Messaging.Client.Calendar
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised when event is added to database.
|
/// Raised when event is added to database.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public record CalendarEventAdded(ICalendarItem CalendarItem);
|
public record CalendarEventAdded(CalendarItem CalendarItem);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ using System.Collections.Generic;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using Ical.Net.CalendarComponents;
|
||||||
using Ical.Net.DataTypes;
|
using Ical.Net.DataTypes;
|
||||||
using SqlKata;
|
using SqlKata;
|
||||||
using Wino.Core.Domain;
|
using Wino.Core.Domain;
|
||||||
using Wino.Core.Domain.Entities.Calendar;
|
using Wino.Core.Domain.Entities.Calendar;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
using Wino.Core.Domain.Models.Calendar;
|
||||||
using Wino.Messaging.Client.Calendar;
|
using Wino.Messaging.Client.Calendar;
|
||||||
using Wino.Services.Extensions;
|
using Wino.Services.Extensions;
|
||||||
|
|
||||||
@@ -71,7 +73,7 @@ namespace Wino.Services
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<CalendarItem>> GetCalendarEventsAsync(IAccountCalendar calendar, DateTime rangeStart, DateTime rangeEnd)
|
public async Task<List<CalendarItem>> GetCalendarEventsAsync(IAccountCalendar calendar, DayRangeRenderModel dayRangeRenderModel)
|
||||||
{
|
{
|
||||||
// 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.
|
||||||
@@ -84,19 +86,22 @@ namespace Wino.Services
|
|||||||
ev.AssignedCalendar = calendar;
|
ev.AssignedCalendar = calendar;
|
||||||
|
|
||||||
// Parse recurrence rules
|
// Parse recurrence rules
|
||||||
var calendarEvent = new Ical.Net.CalendarComponents.CalendarEvent
|
var calendarEvent = new CalendarEvent
|
||||||
{
|
{
|
||||||
Start = new CalDateTime(ev.StartTime.UtcDateTime),
|
Start = new CalDateTime(ev.StartDate),
|
||||||
Duration = TimeSpan.FromMinutes(ev.DurationInMinutes),
|
End = new CalDateTime(ev.EndDate),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(ev.Recurrence))
|
if (string.IsNullOrEmpty(ev.Recurrence))
|
||||||
{
|
{
|
||||||
// No recurrence, only check if we fall into the date range.
|
// No recurrence, only check if we fall into the given period.
|
||||||
// All events are saved in UTC, so we need to convert the range to UTC as well.
|
|
||||||
if (ev.StartTime.UtcDateTime < rangeEnd
|
if (ev.Period.OverlapsWith(dayRangeRenderModel.Period))
|
||||||
&& ev.StartTime.UtcDateTime.AddMinutes(ev.DurationInMinutes) > rangeStart)
|
|
||||||
{
|
{
|
||||||
|
// TODO: We overlap, but this might be a multi-day event.
|
||||||
|
// Should we split the events here or in panel?
|
||||||
|
// For now just continue.
|
||||||
|
|
||||||
result.Add(ev);
|
result.Add(ev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,7 +115,7 @@ namespace Wino.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate occurrences in the range.
|
// Calculate occurrences in the range.
|
||||||
var occurrences = calendarEvent.GetOccurrences(rangeStart, rangeEnd);
|
var occurrences = calendarEvent.GetOccurrences(dayRangeRenderModel.Period.Start, dayRangeRenderModel.Period.End);
|
||||||
|
|
||||||
foreach (var occurrence in occurrences)
|
foreach (var occurrence in occurrences)
|
||||||
{
|
{
|
||||||
@@ -121,5 +126,8 @@ namespace Wino.Services
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<AccountCalendar> GetAccountCalendarAsync(Guid accountCalendarId)
|
||||||
|
=> Connection.GetAsync<AccountCalendar>(accountCalendarId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user