diff --git a/Wino.Calendar.ViewModels/CalendarPageViewModel.cs b/Wino.Calendar.ViewModels/CalendarPageViewModel.cs
index a1b6e348..58bcb271 100644
--- a/Wino.Calendar.ViewModels/CalendarPageViewModel.cs
+++ b/Wino.Calendar.ViewModels/CalendarPageViewModel.cs
@@ -128,6 +128,7 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
private readonly INavigationService _navigationService;
private readonly IKeyPressService _keyPressService;
private readonly IPreferencesService _preferencesService;
+ private readonly IWinoRequestDelegator _winoRequestDelegator;
// Store latest rendered options.
private CalendarDisplayType _currentDisplayType;
@@ -147,7 +148,8 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
INavigationService navigationService,
IKeyPressService keyPressService,
IAccountCalendarStateService accountCalendarStateService,
- IPreferencesService preferencesService)
+ IPreferencesService preferencesService,
+ IWinoRequestDelegator winoRequestDelegator)
{
StatePersistanceService = statePersistanceService;
AccountCalendarStateService = accountCalendarStateService;
@@ -156,6 +158,7 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
_navigationService = navigationService;
_keyPressService = keyPressService;
_preferencesService = preferencesService;
+ _winoRequestDelegator = winoRequestDelegator;
AccountCalendarStateService.AccountCalendarSelectionStateChanged += UpdateAccountCalendarRequested;
AccountCalendarStateService.CollectiveAccountGroupSelectionStateChanged += AccountCalendarStateCollectivelyChanged;
@@ -256,24 +259,47 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
[RelayCommand(AllowConcurrentExecutions = false, CanExecute = nameof(CanSaveQuickEvent))]
private async Task SaveQuickEventAsync()
{
- var durationSeconds = (QuickEventEndTime - QuickEventStartTime).TotalSeconds;
-
- var testCalendarItem = new CalendarItem
+ try
{
- CalendarId = SelectedQuickEventAccountCalendar.Id,
- StartDate = QuickEventStartTime,
- DurationInSeconds = durationSeconds,
- CreatedAt = DateTime.UtcNow,
- Description = string.Empty,
- Location = Location,
- Title = EventName,
- Id = Guid.NewGuid()
- };
+ var startDate = IsAllDay ? SelectedQuickEventDate.Value.Date : QuickEventStartTime;
+ var endDate = IsAllDay ? SelectedQuickEventDate.Value.Date.AddDays(1) : QuickEventEndTime;
+ var durationSeconds = (endDate - startDate).TotalSeconds;
- IsQuickEventDialogOpen = false;
- await _calendarService.CreateNewCalendarItemAsync(testCalendarItem, null);
+ // Get the user's current timezone from the system
+ var currentTimeZone = TimeZoneInfo.Local.Id;
- // TODO: Create the request with the synchronizer.
+ var calendarItem = new CalendarItem
+ {
+ Id = Guid.NewGuid(),
+ CalendarId = SelectedQuickEventAccountCalendar.Id,
+ StartDate = startDate,
+ DurationInSeconds = durationSeconds,
+ StartTimeZone = currentTimeZone,
+ EndTimeZone = currentTimeZone,
+ CreatedAt = DateTime.UtcNow,
+ Description = string.Empty,
+ Location = Location ?? string.Empty,
+ Title = EventName,
+ IsHidden = false,
+ AssignedCalendar = SelectedQuickEventAccountCalendar
+ };
+
+ // Close dialog first
+ IsQuickEventDialogOpen = false;
+
+ // Save to local database first
+ // await _calendarService.CreateNewCalendarItemAsync(calendarItem, null);
+
+ // Queue the request via delegator
+ var preparationRequest = new CalendarOperationPreparationRequest(CalendarSynchronizerOperation.CreateEvent, calendarItem, null);
+ await _winoRequestDelegator.ExecuteAsync(preparationRequest);
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "Error creating quick event");
+ // Re-open dialog if there was an error
+ IsQuickEventDialogOpen = true;
+ }
}
[RelayCommand]
diff --git a/Wino.Core.Domain/Enums/MailOperation.cs b/Wino.Core.Domain/Enums/MailOperation.cs
index 973ff62f..e90f22a7 100644
--- a/Wino.Core.Domain/Enums/MailOperation.cs
+++ b/Wino.Core.Domain/Enums/MailOperation.cs
@@ -21,6 +21,13 @@ public enum FolderSynchronizerOperation
MarkFolderRead,
}
+public enum CalendarSynchronizerOperation
+{
+ CreateEvent,
+ UpdateEvent,
+ DeleteEvent,
+}
+
// UI requests
public enum MailOperation
{
diff --git a/Wino.Core.Domain/Interfaces/IRequestBundle.cs b/Wino.Core.Domain/Interfaces/IRequestBundle.cs
index f88a3dc2..59bd9e2d 100644
--- a/Wino.Core.Domain/Interfaces/IRequestBundle.cs
+++ b/Wino.Core.Domain/Interfaces/IRequestBundle.cs
@@ -1,4 +1,5 @@
-using Wino.Core.Domain.Entities.Mail;
+using Wino.Core.Domain.Entities.Calendar;
+using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Interfaces;
@@ -63,3 +64,9 @@ public interface IFolderActionRequest : IRequestBase
FolderSynchronizerOperation Operation { get; }
}
+
+public interface ICalendarActionRequest : IRequestBase
+{
+ CalendarItem Item { get; }
+ CalendarSynchronizerOperation Operation { get; }
+}
diff --git a/Wino.Core.Domain/Interfaces/ISynchronizationManager.cs b/Wino.Core.Domain/Interfaces/ISynchronizationManager.cs
index 61017244..86ec5cce 100644
--- a/Wino.Core.Domain/Interfaces/ISynchronizationManager.cs
+++ b/Wino.Core.Domain/Interfaces/ISynchronizationManager.cs
@@ -79,7 +79,7 @@ public interface ISynchronizationManager
///
/// Creates a new synchronizer for a newly added account.
///
- Task CreateSynchronizerForAccountAsync(MailAccount account);
+ IWinoSynchronizerBase CreateSynchronizerForAccount(MailAccount account);
///
/// Destroys the synchronizer for the given account.
diff --git a/Wino.Core.Domain/Interfaces/IWinoRequestDelegator.cs b/Wino.Core.Domain/Interfaces/IWinoRequestDelegator.cs
index de50c517..0cf1cabf 100644
--- a/Wino.Core.Domain/Interfaces/IWinoRequestDelegator.cs
+++ b/Wino.Core.Domain/Interfaces/IWinoRequestDelegator.cs
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
+using Wino.Core.Domain.Models.Calendar;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
@@ -29,4 +30,10 @@ public interface IWinoRequestDelegator
///
/// Folder prep request.
Task ExecuteAsync(FolderOperationPreperationRequest folderOperationPreperationRequest);
+
+ ///
+ /// Prepares and queues calendar action requests for proper synchronizers.
+ ///
+ /// Calendar preparation request.
+ Task ExecuteAsync(CalendarOperationPreparationRequest calendarOperationPreparationRequest);
}
diff --git a/Wino.Core.Domain/Models/Calendar/CalendarOperationPreparationRequest.cs b/Wino.Core.Domain/Models/Calendar/CalendarOperationPreparationRequest.cs
new file mode 100644
index 00000000..81a9c342
--- /dev/null
+++ b/Wino.Core.Domain/Models/Calendar/CalendarOperationPreparationRequest.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using Wino.Core.Domain.Entities.Calendar;
+using Wino.Core.Domain.Enums;
+
+namespace Wino.Core.Domain.Models.Calendar;
+
+///
+/// Encapsulates the options for preparing calendar operation requests.
+///
+/// Calendar operation to execute (Create, Update, Delete).
+/// Calendar item to operate on.
+/// List of attendees for the calendar event.
+public record CalendarOperationPreparationRequest(CalendarSynchronizerOperation Operation, CalendarItem CalendarItem, List Attendees);
+//{
+// public CalendarOperationPreparationRequest(CalendarItem calendarItem)
+// : this(calendarItem ?? throw new ArgumentNullException(nameof(calendarItem)), null)
+// {
+// }
+//}
diff --git a/Wino.Core.Domain/Models/Requests/RequestBase.cs b/Wino.Core.Domain/Models/Requests/RequestBase.cs
index af7b6039..f7e008b7 100644
--- a/Wino.Core.Domain/Models/Requests/RequestBase.cs
+++ b/Wino.Core.Domain/Models/Requests/RequestBase.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
@@ -29,6 +30,10 @@ public abstract record FolderRequestBase(MailItemFolder Folder, FolderSynchroniz
public virtual object GroupingKey() { return Operation; }
}
+public abstract record CalendarRequestBase(CalendarItem Item) : RequestBase, ICalendarActionRequest
+{
+}
+
public class BatchCollection : List, IUIChangeRequest where TRequestType : IUIChangeRequest
{
public BatchCollection(IEnumerable collection) : base(collection)
diff --git a/Wino.Core/Requests/Calendar/CreateCalendarEventRequest.cs b/Wino.Core/Requests/Calendar/CreateCalendarEventRequest.cs
new file mode 100644
index 00000000..3135f71e
--- /dev/null
+++ b/Wino.Core/Requests/Calendar/CreateCalendarEventRequest.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using CommunityToolkit.Mvvm.Messaging;
+using Wino.Core.Domain.Entities.Calendar;
+using Wino.Core.Domain.Enums;
+using Wino.Core.Domain.Models.Requests;
+using Wino.Messaging.Client.Calendar;
+
+namespace Wino.Core.Requests.Calendar;
+
+///
+/// Request to create a new calendar event on the server.
+/// The calendar item should be already saved to the local database before queuing this request.
+///
+public record CreateCalendarEventRequest(CalendarItem Item, List Attendees) : CalendarRequestBase(Item)
+{
+ public override CalendarSynchronizerOperation Operation => CalendarSynchronizerOperation.CreateEvent;
+
+ ///
+ /// After successful creation, we need to resync to get the remote event ID.
+ ///
+ public override int ResynchronizationDelay => 2000;
+
+ public override void ApplyUIChanges()
+ {
+ // Notify UI that the event was created locally
+ WeakReferenceMessenger.Default.Send(new CalendarItemAdded(Item));
+ }
+
+ public override void RevertUIChanges()
+ {
+ // If creation fails, we should notify the UI to remove it
+ WeakReferenceMessenger.Default.Send(new CalendarItemDeleted(Item));
+ }
+}
diff --git a/Wino.Core/Services/SynchronizationManager.cs b/Wino.Core/Services/SynchronizationManager.cs
index 65c1cf77..4fae1313 100644
--- a/Wino.Core/Services/SynchronizationManager.cs
+++ b/Wino.Core/Services/SynchronizationManager.cs
@@ -191,7 +191,8 @@ public class SynchronizationManager : ISynchronizationManager
}
///
- /// Queues a mail action request to the corresponding account's synchronizer with optional synchronization triggering.
+ /// Queues a request to the corresponding account's synchronizer with optional synchronization triggering.
+ /// Automatically determines whether to trigger mail or calendar synchronization based on the request type.
///
/// Request to queue
/// Account ID to queue the request for
@@ -214,28 +215,57 @@ public class SynchronizationManager : ISynchronizationManager
if (triggerSynchronization)
{
- // Trigger synchronization to execute the queued request
- _logger.Debug("Triggering synchronization to execute queued request for account {AccountId}", accountId);
+ // Determine if this is a calendar or mail operation
+ bool isCalendarOperation = request is ICalendarActionRequest;
- var synchronizationOptions = new MailSynchronizationOptions()
+ if (isCalendarOperation)
{
- AccountId = accountId,
- Type = MailSynchronizationType.ExecuteRequests
- };
+ // Trigger calendar synchronization
+ _logger.Debug("Triggering calendar synchronization to execute queued request for account {AccountId}", accountId);
- // Trigger synchronization asynchronously without waiting for completion
- // This matches the pattern used in WinoRequestDelegator
- _ = Task.Run(async () =>
+ var calendarSyncOptions = new CalendarSynchronizationOptions()
+ {
+ AccountId = accountId
+ };
+
+ // Trigger synchronization asynchronously without waiting for completion
+ _ = Task.Run(async () =>
+ {
+ try
+ {
+ await SynchronizeCalendarAsync(calendarSyncOptions);
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "Failed to execute calendar synchronization after queuing request for account {AccountId}", accountId);
+ }
+ });
+ }
+ else
{
- try
+ // Trigger mail synchronization (includes mail and folder operations)
+ _logger.Debug("Triggering mail synchronization to execute queued request for account {AccountId}", accountId);
+
+ var mailSyncOptions = new MailSynchronizationOptions()
{
- await SynchronizeMailAsync(synchronizationOptions);
- }
- catch (Exception ex)
+ AccountId = accountId,
+ Type = MailSynchronizationType.ExecuteRequests
+ };
+
+ // Trigger synchronization asynchronously without waiting for completion
+ // This matches the pattern used in WinoRequestDelegator
+ _ = Task.Run(async () =>
{
- _logger.Error(ex, "Failed to execute synchronization after queuing request for account {AccountId}", accountId);
- }
- });
+ try
+ {
+ await SynchronizeMailAsync(mailSyncOptions);
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "Failed to execute mail synchronization after queuing request for account {AccountId}", accountId);
+ }
+ });
+ }
}
}
@@ -387,7 +417,7 @@ public class SynchronizationManager : ISynchronizationManager
///
/// Account to create synchronizer for
/// Created synchronizer
- public Task CreateSynchronizerForAccountAsync(MailAccount account)
+ public IWinoSynchronizerBase CreateSynchronizerForAccount(MailAccount account)
{
EnsureInitialized();
@@ -399,13 +429,13 @@ public class SynchronizationManager : ISynchronizationManager
_logger.Information("Created new synchronizer for account {AccountName} ({AccountId})",
account.Name, account.Id);
- return Task.FromResult(synchronizer);
+ return synchronizer;
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to create synchronizer for account {AccountName} ({AccountId})",
account.Name, account.Id);
- return Task.FromResult(null);
+ return null;
}
}
@@ -463,7 +493,7 @@ public class SynchronizationManager : ISynchronizationManager
var account = await _accountService.GetAccountAsync(accountId);
if (account != null)
{
- return await CreateSynchronizerForAccountAsync(account);
+ return CreateSynchronizerForAccount(account);
}
return null;
diff --git a/Wino.Core/Services/WinoRequestDelegator.cs b/Wino.Core/Services/WinoRequestDelegator.cs
index c58bf34d..1303ec8f 100644
--- a/Wino.Core/Services/WinoRequestDelegator.cs
+++ b/Wino.Core/Services/WinoRequestDelegator.cs
@@ -8,9 +8,11 @@ using Wino.Core.Domain;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Exceptions;
using Wino.Core.Domain.Interfaces;
+using Wino.Core.Domain.Models.Calendar;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Synchronization;
+using Wino.Core.Requests.Calendar;
using Wino.Core.Requests.Mail;
using Wino.Messaging.Server;
@@ -133,6 +135,21 @@ public class WinoRequestDelegator : IWinoRequestDelegator
await QueueSynchronizationAsync(sendDraftPreperationRequest.MailItem.AssignedAccount.Id);
}
+ public async Task ExecuteAsync(CalendarOperationPreparationRequest calendarPreparationRequest)
+ {
+ IRequestBase request = calendarPreparationRequest.Operation switch
+ {
+ CalendarSynchronizerOperation.CreateEvent => new CreateCalendarEventRequest(calendarPreparationRequest.CalendarItem, calendarPreparationRequest.Attendees),
+ // Future support for update and delete operations
+ // CalendarSynchronizerOperation.UpdateEvent => new UpdateCalendarEventRequest(calendarPreparationRequest.CalendarItem, calendarPreparationRequest.Attendees),
+ // CalendarSynchronizerOperation.DeleteEvent => new DeleteCalendarEventRequest(calendarPreparationRequest.CalendarItem),
+ _ => throw new NotImplementedException($"Calendar operation {calendarPreparationRequest.Operation} is not implemented yet.")
+ };
+
+ await QueueRequestAsync(request, calendarPreparationRequest.CalendarItem.AssignedCalendar.AccountId);
+ await QueueCalendarSynchronizationAsync(calendarPreparationRequest.CalendarItem.AssignedCalendar.AccountId);
+ }
+
private async Task QueueRequestAsync(IRequestBase request, Guid accountId)
{
// Don't trigger synchronization for individual requests - we'll trigger it once for all requests
@@ -150,4 +167,16 @@ public class WinoRequestDelegator : IWinoRequestDelegator
WeakReferenceMessenger.Default.Send(new NewMailSynchronizationRequested(options));
return Task.CompletedTask;
}
+
+ private Task QueueCalendarSynchronizationAsync(Guid accountId)
+ {
+ var options = new CalendarSynchronizationOptions()
+ {
+ AccountId = accountId,
+ Type = CalendarSynchronizationType.ExecuteRequests
+ };
+
+ WeakReferenceMessenger.Default.Send(new NewCalendarSynchronizationRequested(options));
+ return Task.CompletedTask;
+ }
}
diff --git a/Wino.Core/Synchronizers/GmailSynchronizer.cs b/Wino.Core/Synchronizers/GmailSynchronizer.cs
index baefed5e..ab01ffe3 100644
--- a/Wino.Core/Synchronizers/GmailSynchronizer.cs
+++ b/Wino.Core/Synchronizers/GmailSynchronizer.cs
@@ -35,6 +35,7 @@ using Wino.Core.Extensions;
using Wino.Core.Http;
using Wino.Core.Integration.Processors;
using Wino.Core.Requests.Bundles;
+using Wino.Core.Requests.Calendar;
using Wino.Core.Requests.Folder;
using Wino.Core.Requests.Mail;
using Wino.Messaging.UI;
@@ -1627,6 +1628,76 @@ public class GmailSynchronizer : WinoSynchronizer> CreateCalendarEvent(CreateCalendarEventRequest request)
+ {
+ var calendarItem = request.Item;
+ var attendees = request.Attendees;
+
+ // Get the calendar for this event
+ var calendar = calendarItem.AssignedCalendar;
+ if (calendar == null)
+ {
+ throw new InvalidOperationException("Calendar item must have an assigned calendar");
+ }
+
+ // Convert CalendarItem to Google Event
+ var googleEvent = new Event
+ {
+ Summary = calendarItem.Title,
+ Description = calendarItem.Description,
+ Location = calendarItem.Location,
+ Status = calendarItem.Status == CalendarItemStatus.Confirmed ? "confirmed" : "tentative"
+ };
+
+ // Set start and end time
+ if (calendarItem.IsAllDayEvent)
+ {
+ // All-day events use Date instead of DateTime
+ googleEvent.Start = new EventDateTime
+ {
+ Date = calendarItem.StartDate.ToString("yyyy-MM-dd")
+ };
+ googleEvent.End = new EventDateTime
+ {
+ Date = calendarItem.EndDate.ToString("yyyy-MM-dd")
+ };
+ }
+ else
+ {
+ // Regular events with time
+ googleEvent.Start = new EventDateTime
+ {
+ DateTimeDateTimeOffset = new DateTimeOffset(calendarItem.StartDate, TimeSpan.Zero),
+ TimeZone = calendarItem.StartTimeZone
+ };
+ googleEvent.End = new EventDateTime
+ {
+ DateTimeDateTimeOffset = new DateTimeOffset(calendarItem.EndDate, TimeSpan.Zero),
+ TimeZone = calendarItem.EndTimeZone
+ };
+ }
+
+ // Add attendees if any
+ if (attendees != null && attendees.Count > 0)
+ {
+ googleEvent.Attendees = attendees.Select(a => new EventAttendee
+ {
+ Email = a.Email,
+ DisplayName = a.Name,
+ Optional = a.IsOptionalAttendee
+ }).ToList();
+ }
+
+ // Create the insert request
+ var insertRequest = _calendarService.Events.Insert(googleEvent, calendar.RemoteCalendarId);
+
+ return [new HttpRequestBundle(insertRequest, request)];
+ }
+
+ #endregion
+
public override async Task KillSynchronizerAsync()
{
await base.KillSynchronizerAsync();
diff --git a/Wino.Core/Synchronizers/OutlookSynchronizer.cs b/Wino.Core/Synchronizers/OutlookSynchronizer.cs
index 42ba1f75..687b6733 100644
--- a/Wino.Core/Synchronizers/OutlookSynchronizer.cs
+++ b/Wino.Core/Synchronizers/OutlookSynchronizer.cs
@@ -39,6 +39,7 @@ using Wino.Core.Http;
using Wino.Core.Integration.Processors;
using Wino.Core.Misc;
using Wino.Core.Requests.Bundles;
+using Wino.Core.Requests.Calendar;
using Wino.Core.Requests.Folder;
using Wino.Core.Requests.Mail;
@@ -1804,6 +1805,89 @@ public class OutlookSynchronizer : WinoSynchronizer> CreateCalendarEvent(CreateCalendarEventRequest request)
+ {
+ var calendarItem = request.Item;
+ var attendees = request.Attendees;
+
+ // Get the calendar for this event
+ var calendar = calendarItem.AssignedCalendar;
+ if (calendar == null)
+ {
+ throw new InvalidOperationException("Calendar item must have an assigned calendar");
+ }
+
+ // Convert CalendarItem to Outlook Event
+ var outlookEvent = new Microsoft.Graph.Models.Event
+ {
+ Subject = calendarItem.Title,
+ Body = new Microsoft.Graph.Models.ItemBody
+ {
+ ContentType = Microsoft.Graph.Models.BodyType.Text,
+ Content = calendarItem.Description
+ },
+ Location = new Microsoft.Graph.Models.Location
+ {
+ DisplayName = calendarItem.Location
+ }
+ };
+
+ // Set start and end time using DateTimeTimeZone
+ if (calendarItem.IsAllDayEvent)
+ {
+ // All-day events
+ outlookEvent.IsAllDay = true;
+ outlookEvent.Start = new Microsoft.Graph.Models.DateTimeTimeZone
+ {
+ DateTime = calendarItem.StartDate.ToString("yyyy-MM-dd"),
+ TimeZone = "UTC"
+ };
+ outlookEvent.End = new Microsoft.Graph.Models.DateTimeTimeZone
+ {
+ DateTime = calendarItem.EndDate.ToString("yyyy-MM-dd"),
+ TimeZone = "UTC"
+ };
+ }
+ else
+ {
+ // Regular events with time
+ outlookEvent.IsAllDay = false;
+ outlookEvent.Start = new Microsoft.Graph.Models.DateTimeTimeZone
+ {
+ DateTime = calendarItem.StartDate.ToString("yyyy-MM-ddTHH:mm:ss"),
+ TimeZone = calendarItem.StartTimeZone ?? "UTC"
+ };
+ outlookEvent.End = new Microsoft.Graph.Models.DateTimeTimeZone
+ {
+ DateTime = calendarItem.EndDate.ToString("yyyy-MM-ddTHH:mm:ss"),
+ TimeZone = calendarItem.EndTimeZone ?? "UTC"
+ };
+ }
+
+ // Add attendees if any
+ if (attendees != null && attendees.Count > 0)
+ {
+ outlookEvent.Attendees = attendees.Select(a => new Microsoft.Graph.Models.Attendee
+ {
+ EmailAddress = new Microsoft.Graph.Models.EmailAddress
+ {
+ Address = a.Email,
+ Name = a.Name
+ },
+ Type = a.IsOptionalAttendee ? Microsoft.Graph.Models.AttendeeType.Optional : Microsoft.Graph.Models.AttendeeType.Required
+ }).ToList();
+ }
+
+ // Create the event using Graph API
+ var createRequest = _graphClient.Me.Calendars[calendar.RemoteCalendarId].Events.ToPostRequestInformation(outlookEvent);
+
+ return [new HttpRequestBundle(createRequest, request)];
+ }
+
+ #endregion
+
public override async Task KillSynchronizerAsync()
{
await base.KillSynchronizerAsync();
diff --git a/Wino.Core/Synchronizers/WinoSynchronizer.cs b/Wino.Core/Synchronizers/WinoSynchronizer.cs
index 468d9c89..eef70a74 100644
--- a/Wino.Core/Synchronizers/WinoSynchronizer.cs
+++ b/Wino.Core/Synchronizers/WinoSynchronizer.cs
@@ -18,6 +18,7 @@ using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Synchronization;
using Wino.Core.Requests.Bundles;
+using Wino.Core.Requests.Calendar;
using Wino.Core.Requests.Folder;
using Wino.Core.Requests.Mail;
using Wino.Messaging.UI;
@@ -337,10 +338,67 @@ public abstract class WinoSynchronizerSynchronization options.
/// Cancellation token.
/// Synchronization result that contains summary of the sync.
- public Task SynchronizeCalendarEventsAsync(CalendarSynchronizationOptions options, CancellationToken cancellationToken = default)
+ public async Task SynchronizeCalendarEventsAsync(CalendarSynchronizationOptions options, CancellationToken cancellationToken = default)
{
- // TODO: Execute requests for calendar events.
- return SynchronizeCalendarEventsInternalAsync(options, cancellationToken);
+ bool shouldExecuteRequests = changeRequestQueue.Any(r => r is ICalendarActionRequest);
+ bool shouldDelayExecution = false;
+ int maxExecutionDelay = 0;
+
+ if (shouldExecuteRequests)
+ {
+ State = AccountSynchronizerState.ExecutingRequests;
+
+ List> nativeRequests = new();
+ List requestCopies = new(changeRequestQueue.Where(r => r is ICalendarActionRequest));
+
+ var keys = requestCopies.GroupBy(a => a.GroupingKey());
+
+ foreach (var group in keys)
+ {
+ var key = group.Key;
+
+ if (key is CalendarSynchronizerOperation calendarSynchronizerOperation)
+ {
+ switch (calendarSynchronizerOperation)
+ {
+ case CalendarSynchronizerOperation.CreateEvent:
+ nativeRequests.AddRange(CreateCalendarEvent(group.ElementAt(0) as CreateCalendarEventRequest));
+ break;
+ case CalendarSynchronizerOperation.UpdateEvent:
+ // TODO: Implement UpdateCalendarEvent
+ break;
+ case CalendarSynchronizerOperation.DeleteEvent:
+ // TODO: Implement DeleteCalendarEvent
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Remove processed calendar requests from queue
+ changeRequestQueue.RemoveAll(r => r is ICalendarActionRequest);
+
+ Console.WriteLine($"Prepared {nativeRequests.Count()} native calendar requests");
+
+ await ExecuteNativeRequestsAsync(nativeRequests, cancellationToken).ConfigureAwait(false);
+
+ // Let servers to finish their job. Sometimes the servers don't respond immediately.
+ shouldDelayExecution = requestCopies.Any(a => a.ResynchronizationDelay > 0);
+
+ if (shouldDelayExecution)
+ {
+ maxExecutionDelay = requestCopies.Aggregate(0, (max, next) => Math.Max(max, next.ResynchronizationDelay));
+ }
+ }
+
+ if (shouldDelayExecution)
+ {
+ await Task.Delay(maxExecutionDelay, cancellationToken);
+ }
+
+ // Execute the actual synchronization
+ return await SynchronizeCalendarEventsInternalAsync(options, cancellationToken);
}
///
@@ -435,6 +493,7 @@ public abstract class WinoSynchronizer> CreateCalendarEvent(CreateCalendarEventRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
#endregion