mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
temp state
This commit is contained in:
BIN
res/HutaoIconSourceTransparentBackgroundGradient1.png
Normal file
BIN
res/HutaoIconSourceTransparentBackgroundGradient1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
@@ -4,50 +4,63 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using System;
|
||||
using Snap.Hutao.SourceGeneration.Primitive;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Snap.Hutao.SourceGeneration.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// 注入HttpClient代码生成器
|
||||
/// 旨在使用源生成器提高注入效率
|
||||
/// 防止在运行时动态查找注入类型
|
||||
/// </summary>
|
||||
[Generator]
|
||||
internal sealed class HttpClientGenerator : ISourceGenerator
|
||||
[Generator(LanguageNames.CSharp)]
|
||||
internal sealed class HttpClientGenerator : IIncrementalGenerator
|
||||
{
|
||||
private const string AttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientAttribute";
|
||||
|
||||
private const string DefaultName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientConfiguration.Default";
|
||||
private const string XRpcName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientConfiguration.XRpc";
|
||||
private const string XRpc2Name = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientConfiguration.XRpc2";
|
||||
private const string XRpc3Name = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientConfiguration.XRpc3";
|
||||
|
||||
private const string PrimaryHttpMessageHandlerAttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.PrimaryHttpMessageHandlerAttribute";
|
||||
private const string DynamicSecretAttributeName = "Snap.Hutao.Web.Hoyolab.DynamicSecret.UseDynamicSecretAttribute";
|
||||
private const string UseDynamicSecretAttributeName = "Snap.Hutao.Web.Hoyolab.DynamicSecret.UseDynamicSecretAttribute";
|
||||
private const string CRLF = "\r\n";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
private static readonly DiagnosticDescriptor invalidConfigurationDescriptor = new("SH100", "无效的 HttpClientConfiguration", "尚未支持生成 {0} 配置", "Quality", DiagnosticSeverity.Error, true);
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
// Register a syntax receiver that will be created for each generation pass
|
||||
context.RegisterForSyntaxNotifications(() => new HttpClientSyntaxContextReceiver());
|
||||
IncrementalValueProvider<ImmutableArray<GeneratorSyntaxContext2>> injectionClasses =
|
||||
context.SyntaxProvider.CreateSyntaxProvider(FilterAttributedClasses, HttpClientClass)
|
||||
.Where(GeneratorSyntaxContext2.NotNull)!
|
||||
.Collect();
|
||||
|
||||
context.RegisterImplementationSourceOutput(injectionClasses, GenerateAddHttpClientsImplementation);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
private static bool FilterAttributedClasses(SyntaxNode node, CancellationToken token)
|
||||
{
|
||||
// retrieve the populated receiver
|
||||
if (context.SyntaxContextReceiver is not HttpClientSyntaxContextReceiver receiver)
|
||||
return node is ClassDeclarationSyntax classDeclarationSyntax && classDeclarationSyntax.AttributeLists.Count > 0;
|
||||
}
|
||||
|
||||
private static GeneratorSyntaxContext2 HttpClientClass(GeneratorSyntaxContext context, CancellationToken token)
|
||||
{
|
||||
if (context.SemanticModel.GetDeclaredSymbol(context.Node, token) is INamedTypeSymbol classSymbol)
|
||||
{
|
||||
return;
|
||||
ImmutableArray<AttributeData> attributes = classSymbol.GetAttributes();
|
||||
if (attributes.Any(data => data.AttributeClass!.ToDisplayString() == AttributeName))
|
||||
{
|
||||
return new(context, classSymbol, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder sourceCodeBuilder = new();
|
||||
return default;
|
||||
}
|
||||
|
||||
sourceCodeBuilder.Append($$"""
|
||||
private static void GenerateAddHttpClientsImplementation(SourceProductionContext context, ImmutableArray<GeneratorSyntaxContext2> context2s)
|
||||
{
|
||||
StringBuilder sourceBuilder = new StringBuilder().Append($$"""
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
@@ -64,45 +77,40 @@ internal sealed class HttpClientGenerator : ISourceGenerator
|
||||
{
|
||||
""");
|
||||
|
||||
FillWithHttpClients(receiver, sourceCodeBuilder);
|
||||
FillUpWithAddHttpClient(sourceBuilder, context, context2s);
|
||||
|
||||
sourceCodeBuilder.Append("""
|
||||
sourceBuilder.Append("""
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
context.AddSource("IocHttpClientConfiguration.g.cs", SourceText.From(sourceCodeBuilder.ToString(), Encoding.UTF8));
|
||||
context.AddSource("IocHttpClientConfiguration.g.cs", sourceBuilder.ToString());
|
||||
}
|
||||
|
||||
private static void FillWithHttpClients(HttpClientSyntaxContextReceiver receiver, StringBuilder sourceCodeBuilder)
|
||||
private static void FillUpWithAddHttpClient(StringBuilder sourceBuilder, SourceProductionContext production, ImmutableArray<GeneratorSyntaxContext2> contexts)
|
||||
{
|
||||
List<string> lines = new();
|
||||
StringBuilder lineBuilder = new();
|
||||
|
||||
foreach (INamedTypeSymbol classSymbol in receiver.Classes)
|
||||
foreach (GeneratorSyntaxContext2 context in contexts)
|
||||
{
|
||||
lineBuilder.Clear().Append("\r\n");
|
||||
lineBuilder.Clear().Append(CRLF);
|
||||
lineBuilder.Append(@" services.AddHttpClient<");
|
||||
|
||||
AttributeData httpClientInfo = classSymbol
|
||||
.GetAttributes()
|
||||
.Single(attr => attr.AttributeClass!.ToDisplayString() == HttpClientSyntaxContextReceiver.AttributeName);
|
||||
ImmutableArray<TypedConstant> arguments = httpClientInfo.ConstructorArguments;
|
||||
AttributeData httpClientData = context.SingleAttributeWithName(AttributeName);
|
||||
ImmutableArray<TypedConstant> arguments = httpClientData.ConstructorArguments;
|
||||
|
||||
if (arguments.Length == 2)
|
||||
{
|
||||
TypedConstant interfaceType = arguments[1];
|
||||
lineBuilder.Append($"{interfaceType.Value}, ");
|
||||
lineBuilder.Append($"{arguments[1].Value}, ");
|
||||
}
|
||||
|
||||
TypedConstant configuration = arguments[0];
|
||||
lineBuilder.Append($"{context.Symbol.ToDisplayString()}>(");
|
||||
|
||||
lineBuilder.Append($"{classSymbol.ToDisplayString()}>(");
|
||||
|
||||
string injectAsName = configuration.ToCSharpString();
|
||||
switch (injectAsName)
|
||||
string configurationName = arguments[0].ToCSharpString();
|
||||
switch (configurationName)
|
||||
{
|
||||
case DefaultName:
|
||||
lineBuilder.Append("DefaultConfiguration)");
|
||||
@@ -117,16 +125,13 @@ internal sealed class HttpClientGenerator : ISourceGenerator
|
||||
lineBuilder.Append("XRpc3Configuration)");
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"非法的 HttpClientConfiguration 值: [{injectAsName}]");
|
||||
production.ReportDiagnostic(Diagnostic.Create(invalidConfigurationDescriptor, null, configurationName));
|
||||
break;
|
||||
}
|
||||
|
||||
AttributeData? handlerInfo = classSymbol
|
||||
.GetAttributes()
|
||||
.SingleOrDefault(attr => attr.AttributeClass!.ToDisplayString() == PrimaryHttpMessageHandlerAttributeName);
|
||||
|
||||
if (handlerInfo != null)
|
||||
if (context.SingleOrDefaultAttributeWithName(PrimaryHttpMessageHandlerAttributeName) is AttributeData handlerData)
|
||||
{
|
||||
ImmutableArray<KeyValuePair<string, TypedConstant>> properties = handlerInfo.NamedArguments;
|
||||
ImmutableArray<KeyValuePair<string, TypedConstant>> properties = handlerData.NamedArguments;
|
||||
lineBuilder.Append(@".ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() {");
|
||||
|
||||
foreach (KeyValuePair<string, TypedConstant> property in properties)
|
||||
@@ -141,7 +146,7 @@ internal sealed class HttpClientGenerator : ISourceGenerator
|
||||
lineBuilder.Append(" })");
|
||||
}
|
||||
|
||||
if (classSymbol.GetAttributes().Any(attr => attr.AttributeClass!.ToDisplayString() == DynamicSecretAttributeName))
|
||||
if (context.HasAttributeWithName(UseDynamicSecretAttributeName))
|
||||
{
|
||||
lineBuilder.Append(".AddHttpMessageHandler<DynamicSecretHandler>()");
|
||||
}
|
||||
@@ -153,37 +158,7 @@ internal sealed class HttpClientGenerator : ISourceGenerator
|
||||
|
||||
foreach (string line in lines.OrderBy(x => x))
|
||||
{
|
||||
sourceCodeBuilder.Append(line);
|
||||
}
|
||||
}
|
||||
|
||||
private class HttpClientSyntaxContextReceiver : ISyntaxContextReceiver
|
||||
{
|
||||
/// <summary>
|
||||
/// 注入特性的名称
|
||||
/// </summary>
|
||||
public const string AttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientAttribute";
|
||||
|
||||
/// <summary>
|
||||
/// 所有需要注入的类型符号
|
||||
/// </summary>
|
||||
public List<INamedTypeSymbol> Classes { get; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
|
||||
{
|
||||
// any class with at least one attribute is a candidate for injection generation
|
||||
if (context.Node is ClassDeclarationSyntax classDeclarationSyntax && classDeclarationSyntax.AttributeLists.Count > 0)
|
||||
{
|
||||
// get as named type symbol
|
||||
if (context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax) is INamedTypeSymbol classSymbol)
|
||||
{
|
||||
if (classSymbol.GetAttributes().Any(ad => ad.AttributeClass!.ToDisplayString() == AttributeName))
|
||||
{
|
||||
Classes.Add(classSymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceBuilder.Append(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,46 +4,58 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using System;
|
||||
using Snap.Hutao.SourceGeneration.Primitive;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Snap.Hutao.SourceGeneration.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// 注入代码生成器
|
||||
/// 旨在使用源生成器提高注入效率
|
||||
/// 防止在运行时动态查找注入类型
|
||||
/// </summary>
|
||||
[Generator]
|
||||
internal sealed class InjectionGenerator : ISourceGenerator
|
||||
[Generator(LanguageNames.CSharp)]
|
||||
internal sealed class InjectionGenerator : IIncrementalGenerator
|
||||
{
|
||||
private const string AttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectionAttribute";
|
||||
private const string InjectAsSingletonName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Singleton";
|
||||
private const string InjectAsTransientName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Transient";
|
||||
private const string InjectAsScopedName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Scoped";
|
||||
private const string CRLF = "\r\n";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
private static readonly DiagnosticDescriptor invalidInjectionDescriptor = new("SH101", "无效的 InjectAs 枚举值", "尚未支持生成 {0} 配置", "Quality", DiagnosticSeverity.Error, true);
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
// Register a syntax receiver that will be created for each generation pass
|
||||
context.RegisterForSyntaxNotifications(() => new InjectionSyntaxContextReceiver());
|
||||
IncrementalValueProvider<ImmutableArray<GeneratorSyntaxContext2>> injectionClasses =
|
||||
context.SyntaxProvider.CreateSyntaxProvider(FilterAttributedClasses, HttpClientClass)
|
||||
.Where(GeneratorSyntaxContext2.NotNull)!
|
||||
.Collect();
|
||||
|
||||
context.RegisterImplementationSourceOutput(injectionClasses, GenerateAddInjectionsImplementation);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
private static bool FilterAttributedClasses(SyntaxNode node, CancellationToken token)
|
||||
{
|
||||
// retrieve the populated receiver
|
||||
if (context.SyntaxContextReceiver is not InjectionSyntaxContextReceiver receiver)
|
||||
return node is ClassDeclarationSyntax classDeclarationSyntax && classDeclarationSyntax.AttributeLists.Count > 0;
|
||||
}
|
||||
|
||||
private static GeneratorSyntaxContext2 HttpClientClass(GeneratorSyntaxContext context, CancellationToken token)
|
||||
{
|
||||
if (context.SemanticModel.GetDeclaredSymbol(context.Node, token) is INamedTypeSymbol classSymbol)
|
||||
{
|
||||
return;
|
||||
ImmutableArray<AttributeData> attributes = classSymbol.GetAttributes();
|
||||
if (attributes.Any(data => data.AttributeClass!.ToDisplayString() == AttributeName))
|
||||
{
|
||||
return new(context, classSymbol, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder sourceCodeBuilder = new();
|
||||
sourceCodeBuilder.Append($$"""
|
||||
return default;
|
||||
}
|
||||
|
||||
private static void GenerateAddInjectionsImplementation(SourceProductionContext context, ImmutableArray<GeneratorSyntaxContext2> context2s)
|
||||
{
|
||||
StringBuilder sourceBuilder = new StringBuilder().Append($$"""
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
@@ -57,36 +69,30 @@ internal sealed class InjectionGenerator : ISourceGenerator
|
||||
{
|
||||
""");
|
||||
|
||||
FillWithInjectionServices(receiver, sourceCodeBuilder);
|
||||
sourceCodeBuilder.Append("""
|
||||
FillUpWithAddServices(sourceBuilder, context, context2s);
|
||||
sourceBuilder.Append("""
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
context.AddSource("ServiceCollectionExtension.g.cs", SourceText.From(sourceCodeBuilder.ToString(), Encoding.UTF8));
|
||||
context.AddSource("ServiceCollectionExtension.g.cs", sourceBuilder.ToString());
|
||||
}
|
||||
|
||||
private static void FillWithInjectionServices(InjectionSyntaxContextReceiver receiver, StringBuilder sourceCodeBuilder)
|
||||
private static void FillUpWithAddServices(StringBuilder sourceBuilder, SourceProductionContext production, ImmutableArray<GeneratorSyntaxContext2> contexts)
|
||||
{
|
||||
List<string> lines = new();
|
||||
StringBuilder lineBuilder = new();
|
||||
|
||||
foreach (INamedTypeSymbol classSymbol in receiver.Classes)
|
||||
foreach (GeneratorSyntaxContext2 context in contexts)
|
||||
{
|
||||
AttributeData injectionInfo = classSymbol
|
||||
.GetAttributes()
|
||||
.Single(attr => attr.AttributeClass!.ToDisplayString() == InjectionSyntaxContextReceiver.AttributeName);
|
||||
|
||||
lineBuilder
|
||||
.Clear()
|
||||
.Append(CRLF);
|
||||
lineBuilder.Clear().Append(CRLF);
|
||||
|
||||
AttributeData injectionInfo = context.SingleAttributeWithName(AttributeName);
|
||||
ImmutableArray<TypedConstant> arguments = injectionInfo.ConstructorArguments;
|
||||
TypedConstant injectAs = arguments[0];
|
||||
|
||||
string injectAsName = injectAs.ToCSharpString();
|
||||
string injectAsName = arguments[0].ToCSharpString();
|
||||
switch (injectAsName)
|
||||
{
|
||||
case InjectAsSingletonName:
|
||||
@@ -99,53 +105,23 @@ internal sealed class InjectionGenerator : ISourceGenerator
|
||||
lineBuilder.Append(@" services.AddScoped<");
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"非法的 InjectAs 值: [{injectAsName}]");
|
||||
production.ReportDiagnostic(Diagnostic.Create(invalidInjectionDescriptor, null, injectAsName));
|
||||
break;
|
||||
}
|
||||
|
||||
if (arguments.Length == 2)
|
||||
{
|
||||
TypedConstant interfaceType = arguments[1];
|
||||
lineBuilder.Append($"{interfaceType.Value}, ");
|
||||
lineBuilder.Append($"{arguments[1].Value}, ");
|
||||
}
|
||||
|
||||
lineBuilder.Append($"{classSymbol.ToDisplayString()}>();");
|
||||
lineBuilder.Append($"{context.Symbol.ToDisplayString()}>();");
|
||||
|
||||
lines.Add(lineBuilder.ToString());
|
||||
}
|
||||
|
||||
foreach (string line in lines.OrderBy(x => x))
|
||||
{
|
||||
sourceCodeBuilder.Append(line);
|
||||
}
|
||||
}
|
||||
|
||||
private class InjectionSyntaxContextReceiver : ISyntaxContextReceiver
|
||||
{
|
||||
/// <summary>
|
||||
/// 注入特性的名称
|
||||
/// </summary>
|
||||
public const string AttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectionAttribute";
|
||||
|
||||
/// <summary>
|
||||
/// 所有需要注入的类型符号
|
||||
/// </summary>
|
||||
public List<INamedTypeSymbol> Classes { get; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
|
||||
{
|
||||
// any class with at least one attribute is a candidate for injection generation
|
||||
if (context.Node is ClassDeclarationSyntax classDeclarationSyntax && classDeclarationSyntax.AttributeLists.Count > 0)
|
||||
{
|
||||
// get as named type symbol
|
||||
if (context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax) is INamedTypeSymbol classSymbol)
|
||||
{
|
||||
if (classSymbol.GetAttributes().Any(ad => ad.AttributeClass!.ToDisplayString() == AttributeName))
|
||||
{
|
||||
Classes.Add(classSymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceBuilder.Append(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,4 +5,4 @@
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("MicrosoftCodeAnalysisReleaseTracking", "RS2008:启用分析器发布跟踪", Justification = "<挂起>", Scope = "member", Target = "~F:Snap.Hutao.SourceGeneration.TypeInternalAnalyzer.typeInternalDescriptor")]
|
||||
[assembly: SuppressMessage("", "RS2008")]
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Snap.Hutao.SourceGeneration.Primitive;
|
||||
|
||||
internal readonly struct GeneratorSyntaxContext2
|
||||
{
|
||||
public readonly GeneratorSyntaxContext Context;
|
||||
public readonly INamedTypeSymbol Symbol;
|
||||
public readonly ImmutableArray<AttributeData> Attributes;
|
||||
public readonly bool HasValue = false;
|
||||
|
||||
public GeneratorSyntaxContext2(GeneratorSyntaxContext context, INamedTypeSymbol symbol, ImmutableArray<AttributeData> attributes)
|
||||
{
|
||||
Context = context;
|
||||
Symbol = symbol;
|
||||
Attributes = attributes;
|
||||
HasValue = true;
|
||||
}
|
||||
|
||||
public static bool NotNull(GeneratorSyntaxContext2 context)
|
||||
{
|
||||
return context.HasValue;
|
||||
}
|
||||
|
||||
public bool HasAttributeWithName(string name)
|
||||
{
|
||||
return Attributes.Any(attr => attr.AttributeClass!.ToDisplayString() == name);
|
||||
}
|
||||
|
||||
public AttributeData SingleAttributeWithName(string name)
|
||||
{
|
||||
return Attributes.Single(attribute => attribute.AttributeClass!.ToDisplayString() == name);
|
||||
}
|
||||
|
||||
public AttributeData? SingleOrDefaultAttributeWithName(string name)
|
||||
{
|
||||
return Attributes.SingleOrDefault(attribute => attribute.AttributeClass!.ToDisplayString() == name);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
@@ -9,29 +8,32 @@ using System.Text;
|
||||
|
||||
namespace Snap.Hutao.SourceGeneration;
|
||||
|
||||
[Generator]
|
||||
internal sealed class ReliquaryWeightConfigurationGenerator : ISourceGenerator
|
||||
[Generator(LanguageNames.CSharp)]
|
||||
internal sealed class ReliquaryWeightConfigurationGenerator : IIncrementalGenerator
|
||||
{
|
||||
private const string ReliquaryWeightConfigurationFileName = "ReliquaryWeightConfiguration.json";
|
||||
private const string FileName = "ReliquaryWeightConfiguration.json";
|
||||
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
IncrementalValueProvider<ImmutableArray<AdditionalText>> provider = context.AdditionalTextsProvider.Where(MatchFileName).Collect();
|
||||
|
||||
context.RegisterSourceOutput(provider, GenerateReliquaryWeightConfiguration);
|
||||
}
|
||||
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
private static bool MatchFileName(AdditionalText text)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Path.GetFileName(text.Path) == FileName;
|
||||
}
|
||||
|
||||
AdditionalText configurationJsonFile = context.AdditionalFiles
|
||||
.First(af => Path.GetFileName(af.Path) == ReliquaryWeightConfigurationFileName);
|
||||
private static void GenerateReliquaryWeightConfiguration(SourceProductionContext context,ImmutableArray<AdditionalText> additionalTexts)
|
||||
{
|
||||
AdditionalText jsonFile = additionalTexts.Single();
|
||||
|
||||
string configurationJson = configurationJsonFile.GetText(context.CancellationToken)!.ToString();
|
||||
string configurationJson = jsonFile.GetText(context.CancellationToken)!.ToString();
|
||||
Dictionary<string, ReliquaryWeightConfigurationMetadata> metadataMap =
|
||||
JsonParser.FromJson<Dictionary<string, ReliquaryWeightConfigurationMetadata>>(configurationJson)!;
|
||||
|
||||
StringBuilder sourceCodeBuilder = new();
|
||||
sourceCodeBuilder.Append($$"""
|
||||
StringBuilder sourceBuilder = new StringBuilder().Append($$"""
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
@@ -59,24 +61,18 @@ internal sealed class ReliquaryWeightConfigurationGenerator : ISourceGenerator
|
||||
|
||||
foreach (KeyValuePair<string, ReliquaryWeightConfigurationMetadata> kvp in metadataMap.OrderBy(kvp => kvp.Key))
|
||||
{
|
||||
AppendAffixWeight(sourceCodeBuilder, kvp.Key, kvp.Value);
|
||||
AppendAffixWeight(sourceBuilder, kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
sourceCodeBuilder.Append($$"""
|
||||
sourceBuilder.Append($$"""
|
||||
};
|
||||
}
|
||||
""");
|
||||
|
||||
context.AddSource("ReliquaryWeightConfiguration.g.cs", SourceText.From(sourceCodeBuilder.ToString(), Encoding.UTF8));
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.AddSource("ReliquaryWeightConfiguration.g.cs", ex.ToString());
|
||||
}
|
||||
context.AddSource("ReliquaryWeightConfiguration.g.cs", sourceBuilder.ToString());
|
||||
}
|
||||
|
||||
private void AppendAffixWeight(StringBuilder builder, string id, ReliquaryWeightConfigurationMetadata metadata)
|
||||
private static void AppendAffixWeight(StringBuilder builder, string id, ReliquaryWeightConfigurationMetadata metadata)
|
||||
{
|
||||
StringBuilder lineBuilder = new StringBuilder()
|
||||
.Append(" new AffixWeight(").Append(id).Append(',')
|
||||
@@ -97,7 +93,6 @@ internal sealed class ReliquaryWeightConfigurationGenerator : ISourceGenerator
|
||||
|
||||
lineBuilder.Append(',');
|
||||
|
||||
|
||||
builder.AppendLine(lineBuilder.ToString());
|
||||
}
|
||||
|
||||
|
||||
@@ -61,4 +61,9 @@ internal static class SettingKeys
|
||||
/// 静态资源合约V4 刷新 AvatarIcon
|
||||
/// </summary>
|
||||
public const string StaticResourceV4Contract = "StaticResourceV4Contract";
|
||||
|
||||
/// <summary>
|
||||
/// 静态资源合约V5 刷新 AvatarIcon
|
||||
/// </summary>
|
||||
public const string StaticResourceV5Contract = "StaticResourceV5Contract";
|
||||
}
|
||||
@@ -53,5 +53,6 @@ internal static class StaticResource
|
||||
LocalSetting.Set(SettingKeys.StaticResourceV2Contract, state);
|
||||
LocalSetting.Set(SettingKeys.StaticResourceV3Contract, state);
|
||||
LocalSetting.Set(SettingKeys.StaticResourceV4Contract, state);
|
||||
LocalSetting.Set(SettingKeys.StaticResourceV5Contract, state);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
@@ -1575,9 +1575,9 @@ namespace Snap.Hutao.Resource.Localization {
|
||||
/// <summary>
|
||||
/// 查找类似 保底 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ViewControlStatisticsCardGuarenteeText {
|
||||
internal static string ViewControlStatisticsCardGuaranteeText {
|
||||
get {
|
||||
return ResourceManager.GetString("ViewControlStatisticsCardGuarenteeText", resourceCulture);
|
||||
return ResourceManager.GetString("ViewControlStatisticsCardGuaranteeText", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3516,6 +3516,15 @@ namespace Snap.Hutao.Resource.Localization {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 设置 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string ViewPageHomeLaunchGameSettingAction {
|
||||
get {
|
||||
return ResourceManager.GetString("ViewPageHomeLaunchGameSettingAction", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 详情 的本地化字符串。
|
||||
/// </summary>
|
||||
|
||||
@@ -609,7 +609,7 @@
|
||||
<data name="ViewControlStatisticsCardBlueText" xml:space="preserve">
|
||||
<value>三星</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardGuarenteeText" xml:space="preserve">
|
||||
<data name="ViewControlStatisticsCardGuaranteeText" xml:space="preserve">
|
||||
<value>保底</value>
|
||||
</data>
|
||||
<data name="ViewControlStatisticsCardOrangeAveragePullText" xml:space="preserve">
|
||||
@@ -1959,4 +1959,7 @@
|
||||
<data name="ViewPageWiKiWeaponAfterAscensionTitle" xml:space="preserve">
|
||||
<value>突破后</value>
|
||||
</data>
|
||||
<data name="ViewPageHomeLaunchGameSettingAction" xml:space="preserve">
|
||||
<value>设置</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -142,9 +142,9 @@ internal sealed class TypedWishSummaryBuilder
|
||||
MaxOrangePull = maxOrangePullTracker,
|
||||
MinOrangePull = minOrangePullTracker,
|
||||
LastOrangePull = lastOrangePullTracker,
|
||||
GuarenteeOrangeThreshold = guarenteeOrangeThreshold,
|
||||
GuaranteeOrangeThreshold = guarenteeOrangeThreshold,
|
||||
LastPurplePull = lastPurplePullTracker,
|
||||
GuarenteePurpleThreshold = guarenteePurpleThreshold,
|
||||
GuaranteePurpleThreshold = guarenteePurpleThreshold,
|
||||
TotalOrangePull = totalOrangePullTracker,
|
||||
TotalPurplePull = totalPurplePullTracker,
|
||||
TotalBluePull = totalBluePullTracker,
|
||||
|
||||
@@ -407,4 +407,41 @@ internal sealed class GachaLogService : IGachaLogService
|
||||
appDbContext.GachaItems.AddRangeAndSave(itemsToAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 祈愿记录导出服务
|
||||
/// </summary>
|
||||
internal sealed class GachaLogExportService
|
||||
{
|
||||
AppDbContext appDbContext;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<UIGF> ExportToUIGFAsync(GachaArchive archive)
|
||||
{
|
||||
List<UIGFItem> list = appDbContext.GachaItems
|
||||
.Where(i => i.ArchiveId == archive.InnerId)
|
||||
.AsEnumerable()
|
||||
.Select(i => i.ToUIGFItem(GetNameQualityByItemId(i.ItemId)))
|
||||
.ToList();
|
||||
|
||||
UIGF uigf = new()
|
||||
{
|
||||
Info = UIGFInfo.Create(archive.Uid),
|
||||
List = list,
|
||||
};
|
||||
|
||||
return Task.FromResult(uigf);
|
||||
}
|
||||
|
||||
private INameQuality GetNameQualityByItemId(int id)
|
||||
{
|
||||
int place = id.Place();
|
||||
return place switch
|
||||
{
|
||||
8 => idAvatarMap![id],
|
||||
5 => idWeaponMap![id],
|
||||
_ => throw Must.NeverHappen($"Id places: {place}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,6 +289,7 @@ internal sealed class GameService : IGameService
|
||||
string gamePath = appOptions.GamePath;
|
||||
if (string.IsNullOrWhiteSpace(gamePath))
|
||||
{
|
||||
// TODO: throw exception
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
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++)
|
||||
{
|
||||
@@ -148,7 +149,13 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
public NameValue<int> Monitor
|
||||
{
|
||||
get => GetOption(ref monitor, SettingEntry.LaunchMonitor, index => Monitors[int.Parse(index) - 1], Monitors[0]);
|
||||
set => SetOption(ref monitor, SettingEntry.LaunchMonitor, value, selected => selected?.Value.ToString() ?? "1");
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
SetOption(ref monitor, SettingEntry.LaunchMonitor, value, selected => selected.Value.ToString() ?? "1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
<None Remove="ReliquaryWeightConfiguration.json" />
|
||||
<None Remove="Resource\Font\CascadiaMono.ttf" />
|
||||
<None Remove="Resource\Font\MiSans-Regular.ttf" />
|
||||
<None Remove="Resource\HutaoIconSourceTransparentBackgroundGradient1.png" />
|
||||
<None Remove="Resource\Icon\UI_AchievementIcon_3_3.png" />
|
||||
<None Remove="Resource\Icon\UI_BagTabIcon_Avatar.png" />
|
||||
<None Remove="Resource\Icon\UI_BagTabIcon_Weapon.png" />
|
||||
@@ -199,6 +200,7 @@
|
||||
<ItemGroup>
|
||||
<Content Include="Resource\Font\CascadiaMono.ttf" />
|
||||
<Content Include="Resource\Font\MiSans-Regular.ttf" />
|
||||
<Content Include="Resource\HutaoIconSourceTransparentBackgroundGradient1.png" />
|
||||
<Content Include="Resource\Icon\UI_AchievementIcon_3_3.png" />
|
||||
<Content Include="Resource\Icon\UI_BagTabIcon_Avatar.png" />
|
||||
<Content Include="Resource\Icon\UI_BagTabIcon_Weapon.png" />
|
||||
@@ -263,7 +265,7 @@
|
||||
|
||||
<!-- Projects -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Snap.Hutao.SourceGeneration\Snap.Hutao.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" SetTargetFramework="TargetFramework=netstandard2.0"/>
|
||||
<ProjectReference Include="..\Snap.Hutao.SourceGeneration\Snap.Hutao.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" SetTargetFramework="TargetFramework=netstandard2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
Margin="0,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="#FF0063FF"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardGuarenteeText}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardGuaranteeText}"
|
||||
Visibility="{Binding IsGuarantee, Converter={StaticResource BoolToVisibilityConverter}}"/>
|
||||
<TextBlock
|
||||
Margin="0,0,8,0"
|
||||
@@ -180,7 +180,7 @@
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
Foreground="{StaticResource OrangeBrush}"
|
||||
IsIndeterminate="False"
|
||||
Maximum="{Binding GuarenteeOrangeThreshold}"
|
||||
Maximum="{Binding GuaranteeOrangeThreshold}"
|
||||
Value="{Binding LastOrangePull}"/>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
@@ -212,7 +212,7 @@
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
Foreground="{StaticResource PurpleBrush}"
|
||||
IsIndeterminate="False"
|
||||
Maximum="{Binding GuarenteePurpleThreshold}"
|
||||
Maximum="{Binding GuaranteePurpleThreshold}"
|
||||
Value="{Binding LastPurplePull}"/>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
x:Class="Snap.Hutao.View.Page.AnnouncementPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:clw="using:CommunityToolkit.Labs.WinUI"
|
||||
xmlns:cwu="using:CommunityToolkit.WinUI.UI"
|
||||
xmlns:cwua="using:CommunityToolkit.WinUI.UI.Animations"
|
||||
xmlns:cwub="using:CommunityToolkit.WinUI.UI.Behaviors"
|
||||
@@ -154,13 +155,178 @@
|
||||
<Grid>
|
||||
<ScrollViewer Padding="0,0,4,0">
|
||||
<StackPanel>
|
||||
<ItemsControl>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<InfoBar/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
Margin="16,16,16,0"
|
||||
Style="{StaticResource TitleTextBlockStyle}"
|
||||
Text="Greeting Text"/>
|
||||
<TextBlock Margin="16,0,16,0" Text="账号邮箱@xx.com"/>
|
||||
<cwucont:AdaptiveGridView
|
||||
Margin="16,16,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
cwua:ItemsReorderAnimation.Duration="0:0:0.1"
|
||||
DesiredWidth="300"
|
||||
ItemContainerStyle="{StaticResource LargeGridViewItemStyle}"
|
||||
SelectionMode="None">
|
||||
<!-- 启动游戏 -->
|
||||
<Button
|
||||
Height="120"
|
||||
Padding="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
Command="{Binding LaunchGameViewModelSlim.LaunchCommand}"
|
||||
Style="{StaticResource AccentButtonStyle}">
|
||||
<mxi:Interaction.Behaviors>
|
||||
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding LaunchGameViewModelSlim.OpenUICommand}"/>
|
||||
</mxi:Interaction.Behaviors>
|
||||
|
||||
<Grid CornerRadius="{ThemeResource ControlCornerRadius}">
|
||||
<Image
|
||||
Margin="0,40,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
Source="ms-appx:///Resource/HutaoIconSourceTransparentBackgroundGradient1.png"
|
||||
Stretch="Uniform"/>
|
||||
<Grid Margin="12" ColumnSpacing="16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<FontIcon
|
||||
Grid.Row="0"
|
||||
VerticalAlignment="Center"
|
||||
Glyph=""/>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Text="{shcm:ResourceString Name=ViewLaunchGameHeader}"/>
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
MinHeight="37.2"
|
||||
Background="Transparent"
|
||||
BorderBrush="{x:Null}"
|
||||
BorderThickness="0"
|
||||
Command="{Binding LaunchGameViewModelSlim.NavigateCommand}"
|
||||
Content=""
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
Foreground="{ThemeResource AccentButtonForeground}"
|
||||
ToolTipService.ToolTip="{shcm:ResourceString Name=ViewPageHomeLaunchGameSettingAction}"/>
|
||||
<ComboBox
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="3"
|
||||
VerticalAlignment="Bottom"
|
||||
DisplayMemberPath="Name"
|
||||
ItemsSource="{Binding LaunchGameViewModelSlim.GameAccounts}"
|
||||
PlaceholderText="选择账号或直接启动"
|
||||
SelectedItem="{Binding LaunchGameViewModelSlim.SelectedGameAccount, Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Button>
|
||||
|
||||
<Border Style="{StaticResource BorderCardStyle}">
|
||||
<FlipView Background="{x:Null}">
|
||||
<Grid Margin="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="卡池名称 1"/>
|
||||
|
||||
<shvc:ItemIcon
|
||||
Width="40"
|
||||
Height="40"
|
||||
Margin="0,12,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Icon="{Binding Icon}"
|
||||
Quality="QUALITY_ORANGE"/>
|
||||
|
||||
<TextBlock Margin="0,6,0,0" Text="XX 抽"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="卡池名称 1"/>
|
||||
|
||||
<shvc:ItemIcon
|
||||
Width="40"
|
||||
Height="40"
|
||||
Margin="0,12,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Icon="{Binding Icon}"
|
||||
Quality="QUALITY_ORANGE"/>
|
||||
|
||||
<TextBlock Margin="0,6,0,0" Text="XX 抽"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid Margin="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="卡池名称 1"/>
|
||||
|
||||
<shvc:ItemIcon
|
||||
Width="40"
|
||||
Height="40"
|
||||
Margin="0,12,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Icon="{Binding Icon}"
|
||||
Quality="QUALITY_ORANGE"/>
|
||||
|
||||
<TextBlock Margin="0,6,0,0" Text="XX 抽"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="卡池名称 1"/>
|
||||
|
||||
<shvc:ItemIcon
|
||||
Width="40"
|
||||
Height="40"
|
||||
Margin="0,12,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Icon="{Binding Icon}"
|
||||
Quality="QUALITY_ORANGE"/>
|
||||
|
||||
<TextBlock Margin="0,6,0,0" Text="XX 抽"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</FlipView>
|
||||
</Border>
|
||||
|
||||
<Border Style="{StaticResource BorderCardStyle}">
|
||||
<FlipView Background="{x:Null}">
|
||||
<Grid Margin="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0">
|
||||
<TextBlock Style="{StaticResource TitleTextBlockStyle}" Text="100/800"/>
|
||||
<TextBlock Text="12.5%"/>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Row="1" Text="Archive Name"/>
|
||||
</Grid>
|
||||
</FlipView>
|
||||
</Border>
|
||||
<Border Style="{StaticResource BorderCardStyle}">
|
||||
<TextBlock Text="实时便笺"/>
|
||||
</Border>
|
||||
<Border Style="{StaticResource BorderCardStyle}">
|
||||
<TextBlock Text="养成计划"/>
|
||||
</Border>
|
||||
<Border Style="{StaticResource BorderCardStyle}">
|
||||
<TextBlock Text="深渊"/>
|
||||
</Border>
|
||||
</cwucont:AdaptiveGridView>
|
||||
</StackPanel>
|
||||
|
||||
<Pivot Style="{StaticResource DefaultPivotStyle}">
|
||||
<PivotItem
|
||||
Content="{Binding Announcement.List[0]}"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:clw="using:CommunityToolkit.Labs.WinUI"
|
||||
xmlns:cwua="using:CommunityToolkit.WinUI.UI.Animations"
|
||||
xmlns:cwuc="using:CommunityToolkit.WinUI.UI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
@@ -122,32 +123,39 @@
|
||||
</CommandBar>
|
||||
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<ItemsControl Margin="0,0,0,16" ItemsSource="{Binding DailyNoteEntries}">
|
||||
<ItemsControl.ItemContainerTransitions>
|
||||
<AddDeleteThemeTransition/>
|
||||
<ContentThemeTransition/>
|
||||
<EntranceThemeTransition IsStaggeringEnabled="False"/>
|
||||
</ItemsControl.ItemContainerTransitions>
|
||||
<cwuc:AdaptiveGridView
|
||||
Margin="16,16,4,-4"
|
||||
cwua:ItemsReorderAnimation.Duration="0:0:0.1"
|
||||
DesiredWidth="280"
|
||||
ItemContainerStyle="{StaticResource LargeGridViewItemStyle}"
|
||||
ItemsSource="{Binding DailyNoteEntries}"
|
||||
SelectionMode="None">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Margin="16,16,16,0"
|
||||
Padding="8"
|
||||
Style="{StaticResource BorderCardStyle}">
|
||||
<Grid>
|
||||
<Border Padding="8" Style="{StaticResource BorderCardStyle}">
|
||||
<Grid Background="Transparent">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid MinHeight="40" HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="4,0,0,4"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BaseTextBlockStyle}"
|
||||
Text="{Binding UserGameRole}"/>
|
||||
Text="{Binding UserGameRole}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
TextWrapping="NoWrap"/>
|
||||
<StackPanel
|
||||
x:Name="ButtonPanel"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal"
|
||||
Visibility="Collapsed">
|
||||
@@ -179,214 +187,159 @@
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<cwuc:UniformGrid
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="0,8,0,0"
|
||||
ColumnSpacing="8"
|
||||
Columns="5">
|
||||
Spacing="12">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Width="40"
|
||||
Height="40"
|
||||
VerticalAlignment="Center">
|
||||
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_ItemIcon_210_256.png"/>
|
||||
<ProgressRing
|
||||
Width="40"
|
||||
Height="40">
|
||||
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_ItemIcon_210_256.png"/>
|
||||
<ProgressRing
|
||||
Width="40"
|
||||
Height="40"
|
||||
Background="{StaticResource CardBackgroundFillColorDefaultBrush}"
|
||||
IsIndeterminate="False"
|
||||
Maximum="{Binding DailyNote.MaxResin, Mode=OneWay}"
|
||||
Value="{Binding DailyNote.CurrentResin, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="12,0,0,4"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{Binding DailyNote.ResinFormatted, Mode=OneWay}"/>
|
||||
Height="40"
|
||||
Background="{StaticResource CardBackgroundFillColorDefaultBrush}"
|
||||
IsIndeterminate="False"
|
||||
Maximum="{Binding DailyNote.MaxResin, Mode=OneWay}"
|
||||
Value="{Binding DailyNote.CurrentResin, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="4,4,0,0"
|
||||
Opacity="0.6"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding DailyNote.ResinRecoveryTargetTime, Mode=OneWay}"/>
|
||||
<StackPanel Grid.Column="1" Margin="12,0,0,0">
|
||||
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{Binding DailyNote.ResinFormatted, Mode=OneWay}"/>
|
||||
<TextBlock
|
||||
Opacity="0.6"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding DailyNote.ResinRecoveryTargetTime, Mode=OneWay}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Width="40"
|
||||
Height="40"
|
||||
VerticalAlignment="Center">
|
||||
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_ItemIcon_204.png"/>
|
||||
<ProgressRing
|
||||
Width="40"
|
||||
Height="40">
|
||||
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_ItemIcon_204.png"/>
|
||||
<ProgressRing
|
||||
Width="40"
|
||||
Height="40"
|
||||
Background="{StaticResource CardBackgroundFillColorDefaultBrush}"
|
||||
IsIndeterminate="False"
|
||||
Maximum="{Binding DailyNote.MaxHomeCoin, Mode=OneWay}"
|
||||
Value="{Binding DailyNote.CurrentHomeCoin, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="12,0,0,4"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{Binding DailyNote.HomeCoinFormatted, Mode=OneWay}"/>
|
||||
Height="40"
|
||||
Background="{StaticResource CardBackgroundFillColorDefaultBrush}"
|
||||
IsIndeterminate="False"
|
||||
Maximum="{Binding DailyNote.MaxHomeCoin, Mode=OneWay}"
|
||||
Value="{Binding DailyNote.CurrentHomeCoin, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="4,4,0,0"
|
||||
Opacity="0.6"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding DailyNote.HomeCoinRecoveryTargetTimeFormatted, Mode=OneWay}"/>
|
||||
<StackPanel Grid.Column="1" Margin="12,0,0,0">
|
||||
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{Binding DailyNote.HomeCoinFormatted, Mode=OneWay}"/>
|
||||
<TextBlock
|
||||
Opacity="0.6"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding DailyNote.HomeCoinRecoveryTargetTimeFormatted, Mode=OneWay}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Width="40"
|
||||
Height="40"
|
||||
VerticalAlignment="Center">
|
||||
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_MarkQuest_Events_Proce.png"/>
|
||||
<ProgressRing
|
||||
Width="40"
|
||||
Height="40">
|
||||
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_MarkQuest_Events_Proce.png"/>
|
||||
<ProgressRing
|
||||
Width="40"
|
||||
Height="40"
|
||||
Background="{StaticResource CardBackgroundFillColorDefaultBrush}"
|
||||
IsIndeterminate="False"
|
||||
Maximum="{Binding DailyNote.TotalTaskNum, Mode=OneWay}"
|
||||
Value="{Binding DailyNote.FinishedTaskNum, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="12,0,0,4"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{Binding DailyNote.TaskFormatted, Mode=OneWay}"/>
|
||||
Height="40"
|
||||
Background="{StaticResource CardBackgroundFillColorDefaultBrush}"
|
||||
IsIndeterminate="False"
|
||||
Maximum="{Binding DailyNote.TotalTaskNum, Mode=OneWay}"
|
||||
Value="{Binding DailyNote.FinishedTaskNum, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="4,4,0,0"
|
||||
Opacity="0.6"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding DailyNote.ExtraTaskRewardDescription, Mode=OneWay}"/>
|
||||
<StackPanel Grid.Column="1" Margin="12,0,0,0">
|
||||
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{Binding DailyNote.TaskFormatted, Mode=OneWay}"/>
|
||||
<TextBlock
|
||||
Opacity="0.6"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding DailyNote.ExtraTaskRewardDescription, Mode=OneWay}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Width="40"
|
||||
Height="40"
|
||||
VerticalAlignment="Center">
|
||||
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_MarkTower.png"/>
|
||||
<ProgressRing
|
||||
Width="40"
|
||||
Height="40">
|
||||
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_MarkTower.png"/>
|
||||
<ProgressRing
|
||||
Width="40"
|
||||
Height="40"
|
||||
Background="{StaticResource CardBackgroundFillColorDefaultBrush}"
|
||||
IsIndeterminate="False"
|
||||
Maximum="{Binding DailyNote.ResinDiscountNumLimit, Mode=OneWay}"
|
||||
Value="{Binding DailyNote.ResinDiscountUsedNum, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="12,0,0,4"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{Binding DailyNote.ResinDiscountFormatted, Mode=OneWay}"/>
|
||||
Height="40"
|
||||
Background="{StaticResource CardBackgroundFillColorDefaultBrush}"
|
||||
IsIndeterminate="False"
|
||||
Maximum="{Binding DailyNote.ResinDiscountNumLimit, Mode=OneWay}"
|
||||
Value="{Binding DailyNote.ResinDiscountUsedNum, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="4,4,0,0"
|
||||
Opacity="0.6"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewPageDailyNoteResinDiscountUsed}"/>
|
||||
<StackPanel Grid.Column="1" Margin="12,0,0,0">
|
||||
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{Binding DailyNote.ResinDiscountFormatted, Mode=OneWay}"/>
|
||||
<TextBlock
|
||||
Opacity="0.6"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewPageDailyNoteResinDiscountUsed}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Width="40"
|
||||
Height="40">
|
||||
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_ItemIcon_220021.png"/>
|
||||
<ProgressRing
|
||||
Width="40"
|
||||
Height="40">
|
||||
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_ItemIcon_220021.png"/>
|
||||
<ProgressRing
|
||||
Width="40"
|
||||
Height="40"
|
||||
Background="{StaticResource CardBackgroundFillColorDefaultBrush}"
|
||||
IsIndeterminate="False"
|
||||
Maximum="604800"
|
||||
Value="{Binding DailyNote.Transformer.RecoveryTime.TotalSeconds, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="12,0,0,4"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{Binding DailyNote.Transformer.RecoveryTime.ReachedFormatted, Mode=OneWay}"/>
|
||||
Height="40"
|
||||
Background="{StaticResource CardBackgroundFillColorDefaultBrush}"
|
||||
IsIndeterminate="False"
|
||||
Maximum="604800"
|
||||
Value="{Binding DailyNote.Transformer.RecoveryTime.TotalSeconds, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
<StackPanel Grid.Column="1" Margin="12,0,0,0">
|
||||
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{Binding DailyNote.Transformer.RecoveryTime.ReachedFormatted, Mode=OneWay}"/>
|
||||
<TextBlock
|
||||
Opacity="0.6"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding DailyNote.Transformer.RecoveryTime.TimeFormatted, Mode=OneWay}"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="4,4,0,0"
|
||||
Opacity="0.6"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding DailyNote.Transformer.RecoveryTime.TimeFormatted, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
</cwuc:UniformGrid>
|
||||
</StackPanel>
|
||||
|
||||
<MenuFlyoutSeparator Grid.Row="2" Margin="8,16,8,0"/>
|
||||
|
||||
<ItemsControl
|
||||
Grid.Row="2"
|
||||
Grid.Row="3"
|
||||
Margin="0,16,0,0"
|
||||
ItemsSource="{Binding DailyNote.Expeditions, Mode=OneWay}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<cwuc:UniformGrid ColumnSpacing="8" Columns="5"/>
|
||||
<cwuc:UniformGrid
|
||||
ColumnSpacing="8"
|
||||
Columns="2"
|
||||
RowSpacing="8"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
@@ -463,8 +416,7 @@
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</cwuc:AdaptiveGridView>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
</shc:ScopedPage>
|
||||
|
||||
@@ -253,6 +253,16 @@
|
||||
DataContext="{Binding GameResource.PreDownloadGame.Latest, Mode=OneWay}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameResourcePreDownloadHeader}"
|
||||
Visibility="{Binding FallbackValue={StaticResource VisibilityCollapsed}, Converter={StaticResource EmptyObjectToVisibilityConverter}}"/>
|
||||
<ItemsControl Margin="0,0,0,0" ItemsSource="{Binding GameResource.PreDownloadGame.Diffs, Mode=OneWay}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<shvc:LaunchGameResourceExpander
|
||||
Margin="16,16,16,0"
|
||||
DataContext="{Binding Mode=OneWay}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameResourceDiffHeader}"/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<shvc:LaunchGameResourceExpander
|
||||
Margin="16,16,16,0"
|
||||
DataContext="{Binding GameResource.Game.Latest, Mode=OneWay}"
|
||||
|
||||
@@ -32,7 +32,11 @@
|
||||
Margin="0,0,0,8"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewWelcomeBody}"/>
|
||||
<ItemsControl Margin="0,0,0,32" ItemsSource="{Binding DownloadSummaries}">
|
||||
<ItemsControl
|
||||
Width="256"
|
||||
Margin="0,0,0,32"
|
||||
HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding DownloadSummaries}">
|
||||
<ItemsControl.ItemContainerTransitions>
|
||||
<TransitionCollection>
|
||||
<AddDeleteThemeTransition/>
|
||||
@@ -47,7 +51,6 @@
|
||||
<StackPanel Margin="8">
|
||||
<TextBlock Text="{Binding DisplayName}"/>
|
||||
<ProgressBar
|
||||
Width="240"
|
||||
Margin="0,4,0,0"
|
||||
Maximum="1"
|
||||
Value="{Binding ProgressValue}"/>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
|
||||
namespace Snap.Hutao.ViewModel.Abstraction;
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
|
||||
namespace Snap.Hutao.ViewModel.Abstraction;
|
||||
|
||||
/// <summary>
|
||||
/// 简化的视图模型抽象类
|
||||
/// </summary>
|
||||
/// <typeparam name="TPage">页面类型</typeparam>
|
||||
internal abstract class ViewModelSlim<TPage> : ObservableObject
|
||||
where TPage : Microsoft.UI.Xaml.Controls.Page
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的简化的视图模型抽象类
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider">服务提供器</param>
|
||||
public ViewModelSlim(IServiceProvider serviceProvider)
|
||||
{
|
||||
ServiceProvider = serviceProvider;
|
||||
|
||||
OpenUICommand = new AsyncRelayCommand(OpenUIAsync);
|
||||
NavigateCommand = new RelayCommand(Navigate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开页面命令
|
||||
/// </summary>
|
||||
public ICommand OpenUICommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 导航命令
|
||||
/// </summary>
|
||||
public ICommand NavigateCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 服务提供器
|
||||
/// </summary>
|
||||
protected IServiceProvider ServiceProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 打开界面执行
|
||||
/// </summary>
|
||||
/// <returns>任务</returns>
|
||||
protected virtual Task OpenUIAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导航到指定的页面类型
|
||||
/// </summary>
|
||||
protected virtual void Navigate()
|
||||
{
|
||||
ServiceProvider.GetRequiredService<INavigationService>().Navigate<TPage>(INavigationAwaiter.Default, true);
|
||||
}
|
||||
}
|
||||
@@ -20,20 +20,23 @@ internal sealed class AnnouncementViewModel : Abstraction.ViewModel
|
||||
/// <summary>
|
||||
/// 构造一个公告视图模型
|
||||
/// </summary>
|
||||
/// <param name="announcementService">公告服务</param>
|
||||
public AnnouncementViewModel(IAnnouncementService announcementService)
|
||||
/// <param name="serviceProvider">服务提供器</param>
|
||||
public AnnouncementViewModel(IServiceProvider serviceProvider)
|
||||
{
|
||||
this.announcementService = announcementService;
|
||||
announcementService = serviceProvider.GetRequiredService<IAnnouncementService>();
|
||||
|
||||
LaunchGameViewModelSlim = serviceProvider.GetRequiredService<Game.LaunchGameViewModelSlim>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 公告
|
||||
/// </summary>
|
||||
public AnnouncementWrapper? Announcement
|
||||
{
|
||||
get => announcement;
|
||||
set => SetProperty(ref announcement, value);
|
||||
}
|
||||
public AnnouncementWrapper? Announcement { get => announcement; set => SetProperty(ref announcement, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 启动游戏视图模型
|
||||
/// </summary>
|
||||
public Game.LaunchGameViewModelSlim LaunchGameViewModelSlim { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OpenUIAsync()
|
||||
|
||||
@@ -332,7 +332,7 @@ internal sealed class GachaLogViewModel : Abstraction.ViewModel
|
||||
/// 需要从主线程调用
|
||||
/// </summary>
|
||||
/// <param name="archive">存档</param>
|
||||
/// <param name="forceUpdate">强制刷新,即使Uid相同也刷新该Uid的记录</param>
|
||||
/// <param name="forceUpdate">强制刷新,即使Uid相同也刷新该 Uid 的记录</param>
|
||||
private void SetSelectedArchiveAndUpdateStatistics(GachaArchive? archive, bool forceUpdate = false)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.ViewModel.GachaLog;
|
||||
|
||||
/// <summary>
|
||||
/// 简化的祈愿记录视图模型
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Scoped)]
|
||||
internal sealed class GachaLogViewModelSlim : Abstraction.ViewModelSlim<View.Page.GachaLogPage>
|
||||
{
|
||||
private List<GachaStatisticsSlim>? statisticsList;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的简化的祈愿记录视图模型
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider">服务提供器</param>
|
||||
public GachaLogViewModelSlim(IServiceProvider serviceProvider)
|
||||
: base(serviceProvider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 统计列表
|
||||
/// </summary>
|
||||
public List<GachaStatisticsSlim>? StatisticsList { get => statisticsList; set => SetProperty(ref statisticsList, value); }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Task OpenUIAsync()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.ViewModel.GachaLog;
|
||||
|
||||
/// <summary>
|
||||
/// 简化的祈愿统计
|
||||
/// </summary>
|
||||
internal sealed class GachaStatisticsSlim
|
||||
{
|
||||
/// <summary>
|
||||
/// Uid
|
||||
/// </summary>
|
||||
public string Uid { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 角色活动
|
||||
/// </summary>
|
||||
public TypedWishSummary AvatarWish { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 神铸赋形
|
||||
/// </summary>
|
||||
public TypedWishSummary WeaponWish { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 奔行世间
|
||||
/// </summary>
|
||||
public TypedWishSummary StandardWish { get; set; } = default!;
|
||||
}
|
||||
@@ -26,24 +26,24 @@ internal sealed class TypedWishSummary : Wish
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 据上个五星抽数
|
||||
/// 距上个五星抽数
|
||||
/// </summary>
|
||||
public int LastOrangePull { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 据上个四星抽数
|
||||
/// 距上个四星抽数
|
||||
/// </summary>
|
||||
public int LastPurplePull { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 五星保底阈值
|
||||
/// </summary>
|
||||
public int GuarenteeOrangeThreshold { get; set; }
|
||||
public int GuaranteeOrangeThreshold { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 四星保底阈值
|
||||
/// </summary>
|
||||
public int GuarenteePurpleThreshold { get; set; }
|
||||
public int GuaranteePurpleThreshold { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 五星格式化字符串
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.ViewModel.GachaLog;
|
||||
|
||||
/// <summary>
|
||||
/// 简化的类型化的祈愿概览
|
||||
/// </summary>
|
||||
internal sealed class TypedWishSummarySlim
|
||||
{
|
||||
/// <summary>
|
||||
/// 卡池名称
|
||||
/// </summary>
|
||||
public string Name { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 距上个五星抽数
|
||||
/// </summary>
|
||||
public int LastOrangePull { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 距上个四星抽数
|
||||
/// </summary>
|
||||
public int LastPurplePull { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 五星保底阈值
|
||||
/// </summary>
|
||||
public int GuaranteeOrangeThreshold { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 四星保底阈值
|
||||
/// </summary>
|
||||
public int GuaranteePurpleThreshold { get; set; }
|
||||
}
|
||||
@@ -149,7 +149,9 @@ internal sealed class LaunchGameViewModel : Abstraction.ViewModel
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OpenUIAsync()
|
||||
{
|
||||
if (File.Exists(serviceProvider.GetRequiredService<AppOptions>().GamePath))
|
||||
IInfoBarService infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
|
||||
|
||||
if (File.Exists(AppOptions.GamePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -162,7 +164,7 @@ internal sealed class LaunchGameViewModel : Abstraction.ViewModel
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelLaunchGameMultiChannelReadFail);
|
||||
infoBarService.Warning(SH.ViewModelLaunchGameMultiChannelReadFail);
|
||||
}
|
||||
|
||||
ObservableCollection<GameAccount> accounts = await gameService.GetGameAccountCollectionAsync().ConfigureAwait(false);
|
||||
@@ -186,7 +188,7 @@ internal sealed class LaunchGameViewModel : Abstraction.ViewModel
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelLaunchGamePathInvalid);
|
||||
infoBarService.Warning(SH.ViewModelLaunchGamePathInvalid);
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
await serviceProvider.GetRequiredService<INavigationService>()
|
||||
.NavigateAsync<View.Page.SettingPage>(INavigationAwaiter.Default, true)
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Service.Game;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Snap.Hutao.ViewModel.Game;
|
||||
|
||||
/// <summary>
|
||||
/// 简化的启动游戏视图模型
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Scoped)]
|
||||
internal sealed class LaunchGameViewModelSlim : Abstraction.ViewModelSlim<View.Page.LaunchGamePage>
|
||||
{
|
||||
private readonly IGameService gameService;
|
||||
|
||||
private ObservableCollection<GameAccount>? gameAccounts;
|
||||
private GameAccount? selectedGameAccount;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的简化的启动游戏视图模型
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider">服务提供器</param>
|
||||
public LaunchGameViewModelSlim(IServiceProvider serviceProvider)
|
||||
: base(serviceProvider)
|
||||
{
|
||||
gameService = serviceProvider.GetRequiredService<IGameService>();
|
||||
|
||||
LaunchCommand = new AsyncRelayCommand(LaunchAsync);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 游戏账号集合
|
||||
/// </summary>
|
||||
public ObservableCollection<GameAccount>? GameAccounts { get => gameAccounts; set => SetProperty(ref gameAccounts, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 选中的账号
|
||||
/// </summary>
|
||||
public GameAccount? SelectedGameAccount { get => selectedGameAccount; set => SetProperty(ref selectedGameAccount, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 启动游戏命令
|
||||
/// </summary>
|
||||
public ICommand LaunchCommand { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OpenUIAsync()
|
||||
{
|
||||
ObservableCollection<GameAccount> accounts = await gameService.GetGameAccountCollectionAsync().ConfigureAwait(false);
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
GameAccounts = accounts;
|
||||
|
||||
// Try set to the current account.
|
||||
SelectedGameAccount ??= gameService.DetectCurrentGameAccount();
|
||||
}
|
||||
|
||||
private async Task LaunchAsync()
|
||||
{
|
||||
IInfoBarService infoBarService = ServiceProvider.GetRequiredService<IInfoBarService>();
|
||||
|
||||
try
|
||||
{
|
||||
if (SelectedGameAccount != null)
|
||||
{
|
||||
if (!gameService.SetGameAccount(SelectedGameAccount))
|
||||
{
|
||||
infoBarService.Warning(SH.ViewModelLaunchGameSwitchGameAccountFail);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await gameService.LaunchAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
infoBarService.Error(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,6 +115,11 @@ internal sealed class WelcomeViewModel : ObservableObject
|
||||
downloadSummaries.TryAdd("AvatarIcon", new(serviceProvider, "AvatarIcon"));
|
||||
}
|
||||
|
||||
if (StaticResource.IsContractUnfulfilled(SettingKeys.StaticResourceV5Contract))
|
||||
{
|
||||
downloadSummaries.TryAdd("MonsterIcon", new(serviceProvider, "MonsterIcon"));
|
||||
}
|
||||
|
||||
return downloadSummaries.Select(x => x.Value);
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ internal class WikiWeaponViewModel : Abstraction.ViewModel
|
||||
.Where(weapon => !skippedWeapons.Contains(weapon.Id))
|
||||
.OrderByDescending(weapon => weapon.RankLevel)
|
||||
.ThenBy(weapon => weapon.WeaponType)
|
||||
.ThenByDescending(weapon => weapon.Id.Value)
|
||||
.ToList();
|
||||
|
||||
await CombineWithWeaponCollocationsAsync(sorted).ConfigureAwait(false);
|
||||
|
||||
Reference in New Issue
Block a user