File scoped namespaces

This commit is contained in:
Aleh Khantsevich
2025-02-16 11:35:43 +01:00
committed by GitHub
parent c1336428dc
commit d31d8f574e
617 changed files with 32118 additions and 32737 deletions

View File

@@ -1,12 +1,11 @@
using Wino.Views.Abstract;
namespace Wino.Views.Settings
namespace Wino.Views.Settings;
public sealed partial class AboutPage : AboutPageAbstract
{
public sealed partial class AboutPage : AboutPageAbstract
public AboutPage()
{
public AboutPage()
{
InitializeComponent();
}
InitializeComponent();
}
}

View File

@@ -1,9 +1,8 @@
using Wino.Core.UWP;
using Wino.Core.ViewModels;
namespace Wino.Views.Abstract
namespace Wino.Views.Abstract;
public abstract class AboutPageAbstract : BasePage<AboutPageViewModel>
{
public abstract class AboutPageAbstract : BasePage<AboutPageViewModel>
{
}
}

View File

@@ -1,9 +1,8 @@
using Wino.Core.UWP;
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
namespace Wino.Views.Abstract;
public abstract class AccountDetailsPageAbstract : BasePage<AccountDetailsPageViewModel>
{
public abstract class AccountDetailsPageAbstract : BasePage<AccountDetailsPageViewModel>
{
}
}

View File

@@ -1,10 +1,9 @@
using Wino.Core.UWP;
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
{
public abstract class AccountManagementPageAbstract : BasePage<AccountManagementViewModel>
{
namespace Wino.Views.Abstract;
public abstract class AccountManagementPageAbstract : BasePage<AccountManagementViewModel>
{
}
}

View File

@@ -1,7 +1,6 @@
using Wino.Core.UWP;
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
{
public abstract class AliasManagementPageAbstract : BasePage<AliasManagementPageViewModel> { }
}
namespace Wino.Views.Abstract;
public abstract class AliasManagementPageAbstract : BasePage<AliasManagementPageViewModel> { }

View File

@@ -1,7 +1,6 @@
using Wino.Core.UWP;
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
{
public abstract class AppPreferencesPageAbstract : BasePage<AppPreferencesPageViewModel> { }
}
namespace Wino.Views.Abstract;
public abstract class AppPreferencesPageAbstract : BasePage<AppPreferencesPageViewModel> { }

View File

@@ -1,9 +1,8 @@
using Wino.Core.UWP;
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
namespace Wino.Views.Abstract;
public abstract class AppShellAbstract : BasePage<AppShellViewModel>
{
public abstract class AppShellAbstract : BasePage<AppShellViewModel>
{
}
}

View File

@@ -1,9 +1,8 @@
using Wino.Core.UWP;
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
namespace Wino.Views.Abstract;
public abstract class ComposePageAbstract : BasePage<ComposePageViewModel>
{
public abstract class ComposePageAbstract : BasePage<ComposePageViewModel>
{
}
}

View File

@@ -1,9 +1,8 @@
using Wino.Core.UWP;
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
namespace Wino.Views.Abstract;
public abstract class IdlePageAbstract : BasePage<IdlePageViewModel>
{
public abstract class IdlePageAbstract : BasePage<IdlePageViewModel>
{
}
}

View File

@@ -1,7 +1,6 @@
using Wino.Core.UWP;
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
{
public abstract class LanguageTimePageAbstract : BasePage<LanguageTimePageViewModel> { }
}
namespace Wino.Views.Abstract;
public abstract class LanguageTimePageAbstract : BasePage<LanguageTimePageViewModel> { }

View File

@@ -2,26 +2,25 @@
using Wino.Core.UWP;
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
namespace Wino.Views.Abstract;
public abstract class MailRenderingPageAbstract : BasePage<MailRenderingPageViewModel>
{
public abstract class MailRenderingPageAbstract : BasePage<MailRenderingPageViewModel>
public bool IsDarkEditor
{
public bool IsDarkEditor
{
get { return (bool)GetValue(IsDarkEditorProperty); }
set { SetValue(IsDarkEditorProperty, value); }
}
public static readonly DependencyProperty IsDarkEditorProperty = DependencyProperty.Register(nameof(IsDarkEditor), typeof(bool), typeof(MailRenderingPageAbstract), new PropertyMetadata(false, OnIsComposerDarkModeChanged));
private static void OnIsComposerDarkModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is MailRenderingPageAbstract page)
{
page.OnEditorThemeChanged();
}
}
public virtual void OnEditorThemeChanged() { }
get { return (bool)GetValue(IsDarkEditorProperty); }
set { SetValue(IsDarkEditorProperty, value); }
}
public static readonly DependencyProperty IsDarkEditorProperty = DependencyProperty.Register(nameof(IsDarkEditor), typeof(bool), typeof(MailRenderingPageAbstract), new PropertyMetadata(false, OnIsComposerDarkModeChanged));
private static void OnIsComposerDarkModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is MailRenderingPageAbstract page)
{
page.OnEditorThemeChanged();
}
}
public virtual void OnEditorThemeChanged() { }
}

View File

@@ -1,9 +1,8 @@
using Wino.Core.UWP;
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
namespace Wino.Views.Abstract;
public abstract class MergedAccountDetailsPageAbstract : BasePage<MergedAccountDetailsPageViewModel>
{
public abstract class MergedAccountDetailsPageAbstract : BasePage<MergedAccountDetailsPageViewModel>
{
}
}

View File

@@ -1,7 +1,6 @@
using Wino.Core.UWP;
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
{
public abstract class MessageListPageAbstract : BasePage<MessageListPageViewModel> { }
}
namespace Wino.Views.Abstract;
public abstract class MessageListPageAbstract : BasePage<MessageListPageViewModel> { }

View File

