iCloud special imap handling.

This commit is contained in:
Burak Kaan Köse
2025-01-21 23:57:58 +01:00
parent 05280dfd42
commit 20010e77ae
21 changed files with 433 additions and 250 deletions

View File

@@ -27,7 +27,7 @@ namespace Wino.Core.Domain.Interfaces
string dontAskAgainConfigurationKey = ""); string dontAskAgainConfigurationKey = "");
Task<bool> ShowCustomThemeBuilderDialogAsync(); Task<bool> ShowCustomThemeBuilderDialogAsync();
Task<AccountCreationDialogResult> ShowAccountProviderSelectionDialogAsync(List<IProviderDetail> availableProviders); Task<AccountCreationDialogResult> ShowAccountProviderSelectionDialogAsync(List<IProviderDetail> availableProviders);
IAccountCreationDialog GetAccountCreationDialog(MailProviderType type); IAccountCreationDialog GetAccountCreationDialog(AccountCreationDialogResult accountCreationDialogResult);
Task<List<SharedFile>> PickFilesAsync(params object[] typeFilters); Task<List<SharedFile>> PickFilesAsync(params object[] typeFilters);
Task<string> PickFilePathAsync(string saveFileName); Task<string> PickFilePathAsync(string saveFileName);
} }

View File

@@ -3,7 +3,7 @@ using Wino.Core.Domain.Entities.Shared;
namespace Wino.Core.Domain.Interfaces namespace Wino.Core.Domain.Interfaces
{ {
public interface ICustomServerAccountCreationDialog : IAccountCreationDialog public interface IImapAccountCreationDialog : IAccountCreationDialog
{ {
/// <summary> /// <summary>
/// Returns the custom server information from the dialog.. /// Returns the custom server information from the dialog..

View File

@@ -0,0 +1,10 @@
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Models.Accounts;
namespace Wino.Core.Domain.Interfaces
{
public interface ISpecialImapProviderConfigResolver
{
CustomServerInformation GetServerInformation(MailAccount account, AccountCreationDialogResult dialogResult);
}
}

View File

@@ -2,5 +2,5 @@
namespace Wino.Core.Domain.Models.Accounts namespace Wino.Core.Domain.Models.Accounts
{ {
public record AccountCreationDialogResult(MailProviderType ProviderType, string AccountName, string AccountColorHex = ""); public record AccountCreationDialogResult(MailProviderType ProviderType, string AccountName, SpecialImapProviderDetails SpecialImapProviderDetails);
} }

View File

@@ -0,0 +1,6 @@
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Models.Accounts
{
public record SpecialImapProviderDetails(string Address, string Password, string SenderName, SpecialImapProvider SpecialImapProvider);
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,76 @@
using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Messaging.UI;
namespace Wino.Core.UWP.Controls
{
public sealed partial class AccountCreationDialogControl : UserControl, IRecipient<CopyAuthURLRequested>
{
private string copyClipboardURL;
public event EventHandler CancelClicked;
public AccountCreationDialogState State
{
get { return (AccountCreationDialogState)GetValue(StateProperty); }
set { SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(nameof(State), typeof(AccountCreationDialogState), typeof(AccountCreationDialogControl), new PropertyMetadata(AccountCreationDialogState.Idle, new PropertyChangedCallback(OnStateChanged)));
public AccountCreationDialogControl()
{
InitializeComponent();
}
private static void OnStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is AccountCreationDialogControl dialog)
{
dialog.UpdateVisualStates();
}
}
private void UpdateVisualStates() => VisualStateManager.GoToState(this, State.ToString(), false);
public async void Receive(CopyAuthURLRequested message)
{
copyClipboardURL = message.AuthURL;
await Task.Delay(2000);
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
AuthHelpDialogButton.Visibility = Windows.UI.Xaml.Visibility.Visible;
});
}
private void ControlLoaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
WeakReferenceMessenger.Default.Register(this);
}
private void ControlUnloaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
WeakReferenceMessenger.Default.UnregisterAll(this);
}
private async void CopyClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
if (string.IsNullOrEmpty(copyClipboardURL)) return;
var clipboardService = WinoApplication.Current.Services.GetService<IClipboardService>();
await clipboardService.CopyClipboardAsync(copyClipboardURL);
}
private void CancelButtonClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e) => CancelClicked?.Invoke(this, null);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,50 +1,58 @@
using System; using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.UWP;
using Wino.Messaging.UI;
namespace Wino.Dialogs namespace Wino.Dialogs
{ {
public sealed partial class AccountCreationDialog : BaseAccountCreationDialog, IRecipient<CopyAuthURLRequested> public sealed partial class AccountCreationDialog : ContentDialog, IAccountCreationDialog
{ {
private string copyClipboardURL; public CancellationTokenSource CancellationTokenSource { get; private set; }
public AccountCreationDialogState State
{
get { return (AccountCreationDialogState)GetValue(StateProperty); }
set { SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(nameof(State), typeof(AccountCreationDialogState), typeof(AccountCreationDialog), new PropertyMetadata(AccountCreationDialogState.Idle));
public AccountCreationDialog() public AccountCreationDialog()
{ {
InitializeComponent(); InitializeComponent();
WeakReferenceMessenger.Default.Register(this);
} }
public override void OnStateChanged(AccountCreationDialogState state) // Prevent users from dismissing it by ESC key.
public void DialogClosing(ContentDialog sender, ContentDialogClosingEventArgs args)
{ {
var tt = VisualStateManager.GoToState(this, state.ToString(), true); if (args.Result == ContentDialogResult.None)
}
public async void Receive(CopyAuthURLRequested message)
{
copyClipboardURL = message.AuthURL;
await Task.Delay(2000);
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{ {
AuthHelpDialogButton.Visibility = Windows.UI.Xaml.Visibility.Visible; args.Cancel = true;
}); }
} }
private void CancelClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e) => Complete(true); public void ShowDialog(CancellationTokenSource cancellationTokenSource)
private async void CopyClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{ {
if (string.IsNullOrEmpty(copyClipboardURL)) return; CancellationTokenSource = cancellationTokenSource;
var clipboardService = WinoApplication.Current.Services.GetService<IClipboardService>(); _ = ShowAsync();
await clipboardService.CopyClipboardAsync(copyClipboardURL);
} }
public void Complete(bool cancel)
{
State = cancel ? AccountCreationDialogState.Canceled : AccountCreationDialogState.Completed;
// Unregister from closing event.
Closing -= DialogClosing;
if (cancel && !CancellationTokenSource.IsCancellationRequested)
{
CancellationTokenSource.Cancel();
}
Hide();
}
private void CancelClicked(object sender, System.EventArgs e) => Complete(true);
} }
} }

View File

@@ -1,60 +0,0 @@
using System.Threading;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
namespace Wino.Dialogs
{
public abstract class BaseAccountCreationDialog : ContentDialog, IAccountCreationDialog
{
public AccountCreationDialogState State
{
get { return (AccountCreationDialogState)GetValue(StateProperty); }
set { SetValue(StateProperty, value); }
}
public CancellationTokenSource CancellationTokenSource { get; private set; }
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(nameof(State), typeof(AccountCreationDialogState), typeof(BaseAccountCreationDialog), new PropertyMetadata(AccountCreationDialogState.Idle, OnStateChanged));
private static void OnStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dialog = d as BaseAccountCreationDialog;
dialog.OnStateChanged((AccountCreationDialogState)e.NewValue);
}
public abstract void OnStateChanged(AccountCreationDialogState state);
// Prevent users from dismissing it by ESC key.
public void DialogClosing(ContentDialog sender, ContentDialogClosingEventArgs args)
{
if (args.Result == ContentDialogResult.None)
{
args.Cancel = true;
}
}
public void ShowDialog(CancellationTokenSource cancellationTokenSource)
{
CancellationTokenSource = cancellationTokenSource;
_ = ShowAsync();
}
public void Complete(bool cancel)
{
State = cancel ? AccountCreationDialogState.Canceled : AccountCreationDialogState.Completed;
// Unregister from closing event.
Closing -= DialogClosing;
if (cancel && !CancellationTokenSource.IsCancellationRequested)
{
CancellationTokenSource.Cancel();
}
Hide();
}
}
}

