Reworked IMAP setup flow. Implemented easy way to share protocol log on failure if possible.
This commit is contained in:
@@ -44,28 +44,6 @@
|
||||
IsAddTabButtonVisible="False"
|
||||
CanReorderTabs="False"
|
||||
TabWidthMode="Equal">
|
||||
<muxc:TabView.TabStripFooter>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button>
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<FontIcon
|
||||
FontFamily="Segoe Fluent Icons"
|
||||
Glyph=""
|
||||
FontSize="13" />
|
||||
<!--<TextBlock
|
||||
Text="Help "
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12" />-->
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
<Button.Flyout>
|
||||
<Flyout />
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</muxc:TabView.TabStripFooter>
|
||||
<muxc:TabViewItem Header="IMAP Settings" IsClosable="False">
|
||||
<!-- IMAP -->
|
||||
<StackPanel Padding="12" Spacing="10">
|
||||
@@ -85,7 +63,7 @@
|
||||
|
||||
<TextBox
|
||||
d:Header="Port"
|
||||
PlaceholderText="993"
|
||||
Text="993"
|
||||
Header="{x:Bind domain:Translator.IMAPSetupDialog_IncomingMailServerPort}"
|
||||
x:Name="IncomingServerPortBox"
|
||||
Grid.Column="1" />
|
||||
@@ -165,6 +143,7 @@
|
||||
d:Header="Port"
|
||||
Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServerPort}"
|
||||
x:Name="OutgoingServerPort"
|
||||
Text="587"
|
||||
Grid.Column="1" />
|
||||
</Grid>
|
||||
|
||||
@@ -230,11 +209,6 @@
|
||||
</StackPanel>
|
||||
</muxc:TabViewItem>
|
||||
</muxc:TabView>
|
||||
|
||||
<TextBlock
|
||||
x:Name="ErrorMessage"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
|
||||
@@ -9,13 +9,14 @@ using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Models.Accounts;
|
||||
using Wino.Core.Domain.Models.AutoDiscovery;
|
||||
using Wino.Core.Messages.Mails;
|
||||
using Wino.Extensions;
|
||||
|
||||
|
||||
namespace Wino.Views.ImapSetup
|
||||
{
|
||||
public sealed partial class AdvancedImapSetupPage : Page
|
||||
{
|
||||
private string _protocolLog;
|
||||
|
||||
public List<ImapAuthenticationMethodModel> AvailableAuthenticationMethods { get; } = new List<ImapAuthenticationMethodModel>()
|
||||
{
|
||||
new ImapAuthenticationMethodModel(Core.Domain.Enums.ImapAuthenticationMethod.Auto, Translator.ImapAuthenticationMethod_Auto),
|
||||
@@ -76,6 +77,8 @@ namespace Wino.Views.ImapSetup
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
// ProtocolLogGrid.Visibility = Visibility.Collapsed;
|
||||
|
||||
// Connection is succesfull but error occurred.
|
||||
// Imap and Smptp settings exists here at this point.
|
||||
|
||||
@@ -93,12 +96,8 @@ namespace Wino.Views.ImapSetup
|
||||
IncomingServerBox.Text = serverInfo.IncomingServer;
|
||||
IncomingServerPortBox.Text = serverInfo.IncomingServerPort;
|
||||
|
||||
|
||||
OutgoingPasswordBox.Password = serverInfo.OutgoingServerPassword;
|
||||
|
||||
OutgoingServerBox.Text = serverInfo.OutgoingServer;
|
||||
OutgoingServerPort.Text = serverInfo.OutgoingServerPort;
|
||||
|
||||
OutgoingUsernameBox.Text = serverInfo.OutgoingServerUsername;
|
||||
|
||||
UseSameCredentialsForSending = OutgoingUsernameBox.Text == UsernameBox.Text;
|
||||
@@ -112,12 +111,6 @@ namespace Wino.Views.ImapSetup
|
||||
DisplayNameBox.Text = autoDiscoveryMinimalSettings.DisplayName;
|
||||
PasswordBox.Password = autoDiscoveryMinimalSettings.Password;
|
||||
}
|
||||
else if (e.Parameter is AutoDiscoveryConnectionTestFailedPackage failedPackage)
|
||||
{
|
||||
ErrorMessage.Text = failedPackage.Error.Message;
|
||||
|
||||
MainScrollviewer.ScrollToElement(ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested(null));
|
||||
@@ -136,8 +129,6 @@ namespace Wino.Views.ImapSetup
|
||||
|
||||
private void SignInClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ErrorMessage.Text = string.Empty;
|
||||
|
||||
var info = new CustomServerInformation()
|
||||
{
|
||||
IncomingServer = GetServerWithoutPort(IncomingServerBox.Text),
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,50 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.AutoDiscovery;
|
||||
using Wino.Core.Messages.Mails;
|
||||
|
||||
namespace Wino.Views.ImapSetup
|
||||
{
|
||||
public sealed partial class AutoDiscoveryPage : Page
|
||||
{
|
||||
private readonly IAutoDiscoveryService _autoDiscoveryService;
|
||||
public AutoDiscoveryPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
_autoDiscoveryService = App.Current.Services.GetService<IAutoDiscoveryService>();
|
||||
}
|
||||
|
||||
protected override async void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
AutoDiscoverySettings discoverySettings = null;
|
||||
|
||||
if (e.Parameter is AutoDiscoveryMinimalSettings userMinimalSettings)
|
||||
{
|
||||
discoverySettings = await _autoDiscoveryService.GetAutoDiscoverySettings(userMinimalSettings);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
if (discoverySettings == null)
|
||||
{
|
||||
// Couldn't find settings.
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(WelcomeImapSetupPage), $"Couldn't find mailbox settings for {userMinimalSettings.Email}. Please configure it manually."));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Settings are found. Test the connection with the given password.
|
||||
|
||||
discoverySettings.UserMinimalSettings = userMinimalSettings;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(TestingImapConnectionPage), discoverySettings));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
72
Wino.Mail/Views/ImapSetup/ImapConnectionFailedPage.xaml
Normal file
72
Wino.Mail/Views/ImapSetup/ImapConnectionFailedPage.xaml
Normal file
@@ -0,0 +1,72 @@
|
||||
<Page
|
||||
x:Class="Wino.Views.ImapSetup.ImapConnectionFailedPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Wino.Views.ImapSetup"
|
||||
xmlns:domain="using:Wino.Core.Domain"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Padding="24" RowSpacing="6">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="{x:Bind domain:Translator.IMAPSetupDialog_ConnectionFailedTitle}" Style="{StaticResource SubtitleTextBlockStyle}" />
|
||||
|
||||
<TextBlock
|
||||
x:Name="ConnectionFailedMessage"
|
||||
Grid.Row="1"
|
||||
Style="{StaticResource BodyTextBlockStyle}" />
|
||||
|
||||
<!-- Protocol Log Area -->
|
||||
<Grid
|
||||
x:Name="ProtocolLogGrid"
|
||||
Grid.Row="2"
|
||||
ColumnSpacing="12"
|
||||
Visibility="Collapsed">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
x:Name="ProtocolLogHeader"
|
||||
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind domain:Translator.ProtocolLogAvailable_Message}" />
|
||||
|
||||
<Button
|
||||
Content="{x:Bind domain:Translator.Buttons_Copy}"
|
||||
Click="CopyProtocolLogButtonClicked"
|
||||
Grid.Column="1" />
|
||||
</Grid>
|
||||
|
||||
<!-- Dismis / GoBack -->
|
||||
<Grid
|
||||
Grid.Row="3"
|
||||
VerticalAlignment="Bottom"
|
||||
ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button
|
||||
Content="{x:Bind domain:Translator.Buttons_Close}"
|
||||
Click="CloseClicked"
|
||||
HorizontalAlignment="Stretch" />
|
||||
|
||||
<Button
|
||||
Content="{x:Bind domain:Translator.Buttons_TryAgain}"
|
||||
Click="TryAgainClicked"
|
||||
Style="{ThemeResource AccentButtonStyle}"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
49
Wino.Mail/Views/ImapSetup/ImapConnectionFailedPage.xaml.cs
Normal file
49
Wino.Mail/Views/ImapSetup/ImapConnectionFailedPage.xaml.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Exceptions;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Messages.Mails;
|
||||
|
||||
namespace Wino.Views.ImapSetup
|
||||
{
|
||||
public sealed partial class ImapConnectionFailedPage : Page
|
||||
{
|
||||
private string _protocolLog;
|
||||
|
||||
private readonly IClipboardService _clipboardService = App.Current.Services.GetService<IClipboardService>();
|
||||
private readonly IDialogService _dialogService = App.Current.Services.GetService<IDialogService>();
|
||||
|
||||
public ImapConnectionFailedPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void CopyProtocolLogButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await _clipboardService.CopyClipboardAsync(_protocolLog);
|
||||
|
||||
_dialogService.InfoBarMessage(Translator.ClipboardTextCopied_Title, string.Format(Translator.ClipboardTextCopied_Message, "Log"), Core.Domain.Enums.InfoBarMessageType.Information);
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if (e.Parameter is ImapConnectionFailedPackage failedPackage)
|
||||
{
|
||||
ConnectionFailedMessage.Text = failedPackage.GetErrorMessage();
|
||||
|
||||
ProtocolLogGrid.Visibility = !string.IsNullOrEmpty(failedPackage.ProtocolLog) ? Visibility.Visible : Visibility.Collapsed;
|
||||
_protocolLog = failedPackage.ProtocolLog;
|
||||
}
|
||||
}
|
||||
|
||||
private void TryAgainClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested());
|
||||
|
||||
private void CloseClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested());
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.AppCenter.Analytics;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Exceptions;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.AutoDiscovery;
|
||||
using Wino.Core.Messages.Mails;
|
||||
@@ -16,13 +15,11 @@ namespace Wino.Views.ImapSetup
|
||||
{
|
||||
public sealed partial class TestingImapConnectionPage : Page
|
||||
{
|
||||
private IImapTestService _imapTestService;
|
||||
private IImapTestService _imapTestService = App.Current.Services.GetService<IImapTestService>();
|
||||
|
||||
public TestingImapConnectionPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_imapTestService = App.Current.Services.GetService<IImapTestService>();
|
||||
}
|
||||
|
||||
private async Task TryTestConnectionAsync(CustomServerInformation serverInformation)
|
||||
@@ -40,39 +37,43 @@ namespace Wino.Views.ImapSetup
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
// We either go back to welcome setup page or advanced config page.
|
||||
// Based on if we come from auto discovery or not.
|
||||
|
||||
if (e.Parameter is AutoDiscoverySettings autoDiscoverySettings)
|
||||
// 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)
|
||||
{
|
||||
var serverInformation = autoDiscoverySettings.ToServerInformation();
|
||||
|
||||
try
|
||||
{
|
||||
await TryTestConnectionAsync(serverInformation);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(WelcomeImapSetupPage),
|
||||
new AutoDiscoveryConnectionTestFailedPackage(autoDiscoverySettings, ex)));
|
||||
}
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested());
|
||||
}
|
||||
else if (e.Parameter is CustomServerInformation customServerInformation)
|
||||
else
|
||||
{
|
||||
// Test connection
|
||||
|
||||
CustomServerInformation serverInformationToTest = null;
|
||||
AutoDiscoverySettings autoDiscoverySettings = null;
|
||||
|
||||
// Discovery settings are passed.
|
||||
// Create server information out of the discovery settings.
|
||||
if (e.Parameter is AutoDiscoverySettings parameterAutoDiscoverySettings)
|
||||
{
|
||||
autoDiscoverySettings = parameterAutoDiscoverySettings;
|
||||
serverInformationToTest = autoDiscoverySettings.ToServerInformation();
|
||||
}
|
||||
else if (e.Parameter is CustomServerInformation customServerInformation)
|
||||
{
|
||||
// Only server information is passed.
|
||||
serverInformationToTest = customServerInformation;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await TryTestConnectionAsync(customServerInformation);
|
||||
await TryTestConnectionAsync(serverInformationToTest);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Analytics.TrackEvent("IMAP Test Failed", new Dictionary<string, string>()
|
||||
{
|
||||
{ "Server", customServerInformation.IncomingServer },
|
||||
{ "Port", customServerInformation.IncomingServerPort },
|
||||
});
|
||||
string protocolLog = ex is ImapClientPoolException clientPoolException ? clientPoolException.ProtocolLog : string.Empty;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(AdvancedImapSetupPage),
|
||||
new AutoDiscoveryConnectionTestFailedPackage(ex)));
|
||||
var failurePackage = new ImapConnectionFailedPackage(ex, protocolLog, autoDiscoverySettings);
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(ImapConnectionFailedPage), failurePackage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,32 +1,51 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Exceptions;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.AutoDiscovery;
|
||||
using Wino.Core.Messages.Mails;
|
||||
using Wino.Extensions;
|
||||
|
||||
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace Wino.Views.ImapSetup
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class WelcomeImapSetupPage : Page
|
||||
{
|
||||
private AutoDiscoveryConnectionTestFailedPackage failedPackage;
|
||||
private ImapConnectionFailedPackage failedPackage;
|
||||
private readonly IAutoDiscoveryService _autoDiscoveryService = App.Current.Services.GetService<IAutoDiscoveryService>();
|
||||
|
||||
public WelcomeImapSetupPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Enabled;
|
||||
}
|
||||
|
||||
private void SignInClicked(object sender, RoutedEventArgs e)
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
failedPackage = null;
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
AutoDiscoveryPanel.Visibility = Visibility.Collapsed;
|
||||
MainSetupPanel.Visibility = Visibility.Visible;
|
||||
|
||||
if (e.Parameter is MailAccount accountProperties)
|
||||
{
|
||||
DisplayNameBox.Text = accountProperties.Name;
|
||||
}
|
||||
}
|
||||
|
||||
private async void SignInClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
MainSetupPanel.Visibility = Visibility.Collapsed;
|
||||
AutoDiscoveryPanel.Visibility = Visibility.Visible;
|
||||
|
||||
// Let users see the discovery message for a while...
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
var minimalSettings = new AutoDiscoveryMinimalSettings()
|
||||
{
|
||||
@@ -35,32 +54,27 @@ namespace Wino.Views.ImapSetup
|
||||
Email = AddressBox.Text,
|
||||
};
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupNavigationRequested(typeof(AutoDiscoveryPage), minimalSettings));
|
||||
}
|
||||
var discoverySettings = await _autoDiscoveryService.GetAutoDiscoverySettings(minimalSettings);
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if (e.Parameter is string errorMessage)
|
||||
if (discoverySettings == null)
|
||||
{
|
||||
ErrorMessageText.Text = errorMessage;
|
||||
// Couldn't find settings.
|
||||
|
||||
MainScrollviewer.ScrollToElement(ErrorMessageText);
|
||||
var failurePackage = new ImapConnectionFailedPackage(new Exception(Translator.Exception_ImapAutoDiscoveryFailed), string.Empty, discoverySettings);
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(ImapConnectionFailedPage), failurePackage));
|
||||
}
|
||||
else if (e.Parameter is AutoDiscoveryConnectionTestFailedPackage autoDiscoveryConnectionTestFailedPackage)
|
||||
else
|
||||
{
|
||||
failedPackage = autoDiscoveryConnectionTestFailedPackage;
|
||||
ErrorMessageText.Text = $"Discovery was successful but connection to the server failed.{Environment.NewLine}{Environment.NewLine}{autoDiscoveryConnectionTestFailedPackage.Error.Message}";
|
||||
// Settings are found. Test the connection with the given password.
|
||||
|
||||
MainScrollviewer.ScrollToElement(ErrorMessageText);
|
||||
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 CancelClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested());
|
||||
|
||||
private void AdvancedConfigurationClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user