Bumping some nugets. More on the imap synchronizers.
This commit is contained in:
@@ -11,12 +11,12 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.3.2" />
|
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.4.0" />
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
<PackageReference Include="Google.Apis.Auth" Version="1.68.0" />
|
<PackageReference Include="Google.Apis.Auth" Version="1.69.0" />
|
||||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.66.2" />
|
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
|
||||||
<PackageReference Include="Microsoft.Identity.Client.Broker" Version="4.66.2" />
|
<PackageReference Include="Microsoft.Identity.Client.Broker" Version="4.67.2" />
|
||||||
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.66.2" />
|
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.67.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -87,7 +87,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Identity.Client">
|
<PackageReference Include="Microsoft.Identity.Client">
|
||||||
<Version>4.66.2</Version>
|
<Version>4.67.2</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||||
<Version>6.2.14</Version>
|
<Version>6.2.14</Version>
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.targets" />
|
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.targets" />
|
||||||
<ItemGroup>
|
<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" />
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" PrivateAssets="all" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<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" />
|
<PackageReference Include="TimePeriodLibrary.NET" Version="2.1.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -328,13 +328,13 @@
|
|||||||
<Version>8.1.240916</Version>
|
<Version>8.1.240916</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Identity.Client">
|
<PackageReference Include="Microsoft.Identity.Client">
|
||||||
<Version>4.66.2</Version>
|
<Version>4.67.2</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||||
<Version>6.2.14</Version>
|
<Version>6.2.14</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Win2D.uwp">
|
<PackageReference Include="Win2D.uwp">
|
||||||
<Version>1.28.0</Version>
|
<Version>1.28.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
|
|
||||||
namespace Wino.Core.Domain.Interfaces
|
namespace Wino.Core.Domain.Interfaces
|
||||||
{
|
{
|
||||||
public interface IAccountCreationDialog
|
public interface IAccountCreationDialog
|
||||||
{
|
{
|
||||||
void ShowDialog(CancellationTokenSource cancellationTokenSource);
|
Task ShowDialogAsync(CancellationTokenSource cancellationTokenSource);
|
||||||
void Complete(bool cancel);
|
void Complete(bool cancel);
|
||||||
AccountCreationDialogState State { get; set; }
|
AccountCreationDialogState State { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,8 +60,8 @@
|
|||||||
<EmbeddedResource Include="Translations\zh_CN\resources.json" />
|
<EmbeddedResource Include="Translations\zh_CN\resources.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.3.2" />
|
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.4.0" />
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
<PackageReference Include="IsExternalInit" Version="1.0.3">
|
<PackageReference Include="IsExternalInit" Version="1.0.3">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
<PackageReference Include="MimeKit" Version="4.9.0" />
|
<PackageReference Include="MimeKit" Version="4.9.0" />
|
||||||
<PackageReference Include="MailKit" Version="4.9.0" />
|
<PackageReference Include="MailKit" Version="4.9.0" />
|
||||||
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
|
<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" />
|
<PackageReference Include="TimePeriodLibrary.NET" Version="2.1.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Windows.UI.Xaml;
|
using Windows.UI.Xaml;
|
||||||
using Windows.UI.Xaml.Controls;
|
using Windows.UI.Xaml.Controls;
|
||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
@@ -8,6 +9,7 @@ namespace Wino.Dialogs
|
|||||||
{
|
{
|
||||||
public sealed partial class AccountCreationDialog : ContentDialog, IAccountCreationDialog
|
public sealed partial class AccountCreationDialog : ContentDialog, IAccountCreationDialog
|
||||||
{
|
{
|
||||||
|
private TaskCompletionSource<bool> dialogOpened = new TaskCompletionSource<bool>();
|
||||||
public CancellationTokenSource CancellationTokenSource { get; private set; }
|
public CancellationTokenSource CancellationTokenSource { get; private set; }
|
||||||
|
|
||||||
public AccountCreationDialogState State
|
public AccountCreationDialogState State
|
||||||
@@ -32,13 +34,6 @@ namespace Wino.Dialogs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowDialog(CancellationTokenSource cancellationTokenSource)
|
|
||||||
{
|
|
||||||
CancellationTokenSource = cancellationTokenSource;
|
|
||||||
|
|
||||||
_ = ShowAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Complete(bool cancel)
|
public void Complete(bool cancel)
|
||||||
{
|
{
|
||||||
State = cancel ? AccountCreationDialogState.Canceled : AccountCreationDialogState.Completed;
|
State = cancel ? AccountCreationDialogState.Canceled : AccountCreationDialogState.Completed;
|
||||||
@@ -53,6 +48,26 @@ namespace Wino.Dialogs
|
|||||||
|
|
||||||
Hide();
|
Hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelClicked(object sender, System.EventArgs e) => Complete(true);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,13 +187,13 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Common">
|
<PackageReference Include="CommunityToolkit.Common">
|
||||||
<Version>8.3.2</Version>
|
<Version>8.4.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="CommunityToolkit.Diagnostics">
|
<PackageReference Include="CommunityToolkit.Diagnostics">
|
||||||
<Version>8.3.2</Version>
|
<Version>8.4.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm">
|
<PackageReference Include="CommunityToolkit.Mvvm">
|
||||||
<Version>8.3.2</Version>
|
<Version>8.4.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="CommunityToolkit.Uwp.Animations">
|
<PackageReference Include="CommunityToolkit.Uwp.Animations">
|
||||||
<Version>8.1.240916</Version>
|
<Version>8.1.240916</Version>
|
||||||
@@ -224,7 +224,7 @@
|
|||||||
<Version>5.0.6</Version>
|
<Version>5.0.6</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Identity.Client">
|
<PackageReference Include="Microsoft.Identity.Client">
|
||||||
<Version>4.66.2</Version>
|
<Version>4.67.2</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||||
<Version>6.2.14</Version>
|
<Version>6.2.14</Version>
|
||||||
@@ -236,7 +236,7 @@
|
|||||||
<Version>2.8.6</Version>
|
<Version>2.8.6</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Win2D.uwp">
|
<PackageReference Include="Win2D.uwp">
|
||||||
<Version>1.28.0</Version>
|
<Version>1.28.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="5.0.6" />
|
<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" />
|
<PackageReference Include="System.Reactive" Version="6.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,10 @@ using System.Threading.Tasks;
|
|||||||
using MailKit;
|
using MailKit;
|
||||||
using MailKit.Net.Imap;
|
using MailKit.Net.Imap;
|
||||||
using MailKit.Search;
|
using MailKit.Search;
|
||||||
using MoreLinq;
|
|
||||||
using Serilog;
|
|
||||||
using Wino.Core.Domain.Entities.Mail;
|
using Wino.Core.Domain.Entities.Mail;
|
||||||
using Wino.Core.Domain.Exceptions;
|
using Wino.Core.Domain.Exceptions;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
using Wino.Core.Domain.Models.MailItem;
|
|
||||||
using Wino.Core.Integration;
|
using Wino.Core.Integration;
|
||||||
using Wino.Services.Extensions;
|
|
||||||
using IMailService = Wino.Core.Domain.Interfaces.IMailService;
|
using IMailService = Wino.Core.Domain.Interfaces.IMailService;
|
||||||
|
|
||||||
namespace Wino.Core.Synchronizers.ImapSync
|
namespace Wino.Core.Synchronizers.ImapSync
|
||||||
@@ -41,6 +37,7 @@ namespace Wino.Core.Synchronizers.ImapSync
|
|||||||
IMailFolder remoteFolder = null;
|
IMailFolder remoteFolder = null;
|
||||||
|
|
||||||
var downloadedMessageIds = new List<string>();
|
var downloadedMessageIds = new List<string>();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
remoteFolder = await winoClient.GetFolderAsync(folder.RemoteFolderId, cancellationToken).ConfigureAwait(false);
|
remoteFolder = await winoClient.GetFolderAsync(folder.RemoteFolderId, cancellationToken).ConfigureAwait(false);
|
||||||
@@ -57,92 +54,10 @@ namespace Wino.Core.Synchronizers.ImapSync
|
|||||||
// the MODSEQ value for deleted messages.
|
// the MODSEQ value for deleted messages.
|
||||||
if (remoteHighestModSeq > localHighestModSeq)
|
if (remoteHighestModSeq > localHighestModSeq)
|
||||||
{
|
{
|
||||||
// Search for emails with a MODSEQ greater than the last known value.
|
var changedUids = await GetChangedUidsAsync(client, folder, remoteFolder, synchronizer, cancellationToken).ConfigureAwait(false);
|
||||||
// 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();
|
|
||||||
|
|
||||||
// Get locally exists mails for the returned UIDs.
|
// Get locally exists mails for the returned UIDs.
|
||||||
var existingMails = await MailService.GetExistingMailsAsync(folder.Id, changedUids).ConfigureAwait(false);
|
downloadedMessageIds = await HandleChangedUIdsAsync(folder, synchronizer, remoteFolder, changedUids, cancellationToken).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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
folder.HighestModeSeq = (long)remoteHighestModSeq;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,11 @@ using System.Threading.Tasks;
|
|||||||
using MailKit;
|
using MailKit;
|
||||||
using MailKit.Net.Imap;
|
using MailKit.Net.Imap;
|
||||||
using MailKit.Search;
|
using MailKit.Search;
|
||||||
|
using MoreLinq;
|
||||||
|
using Serilog;
|
||||||
using Wino.Core.Domain.Entities.Mail;
|
using Wino.Core.Domain.Entities.Mail;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
using Wino.Core.Domain.Models.MailItem;
|
||||||
using Wino.Services.Extensions;
|
using Wino.Services.Extensions;
|
||||||
using IMailService = Wino.Core.Domain.Interfaces.IMailService;
|
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);
|
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)
|
protected async Task HandleMessageFlagsChangeAsync(MailItemFolder folder, UniqueId? uniqueId, MessageFlags flags)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -29,6 +28,8 @@ namespace Wino.Core.Synchronizers.ImapSync
|
|||||||
IImapSynchronizer synchronizer,
|
IImapSynchronizer synchronizer,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
var downloadedMessageIds = new List<string>();
|
||||||
|
|
||||||
if (client is not WinoImapClient winoClient)
|
if (client is not WinoImapClient winoClient)
|
||||||
throw new ImapSynchronizerStrategyException("Client must be of type WinoImapClient.");
|
throw new ImapSynchronizerStrategyException("Client must be of type WinoImapClient.");
|
||||||
|
|
||||||
@@ -54,7 +55,6 @@ namespace Wino.Core.Synchronizers.ImapSync
|
|||||||
if (!isCacheValid)
|
if (!isCacheValid)
|
||||||
{
|
{
|
||||||
// TODO: Remove all local data.
|
// TODO: Remove all local data.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform QRESYNC synchronization.
|
// Perform QRESYNC synchronization.
|
||||||
@@ -70,42 +70,52 @@ namespace Wino.Core.Synchronizers.ImapSync
|
|||||||
|
|
||||||
await remoteFolder.OpenAsync(FolderAccess.ReadOnly, folder.UidValidity, localHighestModSeq, allUniqueIds).ConfigureAwait(false);
|
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)
|
downloadedMessageIds = await HandleChangedUIdsAsync(folder, synchronizer, remoteFolder, changedUids, cancellationToken).ConfigureAwait(false);
|
||||||
{
|
|
||||||
Debug.WriteLine($"Processing message with UID: {uid}");
|
|
||||||
|
|
||||||
// var message = await remoteFolder.GetMessageAsync(uid, cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
// TODO: Process the message.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the local folder with the new highest mod-seq and validity.
|
// Update the local folder with the new highest mod-seq and validity.
|
||||||
folder.HighestModeSeq = (long)remoteHighestModSeq;
|
folder.HighestModeSeq = (long)remoteHighestModSeq;
|
||||||
folder.UidValidity = remoteFolder.UidValidity;
|
folder.UidValidity = remoteFolder.UidValidity;
|
||||||
|
|
||||||
|
await ManageUUIdBasedDeletedMessagesAsync(folder, remoteFolder, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
await FolderService.UpdateFolderAsync(folder).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;
|
throw;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (remoteFolder != null)
|
if (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
remoteFolder.MessagesVanished -= async (c, r) => await HandleMessageDeletedAsync(folder, r.UniqueIds).ConfigureAwait(false);
|
if (remoteFolder != null)
|
||||||
remoteFolder.MessageFlagsChanged -= async (c, r) => await HandleMessageFlagsChangeAsync(folder, r.UniqueId, r.Flags).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (remoteFolder.IsOpen)
|
|
||||||
{
|
{
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MailKit;
|
||||||
using MailKit.Net.Imap;
|
using MailKit.Net.Imap;
|
||||||
|
using MailKit.Search;
|
||||||
using Wino.Core.Domain.Entities.Mail;
|
using Wino.Core.Domain.Entities.Mail;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
using Wino.Core.Integration;
|
using Wino.Core.Integration;
|
||||||
@@ -12,14 +15,65 @@ namespace Wino.Core.Synchronizers.ImapSync
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uid based IMAP Synchronization strategy.
|
/// Uid based IMAP Synchronization strategy.
|
||||||
/// </summary>
|
/// </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)
|
if (client is not WinoImapClient winoClient)
|
||||||
throw new ArgumentException("Client must be of type WinoImapClient.", nameof(client));
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -31,8 +32,9 @@ namespace Wino.Core.Synchronizers.Mail
|
|||||||
{
|
{
|
||||||
public class ImapSynchronizer : WinoSynchronizer<ImapRequest, ImapMessageCreationPackage, object>, IImapSynchronizer
|
public class ImapSynchronizer : WinoSynchronizer<ImapRequest, ImapMessageCreationPackage, object>, IImapSynchronizer
|
||||||
{
|
{
|
||||||
|
[Obsolete("N/A")]
|
||||||
public override uint BatchModificationSize => 1000;
|
public override uint BatchModificationSize => 1000;
|
||||||
public override uint InitialMessageDownloadCountPerFolder => 250;
|
public override uint InitialMessageDownloadCountPerFolder => 500;
|
||||||
|
|
||||||
#region Idle Implementation
|
#region Idle Implementation
|
||||||
|
|
||||||
@@ -631,8 +633,6 @@ namespace Wino.Core.Synchronizers.Mail
|
|||||||
{
|
{
|
||||||
if (!folder.IsSynchronizationEnabled) return default;
|
if (!folder.IsSynchronizationEnabled) return default;
|
||||||
|
|
||||||
var downloadedMessageIds = new List<string>();
|
|
||||||
|
|
||||||
IImapClient availableClient = null;
|
IImapClient availableClient = null;
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
@@ -666,7 +666,6 @@ namespace Wino.Core.Synchronizers.Mail
|
|||||||
return new List<string>();
|
return new List<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the local folder should be updated with the remote folder.
|
/// Whether the local folder should be updated with the remote folder.
|
||||||
/// IMAP only compares folder name for now.
|
/// IMAP only compares folder name for now.
|
||||||
@@ -705,7 +704,6 @@ namespace Wino.Core.Synchronizers.Mail
|
|||||||
// Setup idle client.
|
// Setup idle client.
|
||||||
idleClient = client;
|
idleClient = client;
|
||||||
|
|
||||||
|
|
||||||
idleDoneTokenSource ??= new CancellationTokenSource();
|
idleDoneTokenSource ??= new CancellationTokenSource();
|
||||||
idleCancellationTokenSource ??= new CancellationTokenSource();
|
idleCancellationTokenSource ??= new CancellationTokenSource();
|
||||||
|
|
||||||
@@ -716,6 +714,7 @@ namespace Wino.Core.Synchronizers.Mail
|
|||||||
inboxFolder.CountChanged += IdleNotificationTriggered;
|
inboxFolder.CountChanged += IdleNotificationTriggered;
|
||||||
inboxFolder.MessageFlagsChanged += IdleNotificationTriggered;
|
inboxFolder.MessageFlagsChanged += IdleNotificationTriggered;
|
||||||
inboxFolder.MessageExpunged += IdleNotificationTriggered;
|
inboxFolder.MessageExpunged += IdleNotificationTriggered;
|
||||||
|
inboxFolder.MessagesVanished += IdleNotificationTriggered;
|
||||||
|
|
||||||
await client.IdleAsync(idleDoneTokenSource.Token, idleCancellationTokenSource.Token);
|
await client.IdleAsync(idleDoneTokenSource.Token, idleCancellationTokenSource.Token);
|
||||||
}
|
}
|
||||||
@@ -745,6 +744,7 @@ namespace Wino.Core.Synchronizers.Mail
|
|||||||
inboxFolder.CountChanged -= IdleNotificationTriggered;
|
inboxFolder.CountChanged -= IdleNotificationTriggered;
|
||||||
inboxFolder.MessageFlagsChanged -= IdleNotificationTriggered;
|
inboxFolder.MessageFlagsChanged -= IdleNotificationTriggered;
|
||||||
inboxFolder.MessageExpunged -= IdleNotificationTriggered;
|
inboxFolder.MessageExpunged -= IdleNotificationTriggered;
|
||||||
|
inboxFolder.MessagesVanished -= IdleNotificationTriggered;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idleDoneTokenSource != null)
|
if (idleDoneTokenSource != null)
|
||||||
@@ -776,6 +776,8 @@ namespace Wino.Core.Synchronizers.Mail
|
|||||||
|
|
||||||
private void RequestIdleChangeSynchronization()
|
private void RequestIdleChangeSynchronization()
|
||||||
{
|
{
|
||||||
|
Debug.WriteLine("Detected idle change.");
|
||||||
|
|
||||||
// We don't really need to act on the count change in detail.
|
// 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.
|
// Our synchronization should be enough to handle the changes with on-demand sync.
|
||||||
// We can just trigger a sync here IMAPIdle type.
|
// We can just trigger a sync here IMAPIdle type.
|
||||||
|
|||||||
@@ -16,34 +16,34 @@
|
|||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.3.2" />
|
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.4.0" />
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
<PackageReference Include="Google.Apis.Calendar.v3" Version="1.68.0.3592" />
|
<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.Gmail.v1" Version="1.68.0.3427" />
|
||||||
<PackageReference Include="Google.Apis.PeopleService.v1" Version="1.68.0.3359" />
|
<PackageReference Include="Google.Apis.PeopleService.v1" Version="1.68.0.3359" />
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.70" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.11.72" />
|
||||||
<PackageReference Include="HtmlKit" Version="1.1.0" />
|
<PackageReference Include="HtmlKit" Version="1.2.0" />
|
||||||
<PackageReference Include="IsExternalInit" Version="1.0.3">
|
<PackageReference Include="IsExternalInit" Version="1.0.3">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="MailKit" Version="4.9.0" />
|
<PackageReference Include="MailKit" Version="4.9.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.1" />
|
||||||
<PackageReference Include="Microsoft.Graph" Version="5.67.0" />
|
<PackageReference Include="Microsoft.Graph" Version="5.69.0" />
|
||||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.66.2" />
|
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
|
||||||
<PackageReference Include="Microsoft.Identity.Client.Broker" Version="4.66.2" />
|
<PackageReference Include="Microsoft.Identity.Client.Broker" Version="4.67.2" />
|
||||||
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.66.2" />
|
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.67.2" />
|
||||||
<PackageReference Include="MimeKit" Version="4.9.0" />
|
<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="Nito.AsyncEx.Tasks" Version="5.1.2" />
|
||||||
<PackageReference Include="NodaTime" Version="3.2.0" />
|
<PackageReference Include="NodaTime" Version="3.2.1" />
|
||||||
<PackageReference Include="Serilog" Version="4.1.0" />
|
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||||
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
|
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="6.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="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>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ namespace Wino.Mail.ViewModels
|
|||||||
Id = Guid.NewGuid()
|
Id = Guid.NewGuid()
|
||||||
};
|
};
|
||||||
|
|
||||||
creationDialog.ShowDialog(accountCreationCancellationTokenSource);
|
await creationDialog.ShowDialogAsync(accountCreationCancellationTokenSource);
|
||||||
creationDialog.State = AccountCreationDialogState.SigningIn;
|
creationDialog.State = AccountCreationDialogState.SigningIn;
|
||||||
|
|
||||||
string tokenInformation = string.Empty;
|
string tokenInformation = string.Empty;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="5.0.6" />
|
<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" />
|
<PackageReference Include="System.Reactive" Version="6.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -78,8 +78,17 @@ namespace Wino.Dialogs
|
|||||||
|
|
||||||
public void Receive(ImapSetupDismissRequested message) => _getServerInfoTaskCompletionSource.TrySetResult(message.CompletedServerInformation);
|
public void Receive(ImapSetupDismissRequested message) => _getServerInfoTaskCompletionSource.TrySetResult(message.CompletedServerInformation);
|
||||||
|
|
||||||
public void ShowDialog(CancellationTokenSource cancellationTokenSource)
|
public async Task ShowDialogAsync(CancellationTokenSource cancellationTokenSource)
|
||||||
=> _ = ShowAsync();
|
{
|
||||||
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
|
_ = ShowAsync().AsTask().ContinueWith((t) =>
|
||||||
|
{
|
||||||
|
tcs.TrySetResult(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
public void ShowPreparingFolders()
|
public void ShowPreparingFolders()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -128,16 +128,16 @@
|
|||||||
<Version>1.0.0</Version>
|
<Version>1.0.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="CommunityToolkit.Common">
|
<PackageReference Include="CommunityToolkit.Common">
|
||||||
<Version>8.3.2</Version>
|
<Version>8.4.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="CommunityToolkit.Diagnostics">
|
<PackageReference Include="CommunityToolkit.Diagnostics">
|
||||||
<Version>8.3.2</Version>
|
<Version>8.4.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="CommunityToolkit.Labs.Uwp.Controls.MarkdownTextBlock">
|
<PackageReference Include="CommunityToolkit.Labs.Uwp.Controls.MarkdownTextBlock">
|
||||||
<Version>0.1.240917-build.1755</Version>
|
<Version>0.1.240917-build.1755</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm">
|
<PackageReference Include="CommunityToolkit.Mvvm">
|
||||||
<Version>8.3.2</Version>
|
<Version>8.4.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="CommunityToolkit.Uwp.Animations">
|
<PackageReference Include="CommunityToolkit.Uwp.Animations">
|
||||||
<Version>8.1.240916</Version>
|
<Version>8.1.240916</Version>
|
||||||
@@ -173,10 +173,10 @@
|
|||||||
<Version>5.0.6</Version>
|
<Version>5.0.6</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection">
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection">
|
||||||
<Version>8.0.1</Version>
|
<Version>9.0.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Identity.Client">
|
<PackageReference Include="Microsoft.Identity.Client">
|
||||||
<Version>4.66.2</Version>
|
<Version>4.67.2</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Net.Native.Compiler">
|
<PackageReference Include="Microsoft.Net.Native.Compiler">
|
||||||
<Version>2.2.12-rel-33220-00</Version>
|
<Version>2.2.12-rel-33220-00</Version>
|
||||||
@@ -196,7 +196,7 @@
|
|||||||
<Version>5.1.2</Version>
|
<Version>5.1.2</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Serilog">
|
<PackageReference Include="Serilog">
|
||||||
<Version>4.1.0</Version>
|
<Version>4.2.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Serilog.Exceptions">
|
<PackageReference Include="Serilog.Exceptions">
|
||||||
<Version>8.4.0</Version>
|
<Version>8.4.0</Version>
|
||||||
@@ -205,7 +205,7 @@
|
|||||||
<Version>1.9.172</Version>
|
<Version>1.9.172</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Win2D.uwp">
|
<PackageReference Include="Win2D.uwp">
|
||||||
<Version>1.28.0</Version>
|
<Version>1.28.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
<PackageReference Include="System.Text.Json" Version="9.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -133,6 +133,6 @@
|
|||||||
<ProjectReference Include="..\Wino.Server\Wino.Server.csproj" />
|
<ProjectReference Include="..\Wino.Server\Wino.Server.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.66.2" />
|
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -33,11 +33,11 @@
|
|||||||
</Resource>
|
</Resource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
<PackageReference Include="H.NotifyIcon.Wpf" Version="2.1.4" />
|
<PackageReference Include="H.NotifyIcon.Wpf" Version="2.2.0" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
||||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.66.2" />
|
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Wino.Authentication\Wino.Authentication.csproj" />
|
<ProjectReference Include="..\Wino.Authentication\Wino.Authentication.csproj" />
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<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="Ical.Net" Version="4.3.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" />
|
||||||
<PackageReference Include="Serilog" Version="4.1.0" />
|
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
|
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||||
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
||||||
|
|||||||
@@ -18,9 +18,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<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="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>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user