View File

@@ -85,6 +85,7 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
@@ -103,20 +104,27 @@
Source="{x:Bind SelectedMailProvider.ProviderImage, Mode=OneWay}" /> Source="{x:Bind SelectedMailProvider.ProviderImage, Mode=OneWay}" />
<TextBox <TextBox
x:Name="SpecialImapAddress" x:Name="DisplayNameTextBox"
Grid.Row="1" Grid.Row="1"
TextChanged="InputChanged" Header="Display Name"
PlaceholderText="eg. John Doe"
TextChanged="InputChanged" />
<TextBox
x:Name="SpecialImapAddress"
Grid.Row="2"
Header="E-mail Address" Header="E-mail Address"
PlaceholderText="eg. johndoe@testmail.com" /> PlaceholderText="eg. johndoe@testmail.com"
TextChanged="InputChanged" />
<PasswordBox <PasswordBox
x:Name="AppSpecificPassword" x:Name="AppSpecificPassword"
PasswordChanged="ImapPasswordChanged" Grid.Row="3"
Grid.Row="2" Header="App-Specific Password"
Header="App-Specific Password" /> PasswordChanged="ImapPasswordChanged" />
<HyperlinkButton <HyperlinkButton
Grid.Row="3" Grid.Row="4"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Click="AppSpecificHelpButtonClicked" Click="AppSpecificHelpButtonClicked"
Content="How do I get app-specific password?" /> Content="How do I get app-specific password?" />

