fix translation

This commit is contained in:
DismissedLight
2023-02-09 12:26:42 +08:00
parent 6c83cd3da5
commit 165c33ef2c
26 changed files with 516 additions and 259 deletions

View File

@@ -14,7 +14,7 @@ body:
id: back id: back
attributes: attributes:
label: 背景与动机 label: 背景与动机
description: 添加此功能的理由 description: 添加此功能的理由,如果你想要实现多个功能,请分别发起多个单独的 Issue
validations: validations:
required: true required: true

View File

@@ -43,6 +43,7 @@ internal class AutoHeightBehavior : BehaviorBase<FrameworkElement>
protected override void OnDetaching() protected override void OnDetaching()
{ {
AssociatedObject.SizeChanged -= OnSizeChanged; AssociatedObject.SizeChanged -= OnSizeChanged;
base.OnDetaching();
} }
private void OnSizeChanged(object sender, SizeChangedEventArgs e) private void OnSizeChanged(object sender, SizeChangedEventArgs e)

View File

@@ -43,6 +43,7 @@ internal class AutoWidthBehavior : BehaviorBase<FrameworkElement>
protected override void OnDetaching() protected override void OnDetaching()
{ {
AssociatedObject.SizeChanged -= OnSizeChanged; AssociatedObject.SizeChanged -= OnSizeChanged;
base.OnDetaching();
} }
private void OnSizeChanged(object sender, SizeChangedEventArgs e) private void OnSizeChanged(object sender, SizeChangedEventArgs e)

View File

@@ -30,6 +30,15 @@ internal class ComboBoxExtendsContentIntoTitleBarWorkaroundBehavior : BehaviorBa
AssociatedObject.DropDownClosed += OnDropDownClosed; AssociatedObject.DropDownClosed += OnDropDownClosed;
} }
/// <inheritdoc/>
protected override void OnDetaching()
{
AssociatedObject.DropDownOpened -= OnDropDownOpened;
AssociatedObject.DropDownClosed -= OnDropDownClosed;
base.OnDetaching();
}
private void OnDropDownOpened(object? sender, object e) private void OnDropDownOpened(object? sender, object e)
{ {
messenger.Send(new Message.FlyoutOpenCloseMessage(true)); messenger.Send(new Message.FlyoutOpenCloseMessage(true));

View File

@@ -40,7 +40,5 @@ internal class InvokeCommandOnLoadedBehavior : BehaviorBase<UIElement>
{ {
Command?.Execute(CommandParameter); Command?.Execute(CommandParameter);
} }
base.OnAssociatedObjectLoaded();
} }
} }

View File

@@ -0,0 +1,21 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Core.ExceptionService;
/// <summary>
/// 运行环境异常
/// 用户的计算机中的某些设置不符合要求
/// </summary>
internal class RuntimeEnvironmentException : Exception
{
/// <summary>
/// 构造一个新的运行环境异常
/// </summary>
/// <param name="message">消息</param>
/// <param name="innerException">内部错误</param>
public RuntimeEnvironmentException(string message, Exception innerException)
: base(message, innerException)
{
}
}

View File

@@ -17,8 +17,8 @@ internal static class ThrowHelper
/// </summary> /// </summary>
/// <param name="message">消息</param> /// <param name="message">消息</param>
/// <param name="inner">内部错误</param> /// <param name="inner">内部错误</param>
/// <exception cref="OperationCanceledException">操作取消异常</exception>
/// <returns>nothing</returns> /// <returns>nothing</returns>
/// <exception cref="OperationCanceledException">操作取消异常</exception>
[DoesNotReturn] [DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static OperationCanceledException OperationCanceled(string message, Exception? inner) public static OperationCanceledException OperationCanceled(string message, Exception? inner)
@@ -45,12 +45,26 @@ internal static class ThrowHelper
/// </summary> /// </summary>
/// <param name="message">消息</param> /// <param name="message">消息</param>
/// <param name="inner">内部错误</param> /// <param name="inner">内部错误</param>
/// <exception cref="UserdataCorruptedException">数据损坏</exception>
/// <returns>nothing</returns> /// <returns>nothing</returns>
/// <exception cref="UserdataCorruptedException">数据损坏</exception>
[DoesNotReturn] [DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static UserdataCorruptedException UserdataCorrupted(string message, Exception inner) public static UserdataCorruptedException UserdataCorrupted(string message, Exception inner)
{ {
throw new UserdataCorruptedException(message, inner); throw new UserdataCorruptedException(message, inner);
} }
/// <summary>
/// 运行环境异常
/// </summary>
/// <param name="message">消息</param>
/// <param name="inner">内部错误</param>
/// <returns>nothing</returns>
/// <exception cref="RuntimeEnvironmentException">环境异常</exception>
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
public static RuntimeEnvironmentException RuntimeEnvironment(string message, Exception inner)
{
throw new RuntimeEnvironmentException(message, inner);
}
} }

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license. // Licensed under the MIT license.
using Snap.Hutao.Factory.Abstraction; using Snap.Hutao.Factory.Abstraction;
using Windows.Foundation.Metadata;
using Windows.Storage.Pickers; using Windows.Storage.Pickers;
using WinRT.Interop; using WinRT.Interop;
@@ -37,8 +38,12 @@ internal class PickerFactory : IPickerFactory
picker.FileTypeFilter.Add(type); picker.FileTypeFilter.Add(type);
} }
// below Windows 11
if (!ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 13))
{
// https://github.com/microsoft/WindowsAppSDK/issues/2931 // https://github.com/microsoft/WindowsAppSDK/issues/2931
picker.FileTypeFilter.Add(AnyType); picker.FileTypeFilter.Add(AnyType);
}
return picker; return picker;
} }
@@ -52,7 +57,16 @@ internal class PickerFactory : IPickerFactory
/// <inheritdoc/> /// <inheritdoc/>
public FolderPicker GetFolderPicker() public FolderPicker GetFolderPicker()
{ {
return GetInitializedPicker<FolderPicker>(); FolderPicker picker = GetInitializedPicker<FolderPicker>();
// below Windows 11
if (!ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 13))
{
// https://github.com/microsoft/WindowsAppSDK/issues/2931
picker.FileTypeFilter.Add(AnyType);
}
return picker;
} }
private T GetInitializedPicker<T>() private T GetInitializedPicker<T>()

