1.9.0 package

This commit is contained in:
DismissedLight
2023-12-22 22:02:10 +08:00
parent ad240a543d
commit b8bcad2107
17 changed files with 189 additions and 62 deletions

View File

@@ -130,7 +130,7 @@ internal sealed class HttpShardCopyWorker<TStatus> : IDisposable
BytesRead = bytesRead;
}
public int BytesRead { get; set; }
public int BytesRead { get; }
}
private sealed class ShardProgress : IProgress<ShardStatus>
@@ -152,11 +152,11 @@ internal sealed class HttpShardCopyWorker<TStatus> : IDisposable
public void Report(ShardStatus value)
{
Interlocked.Add(ref totalBytesRead, value.BytesRead);
if (stopwatch.GetElapsedTime().TotalMilliseconds > 500)
if (stopwatch.GetElapsedTime().TotalMilliseconds > 1000 || totalBytesRead == contentLength)
{
lock (syncRoot)
{
if (stopwatch.GetElapsedTime().TotalMilliseconds > 500)
if (stopwatch.GetElapsedTime().TotalMilliseconds > 1000 || totalBytesRead == contentLength)
{
workerProgress.Report(statusFactory(totalBytesRead, contentLength));
stopwatch = ValueStopwatch.StartNew();

View File

@@ -65,7 +65,7 @@ internal class StreamCopyWorker<TStatus>
await destination.WriteAsync(buffer[..bytesRead]).ConfigureAwait(false);
totalBytesRead += bytesRead;
if (stopwatch.GetElapsedTime().TotalMilliseconds > 500)
if (stopwatch.GetElapsedTime().TotalMilliseconds > 1000)
{
progress.Report(statusFactory(totalBytesRead));
stopwatch = ValueStopwatch.StartNew();

View File

@@ -12,7 +12,7 @@
<Identity
Name="60568DGPStudio.SnapHutao"
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
Version="1.8.5.0" />
Version="1.9.0.0" />
<Properties>
<DisplayName>Snap Hutao</DisplayName>

View File

@@ -12,7 +12,7 @@
<Identity
Name="60568DGPStudio.SnapHutaoDev"
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
Version="1.8.4.0" />
Version="1.9.0.0" />
<Properties>
<DisplayName>Snap Hutao Dev</DisplayName>

View File

@@ -995,6 +995,9 @@
<data name="ServiceUIGFImportUnsupportedVersion" xml:space="preserve">
<value>不支持的 UIGF 版本</value>
</data>
<data name="ServiceUpdateStatusVersionDescription" xml:space="preserve">
<value>发现新版本 {0}</value>
</data>
<data name="ServiceUserCurrentMultiMatched" xml:space="preserve">
<value>多个用户记录为选中状态</value>
</data>
@@ -2621,6 +2624,12 @@
<data name="ViewSpiralAbyssUploadRecord" xml:space="preserve">
<value>上传数据</value>
</data>
<data name="ViewTitileUpdatePackageReadyContent" xml:space="preserve">
<value>是否立即安装?</value>
</data>
<data name="ViewTitileUpdatePackageReadyTitle" xml:space="preserve">
<value>胡桃 {0} 版本已准备就绪</value>
</data>
<data name="ViewTitleAutoClicking" xml:space="preserve">
<value>自动连点</value>
</data>
@@ -2877,7 +2886,7 @@
<value>国际服 亚服</value>
</data>
<data name="WebHoyolabRegionOSCHT" xml:space="preserve">
<value>国际服 台服</value>
<value>国际服 港澳台服</value>
</data>
<data name="WebHoyolabRegionOSEURO" xml:space="preserve">
<value>国际服 欧服</value>

View File

@@ -1,8 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Service.Abstraction;
internal interface IUpdateService
{
}

View File

@@ -74,7 +74,7 @@ internal sealed partial class AppOptions : DbStoreOptions
set => SetOption(ref currentCulture, SettingEntry.Culture, value, value => value.Name);
}
public List<NameValue<Region>> Regions { get; } = KnownRegions.Get();
public Lazy<List<NameValue<Region>>> LazyRegions { get; } = new(KnownRegions.Get);
public Region Region
{

View File

@@ -16,6 +16,6 @@ internal static class AppOptionsExtension
public static NameValue<Region>? GetCurrentRegionForSelectionOrDefault(this AppOptions appOptions)
{
return appOptions.Regions.SingleOrDefault(c => c.Value.Value == appOptions.Region.Value);
return appOptions.LazyRegions.Value.SingleOrDefault(c => c.Value.Value == appOptions.Region.Value);
}
}

View File

@@ -13,6 +13,7 @@ internal static class SupportedCultures
ToNameValue(CultureInfo.GetCultureInfo("zh-Hans")),
ToNameValue(CultureInfo.GetCultureInfo("zh-Hant")),
ToNameValue(CultureInfo.GetCultureInfo("en")),
ToNameValue(CultureInfo.GetCultureInfo("ru")),
ToNameValue(CultureInfo.GetCultureInfo("ja")),
ToNameValue(CultureInfo.GetCultureInfo("id")),
ToNameValue(CultureInfo.GetCultureInfo("ko")),

View File

@@ -0,0 +1,13 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Service.Update;
namespace Snap.Hutao.Service.Abstraction;
internal interface IUpdateService
{
ValueTask<bool> CheckForUpdateAndDownloadAsync(IProgress<UpdateStatus> progress, CancellationToken token = default);
void LaunchInstaller();
}

View File

@@ -2,15 +2,17 @@
// Licensed under the MIT license.
using Snap.Hutao.Core;
using Snap.Hutao.Core.IO;
using Snap.Hutao.Core.IO.Hashing;
using Snap.Hutao.Core.IO.Http.Sharding;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Web.Hutao;
using Snap.Hutao.Web.Hutao.Response;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
namespace Snap.Hutao.Service;
namespace Snap.Hutao.Service.Update;
[ConstructorGenerated]
[Injection(InjectAs.Singleton, typeof(IUpdateService))]
@@ -18,7 +20,7 @@ internal sealed partial class UpdateService : IUpdateService
{
private readonly IServiceProvider serviceProvider;
public async ValueTask<bool> InitializeAsync(IProgress<UpdateStatus> progress, CancellationToken token = default)
public async ValueTask<bool> CheckForUpdateAndDownloadAsync(IProgress<UpdateStatus> progress, CancellationToken token = default)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
@@ -34,16 +36,26 @@ internal sealed partial class UpdateService : IUpdateService
}
HutaoVersionInformation versionInformation = response.Data;
string msixPath = GetUpdatePackagePath();
if (scope.ServiceProvider.GetRequiredService<RuntimeOptions>().Version >= versionInformation.Version)
{
if (File.Exists(msixPath))
{
File.Delete(msixPath);
}
return false;
}
progress.Report(new(versionInformation.Version.ToString(), 0, 0));
if (versionInformation.Sha256 is not { } sha256)
{
return false;
}
string dataFolder = scope.ServiceProvider.GetRequiredService<RuntimeOptions>().DataFolder;
string msixPath = Path.Combine(dataFolder, "UpdateCache/Snap.Hutao.msix");
if (await CheckUpdateCacheSHA256Async(msixPath, sha256, token).ConfigureAwait(false))
if (File.Exists(msixPath) && await CheckUpdateCacheSHA256Async(msixPath, sha256, token).ConfigureAwait(false))
{
return true;
}
@@ -52,12 +64,29 @@ internal sealed partial class UpdateService : IUpdateService
}
}
public void LaunchInstaller()
{
Process.Start(new ProcessStartInfo()
{
UseShellExecute = true,
FileName = GetUpdatePackagePath(),
});
}
private static async ValueTask<bool> CheckUpdateCacheSHA256Async(string filePath, string remoteHash, CancellationToken token = default)
{
string localHash = await SHA256.HashFileAsync(filePath, token).ConfigureAwait(false);
return string.Equals(localHash, remoteHash, StringComparison.OrdinalIgnoreCase);
}
private string GetUpdatePackagePath()
{
string dataFolder = serviceProvider.GetRequiredService<RuntimeOptions>().DataFolder;
string directory = Path.Combine(dataFolder, "UpdateCache");
Directory.CreateDirectory(directory);
return Path.Combine(directory, "Snap.Hutao.msix");
}
private async ValueTask<bool> DownloadUpdatePackageAsync(HutaoVersionInformation versionInformation, string filePath, IProgress<UpdateStatus> progress, CancellationToken token = default)
{
using (IServiceScope scope = serviceProvider.CreateScope())

View File

@@ -0,0 +1,30 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Common;
namespace Snap.Hutao.Service.Update;
internal sealed class UpdateStatus
{
public UpdateStatus(string version, long bytesRead, long totalBytes)
{
Version = version;
VersionDescription = SH.FormatServiceUpdateStatusVersionDescription(Version);
BytesRead = bytesRead;
TotalBytes = totalBytes;
ProgressDescription = bytesRead != totalBytes
? $"{Converters.ToFileSizeString(bytesRead)}/{Converters.ToFileSizeString(totalBytes)}"
: string.Empty;
}
public string? Version { get; set; }
public string VersionDescription { get; }
public double BytesRead { get; set; }
public double TotalBytes { get; set; }
public string ProgressDescription { get; }
}

View File

@@ -1,25 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Common;
namespace Snap.Hutao.Service;
internal sealed class UpdateStatus
{
public UpdateStatus(string version, long bytesRead, long totalBytes)
{
Version = version;
BytesRead = bytesRead;
TotalBytes = totalBytes;
ProgressDescription = $"{Converters.ToFileSizeString(bytesRead)}/{Converters.ToFileSizeString(totalBytes)}";
}
public string? Version { get; set; }
public long BytesRead { get; set; }
public long TotalBytes { get; set; }
public string ProgressDescription { get; }
}

View File

@@ -312,7 +312,7 @@
<shc:SizeRestrictedContentControl>
<ComboBox
DisplayMemberPath="Name"
ItemsSource="{Binding AppOptions.Regions}"
ItemsSource="{Binding AppOptions.LazyRegions.Value}"
SelectedItem="{Binding SelectedRegion, Mode=TwoWay}"/>
</shc:SizeRestrictedContentControl>
</cwc:SettingsCard>

View File

@@ -24,11 +24,11 @@
<StackPanel
Grid.Column="1"
Margin="0,0,6,0"
Orientation="Horizontal"
Spacing="6"
Visibility="{x:Bind RuntimeOptions.IsElevated, Converter={StaticResource BoolToVisibilityConverter}}">
<ToggleButton
Margin="0,0,6,0"
VerticalAlignment="Center"
IsChecked="{x:Bind HotKeyOptions.IsMouseClickRepeatForeverOn, Mode=OneWay}"
IsHitTestVisible="False"
@@ -49,6 +49,36 @@
Text="{x:Bind HotKeyOptions.MouseClickRepeatForeverKeyCombination.DisplayName, Mode=OneWay}"/>
</Grid>
</ToggleButton>
<Border
Margin="0,6"
Style="{ThemeResource BorderCardStyle}"
Visibility="{x:Bind UpdateStatus, Converter={StaticResource EmptyObjectToVisibilityConverter}, Mode=OneWay}">
<Grid Padding="12,0" ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ProgressBar
Grid.ColumnSpan="2"
MinHeight="32"
Margin="-12,0"
HorizontalAlignment="Stretch"
Background="Transparent"
CornerRadius="{ThemeResource ControlCornerRadius}"
Maximum="{x:Bind UpdateStatus.TotalBytes, Mode=OneWay}"
Opacity="{ThemeResource LargeBackgroundProgressBarOpacity}"
Value="{x:Bind UpdateStatus.BytesRead, Mode=OneWay}"/>
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
Text="{x:Bind UpdateStatus.ProgressDescription, Mode=OneWay}"/>
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
Text="{x:Bind UpdateStatus.VersionDescription, Mode=OneWay}"/>
</Grid>
</Border>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -1,10 +1,15 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Core;
using Snap.Hutao.Core.Windowing.HotKey;
using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Factory.Progress;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.Update;
namespace Snap.Hutao.View;
@@ -12,19 +17,19 @@ namespace Snap.Hutao.View;
/// 标题视图
/// </summary>
[HighQuality]
[INotifyPropertyChanged]
internal sealed partial class TitleView : UserControl
{
/// <summary>
/// 构造一个新的标题视图
/// </summary>
private CancellationTokenSource checkUpdateTaskCancellationTokenSource = new();
private UpdateStatus? updateStatus;
public TitleView()
{
Loaded += OnTitleViewLoaded;
Unloaded += OnTitleViewUnloaded;
InitializeComponent();
}
/// <summary>
/// 标题
/// </summary>
public string Title
{
[SuppressMessage("", "IDE0027")]
@@ -38,9 +43,6 @@ internal sealed partial class TitleView : UserControl
}
}
/// <summary>
/// 获取可拖动区域
/// </summary>
public FrameworkElement DragArea
{
get => DragableGrid;
@@ -49,4 +51,44 @@ internal sealed partial class TitleView : UserControl
public RuntimeOptions RuntimeOptions { get; } = Ioc.Default.GetRequiredService<RuntimeOptions>();
public HotKeyOptions HotKeyOptions { get; } = Ioc.Default.GetRequiredService<HotKeyOptions>();
}
public UpdateStatus? UpdateStatus { get => updateStatus; set => SetProperty(ref updateStatus, value); }
private void OnTitleViewLoaded(object sender, RoutedEventArgs e)
{
DoCheckUpdateAsync(checkUpdateTaskCancellationTokenSource.Token).SafeForget();
Loaded -= OnTitleViewLoaded;
}
private void OnTitleViewUnloaded(object sender, RoutedEventArgs e)
{
checkUpdateTaskCancellationTokenSource.Cancel();
Unloaded -= OnTitleViewUnloaded;
}
private async ValueTask DoCheckUpdateAsync(CancellationToken token)
{
IServiceProvider serviceProvider = Ioc.Default;
IUpdateService updateService = serviceProvider.GetRequiredService<IUpdateService>();
IProgressFactory progressFactory = serviceProvider.GetRequiredService<IProgressFactory>();
IProgress<UpdateStatus> progress = progressFactory.CreateForMainThread<UpdateStatus>(status => UpdateStatus = status);
if (await updateService.CheckForUpdateAndDownloadAsync(progress, token).ConfigureAwait(false))
{
ContentDialogResult result = await serviceProvider
.GetRequiredService<IContentDialogFactory>()
.CreateForConfirmCancelAsync(
SH.FormatViewTitileUpdatePackageReadyTitle(UpdateStatus?.Version),
SH.ViewTitileUpdatePackageReadyContent,
ContentDialogButton.Primary)
.ConfigureAwait(false);
if (result == ContentDialogResult.Primary)
{
updateService.LaunchInstaller();
}
}
await serviceProvider.GetRequiredService<ITaskContext>().SwitchToMainThreadAsync();
UpdateStatus = null;
}
}

View File

@@ -1,9 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Text.RegularExpressions;
namespace Snap.Hutao.Web.Hutao;
internal sealed class IPInformation
internal sealed partial class IPInformation
{
private const string Unknown = "Unknown";
@@ -26,6 +28,10 @@ internal sealed class IPInformation
return SH.WebHutaoServiceUnAvailable;
}
return SH.FormatViewPageSettingDeviceIpDescription(Ip, Division);
string maskedIp = IpRegex().Replace(Ip, "$1.$2.*.*");
return SH.FormatViewPageSettingDeviceIpDescription(maskedIp, Division);
}
[GeneratedRegex(@"(\d+)\.(\d+)\.\d+\.\d+")]
private static partial Regex IpRegex();
}