update flow rework

This commit is contained in:
Lightczx
2024-04-02 15:37:20 +08:00
parent e041def070
commit a91499171d
10 changed files with 192 additions and 25 deletions

View File

@@ -24,7 +24,13 @@ internal static class DependencyInjection
ServiceProvider serviceProvider = new ServiceCollection()
// Microsoft extension
.AddLogging(builder => builder.AddDebug().AddConsoleWindow())
.AddLogging(builder =>
{
builder
.SetMinimumLevel(LogLevel.Trace)
.AddDebug()
.AddConsoleWindow();
})
.AddMemoryCache()
// Hutao extensions

View File

@@ -9,7 +9,11 @@ internal static class LoggerFactoryExtension
{
builder.Services.AddSingleton<ConsoleWindowLifeTime>();
builder.AddSimpleConsole();
builder.AddSimpleConsole(options =>
{
options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff ";
});
return builder;
}
}

View File

@@ -2834,6 +2834,15 @@
<data name="ViewSpiralAbyssUploadRecord" xml:space="preserve">
<value>上传数据</value>
</data>
<data name="ViewTitileUpdatePackageDownloadContent" xml:space="preserve">
<value>是否立即下载</value>
</data>
<data name="ViewTitileUpdatePackageDownloadFailedMessage" xml:space="preserve">
<value>下载更新失败</value>
</data>
<data name="ViewTitileUpdatePackageDownloadTitle" xml:space="preserve">
<value>胡桃 {0} 版本已发布</value>
</data>
<data name="ViewTitileUpdatePackageReadyContent" xml:space="preserve">
<value>是否立即安装?</value>
</data>
@@ -2843,6 +2852,9 @@
<data name="ViewTitleAutoClicking" xml:space="preserve">
<value>自动连点</value>
</data>
<data name="ViewTitleUpdatePackageInstallingContent" xml:space="preserve">
<value>正在安装更新</value>
</data>
<data name="ViewToolHeader" xml:space="preserve">
<value>工具</value>
</data>

View File

@@ -0,0 +1,25 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core;
using Snap.Hutao.Core.IO.Hashing;
using Snap.Hutao.Core.IO.Http.Sharding;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Web.Hutao;
using Snap.Hutao.Web.Hutao.Response;
using Snap.Hutao.Web.Response;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using Windows.Storage;
namespace Snap.Hutao.Service.Update;
internal sealed class CheckUpdateResult
{
public CheckUpdateResultKind Kind { get; set; }
public HutaoVersionInformation? HutaoVersionInformation { get; set; }
}

View File

@@ -0,0 +1,15 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Service.Update;
internal enum CheckUpdateResultKind
{
None = 0,
VersionApiInvalidResponse = 1,
VersionApiInvalidSha256 = 2,
AlreayUpdated = 3,
NeedDownload = 4,
NeedInstall = 5,
}

View File

@@ -7,7 +7,9 @@ namespace Snap.Hutao.Service.Abstraction;
internal interface IUpdateService
{
ValueTask<bool> CheckForUpdateAndDownloadAsync(IProgress<UpdateStatus> progress, CancellationToken token = default);
ValueTask<CheckUpdateResult> CheckUpdateAsync(IProgress<UpdateStatus> progress, CancellationToken token = default);
ValueTask<bool> LaunchUpdaterAsync();
ValueTask<bool> DownloadUpdateAsync(CheckUpdateResult checkUpdateResult, IProgress<UpdateStatus> progress, CancellationToken token = default);
ValueTask<LaunchUpdaterResult> LaunchUpdaterAsync();
}

View File

@@ -0,0 +1,24 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core;
using Snap.Hutao.Core.IO.Hashing;
using Snap.Hutao.Core.IO.Http.Sharding;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Web.Hutao;
using Snap.Hutao.Web.Hutao.Response;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using Windows.Storage;
namespace Snap.Hutao.Service.Update;
internal sealed class LaunchUpdaterResult
{
public bool IsSuccess { get; set; }
public Process? Process { get; set; }
}

View File

