Merge pull request #857 from DGP-Studio/develop

1.7.0 RC 2
This commit is contained in:
DismissedLight
2023-08-27 20:14:10 +08:00
committed by GitHub
250 changed files with 4303 additions and 1439 deletions

View File

@@ -7,9 +7,10 @@ version: 2
updates:
- package-ecosystem: "nuget"
directory: "/src/Snap.Hutao" # Snap.Hutao.csproj
target-branch: "develop"
schedule:
interval: "weekly"
groups:
main-dependencies:
packages:
patterns:
- "*"

View File

@@ -35,7 +35,6 @@ Snap Hutao is an open-source Genshin Impact toolbox on Windows platform, aim to
* [dotnet/runtime](https://github.com/dotnet/runtime)
* [DotNetAnalyzers/StyleCopAnalyzers](https://github.com/DotNetAnalyzers/StyleCopAnalyzers)
* [microsoft/CsWin32](https://github.com/microsoft/CsWin32)
* [microsoft/vs-threading](https://github.com/microsoft/vs-threading)
* [microsoft/vs-validation](https://github.com/microsoft/vs-validation)
* [microsoft/WindowsAppSDK](https://github.com/microsoft/WindowsAppSDK)
* [microsoft/microsoft-ui-xaml](https://github.com/microsoft/microsoft-ui-xaml)

View File

@@ -2,13 +2,10 @@
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
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.Automation;

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Snap.Hutao.SourceGeneration.Primitive;
using System.Collections.Generic;
@@ -75,7 +74,7 @@ internal sealed class ConstructorGenerator : IIncrementalGenerator
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(ConstructorGenerator)}}", "1.0.0.0")]
partial class {{context2.Symbol.ToDisplayString(SymbolDisplayFormats.QualifiedNonNullableFormat)}}
{
public {{context2.Symbol.Name}}(System.IServiceProvider serviceProvider{{httpclient}}){{(options.CallBaseConstructor? " : base(serviceProvider)" : string.Empty)}}
public {{context2.Symbol.Name}}(System.IServiceProvider serviceProvider{{httpclient}}){{(options.CallBaseConstructor ? " : base(serviceProvider)" : string.Empty)}}
{
""");
@@ -180,7 +179,7 @@ internal sealed class ConstructorGenerator : IIncrementalGenerator
public FieldValueAssignmentOptions(bool resolveHttpClient, bool callBaseConstructor)
{
ResolveHttpClient = resolveHttpClient;
ResolveHttpClient = resolveHttpClient;
CallBaseConstructor = callBaseConstructor;
}
}

View File

@@ -5,7 +5,6 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Snap.Hutao.SourceGeneration.Primitive;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

View File

@@ -3,7 +3,6 @@ using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Snap.Hutao.SourceGeneration.Primitive;
using System;
using System.Collections.Immutable;
using System.Linq;
@@ -15,11 +14,14 @@ internal class ServiceAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor NonSingletonUseServiceProviderDescriptor = new("SH301", "Non Singleton service should avoid direct use of IServiceProvider", "Non Singleton service should avoid direct use of IServiceProvider", "Quality", DiagnosticSeverity.Info, true);
private static readonly DiagnosticDescriptor SingletonServiceCaptureNonSingletonServiceDescriptor = new("SH302", "Singleton service should avoid keep reference of non singleton service", "Singleton service should avoid keep reference of non singleton service", "Quality", DiagnosticSeverity.Info, true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get => new DiagnosticDescriptor[]
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get => new DiagnosticDescriptor[]
{
NonSingletonUseServiceProviderDescriptor,
SingletonServiceCaptureNonSingletonServiceDescriptor,
}.ToImmutableArray(); }
}.ToImmutableArray();
}
public override void Initialize(AnalysisContext context)
{

View File

@@ -116,7 +116,7 @@ internal class LocalizedEnumGenerator : IIncrementalGenerator
.Where(m => m.Kind == SymbolKind.Field)
.Cast<IFieldSymbol>();
foreach(IFieldSymbol fieldSymbol in fields)
foreach (IFieldSymbol fieldSymbol in fields)
{
AttributeData? localizationKeyInfo = fieldSymbol.GetAttributes()
.SingleOrDefault(data => data.AttributeClass!.ToDisplayString() == LocalizationKeyName);

View File

@@ -9,7 +9,7 @@ namespace Snap.Hutao.SourceGeneration.Primitive;
internal static class GeneratorSyntaxContextExtension
{
public static bool TryGetDeclaredSymbol<TSymbol>(this GeneratorSyntaxContext context, System.Threading.CancellationToken token,[NotNullWhen(true)] out TSymbol? symbol)
public static bool TryGetDeclaredSymbol<TSymbol>(this GeneratorSyntaxContext context, System.Threading.CancellationToken token, [NotNullWhen(true)] out TSymbol? symbol)
where TSymbol : class, ISymbol
{
symbol = context.SemanticModel.GetDeclaredSymbol(context.Node, token) as TSymbol;

View File

@@ -3,7 +3,6 @@ using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Snap.Hutao.SourceGeneration.Primitive;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
@@ -129,7 +128,7 @@ internal sealed class UniversalAnalyzer : DiagnosticAnalyzer
}
// ICommand can only use Task or Task<T>
if (methodSymbol.GetAttributes().Any(attr=>attr.AttributeClass!.ToDisplayString() == Automation.CommandGenerator.AttributeName))
if (methodSymbol.GetAttributes().Any(attr => attr.AttributeClass!.ToDisplayString() == Automation.CommandGenerator.AttributeName))
{
return;
}
@@ -241,7 +240,7 @@ internal sealed class UniversalAnalyzer : DiagnosticAnalyzer
Diagnostic diagnostic = Diagnostic.Create(useIsNotNullPatternMatchingDescriptor, location);
context.ReportDiagnostic(diagnostic);
}
else if(syntax.IsKind(SyntaxKind.EqualsExpression) && syntax.Right.IsKind(SyntaxKind.NullLiteralExpression))
else if (syntax.IsKind(SyntaxKind.EqualsExpression) && syntax.Right.IsKind(SyntaxKind.NullLiteralExpression))
{
Location location = syntax.OperatorToken.GetLocation();
Diagnostic diagnostic = Diagnostic.Create(useIsNullPatternMatchingDescriptor, location);

View File

@@ -5,6 +5,7 @@
xmlns:cwcw="using:CommunityToolkit.WinUI.Controls"
xmlns:cwuc="using:CommunityToolkit.WinUI.UI.Converters"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:shci="using:Snap.Hutao.Control.Image"
xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter"
xmlns:shvc="using:Snap.Hutao.View.Converter">
<Application.Resources>
@@ -43,14 +44,14 @@
<CornerRadius x:Key="CompatCornerRadiusRight">0,6,6,0</CornerRadius>
<CornerRadius x:Key="CompatCornerRadiusBottom">0,0,6,6</CornerRadius>
<CornerRadius x:Key="CompatCornerRadiusSmall">2</CornerRadius>
<!-- OpenPaneLength -->
<x:Double x:Key="CompatSplitViewOpenPaneLength">212</x:Double>
<x:Double x:Key="CompatSplitViewOpenPaneLength2">304</x:Double>
<!-- Length -->
<GridLength x:Key="CompatGridLength2">288</GridLength>
<x:Double x:Key="CompatSplitViewOpenPaneLength">212</x:Double>
<x:Double x:Key="CompatSplitViewOpenPaneLength2">304</x:Double>
<x:Double x:Key="CompatSplitViewOpenPaneLength3">320</x:Double>
<x:Double x:Key="HomeAdaptiveCardHeight">180</x:Double>
<x:Double x:Key="ContentDialogMinHeight">64</x:Double>
<x:Double x:Key="LargeAppBarButtonWidth">100</x:Double>
<!-- ProgressBar -->
<x:Double x:Key="LargeBackgroundProgressBarOpacity">0.2</x:Double>
@@ -493,7 +494,83 @@
</Setter.Value>
</Setter>
</Style>
<Style TargetType="shci:CachedImage">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{ThemeResource ApplicationForegroundThemeBrush}"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="LazyLoadingThreshold" Value="300"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="shci:CachedImage">
<Grid
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Image
Name="PlaceholderImage"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Opacity="1.0"
Source="{TemplateBinding PlaceholderSource}"
Stretch="{TemplateBinding PlaceholderStretch}"/>
<Image
Name="Image"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
NineGrid="{TemplateBinding NineGrid}"
Opacity="0.0"
Stretch="{TemplateBinding Stretch}"/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Failed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Image" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderImage" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Loading">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Image" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderImage" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Loaded">
<Storyboard>
<DoubleAnimation
AutoReverse="False"
BeginTime="0"
Storyboard.TargetName="Image"
Storyboard.TargetProperty="Opacity"
From="0"
To="1"
Duration="0:0:0.5"/>
<DoubleAnimation
AutoReverse="False"
BeginTime="0"
Storyboard.TargetName="PlaceholderImage"
Storyboard.TargetProperty="Opacity"
From="1"
To="0"
Duration="0:0:0.5"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Unloaded"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ItemsPanelTemplate -->
<ItemsPanelTemplate x:Key="ItemsStackPanelTemplate">
<ItemsStackPanel/>

View File

@@ -22,17 +22,18 @@ internal sealed partial class AutoHeightBehavior : BehaviorBase<FrameworkElement
}
/// <inheritdoc/>
protected override void OnAssociatedObjectLoaded()
protected override bool Initialize()
{
UpdateElement();
AssociatedObject.SizeChanged += sizeChangedEventHandler;
return true;
}
/// <inheritdoc/>
protected override void OnDetaching()
protected override bool Uninitialize()
{
AssociatedObject.SizeChanged -= sizeChangedEventHandler;
base.OnDetaching();
return true;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)

View File

@@ -22,17 +22,18 @@ internal sealed partial class AutoWidthBehavior : BehaviorBase<FrameworkElement>
}
/// <inheritdoc/>
protected override void OnAssociatedObjectLoaded()
protected override bool Initialize()
{
UpdateElement();
AssociatedObject.SizeChanged += sizeChangedEventHandler;
return true;
}
/// <inheritdoc/>
protected override void OnDetaching()
protected override bool Uninitialize()
{
AssociatedObject.SizeChanged -= sizeChangedEventHandler;
base.OnDetaching();
return true;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)

View File

@@ -28,19 +28,19 @@ internal sealed class ComboBoxExtendsContentIntoTitleBarWorkaroundBehavior : Beh
}
/// <inheritdoc/>
protected override void OnAssociatedObjectLoaded()
protected override bool Initialize()
{
AssociatedObject.DropDownOpened += dropDownOpenedHandler;
AssociatedObject.DropDownClosed += dropDownClosedHandler;
return true;
}
/// <inheritdoc/>
protected override void OnDetaching()
protected override bool Uninitialize()
{
AssociatedObject.DropDownOpened -= dropDownOpenedHandler;
AssociatedObject.DropDownClosed -= dropDownClosedHandler;
base.OnDetaching();
return true;
}
private void OnDropDownOpened(object? sender, object e)

View File

@@ -0,0 +1,20 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Behaviors;
using Microsoft.UI.Xaml.Controls;
namespace Snap.Hutao.Control.Behavior;
internal sealed class SelectedItemInViewBehavior : BehaviorBase<ListViewBase>
{
protected override bool Initialize()
{
if (AssociatedObject.SelectedItem is { } item)
{
AssociatedObject.ScrollIntoView(item);
}
return true;
}
}

View File

@@ -9,21 +9,6 @@ namespace Snap.Hutao.Control;
[HighQuality]
internal static class BoxedValues
{
/// <summary>
/// <see cref="double"/> 0
/// </summary>
public static readonly object DoubleZero = 0D;
/// <summary>
/// <see cref="double"/> 1
/// </summary>
public static readonly object DoubleOne = 1D;
/// <summary>
/// <see cref="int"/> 0
/// </summary>
public static readonly object Int32Zero = 0;
/// <summary>
/// <see cref="true"/>
/// </summary>

View File

@@ -10,7 +10,6 @@ using Snap.Hutao.Control.Animation;
using Snap.Hutao.Control.Extension;
using Snap.Hutao.Core.Caching;
using Snap.Hutao.Service.Notification;
using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Runtime.InteropServices;

View File

@@ -21,7 +21,7 @@ internal sealed class BitmapIconExtension : MarkupExtension
/// <summary>
/// Gets or sets a value indicating whether to display the icon as monochrome.
/// </summary>
public bool ShowAsMonochrome { get; set; } = true;
public bool ShowAsMonochrome { get; set; }
/// <inheritdoc/>
protected override object ProvideValue()

View File

@@ -3,9 +3,7 @@
// Some part of this file came from:
// https://github.com/xunkong/desktop/tree/main/src/Desktop/Desktop/Pages/CharacterInfoPage.xaml.cs
using CommunityToolkit.WinUI;
using System.Buffers.Binary;
using System.Runtime.InteropServices;
using Windows.UI;
namespace Snap.Hutao.Control.Media;

View File

@@ -1,8 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI;
using CommunityToolkit.WinUI.Helpers;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Documents;

View File

@@ -1,12 +1,15 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Diagnostics;
namespace Snap.Hutao.Core.Annotation;
/// <summary>
/// 高质量代码
/// </summary>
[AttributeUsage(AttributeTargets.All, Inherited = false)]
[Conditional("DEBUG")]
internal sealed class HighQualityAttribute : Attribute
{
}

View File

@@ -196,13 +196,13 @@ internal sealed class ImageCache : IImageCache, IImageCacheFilePathOperation
retryCount += 3;
break;
case HttpStatusCode.TooManyRequests:
{
retryCount++;
TimeSpan delay = message.Headers.RetryAfter?.Delta ?? RetryCountToDelay[retryCount];
logger.LogInformation("Retry {Uri} after {Delay}.", uri, delay);
await Task.Delay(delay).ConfigureAwait(false);
break;
}
{
retryCount++;
TimeSpan delay = message.Headers.RetryAfter?.Delta ?? RetryCountToDelay[retryCount];
logger.LogInformation("Retry {Uri} after {Delay}.", uri, delay);
await Task.Delay(delay).ConfigureAwait(false);
break;
}
default:
return;

View File

@@ -34,7 +34,7 @@ internal sealed partial class ExceptionRecorder
#if RELEASE
#pragma warning disable VSTHRD002
serviceProvider
.GetRequiredService<Web.Hutao.HomaLogUploadClient>()
.GetRequiredService<Web.Hutao.Log.HomaLogUploadClient>()
.UploadLogAsync(serviceProvider, e.Exception)
.GetAwaiter()
.GetResult();

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.VisualBasic.FileIO;
using System.IO;
namespace Snap.Hutao.Core.IO;
@@ -14,7 +15,7 @@ internal static class DirectoryOperation
return false;
}
Directory.Move(sourceDirName, destDirName);
FileSystem.MoveDirectory(sourceDirName, destDirName, true);
return true;
}
}
}

View File

@@ -241,10 +241,21 @@ internal sealed class Activation : IActivation
{
case UrlActionRefresh:
{
await serviceProvider
.GetRequiredService<IDailyNoteService>()
.RefreshDailyNotesAsync()
.ConfigureAwait(false);
try
{
if (serviceProvider.GetRequiredService<IHutaoUserService>() is IHutaoUserServiceInitialization hutaoUserServiceInitialization)
{
await hutaoUserServiceInitialization.InitializeInternalAsync().ConfigureAwait(false);
}
await serviceProvider
.GetRequiredService<IDailyNoteService>()
.RefreshDailyNotesAsync()
.ConfigureAwait(false);
}
catch
{
}
// Check if it's redirected.
if (!isRedirected)

View File

@@ -25,7 +25,6 @@ internal static class AppInstanceExtension
/// </summary>
/// <param name="appInstance">app实例</param>
/// <param name="args">参数</param>
[SuppressMessage("", "VSTHRD002")]
public static unsafe void RedirectActivationTo(this AppInstance appInstance, AppActivationArguments args)
{
redirectEventHandle = CreateEvent(default(SECURITY_ATTRIBUTES*), true, false, null);

View File

@@ -8,7 +8,6 @@ using Snap.Hutao.Core.Setting;
using System.IO;
using System.Security.Principal;
using Windows.ApplicationModel;
using Windows.Foundation.Metadata;
using Windows.Storage;
namespace Snap.Hutao.Core;
@@ -131,15 +130,11 @@ internal sealed class RuntimeOptions : IOptions<RuntimeOptions>
private static bool GetElevated()
{
#if DEBUG_AS_FAKE_ELEVATED
return true;
#else
using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
{
WindowsPrincipal principal = new(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
#endif
}
private void DetectWebView2Environment(ref string webView2Version, ref bool isWebView2Supported)

View File

@@ -8,6 +8,8 @@ namespace Snap.Hutao.Core.Shell;
/// </summary>
internal interface IScheduleTaskInterop
{
bool IsDailyNoteRefreshEnabled();
/// <summary>
/// 注册实时便笺刷新任务
/// </summary>
@@ -20,4 +22,6 @@ internal interface IScheduleTaskInterop
/// </summary>
/// <returns>是否卸载成功</returns>
bool UnregisterAllTasks();
bool UnregisterForDailyNoteRefresh();
}

View File

@@ -16,6 +16,7 @@ namespace Snap.Hutao.Core.Shell;
internal sealed class ScheduleTaskInterop : IScheduleTaskInterop
{
private const string DailyNoteRefreshTaskName = "SnapHutaoDailyNoteRefreshTask";
private const string DailyNoteRefreshScriptName = "DailyNoteRefresh";
/// <summary>
/// 注册实时便笺刷新任务
@@ -30,7 +31,7 @@ internal sealed class ScheduleTaskInterop : IScheduleTaskInterop
task.RegistrationInfo.Description = SH.CoreScheduleTaskHelperDailyNoteRefreshTaskDescription;
task.Triggers.Add(new TimeTrigger() { Repetition = new(TimeSpan.FromSeconds(interval), TimeSpan.Zero), });
string scriptPath = EnsureWScriptCreated("DailyNoteRefresh", "hutao://DailyNote/Refresh");
string scriptPath = EnsureWScriptCreated(DailyNoteRefreshScriptName, "hutao://DailyNote/Refresh");
task.Actions.Add("wscript", $@"/b ""{scriptPath}""");
TaskService.Instance.RootFolder.RegisterTaskDefinition(DailyNoteRefreshTaskName, task);
@@ -42,6 +43,29 @@ internal sealed class ScheduleTaskInterop : IScheduleTaskInterop
}
}
public bool UnregisterForDailyNoteRefresh()
{
try
{
TaskService.Instance.RootFolder.DeleteTask(DailyNoteRefreshTaskName, false);
if (WScriptExists(DailyNoteRefreshScriptName, out string fullPath))
{
File.Delete(fullPath);
}
return true;
}
catch (Exception)
{
return false;
}
}
public bool IsDailyNoteRefreshEnabled()
{
return WScriptExists(DailyNoteRefreshScriptName, out _);
}
/// <summary>
/// 卸载全部注册的任务
/// </summary>
@@ -65,18 +89,22 @@ internal sealed class ScheduleTaskInterop : IScheduleTaskInterop
private static string EnsureWScriptCreated(string name, string url, bool forceCreate = false)
{
string tempFolder = ApplicationData.Current.TemporaryFolder.Path;
string fullName = Path.Combine(tempFolder, "Script", $"{name}.vbs");
if (File.Exists(fullName) && !forceCreate)
if (WScriptExists(name, out string fullName) && !forceCreate)
{
return fullName;
}
Directory.CreateDirectory(Path.Combine(tempFolder, "Script"));
string script = $"""CreateObject("WScript.Shell").Run "cmd /c start {url}", 0, False""";
File.WriteAllText(fullName, script);
return fullName;
}
private static bool WScriptExists(string name, out string fullName)
{
string tempFolder = ApplicationData.Current.TemporaryFolder.Path;
fullName = Path.Combine(tempFolder, "Script", $"{name}.vbs");
Directory.CreateDirectory(Path.Combine(tempFolder, "Script"));
return File.Exists(fullName);
}
}

View File

@@ -11,19 +11,16 @@ internal readonly struct Delay
/// <param name="minMilliSeconds">最小,闭</param>
/// <param name="maxMilliSeconds">最小,开</param>
/// <returns>任务</returns>
[SuppressMessage("", "VSTHRD200")]
public static ValueTask Random(int minMilliSeconds, int maxMilliSeconds)
{
return Task.Delay((int)(System.Random.Shared.NextDouble() * (maxMilliSeconds - minMilliSeconds)) + minMilliSeconds).AsValueTask();
}
[SuppressMessage("", "VSTHRD200")]
public static ValueTask FromSeconds(int seconds)
{
return Task.Delay(TimeSpan.FromSeconds(seconds)).AsValueTask();
}
[SuppressMessage("", "VSTHRD200")]
public static ValueTask FromMilliSeconds(int seconds)
{
return Task.Delay(TimeSpan.FromMilliseconds(seconds)).AsValueTask();

View File

@@ -0,0 +1,45 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Core.Threading;
internal class DispatcherQueueProgress<T> : IProgress<T>
{
private readonly SynchronizationContext synchronizationContext;
private readonly Action<T>? handler;
private readonly SendOrPostCallback invokeHandlers;
public DispatcherQueueProgress(Action<T> handler, SynchronizationContext synchronizationContext)
{
this.synchronizationContext = synchronizationContext;
invokeHandlers = new SendOrPostCallback(InvokeHandlers);
ArgumentNullException.ThrowIfNull(handler);
this.handler = handler;
}
public event EventHandler<T>? ProgressChanged;
public void Report(T value)
{
Action<T>? handler = this.handler;
EventHandler<T>? changedEvent = ProgressChanged;
if (handler is not null || changedEvent is not null)
{
synchronizationContext.Post(invokeHandlers, value);
}
}
[SuppressMessage("", "SH007")]
private void InvokeHandlers(object? state)
{
T value = (T)state!;
Action<T>? handler = this.handler;
EventHandler<T>? changedEvent = ProgressChanged;
handler?.Invoke(value);
changedEvent?.Invoke(this, value);
}
}

View File

@@ -11,7 +11,7 @@ namespace Snap.Hutao.Core.Threading;
/// 等待此类型对象后上下文会被切换至主线程
/// </summary>
[HighQuality]
internal readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQueueSwitchOperation>, ICriticalAwaiter
internal readonly struct DispatcherQueueSwitchOperation : IAwaitable<DispatcherQueueSwitchOperation>, ICriticalAwaiter
{
private readonly DispatcherQueue dispatherQueue;
@@ -19,7 +19,7 @@ internal readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQue
/// 构造一个新的调度器队列等待器
/// </summary>
/// <param name="dispatherQueue">调度器队列</param>
public DispatherQueueSwitchOperation(DispatcherQueue dispatherQueue)
public DispatcherQueueSwitchOperation(DispatcherQueue dispatherQueue)
{
this.dispatherQueue = dispatherQueue;
}
@@ -31,7 +31,7 @@ internal readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQue
}
/// <inheritdoc/>
public DispatherQueueSwitchOperation GetAwaiter()
public DispatcherQueueSwitchOperation GetAwaiter()
{
return this;
}

View File

@@ -8,6 +8,8 @@ namespace Snap.Hutao.Core.Threading;
/// </summary>
internal interface ITaskContext
{
IProgress<T> CreateProgressForMainThread<T>(Action<T> handler);
/// <summary>
/// 在主线程上同步等待执行操作
/// </summary>
@@ -26,5 +28,5 @@ internal interface ITaskContext
/// </summary>
/// <remarks>使用 <see cref="SwitchToBackgroundAsync"/> 异步切换到 后台线程</remarks>
/// <returns>等待体</returns>
DispatherQueueSwitchOperation SwitchToMainThreadAsync();
DispatcherQueueSwitchOperation SwitchToMainThreadAsync();
}

View File

@@ -11,6 +11,7 @@ namespace Snap.Hutao.Core.Threading;
[Injection(InjectAs.Singleton, typeof(ITaskContext))]
internal sealed class TaskContext : ITaskContext
{
private readonly DispatcherQueueSynchronizationContext dispatcherQueueSynchronizationContext;
private readonly DispatcherQueue dispatcherQueue;
/// <summary>
@@ -19,8 +20,8 @@ internal sealed class TaskContext : ITaskContext
public TaskContext()
{
dispatcherQueue = DispatcherQueue.GetForCurrentThread();
DispatcherQueueSynchronizationContext context = new(dispatcherQueue);
SynchronizationContext.SetSynchronizationContext(context);
dispatcherQueueSynchronizationContext = new(dispatcherQueue);
SynchronizationContext.SetSynchronizationContext(dispatcherQueueSynchronizationContext);
}
/// <inheritdoc/>
@@ -30,7 +31,7 @@ internal sealed class TaskContext : ITaskContext
}
/// <inheritdoc/>
public DispatherQueueSwitchOperation SwitchToMainThreadAsync()
public DispatcherQueueSwitchOperation SwitchToMainThreadAsync()
{
return new(dispatcherQueue);
}
@@ -47,4 +48,9 @@ internal sealed class TaskContext : ITaskContext
dispatcherQueue.Invoke(action);
}
}
public IProgress<T> CreateProgressForMainThread<T>(Action<T> handler)
{
return new DispatcherQueueProgress<T>(handler, dispatcherQueueSynchronizationContext);
}
}

View File

@@ -10,18 +10,14 @@ namespace Snap.Hutao.Core.Threading;
/// 任务扩展
/// </summary>
[HighQuality]
[SuppressMessage("", "VSTHRD003")]
[SuppressMessage("", "VSTHRD100")]
internal static class TaskExtension
{
[SuppressMessage("", "VSTHRD200")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ValueTask AsValueTask(this Task task)
{
return new(task);
}
[SuppressMessage("", "VSTHRD200")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ValueTask<T> AsValueTask<T>(this Task<T> task)
{

View File

@@ -0,0 +1,19 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
namespace Snap.Hutao.Core.Threading;
internal sealed class Throttler
{
private readonly ConcurrentDictionary<string, SemaphoreSlim> methodSemaphoreMap = new();
public ValueTask<SemaphoreSlimToken> ThrottleAsync(CancellationToken token = default, [CallerMemberName] string callerName = default!, [CallerLineNumber] int callerLine = 0)
{
string key = $"{callerName}L{callerLine}";
SemaphoreSlim semaphore = methodSemaphoreMap.GetOrAdd(key, name => new SemaphoreSlim(1));
return semaphore.EnterAsync(token);
}
}

View File

@@ -7,7 +7,6 @@ using Snap.Hutao.Core.Setting;
using Snap.Hutao.Win32;
using System.Runtime.CompilerServices;
using Windows.Graphics;
using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;
using static Windows.Win32.PInvoke;

View File

@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Runtime.InteropServices;
using Microsoft.UI.Xaml;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Shell;

View File

@@ -9,6 +9,8 @@ namespace Snap.Hutao.Extension;
[HighQuality]
internal static class DateTimeOffsetExtension
{
public static readonly DateTimeOffset DatebaseDefaultTime = new(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0));
/// <summary>
/// 从Unix时间戳转换
/// </summary>

View File

@@ -130,6 +130,19 @@ internal static partial class EnumerableExtension
return results;
}
public static TSource SingleOrAdd<TSource>(this List<TSource> list, Func<TSource, bool> predicate, Func<TSource> valueFactory)
where TSource : class
{
if (list.SingleOrDefault(predicate) is { } source)
{
return source;
}
TSource value = valueFactory();
list.Add(value);
return value;
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static List<TSource> SortBy<TSource, TKey>(this List<TSource> list, Func<TSource, TKey> keySelector)
where TKey : IComparable

View File

@@ -94,18 +94,4 @@ internal static partial class EnumerableExtension
string result = string.Join(separator, collection);
return result.Length > 0 ? result : string.Empty;
}
/// <summary>
/// Concatenates each element from the collection into single string.
/// </summary>
/// <typeparam name="T">Type of array elements.</typeparam>
/// <param name="collection">Collection to convert. Cannot be <see langword="null"/>.</param>
/// <param name="separator">Delimiter between elements in the final string.</param>
/// <param name="defaultValue">A string to be returned if collection has no elements.</param>
/// <returns>Converted collection into string.</returns>
public static string ToString<T>(this IEnumerable<T> collection, char separator, string defaultValue)
{
string result = string.Join(separator, collection);
return result.Length > 0 ? result : defaultValue;
}
}

View File

@@ -3,7 +3,6 @@
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Factory.Abstraction;
using System.Security.Authentication;
namespace Snap.Hutao.Factory;
@@ -68,12 +67,12 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
where TContentDialog : ContentDialog
{
await taskContext.SwitchToMainThreadAsync();
return serviceProvider.CreateInstance<TContentDialog>();
return serviceProvider.CreateInstance<TContentDialog>(parameters);
}
public TContentDialog CreateInstance<TContentDialog>(params object[] parameters)
where TContentDialog : ContentDialog
{
return serviceProvider.CreateInstance<TContentDialog>();
return serviceProvider.CreateInstance<TContentDialog>(parameters);
}
}

View File

@@ -6,6 +6,7 @@ using Microsoft.UI.Xaml;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Core.Windowing;
using Snap.Hutao.Message;
using Windows.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;
namespace Snap.Hutao;
@@ -22,6 +23,8 @@ internal sealed partial class MainWindow : Window, IWindowOptionsSource, IRecipi
private const int MinHeight = 524;
private readonly WindowOptions windowOptions;
private readonly ILogger<MainWindow> logger;
private readonly TypedEventHandler<object, WindowEventArgs> closedEventHander;
/// <summary>
/// 构造一个新的主窗体
@@ -33,9 +36,12 @@ internal sealed partial class MainWindow : Window, IWindowOptionsSource, IRecipi
windowOptions = new(this, TitleBarView.DragArea, new(1200, 741), true);
ExtendedWindow<MainWindow>.Initialize(this, serviceProvider);
serviceProvider.GetRequiredService<IMessenger>().Register(this);
logger = serviceProvider.GetRequiredService<ILogger<MainWindow>>();
// If not complete we should present the welcome view.
ContentSwitchPresenter.Value = StaticResource.IsAnyUnfulfilledContractPresent();
closedEventHander = OnClosed;
Closed += closedEventHander;
}
/// <inheritdoc/>
@@ -53,4 +59,9 @@ internal sealed partial class MainWindow : Window, IWindowOptionsSource, IRecipi
{
ContentSwitchPresenter.Value = false;
}
private void OnClosed(object sender, WindowEventArgs args)
{
logger.LogInformation("MainWindow Closed");
}
}

