avatar filter re-enable

This commit is contained in:
DismissedLight
2022-12-16 15:16:37 +08:00
parent bcf38fbefc
commit 958fecdb77
16 changed files with 229 additions and 245 deletions

View File

@@ -19,9 +19,24 @@ public static class EnumExtension
public static string GetDescription<TEnum>(this TEnum @enum)
where TEnum : struct, Enum
{
string enumName = Must.NotNull(Enum.GetName(@enum)!);
string enumName = Enum.GetName(@enum)!;
FieldInfo? field = @enum.GetType().GetField(enumName);
DescriptionAttribute? attr = field?.GetCustomAttribute<DescriptionAttribute>();
return attr?.Description ?? enumName;
}
/// <summary>
/// 获取枚举的描述
/// </summary>
/// <typeparam name="TEnum">枚举的类型</typeparam>
/// <param name="enum">枚举值</param>
/// <returns>描述</returns>
public static string? GetDescriptionOrNull<TEnum>(this TEnum @enum)
where TEnum : struct, Enum
{
string enumName = Enum.GetName(@enum)!;
FieldInfo? field = @enum.GetType().GetField(enumName);
DescriptionAttribute? attr = field?.GetCustomAttribute<DescriptionAttribute>();
return attr?.Description;
}
}

View File

@@ -1,18 +1,16 @@
<Window
x:Class="Snap.Hutao.LaunchGameWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shv="using:Snap.Hutao.ViewModel"
mc:Ignorable="d">
<Grid
Name="RootGrid"
d:DataContext="{d:DesignInstance shv:LaunchGameViewModel}">
<Grid Name="RootGrid" d:DataContext="{d:DesignInstance shv:LaunchGameViewModel}">
<mxi:Interaction.Behaviors>
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/>
</mxi:Interaction.Behaviors>
@@ -27,17 +25,17 @@
Grid.Row="0"
Height="32">
<TextBlock
Text="选择账号并启动"
TextWrapping="NoWrap"
Style="{StaticResource CaptionTextBlockStyle}"
VerticalAlignment="Center"
Margin="12,0,0,0"/>
Margin="12,0,0,0"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="选择账号并启动"
TextWrapping="NoWrap"/>
</Grid>
<ListView
Grid.Row="1"
ItemsSource="{Binding GameAccounts}"
SelectedItem="{Binding SelectedGameAccount,Mode=TwoWay}">
SelectedItem="{Binding SelectedGameAccount, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
@@ -46,7 +44,7 @@
<TextBlock
Opacity="0.8"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding AttachUid,TargetNullValue=该账号尚未绑定 UID}"/>
Text="{Binding AttachUid, TargetNullValue=该账号尚未绑定 UID}"/>
</StackPanel>
</Grid>
</DataTemplate>
@@ -54,10 +52,10 @@
</ListView>
<Button
Margin="16"
Grid.Row="2"
Margin="16"
HorizontalAlignment="Stretch"
Content="启动游戏"
Command="{Binding LaunchCommand}"/>
Command="{Binding LaunchCommand}"
Content="启动游戏"/>
</Grid>
</Window>

View File

@@ -1,18 +1,18 @@
<Window
x:Class="Snap.Hutao.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:shv="using:Snap.Hutao.View"
mc:Ignorable="d">
<Grid>
<shv:TitleView
Margin="48,0,0,0"
x:Name="TitleBarView"
Height="44"
x:Name="TitleBarView"/>
Margin="48,0,0,0"/>
<shv:MainView/>
</Grid>
</Window>

View File

