Reworked IMAP setup flow. Implemented easy way to share protocol log on failure if possible.

This commit is contained in:
Burak Kaan Köse
2024-06-17 02:16:06 +02:00
parent a788b1706b
commit 49afed7751
24 changed files with 1000 additions and 331 deletions

View File

@@ -43,11 +43,5 @@ namespace Wino.Core.Domain.Entities
public string ProxyServer { get; set; } public string ProxyServer { get; set; }
public string ProxyServerPort { get; set; } public string ProxyServerPort { get; set; }
[Obsolete("As 1.7.0")]
public bool IncomingRequiresSSL { get; set; }
[Obsolete("As 1.7.0")]
public bool OutgoingRequresSSL { get; set; }
} }
} }

View File

@@ -4,8 +4,11 @@ namespace Wino.Core.Domain.Exceptions
{ {
public class ImapClientPoolException : Exception public class ImapClientPoolException : Exception
{ {
public ImapClientPoolException(Exception innerException) : base(Translator.Exception_ImapClientPoolFailed, innerException) public ImapClientPoolException(Exception innerException, string protocolLog) : base(Translator.Exception_ImapClientPoolFailed, innerException)
{ {
} ProtocolLog = protocolLog;
}
public string ProtocolLog { get; }
} }
} }

View File

@@ -0,0 +1,21 @@
using System;
using Wino.Core.Domain.Models.AutoDiscovery;
namespace Wino.Core.Domain.Exceptions
{
public class ImapConnectionFailedPackage
{
public ImapConnectionFailedPackage(Exception error, string protocolLog, AutoDiscoverySettings settings)
{
Error = error;
ProtocolLog = protocolLog;
Settings = settings;
}
public AutoDiscoverySettings Settings { get; }
public Exception Error { get; }
public string ProtocolLog { get; }
public string GetErrorMessage() => Error.InnerException == null ? Error.Message : Error.InnerException.Message;
}
}

View File

@@ -15,5 +15,11 @@ namespace Wino.Core.Domain.Interfaces
/// Displays preparing folders page. /// Displays preparing folders page.
/// </summary> /// </summary>
void ShowPreparingFolders(); void ShowPreparingFolders();
/// <summary>
/// Updates account properties for the welcome imap setup dialog and starts the setup.
/// </summary>
/// <param name="account">Account properties.</param>
void StartImapConnectionSetup(MailAccount account);
} }
} }

View File

@@ -1,21 +0,0 @@
using System;
namespace Wino.Core.Domain.Models.AutoDiscovery
{
public class AutoDiscoveryConnectionTestFailedPackage
{
public AutoDiscoveryConnectionTestFailedPackage(AutoDiscoverySettings settings, Exception error)
{
Settings = settings ?? throw new ArgumentNullException(nameof(settings));
Error = error ?? throw new ArgumentNullException(nameof(error));
}
public AutoDiscoveryConnectionTestFailedPackage(Exception error)
{
Error = error ?? throw new ArgumentNullException(nameof(error));
}
public AutoDiscoverySettings Settings { get; set; }
public Exception Error { get; set; }
}
}

View File

@@ -30,9 +30,6 @@ namespace Wino.Core.Domain.Models.AutoDiscovery
if (imapSettings == null || smtpSettings == null) return null; if (imapSettings == null || smtpSettings == null) return null;
bool imapRequiresSSL = imapSettings.Secure == "SSL";
bool smtpRequiresSSL = smtpSettings.Secure == "SSL";
string imapUrl = imapSettings.Address; string imapUrl = imapSettings.Address;
string smtpUrl = smtpSettings.Address; string smtpUrl = smtpSettings.Address;
@@ -49,8 +46,8 @@ namespace Wino.Core.Domain.Models.AutoDiscovery
Address = UserMinimalSettings.Email, Address = UserMinimalSettings.Email,
IncomingServerPassword = UserMinimalSettings.Password, IncomingServerPassword = UserMinimalSettings.Password,
OutgoingServerPassword = UserMinimalSettings.Password, OutgoingServerPassword = UserMinimalSettings.Password,
IncomingRequiresSSL = imapRequiresSSL, IncomingAuthenticationMethod = Enums.ImapAuthenticationMethod.Auto,
OutgoingRequresSSL = smtpRequiresSSL, OutgoingAuthenticationMethod = Enums.ImapAuthenticationMethod.Auto,
IncomingServer = imapUrl, IncomingServer = imapUrl,
OutgoingServer = smtpUrl, OutgoingServer = smtpUrl,
IncomingServerPort = imapPort.ToString(), IncomingServerPort = imapPort.ToString(),

View File