@@ -1,9 +1,8 @@
using Wino.Core.ViewModels;
namespace Wino.Views.Abstract
{
public abstract class PersonalizationPageAbstract : SettingsPageBase<PersonalizationPageViewModel>
{
namespace Wino.Views.Abstract;
public abstract class PersonalizationPageAbstract : SettingsPageBase<PersonalizationPageViewModel>
{
}
}

View File

@@ -1,7 +1,6 @@
using Wino.Core.UWP;
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
{
public abstract class ReadComposePanePageAbstract : BasePage<ReadComposePanePageViewModel> { }
}
namespace Wino.Views.Abstract;
public abstract class ReadComposePanePageAbstract : BasePage<ReadComposePanePageViewModel> { }

View File

@@ -1,7 +1,6 @@
using Wino.Core.UWP;
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
{
public abstract class SignatureManagementPageAbstract : BasePage<SignatureManagementPageViewModel> { }
}
namespace Wino.Views.Abstract;
public abstract class SignatureManagementPageAbstract : BasePage<SignatureManagementPageViewModel> { }

View File

@@ -1,10 +1,9 @@
using Wino.Core.UWP;
using Wino.Mail.ViewModels;
namespace Wino.Views.Abstract
{
public abstract class WelcomePageAbstract : BasePage<WelcomePageViewModel>
{
namespace Wino.Views.Abstract;
public abstract class WelcomePageAbstract : BasePage<WelcomePageViewModel>
{
}
}

View File

@@ -2,29 +2,28 @@
using Wino.Core.Domain.Models.Folders;
using Wino.Views.Abstract;
namespace Wino.Views
namespace Wino.Views;
public sealed partial class AccountDetailsPage : AccountDetailsPageAbstract
{
public sealed partial class AccountDetailsPage : AccountDetailsPageAbstract
public AccountDetailsPage()
{
public AccountDetailsPage()
{
InitializeComponent();
}
InitializeComponent();
}
private async void SyncFolderToggled(object sender, Windows.UI.Xaml.RoutedEventArgs e)
private async void SyncFolderToggled(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
if (sender is CheckBox checkBox && checkBox.Tag is IMailItemFolder folder)
{
if (sender is CheckBox checkBox && checkBox.Tag is IMailItemFolder folder)
{
await ViewModel.FolderSyncToggledAsync(folder, checkBox.IsChecked.GetValueOrDefault());
}
await ViewModel.FolderSyncToggledAsync(folder, checkBox.IsChecked.GetValueOrDefault());
}
}
private async void UnreadBadgeCheckboxToggled(object sender, Windows.UI.Xaml.RoutedEventArgs e)
private async void UnreadBadgeCheckboxToggled(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
if (sender is CheckBox checkBox && checkBox.Tag is IMailItemFolder folder)
{
if (sender is CheckBox checkBox && checkBox.Tag is IMailItemFolder folder)
{
await ViewModel.FolderShowUnreadToggled(folder, checkBox.IsChecked.GetValueOrDefault());
}
await ViewModel.FolderShowUnreadToggled(folder, checkBox.IsChecked.GetValueOrDefault());
}
}
}

View File

@@ -2,15 +2,14 @@
using Windows.UI.Xaml.Navigation;
using Wino.Views.Abstract;
namespace Wino.Views
{
public sealed partial class AccountManagementPage : AccountManagementPageAbstract
{
public AccountManagementPage()
{
InitializeComponent();
namespace Wino.Views;
NavigationCacheMode = NavigationCacheMode.Enabled;
}
public sealed partial class AccountManagementPage : AccountManagementPageAbstract
{
public AccountManagementPage()
{
InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Enabled;
}
}

View File

@@ -1,13 +1,12 @@
using Wino.Views.Abstract;
namespace Wino.Views.Account
namespace Wino.Views.Account;
public sealed partial class MergedAccountDetailsPage : MergedAccountDetailsPageAbstract
{
public sealed partial class MergedAccountDetailsPage : MergedAccountDetailsPageAbstract
public MergedAccountDetailsPage()
{
public MergedAccountDetailsPage()
{
InitializeComponent();
}
InitializeComponent();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,11 @@
using Wino.Views.Abstract;
namespace Wino.Views
namespace Wino.Views;
public sealed partial class IdlePage : IdlePageAbstract
{
public sealed partial class IdlePage : IdlePageAbstract
public IdlePage()
{
public IdlePage()
{
InitializeComponent();
}
InitializeComponent();
}
}

View File

@@ -11,205 +11,204 @@ using Wino.Core.Domain.Models.AutoDiscovery;
using Wino.Messaging.Client.Mails;
namespace Wino.Views.ImapSetup
namespace Wino.Views.ImapSetup;
public sealed partial class AdvancedImapSetupPage : Page
{
public sealed partial class AdvancedImapSetupPage : Page
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.None, Translator.ImapAuthenticationMethod_None),
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.NormalPassword, Translator.ImapAuthenticationMethod_Plain),
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.EncryptedPassword, Translator.ImapAuthenticationMethod_EncryptedPassword),
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.Ntlm, Translator.ImapAuthenticationMethod_Ntlm),
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.CramMd5, Translator.ImapAuthenticationMethod_CramMD5),
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.DigestMd5, Translator.ImapAuthenticationMethod_DigestMD5)
};
public List<ImapConnectionSecurityModel> AvailableConnectionSecurities { get; set; } = new List<ImapConnectionSecurityModel>()
{
new ImapConnectionSecurityModel(Core.Domain.Enums.ImapConnectionSecurity.Auto, Translator.ImapConnectionSecurity_Auto),
new ImapConnectionSecurityModel(Core.Domain.Enums.ImapConnectionSecurity.SslTls, Translator.ImapConnectionSecurity_SslTls),
new ImapConnectionSecurityModel(Core.Domain.Enums.ImapConnectionSecurity.StartTls, Translator.ImapConnectionSecurity_StartTls),
new ImapConnectionSecurityModel(Core.Domain.Enums.ImapConnectionSecurity.None, Translator.ImapConnectionSecurity_None)
};
public bool UseSameCredentialsForSending
{
get { return (bool)GetValue(UseSameCredentialsForSendingProperty); }
set { SetValue(UseSameCredentialsForSendingProperty, value); }
}
public static readonly DependencyProperty UseSameCredentialsForSendingProperty = DependencyProperty.Register(nameof(UseSameCredentialsForSending), typeof(bool), typeof(AdvancedImapSetupPage), new PropertyMetadata(true, OnUseSameCredentialsForSendingChanged));
public AdvancedImapSetupPage()
{
InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Enabled;
}
private static void OnUseSameCredentialsForSendingChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is AdvancedImapSetupPage page)
{
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.Auto, Translator.ImapAuthenticationMethod_Auto),
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.None, Translator.ImapAuthenticationMethod_None),
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.NormalPassword, Translator.ImapAuthenticationMethod_Plain),
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.EncryptedPassword, Translator.ImapAuthenticationMethod_EncryptedPassword),
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.Ntlm, Translator.ImapAuthenticationMethod_Ntlm),
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.CramMd5, Translator.ImapAuthenticationMethod_CramMD5),
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.DigestMd5, Translator.ImapAuthenticationMethod_DigestMD5)
page.UpdateOutgoingAuthenticationPanel();
}
}
private void UpdateOutgoingAuthenticationPanel()
{
if (UseSameCredentialsForSending)
{
OutgoingUsernameBox.Text = UsernameBox.Text;
OutgoingPasswordBox.Password = PasswordBox.Password;
}
else
{
OutgoingUsernameBox.Text = string.Empty;
OutgoingPasswordBox.Password = string.Empty;
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// Don't override settings on back scenarios.
// User is trying to try again the same configuration.
if (e.NavigationMode == NavigationMode.Back) return;
// Connection is succesfull but error occurred.
// Imap and Smptp settings exists here at this point.
if (e.Parameter is AutoDiscoverySettings preDefinedSettings && preDefinedSettings.UserMinimalSettings != null)
{
// TODO: Auto discovery settings adjustments.
UsernameBox.Text = preDefinedSettings.UserMinimalSettings.Email;
AddressBox.Text = preDefinedSettings.UserMinimalSettings.Email;
DisplayNameBox.Text = preDefinedSettings.UserMinimalSettings.DisplayName;
PasswordBox.Password = preDefinedSettings.UserMinimalSettings.Password;
var serverInfo = preDefinedSettings.ToServerInformation();
IncomingServerBox.Text = serverInfo.IncomingServer;
IncomingServerPortBox.Text = serverInfo.IncomingServerPort;
OutgoingPasswordBox.Password = serverInfo.OutgoingServerPassword;
OutgoingServerPort.Text = serverInfo.OutgoingServerPort;
OutgoingUsernameBox.Text = serverInfo.OutgoingServerUsername;
UseSameCredentialsForSending = OutgoingUsernameBox.Text == UsernameBox.Text;
}
else if (e.Parameter is AutoDiscoveryMinimalSettings autoDiscoveryMinimalSettings)
{
// Auto discovery failed. Only minimal settings are passed.
UsernameBox.Text = autoDiscoveryMinimalSettings.Email;
AddressBox.Text = autoDiscoveryMinimalSettings.Email;
DisplayNameBox.Text = autoDiscoveryMinimalSettings.DisplayName;
PasswordBox.Password = autoDiscoveryMinimalSettings.Password;
}
}
private void CancelClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested(null));
private string GetServerWithoutPort(string server)
{
var splitted = server.Split(':');
if (splitted.Length > 1)
{
return splitted[0];
}
return server;
}
private void SignInClicked(object sender, RoutedEventArgs e)
{
var info = new CustomServerInformation()
{
IncomingServer = GetServerWithoutPort(IncomingServerBox.Text),
Id = Guid.NewGuid(),
IncomingServerPassword = PasswordBox.Password,
IncomingServerType = Core.Domain.Enums.CustomIncomingServerType.IMAP4,
IncomingServerUsername = UsernameBox.Text,
IncomingAuthenticationMethod = (IncomingAuthenticationMethod.SelectedItem as ImapAuthenticationMethodModel).ImapAuthenticationMethod,
IncomingServerSocketOption = (IncomingConnectionSecurity.SelectedItem as ImapConnectionSecurityModel).ImapConnectionSecurity,
IncomingServerPort = IncomingServerPortBox.Text,
OutgoingServer = GetServerWithoutPort(OutgoingServerBox.Text),
OutgoingServerPort = OutgoingServerPort.Text,
OutgoingServerPassword = OutgoingPasswordBox.Password,
OutgoingAuthenticationMethod = (OutgoingAuthenticationMethod.SelectedItem as ImapAuthenticationMethodModel).ImapAuthenticationMethod,
OutgoingServerSocketOption = (OutgoingConnectionSecurity.SelectedItem as ImapConnectionSecurityModel).ImapConnectionSecurity,
OutgoingServerUsername = OutgoingUsernameBox.Text,
ProxyServer = ProxyServerBox.Text,
ProxyServerPort = ProxyServerPortBox.Text,
Address = AddressBox.Text,
DisplayName = DisplayNameBox.Text,
MaxConcurrentClients = 5
};
public List<ImapConnectionSecurityModel> AvailableConnectionSecurities { get; set; } = new List<ImapConnectionSecurityModel>()
if (UseSameCredentialsForSending)
{
new ImapConnectionSecurityModel(Core.Domain.Enums.ImapConnectionSecurity.Auto, Translator.ImapConnectionSecurity_Auto),
new ImapConnectionSecurityModel(Core.Domain.Enums.ImapConnectionSecurity.SslTls, Translator.ImapConnectionSecurity_SslTls),
new ImapConnectionSecurityModel(Core.Domain.Enums.ImapConnectionSecurity.StartTls, Translator.ImapConnectionSecurity_StartTls),
new ImapConnectionSecurityModel(Core.Domain.Enums.ImapConnectionSecurity.None, Translator.ImapConnectionSecurity_None)
};
public bool UseSameCredentialsForSending
info.OutgoingServerUsername = info.IncomingServerUsername;
info.OutgoingServerPassword = info.IncomingServerPassword;
}
else
{
get { return (bool)GetValue(UseSameCredentialsForSendingProperty); }
set { SetValue(UseSameCredentialsForSendingProperty, value); }
info.OutgoingServerUsername = OutgoingUsernameBox.Text;
info.OutgoingServerPassword = OutgoingPasswordBox.Password;
}
public static readonly DependencyProperty UseSameCredentialsForSendingProperty = DependencyProperty.Register(nameof(UseSameCredentialsForSending), typeof(bool), typeof(AdvancedImapSetupPage), new PropertyMetadata(true, OnUseSameCredentialsForSendingChanged));
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(TestingImapConnectionPage), info));
}
public AdvancedImapSetupPage()
private void IncomingServerChanged(object sender, TextChangedEventArgs e)
{
if (sender is TextBox senderTextBox)
{
InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Enabled;
}
private static void OnUseSameCredentialsForSendingChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is AdvancedImapSetupPage page)
{
page.UpdateOutgoingAuthenticationPanel();
}
}
private void UpdateOutgoingAuthenticationPanel()
{
if (UseSameCredentialsForSending)
{
OutgoingUsernameBox.Text = UsernameBox.Text;
OutgoingPasswordBox.Password = PasswordBox.Password;
}
else
{
OutgoingUsernameBox.Text = string.Empty;
OutgoingPasswordBox.Password = string.Empty;
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// Don't override settings on back scenarios.
// User is trying to try again the same configuration.
if (e.NavigationMode == NavigationMode.Back) return;
// Connection is succesfull but error occurred.
// Imap and Smptp settings exists here at this point.
if (e.Parameter is AutoDiscoverySettings preDefinedSettings && preDefinedSettings.UserMinimalSettings != null)
{
// TODO: Auto discovery settings adjustments.
UsernameBox.Text = preDefinedSettings.UserMinimalSettings.Email;
AddressBox.Text = preDefinedSettings.UserMinimalSettings.Email;
DisplayNameBox.Text = preDefinedSettings.UserMinimalSettings.DisplayName;
PasswordBox.Password = preDefinedSettings.UserMinimalSettings.Password;
var serverInfo = preDefinedSettings.ToServerInformation();
IncomingServerBox.Text = serverInfo.IncomingServer;
IncomingServerPortBox.Text = serverInfo.IncomingServerPort;
OutgoingPasswordBox.Password = serverInfo.OutgoingServerPassword;
OutgoingServerPort.Text = serverInfo.OutgoingServerPort;
OutgoingUsernameBox.Text = serverInfo.OutgoingServerUsername;
UseSameCredentialsForSending = OutgoingUsernameBox.Text == UsernameBox.Text;
}
else if (e.Parameter is AutoDiscoveryMinimalSettings autoDiscoveryMinimalSettings)
{
// Auto discovery failed. Only minimal settings are passed.
UsernameBox.Text = autoDiscoveryMinimalSettings.Email;
AddressBox.Text = autoDiscoveryMinimalSettings.Email;
DisplayNameBox.Text = autoDiscoveryMinimalSettings.DisplayName;
PasswordBox.Password = autoDiscoveryMinimalSettings.Password;
}
}
private void CancelClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested(null));
private string GetServerWithoutPort(string server)
{
var splitted = server.Split(':');
var splitted = senderTextBox.Text.Split(':');
if (splitted.Length > 1)
{
return splitted[0];
}
return server;
}
private void SignInClicked(object sender, RoutedEventArgs e)
{
var info = new CustomServerInformation()
{
IncomingServer = GetServerWithoutPort(IncomingServerBox.Text),
Id = Guid.NewGuid(),
IncomingServerPassword = PasswordBox.Password,
IncomingServerType = Core.Domain.Enums.CustomIncomingServerType.IMAP4,
IncomingServerUsername = UsernameBox.Text,
IncomingAuthenticationMethod = (IncomingAuthenticationMethod.SelectedItem as ImapAuthenticationMethodModel).ImapAuthenticationMethod,
IncomingServerSocketOption = (IncomingConnectionSecurity.SelectedItem as ImapConnectionSecurityModel).ImapConnectionSecurity,
IncomingServerPort = IncomingServerPortBox.Text,
OutgoingServer = GetServerWithoutPort(OutgoingServerBox.Text),
OutgoingServerPort = OutgoingServerPort.Text,
OutgoingServerPassword = OutgoingPasswordBox.Password,
OutgoingAuthenticationMethod = (OutgoingAuthenticationMethod.SelectedItem as ImapAuthenticationMethodModel).ImapAuthenticationMethod,
OutgoingServerSocketOption = (OutgoingConnectionSecurity.SelectedItem as ImapConnectionSecurityModel).ImapConnectionSecurity,
OutgoingServerUsername = OutgoingUsernameBox.Text,
ProxyServer = ProxyServerBox.Text,
ProxyServerPort = ProxyServerPortBox.Text,
Address = AddressBox.Text,
DisplayName = DisplayNameBox.Text,
MaxConcurrentClients = 5
};
if (UseSameCredentialsForSending)
{
info.OutgoingServerUsername = info.IncomingServerUsername;
info.OutgoingServerPassword = info.IncomingServerPassword;
}
else
{
info.OutgoingServerUsername = OutgoingUsernameBox.Text;
info.OutgoingServerPassword = OutgoingPasswordBox.Password;
}
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(TestingImapConnectionPage), info));
}
private void IncomingServerChanged(object sender, TextChangedEventArgs e)
{
if (sender is TextBox senderTextBox)
{
var splitted = senderTextBox.Text.Split(':');
if (splitted.Length > 1)
{
IncomingServerPortBox.Text = splitted[splitted.Length - 1];
}
}
}
private void OutgoingServerChanged(object sender, TextChangedEventArgs e)
{
if (sender is TextBox senderTextBox)
{
var splitted = senderTextBox.Text.Split(':');
if (splitted.Length > 1)
{
OutgoingServerPort.Text = splitted[splitted.Length - 1];
}
}
}
private void IncomingUsernameChanged(object sender, TextChangedEventArgs e)
{
if (UseSameCredentialsForSending)
{
OutgoingUsernameBox.Text = UsernameBox.Text;
}
}
private void IncomingPasswordChanged(object sender, RoutedEventArgs e)
{
if (UseSameCredentialsForSending)
{
OutgoingPasswordBox.Password = PasswordBox.Password;
IncomingServerPortBox.Text = splitted[splitted.Length - 1];
}
}
}
private void OutgoingServerChanged(object sender, TextChangedEventArgs e)
{
if (sender is TextBox senderTextBox)
{
var splitted = senderTextBox.Text.Split(':');
if (splitted.Length > 1)
{
OutgoingServerPort.Text = splitted[splitted.Length - 1];
}
}
}
private void IncomingUsernameChanged(object sender, TextChangedEventArgs e)
{
if (UseSameCredentialsForSending)
{
OutgoingUsernameBox.Text = UsernameBox.Text;
}
}
private void IncomingPasswordChanged(object sender, RoutedEventArgs e)
{
if (UseSameCredentialsForSending)
{
OutgoingPasswordBox.Password = PasswordBox.Password;
}
}
}

