From 623893e00ebb7d9d408ad31010c86ea5b330c84d Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Mon, 23 Jan 2023 12:58:00 +0800 Subject: [PATCH] remove visual transition gap in gacha log initialization --- .../Snap.Hutao/Core/CoreEnvironment.cs | 4 +- .../Snap.Hutao/Core/LifeCycle/Activation.cs | 2 +- .../ConcurrentCancellationTokenSource.cs | 27 +- .../Binding/Cultivation/CultivateItem.cs | 4 +- .../Cultivation/CultivateItemHelper.cs | 64 ---- .../Model/Intrinsic/MaterialType.cs | 84 ++--- .../Snap.Hutao/Model/Metadata/Material.cs | 54 +++ .../Snap.Hutao/Package.appxmanifest | 2 +- .../Service/Cultivation/CultivationService.cs | 107 +++--- .../Cultivation/ICultivationService.cs | 9 +- ...r.cs => IMetadataServiceInitialization.cs} | 2 +- .../Service/Metadata/MetadataService.cs | 2 +- .../Snap.Hutao/View/Page/CultivationPage.xaml | 342 +++++++++--------- .../ViewModel/CultivationViewModel.cs | 65 ++-- .../Snap.Hutao/ViewModel/GachaLogViewModel.cs | 13 +- .../Web/Bridge/MiHoYoJSInterface.cs | 15 +- 16 files changed, 426 insertions(+), 370 deletions(-) delete mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItemHelper.cs rename src/Snap.Hutao/Snap.Hutao/Service/Metadata/{IMetadataInitializer.cs => IMetadataServiceInitialization.cs} (89%) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs index 92b8293f..b64e09b1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs @@ -119,8 +119,8 @@ internal static class CoreEnvironment { string myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); #if RELEASE - // 将测试版与正式版的文件目录分离 - string folderName = Package.Current.PublisherDisplayName == "DGP Studio CI" ? "HutaoAlpha" : "Hutao"; + // 将测试版与正式版的文件目录分离 + string folderName = Package.Current.PublisherDisplayName == "DGP Studio CI" ? "HutaoAlpha" : "Hutao"; #else // 使得迁移能正常生成 string folderName = "Hutao"; diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs index 2b0b1b9d..172de3c2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs @@ -149,7 +149,7 @@ internal static class Activation Ioc.Default .GetRequiredService() - .ImplictAs()? + .ImplictAs()? .InitializeInternalAsync() .SafeForget(); } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/ConcurrentCancellationTokenSource.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/ConcurrentCancellationTokenSource.cs index 1bb621d1..f2faf66c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/ConcurrentCancellationTokenSource.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/ConcurrentCancellationTokenSource.cs @@ -6,9 +6,30 @@ using System.Collections.Concurrent; namespace Snap.Hutao.Core.Threading; /// -/// 并发 +/// 无区分项的并发 +/// +[SuppressMessage("", "CA1001")] +internal class ConcurrentCancellationTokenSource +{ + private CancellationTokenSource source = new(); + + /// + /// 注册取消令牌 + /// + /// 取消令牌 + public CancellationToken Register() + { + source.Cancel(); + source = new(); + return source.Token; + } +} + +/// +/// 有区分项的并发 /// /// 项类型 +[SuppressMessage("", "SA1402")] internal class ConcurrentCancellationTokenSource where TItem : notnull { @@ -17,7 +38,7 @@ internal class ConcurrentCancellationTokenSource /// /// 为某个项注册取消令牌 /// - /// 项 + /// 区分项 /// 取消令牌 public CancellationToken Register(TItem item) { @@ -28,4 +49,4 @@ internal class ConcurrentCancellationTokenSource return waitingItems.GetOrAdd(item, new CancellationTokenSource()).Token; } -} +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItem.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItem.cs index 44b5c7fa..05ebfb3a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItem.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItem.cs @@ -4,7 +4,6 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Snap.Hutao.Model.Metadata; -using Snap.Hutao.Service.Cultivation; namespace Snap.Hutao.Model.Binding.Cultivation; @@ -25,7 +24,7 @@ public class CultivateItem : ObservableObject Inner = inner; Entity = entity; isFinished = Entity.IsFinished; - IsToday = CultivateItemHelper.IsTodaysMaterial(inner.Id, DateTimeOffset.Now); + IsToday = inner.IsTodaysItem(); FinishStateCommand = new RelayCommand(FlipIsFinished); } @@ -55,7 +54,6 @@ public class CultivateItem : ObservableObject if (SetProperty(ref isFinished, value)) { Entity.IsFinished = value; - Ioc.Default.GetRequiredService().SaveCultivateItem(Entity); } } } diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItemHelper.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItemHelper.cs deleted file mode 100644 index 775e4563..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItemHelper.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Model.Binding.Cultivation; - -/// -/// 养成物品帮助类 -/// -public static class CultivateItemHelper -{ - /// - /// 判断是否为当日物品 - /// - /// 材料Id - /// 时间 - /// 是否为当日物品 - public static bool IsTodaysMaterial(int itemId, DateTimeOffset now) - { - DateTimeOffset utcNow = now.ToUniversalTime(); - utcNow = utcNow.AddHours(4); - DayOfWeek dayOfWeek = utcNow.DayOfWeek; - - return dayOfWeek switch - { - DayOfWeek.Monday or DayOfWeek.Thursday => itemId switch - { - 104301 or 104302 or 104303 => true, // 「自由」 - 104310 or 104311 or 104312 => true, // 「繁荣」 - 104320 or 104321 or 104322 => true, // 「浮世」 - 104329 or 104330 or 104331 => true, // 「诤言」 - 114001 or 114002 or 114003 or 114004 => true, // 高塔孤王 - 114013 or 114014 or 114015 or 114016 => true, // 孤云寒林 - 114025 or 114026 or 114027 or 114028 => true, // 远海夷地 - 114037 or 114038 or 114039 or 114040 => true, // 谧林涓露 - _ => false, - }, - DayOfWeek.Tuesday or DayOfWeek.Friday => itemId switch - { - 104304 or 104305 or 104306 => true, // 「抗争」 - 104313 or 104314 or 104315 => true, // 「勤劳」 - 104323 or 104324 or 104325 => true, // 「风雅」 - 104332 or 104333 or 104334 => true, // 「巧思」 - 114005 or 114006 or 114007 or 114008 => true, // 凛风奔狼 - 114017 or 114018 or 114019 or 114020 => true, // 雾海云间 - 114029 or 114030 or 114031 or 114032 => true, // 鸣神御灵 - 114041 or 114042 or 114043 or 114044 => true, // 绿洲花园 - _ => false, - }, - DayOfWeek.Wednesday or DayOfWeek.Saturday => itemId switch - { - 104307 or 104308 or 104309 => true, // 「诗文」 - 104316 or 104317 or 104318 => true, // 「黄金」 - 104326 or 104327 or 104328 => true, // 「天光」 - 104335 or 104336 or 104337 => true, // 「笃行」 - 114009 or 114010 or 114011 or 114012 => true, // 狮牙斗士 - 114021 or 114022 or 114023 or 114024 => true, // 漆黑陨铁 - 114033 or 114034 or 114035 or 114036 => true, // 今昔剧画 - 114045 or 114046 or 114047 or 114048 => true, // 谧林涓露 - _ => false, - }, - _ => false, - }; - } -} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/MaterialType.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/MaterialType.cs index 9def3dcd..e77f1a5c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/MaterialType.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/MaterialType.cs @@ -13,47 +13,47 @@ public enum MaterialType MATERIAL_FOOD = 1, MATERIAL_QUEST = 2, MATERIAL_EXCHANGE = 4, - MATERIAL_CONSUME, - MATERIAL_EXP_FRUIT, - MATERIAL_AVATAR, - MATERIAL_ADSORBATE, - MATERIAL_CRICKET, - MATERIAL_ELEM_CRYSTAL, - MATERIAL_WEAPON_EXP_STONE, - MATERIAL_CHEST, - MATERIAL_RELIQUARY_MATERIAL, - MATERIAL_AVATAR_MATERIAL, - MATERIAL_NOTICE_ADD_HP, - MATERIAL_SEA_LAMP, - MATERIAL_SELECTABLE_CHEST, - MATERIAL_FLYCLOAK, - MATERIAL_NAMECARD, - MATERIAL_TALENT, - MATERIAL_WIDGET, - MATERIAL_CHEST_BATCH_USE, - MATERIAL_FAKE_ABSORBATE, - MATERIAL_CONSUME_BATCH_USE, - MATERIAL_WOOD, + MATERIAL_CONSUME = 5, + MATERIAL_EXP_FRUIT = 6, + MATERIAL_AVATAR = 7, + MATERIAL_ADSORBATE = 8, + MATERIAL_CRICKET = 9, + MATERIAL_ELEM_CRYSTAL = 10, + MATERIAL_WEAPON_EXP_STONE = 11, + MATERIAL_CHEST = 12, + MATERIAL_RELIQUARY_MATERIAL = 13, + MATERIAL_AVATAR_MATERIAL = 14, + MATERIAL_NOTICE_ADD_HP = 15, + MATERIAL_SEA_LAMP = 16, + MATERIAL_SELECTABLE_CHEST = 17, + MATERIAL_FLYCLOAK = 18, + MATERIAL_NAMECARD = 19, + MATERIAL_TALENT = 20, + MATERIAL_WIDGET = 21, + MATERIAL_CHEST_BATCH_USE = 22, + MATERIAL_FAKE_ABSORBATE = 23, + MATERIAL_CONSUME_BATCH_USE = 24, + MATERIAL_WOOD = 25, MATERIAL_FURNITURE_FORMULA = 27, - MATERIAL_CHANNELLER_SLAB_BUFF, - MATERIAL_FURNITURE_SUITE_FORMULA, - MATERIAL_COSTUME, - MATERIAL_HOME_SEED, - MATERIAL_FISH_BAIT, - MATERIAL_FISH_ROD, - MATERIAL_SUMO_BUFF, // never appear - MATERIAL_FIREWORKS, - MATERIAL_BGM, - MATERIAL_SPICE_FOOD, - MATERIAL_ACTIVITY_ROBOT, - MATERIAL_ACTIVITY_GEAR, - MATERIAL_ACTIVITY_JIGSAW, - MATERIAL_ARANARA, - MATERIAL_GCG_CARD, - MATERIAL_GCG_CARD_FACE, // 影幻卡面 - MATERIAL_GCG_CARD_BACK, - MATERIAL_GCG_FIELD, - MATERIAL_DESHRET_MANUAL, - MATERIAL_RENAME_ITEM, - MATERIAL_GCG_EXCHANGE_ITEM, + MATERIAL_CHANNELLER_SLAB_BUFF = 28, + MATERIAL_FURNITURE_SUITE_FORMULA = 29, + MATERIAL_COSTUME = 30, + MATERIAL_HOME_SEED = 31, + MATERIAL_FISH_BAIT = 32, + MATERIAL_FISH_ROD = 33, + MATERIAL_SUMO_BUFF = 34, // never appear + MATERIAL_FIREWORKS = 35, + MATERIAL_BGM = 36, + MATERIAL_SPICE_FOOD = 37, + MATERIAL_ACTIVITY_ROBOT = 38, + MATERIAL_ACTIVITY_GEAR = 39, + MATERIAL_ACTIVITY_JIGSAW = 40, + MATERIAL_ARANARA = 41, + MATERIAL_GCG_CARD = 42, + MATERIAL_GCG_CARD_FACE = 43, // 影幻卡面 + MATERIAL_GCG_CARD_BACK = 44, + MATERIAL_GCG_FIELD = 45, + MATERIAL_DESHRET_MANUAL = 46, + MATERIAL_RENAME_ITEM = 47, + MATERIAL_GCG_EXCHANGE_ITEM = 48, } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Material.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Material.cs index 5879ebae..79dac9ef 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Material.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Material.cs @@ -3,6 +3,7 @@ using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Primitive; +using System.Collections.Immutable; namespace Snap.Hutao.Model.Metadata; @@ -11,6 +12,42 @@ namespace Snap.Hutao.Model.Metadata; /// public class Material { + private static readonly ImmutableHashSet MondayThursdayItems = new HashSet + { + 104301, 104302, 104303, // 「自由」 + 104310, 104311, 104312, // 「繁荣」 + 104320, 104321, 104322, // 「浮世」 + 104329, 104330, 104331, // 「诤言」 + 114001, 114002, 114003, 114004, // 高塔孤王 + 114013, 114014, 114015, 114016, // 孤云寒林 + 114025, 114026, 114027, 114028, // 远海夷地 + 114037, 114038, 114039, 114040, // 谧林涓露 + }.ToImmutableHashSet(); + + private static readonly ImmutableHashSet TuesdayFridayItems = new HashSet + { + 104304, 104305, 104306, // 「抗争」 + 104313, 104314, 104315, // 「勤劳」 + 104323, 104324, 104325, // 「风雅」 + 104332, 104333, 104334, // 「巧思」 + 114005, 114006, 114007, 114008, // 凛风奔狼 + 114017, 114018, 114019, 114020, // 雾海云间 + 114029, 114030, 114031, 114032, // 鸣神御灵 + 114041, 114042, 114043, 114044, // 绿洲花园 + }.ToImmutableHashSet(); + + private static readonly ImmutableHashSet WednesdaySaturdayItems = new HashSet + { + 104307, 104308, 104309, // 「诗文」 + 104316, 104317, 104318, // 「黄金」 + 104326, 104327, 104328, // 「天光」 + 104335, 104336, 104337, // 「笃行」 + 114009, 114010, 114011, 114012, // 狮牙斗士 + 114021, 114022, 114023, 114024, // 漆黑陨铁 + 114033, 114034, 114035, 114036, // 今昔剧画 + 114045, 114046, 114047, 114048, // 谧林涓露 + }.ToImmutableHashSet(); + /// /// 物品Id /// @@ -89,4 +126,21 @@ public class Material _ => false, }; } + + /// + /// 判断是否为当日物品 + /// O(1) 操作 + /// + /// 星期日视为当日材料 + /// 是否为当日物品 + public bool IsTodaysItem(bool treatSundayAsTrue = false) + { + return DateTimeOffset.UtcNow.AddHours(4).DayOfWeek switch + { + DayOfWeek.Monday or DayOfWeek.Thursday => MondayThursdayItems.Contains(Id), + DayOfWeek.Tuesday or DayOfWeek.Friday => TuesdayFridayItems.Contains(Id), + DayOfWeek.Wednesday or DayOfWeek.Saturday => WednesdaySaturdayItems.Contains(Id), + _ => false, + }; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest index 2038ec32..5c038e20 100644 --- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest +++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest @@ -12,7 +12,7 @@ + Version="1.3.13.0" /> 胡桃 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs index 6b0b4e90..c70d1963 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs @@ -5,13 +5,16 @@ using CommunityToolkit.Mvvm.Messaging; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Snap.Hutao.Core.Database; +using Snap.Hutao.Extension; using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity.Database; using Snap.Hutao.Model.Primitive; +using Snap.Hutao.Service.Metadata; using System.Collections.ObjectModel; using BindingCultivateEntry = Snap.Hutao.Model.Binding.Cultivation.CultivateEntry; using BindingCultivateItem = Snap.Hutao.Model.Binding.Cultivation.CultivateItem; using BindingInventoryItem = Snap.Hutao.Model.Binding.Inventory.InventoryItem; +using BindingStatisticsItem = Snap.Hutao.Model.Binding.Cultivation.StatisticsCultivateItem; namespace Snap.Hutao.Service.Cultivation; @@ -129,22 +132,21 @@ internal class CultivationService : ICultivationService } /// - public async Task> GetCultivateEntriesAsync( - CultivateProject cultivateProject, - List materials, - Dictionary idAvatarMap, - Dictionary idWeaponMap) + public async Task> GetCultivateEntriesAsync(CultivateProject cultivateProject) { await ThreadHelper.SwitchToBackgroundAsync(); using (IServiceScope scope = scopeFactory.CreateScope()) { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + IMetadataService metadataService = scope.ServiceProvider.GetRequiredService(); - Guid projectId = cultivateProject.InnerId; + List materials = await metadataService.GetMaterialsAsync().ConfigureAwait(false); + Dictionary idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false); + Dictionary idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false); List results = new(); List entries = await appDbContext.CultivateEntries - .Where(e => e.ProjectId == projectId) + .Where(e => e.ProjectId == cultivateProject.InnerId) .ToListAsync() .ConfigureAwait(false); @@ -153,13 +155,8 @@ internal class CultivationService : ICultivationService Guid entryId = entry.InnerId; List resultItems = new(); - List items = await appDbContext.CultivateItems - .Where(i => i.EntryId == entryId) - .OrderBy(i => i.ItemId) - .ToListAsync() - .ConfigureAwait(false); - foreach (CultivateItem item in items) + foreach (CultivateItem item in await GetEntryItemsAsync(appDbContext, entryId).ConfigureAwait(false)) { resultItems.Add(new(materials.Single(m => m.Id == item.ItemId), item)); } @@ -174,71 +171,64 @@ internal class CultivationService : ICultivationService results.Add(new(entry, itemBase, resultItems)); } - return new(results.OrderByDescending(e => e.Items.Any(i => i.IsToday))); + return results + .OrderByDescending(e => e.Items.Any(i => i.IsToday)) + .ToObservableCollection(); } } /// - public async Task> GetStatisticsCultivateItemsAsync(CultivateProject cultivateProject, List materials) + public async Task> GetStatisticsCultivateItemCollectionAsync(CultivateProject cultivateProject, CancellationToken token) { using (IServiceScope scope = scopeFactory.CreateScope()) { + List resultItems = new(); AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + List materials = await scope.ServiceProvider + .GetRequiredService() + .GetMaterialsAsync(default) + .ConfigureAwait(false); + Guid projectId = cultivateProject.InnerId; - List resultItems = new(); + token.ThrowIfCancellationRequested(); - List entries = await appDbContext.CultivateEntries - .AsNoTracking() - .Where(e => e.ProjectId == projectId) - .ToListAsync() - .ConfigureAwait(false); - - foreach (CultivateEntry entry in entries) + foreach (CultivateEntry entry in await GetProjectEntriesAsync(appDbContext, projectId).ConfigureAwait(false)) { - Guid entryId = entry.InnerId; - - List items = await appDbContext.CultivateItems - .AsNoTracking() - .Where(i => i.EntryId == entryId) - .OrderBy(i => i.ItemId) - .ToListAsync() - .ConfigureAwait(false); - - foreach (CultivateItem item in items) + foreach (CultivateItem item in await GetEntryItemsAsync(appDbContext, entry.InnerId).ConfigureAwait(false)) { if (item.IsFinished) { continue; } - if (resultItems.SingleOrDefault(i => i.Inner.Id == item.ItemId) is Model.Binding.Cultivation.StatisticsCultivateItem inPlaceItem) + if (resultItems.SingleOrDefault(i => i.Inner.Id == item.ItemId) is BindingStatisticsItem existedItem) { - inPlaceItem.Count += item.Count; + existedItem.Count += item.Count; } else { - resultItems.Add(new(materials.Single(m => m.Id == item.ItemId), item)); + resultItems.Add(new(materials!.Single(m => m.Id == item.ItemId), item)); } } } - List inventoryItems = await appDbContext.InventoryItems - .AsNoTracking() - .Where(e => e.ProjectId == projectId) - .ToListAsync() - .ConfigureAwait(false); + token.ThrowIfCancellationRequested(); - foreach (InventoryItem inventoryItem in inventoryItems) + foreach (InventoryItem inventoryItem in await GetProjectInventoryAsync(appDbContext, projectId).ConfigureAwait(false)) { - if (resultItems.SingleOrDefault(i => i.Inner.Id == inventoryItem.ItemId) is Model.Binding.Cultivation.StatisticsCultivateItem inPlaceItem) + if (resultItems.SingleOrDefault(i => i.Inner.Id == inventoryItem.ItemId) is BindingStatisticsItem existedItem) { - inPlaceItem.TotalCount += inventoryItem.Count; + existedItem.TotalCount += inventoryItem.Count; } } - return resultItems.OrderByDescending(i => i.Count).ToList(); + token.ThrowIfCancellationRequested(); + + await ThreadHelper.SwitchToMainThreadAsync(); + + return resultItems.OrderByDescending(i => i.Count).ToObservableCollection(); } } @@ -246,9 +236,11 @@ internal class CultivationService : ICultivationService public async Task RemoveCultivateEntryAsync(Guid entryId) { await ThreadHelper.SwitchToBackgroundAsync(); + IEnumerable removed; using (IServiceScope scope = scopeFactory.CreateScope()) { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + removed = await GetEntryItemsAsync(appDbContext, entryId).ConfigureAwait(false); await appDbContext.CultivateEntries.Where(i => i.InnerId == entryId).ExecuteDeleteAsync().ConfigureAwait(false); } } @@ -309,4 +301,29 @@ internal class CultivationService : ICultivationService return true; } + + private static Task> GetProjectInventoryAsync(AppDbContext appDbContext, Guid projectId) + { + return appDbContext.InventoryItems + .AsNoTracking() + .Where(e => e.ProjectId == projectId) + .ToListAsync(); + } + + private static Task> GetProjectEntriesAsync(AppDbContext appDbContext, Guid projectId) + { + return appDbContext.CultivateEntries + .AsNoTracking() + .Where(e => e.ProjectId == projectId) + .ToListAsync(); + } + + private static Task> GetEntryItemsAsync(AppDbContext appDbContext, Guid entryId) + { + return appDbContext.CultivateItems + .AsNoTracking() + .Where(i => i.EntryId == entryId) + .OrderBy(i => i.ItemId) + .ToListAsync(); + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs index dd912efc..c6571346 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/ICultivationService.cs @@ -24,11 +24,8 @@ internal interface ICultivationService /// 获取绑定用的养成列表 /// /// 养成计划 - /// 材料 - /// Id角色映射 - /// Id武器映射 /// 绑定用的养成列表 - Task> GetCultivateEntriesAsync(CultivateProject cultivateProject, List materials, Dictionary idAvatarMap, Dictionary idWeaponMap); + Task> GetCultivateEntriesAsync(CultivateProject cultivateProject); /// /// 获取物品列表 @@ -48,9 +45,9 @@ internal interface ICultivationService /// 异步获取统计物品列表 /// /// 养成计划 - /// 元数据 + /// 取消令牌 /// 统计物品列表 - Task> GetStatisticsCultivateItemsAsync(CultivateProject cultivateProject, List materials); + Task> GetStatisticsCultivateItemCollectionAsync(CultivateProject cultivateProject, CancellationToken token); /// /// 删除养成清单 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataInitializer.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataServiceInitialization.cs similarity index 89% rename from src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataInitializer.cs rename to src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataServiceInitialization.cs index 1427fbbc..7a0de7e0 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataInitializer.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataServiceInitialization.cs @@ -6,7 +6,7 @@ namespace Snap.Hutao.Service.Metadata; /// /// 指示该类为元数据初始化器 /// -public interface IMetadataInitializer +public interface IMetadataServiceInitialization { /// /// 异步初始化元数据 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs index 91a57941..f9c4a6f5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs @@ -19,7 +19,7 @@ namespace Snap.Hutao.Service.Metadata; /// [Injection(InjectAs.Singleton, typeof(IMetadataService))] [HttpClient(HttpClientConfigration.Default)] -internal partial class MetadataService : IMetadataService, IMetadataInitializer +internal partial class MetadataService : IMetadataService, IMetadataServiceInitialization { private const string MetaFileName = "Meta.json"; diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml index 0167cf40..2f36441b 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/CultivationPage.xaml @@ -42,6 +42,7 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + 0 + + + + + Grid.Column="0" + Width="32" + Height="32" + Icon="{Binding Inner.Icon, Converter={StaticResource ItemIconConverter}}" + Quality="{Binding Inner.RankLevel}"/> - + - + - + + + + - - - - - + + + + + + Visible + + + + - - - - - - Visible - - - - + + + + + Collapsed + + + + + - - - - - Collapsed - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - logger; + private readonly ConcurrentCancellationTokenSource statisticsCancellationTokenSource = new(); + private ObservableCollection? projects; private CultivateProject? selectedProject; private List? inventoryItems; private ObservableCollection? cultivateEntries; - private List? statisticsItems; + private ObservableCollection? statisticsItems; /// /// 构造一个新的养成视图模型 @@ -53,8 +55,8 @@ internal class CultivationViewModel : Abstraction.ViewModel RemoveProjectCommand = new AsyncRelayCommand(RemoveProjectAsync); RemoveEntryCommand = new AsyncRelayCommand(RemoveEntryAsync); SaveInventoryItemCommand = new RelayCommand(SaveInventoryItem); - UpdateStatisticsItemsCommand = new AsyncRelayCommand(UpdateStatisticsItemsAsync); NavigateToPageCommand = new RelayCommand(NavigateToPage); + FinishStateCommand = new RelayCommand(FlipFinishedState); } /// @@ -74,7 +76,7 @@ internal class CultivationViewModel : Abstraction.ViewModel cultivationService.Current = value; if (value != null) { - UpdateCultivateEntriesAndInventoryItemsAsync(value).SafeForget(logger); + UpdateEntryCollectionAsync(value).SafeForget(logger); } } } @@ -93,7 +95,7 @@ internal class CultivationViewModel : Abstraction.ViewModel /// /// 统计列表 /// - public List? StatisticsItems { get => statisticsItems; set => SetProperty(ref statisticsItems, value); } + public ObservableCollection? StatisticsItems { get => statisticsItems; set => SetProperty(ref statisticsItems, value); } /// /// 打开界面命令 @@ -120,16 +122,16 @@ internal class CultivationViewModel : Abstraction.ViewModel /// public ICommand SaveInventoryItemCommand { get; } - /// - /// 展示统计物品命令 - /// - public ICommand UpdateStatisticsItemsCommand { get; } - /// /// 导航到指定的页面命令 /// public ICommand NavigateToPageCommand { get; set; } + /// + /// 调整完成状态命令 + /// + public ICommand FinishStateCommand { get; } + private async Task OpenUIAsync() { bool metaInitialized = await metadataService.InitializeAsync().ConfigureAwait(true); @@ -137,7 +139,6 @@ internal class CultivationViewModel : Abstraction.ViewModel { Projects = cultivationService.GetProjectCollection(); SelectedProject = cultivationService.Current; - await UpdateCultivateEntriesAndInventoryItemsAsync(SelectedProject).ConfigureAwait(true); } IsInitialized = metaInitialized; @@ -184,7 +185,7 @@ internal class CultivationViewModel : Abstraction.ViewModel } } - private async Task UpdateCultivateEntriesAndInventoryItemsAsync(CultivateProject? project) + private async Task UpdateEntryCollectionAsync(CultivateProject? project) { if (project != null) { @@ -193,24 +194,25 @@ internal class CultivationViewModel : Abstraction.ViewModel Dictionary idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false); ObservableCollection entries = await cultivationService - .GetCultivateEntriesAsync(project, materials, idAvatarMap, idWeaponMap) + .GetCultivateEntriesAsync(project) .ConfigureAwait(false); await ThreadHelper.SwitchToMainThreadAsync(); CultivateEntries = entries; InventoryItems = cultivationService.GetInventoryItems(project, materials); + + await UpdateStatisticsItemsAsync().ConfigureAwait(false); } } - private Task RemoveEntryAsync(Model.Binding.Cultivation.CultivateEntry? entry) + private async Task RemoveEntryAsync(Model.Binding.Cultivation.CultivateEntry? entry) { if (entry != null) { CultivateEntries!.Remove(entry); - return cultivationService.RemoveCultivateEntryAsync(entry.EntryId); + await cultivationService.RemoveCultivateEntryAsync(entry.EntryId).ConfigureAwait(false); + await UpdateStatisticsItemsAsync().ConfigureAwait(false); } - - return Task.CompletedTask; } private void SaveInventoryItem(Model.Binding.Inventory.InventoryItem? inventoryItem) @@ -218,20 +220,39 @@ internal class CultivationViewModel : Abstraction.ViewModel if (inventoryItem != null) { cultivationService.SaveInventoryItem(inventoryItem); + UpdateStatisticsItemsAsync().SafeForget(); + } + } + + private void FlipFinishedState(Model.Binding.Cultivation.CultivateItem? item) + { + if (item != null) + { + item.IsFinished = !item.IsFinished; + cultivationService.SaveCultivateItem(item.Entity); + UpdateStatisticsItemsAsync().SafeForget(); } } private async Task UpdateStatisticsItemsAsync() { - if (await metadataService.InitializeAsync().ConfigureAwait(true)) + logger.LogInformation("UpdateStatisticsItemsAsync"); + if (SelectedProject != null) { - if (SelectedProject != null) + await ThreadHelper.SwitchToBackgroundAsync(); + CancellationToken token = statisticsCancellationTokenSource.Register(); + ObservableCollection statistics; + try { - List materials = await metadataService.GetMaterialsAsync().ConfigureAwait(false); - List temp = await cultivationService.GetStatisticsCultivateItemsAsync(SelectedProject, materials).ConfigureAwait(false); - await ThreadHelper.SwitchToMainThreadAsync(); - StatisticsItems = temp; + statistics = await cultivationService.GetStatisticsCultivateItemCollectionAsync(SelectedProject, token).ConfigureAwait(false); } + catch (OperationCanceledException) + { + return; + } + + await ThreadHelper.SwitchToMainThreadAsync(); + StatisticsItems = statistics; } } diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs index 07ab21bf..6389dbd4 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs @@ -77,7 +77,7 @@ internal class GachaLogViewModel : Abstraction.ViewModel public GachaArchive? SelectedArchive { get => selectedArchive; - set => SetSelectedArchiveAndUpdateStatistics(value, false); + set => SetSelectedArchiveAndUpdateStatistics(value); } /// @@ -158,7 +158,6 @@ internal class GachaLogViewModel : Abstraction.ViewModel await ThreadHelper.SwitchToMainThreadAsync(); Archives = archives; SelectedArchive = Archives.SingleOrDefault(a => a.IsSelected == true); - IsInitialized = true; } } catch (OperationCanceledException) @@ -323,9 +322,14 @@ internal class GachaLogViewModel : Abstraction.ViewModel OnPropertyChanged(nameof(SelectedArchive)); } - if (changed || forceUpdate) + if (forceUpdate || changed) { - if (archive != null) + if (archive == null) + { + // no gachalog + IsInitialized = true; + } + else { UpdateStatisticsAsync(archive).SafeForget(); } @@ -337,6 +341,7 @@ internal class GachaLogViewModel : Abstraction.ViewModel GachaStatistics temp = await gachaLogService.GetStatisticsAsync(archive).ConfigureAwait(false); await ThreadHelper.SwitchToMainThreadAsync(); Statistics = temp; + IsInitialized = true; } private async Task TryImportUIGFInternalAsync(UIGF uigf) diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs index 7d6ed485..9dec040d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs @@ -230,9 +230,18 @@ public class MiHoYoJSInterface /// /// 参数 /// 响应 - public virtual IJsResult? ClosePage(JsParam param) + public virtual async Task ClosePageAsync(JsParam param) { - ClosePageRequested?.Invoke(); + await ThreadHelper.SwitchToMainThreadAsync(); + if (webView.CanGoBack) + { + webView.GoBack(); + } + else + { + ClosePageRequested?.Invoke(); + } + return null; } @@ -357,7 +366,7 @@ public class MiHoYoJSInterface { return param.Method switch { - "closePage" => ClosePage(param), + "closePage" => await ClosePageAsync(param).ConfigureAwait(false), "configure_share" => ConfigureShare(param), "eventTrack" => null, "getActionTicket" => await GetActionTicketAsync(param).ConfigureAwait(false),