diff --git a/src/Snap.Hutao/SettingsUI/Controls/CheckBoxWithDescriptionControl.cs b/src/Snap.Hutao/SettingsUI/Controls/CheckBoxWithDescriptionControl.cs index 2d692cf6..ffb07ab2 100644 --- a/src/Snap.Hutao/SettingsUI/Controls/CheckBoxWithDescriptionControl.cs +++ b/src/Snap.Hutao/SettingsUI/Controls/CheckBoxWithDescriptionControl.cs @@ -35,13 +35,14 @@ public class CheckBoxWithDescriptionControl : CheckBox private void CheckBoxSubTextControl_Loaded(object sender, RoutedEventArgs e) { - StackPanel panel = new StackPanel() { Orientation = Orientation.Vertical }; + StackPanel panel = new() { Orientation = Orientation.Vertical }; // Add text box only if the description is not empty. Required for additional plugin options. if (!string.IsNullOrWhiteSpace(Description)) { panel.Children.Add(new TextBlock() { Margin = new Thickness(0, 10, 0, 0), Text = Header }); - panel.Children.Add(new IsEnabledTextBlock() { Style = (Style)Application.Current.Resources["SecondaryIsEnabledTextBlockStyle"], Text = Description }); + //Style = (Style)Application.Current.Resources["SecondaryIsEnabledTextBlockStyle"] + panel.Children.Add(new IsEnabledTextBlock() { Text = Description }); } else { diff --git a/src/Snap.Hutao/SettingsUI/Styles/Button.xaml b/src/Snap.Hutao/SettingsUI/Styles/Button.xaml index 10a32327..871b51c5 100644 --- a/src/Snap.Hutao/SettingsUI/Styles/Button.xaml +++ b/src/Snap.Hutao/SettingsUI/Styles/Button.xaml @@ -7,9 +7,9 @@ diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs index 334af782..56dc08d3 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs @@ -3,7 +3,6 @@ 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; @@ -20,7 +19,9 @@ namespace Snap.Hutao.ViewModel; internal class AchievementViewModel : ObservableObject, ISupportCancellation { private readonly IMetadataService metadataService; - private AdvancedCollectionView? achievementsView; + private AdvancedCollectionView? achievements; + private IList? achievementGoals; + private AchievementGoal? selectedAchievementGoal; /// /// 构造一个新的成就视图模型 @@ -39,10 +40,32 @@ internal class AchievementViewModel : ObservableObject, ISupportCancellation /// /// 成就视图 /// - public AdvancedCollectionView? AchievementsView + public AdvancedCollectionView? Achievements { - get => achievementsView; - set => SetProperty(ref achievementsView, value); + get => achievements; + set => SetProperty(ref achievements, value); + } + + /// + /// 成就分类 + /// + public IList? AchievementGoals + { + get => achievementGoals; + set => SetProperty(ref achievementGoals, value); + } + + /// + /// 选中的成就分类 + /// + public AchievementGoal? SelectedAchievementGoal + { + get => selectedAchievementGoal; + set + { + SetProperty(ref selectedAchievementGoal, value); + OnGoalChanged(value); + } } /// @@ -50,17 +73,22 @@ internal class AchievementViewModel : ObservableObject, ISupportCancellation /// public ICommand OpenUICommand { get; } - private async Task OpenUIAsync(CancellationToken token) + private async Task OpenUIAsync() { - using (CancellationTokenExtensions.CombinedCancellationToken combined = token.CombineWith(CancellationToken)) + if (await metadataService.InitializeAsync(CancellationToken)) { - if (await metadataService.InitializeAsync(combined.Token)) - { - IEnumerable achievements = await metadataService.GetAchievementsAsync(combined.Token); - - // TODO - AchievementsView = new(achievements.ToList()); - } + Achievements = new(await metadataService.GetAchievementsAsync(CancellationToken), true); + AchievementGoals = await metadataService.GetAchievementGoalsAsync(CancellationToken); } } -} + + private void OnGoalChanged(AchievementGoal? goal) + { + if (Achievements != null) + { + Achievements.Filter = goal != null + ? ((object o) => o is Achievement achi && achi.Goal == goal.Id) + : ((object o) => true); + } + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs index 5382f279..0c64aa1e 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs @@ -41,7 +41,7 @@ internal class ExperimentalFeaturesViewModel : ObservableObject private Task OpenCacheFolderAsync(CancellationToken token) { - return Launcher.LaunchFolderAsync(App.Current.CacheFolder).AsTask(token); + return Launcher.LaunchFolderAsync(App.CacheFolder).AsTask(token); } private Task OpenDataFolderAsync(CancellationToken token) diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs index 194f38b7..6fb7fa0d 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs @@ -3,9 +3,10 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using Microsoft.UI.Xaml; using Snap.Hutao.Core.Threading; using Snap.Hutao.Factory.Abstraction; -using Snap.Hutao.Model.Entity; +using Snap.Hutao.Model.Binding; using Snap.Hutao.Service.Abstraction; using Snap.Hutao.View.Dialog; using System.Collections.Generic; @@ -27,7 +28,7 @@ internal class UserViewModel : ObservableObject private readonly IInfoBarService infoBarService; private User? selectedUser; - private ObservableCollection? userInfos; + private ObservableCollection? users; /// /// 构造一个新的用户视图模型 @@ -64,7 +65,7 @@ internal class UserViewModel : ObservableObject /// /// 用户信息集合 /// - public ObservableCollection? Users { get => userInfos; set => SetProperty(ref userInfos, value); } + public ObservableCollection? Users { get => users; set => SetProperty(ref users, value); } /// /// 打开界面命令 @@ -90,20 +91,20 @@ internal class UserViewModel : ObservableObject { int validFlag = 4; - filteredCookie = new SortedDictionary(); + SortedDictionary filter = new(); - // O(1) to validate cookie foreach ((string key, string value) in map) { if (key == AccountIdKey || key == "cookie_token" || key == "ltoken" || key == "ltuid") { validFlag--; - filteredCookie.Add(key, value); + filter.Add(key, value); } } if (validFlag == 0) { + filteredCookie = filter; return true; } else @@ -115,41 +116,43 @@ internal class UserViewModel : ObservableObject private async Task OpenUIAsync() { - Users = await userService.GetInitializedUsersAsync(); + Users = await userService.GetUserCollectionAsync(); SelectedUser = userService.CurrentUser; } private async Task AddUserAsync() { - MainWindow mainWindow = Ioc.Default.GetRequiredService(); + // Get cookie from user input + Window mainWindow = Ioc.Default.GetRequiredService(); Result result = await new UserDialog(mainWindow).GetInputCookieAsync(); - // user confirms the input + // User confirms the input if (result.IsOk) { - IDictionary cookieMap = userService.ParseCookie(result.Value); - - if (TryValidateCookie(cookieMap, out IDictionary? filteredCookie)) + if (TryValidateCookie(userService.ParseCookie(result.Value), out IDictionary? filteredCookie)) { string simplifiedCookie = string.Join(';', filteredCookie.Select(kvp => $"{kvp.Key}={kvp.Value}")); - User user = new() { Cookie = simplifiedCookie }; - switch (await userService.TryAddUserAsync(user, filteredCookie[AccountIdKey])) + if (await userService.CreateUserAsync(simplifiedCookie) is User user) { - case UserAddResult.Added: - infoBarService.Success($"用户 [{user.UserInfo!.Nickname}] 添加成功"); - break; - case UserAddResult.Updated: - infoBarService.Success($"用户 [{user.UserInfo!.Nickname}] 更新成功"); - break; - case UserAddResult.AlreadyExists: - infoBarService.Information($"用户 [{user.UserInfo!.Nickname}] 已经存在"); - break; - case UserAddResult.InitializeFailed: - infoBarService.Warning("此 Cookie 无法获取用户信息,请重新输入"); - break; - default: - throw Must.NeverHappen(); + switch (await userService.TryAddUserAsync(user, filteredCookie[AccountIdKey])) + { + case UserAddResult.Added: + infoBarService.Success($"用户 [{user.UserInfo!.Nickname}] 添加成功"); + break; + case UserAddResult.Updated: + infoBarService.Success($"用户 [{user.UserInfo!.Nickname}] 更新成功"); + break; + case UserAddResult.AlreadyExists: + infoBarService.Information($"用户 [{user.UserInfo!.Nickname}] 已经存在"); + break; + default: + throw Must.NeverHappen(); + } + } + else + { + infoBarService.Warning("此 Cookie 无法获取用户信息,请重新输入"); } } else @@ -161,19 +164,14 @@ internal class UserViewModel : ObservableObject private async Task RemoveUserAsync(User? user) { - if (!User.IsNone(user)) - { - await userService.RemoveUserAsync(user); - infoBarService.Success($"用户 [{user.UserInfo!.Nickname}] 成功移除"); - } + Verify.Operation(user != null, "待删除的用户不应为 null"); + await userService.RemoveUserAsync(user); + infoBarService.Success($"用户 [{user.UserInfo?.Nickname}] 成功移除"); } private void CopyCookie(User? user) { - if (User.IsNone(user)) - { - return; - } + Verify.Operation(user != null, "待复制 Cookie 的用户不应为 null"); IInfoBarService infoBarService = Ioc.Default.GetRequiredService(); try diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs index 8e1c8ed4..e30a7cea 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs @@ -20,6 +20,8 @@ namespace Snap.Hutao.ViewModel; internal class WikiAvatarViewModel : ObservableObject { private readonly IMetadataService metadataService; + + // filters private readonly List> filterElementInfos; private readonly List>> filterAssociationInfos; private readonly List>> filterWeaponTypeInfos; @@ -129,12 +131,12 @@ internal class WikiAvatarViewModel : ObservableObject { if (await metadataService.InitializeAsync()) { - IEnumerable? avatars = await metadataService.GetAvatarsAsync(); + IList avatars = await metadataService.GetAvatarsAsync(); IOrderedEnumerable sorted = avatars .OrderBy(avatar => avatar.BeginTime) .ThenBy(avatar => avatar.Sort); - Avatars = new AdvancedCollectionView(new List(sorted), true); + Avatars = new AdvancedCollectionView(sorted.ToList(), true); Avatars.MoveCurrentToFirst(); } } @@ -175,7 +177,10 @@ internal class WikiAvatarViewModel : ObservableObject && targeQualities.Contains(avatar.Quality) && targetBodies.Contains(avatar.Body); - Avatars.MoveCurrentToFirst(); + if (!Avatars.Contains(Selected)) + { + Avatars.MoveCurrentToFirst(); + } } } } \ No newline at end of file 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 3711b84e..86a04145 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 @@ -35,7 +35,7 @@ internal class UserClient /// 用户 /// 取消令牌 /// 详细信息 - public async Task GetUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default) + public async Task GetUserFullInfoAsync(Model.Binding.User user, CancellationToken token = default) { Response? resp = await httpClient .UsingDynamicSecret() diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientCookieExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientCookieExtensions.cs index 2667c678..9e8d3762 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientCookieExtensions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientCookieExtensions.cs @@ -1,7 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Model.Entity; +using Snap.Hutao.Model.Binding; using System.Net.Http; namespace Snap.Hutao.Web.Hoyolab; @@ -19,11 +19,7 @@ internal static class HttpClientCookieExtensions /// 客户端 internal static HttpClient SetUser(this HttpClient httpClient, User user) { - if (!User.IsNone(user)) - { - httpClient.DefaultRequestHeaders.Set("Cookie", user.Cookie); - } - + httpClient.DefaultRequestHeaders.Set("Cookie", user.Cookie); return httpClient; } } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRoleClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRoleClient.cs index 0d81fbe2..6464e9c2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRoleClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRoleClient.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using Snap.Hutao.Extension; +using Snap.Hutao.Model.Binding; using Snap.Hutao.Web.Response; using System.Collections.Generic; using System.Net.Http; @@ -33,7 +34,7 @@ internal class UserGameRoleClient /// 用户 /// 取消令牌 /// 用户角色信息 - public async Task> GetUserGameRolesAsync(Model.Entity.User user, CancellationToken token = default) + public async Task> GetUserGameRolesAsync(User user, CancellationToken token = default) { Response>? resp = await httpClient .SetUser(user) 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 735a3b92..c3663bcb 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 @@ -2,7 +2,7 @@ // Licensed under the MIT license. using Snap.Hutao.Extension; -using Snap.Hutao.Model.Entity; +using Snap.Hutao.Model.Binding; using Snap.Hutao.Web.Hoyolab.DynamicSecret; using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar; using Snap.Hutao.Web.Response; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoClient.cs index 3b41d152..341da788 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoClient.cs @@ -294,7 +294,7 @@ internal class HutaoClient : ISupportAsyncInitialization /// 用户 /// 取消令牌 /// 玩家记录 - public async Task GetPlayerRecordAsync(User user, CancellationToken token = default) + public async Task GetPlayerRecordAsync(Snap.Hutao.Model.Binding.User user, CancellationToken token = default) { PlayerInfo? playerInfo = await gameRecordClient .GetPlayerInfoAsync(user, token)