More abstraction for mail/calendar.

This commit is contained in:
Burak Kaan Köse
2024-12-24 18:30:25 +01:00
parent da2a58a88b
commit 1668dfcce6
28 changed files with 209 additions and 121 deletions

View File

@@ -119,13 +119,13 @@ namespace Wino.Calendar.ViewModels
// Start profile information synchronization. // Start profile information synchronization.
// It's only available for Outlook and Gmail synchronizers. // It's only available for Outlook and Gmail synchronizers.
var profileSyncOptions = new SynchronizationOptions() var profileSyncOptions = new MailSynchronizationOptions()
{ {
AccountId = createdAccount.Id, AccountId = createdAccount.Id,
Type = SynchronizationType.UpdateProfile Type = MailSynchronizationType.UpdateProfile
}; };
var profileSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<SynchronizationResult, NewSynchronizationRequested>(new NewSynchronizationRequested(profileSyncOptions, SynchronizationSource.Client)); var profileSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewSynchronizationRequested>(new NewSynchronizationRequested(profileSyncOptions, SynchronizationSource.Client));
var profileSynchronizationResult = profileSynchronizationResponse.Data; var profileSynchronizationResult = profileSynchronizationResponse.Data;
@@ -141,10 +141,10 @@ namespace Wino.Calendar.ViewModels
accountCreationDialog.State = AccountCreationDialogState.FetchingEvents; accountCreationDialog.State = AccountCreationDialogState.FetchingEvents;
// Start synchronizing events. // Start synchronizing events.
var eventsSyncOptions = new SynchronizationOptions() var eventsSyncOptions = new MailSynchronizationOptions()
{ {
AccountId = createdAccount.Id, AccountId = createdAccount.Id,
Type = SynchronizationType.Events Type = MailSynchronizationType.Events
}; };
} }
} }

View File

@@ -0,0 +1,9 @@
namespace Wino.Core.Domain.Enums
{
public enum CalendarSynchronizationType
{
AllCalendars, // Sync all calendars.
SingleCalendar, // Sync only one calendar.
UpdateProfile // Update profile information only.
}
}

View File

@@ -1,17 +1,14 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums
{ {
public enum SynchronizationType public enum MailSynchronizationType
{ {
// Shared // Shared
UpdateProfile, // Only update profile information UpdateProfile, // Only update profile information
ExecuteRequests, // Run the queued requests, and then synchronize if needed. ExecuteRequests, // Run the queued requests, and then synchronize if needed.
// Wino Mail
FoldersOnly, // Only synchronize folder metadata. FoldersOnly, // Only synchronize folder metadata.
InboxOnly, // Only Inbox, Sent and Draft folders. InboxOnly, // Only Inbox, Sent and Draft folders.
CustomFolders, // Only sync folders that are specified in the options. CustomFolders, // Only sync folders that are specified in the options.
FullFolders, // Synchronize all folders. This won't update profile or alias information. FullFolders, // Synchronize all folders. This won't update profile or alias information.
Alias, // Only update alias information Alias, // Only update alias information
// Calendar
Events
} }
} }

View File

@@ -1,7 +0,0 @@
namespace Wino.Core.Domain.Interfaces
{
public interface IBaseCalendarSynchronizer : IBaseSynchronizer
{
}
}

View File

@@ -26,7 +26,7 @@ namespace Wino.Core.Domain.Interfaces
Task ChangeFolderSynchronizationStateAsync(Guid folderId, bool isSynchronizationEnabled); Task ChangeFolderSynchronizationStateAsync(Guid folderId, bool isSynchronizationEnabled);
Task ChangeFolderShowUnreadCountStateAsync(Guid folderId, bool showUnreadCount); Task ChangeFolderShowUnreadCountStateAsync(Guid folderId, bool showUnreadCount);
Task<List<MailItemFolder>> GetSynchronizationFoldersAsync(SynchronizationOptions options); Task<List<MailItemFolder>> GetSynchronizationFoldersAsync(MailSynchronizationOptions options);
/// <summary> /// <summary>
/// Returns the folder - mail mapping for the given mail copy ids. /// Returns the folder - mail mapping for the given mail copy ids.

View File

@@ -5,7 +5,7 @@ namespace Wino.Core.Domain.Interfaces
{ {
public interface ISynchronizerFactory public interface ISynchronizerFactory
{ {
Task<IBaseMailSynchronizer> GetAccountSynchronizerAsync(Guid accountId); Task<IWinoSynchronizerBase> GetAccountSynchronizerAsync(Guid accountId);
Task InitializeAsync(); Task InitializeAsync();
} }
} }

View File