@@ -10,6 +10,23 @@ namespace Snap.Hutao.Model.Binding.Cultivation;
/// </summary>
public class CultivateEntry : ItemBase
{
/// <summary>
/// 构造一个新的养成清单入口
/// </summary>
/// <param name="entry">实体入口</param>
/// <param name="itemBase">对应物品</param>
/// <param name="items">物品列表</param>
public CultivateEntry(Entity.CultivateEntry entry, ItemBase itemBase, List<CultivateItem> items)
{
Id = entry.Id;
EntryId = entry.InnerId;
Name = itemBase.Name;
Icon = itemBase.Icon;
Badge = itemBase.Badge;
Quality = itemBase.Quality;
Items = items;
}
/// <summary>
/// Id
/// </summary>

View File

@@ -16,11 +16,13 @@ public enum AssociationType
/// <summary>
/// 蒙德
/// </summary>
[Description("蒙德")]
ASSOC_TYPE_MONDSTADT,
/// <summary>
/// 璃月
/// </summary>
[Description("璃月")]
ASSOC_TYPE_LIYUE,
/// <summary>
@@ -31,35 +33,42 @@ public enum AssociationType
/// <summary>
/// 愚人众
/// </summary>
[Description("愚人众")]
ASSOC_TYPE_FATUI,
/// <summary>
/// 稻妻
/// </summary>
[Description("稻妻")]
ASSOC_TYPE_INAZUMA,
/// <summary>
/// 游侠
/// </summary>
[Description("游侠")]
ASSOC_TYPE_RANGER,
/// <summary>
/// 须弥
/// </summary>
[Description("须弥")]
ASSOC_TYPE_SUMERU,
/// <summary>
/// 枫丹
/// </summary>
[Description("枫丹")]
ASSOC_TYPE_FONTAINE,
/// <summary>
/// 纳塔
/// </summary>
[Description("纳塔")]
ASSOC_TYPE_NATLAN,
/// <summary>
/// 至冬
/// </summary>
[Description("至冬")]
ASSOC_TYPE_SNEZHNAYA,
}

View File

@@ -16,25 +16,30 @@ public enum BodyType
/// <summary>
/// 男孩
/// </summary>
[Description("少男")]
BODY_BOY,
/// <summary>
/// 女孩
/// </summary>
[Description("少女")]
BODY_GIRL,
/// <summary>
/// 成女
/// </summary>
[Description("成女")]
BODY_LADY,
/// <summary>
/// 成男
/// </summary>
[Description("成男")]
BODY_MALE,
/// <summary>
/// 萝莉
/// </summary>
[Description("萝莉")]
BODY_LOLI,
}

View File

@@ -18,30 +18,36 @@ public enum ItemQuality
/// <summary>
/// 一星
/// </summary>
[Description("一星")]
QUALITY_WHITE = 1,
/// <summary>
/// 二星
/// </summary>
[Description("二星")]
QUALITY_GREEN = 2,
/// <summary>
/// 三星
/// </summary>
[Description("三星")]
QUALITY_BLUE = 3,
/// <summary>
/// 四星
/// </summary>
[Description("四星")]
QUALITY_PURPLE = 4,
/// <summary>
/// 五星
/// </summary>
[Description("五星")]
QUALITY_ORANGE = 5,
/// <summary>
/// 限定五星
/// </summary>
[Description("限定五星")]
QUALITY_ORANGE_SP = 105,
}

View File

@@ -17,6 +17,7 @@ public enum WeaponType
/// <summary>
/// 单手剑
/// </summary>
[Description("单手剑")]
WEAPON_SWORD_ONE_HAND = 1,
#region Not Used
@@ -73,20 +74,24 @@ public enum WeaponType
/// <summary>
/// 法器
/// </summary>
[Description("单手剑")]
WEAPON_CATALYST = 10,
/// <summary>
/// 双手剑
/// </summary>
[Description("单手剑")]
WEAPON_CLAYMORE = 11,
/// <summary>
/// 弓
/// </summary>
[Description("单手剑")]
WEAPON_BOW = 12,
/// <summary>
/// 长柄武器
/// </summary>
[Description("单手剑")]
WEAPON_POLE = 13,
}

View File

