bulk refactor

This commit is contained in:
DismissedLight
2023-11-15 22:31:56 +08:00
parent 3e0493be31
commit 98f2dd13d1
43 changed files with 187 additions and 128 deletions

View File

@@ -140,6 +140,8 @@ internal sealed class AttributeGenerator : IIncrementalGenerator
public InjectionAttribute(InjectAs injectAs, Type interfaceType) public InjectionAttribute(InjectAs injectAs, Type interfaceType)
{ {
} }
public object Key { get; set; }
} }
"""; """;
context.AddSource("Snap.Hutao.Core.DependencyInjection.Annotation.Attributes.g.cs", coreDependencyInjectionAnnotations); context.AddSource("Snap.Hutao.Core.DependencyInjection.Annotation.Attributes.g.cs", coreDependencyInjectionAnnotations);

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license. // Licensed under the MIT license.
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Snap.Hutao.SourceGeneration.Primitive; using Snap.Hutao.SourceGeneration.Primitive;
using System.Collections.Generic; using System.Collections.Generic;
@@ -68,7 +69,7 @@ internal sealed class DependencyPropertyGenerator : IIncrementalGenerator
string propertyName = (string)arguments[0].Value!; string propertyName = (string)arguments[0].Value!;
string propertyType = arguments[1].Value!.ToString(); string propertyType = arguments[1].Value!.ToString();
string defaultValue = GetLiteralString(arguments.ElementAtOrDefault(2)) ?? "default"; string defaultValue = arguments.ElementAtOrDefault(2).ToCSharpString() ?? "default";
string propertyChangedCallback = arguments.ElementAtOrDefault(3) is { IsNull: false } arg3 ? $", {arg3.Value}" : string.Empty; string propertyChangedCallback = arguments.ElementAtOrDefault(3) is { IsNull: false } arg3 ? $", {arg3.Value}" : string.Empty;
string code; string code;
@@ -125,25 +126,4 @@ internal sealed class DependencyPropertyGenerator : IIncrementalGenerator
production.AddSource($"{normalizedClassName}.{propertyName}.g.cs", code); production.AddSource($"{normalizedClassName}.{propertyName}.g.cs", code);
} }
} }
private static string? GetLiteralString(TypedConstant typedConstant)
{
if (typedConstant.IsNull)
{
return default;
}
if (typedConstant.Value is bool boolValue)
{
return boolValue ? "true" : "false";
}
string result = typedConstant.Value!.ToString();
if (string.IsNullOrEmpty(result))
{
return default;
}
return result;
}
} }

View File

@@ -92,17 +92,29 @@ internal sealed class InjectionGenerator : IIncrementalGenerator
ImmutableArray<TypedConstant> arguments = injectionInfo.ConstructorArguments; ImmutableArray<TypedConstant> arguments = injectionInfo.ConstructorArguments;
string injectAsName = arguments[0].ToCSharpString(); string injectAsName = arguments[0].ToCSharpString();
switch (injectAsName)
bool hasKey = injectionInfo.TryGetNamedArgumentValue("Key", out TypedConstant key);
switch (injectAsName, hasKey)
{ {
case InjectAsSingletonName: case (InjectAsSingletonName, false):
lineBuilder.Append(" services.AddSingleton<"); lineBuilder.Append(" services.AddSingleton<");
break; break;
case InjectAsTransientName: case (InjectAsSingletonName, true):
lineBuilder.Append(" services.AddKeyedSingleton<");
break;
case (InjectAsTransientName, false):
lineBuilder.Append(" services.AddTransient<"); lineBuilder.Append(" services.AddTransient<");
break; break;
case InjectAsScopedName: case (InjectAsTransientName, true):
lineBuilder.Append(" services.AddKeyedTransient<");
break;
case (InjectAsScopedName, false):
lineBuilder.Append(" services.AddScoped<"); lineBuilder.Append(" services.AddScoped<");
break; break;
case (InjectAsScopedName, true):
lineBuilder.Append(" services.AddKeyedScoped<");
break;
default: default:
production.ReportDiagnostic(Diagnostic.Create(invalidInjectionDescriptor, context.Context.Node.GetLocation(), injectAsName)); production.ReportDiagnostic(Diagnostic.Create(invalidInjectionDescriptor, context.Context.Node.GetLocation(), injectAsName));
break; break;
@@ -113,7 +125,14 @@ internal sealed class InjectionGenerator : IIncrementalGenerator
lineBuilder.Append($"{arguments[1].Value}, "); lineBuilder.Append($"{arguments[1].Value}, ");
} }
lineBuilder.Append($"{context.Symbol.ToDisplayString()}>();"); if (hasKey)
{
lineBuilder.Append($"{context.Symbol.ToDisplayString()}>({key.ToCSharpString()});");
}
else
{
lineBuilder.Append($"{context.Symbol.ToDisplayString()}>();");
}
lines.Add(lineBuilder.ToString()); lines.Add(lineBuilder.ToString());
} }

View File