@@ -6,7 +6,7 @@ using Wino.Core.Domain.Models.Synchronization;
namespace Wino.Core.Domain.Interfaces namespace Wino.Core.Domain.Interfaces
{ {
public interface IBaseMailSynchronizer : IBaseSynchronizer public interface IWinoSynchronizerBase : IBaseSynchronizer
{ {
/// <summary> /// <summary>
/// Performs a full synchronization with the server with given options. /// Performs a full synchronization with the server with given options.
@@ -17,7 +17,7 @@ namespace Wino.Core.Domain.Interfaces
/// <param name="options">Options for synchronization.</param> /// <param name="options">Options for synchronization.</param>
/// <param name="cancellationToken">Cancellation token.</param> /// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Result summary of synchronization.</returns> /// <returns>Result summary of synchronization.</returns>
Task<SynchronizationResult> SynchronizeAsync(SynchronizationOptions options, CancellationToken cancellationToken = default); Task<MailSynchronizationResult> SynchronizeMailsAsync(MailSynchronizationOptions options, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Downloads a single MIME message from the server and saves it to disk. /// Downloads a single MIME message from the server and saves it to disk.

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Models.Synchronization
{
public class CalendarSynchronizationOptions
{
/// <summary>
/// Unique id of synchronization.
/// </summary>
public Guid Id { get; } = Guid.NewGuid();
/// <summary>
/// Account to execute synchronization for.
/// </summary>
public Guid AccountId { get; set; }
/// <summary>
/// Type of the synchronization to be performed.
/// </summary>
public CalendarSynchronizationType Type { get; set; }
/// <summary>
/// Calendar ids to synchronize.
/// </summary>
public List<Guid> SynchronizationCalendarIds { get; set; }
public override string ToString() => $"Type: {Type}, Calendars: {(SynchronizationCalendarIds == null ? "All" : string.Join(",", SynchronizationCalendarIds))}";
}
}

View File

@@ -0,0 +1,46 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Accounts;
namespace Wino.Core.Domain.Models.Synchronization
{
public class CalendarSynchronizationResult
{
public CalendarSynchronizationResult() { }
/// <summary>
/// Gets the new downloaded events from synchronization.
/// Server will create notifications for these event.
/// It's ignored in serialization. Client should not react to this.
/// </summary>
[JsonIgnore]
public IEnumerable<ICalendarItem> DownloadedEvents { get; set; } = [];
public ProfileInformation ProfileInformation { get; set; }
public SynchronizationCompletedState CompletedState { get; set; }
public static CalendarSynchronizationResult Empty => new() { CompletedState = SynchronizationCompletedState.Success };
// Mail synchronization
public static CalendarSynchronizationResult Completed(IEnumerable<ICalendarItem> downloadedEvent)
=> new()
{
DownloadedEvents = downloadedEvent,
CompletedState = SynchronizationCompletedState.Success
};
// Profile synchronization
public static CalendarSynchronizationResult Completed(ProfileInformation profileInformation)
=> new()
{
ProfileInformation = profileInformation,
CompletedState = SynchronizationCompletedState.Success
};
public static CalendarSynchronizationResult Canceled => new() { CompletedState = SynchronizationCompletedState.Canceled };
public static CalendarSynchronizationResult Failed => new() { CompletedState = SynchronizationCompletedState.Failed };
}
}

View File

@@ -4,7 +4,7 @@ using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Models.Synchronization namespace Wino.Core.Domain.Models.Synchronization
{ {
public class SynchronizationOptions public class MailSynchronizationOptions
{ {
/// <summary> /// <summary>
/// Unique id of synchronization. /// Unique id of synchronization.
@@ -19,7 +19,7 @@ namespace Wino.Core.Domain.Models.Synchronization
/// <summary> /// <summary>
/// Type of the synchronization to be performed. /// Type of the synchronization to be performed.
/// </summary> /// </summary>
public SynchronizationType Type { get; set; } public MailSynchronizationType Type { get; set; }
/// <summary> /// <summary>
/// Collection of FolderId to perform SynchronizationType.Custom type sync. /// Collection of FolderId to perform SynchronizationType.Custom type sync.
@@ -33,6 +33,6 @@ namespace Wino.Core.Domain.Models.Synchronization
/// </summary> /// </summary>
public Guid? GroupedSynchronizationTrackingId { get; set; } public Guid? GroupedSynchronizationTrackingId { get; set; }
public override string ToString() => $"Type: {Type}, Folders: {(SynchronizationFolderIds == null ? "None" : string.Join(",", SynchronizationFolderIds))}"; public override string ToString() => $"Type: {Type}, Folders: {(SynchronizationFolderIds == null ? "All" : string.Join(",", SynchronizationFolderIds))}";
} }
} }

View File

@@ -1,15 +1,14 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Accounts; using Wino.Core.Domain.Models.Accounts;
using Wino.Core.Domain.Models.MailItem; using Wino.Core.Domain.Models.MailItem;
namespace Wino.Core.Domain.Models.Synchronization namespace Wino.Core.Domain.Models.Synchronization
{ {
public class SynchronizationResult public class MailSynchronizationResult
{ {
public SynchronizationResult() { } public MailSynchronizationResult() { }
/// <summary> /// <summary>
/// Gets the new downloaded messages from synchronization. /// Gets the new downloaded messages from synchronization.
@@ -19,22 +18,14 @@ namespace Wino.Core.Domain.Models.Synchronization
[JsonIgnore] [JsonIgnore]
public IEnumerable<IMailItem> DownloadedMessages { get; set; } = []; public IEnumerable<IMailItem> DownloadedMessages { get; set; } = [];
/// <summary>
/// Gets the new downloaded events from synchronization.
/// Server will create notifications for these events.
/// It's ignored in serialization. Client should not react to this.
/// </summary>
[JsonIgnore]
public IEnumerable<ICalendarItem> DownloadedEvents { get; set; } = [];
public ProfileInformation ProfileInformation { get; set; } public ProfileInformation ProfileInformation { get; set; }
public SynchronizationCompletedState CompletedState { get; set; } public SynchronizationCompletedState CompletedState { get; set; }
public static SynchronizationResult Empty => new() { CompletedState = SynchronizationCompletedState.Success }; public static MailSynchronizationResult Empty => new() { CompletedState = SynchronizationCompletedState.Success };
// Mail synchronization // Mail synchronization
public static SynchronizationResult Completed(IEnumerable<IMailItem> downloadedMessages) public static MailSynchronizationResult Completed(IEnumerable<IMailItem> downloadedMessages)
=> new() => new()
{ {
DownloadedMessages = downloadedMessages, DownloadedMessages = downloadedMessages,
@@ -42,14 +33,14 @@ namespace Wino.Core.Domain.Models.Synchronization
}; };
// Profile synchronization // Profile synchronization
public static SynchronizationResult Completed(ProfileInformation profileInformation) public static MailSynchronizationResult Completed(ProfileInformation profileInformation)
=> new() => new()
{ {
ProfileInformation = profileInformation, ProfileInformation = profileInformation,
CompletedState = SynchronizationCompletedState.Success CompletedState = SynchronizationCompletedState.Success
}; };
public static SynchronizationResult Canceled => new() { CompletedState = SynchronizationCompletedState.Canceled }; public static MailSynchronizationResult Canceled => new() { CompletedState = SynchronizationCompletedState.Canceled };
public static SynchronizationResult Failed => new() { CompletedState = SynchronizationCompletedState.Failed }; public static MailSynchronizationResult Failed => new() { CompletedState = SynchronizationCompletedState.Failed };
} }
} }

View File

@@ -41,7 +41,7 @@ namespace Wino.Core.Integration.Processors
Task<List<MailItemFolder>> GetLocalFoldersAsync(Guid accountId); Task<List<MailItemFolder>> GetLocalFoldersAsync(Guid accountId);
Task<List<MailItemFolder>> GetSynchronizationFoldersAsync(SynchronizationOptions options); Task<List<MailItemFolder>> GetSynchronizationFoldersAsync(MailSynchronizationOptions options);
Task<bool> MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId); Task<bool> MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId);
Task UpdateFolderLastSyncDateAsync(Guid folderId); Task UpdateFolderLastSyncDateAsync(Guid folderId);
@@ -153,7 +153,7 @@ namespace Wino.Core.Integration.Processors
public Task<List<MailItemFolder>> GetLocalFoldersAsync(Guid accountId) public Task<List<MailItemFolder>> GetLocalFoldersAsync(Guid accountId)
=> FolderService.GetFoldersAsync(accountId); => FolderService.GetFoldersAsync(accountId);
public Task<List<MailItemFolder>> GetSynchronizationFoldersAsync(SynchronizationOptions options) public Task<List<MailItemFolder>> GetSynchronizationFoldersAsync(MailSynchronizationOptions options)
=> FolderService.GetSynchronizationFoldersAsync(options); => FolderService.GetSynchronizationFoldersAsync(options);
public Task DeleteFolderAsync(Guid accountId, string remoteFolderId) public Task DeleteFolderAsync(Guid accountId, string remoteFolderId)

View File

@@ -20,7 +20,7 @@ namespace Wino.Core.Services
private readonly IOutlookAuthenticator _outlookAuthenticator; private readonly IOutlookAuthenticator _outlookAuthenticator;
private readonly IGmailAuthenticator _gmailAuthenticator; private readonly IGmailAuthenticator _gmailAuthenticator;
private readonly List<IBaseMailSynchronizer> synchronizerCache = new(); private readonly List<IWinoSynchronizerBase> synchronizerCache = new();
public SynchronizerFactory(IOutlookChangeProcessor outlookChangeProcessor, public SynchronizerFactory(IOutlookChangeProcessor outlookChangeProcessor,
IGmailChangeProcessor gmailChangeProcessor, IGmailChangeProcessor gmailChangeProcessor,
@@ -39,7 +39,7 @@ namespace Wino.Core.Services
_applicationConfiguration = applicationConfiguration; _applicationConfiguration = applicationConfiguration;
} }
public async Task<IBaseMailSynchronizer> GetAccountSynchronizerAsync(Guid accountId) public async Task<IWinoSynchronizerBase> GetAccountSynchronizerAsync(Guid accountId)
{ {
var synchronizer = synchronizerCache.Find(a => a.Account.Id == accountId); var synchronizer = synchronizerCache.Find(a => a.Account.Id == accountId);
@@ -58,7 +58,7 @@ namespace Wino.Core.Services
return synchronizer; return synchronizer;
} }
private IBaseMailSynchronizer CreateIntegratorWithDefaultProcessor(MailAccount mailAccount) private IWinoSynchronizerBase CreateIntegratorWithDefaultProcessor(MailAccount mailAccount)
{ {
var providerType = mailAccount.ProviderType; var providerType = mailAccount.ProviderType;
@@ -78,7 +78,7 @@ namespace Wino.Core.Services
return null; return null;
} }
public IBaseMailSynchronizer CreateNewSynchronizer(MailAccount account) public IWinoSynchronizerBase CreateNewSynchronizer(MailAccount account)
{ {
var synchronizer = CreateIntegratorWithDefaultProcessor(account); var synchronizer = CreateIntegratorWithDefaultProcessor(account);

View File

@@ -143,10 +143,10 @@ namespace Wino.Core.Services
{ {
await EnsureServerConnectedAsync(); await EnsureServerConnectedAsync();
var options = new SynchronizationOptions() var options = new MailSynchronizationOptions()
{ {
AccountId = accountId, AccountId = accountId,
Type = SynchronizationType.ExecuteRequests Type = MailSynchronizationType.ExecuteRequests
}; };
WeakReferenceMessenger.Default.Send(new NewSynchronizationRequested(options, SynchronizationSource.Client)); WeakReferenceMessenger.Default.Send(new NewSynchronizationRequested(options, SynchronizationSource.Client));

View File

@@ -104,9 +104,9 @@ namespace Wino.Core.Synchronizers.Mail
await _gmailChangeProcessor.UpdateRemoteAliasInformationAsync(Account, remoteAliases).ConfigureAwait(false); await _gmailChangeProcessor.UpdateRemoteAliasInformationAsync(Account, remoteAliases).ConfigureAwait(false);
} }
protected override async Task<SynchronizationResult> SynchronizeInternalAsync(SynchronizationOptions options, CancellationToken cancellationToken = default) protected override async Task<MailSynchronizationResult> SynchronizeMailsInternalAsync(MailSynchronizationOptions options, CancellationToken cancellationToken = default)
{ {
_logger.Information("Internal synchronization started for {Name}", Account.Name); _logger.Information("Internal mail synchronization started for {Name}", Account.Name);
// Gmail must always synchronize folders before because it doesn't have a per-folder sync. // Gmail must always synchronize folders before because it doesn't have a per-folder sync.
bool shouldSynchronizeFolders = true; bool shouldSynchronizeFolders = true;
@@ -124,7 +124,7 @@ namespace Wino.Core.Synchronizers.Mail
// Therefore we need to stop the synchronization at this point // Therefore we need to stop the synchronization at this point
// if type is only folder metadata sync. // if type is only folder metadata sync.
if (options.Type == SynchronizationType.FoldersOnly) return SynchronizationResult.Empty; if (options.Type == MailSynchronizationType.FoldersOnly) return MailSynchronizationResult.Empty;
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
@@ -281,7 +281,14 @@ namespace Wino.Core.Synchronizers.Mail
var unreadNewItems = await _gmailChangeProcessor.GetDownloadedUnreadMailsAsync(Account.Id, missingMessageIds).ConfigureAwait(false); var unreadNewItems = await _gmailChangeProcessor.GetDownloadedUnreadMailsAsync(Account.Id, missingMessageIds).ConfigureAwait(false);
return SynchronizationResult.Completed(unreadNewItems); return MailSynchronizationResult.Completed(unreadNewItems);
}
protected override Task<CalendarSynchronizationResult> SynchronizeCalendarEventsInternalAsync(CalendarSynchronizationOptions options, CancellationToken cancellationToken = default)
{
_logger.Information("Internal calendar synchronization started for {Name}", Account.Name);
return default;
} }
private async Task SynchronizeFoldersAsync(CancellationToken cancellationToken = default) private async Task SynchronizeFoldersAsync(CancellationToken cancellationToken = default)
@@ -944,17 +951,16 @@ namespace Wino.Core.Synchronizers.Mail
await _gmailChangeProcessor.MapLocalDraftAsync(Account.Id, localDraftCopy.UniqueId, messageDraft.Message.Id, messageDraft.Id, messageDraft.Message.ThreadId); await _gmailChangeProcessor.MapLocalDraftAsync(Account.Id, localDraftCopy.UniqueId, messageDraft.Message.Id, messageDraft.Id, messageDraft.Message.ThreadId);
var options = new SynchronizationOptions() var options = new MailSynchronizationOptions()
{ {
AccountId = Account.Id, AccountId = Account.Id,
Type = SynchronizationType.FullFolders Type = MailSynchronizationType.FullFolders
}; };
await SynchronizeInternalAsync(options, cancellationToken); await SynchronizeMailsInternalAsync(options, cancellationToken);
} }
} }
/// <summary> /// <summary>
/// Maps existing Gmail Draft resources to local mail copies. /// Maps existing Gmail Draft resources to local mail copies.
/// This uses indexed search, therefore it's quite fast. /// This uses indexed search, therefore it's quite fast.

View File

@@ -435,7 +435,7 @@ namespace Wino.Core.Synchronizers.Mail
]; ];
} }
protected override async Task<SynchronizationResult> SynchronizeInternalAsync(SynchronizationOptions options, CancellationToken cancellationToken = default) protected override async Task<MailSynchronizationResult> SynchronizeMailsInternalAsync(MailSynchronizationOptions options, CancellationToken cancellationToken = default)
{ {
var downloadedMessageIds = new List<string>(); var downloadedMessageIds = new List<string>();
@@ -444,14 +444,14 @@ namespace Wino.Core.Synchronizers.Mail
PublishSynchronizationProgress(1); PublishSynchronizationProgress(1);
bool shouldDoFolderSync = options.Type == SynchronizationType.FullFolders || options.Type == SynchronizationType.FoldersOnly; bool shouldDoFolderSync = options.Type == MailSynchronizationType.FullFolders || options.Type == MailSynchronizationType.FoldersOnly;
if (shouldDoFolderSync) if (shouldDoFolderSync)
{ {
await SynchronizeFoldersAsync(cancellationToken).ConfigureAwait(false); await SynchronizeFoldersAsync(cancellationToken).ConfigureAwait(false);
} }
if (options.Type != SynchronizationType.FoldersOnly) if (options.Type != MailSynchronizationType.FoldersOnly)
{ {
var synchronizationFolders = await _imapChangeProcessor.GetSynchronizationFoldersAsync(options).ConfigureAwait(false); var synchronizationFolders = await _imapChangeProcessor.GetSynchronizationFoldersAsync(options).ConfigureAwait(false);
@@ -474,7 +474,7 @@ namespace Wino.Core.Synchronizers.Mail
var unreadNewItems = await _imapChangeProcessor.GetDownloadedUnreadMailsAsync(Account.Id, downloadedMessageIds).ConfigureAwait(false); var unreadNewItems = await _imapChangeProcessor.GetDownloadedUnreadMailsAsync(Account.Id, downloadedMessageIds).ConfigureAwait(false);
return SynchronizationResult.Completed(unreadNewItems); return MailSynchronizationResult.Completed(unreadNewItems);
} }
public override async Task ExecuteNativeRequestsAsync(List<IRequestBundle<ImapRequest>> batchedRequests, CancellationToken cancellationToken = default) public override async Task ExecuteNativeRequestsAsync(List<IRequestBundle<ImapRequest>> batchedRequests, CancellationToken cancellationToken = default)
@@ -1035,5 +1035,10 @@ namespace Wino.Core.Synchronizers.Mail
/// <param name="localFolder">Local folder.</param> /// <param name="localFolder">Local folder.</param>
public bool ShouldUpdateFolder(IMailFolder remoteFolder, MailItemFolder localFolder) public bool ShouldUpdateFolder(IMailFolder remoteFolder, MailItemFolder localFolder)
=> !localFolder.FolderName.Equals(remoteFolder.Name, StringComparison.OrdinalIgnoreCase); => !localFolder.FolderName.Equals(remoteFolder.Name, StringComparison.OrdinalIgnoreCase);
protected override Task<CalendarSynchronizationResult> SynchronizeCalendarEventsInternalAsync(CalendarSynchronizationOptions options, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
} }
} }

