Proper handling of DateTimeOffset, support for Multi-Day events and reacting to adding/removing events for the days.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Itenso.TimePeriod;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
@@ -12,9 +13,6 @@ namespace Wino.Core.Domain.Collections
|
||||
public event EventHandler<ICalendarItem> CalendarItemAdded;
|
||||
public event EventHandler<ICalendarItem> CalendarItemRemoved;
|
||||
|
||||
public event EventHandler<List<ICalendarItem>> CalendarItemRangeAdded;
|
||||
public event EventHandler<List<ICalendarItem>> CalendarItemRangeRemoved;
|
||||
|
||||
public event EventHandler CalendarItemsCleared;
|
||||
|
||||
private ObservableRangeCollection<ICalendarItem> _internalRegularEvents = [];
|
||||
@@ -22,79 +20,97 @@ namespace Wino.Core.Domain.Collections
|
||||
|
||||
public ReadOnlyObservableCollection<ICalendarItem> RegularEvents { 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);
|
||||
AllDayEvents = new ReadOnlyObservableCollection<ICalendarItem>(_internalAllDayEvents);
|
||||
Period = period;
|
||||
}
|
||||
|
||||
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) ||
|
||||
_internalRegularEvents.Any(x => x.AssignedCalendar.Id == accountCalendar.Id);
|
||||
foreach (var item in _allItems)
|
||||
{
|
||||
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)
|
||||
{
|
||||
AddCalendarItem(calendarItem);
|
||||
}
|
||||
// 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.
|
||||
// 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)
|
||||
{
|
||||
foreach (var calendarItem in calendarItems)
|
||||
{
|
||||
RemoveCalendarItem(calendarItem);
|
||||
}
|
||||
|
||||
CalendarItemRangeRemoved?.Invoke(this, new List<ICalendarItem>(calendarItems));
|
||||
return isAllDayEvent ? _internalAllDayEvents : _internalRegularEvents;
|
||||
}
|
||||
|
||||
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)
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
_internalRegularEvents.Add(calendarItem);
|
||||
_allItems.Add(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()
|
||||
{
|
||||
_internalAllDayEvents.Clear();
|
||||
_internalRegularEvents.Clear();
|
||||
_allItems.Clear();
|
||||
|
||||
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 Description { 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; }
|
||||
|
||||
// TODO
|
||||
public string CustomEventColorHex { get; set; }
|
||||
public string HtmlLink { get; set; }
|
||||
public CalendarItemStatus Status { get; set; }
|
||||
public CalendarItemVisibility Visibility { get; set; }
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
public DateTimeOffset UpdatedAt { get; set; }
|
||||
public Guid CalendarId { get; set; }
|
||||
|
||||
[Ignore]
|
||||
public TimeRange Period => new TimeRange(StartTime.Date, StartTime.Date.AddMinutes(DurationInMinutes));
|
||||
|
||||
[Ignore]
|
||||
public IAccountCalendar AssignedCalendar { get; set; }
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@ namespace Wino.Core.Domain.Interfaces
|
||||
{
|
||||
string Title { get; }
|
||||
Guid Id { get; }
|
||||
DateTimeOffset StartTime { get; }
|
||||
int DurationInMinutes { get; }
|
||||
TimeRange Period { 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.Threading.Tasks;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Core.Domain.Interfaces
|
||||
{
|
||||
public interface ICalendarService
|
||||
{
|
||||
Task<List<AccountCalendar>> GetAccountCalendarsAsync(Guid accountId);
|
||||
Task<AccountCalendar> GetAccountCalendarAsync(Guid accountCalendarId);
|
||||
Task DeleteCalendarItemAsync(Guid calendarItemId);
|
||||
|
||||
Task DeleteAccountCalendarAsync(AccountCalendar accountCalendar);
|
||||
Task InsertAccountCalendarAsync(AccountCalendar accountCalendar);
|
||||
Task UpdateAccountCalendarAsync(AccountCalendar accountCalendar);
|
||||
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>
|
||||
public class CalendarDayModel
|
||||
{
|
||||
public TimeRange Period { get; }
|
||||
public CalendarEventCollection EventsCollection { get; } = new CalendarEventCollection();
|
||||
public ITimePeriod Period { get; }
|
||||
public CalendarEventCollection EventsCollection { get; }
|
||||
|
||||
public CalendarDayModel(DateTime representingDate, CalendarRenderOptions calendarRenderOptions)
|
||||
{
|
||||
RepresentingDate = representingDate;
|
||||
Period = new TimeRange(representingDate, representingDate.AddDays(1));
|
||||
CalendarRenderOptions = calendarRenderOptions;
|
||||
EventsCollection = new CalendarEventCollection(Period);
|
||||
}
|
||||
|
||||
public DateTime RepresentingDate { get; }
|
||||
|
||||
@@ -56,25 +56,16 @@ namespace Wino.Core.Domain.Models.Calendar
|
||||
private void RegisterCalendarDayEvents(CalendarDayModel calendarDayModel)
|
||||
{
|
||||
calendarDayModel.EventsCollection.CalendarItemAdded += CalendarItemAdded;
|
||||
calendarDayModel.EventsCollection.CalendarItemRangeRemoved += CalendarItemRangeRemoved;
|
||||
calendarDayModel.EventsCollection.CalendarItemRemoved += CalendarItemRemoved;
|
||||
calendarDayModel.EventsCollection.CalendarItemRangeAdded += CalendarItemRangeAdded;
|
||||
}
|
||||
|
||||
// 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)
|
||||
=> CalendarDayEventCollectionUpdated?.Invoke(this, sender as CalendarDayModel);
|
||||
|
||||
private void CalendarItemAdded(object sender, ICalendarItem e)
|
||||
=> CalendarDayEventCollectionUpdated?.Invoke(this, sender as CalendarDayModel);
|
||||
|
||||
private void CalendarItemRangeRemoved(object sender, List<ICalendarItem> e)
|
||||
=> CalendarDayEventCollectionUpdated?.Invoke(this, sender as CalendarDayModel);
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters all calendar item change listeners to draw the UI for calendar events.
|
||||
/// </summary>
|
||||
@@ -82,9 +73,7 @@ namespace Wino.Core.Domain.Models.Calendar
|
||||
{
|
||||
foreach (var day in CalendarDays)
|
||||
{
|
||||
day.EventsCollection.CalendarItemRangeRemoved -= CalendarItemRangeRemoved;
|
||||
day.EventsCollection.CalendarItemRemoved -= CalendarItemRemoved;
|
||||
day.EventsCollection.CalendarItemRangeAdded -= CalendarItemRangeAdded;
|
||||
day.EventsCollection.CalendarItemAdded -= CalendarItemAdded;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user