View File

@@ -8,42 +8,41 @@ using Wino.Core.Domain.Exceptions;
using Wino.Core.Domain.Interfaces;
using Wino.Messaging.Client.Mails;
namespace Wino.Views.ImapSetup
namespace Wino.Views.ImapSetup;
public sealed partial class ImapConnectionFailedPage : Page
{
public sealed partial class ImapConnectionFailedPage : Page
private string _protocolLog;
private readonly IClipboardService _clipboardService = App.Current.Services.GetService<IClipboardService>();
private readonly IMailDialogService _dialogService = App.Current.Services.GetService<IMailDialogService>();
public ImapConnectionFailedPage()
{
private string _protocolLog;
private readonly IClipboardService _clipboardService = App.Current.Services.GetService<IClipboardService>();
private readonly IMailDialogService _dialogService = App.Current.Services.GetService<IMailDialogService>();
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.ErrorMessage;
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());
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.ErrorMessage;
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

@@ -15,16 +15,15 @@ using Windows.UI.Xaml.Navigation;
// 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 PreparingImapFoldersPage : Page
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class PreparingImapFoldersPage : Page
public PreparingImapFoldersPage()
{
public PreparingImapFoldersPage()
{
this.InitializeComponent();
}
this.InitializeComponent();
}
}

View File

@@ -14,120 +14,119 @@ using Wino.Messaging.Client.Mails;
using Wino.Messaging.Server;
namespace Wino.Views.ImapSetup
namespace Wino.Views.ImapSetup;
public sealed partial class TestingImapConnectionPage : Page
{
public sealed partial class TestingImapConnectionPage : Page
private IWinoServerConnectionManager _winoServerConnectionManager = App.Current.Services.GetService<IWinoServerConnectionManager>();
private AutoDiscoverySettings autoDiscoverySettings;
private CustomServerInformation serverInformationToTest;
public TestingImapConnectionPage()
{
private IWinoServerConnectionManager _winoServerConnectionManager = App.Current.Services.GetService<IWinoServerConnectionManager>();
private AutoDiscoverySettings autoDiscoverySettings;
private CustomServerInformation serverInformationToTest;
InitializeComponent();
}
public TestingImapConnectionPage()
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// We can only go back to this page from failed connection page.
// We must go back once again in that case to actual setup dialog.
if (e.NavigationMode == NavigationMode.Back)
{
InitializeComponent();
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested());
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
else
{
base.OnNavigatedTo(e);
// Test connection
// We can only go back to this page from failed connection page.
// We must go back once again in that case to actual setup dialog.
if (e.NavigationMode == NavigationMode.Back)
// Discovery settings are passed.
// Create server information out of the discovery settings.
if (e.Parameter is AutoDiscoverySettings parameterAutoDiscoverySettings)
{
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested());
autoDiscoverySettings = parameterAutoDiscoverySettings;
serverInformationToTest = autoDiscoverySettings.ToServerInformation();
}
else if (e.Parameter is CustomServerInformation customServerInformation)
{
// Only server information is passed.
serverInformationToTest = customServerInformation;
}
// Make sure that certificate dialog must be present in case of SSL handshake fails.
await PerformTestAsync(allowSSLHandshake: false);
}
}
private async Task PerformTestAsync(bool allowSSLHandshake)
{
CertificateDialog.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
TestingConnectionPanel.Visibility = Windows.UI.Xaml.Visibility.Visible;
await Task.Delay(1000);
var testResultResponse = await _winoServerConnectionManager
.GetResponseAsync<ImapConnectivityTestResults, ImapConnectivityTestRequested>(new ImapConnectivityTestRequested(serverInformationToTest, allowSSLHandshake));
if (!testResultResponse.IsSuccess)
{
// Wino Server is connection is failed.
ReturnWithError(testResultResponse.Message);
}
else
{
var testResultData = testResultResponse.Data;
if (testResultData.IsSuccess)
{
// All success. Finish setup with validated server information.
ReturnWithSuccess();
}
else
{
// Test connection
// Check if certificate UI is required.
// Discovery settings are passed.
// Create server information out of the discovery settings.
if (e.Parameter is AutoDiscoverySettings parameterAutoDiscoverySettings)
if (testResultData.IsCertificateUIRequired)
{
autoDiscoverySettings = parameterAutoDiscoverySettings;
serverInformationToTest = autoDiscoverySettings.ToServerInformation();
}
else if (e.Parameter is CustomServerInformation customServerInformation)
{
// Only server information is passed.
serverInformationToTest = customServerInformation;
}
// Certificate UI is required. Show certificate dialog.
// Make sure that certificate dialog must be present in case of SSL handshake fails.
await PerformTestAsync(allowSSLHandshake: false);
}
}
CertIssuer.Text = testResultData.CertificateIssuer;
CertValidFrom.Text = testResultData.CertificateValidFromDateString;
CertValidTo.Text = testResultData.CertificateExpirationDateString;
private async Task PerformTestAsync(bool allowSSLHandshake)
{
CertificateDialog.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
TestingConnectionPanel.Visibility = Windows.UI.Xaml.Visibility.Visible;
await Task.Delay(1000);
var testResultResponse = await _winoServerConnectionManager
.GetResponseAsync<ImapConnectivityTestResults, ImapConnectivityTestRequested>(new ImapConnectivityTestRequested(serverInformationToTest, allowSSLHandshake));
if (!testResultResponse.IsSuccess)
{
// Wino Server is connection is failed.
ReturnWithError(testResultResponse.Message);
}
else
{
var testResultData = testResultResponse.Data;
if (testResultData.IsSuccess)
{
// All success. Finish setup with validated server information.
ReturnWithSuccess();
TestingConnectionPanel.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
CertificateDialog.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
else
{
// Check if certificate UI is required.
// Connection test failed. Show error dialog.
if (testResultData.IsCertificateUIRequired)
{
// Certificate UI is required. Show certificate dialog.
var protocolLog = testResultData.FailureProtocolLog;
CertIssuer.Text = testResultData.CertificateIssuer;
CertValidFrom.Text = testResultData.CertificateValidFromDateString;
CertValidTo.Text = testResultData.CertificateExpirationDateString;
TestingConnectionPanel.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
CertificateDialog.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
else
{
// Connection test failed. Show error dialog.
var protocolLog = testResultData.FailureProtocolLog;
ReturnWithError(testResultData.FailedReason, protocolLog);
}
ReturnWithError(testResultData.FailedReason, protocolLog);
}
}
}
}
private void ReturnWithError(string error, string protocolLog = "")
{
var failurePackage = new ImapConnectionFailedPackage(error, protocolLog, autoDiscoverySettings);
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(ImapConnectionFailedPage), failurePackage));
}
private void ReturnWithError(string error, string protocolLog = "")
{
var failurePackage = new ImapConnectionFailedPackage(error, protocolLog, autoDiscoverySettings);
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(ImapConnectionFailedPage), failurePackage));
}
private void ReturnWithSuccess()
=> WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested(serverInformationToTest));
private void ReturnWithSuccess()
=> WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested(serverInformationToTest));
private void DenyClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
=> ReturnWithError(Translator.IMAPSetupDialog_CertificateDenied, string.Empty);
private void DenyClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
=> ReturnWithError(Translator.IMAPSetupDialog_CertificateDenied, string.Empty);
private async void AllowClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
// Run the test again, but this time allow SSL handshake.
// Any authentication error will be shown to the user after this test.
private async void AllowClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
// Run the test again, but this time allow SSL handshake.
// Any authentication error will be shown to the user after this test.
await PerformTestAsync(allowSSLHandshake: true);
}
await PerformTestAsync(allowSSLHandshake: true);
}
}