View File

@@ -133,7 +133,7 @@ namespace Wino.Core.Synchronizers.Mail
#endregion #endregion
protected override async Task<SynchronizationResult> SynchronizeInternalAsync(SynchronizationOptions options, CancellationToken cancellationToken = default) protected override async Task<MailSynchronizationResult> SynchronizeMailsInternalAsync(MailSynchronizationOptions options, CancellationToken cancellationToken = default)
{ {
var downloadedMessageIds = new List<string>(); var downloadedMessageIds = new List<string>();
@@ -146,7 +146,7 @@ namespace Wino.Core.Synchronizers.Mail
await SynchronizeFoldersAsync(cancellationToken).ConfigureAwait(false); await SynchronizeFoldersAsync(cancellationToken).ConfigureAwait(false);
if (options.Type != SynchronizationType.FoldersOnly) if (options.Type != MailSynchronizationType.FoldersOnly)
{ {
var synchronizationFolders = await _outlookChangeProcessor.GetSynchronizationFoldersAsync(options).ConfigureAwait(false); var synchronizationFolders = await _outlookChangeProcessor.GetSynchronizationFoldersAsync(options).ConfigureAwait(false);
@@ -181,7 +181,7 @@ namespace Wino.Core.Synchronizers.Mail
var unreadNewItems = await _outlookChangeProcessor.GetDownloadedUnreadMailsAsync(Account.Id, downloadedMessageIds).ConfigureAwait(false); var unreadNewItems = await _outlookChangeProcessor.GetDownloadedUnreadMailsAsync(Account.Id, downloadedMessageIds).ConfigureAwait(false);
return SynchronizationResult.Completed(unreadNewItems); return MailSynchronizationResult.Completed(unreadNewItems);
} }
private async Task<IEnumerable<string>> SynchronizeFolderAsync(MailItemFolder folder, CancellationToken cancellationToken = default) private async Task<IEnumerable<string>> SynchronizeFolderAsync(MailItemFolder folder, CancellationToken cancellationToken = default)
@@ -956,5 +956,10 @@ namespace Wino.Core.Synchronizers.Mail
return [package]; return [package];
} }
protected override Task<CalendarSynchronizationResult> SynchronizeCalendarEventsInternalAsync(CalendarSynchronizationOptions options, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
} }
} }