@@ -27,6 +27,7 @@
"Buttons_Close": "Close", "Buttons_Close": "Close",
"Buttons_Create": "Create", "Buttons_Create": "Create",
"Buttons_CreateAccount": "Create Account", "Buttons_CreateAccount": "Create Account",
"Buttons_Copy": "Copy",
"Buttons_Delete": "Delete", "Buttons_Delete": "Delete",
"Buttons_Edit": "Edit", "Buttons_Edit": "Edit",
"Buttons_Discard": "Discard", "Buttons_Discard": "Discard",
@@ -39,6 +40,7 @@
"Buttons_SaveConfiguration": "Save Configuration", "Buttons_SaveConfiguration": "Save Configuration",
"Buttons_Share": "Share", "Buttons_Share": "Share",
"Buttons_SignIn": "Sign In", "Buttons_SignIn": "Sign In",
"Buttons_TryAgain": "Try Again",
"Buttons_Yes": "Yes", "Buttons_Yes": "Yes",
"Center": "Center", "Center": "Center",
"ComingSoon": "Coming soon...", "ComingSoon": "Coming soon...",
@@ -99,6 +101,7 @@
"ElementTheme_Default": "Use system setting", "ElementTheme_Default": "Use system setting",
"ElementTheme_Light": "Light mode", "ElementTheme_Light": "Light mode",
"Emoji": "Emoji", "Emoji": "Emoji",
"Exception_ImapAutoDiscoveryFailed": "Couldn't find mailbox settings.",
"Exception_ImapClientPoolFailed": "IMAP Client Pool failed.", "Exception_ImapClientPoolFailed": "IMAP Client Pool failed.",
"Exception_AuthenticationCanceled": "Authentication canceled", "Exception_AuthenticationCanceled": "Authentication canceled",
"Exception_CustomThemeExists": "This theme already exists.", "Exception_CustomThemeExists": "This theme already exists.",
@@ -169,6 +172,8 @@
"IMAPSetupDialog_UseSameConfig": "Use the same username and password for sending email", "IMAPSetupDialog_UseSameConfig": "Use the same username and password for sending email",
"IMAPSetupDialog_Username": "Username", "IMAPSetupDialog_Username": "Username",
"IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domain/johndoe", "IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domain/johndoe",
"IMAPSetupDialog_ConnectionFailedTitle": "Connection Failed",
"IMAPSetupDialog_ConnectionFailedMessage": "IMAP connection failed.",
"ImageRenderingDisabled": "Image rendering is disabled for this message.", "ImageRenderingDisabled": "Image rendering is disabled for this message.",
"InfoBarAction_Enable": "Enable", "InfoBarAction_Enable": "Enable",
"InfoBarMessage_SynchronizationDisabledFolder": "This folder is disabled for synchronization.", "InfoBarMessage_SynchronizationDisabledFolder": "This folder is disabled for synchronization.",
@@ -323,6 +328,7 @@
"ProviderDetail_Gmail_Description": "Google Account", "ProviderDetail_Gmail_Description": "Google Account",
"ProviderDetail_IMAP_Description": "Custom IMAP/SMTP server", "ProviderDetail_IMAP_Description": "Custom IMAP/SMTP server",
"ProviderDetail_IMAP_Title": "IMAP Server", "ProviderDetail_IMAP_Title": "IMAP Server",
"ProtocolLogAvailable_Message": "Protocol logs are available for diagnostics.",
"Results": "Results", "Results": "Results",
"Right": "Right", "Right": "Right",
"SynchronizationFolderReport_Success": "up to date", "SynchronizationFolderReport_Success": "up to date",

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,10 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.IO;
using System.Text;
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.Net.Proxy; using MailKit.Net.Proxy;
using MailKit.Security; using MailKit.Security;
@@ -20,7 +23,7 @@ namespace Wino.Core.Integration
/// TODO: Listens to the Inbox folder for new messages. /// TODO: Listens to the Inbox folder for new messages.
/// </summary> /// </summary>
/// <param name="customServerInformation">Connection/Authentication info to be used to configure ImapClient.</param> /// <param name="customServerInformation">Connection/Authentication info to be used to configure ImapClient.</param>
public class ImapClientPool public class ImapClientPool : IDisposable
{ {
// Hardcoded implementation details for ID extension if the server supports. // Hardcoded implementation details for ID extension if the server supports.
// Some providers like Chinese 126 require Id to be sent before authentication. // Some providers like Chinese 126 require Id to be sent before authentication.
@@ -40,11 +43,13 @@ namespace Wino.Core.Integration
private readonly ConcurrentBag<ImapClient> _clients = []; private readonly ConcurrentBag<ImapClient> _clients = [];
private readonly SemaphoreSlim _semaphore = new(MaxPoolSize); private readonly SemaphoreSlim _semaphore = new(MaxPoolSize);
private readonly CustomServerInformation _customServerInformation; private readonly CustomServerInformation _customServerInformation;
private readonly Stream _protocolLogStream;
private readonly ILogger _logger = Log.ForContext<ImapClientPool>(); private readonly ILogger _logger = Log.ForContext<ImapClientPool>();
public ImapClientPool(CustomServerInformation customServerInformation) public ImapClientPool(CustomServerInformation customServerInformation, Stream protocolLogStream = null)
{ {
_customServerInformation = customServerInformation; _customServerInformation = customServerInformation;
_protocolLogStream = protocolLogStream;
} }
private async Task EnsureConnectivityAsync(ImapClient client, bool isCreatedNew) private async Task EnsureConnectivityAsync(ImapClient client, bool isCreatedNew)
@@ -75,7 +80,7 @@ namespace Wino.Core.Integration
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new ImapClientPoolException(ex); throw new ImapClientPoolException(ex, GetProtocolLogContent());
} }
finally finally
{ {
@@ -84,6 +89,18 @@ namespace Wino.Core.Integration
} }
} }
public string GetProtocolLogContent()
{
if (_protocolLogStream == null) return default;
// Set the position to the beginning of the stream in case it is not already at the start
if (_protocolLogStream.CanSeek)
_protocolLogStream.Seek(0, SeekOrigin.Begin);
using var reader = new StreamReader(_protocolLogStream, Encoding.UTF8, true, 1024, leaveOpen: true);
return reader.ReadToEnd();
}
public async Task<ImapClient> GetClientAsync() public async Task<ImapClient> GetClientAsync()
{ {
await _semaphore.WaitAsync(); await _semaphore.WaitAsync();
@@ -113,7 +130,17 @@ namespace Wino.Core.Integration
public ImapClient CreateNewClient() public ImapClient CreateNewClient()
{ {
var client = new ImapClient(); ImapClient client = null;
// Make sure to create a ImapClient with a protocol logger if enabled.
if (_protocolLogStream != null)
{
client = new ImapClient(new ProtocolLogger(_protocolLogStream));
}
else
{
client = new ImapClient();
}
HttpProxyClient proxyClient = null; HttpProxyClient proxyClient = null;
@@ -175,5 +202,20 @@ namespace Wino.Core.Integration
await client.AuthenticateAsync(_customServerInformation.IncomingServerUsername, _customServerInformation.IncomingServerPassword); await client.AuthenticateAsync(_customServerInformation.IncomingServerUsername, _customServerInformation.IncomingServerPassword);
} }
public void Dispose()
{
foreach (var client in _clients)
{
client.Disconnect(true);
client.Dispose();
}
if (_protocolLogStream != null)
{
_protocolLogStream.Flush();
_protocolLogStream.Dispose();
}
}
} }
} }