View File

@@ -14,83 +14,82 @@ using Wino.Core.Domain.Models.AutoDiscovery;
using Wino.Messaging.Client.Mails;
namespace Wino.Views.ImapSetup
namespace Wino.Views.ImapSetup;
public sealed partial class WelcomeImapSetupPage : Page
{
public sealed partial class WelcomeImapSetupPage : Page
private readonly IAutoDiscoveryService _autoDiscoveryService = App.Current.Services.GetService<IAutoDiscoveryService>();
public WelcomeImapSetupPage()
{
private readonly IAutoDiscoveryService _autoDiscoveryService = App.Current.Services.GetService<IAutoDiscoveryService>();
InitializeComponent();
}
public WelcomeImapSetupPage()
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
AutoDiscoveryPanel.Visibility = Visibility.Collapsed;
MainSetupPanel.Visibility = Visibility.Visible;
if (e.Parameter is MailAccount accountProperties)
{
InitializeComponent();
DisplayNameBox.Text = accountProperties.Name;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
else if (e.Parameter is AccountCreationDialogResult creationDialogResult)
{
base.OnNavigatedTo(e);
AutoDiscoveryPanel.Visibility = Visibility.Collapsed;
MainSetupPanel.Visibility = Visibility.Visible;
if (e.Parameter is MailAccount accountProperties)
{
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)
{
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()
{
Password = PasswordBox.Password,
DisplayName = DisplayNameBox.Text,
Email = AddressBox.Text,
};
var discoverySettings = await _autoDiscoveryService.GetAutoDiscoverySettings(minimalSettings);
if (discoverySettings == null)
{
// Couldn't find settings.
var failurePackage = new ImapConnectionFailedPackage(Translator.Exception_ImapAutoDiscoveryFailed, string.Empty, discoverySettings);
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(ImapConnectionFailedPage), failurePackage));
}
else
{
// Settings are found. Test the connection with the given password.
discoverySettings.UserMinimalSettings = minimalSettings;
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(TestingImapConnectionPage), discoverySettings));
}
}
private void CancelClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested());
private void AdvancedConfigurationClicked(object sender, RoutedEventArgs e)
{
var latestMinimalSettings = new AutoDiscoveryMinimalSettings()
{
DisplayName = DisplayNameBox.Text,
Password = PasswordBox.Password,
Email = AddressBox.Text
};
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(AdvancedImapSetupPage), latestMinimalSettings));
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(TestingImapConnectionPage), creationDialogResult));
}
}
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()
{
Password = PasswordBox.Password,
DisplayName = DisplayNameBox.Text,
Email = AddressBox.Text,
};
var discoverySettings = await _autoDiscoveryService.GetAutoDiscoverySettings(minimalSettings);
if (discoverySettings == null)
{
// Couldn't find settings.
var failurePackage = new ImapConnectionFailedPackage(Translator.Exception_ImapAutoDiscoveryFailed, string.Empty, discoverySettings);
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(ImapConnectionFailedPage), failurePackage));
}
else
{
// Settings are found. Test the connection with the given password.
discoverySettings.UserMinimalSettings = minimalSettings;
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(TestingImapConnectionPage), discoverySettings));
}
}
private void CancelClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested());
private void AdvancedConfigurationClicked(object sender, RoutedEventArgs e)
{
var latestMinimalSettings = new AutoDiscoveryMinimalSettings()
{
DisplayName = DisplayNameBox.Text,
Password = PasswordBox.Password,
Email = AddressBox.Text
};
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(AdvancedImapSetupPage), latestMinimalSettings));
}
}

View File

