diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml b/src/Snap.Hutao/Snap.Hutao/App.xaml index 245ac7ad..100f6c55 100644 --- a/src/Snap.Hutao/Snap.Hutao/App.xaml +++ b/src/Snap.Hutao/Snap.Hutao/App.xaml @@ -81,6 +81,12 @@ https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon250.png https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon272.png https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon293.png + + https://smms.app/image/x9psnPrcbYoCl6U + https://smms.app/image/n4gwxlFGPTX2j8p + https://smms.app/image/kbh1a2YVXpxWuez + https://smms.app/image/zJ4UYqKiD6uQlLc + https://smms.app/image/DQyTF3rv4aA8MZV diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs index 26b97745..0b09464f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs @@ -105,17 +105,19 @@ internal static partial class EnumerableExtension return results; } - /// - /// 按元素的键排序 - /// - /// 元素类型 - /// 键类型 - /// 列表 - /// 键选择器 [MethodImpl(MethodImplOptions.AggressiveOptimization)] - public static void SortBy(this List list, Func keySelector) + public static List SortBy(this List list, Func keySelector) where TKey : IComparable { list.Sort((left, right) => keySelector(left).CompareTo(keySelector(right))); + return list; + } + + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + public static List SortByDescending(this List list, Func keySelector) + where TKey : IComparable + { + list.Sort((left, right) => keySelector(right).CompareTo(keySelector(left))); + return list; } } diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_ItemIcon_220021.png b/src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_ItemIcon_220021.png deleted file mode 100644 index 3d006d7b..00000000 Binary files a/src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_ItemIcon_220021.png and /dev/null differ diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_MarkQuest_Events_Proce.png b/src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_MarkQuest_Events_Proce.png deleted file mode 100644 index 0899c458..00000000 Binary files a/src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_MarkQuest_Events_Proce.png and /dev/null differ diff --git a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs index d1890b35..36653791 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs @@ -141,7 +141,7 @@ internal sealed class DailyNoteNotificationOperation { notifyInfos.Add(new( SH.ServiceDailyNoteNotifierResin, - Web.Hoyolab.OssImages.UIItemIcon210, + Web.Hoyolab.Images.UIItemIcon210, $"{entry.DailyNote.CurrentResin}", string.Format(SH.ServiceDailyNoteNotifierResinCurrent, entry.DailyNote.CurrentResin))); entry.ResinNotifySuppressed = true; @@ -158,7 +158,7 @@ internal sealed class DailyNoteNotificationOperation { notifyInfos.Add(new( SH.ServiceDailyNoteNotifierHomeCoin, - Web.Hoyolab.OssImages.UIItemIcon204, + Web.Hoyolab.Images.UIItemIcon204, $"{entry.DailyNote.CurrentHomeCoin}", string.Format(SH.ServiceDailyNoteNotifierHomeCoinCurrent, entry.DailyNote.CurrentHomeCoin))); entry.HomeCoinNotifySuppressed = true; @@ -175,7 +175,7 @@ internal sealed class DailyNoteNotificationOperation { notifyInfos.Add(new( SH.ServiceDailyNoteNotifierDailyTask, - Web.Hoyolab.OssImages.UIMarkQuestEventsProce, + Web.Hoyolab.Images.UIMarkQuestEventsProce, SH.ServiceDailyNoteNotifierDailyTaskHint, entry.DailyNote.ExtraTaskRewardDescription)); entry.DailyTaskNotifySuppressed = true; @@ -192,7 +192,7 @@ internal sealed class DailyNoteNotificationOperation { notifyInfos.Add(new( SH.ServiceDailyNoteNotifierTransformer, - Web.Hoyolab.OssImages.UIItemIcon220021, + Web.Hoyolab.Images.UIItemIcon220021, SH.ServiceDailyNoteNotifierTransformerAdaptiveHint, SH.ServiceDailyNoteNotifierTransformerHint)); entry.TransformerNotifySuppressed = true; @@ -209,7 +209,7 @@ internal sealed class DailyNoteNotificationOperation { notifyInfos.Add(new( SH.ServiceDailyNoteNotifierExpedition, - Web.Hoyolab.OssImages.UIIconInteeExplore1, + Web.Hoyolab.Images.UIIconInteeExplore1, SH.ServiceDailyNoteNotifierExpeditionAdaptiveHint, SH.ServiceDailyNoteNotifierExpeditionHint)); entry.ExpeditionNotifySuppressed = true; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/ChannelOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/ChannelOptions.cs index 2f8a06ac..e8255f7f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/ChannelOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/ChannelOptions.cs @@ -11,6 +11,9 @@ namespace Snap.Hutao.Service.Game; [HighQuality] internal readonly struct ChannelOptions { + public const string ChannelName = "channel"; + public const string SubChannelName = "sub_channel"; + /// /// 通道 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameDbService.cs new file mode 100644 index 00000000..7c4f10ed --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameDbService.cs @@ -0,0 +1,62 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.EntityFrameworkCore; +using Snap.Hutao.Core.Database; +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Model.Entity.Database; +using System.Collections.ObjectModel; + +namespace Snap.Hutao.Service.Game; + +[ConstructorGenerated] +[Injection(InjectAs.Singleton, typeof(IGameDbService))] +internal sealed partial class GameDbService : IGameDbService +{ + private readonly IServiceProvider serviceProvider; + + public ObservableCollection GetGameAccountCollection() + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + return appDbContext.GameAccounts.AsNoTracking().ToObservableCollection(); + } + } + + public async ValueTask AddGameAccountAsync(GameAccount gameAccount) + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + await appDbContext.GameAccounts.AddAndSaveAsync(gameAccount).ConfigureAwait(false); + } + } + + public async ValueTask UpdateGameAccountAsync(GameAccount gameAccount) + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + await appDbContext.GameAccounts.UpdateAndSaveAsync(gameAccount).ConfigureAwait(false); + } + } + + public void UpdateGameAccount(GameAccount gameAccount) + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + appDbContext.GameAccounts.UpdateAndSave(gameAccount); + } + } + + public async ValueTask DeleteGameAccountByIdAsync(Guid id) + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + await appDbContext.GameAccounts.ExecuteDeleteWhereAsync(a => a.InnerId == id).ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs index 78e45255..fcbd5f8f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs @@ -1,13 +1,10 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Microsoft.EntityFrameworkCore; using Snap.Hutao.Core; -using Snap.Hutao.Core.Database; using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Core.IO.Ini; using Snap.Hutao.Model.Entity; -using Snap.Hutao.Model.Entity.Database; using Snap.Hutao.Service.Game.Locator; using Snap.Hutao.Service.Game.Package; using Snap.Hutao.View.Dialog; @@ -32,7 +29,7 @@ internal sealed partial class GameService : IGameService private readonly IServiceProvider serviceProvider; private readonly IGameDbService gameDbService; private readonly LaunchOptions launchOptions; - private readonly RuntimeOptions hutaoOptions; + private readonly RuntimeOptions runtimeOptions; private readonly ITaskContext taskContext; private readonly AppOptions appOptions; @@ -48,48 +45,45 @@ internal sealed partial class GameService : IGameService /// public async ValueTask> GetGamePathAsync() { - using (IServiceScope scope = serviceProvider.CreateScope()) + // Cannot find in setting + if (string.IsNullOrEmpty(appOptions.GamePath)) { - // Cannot find in setting - if (string.IsNullOrEmpty(appOptions.GamePath)) - { - IGameLocatorFactory locatorFactory = scope.ServiceProvider.GetRequiredService(); + IGameLocatorFactory locatorFactory = serviceProvider.GetRequiredService(); - // Try locate by unity log - ValueResult result = await locatorFactory - .Create(GameLocationSource.UnityLog) + // Try locate by unity log + ValueResult result = await locatorFactory + .Create(GameLocationSource.UnityLog) + .LocateGamePathAsync() + .ConfigureAwait(false); + + if (!result.IsOk) + { + // Try locate by registry + result = await locatorFactory + .Create(GameLocationSource.Registry) .LocateGamePathAsync() .ConfigureAwait(false); - - if (!result.IsOk) - { - // Try locate by registry - result = await locatorFactory - .Create(GameLocationSource.Registry) - .LocateGamePathAsync() - .ConfigureAwait(false); - } - - if (result.IsOk) - { - // Save result. - appOptions.GamePath = result.Value; - } - else - { - return new(false, SH.ServiceGamePathLocateFailed); - } } - if (!string.IsNullOrEmpty(appOptions.GamePath)) + if (result.IsOk) { - return new(true, appOptions.GamePath); + // Save result. + appOptions.GamePath = result.Value; } else { - return new(false, null!); + return new(false, SH.ServiceGamePathLocateFailed); } } + + if (!string.IsNullOrEmpty(appOptions.GamePath)) + { + return new(true, appOptions.GamePath); + } + else + { + return new(false, null!); + } } /// @@ -107,8 +101,8 @@ internal sealed partial class GameService : IGameService using (FileStream stream = File.OpenRead(configPath)) { List parameters = IniSerializer.Deserialize(stream).OfType().ToList(); - string? channel = parameters.FirstOrDefault(p => p.Key == "channel")?.Value; - string? subChannel = parameters.FirstOrDefault(p => p.Key == "sub_channel")?.Value; + string? channel = parameters.FirstOrDefault(p => p.Key == ChannelOptions.ChannelName)?.Value; + string? subChannel = parameters.FirstOrDefault(p => p.Key == ChannelOptions.SubChannelName)?.Value; return new(channel, subChannel, isOversea); } @@ -178,14 +172,10 @@ internal sealed partial class GameService : IGameService string gameFileName = Path.GetFileName(gamePath); progress.Report(new(SH.ServiceGameEnsureGameResourceQueryResourceInformation)); - Response response; - using (IServiceScope scope = serviceProvider.CreateScope()) - { - response = await scope.ServiceProvider - .GetRequiredService() - .GetResourceAsync(launchScheme) - .ConfigureAwait(false); - } + Response response = await serviceProvider + .GetRequiredService() + .GetResourceAsync(launchScheme) + .ConfigureAwait(false); if (response.IsOk()) { @@ -257,7 +247,7 @@ internal sealed partial class GameService : IGameService return; } - Process game = ProcessInterop.PrepareGameProcess(launchOptions, gamePath); + Process game = ProcessInterop.InitializeGameProcess(launchOptions, gamePath); try { @@ -265,7 +255,7 @@ internal sealed partial class GameService : IGameService game.Start(); - bool isAdvancedOptionsAllowed = hutaoOptions.IsElevated && appOptions.IsAdvancedLaunchOptionsEnabled; + bool isAdvancedOptionsAllowed = runtimeOptions.IsElevated && appOptions.IsAdvancedLaunchOptionsEnabled; if (isAdvancedOptionsAllowed && launchOptions.MultipleInstances && !isFirstInstance) { ProcessInterop.DisableProtection(game, gamePath); @@ -317,14 +307,7 @@ internal sealed partial class GameService : IGameService // sync database await taskContext.SwitchToBackgroundAsync(); - using (IServiceScope scope = serviceProvider.CreateScope()) - { - await scope.ServiceProvider - .GetRequiredService() - .GameAccounts - .AddAndSaveAsync(account) - .ConfigureAwait(false); - } + await gameDbService.AddGameAccountAsync(account).ConfigureAwait(false); // sync cache await taskContext.SwitchToMainThreadAsync(); @@ -365,11 +348,8 @@ internal sealed partial class GameService : IGameService /// public void AttachGameAccountToUid(GameAccount gameAccount, string uid) { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - gameAccount.UpdateAttachUid(uid); - scope.ServiceProvider.GetRequiredService().GameAccounts.UpdateAndSave(gameAccount); - } + gameAccount.UpdateAttachUid(uid); + gameDbService.UpdateGameAccount(gameAccount); } /// @@ -385,11 +365,7 @@ internal sealed partial class GameService : IGameService // sync database await taskContext.SwitchToBackgroundAsync(); - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - await appDbContext.GameAccounts.UpdateAndSaveAsync(gameAccount).ConfigureAwait(false); - } + await gameDbService.UpdateGameAccountAsync(gameAccount).ConfigureAwait(false); } } @@ -400,10 +376,7 @@ internal sealed partial class GameService : IGameService gameAccounts!.Remove(gameAccount); await taskContext.SwitchToBackgroundAsync(); - using (IServiceScope scope = serviceProvider.CreateScope()) - { - await scope.ServiceProvider.GetRequiredService().GameAccounts.RemoveAndSaveAsync(gameAccount).ConfigureAwait(false); - } + await gameDbService.DeleteGameAccountByIdAsync(gameAccount.InnerId).ConfigureAwait(false); } private static bool LaunchSchemeMatchesExecutable(LaunchScheme launchScheme, string gameFileName) @@ -415,25 +388,4 @@ internal sealed partial class GameService : IGameService _ => false, }; } -} - -[ConstructorGenerated] -[Injection(InjectAs.Singleton, typeof(IGameDbService))] -internal sealed partial class GameDbService : IGameDbService -{ - private readonly IServiceProvider serviceProvider; - - public ObservableCollection GetGameAccountCollection() - { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - return appDbContext.GameAccounts.AsNoTracking().ToObservableCollection(); - } - } -} - -internal interface IGameDbService -{ - ObservableCollection GetGameAccountCollection(); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/IGameDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/IGameDbService.cs new file mode 100644 index 00000000..42f50de8 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/IGameDbService.cs @@ -0,0 +1,20 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Entity; +using System.Collections.ObjectModel; + +namespace Snap.Hutao.Service.Game; + +internal interface IGameDbService +{ + ValueTask AddGameAccountAsync(GameAccount gameAccount); + + ValueTask DeleteGameAccountByIdAsync(Guid id); + + ObservableCollection GetGameAccountCollection(); + + void UpdateGameAccount(GameAccount gameAccount); + + ValueTask UpdateGameAccountAsync(GameAccount gameAccount); +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs index b96c1a48..baf237b3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs @@ -43,16 +43,7 @@ internal sealed class LaunchOptions : DbStoreOptions primaryScreenWidth = primaryRect.Width; primaryScreenHeight = primaryRect.Height; - // This list can't use foreach - // https://github.com/microsoft/microsoft-ui-xaml/issues/6454 - IReadOnlyList displayAreas = DisplayArea.FindAll(); - for (int i = 0; i < displayAreas.Count; i++) - { - DisplayArea displayArea = displayAreas[i]; - int index = i + 1; - Monitors.Add(new($"{displayArea.DisplayId.Value:X8}:{index}", index)); - } - + InitializeMonitors(Monitors); InitializeScreenFps(out primaryScreenFps); } @@ -168,6 +159,19 @@ internal sealed class LaunchOptions : DbStoreOptions set => SetOption(ref multipleInstances, SettingEntry.MultipleInstances, value); } + private static void InitializeMonitors(List> monitors) + { + // This list can't use foreach + // https://github.com/microsoft/microsoft-ui-xaml/issues/6454 + IReadOnlyList displayAreas = DisplayArea.FindAll(); + for (int i = 0; i < displayAreas.Count; i++) + { + DisplayArea displayArea = displayAreas[i]; + int index = i + 1; + monitors.Add(new($"{displayArea.DisplayId.Value:X8}:{index}", index)); + } + } + private static void InitializeScreenFps(out int fps) { HDC hDC = GetDC(HWND.Null); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/ProcessInterop.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/ProcessInterop.cs index 07ef89e8..123fb6d6 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/ProcessInterop.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/ProcessInterop.cs @@ -24,7 +24,7 @@ internal static class ProcessInterop /// 启动选项 /// 游戏路径 /// 初始化后的游戏进程 - public static Process PrepareGameProcess(LaunchOptions options, string gamePath) + public static Process InitializeGameProcess(LaunchOptions options, string gamePath) { // https://docs.unity.cn/cn/current/Manual/PlayerCommandLineArguments.html string commandLine = new CommandLineBuilder() diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs index 1b49d828..13152b8b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs @@ -95,21 +95,7 @@ internal sealed partial class HutaoCache : IHutaoCache Dictionary idAvatarMap = await GetIdAvatarMapExtendedAsync().ConfigureAwait(false); Dictionary idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false); Dictionary idReliquarySetMap = await metadataService.GetEquipAffixIdToReliquarySetMapAsync().ConfigureAwait(false); - - List avatarCollocationsRaw; - using (IServiceScope scope = serviceProvider.CreateScope()) - { - IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService(); - avatarCollocationsRaw = await hutaoService.GetAvatarCollocationsAsync().ConfigureAwait(false); - } - - AvatarCollocations = avatarCollocationsRaw.Select(co => new AvatarCollocationView() - { - AvatarId = co.AvatarId, - Avatars = co.Avatars.Select(a => new AvatarView(idAvatarMap[a.Item], a.Rate)).ToList(), - Weapons = co.Weapons.Select(w => new WeaponView(idWeaponMap[w.Item], w.Rate)).ToList(), - ReliquarySets = co.Reliquaries.Select(r => new ReliquarySetView(r, idReliquarySetMap)).ToList(), - }).ToList(); + await AvatarCollocationsAsync(idAvatarMap, idWeaponMap, idReliquarySetMap).ConfigureAwait(false); wikiAvatarViewModelTaskSource.TrySetResult(true); return true; @@ -131,19 +117,7 @@ internal sealed partial class HutaoCache : IHutaoCache if (await metadataService.InitializeAsync().ConfigureAwait(false)) { Dictionary idAvatarMap = await GetIdAvatarMapExtendedAsync().ConfigureAwait(false); - - List weaponCollocationsRaw; - using (IServiceScope scope = serviceProvider.CreateScope()) - { - IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService(); - weaponCollocationsRaw = await hutaoService.GetWeaponCollocationsAsync().ConfigureAwait(false); - } - - WeaponCollocations = weaponCollocationsRaw.Select(co => new WeaponCollocationView() - { - WeaponId = co.WeaponId, - Avatars = co.Avatars.Select(a => new AvatarView(idAvatarMap[a.Item], a.Rate)).ToList(), - }).ToList(); + await WeaponCollocationsAsync(idAvatarMap).ConfigureAwait(false); wikiWeaponViewModelTaskSource.TrySetResult(true); return true; @@ -164,6 +138,41 @@ internal sealed partial class HutaoCache : IHutaoCache return idAvatarExtendedMap; } + private async ValueTask AvatarCollocationsAsync(Dictionary idAvatarMap, Dictionary idWeaponMap, Dictionary idReliquarySetMap) + { + List avatarCollocationsRaw; + using (IServiceScope scope = serviceProvider.CreateScope()) + { + IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService(); + avatarCollocationsRaw = await hutaoService.GetAvatarCollocationsAsync().ConfigureAwait(false); + } + + AvatarCollocations = avatarCollocationsRaw.SelectList(co => new AvatarCollocationView() + { + AvatarId = co.AvatarId, + Avatars = co.Avatars.SelectList(a => new AvatarView(idAvatarMap[a.Item], a.Rate)), + Weapons = co.Weapons.SelectList(w => new WeaponView(idWeaponMap[w.Item], w.Rate)), + ReliquarySets = co.Reliquaries.SelectList(r => new ReliquarySetView(r, idReliquarySetMap)), + }); + } + + private async ValueTask WeaponCollocationsAsync(Dictionary idAvatarMap) + { + List weaponCollocationsRaw; + using (IServiceScope scope = serviceProvider.CreateScope()) + { + IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService(); + weaponCollocationsRaw = await hutaoService.GetWeaponCollocationsAsync().ConfigureAwait(false); + } + + WeaponCollocations = weaponCollocationsRaw.SelectList(co => new WeaponCollocationView() + { + WeaponId = co.WeaponId, + Avatars = co.Avatars.SelectList(a => new AvatarView(idAvatarMap[a.Item], a.Rate)), + }); + } + + [SuppressMessage("", "SH003")] private async Task AvatarAppearanceRankAsync(Dictionary idAvatarMap) { List avatarAppearanceRanksRaw; @@ -173,13 +182,14 @@ internal sealed partial class HutaoCache : IHutaoCache avatarAppearanceRanksRaw = await hutaoService.GetAvatarAppearanceRanksAsync().ConfigureAwait(false); } - AvatarAppearanceRanks = avatarAppearanceRanksRaw.OrderByDescending(r => r.Floor).Select(rank => new AvatarRankView + AvatarAppearanceRanks = avatarAppearanceRanksRaw.SortByDescending(r => r.Floor).SelectList(rank => new AvatarRankView { Floor = string.Format(SH.ModelBindingHutaoComplexRankFloor, rank.Floor), - Avatars = rank.Ranks.OrderByDescending(r => r.Rate).Select(rank => new AvatarView(idAvatarMap[rank.Item], rank.Rate)).ToList(), - }).ToList(); + Avatars = rank.Ranks.SortByDescending(r => r.Rate).SelectList(rank => new AvatarView(idAvatarMap[rank.Item], rank.Rate)), + }); } + [SuppressMessage("", "SH003")] private async Task AvatarUsageRanksAsync(Dictionary idAvatarMap) { List avatarUsageRanksRaw; @@ -189,13 +199,14 @@ internal sealed partial class HutaoCache : IHutaoCache avatarUsageRanksRaw = await hutaoService.GetAvatarUsageRanksAsync().ConfigureAwait(false); } - AvatarUsageRanks = avatarUsageRanksRaw.OrderByDescending(r => r.Floor).Select(rank => new AvatarRankView + AvatarUsageRanks = avatarUsageRanksRaw.SortByDescending(r => r.Floor).SelectList(rank => new AvatarRankView { Floor = string.Format(SH.ModelBindingHutaoComplexRankFloor, rank.Floor), - Avatars = rank.Ranks.OrderByDescending(r => r.Rate).Select(rank => new AvatarView(idAvatarMap[rank.Item], rank.Rate)).ToList(), - }).ToList(); + Avatars = rank.Ranks.SortByDescending(r => r.Rate).SelectList(rank => new AvatarView(idAvatarMap[rank.Item], rank.Rate)), + }); } + [SuppressMessage("", "SH003")] private async Task AvatarConstellationInfosAsync(Dictionary idAvatarMap) { List avatarConstellationInfosRaw; @@ -205,12 +216,13 @@ internal sealed partial class HutaoCache : IHutaoCache avatarConstellationInfosRaw = await hutaoService.GetAvatarConstellationInfosAsync().ConfigureAwait(false); } - AvatarConstellationInfos = avatarConstellationInfosRaw.OrderBy(i => i.HoldingRate).Select(info => + AvatarConstellationInfos = avatarConstellationInfosRaw.SortBy(i => i.HoldingRate).SelectList(info => { return new AvatarConstellationInfoView(idAvatarMap[info.AvatarId], info.HoldingRate, info.Constellations.SelectList(x => x.Rate)); - }).ToList(); + }); } + [SuppressMessage("", "SH003")] private async Task TeamAppearancesAsync(Dictionary idAvatarMap) { List teamAppearancesRaw; @@ -220,9 +232,10 @@ internal sealed partial class HutaoCache : IHutaoCache teamAppearancesRaw = await hutaoService.GetTeamAppearancesAsync().ConfigureAwait(false); } - TeamAppearances = teamAppearancesRaw.OrderByDescending(t => t.Floor).Select(team => new TeamAppearanceView(team, idAvatarMap)).ToList(); + TeamAppearances = teamAppearancesRaw.SortByDescending(t => t.Floor).SelectList(team => new TeamAppearanceView(team, idAvatarMap)); } + [SuppressMessage("", "SH003")] private async Task OverviewAsync() { using (IServiceScope scope = serviceProvider.CreateScope()) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs index fa60cea2..42ca5bcf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs @@ -108,8 +108,6 @@ internal sealed partial class HutaoService : IHutaoService await appDbContext.ObjectCache.AddAndSaveAsync(new() { Key = key, - - // We hold the cache for 4 hours ExpireTime = DateTimeOffset.Now.Add(CacheExpireTime), Value = JsonSerializer.Serialize(data, options), }).ConfigureAwait(false); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptions.cs index 53f13b5f..2dc82229 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptions.cs @@ -86,7 +86,7 @@ internal sealed class HutaoUserOptions : ObservableObject, IOptions DateTimeOffset.Now; GachaLogExpireAt = string.Format(Regex.Unescape(SH.ServiceHutaoUserGachaLogExpiredAt), userInfo.GachaLogExpireAt); + IsCloudServiceAllowed = IsLicensedDeveloper || userInfo.GachaLogExpireAt > DateTimeOffset.Now; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index c24a6729..c2d0b685 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -97,11 +97,8 @@ - - - @@ -227,11 +224,8 @@ - - - diff --git a/src/Snap.Hutao/Snap.Hutao/View/Card/DailyNoteCard.xaml b/src/Snap.Hutao/Snap.Hutao/View/Card/DailyNoteCard.xaml index 87d3ade6..b1603e43 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Card/DailyNoteCard.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Card/DailyNoteCard.xaml @@ -6,6 +6,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mxi="using:Microsoft.Xaml.Interactivity" xmlns:shcb="using:Snap.Hutao.Control.Behavior" + xmlns:shci="using:Snap.Hutao.Control.Image" xmlns:shvc="using:Snap.Hutao.View.Control" xmlns:shvd="using:Snap.Hutao.ViewModel.DailyNote" Padding="0" @@ -75,7 +76,7 @@ - + - +