View File

@@ -4,8 +4,9 @@ namespace Wino.Core.Messages.Mails
{ {
/// <summary> /// <summary>
/// When IMAP setup dialog requestes back breadcrumb navigation. /// When IMAP setup dialog requestes back breadcrumb navigation.
/// Not providing PageType will go back to previous page by doing back navigation.
/// </summary> /// </summary>
/// <param name="PageType">Type to go back.</param> /// <param name="PageType">Type to go back.</param>
/// <param name="Parameter">Back parameters.</param> /// <param name="Parameter">Back parameters.</param>
public record ImapSetupBackNavigationRequested(Type PageType, object Parameter); public record ImapSetupBackNavigationRequested(Type PageType = null, object Parameter = null);
} }

View File

@@ -1,9 +1,8 @@
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using MailKit;
using MailKit.Net.Imap;
using Wino.Core.Domain.Entities; using Wino.Core.Domain.Entities;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Integration;
namespace Wino.Core.Services namespace Wino.Core.Services
{ {
@@ -14,20 +13,17 @@ namespace Wino.Core.Services
private readonly IPreferencesService _preferencesService; private readonly IPreferencesService _preferencesService;
private readonly IAppInitializerService _appInitializerService; private readonly IAppInitializerService _appInitializerService;
private Stream _protocolLogStream;
public ImapTestService(IPreferencesService preferencesService, IAppInitializerService appInitializerService) public ImapTestService(IPreferencesService preferencesService, IAppInitializerService appInitializerService)
{ {
_preferencesService = preferencesService; _preferencesService = preferencesService;
_appInitializerService = appInitializerService; _appInitializerService = appInitializerService;
} }
public async Task TestImapConnectionAsync(CustomServerInformation serverInformation) private void EnsureProtocolLogFileExists()
{
ImapClient client = null;
if (_preferencesService.IsMailkitProtocolLoggerEnabled)
{ {
// Create new file for protocol logger. // Create new file for protocol logger.
var localAppFolderPath = _appInitializerService.GetApplicationDataFolder(); var localAppFolderPath = _appInitializerService.GetApplicationDataFolder();
var logFile = Path.Combine(localAppFolderPath, ProtocolLogFileName); var logFile = Path.Combine(localAppFolderPath, ProtocolLogFileName);
@@ -35,18 +31,22 @@ namespace Wino.Core.Services
if (File.Exists(logFile)) if (File.Exists(logFile))
File.Delete(logFile); File.Delete(logFile);
var stream = File.Create(logFile); _protocolLogStream = File.Create(logFile);
client = new ImapClient(new ProtocolLogger(stream));
} }
else
client = new ImapClient();
using (client) public async Task TestImapConnectionAsync(CustomServerInformation serverInformation)
{ {
// todo: test connection EnsureProtocolLogFileExists();
// await client.InitializeAsync(serverInformation);
var clientPool = new ImapClientPool(serverInformation, _protocolLogStream);
using (clientPool)
{
// This call will make sure that everything is authenticated + connected successfully.
var client = await clientPool.GetClientAsync();
await client.DisconnectAsync(true); await client.DisconnectAsync(true);
client.Dispose();
} }
} }
} }