View File

@@ -21,6 +21,7 @@ namespace Wino.Core.UWP.Dialogs
public static readonly DependencyProperty IsSpecialImapServerPartVisibleProperty = DependencyProperty.Register(nameof(IsSpecialImapServerPartVisible), typeof(bool), typeof(NewAccountDialog), new PropertyMetadata(false)); public static readonly DependencyProperty IsSpecialImapServerPartVisibleProperty = DependencyProperty.Register(nameof(IsSpecialImapServerPartVisible), typeof(bool), typeof(NewAccountDialog), new PropertyMetadata(false));
public static readonly DependencyProperty SelectedMailProviderProperty = DependencyProperty.Register(nameof(SelectedMailProvider), typeof(ProviderDetail), typeof(NewAccountDialog), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedProviderChanged))); public static readonly DependencyProperty SelectedMailProviderProperty = DependencyProperty.Register(nameof(SelectedMailProvider), typeof(ProviderDetail), typeof(NewAccountDialog), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedProviderChanged)));
/// <summary> /// <summary>
/// Gets or sets current selected mail provider in the dialog. /// Gets or sets current selected mail provider in the dialog.
/// </summary> /// </summary>
@@ -69,6 +70,17 @@ namespace Wino.Core.UWP.Dialogs
private void CreateClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args) private void CreateClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{ {
if (IsSpecialImapServerPartVisible)
{
// Special imap detail input.
var details = new SpecialImapProviderDetails(SpecialImapAddress.Text.Trim(), AppSpecificPassword.Password.Trim(), DisplayNameTextBox.Text.Trim(), SelectedMailProvider.SpecialImapProvider);
Result = new AccountCreationDialogResult(SelectedMailProvider.Type, AccountNameTextbox.Text.Trim(), details);
Hide();
return;
}
Validate(); Validate();
if (IsSecondaryButtonEnabled) if (IsSecondaryButtonEnabled)
@@ -85,7 +97,7 @@ namespace Wino.Core.UWP.Dialogs
} }
else else
{ {
Result = new AccountCreationDialogResult(SelectedMailProvider.Type, AccountNameTextbox.Text.Trim()); Result = new AccountCreationDialogResult(SelectedMailProvider.Type, AccountNameTextbox.Text.Trim(), null);
Hide(); Hide();
} }
} }
@@ -106,7 +118,9 @@ namespace Wino.Core.UWP.Dialogs
bool shouldEnable = SelectedMailProvider != null bool shouldEnable = SelectedMailProvider != null
&& SelectedMailProvider.IsSupported && SelectedMailProvider.IsSupported
&& !string.IsNullOrEmpty(AccountNameTextbox.Text) && !string.IsNullOrEmpty(AccountNameTextbox.Text)
&& (IsSpecialImapServerPartVisible ? (!string.IsNullOrEmpty(AppSpecificPassword.Password) && EmailValidation.EmailValidator.Validate(SpecialImapAddress.Text)) : true); && (IsSpecialImapServerPartVisible ? (!string.IsNullOrEmpty(AppSpecificPassword.Password)
&& !string.IsNullOrEmpty(DisplayNameTextBox.Text)
&& EmailValidation.EmailValidator.Validate(SpecialImapAddress.Text)) : true);
IsPrimaryButtonEnabled = shouldEnable; IsPrimaryButtonEnabled = shouldEnable;
} }