@@ -3,6 +3,7 @@
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Snap.Hutao.SourceGeneration.Primitive; namespace Snap.Hutao.SourceGeneration.Primitive;
@@ -13,4 +14,19 @@ internal static class AttributeDataExtension
{ {
return data.NamedArguments.Any(a => a.Key == key && predicate((TValue)a.Value.Value!)); return data.NamedArguments.Any(a => a.Key == key && predicate((TValue)a.Value.Value!));
} }
public static bool TryGetNamedArgumentValue(this AttributeData data, string key, out TypedConstant value)
{
foreach (KeyValuePair<string, TypedConstant> pair in data.NamedArguments)
{
if (pair.Key == key)
{
value = pair.Value;
return true;
}
}
value = default;
return false;
}
} }

View File

@@ -8,6 +8,7 @@
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -7,6 +7,10 @@ namespace Snap.Hutao.Test;
[TestClass] [TestClass]
public class JsonSerializeTest public class JsonSerializeTest
{ {
private TestContext? testContext;
public TestContext? TestContext { get => testContext; set => testContext = value; }
private readonly JsonSerializerOptions AlowStringNumberOptions = new() private readonly JsonSerializerOptions AlowStringNumberOptions = new()
{ {
NumberHandling = JsonNumberHandling.AllowReadingFromString, NumberHandling = JsonNumberHandling.AllowReadingFromString,
@@ -53,6 +57,27 @@ public class JsonSerializeTest
Assert.AreEqual(sample[111], "12"); Assert.AreEqual(sample[111], "12");
} }
[TestMethod]
public void ByteArraySerializeAsBase64()
{
byte[] array =
#if NET8_0_OR_GREATER
[1, 2, 3, 4, 5];
#else
{ 1, 2, 3, 4, 5 };
#endif
ByteArraySample sample = new()
{
Array = array,
};
string result = JsonSerializer.Serialize(sample);
TestContext!.WriteLine($"ByteArray Serialize Result: {result}");
Assert.AreEqual(result, """
{"Array":"AQIDBAU="}
""");
}
private sealed class Sample private sealed class Sample
{ {
public int A { get => B; set => B = value; } public int A { get => B; set => B = value; }
@@ -64,4 +89,9 @@ public class JsonSerializeTest
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)] [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
public int A { get; set; } public int A { get; set; }
} }
private sealed class ByteArraySample
{
public byte[]? Array { get; set; }
}
} }

View File