@@ -27,467 +27,466 @@ using Wino.MenuFlyouts.Context;
using Wino.Messaging.Client.Mails;
using Wino.Views.Abstract;
namespace Wino.Views
namespace Wino.Views;
public sealed partial class MailListPage : MailListPageAbstract,
IRecipient<ClearMailSelectionsRequested>,
IRecipient<ActiveMailItemChangedEvent>,
IRecipient<SelectMailItemContainerEvent>,
IRecipient<DisposeRenderingFrameRequested>
{
public sealed partial class MailListPage : MailListPageAbstract,
IRecipient<ClearMailSelectionsRequested>,
IRecipient<ActiveMailItemChangedEvent>,
IRecipient<SelectMailItemContainerEvent>,
IRecipient<DisposeRenderingFrameRequested>
private const double RENDERING_COLUMN_MIN_WIDTH = 375;
private IStatePersistanceService StatePersistenceService { get; } = App.Current.Services.GetService<IStatePersistanceService>();
private IKeyPressService KeyPressService { get; } = App.Current.Services.GetService<IKeyPressService>();
public MailListPage()
{
private const double RENDERING_COLUMN_MIN_WIDTH = 375;
InitializeComponent();
}
private IStatePersistanceService StatePersistenceService { get; } = App.Current.Services.GetService<IStatePersistanceService>();
private IKeyPressService KeyPressService { get; } = App.Current.Services.GetService<IKeyPressService>();
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
public MailListPage()
// Bindings.Update();
// Delegate to ViewModel.
if (e.Parameter is NavigateMailFolderEventArgs folderNavigationArgs)
{
InitializeComponent();
WeakReferenceMessenger.Default.Send(new ActiveMailFolderChangedEvent(folderNavigationArgs.BaseFolderMenuItem, folderNavigationArgs.FolderInitLoadAwaitTask));
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
// Dispose all WinoListView items.
MailListView.Dispose();
this.Bindings.StopTracking();
RenderingFrame.Navigate(typeof(IdlePage));
GC.Collect();
}
private void UpdateSelectAllButtonStatus()
{
// Check all checkbox if all is selected.
// Unhook events to prevent selection overriding.
SelectAllCheckbox.Checked -= SelectAllCheckboxChecked;
SelectAllCheckbox.Unchecked -= SelectAllCheckboxUnchecked;
SelectAllCheckbox.IsChecked = MailListView.Items.Count > 0 && MailListView.SelectedItems.Count == MailListView.Items.Count;
SelectAllCheckbox.Checked += SelectAllCheckboxChecked;
SelectAllCheckbox.Unchecked += SelectAllCheckboxUnchecked;
}
private void SelectionModeToggleChecked(object sender, RoutedEventArgs e)
{
ChangeSelectionMode(ListViewSelectionMode.Multiple);
}
private void FolderPivotChanged(object sender, SelectionChangedEventArgs e)
{
foreach (var addedItem in e.AddedItems)
{
base.OnNavigatedTo(e);
// Bindings.Update();
// Delegate to ViewModel.
if (e.Parameter is NavigateMailFolderEventArgs folderNavigationArgs)
if (addedItem is FolderPivotViewModel pivotItem)
{
WeakReferenceMessenger.Default.Send(new ActiveMailFolderChangedEvent(folderNavigationArgs.BaseFolderMenuItem, folderNavigationArgs.FolderInitLoadAwaitTask));
pivotItem.IsSelected = true;
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
foreach (var removedItem in e.RemovedItems)
{
base.OnNavigatedFrom(e);
// Dispose all WinoListView items.
MailListView.Dispose();
this.Bindings.StopTracking();
RenderingFrame.Navigate(typeof(IdlePage));
GC.Collect();
}
private void UpdateSelectAllButtonStatus()
{
// Check all checkbox if all is selected.
// Unhook events to prevent selection overriding.
SelectAllCheckbox.Checked -= SelectAllCheckboxChecked;
SelectAllCheckbox.Unchecked -= SelectAllCheckboxUnchecked;
SelectAllCheckbox.IsChecked = MailListView.Items.Count > 0 && MailListView.SelectedItems.Count == MailListView.Items.Count;
SelectAllCheckbox.Checked += SelectAllCheckboxChecked;
SelectAllCheckbox.Unchecked += SelectAllCheckboxUnchecked;
}
private void SelectionModeToggleChecked(object sender, RoutedEventArgs e)
{
ChangeSelectionMode(ListViewSelectionMode.Multiple);
}
private void FolderPivotChanged(object sender, SelectionChangedEventArgs e)
{
foreach (var addedItem in e.AddedItems)
if (removedItem is FolderPivotViewModel pivotItem)
{
if (addedItem is FolderPivotViewModel pivotItem)
pivotItem.IsSelected = false;
}
}
SelectAllCheckbox.IsChecked = false;
SelectionModeToggle.IsChecked = false;
MailListView.ClearSelections();
UpdateSelectAllButtonStatus();
ViewModel.SelectedPivotChangedCommand.Execute(null);
}
private void ChangeSelectionMode(ListViewSelectionMode mode)
{
MailListView.ChangeSelectionMode(mode);
if (ViewModel?.PivotFolders != null)
{
ViewModel.PivotFolders.ForEach(a => a.IsExtendedMode = mode == ListViewSelectionMode.Extended);
}
}
private void SelectionModeToggleUnchecked(object sender, RoutedEventArgs e)
{
ChangeSelectionMode(ListViewSelectionMode.Extended);
}
private void SelectAllCheckboxChecked(object sender, RoutedEventArgs e)
{
MailListView.SelectAllWino();
}
private void SelectAllCheckboxUnchecked(object sender, RoutedEventArgs e)
{
MailListView.ClearSelections();
}
private async void MailItemContextRequested(UIElement sender, ContextRequestedEventArgs args)
{
// Context is requested from a single mail point, but we might have multiple selected items.
// This menu should be calculated based on all selected items by providers.
if (sender is MailItemDisplayInformationControl control && args.TryGetPosition(sender, out Point p))
{
await FocusManager.TryFocusAsync(control, FocusState.Keyboard);
if (control.DataContext is IMailItem clickedMailItemContext)
{
var targetItems = ViewModel.GetTargetMailItemViewModels(clickedMailItemContext);
var availableActions = ViewModel.GetAvailableMailActions(targetItems);
if (!availableActions?.Any() ?? false) return;
var t = targetItems.ElementAt(0);
ViewModel.ChangeCustomFocusedState(targetItems, true);
var clickedOperation = await GetMailOperationFromFlyoutAsync(availableActions, control, p.X, p.Y);
ViewModel.ChangeCustomFocusedState(targetItems, false);
if (clickedOperation == null) return;
var prepRequest = new MailOperationPreperationRequest(clickedOperation.Operation, targetItems.Select(a => a.MailCopy));
await ViewModel.ExecuteMailOperationAsync(prepRequest);
}
}
}
private async Task<MailOperationMenuItem> GetMailOperationFromFlyoutAsync(IEnumerable<MailOperationMenuItem> availableActions,
UIElement showAtElement,
double x,
double y)
{
var source = new TaskCompletionSource<MailOperationMenuItem>();
var flyout = new MailOperationFlyout(availableActions, source);
flyout.ShowAt(showAtElement, new FlyoutShowOptions()
{
ShowMode = FlyoutShowMode.Standard,
Position = new Point(x + 30, y - 20)
});
return await source.Task;
}
void IRecipient<ClearMailSelectionsRequested>.Receive(ClearMailSelectionsRequested message)
{
MailListView.ClearSelections(null, preserveThreadExpanding: true);
}
void IRecipient<ActiveMailItemChangedEvent>.Receive(ActiveMailItemChangedEvent message)
{
// No active mail item. Go to empty page.
if (message.SelectedMailItemViewModel == null)
{
WeakReferenceMessenger.Default.Send(new CancelRenderingContentRequested());
}
else
{
// Navigate to composing page.
if (message.SelectedMailItemViewModel.IsDraft)
{
NavigationTransitionType composerPageTransition = NavigationTransitionType.None;
// Dispose active rendering if there is any and go to composer.
if (IsRenderingPageActive())
{
pivotItem.IsSelected = true;
}
}
// Prepare WebView2 animation from Rendering to Composing page.
PrepareRenderingPageWebViewTransition();
foreach (var removedItem in e.RemovedItems)
{
if (removedItem is FolderPivotViewModel pivotItem)
// Dispose existing HTML content from rendering page webview.
WeakReferenceMessenger.Default.Send(new CancelRenderingContentRequested());
}
else if (IsComposingPageActive())
{
pivotItem.IsSelected = false;
// Composer is already active. Prepare composer WebView2 animation.
PrepareComposePageWebViewTransition();
}
}
else
composerPageTransition = NavigationTransitionType.DrillIn;
SelectAllCheckbox.IsChecked = false;
SelectionModeToggle.IsChecked = false;
MailListView.ClearSelections();
UpdateSelectAllButtonStatus();
ViewModel.SelectedPivotChangedCommand.Execute(null);
}
private void ChangeSelectionMode(ListViewSelectionMode mode)
{
MailListView.ChangeSelectionMode(mode);
if (ViewModel?.PivotFolders != null)
{
ViewModel.PivotFolders.ForEach(a => a.IsExtendedMode = mode == ListViewSelectionMode.Extended);
}
}
private void SelectionModeToggleUnchecked(object sender, RoutedEventArgs e)
{
ChangeSelectionMode(ListViewSelectionMode.Extended);
}
private void SelectAllCheckboxChecked(object sender, RoutedEventArgs e)
{
MailListView.SelectAllWino();
}
private void SelectAllCheckboxUnchecked(object sender, RoutedEventArgs e)
{
MailListView.ClearSelections();
}
private async void MailItemContextRequested(UIElement sender, ContextRequestedEventArgs args)
{
// Context is requested from a single mail point, but we might have multiple selected items.
// This menu should be calculated based on all selected items by providers.
if (sender is MailItemDisplayInformationControl control && args.TryGetPosition(sender, out Point p))
{
await FocusManager.TryFocusAsync(control, FocusState.Keyboard);
if (control.DataContext is IMailItem clickedMailItemContext)
{
var targetItems = ViewModel.GetTargetMailItemViewModels(clickedMailItemContext);
var availableActions = ViewModel.GetAvailableMailActions(targetItems);
if (!availableActions?.Any() ?? false) return;
var t = targetItems.ElementAt(0);
ViewModel.ChangeCustomFocusedState(targetItems, true);
var clickedOperation = await GetMailOperationFromFlyoutAsync(availableActions, control, p.X, p.Y);
ViewModel.ChangeCustomFocusedState(targetItems, false);
if (clickedOperation == null) return;
var prepRequest = new MailOperationPreperationRequest(clickedOperation.Operation, targetItems.Select(a => a.MailCopy));
await ViewModel.ExecuteMailOperationAsync(prepRequest);
}
}
}
private async Task<MailOperationMenuItem> GetMailOperationFromFlyoutAsync(IEnumerable<MailOperationMenuItem> availableActions,
UIElement showAtElement,
double x,
double y)
{
var source = new TaskCompletionSource<MailOperationMenuItem>();
var flyout = new MailOperationFlyout(availableActions, source);
flyout.ShowAt(showAtElement, new FlyoutShowOptions()
{
ShowMode = FlyoutShowMode.Standard,
Position = new Point(x + 30, y - 20)
});
return await source.Task;
}
void IRecipient<ClearMailSelectionsRequested>.Receive(ClearMailSelectionsRequested message)
{
MailListView.ClearSelections(null, preserveThreadExpanding: true);
}
void IRecipient<ActiveMailItemChangedEvent>.Receive(ActiveMailItemChangedEvent message)
{
// No active mail item. Go to empty page.
if (message.SelectedMailItemViewModel == null)
{
WeakReferenceMessenger.Default.Send(new CancelRenderingContentRequested());
ViewModel.NavigationService.Navigate(WinoPage.ComposePage, message.SelectedMailItemViewModel, NavigationReferenceFrame.RenderingFrame, composerPageTransition);
}
else
{
// Navigate to composing page.
if (message.SelectedMailItemViewModel.IsDraft)
// Find the MIME and go to rendering page.
if (message.SelectedMailItemViewModel == null) return;
if (IsComposingPageActive())
{
NavigationTransitionType composerPageTransition = NavigationTransitionType.None;
// Dispose active rendering if there is any and go to composer.
if (IsRenderingPageActive())
{
// Prepare WebView2 animation from Rendering to Composing page.
PrepareRenderingPageWebViewTransition();
// Dispose existing HTML content from rendering page webview.
WeakReferenceMessenger.Default.Send(new CancelRenderingContentRequested());
}
else if (IsComposingPageActive())
{
// Composer is already active. Prepare composer WebView2 animation.
PrepareComposePageWebViewTransition();
}
else
composerPageTransition = NavigationTransitionType.DrillIn;
ViewModel.NavigationService.Navigate(WinoPage.ComposePage, message.SelectedMailItemViewModel, NavigationReferenceFrame.RenderingFrame, composerPageTransition);
PrepareComposePageWebViewTransition();
}
else
ViewModel.NavigationService.Navigate(WinoPage.MailRenderingPage, message.SelectedMailItemViewModel, NavigationReferenceFrame.RenderingFrame);
}
}
UpdateAdaptiveness();
}
private bool IsRenderingPageActive() => RenderingFrame.Content is MailRenderingPage;
private bool IsComposingPageActive() => RenderingFrame.Content is ComposePage;
private void PrepareComposePageWebViewTransition()
{
var webView = GetComposerPageWebView();
if (webView != null)
{
var animation = ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("WebViewConnectedAnimation", webView);
animation.Configuration = new BasicConnectedAnimationConfiguration();
}
}
private void PrepareRenderingPageWebViewTransition()
{
var webView = GetRenderingPageWebView();
if (webView != null)
{
var animation = ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("WebViewConnectedAnimation", webView);
animation.Configuration = new BasicConnectedAnimationConfiguration();
}
}
#region Connected Animation Helpers
private WebView2 GetRenderingPageWebView()
{
if (RenderingFrame.Content is MailRenderingPage renderingPage)
return renderingPage.GetWebView();
return null;
}
private WebView2 GetComposerPageWebView()
{
if (RenderingFrame.Content is ComposePage composePage)
return composePage.GetWebView();
return null;
}
#endregion
public async void Receive(SelectMailItemContainerEvent message)
{
if (message.SelectedMailViewModel == null) return;
await ViewModel.ExecuteUIThread(async () =>
{
MailListView.ClearSelections(message.SelectedMailViewModel, true);
int retriedSelectionCount = 0;
trySelection:
bool isSelected = MailListView.SelectMailItemContainer(message.SelectedMailViewModel);
if (!isSelected)
{
for (int i = retriedSelectionCount; i < 5;)
{
// Find the MIME and go to rendering page.
// Retry with delay until the container is realized. Max 1 second.
await Task.Delay(200);
if (message.SelectedMailItemViewModel == null) return;
retriedSelectionCount++;
if (IsComposingPageActive())
{
PrepareComposePageWebViewTransition();
}
ViewModel.NavigationService.Navigate(WinoPage.MailRenderingPage, message.SelectedMailItemViewModel, NavigationReferenceFrame.RenderingFrame);
goto trySelection;
}
}
UpdateAdaptiveness();
}
private bool IsRenderingPageActive() => RenderingFrame.Content is MailRenderingPage;
private bool IsComposingPageActive() => RenderingFrame.Content is ComposePage;
private void PrepareComposePageWebViewTransition()
{
var webView = GetComposerPageWebView();
if (webView != null)
// Automatically scroll to the selected item.
// This is useful when creating draft.
if (isSelected && message.ScrollToItem)
{
var animation = ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("WebViewConnectedAnimation", webView);
animation.Configuration = new BasicConnectedAnimationConfiguration();
}
}
var collectionContainer = ViewModel.MailCollection.GetMailItemContainer(message.SelectedMailViewModel.UniqueId);
private void PrepareRenderingPageWebViewTransition()
{
var webView = GetRenderingPageWebView();
if (webView != null)
{
var animation = ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("WebViewConnectedAnimation", webView);
animation.Configuration = new BasicConnectedAnimationConfiguration();
}
}
#region Connected Animation Helpers
private WebView2 GetRenderingPageWebView()
{
if (RenderingFrame.Content is MailRenderingPage renderingPage)
return renderingPage.GetWebView();
return null;
}
private WebView2 GetComposerPageWebView()
{
if (RenderingFrame.Content is ComposePage composePage)
return composePage.GetWebView();
return null;
}
#endregion
public async void Receive(SelectMailItemContainerEvent message)
{
if (message.SelectedMailViewModel == null) return;
await ViewModel.ExecuteUIThread(async () =>
{
MailListView.ClearSelections(message.SelectedMailViewModel, true);
int retriedSelectionCount = 0;
trySelection:
bool isSelected = MailListView.SelectMailItemContainer(message.SelectedMailViewModel);
if (!isSelected)
// Scroll to thread if available.
if (collectionContainer.ThreadViewModel != null)
{
for (int i = retriedSelectionCount; i < 5;)
{
// Retry with delay until the container is realized. Max 1 second.
await Task.Delay(200);
retriedSelectionCount++;
goto trySelection;
}
MailListView.ScrollIntoView(collectionContainer.ThreadViewModel, ScrollIntoViewAlignment.Default);
}
else if (collectionContainer.ItemViewModel != null)
{
MailListView.ScrollIntoView(collectionContainer.ItemViewModel, ScrollIntoViewAlignment.Default);
}
// Automatically scroll to the selected item.
// This is useful when creating draft.
if (isSelected && message.ScrollToItem)
{
var collectionContainer = ViewModel.MailCollection.GetMailItemContainer(message.SelectedMailViewModel.UniqueId);
// Scroll to thread if available.
if (collectionContainer.ThreadViewModel != null)
{
MailListView.ScrollIntoView(collectionContainer.ThreadViewModel, ScrollIntoViewAlignment.Default);
}
else if (collectionContainer.ItemViewModel != null)
{
MailListView.ScrollIntoView(collectionContainer.ItemViewModel, ScrollIntoViewAlignment.Default);
}
}
});
}
private void SearchBoxFocused(object sender, RoutedEventArgs e)
{
SearchBar.PlaceholderText = string.Empty;
}
private void SearchBarUnfocused(object sender, RoutedEventArgs e)
{
SearchBar.PlaceholderText = Translator.SearchBarPlaceholder;
}
/// <summary>
/// Thread header is mail info display control and it can be dragged spearately out of ListView.
/// We need to prepare a drag package for it from the items inside.
/// </summary>
private void ThreadHeaderDragStart(UIElement sender, DragStartingEventArgs args)
{
if (sender is MailItemDisplayInformationControl control
&& control.ConnectedExpander?.Content is WinoListView contentListView)
{
var allItems = contentListView.Items.Where(a => a is IMailItem);
// Highlight all items.
allItems.Cast<MailItemViewModel>().ForEach(a => a.IsCustomFocused = true);
// Set native drag arg properties.
args.AllowedOperations = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
var dragPackage = new MailDragPackage(allItems.Cast<IMailItem>());
args.Data.Properties.Add(nameof(MailDragPackage), dragPackage);
args.DragUI.SetContentFromDataPackage();
control.ConnectedExpander.IsExpanded = true;
}
}
});
}
private void ThreadHeaderDragFinished(UIElement sender, DropCompletedEventArgs args)
private void SearchBoxFocused(object sender, RoutedEventArgs e)
{
SearchBar.PlaceholderText = string.Empty;
}
private void SearchBarUnfocused(object sender, RoutedEventArgs e)
{
SearchBar.PlaceholderText = Translator.SearchBarPlaceholder;
}
/// <summary>
/// Thread header is mail info display control and it can be dragged spearately out of ListView.
/// We need to prepare a drag package for it from the items inside.
/// </summary>
private void ThreadHeaderDragStart(UIElement sender, DragStartingEventArgs args)
{
if (sender is MailItemDisplayInformationControl control
&& control.ConnectedExpander?.Content is WinoListView contentListView)
{
if (sender is MailItemDisplayInformationControl control && control.ConnectedExpander != null && control.ConnectedExpander.Content is WinoListView contentListView)
var allItems = contentListView.Items.Where(a => a is IMailItem);
// Highlight all items.
allItems.Cast<MailItemViewModel>().ForEach(a => a.IsCustomFocused = true);
// Set native drag arg properties.
args.AllowedOperations = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
var dragPackage = new MailDragPackage(allItems.Cast<IMailItem>());
args.Data.Properties.Add(nameof(MailDragPackage), dragPackage);
args.DragUI.SetContentFromDataPackage();
control.ConnectedExpander.IsExpanded = true;
}
}
private void ThreadHeaderDragFinished(UIElement sender, DropCompletedEventArgs args)
{
if (sender is MailItemDisplayInformationControl control && control.ConnectedExpander != null && control.ConnectedExpander.Content is WinoListView contentListView)
{
contentListView.Items.Where(a => a is MailItemViewModel).Cast<MailItemViewModel>().ForEach(a => a.IsCustomFocused = false);
}
}
private async void LeftSwipeItemInvoked(Microsoft.UI.Xaml.Controls.SwipeItem sender, Microsoft.UI.Xaml.Controls.SwipeItemInvokedEventArgs args)
{
// Delete item for now.
var swipeControl = args.SwipeControl;
swipeControl.Close();
if (swipeControl.Tag is MailItemViewModel mailItemViewModel)
{
var package = new MailOperationPreperationRequest(MailOperation.SoftDelete, mailItemViewModel.MailCopy);
await ViewModel.ExecuteMailOperationAsync(package);
}
else if (swipeControl.Tag is ThreadMailItemViewModel threadMailItemViewModel)
{
var package = new MailOperationPreperationRequest(MailOperation.SoftDelete, threadMailItemViewModel.GetMailCopies());
await ViewModel.ExecuteMailOperationAsync(package);
}
}
private async void RightSwipeItemInvoked(Microsoft.UI.Xaml.Controls.SwipeItem sender, Microsoft.UI.Xaml.Controls.SwipeItemInvokedEventArgs args)
{
// Toggle status only for now.
var swipeControl = args.SwipeControl;
swipeControl.Close();
if (swipeControl.Tag is MailItemViewModel mailItemViewModel)
{
var operation = mailItemViewModel.IsRead ? MailOperation.MarkAsUnread : MailOperation.MarkAsRead;
var package = new MailOperationPreperationRequest(operation, mailItemViewModel.MailCopy);
await ViewModel.ExecuteMailOperationAsync(package);
}
else if (swipeControl.Tag is ThreadMailItemViewModel threadMailItemViewModel)
{
bool isAllRead = threadMailItemViewModel.ThreadItems.All(a => a.IsRead);
var operation = isAllRead ? MailOperation.MarkAsUnread : MailOperation.MarkAsRead;
var package = new MailOperationPreperationRequest(operation, threadMailItemViewModel.GetMailCopies());
await ViewModel.ExecuteMailOperationAsync(package);
}
}
private void PullToRefreshRequested(Microsoft.UI.Xaml.Controls.RefreshContainer sender, Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs args)
{
ViewModel.SyncFolderCommand?.Execute(null);
}
private async void SearchBar_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput && string.IsNullOrWhiteSpace(sender.Text))
{
await ViewModel.PerformSearchAsync();
}
}
public void Receive(DisposeRenderingFrameRequested message)
{
ViewModel.NavigationService.Navigate(WinoPage.IdlePage, null, NavigationReferenceFrame.RenderingFrame, NavigationTransitionType.DrillIn);
}
private void PageSizeChanged(object sender, SizeChangedEventArgs e)
{
ViewModel.MaxMailListLength = e.NewSize.Width - RENDERING_COLUMN_MIN_WIDTH;
StatePersistenceService.IsReaderNarrowed = e.NewSize.Width < StatePersistenceService.MailListPaneLength + RENDERING_COLUMN_MIN_WIDTH;
UpdateAdaptiveness();
}
private void MailListSizerManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
{
StatePersistenceService.MailListPaneLength = ViewModel.MailListLength;
}
private void UpdateAdaptiveness()
{
bool isMultiSelectionEnabled = ViewModel.IsMultiSelectionModeEnabled || KeyPressService.IsCtrlKeyPressed();
if (StatePersistenceService.IsReaderNarrowed)
{
if (ViewModel.HasSingleItemSelection && !isMultiSelectionEnabled)
{
contentListView.Items.Where(a => a is MailItemViewModel).Cast<MailItemViewModel>().ForEach(a => a.IsCustomFocused = false);
}
}
private async void LeftSwipeItemInvoked(Microsoft.UI.Xaml.Controls.SwipeItem sender, Microsoft.UI.Xaml.Controls.SwipeItemInvokedEventArgs args)
{
// Delete item for now.
var swipeControl = args.SwipeControl;
swipeControl.Close();
if (swipeControl.Tag is MailItemViewModel mailItemViewModel)
{
var package = new MailOperationPreperationRequest(MailOperation.SoftDelete, mailItemViewModel.MailCopy);
await ViewModel.ExecuteMailOperationAsync(package);
}
else if (swipeControl.Tag is ThreadMailItemViewModel threadMailItemViewModel)
{
var package = new MailOperationPreperationRequest(MailOperation.SoftDelete, threadMailItemViewModel.GetMailCopies());
await ViewModel.ExecuteMailOperationAsync(package);
}
}
private async void RightSwipeItemInvoked(Microsoft.UI.Xaml.Controls.SwipeItem sender, Microsoft.UI.Xaml.Controls.SwipeItemInvokedEventArgs args)
{
// Toggle status only for now.
var swipeControl = args.SwipeControl;
swipeControl.Close();
if (swipeControl.Tag is MailItemViewModel mailItemViewModel)
{
var operation = mailItemViewModel.IsRead ? MailOperation.MarkAsUnread : MailOperation.MarkAsRead;
var package = new MailOperationPreperationRequest(operation, mailItemViewModel.MailCopy);
await ViewModel.ExecuteMailOperationAsync(package);
}
else if (swipeControl.Tag is ThreadMailItemViewModel threadMailItemViewModel)
{
bool isAllRead = threadMailItemViewModel.ThreadItems.All(a => a.IsRead);
var operation = isAllRead ? MailOperation.MarkAsUnread : MailOperation.MarkAsRead;
var package = new MailOperationPreperationRequest(operation, threadMailItemViewModel.GetMailCopies());
await ViewModel.ExecuteMailOperationAsync(package);
}
}
private void PullToRefreshRequested(Microsoft.UI.Xaml.Controls.RefreshContainer sender, Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs args)
{
ViewModel.SyncFolderCommand?.Execute(null);
}
private async void SearchBar_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput && string.IsNullOrWhiteSpace(sender.Text))
{
await ViewModel.PerformSearchAsync();
}
}
public void Receive(DisposeRenderingFrameRequested message)
{
ViewModel.NavigationService.Navigate(WinoPage.IdlePage, null, NavigationReferenceFrame.RenderingFrame, NavigationTransitionType.DrillIn);
}
private void PageSizeChanged(object sender, SizeChangedEventArgs e)
{
ViewModel.MaxMailListLength = e.NewSize.Width - RENDERING_COLUMN_MIN_WIDTH;
StatePersistenceService.IsReaderNarrowed = e.NewSize.Width < StatePersistenceService.MailListPaneLength + RENDERING_COLUMN_MIN_WIDTH;
UpdateAdaptiveness();
}
private void MailListSizerManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
{
StatePersistenceService.MailListPaneLength = ViewModel.MailListLength;
}
private void UpdateAdaptiveness()
{
bool isMultiSelectionEnabled = ViewModel.IsMultiSelectionModeEnabled || KeyPressService.IsCtrlKeyPressed();
if (StatePersistenceService.IsReaderNarrowed)
{
if (ViewModel.HasSingleItemSelection && !isMultiSelectionEnabled)
{
VisualStateManager.GoToState(this, "NarrowRenderer", true);
}
else
{
VisualStateManager.GoToState(this, "NarrowMailList", true);
}
VisualStateManager.GoToState(this, "NarrowRenderer", true);
}
else
{
if (ViewModel.HasSingleItemSelection && !isMultiSelectionEnabled)
{
VisualStateManager.GoToState(this, "BothPanelsMailSelected", true);
}
else
{
VisualStateManager.GoToState(this, "BothPanelsNoMailSelected", true);
}
VisualStateManager.GoToState(this, "NarrowMailList", true);
}
}
else
{
if (ViewModel.HasSingleItemSelection && !isMultiSelectionEnabled)
{
VisualStateManager.GoToState(this, "BothPanelsMailSelected", true);
}
else
{
VisualStateManager.GoToState(this, "BothPanelsNoMailSelected", true);
}
}
}

