Skip to content

Commit

Permalink
💄 网络测试子页面ViewModel重构
Browse files Browse the repository at this point in the history
💄 网络测试子页面NAT检查部分逻辑重构

💄 网络测试子页面ViewModel重构

💄 网络测试子页面NAT检查部分逻辑重构

💄代码Clean,没有引用到的名称空间与枚举类型

💄 网络测试子页面NAT检查部分逻辑重构

💄 网络测试子页面ViewModel重构

💄 网络测试子页面NAT检查部分逻辑重构

💄代码Clean,没有引用到的名称空间与枚举类型
  • Loading branch information
luojunyuan committed Oct 8, 2024
1 parent 95c10da commit 499edb4
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 62 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
using AppResources = BD.WTTS.Client.Resources.Strings;

using BD.WTTS.UI.Views.Pages;
using STUN.StunResult;
using STUN.Enums;
using AppResources = BD.WTTS.Client.Resources.Strings;

namespace BD.WTTS.UI.ViewModels;

Expand Down Expand Up @@ -78,22 +75,47 @@ public AcceleratorPageViewModel()
var natCheckResult = await networkTestService.TestStunClient3489Async(testServerHostName: SelectedSTUNAddress) ?? new ClassicStunResult { NatType = NatType.Unknown };
var (netSucc, _) = await networkTestService.TestOpenUrlAsync("https://www.baidu.com");

var natStatus = natCheckResult.NatType switch
var publicEndPoint = natCheckResult.PublicEndPoint?.Address.ToString() ?? "Unknown";
var localEndPoint = natCheckResult.LocalEndPoint?.Address.ToString() ?? "Unknown";

var (natLevel, natTypeTip) = natCheckResult.NatType switch
{
NatType.OpenInternet or NatType.FullCone => NatTypeSimple.Open,
NatType.RestrictedCone or NatType.PortRestrictedCone or NatType.SymmetricUdpFirewall => NatTypeSimple.Moderate,
NatType.Symmetric or NatType.UdpBlocked => NatTypeSimple.Strict,
NatType.Unknown or NatType.UnsupportedServer or _ => NatTypeSimple.Unknown,
// Open
NatType.OpenInternet or NatType.FullCone => ("开放 NAT", "您可与在其网络上具有任意 NAT 类型的用户玩多人游戏和发起多人游戏。"),
// Moderate
NatType.RestrictedCone or NatType.PortRestrictedCone or NatType.SymmetricUdpFirewall => ("中等 NAT", "您可与一些用户玩多人游戏;但是,并且通常你将不会被选为比赛的主持人。"),
// Strict
NatType.Symmetric or NatType.UdpBlocked => ("严格 NAT", "您只能与具有开放 NAT 类型的用户玩多人游戏。您不能被选为比赛的主持人。"),
// Unknown
NatType.Unknown or NatType.UnsupportedServer or _ => ("不可用 NAT", "如果 NAT 不可用,您将无法使用群聊天或连接到某些 Xbox 游戏的多人游戏。"),
};

PublicEndPoint = natCheckResult.PublicEndPoint?.Address.ToString() ?? "Unknown";
LocalEndPoint = natCheckResult.LocalEndPoint?.Address.ToString() ?? "Unknown";

return (natStatus, netSucc);
return new NATFetchResult(publicEndPoint, localEndPoint, natLevel, natTypeTip, netSucc);
});
NATCheckCommand
.IsExecuting
.ToPropertyEx(this, x => x.IsNATChecking);
NATCheckCommand
.Select(x => x.PublicEndPoint)
.ToPropertyEx(this, x => x.PublicEndPoint);
NATCheckCommand
.Select(x => x.LocalEndPoint)
.ToPropertyEx(this, x => x.LocalEndPoint);
NATCheckCommand
.Select(x => x.NATLevel)
.ToPropertyEx(this, x => x.NATLevel);
NATCheckCommand
.Select(x => x.NATTypeTip)
.ToPropertyEx(this, x => x.NATTypeTip);