@@ -9,8 +9,12 @@ public sealed class ForEachRuntimeBehaviorTest
[TestMethod] [TestMethod]
public void ListOfStringCanEnumerateAsReadOnlySpanOfChar() public void ListOfStringCanEnumerateAsReadOnlySpanOfChar()
{ {
List<string> strings = ["a", "b", "c"]; List<string> strings =
#if NET8_0_OR_GREATER
["a", "b", "c"];
#else
new() { "a", "b", "c" };
#endif
int count = 0; int count = 0;
foreach (ReadOnlySpan<char> chars in strings) foreach (ReadOnlySpan<char> chars in strings)
{ {

View File

@@ -8,8 +8,13 @@ public sealed class RangeRuntimeBehaviorTest
[TestMethod] [TestMethod]
public void RangeTrimLastOne() public void RangeTrimLastOne()
{ {
#if NET8_0_OR_GREATER
int[] array = [1, 2, 3, 4]; int[] array = [1, 2, 3, 4];
int[] test = [1, 2, 3]; int[] test = [1, 2, 3];
#else
int[] array = { 1, 2, 3, 4 };
int[] test = { 1, 2, 3 };
#endif
int[] result = array[..^1]; int[] result = array[..^1];
Assert.AreEqual(3, result.Length); Assert.AreEqual(3, result.Length);
Assert.IsTrue(MemoryExtensions.SequenceEqual<int>(test, result)); Assert.IsTrue(MemoryExtensions.SequenceEqual<int>(test, result));

View File

@@ -6,7 +6,12 @@ public sealed class UnsafeRuntimeBehaviorTest
[TestMethod] [TestMethod]
public unsafe void UInt32AllSetIs() public unsafe void UInt32AllSetIs()
{ {
byte[] bytes = [0xFF, 0xFF, 0xFF, 0xFF,]; byte[] bytes =
#if NET8_0_OR_GREATER
[0xFF, 0xFF, 0xFF, 0xFF];
#else
{ 0xFF, 0xFF, 0xFF, 0xFF, };
#endif
fixed (byte* pBytes = bytes) fixed (byte* pBytes = bytes)
{ {

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<ImplicitUsings>disable</ImplicitUsings> <ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>

View File

@@ -35,7 +35,6 @@ internal static class DependencyInjection
// Discrete services // Discrete services
.AddSingleton<IMessenger, WeakReferenceMessenger>() .AddSingleton<IMessenger, WeakReferenceMessenger>()
.BuildServiceProvider(true); .BuildServiceProvider(true);
Ioc.Default.ConfigureServices(serviceProvider); Ioc.Default.ConfigureServices(serviceProvider);

View File

@@ -48,7 +48,7 @@ internal sealed partial class GameChannelOptionsService : IGameChannelOptionsSer
{ {
using (FileStream readStream = File.OpenRead(configPath)) using (FileStream readStream = File.OpenRead(configPath))
{ {
elements = IniSerializer.Deserialize(readStream).ToList(); elements = [.. IniSerializer.Deserialize(readStream)];
} }
} }
catch (FileNotFoundException ex) catch (FileNotFoundException ex)

View File

@@ -165,11 +165,11 @@ internal sealed class LaunchOptions : DbStoreOptions
set => SetOption(ref isMonitorEnabled, SettingEntry.LaunchIsMonitorEnabled, value); set => SetOption(ref isMonitorEnabled, SettingEntry.LaunchIsMonitorEnabled, value);
} }
public List<AspectRatio> AspectRatios { get; } = new() public List<AspectRatio> AspectRatios { get; } =
{ [
new(2560, 1440), new(2560, 1440),
new(1920, 1080), new(1920, 1080),
}; ];
public AspectRatio? SelectedAspectRatio public AspectRatio? SelectedAspectRatio
{ {

View File

@@ -178,11 +178,11 @@ internal sealed partial class PackageConverter
private async ValueTask<Dictionary<string, VersionItem>> GetVersionItemsAsync(Stream stream) private async ValueTask<Dictionary<string, VersionItem>> GetVersionItemsAsync(Stream stream)
{ {
Dictionary<string, VersionItem> results = new(); Dictionary<string, VersionItem> results = [];
using (StreamReader reader = new(stream)) using (StreamReader reader = new(stream))
{ {
Regex dataFolderRegex = DataFolderRegex(); Regex dataFolderRegex = DataFolderRegex();
while (await reader.ReadLineAsync().ConfigureAwait(false) is { } row && !string.IsNullOrEmpty(row)) while (await reader.ReadLineAsync().ConfigureAwait(false) is { Length: > 0 } row)
{ {
VersionItem? item = JsonSerializer.Deserialize<VersionItem>(row, options); VersionItem? item = JsonSerializer.Deserialize<VersionItem>(row, options);
ArgumentNullException.ThrowIfNull(item); ArgumentNullException.ThrowIfNull(item);

View File

@@ -30,8 +30,8 @@ internal sealed partial class GameProcessService : IGameProcessService
return false; return false;
} }
return System.Diagnostics.Process.GetProcessesByName(YuanShenProcessName).Any() return System.Diagnostics.Process.GetProcessesByName(YuanShenProcessName).Length > 0
|| System.Diagnostics.Process.GetProcessesByName(GenshinImpactProcessName).Any(); || System.Diagnostics.Process.GetProcessesByName(GenshinImpactProcessName).Length > 0;
} }
public async ValueTask LaunchAsync(IProgress<LaunchStatus> progress) public async ValueTask LaunchAsync(IProgress<LaunchStatus> progress)
@@ -126,7 +126,9 @@ internal sealed partial class GameProcessService : IGameProcessService
private ValueTask UnlockFpsAsync(System.Diagnostics.Process game, IProgress<LaunchStatus> progress, CancellationToken token = default) private ValueTask UnlockFpsAsync(System.Diagnostics.Process game, IProgress<LaunchStatus> progress, CancellationToken token = default)
{ {
#pragma warning disable CA1859
IGameFpsUnlocker unlocker = serviceProvider.CreateInstance<GameFpsUnlocker>(game); IGameFpsUnlocker unlocker = serviceProvider.CreateInstance<GameFpsUnlocker>(game);
#pragma warning restore CA1859
UnlockTimingOptions options = new(100, 20000, 3000); UnlockTimingOptions options = new(100, 20000, 3000);
Progress<UnlockerStatus> lockerProgress = new(unlockStatus => progress.Report(LaunchStatus.FromUnlockStatus(unlockStatus))); Progress<UnlockerStatus> lockerProgress = new(unlockStatus => progress.Report(LaunchStatus.FromUnlockStatus(unlockStatus)));
return unlocker.UnlockAsync(options, lockerProgress, token); return unlocker.UnlockAsync(options, lockerProgress, token);

View File

@@ -28,8 +28,9 @@ internal static class KnownLaunchSchemes
/// <returns>已知的启动方案</returns> /// <returns>已知的启动方案</returns>
public static List<LaunchScheme> Get() public static List<LaunchScheme> Get()
{ {
return new List<LaunchScheme>() return
{ [
// 官服 // 官服
ServerChineseChannelDefaultSubChannelDefaultCompat, ServerChineseChannelDefaultSubChannelDefaultCompat,
ServerChineseChannelOfficialSubChannelDefault, ServerChineseChannelOfficialSubChannelDefault,
@@ -47,6 +48,6 @@ internal static class KnownLaunchSchemes
ServerGlobalChannelOfficialSubChannelOfficial, ServerGlobalChannelOfficialSubChannelOfficial,
ServerGlobalChannelOfficialSubChannelEpic, ServerGlobalChannelOfficialSubChannelEpic,
ServerGlobalChannelOfficialSubChannelGoogle, ServerGlobalChannelOfficialSubChannelGoogle,
}; ];
} }
} }

View File

@@ -135,8 +135,8 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
{ {
// E8 ?? ?? ?? ?? 85 C0 7E 07 E8 ?? ?? ?? ?? EB 05 // E8 ?? ?? ?? ?? 85 C0 7E 07 E8 ?? ?? ?? ?? EB 05
int second = 0; int second = 0;
ReadOnlySpan<byte> secondPart = stackalloc byte[] { 0x85, 0xC0, 0x7E, 0x07, 0xE8, }; ReadOnlySpan<byte> secondPart = [0x85, 0xC0, 0x7E, 0x07, 0xE8,];
ReadOnlySpan<byte> thirdPart = stackalloc byte[] { 0xEB, 0x05, }; ReadOnlySpan<byte> thirdPart = [0xEB, 0x05,];
while (second >= 0 && second < memory.Length) while (second >= 0 && second < memory.Length)
{ {

View File

@@ -39,7 +39,7 @@ internal sealed partial class HutaoAsAService : IHutaoAsAService
} }
else else
{ {
return new(); return [];
} }
} }

View File

@@ -168,6 +168,23 @@ internal sealed class NavigationService : INavigationService, INavigationInitial
}); });
} }
private static IEnumerable<NavigationViewItem> EnumerateMenuItems(IList<object> items)
{
foreach (NavigationViewItem item in items.OfType<NavigationViewItem>())
{
yield return item;
// Suppress recursion method call if possible
if (item.MenuItems.Count > 0)
{
foreach (NavigationViewItem subItem in EnumerateMenuItems(item.MenuItems))
{
yield return subItem;
}
}
}
}
private bool SyncSelectedNavigationViewItemWith(Type? pageType) private bool SyncSelectedNavigationViewItemWith(Type? pageType)
{ {
if (NavigationView is null || pageType is null) if (NavigationView is null || pageType is null)
@@ -191,23 +208,6 @@ internal sealed class NavigationService : INavigationService, INavigationInitial
return true; return true;
} }
private IEnumerable<NavigationViewItem> EnumerateMenuItems(IList<object> items)
{
foreach (NavigationViewItem item in items.OfType<NavigationViewItem>())
{
yield return item;
// Suppress recursion method call if possible
if (item.MenuItems.Count > 0)
{
foreach (NavigationViewItem subItem in EnumerateMenuItems(item.MenuItems))
{
yield return subItem;
}
}
}
}
private void OnItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) private void OnItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
{ {
selected = NavigationView?.SelectedItem as NavigationViewItem; selected = NavigationView?.SelectedItem as NavigationViewItem;

View File

@@ -31,7 +31,7 @@ internal sealed class InfoBarService : IInfoBarService
/// <inheritdoc/> /// <inheritdoc/>
public ObservableCollection<InfoBar> Collection public ObservableCollection<InfoBar> Collection
{ {
get => collection ??= new(); get => collection ??= [];
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -121,7 +121,7 @@ internal sealed class InfoBarService : IInfoBarService
Title = title, Title = title,
Message = message, Message = message,
IsOpen = true, IsOpen = true,
Transitions = new() { new AddDeleteThemeTransition() }, Transitions = [new AddDeleteThemeTransition()],
}; };
infoBar.Closed += infobarClosedEventHandler; infoBar.Closed += infobarClosedEventHandler;

View File

@@ -24,7 +24,7 @@ internal sealed partial class UserInitializationService : IUserInitializationSer
if (!await InitializeUserAsync(user, token).ConfigureAwait(false)) if (!await InitializeUserAsync(user, token).ConfigureAwait(false))
{ {
user.UserInfo = new() { Nickname = SH.ModelBindingUserInitializationFailed }; user.UserInfo = new() { Nickname = SH.ModelBindingUserInitializationFailed };
user.UserGameRoles = new(); user.UserGameRoles = [];
} }
return user; return user;
@@ -176,7 +176,7 @@ internal sealed partial class UserInitializationService : IUserInitializationSer
if (userGameRolesResponse.IsOk()) if (userGameRolesResponse.IsOk())
{ {
user.UserGameRoles = userGameRolesResponse.Data.List; user.UserGameRoles = userGameRolesResponse.Data.List;
return user.UserGameRoles.Any(); return user.UserGameRoles.Count > 0;
} }
else else
{ {

View File

@@ -271,22 +271,22 @@
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.0.230907" /> <PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.0.230907" />
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.0.230907" /> <PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.0.230907" />
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" /> <PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.13" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.13"> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.1.0" /> <PackageReference Include="Microsoft.Graphics.Win2D" Version="1.1.0" />
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.6.11" /> <PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.8.8" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" /> <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.231008000" /> <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.231008000" />
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.507"> <PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.507">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="System.IO.Hashing" Version="7.0.0" /> <PackageReference Include="System.IO.Hashing" Version="8.0.0" />
<PackageReference Include="TaskScheduler" Version="2.10.1" /> <PackageReference Include="TaskScheduler" Version="2.10.1" />
<Manifest Include="$(ApplicationManifest)" /> <Manifest Include="$(ApplicationManifest)" />
</ItemGroup> </ItemGroup>

View File

@@ -252,7 +252,6 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
} }
} }
[SuppressMessage("", "SA1010")]
[Command("ExportAsUIAFToFileCommand")] [Command("ExportAsUIAFToFileCommand")]
private async Task ExportAsUIAFToFileAsync() private async Task ExportAsUIAFToFileAsync()
{ {

View File

@@ -23,8 +23,7 @@ internal sealed class Team : List<AvatarView>
public Team(ItemRate<string, int> team, Dictionary<AvatarId, Avatar> idAvatarMap) public Team(ItemRate<string, int> team, Dictionary<AvatarId, Avatar> idAvatarMap)
: base(4) : base(4)
{ {
// TODO use Collection Literials foreach (StringSegment item in new StringTokenizer(team.Item, [',']))
foreach (StringSegment item in new StringTokenizer(team.Item, new char[] { ',' }))
{ {
uint id = uint.Parse(item.AsSpan(), CultureInfo.InvariantCulture); uint id = uint.Parse(item.AsSpan(), CultureInfo.InvariantCulture);
Add(new(idAvatarMap[id])); Add(new(idAvatarMap[id]));

View File

@@ -38,7 +38,7 @@ internal sealed partial class DailyNoteViewModelSlim : Abstraction.ViewModelSlim
// 此处使用浅拷贝的列表以避免当导航到实时便笺页面后 // 此处使用浅拷贝的列表以避免当导航到实时便笺页面后
// 由于主页尚未卸载,添加或删除便笺可能会崩溃的问题 // 由于主页尚未卸载,添加或删除便笺可能会崩溃的问题
List<DailyNoteEntry> entryList = entries.ToList(); List<DailyNoteEntry> entryList = [.. entries];
await taskContext.SwitchToMainThreadAsync(); await taskContext.SwitchToMainThreadAsync();
DailyNoteEntries = entryList; DailyNoteEntries = entryList;

View File

@@ -231,7 +231,6 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel
} }
} }
[SuppressMessage("", "SA1010")]
[Command("ExportToUIGFJsonCommand")] [Command("ExportToUIGFJsonCommand")]
private async Task ExportToUIGFJsonAsync() private async Task ExportToUIGFJsonAsync()
{ {

View File

@@ -109,7 +109,7 @@ internal static class StaticResource
public static HashSet<string> GetUnfulfilledCategorySet() public static HashSet<string> GetUnfulfilledCategorySet()
{ {
HashSet<string> result = new(); HashSet<string> result = [];
ApplicationDataCompositeValue map = LocalSetting.Get(ContractMap, DefaultResourceVersionMap); ApplicationDataCompositeValue map = LocalSetting.Get(ContractMap, DefaultResourceVersionMap);
foreach ((string key, object value) in LatestResourceVersionMap) foreach ((string key, object value) in LatestResourceVersionMap)
{ {

View File

@@ -230,15 +230,15 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
} }
[Command("OpenCacheFolderCommand")] [Command("OpenCacheFolderCommand")]
private Task OpenCacheFolderAsync() private async Task OpenCacheFolderAsync()
{ {
return Launcher.LaunchFolderPathAsync(runtimeOptions.LocalCache).AsTask(); await Launcher.LaunchFolderPathAsync(runtimeOptions.LocalCache);
} }
[Command("OpenDataFolderCommand")] [Command("OpenDataFolderCommand")]
private Task OpenDataFolderAsync() private async Task OpenDataFolderAsync()
{ {
return Launcher.LaunchFolderPathAsync(runtimeOptions.DataFolder).AsTask(); await Launcher.LaunchFolderPathAsync(runtimeOptions.DataFolder);
} }
[Command("DeleteUsersCommand")] [Command("DeleteUsersCommand")]

View File

@@ -16,11 +16,7 @@ internal sealed class LevelView : IMappingFrom<LevelView, TowerLevel, SpiralAbys
{ {
Index = SH.ModelBindingHutaoComplexRankLevel.Format(towerLevel.Index); Index = SH.ModelBindingHutaoComplexRankLevel.Format(towerLevel.Index);
IndexValue = towerLevel.Index; IndexValue = towerLevel.Index;
Battles = new() Battles = [BattleView.From(towerLevel, 1, context), BattleView.From(towerLevel, 2, context)];
{
BattleView.From(towerLevel, 1, context),
BattleView.From(towerLevel, 2, context),
};
} }
/// <summary> /// <summary>

View File

@@ -24,10 +24,8 @@ internal static class AvatarFilter
private static bool DoFilter(string input, Avatar avatar) private static bool DoFilter(string input, Avatar avatar)
{ {
List<bool> matches = new(); List<bool> matches = [];
foreach (StringSegment segment in new StringTokenizer(input, [' ']))
// TODO: use Collection Literals
foreach (StringSegment segment in new StringTokenizer(input, new char[] { ' ' }))
{ {
string value = segment.ToString(); string value = segment.ToString();

View File

@@ -22,7 +22,6 @@ internal static class WeaponFilter
return (object o) => o is Weapon weapon && DoFilter(input, weapon); return (object o) => o is Weapon weapon && DoFilter(input, weapon);
} }
[SuppressMessage("", "SA1010")]
private static bool DoFilter(string input, Weapon weapon) private static bool DoFilter(string input, Weapon weapon)
{ {
List<bool> matches = []; List<bool> matches = [];

View File

@@ -92,15 +92,15 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
Dictionary<MaterialId, Material> idMaterialMap = await metadataService.GetIdToMaterialMapAsync().ConfigureAwait(false); Dictionary<MaterialId, Material> idMaterialMap = await metadataService.GetIdToMaterialMapAsync().ConfigureAwait(false);
List<Avatar> avatars = await metadataService.GetAvatarsAsync().ConfigureAwait(false); List<Avatar> avatars = await metadataService.GetAvatarsAsync().ConfigureAwait(false);
List<Avatar> sorted = avatars IOrderedEnumerable<Avatar> sorted = avatars
.OrderByDescending(avatar => avatar.BeginTime) .OrderByDescending(avatar => avatar.BeginTime)
.ThenByDescending(avatar => avatar.Sort) .ThenByDescending(avatar => avatar.Sort);
.ToList(); List<Avatar> list = [.. sorted];
await CombineComplexDataAsync(sorted, idMaterialMap).ConfigureAwait(false); await CombineComplexDataAsync(list, idMaterialMap).ConfigureAwait(false);
await taskContext.SwitchToMainThreadAsync(); await taskContext.SwitchToMainThreadAsync();
Avatars = new AdvancedCollectionView(sorted, true); Avatars = new AdvancedCollectionView(list, true);
Selected = Avatars.Cast<Avatar>().FirstOrDefault(); Selected = Avatars.Cast<Avatar>().FirstOrDefault();
return true; return true;
} }
@@ -192,13 +192,13 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
Dictionary<FightProperty, GrowCurveType> avatarGrowCurve = avatar.GrowCurves.ToDictionary(g => g.Type, g => g.Value); Dictionary<FightProperty, GrowCurveType> avatarGrowCurve = avatar.GrowCurves.ToDictionary(g => g.Type, g => g.Value);
FightProperty promoteProperty = avatarPromoteMap[0].AddProperties.Last().Type; FightProperty promoteProperty = avatarPromoteMap[0].AddProperties.Last().Type;
List<PropertyCurveValue> propertyCurveValues = new() List<PropertyCurveValue> propertyCurveValues =
{ [
new(FightProperty.FIGHT_PROP_BASE_HP, avatarGrowCurve, avatar.BaseValue), new(FightProperty.FIGHT_PROP_BASE_HP, avatarGrowCurve, avatar.BaseValue),
new(FightProperty.FIGHT_PROP_BASE_ATTACK, avatarGrowCurve, avatar.BaseValue), new(FightProperty.FIGHT_PROP_BASE_ATTACK, avatarGrowCurve, avatar.BaseValue),
new(FightProperty.FIGHT_PROP_BASE_DEFENSE, avatarGrowCurve, avatar.BaseValue), new(FightProperty.FIGHT_PROP_BASE_DEFENSE, avatarGrowCurve, avatar.BaseValue),
new(promoteProperty, avatarGrowCurve, avatar.BaseValue), new(promoteProperty, avatarGrowCurve, avatar.BaseValue),
}; ];
ArgumentNullException.ThrowIfNull(levelAvatarCurveMap); ArgumentNullException.ThrowIfNull(levelAvatarCurveMap);
BaseValueInfo = new(avatar.MaxLevel, propertyCurveValues, levelAvatarCurveMap, avatarPromoteMap); BaseValueInfo = new(avatar.MaxLevel, propertyCurveValues, levelAvatarCurveMap, avatarPromoteMap);

View File

@@ -461,7 +461,7 @@ internal class MiHoYoJSBridge
} }
[SuppressMessage("", "CA2254")] [SuppressMessage("", "CA2254")]
private IJsResult? LogUnhandledMessage([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string message, params object?[] param) private IJsResult? LogUnhandledMessage(string message, params object?[] param)
{ {
logger.LogWarning(message, param); logger.LogWarning(message, param);
return default; return default;
@@ -490,7 +490,7 @@ internal class MiHoYoJSBridge
"pushPage" => await PushPageAsync(param).ConfigureAwait(false), "pushPage" => await PushPageAsync(param).ConfigureAwait(false),
"share" => Share(param), "share" => Share(param),
"showLoading" => null, "showLoading" => null,
_ => LogUnhandledMessage("Unhandled Message Type: {method}", param.Method), _ => LogUnhandledMessage("Unhandled Message Type: {Method}", param.Method),
}; };
} }
catch (ObjectDisposedException) catch (ObjectDisposedException)

View File

@@ -18,7 +18,7 @@ internal sealed partial class Cookie
/// 构造一个空白的Cookie /// 构造一个空白的Cookie
/// </summary> /// </summary>
public Cookie() public Cookie()
: this(new()) : this([])
{ {
} }
@@ -44,7 +44,7 @@ internal sealed partial class Cookie
/// <returns>新的Cookie对象</returns> /// <returns>新的Cookie对象</returns>
public static Cookie Parse(string cookieString) public static Cookie Parse(string cookieString)
{ {
SortedDictionary<string, string> cookieMap = new(); SortedDictionary<string, string> cookieMap = [];
cookieString = cookieString.Replace(" ", string.Empty, StringComparison.Ordinal); cookieString = cookieString.Replace(" ", string.Empty, StringComparison.Ordinal);
string[] values = cookieString.Split(';', StringSplitOptions.RemoveEmptyEntries); string[] values = cookieString.Split(';', StringSplitOptions.RemoveEmptyEntries);
foreach (string[] parts in values.Select(c => c.Split('=', 2))) foreach (string[] parts in values.Select(c => c.Split('=', 2)))
@@ -59,7 +59,7 @@ internal sealed partial class Cookie
public static Cookie FromCoreWebView2Cookies(IReadOnlyList<CoreWebView2Cookie> webView2Cookies) public static Cookie FromCoreWebView2Cookies(IReadOnlyList<CoreWebView2Cookie> webView2Cookies)
{ {
SortedDictionary<string, string> cookieMap = new(); SortedDictionary<string, string> cookieMap = [];
foreach (CoreWebView2Cookie cookie in webView2Cookies) foreach (CoreWebView2Cookie cookie in webView2Cookies)
{ {

View File

@@ -8,7 +8,6 @@ namespace Snap.Hutao.Web.Hoyolab;
internal static class PlayerUidExtension internal static class PlayerUidExtension
{ {
[SuppressMessage("", "SA1010")]
public static string ToQueryString(this in PlayerUid playerUid) public static string ToQueryString(this in PlayerUid playerUid)
{ {
NameValueCollection collection = []; NameValueCollection collection = [];

View File

@@ -53,12 +53,12 @@ internal sealed class AvatarPromotionDelta
return new() return new()
{ {
AvatarLevelTarget = LocalSetting.Get(SettingKeys.CultivationAvatarLevelTarget, 90U), AvatarLevelTarget = LocalSetting.Get(SettingKeys.CultivationAvatarLevelTarget, 90U),
SkillList = new() SkillList =
{ [
new() { LevelTarget = LocalSetting.Get(SettingKeys.CultivationAvatarSkillATarget, 10U), }, new() { LevelTarget = LocalSetting.Get(SettingKeys.CultivationAvatarSkillATarget, 10U), },
new() { LevelTarget = LocalSetting.Get(SettingKeys.CultivationAvatarSkillETarget, 10U), }, new() { LevelTarget = LocalSetting.Get(SettingKeys.CultivationAvatarSkillETarget, 10U), },
new() { LevelTarget = LocalSetting.Get(SettingKeys.CultivationAvatarSkillQTarget, 10U), }, new() { LevelTarget = LocalSetting.Get(SettingKeys.CultivationAvatarSkillQTarget, 10U), },
}, ],
Weapon = new() { LevelTarget = LocalSetting.Get(SettingKeys.CultivationWeapon90LevelTarget, 90U), }, Weapon = new() { LevelTarget = LocalSetting.Get(SettingKeys.CultivationWeapon90LevelTarget, 90U), },
}; };
} }

View File

@@ -57,7 +57,7 @@ internal sealed partial class CalculateClient
int currentPage = 1; int currentPage = 1;
SyncAvatarFilter filter = new() { Uid = userAndUid.Uid.Value, Region = userAndUid.Uid.Region }; SyncAvatarFilter filter = new() { Uid = userAndUid.Uid.Value, Region = userAndUid.Uid.Region };
List<Avatar> avatars = new(); List<Avatar> avatars = [];
Response<ListWrapper<Avatar>>? resp; Response<ListWrapper<Avatar>>? resp;
do do
@@ -166,10 +166,10 @@ internal sealed partial class CalculateClient
private class SyncAvatarFilter private class SyncAvatarFilter
{ {
[JsonPropertyName("element_attr_ids")] [JsonPropertyName("element_attr_ids")]
public List<int>? ElementAttrIds { get; set; } = new(); public List<int>? ElementAttrIds { get; set; } = [];
[JsonPropertyName("weapon_cat_ids")] [JsonPropertyName("weapon_cat_ids")]
public List<int>? WeaponCatIds { get; set; } = new(); public List<int>? WeaponCatIds { get; set; } = [];
[JsonPropertyName("page")] [JsonPropertyName("page")]
public int Page { get; set; } public int Page { get; set; }

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved. // Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license. // Licensed under the MIT license.
using System.Collections.Frozen;
using System.Collections.Immutable; using System.Collections.Immutable;
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote; namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
@@ -17,10 +18,7 @@ internal sealed class Expedition
private const string Keqing = "https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Keqing.png"; private const string Keqing = "https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Keqing.png";
private const string Sara = "https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Sara.png"; private const string Sara = "https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/UI_AvatarIcon_Side_Sara.png";
private static readonly ImmutableList<string> ShortExpeditionTimeAvatars = new List<string>() private static readonly FrozenSet<string> ShortExpeditionTimeAvatars = FrozenSet.ToFrozenSet([Bennett, Chongyun, Fischl, Keqing, Sara]);
{
Bennett, Chongyun, Fischl, Keqing, Sara,
}.ToImmutableList();
/// <summary> /// <summary>
/// 图标 /// 图标

View File

@@ -53,7 +53,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient
{ {
// Replace message // Replace message
resp.Message = SH.WebDailyNoteVerificationFailed; resp.Message = SH.WebDailyNoteVerificationFailed;
IGeetestCardVerifier verifier = serviceProvider.GetRequiredService<HomaGeetestCardVerifier>(); IGeetestCardVerifier verifier = serviceProvider.GetRequiredKeyedService<IGeetestCardVerifier>(GeetestCardVerifierType.Custom);
if (await verifier.TryValidateXrpcChallengeAsync(userAndUid.User, token).ConfigureAwait(false) is { } challenge) if (await verifier.TryValidateXrpcChallengeAsync(userAndUid.User, token).ConfigureAwait(false) is { } challenge)
{ {
@@ -99,7 +99,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient
{ {
// Replace message // Replace message
resp.Message = SH.WebIndexOrSpiralAbyssVerificationFailed; resp.Message = SH.WebIndexOrSpiralAbyssVerificationFailed;
IGeetestCardVerifier verifier = serviceProvider.GetRequiredService<HomaGeetestCardVerifier>(); IGeetestCardVerifier verifier = serviceProvider.GetRequiredKeyedService<IGeetestCardVerifier>(GeetestCardVerifierType.Custom);
if (await verifier.TryValidateXrpcChallengeAsync(userAndUid.User, token).ConfigureAwait(false) is { } challenge) if (await verifier.TryValidateXrpcChallengeAsync(userAndUid.User, token).ConfigureAwait(false) is { } challenge)
{ {
@@ -146,7 +146,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient
{ {
// Replace message // Replace message
resp.Message = SH.WebIndexOrSpiralAbyssVerificationFailed; resp.Message = SH.WebIndexOrSpiralAbyssVerificationFailed;
IGeetestCardVerifier verifier = serviceProvider.GetRequiredService<HomaGeetestCardVerifier>(); IGeetestCardVerifier verifier = serviceProvider.GetRequiredKeyedService<IGeetestCardVerifier>(GeetestCardVerifierType.Custom);
if (await verifier.TryValidateXrpcChallengeAsync(userAndUid.User, token).ConfigureAwait(false) is { } challenge) if (await verifier.TryValidateXrpcChallengeAsync(userAndUid.User, token).ConfigureAwait(false) is { } challenge)
{ {
@@ -156,9 +156,9 @@ internal sealed partial class GameRecordClient : IGameRecordClient
.SetXrpcChallenge(challenge) .SetXrpcChallenge(challenge)
.Get(); .Get();
await builder.SetDynamicSecretAsync(DynamicSecretVersion.Gen2, SaltType.X4, false).ConfigureAwait(false); await verifiedbuilder.SetDynamicSecretAsync(DynamicSecretVersion.Gen2, SaltType.X4, false).ConfigureAwait(false);
resp = await builder resp = await verifiedbuilder
.TryCatchSendAsync<Response<SpiralAbyss.SpiralAbyss>>(httpClient, logger, token) .TryCatchSendAsync<Response<SpiralAbyss.SpiralAbyss>>(httpClient, logger, token)
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@@ -216,7 +216,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient
{ {
// Replace message // Replace message
resp.Message = SH.WebIndexOrSpiralAbyssVerificationFailed; resp.Message = SH.WebIndexOrSpiralAbyssVerificationFailed;
IGeetestCardVerifier verifier = serviceProvider.GetRequiredService<HomaGeetestCardVerifier>(); IGeetestCardVerifier verifier = serviceProvider.GetRequiredKeyedService<IGeetestCardVerifier>(GeetestCardVerifierType.Custom);
if (await verifier.TryValidateXrpcChallengeAsync(userAndUid.User, token).ConfigureAwait(false) is { } challenge) if (await verifier.TryValidateXrpcChallengeAsync(userAndUid.User, token).ConfigureAwait(false) is { } challenge)
{ {

View File

@@ -0,0 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Verification;
internal enum GeetestCardVerifierType
{
Custom,
}

View File

@@ -7,7 +7,7 @@ using Snap.Hutao.Web.Hutao.Geetest;
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Verification; namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Verification;
[ConstructorGenerated] [ConstructorGenerated]
[Injection(InjectAs.Transient)] [Injection(InjectAs.Transient, Key = GeetestCardVerifierType.Custom)]
internal sealed partial class HomaGeetestCardVerifier : IGeetestCardVerifier internal sealed partial class HomaGeetestCardVerifier : IGeetestCardVerifier
{ {
private readonly CardClient cardClient; private readonly CardClient cardClient;

View File

@@ -14,7 +14,6 @@ internal sealed class ReliquarySetsConverter : JsonConverter<ReliquarySets>
private const char Separator = ','; private const char Separator = ',';
/// <inheritdoc/> /// <inheritdoc/>
[SuppressMessage("", "SA1010")]
public override ReliquarySets? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override ReliquarySets? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.GetString() is { } source) if (reader.GetString() is { } source)

View File

@@ -62,7 +62,7 @@ internal class JsonHttpContentSerializer : HttpContentSerializer
return JsonSerializer.Deserialize(json, contentType, JsonSerializerOptions); return JsonSerializer.Deserialize(json, contentType, JsonSerializerOptions);
} }
private HttpContent? SerializeUtf8(object? content, Type contentType) private ByteArrayContent? SerializeUtf8(object? content, Type contentType)
{ {
byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(content, contentType, JsonSerializerOptions); byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(content, contentType, JsonSerializerOptions);
ByteArrayContent httpContent = new(bytes); ByteArrayContent httpContent = new(bytes);
@@ -75,7 +75,7 @@ internal class JsonHttpContentSerializer : HttpContentSerializer
return httpContent; return httpContent;
} }
private HttpContent? SerializeOtherEncoding(object? content, Type contentType, Encoding encoding) private StringContent? SerializeOtherEncoding(object? content, Type contentType, Encoding encoding)
{ {
string str = JsonSerializer.Serialize(content, contentType, JsonSerializerOptions); string str = JsonSerializer.Serialize(content, contentType, JsonSerializerOptions);
return new StringContent(str, encoding, MediaType.ApplicationJson); return new StringContent(str, encoding, MediaType.ApplicationJson);