mirror of
https://github.com/wangdage12/Snap.Hutao.git
synced 2026-07-31 04:10:25 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 96970e7f4d | |||
| 9200f44e33 | |||
| 2e17a50e6f | |||
| 27871adc63 | |||
| 41cfb8c8f4 |
@@ -95,7 +95,7 @@ jobs:
|
||||
|
||||
- name: Cache NuGet packages
|
||||
if: ${{ needs.select-runner.outputs.runner == 'windows-latest' }}
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/Snap.Hutao.csproj') }}
|
||||
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
|
||||
- name: Cache NuGet packages
|
||||
if: ${{ steps.merge.outputs.continue == 'true' }}
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/Snap.Hutao.csproj') }}
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<IsPackable>true</IsPackable>
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>Snap.Hutao.SourceGeneration</PackageId>
|
||||
<Version>1.3.14</Version>
|
||||
<Version>1.3.15</Version>
|
||||
<Authors>DGP Studio</Authors>
|
||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||
<DevelopmentDependency>true</DevelopmentDependency>
|
||||
|
||||
+3
-1
@@ -4,6 +4,7 @@
|
||||
using JetBrains.Annotations;
|
||||
using Snap.Hutao.Core.IO.Http;
|
||||
using Snap.Hutao.Core.IO.Http.Proxy;
|
||||
using Snap.Hutao.Service;
|
||||
using Snap.Hutao.Service.Game.Package.Advanced;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
using Snap.Hutao.Win32;
|
||||
@@ -36,10 +37,11 @@ internal static partial class ServiceCollectionExtension
|
||||
{
|
||||
SocketsHttpHandler typedHandler = Unsafe.As<SocketsHttpHandler>(handler);
|
||||
typedHandler.UseProxy = true;
|
||||
typedHandler.Proxy = HttpProxyUsingSystemProxy.Instance;
|
||||
typedHandler.Proxy = provider.GetRequiredService<HutaoWebProxy>();
|
||||
})
|
||||
.AddHttpMessageHandler<RetryHttpHandler>();
|
||||
})
|
||||
.AddSingleton(sp => new HutaoWebProxy(sp.GetRequiredService<AppOptions>(), HttpProxyUsingSystemProxy.Instance))
|
||||
.AddHttpClients();
|
||||
|
||||
services
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Service;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Snap.Hutao.Core.IO.Http.Proxy;
|
||||
|
||||
internal sealed class HutaoWebProxy : IWebProxy
|
||||
{
|
||||
private static readonly IWebProxy NoProxy = new DirectWebProxy();
|
||||
private readonly AppOptions appOptions;
|
||||
private readonly HttpProxyUsingSystemProxy systemProxy;
|
||||
|
||||
public HutaoWebProxy(AppOptions appOptions, HttpProxyUsingSystemProxy systemProxy)
|
||||
{
|
||||
this.appOptions = appOptions;
|
||||
this.systemProxy = systemProxy;
|
||||
}
|
||||
|
||||
public ICredentials? Credentials
|
||||
{
|
||||
get => InnerProxy.Credentials;
|
||||
set => InnerProxy.Credentials = value;
|
||||
}
|
||||
|
||||
public string DisplayProxyUri
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!appOptions.ProxyEnabled.Value)
|
||||
{
|
||||
return "DIRECT";
|
||||
}
|
||||
|
||||
return appOptions.ProxyType.Value switch
|
||||
{
|
||||
ProxyType.SystemProxy => systemProxy.DisplayProxyUri,
|
||||
ProxyType.Http => string.IsNullOrEmpty(appOptions.ProxyAddress.Value)
|
||||
? systemProxy.DisplayProxyUri
|
||||
: $"http://{appOptions.ProxyAddress.Value}:{appOptions.ProxyPort.Value}",
|
||||
ProxyType.Socks5 => string.IsNullOrEmpty(appOptions.ProxyAddress.Value)
|
||||
? systemProxy.DisplayProxyUri
|
||||
: $"socks5://{appOptions.ProxyAddress.Value}:{appOptions.ProxyPort.Value}",
|
||||
ProxyType.None => "DIRECT",
|
||||
_ => systemProxy.DisplayProxyUri,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private IWebProxy InnerProxy
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!appOptions.ProxyEnabled.Value)
|
||||
{
|
||||
return NoProxy;
|
||||
}
|
||||
|
||||
return appOptions.ProxyType.Value switch
|
||||
{
|
||||
ProxyType.SystemProxy => systemProxy,
|
||||
ProxyType.Http => CreateHttpProxy(),
|
||||
ProxyType.Socks5 => CreateSocks5Proxy(),
|
||||
ProxyType.None => NoProxy,
|
||||
_ => systemProxy,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public Uri? GetProxy(Uri destination)
|
||||
{
|
||||
return InnerProxy.GetProxy(destination);
|
||||
}
|
||||
|
||||
public bool IsBypassed(Uri host)
|
||||
{
|
||||
return InnerProxy.IsBypassed(host);
|
||||
}
|
||||
|
||||
private IWebProxy CreateHttpProxy()
|
||||
{
|
||||
string address = appOptions.ProxyAddress.Value;
|
||||
int port = appOptions.ProxyPort.Value;
|
||||
|
||||
if (!IsValidProxyAddress(address) || !IsValidProxyPort(port))
|
||||
{
|
||||
return systemProxy;
|
||||
}
|
||||
|
||||
WebProxy webProxy = new()
|
||||
{
|
||||
Address = new Uri($"http://{address}:{port}"),
|
||||
BypassProxyOnLocal = false,
|
||||
UseDefaultCredentials = false,
|
||||
};
|
||||
|
||||
return webProxy;
|
||||
}
|
||||
|
||||
private IWebProxy CreateSocks5Proxy()
|
||||
{
|
||||
string address = appOptions.ProxyAddress.Value;
|
||||
int port = appOptions.ProxyPort.Value;
|
||||
|
||||
if (!IsValidProxyAddress(address) || !IsValidProxyPort(port))
|
||||
{
|
||||
return systemProxy;
|
||||
}
|
||||
|
||||
return new Socks5WebProxy(address, port);
|
||||
}
|
||||
|
||||
private static bool IsValidProxyAddress(string address)
|
||||
{
|
||||
return !string.IsNullOrEmpty(address) &&
|
||||
(Uri.CheckHostName(address) != UriHostNameType.Unknown ||
|
||||
System.Net.IPAddress.TryParse(address, out _));
|
||||
}
|
||||
|
||||
private static bool IsValidProxyPort(int port)
|
||||
{
|
||||
return port is >= 1 and <= 65535;
|
||||
}
|
||||
|
||||
private sealed class DirectWebProxy : IWebProxy
|
||||
{
|
||||
public ICredentials? Credentials { get; set; }
|
||||
|
||||
public Uri? GetProxy(Uri destination) => destination;
|
||||
|
||||
public bool IsBypassed(Uri host) => true;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class Socks5WebProxy : IWebProxy
|
||||
{
|
||||
private readonly string address;
|
||||
private readonly int port;
|
||||
|
||||
public Socks5WebProxy(string address, int port)
|
||||
{
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public ICredentials? Credentials { get; set; }
|
||||
|
||||
public Uri? GetProxy(Uri destination)
|
||||
{
|
||||
return new Uri($"socks5://{address}:{port}");
|
||||
}
|
||||
|
||||
public bool IsBypassed(Uri host)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -152,4 +152,10 @@ internal static class SettingKeys
|
||||
public const string CompactWebView2WindowInactiveOpacity = "Snap::Hutao::Web::WebView::Compact::InactiveOpacity";
|
||||
public const string CompactWebView2WindowPreviousSourceUrl = "Snap::Hutao::Web::WebView::Compact::PreviousSourceUrl";
|
||||
public const string WebView2VideoFastForwardOrRewindSeconds = "Snap::Hutao::Web::WebView::Video::FastForwardOrRewind::Seconds";
|
||||
|
||||
// Proxy
|
||||
public const string ProxyType = "Snap::Hutao::Web::Proxy::Type";
|
||||
public const string ProxyAddress = "Snap::Hutao::Web::Proxy::Address";
|
||||
public const string ProxyPort = "Snap::Hutao::Web::Proxy::Port";
|
||||
public const string ProxyEnabled = "Snap::Hutao::Web::Proxy::Enabled";
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
[ExtendedEnum]
|
||||
internal enum ProxyType
|
||||
{
|
||||
[LocalizationKey(nameof(SH.ServiceProxyTypeNone))]
|
||||
None = 0,
|
||||
|
||||
[LocalizationKey(nameof(SH.ServiceProxyTypeSystemProxy))]
|
||||
SystemProxy = 1,
|
||||
|
||||
[LocalizationKey(nameof(SH.ServiceProxyTypeHttp))]
|
||||
Http = 2,
|
||||
|
||||
[LocalizationKey(nameof(SH.ServiceProxyTypeSocks5))]
|
||||
Socks5 = 3,
|
||||
}
|
||||
@@ -875,6 +875,18 @@
|
||||
<data name="ServiceBackgroundImageTypeNone" xml:space="preserve">
|
||||
<value>无背景图片</value>
|
||||
</data>
|
||||
<data name="ServiceProxyTypeNone" xml:space="preserve">
|
||||
<value>无代理</value>
|
||||
</data>
|
||||
<data name="ServiceProxyTypeSystemProxy" xml:space="preserve">
|
||||
<value>系统代理</value>
|
||||
</data>
|
||||
<data name="ServiceProxyTypeHttp" xml:space="preserve">
|
||||
<value>HTTP 代理</value>
|
||||
</data>
|
||||
<data name="ServiceProxyTypeSocks5" xml:space="preserve">
|
||||
<value>SOCKS5 代理</value>
|
||||
</data>
|
||||
<data name="ServiceCloseButtonBehaviorTypeExit" xml:space="preserve">
|
||||
<value>退出胡桃</value>
|
||||
</data>
|
||||
@@ -2255,6 +2267,24 @@
|
||||
<data name="ViewModelSettingDeleteServerCacheFolderTitle" xml:space="preserve">
|
||||
<value>删除转换服务器游戏客户端缓存</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingProxySaveSuccess" xml:space="preserve">
|
||||
<value>代理设置已保存</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingProxyTesting" xml:space="preserve">
|
||||
<value>正在测试代理连接...</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingProxyTestSuccess" xml:space="preserve">
|
||||
<value>可用 [{0}ms]</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingProxyTestFailed" xml:space="preserve">
|
||||
<value>不可用</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingProxyTestAction" xml:space="preserve">
|
||||
<value>测试</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingProxySaveAction" xml:space="preserve">
|
||||
<value>保存</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingFolderSizeDescription" xml:space="preserve">
|
||||
<value>已使用磁盘空间:{0}</value>
|
||||
</data>
|
||||
@@ -3330,6 +3360,33 @@
|
||||
<data name="ViewPageSettingGeetestVerificationHeader" xml:space="preserve">
|
||||
<value>无感验证</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingProxyHeader" xml:space="preserve">
|
||||
<value>网络代理</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingProxyEnabledDescription" xml:space="preserve">
|
||||
<value>启用自定义代理设置</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingProxyEnabledHeader" xml:space="preserve">
|
||||
<value>启用代理</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingProxyTypeDescription" xml:space="preserve">
|
||||
<value>选择代理类型</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingProxyTypeHeader" xml:space="preserve">
|
||||
<value>代理类型</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingProxyAddressDescription" xml:space="preserve">
|
||||
<value>代理服务器地址</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingProxyAddressHeader" xml:space="preserve">
|
||||
<value>代理地址</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingProxyPortDescription" xml:space="preserve">
|
||||
<value>代理服务器端口</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingProxyPortHeader" xml:space="preserve">
|
||||
<value>代理端口</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHomeAnnouncementRegionDescription" xml:space="preserve">
|
||||
<value>选择想要获取公告的游戏服务器</value>
|
||||
</data>
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core.Property;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Service.BackgroundImage;
|
||||
using Snap.Hutao.UI.Xaml.Media.Backdrop;
|
||||
@@ -49,6 +50,8 @@ internal sealed partial class AppOptions : DbStoreOptions
|
||||
|
||||
public ImmutableArray<NameValue<BridgeShareSaveType>> BridgeShareSaveTypes { get; } = ImmutableCollectionsNameValue.FromEnum<BridgeShareSaveType>(type => type.GetLocalizedDescription(SH.ResourceManager, CultureInfo.CurrentCulture) ?? string.Empty);
|
||||
|
||||
public ImmutableArray<NameValue<ProxyType>> ProxyTypes { get; } = ImmutableCollectionsNameValue.FromEnum<ProxyType>(static @enum => @enum.GetLocalizedDescription(SH.ResourceManager, CultureInfo.CurrentCulture) ?? string.Empty);
|
||||
|
||||
public ImmutableArray<NameValue<LastWindowCloseBehavior>> LastWindowCloseBehaviors { get; } = ImmutableCollectionsNameValue.FromEnum<LastWindowCloseBehavior>(static @enum => @enum.GetLocalizedDescription(SH.ResourceManager, CultureInfo.CurrentCulture) ?? string.Empty);
|
||||
|
||||
[field: MaybeNull]
|
||||
@@ -83,4 +86,16 @@ internal sealed partial class AppOptions : DbStoreOptions
|
||||
|
||||
[field: MaybeNull]
|
||||
public IObservableProperty<LastWindowCloseBehavior> LastWindowCloseBehavior { get => field ??= CreateProperty(SettingKeys.LastWindowCloseBehavior, Service.LastWindowCloseBehavior.EnsureNotifyIconCreated); }
|
||||
|
||||
[field: MaybeNull]
|
||||
public IObservableProperty<ProxyType> ProxyType { get => field ??= CreateProperty(SettingKeys.ProxyType, Model.Intrinsic.ProxyType.SystemProxy); }
|
||||
|
||||
[field: MaybeNull]
|
||||
public IObservableProperty<string> ProxyAddress { get => field ??= CreateProperty(SettingKeys.ProxyAddress, string.Empty); }
|
||||
|
||||
[field: MaybeNull]
|
||||
public IObservableProperty<int> ProxyPort { get => field ??= CreateProperty(SettingKeys.ProxyPort, 1080); }
|
||||
|
||||
[field: MaybeNull]
|
||||
public IObservableProperty<bool> ProxyEnabled { get => field ??= CreateProperty(SettingKeys.ProxyEnabled, false); }
|
||||
}
|
||||
@@ -210,6 +210,78 @@
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
<!-- 网络代理 -->
|
||||
<Border cw:Effects.Shadow="{ThemeResource CompatCardShadow}" DataContext="{Binding Proxy}">
|
||||
<Border Padding="16" Style="{ThemeResource AcrylicBorderCardStyle}">
|
||||
<StackPanel Spacing="{ThemeResource SettingsCardSpacing}">
|
||||
<TextBlock Style="{StaticResource SettingsCardHeaderTextBlockStyle}" Text="{shuxm:ResourceString Name=ViewPageSettingProxyHeader}"/>
|
||||
<cwc:SettingsCard
|
||||
Description="{shuxm:ResourceString Name=ViewPageSettingProxyEnabledDescription}"
|
||||
Header="{shuxm:ResourceString Name=ViewPageSettingProxyEnabledHeader}"
|
||||
HeaderIcon="{shuxm:FontIcon Glyph=}">
|
||||
<ToggleSwitch
|
||||
MinWidth="120"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{Binding IsTestingProxy, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
|
||||
IsOn="{Binding AppOptions.ProxyEnabled.Value, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard
|
||||
Description="{shuxm:ResourceString Name=ViewPageSettingProxyTypeDescription}"
|
||||
Header="{shuxm:ResourceString Name=ViewPageSettingProxyTypeHeader}"
|
||||
HeaderIcon="{shuxm:FontIcon Glyph=}">
|
||||
<shuxc:SizeRestrictedContentControl>
|
||||
<ComboBox
|
||||
DisplayMemberPath="Name"
|
||||
ItemsSource="{Binding AppOptions.ProxyTypes}"
|
||||
IsEnabled="{Binding IsTestingProxy, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
|
||||
SelectedItem="{Binding SelectedProxyType, Mode=TwoWay}"/>
|
||||
</shuxc:SizeRestrictedContentControl>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard
|
||||
Description="{shuxm:ResourceString Name=ViewPageSettingProxyAddressDescription}"
|
||||
Header="{shuxm:ResourceString Name=ViewPageSettingProxyAddressHeader}"
|
||||
HeaderIcon="{shuxm:FontIcon Glyph=}">
|
||||
<TextBox
|
||||
MinWidth="{ThemeResource SettingsCardContentControlMinWidth}"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{Binding IsTestingProxy, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
|
||||
Text="{Binding AppOptions.ProxyAddress.Value, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard
|
||||
Description="{shuxm:ResourceString Name=ViewPageSettingProxyPortDescription}"
|
||||
Header="{shuxm:ResourceString Name=ViewPageSettingProxyPortHeader}"
|
||||
HeaderIcon="{shuxm:FontIcon Glyph=}">
|
||||
<NumberBox
|
||||
MinWidth="{ThemeResource SettingsCardContentControlMinWidth}"
|
||||
VerticalAlignment="Center"
|
||||
AcceptsExpression="False"
|
||||
IsEnabled="{Binding IsTestingProxy, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
|
||||
Maximum="65535"
|
||||
Minimum="1"
|
||||
Value="{Binding AppOptions.ProxyPort.Value, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard
|
||||
Header="{shuxm:ResourceString Name=ViewModelSettingProxyTestAction}"
|
||||
HeaderIcon="{shuxm:FontIcon Glyph=}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button
|
||||
Command="{Binding TestProxyCommand}"
|
||||
Content="{shuxm:ResourceString Name=ViewModelSettingProxyTestAction}"
|
||||
IsEnabled="{Binding IsTestingProxy, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"/>
|
||||
<Button
|
||||
Command="{Binding SaveProxyCommand}"
|
||||
Content="{shuxm:ResourceString Name=ViewModelSettingProxySaveAction}"
|
||||
IsEnabled="{Binding IsTestingProxy, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"/>
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding ProxyTestResult}"
|
||||
Visibility="{Binding ProxyTestResult, Converter={StaticResource EmptyObjectToVisibilityConverter}}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
<!-- 外观 -->
|
||||
<Border cw:Effects.Shadow="{ThemeResource CompatCardShadow}" DataContext="{Binding Appearance}">
|
||||
<Border Padding="16" Style="{ThemeResource AcrylicBorderCardStyle}">
|
||||
|
||||
@@ -33,7 +33,7 @@ internal sealed partial class FeedbackViewModel : Abstraction.ViewModel
|
||||
[GeneratedConstructor]
|
||||
public partial FeedbackViewModel(IServiceProvider serviceProvider);
|
||||
|
||||
public static HttpProxyUsingSystemProxy DynamicHttpProxy { get => HttpProxyUsingSystemProxy.Instance; }
|
||||
public partial HutaoWebProxy DynamicHttpProxy { get; }
|
||||
|
||||
public partial RuntimeOptions RuntimeOptions { get; }
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.IO.Http.Proxy;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Service;
|
||||
using Snap.Hutao.Service.Notification;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Snap.Hutao.ViewModel.Setting;
|
||||
|
||||
[BindableCustomPropertyProvider]
|
||||
[Service(ServiceLifetime.Scoped)]
|
||||
internal sealed partial class SettingProxyViewModel : Abstraction.ViewModel
|
||||
{
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly IMessenger messenger;
|
||||
private readonly HutaoWebProxy hutaoWebProxy;
|
||||
|
||||
[GeneratedConstructor]
|
||||
public partial SettingProxyViewModel(IServiceProvider serviceProvider);
|
||||
|
||||
public partial AppOptions AppOptions { get; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string? ProxyTestResult { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsTestingProxy { get; set; }
|
||||
|
||||
public NameValue<ProxyType>? SelectedProxyType
|
||||
{
|
||||
get => field ??= AppOptions.ProxyTypes.Single(t => t.Value == AppOptions.ProxyType.Value);
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref field, value) && value is not null)
|
||||
{
|
||||
AppOptions.ProxyType.Value = value.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Command("SaveProxyCommand")]
|
||||
private void SaveProxy()
|
||||
{
|
||||
messenger.Send(InfoBarMessage.Success(SH.ViewModelSettingProxySaveSuccess));
|
||||
}
|
||||
|
||||
[Command("TestProxyCommand")]
|
||||
private async Task TestProxyAsync()
|
||||
{
|
||||
IsTestingProxy = true;
|
||||
ProxyTestResult = SH.ViewModelSettingProxyTesting;
|
||||
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
|
||||
try
|
||||
{
|
||||
using HttpClient httpClient = new(new SocketsHttpHandler
|
||||
{
|
||||
Proxy = hutaoWebProxy,
|
||||
UseProxy = true,
|
||||
});
|
||||
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(10);
|
||||
|
||||
Stopwatch stopwatch = Stopwatch.StartNew();
|
||||
HttpResponseMessage response = await httpClient.GetAsync("https://hut.ao").ConfigureAwait(false);
|
||||
stopwatch.Stop();
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
ProxyTestResult = SH.FormatViewModelSettingProxyTestSuccess(stopwatch.ElapsedMilliseconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProxyTestResult = SH.ViewModelSettingProxyTestFailed;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
ProxyTestResult = SH.ViewModelSettingProxyTestFailed;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsTestingProxy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,8 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel, INavigat
|
||||
|
||||
public partial SettingWebViewViewModel WebView { get; }
|
||||
|
||||
public partial SettingProxyViewModel Proxy { get; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string? UpdateInfo { get; set; }
|
||||
|
||||
@@ -80,7 +82,7 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel, INavigat
|
||||
|
||||
protected override ValueTask<bool> LoadOverrideAsync(CancellationToken token)
|
||||
{
|
||||
MakeSubViewModel([Geetest, Appearance, Storage, HotKey, Home, Game, GachaLog, WebView]);
|
||||
MakeSubViewModel([Geetest, Appearance, Storage, HotKey, Home, Game, GachaLog, WebView, Proxy]);
|
||||
|
||||
Storage.CacheFolderView = new(taskContext, HutaoRuntime.LocalCacheDirectory);
|
||||
Storage.DataFolderView = new(taskContext, HutaoRuntime.DataDirectory);
|
||||
|
||||
Reference in New Issue
Block a user