@@ -24,54 +24,71 @@ internal sealed partial class UpdateService : IUpdateService
private readonly IServiceProvider serviceProvider;
public async ValueTask<bool> CheckForUpdateAndDownloadAsync(IProgress<UpdateStatus> progress, CancellationToken token = default)
public async ValueTask<CheckUpdateResult> CheckUpdateAsync(IProgress<UpdateStatus> progress, CancellationToken token = default)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
ITaskContext taskContext = scope.ServiceProvider.GetRequiredService<ITaskContext>();
await taskContext.SwitchToBackgroundAsync();
HutaoInfrastructureClient infrastructureClient = serviceProvider.GetRequiredService<HutaoInfrastructureClient>();
HutaoInfrastructureClient infrastructureClient = scope.ServiceProvider.GetRequiredService<HutaoInfrastructureClient>();
HutaoResponse<HutaoVersionInformation> response = await infrastructureClient.GetHutaoVersionInfomationAsync(token).ConfigureAwait(false);
CheckUpdateResult checkUpdateResult = new();
if (!response.IsOk())
{
return false;
checkUpdateResult.Kind = CheckUpdateResultKind.VersionApiInvalidResponse;
return checkUpdateResult;
}
else
{
checkUpdateResult.Kind = CheckUpdateResultKind.NeedDownload;
checkUpdateResult.HutaoVersionInformation = response.Data;
}
HutaoVersionInformation versionInformation = response.Data;
string msixPath = GetUpdatePackagePath();
if (!LocalSetting.Get(SettingKeys.OverrideUpdateVersionComparison, false))
{
if (scope.ServiceProvider.GetRequiredService<RuntimeOptions>().Version >= versionInformation.Version)
// Launched in an updated version
if (scope.ServiceProvider.GetRequiredService<RuntimeOptions>().Version >= checkUpdateResult.HutaoVersionInformation.Version)
{
if (File.Exists(msixPath))
{
File.Delete(msixPath);
}
return false;
checkUpdateResult.Kind = CheckUpdateResultKind.AlreayUpdated;
return checkUpdateResult;
}
}
progress.Report(new(versionInformation.Version.ToString(), 0, 0));
progress.Report(new(checkUpdateResult.HutaoVersionInformation.Version.ToString(), 0, 0));
if (versionInformation.Sha256 is not { Length: > 0 } sha256)
if (checkUpdateResult.HutaoVersionInformation.Sha256 is not { Length: > 0 } sha256)
{
return false;
checkUpdateResult.Kind = CheckUpdateResultKind.VersionApiInvalidSha256;
return checkUpdateResult;
}
if (File.Exists(msixPath) && await CheckUpdateCacheSHA256Async(msixPath, sha256, token).ConfigureAwait(false))
{
return true;
checkUpdateResult.Kind = CheckUpdateResultKind.NeedInstall;
return checkUpdateResult;
}
return await DownloadUpdatePackageAsync(versionInformation, msixPath, progress, token).ConfigureAwait(false);
return checkUpdateResult;
}
}
public async ValueTask<bool> LaunchUpdaterAsync()
public ValueTask<bool> DownloadUpdateAsync(CheckUpdateResult checkUpdateResult, IProgress<UpdateStatus> progress, CancellationToken token = default)
{
ArgumentNullException.ThrowIfNull(checkUpdateResult.HutaoVersionInformation);
return DownloadUpdatePackageAsync(checkUpdateResult.HutaoVersionInformation, GetUpdatePackagePath(), progress, token);
}
public async ValueTask<LaunchUpdaterResult> LaunchUpdaterAsync()
{
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
string updaterTargetPath = runtimeOptions.GetDataFolderUpdateCacheFolderFile(UpdaterFilename);
@@ -88,19 +105,19 @@ internal sealed partial class UpdateService : IUpdateService
try
{
Process.Start(new ProcessStartInfo()
Process? process = Process.Start(new ProcessStartInfo()
{
Arguments = commandLine,
FileName = updaterTargetPath,
UseShellExecute = true,
});
return true;
return new() { IsSuccess = true, Process = process };
}
catch (Exception ex)
{
serviceProvider.GetRequiredService<IInfoBarService>().Error(ex);
return false;
return new() { IsSuccess = false };
}
}

View File

@@ -321,11 +321,11 @@
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240311000" />
<PackageReference Include="QRCoder" Version="1.4.3" />
<PackageReference Include="Snap.Discord.GameSDK" Version="1.6.0" />
<PackageReference Include="Snap.Hutao.Deployment.Runtime" Version="1.15.3">
<PackageReference Include="Snap.Hutao.Deployment.Runtime" Version="1.16.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Snap.Hutao.SourceGeneration" Version="1.0.6">
<PackageReference Include="Snap.Hutao.SourceGeneration" Version="1.0.7">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -2,12 +2,15 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control.Extension;
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.Notification;
using Snap.Hutao.Service.Update;
using System.Diagnostics;
using System.Globalization;
using System.Text;
@@ -19,6 +22,7 @@ internal sealed partial class TitleViewModel : Abstraction.ViewModel
{
private readonly IContentDialogFactory contentDialogFactory;
private readonly IProgressFactory progressFactory;
private readonly IInfoBarService infoBarService;
private readonly RuntimeOptions runtimeOptions;
private readonly HotKeyOptions hotKeyOptions;
private readonly IUpdateService updateService;
@@ -61,21 +65,79 @@ internal sealed partial class TitleViewModel : Abstraction.ViewModel
private async ValueTask DoCheckUpdateAsync()
{
IProgress<UpdateStatus> progress = progressFactory.CreateForMainThread<UpdateStatus>(status => UpdateStatus = status);
if (await updateService.CheckForUpdateAndDownloadAsync(progress).ConfigureAwait(false))
CheckUpdateResult checkUpdateResult = await updateService.CheckUpdateAsync(progress).ConfigureAwait(false);
if (checkUpdateResult.Kind is CheckUpdateResultKind.NeedDownload)
{
ContentDialogResult result = await contentDialogFactory
ContentDialogResult downloadUpdateUserConsentResult = await contentDialogFactory
.CreateForConfirmCancelAsync(
SH.FormatViewTitileUpdatePackageDownloadTitle(UpdateStatus?.Version),
SH.ViewTitileUpdatePackageDownloadContent,
ContentDialogButton.Primary)
.ConfigureAwait(false);
if (downloadUpdateUserConsentResult is ContentDialogResult.Primary)
{
// This method will set CheckUpdateResult.Kind to NeedInstall if download success
if (!await DownloadPackageAsync(progress, checkUpdateResult).ConfigureAwait(false))
{
infoBarService.Warning(SH.ViewTitileUpdatePackageDownloadFailedMessage);
return;
}
}
}
if (checkUpdateResult.Kind is CheckUpdateResultKind.NeedInstall)
{
ContentDialogResult installUpdateUserConsentResult = await contentDialogFactory
.CreateForConfirmCancelAsync(
SH.FormatViewTitileUpdatePackageReadyTitle(UpdateStatus?.Version),
SH.ViewTitileUpdatePackageReadyContent,
ContentDialogButton.Primary)
.ConfigureAwait(false);
if (result == ContentDialogResult.Primary)
if (installUpdateUserConsentResult is ContentDialogResult.Primary)
{
await updateService.LaunchUpdaterAsync().ConfigureAwait(false);
LaunchUpdaterResult launchUpdaterResult = await updateService.LaunchUpdaterAsync().ConfigureAwait(false);
if (launchUpdaterResult.IsSuccess)
{
ContentDialog contentDialog = await contentDialogFactory
.CreateForIndeterminateProgressAsync(SH.ViewTitleUpdatePackageInstallingContent)
.ConfigureAwait(false);
using (await contentDialog.BlockAsync(taskContext).ConfigureAwait(false))
{
if (launchUpdaterResult.Process is { } updater)
{
await updater.WaitForExitAsync().ConfigureAwait(false);
}
}
}
}
}
await taskContext.SwitchToMainThreadAsync();
UpdateStatus = null;
}
private async ValueTask<bool> DownloadPackageAsync(IProgress<UpdateStatus> progress, CheckUpdateResult checkUpdateResult)
{
bool downloadSuccess = true;
try
{
if (await updateService.DownloadUpdateAsync(checkUpdateResult, progress).ConfigureAwait(false))
{
checkUpdateResult.Kind = CheckUpdateResultKind.NeedInstall;
}
else
{
downloadSuccess = false;
}
}
catch
{
downloadSuccess = false;
}
return downloadSuccess;
}
}