@@ -1,49 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
namespace Snap.Hutao.Model;
/// <summary>
/// 可选择的对象
/// 默认为选中状态
/// </summary>
/// <typeparam name="T">值的类型</typeparam>
public class Selectable<T> : ObservableObject
where T : class
{
private readonly Action? selectedChanged;
private bool isSelected = true;
private T value;
/// <summary>
/// 构造一个新的可选择的对象
/// </summary>
/// <param name="value">值</param>
/// <param name="onSelectedChanged">选中的值发生变化时调用</param>
public Selectable(T value, Action? onSelectedChanged = null)
{
this.value = value;
selectedChanged = onSelectedChanged;
}
/// <summary>
/// 指示当前对象是否选中
/// </summary>
public bool IsSelected
{
get => isSelected;
set
{
SetProperty(ref isSelected, value);
selectedChanged?.Invoke();
}
}
/// <summary>
/// 存放的对象
/// </summary>
public T Value { get => value; set => SetProperty(ref this.value, value); }
}

View File

@@ -12,7 +12,7 @@
<Identity
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
Publisher="CN=DGP Studio"
Version="1.2.10.0" />
Version="1.2.12.0" />
<Properties>
<DisplayName>胡桃</DisplayName>

View File

@@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Context.Database;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Binding.Cultivation;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Primitive;
using System.Collections.ObjectModel;
@@ -136,43 +135,45 @@ internal class CultivationService : ICultivationService
Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap,
Dictionary<WeaponId, Model.Metadata.Weapon.Weapon> idWeaponMap)
{
// TODO: cache the collection
await ThreadHelper.SwitchToBackgroundAsync();
Guid projectId = cultivateProject.InnerId;
using (IServiceScope scope = scopeFactory.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
List<BindingCultivateEntry> bindingEntries = new();
foreach (Model.Entity.CultivateEntry? entry in await appDbContext.CultivateEntries.ToListAsync().ConfigureAwait(false))
Guid projectId = cultivateProject.InnerId;
List<BindingCultivateEntry> results = new();
List<CultivateEntry> entries = await appDbContext.CultivateEntries
.Where(e => e.ProjectId == projectId)
.ToListAsync()
.ConfigureAwait(false);
foreach (CultivateEntry? entry in entries)
{
Guid entryId = entry.InnerId;
List<BindingCultivateItem> items = new();
foreach (Model.Entity.CultivateItem? item in await appDbContext.CultivateItems.Where(i => i.EntryId == entryId).OrderBy(i => i.ItemId).ToListAsync().ConfigureAwait(false))
List<BindingCultivateItem> resultItems = new();
List<CultivateItem> items = await appDbContext.CultivateItems
.Where(i => i.EntryId == entryId)
.OrderBy(i => i.ItemId).ToListAsync()
.ConfigureAwait(false);
foreach (CultivateItem item in items)
{
items.Add(new(metadata.Single(m => m.Id == item.ItemId), item));
resultItems.Add(new(metadata.Single(m => m.Id == item.ItemId), item));
}
Model.Binding.Gacha.Abstraction.ItemBase itemBase = entry.Type switch
{
CultivateType.AvatarAndSkill => idAvatarMap[entry.Id].ToItemBase(),
CultivateType.Weapon => idWeaponMap[entry.Id].ToItemBase(),
Model.Binding.Cultivation.CultivateType.AvatarAndSkill => idAvatarMap[entry.Id].ToItemBase(),
Model.Binding.Cultivation.CultivateType.Weapon => idWeaponMap[entry.Id].ToItemBase(),
_ => null!, // TODO: support furniture calc
};
bindingEntries.Add(new()
{
Id = entry.Id,
EntryId = entryId,
Name = itemBase.Name,
Icon = itemBase.Icon,
Badge = itemBase.Badge,
Quality = itemBase.Quality,
Items = items,
});
results.Add(new(entry, itemBase, resultItems));
}
return new(bindingEntries);
return new(results);
}
}
@@ -197,7 +198,7 @@ internal class CultivationService : ICultivationService
}
/// <inheritdoc/>
public async Task<bool> SaveConsumptionAsync(CultivateType type, int itemId, List<Web.Hoyolab.Takumi.Event.Calculate.Item> items)
public async Task<bool> SaveConsumptionAsync(Model.Binding.Cultivation.CultivateType type, int itemId, List<Web.Hoyolab.Takumi.Event.Calculate.Item> items)
{
using (IServiceScope scope = scopeFactory.CreateScope())
{
@@ -210,19 +211,20 @@ internal class CultivationService : ICultivationService
}
Guid projectId = Current!.InnerId;
Model.Entity.CultivateEntry? entry = await appDbContext.CultivateEntries
CultivateEntry? entry = await appDbContext.CultivateEntries
.SingleOrDefaultAsync(e => e.ProjectId == projectId && e.Id == itemId)
.ConfigureAwait(false);
if (entry == null)
{
entry = Model.Entity.CultivateEntry.Create(projectId, type, itemId);
entry = CultivateEntry.Create(projectId, type, itemId);
await appDbContext.CultivateEntries.AddAndSaveAsync(entry).ConfigureAwait(false);
}
Guid entryId = entry.InnerId;
await appDbContext.CultivateItems.Where(i => i.EntryId == entryId).ExecuteDeleteAsync().ConfigureAwait(false);
IEnumerable<Model.Entity.CultivateItem> toAdd = items.Select(i => Model.Entity.CultivateItem.Create(entryId, i.Id, i.Num));
IEnumerable<CultivateItem> toAdd = items.Select(i => CultivateItem.Create(entryId, i.Id, i.Num));
await appDbContext.CultivateItems.AddRangeAndSaveAsync(toAdd).ConfigureAwait(false);
}
@@ -257,4 +259,4 @@ internal class CultivationService : ICultivationService
_ => false,
};
}
}
}