View File

@@ -18,11 +18,13 @@ internal class InventoryItem : ObservableObject
/// </summary> /// </summary>
/// <param name="inner">元数据</param> /// <param name="inner">元数据</param>
/// <param name="entity">实体</param> /// <param name="entity">实体</param>
public InventoryItem(Material inner, Entity.InventoryItem entity) /// <param name="saveCommand">保存命令</param>
public InventoryItem(Material inner, Entity.InventoryItem entity, ICommand saveCommand)
{ {
Entity = entity; Entity = entity;
Inner = inner; Inner = inner;
count = entity.Count; count = entity.Count;
SaveCountCommand = saveCommand;
} }
/// <summary> /// <summary>
@@ -35,6 +37,11 @@ internal class InventoryItem : ObservableObject
/// </summary> /// </summary>
public Material Inner { get; set; } public Material Inner { get; set; }
/// <summary>
/// 保存个数命令
/// </summary>
public ICommand? SaveCountCommand { get; set; }
/// <summary> /// <summary>
/// 个数 /// 个数
/// </summary> /// </summary>
@@ -45,7 +52,7 @@ internal class InventoryItem : ObservableObject
if (SetProperty(ref count, value)) if (SetProperty(ref count, value))
{ {
Entity.Count = value; Entity.Count = value;
Ioc.Default.GetRequiredService<Service.Cultivation.ICultivationService>().SaveInventoryItem(this); SaveCountCommand?.Execute(this);
} }
} }
} }

View File

