mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
fix #825
This commit is contained in:
@@ -81,6 +81,12 @@
|
||||
<x:String x:Key="UI_EmotionIcon250">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon250.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon272">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon272.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon293">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon293.png</x:String>
|
||||
<!-- 图床 Url -->
|
||||
<x:String x:Key="UI_ItemIcon_204">https://smms.app/image/x9psnPrcbYoCl6U</x:String>
|
||||
<x:String x:Key="UI_ItemIcon_210">https://smms.app/image/n4gwxlFGPTX2j8p</x:String>
|
||||
<x:String x:Key="UI_ItemIcon_220021">https://smms.app/image/kbh1a2YVXpxWuez</x:String>
|
||||
<x:String x:Key="UI_Icon_Intee_Explore_1">https://smms.app/image/zJ4UYqKiD6uQlLc</x:String>
|
||||
<x:String x:Key="UI_MarkQuest_Events_Proce">https://smms.app/image/DQyTF3rv4aA8MZV</x:String>
|
||||
<!-- Converters -->
|
||||
<cwuc:BoolNegationConverter x:Key="BoolNegationConverter"/>
|
||||
<cwuc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
|
||||
|
||||
@@ -105,17 +105,19 @@ internal static partial class EnumerableExtension
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按元素的键排序
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">元素类型</typeparam>
|
||||
/// <typeparam name="TKey">键类型</typeparam>
|
||||
/// <param name="list">列表</param>
|
||||
/// <param name="keySelector">键选择器</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public static void SortBy<TSource, TKey>(this List<TSource> list, Func<TSource, TKey> keySelector)
|
||||
public static List<TSource> SortBy<TSource, TKey>(this List<TSource> list, Func<TSource, TKey> keySelector)
|
||||
where TKey : IComparable
|
||||
{
|
||||
list.Sort((left, right) => keySelector(left).CompareTo(keySelector(right)));
|
||||
return list;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public static List<TSource> SortByDescending<TSource, TKey>(this List<TSource> list, Func<TSource, TKey> keySelector)
|
||||
where TKey : IComparable
|
||||
{
|
||||
list.Sort((left, right) => keySelector(right).CompareTo(keySelector(left)));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 59 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
/// <summary>
|
||||
/// 通道
|
||||
/// </summary>
|
||||
|
||||
62
src/Snap.Hutao/Snap.Hutao/Service/Game/GameDbService.cs
Normal file
62
src/Snap.Hutao/Snap.Hutao/Service/Game/GameDbService.cs
Normal file
@@ -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<GameAccount> GetGameAccountCollection()
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
return appDbContext.GameAccounts.AsNoTracking().ToObservableCollection();
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask AddGameAccountAsync(GameAccount gameAccount)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
await appDbContext.GameAccounts.AddAndSaveAsync(gameAccount).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask UpdateGameAccountAsync(GameAccount gameAccount)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
await appDbContext.GameAccounts.UpdateAndSaveAsync(gameAccount).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateGameAccount(GameAccount gameAccount)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
appDbContext.GameAccounts.UpdateAndSave(gameAccount);
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask DeleteGameAccountByIdAsync(Guid id)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
await appDbContext.GameAccounts.ExecuteDeleteWhereAsync(a => a.InnerId == id).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -47,13 +44,11 @@ internal sealed partial class GameService : IGameService
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<ValueResult<bool, string>> GetGamePathAsync()
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
// Cannot find in setting
|
||||
if (string.IsNullOrEmpty(appOptions.GamePath))
|
||||
{
|
||||
IGameLocatorFactory locatorFactory = scope.ServiceProvider.GetRequiredService<IGameLocatorFactory>();
|
||||
IGameLocatorFactory locatorFactory = serviceProvider.GetRequiredService<IGameLocatorFactory>();
|
||||
|
||||
// Try locate by unity log
|
||||
ValueResult<bool, string> result = await locatorFactory
|
||||
@@ -90,7 +85,6 @@ internal sealed partial class GameService : IGameService
|
||||
return new(false, null!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ChannelOptions GetChannelOptions()
|
||||
@@ -107,8 +101,8 @@ internal sealed partial class GameService : IGameService
|
||||
using (FileStream stream = File.OpenRead(configPath))
|
||||
{
|
||||
List<IniParameter> parameters = IniSerializer.Deserialize(stream).OfType<IniParameter>().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<GameResource> response;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
response = await scope.ServiceProvider
|
||||
Response<GameResource> response = await serviceProvider
|
||||
.GetRequiredService<ResourceClient>()
|
||||
.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<AppDbContext>()
|
||||
.GameAccounts
|
||||
.AddAndSaveAsync(account)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
await gameDbService.AddGameAccountAsync(account).ConfigureAwait(false);
|
||||
|
||||
// sync cache
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
@@ -364,12 +347,9 @@ internal sealed partial class GameService : IGameService
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void AttachGameAccountToUid(GameAccount gameAccount, string uid)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
gameAccount.UpdateAttachUid(uid);
|
||||
scope.ServiceProvider.GetRequiredService<AppDbContext>().GameAccounts.UpdateAndSave(gameAccount);
|
||||
}
|
||||
gameDbService.UpdateGameAccount(gameAccount);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -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<AppDbContext>();
|
||||
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<AppDbContext>().GameAccounts.RemoveAndSaveAsync(gameAccount).ConfigureAwait(false);
|
||||
}
|
||||
await gameDbService.DeleteGameAccountByIdAsync(gameAccount.InnerId).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static bool LaunchSchemeMatchesExecutable(LaunchScheme launchScheme, string gameFileName)
|
||||
@@ -416,24 +389,3 @@ internal sealed partial class GameService : IGameService
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton, typeof(IGameDbService))]
|
||||
internal sealed partial class GameDbService : IGameDbService
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public ObservableCollection<GameAccount> GetGameAccountCollection()
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
return appDbContext.GameAccounts.AsNoTracking().ToObservableCollection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IGameDbService
|
||||
{
|
||||
ObservableCollection<GameAccount> GetGameAccountCollection();
|
||||
}
|
||||
20
src/Snap.Hutao/Snap.Hutao/Service/Game/IGameDbService.cs
Normal file
20
src/Snap.Hutao/Snap.Hutao/Service/Game/IGameDbService.cs
Normal file
@@ -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<GameAccount> GetGameAccountCollection();
|
||||
|
||||
void UpdateGameAccount(GameAccount gameAccount);
|
||||
|
||||
ValueTask UpdateGameAccountAsync(GameAccount gameAccount);
|
||||
}
|
||||
@@ -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<DisplayArea> 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<NameValue<int>> monitors)
|
||||
{
|
||||
// This list can't use foreach
|
||||
// https://github.com/microsoft/microsoft-ui-xaml/issues/6454
|
||||
IReadOnlyList<DisplayArea> 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);
|
||||
|
||||
@@ -24,7 +24,7 @@ internal static class ProcessInterop
|
||||
/// <param name="options">启动选项</param>
|
||||
/// <param name="gamePath">游戏路径</param>
|
||||
/// <returns>初始化后的游戏进程</returns>
|
||||
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()
|
||||
|
||||
@@ -95,21 +95,7 @@ internal sealed partial class HutaoCache : IHutaoCache
|
||||
Dictionary<AvatarId, Avatar> idAvatarMap = await GetIdAvatarMapExtendedAsync().ConfigureAwait(false);
|
||||
Dictionary<WeaponId, Weapon> idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false);
|
||||
Dictionary<EquipAffixId, Model.Metadata.Reliquary.ReliquarySet> idReliquarySetMap = await metadataService.GetEquipAffixIdToReliquarySetMapAsync().ConfigureAwait(false);
|
||||
|
||||
List<AvatarCollocation> avatarCollocationsRaw;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService<IHutaoService>();
|
||||
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<AvatarId, Avatar> idAvatarMap = await GetIdAvatarMapExtendedAsync().ConfigureAwait(false);
|
||||
|
||||
List<WeaponCollocation> weaponCollocationsRaw;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService<IHutaoService>();
|
||||
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<AvatarId, Avatar> idAvatarMap, Dictionary<WeaponId, Weapon> idWeaponMap, Dictionary<EquipAffixId, Model.Metadata.Reliquary.ReliquarySet> idReliquarySetMap)
|
||||
{
|
||||
List<AvatarCollocation> avatarCollocationsRaw;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService<IHutaoService>();
|
||||
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<AvatarId, Avatar> idAvatarMap)
|
||||
{
|
||||
List<WeaponCollocation> weaponCollocationsRaw;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
IHutaoService hutaoService = scope.ServiceProvider.GetRequiredService<IHutaoService>();
|
||||
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<AvatarId, Avatar> idAvatarMap)
|
||||
{
|
||||
List<AvatarAppearanceRank> 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<AvatarId, Avatar> idAvatarMap)
|
||||
{
|
||||
List<AvatarUsageRank> 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<AvatarId, Avatar> idAvatarMap)
|
||||
{
|
||||
List<AvatarConstellationInfo> 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<AvatarId, Avatar> idAvatarMap)
|
||||
{
|
||||
List<TeamAppearance> 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())
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -86,7 +86,7 @@ internal sealed class HutaoUserOptions : ObservableObject, IOptions<HutaoUserOpt
|
||||
public void UpdateUserInfo(UserInfo userInfo)
|
||||
{
|
||||
IsLicensedDeveloper = userInfo.IsLicensedDeveloper;
|
||||
IsCloudServiceAllowed = userInfo.GachaLogExpireAt > DateTimeOffset.Now;
|
||||
GachaLogExpireAt = string.Format(Regex.Unescape(SH.ServiceHutaoUserGachaLogExpiredAt), userInfo.GachaLogExpireAt);
|
||||
IsCloudServiceAllowed = IsLicensedDeveloper || userInfo.GachaLogExpireAt > DateTimeOffset.Now;
|
||||
}
|
||||
}
|
||||
@@ -97,11 +97,8 @@
|
||||
<None Remove="Resource\Icon\UI_Icon_None.png" />
|
||||
<None Remove="Resource\Icon\UI_Icon_Tower_Star.png" />
|
||||
<None Remove="Resource\Icon\UI_ItemIcon_201.png" />
|
||||
<None Remove="Resource\Icon\UI_ItemIcon_204.png" />
|
||||
<None Remove="Resource\Icon\UI_ItemIcon_210.png" />
|
||||
<None Remove="Resource\Icon\UI_ItemIcon_220021.png" />
|
||||
<None Remove="Resource\Icon\UI_MarkCustom_TagMonster.png" />
|
||||
<None Remove="Resource\Icon\UI_MarkQuest_Events_Proce.png" />
|
||||
<None Remove="Resource\Icon\UI_MarkTower.png" />
|
||||
<None Remove="Resource\Icon\UI_MarkTower_Tower.png" />
|
||||
<None Remove="Resource\Segoe Fluent Icons.ttf" />
|
||||
@@ -227,11 +224,8 @@
|
||||
<Content Include="Resource\Icon\UI_Icon_None.png" />
|
||||
<Content Include="Resource\Icon\UI_Icon_Tower_Star.png" />
|
||||
<Content Include="Resource\Icon\UI_ItemIcon_201.png" />
|
||||
<Content Include="Resource\Icon\UI_ItemIcon_204.png" />
|
||||
<Content Include="Resource\Icon\UI_ItemIcon_210.png" />
|
||||
<Content Include="Resource\Icon\UI_ItemIcon_220021.png" />
|
||||
<Content Include="Resource\Icon\UI_MarkCustom_TagMonster.png" />
|
||||
<Content Include="Resource\Icon\UI_MarkQuest_Events_Proce.png" />
|
||||
<Content Include="Resource\Icon\UI_MarkTower.png" />
|
||||
<Content Include="Resource\Icon\UI_MarkTower_Tower.png" />
|
||||
<Content Include="Resource\WelcomeView_Background.png" />
|
||||
|
||||
@@ -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 @@
|
||||
</Grid.RowDefinitions>
|
||||
<Border Style="{StaticResource BorderCardStyle}">
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_ItemIcon_204.png"/>
|
||||
<shci:CachedImage Width="32" Source="{StaticResource UI_ItemIcon_204}"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
|
||||
@@ -229,7 +229,7 @@
|
||||
Width="40"
|
||||
Height="40"
|
||||
VerticalAlignment="Center">
|
||||
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_ItemIcon_204.png"/>
|
||||
<shci:CachedImage Width="32" Source="{StaticResource UI_ItemIcon_204}"/>
|
||||
<ProgressRing
|
||||
Width="40"
|
||||
Height="40"
|
||||
|
||||
13
src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Images.cs
Normal file
13
src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Images.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab;
|
||||
|
||||
internal static class Images
|
||||
{
|
||||
public const string UIItemIcon204 = $"https://smms.app/image/x9psnPrcbYoCl6U";
|
||||
public const string UIItemIcon210 = $"https://smms.app/image/n4gwxlFGPTX2j8p";
|
||||
public const string UIItemIcon220021 = $"https://smms.app/image/kbh1a2YVXpxWuez";
|
||||
public const string UIIconInteeExplore1 = $"https://smms.app/image/zJ4UYqKiD6uQlLc";
|
||||
public const string UIMarkQuestEventsProce = $"https://smms.app/image/DQyTF3rv4aA8MZV";
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab;
|
||||
|
||||
internal static class OssImages
|
||||
{
|
||||
public const string UIMarkQuestEventsProce = "https://upload-bbs.miyoushe.com/upload/2023/07/26/184872832/4b9ba51caa13e5516d507d64c7234b0c_7636696921807680614.png";
|
||||
public const string UIItemIcon220021 = "https://upload-bbs.miyoushe.com/upload/2023/07/26/184872832/7cd02abbf20727ec4fd0dae777b65748_6274739470729303254.png";
|
||||
public const string UIItemIcon210 = "https://upload-bbs.miyoushe.com/upload/2023/07/26/184872832/75dbb40f46380efbdbc23bd3a251c83a_538508470291955954.png";
|
||||
public const string UIItemIcon204 = "https://upload-bbs.miyoushe.com/upload/2023/07/26/184872832/4a3cbdd778648b8aee5c4ebe9672c68b_5158859174407167834.png";
|
||||
public const string UIIconInteeExplore1 = "https://upload-bbs.miyoushe.com/upload/2023/07/26/184872832/830fcd53539be4aa7faa02391f5384c5_8996801294815343498.png";
|
||||
}
|
||||
Reference in New Issue
Block a user