View File

@@ -36,12 +36,11 @@
<AutoSuggestBox
Width="240"
Height="36"
Margin="12,6,12,0"
Margin="12,6,6,0"
HorizontalAlignment="Stretch"
VerticalContentAlignment="Center"
PlaceholderText="搜索成就名称,描述或编号"
QueryIcon="{shcm:FontIcon Glyph=&#xE721;}"
Style="{StaticResource DefaultAutoSuggestBoxStyle}"
Text="{Binding SearchText, Mode=TwoWay}">
<mxi:Interaction.Behaviors>
<mxic:EventTriggerBehavior EventName="QuerySubmitted">

View File

@@ -6,6 +6,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shci="using:Snap.Hutao.Control.Image"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
@@ -36,7 +37,6 @@
PreferredSelectedIndex="9"
Source="{Binding Proud, Mode=OneWay, Converter={StaticResource DescParamDescriptor}}"/>
</StackPanel>
</Grid>
</DataTemplate>
@@ -72,6 +72,23 @@
<CommandBar.Content>
<shcp:PanelSelector x:Name="ItemsPanelSelector" Margin="6,8,0,0"/>
</CommandBar.Content>
<AppBarElementContainer>
<AutoSuggestBox
Width="240"
Height="36"
Margin="16,6,6,0"
HorizontalAlignment="Stretch"
VerticalContentAlignment="Center"
PlaceholderText="筛选角色"
QueryIcon="{shcm:FontIcon Glyph=&#xE721;}"
Text="{Binding FilterText, Mode=TwoWay}">
<mxi:Interaction.Behaviors>
<mxic:EventTriggerBehavior EventName="QuerySubmitted">
<mxic:InvokeCommandAction Command="{Binding FilterCommand}" CommandParameter="{Binding FilterText}"/>
</mxic:EventTriggerBehavior>
</mxi:Interaction.Behaviors>
</AutoSuggestBox>
</AppBarElementContainer>
<AppBarButton
Command="{Binding CultivateCommand}"
CommandParameter="{Binding Selected}"

View File

