From 49c75dde2a0354e0739476f9e05d3d6e212325c9 Mon Sep 17 00:00:00 2001 From: LinkChou Date: Wed, 5 Jun 2024 18:43:45 +0800 Subject: [PATCH 01/61] Chinese text improve --- .../Snap.Hutao/Resource/Localization/SH.resx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index 049ff357..b29b877a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -552,7 +552,7 @@ 精炼 {0} 阶 - 必须登录 米游社/HoYoLAB 并选择一个用户与角色 + 必须登录 米游社 / HoYoLAB 并选择一个用户与角色 删除了 Uid:{0} 的 {1} 条祈愿记录 @@ -1545,10 +1545,10 @@ 养成计划添加失败 - 操作完成:添加/更新:{0} 个,跳过 {1} 个 + 操作完成:添加 / 更新:{0} 个,跳过 {1} 个 - 操作未全部完成:添加/更新:{0} 个,跳过 {1} 个 + 操作未全部完成:添加 / 更新:{0} 个,跳过 {1} 个 已成功添加至当前养成计划 @@ -2067,7 +2067,7 @@ 前往爱发电购买相关服务 - 购买/续费云服务 + 购买 / 续费云服务 删除此 Uid 的云端存档 @@ -2406,7 +2406,7 @@ 重命名 - 切换游戏服务器(国服/渠道服/国际服) + 切换游戏服务器(国服 / 渠道服 / 国际服) 服务器 @@ -2415,7 +2415,7 @@ 版本更新前需要提前转换至与启动器匹配的服务器 - 请在游戏内关闭垂直同步选项,需要高性能的显卡以支持更高的帧率 + 请在游戏内关闭「垂直同步」选项,需要高性能的显卡以支持更高的帧率 解锁帧率限制 @@ -2511,7 +2511,7 @@ 除非开发人员明确要求你这么做,否则不应尝试执行下方的操作! - 用户数据/元数据 在此处存放 + 用户数据 / 元数据 在此处存放 数据 文件夹 @@ -2646,7 +2646,7 @@ 您可以无限制的使用任何测试功能 - 胡桃开发/运维 + 胡桃开发 / 运维 我们有时会向某些用户赠送胡桃云兑换码 @@ -2751,7 +2751,7 @@ 贡献翻译 - 在祈愿记录页面角色与武器页签显示未抽取到的祈愿物品 + 在祈愿记录页面「角色与武器」页签显示未抽取到的祈愿物品 未抽取到的祈愿物品 From 00ad0ef3468ca1a91a43c16dea8593eb55768320 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Thu, 6 Jun 2024 01:39:10 +0800 Subject: [PATCH 02/61] correct format --- src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index b29b877a..88cae015 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -2751,7 +2751,7 @@ 贡献翻译 - 在祈愿记录页面「角色与武器」页签显示未抽取到的祈愿物品 + 在「祈愿记录-角色 / 武器」页签显示未抽取到的祈愿物品 未抽取到的祈愿物品 @@ -3266,4 +3266,4 @@ 显示器编号 - \ No newline at end of file + From 6e149a5be3516aad8cfdb27f97388269a1538eca Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Thu, 6 Jun 2024 01:44:32 +0800 Subject: [PATCH 03/61] Update SH.resx --- src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index 88cae015..c787cbf3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -2751,7 +2751,7 @@ 贡献翻译 - 在「祈愿记录-角色 / 武器」页签显示未抽取到的祈愿物品 + 在「祈愿记录-角色」「祈愿记录-武器」页签显示未抽取到的祈愿物品 未抽取到的祈愿物品 From 374c4d796d0cf75835a7e1e53293a5fed3d60067 Mon Sep 17 00:00:00 2001 From: LinkChou Date: Thu, 6 Jun 2024 07:25:11 +0800 Subject: [PATCH 04/61] reformat --- src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index c787cbf3..fb73d601 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -2751,7 +2751,7 @@ 贡献翻译 - 在「祈愿记录-角色」「祈愿记录-武器」页签显示未抽取到的祈愿物品 + 在「祈愿记录-角色」与「祈愿记录-武器」中显示未抽取到的祈愿物品 未抽取到的祈愿物品 @@ -3266,4 +3266,4 @@ 显示器编号 - + \ No newline at end of file From 82e6b62231057e69ff9d2398b21602ff6f024800 Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Thu, 6 Jun 2024 09:29:50 +0800 Subject: [PATCH 05/61] correctly free library --- .../Game/Unlocker/RequiredLocalModule.cs | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/RequiredLocalModule.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/RequiredLocalModule.cs index 4b16193a..299c0b45 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/RequiredLocalModule.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/RequiredLocalModule.cs @@ -15,30 +15,35 @@ internal readonly struct RequiredLocalModule : IDisposable public readonly Module UnityPlayer; public readonly Module UserAssembly; + private readonly HMODULE hModuleUnityPlayer; + private readonly HMODULE hModuleUserAssembly; + [SuppressMessage("", "SH002")] public RequiredLocalModule(HMODULE unityPlayer, HMODULE userAssembly) { + hModuleUnityPlayer = unityPlayer; + hModuleUserAssembly = userAssembly; + // Align the pointer - unityPlayer = (nint)(unityPlayer & ~0x3L); - userAssembly = (nint)(userAssembly & ~0x3L); + nint unityPlayerMappedView = (nint)(unityPlayer & ~0x3L); + nint userAssemblyMappedView = (nint)(userAssembly & ~0x3L); HasValue = true; - UnityPlayer = new((nuint)(nint)unityPlayer, GetImageSize(unityPlayer)); - UserAssembly = new((nuint)(nint)userAssembly, GetImageSize(userAssembly)); + UnityPlayer = new((nuint)unityPlayerMappedView, GetImageSize(unityPlayerMappedView)); + UserAssembly = new((nuint)userAssemblyMappedView, GetImageSize(userAssemblyMappedView)); } public void Dispose() { - FreeLibrary((nint)UnityPlayer.Address); - FreeLibrary((nint)UserAssembly.Address); + FreeLibrary(hModuleUnityPlayer); + FreeLibrary(hModuleUserAssembly); } - [SuppressMessage("", "SH002")] [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe uint GetImageSize(HMODULE hModule) + private unsafe uint GetImageSize(nint baseAddress) { - IMAGE_DOS_HEADER* pImageDosHeader = (IMAGE_DOS_HEADER*)(nint)hModule; - IMAGE_NT_HEADERS64* pImageNtHeader = (IMAGE_NT_HEADERS64*)(pImageDosHeader->e_lfanew + hModule); + IMAGE_DOS_HEADER* pImageDosHeader = (IMAGE_DOS_HEADER*)baseAddress; + IMAGE_NT_HEADERS64* pImageNtHeader = (IMAGE_NT_HEADERS64*)(pImageDosHeader->e_lfanew + baseAddress); return pImageNtHeader->OptionalHeader.SizeOfImage; } } \ No newline at end of file From 4cd971e1660390bf4c323a32886b5b30c3d43449 Mon Sep 17 00:00:00 2001 From: LinkChou Date: Thu, 6 Jun 2024 11:47:37 +0800 Subject: [PATCH 06/61] Add some --- src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index fb73d601..ad9e46b3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -2703,7 +2703,7 @@ 重置图片资源 - 在启动游戏页面的进程部分加入解锁帧率限制选项 + 在「启动游戏-进程」选项卡加入「解锁帧率限制」选项 启动游戏-解锁帧率限制 From 3c1e9237aaed27cd8db409b21806b8bc54bb5716 Mon Sep 17 00:00:00 2001 From: LinkChou Date: Thu, 6 Jun 2024 11:54:11 +0800 Subject: [PATCH 07/61] replace Uid to UID --- .../Snap.Hutao/Resource/Localization/SH.resx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index ad9e46b3..0b2806e1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -555,7 +555,7 @@ 必须登录 米游社 / HoYoLAB 并选择一个用户与角色 - 删除了 Uid:{0} 的 {1} 条祈愿记录 + 删除了 UID:{0} 的 {1} 条祈愿记录 胡桃云保存的祈愿记录存档数已达当前账号上限 @@ -570,7 +570,7 @@ 数据异常,无法保存至云端,请勿跨账号上传或尝试删除云端数据后重试 - 上传了 Uid:{0} 的 {1} 条祈愿记录,存储了 {2} 条 + 上传了 UID:{0} 的 {1} 条祈愿记录,存储了 {2} 条 请先登录或注册胡桃账号 @@ -621,7 +621,7 @@ 验证请求过快,请 1 分钟后再试 - 上传深渊记录失败,当前 Uid 已被胡桃数据库封禁 + 上传深渊记录失败,当前 UID 已被胡桃数据库封禁 上传深渊记录失败,正在计算统计数据 @@ -636,13 +636,13 @@ 上传深渊记录失败,存在无效的数据 - 无效的 Uid + 无效的 UID 上传深渊记录失败,不是本期数据 - 上传深渊记录失败,当前 Uid 的记录仍在处理中,请勿重复操作 + 上传深渊记录失败,当前 UID 的记录仍在处理中,请勿重复操作 上传深渊记录成功,获赠祈愿记录上传服务时长 @@ -2070,7 +2070,7 @@ 购买 / 续费云服务 - 删除此 Uid 的云端存档 + 删除此 UID 的云端存档 开发者账号无视服务到期时间 @@ -2079,7 +2079,7 @@ 胡桃云服务时长不足 - 下载此 Uid 的云端存档 + 下载此 UID 的云端存档 每期深渊首次上传可免费获得 3 天时长 @@ -2433,7 +2433,7 @@ Windows HDR - 请输入你的 HoYoLab Uid + 请输入你的 HoYoLab UID 你正在通过由我们提供的内嵌网页视图登录 米哈游通行证,我们会在你点击 我已登录 按钮后,读取你的 Cookie 信息,由此视图发起的网络通信只发生于你的计算机与米哈游服务器之间 From 89f8dedb57c6dfea2180b882cf2881a76fe268cf Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Thu, 6 Jun 2024 13:11:24 +0800 Subject: [PATCH 08/61] fix url protocol launch lock --- .../Core/LifeCycle/AppActivation.cs | 49 ++++++++++++------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs index 588c061b..e0ede679 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs @@ -56,30 +56,39 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi /// public void PostInitialization() { - serviceProvider.GetRequiredService().RunAsync().SafeForget(); - ToastNotificationManagerCompat.OnActivated += NotificationActivate; + RunPostInitializationAsync().SafeForget(); - using (activateSemaphore.Enter()) + async ValueTask RunPostInitializationAsync() { - // TODO: Introduced in 1.10.2, remove in later version - serviceProvider.GetRequiredService().ClearAsync().SafeForget(); - serviceProvider.GetRequiredService().UnregisterAllTasks(); + await taskContext.SwitchToBackgroundAsync(); - if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) < GuideState.Completed) + serviceProvider.GetRequiredService().RunAsync().SafeForget(); + ToastNotificationManagerCompat.OnActivated += NotificationActivate; + + using (await activateSemaphore.EnterAsync().ConfigureAwait(false)) { - return; + // TODO: Introduced in 1.10.2, remove in later version + serviceProvider.GetRequiredService().ClearAsync().SafeForget(); + serviceProvider.GetRequiredService().UnregisterAllTasks(); + + if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) < GuideState.Completed) + { + return; + } + + serviceProvider.GetRequiredService().RegisterAll(); + + if (serviceProvider.GetRequiredService().IsNotifyIconEnabled) + { + XamlLifetime.ApplicationLaunchedWithNotifyIcon = true; + + await taskContext.SwitchToMainThreadAsync(); + serviceProvider.GetRequiredService().DispatcherShutdownMode = DispatcherShutdownMode.OnExplicitShutdown; + _ = serviceProvider.GetRequiredService(); + } + + serviceProvider.GetRequiredService().StartAsync(default).SafeForget(); } - - serviceProvider.GetRequiredService().RegisterAll(); - - if (serviceProvider.GetRequiredService().IsNotifyIconEnabled) - { - XamlLifetime.ApplicationLaunchedWithNotifyIcon = true; - serviceProvider.GetRequiredService().DispatcherShutdownMode = DispatcherShutdownMode.OnExplicitShutdown; - _ = serviceProvider.GetRequiredService(); - } - - serviceProvider.GetRequiredService().StartAsync(default).SafeForget(); } } @@ -140,6 +149,8 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi private async ValueTask HandleActivationAsync(HutaoActivationArguments args) { + await taskContext.SwitchToBackgroundAsync(); + if (activateSemaphore.CurrentCount > 0) { using (await activateSemaphore.EnterAsync().ConfigureAwait(false)) From 94a96c76bc25e4a53353917332124236fcb85dd4 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Mon, 10 Jun 2024 10:57:29 +0800 Subject: [PATCH 09/61] fix #1710 --- src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteOptions.cs | 2 +- .../Snap.Hutao/Service/Job/DailyNoteRefreshJobScheduler.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteOptions.cs index ffe57f1c..dd880f22 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteOptions.cs @@ -48,7 +48,7 @@ internal sealed partial class DailyNoteOptions : DbStoreOptions { quartzService.UpdateJobAsync(JobIdentity.DailyNoteGroupName, JobIdentity.DailyNoteRefreshTriggerName, builder => { - return builder.WithSimpleSchedule(sb => sb.WithIntervalInMinutes(SelectedRefreshTime.Value).RepeatForever()); + return builder.WithSimpleSchedule(sb => sb.WithIntervalInSeconds(SelectedRefreshTime.Value).RepeatForever()); }).SafeForget(); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Job/DailyNoteRefreshJobScheduler.cs b/src/Snap.Hutao/Snap.Hutao/Service/Job/DailyNoteRefreshJobScheduler.cs index 15ed1579..9635e576 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Job/DailyNoteRefreshJobScheduler.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Job/DailyNoteRefreshJobScheduler.cs @@ -26,7 +26,7 @@ internal sealed partial class DailyNoteRefreshJobScheduler : IJobScheduler ITrigger dailyNoteTrigger = TriggerBuilder.Create() .WithIdentity(JobIdentity.DailyNoteRefreshTriggerName, JobIdentity.DailyNoteGroupName) .StartNow() - .WithSimpleSchedule(builder => builder.WithIntervalInMinutes(interval).RepeatForever()) + .WithSimpleSchedule(builder => builder.WithIntervalInSeconds(interval).RepeatForever()) .Build(); await scheduler.ScheduleJob(dailyNoteJob, dailyNoteTrigger).ConfigureAwait(false); From b99b34945e0bcf5a04c7711314bb3bd5c864e3a5 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Mon, 10 Jun 2024 11:05:58 +0800 Subject: [PATCH 10/61] fix #1711 --- .../Snap.Hutao/View/Dialog/DailyNoteNotificationDialog.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteNotificationDialog.xaml b/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteNotificationDialog.xaml index 45bb29cd..479b4c70 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteNotificationDialog.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteNotificationDialog.xaml @@ -31,14 +31,14 @@ From 063665e77e87ee822db732be1bf0bcbb363adbc8 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Sun, 9 Jun 2024 22:58:03 +0800 Subject: [PATCH 11/61] refresh inventory --- .../Snap.Hutao/Model/Entity/InventoryItem.cs | 19 ++- .../Metadata/Abstraction/ICultivatable.cs | 11 ++ .../Metadata/Avatar/Avatar.Implementation.cs | 2 +- .../Metadata/Weapon/Weapon.Implementation.cs | 2 +- .../Snap.Hutao/Resource/Localization/SH.resx | 6 + .../Service/Cultivation/CultivationService.cs | 146 ++++++++++++++++++ .../Cultivation/ICultivationService.cs | 2 + .../Service/Inventory/IInventoryDbService.cs | 2 +- .../Service/Inventory/InventoryDbService.cs | 4 +- .../View/Page/AvatarPropertyPage.xaml | 1 - .../Snap.Hutao/View/Page/CultivationPage.xaml | 4 + .../Cultivation/CultivationViewModel.cs | 39 ++++- .../Takumi/Event/Calculate/CalculateClient.cs | 2 +- .../Hoyolab/Takumi/Event/Calculate/Item.cs | 2 +- 14 files changed, 232 insertions(+), 10 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ICultivatable.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs index 4959215d..8aba7d71 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs @@ -12,7 +12,7 @@ namespace Snap.Hutao.Model.Entity; /// [HighQuality] [Table("inventory_items")] -internal sealed class InventoryItem : IDbMappingForeignKeyFrom +internal sealed class InventoryItem : IDbMappingForeignKeyFrom, IDbMappingForeignKeyFrom { /// /// 内部Id @@ -56,4 +56,21 @@ internal sealed class InventoryItem : IDbMappingForeignKeyFrom + /// 构造一个新的个数不为0的物品 + /// + /// 项目Id + /// 物品Id + /// 物品个数 + /// 新的个数不为0的物品 + public static InventoryItem From(in Guid projectId, in uint itemId, in uint count) + { + return new() + { + ProjectId = projectId, + ItemId = itemId, + Count = count, + }; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ICultivatable.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ICultivatable.cs new file mode 100644 index 00000000..285d20c9 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ICultivatable.cs @@ -0,0 +1,11 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Primitive; + +namespace Snap.Hutao.Model.Metadata.Abstraction; + +internal interface ICultivatable +{ + List CultivationItems { get; } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs index 4871692d..19ca1bd8 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs @@ -14,7 +14,7 @@ namespace Snap.Hutao.Model.Metadata.Avatar; /// /// 角色的接口实现部分 /// -internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource +internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource, ICultivatable { /// /// [非元数据] 搭配数据 diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs index 86a4465c..060a0d6d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs @@ -14,7 +14,7 @@ namespace Snap.Hutao.Model.Metadata.Weapon; /// /// 武器的接口实现 /// -internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource +internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource, ICultivatable { /// /// [非元数据] 搭配数据 diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index 0b2806e1..6da301bb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -1568,6 +1568,9 @@ 不能添加名称无效的计划 + + 正在同步背包物品 + 此操作不可逆,此计划的养成物品与背包材料将会丢失 @@ -1925,6 +1928,9 @@ 前往 + + 同步背包物品 + 删除清单 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs index d1fddb5e..b3a3724f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs @@ -2,15 +2,29 @@ // Licensed under the MIT license. using Snap.Hutao.Core.Database; +using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Model; using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity.Primitive; +using Snap.Hutao.Model.Metadata.Abstraction; +using Snap.Hutao.Model.Metadata.Avatar; using Snap.Hutao.Model.Metadata.Item; +using Snap.Hutao.Model.Metadata.Weapon; +using Snap.Hutao.Model.Primitive; using Snap.Hutao.Service.Inventory; +using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.Metadata.ContextAbstraction; +using Snap.Hutao.Service.User; using Snap.Hutao.ViewModel.Cultivation; +using Snap.Hutao.ViewModel.User; +using Snap.Hutao.Web.Response; using System.Collections.ObjectModel; +using System.Runtime.InteropServices; +using AvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta; +using BatchConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.BatchConsumption; +using CalculateClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient; using CalculateItem = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Item; +using PromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.PromotionDelta; namespace Snap.Hutao.Service.Cultivation; @@ -24,7 +38,10 @@ internal sealed partial class CultivationService : ICultivationService { private readonly ScopedDbCurrent dbCurrent; private readonly ICultivationDbService cultivationDbService; + private readonly IServiceScopeFactory serviceScopeFactory; private readonly IInventoryDbService inventoryDbService; + private readonly IMetadataService metadataService; + private readonly IUserService userService; private readonly ITaskContext taskContext; private ObservableCollection? projects; @@ -242,4 +259,133 @@ internal sealed partial class CultivationService : ICultivationService await taskContext.SwitchToBackgroundAsync(); await cultivationDbService.RemoveCultivateProjectByIdAsync(project.InnerId).ConfigureAwait(false); } + + /// + public async ValueTask RefreshInventoryAsync(CultivateProject project) + { + List cultivatables = + [ + .. await metadataService.GetAvatarListAsync().ConfigureAwait(false), + .. (await metadataService.GetWeaponListAsync().ConfigureAwait(false)).Where(weapon => weapon.Quality >= Model.Intrinsic.QualityType.QUALITY_BLUE), + ]; + + BatchConsumption? batchConsumption = default; + using (IServiceScope scope = serviceScopeFactory.CreateScope()) + { + if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid)) + { + CalculateClient calculateClient = scope.ServiceProvider.GetRequiredService(); + + Response? resp = await calculateClient + .BatchComputeAsync(userAndUid, GeneratePromotionDeltas(cultivatables)) + .ConfigureAwait(false); + + if (!resp.IsOk()) + { + return; + } + + batchConsumption = resp.Data; + } + } + + if (batchConsumption is { OverallConsume: { } items }) + { + await inventoryDbService.RemoveInventoryItemRangeByProjectId(project.InnerId, true).ConfigureAwait(false); + await inventoryDbService.AddInventoryItemRangeByProjectId(items.SelectList(item => InventoryItem.From(project.InnerId, item.Id, (uint)((int)item.Num - item.LackNum)))).ConfigureAwait(false); + } + } + + private static List GeneratePromotionDeltas(List cultivatables) + { + List avatars = []; + List weapons = []; + HashSet materialIds = []; + + while (cultivatables.Count > 0) + { + ICultivatable bestItem = cultivatables.OrderByDescending(item => item.CultivationItems.Count(material => !materialIds.Contains(material))).First(); + + if (bestItem.CultivationItems.All(materialIds.Contains)) + { + break; + } + + switch (bestItem) + { + case Avatar avatar: + avatars.Add(avatar); + break; + case Weapon weapon: + weapons.Add(weapon); + break; + default: + throw HutaoException.NotSupported(); + } + + foreach (ref readonly MaterialId materialId in CollectionsMarshal.AsSpan(bestItem.CultivationItems)) + { + materialIds.Add(materialId); + } + + cultivatables.Remove(bestItem); + } + + List deltas = []; + + for (int i = 0; i < Math.Max(avatars.Count, weapons.Count); i++) + { + Avatar? avatar = avatars.ElementAtOrDefault(i); + Weapon? weapon = weapons.ElementAtOrDefault(i); + + if (avatar is not null) + { + AvatarPromotionDelta delta = new() + { + AvatarId = avatar.Id, + AvatarLevelCurrent = 1, + AvatarLevelTarget = 90, + SkillList = avatar.SkillDepot.CompositeSkillsNoInherents().SelectList(skill => new PromotionDelta() + { + Id = skill.GroupId, + LevelCurrent = 1, + LevelTarget = 10, + }), + }; + + if (weapon is not null) + { + delta.Weapon = new() + { + Id = weapon.Id, + LevelCurrent = 1, + LevelTarget = 90, + }; + } + + deltas.Add(delta); + + continue; + } + + if (weapon is not null) + { + AvatarPromotionDelta delta = new() + { + Weapon = new() + { + Id = weapon.Id, + LevelCurrent = 1, + LevelTarget = 90, + }, + }; + + deltas.Add(delta); + + continue; + } + } + + return deltas; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs index 0a83a3e0..9d2b7c55 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs @@ -66,4 +66,6 @@ internal interface ICultivationService /// 项目 /// 添加操作的结果 ValueTask TryAddProjectAsync(CultivateProject project); + + ValueTask RefreshInventoryAsync(CultivateProject project); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs index 14430ec9..db4c8c53 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs @@ -9,7 +9,7 @@ internal interface IInventoryDbService { ValueTask AddInventoryItemRangeByProjectId(List items); - ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId); + ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId, bool includeMora = false); void UpdateInventoryItem(InventoryItem item); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs index bf34de1b..67a66ec2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs @@ -14,14 +14,14 @@ internal sealed partial class InventoryDbService : IInventoryDbService { private readonly IServiceProvider serviceProvider; - public async ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId) + public async ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId, bool includeMora = false) { using (IServiceScope scope = serviceProvider.CreateScope()) { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); await appDbContext.InventoryItems .AsNoTracking() - .Where(a => a.ProjectId == projectId && a.ItemId != 202U) // 摩拉 + .Where(a => a.ProjectId == projectId && (includeMora || a.ItemId != 202U)) // 摩拉 .ExecuteDeleteAsync() .ConfigureAwait(false); } diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml index df1c44af..f9e05085 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml @@ -11,7 +11,6 @@ xmlns:mxi="using:Microsoft.Xaml.Interactivity" xmlns:shc="using:Snap.Hutao.Control" xmlns:shcb="using:Snap.Hutao.Control.Behavior" - xmlns:shcca="using:Snap.Hutao.Control.Collection.Alternating" xmlns:shci="using:Snap.Hutao.Control.Image" xmlns:shcm="using:Snap.Hutao.Control.Markup" xmlns:shcp="using:Snap.Hutao.Control.Panel" diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml index 9eca6358..e1883a5c 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml @@ -269,6 +269,10 @@ Style="{ThemeResource CommandBarComboBoxStyle}"/> + ().ConfigureAwait(false); + + await taskContext.SwitchToMainThreadAsync(); + InventoryItems = cultivationService.GetInventoryItemViews(SelectedProject, context, SaveInventoryItemCommand); + } + } + [Command("NavigateToPageCommand")] private void NavigateToPage(string? typeString) { diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs index 6e0aa7dd..29a1bdbb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs @@ -36,7 +36,7 @@ internal sealed partial class CalculateClient public async ValueTask> BatchComputeAsync(UserAndUid userAndUid, List deltas, CancellationToken token = default) { - ArgumentOutOfRangeException.ThrowIfGreaterThan(deltas.Count, 8); + //ArgumentOutOfRangeException.ThrowIfGreaterThan(deltas.Count, 8); BatchConsumptionData data = new() { diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/Item.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/Item.cs index b0a7b62e..58cda946 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/Item.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/Item.cs @@ -42,5 +42,5 @@ internal sealed class Item public QualityType Level { get; set; } [JsonPropertyName("lack_num")] - public uint LackNum { get; set; } + public int LackNum { get; set; } } \ No newline at end of file From 293b1e214df51451eae758b8077c1ac101d12855 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Mon, 10 Jun 2024 13:54:22 +0800 Subject: [PATCH 12/61] migrate all v2 api to v3 api --- .../Service/Cultivation/CultivationService.cs | 56 +++++++------ .../AvatarProperty/AvatarPropertyViewModel.cs | 83 ++++++++++--------- .../ViewModel/Wiki/WikiAvatarViewModel.cs | 16 ++-- .../ViewModel/Wiki/WikiWeaponViewModel.cs | 13 +-- .../Event/Calculate/BatchConsumption.cs | 4 +- .../Takumi/Event/Calculate/CalculateClient.cs | 11 ++- 6 files changed, 95 insertions(+), 88 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs index b3a3724f..f7d8b514 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs @@ -3,28 +3,26 @@ using Snap.Hutao.Core.Database; using Snap.Hutao.Core.ExceptionService; -using Snap.Hutao.Model; using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity.Primitive; using Snap.Hutao.Model.Metadata.Abstraction; -using Snap.Hutao.Model.Metadata.Avatar; using Snap.Hutao.Model.Metadata.Item; -using Snap.Hutao.Model.Metadata.Weapon; using Snap.Hutao.Model.Primitive; using Snap.Hutao.Service.Inventory; using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.Metadata.ContextAbstraction; +using Snap.Hutao.Service.Notification; using Snap.Hutao.Service.User; using Snap.Hutao.ViewModel.Cultivation; using Snap.Hutao.ViewModel.User; +using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate; using Snap.Hutao.Web.Response; using System.Collections.ObjectModel; using System.Runtime.InteropServices; -using AvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta; -using BatchConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.BatchConsumption; -using CalculateClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient; using CalculateItem = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Item; -using PromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.PromotionDelta; +using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar; +using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon; +using ModelItem = Snap.Hutao.Model.Item; namespace Snap.Hutao.Service.Cultivation; @@ -41,6 +39,7 @@ internal sealed partial class CultivationService : ICultivationService private readonly IServiceScopeFactory serviceScopeFactory; private readonly IInventoryDbService inventoryDbService; private readonly IMetadataService metadataService; + private readonly IInfoBarService infoBarService; private readonly IUserService userService; private readonly ITaskContext taskContext; @@ -103,7 +102,7 @@ internal sealed partial class CultivationService : ICultivationService entryItems.Add(new(cultivateItem, context.GetMaterial(cultivateItem.ItemId))); } - Item item = entry.Type switch + ModelItem item = entry.Type switch { CultivateType.AvatarAndSkill => context.GetAvatar(entry.Id).ToItem(), CultivateType.Weapon => context.GetWeapon(entry.Id).ToItem(), @@ -272,21 +271,24 @@ internal sealed partial class CultivationService : ICultivationService BatchConsumption? batchConsumption = default; using (IServiceScope scope = serviceScopeFactory.CreateScope()) { - if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid)) + if (!UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid)) { - CalculateClient calculateClient = scope.ServiceProvider.GetRequiredService(); - - Response? resp = await calculateClient - .BatchComputeAsync(userAndUid, GeneratePromotionDeltas(cultivatables)) - .ConfigureAwait(false); - - if (!resp.IsOk()) - { - return; - } - - batchConsumption = resp.Data; + infoBarService.Warning(SH.MustSelectUserAndUid); + return; } + + CalculateClient calculateClient = scope.ServiceProvider.GetRequiredService(); + + Response? resp = await calculateClient + .BatchComputeAsync(userAndUid, GeneratePromotionDeltas(cultivatables), true) + .ConfigureAwait(false); + + if (!resp.IsOk()) + { + return; + } + + batchConsumption = resp.Data; } if (batchConsumption is { OverallConsume: { } items }) @@ -298,8 +300,8 @@ internal sealed partial class CultivationService : ICultivationService private static List GeneratePromotionDeltas(List cultivatables) { - List avatars = []; - List weapons = []; + List avatars = []; + List weapons = []; HashSet materialIds = []; while (cultivatables.Count > 0) @@ -313,10 +315,10 @@ internal sealed partial class CultivationService : ICultivationService switch (bestItem) { - case Avatar avatar: + case MetadataAvatar avatar: avatars.Add(avatar); break; - case Weapon weapon: + case MetadataWeapon weapon: weapons.Add(weapon); break; default: @@ -335,8 +337,8 @@ internal sealed partial class CultivationService : ICultivationService for (int i = 0; i < Math.Max(avatars.Count, weapons.Count); i++) { - Avatar? avatar = avatars.ElementAtOrDefault(i); - Weapon? weapon = weapons.ElementAtOrDefault(i); + MetadataAvatar? avatar = avatars.ElementAtOrDefault(i); + MetadataWeapon? weapon = weapons.ElementAtOrDefault(i); if (avatar is not null) { diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs index ede09b51..e6f1c8ef 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs @@ -25,6 +25,7 @@ using Windows.Graphics.Imaging; using Windows.Storage.Streams; using Windows.UI; using CalculatorAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta; +using CalculatorBatchConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.BatchConsumption; using CalculatorClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient; using CalculatorConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Consumption; using CalculatorItem = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Item; @@ -175,7 +176,7 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I return; } - if (userService.Current is null) + if (!UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid)) { infoBarService.Warning(SH.MustSelectUserAndUid); return; @@ -196,17 +197,25 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I return; } - CultivateCoreResult result = await CultivateCoreAsync(userService.Current.Entity, delta, avatar).ConfigureAwait(false); + Response consumptionResponse = await calculatorClient.BatchComputeAsync(userAndUid, delta).ConfigureAwait(false); - switch (result) + if (!consumptionResponse.IsOk()) { - case CultivateCoreResult.Ok: - infoBarService.Success(SH.ViewModelCultivationEntryAddSuccess); - break; - case CultivateCoreResult.SaveConsumptionFailed: - infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning); - break; + return; } + + CalculatorBatchConsumption batchConsumption = consumptionResponse.Data; + + CalculatorConsumption? consumption = batchConsumption.Items.FirstOrDefault(); + ArgumentNullException.ThrowIfNull(consumption); + + if (!await SaveCultivationAsync(consumption, delta).ConfigureAwait(false)) + { + infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning); + return; + } + + infoBarService.Success(SH.ViewModelCultivationEntryAddSuccess); } [Command("BatchCultivateCommand")] @@ -217,7 +226,7 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I return; } - if (userService.Current is null) + if (!UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid)) { infoBarService.Warning(SH.MustSelectUserAndUid); return; @@ -240,6 +249,7 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I using (await progressDialog.BlockAsync(taskContext).ConfigureAwait(false)) { BatchCultivateResult result = default; + List deltas = []; foreach (AvatarView avatar in avatars) { if (!baseline.TryGetNonErrorCopy(avatar, out CalculatorAvatarPromotionDelta? copy)) @@ -248,25 +258,27 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I continue; } - CultivateCoreResult coreResult = await CultivateCoreAsync(userService.Current.Entity, copy, avatar).ConfigureAwait(false); + deltas.Add(copy); + } - switch (coreResult) - { - case CultivateCoreResult.Ok: - ++result.SucceedCount; - break; - case CultivateCoreResult.ComputeConsumptionFailed: - result.Interrupted = true; - break; - case CultivateCoreResult.SaveConsumptionFailed: - result.Interrupted = true; - break; - } + Response consumptionResponse = await calculatorClient.BatchComputeAsync(userAndUid, deltas).ConfigureAwait(false); - if (result.Interrupted) + if (!consumptionResponse.IsOk()) + { + return; + } + + CalculatorBatchConsumption batchConsumption = consumptionResponse.Data; + + foreach ((CalculatorConsumption consumption, CalculatorAvatarPromotionDelta delta) in batchConsumption.Items.Zip(deltas)) + { + if (!await SaveCultivationAsync(consumption, delta).ConfigureAwait(false)) { + result.Interrupted = true; break; } + + ++result.SucceedCount; } if (result.Interrupted) @@ -280,43 +292,32 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I } } - private async ValueTask CultivateCoreAsync(Model.Entity.User user, CalculatorAvatarPromotionDelta delta, AvatarView avatar) + private async ValueTask SaveCultivationAsync(CalculatorConsumption consumption, CalculatorAvatarPromotionDelta delta) { - Response consumptionResponse = await calculatorClient.ComputeAsync(user, delta).ConfigureAwait(false); - - if (!consumptionResponse.IsOk()) - { - return CultivateCoreResult.ComputeConsumptionFailed; - } - - CalculatorConsumption consumption = consumptionResponse.Data; LevelInformation levelInformation = LevelInformation.From(delta); List items = CalculatorItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume); bool avatarSaved = await cultivationService - .SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items, levelInformation) + .SaveConsumptionAsync(CultivateType.AvatarAndSkill, delta.AvatarId, items, levelInformation) .ConfigureAwait(false); try { - ArgumentNullException.ThrowIfNull(avatar.Weapon); + ArgumentNullException.ThrowIfNull(delta.Weapon); // Take a hot path if avatar is not saved. bool avatarAndWeaponSaved = avatarSaved && await cultivationService - .SaveConsumptionAsync(CultivateType.Weapon, avatar.Weapon.Id, consumption.WeaponConsume.EmptyIfNull(), levelInformation) + .SaveConsumptionAsync(CultivateType.Weapon, delta.Weapon.Id, consumption.WeaponConsume.EmptyIfNull(), levelInformation) .ConfigureAwait(false); - if (!avatarAndWeaponSaved) - { - return CultivateCoreResult.SaveConsumptionFailed; - } + return avatarAndWeaponSaved; } catch (HutaoException ex) { infoBarService.Error(ex, SH.ViewModelCultivationAddWarning); } - return CultivateCoreResult.Ok; + return true; } [Command("ExportAsImageCommand")] diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs index cf8c9bb1..cd3efcaf 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs @@ -20,15 +20,14 @@ using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.Notification; using Snap.Hutao.Service.User; using Snap.Hutao.View.Dialog; +using Snap.Hutao.ViewModel.User; using Snap.Hutao.Web.Response; using System.Collections.Frozen; using System.Collections.ObjectModel; using System.Runtime.InteropServices; using CalculateAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta; +using CalculateBatchConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.BatchConsumption; using CalculateClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient; -using CalculateConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Consumption; -using CalculateItem = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Item; -using CalculateItemHelper = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.ItemHelper; namespace Snap.Hutao.ViewModel.Wiki; @@ -163,7 +162,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel return; } - if (userService.Current is null) + if (!UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid)) { infoBarService.Warning(SH.MustSelectUserAndUid); return; @@ -178,8 +177,8 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel return; } - Response consumptionResponse = await calculateClient - .ComputeAsync(userService.Current.Entity, delta) + Response consumptionResponse = await calculateClient + .BatchComputeAsync(userAndUid, delta) .ConfigureAwait(false); if (!consumptionResponse.IsOk()) @@ -187,13 +186,12 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel return; } - CalculateConsumption consumption = consumptionResponse.Data; + CalculateBatchConsumption batchConsumption = consumptionResponse.Data; LevelInformation levelInformation = LevelInformation.From(delta); - List items = CalculateItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume); try { bool saved = await cultivationService - .SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items, levelInformation) + .SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, batchConsumption.OverallConsume, levelInformation) .ConfigureAwait(false); if (saved) diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs index bc09b39b..78673e7c 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs @@ -20,13 +20,14 @@ using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.Notification; using Snap.Hutao.Service.User; using Snap.Hutao.View.Dialog; +using Snap.Hutao.ViewModel.User; using Snap.Hutao.Web.Response; using System.Collections.Frozen; using System.Collections.ObjectModel; using System.Runtime.InteropServices; using CalculateAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta; +using CalculateBatchConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.BatchConsumption; using CalculateClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient; -using CalculateConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Consumption; namespace Snap.Hutao.ViewModel.Wiki; @@ -154,7 +155,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel return; } - if (userService.Current is null) + if (!UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid)) { infoBarService.Warning(SH.MustSelectUserAndUid); return; @@ -169,8 +170,8 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel return; } - Response consumptionResponse = await calculateClient - .ComputeAsync(userService.Current.Entity, delta) + Response consumptionResponse = await calculateClient + .BatchComputeAsync(userAndUid, delta) .ConfigureAwait(false); if (!consumptionResponse.IsOk()) @@ -178,12 +179,12 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel return; } - CalculateConsumption consumption = consumptionResponse.Data; + CalculateBatchConsumption batchConsumption = consumptionResponse.Data; LevelInformation levelInformation = LevelInformation.From(delta); try { bool saved = await cultivationService - .SaveConsumptionAsync(CultivateType.Weapon, weapon.Id, consumption.WeaponConsume.EmptyIfNull(), levelInformation) + .SaveConsumptionAsync(CultivateType.Weapon, weapon.Id, batchConsumption.OverallConsume, levelInformation) .ConfigureAwait(false); if (saved) diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/BatchConsumption.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/BatchConsumption.cs index b43e7f8f..b1dd0310 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/BatchConsumption.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/BatchConsumption.cs @@ -6,13 +6,13 @@ namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate; internal sealed class BatchConsumption { [JsonPropertyName("items")] - public List? Items { get; set; } + public List Items { get; set; } = default!; [JsonPropertyName("available_material")] public List? AvailableMaterial { get; set; } [JsonPropertyName("overall_consume")] - public List? OverallConsume { get; set; } + public List OverallConsume { get; set; } = default!; [JsonPropertyName("has_user_info")] public bool HasUserInfo { get; set; } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs index 29a1bdbb..febbceca 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs @@ -34,15 +34,20 @@ internal sealed partial class CalculateClient return Response.Response.DefaultIfNull(resp); } - public async ValueTask> BatchComputeAsync(UserAndUid userAndUid, List deltas, CancellationToken token = default) + public async ValueTask> BatchComputeAsync(UserAndUid userAndUid, AvatarPromotionDelta delta, bool syncMaterials = false, CancellationToken token = default) + { + return await BatchComputeAsync(userAndUid, [delta], syncMaterials, token).ConfigureAwait(false); + } + + public async ValueTask> BatchComputeAsync(UserAndUid userAndUid, List deltas, bool syncMaterials = false, CancellationToken token = default) { //ArgumentOutOfRangeException.ThrowIfGreaterThan(deltas.Count, 8); BatchConsumptionData data = new() { Items = deltas, - Region = userAndUid.Uid.Region, - Uid = userAndUid.Uid.ToString(), + Region = syncMaterials ? userAndUid.Uid.Region : default!, + Uid = syncMaterials ? userAndUid.Uid.ToString() : default!, }; HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() From 3e26e247cd34bb30ee67851d6a276f7c2b42ac4e Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Mon, 10 Jun 2024 22:43:50 +0800 Subject: [PATCH 13/61] refactor metadata abstraction --- .../Snap.Hutao/Model/Entity/InventoryItem.cs | 3 +- .../Model/InterChange/GachaLog/UIGFItem.cs | 4 +- ...ivatable.cs => ICultivationItemsAccess.cs} | 2 +- .../{IItemSource.cs => IItemConvertible.cs} | 2 +- ...{INameQuality.cs => INameQualityAccess.cs} | 2 +- ...ource.cs => IStatisticsItemConvertible.cs} | 2 +- ...emSource.cs => ISummaryItemConvertible.cs} | 12 +- .../Metadata/Avatar/Avatar.Implementation.cs | 99 -------------- .../Model/Metadata/Avatar/Avatar.cs | 129 ++++++++++-------- .../Metadata/Weapon/Weapon.Implementation.cs | 107 --------------- .../Model/Metadata/Weapon/Weapon.cs | 110 ++++++++++----- .../Service/Cultivation/CultivationService.cs | 8 +- .../Factory/GachaStatisticsExtension.cs | 2 +- .../Factory/GachaStatisticsSlimFactory.cs | 4 +- .../GachaLog/Factory/HistoryWishBuilder.cs | 28 ++-- .../Factory/HutaoStatisticsFactory.cs | 2 +- .../Factory/TypedWishSummaryBuilder.cs | 2 +- .../GachaLogServiceMetadataContext.cs | 2 +- .../Snap.Hutao/View/Page/WikiAvatarPage.xaml | 6 +- .../Snap.Hutao/View/Page/WikiWeaponPage.xaml | 2 +- .../ViewModel/Wiki/WikiAvatarViewModel.cs | 2 +- .../ViewModel/Wiki/WikiWeaponViewModel.cs | 2 +- 22 files changed, 186 insertions(+), 346 deletions(-) rename src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/{ICultivatable.cs => ICultivationItemsAccess.cs} (83%) rename src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/{IItemSource.cs => IItemConvertible.cs} (82%) rename src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/{INameQuality.cs => INameQualityAccess.cs} (91%) rename src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/{IStatisticsItemSource.cs => IStatisticsItemConvertible.cs} (90%) rename src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/{ISummaryItemSource.cs => ISummaryItemConvertible.cs} (53%) delete mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs index 8aba7d71..73136219 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/InventoryItem.cs @@ -12,7 +12,8 @@ namespace Snap.Hutao.Model.Entity; /// [HighQuality] [Table("inventory_items")] -internal sealed class InventoryItem : IDbMappingForeignKeyFrom, IDbMappingForeignKeyFrom +internal sealed class InventoryItem : IDbMappingForeignKeyFrom, + IDbMappingForeignKeyFrom { /// /// 内部Id diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFItem.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFItem.cs index a0b6fe2f..e6f51939 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFItem.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFItem.cs @@ -13,7 +13,7 @@ namespace Snap.Hutao.Model.InterChange.GachaLog; /// UIGF物品 /// [HighQuality] -internal sealed class UIGFItem : GachaLogItem, IMappingFrom +internal sealed class UIGFItem : GachaLogItem, IMappingFrom { /// /// 额外祈愿映射 @@ -22,7 +22,7 @@ internal sealed class UIGFItem : GachaLogItem, IMappingFrom CultivationItems { get; } } diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IItemSource.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IItemConvertible.cs similarity index 82% rename from src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IItemSource.cs rename to src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IItemConvertible.cs index cca32549..535d7b7e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IItemSource.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IItemConvertible.cs @@ -3,7 +3,7 @@ namespace Snap.Hutao.Model.Metadata.Abstraction; -internal interface IItemSource +internal interface IItemConvertible { Model.Item ToItem(); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQuality.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQualityAccess.cs similarity index 91% rename from src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQuality.cs rename to src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQualityAccess.cs index b29640d9..a6d83c8a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQuality.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/INameQualityAccess.cs @@ -9,7 +9,7 @@ namespace Snap.Hutao.Model.Metadata.Abstraction; /// 物品与星级 /// [HighQuality] -internal interface INameQuality +internal interface INameQualityAccess { /// /// 名称 diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IStatisticsItemSource.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IStatisticsItemConvertible.cs similarity index 90% rename from src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IStatisticsItemSource.cs rename to src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IStatisticsItemConvertible.cs index b1e71200..fb51bec9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IStatisticsItemSource.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/IStatisticsItemConvertible.cs @@ -9,7 +9,7 @@ namespace Snap.Hutao.Model.Metadata.Abstraction; /// 指示该类为统计物品的源 /// [HighQuality] -internal interface IStatisticsItemSource +internal interface IStatisticsItemConvertible { /// /// 转换到统计物品 diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ISummaryItemSource.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ISummaryItemConvertible.cs similarity index 53% rename from src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ISummaryItemSource.cs rename to src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ISummaryItemConvertible.cs index 2be603a4..97647310 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ISummaryItemSource.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ISummaryItemConvertible.cs @@ -10,19 +10,9 @@ namespace Snap.Hutao.Model.Metadata.Abstraction; /// 指示该类为简述统计物品的源 /// [HighQuality] -internal interface ISummaryItemSource +internal interface ISummaryItemConvertible { - /// - /// 星级 - /// QualityType Quality { get; } - /// - /// 转换到简述统计物品 - /// - /// 距上个五星 - /// 时间 - /// 是否为Up物品 - /// 简述统计物品 SummaryItem ToSummaryItem(int lastPull, in DateTimeOffset time, bool isUp); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs deleted file mode 100644 index 19ca1bd8..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Model.Calculable; -using Snap.Hutao.Model.Metadata.Abstraction; -using Snap.Hutao.Model.Metadata.Converter; -using Snap.Hutao.Model.Metadata.Item; -using Snap.Hutao.ViewModel.Complex; -using Snap.Hutao.ViewModel.GachaLog; -using Snap.Hutao.ViewModel.Wiki; - -namespace Snap.Hutao.Model.Metadata.Avatar; - -/// -/// 角色的接口实现部分 -/// -internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource, ICultivatable -{ - /// - /// [非元数据] 搭配数据 - /// TODO:Add View suffix. - /// - [JsonIgnore] - public AvatarCollocationView? Collocation { get; set; } - - /// - /// [非元数据] 烹饪奖励 - /// - [JsonIgnore] - public CookBonusView? CookBonusView { get; set; } - - /// - /// [非元数据] 养成物品视图 - /// - [JsonIgnore] - public List? CultivationItemsView { get; set; } - - /// - /// 最大等级 - /// - [SuppressMessage("", "CA1822")] - public uint MaxLevel { get => GetMaxLevel(); } - - public static uint GetMaxLevel() - { - return 90U; - } - - /// - public ICalculableAvatar ToCalculable() - { - return CalculableAvatar.From(this); - } - - /// - /// 转换为基础物品 - /// - /// 基础物品 - public Model.Item ToItem() - { - return new() - { - Name = Name, - Icon = AvatarIconConverter.IconNameToUri(Icon), - Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore), - Quality = Quality, - }; - } - - /// - public StatisticsItem ToStatisticsItem(int count) - { - return new() - { - Name = Name, - Icon = AvatarIconConverter.IconNameToUri(Icon), - Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore), - Quality = Quality, - - Count = count, - }; - } - - /// - public SummaryItem ToSummaryItem(int lastPull, in DateTimeOffset time, bool isUp) - { - return new() - { - Name = Name, - Icon = AvatarIconConverter.IconNameToUri(Icon), - Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore), - Quality = Quality, - - Time = time, - LastPull = lastPull, - IsUp = isUp, - }; - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs index 79a81f4a..e88b0a80 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs @@ -1,99 +1,118 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Model.Calculable; using Snap.Hutao.Model.Intrinsic; +using Snap.Hutao.Model.Metadata.Abstraction; +using Snap.Hutao.Model.Metadata.Converter; +using Snap.Hutao.Model.Metadata.Item; using Snap.Hutao.Model.Primitive; +using Snap.Hutao.ViewModel.Complex; +using Snap.Hutao.ViewModel.GachaLog; +using Snap.Hutao.ViewModel.Wiki; namespace Snap.Hutao.Model.Metadata.Avatar; -/// -/// 角色 -/// [HighQuality] -internal partial class Avatar +internal partial class Avatar : INameQualityAccess, + IStatisticsItemConvertible, + ISummaryItemConvertible, + IItemConvertible, + ICalculableSource, + ICultivationItemsAccess { - /// - /// Id - /// public AvatarId Id { get; set; } - /// - /// 突破提升 Id 外键 - /// public PromoteId PromoteId { get; set; } - /// - /// 排序号 - /// public uint Sort { get; set; } - /// - /// 体型 - /// public BodyType Body { get; set; } = default!; - /// - /// 正面图标 - /// public string Icon { get; set; } = default!; - /// - /// 侧面图标 - /// public string SideIcon { get; set; } = default!; - /// - /// 名称 - /// public string Name { get; set; } = default!; - /// - /// 描述 - /// public string Description { get; set; } = default!; - /// - /// 角色加入游戏时间 - /// public DateTimeOffset BeginTime { get; set; } - /// - /// 星级 - /// public QualityType Quality { get; set; } - /// - /// 武器类型 - /// public WeaponType Weapon { get; set; } - /// - /// 基础数值 - /// public AvatarBaseValue BaseValue { get; set; } = default!; - /// - /// 生长曲线 - /// public List> GrowCurves { get; set; } = default!; - /// - /// 技能 - /// public SkillDepot SkillDepot { get; set; } = default!; - /// - /// 好感信息/基本信息 - /// public FetterInfo FetterInfo { get; set; } = default!; - /// - /// 皮肤 - /// public List Costumes { get; set; } = default!; - /// - /// 养成物品 - /// public List CultivationItems { get; set; } = default!; + + [JsonIgnore] + public AvatarCollocationView? CollocationView { get; set; } + + [JsonIgnore] + public CookBonusView? CookBonusView { get; set; } + + [JsonIgnore] + public List? CultivationItemsView { get; set; } + + [SuppressMessage("", "CA1822")] + public uint MaxLevel { get => GetMaxLevel(); } + + public static uint GetMaxLevel() + { + return 90U; + } + + public ICalculableAvatar ToCalculable() + { + return CalculableAvatar.From(this); + } + + public Model.Item ToItem() + { + return new() + { + Name = Name, + Icon = AvatarIconConverter.IconNameToUri(Icon), + Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore), + Quality = Quality, + }; + } + + public StatisticsItem ToStatisticsItem(int count) + { + return new() + { + Name = Name, + Icon = AvatarIconConverter.IconNameToUri(Icon), + Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore), + Quality = Quality, + + Count = count, + }; + } + + public SummaryItem ToSummaryItem(int lastPull, in DateTimeOffset time, bool isUp) + { + return new() + { + Name = Name, + Icon = AvatarIconConverter.IconNameToUri(Icon), + Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore), + Quality = Quality, + + Time = time, + LastPull = lastPull, + IsUp = isUp, + }; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs deleted file mode 100644 index 060a0d6d..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Model.Calculable; -using Snap.Hutao.Model.Intrinsic; -using Snap.Hutao.Model.Metadata.Abstraction; -using Snap.Hutao.Model.Metadata.Converter; -using Snap.Hutao.Model.Metadata.Item; -using Snap.Hutao.ViewModel.Complex; -using Snap.Hutao.ViewModel.GachaLog; - -namespace Snap.Hutao.Model.Metadata.Weapon; - -/// -/// 武器的接口实现 -/// -internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource, ICultivatable -{ - /// - /// [非元数据] 搭配数据 - /// TODO:Add View suffix. - /// - [JsonIgnore] - public WeaponCollocationView? Collocation { get; set; } - - /// - /// [非元数据] 养成物品视图 - /// - [JsonIgnore] - public List? CultivationItemsView { get; set; } - - /// - [JsonIgnore] - public QualityType Quality - { - get => RankLevel; - } - - /// - /// 最大等级 - /// - internal uint MaxLevel { get => GetMaxLevelByQuality(Quality); } - - public static uint GetMaxLevelByQuality(QualityType quality) - { - return quality >= QualityType.QUALITY_BLUE ? 90U : 70U; - } - - /// - public ICalculableWeapon ToCalculable() - { - return CalculableWeapon.From(this); - } - - /// - /// 转换为基础物品 - /// - /// 基础物品 - public Model.Item ToItem() - { - return new() - { - Name = Name, - Icon = EquipIconConverter.IconNameToUri(Icon), - Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType), - Quality = RankLevel, - }; - } - - /// - /// 转换到统计物品 - /// - /// 个数 - /// 统计物品 - public StatisticsItem ToStatisticsItem(int count) - { - return new() - { - Name = Name, - Icon = EquipIconConverter.IconNameToUri(Icon), - Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType), - Quality = RankLevel, - Count = count, - }; - } - - /// - /// 转换到简述统计物品 - /// - /// 距上个五星 - /// 时间 - /// 是否为Up物品 - /// 简述统计物品 - public SummaryItem ToSummaryItem(int lastPull, in DateTimeOffset time, bool isUp) - { - return new() - { - Name = Name, - Icon = EquipIconConverter.IconNameToUri(Icon), - Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType), - Time = time, - Quality = RankLevel, - LastPull = lastPull, - IsUp = isUp, - }; - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs index 19136d47..46233381 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs @@ -1,69 +1,105 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Model.Calculable; using Snap.Hutao.Model.Intrinsic; +using Snap.Hutao.Model.Metadata.Abstraction; +using Snap.Hutao.Model.Metadata.Converter; +using Snap.Hutao.Model.Metadata.Item; using Snap.Hutao.Model.Primitive; +using Snap.Hutao.ViewModel.Complex; +using Snap.Hutao.ViewModel.GachaLog; namespace Snap.Hutao.Model.Metadata.Weapon; -/// -/// 武器 -/// [HighQuality] -internal sealed partial class Weapon +internal sealed partial class Weapon : INameQualityAccess, + IStatisticsItemConvertible, + ISummaryItemConvertible, + IItemConvertible, + ICalculableSource, + ICultivationItemsAccess { - /// - /// Id - /// public WeaponId Id { get; set; } - /// - /// 突破 Id - /// public PromoteId PromoteId { get; set; } - /// - /// 武器类型 - /// public WeaponType WeaponType { get; set; } - /// - /// 等级 - /// public QualityType RankLevel { get; set; } - /// - /// 名称 - /// public string Name { get; set; } = default!; - /// - /// 描述 - /// public string Description { get; set; } = default!; - /// - /// 图标 - /// public string Icon { get; set; } = default!; - /// - /// 觉醒图标 - /// public string AwakenIcon { get; set; } = default!; - /// - /// 生长曲线 - /// public List GrowCurves { get; set; } = default!; - /// - /// 被动信息, 无被动的武器为 - /// public NameDescriptions? Affix { get; set; } = default!; - /// - /// 养成物品 - /// public List CultivationItems { get; set; } = default!; + + [JsonIgnore] + public WeaponCollocationView? CollocationView { get; set; } + + [JsonIgnore] + public List? CultivationItemsView { get; set; } + + [JsonIgnore] + public QualityType Quality + { + get => RankLevel; + } + + internal uint MaxLevel { get => GetMaxLevelByQuality(Quality); } + + public static uint GetMaxLevelByQuality(QualityType quality) + { + return quality >= QualityType.QUALITY_BLUE ? 90U : 70U; + } + + public ICalculableWeapon ToCalculable() + { + return CalculableWeapon.From(this); + } + + public Model.Item ToItem() + { + return new() + { + Name = Name, + Icon = EquipIconConverter.IconNameToUri(Icon), + Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType), + Quality = RankLevel, + }; + } + + public StatisticsItem ToStatisticsItem(int count) + { + return new() + { + Name = Name, + Icon = EquipIconConverter.IconNameToUri(Icon), + Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType), + Quality = RankLevel, + Count = count, + }; + } + + public SummaryItem ToSummaryItem(int lastPull, in DateTimeOffset time, bool isUp) + { + return new() + { + Name = Name, + Icon = EquipIconConverter.IconNameToUri(Icon), + Badge = WeaponTypeIconConverter.WeaponTypeToIconUri(WeaponType), + Time = time, + Quality = RankLevel, + LastPull = lastPull, + IsUp = isUp, + }; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs index f7d8b514..509a26db 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs @@ -262,7 +262,7 @@ internal sealed partial class CultivationService : ICultivationService /// public async ValueTask RefreshInventoryAsync(CultivateProject project) { - List cultivatables = + List cultivationItemsEntryList = [ .. await metadataService.GetAvatarListAsync().ConfigureAwait(false), .. (await metadataService.GetWeaponListAsync().ConfigureAwait(false)).Where(weapon => weapon.Quality >= Model.Intrinsic.QualityType.QUALITY_BLUE), @@ -280,7 +280,7 @@ internal sealed partial class CultivationService : ICultivationService CalculateClient calculateClient = scope.ServiceProvider.GetRequiredService(); Response? resp = await calculateClient - .BatchComputeAsync(userAndUid, GeneratePromotionDeltas(cultivatables), true) + .BatchComputeAsync(userAndUid, GeneratePromotionDeltas(cultivationItemsEntryList), true) .ConfigureAwait(false); if (!resp.IsOk()) @@ -298,7 +298,7 @@ internal sealed partial class CultivationService : ICultivationService } } - private static List GeneratePromotionDeltas(List cultivatables) + private static List GeneratePromotionDeltas(List cultivatables) { List avatars = []; List weapons = []; @@ -306,7 +306,7 @@ internal sealed partial class CultivationService : ICultivationService while (cultivatables.Count > 0) { - ICultivatable bestItem = cultivatables.OrderByDescending(item => item.CultivationItems.Count(material => !materialIds.Contains(material))).First(); + ICultivationItemsAccess bestItem = cultivatables.OrderByDescending(item => item.CultivationItems.Count(material => !materialIds.Contains(material))).First(); if (bestItem.CultivationItems.All(materialIds.Contains)) { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsExtension.cs index 420fa93e..497ad2dd 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsExtension.cs @@ -49,7 +49,7 @@ internal static class GachaStatisticsExtension /// 计数器 /// 统计物品列表 public static List ToStatisticsList(this Dictionary dict) - where TItem : IStatisticsItemSource + where TItem : IStatisticsItemConvertible { IOrderedEnumerable result = dict .Select(kvp => kvp.Key.ToStatisticsItem(kvp.Value)) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsSlimFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsSlimFactory.cs index 22b02de4..1ef41991 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsSlimFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsSlimFactory.cs @@ -27,7 +27,7 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF return CreateCore(context, items, uid); } - private static void Track(INameQuality nameQuality, ref int orangeTracker, ref int purpleTracker) + private static void Track(INameQualityAccess nameQuality, ref int orangeTracker, ref int purpleTracker) { switch (nameQuality.Quality) { @@ -69,7 +69,7 @@ internal sealed partial class GachaStatisticsSlimFactory : IGachaStatisticsSlimF // O(n) operation foreach (ref readonly GachaItem item in CollectionsMarshal.AsSpan(items)) { - INameQuality nameQuality = context.GetNameQualityByItemId(item.ItemId); + INameQualityAccess nameQuality = context.GetNameQualityByItemId(item.ItemId); switch (item.QueryType) { case GachaType.Standard: diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs index 34ce5071..2541b719 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HistoryWishBuilder.cs @@ -16,11 +16,11 @@ internal sealed class HistoryWishBuilder { private readonly GachaEvent gachaEvent; - private readonly Dictionary orangeUpCounter = []; - private readonly Dictionary purpleUpCounter = []; - private readonly Dictionary orangeCounter = []; - private readonly Dictionary purpleCounter = []; - private readonly Dictionary blueCounter = []; + private readonly Dictionary orangeUpCounter = []; + private readonly Dictionary purpleUpCounter = []; + private readonly Dictionary orangeCounter = []; + private readonly Dictionary purpleCounter = []; + private readonly Dictionary blueCounter = []; private int totalCountTracker; @@ -37,18 +37,18 @@ internal sealed class HistoryWishBuilder switch (ConfigType) { case GachaType.ActivityAvatar or GachaType.SpecialActivityAvatar: - orangeUpCounter = gachaEvent.UpOrangeList.Select(id => context.IdAvatarMap[id]).ToDictionary(a => (IStatisticsItemSource)a, a => 0); - purpleUpCounter = gachaEvent.UpPurpleList.Select(id => context.IdAvatarMap[id]).ToDictionary(a => (IStatisticsItemSource)a, a => 0); + orangeUpCounter = gachaEvent.UpOrangeList.Select(id => context.IdAvatarMap[id]).ToDictionary(a => (IStatisticsItemConvertible)a, a => 0); + purpleUpCounter = gachaEvent.UpPurpleList.Select(id => context.IdAvatarMap[id]).ToDictionary(a => (IStatisticsItemConvertible)a, a => 0); break; case GachaType.ActivityWeapon: - orangeUpCounter = gachaEvent.UpOrangeList.Select(id => context.IdWeaponMap[id]).ToDictionary(w => (IStatisticsItemSource)w, w => 0); - purpleUpCounter = gachaEvent.UpPurpleList.Select(id => context.IdWeaponMap[id]).ToDictionary(w => (IStatisticsItemSource)w, w => 0); + orangeUpCounter = gachaEvent.UpOrangeList.Select(id => context.IdWeaponMap[id]).ToDictionary(w => (IStatisticsItemConvertible)w, w => 0); + purpleUpCounter = gachaEvent.UpPurpleList.Select(id => context.IdWeaponMap[id]).ToDictionary(w => (IStatisticsItemConvertible)w, w => 0); break; case GachaType.ActivityCity: // Avatars are less than weapons, so we try to get the value from avatar map first - orangeUpCounter = gachaEvent.UpOrangeList.Select(id => (IStatisticsItemSource?)context.IdAvatarMap.GetValueOrDefault(id) ?? context.IdWeaponMap[id]).ToDictionary(c => c, c => 0); - purpleUpCounter = gachaEvent.UpPurpleList.Select(id => (IStatisticsItemSource?)context.IdAvatarMap.GetValueOrDefault(id) ?? context.IdWeaponMap[id]).ToDictionary(c => c, c => 0); + orangeUpCounter = gachaEvent.UpOrangeList.Select(id => (IStatisticsItemConvertible?)context.IdAvatarMap.GetValueOrDefault(id) ?? context.IdWeaponMap[id]).ToDictionary(c => c, c => 0); + purpleUpCounter = gachaEvent.UpPurpleList.Select(id => (IStatisticsItemConvertible?)context.IdAvatarMap.GetValueOrDefault(id) ?? context.IdWeaponMap[id]).ToDictionary(c => c, c => 0); break; } } @@ -74,7 +74,7 @@ internal sealed class HistoryWishBuilder /// /// 物品 /// 是否为Up物品 - public bool IncreaseOrange(IStatisticsItemSource item) + public bool IncreaseOrange(IStatisticsItemConvertible item) { orangeCounter.IncreaseOne(item); ++totalCountTracker; @@ -86,7 +86,7 @@ internal sealed class HistoryWishBuilder /// 计数四星物品 /// /// 物品 - public void IncreasePurple(IStatisticsItemSource item) + public void IncreasePurple(IStatisticsItemConvertible item) { purpleUpCounter.TryIncreaseOne(item); purpleCounter.IncreaseOne(item); @@ -97,7 +97,7 @@ internal sealed class HistoryWishBuilder /// 计数三星武器 /// /// 武器 - public void IncreaseBlue(IStatisticsItemSource item) + public void IncreaseBlue(IStatisticsItemConvertible item) { blueCounter.IncreaseOne(item); ++totalCountTracker; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HutaoStatisticsFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HutaoStatisticsFactory.cs index e98ed1d1..39387960 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HutaoStatisticsFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/HutaoStatisticsFactory.cs @@ -55,7 +55,7 @@ internal sealed class HutaoStatisticsFactory foreach (ref readonly ItemCount item in CollectionsMarshal.AsSpan(items)) { - IStatisticsItemSource source = item.Item.StringLength() switch + IStatisticsItemConvertible source = item.Item.StringLength() switch { 8U => context.GetAvatar(item.Item), 5U => context.GetWeapon(item.Item), diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs index 067c739a..1bed7e2e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs @@ -44,7 +44,7 @@ internal sealed class TypedWishSummaryBuilder /// 祈愿物品 /// 对应武器 /// 是否为Up物品 - public void Track(GachaItem item, ISummaryItemSource source, bool isUp) + public void Track(GachaItem item, ISummaryItemConvertible source, bool isUp) { if (!context.TypeEvaluator(item.GachaType)) { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceMetadataContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceMetadataContext.cs index e7dd7207..08c6bd73 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceMetadataContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceMetadataContext.cs @@ -59,7 +59,7 @@ internal sealed class GachaLogServiceMetadataContext : IMetadataContext, return result; } - public INameQuality GetNameQualityByItemId(uint id) + public INameQualityAccess GetNameQualityByItemId(uint id) { uint place = id.StringLength(); return place switch diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml index 843d4cbf..548ea944 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml @@ -571,7 +571,7 @@ Grid.Column="0" ItemTemplate="{StaticResource CollocationTemplate}" ItemsPanel="{StaticResource StackPanelSpacing4Template}" - ItemsSource="{Binding Selected.Collocation.Avatars}"/> + ItemsSource="{Binding Selected.CollocationView.Avatars}"/> + ItemsSource="{Binding Selected.CollocationView.Weapons}"/> + ItemsSource="{Binding Selected.CollocationView.ReliquarySets}"/> diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml index 0e9d5d43..51d7054c 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml @@ -336,7 +336,7 @@ - + idMaterialMap.GetValueOrDefault(i, Material.Default)); } diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs index 78673e7c..b6ed7362 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs @@ -141,7 +141,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel foreach (Weapon weapon in weapons) { - weapon.Collocation = hutaoCache.WeaponCollocations.GetValueOrDefault(weapon.Id); + weapon.CollocationView = hutaoCache.WeaponCollocations.GetValueOrDefault(weapon.Id); weapon.CultivationItemsView ??= weapon.CultivationItems.SelectList(i => idMaterialMap.GetValueOrDefault(i, Material.Default)); } } From 05c3a575bc875477314fef08f4e77034af53ca00 Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Mon, 10 Jun 2024 23:03:23 +0800 Subject: [PATCH 14/61] adjust db service parameter --- .../Service/Cultivation/CultivationService.cs | 2 +- .../Service/Inventory/IInventoryDbService.cs | 2 +- .../Service/Inventory/InventoryDbService.cs | 4 ++-- .../Takumi/Event/Calculate/CalculateClient.cs | 13 ++++++------- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs index 509a26db..114b61ee 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs @@ -293,7 +293,7 @@ internal sealed partial class CultivationService : ICultivationService if (batchConsumption is { OverallConsume: { } items }) { - await inventoryDbService.RemoveInventoryItemRangeByProjectId(project.InnerId, true).ConfigureAwait(false); + await inventoryDbService.RemoveInventoryItemRangeByProjectId(project.InnerId).ConfigureAwait(false); await inventoryDbService.AddInventoryItemRangeByProjectId(items.SelectList(item => InventoryItem.From(project.InnerId, item.Id, (uint)((int)item.Num - item.LackNum)))).ConfigureAwait(false); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs index db4c8c53..14430ec9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs @@ -9,7 +9,7 @@ internal interface IInventoryDbService { ValueTask AddInventoryItemRangeByProjectId(List items); - ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId, bool includeMora = false); + ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId); void UpdateInventoryItem(InventoryItem item); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs index 67a66ec2..6f1b6d50 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs @@ -14,14 +14,14 @@ internal sealed partial class InventoryDbService : IInventoryDbService { private readonly IServiceProvider serviceProvider; - public async ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId, bool includeMora = false) + public async ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId) { using (IServiceScope scope = serviceProvider.CreateScope()) { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); await appDbContext.InventoryItems .AsNoTracking() - .Where(a => a.ProjectId == projectId && (includeMora || a.ItemId != 202U)) // 摩拉 + .Where(a => a.ProjectId == projectId) .ExecuteDeleteAsync() .ConfigureAwait(false); } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs index febbceca..a2fae462 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Event/Calculate/CalculateClient.cs @@ -19,6 +19,7 @@ internal sealed partial class CalculateClient private readonly ILogger logger; private readonly HttpClient httpClient; + [Obsolete("Use BatchComputeAsync instead")] public async ValueTask> ComputeAsync(Model.Entity.User user, AvatarPromotionDelta delta, CancellationToken token = default) { HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() @@ -34,20 +35,18 @@ internal sealed partial class CalculateClient return Response.Response.DefaultIfNull(resp); } - public async ValueTask> BatchComputeAsync(UserAndUid userAndUid, AvatarPromotionDelta delta, bool syncMaterials = false, CancellationToken token = default) + public async ValueTask> BatchComputeAsync(UserAndUid userAndUid, AvatarPromotionDelta delta, bool syncInventory = false, CancellationToken token = default) { - return await BatchComputeAsync(userAndUid, [delta], syncMaterials, token).ConfigureAwait(false); + return await BatchComputeAsync(userAndUid, [delta], syncInventory, token).ConfigureAwait(false); } - public async ValueTask> BatchComputeAsync(UserAndUid userAndUid, List deltas, bool syncMaterials = false, CancellationToken token = default) + public async ValueTask> BatchComputeAsync(UserAndUid userAndUid, List deltas, bool syncInventory = false, CancellationToken token = default) { - //ArgumentOutOfRangeException.ThrowIfGreaterThan(deltas.Count, 8); - BatchConsumptionData data = new() { Items = deltas, - Region = syncMaterials ? userAndUid.Uid.Region : default!, - Uid = syncMaterials ? userAndUid.Uid.ToString() : default!, + Region = syncInventory ? userAndUid.Uid.Region : default!, + Uid = syncInventory ? userAndUid.Uid.ToString() : default!, }; HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() From 905454eb02384923d9e6eeff15969b528c6eb936 Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Mon, 10 Jun 2024 23:31:38 +0800 Subject: [PATCH 15/61] refactor --- .../AvatarProperty/AvatarPropertyViewModel.cs | 38 ++++++++----------- .../ViewModel/Wiki/WikiAvatarViewModel.cs | 6 +-- .../ViewModel/Wiki/WikiWeaponViewModel.cs | 6 +-- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs index e6f1c8ef..2a839600 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs @@ -197,19 +197,14 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I return; } - Response consumptionResponse = await calculatorClient.BatchComputeAsync(userAndUid, delta).ConfigureAwait(false); + Response response = await calculatorClient.BatchComputeAsync(userAndUid, delta).ConfigureAwait(false); - if (!consumptionResponse.IsOk()) + if (!response.IsOk()) { return; } - CalculatorBatchConsumption batchConsumption = consumptionResponse.Data; - - CalculatorConsumption? consumption = batchConsumption.Items.FirstOrDefault(); - ArgumentNullException.ThrowIfNull(consumption); - - if (!await SaveCultivationAsync(consumption, delta).ConfigureAwait(false)) + if (!await SaveCultivationAsync(response.Data.Items.Single(), delta).ConfigureAwait(false)) { infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning); return; @@ -246,9 +241,10 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I ContentDialog progressDialog = await contentDialogFactory .CreateForIndeterminateProgressAsync(SH.ViewModelAvatarPropertyBatchCultivateProgressTitle) .ConfigureAwait(false); + + BatchCultivateResult result = default; using (await progressDialog.BlockAsync(taskContext).ConfigureAwait(false)) { - BatchCultivateResult result = default; List deltas = []; foreach (AvatarView avatar in avatars) { @@ -261,16 +257,14 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I deltas.Add(copy); } - Response consumptionResponse = await calculatorClient.BatchComputeAsync(userAndUid, deltas).ConfigureAwait(false); + Response response = await calculatorClient.BatchComputeAsync(userAndUid, deltas).ConfigureAwait(false); - if (!consumptionResponse.IsOk()) + if (!response.IsOk()) { return; } - CalculatorBatchConsumption batchConsumption = consumptionResponse.Data; - - foreach ((CalculatorConsumption consumption, CalculatorAvatarPromotionDelta delta) in batchConsumption.Items.Zip(deltas)) + foreach ((CalculatorConsumption consumption, CalculatorAvatarPromotionDelta delta) in response.Data.Items.Zip(deltas)) { if (!await SaveCultivationAsync(consumption, delta).ConfigureAwait(false)) { @@ -280,15 +274,15 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I ++result.SucceedCount; } + } - if (result.Interrupted) - { - infoBarService.Warning(SH.FormatViewModelCultivationBatchAddIncompletedFormat(result.SucceedCount, result.SkippedCount)); - } - else - { - infoBarService.Success(SH.FormatViewModelCultivationBatchAddCompletedFormat(result.SucceedCount, result.SkippedCount)); - } + if (result.Interrupted) + { + infoBarService.Warning(SH.FormatViewModelCultivationBatchAddIncompletedFormat(result.SucceedCount, result.SkippedCount)); + } + else + { + infoBarService.Success(SH.FormatViewModelCultivationBatchAddCompletedFormat(result.SucceedCount, result.SkippedCount)); } } diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs index 086cca59..2a7f7b9f 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs @@ -177,16 +177,16 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel return; } - Response consumptionResponse = await calculateClient + Response response = await calculateClient .BatchComputeAsync(userAndUid, delta) .ConfigureAwait(false); - if (!consumptionResponse.IsOk()) + if (!response.IsOk()) { return; } - CalculateBatchConsumption batchConsumption = consumptionResponse.Data; + CalculateBatchConsumption batchConsumption = response.Data; LevelInformation levelInformation = LevelInformation.From(delta); try { diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs index b6ed7362..a90604e2 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs @@ -170,16 +170,16 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel return; } - Response consumptionResponse = await calculateClient + Response response = await calculateClient .BatchComputeAsync(userAndUid, delta) .ConfigureAwait(false); - if (!consumptionResponse.IsOk()) + if (!response.IsOk()) { return; } - CalculateBatchConsumption batchConsumption = consumptionResponse.Data; + CalculateBatchConsumption batchConsumption = response.Data; LevelInformation levelInformation = LevelInformation.From(delta); try { From f0bfea51cf2e05fdc8151eebf0d86db513427d84 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Tue, 11 Jun 2024 00:06:01 +0800 Subject: [PATCH 16/61] move to inventory service --- .../Cultivation/CultivationDbService.cs | 10 - .../Service/Cultivation/CultivationService.cs | 173 +---------------- .../Cultivation/ICultivationDbService.cs | 7 +- .../Cultivation/ICultivationService.cs | 10 - .../Service/Inventory/IInventoryDbService.cs | 13 +- .../Service/Inventory/IInventoryService.cs | 9 + .../Service/Inventory/InventoryDbService.cs | 50 ++--- .../Service/Inventory/InventoryService.cs | 182 +++++++++++++++++- .../Cultivation/CultivationViewModel.cs | 8 +- 9 files changed, 225 insertions(+), 237 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationDbService.cs index c7d5934e..34eca1e2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationDbService.cs @@ -16,16 +16,6 @@ internal sealed partial class CultivationDbService : ICultivationDbService public IServiceProvider ServiceProvider { get => serviceProvider; } - public List GetInventoryItemListByProjectId(Guid projectId) - { - return this.List(i => i.ProjectId == projectId); - } - - public ValueTask> GetInventoryItemListByProjectIdAsync(Guid projectId, CancellationToken token = default) - { - return this.ListAsync(i => i.ProjectId == projectId, token); - } - public ValueTask> GetCultivateEntryListByProjectIdAsync(Guid projectId, CancellationToken token = default) { return this.ListAsync(e => e.ProjectId == projectId, token); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs index 114b61ee..9f476741 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs @@ -2,26 +2,13 @@ // Licensed under the MIT license. using Snap.Hutao.Core.Database; -using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity.Primitive; -using Snap.Hutao.Model.Metadata.Abstraction; -using Snap.Hutao.Model.Metadata.Item; -using Snap.Hutao.Model.Primitive; using Snap.Hutao.Service.Inventory; -using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.Metadata.ContextAbstraction; -using Snap.Hutao.Service.Notification; -using Snap.Hutao.Service.User; using Snap.Hutao.ViewModel.Cultivation; -using Snap.Hutao.ViewModel.User; -using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate; -using Snap.Hutao.Web.Response; using System.Collections.ObjectModel; -using System.Runtime.InteropServices; using CalculateItem = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Item; -using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar; -using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon; using ModelItem = Snap.Hutao.Model.Item; namespace Snap.Hutao.Service.Cultivation; @@ -36,11 +23,7 @@ internal sealed partial class CultivationService : ICultivationService { private readonly ScopedDbCurrent dbCurrent; private readonly ICultivationDbService cultivationDbService; - private readonly IServiceScopeFactory serviceScopeFactory; private readonly IInventoryDbService inventoryDbService; - private readonly IMetadataService metadataService; - private readonly IInfoBarService infoBarService; - private readonly IUserService userService; private readonly ITaskContext taskContext; private ObservableCollection? projects; @@ -67,22 +50,6 @@ internal sealed partial class CultivationService : ICultivationService } } - /// - public List GetInventoryItemViews(CultivateProject cultivateProject, ICultivationMetadataContext context, ICommand saveCommand) - { - Guid projectId = cultivateProject.InnerId; - List entities = cultivationDbService.GetInventoryItemListByProjectId(projectId); - - List results = []; - foreach (Material meta in context.EnumerateInventoryMaterial()) - { - InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.From(projectId, meta.Id); - results.Add(new(entity, meta, saveCommand)); - } - - return results; - } - /// public async ValueTask> GetCultivateEntriesAsync(CultivateProject cultivateProject, ICultivationMetadataContext context) { @@ -146,7 +113,7 @@ internal sealed partial class CultivationService : ICultivationService } } - foreach (InventoryItem inventoryItem in await cultivationDbService.GetInventoryItemListByProjectIdAsync(projectId, token).ConfigureAwait(false)) + foreach (InventoryItem inventoryItem in await inventoryDbService.GetInventoryItemListByProjectIdAsync(projectId, token).ConfigureAwait(false)) { if (resultItems.SingleOrDefault(i => i.Inner.Id == inventoryItem.ItemId) is { } existedItem) { @@ -163,12 +130,6 @@ internal sealed partial class CultivationService : ICultivationService await cultivationDbService.RemoveCultivateEntryByIdAsync(entryId).ConfigureAwait(false); } - /// - public void SaveInventoryItem(InventoryItemView item) - { - inventoryDbService.UpdateInventoryItem(item.Entity); - } - /// public void SaveCultivateItem(CultivateItemView item) { @@ -258,136 +219,4 @@ internal sealed partial class CultivationService : ICultivationService await taskContext.SwitchToBackgroundAsync(); await cultivationDbService.RemoveCultivateProjectByIdAsync(project.InnerId).ConfigureAwait(false); } - - /// - public async ValueTask RefreshInventoryAsync(CultivateProject project) - { - List cultivationItemsEntryList = - [ - .. await metadataService.GetAvatarListAsync().ConfigureAwait(false), - .. (await metadataService.GetWeaponListAsync().ConfigureAwait(false)).Where(weapon => weapon.Quality >= Model.Intrinsic.QualityType.QUALITY_BLUE), - ]; - - BatchConsumption? batchConsumption = default; - using (IServiceScope scope = serviceScopeFactory.CreateScope()) - { - if (!UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid)) - { - infoBarService.Warning(SH.MustSelectUserAndUid); - return; - } - - CalculateClient calculateClient = scope.ServiceProvider.GetRequiredService(); - - Response? resp = await calculateClient - .BatchComputeAsync(userAndUid, GeneratePromotionDeltas(cultivationItemsEntryList), true) - .ConfigureAwait(false); - - if (!resp.IsOk()) - { - return; - } - - batchConsumption = resp.Data; - } - - if (batchConsumption is { OverallConsume: { } items }) - { - await inventoryDbService.RemoveInventoryItemRangeByProjectId(project.InnerId).ConfigureAwait(false); - await inventoryDbService.AddInventoryItemRangeByProjectId(items.SelectList(item => InventoryItem.From(project.InnerId, item.Id, (uint)((int)item.Num - item.LackNum)))).ConfigureAwait(false); - } - } - - private static List GeneratePromotionDeltas(List cultivatables) - { - List avatars = []; - List weapons = []; - HashSet materialIds = []; - - while (cultivatables.Count > 0) - { - ICultivationItemsAccess bestItem = cultivatables.OrderByDescending(item => item.CultivationItems.Count(material => !materialIds.Contains(material))).First(); - - if (bestItem.CultivationItems.All(materialIds.Contains)) - { - break; - } - - switch (bestItem) - { - case MetadataAvatar avatar: - avatars.Add(avatar); - break; - case MetadataWeapon weapon: - weapons.Add(weapon); - break; - default: - throw HutaoException.NotSupported(); - } - - foreach (ref readonly MaterialId materialId in CollectionsMarshal.AsSpan(bestItem.CultivationItems)) - { - materialIds.Add(materialId); - } - - cultivatables.Remove(bestItem); - } - - List deltas = []; - - for (int i = 0; i < Math.Max(avatars.Count, weapons.Count); i++) - { - MetadataAvatar? avatar = avatars.ElementAtOrDefault(i); - MetadataWeapon? weapon = weapons.ElementAtOrDefault(i); - - if (avatar is not null) - { - AvatarPromotionDelta delta = new() - { - AvatarId = avatar.Id, - AvatarLevelCurrent = 1, - AvatarLevelTarget = 90, - SkillList = avatar.SkillDepot.CompositeSkillsNoInherents().SelectList(skill => new PromotionDelta() - { - Id = skill.GroupId, - LevelCurrent = 1, - LevelTarget = 10, - }), - }; - - if (weapon is not null) - { - delta.Weapon = new() - { - Id = weapon.Id, - LevelCurrent = 1, - LevelTarget = 90, - }; - } - - deltas.Add(delta); - - continue; - } - - if (weapon is not null) - { - AvatarPromotionDelta delta = new() - { - Weapon = new() - { - Id = weapon.Id, - LevelCurrent = 1, - LevelTarget = 90, - }, - }; - - deltas.Add(delta); - - continue; - } - } - - return deltas; - } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationDbService.cs index c8173b50..d54dabec 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationDbService.cs @@ -7,8 +7,7 @@ using System.Collections.ObjectModel; namespace Snap.Hutao.Service.Cultivation; -internal interface ICultivationDbService : IAppDbService, - IAppDbService, +internal interface ICultivationDbService : IAppDbService, IAppDbService, IAppDbService, IAppDbService @@ -29,10 +28,6 @@ internal interface ICultivationDbService : IAppDbService, ObservableCollection GetCultivateProjectCollection(); - List GetInventoryItemListByProjectId(Guid projectId); - - ValueTask> GetInventoryItemListByProjectIdAsync(Guid projectId, CancellationToken token = default); - ValueTask AddCultivateEntryAsync(CultivateEntry entry, CancellationToken token = default); ValueTask AddCultivateItemRangeAsync(IEnumerable toAdd, CancellationToken token = default); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs index 9d2b7c55..42cb5211 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs @@ -27,8 +27,6 @@ internal interface ICultivationService ValueTask> GetCultivateEntriesAsync(CultivateProject cultivateProject, ICultivationMetadataContext context); - List GetInventoryItemViews(CultivateProject cultivateProject, ICultivationMetadataContext context, ICommand saveCommand); - ValueTask> GetStatisticsCultivateItemCollectionAsync( CultivateProject cultivateProject, ICultivationMetadataContext context, CancellationToken token); @@ -54,18 +52,10 @@ internal interface ICultivationService /// 养成物品 void SaveCultivateItem(CultivateItemView item); - /// - /// 保存单个物品 - /// - /// 物品 - void SaveInventoryItem(InventoryItemView item); - /// /// 异步尝试添加新的项目 /// /// 项目 /// 添加操作的结果 ValueTask TryAddProjectAsync(CultivateProject project); - - ValueTask RefreshInventoryAsync(CultivateProject project); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs index 14430ec9..0098c263 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryDbService.cs @@ -2,16 +2,21 @@ // Licensed under the MIT license. using Snap.Hutao.Model.Entity; +using Snap.Hutao.Service.Abstraction; namespace Snap.Hutao.Service.Inventory; -internal interface IInventoryDbService +internal interface IInventoryDbService : IAppDbService { - ValueTask AddInventoryItemRangeByProjectId(List items); + ValueTask AddInventoryItemRangeByProjectIdAsync(List items, CancellationToken token = default); - ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId); + ValueTask RemoveInventoryItemRangeByProjectIdAsync(Guid projectId, CancellationToken token = default); void UpdateInventoryItem(InventoryItem item); - ValueTask UpdateInventoryItemAsync(InventoryItem item); + ValueTask UpdateInventoryItemAsync(InventoryItem item, CancellationToken token = default); + + List GetInventoryItemListByProjectId(Guid projectId); + + ValueTask> GetInventoryItemListByProjectIdAsync(Guid projectId, CancellationToken token = default); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryService.cs index 4eb6ef0d..4278bfe7 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/IInventoryService.cs @@ -1,8 +1,17 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Service.Cultivation; +using Snap.Hutao.ViewModel.Cultivation; + namespace Snap.Hutao.Service.Inventory; internal interface IInventoryService { + List GetInventoryItemViews(CultivateProject cultivateProject, ICultivationMetadataContext context, ICommand saveCommand); + + void SaveInventoryItem(InventoryItemView item); + + ValueTask RefreshInventoryAsync(CultivateProject project); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs index 6f1b6d50..236a4544 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryDbService.cs @@ -1,10 +1,8 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Microsoft.EntityFrameworkCore; -using Snap.Hutao.Core.Database; using Snap.Hutao.Model.Entity; -using Snap.Hutao.Model.Entity.Database; +using Snap.Hutao.Service.Abstraction; namespace Snap.Hutao.Service.Inventory; @@ -14,43 +12,35 @@ internal sealed partial class InventoryDbService : IInventoryDbService { private readonly IServiceProvider serviceProvider; - public async ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId) + public IServiceProvider ServiceProvider { get => serviceProvider; } + + public async ValueTask RemoveInventoryItemRangeByProjectIdAsync(Guid projectId, CancellationToken token = default) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - await appDbContext.InventoryItems - .AsNoTracking() - .Where(a => a.ProjectId == projectId) - .ExecuteDeleteAsync() - .ConfigureAwait(false); - } + await this.DeleteAsync(i => i.ProjectId == projectId, token).ConfigureAwait(false); } - public async ValueTask AddInventoryItemRangeByProjectId(List items) + public async ValueTask AddInventoryItemRangeByProjectIdAsync(List items, CancellationToken token = default) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - await appDbContext.InventoryItems.AddRangeAndSaveAsync(items).ConfigureAwait(false); - } + await this.AddRangeAsync(items, token).ConfigureAwait(false); } public void UpdateInventoryItem(InventoryItem item) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - appDbContext.InventoryItems.UpdateAndSave(item); - } + this.Update(item); } - public async ValueTask UpdateInventoryItemAsync(InventoryItem item) + public async ValueTask UpdateInventoryItemAsync(InventoryItem item, CancellationToken token = default) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - await appDbContext.InventoryItems.UpdateAndSaveAsync(item).ConfigureAwait(false); - } + await this.UpdateAsync(item, token).ConfigureAwait(false); + } + + public List GetInventoryItemListByProjectId(Guid projectId) + { + return this.List(i => i.ProjectId == projectId); + } + + public ValueTask> GetInventoryItemListByProjectIdAsync(Guid projectId, CancellationToken token = default) + { + return this.ListAsync(i => i.ProjectId == projectId, token); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs index 88928948..a400fcac 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs @@ -1,9 +1,187 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Core.ExceptionService; +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Model.Metadata.Abstraction; +using Snap.Hutao.Model.Metadata.Item; +using Snap.Hutao.Model.Primitive; +using Snap.Hutao.Service.Cultivation; +using Snap.Hutao.Service.Metadata; +using Snap.Hutao.Service.Metadata.ContextAbstraction; +using Snap.Hutao.Service.Notification; +using Snap.Hutao.Service.User; +using Snap.Hutao.ViewModel.Cultivation; +using Snap.Hutao.ViewModel.User; +using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate; +using Snap.Hutao.Web.Response; +using System.Runtime.InteropServices; +using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar; +using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon; + namespace Snap.Hutao.Service.Inventory; -[Injection(InjectAs.Transient)] -internal sealed class InventoryService : IInventoryService +[ConstructorGenerated] +[Injection(InjectAs.Singleton, typeof(IInventoryService))] +internal sealed partial class InventoryService : IInventoryService { + private readonly IServiceScopeFactory serviceScopeFactory; + private readonly IInventoryDbService inventoryDbService; + private readonly IMetadataService metadataService; + private readonly IInfoBarService infoBarService; + private readonly IUserService userService; + + /// + public List GetInventoryItemViews(CultivateProject cultivateProject, ICultivationMetadataContext context, ICommand saveCommand) + { + Guid projectId = cultivateProject.InnerId; + List entities = inventoryDbService.GetInventoryItemListByProjectId(projectId); + + List results = []; + foreach (Material meta in context.EnumerateInventoryMaterial()) + { + InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.From(projectId, meta.Id); + results.Add(new(entity, meta, saveCommand)); + } + + return results; + } + + /// + public void SaveInventoryItem(InventoryItemView item) + { + inventoryDbService.UpdateInventoryItem(item.Entity); + } + + /// + public async ValueTask RefreshInventoryAsync(CultivateProject project) + { + List cultivationItemsEntryList = + [ + .. await metadataService.GetAvatarListAsync().ConfigureAwait(false), + .. (await metadataService.GetWeaponListAsync().ConfigureAwait(false)).Where(weapon => weapon.Quality >= Model.Intrinsic.QualityType.QUALITY_BLUE), + ]; + + BatchConsumption? batchConsumption = default; + using (IServiceScope scope = serviceScopeFactory.CreateScope()) + { + if (!UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid)) + { + infoBarService.Warning(SH.MustSelectUserAndUid); + return; + } + + CalculateClient calculateClient = scope.ServiceProvider.GetRequiredService(); + + Response? resp = await calculateClient + .BatchComputeAsync(userAndUid, GeneratePromotionDeltas(cultivationItemsEntryList), true) + .ConfigureAwait(false); + + if (!resp.IsOk()) + { + return; + } + + batchConsumption = resp.Data; + } + + if (batchConsumption is { OverallConsume: { } items }) + { + await inventoryDbService.RemoveInventoryItemRangeByProjectIdAsync(project.InnerId).ConfigureAwait(false); + await inventoryDbService.AddInventoryItemRangeByProjectIdAsync(items.SelectList(item => InventoryItem.From(project.InnerId, item.Id, (uint)((int)item.Num - item.LackNum)))).ConfigureAwait(false); + } + } + + private static List GeneratePromotionDeltas(List cultivatables) + { + List avatars = []; + List weapons = []; + HashSet materialIds = []; + + while (cultivatables.Count > 0) + { + ICultivationItemsAccess bestItem = cultivatables.OrderByDescending(item => item.CultivationItems.Count(material => !materialIds.Contains(material))).First(); + + if (bestItem.CultivationItems.All(materialIds.Contains)) + { + break; + } + + switch (bestItem) + { + case MetadataAvatar avatar: + avatars.Add(avatar); + break; + case MetadataWeapon weapon: + weapons.Add(weapon); + break; + default: + throw HutaoException.NotSupported(); + } + + foreach (ref readonly MaterialId materialId in CollectionsMarshal.AsSpan(bestItem.CultivationItems)) + { + materialIds.Add(materialId); + } + + cultivatables.Remove(bestItem); + } + + List deltas = []; + + for (int i = 0; i < Math.Max(avatars.Count, weapons.Count); i++) + { + MetadataAvatar? avatar = avatars.ElementAtOrDefault(i); + MetadataWeapon? weapon = weapons.ElementAtOrDefault(i); + + if (avatar is not null) + { + AvatarPromotionDelta delta = new() + { + AvatarId = avatar.Id, + AvatarLevelCurrent = 1, + AvatarLevelTarget = 90, + SkillList = avatar.SkillDepot.CompositeSkillsNoInherents().SelectList(skill => new PromotionDelta() + { + Id = skill.GroupId, + LevelCurrent = 1, + LevelTarget = 10, + }), + }; + + if (weapon is not null) + { + delta.Weapon = new() + { + Id = weapon.Id, + LevelCurrent = 1, + LevelTarget = 90, + }; + } + + deltas.Add(delta); + + continue; + } + + if (weapon is not null) + { + AvatarPromotionDelta delta = new() + { + Weapon = new() + { + Id = weapon.Id, + LevelCurrent = 1, + LevelTarget = 90, + }, + }; + + deltas.Add(delta); + + continue; + } + } + + return deltas; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs index 09f00f76..0fe1ee39 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs @@ -7,6 +7,7 @@ using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Factory.ContentDialog; using Snap.Hutao.Model.Entity; using Snap.Hutao.Service.Cultivation; +using Snap.Hutao.Service.Inventory; using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.Metadata.ContextAbstraction; using Snap.Hutao.Service.Navigation; @@ -30,6 +31,7 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel private readonly ICultivationService cultivationService; private readonly ILogger logger; private readonly INavigationService navigationService; + private readonly IInventoryService inventoryService; private readonly IMetadataService metadataService; private readonly IInfoBarService infoBarService; private readonly ITaskContext taskContext; @@ -174,7 +176,7 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel { if (inventoryItem is not null) { - cultivationService.SaveInventoryItem(inventoryItem); + inventoryService.SaveInventoryItem(inventoryItem); await UpdateStatisticsItemsAsync().ConfigureAwait(false); } } @@ -195,7 +197,7 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel using (await dialog.BlockAsync(taskContext).ConfigureAwait(false)) { - await cultivationService.RefreshInventoryAsync(SelectedProject).ConfigureAwait(false); + await inventoryService.RefreshInventoryAsync(SelectedProject).ConfigureAwait(false); await UpdateInventoryItemsAsync().ConfigureAwait(false); await UpdateStatisticsItemsAsync().ConfigureAwait(false); @@ -234,7 +236,7 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel CultivationMetadataContext context = await metadataService.GetContextAsync().ConfigureAwait(false); await taskContext.SwitchToMainThreadAsync(); - InventoryItems = cultivationService.GetInventoryItemViews(SelectedProject, context, SaveInventoryItemCommand); + InventoryItems = inventoryService.GetInventoryItemViews(SelectedProject, context, SaveInventoryItemCommand); } } From 9ef0d8c57d0b930e29305b825d3e6da5cc0d0221 Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Tue, 11 Jun 2024 12:31:51 +0800 Subject: [PATCH 17/61] add SCIP solver --- .../Abstraction/ICultivationItemsAccess.cs | 4 +- .../Service/Inventory/InventoryService.cs | 4 +- .../Inventory/MinimalPromotionDelta.cs | 57 +++++++++++++++++++ src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj | 1 + 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ICultivationItemsAccess.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ICultivationItemsAccess.cs index 050f1ba8..dd1fab0b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ICultivationItemsAccess.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ICultivationItemsAccess.cs @@ -7,5 +7,7 @@ namespace Snap.Hutao.Model.Metadata.Abstraction; internal interface ICultivationItemsAccess { + string Name { get; } + List CultivationItems { get; } -} +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs index a400fcac..5452b29a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs @@ -59,9 +59,11 @@ internal sealed partial class InventoryService : IInventoryService List cultivationItemsEntryList = [ .. await metadataService.GetAvatarListAsync().ConfigureAwait(false), - .. (await metadataService.GetWeaponListAsync().ConfigureAwait(false)).Where(weapon => weapon.Quality >= Model.Intrinsic.QualityType.QUALITY_BLUE), + .. await metadataService.GetWeaponListAsync().ConfigureAwait(false), ]; + cultivationItemsEntryList = MinimalPromotionDelta.Find(cultivationItemsEntryList); + BatchConsumption? batchConsumption = default; using (IServiceScope scope = serviceScopeFactory.CreateScope()) { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs new file mode 100644 index 00000000..7ea09766 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs @@ -0,0 +1,57 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Google.OrTools.LinearSolver; +using Snap.Hutao.Core.ExceptionService; +using Snap.Hutao.Model.Metadata.Abstraction; +using Snap.Hutao.Model.Primitive; +using System.Runtime.InteropServices; + +namespace Snap.Hutao.Service.Inventory; + +internal static class MinimalPromotionDelta +{ + public static List Find(List cultivationItems) + { + using (Solver? solver = Solver.CreateSolver("SCIP")) + { + ArgumentNullException.ThrowIfNull(solver); + + Objective objective = solver.Objective(); + objective.SetMinimization(); + + Dictionary itemVariableMap = []; + foreach (ref readonly ICultivationItemsAccess item in CollectionsMarshal.AsSpan(cultivationItems)) + { + Variable variable = solver.MakeBoolVar(item.Name); + itemVariableMap[item] = variable; + objective.SetCoefficient(variable, 1); + } + + Dictionary materialConstraintMap = []; + foreach (ref readonly ICultivationItemsAccess item in CollectionsMarshal.AsSpan(cultivationItems)) + { + foreach (ref readonly MaterialId materialId in CollectionsMarshal.AsSpan(item.CultivationItems)) + { + ref Constraint? constraint = ref CollectionsMarshal.GetValueRefOrAddDefault(materialConstraintMap, materialId, out _); + constraint ??= solver.MakeConstraint(1, double.PositiveInfinity, $"{materialId}"); + constraint.SetCoefficient(itemVariableMap[item], 1); + } + } + + Solver.ResultStatus status = solver.Solve(); + HutaoException.ThrowIf(status != Solver.ResultStatus.OPTIMAL, "Unable to solve minimal item set"); + + List results = []; + foreach ((ICultivationItemsAccess item, Variable variable) in itemVariableMap) + { + if (variable.SolutionValue() > 0.5) + { + results.Add(item); + } + } + + return results; + } + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 40283196..8c589fbd 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -313,6 +313,7 @@ + all From 5f4b68d5382375647fa39c7a9a4d65b384b1f8a7 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Tue, 11 Jun 2024 12:55:54 +0800 Subject: [PATCH 18/61] add cache to minimal deltas --- .../Service/Inventory/InventoryService.cs | 112 +----------------- .../Inventory/MinimalPromotionDelta.cs | 83 ++++++++++++- 2 files changed, 84 insertions(+), 111 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs index 5452b29a..9dea0796 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs @@ -1,13 +1,9 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Model.Entity; -using Snap.Hutao.Model.Metadata.Abstraction; using Snap.Hutao.Model.Metadata.Item; -using Snap.Hutao.Model.Primitive; using Snap.Hutao.Service.Cultivation; -using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.Metadata.ContextAbstraction; using Snap.Hutao.Service.Notification; using Snap.Hutao.Service.User; @@ -15,9 +11,6 @@ using Snap.Hutao.ViewModel.Cultivation; using Snap.Hutao.ViewModel.User; using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate; using Snap.Hutao.Web.Response; -using System.Runtime.InteropServices; -using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar; -using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon; namespace Snap.Hutao.Service.Inventory; @@ -25,9 +18,9 @@ namespace Snap.Hutao.Service.Inventory; [Injection(InjectAs.Singleton, typeof(IInventoryService))] internal sealed partial class InventoryService : IInventoryService { + private readonly MinimalPromotionDelta minimalPromotionDelta; private readonly IServiceScopeFactory serviceScopeFactory; private readonly IInventoryDbService inventoryDbService; - private readonly IMetadataService metadataService; private readonly IInfoBarService infoBarService; private readonly IUserService userService; @@ -56,13 +49,7 @@ internal sealed partial class InventoryService : IInventoryService /// public async ValueTask RefreshInventoryAsync(CultivateProject project) { - List cultivationItemsEntryList = - [ - .. await metadataService.GetAvatarListAsync().ConfigureAwait(false), - .. await metadataService.GetWeaponListAsync().ConfigureAwait(false), - ]; - - cultivationItemsEntryList = MinimalPromotionDelta.Find(cultivationItemsEntryList); + List deltas = await minimalPromotionDelta.GetAsync().ConfigureAwait(false); BatchConsumption? batchConsumption = default; using (IServiceScope scope = serviceScopeFactory.CreateScope()) @@ -76,7 +63,7 @@ internal sealed partial class InventoryService : IInventoryService CalculateClient calculateClient = scope.ServiceProvider.GetRequiredService(); Response? resp = await calculateClient - .BatchComputeAsync(userAndUid, GeneratePromotionDeltas(cultivationItemsEntryList), true) + .BatchComputeAsync(userAndUid, deltas, true) .ConfigureAwait(false); if (!resp.IsOk()) @@ -93,97 +80,4 @@ internal sealed partial class InventoryService : IInventoryService await inventoryDbService.AddInventoryItemRangeByProjectIdAsync(items.SelectList(item => InventoryItem.From(project.InnerId, item.Id, (uint)((int)item.Num - item.LackNum)))).ConfigureAwait(false); } } - - private static List GeneratePromotionDeltas(List cultivatables) - { - List avatars = []; - List weapons = []; - HashSet materialIds = []; - - while (cultivatables.Count > 0) - { - ICultivationItemsAccess bestItem = cultivatables.OrderByDescending(item => item.CultivationItems.Count(material => !materialIds.Contains(material))).First(); - - if (bestItem.CultivationItems.All(materialIds.Contains)) - { - break; - } - - switch (bestItem) - { - case MetadataAvatar avatar: - avatars.Add(avatar); - break; - case MetadataWeapon weapon: - weapons.Add(weapon); - break; - default: - throw HutaoException.NotSupported(); - } - - foreach (ref readonly MaterialId materialId in CollectionsMarshal.AsSpan(bestItem.CultivationItems)) - { - materialIds.Add(materialId); - } - - cultivatables.Remove(bestItem); - } - - List deltas = []; - - for (int i = 0; i < Math.Max(avatars.Count, weapons.Count); i++) - { - MetadataAvatar? avatar = avatars.ElementAtOrDefault(i); - MetadataWeapon? weapon = weapons.ElementAtOrDefault(i); - - if (avatar is not null) - { - AvatarPromotionDelta delta = new() - { - AvatarId = avatar.Id, - AvatarLevelCurrent = 1, - AvatarLevelTarget = 90, - SkillList = avatar.SkillDepot.CompositeSkillsNoInherents().SelectList(skill => new PromotionDelta() - { - Id = skill.GroupId, - LevelCurrent = 1, - LevelTarget = 10, - }), - }; - - if (weapon is not null) - { - delta.Weapon = new() - { - Id = weapon.Id, - LevelCurrent = 1, - LevelTarget = 90, - }; - } - - deltas.Add(delta); - - continue; - } - - if (weapon is not null) - { - AvatarPromotionDelta delta = new() - { - Weapon = new() - { - Id = weapon.Id, - LevelCurrent = 1, - LevelTarget = 90, - }, - }; - - deltas.Add(delta); - - continue; - } - } - - return deltas; - } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs index 7ea09766..91b3ee84 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs @@ -2,16 +2,43 @@ // Licensed under the MIT license. using Google.OrTools.LinearSolver; +using Microsoft.Extensions.Caching.Memory; using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Model.Metadata.Abstraction; using Snap.Hutao.Model.Primitive; +using Snap.Hutao.Service.Metadata; +using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate; using System.Runtime.InteropServices; +using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar; +using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon; namespace Snap.Hutao.Service.Inventory; -internal static class MinimalPromotionDelta +[ConstructorGenerated] +[Injection(InjectAs.Singleton)] +internal sealed partial class MinimalPromotionDelta { - public static List Find(List cultivationItems) + private const string CacheKey = $"{nameof(MinimalPromotionDelta)}.Cache"; + + private readonly IMetadataService metadataService; + private readonly IMemoryCache memoryCache; + + public async ValueTask> GetAsync() + { + if (memoryCache.TryGetRequiredValue(CacheKey, out List? cache)) + { + return cache; + } + + List cultivationItemsEntryList = + [ + .. await metadataService.GetAvatarListAsync().ConfigureAwait(false), + .. (await metadataService.GetWeaponListAsync().ConfigureAwait(false)).Where(w => w.Quality >= Model.Intrinsic.QualityType.QUALITY_BLUE), + ]; + return memoryCache.Set(CacheKey, GeneratePromotionDeltas(Minimize(cultivationItemsEntryList))); + } + + private static List Minimize(List cultivationItems) { using (Solver? solver = Solver.CreateSolver("SCIP")) { @@ -54,4 +81,56 @@ internal static class MinimalPromotionDelta return results; } } + + private static List GeneratePromotionDeltas(List cultivationItems) + { + List deltas = []; + + foreach (ref readonly ICultivationItemsAccess item in CollectionsMarshal.AsSpan(cultivationItems)) + { + switch (item) + { + case MetadataAvatar avatar: + deltas.Add(new() + { + AvatarId = avatar.Id, + AvatarLevelCurrent = 1, + AvatarLevelTarget = 90, + SkillList = avatar.SkillDepot.CompositeSkillsNoInherents().SelectList(skill => new PromotionDelta() + { + Id = skill.GroupId, + LevelCurrent = 1, + LevelTarget = 10, + }), + }); + break; + case MetadataWeapon weapon: + if (deltas.FirstOrDefault(d => d.Weapon is null) is { } delta) + { + delta.Weapon = new() + { + Id = weapon.Id, + LevelCurrent = 1, + LevelTarget = 90, + }; + + break; + } + + deltas.Add(new() + { + Weapon = new() + { + Id = weapon.Id, + LevelCurrent = 1, + LevelTarget = 90, + }, + }); + + break; + } + } + + return deltas; + } } \ No newline at end of file From 23741c4e4816b379b000b798aead7ed449c39e55 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Tue, 11 Jun 2024 13:12:37 +0800 Subject: [PATCH 19/61] exclude unavailable avatars --- src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs | 1 - .../Core/IO/Http/Proxy/HttpProxyUsingSystemProxy.cs | 1 - .../Snap.Hutao/Service/Announcement/AnnouncementService.cs | 2 +- .../Snap.Hutao/Service/Game/Unlocker/GameFpsAddress.cs | 1 - .../Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs | 2 +- src/Snap.Hutao/Snap.Hutao/ViewModel/TestViewModel.cs | 1 - .../Win32/System/SystemInformation/IMAGE_FILE_MACHINE.cs | 6 ------ 7 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs b/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs index 3efa4c37..76de5d85 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs @@ -5,7 +5,6 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Markup; using Microsoft.UI.Xaml.Navigation; -using Snap.Hutao.Core.Abstraction; using Snap.Hutao.Service.Navigation; using Snap.Hutao.View.Helper; using Snap.Hutao.ViewModel.Abstraction; diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Proxy/HttpProxyUsingSystemProxy.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Proxy/HttpProxyUsingSystemProxy.cs index b0169e6c..ba9078e1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Proxy/HttpProxyUsingSystemProxy.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Proxy/HttpProxyUsingSystemProxy.cs @@ -3,7 +3,6 @@ using CommunityToolkit.Mvvm.ComponentModel; using Snap.Hutao.Win32.Registry; -using System.Linq.Expressions; using System.Net; using System.Reflection; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Announcement/AnnouncementService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Announcement/AnnouncementService.cs index eae01ea0..13dfb710 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Announcement/AnnouncementService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Announcement/AnnouncementService.cs @@ -20,7 +20,7 @@ namespace Snap.Hutao.Service; [Injection(InjectAs.Scoped, typeof(IAnnouncementService))] internal sealed partial class AnnouncementService : IAnnouncementService { - private static readonly string CacheKey = $"{nameof(AnnouncementService)}.Cache.{nameof(AnnouncementWrapper)}"; + private const string CacheKey = $"{nameof(AnnouncementService)}.Cache.{nameof(AnnouncementWrapper)}"; private readonly IServiceScopeFactory serviceScopeFactory; private readonly ITaskContext taskContext; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsAddress.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsAddress.cs index a5f3c1e8..c35cebf9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsAddress.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsAddress.cs @@ -3,7 +3,6 @@ using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Win32.Foundation; -using Snap.Hutao.Win32.Memory; using System.Diagnostics; using static Snap.Hutao.Win32.Kernel32; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs index 91b3ee84..329ed1a5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs @@ -32,7 +32,7 @@ internal sealed partial class MinimalPromotionDelta List cultivationItemsEntryList = [ - .. await metadataService.GetAvatarListAsync().ConfigureAwait(false), + .. (await metadataService.GetAvatarListAsync().ConfigureAwait(false)).Where(a => a.BeginTime <= DateTimeOffset.Now), .. (await metadataService.GetWeaponListAsync().ConfigureAwait(false)).Where(w => w.Quality >= Model.Intrinsic.QualityType.QUALITY_BLUE), ]; return memoryCache.Set(CacheKey, GeneratePromotionDeltas(Minimize(cultivationItemsEntryList))); diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/TestViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/TestViewModel.cs index 07e90422..70bdd518 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/TestViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/TestViewModel.cs @@ -10,7 +10,6 @@ using Snap.Hutao.Core.Setting; using Snap.Hutao.Core.Windowing; using Snap.Hutao.Service.Game.Automation.ScreenCapture; using Snap.Hutao.Service.Notification; -using Snap.Hutao.View.Converter; using Snap.Hutao.ViewModel.Guide; using Snap.Hutao.Web.Hutao.HutaoAsAService; using Snap.Hutao.Win32.Foundation; diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/System/SystemInformation/IMAGE_FILE_MACHINE.cs b/src/Snap.Hutao/Snap.Hutao/Win32/System/SystemInformation/IMAGE_FILE_MACHINE.cs index 8e5bad92..d5f20a17 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/System/SystemInformation/IMAGE_FILE_MACHINE.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/System/SystemInformation/IMAGE_FILE_MACHINE.cs @@ -1,12 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Snap.Hutao.Win32.System.SystemInformation; internal enum IMAGE_FILE_MACHINE : ushort From 7cfcc17763b29a39d998296f74741efe89b80201 Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Tue, 11 Jun 2024 14:00:48 +0800 Subject: [PATCH 20/61] refactor --- .../Extension/EnumerableExtension.List.cs | 14 ++++ .../Inventory/MinimalPromotionDelta.cs | 65 +++++++++++++------ 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs index 14cab77b..ea0d0709 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs @@ -199,6 +199,13 @@ internal static partial class EnumerableExtension return list; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + public static List SortBy(this List list, Func keySelector, Comparison comparison) + { + list.Sort((left, right) => comparison(keySelector(left), keySelector(right))); + return list; + } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static List SortByDescending(this List list, Func keySelector) where TKey : IComparable @@ -213,4 +220,11 @@ internal static partial class EnumerableExtension list.Sort((left, right) => comparer.Compare(keySelector(right), keySelector(left))); return list; } + + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + public static List SortByDescending(this List list, Func keySelector, Comparison comparison) + { + list.Sort((left, right) => comparison(keySelector(right), keySelector(left))); + return list; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs index 329ed1a5..1b5066ac 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs @@ -3,6 +3,7 @@ using Google.OrTools.LinearSolver; using Microsoft.Extensions.Caching.Memory; +using Snap.Hutao.Core; using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Model.Metadata.Abstraction; using Snap.Hutao.Model.Primitive; @@ -35,7 +36,13 @@ internal sealed partial class MinimalPromotionDelta .. (await metadataService.GetAvatarListAsync().ConfigureAwait(false)).Where(a => a.BeginTime <= DateTimeOffset.Now), .. (await metadataService.GetWeaponListAsync().ConfigureAwait(false)).Where(w => w.Quality >= Model.Intrinsic.QualityType.QUALITY_BLUE), ]; - return memoryCache.Set(CacheKey, GeneratePromotionDeltas(Minimize(cultivationItemsEntryList))); + + List minimal = Minimize(cultivationItemsEntryList); + + // Gurantee the order of avatar and weapon + // Make sure weapons can have avatar to attach + minimal.Sort(CultivationItemsAccessComparer.Shared); + return memoryCache.Set(CacheKey, ToPromotionDeltaList(minimal)); } private static List Minimize(List cultivationItems) @@ -82,9 +89,10 @@ internal sealed partial class MinimalPromotionDelta } } - private static List GeneratePromotionDeltas(List cultivationItems) + private static List ToPromotionDeltaList(List cultivationItems) { List deltas = []; + int currentWeaponEmptyAvatarIndex = 0; foreach (ref readonly ICultivationItemsAccess item in CollectionsMarshal.AsSpan(cultivationItems)) { @@ -103,29 +111,27 @@ internal sealed partial class MinimalPromotionDelta LevelTarget = 10, }), }); - break; - case MetadataWeapon weapon: - if (deltas.FirstOrDefault(d => d.Weapon is null) is { } delta) - { - delta.Weapon = new() - { - Id = weapon.Id, - LevelCurrent = 1, - LevelTarget = 90, - }; - break; + break; + + case MetadataWeapon weapon: + AvatarPromotionDelta delta; + if (currentWeaponEmptyAvatarIndex < deltas.Count) + { + delta = deltas[currentWeaponEmptyAvatarIndex++]; + } + else + { + delta = new(); + deltas.Add(delta); } - deltas.Add(new() + delta.Weapon = new() { - Weapon = new() - { - Id = weapon.Id, - LevelCurrent = 1, - LevelTarget = 90, - }, - }); + Id = weapon.Id, + LevelCurrent = 1, + LevelTarget = 90, + }; break; } @@ -133,4 +139,21 @@ internal sealed partial class MinimalPromotionDelta return deltas; } + + private sealed class CultivationItemsAccessComparer : IComparer + { + private static readonly LazySlim LazyShared = new(() => new()); + + public static CultivationItemsAccessComparer Shared { get => LazyShared.Value; } + + public int Compare(ICultivationItemsAccess? x, ICultivationItemsAccess? y) + { + return (x, y) switch + { + (MetadataAvatar, MetadataWeapon) => -1, + (MetadataWeapon, MetadataAvatar) => 1, + _ => 0, + }; + } + } } \ No newline at end of file From 069407abbcf9ecdb330e631ca606fb8aedd615c2 Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Tue, 11 Jun 2024 15:06:15 +0800 Subject: [PATCH 21/61] use weapon sort --- src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs | 2 ++ .../Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs | 8 +++++++- .../Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs | 4 +--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs index 46233381..45ae638b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs @@ -24,6 +24,8 @@ internal sealed partial class Weapon : INameQualityAccess, public PromoteId PromoteId { get; set; } + public uint Sort { get; set; } + public WeaponType WeaponType { get; set; } public QualityType RankLevel { get; set; } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs index 1b5066ac..20ff1007 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs @@ -4,6 +4,7 @@ using Google.OrTools.LinearSolver; using Microsoft.Extensions.Caching.Memory; using Snap.Hutao.Core; +using Snap.Hutao.Core.Diagnostics; using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Model.Metadata.Abstraction; using Snap.Hutao.Model.Primitive; @@ -21,6 +22,7 @@ internal sealed partial class MinimalPromotionDelta { private const string CacheKey = $"{nameof(MinimalPromotionDelta)}.Cache"; + private readonly ILogger logger; private readonly IMetadataService metadataService; private readonly IMemoryCache memoryCache; @@ -37,7 +39,11 @@ internal sealed partial class MinimalPromotionDelta .. (await metadataService.GetWeaponListAsync().ConfigureAwait(false)).Where(w => w.Quality >= Model.Intrinsic.QualityType.QUALITY_BLUE), ]; - List minimal = Minimize(cultivationItemsEntryList); + List minimal; + using (ValueStopwatch.MeasureExecution(logger)) + { + minimal = Minimize(cultivationItemsEntryList); + } // Gurantee the order of avatar and weapon // Make sure weapons can have avatar to attach diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs index a90604e2..03a4cc19 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs @@ -102,9 +102,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel List weapons = await metadataService.GetWeaponListAsync().ConfigureAwait(false); IEnumerable sorted = weapons - .OrderByDescending(weapon => weapon.RankLevel) - .ThenBy(weapon => weapon.WeaponType) - .ThenByDescending(weapon => weapon.Id.Value); + .OrderByDescending(weapon => weapon.Sort); List list = [.. sorted]; await CombineComplexDataAsync(list, idMaterialMap).ConfigureAwait(false); From 99b361331944c846a13b098ca48e1666e59302e8 Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Tue, 11 Jun 2024 15:42:23 +0800 Subject: [PATCH 22/61] fix #1688 --- src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs index ab520f6b..093c38e8 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs @@ -278,7 +278,7 @@ internal static class HutaoEndpoints public const string WallpaperBing = $"{ApiSnapGenshin}/wallpaper/bing"; - public const string WallpaperGenshinLauncher = $"{ApiSnapGenshin}/wallpaper/genshin-launcher"; + public const string WallpaperGenshinLauncher = $"{ApiSnapGenshin}/wallpaper/hoyoplay"; public const string WallpaperToday = $"{ApiSnapGenshin}/wallpaper/today"; #endregion From c1b3412ba17acfab91fb1c4fc3fa768e100623d4 Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Tue, 11 Jun 2024 16:56:33 +0800 Subject: [PATCH 23/61] fix QA ComboBox width issue --- .../Snap.Hutao/Control/SizeRestrictedContentControl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Control/SizeRestrictedContentControl.cs b/src/Snap.Hutao/Snap.Hutao/Control/SizeRestrictedContentControl.cs index 587070b6..a4f26248 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/SizeRestrictedContentControl.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/SizeRestrictedContentControl.cs @@ -21,8 +21,8 @@ internal sealed partial class SizeRestrictedContentControl : ContentControl element.Measure(availableSize); Size contentDesiredSize = element.DesiredSize; Size contentActualOrDesiredSize = new( - Math.Max(element.ActualWidth, contentDesiredSize.Width), - Math.Max(element.ActualHeight, contentDesiredSize.Height)); + Math.Min(Math.Max(element.ActualWidth, contentDesiredSize.Width), availableSize.Width), + Math.Min(Math.Max(element.ActualHeight, contentDesiredSize.Height), availableSize.Height)); if (IsWidthRestricted) { From 6f3159ae0c7af17a73eb9e5e3e2f20a5b6c71173 Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Tue, 11 Jun 2024 17:01:14 +0800 Subject: [PATCH 24/61] [skip ci] QA announcement name --- src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx | 2 +- .../View/Dialog/UpdatePackageDownloadConfirmDialog.xaml.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index 6da301bb..53f17cd1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -2595,7 +2595,7 @@ 选择想要获取公告的游戏服务器 - 公告所属服务器 + 游戏公告所属服务器 管理主页仪表板中的卡片 diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/UpdatePackageDownloadConfirmDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/UpdatePackageDownloadConfirmDialog.xaml.cs index a565f816..20a536f1 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/UpdatePackageDownloadConfirmDialog.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/UpdatePackageDownloadConfirmDialog.xaml.cs @@ -11,4 +11,4 @@ internal sealed partial class UpdatePackageDownloadConfirmDialog : ContentDialog { InitializeComponent(); } -} +} \ No newline at end of file From 42764812844b09076b6c8c972e9c22003907f290 Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Tue, 11 Jun 2024 21:05:24 +0800 Subject: [PATCH 25/61] Add CachedImage Debug Layer --- .../Snap.Hutao/Control/Image/CachedImage.cs | 39 ++++++++++++++++++- .../Snap.Hutao/Control/Image/CachedImage.xaml | 9 ++++- .../View/Control/HutaoStatisticsCard.xaml | 5 ++- .../View/Page/AnnouncementPage.xaml | 2 +- .../View/Page/AvatarPropertyPage.xaml | 3 +- .../Snap.Hutao/View/Page/GachaLogPage.xaml | 5 ++- 6 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs index 0315bea3..29f48414 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs @@ -1,10 +1,15 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Microsoft.UI.Xaml.Media.Imaging; using Snap.Hutao.Control.Extension; using Snap.Hutao.Core.Caching; using Snap.Hutao.Core.ExceptionService; +using Snap.Hutao.Core.IO.DataTransfer; +using System.IO; using System.Runtime.InteropServices; +using Windows.Graphics.Imaging; +using Windows.Storage.Streams; namespace Snap.Hutao.Control.Image; @@ -12,8 +17,12 @@ namespace Snap.Hutao.Control.Image; /// 缓存图像 /// [HighQuality] -internal sealed class CachedImage : Implementation.ImageEx +[DependencyProperty("SourceName", typeof(string), "Unknown")] +[DependencyProperty("CachedName", typeof(string), "Unknown")] +internal sealed partial class CachedImage : Implementation.ImageEx { + private string? file; + /// /// 构造一个新的缓存图像 /// @@ -26,12 +35,15 @@ internal sealed class CachedImage : Implementation.ImageEx /// protected override async Task ProvideCachedResourceAsync(Uri imageUri, CancellationToken token) { + SourceName = Path.GetFileName(imageUri.ToString()); IImageCache imageCache = this.ServiceProvider().GetRequiredService(); try { HutaoException.ThrowIf(string.IsNullOrEmpty(imageUri.Host), SH.ControlImageCachedImageInvalidResourceUri); string file = await imageCache.GetFileFromCacheAsync(imageUri).ConfigureAwait(true); // BitmapImage need to be created by main thread. + CachedName = Path.GetFileName(file); + this.file = file; token.ThrowIfCancellationRequested(); // check token state to determine whether the operation should be canceled. return file.ToUri(); } @@ -42,4 +54,27 @@ internal sealed class CachedImage : Implementation.ImageEx return default; } } -} + + [Command("CopyToClipboardCommand")] + private async Task CopyToClipboard() + { + if (Image is Microsoft.UI.Xaml.Controls.Image { Source: BitmapImage bitmap }) + { + using (FileStream netStream = File.OpenRead(bitmap.UriSource.LocalPath)) + { + using (IRandomAccessStream fxStream = netStream.AsRandomAccessStream()) + { + BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fxStream); + SoftwareBitmap softwareBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied); + using (InMemoryRandomAccessStream memory = new()) + { + BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, memory); + encoder.SetSoftwareBitmap(softwareBitmap); + await encoder.FlushAsync(); + Ioc.Default.GetRequiredService().SetBitmap(memory); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.xaml b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.xaml index 372a7f5c..1d351541 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.xaml @@ -1,4 +1,4 @@ - @@ -14,6 +14,13 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="{TemplateBinding CornerRadius}"> + + + + + + + - + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml index d7c37f91..5eb1cc43 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml @@ -62,7 +62,7 @@ - + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml index f9e05085..28988c61 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml @@ -619,7 +619,8 @@ + Fill="#33000000" + IsHitTestVisible="False"/> - + From cc71aa9c82854b9168419eccc757d32d367d7f78 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Thu, 13 Jun 2024 12:51:22 +0800 Subject: [PATCH 26/61] impl #1715 --- .../Behavior/InfoBarDelayCloseBehavior.cs | 29 ++++ .../Service/Notification/IInfoBarService.cs | 3 +- .../Service/Notification/InfoBarOptions.cs | 5 +- .../InfoBarOptionsBuilderExtension.cs | 13 +- .../Service/Notification/InfoBarService.cs | 39 +----- .../Notification/InfoBarServiceExtension.cs | 9 +- .../Snap.Hutao/View/InfoBarView.xaml | 131 +++++++++++------- .../Snap.Hutao/View/InfoBarView.xaml.cs | 7 +- 8 files changed, 129 insertions(+), 107 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Control/Behavior/InfoBarDelayCloseBehavior.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Behavior/InfoBarDelayCloseBehavior.cs b/src/Snap.Hutao/Snap.Hutao/Control/Behavior/InfoBarDelayCloseBehavior.cs new file mode 100644 index 00000000..3e1970a1 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Control/Behavior/InfoBarDelayCloseBehavior.cs @@ -0,0 +1,29 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using CommunityToolkit.WinUI.Behaviors; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Snap.Hutao.Control.Behavior; + +[DependencyProperty("MilliSecondsDelay", typeof(int))] +internal sealed partial class InfoBarDelayCloseBehavior : BehaviorBase +{ + protected override void OnAssociatedObjectLoaded() + { + DelayCoreAsync().SafeForget(); + } + + private async ValueTask DelayCoreAsync() + { + if (MilliSecondsDelay > 0) + { + await Delay.FromMilliSeconds(MilliSecondsDelay).ConfigureAwait(true); + if (AssociatedObject is not null) + { + AssociatedObject.IsOpen = false; + } + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Notification/IInfoBarService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Notification/IInfoBarService.cs index 0e5d11a5..4288c75f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Notification/IInfoBarService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Notification/IInfoBarService.cs @@ -1,7 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Microsoft.UI.Xaml.Controls; using System.Collections.ObjectModel; namespace Snap.Hutao.Service.Notification; @@ -9,7 +8,7 @@ namespace Snap.Hutao.Service.Notification; [HighQuality] internal interface IInfoBarService { - ObservableCollection Collection { get; } + ObservableCollection Collection { get; } void PrepareInfoBarAndShow(Action configure); } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarOptions.cs index 70dfcccf..b6406195 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarOptions.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; namespace Snap.Hutao.Service.Notification; @@ -16,7 +15,9 @@ internal sealed class InfoBarOptions public object? Content { get; set; } - public ButtonBase? ActionButton { get; set; } + public string? ActionButtonContent { get; set; } + + public ICommand? ActionButtonCommand { get; set; } public int MilliSecondsDelay { get; set; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarOptionsBuilderExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarOptionsBuilderExtension.cs index 5e24dfbd..45296409 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarOptionsBuilderExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarOptionsBuilderExtension.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Snap.Hutao.Control.Builder.ButtonBase; using Snap.Hutao.Core.Abstraction.Extension; namespace Snap.Hutao.Service.Notification; @@ -38,20 +36,17 @@ internal static class InfoBarOptionsBuilderExtension return builder; } - public static IInfoBarOptionsBuilder SetActionButton(this TBuilder builder, Action> configureButton) + public static IInfoBarOptionsBuilder SetActionButtonContent(this TBuilder builder, string? buttonContent) where TBuilder : IInfoBarOptionsBuilder - where TButton : ButtonBase, new() { - ButtonBaseBuilder buttonBaseBuilder = new ButtonBaseBuilder().Configure(configureButton); - builder.Configure(builder => builder.Options.ActionButton = buttonBaseBuilder.Button); + builder.Configure(builder => builder.Options.ActionButtonContent = buttonContent); return builder; } - public static IInfoBarOptionsBuilder SetActionButton(this TBuilder builder, Action configureButton) + public static IInfoBarOptionsBuilder SetActionButtonCommand(this TBuilder builder, ICommand? buttonCommand) where TBuilder : IInfoBarOptionsBuilder { - ButtonBuilder buttonBaseBuilder = new ButtonBuilder().Configure(configureButton); - builder.Configure(builder => builder.Options.ActionButton = buttonBaseBuilder.Button); + builder.Configure(builder => builder.Options.ActionButtonCommand = buttonCommand); return builder; } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs index 7cc80586..57c65754 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs @@ -1,11 +1,8 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media.Animation; using Snap.Hutao.Core.Abstraction.Extension; using System.Collections.ObjectModel; -using Windows.Foundation; namespace Snap.Hutao.Service.Notification; @@ -17,20 +14,16 @@ internal sealed class InfoBarService : IInfoBarService private readonly ILogger logger; private readonly ITaskContext taskContext; - private readonly TypedEventHandler infobarClosedEventHandler; - - private ObservableCollection? collection; + private ObservableCollection? collection; public InfoBarService(IServiceProvider serviceProvider) { logger = serviceProvider.GetRequiredService>(); taskContext = serviceProvider.GetRequiredService(); - - infobarClosedEventHandler = OnInfoBarClosed; } /// - public ObservableCollection Collection + public ObservableCollection Collection { get => collection ??= []; } @@ -51,33 +44,7 @@ internal sealed class InfoBarService : IInfoBarService await taskContext.SwitchToMainThreadAsync(); - InfoBar infoBar = new() - { - Severity = builder.Options.Severity, - Title = builder.Options.Title, - Message = builder.Options.Message, - Content = builder.Options.Content, - IsOpen = true, - ActionButton = builder.Options.ActionButton, - Transitions = [new AddDeleteThemeTransition()], - }; - - infoBar.Closed += infobarClosedEventHandler; ArgumentNullException.ThrowIfNull(collection); - collection.Add(infoBar); - - if (builder.Options.MilliSecondsDelay > 0) - { - await Delay.FromMilliSeconds(builder.Options.MilliSecondsDelay).ConfigureAwait(true); - collection.Remove(infoBar); - infoBar.IsOpen = false; - } - } - - private void OnInfoBarClosed(InfoBar sender, InfoBarClosedEventArgs args) - { - ArgumentNullException.ThrowIfNull(collection); - taskContext.BeginInvokeOnMainThread(() => collection.Remove(sender)); - sender.Closed -= infobarClosedEventHandler; + collection.Add(builder.Options); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarServiceExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarServiceExtension.cs index 659a76bd..5c506304 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarServiceExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarServiceExtension.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using Microsoft.UI.Xaml.Controls; -using Snap.Hutao.Control.Builder.ButtonBase; using Snap.Hutao.Core.Abstraction.Extension; namespace Snap.Hutao.Service.Notification; @@ -21,7 +20,7 @@ internal static class InfoBarServiceExtension public static void Information(this IInfoBarService infoBarService, string title, string message, string buttonContent, ICommand buttonCommand, int milliSeconds = 5000) { - infoBarService.Information(builder => builder.SetTitle(title).SetMessage(message).SetActionButton(buttonBuilder => buttonBuilder.SetContent(buttonContent).SetCommand(buttonCommand)).SetDelay(milliSeconds)); + infoBarService.Information(builder => builder.SetTitle(title).SetMessage(message).SetActionButtonContent(buttonContent).SetActionButtonCommand(buttonCommand).SetDelay(milliSeconds)); } public static void Information(this IInfoBarService infoBarService, Action configure) @@ -56,7 +55,7 @@ internal static class InfoBarServiceExtension public static void Warning(this IInfoBarService infoBarService, string title, string message, string buttonContent, ICommand buttonCommand, int milliSeconds = 30000) { - infoBarService.Warning(builder => builder.SetTitle(title).SetMessage(message).SetActionButton(buttonBuilder => buttonBuilder.SetContent(buttonContent).SetCommand(buttonCommand)).SetDelay(milliSeconds)); + infoBarService.Warning(builder => builder.SetTitle(title).SetMessage(message).SetActionButtonContent(buttonContent).SetActionButtonCommand(buttonCommand).SetDelay(milliSeconds)); } public static void Warning(this IInfoBarService infoBarService, Action configure) @@ -76,7 +75,7 @@ internal static class InfoBarServiceExtension public static void Error(this IInfoBarService infoBarService, string title, string message, string buttonContent, ICommand buttonCommand, int milliSeconds = 0) { - infoBarService.Error(builder => builder.SetTitle(title).SetMessage(message).SetActionButton(buttonBuilder => buttonBuilder.SetContent(buttonContent).SetCommand(buttonCommand)).SetDelay(milliSeconds)); + infoBarService.Error(builder => builder.SetTitle(title).SetMessage(message).SetActionButtonContent(buttonContent).SetActionButtonCommand(buttonCommand).SetDelay(milliSeconds)); } public static void Error(this IInfoBarService infoBarService, Exception ex, int milliSeconds = 0) @@ -91,7 +90,7 @@ internal static class InfoBarServiceExtension public static void Error(this IInfoBarService infoBarService, Exception ex, string subtitle, string buttonContent, ICommand buttonCommand, int milliSeconds = 0) { - infoBarService.Error(builder => builder.SetTitle(ex.GetType().Name).SetMessage($"{subtitle}\n{ex.Message}").SetActionButton(buttonBuilder => buttonBuilder.SetContent(buttonContent).SetCommand(buttonCommand)).SetDelay(milliSeconds)); + infoBarService.Error(builder => builder.SetTitle(ex.GetType().Name).SetMessage($"{subtitle}\n{ex.Message}").SetActionButtonContent(buttonContent).SetActionButtonCommand(buttonCommand).SetDelay(milliSeconds)); } public static void Error(this IInfoBarService infoBarService, Action configure) diff --git a/src/Snap.Hutao/Snap.Hutao/View/InfoBarView.xaml b/src/Snap.Hutao/Snap.Hutao/View/InfoBarView.xaml index 694fa10e..a9da4ed8 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/InfoBarView.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/InfoBarView.xaml @@ -5,68 +5,95 @@ xmlns:cw="using:CommunityToolkit.WinUI" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:mxi="using:Microsoft.Xaml.Interactivity" + xmlns:shcb="using:Snap.Hutao.Control.Behavior" xmlns:shcm="using:Snap.Hutao.Control.Markup" + xmlns:shsn="using:Snap.Hutao.Service.Notification" mc:Ignorable="d"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs index 3c3847ae..fc8d7b59 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs @@ -17,6 +17,7 @@ using Snap.Hutao.View.Dialog; using Snap.Hutao.View.Page; using Snap.Hutao.Web.Hoyolab; using Snap.Hutao.Web.Hoyolab.Passport; +using Snap.Hutao.Web.Hoyolab.Takumi.Binding; using Snap.Hutao.Web.Response; using System.Text; using EntityUser = Snap.Hutao.Model.Entity.User; @@ -297,4 +298,10 @@ internal sealed partial class UserViewModel : ObservableObject FlyoutBase.ShowAttachedFlyout(appBarButton); infoBarService.Warning(message); } + + [Command("RefreshUserGameRoleProfilePictureCommand")] + private void RefreshUserGameRoleProfilePicture(UserGameRole userGameRole) + { + userService.RefreshUserGameRoleProfilePictureAsync(userGameRole).SafeForget(); + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs index c02c196c..77e8814b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs @@ -21,11 +21,23 @@ namespace Snap.Hutao.Web.Enka; internal sealed partial class EnkaClient { private const string EnkaAPI = "https://enka.network/api/uid/{0}"; + private const string EnkaInfoAPI = "https://enka.network/api/uid/{0}?info"; private readonly IHttpRequestMessageBuilderFactory httpRequestMessageBuilderFactory; private readonly JsonSerializerOptions options; private readonly HttpClient httpClient; + public ValueTask GetForwardPlayerInfoAsync(in PlayerUid playerUid, CancellationToken token = default) + { + // TODO + return TryGetEnkaResponseCoreAsync($"https://enka-api.hut.ao/{playerUid}?info", token); + } + + public ValueTask GetPlayerInfoAsync(in PlayerUid playerUid, CancellationToken token = default) + { + return TryGetEnkaResponseCoreAsync(string.Format(CultureInfo.CurrentCulture, EnkaInfoAPI, playerUid), token); + } + public ValueTask GetForwardDataAsync(in PlayerUid playerUid, CancellationToken token = default) { return TryGetEnkaResponseCoreAsync(HutaoEndpoints.Enka(playerUid), token); diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs index e927d380..2bf0aafc 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs @@ -12,5 +12,11 @@ namespace Snap.Hutao.Web.Enka.Model; internal sealed class ProfilePicture { [JsonPropertyName("id")] - public ProfilePictureId Id { get; set; } + public ProfilePictureId ProfilePictureId { get; set; } + + [JsonPropertyName("avatarId")] + public AvatarId AvatarId { get; set; } + + [JsonPropertyName("costumeId")] + public CostumeId CostumeId { get; set; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs index e3322d57..1ad7a81b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs @@ -57,6 +57,8 @@ internal sealed class UserGameRole [JsonPropertyName("is_official")] public bool IsOfficial { get; set; } = default!; + public string ProfilePictureIcon { get; set; } = default!; + /// /// 玩家服务器与等级简述 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs index 093c38e8..b2d2dc9c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs @@ -212,6 +212,11 @@ internal static class HutaoEndpoints return $"{ApiSnapGenshinEnka}/{uid}"; } + public static string EnkaPlayerInfo(in PlayerUid uid) + { + return $"{ApiSnapGenshinEnka}/{uid}/info"; + } + public const string Ip = $"{ApiSnapGenshin}/ip"; #region Metadata @@ -292,4 +297,5 @@ internal static class HutaoEndpoints private const string ApiSnapGenshinStaticZip = $"{ApiSnapGenshin}/static/zip"; private const string ApiSnapGenshinEnka = $"{ApiSnapGenshin}/enka"; private const string HomaSnapGenshin = "https://homa.snapgenshin.com"; + private const string EnkaHutao = "https://enka-api.hut.ao"; } \ No newline at end of file From 73825d391eb1ccd8baa6ed7bfcffe28c70ca8407 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Sat, 15 Jun 2024 00:05:26 +0800 Subject: [PATCH 37/61] fix build --- .../Service/Metadata/MetadataServiceDictionaryExtension.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs index 3313145a..f1e4ca7c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs @@ -78,12 +78,12 @@ internal static class MetadataServiceDictionaryExtension public static ValueTask> GetAvatarIdToProfilePictureMapAsync(this IMetadataService metadataService, CancellationToken token = default) { - return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, p => p.AvatarId, token); + return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, (List list) => list.Where(p => p.UnlockType is ProfilePictureUnlockType.Avatar).Select(p => ((AvatarId)p.UnlockParameter, p)), token); } public static ValueTask> GetCostumeIdToProfilePictureMapAsync(this IMetadataService metadataService, CancellationToken token = default) { - return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, p => p.CostumeId, token); + return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, (List list) => list.Where(p => p.UnlockType is ProfilePictureUnlockType.Costume).Select(p => ((CostumeId)p.UnlockParameter, p)), token); } public static ValueTask> GetIdToReliquaryMapAsync(this IMetadataService metadataService, CancellationToken token = default) From 16ac52e71d4afa356a919d2699f88d570c424e58 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Sun, 16 Jun 2024 01:19:56 +0800 Subject: [PATCH 38/61] use list instead of mappppps --- ...aDictionaryAvatarIdProfilePictureSource.cs | 11 ---------- ...DictionaryCostumeIdProfilePictureSource.cs | 11 ---------- ...etadataDictionaryIdProfilePictureSource.cs | 11 ---------- .../IMetadataListProfilePictureSource.cs | 11 ++++++++++ .../MetadataServiceContextExtension.cs | 20 +++++------------ .../MetadataServiceDictionaryExtension.cs | 15 ------------- .../Metadata/MetadataServiceListExtension.cs | 5 +++++ .../Service/User/IUserMetadataContext.cs | 4 +--- .../Service/User/UserInitializationService.cs | 22 ++++++++++++++----- .../Service/User/UserMetadataContext.cs | 7 +----- .../Snap.Hutao/Web/Enka/EnkaClient.cs | 3 +-- .../Snap.Hutao/Web/HutaoEndpoints.cs | 1 - 12 files changed, 40 insertions(+), 81 deletions(-) delete mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryAvatarIdProfilePictureSource.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryCostumeIdProfilePictureSource.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdProfilePictureSource.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataListProfilePictureSource.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryAvatarIdProfilePictureSource.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryAvatarIdProfilePictureSource.cs deleted file mode 100644 index fe755b48..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryAvatarIdProfilePictureSource.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Model.Primitive; - -namespace Snap.Hutao.Service.Metadata.ContextAbstraction; - -internal interface IMetadataDictionaryAvatarIdProfilePictureSource -{ - public Dictionary AvatarIdProfilePictureMap { get; set; } -} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryCostumeIdProfilePictureSource.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryCostumeIdProfilePictureSource.cs deleted file mode 100644 index 655d2cd6..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryCostumeIdProfilePictureSource.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Model.Primitive; - -namespace Snap.Hutao.Service.Metadata.ContextAbstraction; - -internal interface IMetadataDictionaryCostumeIdProfilePictureSource -{ - public Dictionary CostumeIdProfilePictureMap { get; set; } -} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdProfilePictureSource.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdProfilePictureSource.cs deleted file mode 100644 index 66605c17..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdProfilePictureSource.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Model.Primitive; - -namespace Snap.Hutao.Service.Metadata.ContextAbstraction; - -internal interface IMetadataDictionaryIdProfilePictureSource -{ - public Dictionary IdProfilePictureMap { get; set; } -} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataListProfilePictureSource.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataListProfilePictureSource.cs new file mode 100644 index 00000000..34536889 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataListProfilePictureSource.cs @@ -0,0 +1,11 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Metadata.Avatar; + +namespace Snap.Hutao.Service.Metadata.ContextAbstraction; + +internal interface IMetadataListProfilePictureSource +{ + public List ProfilePictures { get; set; } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs index 53a5d504..11fe4b5b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs @@ -38,6 +38,11 @@ internal static class MetadataServiceContextExtension listMaterialSource.Materials = await metadataService.GetMaterialListAsync(token).ConfigureAwait(false); } + if (context is IMetadataListProfilePictureSource dictionaryIdProfilePictureSource) + { + dictionaryIdProfilePictureSource.ProfilePictures = await metadataService.GetProfilePictureListAsync(token).ConfigureAwait(false); + } + if (context is IMetadataListReliquaryMainAffixLevelSource listReliquaryMainAffixLevelSource) { listReliquaryMainAffixLevelSource.ReliquaryMainAffixLevels = await metadataService.GetReliquaryMainAffixLevelListAsync(token).ConfigureAwait(false); @@ -66,21 +71,6 @@ internal static class MetadataServiceContextExtension dictionaryIdMaterialSource.IdMaterialMap = await metadataService.GetIdToMaterialMapAsync(token).ConfigureAwait(false); } - if (context is IMetadataDictionaryIdProfilePictureSource dictionaryIdProfilePictureSource) - { - dictionaryIdProfilePictureSource.IdProfilePictureMap = await metadataService.GetIdToProfilePictureMapAsync(token).ConfigureAwait(false); - } - - if (context is IMetadataDictionaryAvatarIdProfilePictureSource dictionaryAvatarIdProfilePictureSource) - { - dictionaryAvatarIdProfilePictureSource.AvatarIdProfilePictureMap = await metadataService.GetAvatarIdToProfilePictureMapAsync(token).ConfigureAwait(false); - } - - if (context is IMetadataDictionaryCostumeIdProfilePictureSource dictionaryCostumeIdProfilePictureSource) - { - dictionaryCostumeIdProfilePictureSource.CostumeIdProfilePictureMap = await metadataService.GetCostumeIdToProfilePictureMapAsync(token).ConfigureAwait(false); - } - if (context is IMetadataDictionaryIdReliquarySource dictionaryIdReliquarySource) { dictionaryIdReliquarySource.IdReliquaryMap = await metadataService.GetIdToReliquaryMapAsync(token).ConfigureAwait(false); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs index f1e4ca7c..364808a5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs @@ -71,21 +71,6 @@ internal static class MetadataServiceDictionaryExtension return metadataService.FromCacheAsDictionaryAsync(FileNameMaterial, a => a.Id, token); } - public static ValueTask> GetIdToProfilePictureMapAsync(this IMetadataService metadataService, CancellationToken token = default) - { - return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, p => p.Id, token); - } - - public static ValueTask> GetAvatarIdToProfilePictureMapAsync(this IMetadataService metadataService, CancellationToken token = default) - { - return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, (List list) => list.Where(p => p.UnlockType is ProfilePictureUnlockType.Avatar).Select(p => ((AvatarId)p.UnlockParameter, p)), token); - } - - public static ValueTask> GetCostumeIdToProfilePictureMapAsync(this IMetadataService metadataService, CancellationToken token = default) - { - return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, (List list) => list.Where(p => p.UnlockType is ProfilePictureUnlockType.Costume).Select(p => ((CostumeId)p.UnlockParameter, p)), token); - } - public static ValueTask> GetIdToReliquaryMapAsync(this IMetadataService metadataService, CancellationToken token = default) { return metadataService.FromCacheAsDictionaryAsync(FileNameReliquary, (List list) => list.SelectMany(r => r.Ids, (r, i) => (Index: i, Reliquary: r)), token); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceListExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceListExtension.cs index 1532e245..720147cd 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceListExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceListExtension.cs @@ -70,6 +70,11 @@ internal static class MetadataServiceListExtension return metadataService.FromCacheOrFileAsync>(FileNameMonsterCurve, token); } + public static ValueTask> GetProfilePictureListAsync(this IMetadataService metadataService, CancellationToken token = default) + { + return metadataService.FromCacheOrFileAsync>(FileNameProfilePicture, token); + } + public static ValueTask> GetReliquaryListAsync(this IMetadataService metadataService, CancellationToken token = default) { return metadataService.FromCacheOrFileAsync>(FileNameReliquary, token); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserMetadataContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserMetadataContext.cs index 4b64ae97..ac828068 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserMetadataContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserMetadataContext.cs @@ -6,6 +6,4 @@ using Snap.Hutao.Service.Metadata.ContextAbstraction; namespace Snap.Hutao.Service.User; internal interface IUserMetadataContext : IMetadataContext, - IMetadataDictionaryIdProfilePictureSource, - IMetadataDictionaryAvatarIdProfilePictureSource, - IMetadataDictionaryCostumeIdProfilePictureSource; + IMetadataListProfilePictureSource; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs index e0bbe03a..eb2fd95a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs @@ -4,6 +4,7 @@ using Snap.Hutao.Core.DependencyInjection.Abstraction; using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity.Extension; +using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Service.Metadata; using Snap.Hutao.Service.Metadata.ContextAbstraction; using Snap.Hutao.Web.Enka; @@ -294,21 +295,30 @@ internal sealed partial class UserInitializationService : IUserInitializationSer .ConfigureAwait(false); } - if (context.IdProfilePictureMap.TryGetValue(profilePicture.ProfilePictureId, out MetadataProfilePicture? metadataProfilePicture)) + if (profilePicture.ProfilePictureId is not 0U) { - userGameRole.ProfilePictureIcon = metadataProfilePicture.Icon; + userGameRole.ProfilePictureIcon = context.ProfilePictures + .Single(p => p.Id == profilePicture.ProfilePictureId) + .Icon; + return true; } - if (context.CostumeIdProfilePictureMap.TryGetValue(profilePicture.CostumeId, out metadataProfilePicture)) + if (profilePicture.CostumeId is not 0U) { - userGameRole.ProfilePictureIcon = metadataProfilePicture.Icon; + userGameRole.ProfilePictureIcon = context.ProfilePictures + .Single(p => p.UnlockType is ProfilePictureUnlockType.Costume && p.UnlockParameter == profilePicture.CostumeId) + .Icon; + return true; } - if (context.AvatarIdProfilePictureMap.TryGetValue(profilePicture.AvatarId, out metadataProfilePicture)) + if (profilePicture.AvatarId is not 0U) { - userGameRole.ProfilePictureIcon = metadataProfilePicture.Icon; + userGameRole.ProfilePictureIcon = context.ProfilePictures + .Single(p => p.UnlockType is ProfilePictureUnlockType.Avatar && p.UnlockParameter == profilePicture.AvatarId) + .Icon; + return true; } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserMetadataContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserMetadataContext.cs index 03b60b6e..977f5547 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserMetadataContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserMetadataContext.cs @@ -2,15 +2,10 @@ // Licensed under the MIT license. using Snap.Hutao.Model.Metadata.Avatar; -using Snap.Hutao.Model.Primitive; namespace Snap.Hutao.Service.User; internal class UserMetadataContext : IUserMetadataContext { - public Dictionary IdProfilePictureMap { get; set; } = default!; - - public Dictionary AvatarIdProfilePictureMap { get; set; } = default!; - - public Dictionary CostumeIdProfilePictureMap { get; set; } = default!; + public List ProfilePictures { get; set; } = default!; } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs index 77e8814b..f4fdbcc1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs @@ -29,8 +29,7 @@ internal sealed partial class EnkaClient public ValueTask GetForwardPlayerInfoAsync(in PlayerUid playerUid, CancellationToken token = default) { - // TODO - return TryGetEnkaResponseCoreAsync($"https://enka-api.hut.ao/{playerUid}?info", token); + return TryGetEnkaResponseCoreAsync(HutaoEndpoints.EnkaPlayerInfo(playerUid), token); } public ValueTask GetPlayerInfoAsync(in PlayerUid playerUid, CancellationToken token = default) diff --git a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs index b2d2dc9c..c314c07d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs @@ -297,5 +297,4 @@ internal static class HutaoEndpoints private const string ApiSnapGenshinStaticZip = $"{ApiSnapGenshin}/static/zip"; private const string ApiSnapGenshinEnka = $"{ApiSnapGenshin}/enka"; private const string HomaSnapGenshin = "https://homa.snapgenshin.com"; - private const string EnkaHutao = "https://enka-api.hut.ao"; } \ No newline at end of file From 18103b4deb5d9da3ae8727cceeb95969b7cc6be1 Mon Sep 17 00:00:00 2001 From: Mikachu2333 Date: Sun, 16 Jun 2024 01:21:36 +0800 Subject: [PATCH 39/61] modify alpha's tip (#1730) Co-authored-by: LinkChou --- .github/ISSUE_TEMPLATE/CHS-bug-report.yml | 2 +- .github/workflows/alpha.yml | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/CHS-bug-report.yml b/.github/ISSUE_TEMPLATE/CHS-bug-report.yml index ba18fffa..2a777976 100644 --- a/.github/ISSUE_TEMPLATE/CHS-bug-report.yml +++ b/.github/ISSUE_TEMPLATE/CHS-bug-report.yml @@ -19,7 +19,7 @@ body: - label: 我已阅读 Snap Hutao 文档中的[常见问题](https://hut.ao/advanced/FAQ.html)和[常见程序异常](https://hut.ao/advanced/exceptions.html),我的问题没有在文档中得到解答 required: true - - label: 我知道文档站的导航栏中有**搜索功能**,且已经搜索过相关关键词 + - label: 我知道[文档站](https://hut.ao/zh/menu.html)的导航栏中有**搜索功能**,且已经搜索过相关关键词 required: true - label: 我的问题不是[已完成](https://github.com/DGP-Studio/Snap.Hutao/issues?q=is%3Aopen+is%3Aissue+label%3A%E5%B7%B2%E5%AE%8C%E6%88%90)的问题也不是一个别人已发布的**重复的**问题 diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index 818e788f..5c5c5558 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -64,12 +64,10 @@ jobs: > 该版本是由 CI 程序自动打包生成的 `Alpha` 测试版本,**仅供开发者测试使用** > [!TIP] - > 普通用户请[点击这里](https://github.com/DGP-Studio/Snap.Hutao/releases/latest/)下载最新的稳定版本 + > 普通用户请 [点击这里](https://github.com/DGP-Studio/Snap.Hutao/releases/latest/) 下载最新的稳定版本 > [!IMPORTANT] - > 请注意,从 Snap Hutao Alpha 2023.12.21.3 开始,我们将使用全新的 CI 证书,原有的 Snap.Hutao.CI.cer 将在几天后过期停止使用。 - > - > 请安装 [DGP_Studio_CA.crt](https://github.com/DGP-Automation/Hutao-Auto-Release/releases/download/certificate-ca/DGP_Studio_CA.crt) 到 `受信任的根证书颁发机构` 以安装测试版安装包 + > 请先安装 **[DGP_Studio_CA.crt](https://github.com/DGP-Automation/Hutao-Auto-Release/releases/download/certificate-ca/DGP_Studio_CA.crt)** 到 **受信任的根证书颁发机构** 以安装测试版安装包 " echo $summary >> $Env:GITHUB_STEP_SUMMARY @@ -111,12 +109,10 @@ jobs: > 该版本是由 CI 程序自动打包生成的 `Alpha` 测试版本,**仅供开发者测试使用** > [!TIP] - > 普通用户请[点击这里](https://github.com/DGP-Studio/Snap.Hutao/releases/latest/)下载最新的稳定版本 + > 普通用户请 [点击这里](https://github.com/DGP-Studio/Snap.Hutao/releases/latest/) 下载最新的稳定版本 > [!IMPORTANT] - > 请注意,从 Snap Hutao Alpha 2023.12.21.3 开始,我们将使用全新的 CI 证书,原有的 Snap.Hutao.CI.cer 将在几天后过期停止使用。 - > - > 请安装 [DGP_Studio_CA.crt](https://github.com/DGP-Automation/Hutao-Auto-Release/releases/download/certificate-ca/DGP_Studio_CA.crt) 到 `受信任的根证书颁发机构` 以安装测试版安装包 + > 请先安装 **[DGP_Studio_CA.crt](https://github.com/DGP-Automation/Hutao-Auto-Release/releases/download/certificate-ca/DGP_Studio_CA.crt)** 到 **受信任的根证书颁发机构** 以安装测试版安装包 " echo $summary >> $Env:GITHUB_STEP_SUMMARY From 94fda223fc292b96e04146a6ede288f8815e3f7a Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sun, 16 Jun 2024 01:24:22 +0800 Subject: [PATCH 40/61] drop notification --- src/Snap.Hutao/Snap.Hutao/App.xaml.cs | 7 +- .../Core/LifeCycle/AppActivation.cs | 69 ++++++++------- .../AppActivationArgumentsExtensions.cs | 26 +++--- .../LifeCycle/HutaoActivationArguments.cs | 15 +--- .../Core/LifeCycle/IAppActivation.cs | 9 +- .../IAppActivationActionHandlersAccess.cs | 9 ++ .../InterProcess/PrivateNamedPipeServer.cs | 9 ++ .../NotifyIcon/NotifyIconController.cs | 1 - .../Core/Windowing/XamlWindowController.cs | 5 +- .../AppNotificationBuilderExtension.cs | 20 +++++ .../ContentDialog/ContentDialogFactory.cs | 15 +++- .../ContentDialog/IContentDialogFactory.cs | 2 + .../Snap.Hutao/Package.appxmanifest | 2 +- .../Package.development.appxmanifest | 2 +- .../DailyNoteNotificationOperation.cs | 85 +++++++++---------- .../Notification/ToastNotificationLifeTime.cs | 5 +- src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj | 1 - 17 files changed, 169 insertions(+), 113 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivationActionHandlersAccess.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Extension/AppNotificationBuilderExtension.cs diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs index 2e9b7ecb..d084385a 100644 --- a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs @@ -3,6 +3,7 @@ using Microsoft.UI.Xaml; using Microsoft.Windows.AppLifecycle; +using Microsoft.Windows.AppNotifications; using Snap.Hutao.Core; using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Core.LifeCycle; @@ -68,6 +69,10 @@ public sealed partial class App : Application { try { + // Important: You must call AppNotificationManager::Default().Register + // before calling AppInstance.GetCurrent.GetActivatedEventArgs. + AppNotificationManager.Default.NotificationInvoked += activation.NotificationActivate; + AppNotificationManager.Default.Register(); AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs(); if (serviceProvider.GetRequiredService().TryRedirectActivationTo(activatedEventArgs)) @@ -80,7 +85,6 @@ public sealed partial class App : Application logger.LogColorizedInformation((ConsoleBanner, ConsoleColor.DarkYellow)); LogDiagnosticInformation(); - // Manually invoke HutaoActivationArguments hutaoArgs = HutaoActivationArguments.FromAppActivationArguments(activatedEventArgs); if (hutaoArgs.Kind is HutaoActivationKind.Toast) { @@ -88,6 +92,7 @@ public sealed partial class App : Application return; } + // Manually invoke activation.Activate(hutaoArgs); activation.PostInitialization(); } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs index 79144143..4fef7e60 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs @@ -1,9 +1,9 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using CommunityToolkit.WinUI.Notifications; using Microsoft.Extensions.Caching.Memory; using Microsoft.UI.Xaml; +using Microsoft.Windows.AppNotifications; using Snap.Hutao.Core.LifeCycle.InterProcess; using Snap.Hutao.Core.Setting; using Snap.Hutao.Core.Shell; @@ -41,8 +41,8 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi private const string UrlActionImport = "/IMPORT"; private const string UrlActionRefresh = "/REFRESH"; - private readonly IServiceProvider serviceProvider; private readonly ICurrentXamlWindowReference currentWindowReference; + private readonly IServiceProvider serviceProvider; private readonly ITaskContext taskContext; private readonly SemaphoreSlim activateSemaphore = new(1); @@ -53,6 +53,18 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi HandleActivationAsync(args).SafeForget(); } + public void NotificationActivate(AppNotificationManager manager, AppNotificationActivatedEventArgs args) + { + if (args.Arguments.TryGetValue(Action, out string? action)) + { + if (action == LaunchGame) + { + _ = args.Arguments.TryGetValue(Uid, out string? uid); + HandleLaunchGameActionAsync(uid).SafeForget(); + } + } + } + /// public void PostInitialization() { @@ -63,7 +75,6 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi await taskContext.SwitchToBackgroundAsync(); serviceProvider.GetRequiredService().RunAsync().SafeForget(); - ToastNotificationManagerCompat.OnActivated += NotificationActivate; using (await activateSemaphore.EnterAsync().ConfigureAwait(false)) { @@ -134,20 +145,6 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi } } - private void NotificationActivate(ToastNotificationActivatedEventArgsCompat args) - { - ToastArguments toastArgs = ToastArguments.Parse(args.Argument); - - if (toastArgs.TryGetValue(Action, out string? action)) - { - if (action == LaunchGame) - { - _ = toastArgs.TryGetValue(Uid, out string? uid); - HandleLaunchGameActionAsync(uid).SafeForget(); - } - } - } - private async ValueTask HandleActivationAsync(HutaoActivationArguments args) { await taskContext.SwitchToBackgroundAsync(); @@ -163,22 +160,34 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi private async ValueTask HandleActivationCoreAsync(HutaoActivationArguments args) { - if (args.Kind is HutaoActivationKind.Protocol) + switch (args.Kind) { - ArgumentNullException.ThrowIfNull(args.ProtocolActivatedUri); - await HandleUrlActivationAsync(args.ProtocolActivatedUri, args.IsRedirectTo).ConfigureAwait(false); - } - else if (args.Kind is HutaoActivationKind.Launch) - { - ArgumentNullException.ThrowIfNull(args.LaunchActivatedArguments); - switch (args.LaunchActivatedArguments) - { - default: + case HutaoActivationKind.Protocol: + { + ArgumentNullException.ThrowIfNull(args.ProtocolActivatedUri); + await HandleUrlActivationAsync(args.ProtocolActivatedUri, args.IsRedirectTo).ConfigureAwait(false); + break; + } + + case HutaoActivationKind.Launch: + { + ArgumentNullException.ThrowIfNull(args.LaunchActivatedArguments); + switch (args.LaunchActivatedArguments) { - await HandleNormalLaunchActionAsync(args.IsRedirectTo).ConfigureAwait(false); - break; + default: + { + await HandleNormalLaunchActionAsync(args.IsRedirectTo).ConfigureAwait(false); + break; + } } - } + + break; + } + + case HutaoActivationKind.Toast: + { + break; + } } } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivationArgumentsExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivationArgumentsExtensions.cs index c76b1b4d..2fc31d32 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivationArgumentsExtensions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivationArgumentsExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Microsoft.Extensions.Primitives; using Microsoft.Windows.AppLifecycle; using Windows.ApplicationModel.Activation; @@ -12,12 +13,6 @@ namespace Snap.Hutao.Core.LifeCycle; [HighQuality] internal static class AppActivationArgumentsExtensions { - /// - /// 尝试获取协议启动的Uri - /// - /// 应用程序激活参数 - /// 协议Uri - /// 是否存在协议Uri public static bool TryGetProtocolActivatedUri(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out Uri? uri) { uri = null; @@ -30,21 +25,26 @@ internal static class AppActivationArgumentsExtensions return true; } - /// - /// 尝试获取启动的参数 - /// - /// 应用程序激活参数 - /// 参数 - /// 是否存在参数 - public static bool TryGetLaunchActivatedArguments(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out string? arguments) + public static bool TryGetLaunchActivatedArguments(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out string? arguments, out bool isToastActivated) { arguments = null; + isToastActivated = false; + if (activatedEventArgs.Data is not ILaunchActivatedEventArgs launchArgs) { return false; } arguments = launchArgs.Arguments.Trim(); + foreach (StringSegment segment in new StringTokenizer(arguments, [' '])) + { + if (segment.AsSpan().SequenceEqual("----AppNotificationActivated:")) + { + isToastActivated = true; + break; + } + } + return true; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationArguments.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationArguments.cs index 41b8dedb..3fa39818 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationArguments.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationArguments.cs @@ -1,7 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Microsoft.Extensions.Primitives; using Microsoft.Windows.AppLifecycle; namespace Snap.Hutao.Core.LifeCycle; @@ -10,8 +9,6 @@ internal sealed class HutaoActivationArguments { public bool IsRedirectTo { get; set; } - public bool IsToastActivated { get; set; } - public HutaoActivationKind Kind { get; set; } public Uri? ProtocolActivatedUri { get; set; } @@ -30,17 +27,13 @@ internal sealed class HutaoActivationArguments case ExtendedActivationKind.Launch: { result.Kind = HutaoActivationKind.Launch; - if (args.TryGetLaunchActivatedArguments(out string? arguments)) + + if (args.TryGetLaunchActivatedArguments(out string? arguments, out bool isToastActivated)) { result.LaunchActivatedArguments = arguments; - - foreach (StringSegment segment in new StringTokenizer(arguments, [' '])) + if (isToastActivated) { - if (segment.AsSpan().SequenceEqual("-ToastActivated")) - { - result.Kind = HutaoActivationKind.Toast; - break; - } + result.Kind = HutaoActivationKind.Toast; } } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivation.cs index ea3f2a5e..075894fa 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivation.cs @@ -1,16 +1,15 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Microsoft.Windows.AppNotifications; + namespace Snap.Hutao.Core.LifeCycle; internal interface IAppActivation { void Activate(HutaoActivationArguments args); - void PostInitialization(); -} + void NotificationActivate(AppNotificationManager manager, AppNotificationActivatedEventArgs args); -internal interface IAppActivationActionHandlersAccess -{ - ValueTask HandleLaunchGameActionAsync(string? uid = null); + void PostInitialization(); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivationActionHandlersAccess.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivationActionHandlersAccess.cs new file mode 100644 index 00000000..b941d09f --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivationActionHandlersAccess.cs @@ -0,0 +1,9 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Core.LifeCycle; + +internal interface IAppActivationActionHandlersAccess +{ + ValueTask HandleLaunchGameActionAsync(string? uid = null); +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PrivateNamedPipeServer.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PrivateNamedPipeServer.cs index c466cf62..172ee9d5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PrivateNamedPipeServer.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/InterProcess/PrivateNamedPipeServer.cs @@ -13,6 +13,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable { private readonly PrivateNamedPipeMessageDispatcher messageDispatcher; private readonly RuntimeOptions runtimeOptions; + private readonly ILogger logger; private readonly CancellationTokenSource serverTokenSource = new(); private readonly SemaphoreSlim serverSemaphore = new(1); @@ -23,6 +24,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable { messageDispatcher = serviceProvider.GetRequiredService(); runtimeOptions = serviceProvider.GetRequiredService(); + logger = serviceProvider.GetRequiredService>(); PipeSecurity? pipeSecurity = default; @@ -64,6 +66,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable try { await serverStream.WaitForConnectionAsync(serverTokenSource.Token).ConfigureAwait(false); + logger.LogInformation("Pipe session created"); RunPacketSession(serverStream, serverTokenSource.Token); } catch (OperationCanceledException) @@ -78,6 +81,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable while (serverStream.IsConnected && !token.IsCancellationRequested) { serverStream.ReadPacket(out PipePacketHeader header); + logger.LogInformation("Pipe packet: [Type:{Type}] [Command:{Command}]", header.Type, header.Command); switch ((header.Type, header.Command)) { case (PipePacketType.Request, PipePacketCommand.RequestElevationStatus): @@ -87,6 +91,11 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable break; case (PipePacketType.Request, PipePacketCommand.RedirectActivation): HutaoActivationArguments? hutaoArgs = serverStream.ReadJsonContent(in header); + if (hutaoArgs is not null) + { + logger.LogInformation("Redirect activation: [Kind:{Kind}] [Arguments:{Arguments}]", hutaoArgs.Kind, hutaoArgs.LaunchActivatedArguments); + } + messageDispatcher.RedirectActivation(hutaoArgs); break; case (PipePacketType.SessionTermination, _): diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconController.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconController.cs index 901341be..07271056 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconController.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconController.cs @@ -9,7 +9,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; -using Windows.Storage; using static Snap.Hutao.Win32.ConstValues; namespace Snap.Hutao.Core.Windowing.NotifyIcon; diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowController.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowController.cs index 8be2e106..6c9eb666 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowController.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowController.cs @@ -1,7 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using CommunityToolkit.WinUI.Notifications; using Microsoft.UI; using Microsoft.UI.Composition.SystemBackdrops; using Microsoft.UI.Content; @@ -9,11 +8,11 @@ using Microsoft.UI.Input; using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media; +using Microsoft.Windows.AppNotifications.Builder; using Snap.Hutao.Core.LifeCycle; using Snap.Hutao.Core.Setting; using Snap.Hutao.Core.Windowing.Abstraction; using Snap.Hutao.Core.Windowing.NotifyIcon; -using Snap.Hutao.Factory.ContentDialog; using Snap.Hutao.Service; using Snap.Hutao.Win32; using Snap.Hutao.Win32.Foundation; @@ -106,7 +105,7 @@ internal sealed class XamlWindowController { if (!IsNotifyIconVisible()) { - new ToastContentBuilder() + new AppNotificationBuilder() .AddText(SH.CoreWindowingNotifyIconPromotedHint) .Show(); } diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/AppNotificationBuilderExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/AppNotificationBuilderExtension.cs new file mode 100644 index 00000000..33cf0e46 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Extension/AppNotificationBuilderExtension.cs @@ -0,0 +1,20 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.Windows.AppNotifications; +using Microsoft.Windows.AppNotifications.Builder; + +namespace Snap.Hutao.Extension; + +internal static class AppNotificationBuilderExtension +{ + /// + /// Build and show the notification + /// + /// this + /// Defaults to + public static void Show(this AppNotificationBuilder builder, AppNotificationManager? manager = default) + { + (manager ?? AppNotificationManager.Default).Show(builder.BuildNotification()); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/ContentDialogFactory.cs b/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/ContentDialogFactory.cs index 4ea5e8b5..8095c94d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/ContentDialogFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/ContentDialogFactory.cs @@ -22,6 +22,19 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory private readonly ConcurrentQueue> dialogQueue = []; private bool isDialogShowing; + public bool IsDialogShowing + { + get + { + if (currentWindowReference.Window is not { } window) + { + return false; + } + + return isDialogShowing; + } + } + /// public async ValueTask CreateForConfirmAsync(string title, string content) { @@ -115,7 +128,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory } finally { - await ShowNextDialog().ConfigureAwait(false); + ShowNextDialog().SafeForget(); } }); diff --git a/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/IContentDialogFactory.cs b/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/IContentDialogFactory.cs index 703378df..8e7bb08a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/IContentDialogFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/IContentDialogFactory.cs @@ -11,6 +11,8 @@ namespace Snap.Hutao.Factory.ContentDialog; [HighQuality] internal interface IContentDialogFactory { + bool IsDialogShowing { get; } + /// /// 异步确认 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest index 76e05736..b29f3819 100644 --- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest +++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest @@ -50,7 +50,7 @@ - + diff --git a/src/Snap.Hutao/Snap.Hutao/Package.development.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.development.appxmanifest index 170a6c2f..a92e375c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Package.development.appxmanifest +++ b/src/Snap.Hutao/Snap.Hutao/Package.development.appxmanifest @@ -50,7 +50,7 @@ - + diff --git a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs index db80005b..2e28c6ba 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs @@ -1,7 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using CommunityToolkit.WinUI.Notifications; +using Microsoft.Windows.AppNotifications; using Snap.Hutao.Core; using Snap.Hutao.Core.LifeCycle; using Snap.Hutao.Model.Entity; @@ -20,7 +20,6 @@ namespace Snap.Hutao.Service.DailyNote; [Injection(InjectAs.Singleton)] internal sealed partial class DailyNoteNotificationOperation { - private const string ToastHeaderIdArgument = "DAILYNOTE"; private const string ToastAttributionUnknown = "Unknown"; private readonly ITaskContext taskContext; @@ -52,57 +51,57 @@ internal sealed partial class DailyNoteNotificationOperation ? entry.UserGameRole.ToString() : await GetUserUidAsync(entry).ConfigureAwait(false); - ToastContentBuilder builder = new ToastContentBuilder() - .AddHeader(ToastHeaderIdArgument, SH.ServiceDailyNoteNotifierTitle, ToastHeaderIdArgument) - .AddAttributionText(attribution) - .AddButton(new ToastButton() - .SetContent(SH.ServiceDailyNoteNotifierActionLaunchGameButton) - .AddArgument(AppActivation.Action, AppActivation.LaunchGame) - .AddArgument(AppActivation.Uid, entry.Uid)) - .AddButton(new ToastButtonDismiss(SH.ServiceDailyNoteNotifierActionLaunchGameDismiss)); - - if (options.IsReminderNotification) - { - builder.SetToastScenario(ToastScenario.Reminder); - } + string reminder = options.IsReminderNotification ? @"scenario=""reminder""" : string.Empty; + string content; if (notifyInfos.Count > 2) { - builder.AddText(SH.ServiceDailyNoteNotifierMultiValueReached); + string adaptiveSubgroups = string.Join(string.Empty, notifyInfos.Select(info => $""" + + {info.AdaptiveHint} + {info.Title} + + """)); - // Desktop and Mobile started supporting adaptive toasts in API contract 3 (Anniversary Update) - if (UniversalApiContract.IsPresent(WindowsVersion.Windows10AnniversaryUpdate)) - { - AdaptiveGroup group = new(); - foreach (DailyNoteNotifyInfo info in notifyInfos) - { - AdaptiveSubgroup subgroup = new() - { - HintWeight = 1, - Children = - { - // new AdaptiveImage() { Source = info.AdaptiveIcon, HintRemoveMargin = true, }, - new AdaptiveText() { Text = info.AdaptiveHint, HintAlign = AdaptiveTextAlign.Center, }, - new AdaptiveText() { Text = info.Title, HintAlign = AdaptiveTextAlign.Center, HintStyle = AdaptiveTextStyle.CaptionSubtle, }, - }, - }; - - group.Children.Add(subgroup); - } - - builder.AddVisualChild(group); - } + content = $""" + {SH.ServiceDailyNoteNotifierMultiValueReached} + + {adaptiveSubgroups} + + """; } else { - foreach (DailyNoteNotifyInfo info in notifyInfos) - { - builder.AddText(info.Hint); - } + content = string.Join(string.Empty, notifyInfos.Select(info => $""" + {info.Hint} + """)); + } + + string rawXml = $""" + +
+ + + + {content} + {attribution} + + + + + + + + """; + AppNotification notification = new(rawXml); + + if (options.IsSilentWhenPlayingGame && gameService.IsGameRunning()) + { + notification.SuppressDisplay = true; } await taskContext.SwitchToMainThreadAsync(); - builder.Show(toast => toast.SuppressPopup = ShouldSuppressPopup(options)); + AppNotificationManager.Default.Show(notification); } private bool ShouldSuppressPopup(DailyNoteOptions options) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Notification/ToastNotificationLifeTime.cs b/src/Snap.Hutao/Snap.Hutao/Service/Notification/ToastNotificationLifeTime.cs index 5200453b..ee73a177 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Notification/ToastNotificationLifeTime.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Notification/ToastNotificationLifeTime.cs @@ -1,7 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using CommunityToolkit.WinUI.Notifications; +using Microsoft.Windows.AppNotifications; namespace Snap.Hutao.Service.Notification; @@ -16,7 +16,8 @@ internal sealed class ToastNotificationLifeTime : IToastNotificationLifeTime // 用于在程序退出时尝试清除所有的系统通知 try { - ToastNotificationManagerCompat.History.Clear(); + AppNotificationManager.Default.RemoveAllAsync().AsTask().GetAwaiter().GetResult(); + AppNotificationManager.Default.Unregister(); } catch { diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 94af86a5..eef66737 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -312,7 +312,6 @@ - From 4fa52700701e5d0130d78c1421b4da152398a152 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Sun, 16 Jun 2024 01:43:43 +0800 Subject: [PATCH 41/61] fix failed notification activate --- .../Service/DailyNote/DailyNoteNotificationOperation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs index 2e28c6ba..1ee659cd 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs @@ -88,7 +88,7 @@ internal sealed partial class DailyNoteNotificationOperation - + From 502fb6dbed3c0b26608b49a15e02f9f8e82c4fb2 Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sun, 16 Jun 2024 17:27:20 +0800 Subject: [PATCH 42/61] fix notification activation --- src/Snap.Hutao/Snap.Hutao/App.xaml.cs | 11 +- .../Core/LifeCycle/AppActivation.cs | 237 ++++++++---------- .../AppActivationArgumentsExtensions.cs | 25 +- .../LifeCycle/HutaoActivationArguments.cs | 24 +- .../Core/LifeCycle/HutaoActivationKind.cs | 2 +- .../Core/LifeCycle/IAppActivation.cs | 2 +- .../Job/DailyNoteRefreshJobScheduler.cs | 2 +- 7 files changed, 137 insertions(+), 166 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs index d084385a..4e950dc9 100644 --- a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs @@ -71,7 +71,7 @@ public sealed partial class App : Application { // Important: You must call AppNotificationManager::Default().Register // before calling AppInstance.GetCurrent.GetActivatedEventArgs. - AppNotificationManager.Default.NotificationInvoked += activation.NotificationActivate; + AppNotificationManager.Default.NotificationInvoked += activation.NotificationInvoked; AppNotificationManager.Default.Register(); AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs(); @@ -85,15 +85,8 @@ public sealed partial class App : Application logger.LogColorizedInformation((ConsoleBanner, ConsoleColor.DarkYellow)); LogDiagnosticInformation(); - HutaoActivationArguments hutaoArgs = HutaoActivationArguments.FromAppActivationArguments(activatedEventArgs); - if (hutaoArgs.Kind is HutaoActivationKind.Toast) - { - Exit(); - return; - } - // Manually invoke - activation.Activate(hutaoArgs); + activation.Activate(HutaoActivationArguments.FromAppActivationArguments(activatedEventArgs)); activation.PostInitialization(); } catch (Exception ex) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs index 4fef7e60..9eb7cf3a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivation.cs @@ -11,7 +11,6 @@ using Snap.Hutao.Core.Windowing; using Snap.Hutao.Core.Windowing.HotKey; using Snap.Hutao.Core.Windowing.NotifyIcon; using Snap.Hutao.Service; -using Snap.Hutao.Service.DailyNote; using Snap.Hutao.Service.Discord; using Snap.Hutao.Service.Hutao; using Snap.Hutao.Service.Job; @@ -37,9 +36,7 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi public const string ImportUIAFFromClipboard = nameof(ImportUIAFFromClipboard); private const string CategoryAchievement = "ACHIEVEMENT"; - private const string CategoryDailyNote = "DAILYNOTE"; private const string UrlActionImport = "/IMPORT"; - private const string UrlActionRefresh = "/REFRESH"; private readonly ICurrentXamlWindowReference currentWindowReference; private readonly IServiceProvider serviceProvider; @@ -50,21 +47,49 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi /// public void Activate(HutaoActivationArguments args) { - HandleActivationAsync(args).SafeForget(); - } + HandleActivationExclusiveAsync(args).SafeForget(); - public void NotificationActivate(AppNotificationManager manager, AppNotificationActivatedEventArgs args) - { - if (args.Arguments.TryGetValue(Action, out string? action)) + async ValueTask HandleActivationExclusiveAsync(HutaoActivationArguments args) { - if (action == LaunchGame) + await taskContext.SwitchToBackgroundAsync(); + + if (activateSemaphore.CurrentCount > 0) { - _ = args.Arguments.TryGetValue(Uid, out string? uid); - HandleLaunchGameActionAsync(uid).SafeForget(); + using (await activateSemaphore.EnterAsync().ConfigureAwait(false)) + { + switch (args.Kind) + { + case HutaoActivationKind.Protocol: + { + ArgumentNullException.ThrowIfNull(args.ProtocolActivatedUri); + await HandleProtocolActivationAsync(args.ProtocolActivatedUri, args.IsRedirectTo).ConfigureAwait(false); + break; + } + + case HutaoActivationKind.Launch: + { + ArgumentNullException.ThrowIfNull(args.LaunchActivatedArguments); + await HandleLaunchActivationAsync(args.IsRedirectTo).ConfigureAwait(false); + break; + } + + case HutaoActivationKind.AppNotification: + { + ArgumentNullException.ThrowIfNull(args.AppNotificationActivatedArguments); + await HandleAppNotificationActivationAsync(args.AppNotificationActivatedArguments, args.IsRedirectTo).ConfigureAwait(false); + break; + } + } + } } } } + public void NotificationInvoked(AppNotificationManager manager, AppNotificationActivatedEventArgs args) + { + HandleAppNotificationActivationAsync(args.Arguments, false).SafeForget(); + } + /// public void PostInitialization() { @@ -74,19 +99,22 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi { await taskContext.SwitchToBackgroundAsync(); - serviceProvider.GetRequiredService().RunAsync().SafeForget(); - using (await activateSemaphore.EnterAsync().ConfigureAwait(false)) { // TODO: Introduced in 1.10.2, remove in later version - serviceProvider.GetRequiredService().ClearAsync().SafeForget(); - serviceProvider.GetRequiredService().UnregisterAllTasks(); + { + serviceProvider.GetRequiredService().ClearAsync().SafeForget(); + serviceProvider.GetRequiredService().UnregisterAllTasks(); + } if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) < GuideState.Completed) { return; } + serviceProvider.GetRequiredService().RunAsync().SafeForget(); + + // RegisterHotKey should be called from main thread await taskContext.SwitchToMainThreadAsync(); serviceProvider.GetRequiredService().RegisterAll(); @@ -99,7 +127,18 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi _ = serviceProvider.GetRequiredService(); } - serviceProvider.GetRequiredService().StartAsync(default).SafeForget(); + serviceProvider.GetRequiredService().SetNormalActivityAsync().SafeForget(); + serviceProvider.GetRequiredService().StartAsync().SafeForget(); + + if (serviceProvider.GetRequiredService() is IMetadataServiceInitialization metadataServiceInitialization) + { + metadataServiceInitialization.InitializeInternalAsync().SafeForget(); + } + + if (serviceProvider.GetRequiredService() is IHutaoUserServiceInitialization hutaoUserServiceInitialization) + { + hutaoUserServiceInitialization.InitializeInternalAsync().SafeForget(); + } } } } @@ -145,38 +184,36 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi } } - private async ValueTask HandleActivationAsync(HutaoActivationArguments args) + private async ValueTask HandleProtocolActivationAsync(Uri uri, bool isRedirectTo) { - await taskContext.SwitchToBackgroundAsync(); + UriBuilder builder = new(uri); - if (activateSemaphore.CurrentCount > 0) + string category = builder.Host.ToUpperInvariant(); + string action = builder.Path.ToUpperInvariant(); + + // string parameter = builder.Query.ToUpperInvariant(); + switch (category) { - using (await activateSemaphore.EnterAsync().ConfigureAwait(false)) - { - await HandleActivationCoreAsync(args).ConfigureAwait(false); - } - } - } - - private async ValueTask HandleActivationCoreAsync(HutaoActivationArguments args) - { - switch (args.Kind) - { - case HutaoActivationKind.Protocol: + case CategoryAchievement: { - ArgumentNullException.ThrowIfNull(args.ProtocolActivatedUri); - await HandleUrlActivationAsync(args.ProtocolActivatedUri, args.IsRedirectTo).ConfigureAwait(false); - break; - } - - case HutaoActivationKind.Launch: - { - ArgumentNullException.ThrowIfNull(args.LaunchActivatedArguments); - switch (args.LaunchActivatedArguments) + await WaitMainWindowOrCurrentAsync().ConfigureAwait(false); + if (currentWindowReference.Window is not MainWindow) { - default: + // TODO: Send notification to hint? + return; + } + + switch (action) + { + case UrlActionImport: { - await HandleNormalLaunchActionAsync(args.IsRedirectTo).ConfigureAwait(false); + await taskContext.SwitchToMainThreadAsync(); + + INavigationAwaiter navigationAwaiter = new NavigationExtra(ImportUIAFFromClipboard); + await serviceProvider + .GetRequiredService() + .NavigateAsync(navigationAwaiter, true) + .ConfigureAwait(false); break; } } @@ -184,14 +221,15 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi break; } - case HutaoActivationKind.Toast: + default: { + await HandleLaunchActivationAsync(isRedirectTo).ConfigureAwait(false); break; } } } - private async ValueTask HandleNormalLaunchActionAsync(bool isRedirectTo) + private async ValueTask HandleLaunchActivationAsync(bool isRedirectTo) { if (!isRedirectTo) { @@ -223,6 +261,22 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi await WaitMainWindowOrCurrentAsync().ConfigureAwait(false); } + private async ValueTask HandleAppNotificationActivationAsync(IDictionary arguments, bool isRedirectTo) + { + if (arguments.TryGetValue(Action, out string? action)) + { + if (action == LaunchGame) + { + _ = arguments.TryGetValue(Uid, out string? uid); + await HandleLaunchGameActionAsync(uid).ConfigureAwait(false); + } + } + else + { + await HandleLaunchActivationAsync(isRedirectTo).ConfigureAwait(false); + } + } + private async ValueTask WaitMainWindowOrCurrentAsync() { if (currentWindowReference.Window is { } window) @@ -239,100 +293,5 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi mainWindow.SwitchTo(); mainWindow.BringToForeground(); - - await taskContext.SwitchToBackgroundAsync(); - - if (serviceProvider.GetRequiredService() is IMetadataServiceInitialization metadataServiceInitialization) - { - metadataServiceInitialization.InitializeInternalAsync().SafeForget(); - } - - if (serviceProvider.GetRequiredService() is IHutaoUserServiceInitialization hutaoUserServiceInitialization) - { - hutaoUserServiceInitialization.InitializeInternalAsync().SafeForget(); - } - - serviceProvider.GetRequiredService().SetNormalActivityAsync().SafeForget(); - } - - private async ValueTask HandleUrlActivationAsync(Uri uri, bool isRedirectTo) - { - UriBuilder builder = new(uri); - - string category = builder.Host.ToUpperInvariant(); - string action = builder.Path.ToUpperInvariant(); - string parameter = builder.Query.ToUpperInvariant(); - - switch (category) - { - case CategoryAchievement: - { - await WaitMainWindowOrCurrentAsync().ConfigureAwait(false); - await HandleAchievementActionAsync(action, parameter, isRedirectTo).ConfigureAwait(false); - break; - } - - case CategoryDailyNote: - { - await HandleDailyNoteActionAsync(action, parameter, isRedirectTo).ConfigureAwait(false); - break; - } - - default: - { - await HandleNormalLaunchActionAsync(isRedirectTo).ConfigureAwait(false); - break; - } - } - } - - private async ValueTask HandleAchievementActionAsync(string action, string parameter, bool isRedirectTo) - { - _ = parameter; - _ = isRedirectTo; - switch (action) - { - case UrlActionImport: - { - await taskContext.SwitchToMainThreadAsync(); - - INavigationAwaiter navigationAwaiter = new NavigationExtra(ImportUIAFFromClipboard); - await serviceProvider - .GetRequiredService() - .NavigateAsync(navigationAwaiter, true) - .ConfigureAwait(false); - break; - } - } - } - - private async ValueTask HandleDailyNoteActionAsync(string action, string parameter, bool isRedirectTo) - { - _ = parameter; - switch (action) - { - case UrlActionRefresh: - { - try - { - await serviceProvider - .GetRequiredService() - .RefreshDailyNotesAsync() - .ConfigureAwait(false); - } - catch - { - } - - // Check if it's redirected. - if (!isRedirectTo) - { - // It's a direct open process, should exit immediately. - Process.GetCurrentProcess().Kill(); - } - - break; - } - } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivationArgumentsExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivationArgumentsExtensions.cs index 2fc31d32..fc69033f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivationArgumentsExtensions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/AppActivationArgumentsExtensions.cs @@ -1,8 +1,8 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Microsoft.Extensions.Primitives; using Microsoft.Windows.AppLifecycle; +using Microsoft.Windows.AppNotifications; using Windows.ApplicationModel.Activation; namespace Snap.Hutao.Core.LifeCycle; @@ -25,10 +25,9 @@ internal static class AppActivationArgumentsExtensions return true; } - public static bool TryGetLaunchActivatedArguments(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out string? arguments, out bool isToastActivated) + public static bool TryGetLaunchActivatedArguments(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out string? arguments) { arguments = null; - isToastActivated = false; if (activatedEventArgs.Data is not ILaunchActivatedEventArgs launchArgs) { @@ -36,15 +35,23 @@ internal static class AppActivationArgumentsExtensions } arguments = launchArgs.Arguments.Trim(); - foreach (StringSegment segment in new StringTokenizer(arguments, [' '])) + return true; + } + + public static bool TryGetAppNotificationActivatedArguments(this AppActivationArguments activatedEventArgs, out string? argument, [NotNullWhen(true)] out IDictionary? arguments, [NotNullWhen(true)] out IDictionary? userInput) + { + argument = null; + arguments = null; + userInput = null; + + if (activatedEventArgs.Data is not AppNotificationActivatedEventArgs appNotificationArgs) { - if (segment.AsSpan().SequenceEqual("----AppNotificationActivated:")) - { - isToastActivated = true; - break; - } + return false; } + argument = appNotificationArgs.Argument; + arguments = appNotificationArgs.Arguments; + userInput = appNotificationArgs.UserInput; return true; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationArguments.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationArguments.cs index 3fa39818..7b71f052 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationArguments.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationArguments.cs @@ -15,6 +15,10 @@ internal sealed class HutaoActivationArguments public string? LaunchActivatedArguments { get; set; } + public IDictionary? AppNotificationActivatedArguments { get; set; } + + public IDictionary? AppNotificationActivatedUserInput { get; set; } + public static HutaoActivationArguments FromAppActivationArguments(AppActivationArguments args, bool isRedirected = false) { HutaoActivationArguments result = new() @@ -27,14 +31,9 @@ internal sealed class HutaoActivationArguments case ExtendedActivationKind.Launch: { result.Kind = HutaoActivationKind.Launch; - - if (args.TryGetLaunchActivatedArguments(out string? arguments, out bool isToastActivated)) + if (args.TryGetLaunchActivatedArguments(out string? arguments)) { result.LaunchActivatedArguments = arguments; - if (isToastActivated) - { - result.Kind = HutaoActivationKind.Toast; - } } break; @@ -48,6 +47,19 @@ internal sealed class HutaoActivationArguments result.ProtocolActivatedUri = uri; } + break; + } + + case ExtendedActivationKind.AppNotification: + { + result.Kind = HutaoActivationKind.AppNotification; + if (args.TryGetAppNotificationActivatedArguments(out string? argument, out IDictionary? arguments, out IDictionary? userInput)) + { + result.LaunchActivatedArguments = argument; + result.AppNotificationActivatedArguments = arguments; + result.AppNotificationActivatedUserInput = userInput; + } + break; } } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationKind.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationKind.cs index 66211290..8bce6505 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationKind.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/HutaoActivationKind.cs @@ -7,6 +7,6 @@ internal enum HutaoActivationKind { None, Launch, - Toast, + AppNotification, Protocol, } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivation.cs index 075894fa..c14d95e3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IAppActivation.cs @@ -9,7 +9,7 @@ internal interface IAppActivation { void Activate(HutaoActivationArguments args); - void NotificationActivate(AppNotificationManager manager, AppNotificationActivatedEventArgs args); + void NotificationInvoked(AppNotificationManager manager, AppNotificationActivatedEventArgs args); void PostInitialization(); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Job/DailyNoteRefreshJobScheduler.cs b/src/Snap.Hutao/Snap.Hutao/Service/Job/DailyNoteRefreshJobScheduler.cs index 9635e576..14837f3a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Job/DailyNoteRefreshJobScheduler.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Job/DailyNoteRefreshJobScheduler.cs @@ -25,8 +25,8 @@ internal sealed partial class DailyNoteRefreshJobScheduler : IJobScheduler ITrigger dailyNoteTrigger = TriggerBuilder.Create() .WithIdentity(JobIdentity.DailyNoteRefreshTriggerName, JobIdentity.DailyNoteGroupName) - .StartNow() .WithSimpleSchedule(builder => builder.WithIntervalInSeconds(interval).RepeatForever()) + .StartAt(DateTimeOffset.Now.AddSeconds(interval)) .Build(); await scheduler.ScheduleJob(dailyNoteJob, dailyNoteTrigger).ConfigureAwait(false); From d05c196b7cc71cd4ebdddf73be0c51df435d9b76 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Sun, 16 Jun 2024 19:04:51 +0800 Subject: [PATCH 43/61] apply changes --- ...40616104646_UidProfilePicture.Designer.cs} | 58 +++++++++---------- ...cs => 20240616104646_UidProfilePicture.cs} | 11 ++-- .../Migrations/AppDbContextModelSnapshot.cs | 54 ++++++++--------- .../Model/Entity/Database/AppDbContext.cs | 4 +- ...ProfilePicture.cs => UidProfilePicture.cs} | 10 ++-- .../User/IUidProfilePictureDbService.cs | 16 +++++ .../Service/User/IUserGameRoleDbService.cs | 18 ------ .../User/IUserInitializationService.cs | 2 +- .../Snap.Hutao/Service/User/IUserService.cs | 2 +- .../User/UidProfilePictureDbService.cs | 32 ++++++++++ .../Service/User/UserGameRoleDbService.cs | 37 ------------ .../Service/User/UserInitializationService.cs | 26 ++++----- .../Snap.Hutao/Service/User/UserService.cs | 4 +- .../ViewModel/User/UserViewModel.cs | 2 +- 14 files changed, 133 insertions(+), 143 deletions(-) rename src/Snap.Hutao/Snap.Hutao/Migrations/{20240613144942_UserGameRoleProfilePicture.Designer.cs => 20240616104646_UidProfilePicture.Designer.cs} (98%) rename src/Snap.Hutao/Snap.Hutao/Migrations/{20240613144942_UserGameRoleProfilePicture.cs => 20240616104646_UidProfilePicture.cs} (75%) rename src/Snap.Hutao/Snap.Hutao/Model/Entity/{UserGameRoleProfilePicture.cs => UidProfilePicture.cs} (71%) create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/IUidProfilePictureDbService.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/IUserGameRoleDbService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/UserGameRoleDbService.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20240616104646_UidProfilePicture.Designer.cs similarity index 98% rename from src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.Designer.cs rename to src/Snap.Hutao/Snap.Hutao/Migrations/20240616104646_UidProfilePicture.Designer.cs index 3e6db24b..26aa2963 100644 --- a/src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.Designer.cs +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20240616104646_UidProfilePicture.Designer.cs @@ -11,8 +11,8 @@ using Snap.Hutao.Model.Entity.Database; namespace Snap.Hutao.Migrations { [DbContext(typeof(AppDbContext))] - [Migration("20240613144942_UserGameRoleProfilePicture")] - partial class UserGameRoleProfilePicture + [Migration("20240616104646_UidProfilePicture")] + partial class UidProfilePicture { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -469,6 +469,33 @@ namespace Snap.Hutao.Migrations b.ToTable("spiral_abysses"); }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.UidProfilePicture", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AvatarId") + .HasColumnType("INTEGER"); + + b.Property("CostumeId") + .HasColumnType("INTEGER"); + + b.Property("ProfilePictureId") + .HasColumnType("INTEGER"); + + b.Property("RefreshTime") + .HasColumnType("TEXT"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("uid_profile_pictures"); + }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b => { b.Property("InnerId") @@ -518,33 +545,6 @@ namespace Snap.Hutao.Migrations b.ToTable("users"); }); - modelBuilder.Entity("Snap.Hutao.Model.Entity.UserGameRoleProfilePicture", b => - { - b.Property("InnerId") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AvatarId") - .HasColumnType("INTEGER"); - - b.Property("CostumeId") - .HasColumnType("INTEGER"); - - b.Property("LastUpdateTime") - .HasColumnType("TEXT"); - - b.Property("ProfilePictureId") - .HasColumnType("INTEGER"); - - b.Property("Uid") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("InnerId"); - - b.ToTable("profile_pictures"); - }); - modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => { b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive") diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20240616104646_UidProfilePicture.cs similarity index 75% rename from src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.cs rename to src/Snap.Hutao/Snap.Hutao/Migrations/20240616104646_UidProfilePicture.cs index 84e5271f..0c323d59 100644 --- a/src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.cs +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20240616104646_UidProfilePicture.cs @@ -1,4 +1,5 @@ // +using System; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable @@ -6,13 +7,13 @@ using Microsoft.EntityFrameworkCore.Migrations; namespace Snap.Hutao.Migrations { /// - public partial class UserGameRoleProfilePicture : Migration + public partial class UidProfilePicture : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "profile_pictures", + name: "uid_profile_pictures", columns: table => new { InnerId = table.Column(type: "TEXT", nullable: false), @@ -20,11 +21,11 @@ namespace Snap.Hutao.Migrations ProfilePictureId = table.Column(type: "INTEGER", nullable: false), AvatarId = table.Column(type: "INTEGER", nullable: false), CostumeId = table.Column(type: "INTEGER", nullable: false), - LastUpdateTime = table.Column(type: "TEXT", nullable: false) + RefreshTime = table.Column(type: "TEXT", nullable: false) }, constraints: table => { - table.PrimaryKey("PK_profile_pictures", x => x.InnerId); + table.PrimaryKey("PK_uid_profile_pictures", x => x.InnerId); }); } @@ -32,7 +33,7 @@ namespace Snap.Hutao.Migrations protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "profile_pictures"); + name: "uid_profile_pictures"); } } } diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs index 3be71058..03413381 100644 --- a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs @@ -466,6 +466,33 @@ namespace Snap.Hutao.Migrations b.ToTable("spiral_abysses"); }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.UidProfilePicture", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AvatarId") + .HasColumnType("INTEGER"); + + b.Property("CostumeId") + .HasColumnType("INTEGER"); + + b.Property("ProfilePictureId") + .HasColumnType("INTEGER"); + + b.Property("RefreshTime") + .HasColumnType("TEXT"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("uid_profile_pictures"); + }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b => { b.Property("InnerId") @@ -515,33 +542,6 @@ namespace Snap.Hutao.Migrations b.ToTable("users"); }); - modelBuilder.Entity("Snap.Hutao.Model.Entity.UserGameRoleProfilePicture", b => - { - b.Property("InnerId") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AvatarId") - .HasColumnType("INTEGER"); - - b.Property("CostumeId") - .HasColumnType("INTEGER"); - - b.Property("LastUpdateTime") - .HasColumnType("TEXT"); - - b.Property("ProfilePictureId") - .HasColumnType("INTEGER"); - - b.Property("Uid") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("InnerId"); - - b.ToTable("profile_pictures"); - }); - modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => { b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive") diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs index 8e6f5a16..47cfeb54 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs @@ -25,7 +25,7 @@ internal sealed class AppDbContext : DbContext public AppDbContext(DbContextOptions options) : base(options) { - logger = this.GetService>(); + //logger = this.GetService>(); logger?.LogColorizedInformation("{Name}[{Id}] {Action}", nameof(AppDbContext), (ContextId, ConsoleColor.DarkCyan), ("created", ConsoleColor.Green)); } @@ -65,7 +65,7 @@ internal sealed class AppDbContext : DbContext public DbSet SpiralAbysses { get; set; } = default!; - public DbSet UserGameRoleProfilePictures { get; set; } = default!; + public DbSet UidProfilePictures { get; set; } = default!; public static AppDbContext Create(IServiceProvider serviceProvider, string sqlConnectionString) { diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/UserGameRoleProfilePicture.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs similarity index 71% rename from src/Snap.Hutao/Snap.Hutao/Model/Entity/UserGameRoleProfilePicture.cs rename to src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs index 21bc9372..729df91c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/UserGameRoleProfilePicture.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs @@ -9,9 +9,9 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Snap.Hutao.Model.Entity; -[Table("profile_pictures")] +[Table("uid_profile_pictures")] [SuppressMessage("", "SH002")] -internal sealed class UserGameRoleProfilePicture : IMappingFrom +internal sealed class UidProfilePicture : IMappingFrom { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] @@ -25,9 +25,9 @@ internal sealed class UserGameRoleProfilePicture : IMappingFrom +{ + ValueTask SingleUidProfilePictureOrDefaultByUidAsync(string uid, CancellationToken token = default); + + ValueTask UpdateUidProfilePictureAsync(UidProfilePicture profilePicture, CancellationToken token = default); + + ValueTask DeleteUidProfilePictureByUidAsync(string uid, CancellationToken token = default); +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserGameRoleDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserGameRoleDbService.cs deleted file mode 100644 index 217af9a2..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserGameRoleDbService.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Model.Entity; -using Snap.Hutao.Service.Abstraction; - -namespace Snap.Hutao.Service.User; - -internal interface IUserGameRoleDbService : IAppDbService -{ - ValueTask ContainsUidAsync(string uid, CancellationToken token = default); - - ValueTask GetUserGameRoleProfilePictureByUidAsync(string uid, CancellationToken token = default); - - ValueTask UpdateUserGameRoleProfilePictureAsync(UserGameRoleProfilePicture profilePicture, CancellationToken token = default); - - ValueTask DeleteUserGameRoleProfilePictureByUidAsync(string uid, CancellationToken token = default); -} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs index 5509609e..edbc98af 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs @@ -11,5 +11,5 @@ internal interface IUserInitializationService ValueTask ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default(CancellationToken)); - ValueTask RefreshUserGameRolesProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default); + ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs index 08e1fd87..9a11eb7e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs @@ -54,5 +54,5 @@ internal interface IUserService /// 任务 ValueTask RemoveUserAsync(BindingUser user); - ValueTask RefreshUserGameRoleProfilePictureAsync(UserGameRole userGameRole); + ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs new file mode 100644 index 00000000..5622be01 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs @@ -0,0 +1,32 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.EntityFrameworkCore; +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Service.Abstraction; + +namespace Snap.Hutao.Service.User; + +[ConstructorGenerated] +[Injection(InjectAs.Singleton, typeof(IUidProfilePictureDbService))] +internal sealed partial class UidProfilePictureDbService : IUidProfilePictureDbService +{ + private readonly IServiceProvider serviceProvider; + + public IServiceProvider ServiceProvider { get => serviceProvider; } + + public ValueTask SingleUidProfilePictureOrDefaultByUidAsync(string uid, CancellationToken token = default) + { + return this.QueryAsync(query => query.SingleOrDefaultAsync(n => n.Uid == uid)); + } + + public async ValueTask UpdateUidProfilePictureAsync(UidProfilePicture profilePicture, CancellationToken token = default) + { + await this.UpdateAsync(profilePicture, token).ConfigureAwait(false); + } + + public async ValueTask DeleteUidProfilePictureByUidAsync(string uid, CancellationToken token = default) + { + await this.DeleteAsync(profilePicture => profilePicture.Uid == uid, token).ConfigureAwait(false); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserGameRoleDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserGameRoleDbService.cs deleted file mode 100644 index 09ef97f4..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserGameRoleDbService.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Microsoft.EntityFrameworkCore; -using Snap.Hutao.Model.Entity; -using Snap.Hutao.Service.Abstraction; - -namespace Snap.Hutao.Service.User; - -[ConstructorGenerated] -[Injection(InjectAs.Singleton, typeof(IUserGameRoleDbService))] -internal sealed partial class UserGameRoleDbService : IUserGameRoleDbService -{ - private readonly IServiceProvider serviceProvider; - - public IServiceProvider ServiceProvider { get => serviceProvider; } - - public ValueTask ContainsUidAsync(string uid, CancellationToken token = default) - { - return this.QueryAsync(query => query.AnyAsync(n => n.Uid == uid)); - } - - public ValueTask GetUserGameRoleProfilePictureByUidAsync(string uid, CancellationToken token = default) - { - return this.QueryAsync(query => query.FirstAsync(n => n.Uid == uid)); - } - - public async ValueTask UpdateUserGameRoleProfilePictureAsync(UserGameRoleProfilePicture profilePicture, CancellationToken token = default) - { - await this.UpdateAsync(profilePicture, token).ConfigureAwait(false); - } - - public async ValueTask DeleteUserGameRoleProfilePictureByUidAsync(string uid, CancellationToken token = default) - { - await this.DeleteAsync(profilePicture => profilePicture.Uid == uid, token).ConfigureAwait(false); - } -} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs index eb2fd95a..e2868bf1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs @@ -23,7 +23,7 @@ namespace Snap.Hutao.Service.User; internal sealed partial class UserInitializationService : IUserInitializationService { private readonly IUserFingerprintService userFingerprintService; - private readonly IUserGameRoleDbService userGameRoleDbService; + private readonly IUidProfilePictureDbService uidProfilePictureDbService; private readonly IServiceProvider serviceProvider; public async ValueTask ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default) @@ -63,7 +63,7 @@ internal sealed partial class UserInitializationService : IUserInitializationSer } } - public async ValueTask RefreshUserGameRolesProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default) + public async ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default) { EnkaResponse? enkaResponse; using (IServiceScope scope = serviceProvider.CreateScope()) @@ -78,12 +78,12 @@ internal sealed partial class UserInitializationService : IUserInitializationSer if (enkaResponse is { PlayerInfo: { } playerInfo }) { - UserGameRoleProfilePicture profilePicture = UserGameRoleProfilePicture.From(userGameRole, playerInfo.ProfilePicture); + UidProfilePicture profilePicture = UidProfilePicture.From(userGameRole, playerInfo.ProfilePicture); - await userGameRoleDbService.DeleteUserGameRoleProfilePictureByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false); - await userGameRoleDbService.UpdateUserGameRoleProfilePictureAsync(profilePicture, token).ConfigureAwait(false); + await uidProfilePictureDbService.DeleteUidProfilePictureByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false); + await uidProfilePictureDbService.UpdateUidProfilePictureAsync(profilePicture, token).ConfigureAwait(false); - await SetUserGameRolesProfilePictureCoreAsync(userGameRole, profilePicture, token).ConfigureAwait(false); + await SetUserGameRoleProfilePictureCoreAsync(userGameRole, profilePicture, token).ConfigureAwait(false); } } @@ -255,25 +255,21 @@ internal sealed partial class UserInitializationService : IUserInitializationSer { foreach (UserGameRole userGameRole in user.UserGameRoles) { - if (await userGameRoleDbService.ContainsUidAsync(userGameRole.GameUid, token).ConfigureAwait(false)) + if (await uidProfilePictureDbService.SingleUidProfilePictureOrDefaultByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false) is { } profilePicture) { - UserGameRoleProfilePicture savedProfilePicture = await userGameRoleDbService - .GetUserGameRoleProfilePictureByUidAsync(userGameRole.GameUid, token) - .ConfigureAwait(false); - - if (await SetUserGameRolesProfilePictureCoreAsync(userGameRole, savedProfilePicture, token).ConfigureAwait(false)) + if (await SetUserGameRoleProfilePictureCoreAsync(userGameRole, profilePicture, token).ConfigureAwait(false)) { continue; } } - await RefreshUserGameRolesProfilePictureAsync(userGameRole, token).ConfigureAwait(false); + await RefreshUidProfilePictureAsync(userGameRole, token).ConfigureAwait(false); } } - private async ValueTask SetUserGameRolesProfilePictureCoreAsync(UserGameRole userGameRole, UserGameRoleProfilePicture profilePicture, CancellationToken token = default) + private async ValueTask SetUserGameRoleProfilePictureCoreAsync(UserGameRole userGameRole, UidProfilePicture profilePicture, CancellationToken token = default) { - if (profilePicture.LastUpdateTime.AddDays(15) < DateTimeOffset.Now) + if (profilePicture.RefreshTime.AddDays(15) < DateTimeOffset.Now) { return false; } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs index 6766ad1e..4daf87ae 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs @@ -123,8 +123,8 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe return true; } - public async ValueTask RefreshUserGameRoleProfilePictureAsync(UserGameRole userGameRole) + public async ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole) { - await userInitializationService.RefreshUserGameRolesProfilePictureAsync(userGameRole).ConfigureAwait(false); + await userInitializationService.RefreshUidProfilePictureAsync(userGameRole).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs index fc8d7b59..33ee5655 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs @@ -302,6 +302,6 @@ internal sealed partial class UserViewModel : ObservableObject [Command("RefreshUserGameRoleProfilePictureCommand")] private void RefreshUserGameRoleProfilePicture(UserGameRole userGameRole) { - userService.RefreshUserGameRoleProfilePictureAsync(userGameRole).SafeForget(); + userService.RefreshUidProfilePictureAsync(userGameRole).SafeForget(); } } \ No newline at end of file From 558551c8ad46f9a7a8d703426011446de3f9b8f3 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Sun, 16 Jun 2024 19:14:33 +0800 Subject: [PATCH 44/61] rename --- .../Snap.Hutao/Model/Entity/Database/AppDbContext.cs | 2 +- .../Snap.Hutao/Service/User/IUserInitializationService.cs | 2 +- src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs | 2 +- .../Snap.Hutao/Service/User/UserInitializationService.cs | 5 ++--- src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs | 4 ++-- src/Snap.Hutao/Snap.Hutao/View/UserView.xaml | 2 +- src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs | 6 +++--- 7 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs index 47cfeb54..44be285d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs @@ -25,7 +25,7 @@ internal sealed class AppDbContext : DbContext public AppDbContext(DbContextOptions options) : base(options) { - //logger = this.GetService>(); + logger = this.GetService>(); logger?.LogColorizedInformation("{Name}[{Id}] {Action}", nameof(AppDbContext), (ContextId, ConsoleColor.DarkCyan), ("created", ConsoleColor.Green)); } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs index edbc98af..a4ff1728 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs @@ -11,5 +11,5 @@ internal interface IUserInitializationService ValueTask ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default(CancellationToken)); - ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default); + ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs index 9a11eb7e..8c9fe4d9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs @@ -54,5 +54,5 @@ internal interface IUserService /// 任务 ValueTask RemoveUserAsync(BindingUser user); - ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole); + ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs index e2868bf1..a7922055 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs @@ -14,7 +14,6 @@ using Snap.Hutao.Web.Hoyolab.Bbs.User; using Snap.Hutao.Web.Hoyolab.Passport; using Snap.Hutao.Web.Hoyolab.Takumi.Binding; using Snap.Hutao.Web.Response; -using MetadataProfilePicture = Snap.Hutao.Model.Metadata.Avatar.ProfilePicture; namespace Snap.Hutao.Service.User; @@ -63,7 +62,7 @@ internal sealed partial class UserInitializationService : IUserInitializationSer } } - public async ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default) + public async ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default) { EnkaResponse? enkaResponse; using (IServiceScope scope = serviceProvider.CreateScope()) @@ -263,7 +262,7 @@ internal sealed partial class UserInitializationService : IUserInitializationSer } } - await RefreshUidProfilePictureAsync(userGameRole, token).ConfigureAwait(false); + await RefreshProfilePictureAsync(userGameRole, token).ConfigureAwait(false); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs index 4daf87ae..116342dc 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs @@ -123,8 +123,8 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe return true; } - public async ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole) + public async ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole) { - await userInitializationService.RefreshUidProfilePictureAsync(userGameRole).ConfigureAwait(false); + await userInitializationService.RefreshProfilePictureAsync(userGameRole).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml index 022e4bfe..c3fb9c27 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml @@ -40,7 +40,7 @@ Background="Transparent" BorderBrush="Transparent" BorderThickness="0" - Command="{Binding DataContext.RefreshUserGameRoleProfilePictureCommand, Source={StaticResource ViewModelBindingProxy}}" + Command="{Binding DataContext.RefreshProfilePictureCommand, Source={StaticResource ViewModelBindingProxy}}" CommandParameter="{Binding}" CornerRadius="8"> diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs index 33ee5655..3c6a3d42 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs @@ -299,9 +299,9 @@ internal sealed partial class UserViewModel : ObservableObject infoBarService.Warning(message); } - [Command("RefreshUserGameRoleProfilePictureCommand")] - private void RefreshUserGameRoleProfilePicture(UserGameRole userGameRole) + [Command("RefreshProfilePictureCommand")] + private void RefreshProfilePicture(UserGameRole userGameRole) { - userService.RefreshUidProfilePictureAsync(userGameRole).SafeForget(); + userService.RefreshProfilePictureAsync(userGameRole).SafeForget(); } } \ No newline at end of file From 95d64c289510c699bef93a2b05bed4ba173d2521 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Sun, 16 Jun 2024 20:41:19 +0800 Subject: [PATCH 45/61] make UserGameRole observable --- .../Service/User/UserInitializationService.cs | 8 +++++--- .../Snap.Hutao/ViewModel/User/UserViewModel.cs | 4 ++-- .../Web/Hoyolab/Takumi/Binding/UserGameRole.cs | 16 +++++++++++++--- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs index a7922055..85e31f00 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs @@ -24,6 +24,7 @@ internal sealed partial class UserInitializationService : IUserInitializationSer private readonly IUserFingerprintService userFingerprintService; private readonly IUidProfilePictureDbService uidProfilePictureDbService; private readonly IServiceProvider serviceProvider; + private readonly ITaskContext taskContext; public async ValueTask ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default) { @@ -70,9 +71,8 @@ internal sealed partial class UserInitializationService : IUserInitializationSer EnkaClient enkaClient = scope.ServiceProvider .GetRequiredService(); - enkaResponse = await enkaClient - .GetForwardPlayerInfoAsync(userGameRole, token) - .ConfigureAwait(false); + enkaResponse = await enkaClient.GetForwardPlayerInfoAsync(userGameRole, token).ConfigureAwait(false) + ?? await enkaClient.GetPlayerInfoAsync(userGameRole, token).ConfigureAwait(false); } if (enkaResponse is { PlayerInfo: { } playerInfo }) @@ -290,6 +290,8 @@ internal sealed partial class UserInitializationService : IUserInitializationSer .ConfigureAwait(false); } + await taskContext.SwitchToMainThreadAsync(); + if (profilePicture.ProfilePictureId is not 0U) { userGameRole.ProfilePictureIcon = context.ProfilePictures diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs index 3c6a3d42..2e572c3a 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs @@ -300,8 +300,8 @@ internal sealed partial class UserViewModel : ObservableObject } [Command("RefreshProfilePictureCommand")] - private void RefreshProfilePicture(UserGameRole userGameRole) + private async Task RefreshProfilePictureAsync(UserGameRole userGameRole) { - userService.RefreshProfilePictureAsync(userGameRole).SafeForget(); + await userService.RefreshProfilePictureAsync(userGameRole).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs index 1ad7a81b..748957bb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs @@ -1,14 +1,19 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using CommunityToolkit.Mvvm.ComponentModel; +using Snap.Hutao.Model.Metadata.Converter; + namespace Snap.Hutao.Web.Hoyolab.Takumi.Binding; /// /// 用户游戏角色 /// [HighQuality] -internal sealed class UserGameRole +internal sealed class UserGameRole : ObservableObject { + private string? profilePictureIcon; + /// /// hk4e_cn for Genshin Impact /// @@ -57,8 +62,6 @@ internal sealed class UserGameRole [JsonPropertyName("is_official")] public bool IsOfficial { get; set; } = default!; - public string ProfilePictureIcon { get; set; } = default!; - /// /// 玩家服务器与等级简述 /// @@ -67,6 +70,13 @@ internal sealed class UserGameRole get => $"{RegionName} | Lv.{Level}"; } + [JsonIgnore] + public string? ProfilePictureIcon + { + get => profilePictureIcon; + set => SetProperty(ref profilePictureIcon, value); + } + public static implicit operator PlayerUid(UserGameRole userGameRole) { return new PlayerUid(userGameRole.GameUid, userGameRole.Region); From ff9b553a19be8e17361ac7c30346bb3f9d226aea Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sun, 16 Jun 2024 22:46:24 +0800 Subject: [PATCH 46/61] code style --- .../Control/Theme/CornerRadius.xaml | 1 + .../Model/Entity/UidProfilePicture.cs | 4 +- .../Service/User/IProfilePictureService.cs | 14 +++ .../User/IUserInitializationService.cs | 2 - .../Service/User/ProfilePictureService.cs | 109 ++++++++++++++++++ .../User/UidProfilePictureDbService.cs | 2 +- .../Service/User/UserInitializationService.cs | 107 +---------------- .../Snap.Hutao/Service/User/UserService.cs | 4 +- .../Snap.Hutao/View/Guide/GuideView.xaml | 2 +- src/Snap.Hutao/Snap.Hutao/View/UserView.xaml | 5 +- .../ViewModel/Guide/StaticResource.cs | 2 + .../ViewModel/User/UserViewModel.cs | 6 - .../Snap.Hutao/Web/Enka/EnkaClient.cs | 20 +++- .../Web/Enka/Model/ProfilePicture.cs | 2 +- .../Hoyolab/Takumi/Binding/UserGameRole.cs | 15 ++- 15 files changed, 167 insertions(+), 128 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/IProfilePictureService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/ProfilePictureService.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Theme/CornerRadius.xaml b/src/Snap.Hutao/Snap.Hutao/Control/Theme/CornerRadius.xaml index 4dcfd7d4..741bf85e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Theme/CornerRadius.xaml +++ b/src/Snap.Hutao/Snap.Hutao/Control/Theme/CornerRadius.xaml @@ -2,4 +2,5 @@ 4,4,0,0 0,0,4,4 0,4,0,4 + 16 diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs index 729df91c..c19bcb59 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs @@ -10,7 +10,6 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Snap.Hutao.Model.Entity; [Table("uid_profile_pictures")] -[SuppressMessage("", "SH002")] internal sealed class UidProfilePicture : IMappingFrom { [Key] @@ -27,12 +26,13 @@ internal sealed class UidProfilePicture : IMappingFrom CreateUserFromInputCookieOrDefaultAsync(InputCookie inputCookie, CancellationToken token = default(CancellationToken)); ValueTask ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default(CancellationToken)); - - ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/ProfilePictureService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/ProfilePictureService.cs new file mode 100644 index 00000000..2d6438ef --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/ProfilePictureService.cs @@ -0,0 +1,109 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Model.Intrinsic; +using Snap.Hutao.Service.Metadata; +using Snap.Hutao.Service.Metadata.ContextAbstraction; +using Snap.Hutao.Web.Enka; +using Snap.Hutao.Web.Enka.Model; +using Snap.Hutao.Web.Hoyolab.Takumi.Binding; + +namespace Snap.Hutao.Service.User; + +[ConstructorGenerated] +[Injection(InjectAs.Singleton, typeof(IProfilePictureService))] +internal sealed partial class ProfilePictureService : IProfilePictureService +{ + private readonly IUidProfilePictureDbService uidProfilePictureDbService; + private readonly IMetadataService metadataService; + private readonly IServiceProvider serviceProvider; + private readonly ITaskContext taskContext; + + public async ValueTask TryInitializeAsync(ViewModel.User.User user, CancellationToken token = default) + { + foreach (UserGameRole userGameRole in user.UserGameRoles) + { + if (await uidProfilePictureDbService.SingleUidProfilePictureOrDefaultByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false) is { } profilePicture) + { + if (await TryUpdateUserGameRoleAsync(userGameRole, profilePicture, token).ConfigureAwait(false)) + { + continue; + } + } + + // Force update + await RefreshUserGameRoleAsync(userGameRole, token: token).ConfigureAwait(false); + } + } + + public async ValueTask RefreshUserGameRoleAsync(UserGameRole userGameRole, CancellationToken token = default) + { + EnkaResponse? enkaResponse; + using (IServiceScope scope = serviceProvider.CreateScope()) + { + EnkaClient enkaClient = scope.ServiceProvider + .GetRequiredService(); + + enkaResponse = await enkaClient.GetForwardPlayerInfoAsync(userGameRole, token).ConfigureAwait(false) + ?? await enkaClient.GetPlayerInfoAsync(userGameRole, token).ConfigureAwait(false); + } + + if (enkaResponse is { PlayerInfo: { } playerInfo }) + { + UidProfilePicture profilePicture = UidProfilePicture.From(userGameRole, playerInfo.ProfilePicture); + + await uidProfilePictureDbService.DeleteUidProfilePictureByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false); + await uidProfilePictureDbService.UpdateUidProfilePictureAsync(profilePicture, token).ConfigureAwait(false); + + await TryUpdateUserGameRoleAsync(userGameRole, profilePicture, token).ConfigureAwait(false); + } + } + + private async ValueTask TryUpdateUserGameRoleAsync(UserGameRole userGameRole, UidProfilePicture cache, CancellationToken token = default) + { + if (cache.RefreshTime.AddDays(15) < DateTimeOffset.Now) + { + return false; + } + + if (!await metadataService.InitializeAsync().ConfigureAwait(false)) + { + return false; + } + + UserMetadataContext context = await metadataService.GetContextAsync(token).ConfigureAwait(false); + + await taskContext.SwitchToMainThreadAsync(); + + // Most common to most rare + if (cache.ProfilePictureId is not 0U) + { + userGameRole.ProfilePictureIcon = context.ProfilePictures + .Single(p => p.Id == cache.ProfilePictureId) + .Icon; + + return true; + } + + if (cache.AvatarId is not 0U) + { + userGameRole.ProfilePictureIcon = context.ProfilePictures + .Single(p => p.UnlockType is ProfilePictureUnlockType.Avatar && p.UnlockParameter == cache.AvatarId) + .Icon; + + return true; + } + + if (cache.CostumeId is not 0U) + { + userGameRole.ProfilePictureIcon = context.ProfilePictures + .Single(p => p.UnlockType is ProfilePictureUnlockType.Costume && p.UnlockParameter == cache.CostumeId) + .Icon; + + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs index 5622be01..da6887f2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs @@ -29,4 +29,4 @@ internal sealed partial class UidProfilePictureDbService : IUidProfilePictureDbS { await this.DeleteAsync(profilePicture => profilePicture.Uid == uid, token).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs index 85e31f00..a310752c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs @@ -2,13 +2,7 @@ // Licensed under the MIT license. using Snap.Hutao.Core.DependencyInjection.Abstraction; -using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity.Extension; -using Snap.Hutao.Model.Intrinsic; -using Snap.Hutao.Service.Metadata; -using Snap.Hutao.Service.Metadata.ContextAbstraction; -using Snap.Hutao.Web.Enka; -using Snap.Hutao.Web.Enka.Model; using Snap.Hutao.Web.Hoyolab; using Snap.Hutao.Web.Hoyolab.Bbs.User; using Snap.Hutao.Web.Hoyolab.Passport; @@ -22,9 +16,8 @@ namespace Snap.Hutao.Service.User; internal sealed partial class UserInitializationService : IUserInitializationService { private readonly IUserFingerprintService userFingerprintService; - private readonly IUidProfilePictureDbService uidProfilePictureDbService; + private readonly IProfilePictureService profilePictureService; private readonly IServiceProvider serviceProvider; - private readonly ITaskContext taskContext; public async ValueTask ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default) { @@ -63,29 +56,6 @@ internal sealed partial class UserInitializationService : IUserInitializationSer } } - public async ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default) - { - EnkaResponse? enkaResponse; - using (IServiceScope scope = serviceProvider.CreateScope()) - { - EnkaClient enkaClient = scope.ServiceProvider - .GetRequiredService(); - - enkaResponse = await enkaClient.GetForwardPlayerInfoAsync(userGameRole, token).ConfigureAwait(false) - ?? await enkaClient.GetPlayerInfoAsync(userGameRole, token).ConfigureAwait(false); - } - - if (enkaResponse is { PlayerInfo: { } playerInfo }) - { - UidProfilePicture profilePicture = UidProfilePicture.From(userGameRole, playerInfo.ProfilePicture); - - await uidProfilePictureDbService.DeleteUidProfilePictureByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false); - await uidProfilePictureDbService.UpdateUidProfilePictureAsync(profilePicture, token).ConfigureAwait(false); - - await SetUserGameRoleProfilePictureCoreAsync(userGameRole, profilePicture, token).ConfigureAwait(false); - } - } - private async ValueTask InitializeUserAsync(ViewModel.User.User user, CancellationToken token = default) { if (user.IsInitialized) @@ -120,9 +90,8 @@ internal sealed partial class UserInitializationService : IUserInitializationSer return false; } - TrySetUserUserGameRolesProfilePictureAsync(user, token).SafeForget(); - await userFingerprintService.TryInitializeAsync(user, token).ConfigureAwait(false); + await profilePictureService.TryInitializeAsync(user, token).ConfigureAwait(false); return user.IsInitialized = true; } @@ -249,76 +218,4 @@ internal sealed partial class UserInitializationService : IUserInitializationSer return false; } } - - private async ValueTask TrySetUserUserGameRolesProfilePictureAsync(ViewModel.User.User user, CancellationToken token = default) - { - foreach (UserGameRole userGameRole in user.UserGameRoles) - { - if (await uidProfilePictureDbService.SingleUidProfilePictureOrDefaultByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false) is { } profilePicture) - { - if (await SetUserGameRoleProfilePictureCoreAsync(userGameRole, profilePicture, token).ConfigureAwait(false)) - { - continue; - } - } - - await RefreshProfilePictureAsync(userGameRole, token).ConfigureAwait(false); - } - } - - private async ValueTask SetUserGameRoleProfilePictureCoreAsync(UserGameRole userGameRole, UidProfilePicture profilePicture, CancellationToken token = default) - { - if (profilePicture.RefreshTime.AddDays(15) < DateTimeOffset.Now) - { - return false; - } - - UserMetadataContext context; - using (IServiceScope scope = serviceProvider.CreateScope()) - { - IMetadataService metadataService = scope.ServiceProvider - .GetRequiredService(); - - if (!await metadataService.InitializeAsync().ConfigureAwait(false)) - { - return false; - } - - context = await scope.ServiceProvider - .GetRequiredService() - .GetContextAsync(token) - .ConfigureAwait(false); - } - - await taskContext.SwitchToMainThreadAsync(); - - if (profilePicture.ProfilePictureId is not 0U) - { - userGameRole.ProfilePictureIcon = context.ProfilePictures - .Single(p => p.Id == profilePicture.ProfilePictureId) - .Icon; - - return true; - } - - if (profilePicture.CostumeId is not 0U) - { - userGameRole.ProfilePictureIcon = context.ProfilePictures - .Single(p => p.UnlockType is ProfilePictureUnlockType.Costume && p.UnlockParameter == profilePicture.CostumeId) - .Icon; - - return true; - } - - if (profilePicture.AvatarId is not 0U) - { - userGameRole.ProfilePictureIcon = context.ProfilePictures - .Single(p => p.UnlockType is ProfilePictureUnlockType.Avatar && p.UnlockParameter == profilePicture.AvatarId) - .Icon; - - return true; - } - - return false; - } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs index 116342dc..eaf87c88 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs @@ -21,7 +21,7 @@ namespace Snap.Hutao.Service.User; [Injection(InjectAs.Singleton, typeof(IUserService))] internal sealed partial class UserService : IUserService, IUserServiceUnsafe { - private readonly IUserInitializationService userInitializationService; + private readonly IProfilePictureService profilePictureService; private readonly IUserCollectionService userCollectionService; private readonly IServiceProvider serviceProvider; private readonly IUserDbService userDbService; @@ -125,6 +125,6 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe public async ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole) { - await userInitializationService.RefreshProfilePictureAsync(userGameRole).ConfigureAwait(false); + await profilePictureService.RefreshUserGameRoleAsync(userGameRole).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Guide/GuideView.xaml b/src/Snap.Hutao/Snap.Hutao/View/Guide/GuideView.xaml index b6e79770..8f28ee39 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Guide/GuideView.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Guide/GuideView.xaml @@ -301,7 +301,7 @@