View File

@@ -19,258 +19,257 @@ using Wino.Messaging.Client.Mails;
using Wino.Messaging.Client.Shell;
using Wino.Views.Abstract;
namespace Wino.Views
namespace Wino.Views;
public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
IRecipient<HtmlRenderingRequested>,
IRecipient<CancelRenderingContentRequested>,
IRecipient<ApplicationThemeChanged>
{
public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
IRecipient<HtmlRenderingRequested>,
IRecipient<CancelRenderingContentRequested>,
IRecipient<ApplicationThemeChanged>
private readonly IPreferencesService _preferencesService = App.Current.Services.GetService<IPreferencesService>();
private readonly IMailDialogService _dialogService = App.Current.Services.GetService<IMailDialogService>();
private bool isRenderingInProgress = false;
private TaskCompletionSource<bool> DOMLoadedTask = new TaskCompletionSource<bool>();
private bool isChromiumDisposed = false;
public WebView2 GetWebView() => Chromium;
public MailRenderingPage()
{
private readonly IPreferencesService _preferencesService = App.Current.Services.GetService<IPreferencesService>();
private readonly IMailDialogService _dialogService = App.Current.Services.GetService<IMailDialogService>();
InitializeComponent();
private bool isRenderingInProgress = false;
private TaskCompletionSource<bool> DOMLoadedTask = new TaskCompletionSource<bool>();
Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF");
Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation,msWebView2CodeCache");
private bool isChromiumDisposed = false;
public WebView2 GetWebView() => Chromium;
public MailRenderingPage()
ViewModel.SaveHTMLasPDFFunc = new Func<string, Task<bool>>((path) =>
{
InitializeComponent();
return Chromium.CoreWebView2.PrintToPdfAsync(path, null).AsTask();
});
}
Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF");
Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation,msWebView2CodeCache");
public override async void OnEditorThemeChanged()
{
base.OnEditorThemeChanged();
ViewModel.SaveHTMLasPDFFunc = new Func<string, Task<bool>>((path) =>
{
return Chromium.CoreWebView2.PrintToPdfAsync(path, null).AsTask();
});
await UpdateEditorThemeAsync();
}
private async Task<string> InvokeScriptSafeAsync(string function)
{
try
{
return await Chromium.ExecuteScriptAsync(function);
}
catch (Exception) { }
return string.Empty;
}
private async Task RenderInternalAsync(string htmlBody)
{
isRenderingInProgress = true;
await DOMLoadedTask.Task;
await UpdateEditorThemeAsync();
await UpdateReaderFontPropertiesAsync();
if (string.IsNullOrEmpty(htmlBody))
{
await Chromium.ExecuteScriptFunctionAsync("RenderHTML", isChromiumDisposed, JsonSerializer.Serialize(" ", BasicTypesJsonContext.Default.String));
}
else
{
var shouldLinkifyText = ViewModel.CurrentRenderModel?.MailRenderingOptions?.RenderPlaintextLinks ?? true;
await Chromium.ExecuteScriptFunctionAsync("RenderHTML", isChromiumDisposed,
JsonSerializer.Serialize(htmlBody, BasicTypesJsonContext.Default.String),
JsonSerializer.Serialize(shouldLinkifyText, BasicTypesJsonContext.Default.Boolean));
}
public override async void OnEditorThemeChanged()
{
base.OnEditorThemeChanged();
isRenderingInProgress = false;
}
await UpdateEditorThemeAsync();
private async void WindowRequested(CoreWebView2 sender, CoreWebView2NewWindowRequestedEventArgs args)
{
args.Handled = true;
try
{
await Launcher.LaunchUriAsync(new Uri(args.Uri));
}
catch (Exception) { }
}
private void DOMContentLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) => DOMLoadedTask.TrySetResult(true);
async void IRecipient<HtmlRenderingRequested>.Receive(HtmlRenderingRequested message)
{
if (message == null || string.IsNullOrEmpty(message.HtmlBody))
{
await RenderInternalAsync(string.Empty);
return;
}
private async Task<string> InvokeScriptSafeAsync(string function)
await Chromium.EnsureCoreWebView2Async();
await RenderInternalAsync(message.HtmlBody);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
// Disposing the page.
// Make sure the WebView2 is disposed properly.
DisposeWebView2();
}
private void DisposeWebView2()
{
if (Chromium == null) return;
Chromium.CoreWebView2Initialized -= CoreWebViewInitialized;
Chromium.NavigationStarting -= WebViewNavigationStarting;
if (Chromium.CoreWebView2 != null)
{
try
{
return await Chromium.ExecuteScriptAsync(function);
}
catch (Exception) { }
return string.Empty;
}
private async Task RenderInternalAsync(string htmlBody)
{
isRenderingInProgress = true;
await DOMLoadedTask.Task;
await UpdateEditorThemeAsync();
await UpdateReaderFontPropertiesAsync();
if (string.IsNullOrEmpty(htmlBody))
{
await Chromium.ExecuteScriptFunctionAsync("RenderHTML", isChromiumDisposed, JsonSerializer.Serialize(" ", BasicTypesJsonContext.Default.String));
}
else
{
var shouldLinkifyText = ViewModel.CurrentRenderModel?.MailRenderingOptions?.RenderPlaintextLinks ?? true;
await Chromium.ExecuteScriptFunctionAsync("RenderHTML", isChromiumDisposed,
JsonSerializer.Serialize(htmlBody, BasicTypesJsonContext.Default.String),
JsonSerializer.Serialize(shouldLinkifyText, BasicTypesJsonContext.Default.Boolean));
}
isRenderingInProgress = false;
}
private async void WindowRequested(CoreWebView2 sender, CoreWebView2NewWindowRequestedEventArgs args)
{
args.Handled = true;
try
{
await Launcher.LaunchUriAsync(new Uri(args.Uri));
}
catch (Exception) { }
}
private void DOMContentLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) => DOMLoadedTask.TrySetResult(true);
async void IRecipient<HtmlRenderingRequested>.Receive(HtmlRenderingRequested message)
{
if (message == null || string.IsNullOrEmpty(message.HtmlBody))
{
await RenderInternalAsync(string.Empty);
return;
}
await Chromium.EnsureCoreWebView2Async();
await RenderInternalAsync(message.HtmlBody);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
// Disposing the page.
// Make sure the WebView2 is disposed properly.
DisposeWebView2();
}
private void DisposeWebView2()
{
if (Chromium == null) return;
Chromium.CoreWebView2Initialized -= CoreWebViewInitialized;
Chromium.NavigationStarting -= WebViewNavigationStarting;
if (Chromium.CoreWebView2 != null)
{
Chromium.CoreWebView2.DOMContentLoaded -= DOMContentLoaded;
Chromium.CoreWebView2.NewWindowRequested -= WindowRequested;
}
isChromiumDisposed = true;
Chromium.Close();
GC.Collect();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var anim = ConnectedAnimationService.GetForCurrentView().GetAnimation("WebViewConnectedAnimation");
anim?.TryStart(Chromium);
Chromium.CoreWebView2Initialized -= CoreWebViewInitialized;
Chromium.CoreWebView2Initialized += CoreWebViewInitialized;
_ = Chromium.EnsureCoreWebView2Async();
// We don't have shell initialized here. It's only standalone EML viewing.
// Shift command bar from top to adjust the design.
if (ViewModel.StatePersistenceService.ShouldShiftMailRenderingDesign)
RendererGridFrame.Margin = new Thickness(0, 24, 0, 0);
else
RendererGridFrame.Margin = new Thickness(0, 0, 0, 0);
}
private async void CoreWebViewInitialized(WebView2 sender, CoreWebView2InitializedEventArgs args)
{
if (Chromium.CoreWebView2 == null) return;
var editorBundlePath = (await ViewModel.NativeAppService.GetEditorBundlePathAsync()).Replace("editor.html", string.Empty);
Chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.reader", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow);
Chromium.CoreWebView2.DOMContentLoaded -= DOMContentLoaded;
Chromium.CoreWebView2.DOMContentLoaded += DOMContentLoaded;
Chromium.CoreWebView2.NewWindowRequested -= WindowRequested;
Chromium.CoreWebView2.NewWindowRequested += WindowRequested;
Chromium.Source = new Uri("https://app.reader/reader.html");
}
isChromiumDisposed = true;
async void IRecipient<CancelRenderingContentRequested>.Receive(CancelRenderingContentRequested message)
Chromium.Close();
GC.Collect();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var anim = ConnectedAnimationService.GetForCurrentView().GetAnimation("WebViewConnectedAnimation");
anim?.TryStart(Chromium);
Chromium.CoreWebView2Initialized -= CoreWebViewInitialized;
Chromium.CoreWebView2Initialized += CoreWebViewInitialized;
_ = Chromium.EnsureCoreWebView2Async();
// We don't have shell initialized here. It's only standalone EML viewing.
// Shift command bar from top to adjust the design.
if (ViewModel.StatePersistenceService.ShouldShiftMailRenderingDesign)
RendererGridFrame.Margin = new Thickness(0, 24, 0, 0);
else
RendererGridFrame.Margin = new Thickness(0, 0, 0, 0);
}
private async void CoreWebViewInitialized(WebView2 sender, CoreWebView2InitializedEventArgs args)
{
if (Chromium.CoreWebView2 == null) return;
var editorBundlePath = (await ViewModel.NativeAppService.GetEditorBundlePathAsync()).Replace("editor.html", string.Empty);
Chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.reader", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow);
Chromium.CoreWebView2.DOMContentLoaded -= DOMContentLoaded;
Chromium.CoreWebView2.DOMContentLoaded += DOMContentLoaded;
Chromium.CoreWebView2.NewWindowRequested -= WindowRequested;
Chromium.CoreWebView2.NewWindowRequested += WindowRequested;
Chromium.Source = new Uri("https://app.reader/reader.html");
}
async void IRecipient<CancelRenderingContentRequested>.Receive(CancelRenderingContentRequested message)
{
await Chromium.EnsureCoreWebView2Async();
if (!isRenderingInProgress)
{
await Chromium.EnsureCoreWebView2Async();
if (!isRenderingInProgress)
{
await RenderInternalAsync(string.Empty);
}
await RenderInternalAsync(string.Empty);
}
}
private async void WebViewNavigationStarting(WebView2 sender, CoreWebView2NavigationStartingEventArgs args)
private async void WebViewNavigationStarting(WebView2 sender, CoreWebView2NavigationStartingEventArgs args)
{
// This is our reader.
if (args.Uri == "https://app.reader/reader.html")
return;
// Cancel all external navigations since it's navigating to different address inside the WebView2.
args.Cancel = !args.Uri.StartsWith("data:text/html");
// TODO: Check external link navigation setting is enabled.
// Open all external urls in launcher.
if (args.Cancel && Uri.TryCreate(args.Uri, UriKind.Absolute, out Uri newUri))
{
// This is our reader.
if (args.Uri == "https://app.reader/reader.html")
return;
// Cancel all external navigations since it's navigating to different address inside the WebView2.
args.Cancel = !args.Uri.StartsWith("data:text/html");
// TODO: Check external link navigation setting is enabled.
// Open all external urls in launcher.
if (args.Cancel && Uri.TryCreate(args.Uri, UriKind.Absolute, out Uri newUri))
{
await Launcher.LaunchUriAsync(newUri);
}
await Launcher.LaunchUriAsync(newUri);
}
}
private void AttachmentClicked(object sender, ItemClickEventArgs e)
private void AttachmentClicked(object sender, ItemClickEventArgs e)
{
if (e.ClickedItem is MailAttachmentViewModel attachmentViewModel)
{
if (e.ClickedItem is MailAttachmentViewModel attachmentViewModel)
{
ViewModel.OpenAttachmentCommand.Execute(attachmentViewModel);
}
ViewModel.OpenAttachmentCommand.Execute(attachmentViewModel);
}
}
private void BarDynamicOverflowChanging(CommandBar sender, DynamicOverflowItemsChangingEventArgs args)
private void BarDynamicOverflowChanging(CommandBar sender, DynamicOverflowItemsChangingEventArgs args)
{
if (args.Action == CommandBarDynamicOverflowAction.AddingToOverflow || sender.SecondaryCommands.Any())
sender.OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Visible;
else
sender.OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Collapsed;
}
private async Task UpdateEditorThemeAsync()
{
await DOMLoadedTask.Task;
if (ViewModel.IsDarkWebviewRenderer)
{
if (args.Action == CommandBarDynamicOverflowAction.AddingToOverflow || sender.SecondaryCommands.Any())
sender.OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Visible;
else
sender.OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Collapsed;
Chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Dark;
await InvokeScriptSafeAsync("SetDarkEditor();");
}
private async Task UpdateEditorThemeAsync()
else
{
await DOMLoadedTask.Task;
Chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Light;
if (ViewModel.IsDarkWebviewRenderer)
{
Chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Dark;
await InvokeScriptSafeAsync("SetDarkEditor();");
}
else
{
Chromium.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Light;
await InvokeScriptSafeAsync("SetLightEditor();");
}
await InvokeScriptSafeAsync("SetLightEditor();");
}
}
private async Task UpdateReaderFontPropertiesAsync()
private async Task UpdateReaderFontPropertiesAsync()
{
await Chromium.ExecuteScriptFunctionAsync("ChangeFontSize", isChromiumDisposed, JsonSerializer.Serialize(_preferencesService.ReaderFontSize, BasicTypesJsonContext.Default.Int32));
// Prepare font family name with fallback to sans-serif by default.
var fontName = _preferencesService.ReaderFont;
// If font family name is not supported by the browser, fallback to sans-serif.
fontName += ", sans-serif";
await Chromium.ExecuteScriptFunctionAsync("ChangeFontFamily", isChromiumDisposed, JsonSerializer.Serialize(fontName, BasicTypesJsonContext.Default.String));
}
void IRecipient<ApplicationThemeChanged>.Receive(ApplicationThemeChanged message)
{
ViewModel.IsDarkWebviewRenderer = message.IsUnderlyingThemeDark;
}
private void InternetAddressClicked(object sender, RoutedEventArgs e)
{
if (sender is HyperlinkButton hyperlinkButton)
{
await Chromium.ExecuteScriptFunctionAsync("ChangeFontSize", isChromiumDisposed, JsonSerializer.Serialize(_preferencesService.ReaderFontSize, BasicTypesJsonContext.Default.Int32));
// Prepare font family name with fallback to sans-serif by default.
var fontName = _preferencesService.ReaderFont;
// If font family name is not supported by the browser, fallback to sans-serif.
fontName += ", sans-serif";
await Chromium.ExecuteScriptFunctionAsync("ChangeFontFamily", isChromiumDisposed, JsonSerializer.Serialize(fontName, BasicTypesJsonContext.Default.String));
}
void IRecipient<ApplicationThemeChanged>.Receive(ApplicationThemeChanged message)
{
ViewModel.IsDarkWebviewRenderer = message.IsUnderlyingThemeDark;
}
private void InternetAddressClicked(object sender, RoutedEventArgs e)
{
if (sender is HyperlinkButton hyperlinkButton)
{
hyperlinkButton.ContextFlyout.ShowAt(hyperlinkButton);
}
hyperlinkButton.ContextFlyout.ShowAt(hyperlinkButton);
}
}
}

