Bumping some nugets. More on the imap synchronizers.

This commit is contained in:
Burak Kaan Köse
2025-01-28 22:56:19 +01:00
parent b7b51ac4e6
commit b73eb3efcb
25 changed files with 300 additions and 179 deletions

View File

@@ -11,12 +11,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.3.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
<PackageReference Include="Google.Apis.Auth" Version="1.68.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.66.2" />
<PackageReference Include="Microsoft.Identity.Client.Broker" Version="4.66.2" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.66.2" />
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.4.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Google.Apis.Auth" Version="1.69.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
<PackageReference Include="Microsoft.Identity.Client.Broker" Version="4.67.2" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.67.2" />
</ItemGroup>
<ItemGroup>

View File

@@ -87,7 +87,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Identity.Client">
<Version>4.66.2</Version>
<Version>4.67.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.14</Version>

View File

@@ -76,7 +76,7 @@
</ItemGroup>
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.targets" />
<ItemGroup>
<PackageReference Include="Microsoft.Identity.Client" Version="4.66.2" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>

View File

@@ -9,7 +9,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Identity.Client" Version="4.66.2" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
<PackageReference Include="TimePeriodLibrary.NET" Version="2.1.5" />
</ItemGroup>

View File

@@ -328,13 +328,13 @@
<Version>8.1.240916</Version>
</PackageReference>
<PackageReference Include="Microsoft.Identity.Client">
<Version>4.66.2</Version>
<Version>4.67.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.14</Version>
</PackageReference>
<PackageReference Include="Win2D.uwp">
<Version>1.28.0</Version>
<Version>1.28.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>

View File

@@ -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; }
}

View File

@@ -60,8 +60,8 @@
<EmbeddedResource Include="Translations\zh_CN\resources.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.3.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.4.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="IsExternalInit" Version="1.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -69,7 +69,7 @@
<PackageReference Include="MimeKit" Version="4.9.0" />
<PackageReference Include="MailKit" Version="4.9.0" />
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
<PackageReference Include="System.Text.Json" Version="9.0.1" />
<PackageReference Include="TimePeriodLibrary.NET" Version="2.1.5" />
</ItemGroup>
<ItemGroup>

View File

@@ -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<bool> dialogOpened = new TaskCompletionSource<bool>();
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);
}
}
}

View File

@@ -187,13 +187,13 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Common">
<Version>8.3.2</Version>
<Version>8.4.0</Version>
</PackageReference>
<PackageReference Include="CommunityToolkit.Diagnostics">
<Version>8.3.2</Version>
<Version>8.4.0</Version>
</PackageReference>
<PackageReference Include="CommunityToolkit.Mvvm">
<Version>8.3.2</Version>
<Version>8.4.0</Version>
</PackageReference>
<PackageReference Include="CommunityToolkit.Uwp.Animations">
<Version>8.1.240916</Version>
@@ -224,7 +224,7 @@
<Version>5.0.6</Version>
</PackageReference>
<PackageReference Include="Microsoft.Identity.Client">
<Version>4.66.2</Version>
<Version>4.67.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.14</Version>
@@ -236,7 +236,7 @@
<Version>2.8.6</Version>
</PackageReference>
<PackageReference Include="Win2D.uwp">
<Version>1.28.0</Version>
<Version>1.28.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>

View File

@@ -14,7 +14,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="5.0.6" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.66.2" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
<PackageReference Include="System.Reactive" Version="6.0.1" />
</ItemGroup>

View File

@@ -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<string>();
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<UniqueId> 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<IList<UniqueId>> 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<UniqueId> 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;
}
}
}

View File

@@ -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<List<string>> HandleSynchronizationAsync(IImapClient client, MailItemFolder folder, IImapSynchronizer synchronizer, CancellationToken cancellationToken = default);
internal abstract Task<IList<UniqueId>> GetChangedUidsAsync(IImapClient client, MailItemFolder localFolder, IMailFolder remoteFolder, IImapSynchronizer synchronizer, CancellationToken cancellationToken = default);
protected async Task<List<string>> HandleChangedUIdsAsync(MailItemFolder folder, IImapSynchronizer synchronizer, IMailFolder remoteFolder, IList<UniqueId> changedUids, CancellationToken cancellationToken)
{
List<string> 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)
{

View File

@@ -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<string>();
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<IList<UniqueId>> 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);
}
}
}

View File

@@ -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
/// <summary>
/// Uid based IMAP Synchronization strategy.
/// </summary>
internal class UidBasedSynchronizer : IImapSynchronizerStrategy
internal class UidBasedSynchronizer : ImapSynchronizationStrategyBase
{
public Task<List<string>> 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<List<string>> 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<string>());
var downloadedMessageIds = new List<string>();
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<IList<UniqueId>> GetChangedUidsAsync(IImapClient client, MailItemFolder localFolder, IMailFolder remoteFolder, IImapSynchronizer synchronizer, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
}
}

View File

@@ -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<ImapRequest, ImapMessageCreationPackage, object>, 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<string>();
IImapClient availableClient = null;
retry:
@@ -666,7 +666,6 @@ namespace Wino.Core.Synchronizers.Mail
return new List<string>();
}
/// <summary>
/// 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.