View File

@@ -0,0 +1,537 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Snap.Hutao.Model.Entity.Database;
#nullable disable
namespace Snap.Hutao.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20230824084447_AddRefreshTimeOnDailyNoteEntry")]
partial class AddRefreshTimeOnDailyNoteEntry
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.10");
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<Guid>("ArchiveId")
.HasColumnType("TEXT");
b.Property<uint>("Current")
.HasColumnType("INTEGER");
b.Property<uint>("Id")
.HasColumnType("INTEGER");
b.Property<int>("Status")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("Time")
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.HasIndex("ArchiveId");
b.ToTable("achievements");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<bool>("IsSelected")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.ToTable("achievement_archives");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("Info")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Uid")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.ToTable("avatar_infos");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<uint>("Id")
.HasColumnType("INTEGER");
b.Property<Guid>("ProjectId")
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("InnerId");
b.HasIndex("ProjectId");
b.ToTable("cultivate_entries");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<int>("Count")
.HasColumnType("INTEGER");
b.Property<Guid>("EntryId")
.HasColumnType("TEXT");
b.Property<bool>("IsFinished")
.HasColumnType("INTEGER");
b.Property<int>("ItemId")
.HasColumnType("INTEGER");
b.HasKey("InnerId");
b.HasIndex("EntryId");
b.ToTable("cultivate_items");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("AttachedUid")
.HasColumnType("TEXT");
b.Property<bool>("IsSelected")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.ToTable("cultivate_projects");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("DailyNote")
.HasColumnType("TEXT");
b.Property<bool>("DailyTaskNotify")
.HasColumnType("INTEGER");
b.Property<bool>("DailyTaskNotifySuppressed")
.HasColumnType("INTEGER");
b.Property<bool>("ExpeditionNotify")
.HasColumnType("INTEGER");
b.Property<bool>("ExpeditionNotifySuppressed")
.HasColumnType("INTEGER");
b.Property<bool>("HomeCoinNotifySuppressed")
.HasColumnType("INTEGER");
b.Property<int>("HomeCoinNotifyThreshold")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("RefreshTime")
.HasColumnType("TEXT");
b.Property<bool>("ResinNotifySuppressed")
.HasColumnType("INTEGER");
b.Property<int>("ResinNotifyThreshold")
.HasColumnType("INTEGER");
b.Property<bool>("TransformerNotify")
.HasColumnType("INTEGER");
b.Property<bool>("TransformerNotifySuppressed")
.HasColumnType("INTEGER");
b.Property<string>("Uid")
.IsRequired()
.HasColumnType("TEXT");
b.Property<Guid>("UserId")
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.HasIndex("UserId");
b.ToTable("daily_notes");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<bool>("IsSelected")
.HasColumnType("INTEGER");
b.Property<string>("Uid")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.ToTable("gacha_archives");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<Guid>("ArchiveId")
.HasColumnType("TEXT");
b.Property<int>("GachaType")
.HasColumnType("INTEGER");
b.Property<long>("Id")
.HasColumnType("INTEGER");
b.Property<uint>("ItemId")
.HasColumnType("INTEGER");
b.Property<int>("QueryType")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("Time")
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.HasIndex("ArchiveId");
b.ToTable("gacha_items");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("AttachUid")
.HasColumnType("TEXT");
b.Property<string>("MihoyoSDK")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("InnerId");
b.ToTable("game_accounts");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<uint>("Count")
.HasColumnType("INTEGER");
b.Property<uint>("ItemId")
.HasColumnType("INTEGER");
b.Property<Guid>("ProjectId")
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.HasIndex("ProjectId");
b.ToTable("inventory_items");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("AppendPropIdList")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("ItemId")
.HasColumnType("INTEGER");
b.Property<int>("Level")
.HasColumnType("INTEGER");
b.Property<int>("MainPropId")
.HasColumnType("INTEGER");
b.Property<Guid>("ProjectId")
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.HasIndex("ProjectId");
b.ToTable("inventory_reliquaries");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<int>("ItemId")
.HasColumnType("INTEGER");
b.Property<int>("Level")
.HasColumnType("INTEGER");
b.Property<Guid>("ProjectId")
.HasColumnType("TEXT");
b.Property<int>("PromoteLevel")
.HasColumnType("INTEGER");
b.HasKey("InnerId");
b.HasIndex("ProjectId");
b.ToTable("inventory_weapons");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b =>
{
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("ExpireTime")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("Key");
b.ToTable("object_cache");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
{
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("Key");
b.ToTable("settings");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<int>("ScheduleId")
.HasColumnType("INTEGER");
b.Property<string>("SpiralAbyss")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Uid")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.ToTable("spiral_abysses");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("Aid")
.HasColumnType("TEXT");
b.Property<string>("CookieToken")
.HasColumnType("TEXT");
b.Property<bool>("IsOversea")
.HasColumnType("INTEGER");
b.Property<bool>("IsSelected")
.HasColumnType("INTEGER");
b.Property<string>("LToken")
.HasColumnType("TEXT")
.HasColumnName("Ltoken");
b.Property<string>("Mid")
.HasColumnType("TEXT");
b.Property<string>("SToken")
.HasColumnType("TEXT")
.HasColumnName("Stoken");
b.HasKey("InnerId");
b.ToTable("users");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive")
.WithMany()
.HasForeignKey("ArchiveId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Archive");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
.WithMany()
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Project");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
.WithMany()
.HasForeignKey("EntryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Entry");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive")
.WithMany()
.HasForeignKey("ArchiveId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Archive");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
.WithMany()
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Project");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
.WithMany()
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Project");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
.WithMany()
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Project");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,30 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Snap.Hutao.Migrations
{
/// <inheritdoc />
public partial class AddRefreshTimeOnDailyNoteEntry : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTimeOffset>(
name: "RefreshTime",
table: "daily_notes",
type: "TEXT",
nullable: false,
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "RefreshTime",
table: "daily_notes");
}
}
}