View File

@@ -59,14 +59,14 @@ namespace Wino.Core.Synchronizers
private ImapClient _inboxIdleClient; private ImapClient _inboxIdleClient;
public override uint BatchModificationSize => 1000; public override uint BatchModificationSize => 1000;
public override uint InitialMessageDownloadCountPerFolder => 500; public override uint InitialMessageDownloadCountPerFolder => 250;
public ImapSynchronizer(MailAccount account, IImapChangeProcessor imapChangeProcessor) : base(account) public ImapSynchronizer(MailAccount account, IImapChangeProcessor imapChangeProcessor) : base(account)
{ {
_clientPool = new ImapClientPool(Account.ServerInformation); _clientPool = new ImapClientPool(Account.ServerInformation);
_imapChangeProcessor = imapChangeProcessor;
idleDoneToken = new CancellationTokenSource(); idleDoneToken = new CancellationTokenSource();
_imapChangeProcessor = imapChangeProcessor;
} }
// TODO // TODO

View File

@@ -174,6 +174,9 @@ namespace Wino.Mail.ViewModels
// Custom server implementation requires more async waiting. // Custom server implementation requires more async waiting.
if (creationDialog is ICustomServerAccountCreationDialog customServerDialog) if (creationDialog is ICustomServerAccountCreationDialog customServerDialog)
{ {
// Pass along the account properties and perform initial navigation on the imap frame.
customServerDialog.StartImapConnectionSetup(createdAccount);
customServerInformation = await customServerDialog.GetCustomServerInformationAsync() customServerInformation = await customServerDialog.GetCustomServerInformationAsync()
?? throw new AccountSetupCanceledException(); ?? throw new AccountSetupCanceledException();

View File

@@ -1,4 +1,5 @@
using System.Threading.Tasks; using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation; using Windows.UI.Xaml.Media.Animation;
@@ -31,8 +32,6 @@ namespace Wino.Dialogs
public NewImapSetupDialog() public NewImapSetupDialog()
{ {
InitializeComponent(); InitializeComponent();
ImapFrame.Navigate(typeof(WelcomeImapSetupPage), null, new DrillInNavigationTransitionInfo());
} }
// Not used for now. // Not used for now.
@@ -50,10 +49,25 @@ namespace Wino.Dialogs
public Task<CustomServerInformation> GetCustomServerInformationAsync() => _getServerInfoTaskCompletionSource.Task; public Task<CustomServerInformation> GetCustomServerInformationAsync() => _getServerInfoTaskCompletionSource.Task;
public void Receive(ImapSetupBackNavigationRequested message) public async void Receive(ImapSetupBackNavigationRequested message)
{
// Frame go back
if (message.PageType == null)
{
if (ImapFrame.CanGoBack)
{
// Go back using Dispatcher to allow navigations in OnNavigatedTo.
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
ImapFrame.GoBack();
});
}
}
else
{ {
ImapFrame.Navigate(message.PageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromLeft }); ImapFrame.Navigate(message.PageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromLeft });
} }
}
public void Receive(ImapSetupNavigationRequested message) public void Receive(ImapSetupNavigationRequested message)
{ {
@@ -69,6 +83,9 @@ namespace Wino.Dialogs
ImapFrame.Navigate(typeof(PreparingImapFoldersPage), new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromLeft }); ImapFrame.Navigate(typeof(PreparingImapFoldersPage), new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromLeft });
} }
public void StartImapConnectionSetup(MailAccount account) => ImapFrame.Navigate(typeof(WelcomeImapSetupPage), account, new DrillInNavigationTransitionInfo());
private void ImapSetupDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) => WeakReferenceMessenger.Default.UnregisterAll(this); private void ImapSetupDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) => WeakReferenceMessenger.Default.UnregisterAll(this);
private void ImapSetupDialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args) => WeakReferenceMessenger.Default.RegisterAll(this); private void ImapSetupDialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args) => WeakReferenceMessenger.Default.RegisterAll(this);