View File

@@ -16,34 +16,34 @@
<ItemGroup>
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.3.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.4.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Google.Apis.Calendar.v3" Version="1.68.0.3592" />
<PackageReference Include="Google.Apis.Gmail.v1" Version="1.68.0.3427" />
<PackageReference Include="Google.Apis.PeopleService.v1" Version="1.68.0.3359" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.70" />
<PackageReference Include="HtmlKit" Version="1.1.0" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.72" />
<PackageReference Include="HtmlKit" Version="1.2.0" />
<PackageReference Include="IsExternalInit" Version="1.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MailKit" Version="4.9.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageReference Include="Microsoft.Graph" Version="5.67.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.66.2" />
<PackageReference Include="Microsoft.Identity.Client.Broker" Version="4.66.2" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.66.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.1" />
<PackageReference Include="Microsoft.Graph" Version="5.69.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
<PackageReference Include="Microsoft.Identity.Client.Broker" Version="4.67.2" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.67.2" />
<PackageReference Include="MimeKit" Version="4.9.0" />
<PackageReference Include="morelinq" Version="4.3.0" />
<PackageReference Include="morelinq" Version="4.4.0" />
<PackageReference Include="Nito.AsyncEx.Tasks" Version="5.1.2" />
<PackageReference Include="NodaTime" Version="3.2.0" />
<PackageReference Include="Serilog" Version="4.1.0" />
<PackageReference Include="NodaTime" Version="3.2.1" />
<PackageReference Include="Serilog" Version="4.2.0" />
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="SkiaSharp" Version="2.88.9" />
<PackageReference Include="SkiaSharp" Version="3.116.1" />
<PackageReference Include="SqlKata" Version="2.4.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.1" />
</ItemGroup>
<ItemGroup>

View File

@@ -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;

View File

@@ -16,7 +16,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="5.0.6" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.66.2" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
<PackageReference Include="System.Reactive" Version="6.0.1" />
</ItemGroup>

View File

@@ -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<bool>();
_ = ShowAsync().AsTask().ContinueWith((t) =>
{
tcs.TrySetResult(true);
});
await tcs.Task;
}
public void ShowPreparingFolders()
{

View File

@@ -128,16 +128,16 @@
<Version>1.0.0</Version>
</PackageReference>
<PackageReference Include="CommunityToolkit.Common">
<Version>8.3.2</Version>
<Version>8.4.0</Version>
</PackageReference>
<PackageReference Include="CommunityToolkit.Diagnostics">
<Version>8.3.2</Version>
<Version>8.4.0</Version>
</PackageReference>
<PackageReference Include="CommunityToolkit.Labs.Uwp.Controls.MarkdownTextBlock">
<Version>0.1.240917-build.1755</Version>
</PackageReference>
<PackageReference Include="CommunityToolkit.Mvvm">
<Version>8.3.2</Version>
<Version>8.4.0</Version>
</PackageReference>
<PackageReference Include="CommunityToolkit.Uwp.Animations">
<Version>8.1.240916</Version>
@@ -173,10 +173,10 @@
<Version>5.0.6</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection">
<Version>8.0.1</Version>
<Version>9.0.1</Version>
</PackageReference>
<PackageReference Include="Microsoft.Identity.Client">
<Version>4.66.2</Version>
<Version>4.67.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Net.Native.Compiler">
<Version>2.2.12-rel-33220-00</Version>
@@ -196,7 +196,7 @@
<Version>5.1.2</Version>
</PackageReference>
<PackageReference Include="Serilog">
<Version>4.1.0</Version>
<Version>4.2.0</Version>
</PackageReference>
<PackageReference Include="Serilog.Exceptions">
<Version>8.4.0</Version>
@@ -205,7 +205,7 @@
<Version>1.9.172</Version>
</PackageReference>
<PackageReference Include="Win2D.uwp">
<Version>1.28.0</Version>
<Version>1.28.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>

View File

@@ -18,7 +18,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Text.Json" Version="8.0.5" />
<PackageReference Include="System.Text.Json" Version="9.0.1" />
</ItemGroup>
</Project>

View File

@@ -133,6 +133,6 @@
<ProjectReference Include="..\Wino.Server\Wino.Server.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Identity.Client" Version="4.66.2" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
</ItemGroup>
</Project>

View File

@@ -33,11 +33,11 @@
</Resource>
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
<PackageReference Include="H.NotifyIcon.Wpf" Version="2.1.4" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="H.NotifyIcon.Wpf" Version="2.2.0" />
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.66.2" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Wino.Authentication\Wino.Authentication.csproj" />

View File

@@ -7,10 +7,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.70" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.72" />
<PackageReference Include="Ical.Net" Version="4.3.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
<PackageReference Include="Serilog" Version="4.1.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" />
<PackageReference Include="Serilog" Version="4.2.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />

View File

@@ -18,9 +18,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" PrivateAssets="all" />
<PackageReference Include="System.Text.Json" Version="8.0.5" PrivateAssets="all" />
<PackageReference Include="System.Text.Json" Version="9.0.1" PrivateAssets="all" />
</ItemGroup>
<PropertyGroup>