Recalculate recurrences when a new event added.

This commit is contained in:
Burak Kaan Köse
2025-12-30 08:51:50 +01:00
parent 0519bf86b3
commit 72e43e4b7a
3 changed files with 91 additions and 7 deletions
@@ -863,17 +863,29 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
// Check if event falls into the current date range.
if (DayRanges.DisplayRange == null) return;
// Check whether this event falls into any of the loaded date ranges.
var allDaysForEvent = DayRanges.SelectMany(a => a.CalendarDays).Where(a => a.Period.OverlapsWith(calendarItem.Period));
// Get all periods from the visible day ranges
var visiblePeriods = DayRanges.Select(dr => dr.Period).ToList();
foreach (var calendarDay in allDaysForEvent)
// For recurring events, expand them to check if any occurrences fall within visible periods
// For regular events, just check if they overlap with any period
var matchingItems = await _calendarService.GetExpandedRecurringEventsForPeriodsAsync(calendarItem, visiblePeriods);
foreach (var item in matchingItems)
{
var calendarItemViewModel = new CalendarItemViewModel(calendarItem);
// Find the days that the event falls into
var allDaysForEvent = DayRanges
.SelectMany(a => a.CalendarDays)
.Where(a => a.Period.OverlapsWith(item.Period));
await ExecuteUIThread(() =>
foreach (var calendarDay in allDaysForEvent)
{
calendarDay.EventsCollection.AddCalendarItem(calendarItemViewModel);
});
var calendarItemViewModel = new CalendarItemViewModel(item);
await ExecuteUIThread(() =>
{
calendarDay.EventsCollection.AddCalendarItem(calendarItemViewModel);
});
}
}
FilterActiveCalendars(DayRanges);
@@ -25,6 +25,14 @@ public interface ICalendarService
/// <param name="period">The time period to query events for.</param>
/// <returns>List of calendar items including regular events and recurring event occurrences.</returns>
Task<List<CalendarItem>> GetCalendarEventsAsync(IAccountCalendar calendar, ITimePeriod period);
/// <summary>
/// Expands a recurring calendar item to check if any of its occurrences fall within the given periods.
/// </summary>
/// <param name="calendarItem">The calendar item to expand (can be recurring or non-recurring).</param>
/// <param name="periods">The list of periods to check against.</param>
/// <returns>List of calendar items (either the original item or expanded recurrence instances) that fall within the periods.</returns>
Task<List<CalendarItem>> GetExpandedRecurringEventsForPeriodsAsync(CalendarItem calendarItem, IEnumerable<ITimePeriod> periods);
Task<CalendarItem> GetCalendarItemAsync(Guid accountCalendarId, string remoteEventId);
Task UpdateCalendarDeltaSynchronizationToken(Guid calendarId, string deltaToken);
+64
View File
@@ -269,6 +269,70 @@ public class CalendarService : BaseDatabaseService, ICalendarService
deltaToken, calendarId);
}
/// <summary>
/// Expands a recurring calendar item to check if any of its occurrences fall within the given periods.
/// For non-recurring events, returns the item if it overlaps with any period.
/// For recurring events, expands occurrences and returns those that fall within any of the periods.
/// </summary>
/// <param name="calendarItem">The calendar item to expand (can be recurring or non-recurring).</param>
/// <param name="periods">The list of periods to check against.</param>
/// <returns>List of calendar items (either the original item or expanded recurrence instances) that fall within the periods.</returns>
public async Task<List<CalendarItem>> GetExpandedRecurringEventsForPeriodsAsync(CalendarItem calendarItem, IEnumerable<ITimePeriod> periods)
{
var result = new List<CalendarItem>();
if (calendarItem == null || periods == null || !periods.Any())
{
return result;
}
// Ensure AssignedCalendar is loaded
if (calendarItem.AssignedCalendar == null)
{
calendarItem.AssignedCalendar = await GetAccountCalendarAsync(calendarItem.CalendarId);
}
// For non-recurring events, check if it overlaps with any of the provided periods
if (string.IsNullOrEmpty(calendarItem.Recurrence))
{
foreach (var period in periods)
{
if (calendarItem.Period.OverlapsWith(period))
{
result.Add(calendarItem);
break; // Add it only once
}
}
}
else
{
// For recurring events, expand occurrences for the combined date range of all periods
// Find the minimum and maximum dates across all periods
var minDate = periods.Min(p => p.Start);
var maxDate = periods.Max(p => p.End);
var combinedPeriod = new TimeRange(minDate, maxDate);
// Expand the recurring event for the combined period
var expandedOccurrences = await ExpandRecurringEventAsync(calendarItem, combinedPeriod);
// Filter occurrences that fall within any of the individual periods
foreach (var occurrence in expandedOccurrences)
{
foreach (var period in periods)
{
if (occurrence.Period.OverlapsWith(period))
{
result.Add(occurrence);
break; // Add it only once even if it overlaps with multiple periods
}
}
}
}
return result;
}
public Task<List<CalendarEventAttendee>> GetAttendeesAsync(Guid calendarEventTrackingId)
=> Connection.Table<CalendarEventAttendee>().Where(x => x.CalendarItemId == calendarEventTrackingId).ToListAsync();