View File

@@ -44,28 +44,6 @@
IsAddTabButtonVisible="False" IsAddTabButtonVisible="False"
CanReorderTabs="False" CanReorderTabs="False"
TabWidthMode="Equal"> TabWidthMode="Equal">
<muxc:TabView.TabStripFooter>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button>
<Button.Content>
<StackPanel Orientation="Horizontal" Spacing="4">
<FontIcon
FontFamily="Segoe Fluent Icons"
Glyph="&#xE897;"
FontSize="13" />
<!--<TextBlock
Text="Help "
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="12" />-->
</StackPanel>
</Button.Content>
<Button.Flyout>
<Flyout />
</Button.Flyout>
</Button>
</StackPanel>
</muxc:TabView.TabStripFooter>
<muxc:TabViewItem Header="IMAP Settings" IsClosable="False"> <muxc:TabViewItem Header="IMAP Settings" IsClosable="False">
<!-- IMAP --> <!-- IMAP -->
<StackPanel Padding="12" Spacing="10"> <StackPanel Padding="12" Spacing="10">
@@ -85,7 +63,7 @@
<TextBox <TextBox
d:Header="Port" d:Header="Port"
PlaceholderText="993" Text="993"
Header="{x:Bind domain:Translator.IMAPSetupDialog_IncomingMailServerPort}" Header="{x:Bind domain:Translator.IMAPSetupDialog_IncomingMailServerPort}"
x:Name="IncomingServerPortBox" x:Name="IncomingServerPortBox"
Grid.Column="1" /> Grid.Column="1" />
@@ -165,6 +143,7 @@
d:Header="Port" d:Header="Port"
Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServerPort}" Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServerPort}"
x:Name="OutgoingServerPort" x:Name="OutgoingServerPort"
Text="587"
Grid.Column="1" /> Grid.Column="1" />
</Grid> </Grid>
@@ -230,11 +209,6 @@
</StackPanel> </StackPanel>
</muxc:TabViewItem> </muxc:TabViewItem>
</muxc:TabView> </muxc:TabView>
<TextBlock
x:Name="ErrorMessage"
TextWrapping="Wrap"
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}" />
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>

View File