@@ -2,9 +2,11 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.WinUI.UI;
using Microsoft.Extensions.Primitives;
using Snap.Hutao.Extension;
using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Model;
using Snap.Hutao.Model.Binding.Cultivation;
using Snap.Hutao.Model.Binding.Hutao;
using Snap.Hutao.Model.Intrinsic;
@@ -16,6 +18,7 @@ using Snap.Hutao.Service.Hutao;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Service.User;
using Snap.Hutao.View.Dialog;
using System.Collections.Immutable;
using CalcAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta;
using CalcClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient;
using CalcConsumption = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Consumption;
@@ -33,15 +36,9 @@ internal class WikiAvatarViewModel : ObservableObject
private readonly IMetadataService metadataService;
private readonly IHutaoCache hutaoCache;
// filters
private readonly List<Selectable<string>> filterElementInfos;
private readonly List<Selectable<Pair<string, AssociationType>>> filterAssociationInfos;
private readonly List<Selectable<Pair<string, WeaponType>>> filterWeaponTypeInfos;
private readonly List<Selectable<Pair<string, ItemQuality>>> filterQualityInfos;
private readonly List<Selectable<Pair<string, BodyType>>> filterBodyInfos;
private AdvancedCollectionView? avatars;
private Avatar? selected;
private string? filterText;
/// <summary>
/// 构造一个新的角色资料视图模型
@@ -55,52 +52,7 @@ internal class WikiAvatarViewModel : ObservableObject
this.hutaoCache = hutaoCache;
OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync);
CultivateCommand = asyncRelayCommandFactory.Create<Avatar>(CultivateAsync);
filterElementInfos = new()
{
new("火", OnFilterChanged),
new("水", OnFilterChanged),
new("草", OnFilterChanged),
new("雷", OnFilterChanged),
new("冰", OnFilterChanged),
new("风", OnFilterChanged),
new("岩", OnFilterChanged),
};
filterAssociationInfos = new()
{
new(new("蒙德", AssociationType.ASSOC_TYPE_MONDSTADT), OnFilterChanged),
new(new("璃月", AssociationType.ASSOC_TYPE_LIYUE), OnFilterChanged),
new(new("稻妻", AssociationType.ASSOC_TYPE_INAZUMA), OnFilterChanged),
new(new("须弥", AssociationType.ASSOC_TYPE_SUMERU), OnFilterChanged),
new(new("愚人众", AssociationType.ASSOC_TYPE_FATUI), OnFilterChanged),
new(new("游侠", AssociationType.ASSOC_TYPE_RANGER), OnFilterChanged),
};
filterWeaponTypeInfos = new()
{
new(new("单手剑", WeaponType.WEAPON_SWORD_ONE_HAND), OnFilterChanged),
new(new("法器", WeaponType.WEAPON_CATALYST), OnFilterChanged),
new(new("双手剑", WeaponType.WEAPON_CLAYMORE), OnFilterChanged),
new(new("弓", WeaponType.WEAPON_BOW), OnFilterChanged),
new(new("长柄武器", WeaponType.WEAPON_POLE), OnFilterChanged),
};
filterQualityInfos = new()
{
new(new("限定五星", ItemQuality.QUALITY_ORANGE_SP), OnFilterChanged),
new(new("五星", ItemQuality.QUALITY_ORANGE), OnFilterChanged),
new(new("四星", ItemQuality.QUALITY_PURPLE), OnFilterChanged),
};
filterBodyInfos = new()
{
new(new("成女", BodyType.BODY_LADY), OnFilterChanged),
new(new("少女", BodyType.BODY_GIRL), OnFilterChanged),
new(new("幼女", BodyType.BODY_LOLI), OnFilterChanged),
new(new("成男", BodyType.BODY_MALE), OnFilterChanged),
new(new("少男", BodyType.BODY_BOY), OnFilterChanged),
};
FilterCommand = new RelayCommand<string>(ApplyFilter);
}
/// <summary>
@@ -114,44 +66,9 @@ internal class WikiAvatarViewModel : ObservableObject
public Avatar? Selected { get => selected; set => SetProperty(ref selected, value); }
/// <summary>
/// 筛选用元素信息集合
/// 筛选文本
/// </summary>
public IList<Selectable<string>> FilterElementInfos
{
get => filterElementInfos;
}
/// <summary>
/// 筛选用所属国家集合
/// </summary>
public IList<Selectable<Pair<string, AssociationType>>> FilterAssociationInfos
{
get => filterAssociationInfos;
}
/// <summary>
/// 筛选用武器信息集合
/// </summary>
public IList<Selectable<Pair<string, WeaponType>>> FilterWeaponTypeInfos
{
get => filterWeaponTypeInfos;
}
/// <summary>
/// 筛选用星级信息集合
/// </summary>
public IList<Selectable<Pair<string, ItemQuality>>> FilterQualityInfos
{
get => filterQualityInfos;
}
/// <summary>
/// 筛选用体型信息集合
/// </summary>
public IList<Selectable<Pair<string, BodyType>>> FilterBodyInfos
{
get => filterBodyInfos;
}
public string? FilterText { get => filterText; set => SetProperty(ref filterText, value); }
/// <summary>
/// 打开页面命令
@@ -163,6 +80,11 @@ internal class WikiAvatarViewModel : ObservableObject
/// </summary>
public ICommand CultivateCommand { get; }
/// <summary>
/// 筛选命令
/// </summary>
public ICommand FilterCommand { get; }
private async Task OpenUIAsync()
{
if (await metadataService.InitializeAsync().ConfigureAwait(false))
@@ -194,55 +116,13 @@ internal class WikiAvatarViewModel : ObservableObject
}
}
private void OnFilterChanged()
{
if (Avatars is not null)
{
List<string> targetElements = filterElementInfos
.Where(e => e.IsSelected)
.Select(e => e.Value)
.ToList();
List<AssociationType> targetAssociations = filterAssociationInfos
.Where(e => e.IsSelected)
.Select(e => e.Value.Value)
.ToList();
List<WeaponType> targetWeaponTypes = filterWeaponTypeInfos
.Where(e => e.IsSelected)
.Select(e => e.Value.Value)
.ToList();
List<ItemQuality> targetQualities = FilterQualityInfos
.Where(e => e.IsSelected)
.Select(e => e.Value.Value)
.ToList();
List<BodyType> targetBodies = filterBodyInfos
.Where(e => e.IsSelected)
.Select(e => e.Value.Value)
.ToList();
Avatars.Filter = (object o) => o is Avatar avatar
&& targetElements.Contains(avatar.FetterInfo.VisionBefore)
&& targetAssociations.Contains(avatar.FetterInfo.Association)
&& targetWeaponTypes.Contains(avatar.Weapon)
&& targetQualities.Contains(avatar.Quality)
&& targetBodies.Contains(avatar.Body);
if (!Avatars.Contains(Selected))
{
Avatars.MoveCurrentToFirst();
}
}
}
private async Task CultivateAsync(Avatar? avatar)
{
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
if (avatar != null)
{
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
IUserService userService = Ioc.Default.GetRequiredService<IUserService>();
if (userService.Current != null)
{
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
@@ -252,8 +132,11 @@ internal class WikiAvatarViewModel : ObservableObject
if (isOk)
{
CalcClient calculateClient = Ioc.Default.GetRequiredService<CalcClient>();
CalcConsumption? consumption = await calculateClient.ComputeAsync(userService.Current.Entity, delta).ConfigureAwait(false);
CalcConsumption? consumption = await Ioc.Default
.GetRequiredService<CalcClient>()
.ComputeAsync(userService.Current.Entity, delta)
.ConfigureAwait(false);
if (consumption != null)
{
List<CalcItem> items = CalcItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume);
@@ -279,4 +162,84 @@ internal class WikiAvatarViewModel : ObservableObject
}
}
}
private void ApplyFilter(string? input)
{
if (Avatars != null)
{
if (!string.IsNullOrWhiteSpace(input))
{
Avatars.Filter = AvatarFilter.Compile(input);
if (!Avatars.Contains(Selected))
{
Avatars.MoveCurrentToFirst();
}
}
else
{
Avatars.Filter = null!;
}
}
}
private static class AvatarFilter
{
private static readonly ImmutableList<string> AssociationTypes = Enum.GetValues<AssociationType>().Select(e => e.GetDescriptionOrNull()).OfType<string>().ToImmutableList();
private static readonly ImmutableList<string> WeaponTypes = Enum.GetValues<WeaponType>().Select(e => e.GetDescriptionOrNull()).OfType<string>().ToImmutableList();
private static readonly ImmutableList<string> ItemQualities = Enum.GetValues<ItemQuality>().Select(e => e.GetDescriptionOrNull()).OfType<string>().ToImmutableList();
private static readonly ImmutableList<string> BodyTypes = Enum.GetValues<BodyType>().Select(e => e.GetDescriptionOrNull()).OfType<string>().ToImmutableList();
public static Predicate<object> Compile(string input)
{
return (object o) => o is Avatar avatar && DoFilter(input, avatar);
}
private static bool DoFilter(string input, Avatar avatar)
{
bool keep = false;
foreach (StringSegment segment in new StringTokenizer(input, ' '.Enumerate().ToArray()))
{
string value = segment.ToString();
if (value == "火" || value == "水" || value == "草" || value == "雷" || value == "冰" || value == "风" || value == "岩")
{
keep = keep || avatar.FetterInfo.VisionBefore == value;
continue;
}
if (AssociationTypes.Contains(value))
{
keep = keep || avatar.FetterInfo.Association.GetDescriptionOrNull() == value;
continue;
}
if (WeaponTypes.Contains(value))
{
keep = keep || avatar.Weapon.GetDescriptionOrNull() == value;
continue;
}
if (ItemQualities.Contains(value))
{
keep = keep || avatar.Quality.GetDescriptionOrNull() == value;
continue;
}
if (BodyTypes.Contains(value))
{
keep = keep || avatar.Body.GetDescriptionOrNull() == value;
continue;
}
if (avatar.Name == value)
{
keep = true;
}
}
return keep;
}
}
}

