Gmail calendar sync fix.
This commit is contained in:
@@ -46,36 +46,35 @@ public class GmailChangeProcessor : DefaultChangeProcessor, IGmailChangeProcesso
|
|||||||
|
|
||||||
// Check if we have this event before.
|
// Check if we have this event before.
|
||||||
var existingCalendarItem = await CalendarService.GetCalendarItemAsync(assignedCalendar.Id, calendarEvent.Id);
|
var existingCalendarItem = await CalendarService.GetCalendarItemAsync(assignedCalendar.Id, calendarEvent.Id);
|
||||||
|
CalendarItem parentRecurringEvent = null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(recurringEventId))
|
||||||
|
{
|
||||||
|
parentRecurringEvent = await CalendarService.GetCalendarItemAsync(assignedCalendar.Id, recurringEventId).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventStartDateTimeOffset = GoogleIntegratorExtensions.GetEventDateTimeOffset(calendarEvent.Start);
|
||||||
|
var eventEndDateTimeOffset = GoogleIntegratorExtensions.GetEventDateTimeOffset(calendarEvent.End);
|
||||||
|
var eventStartLocalDateTime = GoogleIntegratorExtensions.GetEventLocalDateTime(calendarEvent.Start);
|
||||||
|
var eventEndLocalDateTime = GoogleIntegratorExtensions.GetEventLocalDateTime(calendarEvent.End);
|
||||||
|
|
||||||
|
double totalDurationInSeconds = 0;
|
||||||
|
|
||||||
|
if (eventStartLocalDateTime != null && eventEndLocalDateTime != null)
|
||||||
|
{
|
||||||
|
totalDurationInSeconds = (eventEndLocalDateTime.Value - eventStartLocalDateTime.Value).TotalSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
if (existingCalendarItem == null)
|
if (existingCalendarItem == null)
|
||||||
{
|
{
|
||||||
CalendarItem parentRecurringEvent = null;
|
|
||||||
|
|
||||||
// Manage the recurring event id.
|
// Manage the recurring event id.
|
||||||
if (!string.IsNullOrEmpty(recurringEventId))
|
if (!string.IsNullOrEmpty(recurringEventId) && parentRecurringEvent == null)
|
||||||
{
|
{
|
||||||
parentRecurringEvent = await CalendarService.GetCalendarItemAsync(assignedCalendar.Id, recurringEventId).ConfigureAwait(false);
|
Log.Warning("Parent recurring event is missing for event {EventId}. Skipping creation until parent is available.", calendarEvent.Id);
|
||||||
|
return;
|
||||||
if (parentRecurringEvent == null)
|
|
||||||
{
|
|
||||||
Log.Information($"Parent recurring event is missing for event. Skipping creation of {calendarEvent.Id}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't have this event yet. Create a new one.
|
// We don't have this event yet. Create a new one.
|
||||||
var eventStartDateTimeOffset = GoogleIntegratorExtensions.GetEventDateTimeOffset(calendarEvent.Start);
|
|
||||||
var eventEndDateTimeOffset = GoogleIntegratorExtensions.GetEventDateTimeOffset(calendarEvent.End);
|
|
||||||
var eventStartLocalDateTime = GoogleIntegratorExtensions.GetEventLocalDateTime(calendarEvent.Start);
|
|
||||||
var eventEndLocalDateTime = GoogleIntegratorExtensions.GetEventLocalDateTime(calendarEvent.End);
|
|
||||||
|
|
||||||
double totalDurationInSeconds = 0;
|
|
||||||
|
|
||||||
if (eventStartLocalDateTime != null && eventEndLocalDateTime != null)
|
|
||||||
{
|
|
||||||
totalDurationInSeconds = (eventEndLocalDateTime.Value - eventStartLocalDateTime.Value).TotalSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
CalendarItem calendarItem = null;
|
CalendarItem calendarItem = null;
|
||||||
|
|
||||||
if (parentRecurringEvent != null)
|
if (parentRecurringEvent != null)
|
||||||
@@ -289,6 +288,8 @@ public class GmailChangeProcessor : DefaultChangeProcessor, IGmailChangeProcesso
|
|||||||
{
|
{
|
||||||
await CalendarService.InsertOrReplaceAttachmentsAsync(attachments).ConfigureAwait(false);
|
await CalendarService.InsertOrReplaceAttachmentsAsync(attachments).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -321,6 +322,44 @@ public class GmailChangeProcessor : DefaultChangeProcessor, IGmailChangeProcesso
|
|||||||
// Update the event properties.
|
// Update the event properties.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (eventStartLocalDateTime != null)
|
||||||
|
{
|
||||||
|
existingCalendarItem.StartDate = eventStartLocalDateTime.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalDurationInSeconds <= 0 && parentRecurringEvent != null)
|
||||||
|
{
|
||||||
|
totalDurationInSeconds = parentRecurringEvent.DurationInSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalDurationInSeconds > 0)
|
||||||
|
{
|
||||||
|
existingCalendarItem.DurationInSeconds = totalDurationInSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
existingCalendarItem.CalendarId = assignedCalendar.Id;
|
||||||
|
existingCalendarItem.RemoteEventId = calendarEvent.Id;
|
||||||
|
existingCalendarItem.Description = calendarEvent.Description ?? parentRecurringEvent?.Description ?? existingCalendarItem.Description;
|
||||||
|
existingCalendarItem.Location = string.IsNullOrEmpty(calendarEvent.Location) ? parentRecurringEvent?.Location ?? existingCalendarItem.Location : calendarEvent.Location;
|
||||||
|
existingCalendarItem.StartTimeZone = GoogleIntegratorExtensions.GetEventTimeZone(calendarEvent.Start) ?? parentRecurringEvent?.StartTimeZone ?? existingCalendarItem.StartTimeZone;
|
||||||
|
existingCalendarItem.EndTimeZone = GoogleIntegratorExtensions.GetEventTimeZone(calendarEvent.End) ?? parentRecurringEvent?.EndTimeZone ?? existingCalendarItem.EndTimeZone;
|
||||||
|
existingCalendarItem.Recurrence = GoogleIntegratorExtensions.GetRecurrenceString(calendarEvent) ?? existingCalendarItem.Recurrence ?? string.Empty;
|
||||||
|
existingCalendarItem.Status = GetStatus(calendarEvent.Status);
|
||||||
|
existingCalendarItem.Title = string.IsNullOrEmpty(calendarEvent.Summary) ? parentRecurringEvent?.Title ?? existingCalendarItem.Title : calendarEvent.Summary;
|
||||||
|
existingCalendarItem.UpdatedAt = DateTimeOffset.UtcNow;
|
||||||
|
existingCalendarItem.Visibility = string.IsNullOrEmpty(calendarEvent.Visibility) ? parentRecurringEvent?.Visibility ?? existingCalendarItem.Visibility : GetVisibility(calendarEvent.Visibility);
|
||||||
|
existingCalendarItem.ShowAs = string.IsNullOrEmpty(calendarEvent.Transparency) ? parentRecurringEvent?.ShowAs ?? existingCalendarItem.ShowAs : GetShowAs(calendarEvent.Transparency);
|
||||||
|
existingCalendarItem.HtmlLink = string.IsNullOrEmpty(calendarEvent.HtmlLink) ? parentRecurringEvent?.HtmlLink ?? existingCalendarItem.HtmlLink : calendarEvent.HtmlLink;
|
||||||
|
existingCalendarItem.IsLocked = calendarEvent.Locked.GetValueOrDefault(existingCalendarItem.IsLocked);
|
||||||
|
existingCalendarItem.OrganizerDisplayName = string.IsNullOrEmpty(GetOrganizerName(calendarEvent, organizerAccount)) ? parentRecurringEvent?.OrganizerDisplayName ?? existingCalendarItem.OrganizerDisplayName : GetOrganizerName(calendarEvent, organizerAccount);
|
||||||
|
existingCalendarItem.OrganizerEmail = string.IsNullOrEmpty(GetOrganizerEmail(calendarEvent, organizerAccount)) ? parentRecurringEvent?.OrganizerEmail ?? existingCalendarItem.OrganizerEmail : GetOrganizerEmail(calendarEvent, organizerAccount);
|
||||||
|
existingCalendarItem.AssignedCalendar = assignedCalendar;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(recurringEventId) && parentRecurringEvent != null)
|
||||||
|
{
|
||||||
|
existingCalendarItem.RecurringCalendarItemId = parentRecurringEvent.Id;
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare reminders list from Gmail event for update
|
// Prepare reminders list from Gmail event for update
|
||||||
List<Reminder> reminders = null;
|
List<Reminder> reminders = null;
|
||||||
if (calendarEvent.Reminders?.Overrides != null && calendarEvent.Reminders.Overrides.Count > 0)
|
if (calendarEvent.Reminders?.Overrides != null && calendarEvent.Reminders.Overrides.Count > 0)
|
||||||
|
|||||||
@@ -576,18 +576,79 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
|||||||
// allEvents contains new or updated events.
|
// allEvents contains new or updated events.
|
||||||
// Process them and create/update local calendar items.
|
// Process them and create/update local calendar items.
|
||||||
|
|
||||||
foreach (var @event in allEvents)
|
var eventByRemoteId = allEvents
|
||||||
{
|
.Where(e => !string.IsNullOrWhiteSpace(e.Id))
|
||||||
// TODO: Exception handling for event processing.
|
.GroupBy(e => e.Id, StringComparer.Ordinal)
|
||||||
// TODO: Also update attendees and other properties.
|
.ToDictionary(g => g.Key, g => g.First(), StringComparer.Ordinal);
|
||||||
|
|
||||||
|
foreach (var @event in OrderCalendarEventsForPersistence(allEvents))
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
await EnsureRecurringParentProcessedAsync(calendar, @event, eventByRemoteId, cancellationToken).ConfigureAwait(false);
|
||||||
await _gmailChangeProcessor.ManageCalendarEventAsync(@event, calendar, Account).ConfigureAwait(false);
|
await _gmailChangeProcessor.ManageCalendarEventAsync(@event, calendar, Account).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _gmailChangeProcessor.UpdateAccountCalendarAsync(calendar).ConfigureAwait(false);
|
await _gmailChangeProcessor.UpdateAccountCalendarAsync(calendar).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
return CalendarSynchronizationResult.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<Event> OrderCalendarEventsForPersistence(IEnumerable<Event> events)
|
||||||
|
=> events
|
||||||
|
.OrderBy(e => !string.IsNullOrWhiteSpace(e.RecurringEventId))
|
||||||
|
.ThenByDescending(e => !string.IsNullOrWhiteSpace(GoogleIntegratorExtensions.GetRecurrenceString(e)))
|
||||||
|
.ThenBy(e => GoogleIntegratorExtensions.GetEventDateTimeOffset(e.Start) ?? DateTimeOffset.MinValue);
|
||||||
|
|
||||||
|
private async Task EnsureRecurringParentProcessedAsync(
|
||||||
|
AccountCalendar calendar,
|
||||||
|
Event calendarEvent,
|
||||||
|
Dictionary<string, Event> eventByRemoteId,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var recurringEventId = calendarEvent?.RecurringEventId;
|
||||||
|
if (string.IsNullOrWhiteSpace(recurringEventId))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var parentItem = await _gmailChangeProcessor.GetCalendarItemAsync(calendar.Id, recurringEventId).ConfigureAwait(false);
|
||||||
|
if (parentItem != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!eventByRemoteId.TryGetValue(recurringEventId, out var parentEvent))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
parentEvent = await _calendarService.Events.Get(calendar.RemoteCalendarId, recurringEventId)
|
||||||
|
.ExecuteAsync(cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (GoogleApiException ex)
|
||||||
|
{
|
||||||
|
_logger.Warning(ex,
|
||||||
|
"Failed to fetch recurring parent {ParentRemoteEventId} for child {ChildRemoteEventId} in calendar {CalendarName}",
|
||||||
|
recurringEventId,
|
||||||
|
calendarEvent.Id,
|
||||||
|
calendar.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentEvent != null && !string.IsNullOrWhiteSpace(parentEvent.Id))
|
||||||
|
{
|
||||||
|
eventByRemoteId[parentEvent.Id] = parentEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentEvent == null)
|
||||||
|
{
|
||||||
|
_logger.Warning(
|
||||||
|
"Recurring parent {ParentRemoteEventId} is still missing for child {ChildRemoteEventId} in calendar {CalendarName}",
|
||||||
|
recurringEventId,
|
||||||
|
calendarEvent.Id,
|
||||||
|
calendar.Name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _gmailChangeProcessor.ManageCalendarEventAsync(parentEvent, calendar, Account).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SynchronizeCalendarsAsync(CancellationToken cancellationToken = default)
|
private async Task SynchronizeCalendarsAsync(CancellationToken cancellationToken = default)
|
||||||
|
|||||||
Reference in New Issue
Block a user