View File

@@ -0,0 +1,546 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Snap.Hutao.Model.Entity.Database;
#nullable disable
namespace Snap.Hutao.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20230827040435_AddRefreshTimeOnAvatarInfo")]
partial class AddRefreshTimeOnAvatarInfo
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.10");
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<Guid>("ArchiveId")
.HasColumnType("TEXT");
b.Property<uint>("Current")
.HasColumnType("INTEGER");
b.Property<uint>("Id")
.HasColumnType("INTEGER");
b.Property<int>("Status")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("Time")
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.HasIndex("ArchiveId");
b.ToTable("achievements");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<bool>("IsSelected")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.ToTable("achievement_archives");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("CalculatorRefreshTime")
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("GameRecordRefreshTime")
.HasColumnType("TEXT");
b.Property<string>("Info")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("ShowcaseRefreshTime")
.HasColumnType("TEXT");
b.Property<string>("Uid")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.ToTable("avatar_infos");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<uint>("Id")
.HasColumnType("INTEGER");
b.Property<Guid>("ProjectId")
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("InnerId");
b.HasIndex("ProjectId");
b.ToTable("cultivate_entries");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<int>("Count")
.HasColumnType("INTEGER");
b.Property<Guid>("EntryId")
.HasColumnType("TEXT");
b.Property<bool>("IsFinished")
.HasColumnType("INTEGER");
b.Property<int>("ItemId")
.HasColumnType("INTEGER");
b.HasKey("InnerId");
b.HasIndex("EntryId");
b.ToTable("cultivate_items");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("AttachedUid")
.HasColumnType("TEXT");
b.Property<bool>("IsSelected")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.ToTable("cultivate_projects");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("DailyNote")
.HasColumnType("TEXT");
b.Property<bool>("DailyTaskNotify")
.HasColumnType("INTEGER");
b.Property<bool>("DailyTaskNotifySuppressed")
.HasColumnType("INTEGER");
b.Property<bool>("ExpeditionNotify")
.HasColumnType("INTEGER");
b.Property<bool>("ExpeditionNotifySuppressed")
.HasColumnType("INTEGER");
b.Property<bool>("HomeCoinNotifySuppressed")
.HasColumnType("INTEGER");
b.Property<int>("HomeCoinNotifyThreshold")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("RefreshTime")
.HasColumnType("TEXT");
b.Property<bool>("ResinNotifySuppressed")
.HasColumnType("INTEGER");
b.Property<int>("ResinNotifyThreshold")
.HasColumnType("INTEGER");
b.Property<bool>("TransformerNotify")
.HasColumnType("INTEGER");
b.Property<bool>("TransformerNotifySuppressed")
.HasColumnType("INTEGER");
b.Property<string>("Uid")
.IsRequired()
.HasColumnType("TEXT");
b.Property<Guid>("UserId")
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.HasIndex("UserId");
b.ToTable("daily_notes");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<bool>("IsSelected")
.HasColumnType("INTEGER");
b.Property<string>("Uid")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.ToTable("gacha_archives");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<Guid>("ArchiveId")
.HasColumnType("TEXT");
b.Property<int>("GachaType")
.HasColumnType("INTEGER");
b.Property<long>("Id")
.HasColumnType("INTEGER");
b.Property<uint>("ItemId")
.HasColumnType("INTEGER");
b.Property<int>("QueryType")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("Time")
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.HasIndex("ArchiveId");
b.ToTable("gacha_items");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("AttachUid")
.HasColumnType("TEXT");
b.Property<string>("MihoyoSDK")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("InnerId");
b.ToTable("game_accounts");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<uint>("Count")
.HasColumnType("INTEGER");
b.Property<uint>("ItemId")
.HasColumnType("INTEGER");
b.Property<Guid>("ProjectId")
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.HasIndex("ProjectId");
b.ToTable("inventory_items");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("AppendPropIdList")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("ItemId")
.HasColumnType("INTEGER");
b.Property<int>("Level")
.HasColumnType("INTEGER");
b.Property<int>("MainPropId")
.HasColumnType("INTEGER");
b.Property<Guid>("ProjectId")
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.HasIndex("ProjectId");
b.ToTable("inventory_reliquaries");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<int>("ItemId")
.HasColumnType("INTEGER");
b.Property<int>("Level")
.HasColumnType("INTEGER");
b.Property<Guid>("ProjectId")
.HasColumnType("TEXT");
b.Property<int>("PromoteLevel")
.HasColumnType("INTEGER");
b.HasKey("InnerId");
b.HasIndex("ProjectId");
b.ToTable("inventory_weapons");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b =>
{
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("ExpireTime")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("Key");
b.ToTable("object_cache");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
{
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("Key");
b.ToTable("settings");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<int>("ScheduleId")
.HasColumnType("INTEGER");
b.Property<string>("SpiralAbyss")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Uid")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.ToTable("spiral_abysses");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
{
b.Property<Guid>("InnerId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("Aid")
.HasColumnType("TEXT");
b.Property<string>("CookieToken")
.HasColumnType("TEXT");
b.Property<bool>("IsOversea")
.HasColumnType("INTEGER");
b.Property<bool>("IsSelected")
.HasColumnType("INTEGER");
b.Property<string>("LToken")
.HasColumnType("TEXT")
.HasColumnName("Ltoken");
b.Property<string>("Mid")
.HasColumnType("TEXT");
b.Property<string>("SToken")
.HasColumnType("TEXT")
.HasColumnName("Stoken");
b.HasKey("InnerId");
b.ToTable("users");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive")
.WithMany()
.HasForeignKey("ArchiveId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Archive");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
.WithMany()
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Project");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
.WithMany()
.HasForeignKey("EntryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Entry");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive")
.WithMany()
.HasForeignKey("ArchiveId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Archive");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
.WithMany()
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Project");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
.WithMany()
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Project");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
{
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
.WithMany()
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Project");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,52 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Snap.Hutao.Migrations
{
/// <inheritdoc />
public partial class AddRefreshTimeOnAvatarInfo : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTimeOffset>(
name: "CalculatorRefreshTime",
table: "avatar_infos",
type: "TEXT",
nullable: false,
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
migrationBuilder.AddColumn<DateTimeOffset>(
name: "GameRecordRefreshTime",
table: "avatar_infos",
type: "TEXT",
nullable: false,
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
migrationBuilder.AddColumn<DateTimeOffset>(
name: "ShowcaseRefreshTime",
table: "avatar_infos",
type: "TEXT",
nullable: false,
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "CalculatorRefreshTime",
table: "avatar_infos");
migrationBuilder.DropColumn(
name: "GameRecordRefreshTime",
table: "avatar_infos");
migrationBuilder.DropColumn(
name: "ShowcaseRefreshTime",
table: "avatar_infos");
}
}
}

View File

@@ -15,7 +15,7 @@ namespace Snap.Hutao.Migrations
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.5");
modelBuilder.HasAnnotation("ProductVersion", "7.0.10");
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
{
@@ -26,10 +26,10 @@ namespace Snap.Hutao.Migrations
b.Property<Guid>("ArchiveId")
.HasColumnType("TEXT");
b.Property<int>("Current")
b.Property<uint>("Current")
.HasColumnType("INTEGER");
b.Property<int>("Id")
b.Property<uint>("Id")
.HasColumnType("INTEGER");
b.Property<int>("Status")
@@ -42,7 +42,7 @@ namespace Snap.Hutao.Migrations
b.HasIndex("ArchiveId");
b.ToTable("achievements", (string)null);
b.ToTable("achievements");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
@@ -60,7 +60,7 @@ namespace Snap.Hutao.Migrations
b.HasKey("InnerId");
b.ToTable("achievement_archives", (string)null);
b.ToTable("achievement_archives");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
@@ -69,17 +69,26 @@ namespace Snap.Hutao.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("CalculatorRefreshTime")
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("GameRecordRefreshTime")
.HasColumnType("TEXT");
b.Property<string>("Info")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("ShowcaseRefreshTime")
.HasColumnType("TEXT");
b.Property<string>("Uid")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("InnerId");
b.ToTable("avatar_infos", (string)null);
b.ToTable("avatar_infos");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
@@ -88,7 +97,7 @@ namespace Snap.Hutao.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<int>("Id")
b.Property<uint>("Id")
.HasColumnType("INTEGER");
b.Property<Guid>("ProjectId")
@@ -101,7 +110,7 @@ namespace Snap.Hutao.Migrations
b.HasIndex("ProjectId");
b.ToTable("cultivate_entries", (string)null);
b.ToTable("cultivate_entries");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
@@ -126,7 +135,7 @@ namespace Snap.Hutao.Migrations
b.HasIndex("EntryId");
b.ToTable("cultivate_items", (string)null);
b.ToTable("cultivate_items");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
@@ -147,7 +156,7 @@ namespace Snap.Hutao.Migrations
b.HasKey("InnerId");
b.ToTable("cultivate_projects", (string)null);
b.ToTable("cultivate_projects");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
@@ -177,6 +186,9 @@ namespace Snap.Hutao.Migrations
b.Property<int>("HomeCoinNotifyThreshold")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("RefreshTime")
.HasColumnType("TEXT");
b.Property<bool>("ResinNotifySuppressed")
.HasColumnType("INTEGER");
@@ -200,7 +212,7 @@ namespace Snap.Hutao.Migrations
b.HasIndex("UserId");
b.ToTable("daily_notes", (string)null);
b.ToTable("daily_notes");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
@@ -218,7 +230,7 @@ namespace Snap.Hutao.Migrations
b.HasKey("InnerId");
b.ToTable("gacha_archives", (string)null);
b.ToTable("gacha_archives");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
@@ -236,7 +248,7 @@ namespace Snap.Hutao.Migrations
b.Property<long>("Id")
.HasColumnType("INTEGER");
b.Property<int>("ItemId")
b.Property<uint>("ItemId")
.HasColumnType("INTEGER");
b.Property<int>("QueryType")
@@ -249,7 +261,7 @@ namespace Snap.Hutao.Migrations
b.HasIndex("ArchiveId");
b.ToTable("gacha_items", (string)null);
b.ToTable("gacha_items");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
@@ -274,7 +286,7 @@ namespace Snap.Hutao.Migrations
b.HasKey("InnerId");
b.ToTable("game_accounts", (string)null);
b.ToTable("game_accounts");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
@@ -286,7 +298,7 @@ namespace Snap.Hutao.Migrations
b.Property<uint>("Count")
.HasColumnType("INTEGER");
b.Property<int>("ItemId")
b.Property<uint>("ItemId")
.HasColumnType("INTEGER");
b.Property<Guid>("ProjectId")
@@ -296,7 +308,7 @@ namespace Snap.Hutao.Migrations
b.HasIndex("ProjectId");
b.ToTable("inventory_items", (string)null);
b.ToTable("inventory_items");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
@@ -325,7 +337,7 @@ namespace Snap.Hutao.Migrations
b.HasIndex("ProjectId");
b.ToTable("inventory_reliquaries", (string)null);
b.ToTable("inventory_reliquaries");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
@@ -350,7 +362,7 @@ namespace Snap.Hutao.Migrations
b.HasIndex("ProjectId");
b.ToTable("inventory_weapons", (string)null);
b.ToTable("inventory_weapons");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b =>
@@ -366,7 +378,7 @@ namespace Snap.Hutao.Migrations
b.HasKey("Key");
b.ToTable("object_cache", (string)null);
b.ToTable("object_cache");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
@@ -379,7 +391,7 @@ namespace Snap.Hutao.Migrations
b.HasKey("Key");
b.ToTable("settings", (string)null);
b.ToTable("settings");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b =>
@@ -401,7 +413,7 @@ namespace Snap.Hutao.Migrations
b.HasKey("InnerId");
b.ToTable("spiral_abysses", (string)null);
b.ToTable("spiral_abysses");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
@@ -435,7 +447,7 @@ namespace Snap.Hutao.Migrations
b.HasKey("InnerId");
b.ToTable("users", (string)null);
b.ToTable("users");
});
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>

View File

@@ -31,6 +31,12 @@ internal sealed class AvatarInfo : IMappingFrom<AvatarInfo, string, Web.Enka.Mod
/// </summary>
public Web.Enka.Model.AvatarInfo Info { get; set; } = default!;
public DateTimeOffset ShowcaseRefreshTime { get; set; }
public DateTimeOffset GameRecordRefreshTime { get; set; }
public DateTimeOffset CalculatorRefreshTime { get; set; }
/// <summary>
/// 创建一个新的实体角色信息
/// </summary>

View File

@@ -4,10 +4,8 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
using Snap.Hutao.Web.Request.QueryString;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@@ -54,7 +52,21 @@ internal sealed class DailyNoteEntry : ObservableObject, IMappingFrom<DailyNoteE
/// </summary>
public DailyNote? DailyNote { get; set; }
// TODO: Add RefreshTime
/// <summary>
/// 刷新时间
/// </summary>
public DateTimeOffset RefreshTime { get; set; }
[NotMapped]
public string RefreshTimeFormatted
{
get
{
return RefreshTime == DateTimeOffsetExtension.DatebaseDefaultTime
? SH.ModelEntityDailyNoteNotRefreshed
: SH.ModelEntityDailyNoteRefreshTimeFormat.Format(RefreshTime);
}
}
/// <summary>
/// 树脂提醒阈值
@@ -130,5 +142,8 @@ internal sealed class DailyNoteEntry : ObservableObject, IMappingFrom<DailyNoteE
{
DailyNote = dailyNote;
OnPropertyChanged(nameof(DailyNote));
RefreshTime = DateTimeOffset.Now;
OnPropertyChanged(nameof(RefreshTimeFormatted));
}
}

View File

@@ -1,13 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Service.GachaLog;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

View File

@@ -3,7 +3,6 @@
using Snap.Hutao.Model.Entity.Abstraction;
using Snap.Hutao.Model.InterChange.GachaLog;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

View File

@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Json.Annotation;
using Snap.Hutao.Model.Intrinsic;
namespace Snap.Hutao.Model.Metadata.Avatar;

View File

@@ -89,6 +89,8 @@ internal static class AvatarIds
public static readonly AvatarId Lynette = 10000083;
public static readonly AvatarId Lyney = 10000084;
public static readonly AvatarId Freminet = 10000085;
public static readonly AvatarId Wriothesley = 10000086;
public static readonly AvatarId Neuvillette = 10000087;
/// <summary>
/// 检查该角色是否为主角
@@ -111,7 +113,7 @@ internal static class AvatarIds
{
[PlayerBoy] = new()
{
Name = "旅行者",
Name = SH.ModelMetadataAvatarPlayerName,
Icon = "UI_AvatarIcon_PlayerBoy",
SideIcon = "UI_AvatarIcon_Side_PlayerBoy",
Quality = Intrinsic.QualityType.QUALITY_ORANGE,
@@ -119,7 +121,7 @@ internal static class AvatarIds
[PlayerGirl] = new()
{
Name = "旅行者",
Name = SH.ModelMetadataAvatarPlayerName,
Icon = "UI_AvatarIcon_PlayerGirl",
SideIcon = "UI_AvatarIcon_Side_PlayerGirl",
Quality = Intrinsic.QualityType.QUALITY_ORANGE,

View File

@@ -1,9 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.ViewModel.Cultivation;
using System.Collections.Immutable;
namespace Snap.Hutao.Model.Metadata.Item;

View File

@@ -5,6 +5,7 @@ using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Model.Metadata.Item;
using Snap.Hutao.ViewModel.Complex;
using Snap.Hutao.ViewModel.GachaLog;
@@ -22,6 +23,12 @@ internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource
[JsonIgnore]
public WeaponCollocationView? Collocation { get; set; }
/// <summary>
/// [非元数据] 养成物品视图
/// </summary>
[JsonIgnore]
public List<Material>? CultivationItemsView { get; set; }
/// <inheritdoc cref="INameQuality.Quality" />
[JsonIgnore]
public QualityType Quality

View File

@@ -61,4 +61,9 @@ internal sealed partial class Weapon
/// 被动信息, 无被动的武器为 <see langword="null"/>
/// </summary>
public NameDescriptions? Affix { get; set; } = default!;
/// <summary>
/// 养成物品
/// </summary>
public List<MaterialId> CultivationItems { get; set; } = default!;
}

View File

@@ -420,6 +420,24 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 尚未刷新 的本地化字符串。
/// </summary>
internal static string ModelEntityDailyNoteNotRefreshed {
get {
return ResourceManager.GetString("ModelEntityDailyNoteNotRefreshed", resourceCulture);
}
}
/// <summary>
/// 查找类似 刷新于 {0:yyyy/MM/dd HH:mm:ss} 的本地化字符串。
/// </summary>
internal static string ModelEntityDailyNoteRefreshTimeFormat {
get {
return ResourceManager.GetString("ModelEntityDailyNoteRefreshTimeFormat", resourceCulture);
}
}
/// <summary>
/// 查找类似 第 {0} 期 的本地化字符串。
/// </summary>
@@ -744,6 +762,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 旅行者 的本地化字符串。
/// </summary>
internal static string ModelMetadataAvatarPlayerName {
get {
return ResourceManager.GetString("ModelMetadataAvatarPlayerName", resourceCulture);
}
}
/// <summary>
/// 查找类似 {0} 月 {1} 日 的本地化字符串。
/// </summary>
@@ -1104,6 +1131,60 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 养成计算:尚未刷新 的本地化字符串。
/// </summary>
internal static string ServiceAvatarInfoSummaryCalculatorNotRefreshed {
get {
return ResourceManager.GetString("ServiceAvatarInfoSummaryCalculatorNotRefreshed", resourceCulture);
}
}
/// <summary>
/// 查找类似 养成计算:{0:MM-dd HH:mm} 的本地化字符串。
/// </summary>
internal static string ServiceAvatarInfoSummaryCalculatorRefreshTimeFormat {
get {
return ResourceManager.GetString("ServiceAvatarInfoSummaryCalculatorRefreshTimeFormat", resourceCulture);
}
}
/// <summary>
/// 查找类似 我的角色:尚未刷新 的本地化字符串。
/// </summary>
internal static string ServiceAvatarInfoSummaryGameRecordNotRefreshed {
get {
return ResourceManager.GetString("ServiceAvatarInfoSummaryGameRecordNotRefreshed", resourceCulture);
}
}
/// <summary>
/// 查找类似 我的角色:{0:MM-dd HH:mm} 的本地化字符串。
/// </summary>
internal static string ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat {
get {
return ResourceManager.GetString("ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat", resourceCulture);
}
}
/// <summary>
/// 查找类似 角色橱窗:尚未刷新 的本地化字符串。
/// </summary>
internal static string ServiceAvatarInfoSummaryShowcaseNotRefreshed {
get {
return ResourceManager.GetString("ServiceAvatarInfoSummaryShowcaseNotRefreshed", resourceCulture);
}
}
/// <summary>
/// 查找类似 角色橱窗:{0:MM-dd HH:mm} 的本地化字符串。
/// </summary>
internal static string ServiceAvatarInfoSummaryShowcaseRefreshTimeFormat {
get {
return ResourceManager.GetString("ServiceAvatarInfoSummaryShowcaseRefreshTimeFormat", resourceCulture);
}
}
/// <summary>
/// 查找类似 保存养成计划状态失败 的本地化字符串。
/// </summary>
@@ -1338,6 +1419,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 数据包含异常物品, Id{0} 的本地化字符串。
/// </summary>
internal static string ServiceGachaLogUIGFImportItemInvalidFormat {
get {
return ResourceManager.GetString("ServiceGachaLogUIGFImportItemInvalidFormat", resourceCulture);
}
}
/// <summary>
/// 查找类似 请求验证密钥失败 的本地化字符串。
/// </summary>
@@ -1473,6 +1563,33 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 备份:{0} 的本地化字符串。
/// </summary>
internal static string ServiceGamePackageConvertMoveFileBackupFormat {
get {
return ResourceManager.GetString("ServiceGamePackageConvertMoveFileBackupFormat", resourceCulture);
}
}
/// <summary>
/// 查找类似 重命名:{0} 到:{1} 的本地化字符串。
/// </summary>
internal static string ServiceGamePackageConvertMoveFileRenameFormat {
get {
return ResourceManager.GetString("ServiceGamePackageConvertMoveFileRenameFormat", resourceCulture);
}
}
/// <summary>
/// 查找类似 替换:{0} 的本地化字符串。
/// </summary>
internal static string ServiceGamePackageConvertMoveFileRestoreFormat {
get {
return ResourceManager.GetString("ServiceGamePackageConvertMoveFileRestoreFormat", resourceCulture);
}
}
/// <summary>
/// 查找类似 重命名数据文件夹名称失败 的本地化字符串。
/// </summary>
@@ -1653,6 +1770,33 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 签到失败,{0} 的本地化字符串。
/// </summary>
internal static string ServiceSignInClaimRewardFailedFormat {
get {
return ResourceManager.GetString("ServiceSignInClaimRewardFailedFormat", resourceCulture);
}
}
/// <summary>
/// 查找类似 获取奖励列表失败 的本地化字符串。
/// </summary>
internal static string ServiceSignInRewardListRequestFailed {
get {
return ResourceManager.GetString("ServiceSignInRewardListRequestFailed", resourceCulture);
}
}
/// <summary>
/// 查找类似 签到成功,{0}×{1} 的本地化字符串。
/// </summary>
internal static string ServiceSignInSuccessRewardFormat {
get {
return ResourceManager.GetString("ServiceSignInSuccessRewardFormat", resourceCulture);
}
}
/// <summary>
/// 查找类似 多个用户记录为选中状态 的本地化字符串。
/// </summary>
@@ -2589,6 +2733,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 正在获取实时便笺信息,请稍候 的本地化字符串。
/// </summary>
internal static string ViewModelDailyNoteRequestProgressTitle {
get {
return ResourceManager.GetString("ViewModelDailyNoteRequestProgressTitle", resourceCulture);
}
}
/// <summary>
/// 查找类似 成功保存到指定位置 的本地化字符串。
/// </summary>
@@ -3462,6 +3615,42 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 自动刷新 的本地化字符串。
/// </summary>
internal static string ViewPageDailyNoteSettingAutoRefresh {
get {
return ResourceManager.GetString("ViewPageDailyNoteSettingAutoRefresh", resourceCulture);
}
}
/// <summary>
/// 查找类似 间隔选定的时间后刷新添加的实时便笺 的本地化字符串。
/// </summary>
internal static string ViewPageDailyNoteSettingAutoRefreshDescription {
get {
return ResourceManager.GetString("ViewPageDailyNoteSettingAutoRefreshDescription", resourceCulture);
}
}
/// <summary>
/// 查找类似 这些选项仅允许在非管理员模式下更改 的本地化字符串。
/// </summary>
internal static string ViewPageDailyNoteSettingRefreshElevatedHint {
get {
return ResourceManager.GetString("ViewPageDailyNoteSettingRefreshElevatedHint", resourceCulture);
}
}
/// <summary>
/// 查找类似 刷新 的本地化字符串。
/// </summary>
internal static string ViewPageDailyNoteSettingRefreshHeader {
get {
return ResourceManager.GetString("ViewPageDailyNoteSettingRefreshHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 在我游玩原神时不通知我 的本地化字符串。
/// </summary>
@@ -5317,7 +5506,7 @@ namespace Snap.Hutao.Resource.Localization {
}
/// <summary>
/// 查找类似 HoYoLab 的本地化字符串。
/// 查找类似 HoYoLAB 的本地化字符串。
/// </summary>
internal static string ViewUserCookieOperation2 {
get {
@@ -5325,6 +5514,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 当前用户 的本地化字符串。
/// </summary>
internal static string ViewUserCookieOperation3 {
get {
return ResourceManager.GetString("ViewUserCookieOperation3", resourceCulture);
}
}
/// <summary>
/// 查找类似 网页登录 的本地化字符串。
/// </summary>
@@ -5352,6 +5550,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 领取签到奖励 的本地化字符串。
/// </summary>
internal static string ViewUserCookieOperationSignInRewardAction {
get {
return ResourceManager.GetString("ViewUserCookieOperationSignInRewardAction", resourceCulture);
}
}
/// <summary>
/// 查找类似 复制 Cookie 的本地化字符串。
/// </summary>
@@ -5478,6 +5685,51 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 (?:〓活动时间〓|〓任务开放时间〓).*?\d\.\d版本更新(?:完成|)后永久开放 的本地化字符串。
/// </summary>
internal static string WebAnnouncementMatchPermanentActivityTime {
get {
return ResourceManager.GetString("WebAnnouncementMatchPermanentActivityTime", resourceCulture);
}
}
/// <summary>
/// 查找类似 〓活动时间〓.*?\d\.\d版本期间持续开放 的本地化字符串。
/// </summary>
internal static string WebAnnouncementMatchPersistentActivityTime {
get {
return ResourceManager.GetString("WebAnnouncementMatchPersistentActivityTime", resourceCulture);
}
}
/// <summary>
/// 查找类似 (?:〓活动时间〓|祈愿时间|【上架时间】).*?(\d\.\d版本更新后|\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}).*?~.*?&amp;lt;t class=&quot;t_(?:gl|lc)&quot;.*?&amp;gt;(.*?)&amp;lt;/t&amp;gt; 的本地化字符串。
/// </summary>
internal static string WebAnnouncementMatchTransientActivityTime {
get {
return ResourceManager.GetString("WebAnnouncementMatchTransientActivityTime", resourceCulture);
}
}
/// <summary>
/// 查找类似 〓更新时间〓.+?&amp;lt;t class=\&quot;t_(?:gl|lc)\&quot;.*?&amp;gt;(.*?)&amp;lt;/t&amp;gt; 的本地化字符串。
/// </summary>
internal static string WebAnnouncementMatchVersionUpdateTime {
get {
return ResourceManager.GetString("WebAnnouncementMatchVersionUpdateTime", resourceCulture);
}
}
/// <summary>
/// 查找类似 \d\.\d版本更新说明 的本地化字符串。
/// </summary>
internal static string WebAnnouncementMatchVersionUpdateTitle {
get {
return ResourceManager.GetString("WebAnnouncementMatchVersionUpdateTitle", resourceCulture);
}
}
/// <summary>
/// 查找类似 {0} 天后开始 的本地化字符串。
/// </summary>
@@ -5568,6 +5820,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 尚未解锁洞天 的本地化字符串。
/// </summary>
internal static string WebDailyNoteHomeLocked {
get {
return ResourceManager.GetString("WebDailyNoteHomeLocked", resourceCulture);
}
}
/// <summary>
/// 查找类似 今天 的本地化字符串。
/// </summary>
@@ -5658,6 +5919,24 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 尚未获得 的本地化字符串。
/// </summary>
internal static string WebDailyNoteTransformerNotObtained {
get {
return ResourceManager.GetString("WebDailyNoteTransformerNotObtained", resourceCulture);
}
}
/// <summary>
/// 查找类似 尚未获得参量质变仪 的本地化字符串。
/// </summary>
internal static string WebDailyNoteTransformerNotObtainedDetail {
get {
return ResourceManager.GetString("WebDailyNoteTransformerNotObtainedDetail", resourceCulture);
}
}
/// <summary>
/// 查找类似 冷却中 的本地化字符串。
/// </summary>
@@ -5820,6 +6099,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 请刷新 Cookie原始消息{0} 的本地化字符串。
/// </summary>
internal static string WebResponseRefreshCookieHintFormat {
get {
return ResourceManager.GetString("WebResponseRefreshCookieHintFormat", resourceCulture);
}
}
/// <summary>
/// 查找类似 [{0}] 中的 [{1}] 网络请求异常,请稍后再试 的本地化字符串。
/// </summary>

View File

@@ -237,6 +237,12 @@
<data name="ModelBindingUserInitializationFailed" xml:space="preserve">
<value>网络异常</value>
</data>
<data name="ModelEntityDailyNoteNotRefreshed" xml:space="preserve">
<value>尚未刷新</value>
</data>
<data name="ModelEntityDailyNoteRefreshTimeFormat" xml:space="preserve">
<value>刷新于 {0:yyyy/MM/dd HH:mm:ss}</value>
</data>
<data name="ModelEntitySpiralAbyssScheduleFormat" xml:space="preserve">
<value>第 {0} 期</value>
</data>
@@ -366,6 +372,9 @@
<value>单手剑</value>
<comment>Need EXACT same string in game</comment>
</data>
<data name="ModelMetadataAvatarPlayerName" xml:space="preserve">
<value>旅行者</value>
</data>
<data name="ModelMetadataFetterInfoBirthdayFormat" xml:space="preserve">
<value>{0} 月 {1} 日</value>
</data>
@@ -521,6 +530,24 @@
<value>风元素抗性</value>
<comment>Need EXACT same string in game</comment>
</data>
<data name="ServiceAvatarInfoSummaryCalculatorNotRefreshed" xml:space="preserve">
<value>养成计算:尚未刷新</value>
</data>
<data name="ServiceAvatarInfoSummaryCalculatorRefreshTimeFormat" xml:space="preserve">
<value>养成计算:{0:MM-dd HH:mm}</value>
</data>
<data name="ServiceAvatarInfoSummaryGameRecordNotRefreshed" xml:space="preserve">
<value>我的角色:尚未刷新</value>
</data>
<data name="ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat" xml:space="preserve">
<value>我的角色:{0:MM-dd HH:mm}</value>
</data>
<data name="ServiceAvatarInfoSummaryShowcaseNotRefreshed" xml:space="preserve">
<value>角色橱窗:尚未刷新</value>
</data>
<data name="ServiceAvatarInfoSummaryShowcaseRefreshTimeFormat" xml:space="preserve">
<value>角色橱窗:{0:MM-dd HH:mm}</value>
</data>
<data name="ServiceCultivationProjectCurrentUserdataCourrpted" xml:space="preserve">
<value>保存养成计划状态失败</value>
</data>
@@ -599,6 +626,9 @@
<data name="ServiceGachaLogHutaoCloudServiceNotAllowed" xml:space="preserve">
<value>祈愿记录上传服务不可用</value>
</data>
<data name="ServiceGachaLogUIGFImportItemInvalidFormat" xml:space="preserve">
<value>数据包含异常物品, Id{0}</value>
</data>
<data name="ServiceGachaLogUrlProviderAuthkeyRequestFailed" xml:space="preserve">
<value>请求验证密钥失败</value>
</data>
@@ -644,6 +674,15 @@
<data name="ServiceGameLocatorUnityLogGamePathNotFound" xml:space="preserve">
<value>在 Unity 日志文件中找不到游戏路径</value>
</data>
<data name="ServiceGamePackageConvertMoveFileBackupFormat" xml:space="preserve">
<value>备份:{0}</value>
</data>
<data name="ServiceGamePackageConvertMoveFileRenameFormat" xml:space="preserve">
<value>重命名:{0} 到:{1}</value>
</data>
<data name="ServiceGamePackageConvertMoveFileRestoreFormat" xml:space="preserve">
<value>替换:{0}</value>
</data>
<data name="ServiceGamePackageRenameDataFolderFailed" xml:space="preserve">
<value>重命名数据文件夹名称失败</value>
</data>
@@ -704,6 +743,15 @@
<data name="ServiceMetadataVersionNotSupported" xml:space="preserve">
<value>你的胡桃版本过低,请尽快升级</value>
</data>
<data name="ServiceSignInClaimRewardFailedFormat" xml:space="preserve">
<value>签到失败,{0}</value>
</data>
<data name="ServiceSignInRewardListRequestFailed" xml:space="preserve">
<value>获取奖励列表失败</value>
</data>
<data name="ServiceSignInSuccessRewardFormat" xml:space="preserve">
<value>签到成功,{0}×{1}</value>
</data>
<data name="ServiceUserCurrentMultiMatched" xml:space="preserve">
<value>多个用户记录为选中状态</value>
</data>
@@ -1016,6 +1064,9 @@
<data name="ViewModelDailyNoteRegisterTaskFail" xml:space="preserve">
<value>注册计划任务失败,请使用管理员模式重试</value>
</data>
<data name="ViewModelDailyNoteRequestProgressTitle" xml:space="preserve">
<value>正在获取实时便笺信息,请稍候</value>
</data>
<data name="ViewModelExportSuccessMessage" xml:space="preserve">
<value>成功保存到指定位置</value>
</data>
@@ -1307,6 +1358,18 @@
<data name="ViewPageDailyNoteResinDiscountUsed" xml:space="preserve">
<value>本周已消耗减半次数</value>
</data>
<data name="ViewPageDailyNoteSettingAutoRefresh" xml:space="preserve">
<value>自动刷新</value>
</data>
<data name="ViewPageDailyNoteSettingAutoRefreshDescription" xml:space="preserve">
<value>间隔选定的时间后刷新添加的实时便笺</value>
</data>
<data name="ViewPageDailyNoteSettingRefreshElevatedHint" xml:space="preserve">
<value>这些选项仅允许在非管理员模式下更改</value>
</data>
<data name="ViewPageDailyNoteSettingRefreshHeader" xml:space="preserve">
<value>刷新</value>
</data>
<data name="ViewPageDailyNoteSlientModeDescription" xml:space="preserve">
<value>在我游玩原神时不通知我</value>
</data>
@@ -1926,7 +1989,10 @@
<value>米游社</value>
</data>
<data name="ViewUserCookieOperation2" xml:space="preserve">
<value>HoYoLab</value>
<value>HoYoLAB</value>
</data>
<data name="ViewUserCookieOperation3" xml:space="preserve">
<value>当前用户</value>
</data>
<data name="ViewUserCookieOperationLoginMihoyoUserAction" xml:space="preserve">
<value>网页登录</value>
@@ -1937,6 +2003,9 @@
<data name="ViewUserCookieOperationRefreshCookieAction" xml:space="preserve">
<value>刷新 Cookie</value>
</data>
<data name="ViewUserCookieOperationSignInRewardAction" xml:space="preserve">
<value>领取签到奖励</value>
</data>
<data name="ViewUserCopyCookieAction" xml:space="preserve">
<value>复制 Cookie</value>
</data>
@@ -1979,6 +2048,21 @@
<data name="ViewWikiWeaponHeader" xml:space="preserve">
<value>武器资料</value>
</data>
<data name="WebAnnouncementMatchPermanentActivityTime" xml:space="preserve">
<value>(?:〓活动时间〓|〓任务开放时间〓).*?\d\.\d版本更新(?:完成|)后永久开放</value>
</data>
<data name="WebAnnouncementMatchPersistentActivityTime" xml:space="preserve">
<value>〓活动时间〓.*?\d\.\d版本期间持续开放</value>
</data>
<data name="WebAnnouncementMatchTransientActivityTime" xml:space="preserve">
<value>(?:〓活动时间〓|祈愿时间|【上架时间】).*?(\d\.\d版本更新后|\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}).*?~.*?&amp;lt;t class="t_(?:gl|lc)".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
</data>
<data name="WebAnnouncementMatchVersionUpdateTime" xml:space="preserve">
<value>〓更新时间〓.+?&amp;lt;t class=\"t_(?:gl|lc)\".*?&amp;gt;(.*?)&amp;lt;/t&amp;gt;</value>
</data>
<data name="WebAnnouncementMatchVersionUpdateTitle" xml:space="preserve">
<value>\d\.\d版本更新说明</value>
</data>
<data name="WebAnnouncementTimeDaysBeginFormat" xml:space="preserve">
<value>{0} 天后开始</value>
</data>
@@ -2009,6 +2093,9 @@
<data name="WebDailyNoteHomeCoinRecoveryFormat" xml:space="preserve">
<value>预计 {0} {1:HH:mm} 达到存储上限</value>
</data>
<data name="WebDailyNoteHomeLocked" xml:space="preserve">
<value>尚未解锁洞天</value>
</data>
<data name="WebDailyNoteRecoveryTimeDay0" xml:space="preserve">
<value>今天</value>
</data>
@@ -2039,6 +2126,12 @@
<data name="WebDailyNoteTransformerMinutesFormat" xml:space="preserve">
<value>{0} 分</value>
</data>
<data name="WebDailyNoteTransformerNotObtained" xml:space="preserve">
<value>尚未获得</value>
</data>
<data name="WebDailyNoteTransformerNotObtainedDetail" xml:space="preserve">
<value>尚未获得参量质变仪</value>
</data>
<data name="WebDailyNoteTransformerNotReached" xml:space="preserve">
<value>冷却中</value>
</data>
@@ -2093,6 +2186,9 @@
<data name="WebResponseFormat" xml:space="preserve">
<value>状态:{0} | 信息:{1}</value>
</data>
<data name="WebResponseRefreshCookieHintFormat" xml:space="preserve">
<value>请刷新 Cookie原始消息{0}</value>
</data>
<data name="WebResponseRequestExceptionFormat" xml:space="preserve">
<value>[{0}] 中的 [{1}] 网络请求异常,请稍后再试</value>
</data>

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@@ -3,7 +3,6 @@
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using System.Collections.ObjectModel;
namespace Snap.Hutao.Service.Achievement;

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.InterChange.Achievement;
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;

View File

@@ -3,9 +3,7 @@
using Snap.Hutao.Core;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.Diagnostics;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.ViewModel.Achievement;
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.ViewModel.Achievement;
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;

View File

@@ -5,6 +5,7 @@ using Microsoft.Extensions.Caching.Memory;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
using Snap.Hutao.Web.Response;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
@@ -55,8 +56,7 @@ internal sealed partial class AnnouncementService : IAnnouncementService
// 将活动公告置于前方
wrapper.List.Reverse();
// 将公告内容联入公告列表
JoinAnnouncements(contentMap, wrapper.List);
PreprocessAnnouncements(contentMap, wrapper.List);
return memoryCache.Set(CacheKey, wrapper, TimeSpan.FromMinutes(30));
}
@@ -65,29 +65,92 @@ internal sealed partial class AnnouncementService : IAnnouncementService
return default!;
}
private static void JoinAnnouncements(Dictionary<int, string> contentMap, List<AnnouncementListWrapper> announcementListWrappers)
private static void PreprocessAnnouncements(Dictionary<int, string> contentMap, List<AnnouncementListWrapper> announcementListWrappers)
{
Regex timeTagRegex = XmlTagRegex();
// 将公告内容联入公告列表
foreach (ref AnnouncementListWrapper listWrapper in CollectionsMarshal.AsSpan(announcementListWrappers))
{
foreach (ref Announcement item in CollectionsMarshal.AsSpan(listWrapper.List))
{
contentMap.TryGetValue(item.AnnId, out string? rawContent);
item.Content = rawContent ?? string.Empty;
}
}
AdjustAnnouncementTime(announcementListWrappers);
foreach (ref AnnouncementListWrapper listWrapper in CollectionsMarshal.AsSpan(announcementListWrappers))
{
foreach (ref Announcement item in CollectionsMarshal.AsSpan(listWrapper.List))
{
if (contentMap.TryGetValue(item.AnnId, out string? rawContent))
{
// remove <t/> tag
rawContent = timeTagRegex.Replace(rawContent, x => x.Groups[1].Value);
}
item.Content = rawContent ?? string.Empty;
item.Content = AnnouncementRegex.XmlTimeTagRegex.Replace(item.Content, x => x.Groups[1].Value);
}
}
}
/// <summary>
/// 匹配特殊的时间格式: <t.*?>(.*?)</t>
/// </summary>
/// <returns>正则</returns>
[GeneratedRegex("&lt;t class=\"t_(?:gl|lc)\".*?&gt;(.*?)&lt;/t&gt;", RegexOptions.Multiline)]
private static partial Regex XmlTagRegex();
private static void AdjustAnnouncementTime(List<AnnouncementListWrapper> announcementListWrappers)
{
// 活动公告
List<Announcement> activities = announcementListWrappers
.Single(wrapper => wrapper.TypeId == 1)
.List;
// 更新公告
Announcement versionUpdate = announcementListWrappers
.Single(wrapper => wrapper.TypeId == 2)
.List
.Single(ann => AnnouncementRegex.VersionUpdateTitleRegex.IsMatch(ann.Title));
if (AnnouncementRegex.VersionUpdateTimeRegex.Match(versionUpdate.Content) is { Success: true } match)
{
DateTimeOffset versionUpdateTime = DateTimeOffset.Parse(match.Groups[1].ValueSpan, CultureInfo.InvariantCulture);
_ = 1;
foreach (ref readonly Announcement announcement in CollectionsMarshal.AsSpan(activities))
{
if (AnnouncementRegex.PermanentActivityTimeRegex.Match(announcement.Content) is { Success: true } permanent)
{
announcement.StartTime = versionUpdateTime;
continue;
}
if (AnnouncementRegex.PersistentActivityTimeRegex.Match(announcement.Content) is { Success: true } persistent)
{
announcement.StartTime = versionUpdateTime;
announcement.EndTime = versionUpdateTime + TimeSpan.FromDays(42);
continue;
}
if (AnnouncementRegex.TransientActivityTimeRegex.Match(announcement.Content) is { Success: true } transient)
{
announcement.StartTime = versionUpdateTime;
announcement.EndTime = DateTimeOffset.Parse(transient.Groups[2].ValueSpan, CultureInfo.InvariantCulture);
continue;
}
MatchCollection matches = AnnouncementRegex.XmlTimeTagRegex.Matches(announcement.Content);
if (matches.Count >= 2)
{
List<DateTimeOffset> dateTimes = matches.Select(match => DateTimeOffset.Parse(match.Groups[1].ValueSpan, CultureInfo.InvariantCulture)).ToList();
DateTimeOffset min = DateTimeOffset.MaxValue;
DateTimeOffset max = DateTimeOffset.MinValue;
foreach (DateTimeOffset time in dateTimes)
{
if (time < min)
{
min = time;
}
if (time > max)
{
max = time;
}
}
announcement.StartTime = min;
announcement.EndTime = max;
}
}
}
}
}

View File

@@ -13,9 +13,10 @@ using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
using Snap.Hutao.Web.Response;
using System.Runtime.CompilerServices;
using Windows.Perception.Spatial;
using CalculateAvatar = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.Avatar;
using EnkaAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
using ModelAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo;
using EntityAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo;
using RecordCharacter = Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar.Character;
using RecordPlayerInfo = Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.PlayerInfo;
@@ -39,10 +40,10 @@ internal sealed partial class AvatarInfoDbBulkOperation
/// <param name="webInfos">Enka信息</param>
/// <param name="token">取消令牌</param>
/// <returns>角色列表</returns>
public List<EnkaAvatarInfo> UpdateDbAvatarInfos(string uid, IEnumerable<EnkaAvatarInfo> webInfos, CancellationToken token)
public List<EntityAvatarInfo> UpdateDbAvatarInfosByShowcase(string uid, IEnumerable<EnkaAvatarInfo> webInfos, CancellationToken token)
{
token.ThrowIfCancellationRequested();
List<ModelAvatarInfo> dbInfos = avatarInfoDbService.GetAvatarInfoListByUid(uid);
List<EntityAvatarInfo> dbInfos = avatarInfoDbService.GetAvatarInfoListByUid(uid);
EnsureItemsAvatarIdDistinct(ref dbInfos, uid);
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -57,12 +58,12 @@ internal sealed partial class AvatarInfoDbBulkOperation
}
token.ThrowIfCancellationRequested();
ModelAvatarInfo? entity = dbInfos.SingleOrDefault(i => i.Info.AvatarId == webInfo.AvatarId);
EntityAvatarInfo? entity = dbInfos.SingleOrDefault(i => i.Info.AvatarId == webInfo.AvatarId);
AddOrUpdateAvatarInfo(entity, uid, appDbContext, webInfo);
}
token.ThrowIfCancellationRequested();
return avatarInfoDbService.GetAvatarInfoInfoListByUid(uid);
return avatarInfoDbService.GetAvatarInfoListByUid(uid);
}
}
@@ -72,11 +73,11 @@ internal sealed partial class AvatarInfoDbBulkOperation
/// <param name="userAndUid">用户与角色</param>
/// <param name="token">取消令牌</param>
/// <returns>角色列表</returns>
public async ValueTask<List<EnkaAvatarInfo>> UpdateDbAvatarInfosByGameRecordCharacterAsync(UserAndUid userAndUid, CancellationToken token)
public async ValueTask<List<EntityAvatarInfo>> UpdateDbAvatarInfosByGameRecordCharacterAsync(UserAndUid userAndUid, CancellationToken token)
{
token.ThrowIfCancellationRequested();
string uid = userAndUid.Uid.Value;
List<ModelAvatarInfo> dbInfos = avatarInfoDbService.GetAvatarInfoListByUid(uid);
List<EntityAvatarInfo> dbInfos = avatarInfoDbService.GetAvatarInfoListByUid(uid);
EnsureItemsAvatarIdDistinct(ref dbInfos, uid);
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -113,14 +114,14 @@ internal sealed partial class AvatarInfoDbBulkOperation
}
token.ThrowIfCancellationRequested();
ModelAvatarInfo? entity = dbInfos.SingleOrDefault(i => i.Info.AvatarId == character.Id);
EntityAvatarInfo? entity = dbInfos.SingleOrDefault(i => i.Info.AvatarId == character.Id);
AddOrUpdateAvatarInfo(entity, character.Id, uid, appDbContext, transformer, character);
}
}
}
}
return avatarInfoDbService.GetAvatarInfoInfoListByUid(uid);
return avatarInfoDbService.GetAvatarInfoListByUid(uid);
}
/// <summary>
@@ -129,11 +130,11 @@ internal sealed partial class AvatarInfoDbBulkOperation
/// <param name="userAndUid">用户与角色</param>
/// <param name="token">取消令牌</param>
/// <returns>角色列表</returns>
public async ValueTask<List<EnkaAvatarInfo>> UpdateDbAvatarInfosByCalculateAvatarDetailAsync(UserAndUid userAndUid, CancellationToken token)
public async ValueTask<List<EntityAvatarInfo>> UpdateDbAvatarInfosByCalculateAvatarDetailAsync(UserAndUid userAndUid, CancellationToken token)
{
token.ThrowIfCancellationRequested();
string uid = userAndUid.Uid.Value;
List<ModelAvatarInfo> dbInfos = avatarInfoDbService.GetAvatarInfoListByUid(uid);
List<EntityAvatarInfo> dbInfos = avatarInfoDbService.GetAvatarInfoListByUid(uid);
EnsureItemsAvatarIdDistinct(ref dbInfos, uid);
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -167,22 +168,40 @@ internal sealed partial class AvatarInfoDbBulkOperation
}
token.ThrowIfCancellationRequested();
ModelAvatarInfo? entity = dbInfos.SingleOrDefault(i => i.Info.AvatarId == avatar.Id);
EntityAvatarInfo? entity = dbInfos.SingleOrDefault(i => i.Info.AvatarId == avatar.Id);
AddOrUpdateAvatarInfo(entity, avatar.Id, uid, appDbContext, transformer, detailAvatarResponse.Data);
}
}
return avatarInfoDbService.GetAvatarInfoInfoListByUid(uid);
return avatarInfoDbService.GetAvatarInfoListByUid(uid);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AddOrUpdateAvatarInfo<TSource>(ModelAvatarInfo? entity, in AvatarId avatarId, string uid, AppDbContext appDbContext, IAvatarInfoTransformer<TSource> transformer, TSource source)
private static void AddOrUpdateAvatarInfo(EntityAvatarInfo? entity, string uid, AppDbContext appDbContext, EnkaAvatarInfo webInfo)
{
if (entity is null)
{
entity = EntityAvatarInfo.From(uid, webInfo);
entity.ShowcaseRefreshTime = DateTimeOffset.Now;
appDbContext.AvatarInfos.AddAndSave(entity);
}
else
{
entity.Info = webInfo;
entity.ShowcaseRefreshTime = DateTimeOffset.Now;
appDbContext.AvatarInfos.UpdateAndSave(entity);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AddOrUpdateAvatarInfo(EntityAvatarInfo? entity, in AvatarId avatarId, string uid, AppDbContext appDbContext, CalculateAvatarDetailAvatarInfoTransformer transformer, AvatarDetail source)
{
if (entity is null)
{
EnkaAvatarInfo avatarInfo = new() { AvatarId = avatarId };
transformer.Transform(ref avatarInfo, source);
entity = ModelAvatarInfo.From(uid, avatarInfo);
entity = EntityAvatarInfo.From(uid, avatarInfo);
entity.CalculatorRefreshTime = DateTimeOffset.Now;
appDbContext.AvatarInfos.AddAndSave(entity);
}
else
@@ -190,26 +209,33 @@ internal sealed partial class AvatarInfoDbBulkOperation
EnkaAvatarInfo avatarInfo = entity.Info;
transformer.Transform(ref avatarInfo, source);
entity.Info = avatarInfo;
entity.CalculatorRefreshTime = DateTimeOffset.Now;
appDbContext.AvatarInfos.UpdateAndSave(entity);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AddOrUpdateAvatarInfo(ModelAvatarInfo? entity, string uid, AppDbContext appDbContext, EnkaAvatarInfo webInfo)
private static void AddOrUpdateAvatarInfo(EntityAvatarInfo? entity, in AvatarId avatarId, string uid, AppDbContext appDbContext, GameRecordCharacterAvatarInfoTransformer transformer, Character source)
{
if (entity is null)
{
entity = ModelAvatarInfo.From(uid, webInfo);
EnkaAvatarInfo avatarInfo = new() { AvatarId = avatarId };
transformer.Transform(ref avatarInfo, source);
entity = EntityAvatarInfo.From(uid, avatarInfo);
entity.GameRecordRefreshTime = DateTimeOffset.Now;
appDbContext.AvatarInfos.AddAndSave(entity);
}
else
{
entity.Info = webInfo;
EnkaAvatarInfo avatarInfo = entity.Info;
transformer.Transform(ref avatarInfo, source);
entity.Info = avatarInfo;
entity.GameRecordRefreshTime = DateTimeOffset.Now;
appDbContext.AvatarInfos.UpdateAndSave(entity);
}
}
private void EnsureItemsAvatarIdDistinct(ref List<ModelAvatarInfo> dbInfos, string uid)
private void EnsureItemsAvatarIdDistinct(ref List<EntityAvatarInfo> dbInfos, string uid)
{
int distinctCount = dbInfos.Select(info => info.Info.AvatarId).ToHashSet().Count;

View File

@@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity.Database;
using EnkaAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
using ModelAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo;
using EntityAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo;
namespace Snap.Hutao.Service.AvatarInfo;
@@ -15,7 +15,7 @@ internal sealed partial class AvatarInfoDbService : IAvatarInfoDbService
{
private readonly IServiceProvider serviceProvider;
public List<ModelAvatarInfo> GetAvatarInfoListByUid(string uid)
public List<EntityAvatarInfo> GetAvatarInfoListByUid(string uid)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
@@ -24,19 +24,6 @@ internal sealed partial class AvatarInfoDbService : IAvatarInfoDbService
}
}
public List<EnkaAvatarInfo> GetAvatarInfoInfoListByUid(string uid)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return appDbContext.AvatarInfos
.AsNoTracking()
.Where(i => i.Uid == uid)
.Select(i => i.Info)
.ToList();
}
}
public void DeleteAvatarInfoRangeByUid(string uid)
{
using (IServiceScope scope = serviceProvider.CreateScope())

View File

@@ -10,6 +10,7 @@ using Snap.Hutao.Web.Enka;
using Snap.Hutao.Web.Enka.Model;
using Snap.Hutao.Web.Hoyolab;
using EnkaAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
using EntityAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo;
namespace Snap.Hutao.Service.AvatarInfo;
@@ -56,28 +57,28 @@ internal sealed partial class AvatarInfoService : IAvatarInfoService
return new(RefreshResult.ShowcaseNotOpen, default);
}
List<EnkaAvatarInfo> list = avatarInfoDbBulkOperation.UpdateDbAvatarInfos(userAndUid.Uid.Value, resp.AvatarInfoList, token);
List<EntityAvatarInfo> list = avatarInfoDbBulkOperation.UpdateDbAvatarInfosByShowcase(userAndUid.Uid.Value, resp.AvatarInfoList, token);
Summary summary = await GetSummaryCoreAsync(list, token).ConfigureAwait(false);
return new(RefreshResult.Ok, summary);
}
case RefreshOption.RequestFromHoyolabGameRecord:
{
List<EnkaAvatarInfo> list = await avatarInfoDbBulkOperation.UpdateDbAvatarInfosByGameRecordCharacterAsync(userAndUid, token).ConfigureAwait(false);
List<EntityAvatarInfo> list = await avatarInfoDbBulkOperation.UpdateDbAvatarInfosByGameRecordCharacterAsync(userAndUid, token).ConfigureAwait(false);
Summary summary = await GetSummaryCoreAsync(list, token).ConfigureAwait(false);
return new(RefreshResult.Ok, summary);
}
case RefreshOption.RequestFromHoyolabCalculate:
{
List<EnkaAvatarInfo> list = await avatarInfoDbBulkOperation.UpdateDbAvatarInfosByCalculateAvatarDetailAsync(userAndUid, token).ConfigureAwait(false);
List<EntityAvatarInfo> list = await avatarInfoDbBulkOperation.UpdateDbAvatarInfosByCalculateAvatarDetailAsync(userAndUid, token).ConfigureAwait(false);
Summary summary = await GetSummaryCoreAsync(list, token).ConfigureAwait(false);
return new(RefreshResult.Ok, summary);
}
default:
{
List<EnkaAvatarInfo> list = avatarInfoDbService.GetAvatarInfoInfoListByUid(userAndUid.Uid.Value);
List<EntityAvatarInfo> list = avatarInfoDbService.GetAvatarInfoListByUid(userAndUid.Uid.Value);
Summary summary = await GetSummaryCoreAsync(list, token).ConfigureAwait(false);
token.ThrowIfCancellationRequested();
return new(RefreshResult.Ok, summary.Avatars.Count == 0 ? null : summary);
@@ -96,7 +97,7 @@ internal sealed partial class AvatarInfoService : IAvatarInfoService
?? await enkaClient.GetDataAsync(uid, token).ConfigureAwait(false);
}
private async ValueTask<Summary> GetSummaryCoreAsync(IEnumerable<EnkaAvatarInfo> avatarInfos, CancellationToken token)
private async ValueTask<Summary> GetSummaryCoreAsync(IEnumerable<Model.Entity.AvatarInfo> avatarInfos, CancellationToken token)
{
using (ValueStopwatch.MeasureExecution(logger))
{

View File

@@ -17,5 +17,5 @@ internal interface ISummaryFactory
/// <param name="avatarInfos">角色列表</param>
/// <param name="token">取消令牌</param>
/// <returns>简述对象</returns>
ValueTask<Summary> CreateAsync(IEnumerable<Web.Enka.Model.AvatarInfo> avatarInfos, CancellationToken token);
ValueTask<Summary> CreateAsync(IEnumerable<Model.Entity.AvatarInfo> avatarInfos, CancellationToken token);
}

View File

@@ -6,6 +6,7 @@ using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.ViewModel.AvatarProperty;
using Snap.Hutao.Web.Enka.Model;
using EntityAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo;
using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon;
using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
@@ -21,7 +22,12 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory;
[HighQuality]
internal sealed class SummaryAvatarFactory
{
private static readonly DateTimeOffset DefaultRefreshTime = new(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0));
private readonly ModelAvatarInfo avatarInfo;
private readonly DateTimeOffset showcaseRefreshTime;
private readonly DateTimeOffset gameRecordRefreshTime;
private readonly DateTimeOffset calculatorRefreshTime;
private readonly SummaryMetadataContext metadataContext;
/// <summary>
@@ -29,10 +35,14 @@ internal sealed class SummaryAvatarFactory
/// </summary>
/// <param name="metadataContext">元数据上下文</param>
/// <param name="avatarInfo">角色信息</param>
public SummaryAvatarFactory(SummaryMetadataContext metadataContext, ModelAvatarInfo avatarInfo)
public SummaryAvatarFactory(SummaryMetadataContext metadataContext, EntityAvatarInfo avatarInfo)
{
this.metadataContext = metadataContext;
this.avatarInfo = avatarInfo;
this.avatarInfo = avatarInfo.Info;
showcaseRefreshTime = avatarInfo.ShowcaseRefreshTime;
gameRecordRefreshTime = avatarInfo.GameRecordRefreshTime;
calculatorRefreshTime = avatarInfo.CalculatorRefreshTime;
}
/// <summary>
@@ -67,6 +77,17 @@ internal sealed class SummaryAvatarFactory
Weapon = reliquaryAndWeapon.Weapon,
Reliquaries = reliquaryAndWeapon.Reliquaries,
Score = $"{reliquaryAndWeapon.Reliquaries.Sum(r => r.Score):F2}",
// times
ShowcaseRefreshTimeFormat = showcaseRefreshTime == DateTimeOffsetExtension.DatebaseDefaultTime
? SH.ServiceAvatarInfoSummaryShowcaseNotRefreshed
: SH.ServiceAvatarInfoSummaryShowcaseRefreshTimeFormat.Format(showcaseRefreshTime),
GameRecordRefreshTimeFormat = gameRecordRefreshTime == DateTimeOffsetExtension.DatebaseDefaultTime
? SH.ServiceAvatarInfoSummaryGameRecordNotRefreshed
: SH.ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat.Format(gameRecordRefreshTime),
CalculatorRefreshTimeFormat = calculatorRefreshTime == DateTimeOffsetExtension.DatebaseDefaultTime
? SH.ServiceAvatarInfoSummaryCalculatorNotRefreshed
: SH.ServiceAvatarInfoSummaryCalculatorRefreshTimeFormat.Format(calculatorRefreshTime),
};
ApplyCostumeIconOrDefault(ref propertyAvatar, avatar);

