diff --git a/Wino.Authentication/Wino.Authentication.csproj b/Wino.Authentication/Wino.Authentication.csproj
index a44ce5dc..01750ff7 100644
--- a/Wino.Authentication/Wino.Authentication.csproj
+++ b/Wino.Authentication/Wino.Authentication.csproj
@@ -11,12 +11,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/Wino.BackgroundTasks/Wino.BackgroundTasks.csproj b/Wino.BackgroundTasks/Wino.BackgroundTasks.csproj
index 67f9296c..07fb3d5e 100644
--- a/Wino.BackgroundTasks/Wino.BackgroundTasks.csproj
+++ b/Wino.BackgroundTasks/Wino.BackgroundTasks.csproj
@@ -87,7 +87,7 @@
- 4.66.2
+ 4.67.2
6.2.14
diff --git a/Wino.Calendar.Packaging/Wino.Calendar.Packaging.wapproj b/Wino.Calendar.Packaging/Wino.Calendar.Packaging.wapproj
index 47f8014e..7af3201e 100644
--- a/Wino.Calendar.Packaging/Wino.Calendar.Packaging.wapproj
+++ b/Wino.Calendar.Packaging/Wino.Calendar.Packaging.wapproj
@@ -76,7 +76,7 @@
-
+
diff --git a/Wino.Calendar.ViewModels/Wino.Calendar.ViewModels.csproj b/Wino.Calendar.ViewModels/Wino.Calendar.ViewModels.csproj
index 4b029e10..002a1e9b 100644
--- a/Wino.Calendar.ViewModels/Wino.Calendar.ViewModels.csproj
+++ b/Wino.Calendar.ViewModels/Wino.Calendar.ViewModels.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/Wino.Calendar/Wino.Calendar.csproj b/Wino.Calendar/Wino.Calendar.csproj
index 22555e01..7bd17912 100644
--- a/Wino.Calendar/Wino.Calendar.csproj
+++ b/Wino.Calendar/Wino.Calendar.csproj
@@ -328,13 +328,13 @@
8.1.240916
- 4.66.2
+ 4.67.2
6.2.14
- 1.28.0
+ 1.28.1
diff --git a/Wino.Core.Domain/Interfaces/IAccountCreationDialog.cs b/Wino.Core.Domain/Interfaces/IAccountCreationDialog.cs
index e534a042..8454a4ed 100644
--- a/Wino.Core.Domain/Interfaces/IAccountCreationDialog.cs
+++ b/Wino.Core.Domain/Interfaces/IAccountCreationDialog.cs
@@ -1,11 +1,12 @@
using System.Threading;
+using System.Threading.Tasks;
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Interfaces
{
public interface IAccountCreationDialog
{
- void ShowDialog(CancellationTokenSource cancellationTokenSource);
+ Task ShowDialogAsync(CancellationTokenSource cancellationTokenSource);
void Complete(bool cancel);
AccountCreationDialogState State { get; set; }
}
diff --git a/Wino.Core.Domain/Wino.Core.Domain.csproj b/Wino.Core.Domain/Wino.Core.Domain.csproj
index bf3c6728..da204424 100644
--- a/Wino.Core.Domain/Wino.Core.Domain.csproj
+++ b/Wino.Core.Domain/Wino.Core.Domain.csproj
@@ -60,8 +60,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -69,7 +69,7 @@
-
+
diff --git a/Wino.Core.UWP/Dialogs/AccountCreationDialog.xaml.cs b/Wino.Core.UWP/Dialogs/AccountCreationDialog.xaml.cs
index 3abb5897..c5026c8e 100644
--- a/Wino.Core.UWP/Dialogs/AccountCreationDialog.xaml.cs
+++ b/Wino.Core.UWP/Dialogs/AccountCreationDialog.xaml.cs
@@ -1,4 +1,5 @@
using System.Threading;
+using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
@@ -8,6 +9,7 @@ namespace Wino.Dialogs
{
public sealed partial class AccountCreationDialog : ContentDialog, IAccountCreationDialog
{
+ private TaskCompletionSource dialogOpened = new TaskCompletionSource();
public CancellationTokenSource CancellationTokenSource { get; private set; }
public AccountCreationDialogState State
@@ -32,13 +34,6 @@ namespace Wino.Dialogs
}
}
- public void ShowDialog(CancellationTokenSource cancellationTokenSource)
- {
- CancellationTokenSource = cancellationTokenSource;
-
- _ = ShowAsync();
- }
-
public void Complete(bool cancel)
{
State = cancel ? AccountCreationDialogState.Canceled : AccountCreationDialogState.Completed;
@@ -53,6 +48,26 @@ namespace Wino.Dialogs
Hide();
}
+
private void CancelClicked(object sender, System.EventArgs e) => Complete(true);
+
+ public async Task ShowDialogAsync(CancellationTokenSource cancellationTokenSource)
+ {
+ var tcs =
+
+ CancellationTokenSource = cancellationTokenSource;
+
+ Opened += DialogOpened;
+ _ = ShowAsync();
+
+ await dialogOpened.Task;
+ }
+
+ private void DialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args)
+ {
+ Opened -= DialogOpened;
+
+ dialogOpened?.SetResult(true);
+ }
}
}
diff --git a/Wino.Core.UWP/Wino.Core.UWP.csproj b/Wino.Core.UWP/Wino.Core.UWP.csproj
index 0e4f1e73..7f2ab5d1 100644
--- a/Wino.Core.UWP/Wino.Core.UWP.csproj
+++ b/Wino.Core.UWP/Wino.Core.UWP.csproj
@@ -187,13 +187,13 @@
- 8.3.2
+ 8.4.0
- 8.3.2
+ 8.4.0
- 8.3.2
+ 8.4.0
8.1.240916
@@ -224,7 +224,7 @@
5.0.6
- 4.66.2
+ 4.67.2
6.2.14
@@ -236,7 +236,7 @@
2.8.6
- 1.28.0
+ 1.28.1
diff --git a/Wino.Core.ViewModels/Wino.Core.ViewModels.csproj b/Wino.Core.ViewModels/Wino.Core.ViewModels.csproj
index d53fa682..17808ec2 100644
--- a/Wino.Core.ViewModels/Wino.Core.ViewModels.csproj
+++ b/Wino.Core.ViewModels/Wino.Core.ViewModels.csproj
@@ -14,7 +14,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/Wino.Core/Synchronizers/ImapSync/CondstoreSynchronizer.cs b/Wino.Core/Synchronizers/ImapSync/CondstoreSynchronizer.cs
index b5594d6b..b0028851 100644
--- a/Wino.Core/Synchronizers/ImapSync/CondstoreSynchronizer.cs
+++ b/Wino.Core/Synchronizers/ImapSync/CondstoreSynchronizer.cs
@@ -6,14 +6,10 @@ using System.Threading.Tasks;
using MailKit;
using MailKit.Net.Imap;
using MailKit.Search;
-using MoreLinq;
-using Serilog;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Exceptions;
using Wino.Core.Domain.Interfaces;
-using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Integration;
-using Wino.Services.Extensions;
using IMailService = Wino.Core.Domain.Interfaces.IMailService;
namespace Wino.Core.Synchronizers.ImapSync
@@ -41,6 +37,7 @@ namespace Wino.Core.Synchronizers.ImapSync
IMailFolder remoteFolder = null;
var downloadedMessageIds = new List();
+
try
{
remoteFolder = await winoClient.GetFolderAsync(folder.RemoteFolderId, cancellationToken).ConfigureAwait(false);
@@ -57,92 +54,10 @@ namespace Wino.Core.Synchronizers.ImapSync
// the MODSEQ value for deleted messages.
if (remoteHighestModSeq > localHighestModSeq)
{
- // Search for emails with a MODSEQ greater than the last known value.
- // Use SORT extension if server supports.
-
- IList changedUids = null;
-
- if (winoClient.Capabilities.HasFlag(ImapCapabilities.Sort))
- {
- changedUids = await remoteFolder.SortAsync(SearchQuery.ChangedSince(localHighestModSeq), [OrderBy.ReverseDate], cancellationToken).ConfigureAwait(false);
- }
- else
- {
- changedUids = await remoteFolder.SearchAsync(SearchQuery.ChangedSince(localHighestModSeq), cancellationToken).ConfigureAwait(false);
- }
-
- // For initial synchronizations, take the first allowed number of items.
- // For consequtive synchronizations, take all the items. We don't want to miss any changes.
-
- if (isInitialSynchronization)
- changedUids = changedUids.Take((int)synchronizer.InitialMessageDownloadCountPerFolder).ToList();
+ var changedUids = await GetChangedUidsAsync(client, folder, remoteFolder, synchronizer, cancellationToken).ConfigureAwait(false);
// Get locally exists mails for the returned UIDs.
- var existingMails = await MailService.GetExistingMailsAsync(folder.Id, changedUids).ConfigureAwait(false);
- var existingMailUids = existingMails.Select(m => MailkitClientExtensions.ResolveUidStruct(m.Id)).ToArray();
-
- // These are the non-existing mails. They will be downloaded + processed.
- var newMessageIds = changedUids.Except(existingMailUids).ToList();
-
- // Fetch minimum data for the existing mails in one query.
- var existingFlagData = await remoteFolder.FetchAsync(existingMailUids, MessageSummaryItems.Flags | MessageSummaryItems.UniqueId).ConfigureAwait(false);
-
- foreach (var update in existingFlagData)
- {
- if (update.UniqueId == null)
- {
- Log.Warning($"Couldn't fetch UniqueId for the mail. FetchAsync failed.");
- continue;
- }
-
- if (update.Flags == null)
- {
- Log.Warning($"Couldn't fetch flags for the mail with UID {update.UniqueId.Id}. FetchAsync failed.");
- continue;
- }
-
- var existingMail = existingMails.FirstOrDefault(m => MailkitClientExtensions.ResolveUidStruct(m.Id).Id == update.UniqueId.Id);
-
- if (existingMail == null)
- {
- Log.Warning($"Couldn't find the mail with UID {update.UniqueId.Id} in the local database. Flag update is ignored.");
- continue;
- }
-
- await HandleMessageFlagsChangeAsync(existingMail, update.Flags.Value).ConfigureAwait(false);
- }
-
- // Fetch the new mails in batch.
-
- var batchedMessageIds = newMessageIds.Batch(50);
-
- foreach (var group in batchedMessageIds)
- {
- var summaries = await remoteFolder.FetchAsync(group, MailSynchronizationFlags, cancellationToken).ConfigureAwait(false);
-
- foreach (var summary in summaries)
- {
- var mimeMessage = await remoteFolder.GetMessageAsync(summary.UniqueId, cancellationToken).ConfigureAwait(false);
-
- var creationPackage = new ImapMessageCreationPackage(summary, mimeMessage);
-
- var mailPackages = await synchronizer.CreateNewMailPackagesAsync(creationPackage, folder, cancellationToken).ConfigureAwait(false);
-
- if (mailPackages != null)
- {
- foreach (var package in mailPackages)
- {
- // Local draft is mapped. We don't need to create a new mail copy.
- if (package == null) continue;
-
- bool isCreatedNew = await MailService.CreateMailAsync(folder.MailAccountId, package).ConfigureAwait(false);
-
- // This is upsert. We are not interested in updated mails.
- if (isCreatedNew) downloadedMessageIds.Add(package.Copy.Id);
- }
- }
- }
- }
+ downloadedMessageIds = await HandleChangedUIdsAsync(folder, synchronizer, remoteFolder, changedUids, cancellationToken).ConfigureAwait(false);
folder.HighestModeSeq = (long)remoteHighestModSeq;
@@ -177,5 +92,41 @@ namespace Wino.Core.Synchronizers.ImapSync
}
}
}
+
+ internal override async Task> GetChangedUidsAsync(IImapClient winoClient, MailItemFolder localFolder, IMailFolder remoteFolder, IImapSynchronizer synchronizer, CancellationToken cancellationToken = default)
+ {
+ var localHighestModSeq = (ulong)localFolder.HighestModeSeq;
+ var remoteHighestModSeq = remoteFolder.HighestModSeq;
+
+ // Search for emails with a MODSEQ greater than the last known value.
+ // Use SORT extension if server supports.
+
+ IList changedUids = null;
+
+ // TODO: Temporarily disabled.
+ //if (winoClient.Capabilities.HasFlag(ImapCapabilities.Sort))
+ //{
+ // changedUids = await remoteFolder.SortAsync(SearchQuery.ChangedSince(localHighestModSeq), [OrderBy.ReverseDate], cancellationToken).ConfigureAwait(false);
+ //}
+ //else
+ //{
+ // changedUids = await remoteFolder.SearchAsync(SearchQuery.ChangedSince(localHighestModSeq), cancellationToken).ConfigureAwait(false);
+ //}
+
+ changedUids = await remoteFolder.SearchAsync(SearchQuery.ChangedSince(localHighestModSeq), cancellationToken).ConfigureAwait(false);
+
+ // For initial synchronizations, take the first allowed number of items.
+ // For consequtive synchronizations, take all the items. We don't want to miss any changes.
+ // Smaller uid means newer message. For initial sync, we need start taking items from the top.
+
+ bool isInitialSynchronization = localHighestModSeq == 0;
+
+ if (isInitialSynchronization)
+ {
+ changedUids = changedUids.OrderByDescending(a => a.Id).Take((int)synchronizer.InitialMessageDownloadCountPerFolder).ToList();
+ }
+
+ return changedUids;
+ }
}
}
diff --git a/Wino.Core/Synchronizers/ImapSync/ImapSynchronizationStrategyBase.cs b/Wino.Core/Synchronizers/ImapSync/ImapSynchronizationStrategyBase.cs
index 7f721a68..8b15b975 100644
--- a/Wino.Core/Synchronizers/ImapSync/ImapSynchronizationStrategyBase.cs
+++ b/Wino.Core/Synchronizers/ImapSync/ImapSynchronizationStrategyBase.cs
@@ -5,8 +5,11 @@ using System.Threading.Tasks;
using MailKit;
using MailKit.Net.Imap;
using MailKit.Search;
+using MoreLinq;
+using Serilog;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Interfaces;
+using Wino.Core.Domain.Models.MailItem;
using Wino.Services.Extensions;
using IMailService = Wino.Core.Domain.Interfaces.IMailService;
@@ -36,6 +39,82 @@ namespace Wino.Core.Synchronizers.ImapSync
}
public abstract Task> HandleSynchronizationAsync(IImapClient client, MailItemFolder folder, IImapSynchronizer synchronizer, CancellationToken cancellationToken = default);
+ internal abstract Task> GetChangedUidsAsync(IImapClient client, MailItemFolder localFolder, IMailFolder remoteFolder, IImapSynchronizer synchronizer, CancellationToken cancellationToken = default);
+
+ protected async Task> HandleChangedUIdsAsync(MailItemFolder folder, IImapSynchronizer synchronizer, IMailFolder remoteFolder, IList changedUids, CancellationToken cancellationToken)
+ {
+ List downloadedMessageIds = new();
+
+ var existingMails = await MailService.GetExistingMailsAsync(folder.Id, changedUids).ConfigureAwait(false);
+ var existingMailUids = existingMails.Select(m => MailkitClientExtensions.ResolveUidStruct(m.Id)).ToArray();
+
+ // These are the non-existing mails. They will be downloaded + processed.
+ var newMessageIds = changedUids.Except(existingMailUids).ToList();
+ var deletedMessageIds = existingMailUids.Except(changedUids).ToList();
+
+ // Fetch minimum data for the existing mails in one query.
+ var existingFlagData = await remoteFolder.FetchAsync(existingMailUids, MessageSummaryItems.Flags | MessageSummaryItems.UniqueId).ConfigureAwait(false);
+
+ foreach (var update in existingFlagData)
+ {
+ if (update.UniqueId == null)
+ {
+ Log.Warning($"Couldn't fetch UniqueId for the mail. FetchAsync failed.");
+ continue;
+ }
+
+ if (update.Flags == null)
+ {
+ Log.Warning($"Couldn't fetch flags for the mail with UID {update.UniqueId.Id}. FetchAsync failed.");
+ continue;
+ }
+
+ var existingMail = existingMails.FirstOrDefault(m => MailkitClientExtensions.ResolveUidStruct(m.Id).Id == update.UniqueId.Id);
+
+ if (existingMail == null)
+ {
+ Log.Warning($"Couldn't find the mail with UID {update.UniqueId.Id} in the local database. Flag update is ignored.");
+ continue;
+ }
+
+ await HandleMessageFlagsChangeAsync(existingMail, update.Flags.Value).ConfigureAwait(false);
+ }
+
+ // Fetch the new mails in batch.
+
+ var batchedMessageIds = newMessageIds.Batch(50);
+
+ foreach (var group in batchedMessageIds)
+ {
+ var summaries = await remoteFolder.FetchAsync(group, MailSynchronizationFlags, cancellationToken).ConfigureAwait(false);
+
+ foreach (var summary in summaries)
+ {
+ var mimeMessage = await remoteFolder.GetMessageAsync(summary.UniqueId, cancellationToken).ConfigureAwait(false);
+
+ var creationPackage = new ImapMessageCreationPackage(summary, mimeMessage);
+
+ var mailPackages = await synchronizer.CreateNewMailPackagesAsync(creationPackage, folder, cancellationToken).ConfigureAwait(false);
+
+ if (mailPackages != null)
+ {
+ foreach (var package in mailPackages)
+ {
+ // Local draft is mapped. We don't need to create a new mail copy.
+ if (package == null) continue;
+
+ bool isCreatedNew = await MailService.CreateMailAsync(folder.MailAccountId, package).ConfigureAwait(false);
+
+ // This is upsert. We are not interested in updated mails.
+ if (isCreatedNew) downloadedMessageIds.Add(package.Copy.Id);
+ }
+ }
+ }
+ }
+
+
+ return downloadedMessageIds;
+ }
protected async Task HandleMessageFlagsChangeAsync(MailItemFolder folder, UniqueId? uniqueId, MessageFlags flags)
{
diff --git a/Wino.Core/Synchronizers/ImapSync/QResyncSynchronizer.cs b/Wino.Core/Synchronizers/ImapSync/QResyncSynchronizer.cs
index 356a9b9a..00b1daf0 100644
--- a/Wino.Core/Synchronizers/ImapSync/QResyncSynchronizer.cs
+++ b/Wino.Core/Synchronizers/ImapSync/QResyncSynchronizer.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -29,6 +28,8 @@ namespace Wino.Core.Synchronizers.ImapSync
IImapSynchronizer synchronizer,
CancellationToken cancellationToken = default)
{
+ var downloadedMessageIds = new List();
+
if (client is not WinoImapClient winoClient)
throw new ImapSynchronizerStrategyException("Client must be of type WinoImapClient.");
@@ -54,7 +55,6 @@ namespace Wino.Core.Synchronizers.ImapSync
if (!isCacheValid)
{
// TODO: Remove all local data.
-
}
// Perform QRESYNC synchronization.
@@ -70,42 +70,52 @@ namespace Wino.Core.Synchronizers.ImapSync
await remoteFolder.OpenAsync(FolderAccess.ReadOnly, folder.UidValidity, localHighestModSeq, allUniqueIds).ConfigureAwait(false);
- var changedUids = await remoteFolder.SearchAsync(SearchQuery.ChangedSince(localHighestModSeq), cancellationToken).ConfigureAwait(false);
+ var changedUids = await GetChangedUidsAsync(client, folder, remoteFolder, synchronizer, cancellationToken).ConfigureAwait(false);
- foreach (var uid in changedUids)
- {
- Debug.WriteLine($"Processing message with UID: {uid}");
-
- // var message = await remoteFolder.GetMessageAsync(uid, cancellationToken).ConfigureAwait(false);
-
- // TODO: Process the message.
- }
+ downloadedMessageIds = await HandleChangedUIdsAsync(folder, synchronizer, remoteFolder, changedUids, cancellationToken).ConfigureAwait(false);
// Update the local folder with the new highest mod-seq and validity.
folder.HighestModeSeq = (long)remoteHighestModSeq;
folder.UidValidity = remoteFolder.UidValidity;
+ await ManageUUIdBasedDeletedMessagesAsync(folder, remoteFolder, cancellationToken).ConfigureAwait(false);
+
await FolderService.UpdateFolderAsync(folder).ConfigureAwait(false);
}
- catch (Exception ex)
+ catch (FolderNotFoundException)
+ {
+ await FolderService.DeleteFolderAsync(folder.MailAccountId, folder.RemoteFolderId).ConfigureAwait(false);
+
+ return default;
+ }
+ catch (Exception)
{
throw;
}
finally
{
- if (remoteFolder != null)
+ if (!cancellationToken.IsCancellationRequested)
{
- remoteFolder.MessagesVanished -= async (c, r) => await HandleMessageDeletedAsync(folder, r.UniqueIds).ConfigureAwait(false);
- remoteFolder.MessageFlagsChanged -= async (c, r) => await HandleMessageFlagsChangeAsync(folder, r.UniqueId, r.Flags).ConfigureAwait(false);
-
- if (remoteFolder.IsOpen)
+ if (remoteFolder != null)
{
- await remoteFolder.CloseAsync();
+ remoteFolder.MessagesVanished -= async (c, r) => await HandleMessageDeletedAsync(folder, r.UniqueIds).ConfigureAwait(false);
+ remoteFolder.MessageFlagsChanged -= async (c, r) => await HandleMessageFlagsChangeAsync(folder, r.UniqueId, r.Flags).ConfigureAwait(false);
+
+ if (remoteFolder.IsOpen)
+ {
+ await remoteFolder.CloseAsync();
+ }
}
}
}
- return default;
+ return downloadedMessageIds;
+ }
+
+ internal override async Task> GetChangedUidsAsync(IImapClient client, MailItemFolder localFolder, IMailFolder remoteFolder, IImapSynchronizer synchronizer, CancellationToken cancellationToken = default)
+ {
+ var localHighestModSeq = (ulong)localFolder.HighestModeSeq;
+ return await remoteFolder.SearchAsync(SearchQuery.ChangedSince(localHighestModSeq), cancellationToken).ConfigureAwait(false);
}
}
}
diff --git a/Wino.Core/Synchronizers/ImapSync/UidBasedSynchronizer.cs b/Wino.Core/Synchronizers/ImapSync/UidBasedSynchronizer.cs
index bec3f93d..ebb33608 100644
--- a/Wino.Core/Synchronizers/ImapSync/UidBasedSynchronizer.cs
+++ b/Wino.Core/Synchronizers/ImapSync/UidBasedSynchronizer.cs
@@ -1,8 +1,11 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MailKit;
using MailKit.Net.Imap;
+using MailKit.Search;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Integration;
@@ -12,14 +15,65 @@ namespace Wino.Core.Synchronizers.ImapSync
///
/// Uid based IMAP Synchronization strategy.
///
- internal class UidBasedSynchronizer : IImapSynchronizerStrategy
+ internal class UidBasedSynchronizer : ImapSynchronizationStrategyBase
{
- public Task> HandleSynchronizationAsync(IImapClient client, MailItemFolder folder, IImapSynchronizer synchronizer, CancellationToken cancellationToken = default)
+ public UidBasedSynchronizer(IFolderService folderService, Domain.Interfaces.IMailService mailService) : base(folderService, mailService)
+ {
+ }
+
+ public override async Task> HandleSynchronizationAsync(IImapClient client, MailItemFolder folder, IImapSynchronizer synchronizer, CancellationToken cancellationToken = default)
{
if (client is not WinoImapClient winoClient)
throw new ArgumentException("Client must be of type WinoImapClient.", nameof(client));
- return Task.FromResult(new List());
+ var downloadedMessageIds = new List();
+ IMailFolder remoteFolder = null;
+
+ try
+ {
+ remoteFolder = await winoClient.GetFolderAsync(folder.RemoteFolderId, cancellationToken).ConfigureAwait(false);
+
+ await remoteFolder.OpenAsync(FolderAccess.ReadOnly, cancellationToken).ConfigureAwait(false);
+
+ // Fetch UIDs from the remote folder
+ var remoteUids = await remoteFolder.SearchAsync(SearchQuery.All, cancellationToken).ConfigureAwait(false);
+
+ remoteUids = remoteUids.OrderByDescending(a => a.Id).Take((int)synchronizer.InitialMessageDownloadCountPerFolder).ToList();
+
+ await HandleChangedUIdsAsync(folder, synchronizer, remoteFolder, remoteUids, cancellationToken).ConfigureAwait(false);
+ await ManageUUIdBasedDeletedMessagesAsync(folder, remoteFolder, cancellationToken).ConfigureAwait(false);
+ }
+ catch (FolderNotFoundException)
+ {
+ await FolderService.DeleteFolderAsync(folder.MailAccountId, folder.RemoteFolderId).ConfigureAwait(false);
+
+ return default;
+ }
+ catch (Exception)
+ {
+
+ throw;
+ }
+ finally
+ {
+ if (!cancellationToken.IsCancellationRequested)
+ {
+ if (remoteFolder != null)
+ {
+ if (remoteFolder.IsOpen)
+ {
+ await remoteFolder.CloseAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
+ }
+ }
+ }
+ }
+
+ return downloadedMessageIds;
+ }
+
+ internal override async Task> GetChangedUidsAsync(IImapClient client, MailItemFolder localFolder, IMailFolder remoteFolder, IImapSynchronizer synchronizer, CancellationToken cancellationToken = default)
+ {
+ throw new NotImplementedException();
}
}
}
diff --git a/Wino.Core/Synchronizers/ImapSynchronizer.cs b/Wino.Core/Synchronizers/ImapSynchronizer.cs
index a3aac329..a86c5e27 100644
--- a/Wino.Core/Synchronizers/ImapSynchronizer.cs
+++ b/Wino.Core/Synchronizers/ImapSynchronizer.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
@@ -31,8 +32,9 @@ namespace Wino.Core.Synchronizers.Mail
{
public class ImapSynchronizer : WinoSynchronizer, IImapSynchronizer
{
+ [Obsolete("N/A")]
public override uint BatchModificationSize => 1000;
- public override uint InitialMessageDownloadCountPerFolder => 250;
+ public override uint InitialMessageDownloadCountPerFolder => 500;
#region Idle Implementation
@@ -631,8 +633,6 @@ namespace Wino.Core.Synchronizers.Mail
{
if (!folder.IsSynchronizationEnabled) return default;
- var downloadedMessageIds = new List();
-
IImapClient availableClient = null;
retry:
@@ -666,7 +666,6 @@ namespace Wino.Core.Synchronizers.Mail
return new List();
}
-
///
/// Whether the local folder should be updated with the remote folder.
/// IMAP only compares folder name for now.
@@ -705,7 +704,6 @@ namespace Wino.Core.Synchronizers.Mail
// Setup idle client.
idleClient = client;
-
idleDoneTokenSource ??= new CancellationTokenSource();
idleCancellationTokenSource ??= new CancellationTokenSource();
@@ -716,6 +714,7 @@ namespace Wino.Core.Synchronizers.Mail
inboxFolder.CountChanged += IdleNotificationTriggered;
inboxFolder.MessageFlagsChanged += IdleNotificationTriggered;
inboxFolder.MessageExpunged += IdleNotificationTriggered;
+ inboxFolder.MessagesVanished += IdleNotificationTriggered;
await client.IdleAsync(idleDoneTokenSource.Token, idleCancellationTokenSource.Token);
}
@@ -745,6 +744,7 @@ namespace Wino.Core.Synchronizers.Mail
inboxFolder.CountChanged -= IdleNotificationTriggered;
inboxFolder.MessageFlagsChanged -= IdleNotificationTriggered;
inboxFolder.MessageExpunged -= IdleNotificationTriggered;
+ inboxFolder.MessagesVanished -= IdleNotificationTriggered;
}
if (idleDoneTokenSource != null)
@@ -776,6 +776,8 @@ namespace Wino.Core.Synchronizers.Mail
private void RequestIdleChangeSynchronization()
{
+ Debug.WriteLine("Detected idle change.");
+
// We don't really need to act on the count change in detail.
// Our synchronization should be enough to handle the changes with on-demand sync.
// We can just trigger a sync here IMAPIdle type.
diff --git a/Wino.Core/Wino.Core.csproj b/Wino.Core/Wino.Core.csproj
index e632e9fb..43f67b00 100644
--- a/Wino.Core/Wino.Core.csproj
+++ b/Wino.Core/Wino.Core.csproj
@@ -16,34 +16,34 @@
-
-
+
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
+
+
+
+
+
-
+
-
-
+
+
-
+
-
+
diff --git a/Wino.Mail.ViewModels/AccountManagementViewModel.cs b/Wino.Mail.ViewModels/AccountManagementViewModel.cs
index fe0b89f0..03e483de 100644
--- a/Wino.Mail.ViewModels/AccountManagementViewModel.cs
+++ b/Wino.Mail.ViewModels/AccountManagementViewModel.cs
@@ -112,7 +112,7 @@ namespace Wino.Mail.ViewModels
Id = Guid.NewGuid()
};
- creationDialog.ShowDialog(accountCreationCancellationTokenSource);
+ await creationDialog.ShowDialogAsync(accountCreationCancellationTokenSource);
creationDialog.State = AccountCreationDialogState.SigningIn;
string tokenInformation = string.Empty;
diff --git a/Wino.Mail.ViewModels/Wino.Mail.ViewModels.csproj b/Wino.Mail.ViewModels/Wino.Mail.ViewModels.csproj
index b84ad270..fb8a0477 100644
--- a/Wino.Mail.ViewModels/Wino.Mail.ViewModels.csproj
+++ b/Wino.Mail.ViewModels/Wino.Mail.ViewModels.csproj
@@ -16,7 +16,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/Wino.Mail/Dialogs/NewImapSetupDialog.xaml.cs b/Wino.Mail/Dialogs/NewImapSetupDialog.xaml.cs
index bd2d907f..c24050aa 100644
--- a/Wino.Mail/Dialogs/NewImapSetupDialog.xaml.cs
+++ b/Wino.Mail/Dialogs/NewImapSetupDialog.xaml.cs
@@ -78,8 +78,17 @@ namespace Wino.Dialogs
public void Receive(ImapSetupDismissRequested message) => _getServerInfoTaskCompletionSource.TrySetResult(message.CompletedServerInformation);
- public void ShowDialog(CancellationTokenSource cancellationTokenSource)
- => _ = ShowAsync();
+ public async Task ShowDialogAsync(CancellationTokenSource cancellationTokenSource)
+ {
+ var tcs = new TaskCompletionSource();
+
+ _ = ShowAsync().AsTask().ContinueWith((t) =>
+ {
+ tcs.TrySetResult(true);
+ });
+
+ await tcs.Task;
+ }
public void ShowPreparingFolders()
{
diff --git a/Wino.Mail/Wino.Mail.csproj b/Wino.Mail/Wino.Mail.csproj
index 8e8f1b17..51ed2236 100644
--- a/Wino.Mail/Wino.Mail.csproj
+++ b/Wino.Mail/Wino.Mail.csproj
@@ -128,16 +128,16 @@
1.0.0
- 8.3.2
+ 8.4.0
- 8.3.2
+ 8.4.0
0.1.240917-build.1755
- 8.3.2
+ 8.4.0
8.1.240916
@@ -173,10 +173,10 @@
5.0.6
- 8.0.1
+ 9.0.1
- 4.66.2
+ 4.67.2
2.2.12-rel-33220-00
@@ -196,7 +196,7 @@
5.1.2
- 4.1.0
+ 4.2.0
8.4.0
@@ -205,7 +205,7 @@
1.9.172
- 1.28.0
+ 1.28.1
diff --git a/Wino.Messages/Wino.Messaging.csproj b/Wino.Messages/Wino.Messaging.csproj
index c012b615..54f1eda7 100644
--- a/Wino.Messages/Wino.Messaging.csproj
+++ b/Wino.Messages/Wino.Messaging.csproj
@@ -18,7 +18,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/Wino.Packaging/Wino.Packaging.wapproj b/Wino.Packaging/Wino.Packaging.wapproj
index c66a9cdc..42cb6d6d 100644
--- a/Wino.Packaging/Wino.Packaging.wapproj
+++ b/Wino.Packaging/Wino.Packaging.wapproj
@@ -133,6 +133,6 @@
-
+
\ No newline at end of file
diff --git a/Wino.Server/Wino.Server.csproj b/Wino.Server/Wino.Server.csproj
index b886e60f..f7e7c1cd 100644
--- a/Wino.Server/Wino.Server.csproj
+++ b/Wino.Server/Wino.Server.csproj
@@ -33,11 +33,11 @@
-
-
+
+
-
-
+
+
diff --git a/Wino.Services/Wino.Services.csproj b/Wino.Services/Wino.Services.csproj
index 718d9107..9179bdac 100644
--- a/Wino.Services/Wino.Services.csproj
+++ b/Wino.Services/Wino.Services.csproj
@@ -7,10 +7,10 @@
-
+
-
-
+
+
diff --git a/Wino.SourceGenerators/Wino.SourceGenerators.csproj b/Wino.SourceGenerators/Wino.SourceGenerators.csproj
index dba6e9d2..36506043 100644
--- a/Wino.SourceGenerators/Wino.SourceGenerators.csproj
+++ b/Wino.SourceGenerators/Wino.SourceGenerators.csproj
@@ -18,9 +18,9 @@
-
+
-
+