Refactored the calendar synchronization code using AI.
This commit is contained in:
@@ -21,7 +21,7 @@ namespace Wino.Core.Integration.Processors;
|
||||
/// <see cref="IGmailChangeProcessor"/>, <see cref="IOutlookChangeProcessor"/> and <see cref="IImapChangeProcessor"/>
|
||||
/// None of the synchronizers can directly change anything in the database.
|
||||
/// </summary>
|
||||
public interface IDefaultChangeProcessor
|
||||
public interface IDefaultChangeProcessor : ICalendarServiceEx
|
||||
{
|
||||
Task UpdateAccountAsync(MailAccount account);
|
||||
// Task<string> UpdateAccountDeltaSynchronizationIdentifierAsync(Guid accountId, string deltaSynchronizationIdentifier);
|
||||
@@ -113,13 +113,14 @@ public class DefaultChangeProcessor(IDatabaseService databaseService,
|
||||
IMailService mailService,
|
||||
ICalendarService calendarService,
|
||||
IAccountService accountService,
|
||||
IMimeFileService mimeFileService) : BaseDatabaseService(databaseService), IDefaultChangeProcessor
|
||||
ICalendarServiceEx calendarServiceEx,
|
||||
IMimeFileService mimeFileService) : BaseDatabaseService(databaseService), IDefaultChangeProcessor, ICalendarServiceEx
|
||||
{
|
||||
protected IMailService MailService = mailService;
|
||||
protected ICalendarService CalendarService = calendarService;
|
||||
protected IFolderService FolderService = folderService;
|
||||
protected IAccountService AccountService = accountService;
|
||||
|
||||
private readonly ICalendarServiceEx _calendarServiceEx = calendarServiceEx;
|
||||
private readonly IMimeFileService _mimeFileService = mimeFileService;
|
||||
|
||||
public Task<string> UpdateAccountDeltaSynchronizationIdentifierAsync(Guid accountId, string synchronizationDeltaIdentifier)
|
||||
@@ -208,4 +209,234 @@ public class DefaultChangeProcessor(IDatabaseService databaseService,
|
||||
|
||||
public Task<bool> IsMailExistsInFolderAsync(string messageId, Guid folderId)
|
||||
=> MailService.IsMailExistsAsync(messageId, folderId);
|
||||
|
||||
// TODO: Normalize this shit. Not everything needs to be exposed for processor.
|
||||
#region ICalendarServiceEx
|
||||
|
||||
public Task<int> ClearAllCalendarEventAttendeesAsync()
|
||||
{
|
||||
return _calendarServiceEx.ClearAllCalendarEventAttendeesAsync();
|
||||
}
|
||||
|
||||
public Task<int> ClearAllCalendarsAsync()
|
||||
{
|
||||
return _calendarServiceEx.ClearAllCalendarsAsync();
|
||||
}
|
||||
|
||||
public Task<int> ClearAllDataAsync()
|
||||
{
|
||||
return _calendarServiceEx.ClearAllDataAsync();
|
||||
}
|
||||
|
||||
public Task<int> ClearAllEventsAsync()
|
||||
{
|
||||
return _calendarServiceEx.ClearAllEventsAsync();
|
||||
}
|
||||
|
||||
public Task<int> DeleteCalendarAsync(string remoteCalendarId)
|
||||
{
|
||||
return _calendarServiceEx.DeleteCalendarAsync(remoteCalendarId);
|
||||
}
|
||||
|
||||
public Task<int> DeleteCalendarEventAttendeesForEventAsync(Guid eventId)
|
||||
{
|
||||
return _calendarServiceEx.DeleteCalendarEventAttendeesForEventAsync(eventId);
|
||||
}
|
||||
|
||||
public Task<int> DeleteEventAsync(string remoteEventId)
|
||||
{
|
||||
return _calendarServiceEx.DeleteEventAsync(remoteEventId);
|
||||
}
|
||||
|
||||
public Task<List<CalendarEventAttendee>> GetAllCalendarEventAttendeesAsync()
|
||||
{
|
||||
return _calendarServiceEx.GetAllCalendarEventAttendeesAsync();
|
||||
}
|
||||
|
||||
public Task<List<AccountCalendar>> GetAllCalendarsAsync()
|
||||
{
|
||||
return _calendarServiceEx.GetAllCalendarsAsync();
|
||||
}
|
||||
|
||||
public Task<List<CalendarItem>> GetAllDayEventsAsync()
|
||||
{
|
||||
return _calendarServiceEx.GetAllDayEventsAsync();
|
||||
}
|
||||
|
||||
public Task<List<CalendarItem>> GetAllEventsAsync()
|
||||
{
|
||||
return _calendarServiceEx.GetAllEventsAsync();
|
||||
}
|
||||
|
||||
public Task<List<CalendarItem>> GetAllEventsIncludingDeletedAsync()
|
||||
{
|
||||
return _calendarServiceEx.GetAllEventsIncludingDeletedAsync();
|
||||
}
|
||||
|
||||
public Task<List<CalendarItem>> GetAllRecurringEventsByTypeAsync()
|
||||
{
|
||||
return _calendarServiceEx.GetAllRecurringEventsByTypeAsync();
|
||||
}
|
||||
|
||||
public Task<AccountCalendar> GetCalendarByRemoteIdAsync(string remoteCalendarId)
|
||||
{
|
||||
return _calendarServiceEx.GetCalendarByRemoteIdAsync(remoteCalendarId);
|
||||
}
|
||||
|
||||
public Task<Dictionary<AttendeeResponseStatus, int>> GetCalendarEventAttendeeResponseCountsAsync(Guid eventId)
|
||||
{
|
||||
return _calendarServiceEx.GetCalendarEventAttendeeResponseCountsAsync(eventId);
|
||||
}
|
||||
|
||||
public Task<List<CalendarEventAttendee>> GetCalendarEventAttendeesForEventAsync(Guid eventId)
|
||||
{
|
||||
return _calendarServiceEx.GetCalendarEventAttendeesForEventAsync(eventId);
|
||||
}
|
||||
|
||||
public Task<List<CalendarEventAttendee>> GetCalendarEventAttendeesForEventByRemoteIdAsync(string remoteEventId)
|
||||
{
|
||||
return _calendarServiceEx.GetCalendarEventAttendeesForEventByRemoteIdAsync(remoteEventId);
|
||||
}
|
||||
|
||||
public Task<string> GetCalendarSyncTokenAsync(string calendarId)
|
||||
{
|
||||
return _calendarServiceEx.GetCalendarSyncTokenAsync(calendarId);
|
||||
}
|
||||
|
||||
public Task<CalendarItem> GetEventByRemoteIdAsync(string remoteEventId)
|
||||
{
|
||||
return _calendarServiceEx.GetEventByRemoteIdAsync(remoteEventId);
|
||||
}
|
||||
|
||||
public Task<List<CalendarItem>> GetEventsByItemTypeAsync(CalendarItemType itemType)
|
||||
{
|
||||
return _calendarServiceEx.GetEventsByItemTypeAsync(itemType);
|
||||
}
|
||||
|
||||
public Task<List<CalendarItem>> GetEventsByItemTypesAsync(params CalendarItemType[] itemTypes)
|
||||
{
|
||||
return _calendarServiceEx.GetEventsByItemTypesAsync(itemTypes);
|
||||
}
|
||||
|
||||
public Task<List<CalendarItem>> GetEventsByremoteCalendarIdAsync(string remoteCalendarId)
|
||||
{
|
||||
return _calendarServiceEx.GetEventsByremoteCalendarIdAsync(remoteCalendarId);
|
||||
}
|
||||
|
||||
public Task<List<CalendarItem>> GetEventsForCalendarAsync(Guid calendarId)
|
||||
{
|
||||
return _calendarServiceEx.GetEventsForCalendarAsync(calendarId);
|
||||
}
|
||||
|
||||
public Task<List<CalendarItem>> GetEventsInDateRangeAsync(DateTime startDate, DateTime endDate)
|
||||
{
|
||||
return _calendarServiceEx.GetEventsInDateRangeAsync(startDate, endDate);
|
||||
}
|
||||
|
||||
public Task<List<CalendarItem>> GetEventsSinceLastSyncAsync(DateTime? lastSyncTime)
|
||||
{
|
||||
return _calendarServiceEx.GetEventsSinceLastSyncAsync(lastSyncTime);
|
||||
}
|
||||
|
||||
public Task<Dictionary<CalendarItemType, int>> GetEventStatsByItemTypeAsync()
|
||||
{
|
||||
return _calendarServiceEx.GetEventStatsByItemTypeAsync();
|
||||
}
|
||||
|
||||
public Task<List<CalendarItem>> GetExpandedEventsInDateRangeAsync(DateTime startDate, DateTime endDate)
|
||||
{
|
||||
return _calendarServiceEx.GetExpandedEventsInDateRangeAsync(startDate, endDate);
|
||||
}
|
||||
|
||||
public Task<List<CalendarItem>> GetExpandedEventsInDateRangeWithExceptionsAsync(DateTime startDate, DateTime endDate, AccountCalendar calendar)
|
||||
{
|
||||
return _calendarServiceEx.GetExpandedEventsInDateRangeWithExceptionsAsync(startDate, endDate, calendar);
|
||||
}
|
||||
|
||||
public Task<DateTime?> GetLastSyncTimeAsync(string calendarId)
|
||||
{
|
||||
return _calendarServiceEx.GetLastSyncTimeAsync(calendarId);
|
||||
}
|
||||
|
||||
public Task<List<CalendarItem>> GetMultiDayEventsAsync()
|
||||
{
|
||||
return _calendarServiceEx.GetMultiDayEventsAsync();
|
||||
}
|
||||
|
||||
public Task<List<CalendarItem>> GetRecurringEventsAsync()
|
||||
{
|
||||
return _calendarServiceEx.GetRecurringEventsAsync();
|
||||
}
|
||||
|
||||
public Task<int> HardDeleteEventAsync(string remoteEventId)
|
||||
{
|
||||
return _calendarServiceEx.HardDeleteEventAsync(remoteEventId);
|
||||
}
|
||||
|
||||
public Task<int> InsertCalendarAsync(AccountCalendar calendar)
|
||||
{
|
||||
return _calendarServiceEx.InsertCalendarAsync(calendar);
|
||||
}
|
||||
|
||||
public Task<int> InsertCalendarEventAttendeeAsync(CalendarEventAttendee calendareventattendee)
|
||||
{
|
||||
return _calendarServiceEx.InsertCalendarEventAttendeeAsync(calendareventattendee);
|
||||
}
|
||||
|
||||
public Task<int> InsertEventAsync(CalendarItem calendarItem)
|
||||
{
|
||||
return _calendarServiceEx.InsertEventAsync(calendarItem);
|
||||
}
|
||||
|
||||
public Task<int> MarkEventAsDeletedAsync(string remoteEventId, string remoteCalendarId)
|
||||
{
|
||||
return _calendarServiceEx.MarkEventAsDeletedAsync(remoteEventId, remoteCalendarId);
|
||||
}
|
||||
|
||||
public Task<int> SyncCalendarEventAttendeesForEventAsync(Guid eventId, List<CalendarEventAttendee> calendareventattendees)
|
||||
{
|
||||
return _calendarServiceEx.SyncCalendarEventAttendeesForEventAsync(eventId, calendareventattendees);
|
||||
}
|
||||
|
||||
public Task<int> UpdateAllEventItemTypesAsync()
|
||||
{
|
||||
return _calendarServiceEx.UpdateAllEventItemTypesAsync();
|
||||
}
|
||||
|
||||
public Task<int> UpdateCalendarAsync(AccountCalendar calendar)
|
||||
{
|
||||
return _calendarServiceEx.UpdateCalendarAsync(calendar);
|
||||
}
|
||||
|
||||
public Task<int> UpdateCalendarEventAttendeeAsync(CalendarEventAttendee calendareventattendee)
|
||||
{
|
||||
return _calendarServiceEx.UpdateCalendarEventAttendeeAsync(calendareventattendee);
|
||||
}
|
||||
|
||||
public Task<int> UpdateCalendarSyncTokenAsync(string calendarId, string syncToken)
|
||||
{
|
||||
return _calendarServiceEx.UpdateCalendarSyncTokenAsync(calendarId, syncToken);
|
||||
}
|
||||
|
||||
public Task<int> UpdateEventAsync(CalendarItem calendarItem)
|
||||
{
|
||||
return _calendarServiceEx.UpdateEventAsync(calendarItem);
|
||||
}
|
||||
|
||||
public Task<int> UpsertCalendarAsync(AccountCalendar calendar)
|
||||
{
|
||||
return _calendarServiceEx.UpsertCalendarAsync(calendar);
|
||||
}
|
||||
|
||||
public Task<int> UpsertEventAsync(CalendarItem calendarItem)
|
||||
{
|
||||
return _calendarServiceEx.UpsertEventAsync(calendarItem);
|
||||
}
|
||||
|
||||
public Task<int> SyncAttendeesForEventAsync(Guid eventId, List<CalendarEventAttendee> attendees)
|
||||
{
|
||||
return _calendarServiceEx.SyncAttendeesForEventAsync(eventId, attendees);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Google.Apis.Calendar.v3.Data;
|
||||
using Serilog;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
using Wino.Core.Extensions;
|
||||
using Wino.Services;
|
||||
using CalendarEventAttendee = Wino.Core.Domain.Entities.Calendar.CalendarEventAttendee;
|
||||
using CalendarItem = Wino.Core.Domain.Entities.Calendar.CalendarItem;
|
||||
|
||||
namespace Wino.Core.Integration.Processors;
|
||||
|
||||
@@ -23,7 +18,8 @@ public class GmailChangeProcessor : DefaultChangeProcessor, IGmailChangeProcesso
|
||||
IMailService mailService,
|
||||
ICalendarService calendarService,
|
||||
IAccountService accountService,
|
||||
IMimeFileService mimeFileService) : base(databaseService, folderService, mailService, calendarService, accountService, mimeFileService)
|
||||
ICalendarServiceEx calendarServiceEx,
|
||||
IMimeFileService mimeFileService) : base(databaseService, folderService, mailService, calendarService, accountService, calendarServiceEx, mimeFileService)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -36,224 +32,7 @@ public class GmailChangeProcessor : DefaultChangeProcessor, IGmailChangeProcesso
|
||||
|
||||
public async Task ManageCalendarEventAsync(Event calendarEvent, AccountCalendar assignedCalendar, MailAccount organizerAccount)
|
||||
{
|
||||
var status = calendarEvent.Status;
|
||||
|
||||
var recurringEventId = calendarEvent.RecurringEventId;
|
||||
|
||||
// 1. Canceled exceptions of recurred events are only guaranteed to have recurringEventId, Id and start time.
|
||||
// 2. Updated exceptions of recurred events have different Id, but recurringEventId is the same as parent.
|
||||
|
||||
// Check if we have this event before.
|
||||
var existingCalendarItem = await CalendarService.GetCalendarItemAsync(assignedCalendar.Id, calendarEvent.Id);
|
||||
|
||||
if (existingCalendarItem == null)
|
||||
{
|
||||
CalendarItem parentRecurringEvent = null;
|
||||
|
||||
// Manage the recurring event id.
|
||||
if (!string.IsNullOrEmpty(recurringEventId))
|
||||
{
|
||||
parentRecurringEvent = await CalendarService.GetCalendarItemAsync(assignedCalendar.Id, recurringEventId).ConfigureAwait(false);
|
||||
|
||||
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.
|
||||
var eventStartDateTimeOffset = GoogleIntegratorExtensions.GetEventDateTimeOffset(calendarEvent.Start);
|
||||
var eventEndDateTimeOffset = GoogleIntegratorExtensions.GetEventDateTimeOffset(calendarEvent.End);
|
||||
|
||||
double totalDurationInSeconds = 0;
|
||||
|
||||
if (eventStartDateTimeOffset != null && eventEndDateTimeOffset != null)
|
||||
{
|
||||
totalDurationInSeconds = (eventEndDateTimeOffset.Value - eventStartDateTimeOffset.Value).TotalSeconds;
|
||||
}
|
||||
|
||||
CalendarItem calendarItem = null;
|
||||
|
||||
if (parentRecurringEvent != null)
|
||||
{
|
||||
// Exceptions of parent events might not have all the fields populated.
|
||||
// We must use the parent event's data for fields that don't exists.
|
||||
|
||||
// Update duration if it's not populated.
|
||||
if (totalDurationInSeconds == 0)
|
||||
{
|
||||
totalDurationInSeconds = parentRecurringEvent.DurationInSeconds;
|
||||
}
|
||||
|
||||
var organizerMail = GetOrganizerEmail(calendarEvent, organizerAccount);
|
||||
var organizerName = GetOrganizerName(calendarEvent, organizerAccount);
|
||||
|
||||
|
||||
calendarItem = new CalendarItem()
|
||||
{
|
||||
CalendarId = assignedCalendar.Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
Description = calendarEvent.Description ?? parentRecurringEvent.Description,
|
||||
Id = Guid.NewGuid(),
|
||||
StartDate = eventStartDateTimeOffset.Value.DateTime,
|
||||
StartDateOffset = eventStartDateTimeOffset.Value.Offset,
|
||||
EndDateOffset = eventEndDateTimeOffset?.Offset ?? parentRecurringEvent.EndDateOffset,
|
||||
DurationInSeconds = totalDurationInSeconds,
|
||||
Location = string.IsNullOrEmpty(calendarEvent.Location) ? parentRecurringEvent.Location : calendarEvent.Location,
|
||||
|
||||
// Leave it empty if it's not populated.
|
||||
Recurrence = GoogleIntegratorExtensions.GetRecurrenceString(calendarEvent) == null ? string.Empty : GoogleIntegratorExtensions.GetRecurrenceString(calendarEvent),
|
||||
Status = GetStatus(calendarEvent.Status),
|
||||
Title = string.IsNullOrEmpty(calendarEvent.Summary) ? parentRecurringEvent.Title : calendarEvent.Summary,
|
||||
UpdatedAt = DateTimeOffset.UtcNow,
|
||||
Visibility = string.IsNullOrEmpty(calendarEvent.Visibility) ? parentRecurringEvent.Visibility : GetVisibility(calendarEvent.Visibility),
|
||||
HtmlLink = string.IsNullOrEmpty(calendarEvent.HtmlLink) ? parentRecurringEvent.HtmlLink : calendarEvent.HtmlLink,
|
||||
RemoteEventId = calendarEvent.Id,
|
||||
IsLocked = calendarEvent.Locked.GetValueOrDefault(),
|
||||
OrganizerDisplayName = string.IsNullOrEmpty(organizerName) ? parentRecurringEvent.OrganizerDisplayName : organizerName,
|
||||
OrganizerEmail = string.IsNullOrEmpty(organizerMail) ? parentRecurringEvent.OrganizerEmail : organizerMail
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a parent event creation.
|
||||
// Start-End dates are guaranteed to be populated.
|
||||
|
||||
if (eventStartDateTimeOffset == null || eventEndDateTimeOffset == null)
|
||||
{
|
||||
Log.Error("Failed to create parent event because either start or end date is not specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
calendarItem = new CalendarItem()
|
||||
{
|
||||
CalendarId = assignedCalendar.Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
Description = calendarEvent.Description,
|
||||
Id = Guid.NewGuid(),
|
||||
StartDate = eventStartDateTimeOffset.Value.DateTime,
|
||||
StartDateOffset = eventStartDateTimeOffset.Value.Offset,
|
||||
EndDateOffset = eventEndDateTimeOffset.Value.Offset,
|
||||
DurationInSeconds = totalDurationInSeconds,
|
||||
Location = calendarEvent.Location,
|
||||
Recurrence = GoogleIntegratorExtensions.GetRecurrenceString(calendarEvent),
|
||||
Status = GetStatus(calendarEvent.Status),
|
||||
Title = calendarEvent.Summary,
|
||||
UpdatedAt = DateTimeOffset.UtcNow,
|
||||
Visibility = GetVisibility(calendarEvent.Visibility),
|
||||
HtmlLink = calendarEvent.HtmlLink,
|
||||
RemoteEventId = calendarEvent.Id,
|
||||
IsLocked = calendarEvent.Locked.GetValueOrDefault(),
|
||||
OrganizerDisplayName = GetOrganizerName(calendarEvent, organizerAccount),
|
||||
OrganizerEmail = GetOrganizerEmail(calendarEvent, organizerAccount)
|
||||
};
|
||||
}
|
||||
|
||||
// Hide canceled events.
|
||||
calendarItem.IsHidden = calendarItem.Status == CalendarItemStatus.Cancelled;
|
||||
|
||||
// Manage the recurring event id.
|
||||
if (parentRecurringEvent != null)
|
||||
{
|
||||
calendarItem.RecurringCalendarItemId = parentRecurringEvent.Id;
|
||||
}
|
||||
|
||||
Debug.WriteLine($"({assignedCalendar.Name}) {calendarItem.Title}, Start: {calendarItem.StartDate.ToString("f")}, End: {calendarItem.EndDate.ToString("f")}");
|
||||
|
||||
// Attendees
|
||||
var attendees = new List<CalendarEventAttendee>();
|
||||
|
||||
if (calendarEvent.Attendees == null)
|
||||
{
|
||||
// Self-only event.
|
||||
|
||||
attendees.Add(new CalendarEventAttendee()
|
||||
{
|
||||
CalendarItemId = calendarItem.Id,
|
||||
IsOrganizer = true,
|
||||
Email = organizerAccount.Address,
|
||||
Name = organizerAccount.SenderName,
|
||||
AttendenceStatus = AttendeeStatus.Accepted,
|
||||
Id = Guid.NewGuid(),
|
||||
IsOptionalAttendee = false,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var attendee in calendarEvent.Attendees)
|
||||
{
|
||||
if (attendee.Self == true)
|
||||
{
|
||||
// TODO:
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(attendee.Email))
|
||||
{
|
||||
AttendeeStatus GetAttendenceStatus(string responseStatus)
|
||||
{
|
||||
return responseStatus switch
|
||||
{
|
||||
"accepted" => AttendeeStatus.Accepted,
|
||||
"declined" => AttendeeStatus.Declined,
|
||||
"tentative" => AttendeeStatus.Tentative,
|
||||
"needsAction" => AttendeeStatus.NeedsAction,
|
||||
_ => AttendeeStatus.NeedsAction
|
||||
};
|
||||
}
|
||||
|
||||
var eventAttendee = new CalendarEventAttendee()
|
||||
{
|
||||
CalendarItemId = calendarItem.Id,
|
||||
IsOrganizer = attendee.Organizer ?? false,
|
||||
Comment = attendee.Comment,
|
||||
Email = attendee.Email,
|
||||
Name = attendee.DisplayName,
|
||||
AttendenceStatus = GetAttendenceStatus(attendee.ResponseStatus),
|
||||
Id = Guid.NewGuid(),
|
||||
IsOptionalAttendee = attendee.Optional ?? false,
|
||||
};
|
||||
|
||||
attendees.Add(eventAttendee);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await CalendarService.CreateNewCalendarItemAsync(calendarItem, attendees);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have this event already. Update it.
|
||||
if (calendarEvent.Status == "cancelled")
|
||||
{
|
||||
// Parent event is canceled. We must delete everything.
|
||||
if (string.IsNullOrEmpty(recurringEventId))
|
||||
{
|
||||
Log.Information("Parent event is canceled. Deleting all instances of {Id}", existingCalendarItem.Id);
|
||||
|
||||
await CalendarService.DeleteCalendarItemAsync(existingCalendarItem.Id).ConfigureAwait(false);
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Child event is canceled.
|
||||
// Child should live as long as parent lives, but must not be displayed to the user.
|
||||
|
||||
existingCalendarItem.IsHidden = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure to unhide the event.
|
||||
// It might be marked as hidden before.
|
||||
existingCalendarItem.IsHidden = false;
|
||||
|
||||
// Update the event properties.
|
||||
}
|
||||
}
|
||||
|
||||
// Upsert the event.
|
||||
await Connection.InsertOrReplaceAsync(existingCalendarItem);
|
||||
// TODO:
|
||||
}
|
||||
|
||||
private string GetOrganizerName(Event calendarEvent, MailAccount account)
|
||||
|
||||
@@ -13,7 +13,8 @@ public class ImapChangeProcessor : DefaultChangeProcessor, IImapChangeProcessor
|
||||
IMailService mailService,
|
||||
IAccountService accountService,
|
||||
ICalendarService calendarService,
|
||||
IMimeFileService mimeFileService) : base(databaseService, folderService, mailService, calendarService, accountService, mimeFileService)
|
||||
ICalendarServiceEx calendarServiceEx,
|
||||
IMimeFileService mimeFileService) : base(databaseService, folderService, mailService, calendarService, accountService, calendarServiceEx, mimeFileService)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Graph.Models;
|
||||
using Serilog;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Extensions;
|
||||
using Wino.Services;
|
||||
|
||||
namespace Wino.Core.Integration.Processors;
|
||||
@@ -17,7 +13,8 @@ public class OutlookChangeProcessor(IDatabaseService databaseService,
|
||||
ICalendarService calendarService,
|
||||
IMailService mailService,
|
||||
IAccountService accountService,
|
||||
IMimeFileService mimeFileService) : DefaultChangeProcessor(databaseService, folderService, mailService, calendarService, accountService, mimeFileService)
|
||||
ICalendarServiceEx calendarServiceEx,
|
||||
IMimeFileService mimeFileService) : DefaultChangeProcessor(databaseService, folderService, mailService, calendarService, accountService, calendarServiceEx, mimeFileService)
|
||||
, IOutlookChangeProcessor
|
||||
{
|
||||
|
||||
@@ -40,106 +37,6 @@ public class OutlookChangeProcessor(IDatabaseService databaseService,
|
||||
|
||||
public async Task ManageCalendarEventAsync(Event calendarEvent, AccountCalendar assignedCalendar, MailAccount organizerAccount)
|
||||
{
|
||||
// We parse the occurrences based on the parent event.
|
||||
// There is literally no point to store them because
|
||||
// type=Exception events are the exceptional childs of recurrency parent event.
|
||||
|
||||
if (calendarEvent.Type == EventType.Occurrence) return;
|
||||
|
||||
var savingItem = await CalendarService.GetCalendarItemAsync(assignedCalendar.Id, calendarEvent.Id);
|
||||
|
||||
Guid savingItemId = Guid.Empty;
|
||||
|
||||
if (savingItem != null)
|
||||
savingItemId = savingItem.Id;
|
||||
else
|
||||
{
|
||||
savingItemId = Guid.NewGuid();
|
||||
savingItem = new CalendarItem() { Id = savingItemId };
|
||||
}
|
||||
|
||||
DateTimeOffset eventStartDateTimeOffset = OutlookIntegratorExtensions.GetDateTimeOffsetFromDateTimeTimeZone(calendarEvent.Start);
|
||||
DateTimeOffset eventEndDateTimeOffset = OutlookIntegratorExtensions.GetDateTimeOffsetFromDateTimeTimeZone(calendarEvent.End);
|
||||
|
||||
var durationInSeconds = (eventEndDateTimeOffset - eventStartDateTimeOffset).TotalSeconds;
|
||||
|
||||
savingItem.RemoteEventId = calendarEvent.Id;
|
||||
savingItem.StartDate = eventStartDateTimeOffset.DateTime;
|
||||
savingItem.StartDateOffset = eventStartDateTimeOffset.Offset;
|
||||
savingItem.EndDateOffset = eventEndDateTimeOffset.Offset;
|
||||
savingItem.DurationInSeconds = durationInSeconds;
|
||||
|
||||
savingItem.Title = calendarEvent.Subject;
|
||||
savingItem.Description = calendarEvent.Body?.Content;
|
||||
savingItem.Location = calendarEvent.Location?.DisplayName;
|
||||
|
||||
if (calendarEvent.Type == EventType.Exception && !string.IsNullOrEmpty(calendarEvent.SeriesMasterId))
|
||||
{
|
||||
// This is a recurring event exception.
|
||||
// We need to find the parent event and set it as recurring event id.
|
||||
|
||||
var parentEvent = await CalendarService.GetCalendarItemAsync(assignedCalendar.Id, calendarEvent.SeriesMasterId);
|
||||
|
||||
if (parentEvent != null)
|
||||
{
|
||||
savingItem.RecurringCalendarItemId = parentEvent.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning($"Parent recurring event is missing for event. Skipping creation of {calendarEvent.Id}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the recurrence pattern to string for parent recurring events.
|
||||
if (calendarEvent.Type == EventType.SeriesMaster && calendarEvent.Recurrence != null)
|
||||
{
|
||||
savingItem.Recurrence = OutlookIntegratorExtensions.ToRfc5545RecurrenceString(calendarEvent.Recurrence);
|
||||
}
|
||||
|
||||
savingItem.HtmlLink = calendarEvent.WebLink;
|
||||
savingItem.CalendarId = assignedCalendar.Id;
|
||||
savingItem.OrganizerEmail = calendarEvent.Organizer?.EmailAddress?.Address;
|
||||
savingItem.OrganizerDisplayName = calendarEvent.Organizer?.EmailAddress?.Name;
|
||||
savingItem.IsHidden = false;
|
||||
|
||||
if (calendarEvent.ResponseStatus?.Response != null)
|
||||
{
|
||||
switch (calendarEvent.ResponseStatus.Response.Value)
|
||||
{
|
||||
case ResponseType.None:
|
||||
case ResponseType.NotResponded:
|
||||
savingItem.Status = CalendarItemStatus.NotResponded;
|
||||
break;
|
||||
case ResponseType.TentativelyAccepted:
|
||||
savingItem.Status = CalendarItemStatus.Tentative;
|
||||
break;
|
||||
case ResponseType.Accepted:
|
||||
case ResponseType.Organizer:
|
||||
savingItem.Status = CalendarItemStatus.Confirmed;
|
||||
break;
|
||||
case ResponseType.Declined:
|
||||
savingItem.Status = CalendarItemStatus.Cancelled;
|
||||
savingItem.IsHidden = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
savingItem.Status = CalendarItemStatus.Confirmed;
|
||||
}
|
||||
|
||||
// Upsert the event.
|
||||
await Connection.InsertOrReplaceAsync(savingItem);
|
||||
|
||||
// Manage attendees.
|
||||
if (calendarEvent.Attendees != null)
|
||||
{
|
||||
// Clear all attendees for this event.
|
||||
var attendees = calendarEvent.Attendees.Select(a => a.CreateAttendee(savingItemId)).ToList();
|
||||
await CalendarService.ManageEventAttendeesAsync(savingItemId, attendees).ConfigureAwait(false);
|
||||
}
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user