mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
2 Commits
fix/chroni
...
feat/versi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f998dc87f | ||
|
|
2367c4759d |
@@ -110,7 +110,6 @@ dotnet_diagnostic.SA1642.severity = none
|
|||||||
|
|
||||||
dotnet_diagnostic.IDE0005.severity = warning
|
dotnet_diagnostic.IDE0005.severity = warning
|
||||||
dotnet_diagnostic.IDE0060.severity = none
|
dotnet_diagnostic.IDE0060.severity = none
|
||||||
dotnet_diagnostic.IDE0290.severity = none
|
|
||||||
|
|
||||||
# SA1208: System using directives should be placed before other using directives
|
# SA1208: System using directives should be placed before other using directives
|
||||||
dotnet_diagnostic.SA1208.severity = none
|
dotnet_diagnostic.SA1208.severity = none
|
||||||
@@ -321,7 +320,8 @@ dotnet_diagnostic.CA2227.severity = suggestion
|
|||||||
|
|
||||||
# CA2251: 使用 “string.Equals”
|
# CA2251: 使用 “string.Equals”
|
||||||
dotnet_diagnostic.CA2251.severity = suggestion
|
dotnet_diagnostic.CA2251.severity = suggestion
|
||||||
csharp_style_prefer_primary_constructors = true:suggestion
|
|
||||||
|
csharp_style_prefer_primary_constructors = false:none
|
||||||
|
|
||||||
[*.vb]
|
[*.vb]
|
||||||
#### 命名样式 ####
|
#### 命名样式 ####
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using Microsoft.Win32.SafeHandles;
|
using Microsoft.Win32.SafeHandles;
|
||||||
using Snap.Hutao.Core.Diagnostics;
|
using Snap.Hutao.Core.Diagnostics;
|
||||||
|
using System.Buffers;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
@@ -77,34 +78,36 @@ internal sealed class HttpShardCopyWorker<TStatus> : IDisposable
|
|||||||
using (HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false))
|
using (HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
using (IMemoryOwner<byte> memoryOwner = MemoryPool<byte>.Shared.Rent())
|
||||||
Memory<byte> buffer = new byte[bufferSize];
|
|
||||||
using (Stream stream = await response.Content.ReadAsStreamAsync(token).ConfigureAwait(false))
|
|
||||||
{
|
{
|
||||||
int totalBytesRead = 0;
|
Memory<byte> buffer = memoryOwner.Memory;
|
||||||
int bytesReadAfterPreviousReport = 0;
|
using (Stream stream = await response.Content.ReadAsStreamAsync(token).ConfigureAwait(false))
|
||||||
do
|
|
||||||
{
|
{
|
||||||
int bytesRead = await stream.ReadAsync(buffer, token).ConfigureAwait(false);
|
int totalBytesRead = 0;
|
||||||
if (bytesRead <= 0)
|
int bytesReadAfterPreviousReport = 0;
|
||||||
|
do
|
||||||
{
|
{
|
||||||
progress.Report(new(bytesReadAfterPreviousReport));
|
int bytesRead = await stream.ReadAsync(buffer, token).ConfigureAwait(false);
|
||||||
bytesReadAfterPreviousReport = 0;
|
if (bytesRead <= 0)
|
||||||
break;
|
{
|
||||||
}
|
progress.Report(new(bytesReadAfterPreviousReport));
|
||||||
|
bytesReadAfterPreviousReport = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
await RandomAccess.WriteAsync(destFileHandle, buffer[..bytesRead], shard.StartOffset + totalBytesRead, token).ConfigureAwait(false);
|
await RandomAccess.WriteAsync(destFileHandle, buffer[..bytesRead], shard.StartOffset + totalBytesRead, token).ConfigureAwait(false);
|
||||||
|
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
bytesReadAfterPreviousReport += bytesRead;
|
bytesReadAfterPreviousReport += bytesRead;
|
||||||
if (stopwatch.GetElapsedTime().TotalMilliseconds > 500)
|
if (stopwatch.GetElapsedTime().TotalMilliseconds > 500)
|
||||||
{
|
{
|
||||||
progress.Report(new(bytesReadAfterPreviousReport));
|
progress.Report(new(bytesReadAfterPreviousReport));
|
||||||
bytesReadAfterPreviousReport = 0;
|
bytesReadAfterPreviousReport = 0;
|
||||||
stopwatch = ValueStopwatch.StartNew();
|
stopwatch = ValueStopwatch.StartNew();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
while (true);
|
||||||
}
|
}
|
||||||
while (true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Diagnostics;
|
using Snap.Hutao.Core.Diagnostics;
|
||||||
|
using System.Buffers;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.IO;
|
namespace Snap.Hutao.Core.IO;
|
||||||
@@ -51,26 +52,30 @@ internal class StreamCopyWorker<TStatus>
|
|||||||
|
|
||||||
long totalBytesRead = 0;
|
long totalBytesRead = 0;
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
Memory<byte> buffer = new byte[bufferSize];
|
|
||||||
|
|
||||||
do
|
using (IMemoryOwner<byte> memoryOwner = MemoryPool<byte>.Shared.Rent(bufferSize))
|
||||||
{
|
{
|
||||||
bytesRead = await source.ReadAsync(buffer).ConfigureAwait(false);
|
Memory<byte> buffer = memoryOwner.Memory;
|
||||||
if (bytesRead == 0)
|
|
||||||
{
|
|
||||||
progress.Report(statusFactory(totalBytesRead));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
await destination.WriteAsync(buffer[..bytesRead]).ConfigureAwait(false);
|
do
|
||||||
|
|
||||||
totalBytesRead += bytesRead;
|
|
||||||
if (stopwatch.GetElapsedTime().TotalMilliseconds > 1000)
|
|
||||||
{
|
{
|
||||||
progress.Report(statusFactory(totalBytesRead));
|
bytesRead = await source.ReadAsync(buffer).ConfigureAwait(false);
|
||||||
stopwatch = ValueStopwatch.StartNew();
|
if (bytesRead is 0)
|
||||||
|
{
|
||||||
|
progress.Report(statusFactory(totalBytesRead));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await destination.WriteAsync(buffer[..bytesRead]).ConfigureAwait(false);
|
||||||
|
|
||||||
|
totalBytesRead += bytesRead;
|
||||||
|
if (stopwatch.GetElapsedTime().TotalMilliseconds > 1000)
|
||||||
|
{
|
||||||
|
progress.Report(statusFactory(totalBytesRead));
|
||||||
|
stopwatch = ValueStopwatch.StartNew();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
while (bytesRead > 0);
|
||||||
}
|
}
|
||||||
while (bytesRead > 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,15 +116,15 @@ internal sealed partial class Activation : IActivation
|
|||||||
|
|
||||||
// If it's the first time launch, we show the guide window anyway.
|
// If it's the first time launch, we show the guide window anyway.
|
||||||
// Otherwise, we check if there's any unfulfilled resource category present.
|
// Otherwise, we check if there's any unfulfilled resource category present.
|
||||||
if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor7Revision0GuideState, GuideState.Language) >= GuideState.StaticResourceBegin)
|
if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) >= GuideState.StaticResourceBegin)
|
||||||
{
|
{
|
||||||
if (StaticResource.IsAnyUnfulfilledCategoryPresent())
|
if (StaticResource.IsAnyUnfulfilledCategoryPresent())
|
||||||
{
|
{
|
||||||
UnsafeLocalSetting.Set(SettingKeys.Major1Minor7Revision0GuideState, GuideState.StaticResourceBegin);
|
UnsafeLocalSetting.Set(SettingKeys.Major1Minor10Revision0GuideState, GuideState.StaticResourceBegin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor7Revision0GuideState, GuideState.Language) < GuideState.Completed)
|
if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) < GuideState.Completed)
|
||||||
{
|
{
|
||||||
await taskContext.SwitchToMainThreadAsync();
|
await taskContext.SwitchToMainThreadAsync();
|
||||||
serviceProvider.GetRequiredService<GuideWindow>();
|
serviceProvider.GetRequiredService<GuideWindow>();
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ internal static class SettingKeys
|
|||||||
#region Application
|
#region Application
|
||||||
public const string LaunchTimes = "LaunchTimes";
|
public const string LaunchTimes = "LaunchTimes";
|
||||||
public const string DataFolderPath = "DataFolderPath";
|
public const string DataFolderPath = "DataFolderPath";
|
||||||
public const string Major1Minor7Revision0GuideState = "Major1Minor7Revision0GuideState";
|
public const string Major1Minor10Revision0GuideState = "Major1Minor10Revision0GuideState1";
|
||||||
|
public const string StaticResourceImageQuality = "StaticResourceImageQuality";
|
||||||
|
public const string StaticResourceUseTrimmedArchive = "StaticResourceUseTrimmedArchive";
|
||||||
public const string HotKeyMouseClickRepeatForever = "HotKeyMouseClickRepeatForever";
|
public const string HotKeyMouseClickRepeatForever = "HotKeyMouseClickRepeatForever";
|
||||||
public const string IsAllocConsoleDebugModeEnabled = "IsAllocConsoleDebugModeEnabled2";
|
public const string IsAllocConsoleDebugModeEnabled = "IsAllocConsoleDebugModeEnabled2";
|
||||||
#endregion
|
#endregion
|
||||||
@@ -60,6 +62,10 @@ internal static class SettingKeys
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Obsolete
|
#region Obsolete
|
||||||
|
|
||||||
|
[Obsolete("重置新手引导状态")]
|
||||||
|
public const string Major1Minor7Revision0GuideState = "Major1Minor7Revision0GuideState";
|
||||||
|
|
||||||
[Obsolete("重置调试控制台开关")]
|
[Obsolete("重置调试控制台开关")]
|
||||||
public const string IsAllocConsoleDebugModeEnabledLegacy1 = "IsAllocConsoleDebugModeEnabled";
|
public const string IsAllocConsoleDebugModeEnabledLegacy1 = "IsAllocConsoleDebugModeEnabled";
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
xmlns:shvg="using:Snap.Hutao.View.Guide"
|
xmlns:shvg="using:Snap.Hutao.View.Guide"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Grid x:Name="RootGrid">
|
<Grid x:Name="RootGrid" Background="{ThemeResource SolidBackgroundFillColorBaseBrush}">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="auto"/>
|
<RowDefinition Height="auto"/>
|
||||||
<RowDefinition/>
|
<RowDefinition/>
|
||||||
|
|||||||
@@ -1397,6 +1397,12 @@
|
|||||||
<data name="ViewGuideStepAgreementTermOfService" xml:space="preserve">
|
<data name="ViewGuideStepAgreementTermOfService" xml:space="preserve">
|
||||||
<value>用户使用协议与法律声明</value>
|
<value>用户使用协议与法律声明</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ViewGuideStepCommonSetting" xml:space="preserve">
|
||||||
|
<value>基础设置</value>
|
||||||
|
</data>
|
||||||
|
<data name="ViewGuideStepCommonSettingHint" xml:space="preserve">
|
||||||
|
<value>稍后可以在设置中修改</value>
|
||||||
|
</data>
|
||||||
<data name="ViewGuideStepDocument" xml:space="preserve">
|
<data name="ViewGuideStepDocument" xml:space="preserve">
|
||||||
<value>文档</value>
|
<value>文档</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1407,7 +1413,7 @@
|
|||||||
<value>安装完成后重启胡桃以查看是否正常生效</value>
|
<value>安装完成后重启胡桃以查看是否正常生效</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewGuideStepEnvironmentFontDescription1" xml:space="preserve">
|
<data name="ViewGuideStepEnvironmentFontDescription1" xml:space="preserve">
|
||||||
<value>如果上方的图标中存在乱码,请前往</value>
|
<value>如果上方的图标中存在乱码或方块字,请前往</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewGuideStepEnvironmentFontDescription2" xml:space="preserve">
|
<data name="ViewGuideStepEnvironmentFontDescription2" xml:space="preserve">
|
||||||
<value>下载并自行安装图标字体</value>
|
<value>下载并自行安装图标字体</value>
|
||||||
@@ -1424,6 +1430,24 @@
|
|||||||
<data name="ViewGuideStepStaticResource" xml:space="preserve">
|
<data name="ViewGuideStepStaticResource" xml:space="preserve">
|
||||||
<value>资源</value>
|
<value>资源</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ViewGuideStepStaticResourceSetting" xml:space="preserve">
|
||||||
|
<value>图像资源设置</value>
|
||||||
|
</data>
|
||||||
|
<data name="ViewGuideStepStaticResourceSettingHint" xml:space="preserve">
|
||||||
|
<value>* 除非你卸载并重新安装胡桃,否则你将无法更改这些设置</value>
|
||||||
|
</data>
|
||||||
|
<data name="ViewGuideStepStaticResourceSettingMinimumHeader" xml:space="preserve">
|
||||||
|
<value>图片资源包体</value>
|
||||||
|
</data>
|
||||||
|
<data name="ViewGuideStepStaticResourceSettingMinimumOff" xml:space="preserve">
|
||||||
|
<value>全部资源(节省 0% 磁盘空间占用)</value>
|
||||||
|
</data>
|
||||||
|
<data name="ViewGuideStepStaticResourceSettingMinimumOn" xml:space="preserve">
|
||||||
|
<value>部分资源(节省 13.5% 磁盘空间占用)</value>
|
||||||
|
</data>
|
||||||
|
<data name="ViewGuideStepStaticResourceSettingQualityHeader" xml:space="preserve">
|
||||||
|
<value>图片资源质量</value>
|
||||||
|
</data>
|
||||||
<data name="ViewHutaoDatabaseHeader" xml:space="preserve">
|
<data name="ViewHutaoDatabaseHeader" xml:space="preserve">
|
||||||
<value>深渊统计</value>
|
<value>深渊统计</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1613,6 +1637,12 @@
|
|||||||
<data name="ViewModelGuideActionStaticResourceBegin" xml:space="preserve">
|
<data name="ViewModelGuideActionStaticResourceBegin" xml:space="preserve">
|
||||||
<value>下载资源文件中,请稍候</value>
|
<value>下载资源文件中,请稍候</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ViewModelGuideStaticResourceQualityHigh" xml:space="preserve">
|
||||||
|
<value>高质量(节省 72.8% 磁盘空间占用)</value>
|
||||||
|
</data>
|
||||||
|
<data name="ViewModelGuideStaticResourceQualityRaw" xml:space="preserve">
|
||||||
|
<value>原图(节省 0% 磁盘空间占用)</value>
|
||||||
|
</data>
|
||||||
<data name="ViewModelHutaoPassportEmailNotValidHint" xml:space="preserve">
|
<data name="ViewModelHutaoPassportEmailNotValidHint" xml:space="preserve">
|
||||||
<value>请输入正确的邮箱</value>
|
<value>请输入正确的邮箱</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ internal sealed class HutaoStatisticsFactory
|
|||||||
private readonly GachaEvent avatarEvent;
|
private readonly GachaEvent avatarEvent;
|
||||||
private readonly GachaEvent avatarEvent2;
|
private readonly GachaEvent avatarEvent2;
|
||||||
private readonly GachaEvent weaponEvent;
|
private readonly GachaEvent weaponEvent;
|
||||||
private readonly GachaEvent? chronicledEvent;
|
private readonly GachaEvent chronicledEvent;
|
||||||
|
|
||||||
public HutaoStatisticsFactory(in HutaoStatisticsFactoryMetadataContext context)
|
public HutaoStatisticsFactory(in HutaoStatisticsFactoryMetadataContext context)
|
||||||
{
|
{
|
||||||
@@ -32,7 +32,7 @@ internal sealed class HutaoStatisticsFactory
|
|||||||
avatarEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.ActivityAvatar);
|
avatarEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.ActivityAvatar);
|
||||||
avatarEvent2 = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.SpecialActivityAvatar);
|
avatarEvent2 = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.SpecialActivityAvatar);
|
||||||
weaponEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.ActivityWeapon);
|
weaponEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.ActivityWeapon);
|
||||||
chronicledEvent = context.GachaEvents.SingleOrDefault(g => g.From < now && g.To > now && g.Type == GachaType.ActivityCity);
|
chronicledEvent = context.GachaEvents.Single(g => g.From < now && g.To > now && g.Type == GachaType.ActivityCity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HutaoStatistics Create(GachaEventStatistics raw)
|
public HutaoStatistics Create(GachaEventStatistics raw)
|
||||||
@@ -42,7 +42,7 @@ internal sealed class HutaoStatisticsFactory
|
|||||||
AvatarEvent = CreateWishSummary(avatarEvent, raw.AvatarEvent),
|
AvatarEvent = CreateWishSummary(avatarEvent, raw.AvatarEvent),
|
||||||
AvatarEvent2 = CreateWishSummary(avatarEvent2, raw.AvatarEvent2),
|
AvatarEvent2 = CreateWishSummary(avatarEvent2, raw.AvatarEvent2),
|
||||||
WeaponEvent = CreateWishSummary(weaponEvent, raw.WeaponEvent),
|
WeaponEvent = CreateWishSummary(weaponEvent, raw.WeaponEvent),
|
||||||
Chronicled = chronicledEvent is null ? null : CreateWishSummary(chronicledEvent, raw.Chronicled),
|
Chronicled = CreateWishSummary(chronicledEvent, raw.Chronicled),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -151,7 +151,6 @@
|
|||||||
<None Remove="View\Control\DescParamComboBox.xaml" />
|
<None Remove="View\Control\DescParamComboBox.xaml" />
|
||||||
<None Remove="View\Control\Elevation.xaml" />
|
<None Remove="View\Control\Elevation.xaml" />
|
||||||
<None Remove="View\Control\HutaoStatisticsCard.xaml" />
|
<None Remove="View\Control\HutaoStatisticsCard.xaml" />
|
||||||
<None Remove="View\Control\HutaoStatisticsPanel.xaml" />
|
|
||||||
<None Remove="View\Control\ItemIcon.xaml" />
|
<None Remove="View\Control\ItemIcon.xaml" />
|
||||||
<None Remove="View\Control\LaunchGameResourceExpander.xaml" />
|
<None Remove="View\Control\LaunchGameResourceExpander.xaml" />
|
||||||
<None Remove="View\Control\LoadingView.xaml" />
|
<None Remove="View\Control\LoadingView.xaml" />
|
||||||
@@ -348,14 +347,6 @@
|
|||||||
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnablePreviewMsixTooling)'=='true'">
|
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnablePreviewMsixTooling)'=='true'">
|
||||||
<ProjectCapability Include="Msix" />
|
<ProjectCapability Include="Msix" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Page Update="View\Control\HutaoStatisticsPanel.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Update="View\Dialog\LaunchGameConfigurationFixDialog.xaml">
|
<Page Update="View\Dialog\LaunchGameConfigurationFixDialog.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
<shcp:HorizontalEqualPanel
|
|
||||||
x:Class="Snap.Hutao.View.Control.HutaoStatisticsPanel"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:shcp="using:Snap.Hutao.Control.Panel"
|
|
||||||
xmlns:shvc="using:Snap.Hutao.View.Control"
|
|
||||||
mc:Ignorable="d">
|
|
||||||
<shvc:HutaoStatisticsCard DataContext="{x:Bind Statistics.AvatarEvent}"/>
|
|
||||||
<shvc:HutaoStatisticsCard DataContext="{x:Bind Statistics.AvatarEvent2}"/>
|
|
||||||
<shvc:HutaoStatisticsCard DataContext="{x:Bind Statistics.WeaponEvent}"/>
|
|
||||||
</shcp:HorizontalEqualPanel>
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Snap.Hutao.Control.Panel;
|
|
||||||
using Snap.Hutao.ViewModel.GachaLog;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.View.Control;
|
|
||||||
|
|
||||||
[DependencyProperty("Statistics", typeof(HutaoStatistics), default, nameof(OnStatisticsChanged))]
|
|
||||||
internal sealed partial class HutaoStatisticsPanel : HorizontalEqualPanel
|
|
||||||
{
|
|
||||||
public HutaoStatisticsPanel()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
|
|
||||||
if (DataContext is HutaoStatistics statistics && statistics.Chronicled is { } chronicled)
|
|
||||||
{
|
|
||||||
Children.Add(new HutaoStatisticsCard
|
|
||||||
{
|
|
||||||
DataContext = chronicled,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnStatisticsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
|
||||||
{
|
|
||||||
HutaoStatisticsPanel panel = (HutaoStatisticsPanel)obj;
|
|
||||||
|
|
||||||
if (args.NewValue is HutaoStatistics { Chronicled: { } chronicled })
|
|
||||||
{
|
|
||||||
panel.Children.Add(new HutaoStatisticsCard
|
|
||||||
{
|
|
||||||
DataContext = chronicled,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||||
|
xmlns:shc="using:Snap.Hutao.Control"
|
||||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||||
xmlns:shvg="using:Snap.Hutao.ViewModel.Guide"
|
xmlns:shvg="using:Snap.Hutao.ViewModel.Guide"
|
||||||
@@ -49,7 +50,7 @@
|
|||||||
<RowDefinition/>
|
<RowDefinition/>
|
||||||
<RowDefinition Height="auto"/>
|
<RowDefinition Height="auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<cwc:SwitchPresenter Value="{Binding State, Mode=OneWay}">
|
<cwc:SwitchPresenter ContentTransitions="{ThemeResource EntranceThemeTransitions}" Value="{Binding State, Mode=OneWay}">
|
||||||
<cwc:Case Value="{shcm:UInt32 Value=0}">
|
<cwc:Case Value="{shcm:UInt32 Value=0}">
|
||||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
<GridView
|
<GridView
|
||||||
@@ -125,10 +126,11 @@
|
|||||||
<StackPanel
|
<StackPanel
|
||||||
Margin="16"
|
Margin="16"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center">
|
VerticalAlignment="Center"
|
||||||
|
Spacing="{ThemeResource SettingsCardSpacing}">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="1,0,0,5"
|
Margin="1,0,0,5"
|
||||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
Style="{StaticResource TitleTextBlockStyle}"
|
||||||
Text="Segoe Fluent Icons"/>
|
Text="Segoe Fluent Icons"/>
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Margin="0,8"
|
Margin="0,8"
|
||||||
@@ -155,7 +157,10 @@
|
|||||||
<Run Text="{shcm:ResourceString Name=ViewGuideStepEnvironmentFontDescription2}"/>
|
<Run Text="{shcm:ResourceString Name=ViewGuideStepEnvironmentFontDescription2}"/>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Text="{shcm:ResourceString Name=ViewGuideStepEnvironmentAfterInstallDescription}"/>
|
<TextBlock Text="{shcm:ResourceString Name=ViewGuideStepEnvironmentAfterInstallDescription}"/>
|
||||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageSettingWebview2Header}"/>
|
<TextBlock
|
||||||
|
Margin="1,32,0,5"
|
||||||
|
Style="{StaticResource TitleTextBlockStyle}"
|
||||||
|
Text="{shcm:ResourceString Name=ViewPageSettingWebview2Header}"/>
|
||||||
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{Binding RuntimeOptions.WebView2Version}"/>
|
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{Binding RuntimeOptions.WebView2Version}"/>
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
<Run Text="{shcm:ResourceString Name=ViewGuideStepEnvironmentWebView2Description1}"/>
|
<Run Text="{shcm:ResourceString Name=ViewGuideStepEnvironmentWebView2Description1}"/>
|
||||||
@@ -169,6 +174,74 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</cwc:Case>
|
</cwc:Case>
|
||||||
<cwc:Case Value="{shcm:UInt32 Value=3}">
|
<cwc:Case Value="{shcm:UInt32 Value=3}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition Height="auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="0"
|
||||||
|
Margin="16"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageSettingHomeAnnouncementRegionHeader}"/>
|
||||||
|
<ListView
|
||||||
|
MinWidth="320"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
DisplayMemberPath="Name"
|
||||||
|
ItemsSource="{Binding AppOptions.LazyRegions.Value}"
|
||||||
|
SelectedItem="{Binding SelectedRegion, Mode=TwoWay}"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="16"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalTextAlignment="Center"
|
||||||
|
Opacity="0.7"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Text="{shcm:ResourceString Name=ViewGuideStepCommonSettingHint}"/>
|
||||||
|
</Grid>
|
||||||
|
</cwc:Case>
|
||||||
|
<cwc:Case Value="{shcm:UInt32 Value=4}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition Height="auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="0"
|
||||||
|
Margin="16"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{shcm:ResourceString Name=ViewGuideStepStaticResourceSettingQualityHeader}"/>
|
||||||
|
<ListView
|
||||||
|
MinWidth="320"
|
||||||
|
Margin="0,8,0,32"
|
||||||
|
DisplayMemberPath="Name"
|
||||||
|
ItemsSource="{Binding StaticResourceOptions.ImageQualities}"
|
||||||
|
SelectedItem="{Binding StaticResourceOptions.ImageQuality, Mode=TwoWay}"/>
|
||||||
|
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{shcm:ResourceString Name=ViewGuideStepStaticResourceSettingMinimumHeader}"/>
|
||||||
|
<ToggleSwitch
|
||||||
|
IsOn="{Binding StaticResourceOptions.UseTrimmedArchive, Mode=TwoWay}"
|
||||||
|
OffContent="{shcm:ResourceString Name=ViewGuideStepStaticResourceSettingMinimumOff}"
|
||||||
|
OnContent="{shcm:ResourceString Name=ViewGuideStepStaticResourceSettingMinimumOn}"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="16"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
|
||||||
|
HorizontalTextAlignment="Center"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Text="{shcm:ResourceString Name=ViewGuideStepStaticResourceSettingHint}"/>
|
||||||
|
</Grid>
|
||||||
|
</cwc:Case>
|
||||||
|
<cwc:Case Value="{shcm:UInt32 Value=5}">
|
||||||
<StackPanel Margin="32,0" HorizontalAlignment="Left">
|
<StackPanel Margin="32,0" HorizontalAlignment="Left">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="1,16,0,5"
|
Margin="1,16,0,5"
|
||||||
@@ -190,11 +263,14 @@
|
|||||||
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
||||||
<cwc:Segmented
|
<cwc:Segmented
|
||||||
Margin="16"
|
Margin="16"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
IsHitTestVisible="False"
|
IsHitTestVisible="False"
|
||||||
SelectedIndex="{Binding State, Mode=TwoWay}">
|
SelectedIndex="{Binding State, Mode=TwoWay}">
|
||||||
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepLanguage}" Icon="{shcm:FontIcon Glyph=}"/>
|
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepLanguage}" Icon="{shcm:FontIcon Glyph=}"/>
|
||||||
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepDocument}" Icon="{shcm:FontIcon Glyph=}"/>
|
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepDocument}" Icon="{shcm:FontIcon Glyph=}"/>
|
||||||
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepEnvironment}" Icon="{shcm:FontIcon Glyph=}"/>
|
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepEnvironment}" Icon="{shcm:FontIcon Glyph=}"/>
|
||||||
|
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepCommonSetting}" Icon="{shcm:FontIcon Glyph=}"/>
|
||||||
|
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepStaticResourceSetting}" Icon="{shcm:FontIcon Glyph=}"/>
|
||||||
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepStaticResource}" Icon="{shcm:FontIcon Glyph=}"/>
|
<cwc:SegmentedItem Content="{shcm:ResourceString Name=ViewGuideStepStaticResource}" Icon="{shcm:FontIcon Glyph=}"/>
|
||||||
</cwc:Segmented>
|
</cwc:Segmented>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -468,15 +468,24 @@
|
|||||||
Margin="16"
|
Margin="16"
|
||||||
CornerRadius="{ThemeResource ControlCornerRadius}"
|
CornerRadius="{ThemeResource ControlCornerRadius}"
|
||||||
IsLoading="{Binding HutaoCloudStatisticsViewModel.IsInitialized, Converter={StaticResource BoolNegationConverter}}"/>
|
IsLoading="{Binding HutaoCloudStatisticsViewModel.IsInitialized, Converter={StaticResource BoolNegationConverter}}"/>
|
||||||
<shvc:HutaoStatisticsPanel
|
<Grid
|
||||||
Margin="16"
|
Margin="16"
|
||||||
Spacing="16"
|
ColumnSpacing="16"
|
||||||
Statistics="{Binding HutaoCloudStatisticsViewModel.Statistics}"
|
|
||||||
Visibility="{Binding HutaoCloudStatisticsViewModel.IsInitialized, Converter={StaticResource BoolToVisibilityConverter}}">
|
Visibility="{Binding HutaoCloudStatisticsViewModel.IsInitialized, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||||
<mxi:Interaction.Behaviors>
|
<mxi:Interaction.Behaviors>
|
||||||
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding HutaoCloudStatisticsViewModel.OpenUICommand}"/>
|
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding HutaoCloudStatisticsViewModel.OpenUICommand}"/>
|
||||||
</mxi:Interaction.Behaviors>
|
</mxi:Interaction.Behaviors>
|
||||||
</shvc:HutaoStatisticsPanel>
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<shvc:HutaoStatisticsCard Grid.Column="0" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.AvatarEvent}"/>
|
||||||
|
<shvc:HutaoStatisticsCard Grid.Column="1" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.AvatarEvent2}"/>
|
||||||
|
<shvc:HutaoStatisticsCard Grid.Column="2" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.WeaponEvent}"/>
|
||||||
|
<shvc:HutaoStatisticsCard Grid.Column="3" DataContext="{Binding HutaoCloudStatisticsViewModel.Statistics.Chronicled}"/>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
</Pivot>
|
</Pivot>
|
||||||
|
|||||||
@@ -26,5 +26,5 @@ internal sealed class HutaoStatistics
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 集录祈愿
|
/// 集录祈愿
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public HutaoWishSummary? Chronicled { get; set; }
|
public HutaoWishSummary Chronicled { get; set; } = default!;
|
||||||
}
|
}
|
||||||
@@ -7,105 +7,107 @@ using Snap.Hutao.Core;
|
|||||||
using Snap.Hutao.Core.Caching;
|
using Snap.Hutao.Core.Caching;
|
||||||
using Snap.Hutao.Core.ExceptionService;
|
using Snap.Hutao.Core.ExceptionService;
|
||||||
using Snap.Hutao.Core.IO;
|
using Snap.Hutao.Core.IO;
|
||||||
|
using Snap.Hutao.Factory.Progress;
|
||||||
|
using Snap.Hutao.Web.Request.Builder;
|
||||||
|
using Snap.Hutao.Web.Request.Builder.Abstraction;
|
||||||
|
using System.Collections.Frozen;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace Snap.Hutao.ViewModel.Guide;
|
namespace Snap.Hutao.ViewModel.Guide;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 下载信息
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class DownloadSummary : ObservableObject
|
internal sealed class DownloadSummary : ObservableObject
|
||||||
{
|
{
|
||||||
|
private static readonly FrozenSet<string?> AllowedMediaTypes = FrozenSet.ToFrozenSet(
|
||||||
|
[
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/zip",
|
||||||
|
]);
|
||||||
|
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
private readonly ITaskContext taskContext;
|
private readonly ITaskContext taskContext;
|
||||||
private readonly IImageCache imageCache;
|
private readonly IImageCache imageCache;
|
||||||
|
private readonly IHttpRequestMessageBuilderFactory httpRequestMessageBuilderFactory;
|
||||||
private readonly HttpClient httpClient;
|
private readonly HttpClient httpClient;
|
||||||
|
|
||||||
private readonly string fileName;
|
private readonly string fileName;
|
||||||
private readonly string fileUrl;
|
private readonly string fileUrl;
|
||||||
private readonly Progress<StreamCopyStatus> progress;
|
private readonly IProgress<StreamCopyStatus> progress;
|
||||||
|
|
||||||
private string description = SH.ViewModelWelcomeDownloadSummaryDefault;
|
private string description = SH.ViewModelWelcomeDownloadSummaryDefault;
|
||||||
private double progressValue;
|
private double progressValue;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 构造一个新的下载信息
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="serviceProvider">服务提供器</param>
|
|
||||||
/// <param name="fileName">压缩文件名称</param>
|
|
||||||
public DownloadSummary(IServiceProvider serviceProvider, string fileName)
|
public DownloadSummary(IServiceProvider serviceProvider, string fileName)
|
||||||
{
|
{
|
||||||
taskContext = serviceProvider.GetRequiredService<ITaskContext>();
|
taskContext = serviceProvider.GetRequiredService<ITaskContext>();
|
||||||
|
httpRequestMessageBuilderFactory = serviceProvider.GetRequiredService<IHttpRequestMessageBuilderFactory>();
|
||||||
httpClient = serviceProvider.GetRequiredService<HttpClient>();
|
httpClient = serviceProvider.GetRequiredService<HttpClient>();
|
||||||
|
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(serviceProvider.GetRequiredService<RuntimeOptions>().UserAgent);
|
||||||
imageCache = serviceProvider.GetRequiredService<IImageCache>();
|
imageCache = serviceProvider.GetRequiredService<IImageCache>();
|
||||||
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
|
||||||
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(runtimeOptions.UserAgent);
|
|
||||||
|
|
||||||
this.serviceProvider = serviceProvider;
|
this.serviceProvider = serviceProvider;
|
||||||
|
|
||||||
DisplayName = fileName;
|
|
||||||
this.fileName = fileName;
|
this.fileName = fileName;
|
||||||
fileUrl = Web.HutaoEndpoints.StaticZip(fileName);
|
fileUrl = Web.HutaoEndpoints.StaticZip(fileName);
|
||||||
|
|
||||||
progress = new(UpdateProgressStatus);
|
progress = serviceProvider.GetRequiredService<IProgressFactory>().CreateForMainThread<StreamCopyStatus>(UpdateProgressStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public string DisplayName { get => fileName; }
|
||||||
/// 显示名称
|
|
||||||
/// </summary>
|
|
||||||
public string DisplayName { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 描述
|
|
||||||
/// </summary>
|
|
||||||
public string Description { get => description; private set => SetProperty(ref description, value); }
|
public string Description { get => description; private set => SetProperty(ref description, value); }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 进度值,最大1
|
|
||||||
/// </summary>
|
|
||||||
public double ProgressValue { get => progressValue; set => SetProperty(ref progressValue, value); }
|
public double ProgressValue { get => progressValue; set => SetProperty(ref progressValue, value); }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 异步下载并解压
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>任务</returns>
|
|
||||||
public async ValueTask<bool> DownloadAndExtractAsync()
|
public async ValueTask<bool> DownloadAndExtractAsync()
|
||||||
{
|
{
|
||||||
ILogger<DownloadSummary> logger = serviceProvider.GetRequiredService<ILogger<DownloadSummary>>();
|
ILogger<DownloadSummary> logger = serviceProvider.GetRequiredService<ILogger<DownloadSummary>>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await httpClient.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
HttpRequestMessage message = httpRequestMessageBuilderFactory
|
||||||
|
.Create()
|
||||||
|
.SetRequestUri(fileUrl)
|
||||||
|
.SetStaticResourceControlHeaders()
|
||||||
|
.Get()
|
||||||
|
.HttpRequestMessage;
|
||||||
|
|
||||||
if (response.Content.Headers.ContentType?.MediaType is not ("application/octet-stream" or "application/zip"))
|
using (message)
|
||||||
{
|
{
|
||||||
logger.LogWarning("Download Static Zip failed, Content-Type is {Type}", response.Content.Headers.ContentType);
|
using (HttpResponseMessage response = await httpClient.SendAsync(message, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
|
||||||
await taskContext.SwitchToMainThreadAsync();
|
|
||||||
Description = SH.ViewModelWelcomeDownloadSummaryContentTypeNotMatch;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
long contentLength = response.Content.Headers.ContentLength ?? 0;
|
|
||||||
logger.LogInformation("Begin download, length: {length}", contentLength);
|
|
||||||
using (Stream content = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
using (TempFileStream temp = new(FileMode.OpenOrCreate, FileAccess.ReadWrite))
|
|
||||||
{
|
{
|
||||||
await new StreamCopyWorker(content, temp, contentLength).CopyAsync(progress).ConfigureAwait(false);
|
if (!AllowedMediaTypes.Contains(response.Content.Headers.ContentType?.MediaType))
|
||||||
ExtractFiles(temp);
|
{
|
||||||
|
logger.LogWarning("Download Static Zip failed, Content-Type is {Type}", response.Content.Headers.ContentType);
|
||||||
|
await taskContext.SwitchToMainThreadAsync();
|
||||||
|
Description = SH.ViewModelWelcomeDownloadSummaryContentTypeNotMatch;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
await taskContext.SwitchToMainThreadAsync();
|
long contentLength = response.Content.Headers.ContentLength ?? 0;
|
||||||
ProgressValue = 1;
|
logger.LogInformation("Begin download, size: {length}", Converters.ToFileSizeString(contentLength));
|
||||||
Description = SH.ViewModelWelcomeDownloadSummaryComplete;
|
using (Stream content = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
|
||||||
return true;
|
{
|
||||||
|
using (TempFileStream temp = new(FileMode.OpenOrCreate, FileAccess.ReadWrite))
|
||||||
|
{
|
||||||
|
await new StreamCopyWorker(content, temp, contentLength).CopyAsync(progress).ConfigureAwait(false);
|
||||||
|
ExtractFiles(temp);
|
||||||
|
await taskContext.SwitchToMainThreadAsync();
|
||||||
|
ProgressValue = 1;
|
||||||
|
Description = SH.ViewModelWelcomeDownloadSummaryComplete;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogError(ex, "Download Static Zip failed");
|
logger.LogError(ex, "Download static zip failed");
|
||||||
|
|
||||||
await taskContext.SwitchToMainThreadAsync();
|
await taskContext.SwitchToMainThreadAsync();
|
||||||
Description = ex is HttpRequestException httpRequestException
|
Description = ex is HttpRequestException httpRequestEx
|
||||||
? $"{SH.ViewModelWelcomeDownloadSummaryException} - [HTTP '{httpRequestException.StatusCode:D}'] [Error '{httpRequestException.HttpRequestError}']"
|
? $"{SH.ViewModelWelcomeDownloadSummaryException} - [HTTP '{httpRequestEx.StatusCode:D}'] [Error '{httpRequestEx.HttpRequestError}']"
|
||||||
: ex.Message;
|
: ex.Message;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -114,7 +116,7 @@ internal sealed class DownloadSummary : ObservableObject
|
|||||||
private void UpdateProgressStatus(StreamCopyStatus status)
|
private void UpdateProgressStatus(StreamCopyStatus status)
|
||||||
{
|
{
|
||||||
Description = $"{Converters.ToFileSizeString(status.BytesCopied)}/{Converters.ToFileSizeString(status.TotalBytes)}";
|
Description = $"{Converters.ToFileSizeString(status.BytesCopied)}/{Converters.ToFileSizeString(status.TotalBytes)}";
|
||||||
ProgressValue = status.TotalBytes == 0 ? 0 : (double)status.BytesCopied / status.TotalBytes;
|
ProgressValue = status.TotalBytes is 0 ? 0 : (double)status.BytesCopied / status.TotalBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExtractFiles(Stream stream)
|
private void ExtractFiles(Stream stream)
|
||||||
|
|||||||
@@ -24,7 +24,17 @@ internal enum GuideState : uint
|
|||||||
Environment,
|
Environment,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 开始下载资源
|
/// 正在查看常用设置
|
||||||
|
/// </summary>
|
||||||
|
CommonSetting,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 正在查看图像资源设置
|
||||||
|
/// </summary>
|
||||||
|
StaticResourceSetting,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开始下载图像资源
|
||||||
/// </summary>
|
/// </summary>
|
||||||
StaticResourceBegin,
|
StaticResourceBegin,
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Snap.Hutao.Core;
|
|||||||
using Snap.Hutao.Core.Setting;
|
using Snap.Hutao.Core.Setting;
|
||||||
using Snap.Hutao.Model;
|
using Snap.Hutao.Model;
|
||||||
using Snap.Hutao.Service;
|
using Snap.Hutao.Service;
|
||||||
|
using Snap.Hutao.Web.Hoyolab;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
@@ -20,12 +21,15 @@ internal sealed partial class GuideViewModel : Abstraction.ViewModel
|
|||||||
{
|
{
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
private readonly ITaskContext taskContext;
|
private readonly ITaskContext taskContext;
|
||||||
|
private readonly StaticResourceOptions staticResourceOptions;
|
||||||
private readonly CultureOptions cultureOptions;
|
private readonly CultureOptions cultureOptions;
|
||||||
private readonly RuntimeOptions runtimeOptions;
|
private readonly RuntimeOptions runtimeOptions;
|
||||||
|
private readonly AppOptions appOptions;
|
||||||
|
|
||||||
private string nextOrCompleteButtonText = SH.ViewModelGuideActionNext;
|
private string nextOrCompleteButtonText = SH.ViewModelGuideActionNext;
|
||||||
private bool isNextOrCompleteButtonEnabled = true;
|
private bool isNextOrCompleteButtonEnabled = true;
|
||||||
private NameValue<CultureInfo>? selectedCulture;
|
private NameValue<CultureInfo>? selectedCulture;
|
||||||
|
private NameValue<Region>? selectedRegion;
|
||||||
private bool isTermOfServiceAgreed;
|
private bool isTermOfServiceAgreed;
|
||||||
private bool isPrivacyPolicyAgreed;
|
private bool isPrivacyPolicyAgreed;
|
||||||
private bool isIssueReportAgreed;
|
private bool isIssueReportAgreed;
|
||||||
@@ -36,8 +40,7 @@ internal sealed partial class GuideViewModel : Abstraction.ViewModel
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
uint value = LocalSetting.Get(SettingKeys.Major1Minor7Revision0GuideState, 0U);
|
GuideState state = UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language);
|
||||||
GuideState state = (GuideState)value;
|
|
||||||
|
|
||||||
if (state is GuideState.Document)
|
if (state is GuideState.Document)
|
||||||
{
|
{
|
||||||
@@ -61,12 +64,12 @@ internal sealed partial class GuideViewModel : Abstraction.ViewModel
|
|||||||
(NextOrCompleteButtonText, IsNextOrCompleteButtonEnabled) = (SH.ViewModelGuideActionNext, true);
|
(NextOrCompleteButtonText, IsNextOrCompleteButtonEnabled) = (SH.ViewModelGuideActionNext, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return (uint)state;
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
LocalSetting.Set(SettingKeys.Major1Minor7Revision0GuideState, value);
|
LocalSetting.Set(SettingKeys.Major1Minor10Revision0GuideState, value);
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,6 +82,10 @@ internal sealed partial class GuideViewModel : Abstraction.ViewModel
|
|||||||
|
|
||||||
public RuntimeOptions RuntimeOptions { get => runtimeOptions; }
|
public RuntimeOptions RuntimeOptions { get => runtimeOptions; }
|
||||||
|
|
||||||
|
public AppOptions AppOptions { get => appOptions; }
|
||||||
|
|
||||||
|
public StaticResourceOptions StaticResourceOptions { get => staticResourceOptions; }
|
||||||
|
|
||||||
public NameValue<CultureInfo>? SelectedCulture
|
public NameValue<CultureInfo>? SelectedCulture
|
||||||
{
|
{
|
||||||
get => selectedCulture ??= CultureOptions.GetCurrentCultureForSelectionOrDefault();
|
get => selectedCulture ??= CultureOptions.GetCurrentCultureForSelectionOrDefault();
|
||||||
@@ -93,13 +100,26 @@ internal sealed partial class GuideViewModel : Abstraction.ViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NameValue<Region>? SelectedRegion
|
||||||
|
{
|
||||||
|
get => selectedRegion ??= AppOptions.GetCurrentRegionForSelectionOrDefault();
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref selectedRegion, value) && value is not null)
|
||||||
|
{
|
||||||
|
AppOptions.Region = value.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Agreement
|
||||||
public bool IsTermOfServiceAgreed
|
public bool IsTermOfServiceAgreed
|
||||||
{
|
{
|
||||||
get => isTermOfServiceAgreed; set
|
get => isTermOfServiceAgreed; set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref isTermOfServiceAgreed, value))
|
if (SetProperty(ref isTermOfServiceAgreed, value))
|
||||||
{
|
{
|
||||||
OnAgreeSateChanged();
|
OnAgreementStateChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,7 +130,7 @@ internal sealed partial class GuideViewModel : Abstraction.ViewModel
|
|||||||
{
|
{
|
||||||
if (SetProperty(ref isPrivacyPolicyAgreed, value))
|
if (SetProperty(ref isPrivacyPolicyAgreed, value))
|
||||||
{
|
{
|
||||||
OnAgreeSateChanged();
|
OnAgreementStateChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,7 +141,7 @@ internal sealed partial class GuideViewModel : Abstraction.ViewModel
|
|||||||
{
|
{
|
||||||
if (SetProperty(ref isIssueReportAgreed, value))
|
if (SetProperty(ref isIssueReportAgreed, value))
|
||||||
{
|
{
|
||||||
OnAgreeSateChanged();
|
OnAgreementStateChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,14 +152,12 @@ internal sealed partial class GuideViewModel : Abstraction.ViewModel
|
|||||||
{
|
{
|
||||||
if (SetProperty(ref isOpenSourceLicenseAgreed, value))
|
if (SetProperty(ref isOpenSourceLicenseAgreed, value))
|
||||||
{
|
{
|
||||||
OnAgreeSateChanged();
|
OnAgreementStateChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 下载信息
|
|
||||||
/// </summary>
|
|
||||||
public ObservableCollection<DownloadSummary>? DownloadSummaries
|
public ObservableCollection<DownloadSummary>? DownloadSummaries
|
||||||
{
|
{
|
||||||
get => downloadSummaries;
|
get => downloadSummaries;
|
||||||
@@ -152,7 +170,7 @@ internal sealed partial class GuideViewModel : Abstraction.ViewModel
|
|||||||
++State;
|
++State;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAgreeSateChanged()
|
private void OnAgreementStateChanged()
|
||||||
{
|
{
|
||||||
IsNextOrCompleteButtonEnabled = IsTermOfServiceAgreed && IsPrivacyPolicyAgreed && IsIssueReportAgreed && IsOpenSourceLicenseAgreed;
|
IsNextOrCompleteButtonEnabled = IsTermOfServiceAgreed && IsPrivacyPolicyAgreed && IsIssueReportAgreed && IsOpenSourceLicenseAgreed;
|
||||||
}
|
}
|
||||||
@@ -173,7 +191,7 @@ internal sealed partial class GuideViewModel : Abstraction.ViewModel
|
|||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
|
|
||||||
StaticResource.FulfillAll();
|
StaticResource.FulfillAll();
|
||||||
UnsafeLocalSetting.Set(SettingKeys.Major1Minor7Revision0GuideState, GuideState.Completed);
|
UnsafeLocalSetting.Set(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Completed);
|
||||||
AppInstance.Restart(string.Empty);
|
AppInstance.Restart(string.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ internal static class StaticResource
|
|||||||
|
|
||||||
private static readonly ApplicationDataCompositeValue DefaultResourceVersionMap = new()
|
private static readonly ApplicationDataCompositeValue DefaultResourceVersionMap = new()
|
||||||
{
|
{
|
||||||
// DO NOT MIDIFY THIS MAP
|
// DO NOT MODIFY THIS MAP
|
||||||
{ "AchievementIcon", 0 },
|
{ "AchievementIcon", 0 },
|
||||||
{ "AvatarCard", 0 },
|
{ "AvatarCard", 0 },
|
||||||
{ "AvatarIcon", 0 },
|
{ "AvatarIcon", 0 },
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Core.Setting;
|
||||||
|
using Snap.Hutao.Web.Request.Builder;
|
||||||
|
using Snap.Hutao.Web.Request.Builder.Abstraction;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.ViewModel.Guide;
|
||||||
|
|
||||||
|
internal static class StaticResourceHttpHeaderBuilderExtension
|
||||||
|
{
|
||||||
|
public static TBuilder SetStaticResourceControlHeaders<TBuilder>(this TBuilder builder)
|
||||||
|
where TBuilder : IHttpHeadersBuilder<HttpHeaders>
|
||||||
|
{
|
||||||
|
return builder
|
||||||
|
.SetHeader("x-quality", $"{UnsafeLocalSetting.Get(SettingKeys.StaticResourceImageQuality, StaticResourceQuality.Raw)}")
|
||||||
|
.SetHeader("x-minimum", $"{LocalSetting.Get(SettingKeys.StaticResourceUseTrimmedArchive, false)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Core.Setting;
|
||||||
|
using Snap.Hutao.Model;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.ViewModel.Guide;
|
||||||
|
|
||||||
|
[Injection(InjectAs.Singleton)]
|
||||||
|
internal sealed class StaticResourceOptions
|
||||||
|
{
|
||||||
|
private readonly List<NameValue<StaticResourceQuality>> imageQualities = CollectionsNameValue.FromEnum<StaticResourceQuality>(q => q.GetLocalizedDescription());
|
||||||
|
|
||||||
|
private NameValue<StaticResourceQuality>? imageQuality;
|
||||||
|
|
||||||
|
public StaticResourceOptions()
|
||||||
|
{
|
||||||
|
ImageQuality = ImageQualities.First(q => q.Value == UnsafeLocalSetting.Get(SettingKeys.StaticResourceImageQuality, StaticResourceQuality.Raw));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<NameValue<StaticResourceQuality>> ImageQualities { get => imageQualities; }
|
||||||
|
|
||||||
|
public NameValue<StaticResourceQuality>? ImageQuality
|
||||||
|
{
|
||||||
|
get => imageQuality;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value is not null)
|
||||||
|
{
|
||||||
|
imageQuality = value;
|
||||||
|
UnsafeLocalSetting.Set(SettingKeys.StaticResourceImageQuality, value.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UseTrimmedArchive
|
||||||
|
{
|
||||||
|
get => LocalSetting.Get(SettingKeys.StaticResourceUseTrimmedArchive, false);
|
||||||
|
set => LocalSetting.Set(SettingKeys.StaticResourceUseTrimmedArchive, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.ViewModel.Guide;
|
||||||
|
|
||||||
|
[Localization]
|
||||||
|
internal enum StaticResourceQuality
|
||||||
|
{
|
||||||
|
[LocalizationKey(nameof(SH.ViewModelGuideStaticResourceQualityRaw))]
|
||||||
|
Raw,
|
||||||
|
|
||||||
|
[LocalizationKey(nameof(SH.ViewModelGuideStaticResourceQualityHigh))]
|
||||||
|
High,
|
||||||
|
}
|
||||||
@@ -224,7 +224,7 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
|
|||||||
private static void ResetStaticResource()
|
private static void ResetStaticResource()
|
||||||
{
|
{
|
||||||
StaticResource.FailAll();
|
StaticResource.FailAll();
|
||||||
UnsafeLocalSetting.Set(SettingKeys.Major1Minor7Revision0GuideState, GuideState.StaticResourceBegin);
|
UnsafeLocalSetting.Set(SettingKeys.Major1Minor10Revision0GuideState, GuideState.StaticResourceBegin);
|
||||||
AppInstance.Restart(string.Empty);
|
AppInstance.Restart(string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ internal sealed partial class TestViewModel : Abstraction.ViewModel
|
|||||||
[Command("ResetGuideStateCommand")]
|
[Command("ResetGuideStateCommand")]
|
||||||
private static void ResetGuideState()
|
private static void ResetGuideState()
|
||||||
{
|
{
|
||||||
UnsafeLocalSetting.Set(SettingKeys.Major1Minor7Revision0GuideState, GuideState.Language);
|
UnsafeLocalSetting.Set(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command("ExceptionCommand")]
|
[Command("ExceptionCommand")]
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ internal static class HttpContentBuilderExtension
|
|||||||
}
|
}
|
||||||
|
|
||||||
[DebuggerStepThrough]
|
[DebuggerStepThrough]
|
||||||
public static T SetFormUrlEncodedContent<T>(
|
public static T SetFormUrlEncodedContent<T>(this T builder, IEnumerable<KeyValuePair<string, string>> content)
|
||||||
this T builder, IEnumerable<KeyValuePair<string, string>> content)
|
|
||||||
where T : IHttpContentBuilder
|
where T : IHttpContentBuilder
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(builder);
|
ArgumentNullException.ThrowIfNull(builder);
|
||||||
|
|||||||
@@ -41,12 +41,6 @@ internal static partial class HttpHeadersBuilderExtension
|
|||||||
return builder.ConfigureHeaders<TBuilder, THeaders>(headers => headers.Add(name, value));
|
return builder.ConfigureHeaders<TBuilder, THeaders>(headers => headers.Add(name, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T SetReferer<T>(this T builder, string referer)
|
|
||||||
where T : IHttpHeadersBuilder<HttpHeaders>
|
|
||||||
{
|
|
||||||
return builder.SetHeader("Referer", referer);
|
|
||||||
}
|
|
||||||
|
|
||||||
[DebuggerStepThrough]
|
[DebuggerStepThrough]
|
||||||
public static T SetHeader<T>(this T builder, string name)
|
public static T SetHeader<T>(this T builder, string name)
|
||||||
where T : IHttpHeadersBuilder<HttpHeaders>
|
where T : IHttpHeadersBuilder<HttpHeaders>
|
||||||
@@ -80,6 +74,12 @@ internal static partial class HttpHeadersBuilderExtension
|
|||||||
.AddHeader<TBuilder, THeaders>(name, value);
|
.AddHeader<TBuilder, THeaders>(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static T SetReferer<T>(this T builder, string referer)
|
||||||
|
where T : IHttpHeadersBuilder<HttpHeaders>
|
||||||
|
{
|
||||||
|
return builder.SetHeader("Referer", referer);
|
||||||
|
}
|
||||||
|
|
||||||
[DebuggerStepThrough]
|
[DebuggerStepThrough]
|
||||||
public static T RemoveHeader<T>(this T builder, params string?[]? names)
|
public static T RemoveHeader<T>(this T builder, params string?[]? names)
|
||||||
where T : IHttpHeadersBuilder<HttpHeaders>
|
where T : IHttpHeadersBuilder<HttpHeaders>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ internal static class HttpHeadersExtension
|
|||||||
ArgumentNullException.ThrowIfNull(name);
|
ArgumentNullException.ThrowIfNull(name);
|
||||||
|
|
||||||
// We have to work around the .NET API a little bit. See the comment below for details.
|
// We have to work around the .NET API a little bit. See the comment below for details.
|
||||||
values ??= Enumerable.Empty<string?>();
|
values ??= [];
|
||||||
values = values.Where(v => v is not null);
|
values = values.Where(v => v is not null);
|
||||||
|
|
||||||
if (values.Any())
|
if (values.Any())
|
||||||
|
|||||||
Reference in New Issue
Block a user