View File

@@ -23,7 +23,7 @@ using Wino.Messaging.UI;
namespace Wino.Core.Synchronizers namespace Wino.Core.Synchronizers
{ {
public abstract class WinoSynchronizer<TBaseRequest, TMessageType, TCalendarEventType> : BaseSynchronizer<TBaseRequest>, IBaseMailSynchronizer public abstract class WinoSynchronizer<TBaseRequest, TMessageType, TCalendarEventType> : BaseSynchronizer<TBaseRequest>, IWinoSynchronizerBase
{ {
protected ILogger Logger = Log.ForContext<WinoSynchronizer<TBaseRequest, TMessageType, TCalendarEventType>>(); protected ILogger Logger = Log.ForContext<WinoSynchronizer<TBaseRequest, TMessageType, TCalendarEventType>>();
@@ -55,18 +55,23 @@ namespace Wino.Core.Synchronizers
/// </summary> /// </summary>
protected virtual Task SynchronizeAliasesAsync() => Task.CompletedTask; protected virtual Task SynchronizeAliasesAsync() => Task.CompletedTask;
/// <summary> /// <summary>
/// Internally synchronizes the account with the given options. /// Internally synchronizes the account's mails with the given options.
/// Not exposed and overriden for each synchronizer. /// Not exposed and overriden for each synchronizer.
/// </summary> /// </summary>
/// <param name="options">Synchronization options.</param> /// <param name="options">Synchronization options.</param>
/// <param name="cancellationToken">Cancellation token.</param> /// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Synchronization result that contains summary of the sync.</returns> /// <returns>Synchronization result that contains summary of the sync.</returns>
protected abstract Task<SynchronizationResult> SynchronizeInternalAsync(SynchronizationOptions options, CancellationToken cancellationToken = default); protected abstract Task<MailSynchronizationResult> SynchronizeMailsInternalAsync(MailSynchronizationOptions options, CancellationToken cancellationToken = default);
/// <summary>
/// Internally synchronizes the events of the account with given options.
/// Not exposed and overriden for each synchronizer.
/// </summary>
/// <param name="options">Synchronization options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Synchronization result that contains summary of the sync.</returns>
protected abstract Task<CalendarSynchronizationResult> SynchronizeCalendarEventsInternalAsync(CalendarSynchronizationOptions options, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Batches network requests, executes them, and does the needed synchronization after the batch request execution. /// Batches network requests, executes them, and does the needed synchronization after the batch request execution.
@@ -74,7 +79,7 @@ namespace Wino.Core.Synchronizers
/// <param name="options">Synchronization options.</param> /// <param name="options">Synchronization options.</param>
/// <param name="cancellationToken">Cancellation token.</param> /// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Synchronization result that contains summary of the sync.</returns> /// <returns>Synchronization result that contains summary of the sync.</returns>
public async Task<SynchronizationResult> SynchronizeAsync(SynchronizationOptions options, CancellationToken cancellationToken = default) public async Task<MailSynchronizationResult> SynchronizeMailsAsync(MailSynchronizationOptions options, CancellationToken cancellationToken = default)
{ {
try try
{ {
@@ -159,7 +164,7 @@ namespace Wino.Core.Synchronizers
// Execute request sync options should be re-calculated after execution. // Execute request sync options should be re-calculated after execution.
// This is the part we decide which individual folders must be synchronized // This is the part we decide which individual folders must be synchronized
// after the batch request execution. // after the batch request execution.
if (options.Type == SynchronizationType.ExecuteRequests) if (options.Type == MailSynchronizationType.ExecuteRequests)
options = GetSynchronizationOptionsAfterRequestExecution(requestCopies); options = GetSynchronizationOptionsAfterRequestExecution(requestCopies);
State = AccountSynchronizerState.Synchronizing; State = AccountSynchronizerState.Synchronizing;
@@ -169,9 +174,9 @@ namespace Wino.Core.Synchronizers
// Handle special synchronization types. // Handle special synchronization types.
// Profile information sync. // Profile information sync.
if (options.Type == SynchronizationType.UpdateProfile) if (options.Type == MailSynchronizationType.UpdateProfile)
{ {
if (!Account.IsProfileInfoSyncSupported) return SynchronizationResult.Empty; if (!Account.IsProfileInfoSyncSupported) return MailSynchronizationResult.Empty;
ProfileInformation newProfileInformation = null; ProfileInformation newProfileInformation = null;
@@ -183,28 +188,28 @@ namespace Wino.Core.Synchronizers
{ {
Log.Error(ex, "Failed to update profile information for {Name}", Account.Name); Log.Error(ex, "Failed to update profile information for {Name}", Account.Name);
return SynchronizationResult.Failed; return MailSynchronizationResult.Failed;
} }
return SynchronizationResult.Completed(newProfileInformation); return MailSynchronizationResult.Completed(newProfileInformation);
} }
// Alias sync. // Alias sync.
if (options.Type == SynchronizationType.Alias) if (options.Type == MailSynchronizationType.Alias)
{ {
if (!Account.IsAliasSyncSupported) return SynchronizationResult.Empty; if (!Account.IsAliasSyncSupported) return MailSynchronizationResult.Empty;
try try
{ {
await SynchronizeAliasesAsync(); await SynchronizeAliasesAsync();
return SynchronizationResult.Empty; return MailSynchronizationResult.Empty;
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, "Failed to update aliases for {Name}", Account.Name); Log.Error(ex, "Failed to update aliases for {Name}", Account.Name);
return SynchronizationResult.Failed; return MailSynchronizationResult.Failed;
} }
} }
@@ -224,7 +229,7 @@ namespace Wino.Core.Synchronizers
} }
// Start the internal synchronization. // Start the internal synchronization.
var synchronizationResult = await SynchronizeInternalAsync(options, activeSynchronizationCancellationToken).ConfigureAwait(false); var synchronizationResult = await SynchronizeMailsInternalAsync(options, activeSynchronizationCancellationToken).ConfigureAwait(false);
PublishUnreadItemChanges(); PublishUnreadItemChanges();
@@ -234,7 +239,7 @@ namespace Wino.Core.Synchronizers
{ {
Logger.Warning("Synchronization canceled."); Logger.Warning("Synchronization canceled.");
return SynchronizationResult.Canceled; return MailSynchronizationResult.Canceled;
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -271,7 +276,7 @@ namespace Wino.Core.Synchronizers
/// </summary> /// </summary>
/// <param name="batches">Batch requests to run in synchronization.</param> /// <param name="batches">Batch requests to run in synchronization.</param>
/// <returns>New synchronization options with minimal HTTP effort.</returns> /// <returns>New synchronization options with minimal HTTP effort.</returns>
private SynchronizationOptions GetSynchronizationOptionsAfterRequestExecution(List<IRequestBase> requests) private MailSynchronizationOptions GetSynchronizationOptionsAfterRequestExecution(List<IRequestBase> requests)
{ {
List<Guid> synchronizationFolderIds = requests List<Guid> synchronizationFolderIds = requests
.Where(a => a is ICustomFolderSynchronizationRequest) .Where(a => a is ICustomFolderSynchronizationRequest)
@@ -279,7 +284,7 @@ namespace Wino.Core.Synchronizers
.SelectMany(a => a.SynchronizationFolderIds) .SelectMany(a => a.SynchronizationFolderIds)
.ToList(); .ToList();
var options = new SynchronizationOptions() var options = new MailSynchronizationOptions()
{ {
AccountId = Account.Id, AccountId = Account.Id,
}; };
@@ -288,13 +293,13 @@ namespace Wino.Core.Synchronizers
{ {
// Gather FolderIds to synchronize. // Gather FolderIds to synchronize.
options.Type = SynchronizationType.CustomFolders; options.Type = MailSynchronizationType.CustomFolders;
options.SynchronizationFolderIds = synchronizationFolderIds; options.SynchronizationFolderIds = synchronizationFolderIds;
} }
else else
{ {
// At this point it's a mix of everything. Do full sync. // At this point it's a mix of everything. Do full sync.
options.Type = SynchronizationType.FullFolders; options.Type = MailSynchronizationType.FullFolders;
} }
return options; return options;

View File

@@ -159,13 +159,13 @@ namespace Wino.Mail.ViewModels
// Start profile information synchronization. // Start profile information synchronization.
// It's only available for Outlook and Gmail synchronizers. // It's only available for Outlook and Gmail synchronizers.
var profileSyncOptions = new SynchronizationOptions() var profileSyncOptions = new MailSynchronizationOptions()
{ {
AccountId = createdAccount.Id, AccountId = createdAccount.Id,
Type = SynchronizationType.UpdateProfile Type = MailSynchronizationType.UpdateProfile
}; };
var profileSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<SynchronizationResult, NewSynchronizationRequested>(new NewSynchronizationRequested(profileSyncOptions, SynchronizationSource.Client)); var profileSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewSynchronizationRequested>(new NewSynchronizationRequested(profileSyncOptions, SynchronizationSource.Client));
var profileSynchronizationResult = profileSynchronizationResponse.Data; var profileSynchronizationResult = profileSynchronizationResponse.Data;
@@ -189,13 +189,13 @@ namespace Wino.Mail.ViewModels
creationDialog.State = AccountCreationDialogState.PreparingFolders; creationDialog.State = AccountCreationDialogState.PreparingFolders;
// Start synchronizing folders. // Start synchronizing folders.
var folderSyncOptions = new SynchronizationOptions() var folderSyncOptions = new MailSynchronizationOptions()
{ {
AccountId = createdAccount.Id, AccountId = createdAccount.Id,
Type = SynchronizationType.FoldersOnly Type = MailSynchronizationType.FoldersOnly
}; };
var folderSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<SynchronizationResult, NewSynchronizationRequested>(new NewSynchronizationRequested(folderSyncOptions, SynchronizationSource.Client)); var folderSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewSynchronizationRequested>(new NewSynchronizationRequested(folderSyncOptions, SynchronizationSource.Client));
var folderSynchronizationResult = folderSynchronizationResponse.Data; var folderSynchronizationResult = folderSynchronizationResponse.Data;
@@ -207,13 +207,13 @@ namespace Wino.Mail.ViewModels
{ {
// Try to synchronize aliases for the account. // Try to synchronize aliases for the account.
var aliasSyncOptions = new SynchronizationOptions() var aliasSyncOptions = new MailSynchronizationOptions()
{ {
AccountId = createdAccount.Id, AccountId = createdAccount.Id,
Type = SynchronizationType.Alias Type = MailSynchronizationType.Alias
}; };
var aliasSyncResponse = await WinoServerConnectionManager.GetResponseAsync<SynchronizationResult, NewSynchronizationRequested>(new NewSynchronizationRequested(aliasSyncOptions, SynchronizationSource.Client)); var aliasSyncResponse = await WinoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewSynchronizationRequested>(new NewSynchronizationRequested(aliasSyncOptions, SynchronizationSource.Client));
var aliasSynchronizationResult = folderSynchronizationResponse.Data; var aliasSynchronizationResult = folderSynchronizationResponse.Data;
if (aliasSynchronizationResult.CompletedState != SynchronizationCompletedState.Success) if (aliasSynchronizationResult.CompletedState != SynchronizationCompletedState.Success)

View File

@@ -76,13 +76,13 @@ namespace Wino.Mail.ViewModels
{ {
if (!CanSynchronizeAliases) return; if (!CanSynchronizeAliases) return;
var aliasSyncOptions = new SynchronizationOptions() var aliasSyncOptions = new MailSynchronizationOptions()
{ {
AccountId = Account.Id, AccountId = Account.Id,
Type = SynchronizationType.Alias Type = MailSynchronizationType.Alias
}; };
var aliasSyncResponse = await _winoServerConnectionManager.GetResponseAsync<SynchronizationResult, NewSynchronizationRequested>(new NewSynchronizationRequested(aliasSyncOptions, SynchronizationSource.Client)); var aliasSyncResponse = await _winoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewSynchronizationRequested>(new NewSynchronizationRequested(aliasSyncOptions, SynchronizationSource.Client));
if (aliasSyncResponse.IsSuccess) if (aliasSyncResponse.IsSuccess)
await LoadAliasesAsync(); await LoadAliasesAsync();

View File

@@ -312,10 +312,10 @@ namespace Wino.Mail.ViewModels
foreach (var account in accounts) foreach (var account in accounts)
{ {
var options = new SynchronizationOptions() var options = new MailSynchronizationOptions()
{ {
AccountId = account.Id, AccountId = account.Id,
Type = SynchronizationType.FullFolders Type = MailSynchronizationType.FullFolders
}; };
Messenger.Send(new NewSynchronizationRequested(options, SynchronizationSource.Client)); Messenger.Send(new NewSynchronizationRequested(options, SynchronizationSource.Client));
@@ -885,10 +885,10 @@ namespace Wino.Mail.ViewModels
await ChangeLoadedAccountAsync(createdMenuItem); await ChangeLoadedAccountAsync(createdMenuItem);
// Each created account should start a new synchronization automatically. // Each created account should start a new synchronization automatically.
var options = new SynchronizationOptions() var options = new MailSynchronizationOptions()
{ {
AccountId = createdAccount.Id, AccountId = createdAccount.Id,
Type = SynchronizationType.FullFolders, Type = MailSynchronizationType.FullFolders,
}; };
Messenger.Send(new NewSynchronizationRequested(options, SynchronizationSource.Client)); Messenger.Send(new NewSynchronizationRequested(options, SynchronizationSource.Client));

View File

@@ -471,10 +471,10 @@ namespace Wino.Mail.ViewModels
foreach (var folder in ActiveFolder.HandlingFolders) foreach (var folder in ActiveFolder.HandlingFolders)
{ {
var options = new SynchronizationOptions() var options = new MailSynchronizationOptions()
{ {
AccountId = folder.MailAccountId, AccountId = folder.MailAccountId,
Type = SynchronizationType.CustomFolders, Type = MailSynchronizationType.CustomFolders,
SynchronizationFolderIds = [folder.Id], SynchronizationFolderIds = [folder.Id],
GroupedSynchronizationTrackingId = trackingSynchronizationId GroupedSynchronizationTrackingId = trackingSynchronizationId
}; };
@@ -516,7 +516,7 @@ namespace Wino.Mail.ViewModels
{ {
if (string.IsNullOrEmpty(SearchQuery) && IsInSearchMode) if (string.IsNullOrEmpty(SearchQuery) && IsInSearchMode)
{ {
UpdateFolderPivotsAsync(); await UpdateFolderPivotsAsync();
IsInSearchMode = false; IsInSearchMode = false;
await InitializeFolderAsync(); await InitializeFolderAsync();
} }
@@ -892,7 +892,7 @@ namespace Wino.Mail.ViewModels
OnPropertyChanged(nameof(IsArchiveSpecialFolder)); OnPropertyChanged(nameof(IsArchiveSpecialFolder));
// Prepare Focused - Other or folder name tabs. // Prepare Focused - Other or folder name tabs.
UpdateFolderPivotsAsync(); await UpdateFolderPivotsAsync();
// Reset filters and sorting options. // Reset filters and sorting options.
ResetFilters(); ResetFilters();

View File

@@ -228,7 +228,7 @@ namespace Wino
{ {
try try
{ {
var synchronizationResultResponse = await AppServiceConnectionManager.GetResponseAsync<SynchronizationResult, NewSynchronizationRequested>(message); var synchronizationResultResponse = await AppServiceConnectionManager.GetResponseAsync<MailSynchronizationResult, NewSynchronizationRequested>(message);
synchronizationResultResponse.ThrowIfFailed(); synchronizationResultResponse.ThrowIfFailed();
} }
catch (WinoServerException serverException) catch (WinoServerException serverException)

View File

@@ -91,10 +91,10 @@ namespace Wino.Services
WeakReferenceMessenger.Default.Send(new AccountFolderConfigurationUpdated(accountId)); WeakReferenceMessenger.Default.Send(new AccountFolderConfigurationUpdated(accountId));
var options = new SynchronizationOptions() var options = new MailSynchronizationOptions()
{ {
AccountId = accountId, AccountId = accountId,
Type = SynchronizationType.FullFolders, Type = MailSynchronizationType.FullFolders,
}; };
WeakReferenceMessenger.Default.Send(new NewSynchronizationRequested(options, SynchronizationSource.Client)); WeakReferenceMessenger.Default.Send(new NewSynchronizationRequested(options, SynchronizationSource.Client));

View File

@@ -8,5 +8,5 @@ namespace Wino.Messaging.Server
/// Triggers a new synchronization if possible. /// Triggers a new synchronization if possible.
/// </summary> /// </summary>
/// <param name="Options">Options for synchronization.</param> /// <param name="Options">Options for synchronization.</param>
public record NewSynchronizationRequested(SynchronizationOptions Options, SynchronizationSource Source) : IClientMessage; public record NewSynchronizationRequested(MailSynchronizationOptions Options, SynchronizationSource Source) : IClientMessage;
} }

View File

@@ -16,10 +16,10 @@ namespace Wino.Server.MessageHandlers
/// <summary> /// <summary>
/// Handler for NewSynchronizationRequested from the client. /// Handler for NewSynchronizationRequested from the client.
/// </summary> /// </summary>
public class SynchronizationRequestHandler : ServerMessageHandler<NewSynchronizationRequested, SynchronizationResult> public class SynchronizationRequestHandler : ServerMessageHandler<NewSynchronizationRequested, MailSynchronizationResult>
{ {
public override WinoServerResponse<SynchronizationResult> FailureDefaultResponse(Exception ex) public override WinoServerResponse<MailSynchronizationResult> FailureDefaultResponse(Exception ex)
=> WinoServerResponse<SynchronizationResult>.CreateErrorResponse(ex.Message); => WinoServerResponse<MailSynchronizationResult>.CreateErrorResponse(ex.Message);
private readonly ISynchronizerFactory _synchronizerFactory; private readonly ISynchronizerFactory _synchronizerFactory;
private readonly INotificationBuilder _notificationBuilder; private readonly INotificationBuilder _notificationBuilder;
@@ -34,7 +34,7 @@ namespace Wino.Server.MessageHandlers
_folderService = folderService; _folderService = folderService;
} }
protected override async Task<WinoServerResponse<SynchronizationResult>> HandleAsync(NewSynchronizationRequested message, CancellationToken cancellationToken = default) protected override async Task<WinoServerResponse<MailSynchronizationResult>> HandleAsync(NewSynchronizationRequested message, CancellationToken cancellationToken = default)
{ {
var synchronizer = await _synchronizerFactory.GetAccountSynchronizerAsync(message.Options.AccountId); var synchronizer = await _synchronizerFactory.GetAccountSynchronizerAsync(message.Options.AccountId);
@@ -45,12 +45,12 @@ namespace Wino.Server.MessageHandlers
// It happens very common and there is no need to send a message for each synchronization. // It happens very common and there is no need to send a message for each synchronization.
bool shouldReportSynchronizationResult = bool shouldReportSynchronizationResult =
message.Options.Type != SynchronizationType.ExecuteRequests && message.Options.Type != MailSynchronizationType.ExecuteRequests &&
message.Source == SynchronizationSource.Client; message.Source == SynchronizationSource.Client;
try try
{ {
var synchronizationResult = await synchronizer.SynchronizeAsync(message.Options, cancellationToken).ConfigureAwait(false); var synchronizationResult = await synchronizer.SynchronizeMailsAsync(message.Options, cancellationToken).ConfigureAwait(false);
if (synchronizationResult.DownloadedMessages?.Any() ?? false || !synchronizer.Account.Preferences.IsNotificationsEnabled) if (synchronizationResult.DownloadedMessages?.Any() ?? false || !synchronizer.Account.Preferences.IsNotificationsEnabled)
{ {
@@ -79,7 +79,7 @@ namespace Wino.Server.MessageHandlers
WeakReferenceMessenger.Default.Send(completedMessage); WeakReferenceMessenger.Default.Send(completedMessage);
} }
return WinoServerResponse<SynchronizationResult>.CreateSuccessResponse(synchronizationResult); return WinoServerResponse<MailSynchronizationResult>.CreateSuccessResponse(synchronizationResult);
} }
// TODO: Following cases might always be thrown from server. Handle them properly. // TODO: Following cases might always be thrown from server. Handle them properly.

View File

@@ -90,10 +90,10 @@ namespace Wino.Server
foreach (var account in accounts) foreach (var account in accounts)
{ {
var options = new SynchronizationOptions var options = new MailSynchronizationOptions
{ {
AccountId = account.Id, AccountId = account.Id,
Type = SynchronizationType.InboxOnly, Type = MailSynchronizationType.InboxOnly,
}; };
var request = new NewSynchronizationRequested(options, SynchronizationSource.Server); var request = new NewSynchronizationRequested(options, SynchronizationSource.Server);

View File

@@ -542,11 +542,11 @@ namespace Wino.Services
public Task<List<MailFolderPairMetadata>> GetMailFolderPairMetadatasAsync(string mailCopyId) public Task<List<MailFolderPairMetadata>> GetMailFolderPairMetadatasAsync(string mailCopyId)
=> GetMailFolderPairMetadatasAsync(new List<string>() { mailCopyId }); => GetMailFolderPairMetadatasAsync(new List<string>() { mailCopyId });
public async Task<List<MailItemFolder>> GetSynchronizationFoldersAsync(SynchronizationOptions options) public async Task<List<MailItemFolder>> GetSynchronizationFoldersAsync(MailSynchronizationOptions options)
{ {
var folders = new List<MailItemFolder>(); var folders = new List<MailItemFolder>();
if (options.Type == SynchronizationType.FullFolders) if (options.Type == MailSynchronizationType.FullFolders)
{ {
// Only get sync enabled folders. // Only get sync enabled folders.
@@ -564,11 +564,11 @@ namespace Wino.Services
var mustHaveFolders = await GetInboxSynchronizationFoldersAsync(options.AccountId); var mustHaveFolders = await GetInboxSynchronizationFoldersAsync(options.AccountId);
if (options.Type == SynchronizationType.InboxOnly) if (options.Type == MailSynchronizationType.InboxOnly)
{ {
return mustHaveFolders; return mustHaveFolders;
} }
else if (options.Type == SynchronizationType.CustomFolders) else if (options.Type == MailSynchronizationType.CustomFolders)
{ {
// Only get the specified and enabled folders. // Only get the specified and enabled folders.