@@ -9,13 +9,14 @@ using Wino.Core.Domain.Entities;
using Wino.Core.Domain.Models.Accounts; using Wino.Core.Domain.Models.Accounts;
using Wino.Core.Domain.Models.AutoDiscovery; using Wino.Core.Domain.Models.AutoDiscovery;
using Wino.Core.Messages.Mails; using Wino.Core.Messages.Mails;
using Wino.Extensions;
namespace Wino.Views.ImapSetup namespace Wino.Views.ImapSetup
{ {
public sealed partial class AdvancedImapSetupPage : Page public sealed partial class AdvancedImapSetupPage : Page
{ {
private string _protocolLog;
public List<ImapAuthenticationMethodModel> AvailableAuthenticationMethods { get; } = new List<ImapAuthenticationMethodModel>() public List<ImapAuthenticationMethodModel> AvailableAuthenticationMethods { get; } = new List<ImapAuthenticationMethodModel>()
{ {
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.Auto, Translator.ImapAuthenticationMethod_Auto), new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.Auto, Translator.ImapAuthenticationMethod_Auto),
@@ -76,6 +77,8 @@ namespace Wino.Views.ImapSetup
{ {
base.OnNavigatedTo(e); base.OnNavigatedTo(e);
// ProtocolLogGrid.Visibility = Visibility.Collapsed;
// Connection is succesfull but error occurred. // Connection is succesfull but error occurred.
// Imap and Smptp settings exists here at this point. // Imap and Smptp settings exists here at this point.
@@ -93,12 +96,8 @@ namespace Wino.Views.ImapSetup
IncomingServerBox.Text = serverInfo.IncomingServer; IncomingServerBox.Text = serverInfo.IncomingServer;
IncomingServerPortBox.Text = serverInfo.IncomingServerPort; IncomingServerPortBox.Text = serverInfo.IncomingServerPort;
OutgoingPasswordBox.Password = serverInfo.OutgoingServerPassword; OutgoingPasswordBox.Password = serverInfo.OutgoingServerPassword;
OutgoingServerBox.Text = serverInfo.OutgoingServer;
OutgoingServerPort.Text = serverInfo.OutgoingServerPort; OutgoingServerPort.Text = serverInfo.OutgoingServerPort;
OutgoingUsernameBox.Text = serverInfo.OutgoingServerUsername; OutgoingUsernameBox.Text = serverInfo.OutgoingServerUsername;
UseSameCredentialsForSending = OutgoingUsernameBox.Text == UsernameBox.Text; UseSameCredentialsForSending = OutgoingUsernameBox.Text == UsernameBox.Text;
@@ -112,12 +111,6 @@ namespace Wino.Views.ImapSetup
DisplayNameBox.Text = autoDiscoveryMinimalSettings.DisplayName; DisplayNameBox.Text = autoDiscoveryMinimalSettings.DisplayName;
PasswordBox.Password = autoDiscoveryMinimalSettings.Password; PasswordBox.Password = autoDiscoveryMinimalSettings.Password;
} }
else if (e.Parameter is AutoDiscoveryConnectionTestFailedPackage failedPackage)
{
ErrorMessage.Text = failedPackage.Error.Message;
MainScrollviewer.ScrollToElement(ErrorMessage);
}
} }
private void CancelClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested(null)); private void CancelClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested(null));
@@ -136,8 +129,6 @@ namespace Wino.Views.ImapSetup
private void SignInClicked(object sender, RoutedEventArgs e) private void SignInClicked(object sender, RoutedEventArgs e)
{ {
ErrorMessage.Text = string.Empty;
var info = new CustomServerInformation() var info = new CustomServerInformation()
{ {
IncomingServer = GetServerWithoutPort(IncomingServerBox.Text), IncomingServer = GetServerWithoutPort(IncomingServerBox.Text),

File diff suppressed because one or more lines are too long

View File

@@ -1,50 +0,0 @@
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.AutoDiscovery;
using Wino.Core.Messages.Mails;
namespace Wino.Views.ImapSetup
{
public sealed partial class AutoDiscoveryPage : Page
{
private readonly IAutoDiscoveryService _autoDiscoveryService;
public AutoDiscoveryPage()
{
InitializeComponent();
_autoDiscoveryService = App.Current.Services.GetService<IAutoDiscoveryService>();
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
AutoDiscoverySettings discoverySettings = null;
if (e.Parameter is AutoDiscoveryMinimalSettings userMinimalSettings)
{
discoverySettings = await _autoDiscoveryService.GetAutoDiscoverySettings(userMinimalSettings);
await Task.Delay(1000);
if (discoverySettings == null)
{
// Couldn't find settings.
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(WelcomeImapSetupPage), $"Couldn't find mailbox settings for {userMinimalSettings.Email}. Please configure it manually."));
}
else
{
// Settings are found. Test the connection with the given password.
discoverySettings.UserMinimalSettings = userMinimalSettings;
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(TestingImapConnectionPage), discoverySettings));
}
}
}
}
}

View File

@@ -0,0 +1,72 @@
<Page
x:Class="Wino.Views.ImapSetup.ImapConnectionFailedPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Wino.Views.ImapSetup"
xmlns:domain="using:Wino.Core.Domain"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Padding="24" RowSpacing="6">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="{x:Bind domain:Translator.IMAPSetupDialog_ConnectionFailedTitle}" Style="{StaticResource SubtitleTextBlockStyle}" />
<TextBlock
x:Name="ConnectionFailedMessage"
Grid.Row="1"
Style="{StaticResource BodyTextBlockStyle}" />
<!-- Protocol Log Area -->
<Grid
x:Name="ProtocolLogGrid"
Grid.Row="2"
ColumnSpacing="12"
Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
x:Name="ProtocolLogHeader"
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}"
VerticalAlignment="Center"
Text="{x:Bind domain:Translator.ProtocolLogAvailable_Message}" />
<Button
Content="{x:Bind domain:Translator.Buttons_Copy}"
Click="CopyProtocolLogButtonClicked"
Grid.Column="1" />
</Grid>
<!-- Dismis / GoBack -->
<Grid
Grid.Row="3"
VerticalAlignment="Bottom"
ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button
Content="{x:Bind domain:Translator.Buttons_Close}"
Click="CloseClicked"
HorizontalAlignment="Stretch" />
<Button
Content="{x:Bind domain:Translator.Buttons_TryAgain}"
Click="TryAgainClicked"
Style="{ThemeResource AccentButtonStyle}"
Grid.Column="1"
HorizontalAlignment="Stretch" />
</Grid>
</Grid>
</Page>

View File

