From 3db52e81847a006196e8d57a357a7238e0b2bcfd Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sun, 17 Sep 2023 16:05:10 +0800 Subject: [PATCH] spiral abyss rework --- .../Identity/IdentityGenerator.cs | 6 ++ src/Snap.Hutao/Snap.Hutao/App.xaml | 2 +- src/Snap.Hutao/Snap.Hutao/Control/Loading.cs | 2 +- .../EnumerableExtension.Collection.cs | 27 ++++---- .../Model/Entity/SpiralAbyssEntry.cs | 10 --- .../Resource/Localization/SH.Designer.cs | 8 +-- .../Snap.Hutao/Resource/Localization/SH.resx | 8 +-- .../Metadata/IMetadataServiceIdDataMap.cs | 10 +++ .../Metadata/MetadataService.Indexing.cs | 32 +++++++++ .../ISpiralAbyssRecordDbService.cs | 2 +- .../SpiralAbyss/ISpiralAbyssRecordService.cs | 5 +- .../SpiralAbyss/SpiralAbyssRecordDbService.cs | 7 +- .../SpiralAbyss/SpiralAbyssRecordService.cs | 65 +++++++++++++++---- .../Snap.Hutao/View/Page/AchievementPage.xaml | 7 +- .../View/Page/SpiralAbyssRecordPage.xaml | 49 ++++++++------ .../SpiralAbyss/SpiralAbyssMetadataContext.cs | 22 +++++++ .../SpiralAbyss/SpiralAbyssRecordViewModel.cs | 47 ++++---------- .../ViewModel/SpiralAbyss/SpiralAbyssView.cs | 50 +++++++++++--- 18 files changed, 239 insertions(+), 120 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssMetadataContext.cs diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Identity/IdentityGenerator.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Identity/IdentityGenerator.cs index 26de90b7..a3440e94 100644 --- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Identity/IdentityGenerator.cs +++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Identity/IdentityGenerator.cs @@ -89,6 +89,12 @@ internal sealed class IdentityGenerator : IIncrementalGenerator { return Value.GetHashCode(); } + + /// + public override string ToString() + { + return Value.ToString(); + } } """); diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml b/src/Snap.Hutao/Snap.Hutao/App.xaml index 84305f5c..a4d22ee8 100644 --- a/src/Snap.Hutao/Snap.Hutao/App.xaml +++ b/src/Snap.Hutao/Snap.Hutao/App.xaml @@ -2,8 +2,8 @@ x:Class="Snap.Hutao.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:cwcw="using:CommunityToolkit.WinUI.Controls" xmlns:cwc="using:CommunityToolkit.WinUI.Converters" + xmlns:cwcw="using:CommunityToolkit.WinUI.Controls" xmlns:muxc="using:Microsoft.UI.Xaml.Controls" xmlns:shci="using:Snap.Hutao.Control.Image" xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter" diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Loading.cs b/src/Snap.Hutao/Snap.Hutao/Control/Loading.cs index b35da832..bb443a05 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Loading.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/Loading.cs @@ -9,7 +9,7 @@ namespace Snap.Hutao.Control; [TemplateVisualState(Name = "LoadingOut", GroupName = "CommonStates")] internal class Loading : Microsoft.UI.Xaml.Controls.ContentControl { - private static readonly DependencyProperty IsLoadingProperty = DependencyProperty.Register(nameof(IsLoading), typeof(bool), typeof(Loading), new PropertyMetadata(default(bool), IsLoadingPropertyChanged)); + public static readonly DependencyProperty IsLoadingProperty = DependencyProperty.Register(nameof(IsLoading), typeof(bool), typeof(Loading), new PropertyMetadata(default(bool), IsLoadingPropertyChanged)); private FrameworkElement? presenter; diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Collection.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Collection.cs index 50acd5fc..934f6be5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Collection.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Collection.cs @@ -11,20 +11,6 @@ namespace Snap.Hutao.Extension; /// internal static partial class EnumerableExtension { - /// - /// 尝试添加物品 - /// - /// 物品类型 - /// 集合 - /// 物品 - public static void AddIfNotContains(this Collection collection, T item) - { - if (!collection.Contains(item)) - { - collection.Add(item); - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNullOrEmpty([NotNullWhen(false)][MaybeNullWhen(true)] this Collection? source) { @@ -57,4 +43,17 @@ internal static partial class EnumerableExtension return count; } + + public static int FirstIndexOf(this Collection collection, Func predicate) + { + for (int index = 0; index < collection.Count; index++) + { + if (predicate(collection[index])) + { + return index; + } + } + + return -1; + } } diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SpiralAbyssEntry.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SpiralAbyssEntry.cs index ad52f3b8..4b4e3aef 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SpiralAbyssEntry.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SpiralAbyssEntry.cs @@ -59,14 +59,4 @@ internal sealed class SpiralAbyssEntry : ObservableObject, SpiralAbyss = spiralAbyss, }; } - - /// - /// 更新深渊信息 - /// - /// 深渊信息 - public void UpdateSpiralAbyss(Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss spiralAbyss) - { - SpiralAbyss = spiralAbyss; - OnPropertyChanged(nameof(SpiralAbyss)); - } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs index 2a5f979f..047429df 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs @@ -862,7 +862,7 @@ namespace Snap.Hutao.Resource.Localization { } /// - /// 查找类似 打怪 的本地化字符串。 + /// 查找类似 击败怪物 的本地化字符串。 /// internal static string ModelMetadataTowerGoalTypeDefeatMonsters { get { @@ -871,7 +871,7 @@ namespace Snap.Hutao.Resource.Localization { } /// - /// 查找类似 守塔 的本地化字符串。 + /// 查找类似 守护目标 的本地化字符串。 /// internal static string ModelMetadataTowerGoalTypeDefendTarget { get { @@ -880,7 +880,7 @@ namespace Snap.Hutao.Resource.Localization { } /// - /// 查找类似 附加 的本地化字符串。 + /// 查找类似 附加:增援怪物 的本地化字符串。 /// internal static string ModelMetadataTowerWaveTypeAdditional { get { @@ -1060,7 +1060,7 @@ namespace Snap.Hutao.Resource.Localization { } /// - /// 查找类似 第一波附加:为第一波补充怪物 的本地化字符串。 + /// 查找类似 第一波附加:增援第一波怪物 的本地化字符串。 /// internal static string ModelMetadataTowerWaveTypeWave1Additional { get { diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index eaa6b75d..b466351e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -414,13 +414,13 @@ Need EXACT same string in game - 打怪 + 击败怪物 - 守塔 + 守护目标 - 附加 + 附加:增援怪物 A组:不同的组同时在场,各自分波独立 @@ -480,7 +480,7 @@ 第一波:击败所有怪物,下一波才会出现 - 第一波附加:为第一波补充怪物 + 第一波附加:增援第一波怪物 第二波:击败所有怪物,下一波才会出现 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataServiceIdDataMap.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataServiceIdDataMap.cs index c9a0fc28..b7dcd248 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataServiceIdDataMap.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataServiceIdDataMap.cs @@ -4,7 +4,9 @@ using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Metadata.Avatar; using Snap.Hutao.Model.Metadata.Item; +using Snap.Hutao.Model.Metadata.Monster; using Snap.Hutao.Model.Metadata.Reliquary; +using Snap.Hutao.Model.Metadata.Tower; using Snap.Hutao.Model.Metadata.Weapon; using Snap.Hutao.Model.Primitive; @@ -70,10 +72,18 @@ internal interface IMetadataServiceIdDataMap /// 字典 ValueTask> GetIdToReliquaryMainPropertyMapAsync(CancellationToken token = default); + ValueTask> GetIdToTowerScheduleMapAsync(CancellationToken token = default); + /// /// 异步获取ID到武器的字典 /// /// 取消令牌 /// Id到武器的字典 ValueTask> GetIdToWeaponMapAsync(CancellationToken token = default); + + ValueTask>> GetGroupIdToTowerLevelGroupMapAsync(CancellationToken token = default); + + ValueTask> GetIdToTowerFloorMapAsync(CancellationToken token = default); + + ValueTask> GetRelationshipIdToMonsterMapAsync(CancellationToken token = default); } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Indexing.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Indexing.cs index 2276cd28..f6b0c259 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Indexing.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Indexing.cs @@ -6,7 +6,9 @@ using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Metadata; using Snap.Hutao.Model.Metadata.Avatar; using Snap.Hutao.Model.Metadata.Item; +using Snap.Hutao.Model.Metadata.Monster; using Snap.Hutao.Model.Metadata.Reliquary; +using Snap.Hutao.Model.Metadata.Tower; using Snap.Hutao.Model.Metadata.Weapon; using Snap.Hutao.Model.Primitive; @@ -41,6 +43,21 @@ internal sealed partial class MetadataService return memoryCache.Set(cacheKey, dict); } + public async ValueTask>> GetGroupIdToTowerLevelGroupMapAsync(CancellationToken token = default) + { + string cacheKey = $"{nameof(MetadataService)}.Cache.{FileNameTowerLevel}.Map.Group.{nameof(TowerLevelGroupId)}"; + + if (memoryCache.TryGetValue(cacheKey, out object? value)) + { + ArgumentNullException.ThrowIfNull(value); + return (Dictionary>)value; + } + + List list = await FromCacheOrFileAsync>(FileNameTowerLevel, token).ConfigureAwait(false); + Dictionary> dict = list.GroupBy(l => l.GroupId).ToDictionary(g => g.Key, g => g.ToList()); + return memoryCache.Set(cacheKey, dict); + } + /// public ValueTask> GetIdToAchievementMapAsync(CancellationToken token = default) { @@ -102,6 +119,16 @@ internal sealed partial class MetadataService return FromCacheAsDictionaryAsync(FileNameReliquaryMainAffix, r => r.Id, r => r.Type, token); } + public ValueTask> GetIdToTowerFloorMapAsync(CancellationToken token = default) + { + return FromCacheAsDictionaryAsync(FileNameTowerFloor, t => t.Id, token); + } + + public ValueTask> GetIdToTowerScheduleMapAsync(CancellationToken token = default) + { + return FromCacheAsDictionaryAsync(FileNameTowerSchedule, t => t.Id, token); + } + /// public ValueTask> GetIdToWeaponMapAsync(CancellationToken token = default) { @@ -126,6 +153,11 @@ internal sealed partial class MetadataService return FromCacheAsDictionaryAsync, GrowCurve>(FileNameWeaponCurve, w => w.Level, w => w.Map, token); } + public ValueTask> GetRelationshipIdToMonsterMapAsync(CancellationToken token = default) + { + return FromCacheAsDictionaryAsync(FileNameMonster, m => m.RelationshipId, token); + } + /// public ValueTask> GetNameToAvatarMapAsync(CancellationToken token = default) { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordDbService.cs index 6cdcd94c..097280ee 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordDbService.cs @@ -10,7 +10,7 @@ internal interface ISpiralAbyssRecordDbService { ValueTask AddSpiralAbyssEntryAsync(SpiralAbyssEntry entry); - ValueTask> GetSpiralAbyssEntryCollectionByUidAsync(string uid); + ValueTask> GetSpiralAbyssEntryListByUidAsync(string uid); ValueTask UpdateSpiralAbyssEntryAsync(SpiralAbyssEntry entry); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordService.cs b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordService.cs index 52090398..3fcf4a4b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/ISpiralAbyssRecordService.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using Snap.Hutao.Model.Entity; +using Snap.Hutao.ViewModel.SpiralAbyss; using Snap.Hutao.ViewModel.User; using System.Collections.ObjectModel; @@ -18,7 +19,9 @@ internal interface ISpiralAbyssRecordService /// /// 当前角色 /// 深渊记录集合 - ValueTask> GetSpiralAbyssCollectionAsync(UserAndUid userAndUid); + ValueTask> GetSpiralAbyssViewCollectionAsync(UserAndUid userAndUid); + + ValueTask InitializeAsync(); /// /// 异步刷新深渊记录 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordDbService.cs index 2cdd5ef4..668cb07a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordDbService.cs @@ -15,19 +15,16 @@ internal sealed partial class SpiralAbyssRecordDbService : ISpiralAbyssRecordDbS { private readonly IServiceProvider serviceProvider; - public async ValueTask> GetSpiralAbyssEntryCollectionByUidAsync(string uid) + public async ValueTask> GetSpiralAbyssEntryListByUidAsync(string uid) { using (IServiceScope scope = serviceProvider.CreateScope()) { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - - List entries = await appDbContext.SpiralAbysses + return await appDbContext.SpiralAbysses .Where(s => s.Uid == uid) .OrderByDescending(s => s.ScheduleId) .ToListAsync() .ConfigureAwait(false); - - return entries.ToObservableCollection(); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordService.cs b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordService.cs index ba71507f..fe791ab4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordService.cs @@ -3,6 +3,10 @@ using Snap.Hutao.Core.DependencyInjection.Abstraction; using Snap.Hutao.Model.Entity; +using Snap.Hutao.Model.Metadata; +using Snap.Hutao.Model.Primitive; +using Snap.Hutao.Service.Metadata; +using Snap.Hutao.ViewModel.SpiralAbyss; using Snap.Hutao.ViewModel.User; using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord; using Snap.Hutao.Web.Response; @@ -18,15 +22,35 @@ namespace Snap.Hutao.Service.SpiralAbyss; [Injection(InjectAs.Scoped, typeof(ISpiralAbyssRecordService))] internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordService { - private readonly ITaskContext taskContext; private readonly IOverseaSupportFactory gameRecordClientFactory; private readonly ISpiralAbyssRecordDbService spiralAbyssRecordDbService; + private readonly IMetadataService metadataService; + private readonly ITaskContext taskContext; private string? uid; - private ObservableCollection? spiralAbysses; + private ObservableCollection? spiralAbysses; + private SpiralAbyssMetadataContext? metadataContext; + + public async ValueTask InitializeAsync() + { + if (await metadataService.InitializeAsync().ConfigureAwait(false)) + { + metadataContext = new() + { + IdScheduleMap = await metadataService.GetIdToTowerScheduleMapAsync().ConfigureAwait(false), + IdFloorMap = await metadataService.GetIdToTowerFloorMapAsync().ConfigureAwait(false), + IdLevelGroupMap = await metadataService.GetGroupIdToTowerLevelGroupMapAsync().ConfigureAwait(false), + IdMonsterMap = await metadataService.GetRelationshipIdToMonsterMapAsync().ConfigureAwait(false), + IdAvatarMap = AvatarIds.WithPlayers(await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false)), + }; + return true; + } + + return false; + } /// - public async ValueTask> GetSpiralAbyssCollectionAsync(UserAndUid userAndUid) + public async ValueTask> GetSpiralAbyssViewCollectionAsync(UserAndUid userAndUid) { if (uid != userAndUid.Uid.Value) { @@ -34,9 +58,15 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi } uid = userAndUid.Uid.Value; - spiralAbysses ??= await spiralAbyssRecordDbService - .GetSpiralAbyssEntryCollectionByUidAsync(userAndUid.Uid.Value) - .ConfigureAwait(false); + if (spiralAbysses is null) + { + List list = await spiralAbyssRecordDbService + .GetSpiralAbyssEntryListByUidAsync(userAndUid.Uid.Value) + .ConfigureAwait(false); + + ArgumentNullException.ThrowIfNull(metadataContext); + spiralAbysses = list.SelectList(entity => SpiralAbyssView.From(entity, metadataContext)).ToObservableCollection(); + } return spiralAbysses; } @@ -44,6 +74,11 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi /// public async ValueTask RefreshSpiralAbyssAsync(UserAndUid userAndUid) { + // request the index first + await gameRecordClientFactory + .Create(userAndUid.User.IsOversea) + .GetPlayerInfoAsync(userAndUid) + .ConfigureAwait(false); await RefreshSpiralAbyssCoreAsync(userAndUid, SpiralAbyssSchedule.Last).ConfigureAwait(false); await RefreshSpiralAbyssCoreAsync(userAndUid, SpiralAbyssSchedule.Current).ConfigureAwait(false); } @@ -60,20 +95,26 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss webSpiralAbyss = response.Data; ArgumentNullException.ThrowIfNull(spiralAbysses); - if (spiralAbysses.SingleOrDefault(s => s.ScheduleId == webSpiralAbyss.ScheduleId) is { } existEntry) - { - await taskContext.SwitchToMainThreadAsync(); - existEntry.UpdateSpiralAbyss(webSpiralAbyss); + ArgumentNullException.ThrowIfNull(metadataContext); + int index = spiralAbysses.FirstIndexOf(s => s.Entity.ScheduleId == webSpiralAbyss.ScheduleId); + if (index > 0) + { await taskContext.SwitchToBackgroundAsync(); - await spiralAbyssRecordDbService.UpdateSpiralAbyssEntryAsync(existEntry).ConfigureAwait(false); + SpiralAbyssView view = spiralAbysses[index]; + view.Entity.SpiralAbyss = webSpiralAbyss; + await spiralAbyssRecordDbService.UpdateSpiralAbyssEntryAsync(view.Entity).ConfigureAwait(false); + + await taskContext.SwitchToMainThreadAsync(); + spiralAbysses.RemoveAt(index); + spiralAbysses.Insert(index, SpiralAbyssView.From(view.Entity, metadataContext)); } else { SpiralAbyssEntry newEntry = SpiralAbyssEntry.From(userAndUid.Uid.Value, webSpiralAbyss); await taskContext.SwitchToMainThreadAsync(); - spiralAbysses.Insert(0, newEntry); + spiralAbysses.Insert(0, SpiralAbyssView.From(newEntry, metadataContext)); await taskContext.SwitchToBackgroundAsync(); await spiralAbyssRecordDbService.AddSpiralAbyssEntryAsync(newEntry).ConfigureAwait(false); diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml index 090b434e..185fe6fd 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml @@ -188,12 +188,17 @@ - + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/SpiralAbyssRecordPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/SpiralAbyssRecordPage.xaml index 96a88c80..a19e01e1 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/SpiralAbyssRecordPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/SpiralAbyssRecordPage.xaml @@ -27,15 +27,15 @@ IsPaneOpen="True" OpenPaneLength="120" PaneBackground="Transparent" - Visibility="{Binding SpiralAbyssView, Converter={StaticResource EmptyObjectToVisibilityConverter}}"> + Visibility="{Binding SelectedView, Converter={StaticResource EmptyObjectToVisibilityConverter}}"> + SelectedItem="{Binding SelectedView, Mode=TwoWay}"> - + @@ -55,30 +55,32 @@ Label="{shcm:ResourceString Name=ViewSpiralAbyssRefresh}"/> - + + + 0 + 0 + 0 + - + + - - 0 - 0 - 0 - - - + + - + + @@ -88,13 +90,19 @@ - + - + + + - - + - + IdScheduleMap { get; set; } = default!; + + public Dictionary IdFloorMap { get; set; } = default!; + + public Dictionary> IdLevelGroupMap { get; set; } = default!; + + public Dictionary IdMonsterMap { get; set; } = default!; + + public Dictionary IdAvatarMap { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssRecordViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssRecordViewModel.cs index a81297e3..d3d08bf7 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssRecordViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssRecordViewModel.cs @@ -27,42 +27,22 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel { private readonly ISpiralAbyssRecordService spiralAbyssRecordService; private readonly HomaSpiralAbyssClient spiralAbyssClient; - private readonly IMetadataService metadataService; private readonly IInfoBarService infoBarService; private readonly ITaskContext taskContext; private readonly IUserService userService; - private Dictionary? idAvatarMap; - private ObservableCollection? spiralAbyssEntries; - private SpiralAbyssEntry? selectedEntry; - private SpiralAbyssView? spiralAbyssView; + private ObservableCollection? spiralAbyssEntries; + private SpiralAbyssView? selectedView; /// /// 深渊记录 /// - public ObservableCollection? SpiralAbyssEntries { get => spiralAbyssEntries; set => SetProperty(ref spiralAbyssEntries, value); } + public ObservableCollection? SpiralAbyssEntries { get => spiralAbyssEntries; set => SetProperty(ref spiralAbyssEntries, value); } /// /// 选中的深渊信息 /// - public SpiralAbyssEntry? SelectedEntry - { - get => selectedEntry; set - { - // We dont need to check the result here, - // just refresh the view anyway. - SetProperty(ref selectedEntry, value); - if (value is not null && idAvatarMap is not null) - { - SpiralAbyssView = new(value.SpiralAbyss, idAvatarMap); - } - } - } - - /// - /// 深渊的只读视图 - /// - public SpiralAbyssView? SpiralAbyssView { get => spiralAbyssView; set => SetProperty(ref spiralAbyssView, value); } + public SpiralAbyssView? SelectedView { get => selectedView; set => SetProperty(ref selectedView, value); } /// public void Receive(UserChangedMessage message) @@ -73,17 +53,14 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel } else { - SpiralAbyssView = null; + SelectedView = null; } } protected override async ValueTask InitializeUIAsync() { - if (await metadataService.InitializeAsync().ConfigureAwait(false)) + if (await spiralAbyssRecordService.InitializeAsync().ConfigureAwait(false)) { - idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false); - idAvatarMap = AvatarIds.WithPlayers(idAvatarMap); - if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid)) { await UpdateSpiralAbyssCollectionAsync(userAndUid).ConfigureAwait(false); @@ -100,13 +77,13 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel private async ValueTask UpdateSpiralAbyssCollectionAsync(UserAndUid userAndUid) { - ObservableCollection? temp = null; + ObservableCollection? collection = null; try { using (await EnterCriticalExecutionAsync().ConfigureAwait(false)) { - temp = await spiralAbyssRecordService - .GetSpiralAbyssCollectionAsync(userAndUid) + collection = await spiralAbyssRecordService + .GetSpiralAbyssViewCollectionAsync(userAndUid) .ConfigureAwait(false); } } @@ -115,8 +92,8 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel } await taskContext.SwitchToMainThreadAsync(); - SpiralAbyssEntries = temp; - SelectedEntry = SpiralAbyssEntries?.FirstOrDefault(); + SpiralAbyssEntries = collection; + SelectedView = SpiralAbyssEntries?.FirstOrDefault(); } [Command("RefreshCommand")] @@ -140,7 +117,7 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel } await taskContext.SwitchToMainThreadAsync(); - SelectedEntry = SpiralAbyssEntries.FirstOrDefault(); + SelectedView = SpiralAbyssEntries.FirstOrDefault(); } } } diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssView.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssView.cs index adf28d8f..d6710711 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssView.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssView.cs @@ -1,6 +1,11 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Core.Abstraction; +using Snap.Hutao.Model; +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Model.Metadata.Avatar; +using Snap.Hutao.Model.Metadata.Tower; using Snap.Hutao.Model.Primitive; namespace Snap.Hutao.ViewModel.SpiralAbyss; @@ -9,27 +14,47 @@ namespace Snap.Hutao.ViewModel.SpiralAbyss; /// 深渊视图 /// [HighQuality] -internal sealed class SpiralAbyssView +internal sealed class SpiralAbyssView : IEntityOnly, + IMappingFrom { + private readonly SpiralAbyssEntry entity; + /// /// 构造一个新的深渊视图 /// - /// 深渊信息 + /// 实体 /// Id角色映射 - public SpiralAbyssView(Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss spiralAbyss, Dictionary idAvatarMap) + private SpiralAbyssView(SpiralAbyssEntry entity, SpiralAbyssMetadataContext context) { + this.entity = entity; + Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss spiralAbyss = entity.SpiralAbyss; + + TowerSchedule towerSchedule = context.IdScheduleMap[(uint)entity.ScheduleId]; + TimeFormatted = $"{towerSchedule.Open:yyyy/MM/dd HH:mm:ss} - {towerSchedule.Close:yyyy/MM/dd HH:mm:ss}"; + + BlessingName = towerSchedule.BuffName; + Blessings = towerSchedule.Descriptions; + TotalBattleTimes = spiralAbyss.TotalBattleTimes; TotalStar = spiralAbyss.TotalStar; MaxFloor = spiralAbyss.MaxFloor; - Reveals = spiralAbyss.RevealRank.SelectList(r => new RankAvatar(r.Value, idAvatarMap[r.AvatarId])); - Defeat = spiralAbyss.DefeatRank.Select(r => new RankAvatar(r.Value, idAvatarMap[r.AvatarId])).SingleOrDefault(); - Damage = spiralAbyss.DamageRank.Select(r => new RankAvatar(r.Value, idAvatarMap[r.AvatarId])).SingleOrDefault(); - TakeDamage = spiralAbyss.TakeDamageRank.Select(r => new RankAvatar(r.Value, idAvatarMap[r.AvatarId])).SingleOrDefault(); - NormalSkill = spiralAbyss.NormalSkillRank.Select(r => new RankAvatar(r.Value, idAvatarMap[r.AvatarId])).SingleOrDefault(); - EnergySkill = spiralAbyss.EnergySkillRank.Select(r => new RankAvatar(r.Value, idAvatarMap[r.AvatarId])).SingleOrDefault(); - Floors = spiralAbyss.Floors.Select(f => new FloorView(f, idAvatarMap)).Reverse().ToList(); + Reveals = spiralAbyss.RevealRank.SelectList(r => new RankAvatar(r.Value, context.IdAvatarMap[r.AvatarId])); + Defeat = spiralAbyss.DefeatRank.Select(r => new RankAvatar(r.Value, context.IdAvatarMap[r.AvatarId])).SingleOrDefault(); + Damage = spiralAbyss.DamageRank.Select(r => new RankAvatar(r.Value, context.IdAvatarMap[r.AvatarId])).SingleOrDefault(); + TakeDamage = spiralAbyss.TakeDamageRank.Select(r => new RankAvatar(r.Value, context.IdAvatarMap[r.AvatarId])).SingleOrDefault(); + NormalSkill = spiralAbyss.NormalSkillRank.Select(r => new RankAvatar(r.Value, context.IdAvatarMap[r.AvatarId])).SingleOrDefault(); + EnergySkill = spiralAbyss.EnergySkillRank.Select(r => new RankAvatar(r.Value, context.IdAvatarMap[r.AvatarId])).SingleOrDefault(); + Floors = spiralAbyss.Floors.Select(f => new FloorView(f, context.IdAvatarMap)).Reverse().ToList(); } + public SpiralAbyssEntry Entity { get => entity; } + + public string TimeFormatted { get; } + + public string BlessingName { get; } + + public List Blessings { get; } + /// /// 战斗次数 /// @@ -79,4 +104,9 @@ internal sealed class SpiralAbyssView /// 层信息 /// public List Floors { get; } + + public static SpiralAbyssView From(SpiralAbyssEntry entity, SpiralAbyssMetadataContext context) + { + return new(entity, context); + } } \ No newline at end of file