View File

@@ -1,12 +1,11 @@
using Wino.Views.Abstract;
namespace Wino.Views.Settings
namespace Wino.Views.Settings;
public sealed partial class PersonalizationPage : PersonalizationPageAbstract
{
public sealed partial class PersonalizationPage : PersonalizationPageAbstract
public PersonalizationPage()
{
public PersonalizationPage()
{
InitializeComponent();
}
InitializeComponent();
}
}

View File

@@ -1,12 +1,11 @@
using Wino.Views.Abstract;
namespace Wino.Views.Settings
namespace Wino.Views.Settings;
public sealed partial class AliasManagementPage : AliasManagementPageAbstract
{
public sealed partial class AliasManagementPage : AliasManagementPageAbstract
public AliasManagementPage()
{
public AliasManagementPage()
{
this.InitializeComponent();
}
this.InitializeComponent();
}
}

View File

@@ -1,12 +1,11 @@
using Wino.Views.Abstract;
namespace Wino.Views.Settings
namespace Wino.Views.Settings;
public sealed partial class AppPreferencesPage : AppPreferencesPageAbstract
{
public sealed partial class AppPreferencesPage : AppPreferencesPageAbstract
public AppPreferencesPage()
{
public AppPreferencesPage()
{
this.InitializeComponent();
}
this.InitializeComponent();
}
}