View File

@@ -4,7 +4,6 @@
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.ViewModel.AvatarProperty;
using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
namespace Snap.Hutao.Service.AvatarInfo.Factory;
@@ -19,7 +18,7 @@ internal sealed partial class SummaryFactory : ISummaryFactory
private readonly IMetadataService metadataService;
/// <inheritdoc/>
public async ValueTask<Summary> CreateAsync(IEnumerable<ModelAvatarInfo> avatarInfos, CancellationToken token)
public async ValueTask<Summary> CreateAsync(IEnumerable<Model.Entity.AvatarInfo> avatarInfos, CancellationToken token)
{
SummaryMetadataContext metadataContext = new()
{
@@ -35,14 +34,14 @@ internal sealed partial class SummaryFactory : ISummaryFactory
return new()
{
Avatars = avatarInfos
.Where(a => !AvatarIds.IsPlayer(a.AvatarId))
.Where(a => !AvatarIds.IsPlayer(a.Info.AvatarId))
.Select(a => new SummaryAvatarFactory(metadataContext, a).Create())
.OrderByDescending(a => a.LevelNumber)
.ThenByDescending(a => a.Name)
.ThenBy(a => a.Name)
.ToList(),
// .ThenByDescending(a => a.Quality)
// .ThenByDescending(a => a.ActivatedConstellationCount)
// .ThenByDescending(a => a.Quality)
// .ThenByDescending(a => a.ActivatedConstellationCount)
};
}
}