var hidePingResultStream = NATCheckCommand
.IsExecuting
.Where(x => x == true)
.Select(x => PingStatus.Blank);
NATCheckCommand
.Select(x => x.PingResult == true ? PingStatus.Ok : PingStatus.Error)
.Merge(hidePingResultStream)
.ToPropertyEx(this, x => x.PingResultStatus);

DNSCheckCommand = ReactiveCommand.CreateFromTask(async () =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ namespace BD.WTTS.UI.ViewModels;

public sealed partial class AcceleratorPageViewModel : TabItemViewModel
{
public enum NatTypeSimple
public record NATFetchResult(string PublicEndPoint, string LocalEndPoint, string NATLevel, string NATTypeTip, bool PingResult);

public enum PingStatus
{
Unknown,
Open,
Moderate,
Strict,
Blank,
Ok,
Error,
}

public override string Name => Strings.Welcome;
Expand All @@ -25,6 +26,21 @@ public enum NatTypeSimple
"stun.miwifi.com",
];

[ObservableAsProperty]
public string NATLevel { get; } = string.Empty;

[ObservableAsProperty]
public string NATTypeTip { get; } = string.Empty;

[ObservableAsProperty]
public string LocalEndPoint { get; set; } = string.Empty;

[ObservableAsProperty]
public string PublicEndPoint { get; set; } = string.Empty;

[ObservableAsProperty]
public PingStatus PingResultStatus { get; }

[ObservableAsProperty]
public bool IsNATChecking { get; }

Expand All @@ -40,12 +56,6 @@ public enum NatTypeSimple
[Reactive]
public ReadOnlyCollection<ProxyDomainGroupViewModel>? EnableProxyDomainGroupVMs { get; set; }

[Reactive]
public string LocalEndPoint { get; set; } = string.Empty;

[Reactive]
public string PublicEndPoint { get; set; } = string.Empty;

[Reactive]
public string DNSTestDelay { get; set; } = string.Empty;

Expand All @@ -58,7 +68,7 @@ public enum NatTypeSimple
[Reactive]
public bool IsSupportIPv6 { get; set; }

public ReactiveCommand<Unit, (NatTypeSimple Nat, bool PingSuccess)> NATCheckCommand { get; }
public ReactiveCommand<Unit, NATFetchResult> NATCheckCommand { get; }

public ReactiveCommand<Unit, Unit> DNSCheckCommand { get; }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
using STUN.Enums;
using STUN.StunResult;
using System.Reactive;

namespace BD.WTTS.UI.ViewModels;

public class NetworkCheckControlViewModel : ViewModelBase
{
private readonly INetworkTestService _networkTestService = INetworkTestService.Instance;

public record NATFetchResult(string PublicEndPoint, string LocalEndPoint, string NATLevel, string NATTypeTip, bool PingResult);

[Reactive]
public string SelectedSTUNAddress { get; set; }

public string[] STUNAddress { get; } =
[
"stun.syncthing.net",
"stun.hot-chilli.net",
"stun.fitauto.ru",
"stun.miwifi.com",
];

[ObservableAsProperty]
public string NATLevel { get; } = string.Empty;

[ObservableAsProperty]
public string NATTypeTip { get; } = string.Empty;

[ObservableAsProperty]
public string LocalEndPoint { get; } = string.Empty;

[ObservableAsProperty]
public string PublicEndPoint { get; } = string.Empty;

[ObservableAsProperty]
public bool PingOkVisible { get; }

[ObservableAsProperty]
public bool PingErrorVisible { get; }

[ObservableAsProperty]
public bool IsNATChecking { get; }

[ObservableAsProperty]
public bool IsDNSChecking { get; }

[ObservableAsProperty]
public bool IsIPv6Checking { get; }

[Reactive]
public string DomainPendingTest { get; set; } = string.Empty;

[Reactive]
public string DNSTestDelay { get; set; } = string.Empty;

[Reactive]
public string DNSTestResult { get; set; } = string.Empty;

[Reactive]
public string IPv6Address { get; set; } = string.Empty;

[Reactive]
public bool IsSupportIPv6 { get; set; }

public ReactiveCommand<Unit, NATFetchResult> NATCheckCommand { get; }

public ReactiveCommand<Unit, Unit> DNSCheckCommand { get; }

public ReactiveCommand<Unit, Unit> IPv6CheckCommand { get; }

public NetworkCheckControlViewModel()
{
SelectedSTUNAddress = STUNAddress[0];

NATCheckCommand = ReactiveCommand.CreateFromTask(async () =>
{
var natCheckResult = await _networkTestService.TestStunClient3489Async(testServerHostName: SelectedSTUNAddress) ?? new ClassicStunResult { NatType = NatType.Unknown };
var (netSucc, _) = await _networkTestService.TestOpenUrlAsync("https://www.baidu.com");

var publicEndPoint = natCheckResult.PublicEndPoint?.Address.ToString() ?? "Unknown";
var localEndPoint = natCheckResult.LocalEndPoint?.Address.ToString() ?? "Unknown";

var (natLevel, natTypeTip) = natCheckResult.NatType switch
{
// Open
NatType.OpenInternet or NatType.FullCone => ("开放 NAT", "您可与在其网络上具有任意 NAT 类型的用户玩多人游戏和发起多人游戏。"),
// Moderate
NatType.RestrictedCone or NatType.PortRestrictedCone or NatType.SymmetricUdpFirewall => ("中等 NAT", "您可与一些用户玩多人游戏;但是,并且通常你将不会被选为比赛的主持人。"),
// Strict
NatType.Symmetric or NatType.UdpBlocked => ("严格 NAT", "您只能与具有开放 NAT 类型的用户玩多人游戏。您不能被选为比赛的主持人。"),
// Unknown
NatType.Unknown or NatType.UnsupportedServer or _ => ("不可用 NAT", "如果 NAT 不可用,您将无法使用群聊天或连接到某些 Xbox 游戏的多人游戏。"),
};

return new NATFetchResult(publicEndPoint, localEndPoint, natLevel, natTypeTip, netSucc);
});
NATCheckCommand
.IsExecuting
.ToPropertyEx(this, x => x.IsNATChecking);
NATCheckCommand
.Select(x => x.PublicEndPoint)
.ToPropertyEx(this, x => x.PublicEndPoint);
NATCheckCommand
.Select(x => x.LocalEndPoint)
.ToPropertyEx(this, x => x.LocalEndPoint);
NATCheckCommand
.Select(x => x.NATLevel)
.Merge(Observable.Return("未知"))
.ToPropertyEx(this, x => x.NATLevel);
NATCheckCommand
.Select(x => x.NATTypeTip)
.Merge(Observable.Return("未知类型"))
.ToPropertyEx(this, x => x.NATTypeTip);

var hidePingResultStream = NATCheckCommand
.IsExecuting
.Where(x => x == true)
.Select(x => false);
NATCheckCommand
.Select(x => x.PingResult == true)
.Merge(hidePingResultStream)
.ToPropertyEx(this, x => x.PingOkVisible);
NATCheckCommand
.Select(x => x.PingResult == false)
.Merge(hidePingResultStream)
.ToPropertyEx(this, x => x.PingErrorVisible);

DNSCheckCommand = ReactiveCommand.CreateFromTask(async () =>
{
var testDomain = DomainPendingTest == string.Empty ? "store.steampowered.com" : DomainPendingTest;
try
{
long delayMs;
IPAddress[] address;
if (ProxySettings.UseDoh)
{
var configDoh = ProxySettings.CustomDohAddres2.Value ?? ProxySettingsWindowViewModel.DohAddress.FirstOrDefault() ?? string.Empty;
(delayMs, address) = await _networkTestService.TestDNSOverHttpsAsync(testDomain, configDoh);
}
else
{
var configDns = ProxySettings.ProxyMasterDns.Value ?? string.Empty;
(delayMs, address) = await _networkTestService.TestDNSAsync(testDomain, configDns, 53);
}
if (address.Length == 0)
throw new Exception("Parsing failed. Return empty ip address.");

DNSTestDelay = delayMs + "ms ";
DNSTestResult = string.Empty + address.FirstOrDefault();
}
catch (Exception ex)
{
Log.Error(nameof(AcceleratorPageViewModel), ex.ToString());
DNSTestDelay = string.Empty;
DNSTestResult = "error";
}
});
DNSCheckCommand
.IsExecuting
.ToPropertyEx(this, x => x.IsDNSChecking);

IPv6CheckCommand = ReactiveCommand.CreateFromTask(async () =>
{
var result = await IMicroServiceClient.Instance.Accelerate.GetMyIP(ipV6: true);
if (result.IsSuccess)
{
IsSupportIPv6 = true;
IPv6Address = result.Content ?? string.Empty;
}
else
{
IsSupportIPv6 = false;
IPv6Address = string.Empty;
}
});
IPv6CheckCommand
.IsExecuting
.ToPropertyEx(this, x => x.IsIPv6Checking);

IPv6CheckCommand.Execute().Subscribe();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
xmlns:ui="using:FluentAvalonia.UI.Controls"
d:DesignHeight="450"
d:DesignWidth="800"
x:DataType="spp:AcceleratorPageViewModel"
x:DataType="spp:NetworkCheckControlViewModel"
mc:Ignorable="d">
<ScrollViewer
Padding="0,0,10,0"
Expand Down Expand Up @@ -42,7 +42,6 @@
x:Name="NATCheckButton"
Width="100"
Margin="0,0,0,4"
x:FieldModifier="public"
Command="{Binding NATCheckCommand}"
Content="检测"
IsEnabled="True" />
Expand All @@ -63,14 +62,14 @@
x:Name="NATTextBlock"
VerticalAlignment="Center"
Foreground="Gray"
Text="未知" />
Text="{Binding NATLevel}" />
<ui:FontIcon
Margin="3,0"
FontSize="20"
Glyph="&#xE946;"
ToolTip.ShowDelay="0">
<ToolTip.Tip>
<TextBlock x:Name="NATTypeTip" Text="未知类型" />
<TextBlock x:Name="NATTypeTip" Text="{Binding NATTypeTip}" />
</ToolTip.Tip>
</ui:FontIcon>
</DockPanel>
Expand Down Expand Up @@ -126,13 +125,13 @@
<ui:FontIcon
x:Name="PingOK"
HorizontalAlignment="Right"
x:FieldModifier="public"
FontFamily="{StaticResource SymbolThemeFontFamily}"
FontSize="26"
Foreground="Green"
Glyph="&#xe73d;" />
Glyph="&#xe73d;"
IsVisible="{Binding PingOkVisible}" />

<Grid x:Name="PingError" x:FieldModifier="public">
<Grid x:Name="PingError" IsVisible="{Binding PingErrorVisible}">
<ui:FontIcon
Margin="3"
HorizontalAlignment="Right"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Org.BouncyCastle.Asn1.Utilities;

namespace BD.WTTS.UI.Views.Pages;

public partial class NetworkCheck : UserControl
public partial class NetworkCheck : UserControl, IViewFor<AcceleratorPageViewModel>
{
public NetworkCheck()
{
InitializeComponent();

Loaded += (_, _) =>
{
ViewModel = DataContext as AcceleratorPageViewModel;

this.OneWayBind(ViewModel, vm => vm.PingResultStatus, v => v.PingOK.IsVisible, result => result == AcceleratorPageViewModel.PingStatus.Ok);
this.OneWayBind(ViewModel, vm => vm.PingResultStatus, v => v.PingError.IsVisible, result => result == AcceleratorPageViewModel.PingStatus.Error);
this.OneWayBind(ViewModel, vm => vm.NATLevel, v => v.NATTextBlock.Text);
this.OneWayBind(ViewModel, vm => vm.NATTypeTip, v => v.NATTypeTip.Text);
};
}

public AcceleratorPageViewModel? ViewModel { get; set; }

object? IViewFor.ViewModel { get; set; }
}
Loading

0 comments on commit 499edb4

Please sign in to comment.