View File

@@ -1,19 +1,18 @@
using Wino.Views.Abstract;
namespace Wino.Views.Settings
namespace Wino.Views.Settings;
public sealed partial class LanguageTimePage : LanguageTimePageAbstract
{
public sealed partial class LanguageTimePage : LanguageTimePageAbstract
public LanguageTimePage()
{
public LanguageTimePage()
{
this.InitializeComponent();
}
this.InitializeComponent();
}
public override void OnLanguageChanged()
{
base.OnLanguageChanged();
public override void OnLanguageChanged()
{
base.OnLanguageChanged();
Bindings.Update();
}
Bindings.Update();
}
}

View File

@@ -1,12 +1,11 @@
using Wino.Views.Abstract;
namespace Wino.Views.Settings
namespace Wino.Views.Settings;
public sealed partial class MessageListPage : MessageListPageAbstract
{
public sealed partial class MessageListPage : MessageListPageAbstract
public MessageListPage()
{
public MessageListPage()
{
this.InitializeComponent();
}
this.InitializeComponent();
}
}

View File

@@ -1,12 +1,11 @@
using Wino.Views.Abstract;
namespace Wino.Views.Settings
namespace Wino.Views.Settings;
public sealed partial class ReadComposePanePage : ReadComposePanePageAbstract
{
public sealed partial class ReadComposePanePage : ReadComposePanePageAbstract
public ReadComposePanePage()
{
public ReadComposePanePage()
{
InitializeComponent();
}
InitializeComponent();
}
}

View File

@@ -1,12 +1,11 @@
using Wino.Views.Abstract;
namespace Wino.Views.Settings
namespace Wino.Views.Settings;
public sealed partial class SignatureManagementPage : SignatureManagementPageAbstract
{
public sealed partial class SignatureManagementPage : SignatureManagementPageAbstract
public SignatureManagementPage()
{
public SignatureManagementPage()
{
this.InitializeComponent();
}
this.InitializeComponent();
}
}