View File

@@ -23,10 +23,7 @@ internal static class SummaryHelper
/// <returns>命之座</returns>
public static List<ConstellationView> CreateConstellations(List<Skill> talents, List<SkillId>? talentIds)
{
if (talentIds.IsNullOrEmpty())
{
return new();
}
talentIds ??= new();
return talents.SelectList(talent => new ConstellationView()
{

View File

@@ -1,13 +1,13 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Intrinsic.Format;
using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Model.Metadata.Reliquary;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.ViewModel.AvatarProperty;
using Snap.Hutao.Web.Enka.Model;
using System.Runtime.InteropServices;
using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
@@ -57,7 +57,7 @@ internal sealed class SummaryReliquaryFactory
Description = reliquary.Description,
// EquipBase
Level = $"+{equip.Reliquary.Level - 1}",
Level = $"+{equip.Reliquary.Level - 1U}",
Quality = reliquary.RankLevel,
};
@@ -67,7 +67,7 @@ internal sealed class SummaryReliquaryFactory
result.SecondarySubProperties = subProperty.GetRange(^affixCount..);
ArgumentNullException.ThrowIfNull(equip.Flat.ReliquarySubstats);
result.ComposedSubProperties = equip.Flat.ReliquarySubstats.SelectList(CreateComposedSubProperty);
result.ComposedSubProperties = CreateComposedSubProperties(equip.Reliquary.AppendPropIdList);
ReliquaryMainAffixLevel relicLevel = metadataContext.ReliquaryLevels.Single(r => r.Level == equip.Reliquary.Level && r.Rank == reliquary.RankLevel);
FightProperty property = metadataContext.IdReliquaryMainAffixMap[equip.Reliquary.MainPropId];
@@ -112,11 +112,36 @@ internal sealed class SummaryReliquaryFactory
};
}
private List<ReliquaryComposedSubProperty> CreateComposedSubProperties(List<ReliquarySubAffixId> appendProps)
{
List<SummaryReliquarySubPropertyCompositionInfo> infos = new();
foreach (ref readonly ReliquarySubAffixId subAffixId in CollectionsMarshal.AsSpan(appendProps))
{
ReliquarySubAffix subAffix = metadataContext.IdReliquarySubAffixMap[subAffixId];
SummaryReliquarySubPropertyCompositionInfo info = infos.SingleOrAdd(prop => prop.Type == subAffix.Type, () => new(subAffix.Type));
info.Count += 1;
info.Value += subAffix.Value;
}
if (infos.Count > 4)
{
ThrowHelper.InvalidOperation("无效的圣遗物数据");
}
List<ReliquaryComposedSubProperty> results = new();
foreach (ref readonly SummaryReliquarySubPropertyCompositionInfo info in CollectionsMarshal.AsSpan(infos))
{
results.Add(info.ToReliquaryComposedSubProperty());
}
return results;
}
private float ScoreReliquary(FightProperty property, MetadataReliquary reliquary, ReliquaryMainAffixLevel relicLevel, List<ReliquarySubProperty> subProperties)
{
// 沙/杯/头
// equip.Flat.EquipType is EquipType.EQUIP_SHOES or EquipType.EQUIP_RING or EquipType.EQUIP_DRESS
if (equip.Flat.EquipType > EquipType.EQUIP_SHOES)
if (equip.Flat.EquipType >= EquipType.EQUIP_SHOES)
{
// 从喵插件抓取的圣遗物评分权重
// 部分复杂的角色暂时使用了默认值
@@ -138,29 +163,13 @@ internal sealed class SummaryReliquaryFactory
}
}
private ReliquarySubProperty CreateComposedSubProperty(ReliquarySubstat substat)
{
FormatMethod method = substat.AppendPropId.GetFormatMethod();
string valueFormatted = method switch
{
FormatMethod.Integer => $"{MathF.Round(substat.StatValue, MidpointRounding.AwayFromZero)}",
FormatMethod.Percent => $"{substat.StatValue}%",
_ => $"{substat.StatValue}",
};
return new(substat.AppendPropId.GetLocalizedDescription(), valueFormatted, 0);
}
[SuppressMessage("", "SH002")]
private ReliquarySubProperty CreateSubProperty(ReliquarySubAffixId appendPropId)
{
ReliquarySubAffix affix = metadataContext.IdReliquarySubAffixMap[appendPropId];
FightProperty property = affix.Type;
return new(
property.GetLocalizedDescription(),
FightPropertyFormat.FormatValue(property, affix.Value),
ScoreSubAffix(appendPropId));
return new(property, FightPropertyFormat.FormatValue(property, affix.Value), ScoreSubAffix(appendPropId));
}
private float ScoreSubAffix(in ReliquarySubAffixId appendId)