View File

@@ -80,13 +80,13 @@ internal static class HttpClientExtensions
}
/// <inheritdoc cref="HttpClientJsonExtensions.PostAsJsonAsync{TValue}(HttpClient, string?, TValue, JsonSerializerOptions?, CancellationToken)"/>
internal static async Task<TResult?> TryCatchPostAsJsonAsync<TValue, TResult>(this HttpClient httpClient, string requestUri, TValue value, JsonSerializerOptions options, CancellationToken token = default)
internal static async Task<TResult?> TryCatchPostAsJsonAsync<TValue, TResult>(this HttpClient httpClient, string requestUri, TValue value, CancellationToken token = default)
where TResult : class
{
try
{
HttpResponseMessage message = await httpClient.PostAsJsonAsync(requestUri, value, options, token).ConfigureAwait(false);
return await message.Content.ReadFromJsonAsync<TResult>(options, token).ConfigureAwait(false);
HttpResponseMessage message = await httpClient.PostAsJsonAsync(requestUri, value, token).ConfigureAwait(false);
return await message.Content.ReadFromJsonAsync<TResult>(cancellationToken: token).ConfigureAwait(false);
}
catch (HttpRequestException)
{

View File

@@ -16,17 +16,14 @@ internal class HomaClient2
{
private const string HutaoAPI = "https://homa.snapgenshin.com";
private readonly HttpClient httpClient;
private readonly JsonSerializerOptions options;
/// <summary>
/// 构造一个新的胡桃日志客户端
/// </summary>
/// <param name="httpClient">Http客户端</param>
/// <param name="options">Json序列化选项</param>
public HomaClient2(HttpClient httpClient, JsonSerializerOptions options)
public HomaClient2(HttpClient httpClient)
{
this.httpClient = httpClient;
this.options = options;
}
/// <summary>
@@ -44,7 +41,7 @@ internal class HomaClient2
};
Response<string>? a = await httpClient
.TryCatchPostAsJsonAsync<HutaoLog, Response<string>>($"{HutaoAPI}/HutaoLog/Upload", log, options)
.TryCatchPostAsJsonAsync<HutaoLog, Response<string>>($"{HutaoAPI}/HutaoLog/Upload", log)
.ConfigureAwait(false);
return a?.Data;
}