View File

@@ -132,7 +132,7 @@ namespace Wino.Core.UWP.Services
return file; return file;
} }
public virtual IAccountCreationDialog GetAccountCreationDialog(MailProviderType type) public virtual IAccountCreationDialog GetAccountCreationDialog(AccountCreationDialogResult accountCreationDialogResult)
{ {
return new AccountCreationDialog return new AccountCreationDialog
{ {

View File

@@ -84,6 +84,9 @@
<ItemGroup> <ItemGroup>
<Compile Include="Activation\ActivationHandler.cs" /> <Compile Include="Activation\ActivationHandler.cs" />
<Compile Include="BasePage.cs" /> <Compile Include="BasePage.cs" />
<Compile Include="Controls\AccountCreationDialogControl.xaml.cs">
<DependentUpon>AccountCreationDialogControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ControlConstants.cs" /> <Compile Include="Controls\ControlConstants.cs" />
<Compile Include="Controls\CustomWrapPanel.cs" /> <Compile Include="Controls\CustomWrapPanel.cs" />
<Compile Include="Controls\EqualGridPanel.cs" /> <Compile Include="Controls\EqualGridPanel.cs" />
@@ -115,7 +118,6 @@
<Compile Include="Dialogs\AccountPickerDialog.xaml.cs"> <Compile Include="Dialogs\AccountPickerDialog.xaml.cs">
<DependentUpon>AccountPickerDialog.xaml</DependentUpon> <DependentUpon>AccountPickerDialog.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Dialogs\BaseAccountCreationDialog.cs" />
<Compile Include="Dialogs\CustomMessageDialogInformationContainer.cs" /> <Compile Include="Dialogs\CustomMessageDialogInformationContainer.cs" />
<Compile Include="Dialogs\CustomThemeBuilderDialog.xaml.cs"> <Compile Include="Dialogs\CustomThemeBuilderDialog.xaml.cs">
<DependentUpon>CustomThemeBuilderDialog.xaml</DependentUpon> <DependentUpon>CustomThemeBuilderDialog.xaml</DependentUpon>
@@ -341,6 +343,10 @@
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Page Include="Controls\AccountCreationDialogControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\WinoAppTitleBar.xaml"> <Page Include="Controls\WinoAppTitleBar.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>

View File

@@ -626,13 +626,21 @@ namespace Wino.Core.Synchronizers.Mail
IImapClient availableClient = null; IImapClient availableClient = null;
retry:
try try
{ {
availableClient = await _clientPool.GetClientAsync().ConfigureAwait(false); availableClient = await _clientPool.GetClientAsync().ConfigureAwait(false);
var strategy = _imapSynchronizationStrategyProvider.GetSynchronizationStrategy(availableClient); var strategy = _imapSynchronizationStrategyProvider.GetSynchronizationStrategy(availableClient);
return await strategy.HandleSynchronizationAsync(availableClient, folder, this, cancellationToken).ConfigureAwait(false); return await strategy.HandleSynchronizationAsync(availableClient, folder, this, cancellationToken).ConfigureAwait(false);
} }
catch (IOException)
{
_clientPool.Release(availableClient, false);
goto retry;
}
catch (Exception) catch (Exception)
{ {

View File

@@ -28,18 +28,25 @@ namespace Wino.Mail.ViewModels
{ {
public partial class AccountManagementViewModel : AccountManagementPageViewModelBase public partial class AccountManagementViewModel : AccountManagementPageViewModelBase
{ {
private readonly ISpecialImapProviderConfigResolver _specialImapProviderConfigResolver;
private readonly IImapTestService _imapTestService;
public IMailDialogService MailDialogService { get; } public IMailDialogService MailDialogService { get; }
public AccountManagementViewModel(IMailDialogService dialogService, public AccountManagementViewModel(IMailDialogService dialogService,
IWinoServerConnectionManager winoServerConnectionManager, IWinoServerConnectionManager winoServerConnectionManager,
INavigationService navigationService, INavigationService navigationService,
IAccountService accountService, IAccountService accountService,
ISpecialImapProviderConfigResolver specialImapProviderConfigResolver,
IProviderService providerService, IProviderService providerService,
IImapTestService imapTestService,
IStoreManagementService storeManagementService, IStoreManagementService storeManagementService,
IAuthenticationProvider authenticationProvider, IAuthenticationProvider authenticationProvider,
IPreferencesService preferencesService) : base(dialogService, winoServerConnectionManager, navigationService, accountService, providerService, storeManagementService, authenticationProvider, preferencesService) IPreferencesService preferencesService) : base(dialogService, winoServerConnectionManager, navigationService, accountService, providerService, storeManagementService, authenticationProvider, preferencesService)
{ {
MailDialogService = dialogService; MailDialogService = dialogService;
_specialImapProviderConfigResolver = specialImapProviderConfigResolver;
_imapTestService = imapTestService;
} }
[RelayCommand] [RelayCommand]
@@ -93,7 +100,7 @@ namespace Wino.Mail.ViewModels
if (accountCreationDialogResult != null) if (accountCreationDialogResult != null)
{ {
creationDialog = MailDialogService.GetAccountCreationDialog(accountCreationDialogResult.ProviderType); creationDialog = MailDialogService.GetAccountCreationDialog(accountCreationDialogResult);
CustomServerInformation customServerInformation = null; CustomServerInformation customServerInformation = null;
@@ -101,17 +108,20 @@ namespace Wino.Mail.ViewModels
{ {
ProviderType = accountCreationDialogResult.ProviderType, ProviderType = accountCreationDialogResult.ProviderType,
Name = accountCreationDialogResult.AccountName, Name = accountCreationDialogResult.AccountName,
AccountColorHex = accountCreationDialogResult.AccountColorHex, SpecialImapProvider = accountCreationDialogResult.SpecialImapProviderDetails?.SpecialImapProvider ?? SpecialImapProvider.None,
Id = Guid.NewGuid() Id = Guid.NewGuid()
}; };
creationDialog.ShowDialog(accountCreationCancellationTokenSource); creationDialog.ShowDialog(accountCreationCancellationTokenSource);
await Task.Delay(1000);
creationDialog.State = AccountCreationDialogState.SigningIn; creationDialog.State = AccountCreationDialogState.SigningIn;
string tokenInformation = string.Empty; string tokenInformation = string.Empty;
// Custom server implementation requires more async waiting. // Custom server implementation requires more async waiting.
if (creationDialog is ICustomServerAccountCreationDialog customServerDialog) if (creationDialog is IImapAccountCreationDialog customServerDialog)
{ {
// Pass along the account properties and perform initial navigation on the imap frame. // Pass along the account properties and perform initial navigation on the imap frame.
customServerDialog.StartImapConnectionSetup(createdAccount); customServerDialog.StartImapConnectionSetup(createdAccount);
@@ -130,20 +140,35 @@ namespace Wino.Mail.ViewModels
} }
else else
{ {
// OAuth authentication is handled here. if (accountCreationDialogResult.SpecialImapProviderDetails != null)
// Server authenticates, returns the token info here. {
createdAccount.SenderName = accountCreationDialogResult.SpecialImapProviderDetails.SenderName;
createdAccount.Address = customServerInformation.Address;
var tokenInformationResponse = await WinoServerConnectionManager // Special imap provider testing dialog. This is only available for iCloud and Yahoo.
.GetResponseAsync<TokenInformationEx, AuthorizationRequested>(new AuthorizationRequested(accountCreationDialogResult.ProviderType, customServerInformation = _specialImapProviderConfigResolver.GetServerInformation(createdAccount, accountCreationDialogResult);
createdAccount, customServerInformation.Id = Guid.NewGuid();
createdAccount.ProviderType == MailProviderType.Gmail), accountCreationCancellationTokenSource.Token); customServerInformation.AccountId = createdAccount.Id;
if (creationDialog.State == AccountCreationDialogState.Canceled) await _imapTestService.TestImapConnectionAsync(customServerInformation, true);
throw new AccountSetupCanceledException(); }
else
{
// OAuth authentication is handled here.
// Server authenticates, returns the token info here.
createdAccount.Address = tokenInformationResponse.Data.AccountAddress; var tokenInformationResponse = await WinoServerConnectionManager
.GetResponseAsync<TokenInformationEx, AuthorizationRequested>(new AuthorizationRequested(accountCreationDialogResult.ProviderType,
createdAccount,
createdAccount.ProviderType == MailProviderType.Gmail), accountCreationCancellationTokenSource.Token);
tokenInformationResponse.ThrowIfFailed(); if (creationDialog.State == AccountCreationDialogState.Canceled)
throw new AccountSetupCanceledException();
createdAccount.Address = tokenInformationResponse.Data.AccountAddress;
tokenInformationResponse.ThrowIfFailed();
}
} }
// Address is still doesn't have a value for API synchronizers. // Address is still doesn't have a value for API synchronizers.
@@ -183,7 +208,7 @@ namespace Wino.Mail.ViewModels
await AccountService.UpdateProfileInformationAsync(createdAccount.Id, profileSynchronizationResult.ProfileInformation); await AccountService.UpdateProfileInformationAsync(createdAccount.Id, profileSynchronizationResult.ProfileInformation);
} }
if (creationDialog is ICustomServerAccountCreationDialog customServerAccountCreationDialog) if (creationDialog is IImapAccountCreationDialog customServerAccountCreationDialog)
customServerAccountCreationDialog.ShowPreparingFolders(); customServerAccountCreationDialog.ShowPreparingFolders();
else else
creationDialog.State = AccountCreationDialogState.PreparingFolders; creationDialog.State = AccountCreationDialogState.PreparingFolders;
@@ -227,14 +252,6 @@ namespace Wino.Mail.ViewModels
await AccountService.CreateRootAliasAsync(createdAccount.Id, createdAccount.Address); await AccountService.CreateRootAliasAsync(createdAccount.Id, createdAccount.Address);
} }
// TODO: Temporary disabled. Is this even needed? Users can configure special folders manually later on if discovery fails.
// Check if Inbox folder is available for the account after synchronization.
//var isInboxAvailable = await _folderService.IsInboxAvailableForAccountAsync(createdAccount.Id);
//if (!isInboxAvailable)
// throw new Exception(Translator.Exception_InboxNotAvailable);
// Send changes to listeners. // Send changes to listeners.
ReportUIChange(new AccountCreatedMessage(createdAccount)); ReportUIChange(new AccountCreatedMessage(createdAccount));
@@ -250,6 +267,10 @@ namespace Wino.Mail.ViewModels
{ {
// Ignore // Ignore
} }
catch (ImapClientPoolException clientPoolException)
{
DialogService.InfoBarMessage(Translator.Info_AccountCreationFailedTitle, clientPoolException.InnerException.Message, InfoBarMessageType.Error);
}
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, WinoErrors.AccountCreation); Log.Error(ex, WinoErrors.AccountCreation);

View File

@@ -7,6 +7,7 @@ using Windows.UI.Xaml.Media.Animation;
using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Accounts;
using Wino.Messaging.Client.Mails; using Wino.Messaging.Client.Mails;
using Wino.Views.ImapSetup; using Wino.Views.ImapSetup;
@@ -24,7 +25,7 @@ namespace Wino.Dialogs
IRecipient<ImapSetupNavigationRequested>, IRecipient<ImapSetupNavigationRequested>,
IRecipient<ImapSetupBackNavigationRequested>, IRecipient<ImapSetupBackNavigationRequested>,
IRecipient<ImapSetupDismissRequested>, IRecipient<ImapSetupDismissRequested>,
ICustomServerAccountCreationDialog IImapAccountCreationDialog
{ {
private TaskCompletionSource<CustomServerInformation> _getServerInfoTaskCompletionSource = new TaskCompletionSource<CustomServerInformation>(); private TaskCompletionSource<CustomServerInformation> _getServerInfoTaskCompletionSource = new TaskCompletionSource<CustomServerInformation>();
@@ -86,7 +87,7 @@ namespace Wino.Dialogs
} }
public void StartImapConnectionSetup(MailAccount account) => ImapFrame.Navigate(typeof(WelcomeImapSetupPage), account, new DrillInNavigationTransitionInfo()); public void StartImapConnectionSetup(MailAccount account) => ImapFrame.Navigate(typeof(WelcomeImapSetupPage), account, new DrillInNavigationTransitionInfo());
public void StartImapConnectionSetup(AccountCreationDialogResult accountCreationDialogResult) => ImapFrame.Navigate(typeof(WelcomeImapSetupPage), accountCreationDialogResult, new DrillInNavigationTransitionInfo());
private void ImapSetupDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) => WeakReferenceMessenger.Default.UnregisterAll(this); private void ImapSetupDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) => WeakReferenceMessenger.Default.UnregisterAll(this);

View File

@@ -10,6 +10,7 @@ using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Accounts;
using Wino.Core.Domain.Models.Folders; using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.Synchronization; using Wino.Core.Domain.Models.Synchronization;
using Wino.Core.UWP.Extensions; using Wino.Core.UWP.Extensions;
@@ -29,18 +30,27 @@ namespace Wino.Services
} }
public override IAccountCreationDialog GetAccountCreationDialog(MailProviderType type) public override IAccountCreationDialog GetAccountCreationDialog(AccountCreationDialogResult accountCreationDialogResult)
{ {
if (type == MailProviderType.IMAP4) if (accountCreationDialogResult.SpecialImapProviderDetails == null)
{ {
return new NewImapSetupDialog if (accountCreationDialogResult.ProviderType == MailProviderType.IMAP4)
{ {
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme() return new NewImapSetupDialog
}; {
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
};
}
else
{
return base.GetAccountCreationDialog(accountCreationDialogResult);
}
} }
else else
{ {
return base.GetAccountCreationDialog(type); // Special IMAP provider like iCloud or Yahoo.
return base.GetAccountCreationDialog(accountCreationDialogResult);
} }
} }

View File

@@ -9,6 +9,7 @@ using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Entities.Shared;
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.Accounts;
using Wino.Core.Domain.Models.AutoDiscovery; using Wino.Core.Domain.Models.AutoDiscovery;
using Wino.Messaging.Client.Mails; using Wino.Messaging.Client.Mails;
@@ -35,6 +36,10 @@ namespace Wino.Views.ImapSetup
{ {
DisplayNameBox.Text = accountProperties.Name; DisplayNameBox.Text = accountProperties.Name;
} }
else if (e.Parameter is AccountCreationDialogResult creationDialogResult)
{
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(TestingImapConnectionPage), creationDialogResult));
}
} }
private async void SignInClicked(object sender, RoutedEventArgs e) private async void SignInClicked(object sender, RoutedEventArgs e)