View File

@@ -0,0 +1,27 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.ViewModel.AvatarProperty;
namespace Snap.Hutao.Service.AvatarInfo.Factory;
internal sealed class SummaryReliquarySubPropertyCompositionInfo
{
public SummaryReliquarySubPropertyCompositionInfo(FightProperty type)
{
Type = type;
}
public FightProperty Type { get; set; }
public float Value { get; set; }
public uint Count { get; set; }
public ReliquaryComposedSubProperty ToReliquaryComposedSubProperty()
{
return new(Type, FightPropertyFormat.FormatValue(Type, Value), 0) { EnhancedCount = Count };
}
}

View File

@@ -1,8 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using EnkaAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo;
using ModelAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo;
using EntityAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo;
namespace Snap.Hutao.Service.AvatarInfo;
@@ -10,7 +9,5 @@ internal interface IAvatarInfoDbService
{
void DeleteAvatarInfoRangeByUid(string uid);
List<EnkaAvatarInfo> GetAvatarInfoInfoListByUid(string uid);
List<ModelAvatarInfo> GetAvatarInfoListByUid(string uid);
List<EntityAvatarInfo> GetAvatarInfoListByUid(string uid);
}

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Web.Enka.Model;
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;

View File

@@ -1,16 +1,13 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.Entity.Primitive;
using Snap.Hutao.Model.Metadata.Item;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.ViewModel.Cultivation;
using System.Collections.ObjectModel;

