296 lines
13 KiB
C#
296 lines
13 KiB
C#
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;
|
|
|
|
public class GmailChangeProcessor : DefaultChangeProcessor, IGmailChangeProcessor
|
|
{
|
|
public GmailChangeProcessor(IDatabaseService databaseService,
|
|
IFolderService folderService,
|
|
IMailService mailService,
|
|
ICalendarService calendarService,
|
|
IAccountService accountService,
|
|
IMimeFileService mimeFileService) : base(databaseService, folderService, mailService, calendarService, accountService, mimeFileService)
|
|
{
|
|
|
|
}
|
|
|
|
public Task MapLocalDraftAsync(string mailCopyId, string newDraftId, string newThreadId)
|
|
=> MailService.MapLocalDraftAsync(mailCopyId, newDraftId, newThreadId);
|
|
|
|
public Task CreateAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId)
|
|
=> MailService.CreateAssignmentAsync(accountId, mailCopyId, remoteFolderId);
|
|
|
|
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);
|
|
|
|
|
|
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.
|
|
|
|
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(),
|
|
StartTimeOffset = eventStartDateTimeOffset.ToString("o"),
|
|
EndTimeOffset = eventEndDateTimeOffset.ToString("o"),
|
|
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.
|
|
|
|
calendarItem = new CalendarItem()
|
|
{
|
|
CalendarId = assignedCalendar.Id,
|
|
CreatedAt = DateTimeOffset.UtcNow,
|
|
Description = calendarEvent.Description,
|
|
Id = Guid.NewGuid(),
|
|
StartTimeOffset = eventStartDateTimeOffset.ToString("o"),
|
|
EndTimeOffset = eventEndDateTimeOffset.ToString("o"),
|
|
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);
|
|
}
|
|
|
|
private string GetOrganizerName(Event calendarEvent, MailAccount account)
|
|
{
|
|
if (calendarEvent.Organizer == null) return string.Empty;
|
|
|
|
if (calendarEvent.Organizer.Self == true)
|
|
{
|
|
return account.SenderName;
|
|
}
|
|
else
|
|
return calendarEvent.Organizer.DisplayName;
|
|
}
|
|
|
|
private string GetOrganizerEmail(Event calendarEvent, MailAccount account)
|
|
{
|
|
if (calendarEvent.Organizer == null) return string.Empty;
|
|
|
|
if (calendarEvent.Organizer.Self == true)
|
|
{
|
|
return account.Address;
|
|
}
|
|
else
|
|
return calendarEvent.Organizer.Email;
|
|
}
|
|
|
|
private CalendarItemStatus GetStatus(string status)
|
|
{
|
|
return status switch
|
|
{
|
|
"confirmed" => CalendarItemStatus.Confirmed,
|
|
"tentative" => CalendarItemStatus.Tentative,
|
|
"cancelled" => CalendarItemStatus.Cancelled,
|
|
_ => CalendarItemStatus.Confirmed
|
|
};
|
|
}
|
|
|
|
private CalendarItemVisibility GetVisibility(string visibility)
|
|
{
|
|
/// Visibility of the event. Optional. Possible values are: - "default" - Uses the default visibility for
|
|
/// events on the calendar. This is the default value. - "public" - The event is public and event details are
|
|
/// visible to all readers of the calendar. - "private" - The event is private and only event attendees may
|
|
/// view event details. - "confidential" - The event is private. This value is provided for compatibility
|
|
/// reasons.
|
|
|
|
return visibility switch
|
|
{
|
|
"default" => CalendarItemVisibility.Default,
|
|
"public" => CalendarItemVisibility.Public,
|
|
"private" => CalendarItemVisibility.Private,
|
|
"confidential" => CalendarItemVisibility.Confidential,
|
|
_ => CalendarItemVisibility.Default
|
|
};
|
|
}
|
|
|
|
public Task<bool> HasAccountAnyDraftAsync(Guid accountId)
|
|
=> MailService.HasAccountAnyDraftAsync(accountId);
|
|
|
|
public Task<GmailArchiveComparisonResult> GetGmailArchiveComparisonResultAsync(Guid archiveFolderId, List<string> onlineArchiveMailIds)
|
|
=> MailService.GetGmailArchiveComparisonResultAsync(archiveFolderId, onlineArchiveMailIds);
|
|
}
|