diff --git a/Wino.Core.Domain/Interfaces/ISynchronizationManager.cs b/Wino.Core.Domain/Interfaces/ISynchronizationManager.cs
new file mode 100644
index 00000000..c1ee65c6
--- /dev/null
+++ b/Wino.Core.Domain/Interfaces/ISynchronizationManager.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Wino.Core.Domain.Entities.Mail;
+using Wino.Core.Domain.Entities.Shared;
+using Wino.Core.Domain.Enums;
+using Wino.Core.Domain.Models.Authentication;
+using Wino.Core.Domain.Models.Connectivity;
+using Wino.Core.Domain.Models.Synchronization;
+
+namespace Wino.Core.Domain.Interfaces;
+
+///
+/// Interface for the singleton synchronization manager that handles synchronizer instances and operations.
+///
+public interface ISynchronizationManager
+{
+ ///
+ /// Initializes the SynchronizationManager with required dependencies.
+ ///
+ Task InitializeAsync(ISynchronizerFactory synchronizerFactory,
+ IImapTestService imapTestService,
+ IAccountService accountService,
+ IAuthenticationProvider authenticationProvider);
+
+ ///
+ /// Tests IMAP server connectivity for the given server information.
+ ///
+ Task TestImapConnectivityAsync(CustomServerInformation serverInformation, bool allowSSLHandshake);
+
+ ///
+ /// Starts a new mail synchronization for the given account.
+ ///
+ Task SynchronizeMailAsync(MailSynchronizationOptions options,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// Checks if there is an ongoing synchronization for the given account.
+ ///
+ bool IsAccountSynchronizing(Guid accountId);
+
+ ///
+ /// Queues a mail action request to the corresponding account's synchronizer.
+ ///
+ Task QueueRequestAsync(IRequestBase request, Guid accountId);
+
+ ///
+ /// Handles folder synchronization for the given account.
+ ///
+ Task SynchronizeFoldersAsync(Guid accountId,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// Handles alias synchronization for the given account.
+ ///
+ Task SynchronizeAliasesAsync(Guid accountId,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// Handles profile synchronization for the given account.
+ ///
+ Task SynchronizeProfileAsync(Guid accountId,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// Downloads a MIME message for the given mail item.
+ ///
+ Task DownloadMimeMessageAsync(MailCopy mailItem, Guid accountId,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// Creates a new synchronizer for a newly added account.
+ ///
+ Task CreateSynchronizerForAccountAsync(MailAccount account);
+
+ ///
+ /// Destroys the synchronizer for the given account.
+ ///
+ Task DestroySynchronizerAsync(Guid accountId);
+
+ ///
+ /// Gets all cached synchronizers.
+ ///
+ IEnumerable GetAllSynchronizers();
+
+ ///
+ /// Gets a synchronizer for the given account ID.
+ ///
+ Task GetSynchronizerAsync(Guid accountId);
+
+ ///
+ /// Handles OAuth authentication for the specified provider.
+ ///
+ Task HandleAuthorizationAsync(MailProviderType providerType,
+ MailAccount account = null,
+ bool proposeCopyAuthorizationURL = false);
+}
\ No newline at end of file
diff --git a/Wino.Core.Domain/Interfaces/IWinoServerConnectionManager.cs b/Wino.Core.Domain/Interfaces/IWinoServerConnectionManager.cs
deleted file mode 100644
index 7fc5c4fa..00000000
--- a/Wino.Core.Domain/Interfaces/IWinoServerConnectionManager.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Wino.Core.Domain.Interfaces;
-
-///
-/// Simple wrapper class to maintain compatibility with the original WinoServerResponse structure.
-///
-/// Type of the expected response.
-public class WinoServerResponse
-{
- public bool IsSuccess { get; set; }
- public string Message { get; set; }
- public T Data { get; set; }
-
- public static WinoServerResponse CreateSuccessResponse(T data)
- {
- return new WinoServerResponse
- {
- IsSuccess = true,
- Data = data
- };
- }
-
- public static WinoServerResponse CreateErrorResponse(string message)
- {
- return new WinoServerResponse
- {
- IsSuccess = false,
- Message = message
- };
- }
-
- public void ThrowIfFailed()
- {
- if (!IsSuccess)
- throw new InvalidOperationException(Message);
- }
-}
-
-///
-/// Connection status enum to maintain compatibility.
-///
-public enum WinoServerConnectionStatus
-{
- None,
- Connecting,
- Connected,
- Disconnected,
- Failed
-}
-
-public interface IWinoServerConnectionManager
-{
- ///
- /// When the connection status changes, this event will be triggered.
- ///
- event EventHandler StatusChanged;
-
- ///
- /// Gets the connection status.
- ///
- WinoServerConnectionStatus Status { get; }
-
- ///
- /// Launches Full Trust process (Wino Server) and awaits connection completion.
- /// If connection is not established in 10 seconds, it will return false.
- /// If the server process is already running, it'll connect to existing one.
- /// If the server process is not running, it'll be launched and connection establishment is awaited.
- ///
- /// Whether connection is established or not.
- Task ConnectAsync();
-
- ///
- /// Queues a new user request to be processed by Wino Server.
- /// Healthy connection must present before calling this method.
- ///
- /// Request to queue for synchronizer in the server.
- /// Account id to queueu request for.
- Task QueueRequestAsync(IRequestBase request, Guid accountId);
-
- ///
- /// Returns response from server for the given request.
- ///
- /// Response type.
- /// Request type.
- /// Request type.
- /// Response received from the server for the given TResponse type.
- Task> GetResponseAsync(TRequestType clientMessage, CancellationToken cancellationToken = default) where TRequestType : IClientMessage;
-
- ///
- /// Handle for connecting to the server.
- /// If the server is already running, it'll connect to existing one.
- /// Callers can await this handle to wait for connection establishment.
- ///
- TaskCompletionSource ConnectingHandle { get; }
-}
-
-public interface IWinoServerConnectionManager : IWinoServerConnectionManager, IInitializeAsync
-{
- ///
- /// Existing connection handle to the server of TAppServiceConnection type.
- ///
- TAppServiceConnection Connection { get; set; }
-}
diff --git a/Wino.Core.ViewModels/AccountManagementPageViewModelBase.cs b/Wino.Core.ViewModels/AccountManagementPageViewModelBase.cs
index cb282865..03184d43 100644
--- a/Wino.Core.ViewModels/AccountManagementPageViewModelBase.cs
+++ b/Wino.Core.ViewModels/AccountManagementPageViewModelBase.cs
@@ -40,7 +40,6 @@ public abstract partial class AccountManagementPageViewModelBase : CoreBaseViewM
public int FREE_ACCOUNT_COUNT { get; } = 3;
protected IDialogServiceBase DialogService { get; }
- protected IWinoServerConnectionManager WinoServerConnectionManager { get; }
protected INavigationService NavigationService { get; }
protected IAccountService AccountService { get; }
protected IProviderService ProviderService { get; }
@@ -49,7 +48,6 @@ public abstract partial class AccountManagementPageViewModelBase : CoreBaseViewM
protected IPreferencesService PreferencesService { get; }
public AccountManagementPageViewModelBase(IDialogServiceBase dialogService,
- IWinoServerConnectionManager winoServerConnectionManager,
INavigationService navigationService,
IAccountService accountService,
IProviderService providerService,
@@ -58,7 +56,6 @@ public abstract partial class AccountManagementPageViewModelBase : CoreBaseViewM
IPreferencesService preferencesService)
{
DialogService = dialogService;
- WinoServerConnectionManager = winoServerConnectionManager;
NavigationService = navigationService;
AccountService = accountService;
ProviderService = providerService;
diff --git a/Wino.Core.WinUI/CoreUWPContainerSetup.cs b/Wino.Core.WinUI/CoreUWPContainerSetup.cs
index 95d74bc0..02f4aeac 100644
--- a/Wino.Core.WinUI/CoreUWPContainerSetup.cs
+++ b/Wino.Core.WinUI/CoreUWPContainerSetup.cs
@@ -1,6 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
-using Windows.ApplicationModel.AppService;
using Wino.Core.Domain.Interfaces;
using Wino.Core.ViewModels;
using Wino.Core.WinUI.Services;
@@ -12,10 +11,6 @@ public static class CoreUWPContainerSetup
{
public static void RegisterCoreUWPServices(this IServiceCollection services)
{
- var serverConnectionManager = new EmptyWinoServerConnectionManager();
-
- services.AddSingleton(serverConnectionManager);
- services.AddSingleton>(serverConnectionManager);
services.AddSingleton, ApplicationResourceManager>();
services.AddSingleton();
diff --git a/Wino.Core.WinUI/Services/EmptyWinoServerConnectionManager.cs b/Wino.Core.WinUI/Services/EmptyWinoServerConnectionManager.cs
deleted file mode 100644
index a6b0da50..00000000
--- a/Wino.Core.WinUI/Services/EmptyWinoServerConnectionManager.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using Wino.Core.Domain.Interfaces;
-
-namespace Wino.Core.WinUI.Services;
-
-///
-/// Empty implementation of IWinoServerConnectionManager that returns default values.
-/// This replaces the old AppServiceConnection-based implementation.
-///
-public class EmptyWinoServerConnectionManager : IWinoServerConnectionManager
-{
- public event EventHandler StatusChanged { add { } remove { } }
-
- public WinoServerConnectionStatus Status => WinoServerConnectionStatus.Connected;
-
- public TaskCompletionSource ConnectingHandle { get; } = new TaskCompletionSource();
-
- public EmptyWinoServerConnectionManager()
- {
- ConnectingHandle.SetResult(true);
- }
-
- public Task ConnectAsync()
- {
- return Task.FromResult(true);
- }
-
- public Task QueueRequestAsync(IRequestBase request, Guid accountId)
- {
- return Task.CompletedTask;
- }
-
- public Task> GetResponseAsync(TRequestType clientMessage, CancellationToken cancellationToken = default)
- where TRequestType : IClientMessage
- {
- var response = WinoServerResponse.CreateSuccessResponse(default(TResponse));
- return Task.FromResult(response);
- }
-}
-
-///
-/// Generic empty implementation for typed connection managers.
-///
-/// The connection type (not used in this implementation)
-public class EmptyWinoServerConnectionManager : EmptyWinoServerConnectionManager, IWinoServerConnectionManager
-{
- public TAppServiceConnection Connection { get; set; }
-
- public Task InitializeAsync()
- {
- return Task.CompletedTask;
- }
-}
\ No newline at end of file
diff --git a/Wino.Core.WinUI/WinoApplication.cs b/Wino.Core.WinUI/WinoApplication.cs
index d1dd2f2c..efbb297d 100644
--- a/Wino.Core.WinUI/WinoApplication.cs
+++ b/Wino.Core.WinUI/WinoApplication.cs
@@ -18,6 +18,7 @@ using Windows.Storage;
using Wino.Core.Domain;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Translations;
+using Wino.Core.Services;
using Wino.Messaging.Client.Shell;
using Wino.Services;
using WinUIEx;
@@ -84,6 +85,7 @@ public abstract class WinoApplication : Application, IRecipient
yield return DatabaseService;
yield return TranslationService;
yield return NewThemeService; // Initialize NewThemeService instead of old ThemeService
+ yield return Services.GetService();
// yield return ThemeService; // Keep old service for backward compatibility but don't initialize
}
diff --git a/Wino.Core/CoreContainerSetup.cs b/Wino.Core/CoreContainerSetup.cs
index 1853c4c9..2645d3de 100644
--- a/Wino.Core/CoreContainerSetup.cs
+++ b/Wino.Core/CoreContainerSetup.cs
@@ -17,6 +17,8 @@ public static class CoreContainerSetup
services.AddSingleton(loggerLevelSwitcher);
services.AddSingleton();
+ services.AddSingleton(provider => SynchronizationManager.Instance);
+ services.AddTransient();
services.AddTransient();
services.AddTransient();
diff --git a/Wino.Core/Services/SynchronizationManager.cs b/Wino.Core/Services/SynchronizationManager.cs
new file mode 100644
index 00000000..2f8f453e
--- /dev/null
+++ b/Wino.Core/Services/SynchronizationManager.cs
@@ -0,0 +1,451 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Serilog;
+using Wino.Core.Domain.Entities.Mail;
+using Wino.Core.Domain.Entities.Shared;
+using Wino.Core.Domain.Enums;
+using Wino.Core.Domain.Exceptions;
+using Wino.Core.Domain.Interfaces;
+using Wino.Core.Domain.Models.Authentication;
+using Wino.Core.Domain.Models.Connectivity;
+using Wino.Core.Domain.Models.Synchronization;
+using Wino.Messaging.Server;
+
+namespace Wino.Core.Services;
+
+///
+/// Singleton manager that handles synchronizer instances and operations for all accounts.
+/// Replaces the old WinoServerConnectionManager functionality.
+///
+public class SynchronizationManager : ISynchronizationManager
+{
+ private static readonly Lazy _instance = new(() => new SynchronizationManager());
+ public static SynchronizationManager Instance => _instance.Value;
+
+ private readonly ConcurrentDictionary _synchronizerCache = new();
+ private readonly SemaphoreSlim _initializationSemaphore = new(1, 1);
+ private readonly ILogger _logger = Log.ForContext();
+
+ private ISynchronizerFactory _synchronizerFactory;
+ private SynchronizerFactory _concreteSynchronizerFactory;
+ private IImapTestService _imapTestService;
+ private IAccountService _accountService;
+ private IAuthenticationProvider _authenticationProvider;
+ private bool _isInitialized = false;
+
+ private SynchronizationManager() { }
+
+ ///
+ /// Initializes the SynchronizationManager with required dependencies.
+ /// This must be called before using any other methods.
+ ///
+ /// Factory for creating synchronizers
+ /// Service for testing IMAP connectivity
+ /// Service for account operations
+ /// Provider for OAuth authentication
+ public async Task InitializeAsync(ISynchronizerFactory synchronizerFactory,
+ IImapTestService imapTestService,
+ IAccountService accountService,
+ IAuthenticationProvider authenticationProvider)
+ {
+ await _initializationSemaphore.WaitAsync();
+ try
+ {
+ if (_isInitialized) return;
+
+ _synchronizerFactory = synchronizerFactory ?? throw new ArgumentNullException(nameof(synchronizerFactory));
+ _concreteSynchronizerFactory = synchronizerFactory as SynchronizerFactory ?? throw new ArgumentException("SynchronizerFactory must be the concrete implementation");
+ _imapTestService = imapTestService ?? throw new ArgumentNullException(nameof(imapTestService));
+ _accountService = accountService ?? throw new ArgumentNullException(nameof(accountService));
+ _authenticationProvider = authenticationProvider ?? throw new ArgumentNullException(nameof(authenticationProvider));
+
+ // Get all accounts and create synchronizers for them
+ var accounts = await _accountService.GetAccountsAsync();
+
+ foreach (var account in accounts)
+ {
+ try
+ {
+ var synchronizer = _concreteSynchronizerFactory.CreateNewSynchronizer(account);
+ _synchronizerCache.TryAdd(account.Id, synchronizer);
+
+ _logger.Information("Created synchronizer for account {AccountName} ({AccountId})",
+ account.Name, account.Id);
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "Failed to create synchronizer for account {AccountName} ({AccountId})",
+ account.Name, account.Id);
+ }
+ }
+
+ _isInitialized = true;
+ _logger.Information("SynchronizationManager initialized with {Count} synchronizers", _synchronizerCache.Count);
+ }
+ finally
+ {
+ _initializationSemaphore.Release();
+ }
+ }
+
+ ///
+ /// Tests IMAP server connectivity for the given server information.
+ ///
+ /// Server information to test
+ /// Whether to allow SSL handshake
+ /// Test results indicating success or failure with details
+ public async Task TestImapConnectivityAsync(CustomServerInformation serverInformation, bool allowSSLHandshake)
+ {
+ EnsureInitialized();
+
+ try
+ {
+ _logger.Information("Testing IMAP connectivity for {Server}:{Port}",
+ serverInformation.IncomingServer,
+ serverInformation.IncomingServerPort);
+
+ await _imapTestService.TestImapConnectionAsync(serverInformation, allowSSLHandshake);
+
+ _logger.Information("IMAP connectivity test successful");
+ return ImapConnectivityTestResults.Success();
+ }
+ catch (ImapTestSSLCertificateException sslTestException)
+ {
+ _logger.Warning("IMAP connectivity test requires SSL certificate confirmation");
+ return ImapConnectivityTestResults.CertificateUIRequired(
+ sslTestException.Issuer,
+ sslTestException.ExpirationDateString,
+ sslTestException.ValidFromDateString);
+ }
+ catch (ImapClientPoolException clientPoolException)
+ {
+ _logger.Error(clientPoolException, "IMAP connectivity test failed with protocol log");
+ return ImapConnectivityTestResults.Failure(clientPoolException, clientPoolException.ProtocolLog);
+ }
+ catch (Exception exception)
+ {
+ _logger.Error(exception, "IMAP connectivity test failed");
+ return ImapConnectivityTestResults.Failure(exception, string.Empty);
+ }
+ }
+
+ ///
+ /// Starts a new mail synchronization for the given account.
+ ///
+ /// Mail synchronization options
+ /// Cancellation token
+ /// Synchronization result
+ public async Task SynchronizeMailAsync(MailSynchronizationOptions options,
+ CancellationToken cancellationToken = default)
+ {
+ EnsureInitialized();
+
+ var synchronizer = await GetOrCreateSynchronizerAsync(options.AccountId);
+ if (synchronizer == null)
+ {
+ _logger.Error("Could not find or create synchronizer for account {AccountId}", options.AccountId);
+ return MailSynchronizationResult.Failed;
+ }
+
+ _logger.Information("Starting mail synchronization for account {AccountId} with type {SyncType}",
+ options.AccountId, options.Type);
+
+ try
+ {
+ var result = await synchronizer.SynchronizeMailsAsync(options, cancellationToken);
+
+ _logger.Information("Mail synchronization completed for account {AccountId} with state {State}",
+ options.AccountId, result.CompletedState);
+
+ return result;
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "Mail synchronization failed for account {AccountId}", options.AccountId);
+ return MailSynchronizationResult.Failed;
+ }
+ }
+
+ ///
+ /// Checks if there is an ongoing synchronization for the given account.
+ ///
+ /// Account ID to check
+ /// True if synchronization is ongoing, false otherwise
+ public bool IsAccountSynchronizing(Guid accountId)
+ {
+ EnsureInitialized();
+
+ if (_synchronizerCache.TryGetValue(accountId, out var synchronizer))
+ {
+ return synchronizer.State == AccountSynchronizerState.Synchronizing ||
+ synchronizer.State == AccountSynchronizerState.ExecutingRequests;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Queues a mail action request to the corresponding account's synchronizer.
+ ///
+ /// Request to queue
+ /// Account ID to queue the request for
+ public async Task QueueRequestAsync(IRequestBase request, Guid accountId)
+ {
+ EnsureInitialized();
+
+ var synchronizer = await GetOrCreateSynchronizerAsync(accountId);
+ if (synchronizer == null)
+ {
+ _logger.Error("Could not find or create synchronizer for account {AccountId} to queue request", accountId);
+ return;
+ }
+
+ _logger.Debug("Queuing request {RequestType} for account {AccountId}",
+ request.GetType().Name, accountId);
+
+ synchronizer.QueueRequest(request);
+ }
+
+ ///
+ /// Handles folder synchronization for the given account.
+ ///
+ /// Account ID to synchronize folders for
+ /// Cancellation token
+ /// Synchronization result
+ public async Task SynchronizeFoldersAsync(Guid accountId,
+ CancellationToken cancellationToken = default)
+ {
+ EnsureInitialized();
+
+ var options = new MailSynchronizationOptions
+ {
+ AccountId = accountId,
+ Type = MailSynchronizationType.FoldersOnly
+ };
+
+ return await SynchronizeMailAsync(options, cancellationToken);
+ }
+
+ ///
+ /// Handles alias synchronization for the given account.
+ ///
+ /// Account ID to synchronize aliases for
+ /// Cancellation token
+ /// Synchronization result
+ public async Task SynchronizeAliasesAsync(Guid accountId,
+ CancellationToken cancellationToken = default)
+ {
+ EnsureInitialized();
+
+ var options = new MailSynchronizationOptions
+ {
+ AccountId = accountId,
+ Type = MailSynchronizationType.Alias
+ };
+
+ return await SynchronizeMailAsync(options, cancellationToken);
+ }
+
+ ///
+ /// Handles profile synchronization for the given account.
+ ///
+ /// Account ID to synchronize profile for
+ /// Cancellation token
+ /// Synchronization result
+ public async Task SynchronizeProfileAsync(Guid accountId,
+ CancellationToken cancellationToken = default)
+ {
+ EnsureInitialized();
+
+ var options = new MailSynchronizationOptions
+ {
+ AccountId = accountId,
+ Type = MailSynchronizationType.UpdateProfile
+ };
+
+ return await SynchronizeMailAsync(options, cancellationToken);
+ }
+
+ ///
+ /// Downloads a MIME message for the given mail item.
+ ///
+ /// Mail item to download
+ /// Account ID that owns the mail item
+ /// Cancellation token
+ /// Downloaded MIME content path
+ public async Task DownloadMimeMessageAsync(MailCopy mailItem, Guid accountId,
+ CancellationToken cancellationToken = default)
+ {
+ EnsureInitialized();
+
+ var synchronizer = await GetOrCreateSynchronizerAsync(accountId);
+ if (synchronizer == null)
+ {
+ _logger.Error("Could not find or create synchronizer for account {AccountId} to download MIME", accountId);
+ return null;
+ }
+
+ _logger.Debug("Downloading MIME message for mail item {MailItemId}", mailItem.Id);
+
+ try
+ {
+ await synchronizer.DownloadMissingMimeMessageAsync(mailItem, null, cancellationToken);
+ return mailItem.Id.ToString(); // Return some identifier, actual implementation might be different
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "Failed to download MIME message for mail item {MailItemId}", mailItem.Id);
+ return null;
+ }
+ }
+
+ ///
+ /// Creates a new synchronizer for a newly added account.
+ ///
+ /// Account to create synchronizer for
+ /// Created synchronizer
+ public Task CreateSynchronizerForAccountAsync(MailAccount account)
+ {
+ EnsureInitialized();
+
+ try
+ {
+ var synchronizer = _concreteSynchronizerFactory.CreateNewSynchronizer(account);
+ _synchronizerCache.TryAdd(account.Id, synchronizer);
+
+ _logger.Information("Created new synchronizer for account {AccountName} ({AccountId})",
+ account.Name, account.Id);
+
+ return Task.FromResult(synchronizer);
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "Failed to create synchronizer for account {AccountName} ({AccountId})",
+ account.Name, account.Id);
+ return Task.FromResult(null);
+ }
+ }
+
+ ///
+ /// Destroys the synchronizer for the given account.
+ ///
+ /// Account ID to destroy synchronizer for
+ public async Task DestroySynchronizerAsync(Guid accountId)
+ {
+ EnsureInitialized();
+
+ if (_synchronizerCache.TryRemove(accountId, out var synchronizer))
+ {
+ try
+ {
+ await synchronizer.KillSynchronizerAsync();
+ _logger.Information("Destroyed synchronizer for account {AccountId}", accountId);
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "Failed to destroy synchronizer for account {AccountId}", accountId);
+ }
+ }
+ }
+
+ ///
+ /// Gets all cached synchronizers.
+ ///
+ /// Collection of all cached synchronizers
+ public IEnumerable GetAllSynchronizers()
+ {
+ EnsureInitialized();
+ return _synchronizerCache.Values.ToList();
+ }
+
+ ///
+ /// Gets a synchronizer for the given account ID.
+ ///
+ /// Account ID
+ /// Synchronizer if found, null otherwise
+ public async Task GetSynchronizerAsync(Guid accountId)
+ {
+ EnsureInitialized();
+ return await GetOrCreateSynchronizerAsync(accountId);
+ }
+
+ private async Task GetOrCreateSynchronizerAsync(Guid accountId)
+ {
+ if (_synchronizerCache.TryGetValue(accountId, out var existingSynchronizer))
+ {
+ return existingSynchronizer;
+ }
+
+ // Try to create a new synchronizer if not found
+ var account = await _accountService.GetAccountAsync(accountId);
+ if (account != null)
+ {
+ return await CreateSynchronizerForAccountAsync(account);
+ }
+
+ return null;
+ }
+
+ ///
+ /// Handles OAuth authentication for the specified provider.
+ ///
+ /// The mail provider type to authenticate
+ /// Optional account to authenticate (null for initial authentication)
+ /// Whether to propose copying auth URL for Gmail
+ /// Token information containing access token and username
+ public async Task HandleAuthorizationAsync(MailProviderType providerType,
+ MailAccount account = null,
+ bool proposeCopyAuthorizationURL = false)
+ {
+ EnsureInitialized();
+
+ try
+ {
+ var authenticator = _authenticationProvider.GetAuthenticator(providerType);
+
+ // Some users are having issues with Gmail authentication.
+ // Their browsers may never launch to complete authentication.
+ // Offer to copy auth url for them to complete it manually.
+ // Redirection will occur to the app and the token will be saved.
+ if (proposeCopyAuthorizationURL && authenticator is IGmailAuthenticator gmailAuthenticator)
+ {
+ gmailAuthenticator.ProposeCopyAuthURL = true;
+ }
+
+ TokenInformationEx tokenInfo;
+
+ if (account != null)
+ {
+ // Get token for existing account (may trigger interactive auth if token is expired)
+ tokenInfo = await authenticator.GetTokenInformationAsync(account);
+ _logger.Information("Retrieved token for existing account {AccountAddress}", account.Address);
+ }
+ else
+ {
+ // Initial authentication request - there is no account to get token for
+ // This will always trigger interactive authentication
+ tokenInfo = await authenticator.GenerateTokenInformationAsync(null);
+ _logger.Information("Generated new token for {ProviderType} authentication", providerType);
+ }
+
+ return tokenInfo;
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "Failed to handle authorization for {ProviderType}", providerType);
+ throw;
+ }
+ }
+
+ private void EnsureInitialized()
+ {
+ if (!_isInitialized)
+ {
+ throw new InvalidOperationException("SynchronizationManager must be initialized before use. Call InitializeAsync first.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Wino.Core/Services/SynchronizationManagerInitializer.cs b/Wino.Core/Services/SynchronizationManagerInitializer.cs
new file mode 100644
index 00000000..ddae076f
--- /dev/null
+++ b/Wino.Core/Services/SynchronizationManagerInitializer.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Wino.Core.Domain.Interfaces;
+using Wino.Core.Services;
+
+namespace Wino.Core.Services;
+
+///
+/// Service responsible for initializing the SynchronizationManager during app startup.
+///
+public class SynchronizationManagerInitializer : IInitializeAsync
+{
+ private readonly IServiceProvider _serviceProvider;
+
+ public SynchronizationManagerInitializer(IServiceProvider serviceProvider)
+ {
+ _serviceProvider = serviceProvider;
+ }
+
+ public async Task InitializeAsync()
+ {
+ var synchronizerFactory = _serviceProvider.GetRequiredService();
+ var imapTestService = _serviceProvider.GetRequiredService();
+ var accountService = _serviceProvider.GetRequiredService();
+ var authenticationProvider = _serviceProvider.GetRequiredService();
+
+ // Cast to concrete type to access CreateNewSynchronizer method
+ var concreteSynchronizerFactory = synchronizerFactory as SynchronizerFactory;
+
+ await SynchronizationManager.Instance.InitializeAsync(concreteSynchronizerFactory, imapTestService, accountService, authenticationProvider);
+ }
+}
\ No newline at end of file
diff --git a/Wino.Core/Services/WinoRequestDelegator.cs b/Wino.Core/Services/WinoRequestDelegator.cs
index e57c2b0f..7686a9c9 100644
--- a/Wino.Core/Services/WinoRequestDelegator.cs
+++ b/Wino.Core/Services/WinoRequestDelegator.cs
@@ -19,17 +19,14 @@ namespace Wino.Core.Services;
public class WinoRequestDelegator : IWinoRequestDelegator
{
private readonly IWinoRequestProcessor _winoRequestProcessor;
- private readonly IWinoServerConnectionManager _winoServerConnectionManager;
private readonly IFolderService _folderService;
private readonly IMailDialogService _dialogService;
public WinoRequestDelegator(IWinoRequestProcessor winoRequestProcessor,
- IWinoServerConnectionManager winoServerConnectionManager,
IFolderService folderService,
IMailDialogService dialogService)
{
_winoRequestProcessor = winoRequestProcessor;
- _winoServerConnectionManager = winoServerConnectionManager;
_folderService = folderService;
_dialogService = dialogService;
}
@@ -138,14 +135,11 @@ public class WinoRequestDelegator : IWinoRequestDelegator
private async Task QueueRequestAsync(IRequestBase request, Guid accountId)
{
- await EnsureServerConnectedAsync();
- await _winoServerConnectionManager.QueueRequestAsync(request, accountId);
+ await SynchronizationManager.Instance.QueueRequestAsync(request, accountId);
}
- private async Task QueueSynchronizationAsync(Guid accountId)
+ private Task QueueSynchronizationAsync(Guid accountId)
{
- await EnsureServerConnectedAsync();
-
var options = new MailSynchronizationOptions()
{
AccountId = accountId,
@@ -153,12 +147,6 @@ public class WinoRequestDelegator : IWinoRequestDelegator
};
WeakReferenceMessenger.Default.Send(new NewMailSynchronizationRequested(options, SynchronizationSource.Client));
- }
-
- private async Task EnsureServerConnectedAsync()
- {
- if (_winoServerConnectionManager.Status == WinoServerConnectionStatus.Connected) return;
-
- await _winoServerConnectionManager.ConnectAsync();
+ return Task.CompletedTask;
}
}
diff --git a/Wino.Mail.ViewModels/AccountDetailsPageViewModel.cs b/Wino.Mail.ViewModels/AccountDetailsPageViewModel.cs
index 494d8813..073b9204 100644
--- a/Wino.Mail.ViewModels/AccountDetailsPageViewModel.cs
+++ b/Wino.Mail.ViewModels/AccountDetailsPageViewModel.cs
@@ -11,8 +11,8 @@ using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.Navigation;
+using Wino.Core.Services;
using Wino.Messaging.Client.Navigation;
-using Wino.Messaging.Server;
namespace Wino.Mail.ViewModels;
@@ -20,7 +20,6 @@ public partial class AccountDetailsPageViewModel : MailBaseViewModel
{
private readonly IMailDialogService _dialogService;
private readonly IAccountService _accountService;
- private readonly IWinoServerConnectionManager _serverConnectionManager;
private readonly IFolderService _folderService;
private bool isLoaded = false;
@@ -50,12 +49,10 @@ public partial class AccountDetailsPageViewModel : MailBaseViewModel
public AccountDetailsPageViewModel(IMailDialogService dialogService,
IAccountService accountService,
- IWinoServerConnectionManager serverConnectionManager,
IFolderService folderService)
{
_dialogService = dialogService;
_accountService = accountService;
- _serverConnectionManager = serverConnectionManager;
_folderService = folderService;
}
@@ -95,21 +92,16 @@ public partial class AccountDetailsPageViewModel : MailBaseViewModel
return;
- var isSynchronizerKilledResponse = await _serverConnectionManager.GetResponseAsync(new KillAccountSynchronizerRequested(Account.Id));
+ await SynchronizationManager.Instance.DestroySynchronizerAsync(Account.Id);
- if (isSynchronizerKilledResponse.IsSuccess)
- {
- await _accountService.DeleteAccountAsync(Account);
+ await _accountService.DeleteAccountAsync(Account);
- _dialogService.InfoBarMessage(Translator.Info_AccountDeletedTitle, string.Format(Translator.Info_AccountDeletedMessage, Account.Name), InfoBarMessageType.Success);
+ _dialogService.InfoBarMessage(Translator.Info_AccountDeletedTitle, string.Format(Translator.Info_AccountDeletedMessage, Account.Name), InfoBarMessageType.Success);
- Messenger.Send(new BackBreadcrumNavigationRequested());
- }
+ Messenger.Send(new BackBreadcrumNavigationRequested());
}
-
-
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
{
base.OnNavigatedTo(mode, parameters);
diff --git a/Wino.Mail.ViewModels/AccountManagementViewModel.cs b/Wino.Mail.ViewModels/AccountManagementViewModel.cs
index a225d76d..f15ff5ef 100644
--- a/Wino.Mail.ViewModels/AccountManagementViewModel.cs
+++ b/Wino.Mail.ViewModels/AccountManagementViewModel.cs
@@ -12,15 +12,13 @@ using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Exceptions;
using Wino.Core.Domain.Interfaces;
-using Wino.Core.Domain.Models.Authentication;
-using Wino.Core.Domain.Models.Connectivity;
using Wino.Core.Domain.Models.Navigation;
using Wino.Core.Domain.Models.Synchronization;
+using Wino.Core.Services;
using Wino.Core.ViewModels;
using Wino.Core.ViewModels.Data;
using Wino.Mail.ViewModels.Data;
using Wino.Messaging.Client.Navigation;
-using Wino.Messaging.Server;
using Wino.Messaging.UI;
namespace Wino.Mail.ViewModels;
@@ -34,7 +32,6 @@ public partial class AccountManagementViewModel : AccountManagementPageViewModel
public IMailDialogService MailDialogService { get; }
public AccountManagementViewModel(IMailDialogService dialogService,
- IWinoServerConnectionManager winoServerConnectionManager,
INavigationService navigationService,
IAccountService accountService,
ISpecialImapProviderConfigResolver specialImapProviderConfigResolver,
@@ -43,7 +40,7 @@ public partial class AccountManagementViewModel : AccountManagementPageViewModel
IStoreManagementService storeManagementService,
IWinoLogger winoLogger,
IAuthenticationProvider authenticationProvider,
- IPreferencesService preferencesService) : base(dialogService, winoServerConnectionManager, navigationService, accountService, providerService, storeManagementService, authenticationProvider, preferencesService)
+ IPreferencesService preferencesService) : base(dialogService, navigationService, accountService, providerService, storeManagementService, authenticationProvider, preferencesService)
{
MailDialogService = dialogService;
_specialImapProviderConfigResolver = specialImapProviderConfigResolver;
@@ -154,37 +151,36 @@ public partial class AccountManagementViewModel : AccountManagementPageViewModel
createdAccount.Address = customServerInformation.Address;
// Let server validate the imap/smtp connection.
- var testResultResponse = await WinoServerConnectionManager.GetResponseAsync(new ImapConnectivityTestRequested(customServerInformation, true));
+ // TODO: Protocol log with detailed failure.
- if (!testResultResponse.IsSuccess)
- {
- throw new Exception($"{Translator.IMAPSetupDialog_ConnectionFailedTitle}\n{testResultResponse.Message}");
- }
- else if (!testResultResponse.Data.IsSuccess)
- {
- // Server connectivity might succeed, but result might be failed.
- throw new ImapClientPoolException(testResultResponse.Data.FailedReason, customServerInformation, testResultResponse.Data.FailureProtocolLog);
- }
+ await _imapTestService.TestImapConnectionAsync(customServerInformation, true);
+ //var testResultResponse = await WinoServerConnectionManager.GetResponseAsync(new ImapConnectivityTestRequested(customServerInformation, true));
+
+ //if (!testResultResponse.IsSuccess)
+ //{
+ // throw new Exception($"{Translator.IMAPSetupDialog_ConnectionFailedTitle}\n{testResultResponse.Message}");
+ //}
+ //else if (!testResultResponse.Data.IsSuccess)
+ //{
+ // // Server connectivity might succeed, but result might be failed.
+ // throw new ImapClientPoolException(testResultResponse.Data.FailedReason, customServerInformation, testResultResponse.Data.FailureProtocolLog);
+ //}
}
else
{
// OAuth authentication is handled here.
- // Server authenticates, returns the token info here.
+ // Use SynchronizationManager to handle OAuth authentication.
- var tokenInformationResponse = await WinoServerConnectionManager
- .GetResponseAsync(new AuthorizationRequested(accountCreationDialogResult.ProviderType,
- createdAccount,
- createdAccount.ProviderType == MailProviderType.Gmail), accountCreationCancellationTokenSource.Token);
+ var authTokenInfo = await SynchronizationManager.Instance.HandleAuthorizationAsync(
+ accountCreationDialogResult.ProviderType,
+ createdAccount,
+ createdAccount.ProviderType == MailProviderType.Gmail);
if (creationDialog.State == AccountCreationDialogState.Canceled)
throw new AccountSetupCanceledException();
- if (!tokenInformationResponse.IsSuccess)
- throw new Exception(tokenInformationResponse.Message);
-
- createdAccount.Address = tokenInformationResponse.Data.AccountAddress;
-
- tokenInformationResponse.ThrowIfFailed();
+ // Update account address with authenticated user information
+ createdAccount.Address = authTokenInfo.AccountAddress;
}
}
@@ -207,22 +203,23 @@ public partial class AccountManagementViewModel : AccountManagementPageViewModel
Type = MailSynchronizationType.UpdateProfile
};
- var profileSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync(new NewMailSynchronizationRequested(profileSyncOptions, SynchronizationSource.Client));
-
- var profileSynchronizationResult = profileSynchronizationResponse.Data;
+ var profileSynchronizationResult = await SynchronizationManager.Instance.SynchronizeProfileAsync(createdAccount.Id);
if (profileSynchronizationResult.CompletedState != SynchronizationCompletedState.Success)
throw new Exception(Translator.Exception_FailedToSynchronizeProfileInformation);
- createdAccount.SenderName = profileSynchronizationResult.ProfileInformation.SenderName;
- createdAccount.Base64ProfilePictureData = profileSynchronizationResult.ProfileInformation.Base64ProfilePictureData;
-
- if (!string.IsNullOrEmpty(profileSynchronizationResult.ProfileInformation.AccountAddress))
+ if (profileSynchronizationResult.ProfileInformation != null)
{
- createdAccount.Address = profileSynchronizationResult.ProfileInformation.AccountAddress;
- }
+ createdAccount.SenderName = profileSynchronizationResult.ProfileInformation.SenderName;
+ createdAccount.Base64ProfilePictureData = profileSynchronizationResult.ProfileInformation.Base64ProfilePictureData;
- await AccountService.UpdateProfileInformationAsync(createdAccount.Id, profileSynchronizationResult.ProfileInformation);
+ if (!string.IsNullOrEmpty(profileSynchronizationResult.ProfileInformation.AccountAddress))
+ {
+ createdAccount.Address = profileSynchronizationResult.ProfileInformation.AccountAddress;
+ }
+
+ await AccountService.UpdateProfileInformationAsync(createdAccount.Id, profileSynchronizationResult.ProfileInformation);
+ }
}
if (creationDialog is IImapAccountCreationDialog customServerAccountCreationDialog)
@@ -237,26 +234,16 @@ public partial class AccountManagementViewModel : AccountManagementPageViewModel
Type = MailSynchronizationType.FoldersOnly
};
- var folderSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync(new NewMailSynchronizationRequested(folderSyncOptions, SynchronizationSource.Client));
-
- var folderSynchronizationResult = folderSynchronizationResponse.Data;
+ var folderSynchronizationResult = await SynchronizationManager.Instance.SynchronizeFoldersAsync(createdAccount.Id);
if (folderSynchronizationResult == null || folderSynchronizationResult.CompletedState != SynchronizationCompletedState.Success)
- throw new Exception($"{Translator.Exception_FailedToSynchronizeFolders}\n{folderSynchronizationResponse.Message}");
+ throw new Exception(Translator.Exception_FailedToSynchronizeFolders);
// Sync aliases if supported.
if (createdAccount.IsAliasSyncSupported)
{
// Try to synchronize aliases for the account.
-
- var aliasSyncOptions = new MailSynchronizationOptions()
- {
- AccountId = createdAccount.Id,
- Type = MailSynchronizationType.Alias
- };
-
- var aliasSyncResponse = await WinoServerConnectionManager.GetResponseAsync(new NewMailSynchronizationRequested(aliasSyncOptions, SynchronizationSource.Client));
- var aliasSynchronizationResult = folderSynchronizationResponse.Data;
+ var aliasSynchronizationResult = await SynchronizationManager.Instance.SynchronizeAliasesAsync(createdAccount.Id);
if (aliasSynchronizationResult.CompletedState != SynchronizationCompletedState.Success)
throw new Exception(Translator.Exception_FailedToSynchronizeAliases);
diff --git a/Wino.Mail.ViewModels/AliasManagementPageViewModel.cs b/Wino.Mail.ViewModels/AliasManagementPageViewModel.cs
index 71b1f1bc..00453f05 100644
--- a/Wino.Mail.ViewModels/AliasManagementPageViewModel.cs
+++ b/Wino.Mail.ViewModels/AliasManagementPageViewModel.cs
@@ -12,7 +12,7 @@ using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Navigation;
using Wino.Core.Domain.Models.Synchronization;
-using Wino.Messaging.Server;
+using Wino.Core.Services;
namespace Wino.Mail.ViewModels;
@@ -20,7 +20,6 @@ public partial class AliasManagementPageViewModel : MailBaseViewModel
{
private readonly IMailDialogService _dialogService;
private readonly IAccountService _accountService;
- private readonly IWinoServerConnectionManager _winoServerConnectionManager;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanSynchronizeAliases))]
@@ -32,12 +31,10 @@ public partial class AliasManagementPageViewModel : MailBaseViewModel
public bool CanSynchronizeAliases => Account?.IsAliasSyncSupported ?? false;
public AliasManagementPageViewModel(IMailDialogService dialogService,
- IAccountService accountService,
- IWinoServerConnectionManager winoServerConnectionManager)
+ IAccountService accountService)
{
_dialogService = dialogService;
_accountService = accountService;
- _winoServerConnectionManager = winoServerConnectionManager;
}
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
@@ -82,12 +79,12 @@ public partial class AliasManagementPageViewModel : MailBaseViewModel
Type = MailSynchronizationType.Alias
};
- var aliasSyncResponse = await _winoServerConnectionManager.GetResponseAsync(new NewMailSynchronizationRequested(aliasSyncOptions, SynchronizationSource.Client));
+ var aliasSyncResult = await SynchronizationManager.Instance.SynchronizeAliasesAsync(Account.Id);
- if (aliasSyncResponse.IsSuccess)
+ if (aliasSyncResult.CompletedState == SynchronizationCompletedState.Success)
await LoadAliasesAsync();
else
- _dialogService.InfoBarMessage(Translator.GeneralTitle_Error, aliasSyncResponse.Message, InfoBarMessageType.Error);
+ _dialogService.InfoBarMessage(Translator.GeneralTitle_Error, "Failed to synchronize aliases", InfoBarMessageType.Error);
}
[RelayCommand]
diff --git a/Wino.Mail.ViewModels/ComposePageViewModel.cs b/Wino.Mail.ViewModels/ComposePageViewModel.cs
index 979fcf0a..9363d087 100644
--- a/Wino.Mail.ViewModels/ComposePageViewModel.cs
+++ b/Wino.Mail.ViewModels/ComposePageViewModel.cs
@@ -13,11 +13,11 @@ using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Exceptions;
+using Wino.Core.Services;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Navigation;
using Wino.Core.Extensions;
-using Wino.Core.Services;
using Wino.Mail.ViewModels.Data;
using Wino.Messaging.Client.Mails;
using Wino.Messaging.Server;
@@ -102,7 +102,6 @@ public partial class ComposePageViewModel : MailBaseViewModel
private readonly IWinoRequestDelegator _worker;
public readonly IFontService FontService;
public readonly IPreferencesService PreferencesService;
- private readonly IWinoServerConnectionManager _winoServerConnectionManager;
public readonly IContactService ContactService;
public ComposePageViewModel(IMailDialogService dialogService,
@@ -115,8 +114,7 @@ public partial class ComposePageViewModel : MailBaseViewModel
IWinoRequestDelegator worker,
IContactService contactService,
IFontService fontService,
- IPreferencesService preferencesService,
- IWinoServerConnectionManager winoServerConnectionManager)
+ IPreferencesService preferencesService)
{
NativeAppService = nativeAppService;
ContactService = contactService;
@@ -130,7 +128,6 @@ public partial class ComposePageViewModel : MailBaseViewModel
_fileService = fileService;
_accountService = accountService;
_worker = worker;
- _winoServerConnectionManager = winoServerConnectionManager;
}
[RelayCommand]
@@ -412,13 +409,12 @@ public partial class ComposePageViewModel : MailBaseViewModel
{
downloadIfNeeded = false;
- var package = new DownloadMissingMessageRequested(CurrentMailDraftItem.AssignedAccount.Id, CurrentMailDraftItem.MailCopy);
- var downloadResponse = await _winoServerConnectionManager.GetResponseAsync(package);
+ // Download missing MIME message using SynchronizationManager
+ await SynchronizationManager.Instance.DownloadMimeMessageAsync(
+ CurrentMailDraftItem.MailCopy,
+ CurrentMailDraftItem.AssignedAccount.Id);
- if (downloadResponse.IsSuccess)
- {
- goto retry;
- }
+ goto retry;
}
else
_dialogService.InfoBarMessage(Translator.Info_ComposerMissingMIMETitle, Translator.Info_ComposerMissingMIMEMessage, InfoBarMessageType.Error);
diff --git a/Wino.Mail.ViewModels/MailListPageViewModel.cs b/Wino.Mail.ViewModels/MailListPageViewModel.cs
index 78e0c54b..58c573a7 100644
--- a/Wino.Mail.ViewModels/MailListPageViewModel.cs
+++ b/Wino.Mail.ViewModels/MailListPageViewModel.cs
@@ -23,6 +23,7 @@ using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Menus;
using Wino.Core.Domain.Models.Reader;
using Wino.Core.Domain.Models.Synchronization;
+using Wino.Core.Services;
using Wino.Mail.ViewModels.Collections;
using Wino.Mail.ViewModels.Data;
using Wino.Mail.ViewModels.Messages;
@@ -81,7 +82,6 @@ public partial class MailListPageViewModel : MailBaseViewModel,
private readonly IWinoRequestDelegator _winoRequestDelegator;
private readonly IKeyPressService _keyPressService;
private readonly IWinoLogger _winoLogger;
- private readonly IWinoServerConnectionManager _winoServerConnectionManager;
private MailItemViewModel _activeMailItem;
public List SortingOptions { get; } =
@@ -160,14 +160,12 @@ public partial class MailListPageViewModel : MailBaseViewModel,
IKeyPressService keyPressService,
IPreferencesService preferencesService,
INewThemeService themeService,
- IWinoLogger winoLogger,
- IWinoServerConnectionManager winoServerConnectionManager)
+ IWinoLogger winoLogger)
{
MailCollection = new WinoMailCollection(threadingStrategyProvider);
PreferencesService = preferencesService;
ThemeService = themeService;
_winoLogger = winoLogger;
- _winoServerConnectionManager = winoServerConnectionManager;
StatePersistenceService = statePersistenceService;
NavigationService = navigationService;
_accountService = accountService;
@@ -841,51 +839,52 @@ public partial class MailListPageViewModel : MailBaseViewModel,
// Perform online search.
if (isDoingOnlineSearch)
{
- WinoServerResponse onlineSearchResult = null;
- string onlineSearchFailedMessage = null;
+ // TODO: Burak: Handle online search.
+ //WinoServerResponse onlineSearchResult = null;
+ //string onlineSearchFailedMessage = null;
- try
- {
- var accountIds = ActiveFolder.HandlingFolders.Select(a => a.MailAccountId).ToList();
- var folders = ActiveFolder.HandlingFolders.ToList();
- var searchRequest = new OnlineSearchRequested(accountIds, SearchQuery, folders);
+ //try
+ //{
+ // var accountIds = ActiveFolder.HandlingFolders.Select(a => a.MailAccountId).ToList();
+ // var folders = ActiveFolder.HandlingFolders.ToList();
+ // var searchRequest = new OnlineSearchRequested(accountIds, SearchQuery, folders);
- onlineSearchResult = await _winoServerConnectionManager.GetResponseAsync(searchRequest, cancellationToken);
+ // onlineSearchResult = await _winoServerConnectionManager.GetResponseAsync(searchRequest, cancellationToken);
- if (onlineSearchResult.IsSuccess)
- {
- await ExecuteUIThread(() => { AreSearchResultsOnline = true; });
+ // if (onlineSearchResult.IsSuccess)
+ // {
+ // await ExecuteUIThread(() => { AreSearchResultsOnline = true; });
- onlineSearchItems = onlineSearchResult.Data.SearchResult;
- }
- else
- {
- onlineSearchFailedMessage = onlineSearchResult.Message;
- }
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- Log.Warning(ex, "Failed to perform online search.");
- onlineSearchFailedMessage = ex.Message;
- }
+ // onlineSearchItems = onlineSearchResult.Data.SearchResult;
+ // }
+ // else
+ // {
+ // onlineSearchFailedMessage = onlineSearchResult.Message;
+ // }
+ //}
+ //catch (OperationCanceledException)
+ //{
+ // throw;
+ //}
+ //catch (Exception ex)
+ //{
+ // Log.Warning(ex, "Failed to perform online search.");
+ // onlineSearchFailedMessage = ex.Message;
+ //}
- if (onlineSearchResult != null && !onlineSearchResult.IsSuccess)
- {
- // Query or server error.
- var serverErrorMessage = string.Format(Translator.OnlineSearchFailed_Message, onlineSearchResult.Message);
- _mailDialogService.InfoBarMessage(Translator.GeneralTitle_Error, serverErrorMessage, InfoBarMessageType.Warning);
+ //if (onlineSearchResult != null && !onlineSearchResult.IsSuccess)
+ //{
+ // // Query or server error.
+ // var serverErrorMessage = string.Format(Translator.OnlineSearchFailed_Message, onlineSearchResult.Message);
+ // _mailDialogService.InfoBarMessage(Translator.GeneralTitle_Error, serverErrorMessage, InfoBarMessageType.Warning);
- }
- else if (!string.IsNullOrEmpty(onlineSearchFailedMessage))
- {
- // Fatal error.
- var serverErrorMessage = string.Format(Translator.OnlineSearchFailed_Message, onlineSearchFailedMessage);
- _mailDialogService.InfoBarMessage(Translator.GeneralTitle_Error, serverErrorMessage, InfoBarMessageType.Warning);
- }
+ //}
+ //else if (!string.IsNullOrEmpty(onlineSearchFailedMessage))
+ //{
+ // // Fatal error.
+ // var serverErrorMessage = string.Format(Translator.OnlineSearchFailed_Message, onlineSearchFailedMessage);
+ // _mailDialogService.InfoBarMessage(Translator.GeneralTitle_Error, serverErrorMessage, InfoBarMessageType.Warning);
+ //}
}
}
@@ -1110,9 +1109,7 @@ public partial class MailListPageViewModel : MailBaseViewModel,
foreach (var accountId in accountIds)
{
- var serverResponse = await _winoServerConnectionManager.GetResponseAsync(new SynchronizationExistenceCheckRequest(accountId));
-
- if (serverResponse.IsSuccess && serverResponse.Data == true)
+ if (SynchronizationManager.Instance.IsAccountSynchronizing(accountId))
{
isAnyAccountSynchronizing = true;
break;
diff --git a/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs b/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs
index 3a2818c6..6be1fe8b 100644
--- a/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs
+++ b/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs
@@ -21,6 +21,7 @@ using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Menus;
using Wino.Core.Domain.Models.Navigation;
using Wino.Core.Domain.Models.Reader;
+using Wino.Core.Services;
using Wino.Mail.ViewModels.Data;
using Wino.Mail.ViewModels.Messages;
using Wino.Messaging.Client.Mails;
@@ -46,7 +47,6 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
private readonly IClipboardService _clipboardService;
private readonly IUnsubscriptionService _unsubscriptionService;
private readonly IApplicationConfiguration _applicationConfiguration;
- private readonly IWinoServerConnectionManager _winoServerConnectionManager;
private bool forceImageLoading = false;
private MailItemViewModel initializedMailItemViewModel = null;
@@ -142,8 +142,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
IUnsubscriptionService unsubscriptionService,
IPreferencesService preferencesService,
IPrintService printService,
- IApplicationConfiguration applicationConfiguration,
- IWinoServerConnectionManager winoServerConnectionManager)
+ IApplicationConfiguration applicationConfiguration)
{
_dialogService = dialogService;
NativeAppService = nativeAppService;
@@ -152,7 +151,6 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
PreferencesService = preferencesService;
PrintService = printService;
_applicationConfiguration = applicationConfiguration;
- _winoServerConnectionManager = winoServerConnectionManager;
_clipboardService = clipboardService;
_unsubscriptionService = unsubscriptionService;
_underlyingThemeService = underlyingThemeService;
@@ -355,8 +353,10 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
// To show the progress on the UI.
CurrentDownloadPercentage = 1;
- var package = new DownloadMissingMessageRequested(mailItemViewModel.AssignedAccount.Id, mailItemViewModel.MailCopy);
- await _winoServerConnectionManager.GetResponseAsync(package);
+ // Download missing MIME message using SynchronizationManager
+ await SynchronizationManager.Instance.DownloadMimeMessageAsync(
+ mailItemViewModel.MailCopy,
+ mailItemViewModel.AssignedAccount.Id);
}
catch (OperationCanceledException)
{
diff --git a/Wino.Mail.WinUI/Views/ImapSetup/TestingImapConnectionPage.xaml.cs b/Wino.Mail.WinUI/Views/ImapSetup/TestingImapConnectionPage.xaml.cs
index 2bd04975..4d99cbdf 100644
--- a/Wino.Mail.WinUI/Views/ImapSetup/TestingImapConnectionPage.xaml.cs
+++ b/Wino.Mail.WinUI/Views/ImapSetup/TestingImapConnectionPage.xaml.cs
@@ -1,25 +1,20 @@
using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
-using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Exceptions;
-using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.AutoDiscovery;
-using Wino.Core.Domain.Models.Connectivity;
-using Wino.Mail.WinUI;
+using Wino.Core.Services;
using Wino.Messaging.Client.Mails;
-using Wino.Messaging.Server;
namespace Wino.Views.ImapSetup;
public sealed partial class TestingImapConnectionPage : Page
{
- private IWinoServerConnectionManager _winoServerConnectionManager = App.Current.Services.GetService();
private AutoDiscoverySettings autoDiscoverySettings;
private CustomServerInformation serverInformationToTest;
@@ -67,46 +62,34 @@ public sealed partial class TestingImapConnectionPage : Page
await Task.Delay(1000);
- var testResultResponse = await _winoServerConnectionManager
- .GetResponseAsync(new ImapConnectivityTestRequested(serverInformationToTest, allowSSLHandshake));
+ var testResultData = await SynchronizationManager.Instance.TestImapConnectivityAsync(serverInformationToTest, allowSSLHandshake);
- if (!testResultResponse.IsSuccess)
+ if (testResultData.IsSuccess)
{
- // Wino Server is connection is failed.
- ReturnWithError(testResultResponse.Message);
+ // All success. Finish setup with validated server information.
+ ReturnWithSuccess();
}
else
{
- var testResultData = testResultResponse.Data;
-
- if (testResultData.IsSuccess)
+ // Check if certificate UI is required.
+ if (testResultData.IsCertificateUIRequired)
{
- // All success. Finish setup with validated server information.
- ReturnWithSuccess();
+ // Certificate UI is required. Show certificate dialog.
+
+ CertIssuer.Text = testResultData.CertificateIssuer;
+ CertValidFrom.Text = testResultData.CertificateValidFromDateString;
+ CertValidTo.Text = testResultData.CertificateExpirationDateString;
+
+ TestingConnectionPanel.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
+ CertificateDialog.Visibility = Microsoft.UI.Xaml.Visibility.Visible;
}
else
{
- // Check if certificate UI is required.
+ // Connection test failed. Show error dialog.
- if (testResultData.IsCertificateUIRequired)
- {
- // Certificate UI is required. Show certificate dialog.
+ var protocolLog = testResultData.FailureProtocolLog;
- CertIssuer.Text = testResultData.CertificateIssuer;
- CertValidFrom.Text = testResultData.CertificateValidFromDateString;
- CertValidTo.Text = testResultData.CertificateExpirationDateString;
-
- TestingConnectionPanel.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
- CertificateDialog.Visibility = Microsoft.UI.Xaml.Visibility.Visible;
- }
- else
- {
- // Connection test failed. Show error dialog.
-
- var protocolLog = testResultData.FailureProtocolLog;
-
- ReturnWithError(testResultData.FailedReason, protocolLog);
- }
+ ReturnWithError(testResultData.FailedReason, protocolLog);
}
}
}