View File

@@ -6,12 +6,9 @@ using Snap.Hutao.Core;
using Snap.Hutao.Core.LifeCycle;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service.Game;
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
using Snap.Hutao.Web.Response;
using System.Runtime.InteropServices;
using Windows.Foundation.Metadata;
namespace Snap.Hutao.Service.DailyNote;
@@ -49,14 +46,21 @@ internal sealed partial class DailyNoteNotificationOperation
string? attribution = SH.ServiceDailyNoteNotifierAttribution;
Response<ListWrapper<UserGameRole>> rolesResponse = await bindingClient
.GetUserGameRolesOverseaAwareAsync(entry.User)
.ConfigureAwait(false);
if (rolesResponse.IsOk())
if (entry.UserGameRole is not null)
{
List<UserGameRole> roles = rolesResponse.Data.List;
attribution = roles.SingleOrDefault(r => r.GameUid == entry.Uid)?.ToString() ?? ToastAttributionUnknown;
attribution = entry.UserGameRole.ToString();
}
else
{
Response<ListWrapper<UserGameRole>> rolesResponse = await bindingClient
.GetUserGameRolesOverseaAwareAsync(entry.User)
.ConfigureAwait(false);
if (rolesResponse.IsOk())
{
List<UserGameRole> roles = rolesResponse.Data.List;
attribution = roles.SingleOrDefault(r => r.GameUid == entry.Uid)?.ToString() ?? ToastAttributionUnknown;
}
}
ToastContentBuilder builder = new ToastContentBuilder()

