1
0
mirror of https://github.com/wangdage12/Snap.Hutao.git synced 2026-07-31 04:10:25 +08:00

8 Commits

Author SHA1 Message Date
dependabot[bot] 6bfd7542c5 Bump actions/setup-dotnet from 5 to 6 in /.github/workflows
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 5 to 6.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-20 02:54:12 +00:00
wangdage12 9200f44e33 Merge pull request #41 from a1204507929/feature/proxy-settings
添加网络代理设置功能
2026-06-28 18:19:35 +08:00
cheri 2e17a50e6f 修复HTTP代理本地地址绕过问题 2026-06-22 13:45:41 +08:00
cheri 27871adc63 修复代理功能问题 (CodeRabbit AI 评论修复) 2026-06-22 09:30:42 +08:00
cheri 41cfb8c8f4 添加网络代理设置功能
- 支持HTTP和SOCKS5代理配置
- 在设置界面添加代理启用开关、类型选择、地址和端口输入
- 添加代理测试功能,显示延迟结果
- 反馈中心正确显示当前代理状态
2026-06-22 08:25:10 +08:00
fanbook-wangdage 9da2c2eb8b 提升版本号
修复部分操作下添加养成计划时,养成计划页面缓存未刷新的问题
添加部分角色和武器id
2026-05-23 16:38:44 +08:00
wangdage12 ed3f2270b3 Merge pull request #35 from wangdage12/dependabot/github_actions/dot-github/workflows/benc-uk/workflow-dispatch-1.3.2
Bump benc-uk/workflow-dispatch from 1.3.1 to 1.3.2 in /.github/workflows
2026-05-21 18:57:25 +08:00
dependabot[bot] 0eb8b58711 Bump benc-uk/workflow-dispatch from 1.3.1 to 1.3.2 in /.github/workflows
Bumps [benc-uk/workflow-dispatch](https://github.com/benc-uk/workflow-dispatch) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/benc-uk/workflow-dispatch/releases)
- [Commits](https://github.com/benc-uk/workflow-dispatch/compare/v1.3.1...v1.3.2)

---
updated-dependencies:
- dependency-name: benc-uk/workflow-dispatch
  dependency-version: 1.3.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-18 03:24:24 +00:00
26 changed files with 490 additions and 33 deletions
+1 -1
View File
@@ -64,7 +64,7 @@ jobs:
-Body $r2Data `
-ContentType "application/json"
Write-Output $response2.Content
- uses: benc-uk/workflow-dispatch@v1.3.1
- uses: benc-uk/workflow-dispatch@v1.3.2
with:
workflow: Build
repo: DGP-Studio/hutao-installer
+1 -1
View File
@@ -89,7 +89,7 @@ jobs:
- name: Setup .NET
if: ${{ needs.select-runner.outputs.runner == 'windows-latest' }}
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.0
+1 -1
View File
@@ -53,7 +53,7 @@ jobs:
- name: Setup .NET
if: ${{ steps.merge.outputs.continue == 'true' }}
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.0
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
uses: actions/checkout@v6
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.0.x
@@ -3,7 +3,7 @@
<Package
Name="Snap.Hutao"
Manufacturer="Millennium Science Technology R-D Inst"
Version="1.18.6.0"
Version="1.18.7.0"
UpgradeCode="121203be-60cb-408f-92cc-7080f6598e68"
Language="2052"
Scope="perMachine">
@@ -1,4 +1,4 @@
<Project Sdk="WixToolset.Sdk/6.0.2">
<Project Sdk="WixToolset.Sdk/6.0.2">
<PropertyGroup>
<SuppressIces>ICE03;ICE60</SuppressIces>
<Platform>x64</Platform>
@@ -9,11 +9,11 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Snap.Hutao\Snap.Hutao.csproj" />
<ProjectReference Include="..\Snap.Hutao\Snap.Hutao.csproj" SetPlatform="Platform=x64" />
</ItemGroup>
<ItemGroup>
<HarvestDirectory Include="..\Snap.Hutao\bin\Release\net10.0-windows10.0.26100.0\win-x64">
<HarvestDirectory Include="..\Snap.Hutao\bin\x64\Release\net10.0-windows10.0.26100.0\win-x64">
<ComponentGroupName>MainAppComponents</ComponentGroupName>
<DirectoryRefId>INSTALLFOLDER</DirectoryRefId>
<SuppressCom>true</SuppressCom>
@@ -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>
@@ -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,
}
@@ -129,6 +129,11 @@ internal static class AvatarIds
public static readonly AvatarId Columbina = 10000125;
public static readonly AvatarId Zibai = 10000126;
public static readonly AvatarId Illuga = 10000127;
public static readonly AvatarId Varka = 10000128;
public static readonly AvatarId Lohen = 10000129;
public static readonly AvatarId Linnea = 10000130;
public static readonly AvatarId Nicole = 10000131;
public static readonly AvatarId Prune = 10000132;
private static readonly FrozenSet<AvatarId> StandardWishIds =
[
@@ -28,13 +28,11 @@ internal static class WeaponIds
public static readonly FrozenSet<WeaponId> OrangeStandardWishIds =
[
11501U, 11502U,
11501U, 11502U, 11518U, 11519U,
12501U, 12502U,
13502U, 13505U,
14501U, 14502U,
15501U, 15502U,
15515U, 11518U,
14522U, 11519U
13502U, 13505U, 13517U,
14501U, 14502U, 14522U, 14523U,
15501U, 15502U, 15515U
];
public static bool IsOrangeStandardWish(in WeaponId weaponId)
@@ -13,7 +13,7 @@
<Identity
Name="60568DGPStudio.SnapHutao"
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
Version="1.18.6.0" />
Version="1.18.7.0" />
<Properties>
<DisplayName>Snap Hutao</DisplayName>
@@ -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); }
}
@@ -297,6 +297,7 @@ internal sealed partial class CultivationService : ICultivationService
}
ArgumentNullException.ThrowIfNull(projects.CurrentItem);
Guid projectId = projects.CurrentItem.InnerId;
await taskContext.SwitchToBackgroundAsync();
@@ -310,7 +311,7 @@ internal sealed partial class CultivationService : ICultivationService
if (inputConsumption.Strategy is not ConsumptionSaveStrategyKind.CreateNewEntry)
{
// Check for existing entries
ImmutableArray<CultivateEntry> entries = cultivationRepository.GetCultivateEntryImmutableArrayByProjectIdAndItemId(projects.CurrentItem.InnerId, inputConsumption.ItemId);
ImmutableArray<CultivateEntry> entries = cultivationRepository.GetCultivateEntryImmutableArrayByProjectIdAndItemId(projectId, inputConsumption.ItemId);
if (entries.Length > 0)
{
@@ -330,6 +331,7 @@ internal sealed partial class CultivationService : ICultivationService
if (inputConsumption.Items is [])
{
entryCollectionCache.TryRemove(projectId, out _);
return new(ConsumptionSaveResultKind.Removed);
}
}
@@ -344,7 +346,7 @@ internal sealed partial class CultivationService : ICultivationService
}
{
CultivateEntry entry = CultivateEntry.From(projects.CurrentItem.InnerId, inputConsumption.Type, inputConsumption.ItemId);
CultivateEntry entry = CultivateEntry.From(projectId, inputConsumption.Type, inputConsumption.ItemId);
entry.RelatedEntryId = inputConsumption.RelatedEntryId;
cultivationRepository.AddCultivateEntry(entry);
@@ -356,7 +358,7 @@ internal sealed partial class CultivationService : ICultivationService
// The consumption save operation is always performed outside cultivation page
// and without touching the cache. So we have to invalidate the cache manually.
entryCollectionCache.TryRemove(projects.CurrentItem.InnerId, out _);
entryCollectionCache.TryRemove(projectId, out _);
return new(ConsumptionSaveResultKind.Added, entry.InnerId);
}
@@ -685,4 +687,4 @@ internal sealed partial class CultivationService : ICultivationService
&& li.SkillELevelFrom == li.SkillELevelTo
&& li.SkillQLevelFrom == li.SkillQLevelTo;
}
}
}
+1 -1
View File
@@ -11,7 +11,7 @@
<UseWinUI>true</UseWinUI>
<UseWPF>False</UseWPF>
<!-- 配置版本号 -->
<Version>1.18.6.0</Version>
<Version>1.18.7.0</Version>
<UseWindowsForms>False</UseWindowsForms>
<ImplicitUsings>False</ImplicitUsings>
@@ -9,13 +9,18 @@ namespace Snap.Hutao.UI.Xaml.Markup;
[MarkupExtensionReturnType(ReturnType = typeof(string))]
internal sealed partial class ResourceStringExtension : MarkupExtension
{
public SHName Name { get; set; }
public string? Name { get; set; }
public string? CultureName { get; set; }
protected override object ProvideValue()
{
if (string.IsNullOrEmpty(Name))
{
return string.Empty;
}
CultureInfo cultureInfo = CultureName is not null ? CultureInfo.GetCultureInfo(CultureName) : CultureInfo.CurrentCulture;
return SH.ResourceManager.GetString(string.Intern(Name.ToString()), cultureInfo) ?? string.Empty;
return SH.ResourceManager.GetString(string.Intern(Name), cultureInfo) ?? string.Empty;
}
}
}
@@ -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=&#xE774;}">
<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=&#xE968;}">
<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=&#xE71B;}">
<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=&#xE8B7;}">
<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=&#xE768;}">
<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}">
@@ -230,7 +230,10 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
if (!await SaveCultivationAsync(batchConsumption.Items.Single(), deltaOptions).ConfigureAwait(false))
{
scopeContext.Messenger.Send(InfoBarMessage.Warning(SH.ViewModelCultivationEntryAddWarning));
return;
}
scopeContext.Messenger.Send(CultivationProjectEntriesChangedMessage.Empty);
}
[Command("BatchCultivateCommand")]
@@ -412,4 +415,4 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
Summary.Avatars.MoveCurrentToFirst();
}
}
}
}
@@ -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);
@@ -20,6 +20,7 @@ using Snap.Hutao.Service.Notification;
using Snap.Hutao.UI.Xaml.Control.AutoSuggestBox;
using Snap.Hutao.UI.Xaml.Data;
using Snap.Hutao.UI.Xaml.View.Dialog;
using Snap.Hutao.ViewModel.Cultivation;
using System.Collections.Immutable;
using CalculateBatchConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.BatchConsumption;
@@ -155,7 +156,8 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
Strategy = deltaOptions.Strategy,
};
InfoBarMessage? message = (await cultivationService.SaveConsumptionAsync(input).ConfigureAwait(false)).Kind switch
ConsumptionSaveResult result = await cultivationService.SaveConsumptionAsync(input).ConfigureAwait(false);
InfoBarMessage? message = result.Kind switch
{
ConsumptionSaveResultKind.NoProject => InfoBarMessage.Warning(SH.ViewModelCultivationEntryAddWarning),
ConsumptionSaveResultKind.Skipped => InfoBarMessage.Information(SH.ViewModelCultivationConsumptionSaveSkippedHint),
@@ -168,6 +170,11 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
{
messenger.Send(message);
}
if (result.Kind is not ConsumptionSaveResultKind.NoProject)
{
messenger.Send(CultivationProjectEntriesChangedMessage.Empty);
}
}
catch (HutaoException ex)
{
@@ -225,4 +232,4 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
Avatars.MoveCurrentToFirst();
}
}
}
}
@@ -19,6 +19,7 @@ using Snap.Hutao.Service.Notification;
using Snap.Hutao.UI.Xaml.Control.AutoSuggestBox;
using Snap.Hutao.UI.Xaml.Data;
using Snap.Hutao.UI.Xaml.View.Dialog;
using Snap.Hutao.ViewModel.Cultivation;
using System.Collections.Immutable;
using CalculateBatchConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.BatchConsumption;
@@ -146,7 +147,8 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
Strategy = deltaOptions.Strategy,
};
InfoBarMessage? message = (await cultivationService.SaveConsumptionAsync(input).ConfigureAwait(false)).Kind switch
ConsumptionSaveResult result = await cultivationService.SaveConsumptionAsync(input).ConfigureAwait(false);
InfoBarMessage? message = result.Kind switch
{
ConsumptionSaveResultKind.NoProject => InfoBarMessage.Warning(SH.ViewModelCultivationEntryAddWarning),
ConsumptionSaveResultKind.Skipped => InfoBarMessage.Information(SH.ViewModelCultivationConsumptionSaveSkippedHint),
@@ -159,6 +161,11 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
{
messenger.Send(message);
}
if (result.Kind is not ConsumptionSaveResultKind.NoProject)
{
messenger.Send(CultivationProjectEntriesChangedMessage.Empty);
}
}
catch (HutaoException ex)
{
@@ -200,4 +207,4 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
Weapons.MoveCurrentToFirst();
}
}
}
}