From a912ada89076bdc6eba66d205d3b186918d29ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Fri, 20 Feb 2026 10:03:16 +0100 Subject: [PATCH] Fixing some messaging issues with calendar add/delete. --- .../CalendarPageViewModel.cs | 59 ++++++++++++++++--- Wino.Core/Synchronizers/WinoSynchronizer.cs | 28 ++++++--- 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/Wino.Calendar.ViewModels/CalendarPageViewModel.cs b/Wino.Calendar.ViewModels/CalendarPageViewModel.cs index 269af68c..b067c279 100644 --- a/Wino.Calendar.ViewModels/CalendarPageViewModel.cs +++ b/Wino.Calendar.ViewModels/CalendarPageViewModel.cs @@ -8,6 +8,7 @@ using CommunityToolkit.Diagnostics; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; +using Itenso.TimePeriod; using MoreLinq; using Serilog; using Wino.Calendar.ViewModels.Data; @@ -1001,8 +1002,11 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel, Debug.WriteLine($"Calendar item deleted: {calendarItem.Id}"); - // Check if the deleted item is currently displayed in details view - if (DisplayDetailsCalendarItemViewModel?.Id == calendarItem.Id) + // Check if the deleted item (or its series master) is currently displayed in details view. + var isDeletedDetailsItem = DisplayDetailsCalendarItemViewModel?.Id == calendarItem.Id; + var isDeletedSeriesMasterOfDetailsItem = DisplayDetailsCalendarItemViewModel?.CalendarItem?.RecurringCalendarItemId == calendarItem.Id; + + if (isDeletedDetailsItem || isDeletedSeriesMasterOfDetailsItem) { // Clear the details view since this item was deleted DisplayDetailsCalendarItemViewModel = null; @@ -1015,11 +1019,9 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel, { foreach (var calendarDay in dayRange.CalendarDays) { - var existingItem = calendarDay.EventsCollection.GetCalendarItem(calendarItem.Id); - if (existingItem != null) - { - calendarDay.EventsCollection.RemoveCalendarItem(existingItem); - } + calendarDay.EventsCollection.RemoveCalendarItems(item => + item.Id == calendarItem.Id || + (item is CalendarItemViewModel vm && vm.CalendarItem.RecurringCalendarItemId == calendarItem.Id)); } } }); @@ -1119,9 +1121,11 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel, // Series master events should not be visible on the UI. // Their instances are already expanded and synced individually. + // For revert scenarios, restore visible child instances from local storage. if (calendarItem.IsRecurringParent) { Debug.WriteLine($"Skipping series master event: {calendarItem.Title}"); + await RestoreVisibleRecurringSeriesInstancesAsync(calendarItem); return; } @@ -1167,4 +1171,45 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel, FilterActiveCalendars(DayRanges); } + + private async Task RestoreVisibleRecurringSeriesInstancesAsync(CalendarItem recurringParent) + { + if (DayRanges.DisplayRange == null || recurringParent?.AssignedCalendar == null) + return; + + var visibleRange = new TimeRange(DayRanges.DisplayRange.StartDate, DayRanges.DisplayRange.EndDate); + var visibleItems = await _calendarService.GetCalendarEventsAsync(recurringParent.AssignedCalendar, visibleRange).ConfigureAwait(false); + + var recurringChildren = visibleItems + .Where(item => item.RecurringCalendarItemId == recurringParent.Id && !item.IsHidden && !item.IsRecurringParent) + .ToList(); + + if (!recurringChildren.Any()) + return; + + await ExecuteUIThread(() => + { + foreach (var child in recurringChildren) + { + child.AssignedCalendar ??= recurringParent.AssignedCalendar; + + var targetDays = DayRanges + .SelectMany(a => a.CalendarDays) + .Where(day => day.Period.OverlapsWith(child.Period)); + + foreach (var day in targetDays) + { + if (day.EventsCollection.GetCalendarItem(child.Id) != null) + continue; + + day.EventsCollection.AddCalendarItem(new CalendarItemViewModel(child) + { + IsBusy = string.IsNullOrEmpty(child.RemoteEventId) + }); + } + } + }); + + FilterActiveCalendars(DayRanges); + } } diff --git a/Wino.Core/Synchronizers/WinoSynchronizer.cs b/Wino.Core/Synchronizers/WinoSynchronizer.cs index 17a4a7d5..bfb3f66f 100644 --- a/Wino.Core/Synchronizers/WinoSynchronizer.cs +++ b/Wino.Core/Synchronizers/WinoSynchronizer.cs @@ -370,29 +370,43 @@ public abstract class WinoSynchronizer() + .SelectMany(CreateCalendarEvent)); break; case CalendarSynchronizerOperation.AcceptEvent: - nativeRequests.AddRange(AcceptEvent(group.ElementAt(0) as AcceptEventRequest)); + nativeRequests.AddRange(group + .OfType() + .SelectMany(AcceptEvent)); break; case CalendarSynchronizerOperation.DeclineEvent: if (Account.ProviderType == MailProviderType.Outlook) { - nativeRequests.AddRange(OutlookDeclineEvent(group.ElementAt(0) as OutlookDeclineEventRequest)); + nativeRequests.AddRange(group + .OfType() + .SelectMany(OutlookDeclineEvent)); } else { - nativeRequests.AddRange(DeclineEvent(group.ElementAt(0) as DeclineEventRequest)); + nativeRequests.AddRange(group + .OfType() + .SelectMany(DeclineEvent)); } break; case CalendarSynchronizerOperation.TentativeEvent: - nativeRequests.AddRange(TentativeEvent(group.ElementAt(0) as TentativeEventRequest)); + nativeRequests.AddRange(group + .OfType() + .SelectMany(TentativeEvent)); break; case CalendarSynchronizerOperation.UpdateEvent: - nativeRequests.AddRange(UpdateCalendarEvent(group.ElementAt(0) as UpdateCalendarEventRequest)); + nativeRequests.AddRange(group + .OfType() + .SelectMany(UpdateCalendarEvent)); break; case CalendarSynchronizerOperation.DeleteEvent: - nativeRequests.AddRange(DeleteCalendarEvent(group.ElementAt(0) as DeleteCalendarEventRequest)); + nativeRequests.AddRange(group + .OfType() + .SelectMany(DeleteCalendarEvent)); break; default: break;