@@ -0,0 +1,49 @@
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using Wino.Core.Domain;
using Wino.Core.Domain.Exceptions;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Messages.Mails;
namespace Wino.Views.ImapSetup
{
public sealed partial class ImapConnectionFailedPage : Page
{
private string _protocolLog;
private readonly IClipboardService _clipboardService = App.Current.Services.GetService<IClipboardService>();
private readonly IDialogService _dialogService = App.Current.Services.GetService<IDialogService>();
public ImapConnectionFailedPage()
{
InitializeComponent();
}
private async void CopyProtocolLogButtonClicked(object sender, RoutedEventArgs e)
{
await _clipboardService.CopyClipboardAsync(_protocolLog);
_dialogService.InfoBarMessage(Translator.ClipboardTextCopied_Title, string.Format(Translator.ClipboardTextCopied_Message, "Log"), Core.Domain.Enums.InfoBarMessageType.Information);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.Parameter is ImapConnectionFailedPackage failedPackage)
{
ConnectionFailedMessage.Text = failedPackage.GetErrorMessage();
ProtocolLogGrid.Visibility = !string.IsNullOrEmpty(failedPackage.ProtocolLog) ? Visibility.Visible : Visibility.Collapsed;
_protocolLog = failedPackage.ProtocolLog;
}
}
private void TryAgainClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested());
private void CloseClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested());
}
}

View File

@@ -1,12 +1,11 @@
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.AppCenter.Analytics;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
using Wino.Core.Domain.Entities; using Wino.Core.Domain.Entities;
using Wino.Core.Domain.Exceptions;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.AutoDiscovery; using Wino.Core.Domain.Models.AutoDiscovery;
using Wino.Core.Messages.Mails; using Wino.Core.Messages.Mails;
@@ -16,13 +15,11 @@ namespace Wino.Views.ImapSetup
{ {
public sealed partial class TestingImapConnectionPage : Page public sealed partial class TestingImapConnectionPage : Page
{ {
private IImapTestService _imapTestService; private IImapTestService _imapTestService = App.Current.Services.GetService<IImapTestService>();
public TestingImapConnectionPage() public TestingImapConnectionPage()
{ {
InitializeComponent(); InitializeComponent();
_imapTestService = App.Current.Services.GetService<IImapTestService>();
} }
private async Task TryTestConnectionAsync(CustomServerInformation serverInformation) private async Task TryTestConnectionAsync(CustomServerInformation serverInformation)
@@ -40,39 +37,43 @@ namespace Wino.Views.ImapSetup
{ {
base.OnNavigatedTo(e); base.OnNavigatedTo(e);
// We either go back to welcome setup page or advanced config page. // We can only go back to this page from failed connection page.
// Based on if we come from auto discovery or not. // We must go back once again in that case to actual setup dialog.
if (e.NavigationMode == NavigationMode.Back)
if (e.Parameter is AutoDiscoverySettings autoDiscoverySettings)
{ {
var serverInformation = autoDiscoverySettings.ToServerInformation(); WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested());
try
{
await TryTestConnectionAsync(serverInformation);
} }
catch (Exception ex) else
{ {
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(WelcomeImapSetupPage), // Test connection
new AutoDiscoveryConnectionTestFailedPackage(autoDiscoverySettings, ex)));
} CustomServerInformation serverInformationToTest = null;
AutoDiscoverySettings autoDiscoverySettings = null;
// Discovery settings are passed.
// Create server information out of the discovery settings.
if (e.Parameter is AutoDiscoverySettings parameterAutoDiscoverySettings)
{
autoDiscoverySettings = parameterAutoDiscoverySettings;
serverInformationToTest = autoDiscoverySettings.ToServerInformation();
} }
else if (e.Parameter is CustomServerInformation customServerInformation) else if (e.Parameter is CustomServerInformation customServerInformation)
{ {
// Only server information is passed.
serverInformationToTest = customServerInformation;
}
try try
{ {
await TryTestConnectionAsync(customServerInformation); await TryTestConnectionAsync(serverInformationToTest);
} }
catch (Exception ex) catch (Exception ex)
{ {
Analytics.TrackEvent("IMAP Test Failed", new Dictionary<string, string>() string protocolLog = ex is ImapClientPoolException clientPoolException ? clientPoolException.ProtocolLog : string.Empty;
{
{ "Server", customServerInformation.IncomingServer },
{ "Port", customServerInformation.IncomingServerPort },
});
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(AdvancedImapSetupPage), var failurePackage = new ImapConnectionFailedPackage(ex, protocolLog, autoDiscoverySettings);
new AutoDiscoveryConnectionTestFailedPackage(ex)));
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(ImapConnectionFailedPage), failurePackage));
} }
} }
} }

File diff suppressed because one or more lines are too long

View File