View File

@@ -48,6 +48,27 @@ internal sealed class DailyNoteOptions : DbStoreOptions
new(SH.ViewModelDailyNoteRefreshTime60, OneMinute * 60),
};
public bool IsAutoRefreshEnabled
{
get => scheduleTaskInterop.IsDailyNoteRefreshEnabled();
set
{
if (value)
{
if (SelectedRefreshTime is not null)
{
scheduleTaskInterop.RegisterForDailyNoteRefresh(SelectedRefreshTime.Value);
}
}
else
{
scheduleTaskInterop.UnregisterForDailyNoteRefresh();
}
OnPropertyChanged();
}
}
/// <summary>
/// 选中的刷新时间
/// </summary>

View File

@@ -2,13 +2,9 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.DependencyInjection.Abstraction;
using Snap.Hutao.Message;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.User;
using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Hoyolab;
@@ -71,13 +67,13 @@ internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient<U
}
/// <inheritdoc/>
public async ValueTask<ObservableCollection<DailyNoteEntry>> GetDailyNoteEntryCollectionAsync()
public async ValueTask<ObservableCollection<DailyNoteEntry>> GetDailyNoteEntryCollectionAsync(bool forceRefresh = false)
{
if (entries is null)
{
// IUserService.GetUserGameRoleByUid only usable after call IUserService.GetRoleCollectionAsync
await userService.GetRoleCollectionAsync().ConfigureAwait(false);
await RefreshDailyNotesAsync().ConfigureAwait(false);
await RefreshDailyNotesCoreAsync(forceRefresh).ConfigureAwait(false);
List<DailyNoteEntry> entryList = dailyNoteDbService.GetDailyNoteEntryIncludeUserList();
entryList.ForEach(entry => { entry.UserGameRole = userService.GetUserGameRoleByUid(entry.Uid); });
@@ -88,30 +84,9 @@ internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient<U
}
/// <inheritdoc/>
public async ValueTask RefreshDailyNotesAsync()
public ValueTask RefreshDailyNotesAsync()
{
foreach (DailyNoteEntry entry in dailyNoteDbService.GetDailyNoteEntryIncludeUserList())
{
Web.Response.Response<WebDailyNote> dailyNoteResponse = await serviceProvider
.GetRequiredService<IOverseaSupportFactory<IGameRecordClient>>()
.Create(PlayerUid.IsOversea(entry.Uid))
.GetDailyNoteAsync(new(entry.User, entry.Uid))
.ConfigureAwait(false);
if (dailyNoteResponse.IsOk())
{
WebDailyNote dailyNote = dailyNoteResponse.Data;
// cache
await taskContext.SwitchToMainThreadAsync();
entries?.SingleOrDefault(e => e.UserId == entry.UserId && e.Uid == entry.Uid)?.UpdateDailyNote(dailyNote);
// database
await dailyNoteNotificationOperation.SendAsync(entry).ConfigureAwait(false);
entry.DailyNote = dailyNote;
await dailyNoteDbService.UpdateDailyNoteEntryAsync(entry).ConfigureAwait(false);
}
}
return RefreshDailyNotesCoreAsync(true);
}
/// <inheritdoc/>
@@ -130,4 +105,38 @@ internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient<U
await taskContext.SwitchToBackgroundAsync();
await dailyNoteDbService.UpdateDailyNoteEntryAsync(entry).ConfigureAwait(false);
}
private async ValueTask RefreshDailyNotesCoreAsync(bool forceRefresh)
{
foreach (DailyNoteEntry entry in dailyNoteDbService.GetDailyNoteEntryIncludeUserList())
{
if (!forceRefresh && entry.DailyNote is not null)
{
continue;
}
Web.Response.Response<WebDailyNote> dailyNoteResponse = await serviceProvider
.GetRequiredService<IOverseaSupportFactory<IGameRecordClient>>()
.Create(PlayerUid.IsOversea(entry.Uid))
.GetDailyNoteAsync(new(entry.User, entry.Uid))
.ConfigureAwait(false);
if (dailyNoteResponse.IsOk())
{
WebDailyNote dailyNote = dailyNoteResponse.Data;
// 集合内的实时便笺与数据库取出的非同一个对象,需要分别更新
// cache
await taskContext.SwitchToMainThreadAsync();
entries?.SingleOrDefault(e => e.UserId == entry.UserId && e.Uid == entry.Uid)?.UpdateDailyNote(dailyNote);
// 发送通知
await dailyNoteNotificationOperation.SendAsync(entry).ConfigureAwait(false);
// database
entry.UpdateDailyNote(dailyNote);
await dailyNoteDbService.UpdateDailyNoteEntryAsync(entry).ConfigureAwait(false);
}
}
}
}

View File

@@ -20,11 +20,7 @@ internal interface IDailyNoteService
/// <returns>任务</returns>
ValueTask AddDailyNoteAsync(UserAndUid userAndUid);
/// <summary>
/// 异步获取实时便笺列表
/// </summary>
/// <returns>实时便笺列表</returns>
ValueTask<ObservableCollection<DailyNoteEntry>> GetDailyNoteEntryCollectionAsync();
ValueTask<ObservableCollection<DailyNoteEntry>> GetDailyNoteEntryCollectionAsync(bool forceRefresh = false);
/// <summary>
/// 异步刷新实时便笺

View File

@@ -2,14 +2,13 @@
// Licensed under the MIT license.
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Weapon;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.ViewModel.GachaLog;
using Snap.Hutao.Web.Hutao;
using Snap.Hutao.Web.Hutao.GachaLog;
using System.Runtime.InteropServices;
namespace Snap.Hutao.Service.GachaLog.Factory;
@@ -28,7 +27,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
private readonly AppOptions options;
/// <inheritdoc/>
public async ValueTask<GachaStatistics> CreateAsync(List<GachaItem> items, GachaLogServiceMetadataContext context)
public async ValueTask<GachaStatistics> CreateAsync(List<Model.Entity.GachaItem> items, GachaLogServiceMetadataContext context)
{
await taskContext.SwitchToBackgroundAsync();
List<GachaEvent> gachaEvents = await metadataService.GetGachaEventsAsync().ConfigureAwait(false);
@@ -40,7 +39,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
private static GachaStatistics CreateCore(
ITaskContext taskContext,
HomaGachaLogClient gachaLogClient,
List<GachaItem> items,
List<Model.Entity.GachaItem> items,
List<HistoryWishBuilder> historyWishBuilders,
in GachaLogServiceMetadataContext context,
bool isEmptyHistoryWishVisible)
@@ -62,7 +61,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
// Items are ordered by precise time, first is oldest
// 'ref' is not allowed here because we have lambda below
foreach (GachaItem item in CollectionsMarshal.AsSpan(items))
foreach (Model.Entity.GachaItem item in CollectionsMarshal.AsSpan(items))
{
// Find target history wish to operate.
HistoryWishBuilder? targetHistoryWishBuilder = historyWishBuilders

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.ViewModel.GachaLog;
using Snap.Hutao.Web.Hutao;
using Snap.Hutao.Web.Hutao.GachaLog;
using Snap.Hutao.Web.Response;
using System.Runtime.InteropServices;
@@ -28,9 +27,9 @@ internal sealed class PullPrediction
await context.TaskContext.SwitchToBackgroundAsync();
Response<GachaDistribution> response = await context.GetGachaDistributionAsync().ConfigureAwait(false);
if (response.IsOk())
if (response is { ReturnCode: 0, Data: GachaDistribution data })
{
PredictResult result = PredictCore(response.Data.Distribution, typedWishSummary);
PredictResult result = PredictCore(data.Distribution, typedWishSummary);
await barrier.SignalAndWaitAsync().ConfigureAwait(false);
await context.TaskContext.SwitchToMainThreadAsync();

View File

@@ -6,7 +6,6 @@ using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.ViewModel.GachaLog;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using Snap.Hutao.Web.Hutao;
namespace Snap.Hutao.Service.GachaLog.Factory;

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using Snap.Hutao.Web.Hutao;
using Snap.Hutao.Web.Hutao.GachaLog;
using Snap.Hutao.Web.Response;

View File

@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Model.Entity;
namespace Snap.Hutao.Service.GachaLog;

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Service.GachaLog.QueryProvider;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using System.Collections.ObjectModel;

View File

@@ -34,4 +34,14 @@ internal sealed class GachaLogFetchStatus
/// 当前获取的物品
/// </summary>
public List<Item> Items { get; set; } = new(20);
public string Header
{
get
{
return AuthKeyTimeout
? SH.ViewDialogGachaLogRefreshProgressAuthkeyTimeout
: SH.ViewDialogGachaLogRefreshProgressDescription.Format(ConfigType.GetLocalizedDescription());
}
}
}

View File

@@ -1,9 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Weapon;
@@ -12,7 +10,6 @@ using Snap.Hutao.Service.GachaLog.Factory;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.ViewModel.GachaLog;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using Snap.Hutao.Web.Hutao;
using Snap.Hutao.Web.Hutao.GachaLog;
using Snap.Hutao.Web.Response;

View File

@@ -2,13 +2,11 @@
// Licensed under the MIT license.
using Snap.Hutao.Model;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Weapon;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using System.Collections.ObjectModel;
namespace Snap.Hutao.Service.GachaLog;

Some files were not shown because too many files have changed in this diff Show More