@@ -50,7 +50,7 @@ public class UIGF
{ {
foreach (UIGFItem item in List) foreach (UIGFItem item in List)
{ {
if (item.ItemType != "角色" || item.ItemType != "武器") if (item.ItemType != "角色" && item.ItemType != "武器")
{ {
return false; return false;
} }

View File

@@ -5,7 +5,6 @@
<DeveloperAccountType>MSA</DeveloperAccountType> <DeveloperAccountType>MSA</DeveloperAccountType>
<GeneratePackageHash>http://www.w3.org/2001/04/xmlenc#sha256</GeneratePackageHash> <GeneratePackageHash>http://www.w3.org/2001/04/xmlenc#sha256</GeneratePackageHash>
<SupportedLocales> <SupportedLocales>
<Language Code="en-us" InMinimumRequirementSet="true" />
<Language Code="zh-cn" InMinimumRequirementSet="true" /> <Language Code="zh-cn" InMinimumRequirementSet="true" />
</SupportedLocales> </SupportedLocales>
<ProductReservedInfo> <ProductReservedInfo>

View File

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

View File

@@ -169,7 +169,7 @@ namespace Snap.Hutao.Resource.Localization {
} }
/// <summary> /// <summary>
/// 查找类似 用户数据已损坏: {0} 的本地化字符串。 /// 查找类似 用户数据已损坏{0} 的本地化字符串。
/// </summary> /// </summary>
internal static string CoreExceptionServiceUserdataCorruptedMessage { internal static string CoreExceptionServiceUserdataCorruptedMessage {
get { get {
@@ -511,7 +511,7 @@ namespace Snap.Hutao.Resource.Localization {
} }
/// <summary> /// <summary>
/// 查找类似 无法获取祈愿记录: {0} 的本地化字符串。 /// 查找类似 无法获取祈愿记录{0} 的本地化字符串。
/// </summary> /// </summary>
internal static string ServiceGachaLogArchiveCollectionUserdataCorruptedMessage { internal static string ServiceGachaLogArchiveCollectionUserdataCorruptedMessage {
get { get {
@@ -601,7 +601,7 @@ namespace Snap.Hutao.Resource.Localization {
} }
/// <summary> /// <summary>
/// 查找类似 不支持的 Item Id: {0} 的本地化字符串。 /// 查找类似 不支持的 Item Id{0} 的本地化字符串。
/// </summary> /// </summary>
internal static string ServiceGachaStatisticsFactoryItemIdInvalid { internal static string ServiceGachaStatisticsFactoryItemIdInvalid {
get { get {
@@ -628,7 +628,7 @@ namespace Snap.Hutao.Resource.Localization {
} }
/// <summary> /// <summary>
/// 查找类似 游戏文件操作失败: {0} 的本地化字符串。 /// 查找类似 游戏文件操作失败{0} 的本地化字符串。
/// </summary> /// </summary>
internal static string ServiceGameFileOperationExceptionMessage { internal static string ServiceGameFileOperationExceptionMessage {
get { get {
@@ -690,6 +690,15 @@ namespace Snap.Hutao.Resource.Localization {
} }
} }
/// <summary>
/// 查找类似 未开启长路径功能,无法设置注册表键值 的本地化字符串。
/// </summary>
internal static string ServiceGameRegisteryInteropLongPathsDisabled {
get {
return ResourceManager.GetString("ServiceGameRegisteryInteropLongPathsDisabled", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 找不到游戏配置文件 {0} 的本地化字符串。 /// 查找类似 找不到游戏配置文件 {0} 的本地化字符串。
/// </summary> /// </summary>
@@ -1537,7 +1546,7 @@ namespace Snap.Hutao.Resource.Localization {
} }
/// <summary> /// <summary>
/// 查找类似 清除计划任务失败 的本地化字符串。 /// 查找类似 清除计划任务失败,请使用管理员模式重试 的本地化字符串。
/// </summary> /// </summary>
internal static string ViewModelExperimentalDeleteTaskWarning { internal static string ViewModelExperimentalDeleteTaskWarning {
get { get {
@@ -1546,7 +1555,7 @@ namespace Snap.Hutao.Resource.Localization {
} }
/// <summary> /// <summary>
/// 查找类似 清除用户数据成功,请立即重启胡桃 的本地化字符串。 /// 查找类似 清除用户数据成功请立即重启胡桃 的本地化字符串。
/// </summary> /// </summary>
internal static string ViewModelExperimentalDeleteUserSuccess { internal static string ViewModelExperimentalDeleteUserSuccess {
get { get {
@@ -1680,6 +1689,15 @@ namespace Snap.Hutao.Resource.Localization {
} }
} }
/// <summary>
/// 查找类似 剪贴板中的文本格式不正确 的本地化字符串。
/// </summary>
internal static string ViewModelImportFromClipboardErrorTitle {
get {
return ResourceManager.GetString("ViewModelImportFromClipboardErrorTitle", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 数据格式不正确 的本地化字符串。 /// 查找类似 数据格式不正确 的本地化字符串。
/// </summary> /// </summary>
@@ -1689,6 +1707,15 @@ namespace Snap.Hutao.Resource.Localization {
} }
} }
/// <summary>
/// 查找类似 请先创建一个成就存档 的本地化字符串。
/// </summary>
internal static string ViewModelImportWarningMessage2 {
get {
return ResourceManager.GetString("ViewModelImportWarningMessage2", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 导入失败 的本地化字符串。 /// 查找类似 导入失败 的本地化字符串。
/// </summary> /// </summary>
@@ -2959,7 +2986,7 @@ namespace Snap.Hutao.Resource.Localization {
} }
/// <summary> /// <summary>
/// 查找类似 在游戏内切换账号,网络环境发生变化后需要重新手动检测 的本地化字符串。 /// 查找类似 在游戏内切换账号网络环境发生变化后需要重新手动检测 的本地化字符串。
/// </summary> /// </summary>
internal static string ViewPageLaunchGameSwitchAccountDescription { internal static string ViewPageLaunchGameSwitchAccountDescription {
get { get {

View File

@@ -154,7 +154,7 @@
<value>列表</value> <value>列表</value>
</data> </data>
<data name="CoreExceptionServiceUserdataCorruptedMessage" xml:space="preserve"> <data name="CoreExceptionServiceUserdataCorruptedMessage" xml:space="preserve">
<value>用户数据已损坏: {0}</value> <value>用户数据已损坏{0}</value>
</data> </data>
<data name="CoreIOPickerExtensionPickerExceptionInfoBarMessage" xml:space="preserve"> <data name="CoreIOPickerExtensionPickerExceptionInfoBarMessage" xml:space="preserve">
<value>请勿在管理员模式下使用此功能 {0}</value> <value>请勿在管理员模式下使用此功能 {0}</value>
@@ -268,7 +268,7 @@
<value>参量质变仪已准备完成</value> <value>参量质变仪已准备完成</value>
</data> </data>
<data name="ServiceGachaLogArchiveCollectionUserdataCorruptedMessage" xml:space="preserve"> <data name="ServiceGachaLogArchiveCollectionUserdataCorruptedMessage" xml:space="preserve">
<value>无法获取祈愿记录: {0}</value> <value>无法获取祈愿记录{0}</value>
</data> </data>
<data name="ServiceGachaLogEndIdUserdataCorruptedMessage" xml:space="preserve"> <data name="ServiceGachaLogEndIdUserdataCorruptedMessage" xml:space="preserve">
<value>无法获取祈愿记录 End Id</value> <value>无法获取祈愿记录 End Id</value>
@@ -298,7 +298,7 @@
<value>提供的 Url 无效</value> <value>提供的 Url 无效</value>
</data> </data>
<data name="ServiceGachaStatisticsFactoryItemIdInvalid" xml:space="preserve"> <data name="ServiceGachaStatisticsFactoryItemIdInvalid" xml:space="preserve">
<value>不支持的 Item Id: {0}</value> <value>不支持的 Item Id{0}</value>
</data> </data>
<data name="ServiceGameDetectGameAccountMultiMatched" xml:space="preserve"> <data name="ServiceGameDetectGameAccountMultiMatched" xml:space="preserve">
<value>存在多个匹配账号,请删除重复的账号</value> <value>存在多个匹配账号,请删除重复的账号</value>
@@ -307,7 +307,7 @@
<value>查询游戏资源信息</value> <value>查询游戏资源信息</value>
</data> </data>
<data name="ServiceGameFileOperationExceptionMessage" xml:space="preserve"> <data name="ServiceGameFileOperationExceptionMessage" xml:space="preserve">
<value>游戏文件操作失败: {0}</value> <value>游戏文件操作失败{0}</value>
</data> </data>
<data name="ServiceGameLocatorFileOpenPickerCommitText" xml:space="preserve"> <data name="ServiceGameLocatorFileOpenPickerCommitText" xml:space="preserve">
<value>选择游戏本体</value> <value>选择游戏本体</value>
@@ -327,6 +327,9 @@
<data name="ServiceGamePathLocateFailed" xml:space="preserve"> <data name="ServiceGamePathLocateFailed" xml:space="preserve">
<value>无法找到游戏路径,请前往设置修改</value> <value>无法找到游戏路径,请前往设置修改</value>
</data> </data>
<data name="ServiceGameRegisteryInteropLongPathsDisabled" xml:space="preserve">
<value>未开启长路径功能,无法设置注册表键值</value>
</data>
<data name="ServiceGameSetMultiChannelConfigFileNotFound" xml:space="preserve"> <data name="ServiceGameSetMultiChannelConfigFileNotFound" xml:space="preserve">
<value>找不到游戏配置文件 {0}</value> <value>找不到游戏配置文件 {0}</value>
</data> </data>
@@ -610,10 +613,10 @@
<value>清除计划任务成功</value> <value>清除计划任务成功</value>
</data> </data>
<data name="ViewModelExperimentalDeleteTaskWarning" xml:space="preserve"> <data name="ViewModelExperimentalDeleteTaskWarning" xml:space="preserve">
<value>清除计划任务失败</value> <value>清除计划任务失败,请使用管理员模式重试</value>
</data> </data>
<data name="ViewModelExperimentalDeleteUserSuccess" xml:space="preserve"> <data name="ViewModelExperimentalDeleteUserSuccess" xml:space="preserve">
<value>清除用户数据成功,请立即重启胡桃</value> <value>清除用户数据成功请立即重启胡桃</value>
</data> </data>
<data name="ViewModelExportSuccessMessage" xml:space="preserve"> <data name="ViewModelExportSuccessMessage" xml:space="preserve">
<value>成功保存到指定位置</value> <value>成功保存到指定位置</value>
@@ -657,9 +660,15 @@
<data name="ViewModelGachaLogRemoveArchiveTitle" xml:space="preserve"> <data name="ViewModelGachaLogRemoveArchiveTitle" xml:space="preserve">
<value>确定要删除存档 {0} 吗?</value> <value>确定要删除存档 {0} 吗?</value>
</data> </data>
<data name="ViewModelImportFromClipboardErrorTitle" xml:space="preserve">
<value>剪贴板中的文本格式不正确</value>
</data>
<data name="ViewModelImportWarningMessage" xml:space="preserve"> <data name="ViewModelImportWarningMessage" xml:space="preserve">
<value>数据格式不正确</value> <value>数据格式不正确</value>
</data> </data>
<data name="ViewModelImportWarningMessage2" xml:space="preserve">
<value>请先创建一个成就存档</value>
</data>
<data name="ViewModelImportWarningTitle" xml:space="preserve"> <data name="ViewModelImportWarningTitle" xml:space="preserve">
<value>导入失败</value> <value>导入失败</value>
</data> </data>
@@ -1084,7 +1093,7 @@
<value>绑定当前用户的角色</value> <value>绑定当前用户的角色</value>
</data> </data>
<data name="ViewPageLaunchGameSwitchAccountDescription" xml:space="preserve"> <data name="ViewPageLaunchGameSwitchAccountDescription" xml:space="preserve">
<value>在游戏内切换账号,网络环境发生变化后需要重新手动检测</value> <value>在游戏内切换账号网络环境发生变化后需要重新手动检测</value>
</data> </data>
<data name="ViewPageLaunchGameSwitchAccountDetectAction" xml:space="preserve"> <data name="ViewPageLaunchGameSwitchAccountDetectAction" xml:space="preserve">
<value>检测</value> <value>检测</value>

View File

@@ -113,7 +113,7 @@ internal class CultivationService : ICultivationService
} }
/// <inheritdoc/> /// <inheritdoc/>
public List<BindingInventoryItem> GetInventoryItems(CultivateProject cultivateProject, List<Model.Metadata.Material> metadata) public List<BindingInventoryItem> GetInventoryItems(CultivateProject cultivateProject, List<Model.Metadata.Material> metadata, ICommand saveCommand)
{ {
Guid projectId = cultivateProject.InnerId; Guid projectId = cultivateProject.InnerId;
using (IServiceScope scope = scopeFactory.CreateScope()) using (IServiceScope scope = scopeFactory.CreateScope())
@@ -127,7 +127,7 @@ internal class CultivationService : ICultivationService
foreach (Model.Metadata.Material meta in metadata.Where(m => m.IsInventoryItem()).OrderBy(m => m.Id)) foreach (Model.Metadata.Material meta in metadata.Where(m => m.IsInventoryItem()).OrderBy(m => m.Id))
{ {
InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.Create(projectId, meta.Id); InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.Create(projectId, meta.Id);
results.Add(new(meta, entity)); results.Add(new(meta, entity, saveCommand));
} }
return results; return results;
@@ -230,7 +230,6 @@ internal class CultivationService : ICultivationService
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
await ThreadHelper.SwitchToMainThreadAsync(); await ThreadHelper.SwitchToMainThreadAsync();
return resultItems.OrderByDescending(i => i.Count).ToObservableCollection(); return resultItems.OrderByDescending(i => i.Count).ToObservableCollection();
} }
} }

View File

@@ -32,8 +32,9 @@ internal interface ICultivationService
/// </summary> /// </summary>
/// <param name="cultivateProject">养成计划</param> /// <param name="cultivateProject">养成计划</param>
/// <param name="metadata">元数据</param> /// <param name="metadata">元数据</param>
/// <param name="saveCommand">保存命令</param>
/// <returns>物品列表</returns> /// <returns>物品列表</returns>
List<Model.Binding.Inventory.InventoryItem> GetInventoryItems(CultivateProject cultivateProject, List<Material> metadata); List<Model.Binding.Inventory.InventoryItem> GetInventoryItems(CultivateProject cultivateProject, List<Material> metadata, ICommand saveCommand);
/// <summary> /// <summary>
/// 获取用于绑定的项目集合 /// 获取用于绑定的项目集合

View File

@@ -2,8 +2,10 @@
// Licensed under the MIT license. // Licensed under the MIT license.
using Microsoft.Win32; using Microsoft.Win32;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Text; using System.Text;
namespace Snap.Hutao.Service.Game; namespace Snap.Hutao.Service.Game;
@@ -34,14 +36,25 @@ internal static class RegistryInterop
Set-ItemProperty -Path '{path}' -Name '{SdkKey}' -Value $value -Force; Set-ItemProperty -Path '{path}' -Name '{SdkKey}' -Value $value -Force;
"""; """;
string psExecutablePath = @"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe";
ProcessStartInfo startInfo = new() ProcessStartInfo startInfo = new()
{ {
Arguments = command, Arguments = command,
WorkingDirectory = Path.GetDirectoryName(psExecutablePath),
CreateNoWindow = true, CreateNoWindow = true,
FileName = "PowerShell", FileName = psExecutablePath,
}; };
try
{
Process.Start(startInfo)?.WaitForExit(); Process.Start(startInfo)?.WaitForExit();
}
catch (Win32Exception ex)
{
ThrowHelper.RuntimeEnvironment(SH.ServiceGameRegisteryInteropLongPathsDisabled, ex);
}
if (Get() == account.MihoyoSDK) if (Get() == account.MihoyoSDK)
{ {
return true; return true;

View File

@@ -34,7 +34,7 @@
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
<ApplicationIcon>Assets\Logo.ico</ApplicationIcon> <ApplicationIcon>Assets\Logo.ico</ApplicationIcon>
<GenerateTemporaryStoreCertificate>True</GenerateTemporaryStoreCertificate> <GenerateTemporaryStoreCertificate>True</GenerateTemporaryStoreCertificate>
<AppxBundlePlatforms>x86|x64</AppxBundlePlatforms> <AppxBundlePlatforms>x64</AppxBundlePlatforms>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -2,7 +2,7 @@
// Licensed under the MIT license. // Licensed under the MIT license.
using Snap.Hutao.Control; using Snap.Hutao.Control;
using Snap.Hutao.ViewModel; using Snap.Hutao.ViewModel.Achievement;
namespace Snap.Hutao.View.Page; namespace Snap.Hutao.View.Page;

View File

@@ -0,0 +1,64 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.UI;
using System.Runtime.InteropServices;
using BindingAchievement = Snap.Hutao.Model.Binding.Achievement.Achievement;
using BindingAchievementGoal = Snap.Hutao.Model.Binding.Achievement.AchievementGoal;
namespace Snap.Hutao.ViewModel.Achievement;
/// <summary>
/// 成就完成进度更新器
/// </summary>
internal class AchievementFinishPercentUpdater
{
private readonly AchievementViewModel viewModel;
/// <summary>
/// 构造一个新的成就进度更新器
/// </summary>
/// <param name="viewModel">视图模型</param>
public AchievementFinishPercentUpdater(AchievementViewModel viewModel)
{
this.viewModel = viewModel;
}
/// <summary>
/// 更新完成进度
/// </summary>
public void Update()
{
int finished = 0;
int count = 0;
if (viewModel.Achievements is AdvancedCollectionView achievements)
{
if (viewModel.AchievementGoals is List<BindingAchievementGoal> achievementGoals)
{
Dictionary<int, AchievementGoalStatistics> counter = achievementGoals.ToDictionary(x => x.Id, x => new AchievementGoalStatistics(x));
foreach (BindingAchievement achievement in achievements.SourceCollection.Cast<BindingAchievement>())
{
// We want to make the state update as fast as possible,
// so we use CollectionsMarshal here to get the ref.
ref AchievementGoalStatistics stat = ref CollectionsMarshal.GetValueRefOrNullRef(counter, achievement.Inner.Goal);
stat.TotalCount += 1;
count += 1;
if (achievement.IsChecked)
{
stat.Finished += 1;
finished += 1;
}
}
foreach (AchievementGoalStatistics statistics in counter.Values)
{
statistics.AchievementGoal.UpdateFinishPercent(statistics.Finished, statistics.TotalCount);
}
viewModel.FinishDescription = $"{finished}/{count} - {(double)finished / count:P2}";
}
}
}
}

View File

@@ -0,0 +1,36 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using BindingAchievementGoal = Snap.Hutao.Model.Binding.Achievement.AchievementGoal;
namespace Snap.Hutao.ViewModel.Achievement;
/// <summary>
/// 成就分类统计
/// </summary>
internal struct AchievementGoalStatistics
{
/// <summary>
/// 成就分类
/// </summary>
public readonly BindingAchievementGoal AchievementGoal;
/// <summary>
/// 完成数
/// </summary>
public int Finished;
/// <summary>
/// 总数
/// </summary>
public int TotalCount;
/// <summary>
/// 构造一个新的成就分类统计
/// </summary>
/// <param name="goal">分类</param>
public AchievementGoalStatistics(BindingAchievementGoal goal)
{
AchievementGoal = goal;
}
}

View File

@@ -0,0 +1,147 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control.Extension;
using Snap.Hutao.Core.IO;
using Snap.Hutao.Core.IO.DataTransfer;
using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Model.InterChange.Achievement;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.Achievement;
using Snap.Hutao.View.Dialog;
using Windows.Storage.Pickers;
using EntityAchievementArchive = Snap.Hutao.Model.Entity.AchievementArchive;
namespace Snap.Hutao.ViewModel.Achievement;
/// <summary>
/// 成就导入器
/// </summary>
internal class AchievementImporter
{
private readonly IServiceProvider serviceProvider;
private readonly IAchievementService achievementService;
private readonly IInfoBarService infoBarService;
private readonly JsonSerializerOptions options;
/// <summary>
/// 构造一个新的成就导入器
/// </summary>
/// <param name="serviceProvider">服务提供器</param>
public AchievementImporter(IServiceProvider serviceProvider)
{
achievementService = serviceProvider.GetRequiredService<IAchievementService>();
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
options = serviceProvider.GetRequiredService<JsonSerializerOptions>();
this.serviceProvider = serviceProvider;
}
/// <summary>
/// 从剪贴板导入
/// </summary>
/// <returns>是否导入成功</returns>
public async Task<bool> FromClipboardAsync()
{
if (achievementService.CurrentArchive != null)
{
if (await GetUIAFFromClipboardAsync().ConfigureAwait(false) is UIAF uiaf)
{
return await ImportAsync(achievementService.CurrentArchive!, uiaf).ConfigureAwait(false);
}
else
{
infoBarService.Warning(SH.ViewModelImportWarningTitle, SH.ViewModelImportWarningMessage);
}
}
else
{
infoBarService.Warning(SH.ViewModelImportWarningTitle, SH.ViewModelImportWarningMessage2);
}
return false;
}
/// <summary>
/// 从文件导入
/// </summary>
/// <returns>是否导入成功</returns>
public async Task<bool> FromFileAsync()
{
if (achievementService.CurrentArchive != null)
{
(bool isPickerOk, FilePath file) = await serviceProvider
.GetRequiredService<IPickerFactory>()
.GetFileOpenPicker(PickerLocationId.Desktop, SH.FilePickerImportCommit, ".json")
.TryPickSingleFileAsync()
.ConfigureAwait(false);
if (isPickerOk)
{
(bool isOk, UIAF? uiaf) = await file.DeserializeFromJsonAsync<UIAF>(options).ConfigureAwait(false);
if (isOk)
{
return await ImportAsync(achievementService.CurrentArchive, uiaf!).ConfigureAwait(false);
}
else
{
infoBarService.Warning(SH.ViewModelImportWarningTitle, SH.ViewModelImportWarningMessage);
}
}
}
else
{
infoBarService.Warning(SH.ViewModelImportWarningTitle, SH.ViewModelImportWarningMessage2);
}
return false;
}
private async Task<UIAF?> GetUIAFFromClipboardAsync()
{
try
{
return await Clipboard.DeserializeTextAsync<UIAF>(options).ConfigureAwait(false);
}
catch (Exception ex)
{
infoBarService?.Error(ex, SH.ViewModelImportFromClipboardErrorTitle);
return null;
}
}
private async Task<bool> ImportAsync(EntityAchievementArchive archive, UIAF uiaf)
{
if (uiaf.IsCurrentVersionSupported())
{
// ContentDialog must be created by main thread.
await ThreadHelper.SwitchToMainThreadAsync();
(bool isOk, ImportStrategy strategy) = await new AchievementImportDialog(uiaf).GetImportStrategyAsync().ConfigureAwait(true);
if (isOk)
{
ImportResult result;
ContentDialog dialog = await serviceProvider.GetRequiredService<IContentDialogFactory>()
.CreateForIndeterminateProgressAsync(SH.ViewModelAchievementImportProgress)
.ConfigureAwait(false);
await using (await dialog.BlockAsync().ConfigureAwait(false))
{
result = await achievementService.ImportFromUIAFAsync(archive, uiaf.List, strategy).ConfigureAwait(false);
}
infoBarService.Success(result.ToString());
return true;
}
}
else
{
infoBarService.Warning(SH.ViewModelImportWarningTitle, SH.ViewModelAchievementImportWarningMessage);
}
return false;
}
}

View File

@@ -19,7 +19,6 @@ using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Service.Navigation; using Snap.Hutao.Service.Navigation;
using Snap.Hutao.View.Dialog; using Snap.Hutao.View.Dialog;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using Windows.Storage.Pickers; using Windows.Storage.Pickers;
using BindingAchievement = Snap.Hutao.Model.Binding.Achievement.Achievement; using BindingAchievement = Snap.Hutao.Model.Binding.Achievement.Achievement;
using BindingAchievementGoal = Snap.Hutao.Model.Binding.Achievement.AchievementGoal; using BindingAchievementGoal = Snap.Hutao.Model.Binding.Achievement.AchievementGoal;
@@ -27,7 +26,7 @@ using EntityAchievementArchive = Snap.Hutao.Model.Entity.AchievementArchive;
using MetadataAchievement = Snap.Hutao.Model.Metadata.Achievement.Achievement; using MetadataAchievement = Snap.Hutao.Model.Metadata.Achievement.Achievement;
using MetadataAchievementGoal = Snap.Hutao.Model.Metadata.Achievement.AchievementGoal; using MetadataAchievementGoal = Snap.Hutao.Model.Metadata.Achievement.AchievementGoal;
namespace Snap.Hutao.ViewModel; namespace Snap.Hutao.ViewModel.Achievement;
/// <summary> /// <summary>
/// 成就视图模型 /// 成就视图模型
@@ -45,6 +44,9 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
private readonly IContentDialogFactory contentDialogFactory; private readonly IContentDialogFactory contentDialogFactory;
private readonly JsonSerializerOptions options; private readonly JsonSerializerOptions options;
private readonly AchievementFinishPercentUpdater achievementFinishPercentUpdater;
private readonly AchievementImporter achievementImporter;
private readonly TaskCompletionSource<bool> openUICompletionSource = new(); private readonly TaskCompletionSource<bool> openUICompletionSource = new();
private AdvancedCollectionView? achievements; private AdvancedCollectionView? achievements;
@@ -59,6 +61,7 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
/// <summary> /// <summary>
/// 构造一个新的成就视图模型 /// 构造一个新的成就视图模型
/// </summary> /// </summary>
/// <param name="serviceProvider">服务提供器</param>
/// <param name="metadataService">元数据服务</param> /// <param name="metadataService">元数据服务</param>
/// <param name="achievementService">成就服务</param> /// <param name="achievementService">成就服务</param>
/// <param name="infoBarService">信息条服务</param> /// <param name="infoBarService">信息条服务</param>
@@ -66,6 +69,7 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
/// <param name="contentDialogFactory">内容对话框工厂</param> /// <param name="contentDialogFactory">内容对话框工厂</param>
/// <param name="messenger">消息器</param> /// <param name="messenger">消息器</param>
public AchievementViewModel( public AchievementViewModel(
IServiceProvider serviceProvider,
IMetadataService metadataService, IMetadataService metadataService,
IAchievementService achievementService, IAchievementService achievementService,
IInfoBarService infoBarService, IInfoBarService infoBarService,
@@ -79,13 +83,16 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
this.contentDialogFactory = contentDialogFactory; this.contentDialogFactory = contentDialogFactory;
this.options = options; this.options = options;
achievementFinishPercentUpdater = new(this);
achievementImporter = new(serviceProvider);
OpenUICommand = new AsyncRelayCommand(OpenUIAsync); OpenUICommand = new AsyncRelayCommand(OpenUIAsync);
ImportUIAFFromClipboardCommand = new AsyncRelayCommand(ImportUIAFFromClipboardAsync); ImportUIAFFromClipboardCommand = new AsyncRelayCommand(ImportUIAFFromClipboardAsync);
ImportUIAFFromFileCommand = new AsyncRelayCommand(ImportUIAFFromFileAsync); ImportUIAFFromFileCommand = new AsyncRelayCommand(ImportUIAFFromFileAsync);
ExportAsUIAFToFileCommand = new AsyncRelayCommand(ExportAsUIAFToFileAsync); ExportAsUIAFToFileCommand = new AsyncRelayCommand(ExportAsUIAFToFileAsync);
AddArchiveCommand = new AsyncRelayCommand(AddArchiveAsync); AddArchiveCommand = new AsyncRelayCommand(AddArchiveAsync);
RemoveArchiveCommand = new AsyncRelayCommand(RemoveArchiveAsync); RemoveArchiveCommand = new AsyncRelayCommand(RemoveArchiveAsync);
SearchAchievementCommand = new RelayCommand<string>(SearchAchievement); SearchAchievementCommand = new RelayCommand<string>(UpdateAchievementsFilterBySerach);
SortIncompletedSwitchCommand = new RelayCommand(UpdateAchievementsSort); SortIncompletedSwitchCommand = new RelayCommand(UpdateAchievementsSort);
SaveAchievementCommand = new RelayCommand<BindingAchievement>(SaveAchievement); SaveAchievementCommand = new RelayCommand<BindingAchievement>(SaveAchievement);
} }
@@ -146,7 +153,7 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
{ {
SetProperty(ref selectedAchievementGoal, value); SetProperty(ref selectedAchievementGoal, value);
SearchText = string.Empty; SearchText = string.Empty;
UpdateAchievementsFilter(value); UpdateAchievementsFilterByGoal(value);
} }
} }
@@ -277,7 +284,6 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
openUICompletionSource.TrySetResult(metaInitialized); openUICompletionSource.TrySetResult(metaInitialized);
} }
#region
private async Task AddArchiveAsync() private async Task AddArchiveAsync()
{ {
if (Archives != null) if (Archives != null)
@@ -324,10 +330,8 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
{ {
try try
{ {
ThrowIfViewDisposed(); using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
using (await DisposeLock.EnterAsync(CancellationToken).ConfigureAwait(false))
{ {
ThrowIfViewDisposed();
await achievementService.RemoveArchiveAsync(SelectedArchive).ConfigureAwait(false); await achievementService.RemoveArchiveAsync(SelectedArchive).ConfigureAwait(false);
} }
@@ -341,16 +345,11 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
} }
} }
} }
#endregion
#region
private async Task ExportAsUIAFToFileAsync() private async Task ExportAsUIAFToFileAsync()
{ {
if (SelectedArchive == null || Achievements == null) if (SelectedArchive != null && Achievements != null)
{ {
return;
}
FileSavePicker picker = Ioc.Default.GetRequiredService<IPickerFactory>().GetFileSavePicker(); FileSavePicker picker = Ioc.Default.GetRequiredService<IPickerFactory>().GetFileSavePicker();
picker.FileTypeChoices.Add(SH.ViewModelAchievementExportFileType, ".json".Enumerate().ToList()); picker.FileTypeChoices.Add(SH.ViewModelAchievementExportFileType, ".json".Enumerate().ToList());
picker.SuggestedStartLocation = PickerLocationId.Desktop; picker.SuggestedStartLocation = PickerLocationId.Desktop;
@@ -373,102 +372,88 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
} }
} }
} }
}
private async Task ImportUIAFFromClipboardAsync() private async Task ImportUIAFFromClipboardAsync()
{ {
if (achievementService.CurrentArchive == null) if (await achievementImporter.FromClipboardAsync().ConfigureAwait(false))
{ {
// Basically can't happen now await UpdateAchievementsAsync(achievementService.CurrentArchive!).ConfigureAwait(false);
// infoBarService.Information("必须选择一个存档才能导入成就");
return;
}
if (await GetUIAFFromClipboardAsync().ConfigureAwait(false) is UIAF uiaf)
{
await TryImportUIAFInternalAsync(achievementService.CurrentArchive!, uiaf).ConfigureAwait(false);
}
else
{
infoBarService.Warning(SH.ViewModelImportWarningTitle, SH.ViewModelImportWarningMessage);
} }
} }
private async Task ImportUIAFFromFileAsync() private async Task ImportUIAFFromFileAsync()
{ {
if (achievementService.CurrentArchive == null) if (await achievementImporter.FromFileAsync().ConfigureAwait(false))
{ {
// Basically can't happen now await UpdateAchievementsAsync(achievementService.CurrentArchive!).ConfigureAwait(false);
// infoBarService.Information("必须选择一个存档才能导入成就");
return;
}
FileOpenPicker picker = Ioc.Default.GetRequiredService<IPickerFactory>()
.GetFileOpenPicker(PickerLocationId.Desktop, SH.FilePickerImportCommit, ".json");
(bool isPickerOk, FilePath file) = await picker.TryPickSingleFileAsync().ConfigureAwait(false);
if (isPickerOk)
{
(bool isOk, UIAF? uiaf) = await file.DeserializeFromJsonAsync<UIAF>(options).ConfigureAwait(false);
if (isOk)
{
await TryImportUIAFInternalAsync(achievementService.CurrentArchive, uiaf!).ConfigureAwait(false);
}
else
{
infoBarService.Warning(SH.ViewModelImportWarningTitle, SH.ViewModelImportWarningMessage);
}
} }
} }
private async Task<UIAF?> GetUIAFFromClipboardAsync() private async Task UpdateAchievementsAsync(EntityAchievementArchive archive)
{
List<MetadataAchievement> rawAchievements = await metadataService.GetAchievementsAsync(CancellationToken).ConfigureAwait(false);
if (TryGetAchievements(archive, rawAchievements, out List<BindingAchievement>? combined))
{
// Assemble achievements on the UI thread.
await ThreadHelper.SwitchToMainThreadAsync();
Achievements = new(combined, true);
UpdateAchievementsFinishPercent();
UpdateAchievementsFilterByGoal(SelectedAchievementGoal);
UpdateAchievementsSort();
}
}
private bool TryGetAchievements(EntityAchievementArchive archive, List<MetadataAchievement> achievements, out List<BindingAchievement>? combined)
{ {
try try
{ {
return await Clipboard.DeserializeTextAsync<UIAF>(options).ConfigureAwait(false); combined = achievementService.GetAchievements(archive, achievements);
}
catch (Exception ex)
{
infoBarService?.Error(ex);
return null;
}
}
private async Task<bool> TryImportUIAFInternalAsync(EntityAchievementArchive archive, UIAF uiaf)
{
if (uiaf.IsCurrentVersionSupported())
{
// ContentDialog must be created by main thread.
await ThreadHelper.SwitchToMainThreadAsync();
(bool isOk, ImportStrategy strategy) = await new AchievementImportDialog(uiaf).GetImportStrategyAsync().ConfigureAwait(true);
if (isOk)
{
ImportResult result;
ContentDialog dialog = await contentDialogFactory
.CreateForIndeterminateProgressAsync(SH.ViewModelAchievementImportProgress)
.ConfigureAwait(false);
await using (await dialog.BlockAsync().ConfigureAwait(false))
{
result = await achievementService.ImportFromUIAFAsync(archive, uiaf.List, strategy).ConfigureAwait(false);
}
infoBarService.Success(result.ToString());
await UpdateAchievementsAsync(archive).ConfigureAwait(false);
return true; return true;
} }
catch (Core.ExceptionService.UserdataCorruptedException ex)
{
combined = default;
infoBarService.Error(ex);
return false;
}
}
private void UpdateAchievementsSort()
{
if (Achievements != null)
{
if (IsIncompletedItemsFirst)
{
Achievements.SortDescriptions.Add(IncompletedItemsFirstSortDescription);
Achievements.SortDescriptions.Add(CompletionTimeSortDescription);
} }
else else
{ {
infoBarService.Warning(SH.ViewModelImportWarningTitle, SH.ViewModelAchievementImportWarningMessage); Achievements.SortDescriptions.Clear();
}
}
} }
return false; private void UpdateAchievementsFilterByGoal(BindingAchievementGoal? goal)
{
if (Achievements != null)
{
if (goal == null)
{
Achievements.Filter = null;
}
else
{
int goalId = goal.Id;
Achievements.Filter = (object o) => o is BindingAchievement achi && achi.Inner.Goal == goalId;
}
}
} }
#endregion
private void SearchAchievement(string? search) private void UpdateAchievementsFilterBySerach(string? search)
{ {
if (Achievements != null) if (Achievements != null)
{ {
@@ -492,81 +477,10 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
} }
} }
private async Task UpdateAchievementsAsync(EntityAchievementArchive archive) // 仅 读取成就列表 与 保存成就状态 时需要刷新成就进度
{
List<MetadataAchievement> rawAchievements = await metadataService.GetAchievementsAsync(CancellationToken).ConfigureAwait(false);
List<BindingAchievement> combined;
try
{
combined = achievementService.GetAchievements(archive, rawAchievements);
}
catch (Core.ExceptionService.UserdataCorruptedException ex)
{
infoBarService.Error(ex);
return;
}
// Assemble achievements on the UI thread.
await ThreadHelper.SwitchToMainThreadAsync();
Achievements = new(combined, true);
UpdateAchievementsFinishPercent();
UpdateAchievementsFilter(SelectedAchievementGoal);
UpdateAchievementsSort();
}
private void UpdateAchievementsSort()
{
if (Achievements != null)
{
if (IsIncompletedItemsFirst)
{
Achievements.SortDescriptions.Add(IncompletedItemsFirstSortDescription);
Achievements.SortDescriptions.Add(CompletionTimeSortDescription);
}
else
{
Achievements.SortDescriptions.Clear();
}
}
}
private void UpdateAchievementsFilter(BindingAchievementGoal? goal)
{
if (Achievements != null)
{
Achievements.Filter = goal != null
? ((object o) => o is BindingAchievement achi && achi.Inner.Goal == goal.Id)
: null;
}
}
private void UpdateAchievementsFinishPercent() private void UpdateAchievementsFinishPercent()
{ {
int finished = 0; achievementFinishPercentUpdater.Update();
int count = 0;
if (Achievements != null && AchievementGoals != null)
{
Dictionary<int, GoalStatistics> counter = AchievementGoals.ToDictionary(x => x.Id, x => new GoalStatistics(x));
foreach (BindingAchievement achievement in Achievements.SourceCollection.Cast<BindingAchievement>())
{
ref GoalStatistics stat = ref CollectionsMarshal.GetValueRefOrNullRef(counter, achievement.Inner.Goal);
stat.Count += 1;
count += 1;
if (achievement.IsChecked)
{
stat.Finished += 1;
finished += 1;
}
}
foreach (GoalStatistics statistics in counter.Values)
{
statistics.AchievementGoal.UpdateFinishPercent(statistics.Finished, statistics.Count);
}
FinishDescription = $"{finished}/{count} - {(double)finished / count:P2}";
}
} }
private void SaveAchievement(BindingAchievement? achievement) private void SaveAchievement(BindingAchievement? achievement)
@@ -577,16 +491,4 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
UpdateAchievementsFinishPercent(); UpdateAchievementsFinishPercent();
} }
} }
private struct GoalStatistics
{
public readonly BindingAchievementGoal AchievementGoal;
public int Finished;
public int Count;
public GoalStatistics(BindingAchievementGoal goal)
{
AchievementGoal = goal;
}
}
} }

View File

@@ -233,7 +233,7 @@ internal class AvatarPropertyViewModel : Abstraction.ViewModel
.SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items) .SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items)
.ConfigureAwait(false); .ConfigureAwait(false);
// take a short path if avatar is not saved. // take a hot path if avatar is not saved.
bool avatarAndWeaponSaved = avatarSaved && await cultivationService bool avatarAndWeaponSaved = avatarSaved && await cultivationService
.SaveConsumptionAsync(CultivateType.Weapon, avatar.Weapon.Id, consumption.WeaponConsume.EmptyIfNull()) .SaveConsumptionAsync(CultivateType.Weapon, avatar.Weapon.Id, consumption.WeaponConsume.EmptyIfNull())
.ConfigureAwait(false); .ConfigureAwait(false);
@@ -258,11 +258,8 @@ internal class AvatarPropertyViewModel : Abstraction.ViewModel
private async Task ExportAsImageAsync(UIElement? element) private async Task ExportAsImageAsync(UIElement? element)
{ {
if (element == null) if (element != null)
{ {
return;
}
RenderTargetBitmap bitmap = new(); RenderTargetBitmap bitmap = new();
await bitmap.RenderAsync(element); await bitmap.RenderAsync(element);
@@ -273,7 +270,6 @@ internal class AvatarPropertyViewModel : Abstraction.ViewModel
Color tintColor = (Color)Ioc.Default.GetRequiredService<App>().Resources["CompatBackgroundColor"]; Color tintColor = (Color)Ioc.Default.GetRequiredService<App>().Resources["CompatBackgroundColor"];
Bgra8 tint = Bgra8.FromColor(tintColor); Bgra8 tint = Bgra8.FromColor(tintColor);
softwareBitmap.NormalBlend(tint); softwareBitmap.NormalBlend(tint);
using (InMemoryRandomAccessStream memory = new()) using (InMemoryRandomAccessStream memory = new())
{ {
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, memory); BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, memory);
@@ -302,3 +298,4 @@ internal class AvatarPropertyViewModel : Abstraction.ViewModel
} }
} }
} }
}

View File

@@ -198,7 +198,7 @@ internal class CultivationViewModel : Abstraction.ViewModel
await ThreadHelper.SwitchToMainThreadAsync(); await ThreadHelper.SwitchToMainThreadAsync();
CultivateEntries = entries; CultivateEntries = entries;
InventoryItems = cultivationService.GetInventoryItems(project, materials); InventoryItems = cultivationService.GetInventoryItems(project, materials, SaveInventoryItemCommand);
await UpdateStatisticsItemsAsync().ConfigureAwait(false); await UpdateStatisticsItemsAsync().ConfigureAwait(false);
} }

View File

@@ -17,8 +17,6 @@ using Snap.Hutao.Service.Game;
using Snap.Hutao.Service.Game.Locator; using Snap.Hutao.Service.Game.Locator;
using Snap.Hutao.View.Dialog; using Snap.Hutao.View.Dialog;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using Windows.Storage;
using Windows.Storage.Pickers; using Windows.Storage.Pickers;
namespace Snap.Hutao.ViewModel; namespace Snap.Hutao.ViewModel;