@@ -1,32 +1,51 @@
using System; using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities;
using Wino.Core.Domain.Exceptions;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.AutoDiscovery; using Wino.Core.Domain.Models.AutoDiscovery;
using Wino.Core.Messages.Mails; using Wino.Core.Messages.Mails;
using Wino.Extensions;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
namespace Wino.Views.ImapSetup namespace Wino.Views.ImapSetup
{ {
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class WelcomeImapSetupPage : Page public sealed partial class WelcomeImapSetupPage : Page
{ {
private AutoDiscoveryConnectionTestFailedPackage failedPackage; private ImapConnectionFailedPackage failedPackage;
private readonly IAutoDiscoveryService _autoDiscoveryService = App.Current.Services.GetService<IAutoDiscoveryService>();
public WelcomeImapSetupPage() public WelcomeImapSetupPage()
{ {
InitializeComponent(); InitializeComponent();
NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Enabled;
} }
private void SignInClicked(object sender, RoutedEventArgs e) protected override void OnNavigatedTo(NavigationEventArgs e)
{ {
failedPackage = null; base.OnNavigatedTo(e);
AutoDiscoveryPanel.Visibility = Visibility.Collapsed;
MainSetupPanel.Visibility = Visibility.Visible;
if (e.Parameter is MailAccount accountProperties)
{
DisplayNameBox.Text = accountProperties.Name;
}
}
private async void SignInClicked(object sender, RoutedEventArgs e)
{
MainSetupPanel.Visibility = Visibility.Collapsed;
AutoDiscoveryPanel.Visibility = Visibility.Visible;
// Let users see the discovery message for a while...
await Task.Delay(1000);
var minimalSettings = new AutoDiscoveryMinimalSettings() var minimalSettings = new AutoDiscoveryMinimalSettings()
{ {
@@ -35,32 +54,27 @@ namespace Wino.Views.ImapSetup
Email = AddressBox.Text, Email = AddressBox.Text,
}; };
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(AutoDiscoveryPage), minimalSettings)); var discoverySettings = await _autoDiscoveryService.GetAutoDiscoverySettings(minimalSettings);
}
protected override void OnNavigatedTo(NavigationEventArgs e) if (discoverySettings == null)
{ {
base.OnNavigatedTo(e); // Couldn't find settings.
if (e.Parameter is string errorMessage) var failurePackage = new ImapConnectionFailedPackage(new Exception(Translator.Exception_ImapAutoDiscoveryFailed), string.Empty, discoverySettings);
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(ImapConnectionFailedPage), failurePackage));
}
else
{ {
ErrorMessageText.Text = errorMessage; // Settings are found. Test the connection with the given password.
MainScrollviewer.ScrollToElement(ErrorMessageText); discoverySettings.UserMinimalSettings = minimalSettings;
}
else if (e.Parameter is AutoDiscoveryConnectionTestFailedPackage autoDiscoveryConnectionTestFailedPackage)
{
failedPackage = autoDiscoveryConnectionTestFailedPackage;
ErrorMessageText.Text = $"Discovery was successful but connection to the server failed.{Environment.NewLine}{Environment.NewLine}{autoDiscoveryConnectionTestFailedPackage.Error.Message}";
MainScrollviewer.ScrollToElement(ErrorMessageText); WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(TestingImapConnectionPage), discoverySettings));
} }
} }
private void CancelClicked(object sender, RoutedEventArgs e) private void CancelClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested());
{
WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested());
}
private void AdvancedConfigurationClicked(object sender, RoutedEventArgs e) private void AdvancedConfigurationClicked(object sender, RoutedEventArgs e)
{ {

View File

@@ -380,8 +380,8 @@
<Compile Include="Views\ImapSetup\AdvancedImapSetupPage.xaml.cs"> <Compile Include="Views\ImapSetup\AdvancedImapSetupPage.xaml.cs">
<DependentUpon>AdvancedImapSetupPage.xaml</DependentUpon> <DependentUpon>AdvancedImapSetupPage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\ImapSetup\AutoDiscoveryPage.xaml.cs"> <Compile Include="Views\ImapSetup\ImapConnectionFailedPage.xaml.cs">
<DependentUpon>AutoDiscoveryPage.xaml</DependentUpon> <DependentUpon>ImapConnectionFailedPage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\ImapSetup\PreparingImapFoldersPage.xaml.cs"> <Compile Include="Views\ImapSetup\PreparingImapFoldersPage.xaml.cs">
<DependentUpon>PreparingImapFoldersPage.xaml</DependentUpon> <DependentUpon>PreparingImapFoldersPage.xaml</DependentUpon>
@@ -595,7 +595,7 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Views\ImapSetup\AutoDiscoveryPage.xaml"> <Page Include="Views\ImapSetup\ImapConnectionFailedPage.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>