mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
fix translation
This commit is contained in:
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -14,7 +14,7 @@ body:
|
|||||||
id: back
|
id: back
|
||||||
attributes:
|
attributes:
|
||||||
label: 背景与动机
|
label: 背景与动机
|
||||||
description: 添加此功能的理由
|
description: 添加此功能的理由,如果你想要实现多个功能,请分别发起多个单独的 Issue
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -40,7 +40,5 @@ internal class InvokeCommandOnLoadedBehavior : BehaviorBase<UIElement>
|
|||||||
{
|
{
|
||||||
Command?.Execute(CommandParameter);
|
Command?.Execute(CommandParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnAssociatedObjectLoaded();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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>()
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
/// 获取用于绑定的项目集合
|
/// 获取用于绑定的项目集合
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user