From ddc06c014f45d12a422f25bc0366bc5655bdf04a Mon Sep 17 00:00:00 2001
From: DismissedLight <1686188646@qq.com>
Date: Sat, 2 Jul 2022 22:02:23 +0800
Subject: [PATCH] add metadata service
---
src/Snap.Hutao/SettingsUI/SettingsUI.csproj | 3 +-
.../DedendencyInjection/InjectionGenerator.cs | 2 +-
src/Snap.Hutao/Snap.Hutao.sln | 4 +-
src/Snap.Hutao/Snap.Hutao/App.xaml | 2 +
src/Snap.Hutao/Snap.Hutao/App.xaml.cs | 44 +-
.../Context/Database/AppDbContext.cs | 2 +-
.../Database/AppDbContextDesignTimeFactory.cs | 2 +-
.../Context/FileSystem/FileSystemContext.cs | 78 +--
.../Context/FileSystem/Location/Metadata.cs | 27 +
.../Context/FileSystem/Location/MyDocument.cs | 2 +-
.../Context/FileSystem/MetadataContext.cs | 19 +
.../Control/Cancellable/CancellablePage.cs | 6 +-
.../Snap.Hutao/Control/Image/CachedImage.cs | 57 ++
.../ISupportAsyncInitialization.cs | 4 +-
.../Annotation/InjectionAttribute.cs | 18 +-
.../Json/Converter/DateTimeOffsetConverter.cs | 1 -
.../SeparatorCommaInt32EnumerableConverter.cs | 1 -
.../StringEnumKeyDictionaryConverter.cs | 131 +++++
src/Snap.Hutao/Snap.Hutao/Core/Property.cs | 12 +-
.../Snap.Hutao/Core/Setting/LocalSetting.cs | 2 +-
.../Factory/AsyncRelayCommandFactory.cs | 5 +-
src/Snap.Hutao/Snap.Hutao/GlobalUsing.cs | 3 +-
src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs | 13 +-
.../Snap.Hutao/IocHttpClientConfiguration.cs | 8 +
src/Snap.Hutao/Snap.Hutao/MainWindow.xaml | 5 +-
src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs | 11 +-
.../Snap.Hutao/Model/Intrinsic/EquipType.cs | 46 ++
.../Model/Intrinsic/FightProperty.cs | 491 ++++++++++++++++++
.../Avatar => Model/Intrinsic}/ItemQuality.cs | 2 +-
.../Snap.Hutao/Model/Intrinsic/ItemType.cs | 46 ++
.../Avatar => Model/Intrinsic}/WeaponType.cs | 2 +-
.../Model/Metadata/Achievement/Achievement.cs | 57 ++
.../Metadata/Achievement/AchievementGoal.cs | 30 ++
.../Achievement/AchievementTrigger.cs | 30 ++
.../Achievement/AchievementTriggerType.cs | 25 +
.../Model/Metadata/Achievement/Reward.cs | 20 +
.../Model/Metadata/Avatar/Avatar.cs | 83 +++
.../Model/Metadata/Avatar/Costume.cs | 30 ++
.../Model/Metadata/Avatar/DescParam.cs | 22 +
.../Model/Metadata/Avatar/Fetter.cs | 20 +
.../Model/Metadata/Avatar/FetterInfo.cs | 92 ++++
.../Model/Metadata/Avatar/ProudableSkill.cs | 21 +
.../Model/Metadata/Avatar/SkillBase.cs | 31 ++
.../Model/Metadata/Avatar/SkillDepot.cs | 32 ++
.../Snap.Hutao/Model/Metadata/LevelParam.cs | 23 +
.../Snap.Hutao/Model/Metadata/PropertyInfo.cs | 23 +
.../Model/Metadata/Reliquary/Reliquary.cs | 53 ++
.../Metadata/Reliquary/ReliquaryAffix.cs | 15 +
.../Metadata/Reliquary/ReliquaryAffixBase.cs | 22 +
.../Model/Metadata/Reliquary/ReliquarySet.cs | 27 +
.../Model/Metadata/Weapon/Weapon.cs | 52 ++
.../Snap.Hutao/Package.appxmanifest | 11 +-
src/Snap.Hutao/Snap.Hutao/Program.cs | 2 +-
.../Service/Abstraction/IInfoBarService.cs | 8 +-
.../Service/Abstraction/IMetadataService.cs | 79 +++
.../Snap.Hutao/Service/AnnouncementService.cs | 2 +-
.../Snap.Hutao/Service/InfoBarService.cs | 8 +-
.../Snap.Hutao/Service/MetadataService.cs | 249 +++++++++
.../Navigation/INavigationAwaiter.cs | 2 +-
.../Navigation/INavigationExtra.cs | 5 +-
.../Navigation/INavigationService.cs | 2 +-
.../Navigation/NavigationExtra.cs | 2 +-
.../Navigation/NavigationResult.cs | 2 +-
.../{ => Navigation}/NavigationService.cs | 3 +-
src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj | 16 +-
src/Snap.Hutao/Snap.Hutao/View/MainView.xaml | 6 +
.../Snap.Hutao/View/MainView.xaml.cs | 5 +-
.../Snap.Hutao/View/Page/AchievementPage.xaml | 35 ++
.../View/Page/AchievementPage.xaml.cs | 22 +
.../View/Page/AnnouncementContentPage.xaml.cs | 7 +-
.../View/Page/AnnouncementPage.xaml | 4 +-
.../View/Page/AnnouncementPage.xaml.cs | 2 +-
.../Snap.Hutao/View/Page/WelcomePage.xaml | 16 +
.../Snap.Hutao/View/Page/WelcomePage.xaml.cs | 18 +
.../ViewModel/AchievementViewModel.cs | 66 +++
.../ViewModel/AnnouncementViewModel.cs | 5 +-
.../Snap.Hutao/Web/Enka/EnkaClient.cs | 40 ++
.../Snap.Hutao/Web/Enka/Model/AvatarInfo.cs | 92 ++++
.../Snap.Hutao/Web/Enka/Model/EnkaResponse.cs | 48 ++
.../Snap.Hutao/Web/Enka/Model/Equip.cs | 39 ++
.../Snap.Hutao/Web/Enka/Model/FetterInfo.cs | 18 +
.../Snap.Hutao/Web/Enka/Model/Flat.cs | 84 +++
.../Snap.Hutao/Web/Enka/Model/PlayerInfo.cs | 89 ++++
.../Web/Enka/Model/PlayerProperty.cs | 228 ++++++++
.../Web/Enka/Model/ProfilePicture.cs | 18 +
.../Snap.Hutao/Web/Enka/Model/Reliquary.cs | 34 ++
.../Web/Enka/Model/ReliquaryMainstat.cs | 26 +
.../Web/Enka/Model/ReliquarySubstat.cs | 26 +
.../Web/Enka/Model/ShowAvatarInfo.cs | 32 ++
.../Snap.Hutao/Web/Enka/Model/TypeValue.cs | 24 +
.../Snap.Hutao/Web/Enka/Model/Weapon.cs | 34 ++
.../Snap.Hutao/Web/Enka/Model/WeaponStat.cs | 26 +
.../Web/Hoyolab/Bbs/User/CommunityInfo.cs | 1 -
.../Web/Hoyolab/Bbs/User/UserClient.cs | 1 -
.../Hoyolab/Bbs/User/UserFullInfoWrapper.cs | 1 -
.../DynamicSecret/DynamicSecretProvider2.cs | 1 -
.../HttpClientDynamicSecretExtensions.cs | 1 -
.../Common/Announcement/AnnouncementClient.cs | 5 +-
.../Web/Hoyolab/{Takumi => }/PlayerUid.cs | 2 +-
.../Takumi/GameRecord/Avatar/Avatar.cs | 1 +
.../Takumi/GameRecord/Avatar/Reliquary.cs | 1 +
.../Takumi/GameRecord/Avatar/Weapon.cs | 1 +
.../Takumi/GameRecord/GameRecordClient.cs | 1 -
.../Snap.Hutao/Web/Hutao/HutaoClient.cs | 35 +-
.../Model/Converter/ReliquarySetsConverter.cs | 1 -
.../Web/Hutao/Model/Post/PlayerRecord.cs | 22 +-
106 files changed, 3093 insertions(+), 173 deletions(-)
create mode 100644 src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/Metadata.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Context/FileSystem/MetadataContext.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/StringEnumKeyDictionaryConverter.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/EquipType.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/FightProperty.cs
rename src/Snap.Hutao/Snap.Hutao/{Web/Hoyolab/Takumi/GameRecord/Avatar => Model/Intrinsic}/ItemQuality.cs (93%)
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ItemType.cs
rename src/Snap.Hutao/Snap.Hutao/{Web/Hoyolab/Takumi/GameRecord/Avatar => Model/Intrinsic}/WeaponType.cs (96%)
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Achievement.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementGoal.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTrigger.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTriggerType.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Reward.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Costume.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/DescParam.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Fetter.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/FetterInfo.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillBase.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/LevelParam.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/PropertyInfo.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/Reliquary.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquaryAffix.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquaryAffixBase.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquarySet.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IMetadataService.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/MetadataService.cs
rename src/Snap.Hutao/Snap.Hutao/Service/{Abstraction => }/Navigation/INavigationAwaiter.cs (90%)
rename src/Snap.Hutao/Snap.Hutao/Service/{Abstraction => }/Navigation/INavigationExtra.cs (80%)
rename src/Snap.Hutao/Snap.Hutao/Service/{Abstraction => }/Navigation/INavigationService.cs (98%)
rename src/Snap.Hutao/Snap.Hutao/Service/{Abstraction => }/Navigation/NavigationExtra.cs (95%)
rename src/Snap.Hutao/Snap.Hutao/Service/{Abstraction => }/Navigation/NavigationResult.cs (88%)
rename src/Snap.Hutao/Snap.Hutao/Service/{ => Navigation}/NavigationService.cs (98%)
create mode 100644 src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml
create mode 100644 src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/View/Page/WelcomePage.xaml
create mode 100644 src/Snap.Hutao/Snap.Hutao/View/Page/WelcomePage.xaml.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/AvatarInfo.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/EnkaResponse.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Equip.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/FetterInfo.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Flat.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/PlayerInfo.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/PlayerProperty.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Reliquary.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ReliquaryMainstat.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ReliquarySubstat.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ShowAvatarInfo.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/TypeValue.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Weapon.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/WeaponStat.cs
rename src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/{Takumi => }/PlayerUid.cs (97%)
diff --git a/src/Snap.Hutao/SettingsUI/SettingsUI.csproj b/src/Snap.Hutao/SettingsUI/SettingsUI.csproj
index d06543fb..bc952b96 100644
--- a/src/Snap.Hutao/SettingsUI/SettingsUI.csproj
+++ b/src/Snap.Hutao/SettingsUI/SettingsUI.csproj
@@ -3,7 +3,8 @@
net6.0-windows10.0.17763.0
10.0.17763.0
SettingsUI
- win10-x86;win10-x64;win10-arm64
+ x64
+ win10-x64
true
latest
enable
diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/InjectionGenerator.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/InjectionGenerator.cs
index b3c2dc99..40154b3e 100644
--- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/InjectionGenerator.cs
+++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/DedendencyInjection/InjectionGenerator.cs
@@ -107,7 +107,7 @@ internal static partial class ServiceCollectionExtensions
lines.Add(lineBuilder.ToString());
}
- foreach (string line in lines.OrderByDescending(x => x))
+ foreach (string line in lines.OrderBy(x => x))
{
sourceCodeBuilder.Append(line);
}
diff --git a/src/Snap.Hutao/Snap.Hutao.sln b/src/Snap.Hutao/Snap.Hutao.sln
index 2f861db8..bb171023 100644
--- a/src/Snap.Hutao/Snap.Hutao.sln
+++ b/src/Snap.Hutao/Snap.Hutao.sln
@@ -54,8 +54,8 @@ Global
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|arm64.ActiveCfg = Debug|Any CPU
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|arm64.Build.0 = Debug|Any CPU
- {DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x64.ActiveCfg = Debug|Any CPU
- {DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x64.Build.0 = Debug|Any CPU
+ {DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x64.ActiveCfg = Debug|x64
+ {DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x64.Build.0 = Debug|x64
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x86.ActiveCfg = Debug|Any CPU
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x86.Build.0 = Debug|Any CPU
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Release|Any CPU.ActiveCfg = Release|Any CPU
diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml b/src/Snap.Hutao/Snap.Hutao/App.xaml
index f051bbca..dacaca02 100644
--- a/src/Snap.Hutao/Snap.Hutao/App.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/App.xaml
@@ -17,6 +17,8 @@
+
+
6,16,16,16
16,0,0,0
diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
index e3216d7e..8a9c2bba 100644
--- a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
@@ -4,7 +4,11 @@
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
+using Microsoft.Windows.AppLifecycle;
using Snap.Hutao.Core.Logging;
+using Snap.Hutao.Service.Abstraction;
+using System.Diagnostics;
+using Windows.ApplicationModel.Activation;
namespace Snap.Hutao;
@@ -14,10 +18,10 @@ namespace Snap.Hutao;
public partial class App : Application
{
private static Window? window;
+ private readonly ILogger logger;
///
/// Initializes the singleton application object.
- /// This is the first line of authored code executed, and as such is the logical equivalent of main() or WinMain().
///
public App()
{
@@ -25,6 +29,7 @@ public partial class App : Application
InitializeComponent();
InitializeDependencyInjection();
+ logger = Ioc.Default.GetRequiredService>();
UnhandledException += AppUnhandledException;
}
@@ -34,14 +39,38 @@ public partial class App : Application
public static Window? Window { get => window; set => window = value; }
///
- /// Invoked when the application is launched normally by the end user.
- /// Other entry points will be used such as when the application is launched to open a specific file.
+ /// Invoked when the application is launched.
///
/// Details about the launch request and process.
- protected override void OnLaunched(LaunchActivatedEventArgs args)
+ [SuppressMessage("", "VSTHRD100")]
+ protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
- Window = Ioc.Default.GetRequiredService();
- Window.Activate();
+ AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
+ AppInstance mainInstance = AppInstance.FindOrRegisterForKey("main");
+
+ if (!mainInstance.IsCurrent)
+ {
+ // Redirect the activation (and args) to the "main" instance, and exit.
+ await mainInstance.RedirectActivationToAsync(activatedEventArgs);
+ Process.GetCurrentProcess().Kill();
+ }
+ else
+ {
+ Uri? uri = null;
+ if (activatedEventArgs.Kind == ExtendedActivationKind.Protocol)
+ {
+ IProtocolActivatedEventArgs protocolArgs = (activatedEventArgs.Data as IProtocolActivatedEventArgs)!;
+ uri = protocolArgs.Uri;
+ }
+
+ Window = Ioc.Default.GetRequiredService();
+ Window.Activate();
+
+ if (uri != null)
+ {
+ Ioc.Default.GetRequiredService().Information(uri.ToString());
+ }
+ }
}
private static void InitializeDependencyInjection()
@@ -56,7 +85,7 @@ public partial class App : Application
.AddInjections()
.AddDatebase()
.AddHttpClients()
- .AddDefaultJsonSerializerOptions()
+ .AddJsonSerializerOptions()
// Discrete services
.AddSingleton(WeakReferenceMessenger.Default)
@@ -68,7 +97,6 @@ public partial class App : Application
private void AppUnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
{
- ILogger logger = Ioc.Default.GetRequiredService>();
logger.LogError(EventIds.UnhandledException, e.Exception, "未经处理的异常");
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs
index c6cef5c8..6d9b092d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs
@@ -35,7 +35,7 @@ public class AppDbContext : DbContext
///
/// 连接字符串
/// 应用程序数据库上下文
- public static AppDbContext CreateFrom(string sqlConnectionString)
+ public static AppDbContext Create(string sqlConnectionString)
{
return new(new DbContextOptionsBuilder().UseSqlite(sqlConnectionString).Options);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContextDesignTimeFactory.cs b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContextDesignTimeFactory.cs
index 43027d32..774fe791 100644
--- a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContextDesignTimeFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContextDesignTimeFactory.cs
@@ -22,6 +22,6 @@ public class AppDbContextDesignTimeFactory : IDesignTimeDbContextFactory
+ /// 创建文件,若已存在文件,则不会创建
+ ///
+ /// 文件
+ public void CreateFileOrIgnore(string file)
+ {
+ file = Locate(file);
+ if (!File.Exists(file))
+ {
+ File.Create(file).Dispose();
+ }
+ }
+
+ ///
+ /// 创建文件夹,若已存在文件,则不会创建
+ ///
+ /// 文件夹
+ public void CreateFolderOrIgnore(string folder)
+ {
+ folder = Locate(folder);
+ if (!Directory.Exists(folder))
+ {
+ Directory.CreateDirectory(folder);
+ }
+ }
+
+ ///
+ /// 尝试删除文件夹
+ ///
+ /// 文件夹
+ public void DeleteFolderOrIgnore(string folder)
+ {
+ folder = Locate(folder);
+ if (Directory.Exists(folder))
+ {
+ Directory.Delete(folder, true);
+ }
+ }
+
///
/// 检查根目录
///
@@ -113,41 +152,22 @@ internal abstract class FileSystemContext
}
///
- /// 创建文件,若已存在文件,则不会创建
+ /// 等效于 ,但路径经过解析
///
- /// 文件
- public void CreateFileOrIgnore(string file)
+ /// 文件名
+ /// 文件流
+ public FileStream OpenRead(string file)
{
- file = Locate(file);
- if (!File.Exists(file))
- {
- File.Create(file).Dispose();
- }
+ return File.OpenRead(Locate(file));
}
///
- /// 创建文件夹,若已存在文件,则不会创建
+ /// 等效于 ,但路径经过解析
///
- /// 文件夹
- public void CreateFolderOrIgnore(string folder)
+ /// 文件名
+ /// 文件流
+ public FileStream Create(string file)
{
- folder = Locate(folder);
- if (!Directory.Exists(folder))
- {
- Directory.CreateDirectory(folder);
- }
- }
-
- ///
- /// 尝试删除文件夹
- ///
- /// 文件夹
- public void DeleteFolderOrIgnore(string folder)
- {
- folder = Locate(folder);
- if (Directory.Exists(folder))
- {
- Directory.Delete(folder, true);
- }
+ return File.Create(Locate(file));
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/Metadata.cs b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/Metadata.cs
new file mode 100644
index 00000000..7110d6a8
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/Metadata.cs
@@ -0,0 +1,27 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.IO;
+
+namespace Snap.Hutao.Context.FileSystem.Location;
+
+///
+/// 我的文档位置
+///
+[Injection(InjectAs.Transient)]
+public class Metadata : IFileSystemLocation
+{
+ private string? path;
+
+ ///
+ public string GetPath()
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ string myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
+ path = Path.GetFullPath(Path.Combine(myDocument, "Hutao", "Metadata"));
+ }
+
+ return path;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/MyDocument.cs b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/MyDocument.cs
index e0678e46..1dfa1a24 100644
--- a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/MyDocument.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/MyDocument.cs
@@ -24,4 +24,4 @@ public class MyDocument : IFileSystemLocation
return path;
}
-}
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/MetadataContext.cs b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/MetadataContext.cs
new file mode 100644
index 00000000..f50a29ca
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/MetadataContext.cs
@@ -0,0 +1,19 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Context.FileSystem.Location;
+
+namespace Snap.Hutao.Context.FileSystem;
+
+///
+/// 元数据上下文
+///
+[Injection(InjectAs.Transient)]
+internal class MetadataContext : FileSystemContext
+{
+ ///
+ public MetadataContext(Metadata metadata)
+ : base(metadata)
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Cancellable/CancellablePage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Cancellable/CancellablePage.cs
index 89f43683..82a8d272 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Cancellable/CancellablePage.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Cancellable/CancellablePage.cs
@@ -17,9 +17,11 @@ public class CancellablePage : Page
///
/// 初始化
///
- /// 视图模型
- public void Initialize(ISupportCancellation viewModel)
+ /// 视图模型类型
+ public void InitializeWith()
+ where TViewModel : class, ISupportCancellation
{
+ ISupportCancellation viewModel = Ioc.Default.GetRequiredService();
viewModel.CancellationToken = viewLoadingConcellationTokenSource.Token;
DataContext = viewModel;
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
new file mode 100644
index 00000000..8e6dcbe1
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
@@ -0,0 +1,57 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.WinUI.UI;
+using CommunityToolkit.WinUI.UI.Controls;
+using Microsoft.UI.Xaml.Media;
+using Microsoft.UI.Xaml.Media.Imaging;
+using Snap.Hutao.Extension;
+
+namespace Snap.Hutao.Control.Image;
+
+///
+/// 缓存图像
+///
+public class CachedImage : ImageEx
+{
+ static CachedImage()
+ {
+ ImageCache.Instance.CacheDuration = Timeout.InfiniteTimeSpan;
+ ImageCache.Instance.RetryCount = 3;
+ }
+
+ ///
+ /// 构造一个新的缓存图像
+ ///
+ public CachedImage()
+ {
+ IsCacheEnabled = true;
+ EnableLazyLoading = true;
+ }
+
+ ///
+ protected override async Task ProvideCachedResourceAsync(Uri imageUri, CancellationToken token)
+ {
+ BitmapImage image;
+ try
+ {
+ image = await ImageCache.Instance.GetFromCacheAsync(imageUri, true, token);
+ }
+ catch
+ {
+ // maybe the image is corrupted remove it and re-download
+ await ImageCache.Instance.RemoveAsync(imageUri.Enumerate());
+ image = await ImageCache.Instance.GetFromCacheAsync(imageUri, false, token);
+ }
+
+ // check token state to determine whether the operation should be canceled.
+ if (token.IsCancellationRequested)
+ {
+ throw new TaskCanceledException("Image source has changed.");
+ }
+ else
+ {
+ return Must.NotNull(image);
+ }
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/ISupportAsyncInitialization.cs b/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/ISupportAsyncInitialization.cs
index d89bba52..3758457d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/ISupportAsyncInitialization.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/ISupportAsyncInitialization.cs
@@ -16,7 +16,7 @@ internal interface ISupportAsyncInitialization
///
/// 异步初始化
///
- /// 取消令牌
+ /// 取消令牌
/// 初始化任务
- Task InitializeAsync(CancellationToken cancellationToken = default);
+ ValueTask InitializeAsync(CancellationToken token = default);
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/InjectionAttribute.cs b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/InjectionAttribute.cs
index 0a6a4dd6..ffc1cbbb 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/InjectionAttribute.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/InjectionAttribute.cs
@@ -5,6 +5,7 @@ namespace Snap.Hutao.Core.DependencyInjection.Annotation;
///
/// 指示被标注的类型可注入
+/// 由源生成器生成注入代码
///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class InjectionAttribute : Attribute
@@ -15,27 +16,14 @@ public class InjectionAttribute : Attribute
/// 指示注入方法
public InjectionAttribute(InjectAs injectAs)
{
- InjectAs = injectAs;
}
///
/// 指示该类将注入为带有接口实现的类
///
/// 指示注入方法
- /// 实现的接口类型
- public InjectionAttribute(InjectAs injectAs, Type impl)
+ /// 实现的接口类型
+ public InjectionAttribute(InjectAs injectAs, Type interfaceType)
{
- InterfaceType = impl;
- InjectAs = injectAs;
}
-
- ///
- /// 注入类型
- ///
- public InjectAs InjectAs { get; }
-
- ///
- /// 该类实现的接口类型
- ///
- public Type? InterfaceType { get; }
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/DateTimeOffsetConverter.cs b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/DateTimeOffsetConverter.cs
index 595e5d6e..5d7c2a5f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/DateTimeOffsetConverter.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/DateTimeOffsetConverter.cs
@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using System.Text.Json;
using System.Text.Json.Serialization;
namespace Snap.Hutao.Core.Json.Converter;
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs
index 6c2cd69b..e23a3299 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/SeparatorCommaInt32EnumerableConverter.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
-using System.Text.Json;
using System.Text.Json.Serialization;
namespace Snap.Hutao.Core.Json.Converter;
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/StringEnumKeyDictionaryConverter.cs b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/StringEnumKeyDictionaryConverter.cs
new file mode 100644
index 00000000..f9d93129
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Json/Converter/StringEnumKeyDictionaryConverter.cs
@@ -0,0 +1,131 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Core.Json.Converter;
+
+///
+/// Json字典转换器
+///
+/// 键的类型
+public class StringEnumKeyDictionaryConverter : JsonConverterFactory
+{
+ ///
+ public override bool CanConvert(Type typeToConvert)
+ {
+ if (!typeToConvert.IsGenericType)
+ {
+ return false;
+ }
+
+ if (typeToConvert.GetGenericTypeDefinition() != typeof(Dictionary<,>))
+ {
+ return false;
+ }
+
+ return typeToConvert.GetGenericArguments()[0].IsEnum;
+ }
+
+ ///
+ public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options)
+ {
+ Type keyType = type.GetGenericArguments()[0];
+ Type valueType = type.GetGenericArguments()[1];
+
+ Type innerConverterType = typeof(StringEnumDictionaryConverterInner<,>).MakeGenericType(keyType, valueType);
+ JsonConverter converter = (JsonConverter)Activator.CreateInstance(innerConverterType, BindingFlags.Instance | BindingFlags.Public, null, new object[] { options }, null)!;
+ return converter;
+ }
+
+ private class StringEnumDictionaryConverterInner : JsonConverter>
+ where TKey : struct, Enum
+ {
+ private readonly JsonConverter? valueConverter;
+ private readonly Type keyType;
+ private readonly Type valueType;
+
+ public StringEnumDictionaryConverterInner(JsonSerializerOptions options)
+ {
+ valueConverter = (JsonConverter)options.GetConverter(typeof(TValue));
+
+ // Cache the key and value types.
+ keyType = typeof(TKey);
+ valueType = typeof(TValue);
+ }
+
+ public override IDictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType != JsonTokenType.StartObject)
+ {
+ throw new JsonException();
+ }
+
+ Dictionary dictionary = new();
+
+ while (reader.Read())
+ {
+ if (reader.TokenType == JsonTokenType.EndObject)
+ {
+ return dictionary;
+ }
+
+ // Get the key.
+ if (reader.TokenType != JsonTokenType.PropertyName)
+ {
+ throw new JsonException();
+ }
+
+ string? propertyName = reader.GetString();
+
+ if (!Enum.TryParse(propertyName, out TKey key))
+ {
+ throw new JsonException($"Unable to convert \"{propertyName}\" to Enum \"{keyType}\".");
+ }
+
+ // Get the value.
+ TValue value;
+ if (valueConverter != null)
+ {
+ reader.Read();
+ value = valueConverter.Read(ref reader, valueType, options)!;
+ }
+ else
+ {
+ value = JsonSerializer.Deserialize(ref reader, options)!;
+ }
+
+ // Add to dictionary.
+ dictionary.Add(key, value);
+ }
+
+ throw new JsonException();
+ }
+
+ public override void Write(Utf8JsonWriter writer, IDictionary dictionary, JsonSerializerOptions options)
+ {
+ writer.WriteStartObject();
+
+ foreach ((TKey key, TValue value) in dictionary)
+ {
+ string? propertyName = key.ToString();
+ string? convertedName = options.PropertyNamingPolicy?.ConvertName(propertyName) ?? propertyName;
+
+ writer.WritePropertyName(convertedName);
+
+ if (valueConverter != null)
+ {
+ valueConverter.Write(writer, value, options);
+ }
+ else
+ {
+ JsonSerializer.Serialize(writer, value, options);
+ }
+ }
+
+ writer.WriteEndObject();
+ }
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Property.cs b/src/Snap.Hutao/Snap.Hutao/Core/Property.cs
index c7070b2c..218acaff 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Property.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Property.cs
@@ -19,7 +19,7 @@ internal static class Property
/// 注册的依赖属性
public static DependencyProperty Depend(string name)
{
- return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new PropertyMetadata(null));
+ return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new(default(TProperty)));
}
///
@@ -31,7 +31,7 @@ internal static class Property
/// 注册的依赖属性
public static DependencyProperty Depend(string name, TProperty defaultValue)
{
- return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new PropertyMetadata(defaultValue));
+ return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new(defaultValue));
}
///
@@ -44,7 +44,7 @@ internal static class Property
/// 注册的依赖属性
public static DependencyProperty Depend(string name, TProperty defaultValue, PropertyChangedCallback callback)
{
- return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new PropertyMetadata(defaultValue, callback));
+ return DependencyProperty.Register(name, typeof(TProperty), typeof(TOwner), new(defaultValue, callback));
}
///
@@ -55,7 +55,7 @@ internal static class Property
/// 注册的附加属性
public static DependencyProperty Attach(string name)
{
- return DependencyProperty.RegisterAttached(name, typeof(TProperty), typeof(TOwner), new PropertyMetadata(null));
+ return DependencyProperty.RegisterAttached(name, typeof(TProperty), typeof(TOwner), new(default(TProperty)));
}
///
@@ -67,7 +67,7 @@ internal static class Property
/// 注册的附加属性
public static DependencyProperty Attach(string name, TProperty defaultValue)
{
- return DependencyProperty.RegisterAttached(name, typeof(TProperty), typeof(TOwner), new PropertyMetadata(defaultValue));
+ return DependencyProperty.RegisterAttached(name, typeof(TProperty), typeof(TOwner), new(defaultValue));
}
///
@@ -79,6 +79,6 @@ internal static class Property
/// 注册的附加属性
public static DependencyProperty Attach(string name, PropertyChangedCallback callback)
{
- return DependencyProperty.RegisterAttached(name, typeof(TProperty), typeof(TOwner), new PropertyMetadata(callback));
+ return DependencyProperty.RegisterAttached(name, typeof(TProperty), typeof(TOwner), new(callback));
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Setting/LocalSetting.cs b/src/Snap.Hutao/Snap.Hutao/Core/Setting/LocalSetting.cs
index 553e7997..c939b04d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Setting/LocalSetting.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Setting/LocalSetting.cs
@@ -12,7 +12,7 @@ namespace Snap.Hutao.Core.Setting;
internal static class LocalSetting
{
///
- /// 由于 没有启用 nullable,
+ /// 由于 没有 nullable context,
/// 在处理引用类型时需要格外小心
/// 将值类型的操作与引用类型区分开,可以提升一定的性能
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs b/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs
index 90b1472d..99d5393b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs
@@ -92,8 +92,9 @@ internal class AsyncRelayCommandFactory : IAsyncRelayCommandFactory
{
if (asyncRelayCommand.ExecutionTask?.Exception is AggregateException exception)
{
- logger.LogError(exception, "异步命令发生了错误");
- Crashes.TrackError(exception);
+ Exception baseException = exception.GetBaseException();
+ logger.LogError(baseException, "异步命令发生了错误");
+ Crashes.TrackError(baseException);
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/GlobalUsing.cs b/src/Snap.Hutao/Snap.Hutao/GlobalUsing.cs
index d8cc82de..174fead5 100644
--- a/src/Snap.Hutao/Snap.Hutao/GlobalUsing.cs
+++ b/src/Snap.Hutao/Snap.Hutao/GlobalUsing.cs
@@ -10,6 +10,7 @@ global using Snap.Hutao.Core.Validation;
global using System;
global using System.ComponentModel;
global using System.Diagnostics.CodeAnalysis;
+global using System.Text.Json;
global using System.Threading;
global using System.Threading.Tasks;
-global using System.Windows.Input;
+global using System.Windows.Input;
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs
index 0cd30cd7..1c3eb592 100644
--- a/src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs
+++ b/src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs
@@ -7,7 +7,7 @@ using Snap.Hutao.Context.Database;
using Snap.Hutao.Context.FileSystem;
using System.Diagnostics;
using System.Linq;
-using System.Text.Json;
+using System.Text.Encodings.Web;
using System.Text.Json.Serialization;
namespace Snap.Hutao;
@@ -22,20 +22,20 @@ internal static class IocConfiguration
///
/// 集合
/// 可继续操作的集合
- public static IServiceCollection AddDefaultJsonSerializerOptions(this IServiceCollection services)
+ public static IServiceCollection AddJsonSerializerOptions(this IServiceCollection services)
{
- // default json options, global configuration
return services
.AddSingleton(new JsonSerializerOptions()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
PropertyNameCaseInsensitive = true,
WriteIndented = true,
});
}
///
- /// 添加数据库
+ /// 添加专用数据库
///
/// 集合
/// 可继续操作的集合
@@ -48,13 +48,12 @@ internal static class IocConfiguration
string sqlConnectionString = $"Data Source={dbFile}";
// temporarily create a context
- using (AppDbContext context = AppDbContext.CreateFrom(sqlConnectionString))
+ using (AppDbContext context = AppDbContext.Create(sqlConnectionString))
{
if (context.Database.GetPendingMigrations().Any())
{
- Debug.WriteLine("Migrate started");
+ Debug.WriteLine("Performing Migrations");
context.Database.Migrate();
- Debug.WriteLine("Migrate completed");
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs
index bdb7320e..436cf0e9 100644
--- a/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs
+++ b/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs
@@ -2,6 +2,8 @@
// Licensed under the MIT license.
using Microsoft.Extensions.DependencyInjection;
+using Snap.Hutao.Service.Metadata;
+using Snap.Hutao.Web.Enka;
using Snap.Hutao.Web.Hoyolab.Bbs.User;
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
@@ -25,10 +27,16 @@ internal static class IocHttpClientConfiguration
/// 可继续操作的集合
public static IServiceCollection AddHttpClients(this IServiceCollection services)
{
+ // services
+ services.AddHttpClient(DefaultConfiguration);
+
+ // normal clients
services.AddHttpClient(DefaultConfiguration);
services.AddHttpClient(DefaultConfiguration);
services.AddHttpClient(DefaultConfiguration);
+ services.AddHttpClient(DefaultConfiguration);
+ // x-rpc clients
services.AddHttpClient(XRpcConfiguration);
services.AddHttpClient(XRpcConfiguration);
diff --git a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml
index 879a8114..00f8412f 100644
--- a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml
@@ -5,9 +5,10 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:view="using:Snap.Hutao.View"
- mc:Ignorable="d">
+ mc:Ignorable="d"
+ Closed="MainWindowClosed">
-
+
diff --git a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs
index bf7007ea..ab7740ef 100644
--- a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs
@@ -12,24 +12,25 @@ namespace Snap.Hutao;
[Injection(InjectAs.Singleton)]
public sealed partial class MainWindow : Window
{
+ private readonly AppDbContext appDbContext;
+
///
/// 构造一个新的主窗体
///
- public MainWindow()
+ /// 数据库上下文
+ public MainWindow(AppDbContext appDbContext)
{
+ this.appDbContext = appDbContext;
+
InitializeComponent();
ExtendsContentIntoTitleBar = true;
SetTitleBar(TitleBarView.DragableArea);
-
- Closed += MainWindowClosed;
}
private void MainWindowClosed(object sender, WindowEventArgs args)
{
// save datebase
- AppDbContext appDbContext = Ioc.Default.GetRequiredService();
int changes = appDbContext.SaveChanges();
-
Verify.Operation(changes == 0, "存在可避免的未经处理的数据库更改");
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/EquipType.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/EquipType.cs
new file mode 100644
index 00000000..515c2ffd
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/EquipType.cs
@@ -0,0 +1,46 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Intrinsic;
+
+///
+/// 装备类型
+/// https://github.com/Grasscutters/Grasscutter/blob/development/src/main/java/emu/grasscutter/game/inventory/EquipType.java
+///
+public enum EquipType
+{
+ ///
+ /// 无
+ ///
+ EQUIP_NONE = 0,
+
+ ///
+ /// 花
+ ///
+ EQUIP_BRACER = 1,
+
+ ///
+ /// 羽毛
+ ///
+ EQUIP_NECKLACE = 2,
+
+ ///
+ /// 沙
+ ///
+ EQUIP_SHOES = 3,
+
+ ///
+ /// 杯
+ ///
+ EQUIP_RING = 4,
+
+ ///
+ /// 头
+ ///
+ EQUIP_DRESS = 5,
+
+ ///
+ /// 武器
+ ///
+ EQUIP_WEAPON = 6,
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/FightProperty.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/FightProperty.cs
new file mode 100644
index 00000000..1a944118
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/FightProperty.cs
@@ -0,0 +1,491 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Intrinsic;
+
+///
+/// 战斗属性
+/// https://github.com/Grasscutters/Grasscutter/tree/development/src/main/java/emu/grasscutter/game/props/FightProperty.java
+///
+public enum FightProperty
+{
+ ///
+ /// 空
+ ///
+ FIGHT_PROP_NONE = 0,
+
+ ///
+ /// 基础生命值
+ ///
+ FIGHT_PROP_BASE_HP = 1,
+
+ ///
+ /// 生命值加成
+ ///
+ FIGHT_PROP_HP = 2,
+
+ ///
+ /// 生命值加成百分比
+ ///
+ FIGHT_PROP_HP_PERCENT = 3,
+
+ ///
+ /// 基础攻击力
+ ///
+ FIGHT_PROP_BASE_ATTACK = 4,
+
+ ///
+ /// 攻击力加成
+ ///
+ FIGHT_PROP_ATTACK = 5,
+
+ ///
+ /// 攻击力百分比
+ ///
+ FIGHT_PROP_ATTACK_PERCENT = 6,
+
+ ///
+ /// 基础防御力
+ ///
+ FIGHT_PROP_BASE_DEFENSE = 7,
+
+ ///
+ /// 防御力加成
+ ///
+ FIGHT_PROP_DEFENSE = 8,
+
+ ///
+ /// 防御力百分比
+ ///
+ FIGHT_PROP_DEFENSE_PERCENT = 9,
+
+ ///
+ /// 基础速度
+ ///
+ FIGHT_PROP_BASE_SPEED = 10,
+
+ ///
+ /// 速度加成
+ ///
+ FIGHT_PROP_SPEED_PERCENT = 11,
+
+ ///
+ /// ?
+ ///
+ FIGHT_PROP_HP_MP_PERCENT = 12,
+
+ ///
+ /// ?
+ ///
+ FIGHT_PROP_ATTACK_MP_PERCENT = 13,
+
+ ///
+ /// 暴击率
+ ///
+ FIGHT_PROP_CRITICAL = 20,
+
+ ///
+ /// 抗暴击率
+ ///
+ FIGHT_PROP_ANTI_CRITICAL = 21,
+
+ ///
+ /// 暴击伤害
+ ///
+ FIGHT_PROP_CRITICAL_HURT = 22,
+
+ ///
+ /// 元素充能效率
+ ///
+ FIGHT_PROP_CHARGE_EFFICIENCY = 23,
+
+ ///
+ /// 伤害加成
+ ///
+ FIGHT_PROP_ADD_HURT = 24,
+
+ ///
+ /// 抗性提升
+ ///
+ FIGHT_PROP_SUB_HURT = 25,
+
+ ///
+ /// 治疗提升
+ ///
+ FIGHT_PROP_HEAL_ADD = 26,
+
+ ///
+ /// 受治疗提升
+ ///
+ FIGHT_PROP_HEALED_ADD = 27,
+
+ ///
+ /// 元素精通
+ ///
+ FIGHT_PROP_ELEMENT_MASTERY = 28,
+
+ ///
+ /// 物理抗性提升
+ ///
+ FIGHT_PROP_PHYSICAL_SUB_HURT = 29,
+
+ ///
+ /// 物理伤害加成
+ ///
+ FIGHT_PROP_PHYSICAL_ADD_HURT = 30,
+
+ ///
+ /// 无视防御力百分比
+ ///
+ FIGHT_PROP_DEFENCE_IGNORE_RATIO = 31,
+
+ ///
+ /// 防御力降低
+ ///
+ FIGHT_PROP_DEFENCE_IGNORE_DELTA = 32,
+
+ ///
+ /// 火元素伤害加成
+ ///
+ FIGHT_PROP_FIRE_ADD_HURT = 40,
+
+ ///
+ /// 雷元素伤害加成
+ ///
+ FIGHT_PROP_ELEC_ADD_HURT = 41,
+
+ ///
+ /// 水元素伤害加成
+ ///
+ FIGHT_PROP_WATER_ADD_HURT = 42,
+
+ ///
+ /// 草元素伤害加成
+ ///
+ FIGHT_PROP_GRASS_ADD_HURT = 43,
+
+ ///
+ /// 风元素伤害加成
+ ///
+ FIGHT_PROP_WIND_ADD_HURT = 44,
+
+ ///
+ /// 岩元素伤害加成
+ ///
+ FIGHT_PROP_ROCK_ADD_HURT = 45,
+
+ ///
+ /// 冰元素伤害加成
+ ///
+ FIGHT_PROP_ICE_ADD_HURT = 46,
+
+ ///
+ /// 弱点伤害加成
+ ///
+ FIGHT_PROP_HIT_HEAD_ADD_HURT = 47,
+
+ ///
+ /// 火元素抗性提升
+ ///
+ FIGHT_PROP_FIRE_SUB_HURT = 50,
+
+ ///
+ /// 雷元素抗性提升
+ ///
+ FIGHT_PROP_ELEC_SUB_HURT = 51,
+
+ ///
+ /// 雷元素抗性提升
+ ///
+ FIGHT_PROP_WATER_SUB_HURT = 52,
+
+ ///
+ /// 草元素抗性提升
+ ///
+ FIGHT_PROP_GRASS_SUB_HURT = 53,
+
+ ///
+ /// 风元素抗性提升
+ ///
+ FIGHT_PROP_WIND_SUB_HURT = 54,
+
+ ///
+ /// 岩元素抗性提升
+ ///
+ FIGHT_PROP_ROCK_SUB_HURT = 55,
+
+ ///
+ /// 冰元素抗性提升
+ ///
+ FIGHT_PROP_ICE_SUB_HURT = 56,
+
+ ///
+ /// ?
+ ///
+ FIGHT_PROP_EFFECT_HIT = 60,
+
+ ///
+ /// ?
+ ///
+ FIGHT_PROP_EFFECT_RESIST = 61,
+
+ ///
+ /// 冻结抗性
+ ///
+ FIGHT_PROP_FREEZE_RESIST = 62,
+
+ ///
+ /// 迟缓抗性
+ ///
+ FIGHT_PROP_TORPOR_RESIST = 63,
+
+ ///
+ /// 眩晕抗性
+ ///
+ FIGHT_PROP_DIZZY_RESIST = 64,
+
+ ///
+ /// 冻结缩减
+ ///
+ FIGHT_PROP_FREEZE_SHORTEN = 65,
+
+ ///
+ /// 迟缓缩减
+ ///
+ FIGHT_PROP_TORPOR_SHORTEN = 66,
+
+ ///
+ /// 眩晕缩减
+ ///
+ FIGHT_PROP_DIZZY_SHORTEN = 67,
+
+ ///
+ /// 火元素爆发能量
+ ///
+ FIGHT_PROP_MAX_FIRE_ENERGY = 70,
+
+ ///
+ /// 雷元素爆发能量
+ ///
+ FIGHT_PROP_MAX_ELEC_ENERGY = 71,
+
+ ///
+ /// 水元素爆发能量
+ ///
+ FIGHT_PROP_MAX_WATER_ENERGY = 72,
+
+ ///
+ /// 草元素爆发能量
+ ///
+ FIGHT_PROP_MAX_GRASS_ENERGY = 73,
+
+ ///
+ /// 风元素爆发能量
+ ///
+ FIGHT_PROP_MAX_WIND_ENERGY = 74,
+
+ ///
+ /// 冰元素爆发能量
+ ///
+ FIGHT_PROP_MAX_ICE_ENERGY = 75,
+
+ ///
+ /// 岩元素爆发能量
+ ///
+ FIGHT_PROP_MAX_ROCK_ENERGY = 76,
+
+ ///
+ /// 技能冷却缩减
+ ///
+ FIGHT_PROP_SKILL_CD_MINUS_RATIO = 80,
+
+ ///
+ /// 护盾强效
+ ///
+ FIGHT_PROP_SHIELD_COST_MINUS_RATIO = 81,
+
+ ///
+ /// 火元素爆发当前能量
+ ///
+ FIGHT_PROP_CUR_FIRE_ENERGY = 1000,
+
+ ///
+ /// 雷元素爆发当前能量
+ ///
+ FIGHT_PROP_CUR_ELEC_ENERGY = 1001,
+
+ ///
+ /// 水元素爆发当前能量
+ ///
+ FIGHT_PROP_CUR_WATER_ENERGY = 1002,
+
+ ///
+ /// 草元素爆发当前能量
+ ///
+ FIGHT_PROP_CUR_GRASS_ENERGY = 1003,
+
+ ///
+ /// 风元素爆发当前能量
+ ///
+ FIGHT_PROP_CUR_WIND_ENERGY = 1004,
+
+ ///
+ /// 冰元素爆发当前能量
+ ///
+ FIGHT_PROP_CUR_ICE_ENERGY = 1005,
+
+ ///
+ /// 岩元素爆发当前能量
+ ///
+ FIGHT_PROP_CUR_ROCK_ENERGY = 1006,
+
+ ///
+ /// 当前生命值
+ ///
+ FIGHT_PROP_CUR_HP = 1010,
+
+ ///
+ /// 最大生命值
+ ///
+ FIGHT_PROP_MAX_HP = 2000,
+
+ ///
+ /// 当前攻击力
+ ///
+ FIGHT_PROP_CUR_ATTACK = 2001,
+
+ ///
+ /// 当前防御力
+ ///
+ FIGHT_PROP_CUR_DEFENSE = 2002,
+
+ ///
+ /// 当前速度
+ ///
+ FIGHT_PROP_CUR_SPEED = 2003,
+
+ ///
+ /// 总攻击力
+ ///
+ FIGHT_PROP_NONEXTRA_ATTACK = 3000,
+
+ ///
+ /// 总防御力
+ ///
+ FIGHT_PROP_NONEXTRA_DEFENSE = 3001,
+
+ ///
+ /// 总暴击率
+ ///
+ FIGHT_PROP_NONEXTRA_CRITICAL = 3002,
+
+ ///
+ /// 总抗暴击率
+ ///
+ FIGHT_PROP_NONEXTRA_ANTI_CRITICAL = 3003,
+
+ ///
+ /// 总暴击伤害
+ ///
+ FIGHT_PROP_NONEXTRA_CRITICAL_HURT = 3004,
+
+ ///
+ /// 总元素充能效率
+ ///
+ FIGHT_PROP_NONEXTRA_CHARGE_EFFICIENCY = 3005,
+
+ ///
+ /// 元素精通
+ ///
+ FIGHT_PROP_NONEXTRA_ELEMENT_MASTERY = 3006,
+
+ ///
+ /// 总物理抗性提升
+ ///
+ FIGHT_PROP_NONEXTRA_PHYSICAL_SUB_HURT = 3007,
+
+ ///
+ /// 总火元素伤害提升
+ ///
+ FIGHT_PROP_NONEXTRA_FIRE_ADD_HURT = 3008,
+
+ ///
+ /// 总雷元素伤害加成
+ ///
+ FIGHT_PROP_NONEXTRA_ELEC_ADD_HURT = 3009,
+
+ ///
+ /// 总水元素伤害加成
+ ///
+ FIGHT_PROP_NONEXTRA_WATER_ADD_HURT = 3010,
+
+ ///
+ /// 总草元素伤害加成
+ ///
+ FIGHT_PROP_NONEXTRA_GRASS_ADD_HURT = 3011,
+
+ ///
+ /// 总风元素伤害加成
+ ///
+ FIGHT_PROP_NONEXTRA_WIND_ADD_HURT = 3012,
+
+ ///
+ /// 总岩元素伤害加成
+ ///
+ FIGHT_PROP_NONEXTRA_ROCK_ADD_HURT = 3013,
+
+ ///
+ /// 总冰元素伤害加成
+ ///
+ FIGHT_PROP_NONEXTRA_ICE_ADD_HURT = 3014,
+
+ ///
+ /// 总火元素抗性提升
+ ///
+ FIGHT_PROP_NONEXTRA_FIRE_SUB_HURT = 3015,
+
+ ///
+ /// 总雷元素抗性提升
+ ///
+ FIGHT_PROP_NONEXTRA_ELEC_SUB_HURT = 3016,
+
+ ///
+ /// 总水元素抗性提升
+ ///
+ FIGHT_PROP_NONEXTRA_WATER_SUB_HURT = 3017,
+
+ ///
+ /// 总草元素抗性提升
+ ///
+ FIGHT_PROP_NONEXTRA_GRASS_SUB_HURT = 3018,
+
+ ///
+ /// 总风元素抗性提升
+ ///
+ FIGHT_PROP_NONEXTRA_WIND_SUB_HURT = 3019,
+
+ ///
+ /// 总岩元素抗性提升
+ ///
+ FIGHT_PROP_NONEXTRA_ROCK_SUB_HURT = 3020,
+
+ ///
+ /// 总冰元素抗性提升
+ ///
+ FIGHT_PROP_NONEXTRA_ICE_SUB_HURT = 3021,
+
+ ///
+ /// 总冷却缩减
+ ///
+ FIGHT_PROP_NONEXTRA_SKILL_CD_MINUS_RATIO = 3022,
+
+ ///
+ /// 总护盾强效
+ ///
+ FIGHT_PROP_NONEXTRA_SHIELD_COST_MINUS_RATIO = 3023,
+
+ ///
+ /// 总物理伤害加成
+ ///
+ FIGHT_PROP_NONEXTRA_PHYSICAL_ADD_HURT = 3024,
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/ItemQuality.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ItemQuality.cs
similarity index 93%
rename from src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/ItemQuality.cs
rename to src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ItemQuality.cs
index b6af18e7..f6b82fd3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/ItemQuality.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ItemQuality.cs
@@ -1,7 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
+namespace Snap.Hutao.Model.Intrinsic;
///
/// 稀有度
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ItemType.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ItemType.cs
new file mode 100644
index 00000000..a415446c
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/ItemType.cs
@@ -0,0 +1,46 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Intrinsic;
+
+///
+/// 物品类型
+/// https://github.com/Grasscutters/Grasscutter/tree/development/src/main/java/emu/grasscutter/game/inventory/ItemType.java
+///
+public enum ItemType
+{
+ ///
+ /// 无
+ ///
+ ITEM_NONE = 0,
+
+ ///
+ /// 贵重道具
+ ///
+ ITEM_VIRTUAL = 1,
+
+ ///
+ /// 材料
+ ///
+ ITEM_MATERIAL = 2,
+
+ ///
+ /// 圣遗物
+ ///
+ ITEM_RELIQUARY = 3,
+
+ ///
+ /// 武器
+ ///
+ ITEM_WEAPON = 4,
+
+ ///
+ /// 任务等
+ ///
+ ITEM_DISPLAY = 5,
+
+ ///
+ /// 家具
+ ///
+ ITEM_FURNITURE = 6,
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/WeaponType.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/WeaponType.cs
similarity index 96%
rename from src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/WeaponType.cs
rename to src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/WeaponType.cs
index 3709bd1d..724bef79 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/WeaponType.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/WeaponType.cs
@@ -1,7 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
+namespace Snap.Hutao.Model.Intrinsic;
///
/// 武器类型
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Achievement.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Achievement.cs
new file mode 100644
index 00000000..1354d463
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Achievement.cs
@@ -0,0 +1,57 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+
+namespace Snap.Hutao.Model.Metadata.Achievement;
+
+///
+/// 成就
+///
+public class Achievement
+{
+ ///
+ /// Id
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 分类Id
+ ///
+ public int Goal { get; set; }
+
+ ///
+ /// 排序顺序
+ ///
+ public int Order { get; set; }
+
+ ///
+ /// 标题
+ ///
+ public string Title { get; set; } = default!;
+
+ ///
+ /// 描述
+ ///
+ public string Description { get; set; } = default!;
+
+ ///
+ /// 完成奖励
+ ///
+ public Reward FinishReward { get; set; } = default!;
+
+ ///
+ /// 总进度
+ ///
+ public int Progress { get; set; }
+
+ ///
+ /// 触发器
+ ///
+ public IEnumerable? Triggers { get; set; }
+
+ ///
+ /// 图标
+ ///
+ public string? Icon { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementGoal.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementGoal.cs
new file mode 100644
index 00000000..279827d7
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementGoal.cs
@@ -0,0 +1,30 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Metadata.Achievement;
+
+///
+/// 成就分类
+///
+public class AchievementGoal
+{
+ ///
+ /// 排序顺序
+ ///
+ public int Order { get; set; }
+
+ ///
+ /// 名称
+ ///
+ public string Name { get; set; } = default!;
+
+ ///
+ /// 完成奖励
+ ///
+ public Reward? FinishReward { get; set; }
+
+ ///
+ /// 图标
+ ///
+ public string? Icon { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTrigger.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTrigger.cs
new file mode 100644
index 00000000..5178701f
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTrigger.cs
@@ -0,0 +1,30 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Metadata.Achievement;
+
+///
+/// 成就触发器
+///
+public class AchievementTrigger
+{
+ ///
+ /// 触发器类型
+ ///
+ public AchievementTriggerType Type { get; set; }
+
+ ///
+ /// Id
+ ///
+ public string Id { get; set; } = default!;
+
+ ///
+ /// 标题
+ ///
+ public string Title { get; set; } = default!;
+
+ ///
+ /// 描述
+ ///
+ public string Description { get; set; } = default!;
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTriggerType.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTriggerType.cs
new file mode 100644
index 00000000..ec1d0dcb
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTriggerType.cs
@@ -0,0 +1,25 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Metadata.Achievement;
+
+///
+/// 成就触发器类型
+///
+public enum AchievementTriggerType
+{
+ ///
+ /// 任务
+ ///
+ Quest = 1,
+
+ ///
+ /// 子任务
+ ///
+ SubQuest = 2,
+
+ ///
+ /// 日常任务
+ ///
+ DailyTask = 3,
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Reward.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Reward.cs
new file mode 100644
index 00000000..2472f1b6
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Reward.cs
@@ -0,0 +1,20 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Metadata.Achievement;
+
+///
+/// 奖励
+///
+public class Reward
+{
+ ///
+ /// Id
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 数量
+ ///
+ public int Count { get; set; }
+}
\ 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
new file mode 100644
index 00000000..e59594ad
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs
@@ -0,0 +1,83 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Intrinsic;
+using System.Collections.Generic;
+
+namespace Snap.Hutao.Model.Metadata.Avatar;
+
+///
+/// 角色
+///
+public class Avatar
+{
+ ///
+ /// Id
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 排序号
+ ///
+ public int Sort { get; set; }
+
+ ///
+ /// 体型
+ ///
+ public string 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 ItemQuality Quality { get; set; }
+
+ ///
+ /// 武器类型
+ ///
+ public WeaponType Weapon { get; set; }
+
+ ///
+ /// 属性
+ ///
+ public PropertyInfo Property { get; set; } = default!;
+
+ ///
+ /// 技能
+ ///
+ public SkillDepot SkillDepot { get; set; } = default!;
+
+ ///
+ /// 好感信息
+ ///
+ public FetterInfo FetterInfo { get; set; } = default!;
+
+ ///
+ /// 皮肤
+ ///
+ public IEnumerable Costumes { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Costume.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Costume.cs
new file mode 100644
index 00000000..a434f13b
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Costume.cs
@@ -0,0 +1,30 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Metadata.Avatar;
+
+///
+/// 皮肤
+///
+public class Costume
+{
+ ///
+ /// Id
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 名称
+ ///
+ public string Name { get; set; } = default!;
+
+ ///
+ /// 描述
+ ///
+ public string Description { get; set; } = default!;
+
+ ///
+ /// 是否为默认
+ ///
+ public bool IsDefault { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/DescParam.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/DescParam.cs
new file mode 100644
index 00000000..452982c5
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/DescParam.cs
@@ -0,0 +1,22 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+
+namespace Snap.Hutao.Model.Metadata.Avatar;
+
+///
+/// 描述与参数
+///
+public class DescParam
+{
+ ///
+ /// 描述
+ ///
+ public IEnumerable Descriptions { get; set; } = default!;
+
+ ///
+ /// 参数
+ ///
+ public IEnumerable> Parameters { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Fetter.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Fetter.cs
new file mode 100644
index 00000000..139ba087
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Fetter.cs
@@ -0,0 +1,20 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Metadata.Avatar;
+
+///
+/// 好感故事
+///
+public class Fetter
+{
+ ///
+ /// 标题
+ ///
+ public string Title { get; set; } = default!;
+
+ ///
+ /// 上下文
+ ///
+ public string Context { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/FetterInfo.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/FetterInfo.cs
new file mode 100644
index 00000000..e7487d0e
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/FetterInfo.cs
@@ -0,0 +1,92 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+
+namespace Snap.Hutao.Model.Metadata.Avatar;
+
+///
+/// 好感信息
+///
+public class FetterInfo
+{
+ ///
+ /// 称号
+ ///
+ public string Title { get; set; } = default!;
+
+ ///
+ /// 详细
+ ///
+ public string Detail { get; set; } = default!;
+
+ ///
+ /// 地区
+ ///
+ public string Association { get; set; } = default!;
+
+ ///
+ /// 属于组织
+ ///
+ public string Native { get; set; } = default!;
+
+ ///
+ /// 生月
+ ///
+ public int BirthMonth { get; set; }
+
+ ///
+ /// 生日
+ ///
+ public int BirthDay { get; set; }
+
+ ///
+ /// 神之眼属性-前
+ ///
+ public string VisionBefore { get; set; } = default!;
+
+ ///
+ /// 神之眼属性-后
+ ///
+ public string VisionAfter { get; set; } = default!;
+
+ ///
+ /// 命座-前
+ ///
+ public string ConstellationBefore { get; set; } = default!;
+
+ ///
+ /// 命座-后
+ ///
+ public string ConstellationAfter { get; set; } = default!;
+
+ ///
+ /// 中文CV
+ ///
+ public string CvChinese { get; set; } = default!;
+
+ ///
+ /// 日语CV
+ ///
+ public string CvJapanese { get; set; } = default!;
+
+ ///
+ /// 英语CV
+ ///
+ public string CvEnglish { get; set; } = default!;
+
+ ///
+ /// 韩语CV
+ ///
+ public string CvKorean { get; set; } = default!;
+
+ ///
+ /// 好感语音
+ ///
+ public IEnumerable Fetters { get; set; } = default!;
+
+ ///
+ /// 好感故事
+ ///
+ public IEnumerable FetterStories { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.cs
new file mode 100644
index 00000000..b24fdcea
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProudableSkill.cs
@@ -0,0 +1,21 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Metadata.Avatar;
+
+///
+/// 技能信息
+///
+public class ProudableSkill : SkillBase
+{
+
+ ///
+ /// 组Id
+ ///
+ public int GroupId { get; set; }
+
+ ///
+ /// 提升属性
+ ///
+ public DescParam Proud { get; set; } = default!;
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillBase.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillBase.cs
new file mode 100644
index 00000000..57575ec3
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillBase.cs
@@ -0,0 +1,31 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Metadata.Avatar;
+
+///
+/// 技能基础
+/// 命座
+///
+public class SkillBase
+{
+ ///
+ /// Id
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 名称
+ ///
+ public string Name { get; set; } = default!;
+
+ ///
+ /// 描述
+ ///
+ public string Description { get; set; } = default!;
+
+ ///
+ /// 图标
+ ///
+ public string Icon { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs
new file mode 100644
index 00000000..a61e6604
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs
@@ -0,0 +1,32 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+
+namespace Snap.Hutao.Model.Metadata.Avatar;
+
+///
+/// 技能组
+///
+public class SkillDepot
+{
+ ///
+ /// 技能天赋
+ ///
+ public IEnumerable Skills { get; set; } = default!;
+
+ ///
+ /// 大招
+ ///
+ public ProudableSkill EnergySkill { get; set; } = default!;
+
+ ///
+ /// 固有天赋
+ ///
+ public IEnumerable Inherents { get; set; } = default!;
+
+ ///
+ /// 命之座
+ ///
+ public IEnumerable Talents { get; set; } = default!;
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/LevelParam.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/LevelParam.cs
new file mode 100644
index 00000000..ad14e546
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/LevelParam.cs
@@ -0,0 +1,23 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+
+namespace Snap.Hutao.Model.Metadata;
+
+///
+/// 等级与参数
+///
+/// 等级的类型
+public class LevelParam
+{
+ ///
+ /// 等级
+ ///
+ public TLevel Level { get; set; } = default!;
+
+ ///
+ /// 参数
+ ///
+ public IEnumerable Parameters { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/PropertyInfo.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/PropertyInfo.cs
new file mode 100644
index 00000000..f4263a7d
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/PropertyInfo.cs
@@ -0,0 +1,23 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Intrinsic;
+using System.Collections.Generic;
+
+namespace Snap.Hutao.Model.Metadata;
+
+///
+/// 属性信息
+///
+public class PropertyInfo
+{
+ ///
+ /// 提升的属性
+ ///
+ public IEnumerable Properties { get; set; } = default!;
+
+ ///
+ /// 参数
+ ///
+ public IEnumerable> Parameters { get; set; } = default!;
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/Reliquary.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/Reliquary.cs
new file mode 100644
index 00000000..42310af5
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/Reliquary.cs
@@ -0,0 +1,53 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Intrinsic;
+using System.Collections.Generic;
+
+namespace Snap.Hutao.Model.Metadata.Reliquary;
+
+///
+/// 圣遗物信息
+///
+public class Reliquary
+{
+ ///
+ /// 表示同种类的Id
+ ///
+ public IEnumerable Ids { get; set; } = default!;
+
+ ///
+ /// 允许出现的等级
+ ///
+ public IEnumerable RankLevels { get; set; } = default!;
+
+ ///
+ /// 套装Id
+ ///
+ public int SetId { get; set; }
+
+ ///
+ /// 装备类型
+ ///
+ public EquipType EquipType { get; set; } = default!;
+
+ ///
+ /// 物品类型
+ ///
+ public ItemType ItemType { get; set; } = default!;
+
+ ///
+ /// 名称
+ ///
+ public string Name { get; set; } = default!;
+
+ ///
+ /// 描述
+ ///
+ public string Description { get; set; } = default!;
+
+ ///
+ /// 图标
+ ///
+ public string Icon { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquaryAffix.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquaryAffix.cs
new file mode 100644
index 00000000..1cd4779f
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquaryAffix.cs
@@ -0,0 +1,15 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Metadata.Reliquary;
+
+///
+/// 圣遗物突破属性
+///
+public class ReliquaryAffix : ReliquaryAffixBase
+{
+ ///
+ /// 值
+ ///
+ public double Value { get; set; }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquaryAffixBase.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquaryAffixBase.cs
new file mode 100644
index 00000000..d4dd9c22
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquaryAffixBase.cs
@@ -0,0 +1,22 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Intrinsic;
+
+namespace Snap.Hutao.Model.Metadata.Reliquary;
+
+///
+/// 圣遗物主属性
+///
+public class ReliquaryAffixBase
+{
+ ///
+ /// Id
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 战斗属性
+ ///
+ public FightProperty Type { get; set; }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquarySet.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquarySet.cs
new file mode 100644
index 00000000..743e55e7
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Reliquary/ReliquarySet.cs
@@ -0,0 +1,27 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+
+namespace Snap.Hutao.Model.Metadata.Reliquary;
+
+///
+/// 圣遗物套装
+///
+public class ReliquarySet
+{
+ ///
+ /// 套装Id
+ ///
+ public int SetId { get; set; } = default!;
+
+ ///
+ /// 需要的数量
+ ///
+ public IEnumerable NeedNumber { get; set; } = default!;
+
+ ///
+ /// 描述
+ ///
+ public IEnumerable Descriptions { get; set; } = default!;
+}
\ 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
new file mode 100644
index 00000000..90371b71
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.cs
@@ -0,0 +1,52 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Intrinsic;
+
+namespace Snap.Hutao.Model.Metadata.Weapon;
+
+///
+/// 武器
+///
+public class Weapon
+{
+ ///
+ /// Id
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 武器类型
+ ///
+ public WeaponType WeaponType { get; set; }
+
+ ///
+ /// 等级
+ ///
+ public ItemQuality 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 PropertyInfo Property { get; set; } = default!;
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
index 34781fb9..04580171 100644
--- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
+++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
@@ -9,7 +9,7 @@
+ Version="1.0.6.0" />
胡桃
@@ -32,13 +32,20 @@
EntryPoint="$targetentrypoint$">
+
+
+
+ 胡桃
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/Program.cs b/src/Snap.Hutao/Snap.Hutao/Program.cs
index a61fd43f..8fd06c9a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Program.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Program.cs
@@ -20,8 +20,8 @@ public static class Program
private static void Main(string[] args)
{
XamlCheckProcessRequirements();
-
ComWrappersSupport.InitializeComWrappers();
+
Application.Start(p =>
{
DispatcherQueueSynchronizationContext context = new(DispatcherQueue.GetForCurrentThread());
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IInfoBarService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IInfoBarService.cs
index 74e40a43..86d35622 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IInfoBarService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IInfoBarService.cs
@@ -45,7 +45,7 @@ public interface IInfoBarService
///
/// 消息
/// 关闭延迟
- void Information(string message, int delay = 3000);
+ void Information(string message, int delay = 5000);
///
/// 显示提示信息
@@ -53,7 +53,7 @@ public interface IInfoBarService
/// 标题
/// 消息
/// 关闭延迟
- void Information(string title, string message, int delay = 3000);
+ void Information(string title, string message, int delay = 5000);
///
/// 使用指定的 初始化服务
@@ -66,7 +66,7 @@ public interface IInfoBarService
///
/// 消息
/// 关闭延迟
- void Success(string message, int delay = 3000);
+ void Success(string message, int delay = 5000);
///
/// 显示成功信息
@@ -74,7 +74,7 @@ public interface IInfoBarService
/// 标题
/// 消息
/// 关闭延迟
- void Success(string title, string message, int delay = 3000);
+ void Success(string title, string message, int delay = 5000);
///
/// 显示警告信息
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IMetadataService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IMetadataService.cs
new file mode 100644
index 00000000..597ed47b
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/IMetadataService.cs
@@ -0,0 +1,79 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Metadata.Achievement;
+using Snap.Hutao.Model.Metadata.Avatar;
+using Snap.Hutao.Model.Metadata.Reliquary;
+using Snap.Hutao.Model.Metadata.Weapon;
+using System.Collections.Generic;
+
+namespace Snap.Hutao.Service.Metadata;
+
+///
+/// 元数据服务
+///
+internal interface IMetadataService
+{
+ ///
+ /// 异步初始化服务,尝试更新元数据
+ ///
+ /// 取消令牌
+ /// 初始化是否成功
+ ValueTask InitializeAsync(CancellationToken token = default);
+
+ ///
+ /// 异步获取成就列表
+ ///
+ /// 取消令牌
+ /// 成就列表
+ ValueTask> GetAchievementsAsync(CancellationToken token = default);
+
+ ///
+ /// 异步获取成就分类列表
+ ///
+ /// 取消令牌
+ /// 成就分类列表
+ ValueTask> GetAchievementGoalsAsync(CancellationToken token = default);
+
+ ///
+ /// 异步获取角色列表
+ ///
+ /// 取消令牌
+ /// 角色列表
+ ValueTask> GetAvatarsAsync(CancellationToken token = default);
+
+ ///
+ /// 异步获取圣遗物列表
+ ///
+ /// 取消令牌
+ /// 圣遗物列表
+ ValueTask> GetReliquariesAsync(CancellationToken token = default);
+
+ ///
+ /// 异步获取圣遗物强化属性列表
+ ///
+ /// 取消令牌
+ /// 圣遗物强化属性列表
+ ValueTask> GetReliquaryAffixesAsync(CancellationToken token = default);
+
+ ///
+ /// 异步获取圣遗物主属性强化属性列表
+ ///
+ /// 取消令牌
+ /// 圣遗物强化属性列表
+ ValueTask> GetReliquaryMainAffixesAsync(CancellationToken token = default);
+
+ ///
+ /// 异步获取武器列表
+ ///
+ /// 取消令牌
+ /// 武器列表
+ ValueTask> GetWeaponsAsync(CancellationToken token = default);
+
+ ///
+ /// 异步更新元数据
+ ///
+ /// 取消令牌
+ /// 更新是否完成
+ Task UpdateMetadataAsync(CancellationToken token = default);
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs b/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs
index 4f76b449..1f829ce8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs
@@ -14,7 +14,7 @@ namespace Snap.Hutao.Service;
[Injection(InjectAs.Transient, typeof(IAnnouncementService))]
internal class AnnouncementService : IAnnouncementService
{
- private const string CacheKey = $"{nameof(IAnnouncementService)}.Cache.{nameof(AnnouncementWrapper)}";
+ private const string CacheKey = $"{nameof(AnnouncementService)}.Cache.{nameof(AnnouncementWrapper)}";
private readonly AnnouncementClient announcementClient;
private readonly IMemoryCache memoryCache;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs b/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs
index cf3d5db2..718d05da 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs
@@ -19,25 +19,25 @@ internal class InfoBarService : IInfoBarService
}
///
- public void Information(string message, int delay = 3000)
+ public void Information(string message, int delay = 5000)
{
PrepareInfoBarAndShow(InfoBarSeverity.Informational, null, message, delay);
}
///
- public void Information(string title, string message, int delay = 3000)
+ public void Information(string title, string message, int delay = 5000)
{
PrepareInfoBarAndShow(InfoBarSeverity.Informational, title, message, delay);
}
///
- public void Success(string message, int delay = 3000)
+ public void Success(string message, int delay = 5000)
{
PrepareInfoBarAndShow(InfoBarSeverity.Success, null, message, delay);
}
///
- public void Success(string title, string message, int delay = 3000)
+ public void Success(string title, string message, int delay = 5000)
{
PrepareInfoBarAndShow(InfoBarSeverity.Success, title, message, delay);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/MetadataService.cs b/src/Snap.Hutao/Snap.Hutao/Service/MetadataService.cs
new file mode 100644
index 00000000..08109eae
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/MetadataService.cs
@@ -0,0 +1,249 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Microsoft.Extensions.Caching.Memory;
+using Snap.Hutao.Context.FileSystem;
+using Snap.Hutao.Core.Abstraction;
+using Snap.Hutao.Model.Metadata.Achievement;
+using Snap.Hutao.Model.Metadata.Avatar;
+using Snap.Hutao.Model.Metadata.Reliquary;
+using Snap.Hutao.Model.Metadata.Weapon;
+using Snap.Hutao.Service.Abstraction;
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Http;
+using System.Net.Http.Json;
+using System.Security.Cryptography;
+
+namespace Snap.Hutao.Service.Metadata;
+
+///
+/// 元数据服务
+///
+[Injection(InjectAs.Singleton, typeof(IMetadataService))]
+internal class MetadataService : IMetadataService, ISupportAsyncInitialization
+{
+ private const string MetaAPIHost = "http://hutao-metadata.snapgenshin.com";
+ private const string MetaFileName = "Meta.json";
+ private readonly IInfoBarService infoBarService;
+ private readonly HttpClient httpClient;
+ private readonly FileSystemContext metadataContext;
+ private readonly JsonSerializerOptions options;
+ private readonly ILogger logger;
+ private readonly IMemoryCache memoryCache;
+
+ private bool isInitialized = false;
+
+ ///
+ /// 构造一个新的元数据服务
+ ///
+ /// 信息条服务
+ /// http客户端
+ /// 我的文档上下文
+ /// json序列化选项
+ /// 日志器
+ /// 内存缓存
+ public MetadataService(
+ IInfoBarService infoBarService,
+ HttpClient httpClient,
+ MetadataContext metadataContext,
+ JsonSerializerOptions options,
+ ILogger logger,
+ IMemoryCache memoryCache)
+ {
+ this.infoBarService = infoBarService;
+ this.httpClient = httpClient;
+ this.metadataContext = metadataContext;
+ this.options = options;
+ this.logger = logger;
+ this.memoryCache = memoryCache;
+ }
+
+ ///
+ public bool IsInitialized { get => isInitialized; private set => isInitialized = value; }
+
+ ///
+ public async ValueTask InitializeAsync(CancellationToken token = default)
+ {
+ if (IsInitialized)
+ {
+ return true;
+ }
+
+ metadataContext.EnsureDirectory();
+ if (metadataContext.FileExists(MetaFileName))
+ {
+ IDictionary? metaMd5Map;
+ using (Stream metaFile = metadataContext.OpenRead(MetaFileName))
+ {
+ metaMd5Map = await JsonSerializer
+ .DeserializeAsync>(metaFile, options, token)
+ .ConfigureAwait(false);
+ }
+
+ await CheckMetadataAsync(Must.NotNull(metaMd5Map!), token)
+ .ConfigureAwait(false);
+
+ IsInitialized = true;
+ }
+ else
+ {
+ IsInitialized = await UpdateMetadataAsync(token)
+ .ConfigureAwait(false);
+ }
+
+ return IsInitialized;
+ }
+
+ ///
+ public async Task UpdateMetadataAsync(CancellationToken token = default)
+ {
+ IDictionary? metaMd5Map = await httpClient
+ .GetFromJsonAsync>($"{MetaAPIHost}/{MetaFileName}", options, token)
+ .ConfigureAwait(false);
+
+ if (metaMd5Map is null)
+ {
+ infoBarService.Error("元数据校验文件解析失败");
+ return false;
+ }
+
+ await CheckMetadataAsync(metaMd5Map, token).ConfigureAwait(false);
+
+ using (FileStream metaFileStream = metadataContext.Create(MetaFileName))
+ {
+ await JsonSerializer
+ .SerializeAsync(metaFileStream, metaMd5Map, options, token)
+ .ConfigureAwait(false);
+ }
+
+ return true;
+ }
+
+ ///
+ public ValueTask> GetAchievementGoalsAsync(CancellationToken token = default)
+ {
+ return GetMetadataAsync>("AchievementGoal", token);
+ }
+
+ ///
+ public ValueTask> GetAchievementsAsync(CancellationToken token = default)
+ {
+ return GetMetadataAsync>("Achievement", token);
+ }
+
+ ///
+ public ValueTask> GetAvatarsAsync(CancellationToken token = default)
+ {
+ return GetMetadataAsync>("Avatar", token);
+ }
+
+ ///
+ public ValueTask> GetReliquariesAsync(CancellationToken token = default)
+ {
+ return GetMetadataAsync>("Reliquary", token);
+ }
+
+ ///
+ public ValueTask> GetReliquaryAffixesAsync(CancellationToken token = default)
+ {
+ return GetMetadataAsync>("ReliquaryAffix", token);
+ }
+
+ ///
+ public ValueTask> GetReliquaryMainAffixesAsync(CancellationToken token = default)
+ {
+ return GetMetadataAsync>("ReliquaryMainAffix", token);
+ }
+
+ ///
+ public ValueTask> GetWeaponsAsync(CancellationToken token = default)
+ {
+ return GetMetadataAsync>("Weapon", token);
+ }
+
+ private async ValueTask GetMetadataAsync(string fileName, CancellationToken token)
+ where T : class
+ {
+ Verify.Operation(IsInitialized, "元数据服务尚未初始化,或初始化失败");
+ string cacheKey = $"{nameof(MetadataService)}.Cache.{fileName}";
+
+ if (memoryCache.TryGetValue(cacheKey, out object? value))
+ {
+ return Must.NotNull((value as T)!);
+ }
+
+ T? result = await JsonSerializer
+ .DeserializeAsync(metadataContext.OpenRead($"{fileName}.json"), options, token)
+ .ConfigureAwait(false);
+
+ return memoryCache.Set(cacheKey, Must.NotNull(result!));
+ }
+
+ private async Task GetFileMd5Async(string fileFullName, CancellationToken token)
+ {
+ using (FileStream stream = metadataContext.OpenRead(fileFullName))
+ {
+ byte[] bytes = await MD5.Create()
+ .ComputeHashAsync(stream, token)
+ .ConfigureAwait(false);
+
+ return Convert.ToHexString(bytes);
+ }
+ }
+
+ ///
+ /// 检查元数据的Md5值是否匹配
+ /// 如果不匹配则尝试下载
+ ///
+ /// 元数据校验表
+ /// 取消令牌
+ /// 令牌
+ private async Task CheckMetadataAsync(IDictionary metaMd5Map, CancellationToken token)
+ {
+ // enumerate files and compare md5
+ foreach ((string fileName, string md5) in metaMd5Map)
+ {
+ string fileFullName = $"{fileName}.json";
+ bool skip = false;
+
+ if (metadataContext.FileExists(fileFullName))
+ {
+ skip = md5 == await GetFileMd5Async(fileFullName, token)
+ .ConfigureAwait(false);
+ }
+
+ if (!skip)
+ {
+ logger.LogInformation("{file} 文件 MD5 不匹配", fileFullName);
+
+ await DownloadMetadataAsync(fileFullName, token)
+ .ConfigureAwait(false);
+ }
+ }
+ }
+
+ private async Task DownloadMetadataAsync(string fileFullName, CancellationToken token)
+ {
+ Stream sourceStream = await httpClient
+ .GetStreamAsync($"{MetaAPIHost}/{fileFullName}", token)
+ .ConfigureAwait(false);
+
+ // Write stream while convert LF to CRLF
+ using (StreamReader streamReader = new(sourceStream))
+ {
+ using (StreamWriter streamWriter = new(metadataContext.Create(fileFullName)))
+ {
+ while (await streamReader.ReadLineAsync().ConfigureAwait(false) is string line)
+ {
+ await (streamReader.EndOfStream
+ ? streamWriter.WriteAsync(line) // Don't append the last line
+ : streamWriter.WriteLineAsync(line))
+ .ConfigureAwait(false);
+ }
+ }
+ }
+
+ logger.LogInformation("{file} 下载完成", fileFullName);
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationAwaiter.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationAwaiter.cs
similarity index 90%
rename from src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationAwaiter.cs
rename to src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationAwaiter.cs
index b25ad268..15ad7ec4 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationAwaiter.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationAwaiter.cs
@@ -1,7 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-namespace Snap.Hutao.Service.Abstraction.Navigation;
+namespace Snap.Hutao.Service.Navigation;
///
/// 表示导航等待器
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationExtra.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationExtra.cs
similarity index 80%
rename from src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationExtra.cs
rename to src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationExtra.cs
index 418f0c4c..6d286a04 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationExtra.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationExtra.cs
@@ -1,8 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-namespace Snap.Hutao.Service.Abstraction.Navigation;
+namespace Snap.Hutao.Service.Navigation;
+///
+/// 为 提供抽象接口
+///
public interface INavigationExtra
{
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs
similarity index 98%
rename from src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationService.cs
rename to src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs
index e3921cc2..19ae3f58 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/INavigationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs
@@ -3,7 +3,7 @@
using Microsoft.UI.Xaml.Controls;
-namespace Snap.Hutao.Service.Abstraction.Navigation;
+namespace Snap.Hutao.Service.Navigation;
///
/// 导航服务
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/NavigationExtra.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationExtra.cs
similarity index 95%
rename from src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/NavigationExtra.cs
rename to src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationExtra.cs
index fb0508d9..7b120f6f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/NavigationExtra.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationExtra.cs
@@ -1,7 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-namespace Snap.Hutao.Service.Abstraction.Navigation;
+namespace Snap.Hutao.Service.Navigation;
///
/// 导航额外信息
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/NavigationResult.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationResult.cs
similarity index 88%
rename from src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/NavigationResult.cs
rename to src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationResult.cs
index 0f6e36bc..c56898cf 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Abstraction/Navigation/NavigationResult.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationResult.cs
@@ -1,7 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-namespace Snap.Hutao.Service.Abstraction.Navigation;
+namespace Snap.Hutao.Service.Navigation;
///
/// 导航结果
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/NavigationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs
similarity index 98%
rename from src/Snap.Hutao/Snap.Hutao/Service/NavigationService.cs
rename to src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs
index 4d8d4d35..91855e21 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/NavigationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs
@@ -4,12 +4,11 @@
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Service.Abstraction;
-using Snap.Hutao.Service.Abstraction.Navigation;
using Snap.Hutao.View.Helper;
using Snap.Hutao.View.Page;
using System.Linq;
-namespace Snap.Hutao.Service;
+namespace Snap.Hutao.Service.Navigation;
///
/// 导航服务
diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
index 1ac200a0..98e100f4 100644
--- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
+++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
@@ -5,8 +5,8 @@
10.0.17763.0
Snap.Hutao
app.manifest
- x86;x64;arm64
- win10-x86;win10-x64;win10-arm64
+ x64
+ win10-x64
win10-$(Platform).pubxml
true
false
@@ -31,9 +31,11 @@
+
+
@@ -130,6 +132,16 @@
+
+
+ MSBuild:Compile
+
+
+
+
+ MSBuild:Compile
+
+
MSBuild:Compile
diff --git a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml
index fa1849e1..b7d33f69 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml
@@ -28,6 +28,12 @@
+
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs
index 4b0109b7..a78cba36 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs
@@ -3,7 +3,8 @@
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Service.Abstraction;
-using Snap.Hutao.Service.Abstraction.Navigation;
+using Snap.Hutao.Service.Navigation;
+using Snap.Hutao.View.Page;
namespace Snap.Hutao.View;
@@ -27,6 +28,6 @@ public sealed partial class MainView : UserControl
navigationService = Ioc.Default.GetRequiredService();
navigationService.Initialize(NavView, ContentFrame);
- navigationService.Navigate(INavigationAwaiter.Default, true);
+ navigationService.Navigate(INavigationAwaiter.Default, false);
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml
new file mode 100644
index 00000000..86c0aed5
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml.cs
new file mode 100644
index 00000000..c4cfb5f9
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml.cs
@@ -0,0 +1,22 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Control.Cancellable;
+using Snap.Hutao.ViewModel;
+
+namespace Snap.Hutao.View.Page;
+
+///
+/// 成就页面
+///
+public sealed partial class AchievementPage : CancellablePage
+{
+ ///
+ /// 构造一个新的成就页面
+ ///
+ public AchievementPage()
+ {
+ InitializeWith();
+ InitializeComponent();
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs
index 316a7b6b..f9d36df9 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs
@@ -4,7 +4,8 @@
using Microsoft.UI.Xaml.Navigation;
using Microsoft.VisualStudio.Threading;
using Snap.Hutao.Core;
-using Snap.Hutao.Service.Abstraction.Navigation;
+using Snap.Hutao.Extension;
+using Snap.Hutao.Service.Navigation;
namespace Snap.Hutao.View.Page;
@@ -38,7 +39,7 @@ openInWebview: function(url){ location.href = url }}";
if (e.Parameter is INavigationExtra extra)
{
targetContent = extra.Data as string;
- LoadAnnouncementAsync(extra).Forget();
+ LoadAnnouncementAsync(extra).SafeForget();
}
}
@@ -60,4 +61,4 @@ openInWebview: function(url){ location.href = url }}";
WebView.NavigateToString(targetContent);
extra.NotifyNavigationCompleted();
}
-}
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml
index 6ffc9406..d2c5bf77 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml
@@ -43,9 +43,9 @@
Margin="0,0,0,12"
Style="{StaticResource TitleTextBlockStyle}"/>
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml.cs
index dfc07c1d..81a28f8c 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml.cs
@@ -16,7 +16,7 @@ public sealed partial class AnnouncementPage : CancellablePage
///
public AnnouncementPage()
{
- Initialize(Ioc.Default.GetRequiredService());
+ InitializeWith();
InitializeComponent();
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WelcomePage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WelcomePage.xaml
new file mode 100644
index 00000000..5f7fba55
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WelcomePage.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WelcomePage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/WelcomePage.xaml.cs
new file mode 100644
index 00000000..3ac10f4e
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WelcomePage.xaml.cs
@@ -0,0 +1,18 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.View.Page;
+
+///
+/// 欢迎页
+///
+public sealed partial class WelcomePage : Microsoft.UI.Xaml.Controls.Page
+{
+ ///
+ /// 构造一个新的欢迎页
+ ///
+ public WelcomePage()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs
new file mode 100644
index 00000000..334af782
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs
@@ -0,0 +1,66 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.WinUI.UI;
+using Microsoft.VisualStudio.Threading;
+using Snap.Hutao.Control.Cancellable;
+using Snap.Hutao.Factory.Abstraction;
+using Snap.Hutao.Model.Metadata.Achievement;
+using Snap.Hutao.Service.Metadata;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Snap.Hutao.ViewModel;
+
+///
+/// 成就视图模型
+///
+[Injection(InjectAs.Transient)]
+internal class AchievementViewModel : ObservableObject, ISupportCancellation
+{
+ private readonly IMetadataService metadataService;
+ private AdvancedCollectionView? achievementsView;
+
+ ///
+ /// 构造一个新的成就视图模型
+ ///
+ /// 元数据服务
+ /// 异步命令工厂
+ public AchievementViewModel(IMetadataService metadataService, IAsyncRelayCommandFactory asyncRelayCommandFactory)
+ {
+ this.metadataService = metadataService;
+ OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync);
+ }
+
+ ///
+ public CancellationToken CancellationToken { get; set; }
+
+ ///
+ /// 成就视图
+ ///
+ public AdvancedCollectionView? AchievementsView
+ {
+ get => achievementsView;
+ set => SetProperty(ref achievementsView, value);
+ }
+
+ ///
+ /// 打开页面命令
+ ///
+ public ICommand OpenUICommand { get; }
+
+ private async Task OpenUIAsync(CancellationToken token)
+ {
+ using (CancellationTokenExtensions.CombinedCancellationToken combined = token.CombineWith(CancellationToken))
+ {
+ if (await metadataService.InitializeAsync(combined.Token))
+ {
+ IEnumerable achievements = await metadataService.GetAchievementsAsync(combined.Token);
+
+ // TODO
+ AchievementsView = new(achievements.ToList());
+ }
+ }
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs
index c9f20d10..a2e1bc72 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AnnouncementViewModel.cs
@@ -8,7 +8,8 @@ using Snap.Hutao.Core;
using Snap.Hutao.Core.Threading;
using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Service.Abstraction;
-using Snap.Hutao.Service.Abstraction.Navigation;
+using Snap.Hutao.Service.Navigation;
+using Snap.Hutao.View.Page;
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
namespace Snap.Hutao.ViewModel;
@@ -99,7 +100,7 @@ internal class AnnouncementViewModel : ObservableObject, ISupportCancellation
if (WebView2Helper.IsSupported)
{
- navigationService.Navigate(data: new NavigationExtra(content));
+ navigationService.Navigate(data: new NavigationExtra(content));
}
else
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs
new file mode 100644
index 00000000..d24f964f
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs
@@ -0,0 +1,40 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Web.Enka.Model;
+using Snap.Hutao.Web.Hoyolab;
+using System.Net.Http;
+using System.Net.Http.Json;
+
+namespace Snap.Hutao.Web.Enka;
+
+///
+/// Enka API 客户端
+///
+[Injection(InjectAs.Transient)]
+internal class EnkaClient
+{
+ private const string EnkaAPI = "https://enka.shinshin.moe/u/{0}/__data.json";
+
+ private readonly HttpClient httpClient;
+
+ ///
+ /// 构造一个新的 Enka API 客户端
+ ///
+ /// http客户端
+ public EnkaClient(HttpClient httpClient)
+ {
+ this.httpClient = httpClient;
+ }
+
+ ///
+ /// 异步获取 Enka API 响应
+ ///
+ /// 玩家Uid
+ /// 取消令牌
+ /// Enka API 响应
+ public Task GetDataAsync(PlayerUid playerUid, CancellationToken token)
+ {
+ return httpClient.GetFromJsonAsync(string.Format(EnkaAPI, playerUid.Value), token);
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/AvatarInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/AvatarInfo.cs
new file mode 100644
index 00000000..118faa96
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/AvatarInfo.cs
@@ -0,0 +1,92 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Core.Json.Converter;
+using Snap.Hutao.Model.Intrinsic;
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// 角色信息
+///
+public class AvatarInfo
+{
+ ///
+ /// 角色Id
+ /// Character ID
+ ///
+ [JsonPropertyName("avatarId")]
+ public int AvatarId { get; set; }
+
+ ///
+ /// 基础属性
+ /// Character Info Properties List
+ ///
+ ///
+ [JsonPropertyName("propMap")]
+ [JsonConverter(typeof(StringEnumKeyDictionaryConverter))]
+ public IDictionary PropMap { get; set; } = default!;
+
+ ///
+ /// 命座 Id
+ ///
+ [JsonPropertyName("talentIdList")]
+ public IList TalentIdList { get; set; } = default!;
+
+ ///
+ /// 属性Map
+ /// Map of Character's Combat Properties.
+ ///
+ [JsonPropertyName("fightPropMap")]
+ [JsonConverter(typeof(StringEnumKeyDictionaryConverter))]
+ public IDictionary FightPropMap { get; set; } = default!;
+
+ ///
+ /// 技能组Id
+ /// Character Skill Set ID
+ ///
+ [JsonPropertyName("skillDepotId")]
+ public int SkillDepotId { get; set; }
+
+ ///
+ /// List of Unlocked Skill Ids
+ /// 被动天赋
+ ///
+ [JsonPropertyName("inherentProudSkillList")]
+ public IList InherentProudSkillList { get; set; } = default!;
+
+ ///
+ /// Map of Skill Levels
+ ///
+ [JsonPropertyName("skillLevelMap")]
+ public IDictionary SkillLevelMap { get; set; } = default!;
+
+ ///
+ /// 装备列表
+ /// 最后一个为武器
+ /// List of Equipments: Weapon, Ariftacts
+ ///
+ [JsonPropertyName("equipList")]
+ public IList EquipList { get; set; } = default!;
+
+ ///
+ /// 好感度信息
+ /// Character Friendship Level
+ ///
+ [JsonPropertyName("fetterInfo")]
+ public FetterInfo FetterInfo { get; set; } = default!;
+
+ ///
+ /// 皮肤 Id
+ ///
+ [JsonPropertyName("costumeId")]
+ public int? CostumeId { get; set; }
+
+ ///
+ /// 命座额外技能等级
+ ///
+ [JsonPropertyName("proudSkillExtraLevelMap")]
+ public IDictionary ProudSkillExtraLevelMap { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/EnkaResponse.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/EnkaResponse.cs
new file mode 100644
index 00000000..f147df69
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/EnkaResponse.cs
@@ -0,0 +1,48 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// Enka API 响应
+///
+public class EnkaResponse
+{
+ ///
+ /// 玩家基础信息
+ ///
+ [JsonPropertyName("playerInfo")]
+ public PlayerInfo? PlayerInfo { get; set; } = default!;
+
+ ///
+ /// 展示的角色详细信息列表
+ ///
+ [JsonPropertyName("avatarInfoList")]
+ public IList? AvatarInfoList { get; set; } = default!;
+
+ ///
+ /// 刷新剩余秒数
+ /// 生存时间值
+ ///
+ [JsonPropertyName("ttl")]
+ public int? Ttl { get; set; }
+
+ ///
+ /// 此响应是否有效
+ ///
+ public bool IsValid
+ {
+ get => Ttl.HasValue;
+ }
+
+ ///
+ /// 是否包含角色详细数据
+ ///
+ public bool HasDetail
+ {
+ get => AvatarInfoList != null;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Equip.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Equip.cs
new file mode 100644
index 00000000..8b7f8eff
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Equip.cs
@@ -0,0 +1,39 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// 装备
+///
+public class Equip
+{
+ ///
+ /// 物品Id
+ /// Equipment ID
+ ///
+ [JsonPropertyName("itemId")]
+ public int ItemId { get; set; }
+
+ ///
+ /// 圣遗物
+ /// Artifact Base Info
+ ///
+ [JsonPropertyName("reliquary")]
+ public Reliquary? Reliquary { get; set; }
+
+ ///
+ /// 武器
+ /// Weapon Base Info
+ ///
+ [JsonPropertyName("weapon")]
+ public Weapon? Weapon { get; set; }
+
+ ///
+ /// Detailed Info of Equipment
+ ///
+ [JsonPropertyName("flat")]
+ public Flat Flat { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/FetterInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/FetterInfo.cs
new file mode 100644
index 00000000..569d7bcb
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/FetterInfo.cs
@@ -0,0 +1,18 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// 好感度信息
+///
+public class FetterInfo
+{
+ ///
+ /// 好感度等级
+ ///
+ [JsonPropertyName("expLevel")]
+ public int ExpLevel { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Flat.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Flat.cs
new file mode 100644
index 00000000..e54ebd74
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Flat.cs
@@ -0,0 +1,84 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Intrinsic;
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// 平展值
+///
+public class Flat
+{
+ ///
+ /// 名称
+ /// Hash for Equipment Name
+ ///
+ [JsonPropertyName("nameTextMapHash")]
+ public string NameTextMapHash { get; set; } = default!;
+
+ ///
+ /// 套装名称
+ /// Hash for Artifact Set Name
+ ///
+ [JsonPropertyName("setNameTextMapHash")]
+ public string? SetNameTextMapHash { get; set; }
+
+ ///
+ /// 等级
+ /// Rarity Level of Equipment
+ ///
+ [JsonPropertyName("rankLevel")]
+ public int RankLevel { get; set; }
+
+ ///
+ /// 圣遗物主属性
+ /// Artifact Main Stat
+ ///
+ [JsonPropertyName("reliquaryMainstat")]
+ public ReliquaryMainstat? ReliquaryMainstat { get; set; }
+
+ ///
+ /// 圣遗物副属性
+ /// List of Artifact Substats
+ ///
+ [JsonPropertyName("reliquarySubstats")]
+ public IList? ReliquarySubstats { get; set; }
+
+ ///
+ /// 物品类型
+ /// Equipment Type: Weapon or Artifact
+ /// ITEM_WEAPON
+ /// ITEM_RELIQUARY
+ ///
+ [JsonPropertyName("itemType")]
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public ItemType ItemType { get; set; } = default!;
+
+ ///
+ /// 图标
+ /// Equipment Icon Name
+ ///
+ [JsonPropertyName("icon")]
+ public string Icon { get; set; } = default!;
+
+ ///
+ /// 圣遗物类型
+ /// 当为武器时
+ /// 值为
+ ///
+ [JsonPropertyName("equipType")]
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public EquipType EquipType { get; set; }
+
+ ///
+ /// 武器主副属性
+ /// 0 基础攻击力
+ /// 1 主属性
+ /// List of Weapon Stat: Base ATK, Substat
+ ///
+ [JsonPropertyName("weaponStats")]
+ public IList? WeaponStats { get; set; }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/PlayerInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/PlayerInfo.cs
new file mode 100644
index 00000000..15ba1143
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/PlayerInfo.cs
@@ -0,0 +1,89 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// 玩家信息
+///
+public class PlayerInfo
+{
+ ///
+ /// 昵称
+ /// Player Nickname
+ ///
+ [JsonPropertyName("nickname")]
+ public string Nickname { get; set; } = default!;
+
+ ///
+ /// 等级
+ ///
+ [JsonPropertyName("level")]
+ public int Level { get; set; }
+
+ ///
+ /// 签名
+ /// Profile Signature
+ ///
+ [JsonPropertyName("signature")]
+ public string Signature { get; set; } = default!;
+
+ ///
+ /// 世界等级
+ /// Player World Level
+ ///
+ [JsonPropertyName("worldLevel")]
+ public int WorldLevel { get; set; }
+
+ ///
+ /// 名片的Id
+ /// Profile Namecard ID
+ ///
+ [JsonPropertyName("nameCardId")]
+ public int NameCardId { get; set; }
+
+ ///
+ /// 完成的成就个数
+ /// Number of Completed Achievements
+ ///
+ [JsonPropertyName("finishAchievementNum")]
+ public int FinishAchievementNum { get; set; }
+
+ ///
+ /// 深渊层数
+ /// Abyss Floor
+ ///
+ [JsonPropertyName("towerFloorIndex")]
+ public int TowerFloorIndex { get; set; }
+
+ ///
+ /// 深渊间数
+ /// Abyss Floor's Level
+ ///
+ [JsonPropertyName("towerLevelIndex")]
+ public int TowerLevelIndex { get; set; }
+
+ ///
+ /// 展示的角色信息
+ /// List of Character IDs and Levels
+ ///
+ [JsonPropertyName("showAvatarInfoList")]
+ public IList ShowAvatarInfoList { get; set; } = default!;
+
+ ///
+ /// 展示的名片信息
+ /// List of Namecard IDs
+ ///
+ [JsonPropertyName("showNameCardIdList")]
+ public IList ShowNameCardIdList { get; set; } = default!;
+
+ ///
+ /// 头像信息
+ /// Character ID of Profile Picture
+ ///
+ [JsonPropertyName("profilePicture")]
+ public ProfilePicture ProfilePicture { get; set; } = default!;
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/PlayerProperty.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/PlayerProperty.cs
new file mode 100644
index 00000000..2d59fe20
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/PlayerProperty.cs
@@ -0,0 +1,228 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// 玩家属性
+/// https://github.com/Grasscutters/Grasscutter/tree/development/src/main/java/emu/grasscutter/game/props/PlayerProperty.java
+///
+public enum PlayerProperty
+{
+ ///
+ /// 空
+ ///
+ PROP_NONE = 0,
+
+ ///
+ /// 经验值
+ ///
+ PROP_EXP = 1001,
+
+ ///
+ /// 突破等级
+ ///
+ PROP_BREAK_LEVEL = 1002,
+
+ ///
+ /// 饱食度
+ ///
+ PROP_SATIATION_VAL = 1003,
+
+ ///
+ /// 饱食度冷却
+ ///
+ PROP_SATIATION_PENALTY_TIME = 1004,
+
+ ///
+ /// 等级
+ ///
+ PROP_LEVEL = 4001,
+
+ ///
+ /// 上次切换角色的时间
+ ///
+ PROP_LAST_CHANGE_AVATAR_TIME = 10001,
+
+ ///
+ /// Maximum volume of the Statue of the Seven for the player [0, 8500000]
+ ///
+ PROP_MAX_SPRING_VOLUME = 10002,
+
+ ///
+ /// Current volume of the Statue of the Seven [0, PROP_MAX_SPRING_VOLUME]
+ ///
+ PROP_CUR_SPRING_VOLUME = 10003,
+
+ ///
+ /// Auto HP recovery when approaching the Statue of the Seven [0, 1]
+ ///
+ PROP_IS_SPRING_AUTO_USE = 10004,
+
+ ///
+ /// Auto HP recovery percentage [0, 100]
+ ///
+ PROP_SPRING_AUTO_USE_PERCENT = 10005,
+
+ ///
+ /// Are you in a state that disables your flying ability? e.g. new player [0, 1]
+ ///
+ PROP_IS_FLYABLE = 10006,
+
+ ///
+ /// 天气是否锁定
+ ///
+ PROP_IS_WEATHER_LOCKED = 10007,
+
+ ///
+ /// 游戏时间是否锁定
+ ///
+ PROP_IS_GAME_TIME_LOCKED = 10008,
+
+ ///
+ /// ?
+ ///
+ PROP_IS_TRANSFERABLE = 10009,
+
+ ///
+ /// Maximum stamina of the player =0 - 24000
+ ///
+ PROP_MAX_STAMINA = 10010,
+
+ ///
+ /// Used stamina of the player =0 - PROP_MAX_STAMINA
+ ///
+ PROP_CUR_PERSIST_STAMINA = 10011,
+
+ ///
+ /// 临时体力,食物?
+ ///
+ PROP_CUR_TEMPORARY_STAMINA = 10012,
+
+ ///
+ /// 玩家等级
+ ///
+ PROP_PLAYER_LEVEL = 10013,
+
+ ///
+ /// 玩家经验
+ ///
+ PROP_PLAYER_EXP = 10014,
+
+ ///
+ /// Primogem =-inf, +inf
+ /// It is known that Mihoyo will make Primogem negative in the cases that a player spends
+ /// his gems and then got a money refund, so negative is allowed.
+ ///
+ PROP_PLAYER_HCOIN = 10015,
+
+ ///
+ /// Mora [0, +inf
+ ///
+ PROP_PLAYER_SCOIN = 10016,
+
+ ///
+ /// Do you allow other players to join your game? [0=no 1=direct 2=approval]
+ ///
+ PROP_PLAYER_MP_SETTING_TYPE = 10017,
+
+ ///
+ /// 0 if in quest or something that disables MP [0, 1]
+ ///
+ PROP_IS_MP_MODE_AVAILABLE = 10018,
+
+ ///
+ /// [0, 8]
+ ///
+ PROP_PLAYER_WORLD_LEVEL = 10019,
+
+ ///
+ /// Original Resin [0, 2000] - note that values above 160 require refills
+ ///
+ PROP_PLAYER_RESIN = 10020,
+
+ ///
+ /// ?
+ ///
+ PROP_PLAYER_WAIT_SUB_HCOIN = 10022,
+
+ ///
+ /// ?
+ ///
+ PROP_PLAYER_WAIT_SUB_SCOIN = 10023,
+
+ ///
+ /// Is only MP with PlayStation players? [0, 1]
+ ///
+ PROP_IS_ONLY_MP_WITH_PS_PLAYER = 10024,
+
+ ///
+ /// Genesis Crystal =-inf, +inf see 10015
+ ///
+ PROP_PLAYER_MCOIN = 10025,
+
+ ///
+ /// ?
+ ///
+ PROP_PLAYER_WAIT_SUB_MCOIN = 10026,
+
+ ///
+ /// ?
+ ///
+ PROP_PLAYER_LEGENDARY_KEY = 10027,
+
+ ///
+ /// ?
+ ///
+ PROP_IS_HAS_FIRST_SHARE = 10028,
+
+ ///
+ /// ?
+ ///
+ PROP_PLAYER_FORGE_POINT = 10029,
+
+ ///
+ /// ?
+ ///
+ PROP_CUR_CLIMATE_METER = 10035,
+
+ ///
+ /// ?
+ ///
+ PROP_CUR_CLIMATE_TYPE = 10036,
+
+ ///
+ /// ?
+ ///
+ PROP_CUR_CLIMATE_AREA_ID = 10037,
+
+ ///
+ /// ?
+ ///
+ PROP_CUR_CLIMATE_AREA_CLIMATE_TYPE = 10038,
+
+ ///
+ /// ?
+ ///
+ PROP_PLAYER_WORLD_LEVEL_LIMIT = 10039,
+
+ ///
+ /// ?
+ ///
+ PROP_PLAYER_WORLD_LEVEL_ADJUST_CD = 10040,
+
+ ///
+ /// ?
+ ///
+ PROP_PLAYER_LEGENDARY_DAILY_TASK_NUM = 10041,
+
+ ///
+ /// Realm currency [0, +inf
+ ///
+ PROP_PLAYER_HOME_COIN = 10042,
+
+ ///
+ /// ?
+ ///
+ PROP_PLAYER_WAIT_SUB_HOME_COIN = 10043,
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs
new file mode 100644
index 00000000..fbaf35eb
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs
@@ -0,0 +1,18 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// 档案头像
+///
+public class ProfilePicture
+{
+ ///
+ /// 使用的角色Id
+ ///
+ [JsonPropertyName("avatarId")]
+ public int AvatarId { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Reliquary.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Reliquary.cs
new file mode 100644
index 00000000..d0314cb3
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Reliquary.cs
@@ -0,0 +1,34 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// 圣遗物
+///
+public class Reliquary
+{
+ ///
+ /// 等级 +20 = 21
+ /// [1,21]
+ /// Artifact Level [1-21]
+ ///
+ [JsonPropertyName("level")]
+ public int Level { get; set; }
+
+ ///
+ /// 主属性Id
+ /// Artifact Main Stat ID
+ ///
+ [JsonPropertyName("mainPropId")]
+ public int MainPropId { get; set; }
+
+ ///
+ /// 强化属性Id
+ ///
+ [JsonPropertyName("appendPropIdList")]
+ public IList AppendPropIdList { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ReliquaryMainstat.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ReliquaryMainstat.cs
new file mode 100644
index 00000000..56999a39
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ReliquaryMainstat.cs
@@ -0,0 +1,26 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Intrinsic;
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// 圣遗物主属性
+///
+public class ReliquaryMainstat
+{
+ ///
+ /// Equipment Append Property Name.
+ ///
+ [JsonPropertyName("mainPropId")]
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public FightProperty MainPropId { get; set; }
+
+ ///
+ /// Property Value
+ ///
+ [JsonPropertyName("statValue")]
+ public double StatValue { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ReliquarySubstat.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ReliquarySubstat.cs
new file mode 100644
index 00000000..a3859edc
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ReliquarySubstat.cs
@@ -0,0 +1,26 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Intrinsic;
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// 圣遗物副属性
+///
+public class ReliquarySubstat
+{
+ ///
+ /// 增加属性
+ ///
+ [JsonPropertyName("appendPropId")]
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public FightProperty AppendPropId { get; set; }
+
+ ///
+ /// 值
+ ///
+ [JsonPropertyName("statValue")]
+ public double StatValue { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ShowAvatarInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ShowAvatarInfo.cs
new file mode 100644
index 00000000..fd74788f
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ShowAvatarInfo.cs
@@ -0,0 +1,32 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// 角色列表信息
+///
+public class ShowAvatarInfo
+{
+ ///
+ /// 角色Id
+ /// Character ID
+ ///
+ [JsonPropertyName("avatarId")]
+ public int AvatarId { get; set; }
+
+ ///
+ /// 角色等级
+ /// Character Level
+ ///
+ [JsonPropertyName("level")]
+ public int Level { get; set; }
+
+ ///
+ /// 可能的皮肤Id
+ ///
+ [JsonPropertyName("costumeId")]
+ public int? CostumeId { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/TypeValue.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/TypeValue.cs
new file mode 100644
index 00000000..61aaa9eb
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/TypeValue.cs
@@ -0,0 +1,24 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// 类型与值
+///
+public class TypeValue
+{
+ ///
+ /// 类型
+ ///
+ [JsonPropertyName("type")]
+ public PlayerProperty Type { get; set; }
+
+ ///
+ /// 值
+ ///
+ [JsonPropertyName("val")]
+ public string? Value { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Weapon.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Weapon.cs
new file mode 100644
index 00000000..9c7d7805
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/Weapon.cs
@@ -0,0 +1,34 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// 武器信息
+///
+public class Weapon
+{
+ ///
+ /// 等级
+ /// Weapon Level
+ ///
+ [JsonPropertyName("level")]
+ public int Level { get; set; }
+
+ ///
+ /// 突破等级
+ /// Weapon Ascension Level
+ ///
+ [JsonPropertyName("promoteLevel")]
+ public int PromoteLevel { get; set; }
+
+ ///
+ /// 精炼 相较于实际等级 -1
+ /// Weapon Refinement Level [0-4]
+ ///
+ [JsonPropertyName("affixMap")]
+ public IDictionary AffixMap { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/WeaponStat.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/WeaponStat.cs
new file mode 100644
index 00000000..6575ea94
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/WeaponStat.cs
@@ -0,0 +1,26 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Intrinsic;
+using System.Text.Json.Serialization;
+
+namespace Snap.Hutao.Web.Enka.Model;
+
+///
+/// 武器属性
+///
+public class WeaponStat
+{
+ ///
+ /// 提升属性Id
+ ///
+ [JsonPropertyName("appendPropId")]
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public FightProperty AppendPropId { get; set; }
+
+ ///
+ /// 值
+ ///
+ [JsonPropertyName("statValue")]
+ public double StatValue { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/CommunityInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/CommunityInfo.cs
index d38ef786..7ea79c19 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/CommunityInfo.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/CommunityInfo.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using System.Collections.Generic;
-using System.Text.Json;
using System.Text.Json.Serialization;
namespace Snap.Hutao.Web.Hoyolab.Bbs.User;
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs
index b737df07..3711b84e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs
@@ -5,7 +5,6 @@ using Snap.Hutao.Web.Hoyolab.DynamicSecret;
using Snap.Hutao.Web.Response;
using System.Net.Http;
using System.Net.Http.Json;
-using System.Text.Json;
namespace Snap.Hutao.Web.Hoyolab.Bbs.User;
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserFullInfoWrapper.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserFullInfoWrapper.cs
index 0274e316..cffc0a6a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserFullInfoWrapper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserFullInfoWrapper.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using System.Collections.Generic;
-using System.Text.Json;
using System.Text.Json.Serialization;
namespace Snap.Hutao.Web.Hoyolab.Bbs.User;
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs
index fbc0d545..95c27d59 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs
@@ -3,7 +3,6 @@
using Snap.Hutao.Core.Convertion;
using System.Linq;
-using System.Text.Json;
namespace Snap.Hutao.Web.Hoyolab.DynamicSecret;
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/HttpClientDynamicSecretExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/HttpClientDynamicSecretExtensions.cs
index 6c48bb74..28b8aafe 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/HttpClientDynamicSecretExtensions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/HttpClientDynamicSecretExtensions.cs
@@ -3,7 +3,6 @@
using Snap.Hutao.Extension;
using System.Net.Http;
-using System.Text.Json;
namespace Snap.Hutao.Web.Hoyolab.DynamicSecret;
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Common/Announcement/AnnouncementClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Common/Announcement/AnnouncementClient.cs
index 7a488087..d677fdef 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Common/Announcement/AnnouncementClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Common/Announcement/AnnouncementClient.cs
@@ -6,8 +6,6 @@ using Snap.Hutao.Web.Response;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
-using System.Text;
-using System.Text.Json;
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
@@ -52,8 +50,7 @@ internal class AnnouncementClient
/// 公告内容列表
public async Task> GetAnnouncementContentsAsync(CancellationToken cancellationToken = default)
{
- Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
-
+ // Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Response>? resp = await httpClient
.GetFromJsonAsync>>(ApiEndpoints.AnnContent, jsonSerializerOptions, cancellationToken)
.ConfigureAwait(false);
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/PlayerUid.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs
similarity index 97%
rename from src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/PlayerUid.cs
rename to src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs
index f214d0a0..ab6ec83e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/PlayerUid.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs
@@ -1,7 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-namespace Snap.Hutao.Web.Hoyolab.Takumi;
+namespace Snap.Hutao.Web.Hoyolab;
///
/// 玩家 Uid
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Avatar.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Avatar.cs
index 5d60f719..5a75410c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Avatar.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Avatar.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Model.Intrinsic;
using System.Text.Json.Serialization;
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Reliquary.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Reliquary.cs
index ee7ef9c6..efc2a5cf 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Reliquary.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Reliquary.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Model.Intrinsic;
using System.Text.Json.Serialization;
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Weapon.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Weapon.cs
index 70819bce..3d7ef97b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Weapon.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/Avatar/Weapon.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Model.Intrinsic;
using System.Text.Json.Serialization;
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs
index 7e1908bf..735a3b92 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs
@@ -10,7 +10,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
-using System.Text.Json;
using System.Text.Json.Serialization;
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoClient.cs
index 81cc077f..2de44aeb 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoClient.cs
@@ -14,7 +14,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
-using System.Text.Json;
namespace Snap.Hutao.Web.Hutao;
@@ -50,29 +49,28 @@ internal class HutaoClient : ISupportAsyncInitialization
}
///
- public bool IsInitialized { get => isInitialized; }
+ public bool IsInitialized { get => isInitialized; private set => isInitialized = value; }
///
- public async Task InitializeAsync(CancellationToken token = default)
+ public async ValueTask InitializeAsync(CancellationToken token = default)
{
- if (isInitialized)
+ if (!IsInitialized)
{
- return true;
+ Auth auth = new(
+ "08d9e212-0cb3-4d71-8ed7-003606da7b20",
+ "7ueWgZGn53dDhrm8L5ZRw+YWfOeSWtgQmJWquRgaygw=");
+
+ HttpResponseMessage response = await httpClient
+ .PostAsJsonAsync($"{AuthAPIHost}/Auth/Login", auth, jsonSerializerOptions, token)
+ .ConfigureAwait(false);
+ Response? resp = await response.Content
+ .ReadFromJsonAsync>(jsonSerializerOptions, token)
+ .ConfigureAwait(false);
+
+ httpClient.DefaultRequestHeaders.Authorization = new("Bearer", Must.NotNull(resp?.Data?.AccessToken!));
+ IsInitialized = true;
}
- Auth auth = new(
- "08d9e212-0cb3-4d71-8ed7-003606da7b20",
- "7ueWgZGn53dDhrm8L5ZRw+YWfOeSWtgQmJWquRgaygw=");
-
- HttpResponseMessage response = await httpClient
- .PostAsJsonAsync($"{AuthAPIHost}/Auth/Login", auth, jsonSerializerOptions, token)
- .ConfigureAwait(false);
- Response? resp = await response.Content
- .ReadFromJsonAsync>(jsonSerializerOptions, token)
- .ConfigureAwait(false);
-
- httpClient.DefaultRequestHeaders.Authorization = new("Bearer", Must.NotNull(resp?.Data?.AccessToken!));
- isInitialized = true;
return true;
}
@@ -333,6 +331,7 @@ internal class HutaoClient : ISupportAsyncInitialization
/// 角色详细信息
/// 取消令牌
/// 响应
+ [Obsolete("不再强制要求上传物品")]
[EditorBrowsable(EditorBrowsableState.Never)]
internal async Task?> UploadItemsAsync(List characters, CancellationToken token = default)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Converter/ReliquarySetsConverter.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Converter/ReliquarySetsConverter.cs
index 937c95aa..07f1be78 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Converter/ReliquarySetsConverter.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Converter/ReliquarySetsConverter.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using System.Linq;
-using System.Text.Json;
using System.Text.Json.Serialization;
namespace Snap.Hutao.Web.Hutao.Model.Converter;
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/PlayerRecord.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/PlayerRecord.cs
index 5f0b34f6..de752f9c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/PlayerRecord.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/PlayerRecord.cs
@@ -43,8 +43,6 @@ public class PlayerRecord
///
public IEnumerable PlayerSpiralAbyssesLevels { get; }
- private List CachedAvatars { get; set; } = default!;
-
///
/// 建造玩家记录
///
@@ -61,25 +59,7 @@ public class PlayerRecord
.SelectMany(f => f.Levels, (f, level) => new IndexedLevel(f.Index, level))
.Select(indexedLevel => new PlayerSpiralAbyssLevel(indexedLevel));
- return new PlayerRecord(uid, playerAvatars, playerSpiralAbyssLevels)
- {
- CachedAvatars = detailAvatars,
- };
- }
-
- ///
- /// 代替胡桃客户端上传物品数据
- ///
- /// 使用的客户端
- /// 取消令牌
- /// 是否上传成功
- internal async Task UploadItemsAsync(HutaoClient hutaoClient, CancellationToken token)
- {
- Response? resp = await hutaoClient
- .UploadItemsAsync(CachedAvatars, token)
- .ConfigureAwait(false);
-
- return Response.Response.IsOk(resp);
+ return new PlayerRecord(uid, playerAvatars, playerSpiralAbyssLevels);
}
///