View File

@@ -23,6 +23,7 @@ namespace Wino.Services
services.AddTransient<IContactService, ContactService>(); services.AddTransient<IContactService, ContactService>();
services.AddTransient<ISignatureService, SignatureService>(); services.AddTransient<ISignatureService, SignatureService>();
services.AddTransient<IContextMenuItemService, ContextMenuItemService>(); services.AddTransient<IContextMenuItemService, ContextMenuItemService>();
services.AddTransient<ISpecialImapProviderConfigResolver, SpecialImapProviderConfigResolver>();
services.AddSingleton<IThreadingStrategyProvider, ThreadingStrategyProvider>(); services.AddSingleton<IThreadingStrategyProvider, ThreadingStrategyProvider>();
services.AddTransient<IOutlookThreadingStrategy, OutlookThreadingStrategy>(); services.AddTransient<IOutlookThreadingStrategy, OutlookThreadingStrategy>();

View File

@@ -0,0 +1,46 @@
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Accounts;
namespace Wino.Services
{
public class SpecialImapProviderConfigResolver : ISpecialImapProviderConfigResolver
{
private readonly CustomServerInformation iCloudServerConfig = new CustomServerInformation()
{
IncomingServer = "imap.mail.me.com",
IncomingServerPort = "993",
IncomingServerType = CustomIncomingServerType.IMAP4,
IncomingServerSocketOption = ImapConnectionSecurity.Auto,
IncomingAuthenticationMethod = ImapAuthenticationMethod.Auto,
OutgoingServer = "smtp.mail.me.com",
OutgoingServerPort = "587",
OutgoingServerSocketOption = ImapConnectionSecurity.Auto,
OutgoingAuthenticationMethod = ImapAuthenticationMethod.Auto,
MaxConcurrentClients = 5,
};
public CustomServerInformation GetServerInformation(MailAccount account, AccountCreationDialogResult dialogResult)
{
CustomServerInformation resolvedConfig = null;
if (dialogResult.SpecialImapProviderDetails.SpecialImapProvider == SpecialImapProvider.iCloud)
{
resolvedConfig = iCloudServerConfig;
// iCloud takes username before the @icloud part for incoming, but full address as outgoing.
resolvedConfig.IncomingServerUsername = dialogResult.SpecialImapProviderDetails.Address.Split('@')[0];
resolvedConfig.OutgoingServerUsername = dialogResult.SpecialImapProviderDetails.Address;
}
// Fill in account details.
resolvedConfig.Address = dialogResult.SpecialImapProviderDetails.Address;
resolvedConfig.IncomingServerPassword = dialogResult.SpecialImapProviderDetails.Password;
resolvedConfig.OutgoingServerPassword = dialogResult.SpecialImapProviderDetails.Password;
resolvedConfig.DisplayName = dialogResult.SpecialImapProviderDetails.SenderName;
return resolvedConfig;
}
}
}