diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/UniversalAnalyzer.cs b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/UniversalAnalyzer.cs
index cbffff49..a8ad8607 100644
--- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/UniversalAnalyzer.cs
+++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/UniversalAnalyzer.cs
@@ -277,6 +277,12 @@ internal sealed class UniversalAnalyzer : DiagnosticAnalyzer
}
}
+ if (syntax.Operand is DefaultExpressionSyntax expression)
+ {
+ return;
+ }
+
+
Location location = syntax.GetLocation();
Diagnostic diagnostic = Diagnostic.Create(useArgumentNullExceptionThrowIfNullDescriptor, location);
context.ReportDiagnostic(diagnostic);
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionExtension.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionExtension.cs
index a27ba5d9..aa7ff0b1 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionExtension.cs
@@ -52,12 +52,15 @@ internal static class CompositionExtension
Mode = blendEffectMode,
};
- CompositionEffectBrush brush = compositor.CreateEffectFactory(effect).CreateBrush();
+ using (effect)
+ {
+ CompositionEffectBrush brush = compositor.CreateEffectFactory(effect).CreateBrush();
- brush.SetSourceParameter(Background, background);
- brush.SetSourceParameter(Foreground, foreground);
+ brush.SetSourceParameter(Background, background);
+ brush.SetSourceParameter(Foreground, foreground);
- return brush;
+ return brush;
+ }
}
///
@@ -75,11 +78,14 @@ internal static class CompositionExtension
Source = new CompositionEffectSourceParameter(Source),
};
- CompositionEffectBrush brush = compositor.CreateEffectFactory(effect).CreateBrush();
+ using (effect)
+ {
+ CompositionEffectBrush brush = compositor.CreateEffectFactory(effect).CreateBrush();
- brush.SetSourceParameter(Source, source);
+ brush.SetSourceParameter(Source, source);
- return brush;
+ return brush;
+ }
}
///
@@ -97,11 +103,14 @@ internal static class CompositionExtension
Source = new CompositionEffectSourceParameter(Source),
};
- CompositionEffectBrush brush = compositor.CreateEffectFactory(effect).CreateBrush();
+ using (effect)
+ {
+ CompositionEffectBrush brush = compositor.CreateEffectFactory(effect).CreateBrush();
- brush.SetSourceParameter(Source, sourceBrush);
+ brush.SetSourceParameter(Source, sourceBrush);
- return brush;
+ return brush;
+ }
}
///
@@ -116,18 +125,21 @@ internal static class CompositionExtension
CompositionBrush sourceBrush,
CompositionBrush alphaMask)
{
- AlphaMaskEffect maskEffect = new()
+ AlphaMaskEffect effect = new()
{
AlphaMask = new CompositionEffectSourceParameter(AlphaMask),
Source = new CompositionEffectSourceParameter(Source),
};
- CompositionEffectBrush brush = compositor.CreateEffectFactory(maskEffect).CreateBrush();
+ using (effect)
+ {
+ CompositionEffectBrush brush = compositor.CreateEffectFactory(effect).CreateBrush();
- brush.SetSourceParameter(AlphaMask, alphaMask);
- brush.SetSourceParameter(Source, sourceBrush);
+ brush.SetSourceParameter(AlphaMask, alphaMask);
+ brush.SetSourceParameter(Source, sourceBrush);
- return brush;
+ return brush;
+ }
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/IMappingFrom.cs b/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/IMappingFrom.cs
index e17b9c92..d4ba0bd9 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/IMappingFrom.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/IMappingFrom.cs
@@ -17,4 +17,11 @@ internal interface IMappingFrom
{
[Pure]
static abstract TSelf From(T1 t1, T2 t2);
+}
+
+internal interface IMappingFrom
+ where TSelf : IMappingFrom
+{
+ [Pure]
+ static abstract TSelf From(T1 t1, T2 t2, T3 t3);
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocHttpClientConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocHttpClientConfiguration.cs
index b5876a2d..db80c281 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocHttpClientConfiguration.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocHttpClientConfiguration.cs
@@ -88,6 +88,7 @@ internal static partial class IocHttpClientConfiguration
/// HoYoLAB web
///
/// 配置后的客户端
+ [SuppressMessage("", "IDE0051")]
private static void XRpc4Configuration(HttpClient client)
{
client.Timeout = Timeout.InfiniteTimeSpan;
diff --git a/src/Snap.Hutao/Snap.Hutao/Factory/Abstraction/IContentDialogFactory.cs b/src/Snap.Hutao/Snap.Hutao/Factory/Abstraction/IContentDialogFactory.cs
index 1794360a..b09c2f6f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Factory/Abstraction/IContentDialogFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Factory/Abstraction/IContentDialogFactory.cs
@@ -34,4 +34,10 @@ internal interface IContentDialogFactory
/// 标题
/// 内容对话框
ValueTask CreateForIndeterminateProgressAsync(string title);
+
+ TContentDialog CreateInstance(params object[] parameters)
+ where TContentDialog : ContentDialog;
+
+ ValueTask CreateInstanceAsync(params object[] parameters)
+ where TContentDialog : ContentDialog;
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialogFactory.cs b/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialogFactory.cs
index d89d5359..3716ee64 100644
--- a/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialogFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialogFactory.cs
@@ -3,27 +3,19 @@
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Factory.Abstraction;
+using System.Security.Authentication;
namespace Snap.Hutao.Factory;
///
[HighQuality]
-[Injection(InjectAs.Transient, typeof(IContentDialogFactory))]
-internal sealed class ContentDialogFactory : IContentDialogFactory
+[ConstructorGenerated]
+[Injection(InjectAs.Singleton, typeof(IContentDialogFactory))]
+internal sealed partial class ContentDialogFactory : IContentDialogFactory
{
- private readonly MainWindow mainWindow;
+ private readonly IServiceProvider serviceProvider;
private readonly ITaskContext taskContext;
-
- ///
- /// 构造一个新的内容对话框工厂
- ///
- /// 任务上下文
- /// 主窗体
- public ContentDialogFactory(ITaskContext taskContext, MainWindow mainWindow)
- {
- this.taskContext = taskContext;
- this.mainWindow = mainWindow;
- }
+ private readonly MainWindow mainWindow;
///
public async ValueTask CreateForConfirmAsync(string title, string content)
@@ -71,4 +63,17 @@ internal sealed class ContentDialogFactory : IContentDialogFactory
return dialog;
}
+
+ public async ValueTask CreateInstanceAsync(params object[] parameters)
+ where TContentDialog : ContentDialog
+ {
+ await taskContext.SwitchToMainThreadAsync();
+ return serviceProvider.CreateInstance();
+ }
+
+ public TContentDialog CreateInstance(params object[] parameters)
+ where TContentDialog : ContentDialog
+ {
+ return serviceProvider.CreateInstance();
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/GachaArchive.Operation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/GachaArchive.Operation.cs
deleted file mode 100644
index 369e09ab..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/GachaArchive.Operation.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using Microsoft.Data.Sqlite;
-using Microsoft.EntityFrameworkCore;
-using Snap.Hutao.Core.Database;
-using Snap.Hutao.Core.ExceptionService;
-using Snap.Hutao.Service.GachaLog;
-using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
-
-namespace Snap.Hutao.Model.Entity;
-
-///
-/// 操作部分
-///
-internal sealed partial class GachaArchive
-{
- ///
- /// 保存祈愿物品
- ///
- /// 上下文
- [SuppressMessage("", "SH002")]
- public void SaveItems(GachaItemSaveContext context)
- {
- if (context.ItemsToAdd.Count > 0)
- {
- // 全量刷新
- if (!context.IsLazy)
- {
- context.GachaItems
- .Where(i => i.ArchiveId == InnerId)
- .Where(i => i.Id >= context.EndId)
- .ExecuteDelete();
- }
-
- context.GachaItems.AddRangeAndSave(context.ItemsToAdd);
- }
- }
-}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFInfo.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFInfo.cs
index 0d4e8511..0b548800 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFInfo.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/GachaLog/UIGFInfo.cs
@@ -11,7 +11,7 @@ namespace Snap.Hutao.Model.InterChange.GachaLog;
/// UIGF格式的信息
///
[HighQuality]
-internal sealed class UIGFInfo : IMappingFrom
+internal sealed class UIGFInfo : IMappingFrom
{
///
/// 用户Uid
@@ -58,17 +58,8 @@ internal sealed class UIGFInfo : IMappingFrom
- /// 构造一个新的专用 UIGF 信息
- ///
- /// 服务提供器
- /// uid
- /// 专用 UIGF 信息
- public static UIGFInfo From(IServiceProvider serviceProvider, string uid)
+ public static UIGFInfo From(RuntimeOptions runtimeOptions, MetadataOptions metadataOptions, string uid)
{
- RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService();
- MetadataOptions metadataOptions = serviceProvider.GetRequiredService();
-
return new()
{
Uid = uid,
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs
index c5703dd3..73726a78 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementDbService.cs
@@ -57,6 +57,7 @@ internal sealed partial class AchievementDbService : IAchievementDbService
}
}
+ [SuppressMessage("", "CA1305")]
public async ValueTask> GetLatestFinishedAchievementListByArchiveIdAsync(Guid archiveId, int take)
{
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -66,7 +67,7 @@ internal sealed partial class AchievementDbService : IAchievementDbService
.AsNoTracking()
.Where(a => a.ArchiveId == archiveId)
.Where(a => a.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
- .OrderByDescending(a => a.Time)
+ .OrderByDescending(a => a.Time.ToString())
.Take(take)
.ToListAsync()
.ConfigureAwait(false);
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs
index 78fcc3b9..ada9aadb 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs
@@ -25,8 +25,8 @@ internal sealed partial class AvatarInfoService : IAvatarInfoService
private readonly IAvatarInfoDbService avatarInfoDbService;
private readonly ILogger logger;
private readonly IMetadataService metadataService;
- private readonly IServiceProvider serviceProvider;
private readonly ISummaryFactory summaryFactory;
+ private readonly EnkaClient enkaClient;
///
public async ValueTask> GetSummaryAsync(UserAndUid userAndUid, RefreshOption refreshOption, CancellationToken token = default)
@@ -92,8 +92,6 @@ internal sealed partial class AvatarInfoService : IAvatarInfoService
private async ValueTask GetEnkaResponseAsync(PlayerUid uid, CancellationToken token = default)
{
- EnkaClient enkaClient = serviceProvider.GetRequiredService();
-
return await enkaClient.GetForwardDataAsync(uid, token).ConfigureAwait(false)
?? await enkaClient.GetDataAsync(uid, token).ConfigureAwait(false);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs
index b399c97b..e8d58cf2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotificationOperation.cs
@@ -10,6 +10,7 @@ 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;
@@ -18,38 +19,26 @@ namespace Snap.Hutao.Service.DailyNote;
/// 实时便笺通知器
///
[HighQuality]
-internal sealed class DailyNoteNotificationOperation
+[ConstructorGenerated]
+[Injection(InjectAs.Singleton)]
+internal sealed partial class DailyNoteNotificationOperation
{
private const string ToastHeaderIdArgument = "DAILYNOTE";
private const string ToastAttributionUnknown = "Unknown";
+
private readonly ITaskContext taskContext;
- private readonly IServiceProvider serviceProvider;
- private readonly DailyNoteEntry entry;
+ private readonly IGameService gameService;
+ private readonly BindingClient bindingClient;
+ private readonly DailyNoteOptions options;
- ///
- /// 构造一个新的实时便笺通知器
- ///
- /// 服务提供器
- /// 实时便笺入口
- public DailyNoteNotificationOperation(IServiceProvider serviceProvider, DailyNoteEntry entry)
- {
- taskContext = serviceProvider.GetRequiredService();
- this.serviceProvider = serviceProvider;
- this.entry = entry;
- }
-
- ///
- /// 异步通知
- ///
- /// 任务
- public async ValueTask SendAsync()
+ public async ValueTask SendAsync(DailyNoteEntry entry)
{
if (entry.DailyNote is null)
{
return;
}
- List notifyInfos = new();
+ List notifyInfos = new();
CheckNotifySuppressed(entry, notifyInfos);
@@ -58,92 +47,95 @@ internal sealed class DailyNoteNotificationOperation
return;
}
- using (IServiceScope scope = serviceProvider.CreateScope())
+ string? attribution = SH.ServiceDailyNoteNotifierAttribution;
+
+ Response> rolesResponse = await bindingClient
+ .GetUserGameRolesOverseaAwareAsync(entry.User)
+ .ConfigureAwait(false);
+
+ if (rolesResponse.IsOk())
{
- DailyNoteOptions options = scope.ServiceProvider.GetRequiredService();
-
- string? attribution = SH.ServiceDailyNoteNotifierAttribution;
-
- Response> rolesResponse = await scope.ServiceProvider
- .GetRequiredService()
- .GetUserGameRolesOverseaAwareAsync(entry.User)
- .ConfigureAwait(false);
-
- if (rolesResponse.IsOk())
- {
- List roles = rolesResponse.Data.List;
- attribution = roles.SingleOrDefault(r => r.GameUid == entry.Uid)?.ToString() ?? ToastAttributionUnknown;
- }
-
- ToastContentBuilder builder = new ToastContentBuilder()
- .AddHeader(ToastHeaderIdArgument, SH.ServiceDailyNoteNotifierTitle, ToastHeaderIdArgument)
- .AddAttributionText(attribution)
- .AddButton(new ToastButton()
- .SetContent(SH.ServiceDailyNoteNotifierActionLaunchGameButton)
- .AddArgument(Activation.Action, Activation.LaunchGame)
- .AddArgument(Activation.Uid, entry.Uid))
- .AddButton(new ToastButtonDismiss(SH.ServiceDailyNoteNotifierActionLaunchGameDismiss));
-
- if (options.IsReminderNotification)
- {
- builder.SetToastScenario(ToastScenario.Reminder);
- }
-
- if (notifyInfos.Count > 2)
- {
- builder.AddText(SH.ServiceDailyNoteNotifierMultiValueReached);
-
- // Desktop and Mobile started supporting adaptive toasts in API contract 3 (Anniversary Update)
- if (UniversalApiContract.IsPresent(WindowsVersion.Windows10AnniversaryUpdate))
- {
- AdaptiveGroup group = new();
- foreach (NotifyInfo info in notifyInfos)
- {
- AdaptiveSubgroup subgroup = new()
- {
- HintWeight = 1,
- Children =
- {
- new AdaptiveImage() { Source = info.AdaptiveIcon, HintRemoveMargin = true, },
- new AdaptiveText() { Text = info.AdaptiveHint, HintAlign = AdaptiveTextAlign.Center, },
- new AdaptiveText() { Text = info.Title, HintAlign = AdaptiveTextAlign.Center, HintStyle = AdaptiveTextStyle.CaptionSubtle, },
- },
- };
-
- group.Children.Add(subgroup);
- }
-
- builder.AddVisualChild(group);
- }
- }
- else
- {
- foreach (NotifyInfo info in notifyInfos)
- {
- builder.AddText(info.Hint);
- }
- }
-
- await taskContext.SwitchToMainThreadAsync();
- builder.Show(toast => toast.SuppressPopup = ShouldSuppressPopup(options));
+ List roles = rolesResponse.Data.List;
+ attribution = roles.SingleOrDefault(r => r.GameUid == entry.Uid)?.ToString() ?? ToastAttributionUnknown;
}
+
+ ToastContentBuilder builder = new ToastContentBuilder()
+ .AddHeader(ToastHeaderIdArgument, SH.ServiceDailyNoteNotifierTitle, ToastHeaderIdArgument)
+ .AddAttributionText(attribution)
+ .AddButton(new ToastButton()
+ .SetContent(SH.ServiceDailyNoteNotifierActionLaunchGameButton)
+ .AddArgument(Activation.Action, Activation.LaunchGame)
+ .AddArgument(Activation.Uid, entry.Uid))
+ .AddButton(new ToastButtonDismiss(SH.ServiceDailyNoteNotifierActionLaunchGameDismiss));
+
+ if (options.IsReminderNotification)
+ {
+ builder.SetToastScenario(ToastScenario.Reminder);
+ }
+
+ if (notifyInfos.Count > 2)
+ {
+ builder.AddText(SH.ServiceDailyNoteNotifierMultiValueReached);
+
+ // Desktop and Mobile started supporting adaptive toasts in API contract 3 (Anniversary Update)
+ if (UniversalApiContract.IsPresent(WindowsVersion.Windows10AnniversaryUpdate))
+ {
+ AdaptiveGroup group = new();
+ foreach (DailyNoteNotifyInfo info in notifyInfos)
+ {
+ AdaptiveSubgroup subgroup = new()
+ {
+ HintWeight = 1,
+ Children =
+ {
+ new AdaptiveImage() { Source = info.AdaptiveIcon, HintRemoveMargin = true, },
+ new AdaptiveText() { Text = info.AdaptiveHint, HintAlign = AdaptiveTextAlign.Center, },
+ new AdaptiveText() { Text = info.Title, HintAlign = AdaptiveTextAlign.Center, HintStyle = AdaptiveTextStyle.CaptionSubtle, },
+ },
+ };
+
+ group.Children.Add(subgroup);
+ }
+
+ builder.AddVisualChild(group);
+ }
+ }
+ else
+ {
+ foreach (DailyNoteNotifyInfo info in notifyInfos)
+ {
+ builder.AddText(info.Hint);
+ }
+ }
+
+ await taskContext.SwitchToMainThreadAsync();
+ builder.Show(toast => toast.SuppressPopup = ShouldSuppressPopup(options));
}
- private static void CheckNotifySuppressed(DailyNoteEntry entry, List notifyInfos)
+ private static void CheckNotifySuppressed(DailyNoteEntry entry, List notifyInfos)
{
- // https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/send-local-toast?tabs=uwp#adding-images
// Image limitation.
-
+ // https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/send-local-toast?tabs=uwp#adding-images
// NotifySuppressed judge
- if (entry.DailyNote!.CurrentResin >= entry.ResinNotifyThreshold)
+ ChcekResinNotifySuppressed(entry, notifyInfos);
+ CheckHomeCoinNotifySuppressed(entry, notifyInfos);
+ CheckDailyTaskNotifySuppressed(entry, notifyInfos);
+ CheckTransformerNotifySuppressed(entry, notifyInfos);
+ CheckExpeditionNotifySuppressed(entry, notifyInfos);
+ }
+
+ private static void ChcekResinNotifySuppressed(DailyNoteEntry entry, List notifyInfos)
+ {
+ ArgumentNullException.ThrowIfNull(entry.DailyNote);
+ if (entry.DailyNote.CurrentResin >= entry.ResinNotifyThreshold)
{
if (!entry.ResinNotifySuppressed)
{
notifyInfos.Add(new(
SH.ServiceDailyNoteNotifierResin,
- Web.Hoyolab.Images.UIItemIcon210,
+ Web.HutaoEndpoints.StaticFile("ItemIcon", "UI_ItemIcon_210.png"),
$"{entry.DailyNote.CurrentResin}",
- string.Format(SH.ServiceDailyNoteNotifierResinCurrent, entry.DailyNote.CurrentResin)));
+ SH.ServiceDailyNoteNotifierResinCurrent.Format(entry.DailyNote.CurrentResin)));
entry.ResinNotifySuppressed = true;
}
}
@@ -151,16 +143,20 @@ internal sealed class DailyNoteNotificationOperation
{
entry.ResinNotifySuppressed = false;
}
+ }
+ private static void CheckHomeCoinNotifySuppressed(DailyNoteEntry entry, List notifyInfos)
+ {
+ ArgumentNullException.ThrowIfNull(entry.DailyNote);
if (entry.DailyNote.CurrentHomeCoin >= entry.HomeCoinNotifyThreshold)
{
if (!entry.HomeCoinNotifySuppressed)
{
notifyInfos.Add(new(
SH.ServiceDailyNoteNotifierHomeCoin,
- Web.Hoyolab.Images.UIItemIcon204,
+ Web.HutaoEndpoints.StaticFile("ItemIcon", "UI_ItemIcon_204.png"),
$"{entry.DailyNote.CurrentHomeCoin}",
- string.Format(SH.ServiceDailyNoteNotifierHomeCoinCurrent, entry.DailyNote.CurrentHomeCoin)));
+ SH.ServiceDailyNoteNotifierHomeCoinCurrent.Format(entry.DailyNote.CurrentHomeCoin)));
entry.HomeCoinNotifySuppressed = true;
}
}
@@ -168,14 +164,17 @@ internal sealed class DailyNoteNotificationOperation
{
entry.HomeCoinNotifySuppressed = false;
}
+ }
- if (entry.DailyTaskNotify && !entry.DailyNote.IsExtraTaskRewardReceived)
+ private static void CheckDailyTaskNotifySuppressed(DailyNoteEntry entry, List notifyInfos)
+ {
+ if (entry is { DailyTaskNotify: true, DailyNote.IsExtraTaskRewardReceived: false })
{
if (!entry.DailyTaskNotifySuppressed)
{
notifyInfos.Add(new(
SH.ServiceDailyNoteNotifierDailyTask,
- Web.Hoyolab.Images.UIMarkQuestEventsProce,
+ Web.HutaoEndpoints.StaticFile("Bg", "UI_MarkQuest_Events_Proce.png"),
SH.ServiceDailyNoteNotifierDailyTaskHint,
entry.DailyNote.ExtraTaskRewardDescription));
entry.DailyTaskNotifySuppressed = true;
@@ -185,14 +184,17 @@ internal sealed class DailyNoteNotificationOperation
{
entry.DailyTaskNotifySuppressed = false;
}
+ }
- if (entry.TransformerNotify && entry.DailyNote.Transformer.Obtained && entry.DailyNote.Transformer.RecoveryTime.Reached)
+ private static void CheckTransformerNotifySuppressed(DailyNoteEntry entry, List notifyInfos)
+ {
+ if (entry is { TransformerNotify: true, DailyNote.Transformer: { Obtained: true, RecoveryTime.Reached: true } })
{
if (!entry.TransformerNotifySuppressed)
{
notifyInfos.Add(new(
SH.ServiceDailyNoteNotifierTransformer,
- Web.Hoyolab.Images.UIItemIcon220021,
+ Web.HutaoEndpoints.StaticFile("ItemIcon", "UI_ItemIcon_220021.png"),
SH.ServiceDailyNoteNotifierTransformerAdaptiveHint,
SH.ServiceDailyNoteNotifierTransformerHint));
entry.TransformerNotifySuppressed = true;
@@ -202,14 +204,18 @@ internal sealed class DailyNoteNotificationOperation
{
entry.TransformerNotifySuppressed = false;
}
+ }
+ private static void CheckExpeditionNotifySuppressed(DailyNoteEntry entry, List notifyInfos)
+ {
+ ArgumentNullException.ThrowIfNull(entry.DailyNote);
if (entry.ExpeditionNotify && entry.DailyNote.Expeditions.All(e => e.Status == ExpeditionStatus.Finished))
{
if (!entry.ExpeditionNotifySuppressed)
{
notifyInfos.Add(new(
SH.ServiceDailyNoteNotifierExpedition,
- Web.Hoyolab.Images.UIIconInteeExplore1,
+ Web.HutaoEndpoints.StaticFile("Bg", "UI_Icon_Intee_Explore_1.png"),
SH.ServiceDailyNoteNotifierExpeditionAdaptiveHint,
SH.ServiceDailyNoteNotifierExpeditionHint));
entry.ExpeditionNotifySuppressed = true;
@@ -224,22 +230,6 @@ internal sealed class DailyNoteNotificationOperation
private bool ShouldSuppressPopup(DailyNoteOptions options)
{
// Prevent notify when we are in game && silent mode.
- return options.IsSilentWhenPlayingGame && serviceProvider.GetRequiredService().IsGameRunning();
- }
-
- private readonly struct NotifyInfo
- {
- public readonly string Title;
- public readonly string AdaptiveIcon;
- public readonly string AdaptiveHint;
- public readonly string Hint;
-
- public NotifyInfo(string title, string adaptiveIcon, string adaptiveHint, string hint)
- {
- Title = title;
- AdaptiveIcon = adaptiveIcon;
- AdaptiveHint = adaptiveHint;
- Hint = hint;
- }
+ return options.IsSilentWhenPlayingGame && gameService.IsGameRunning();
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifyInfo.cs b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifyInfo.cs
new file mode 100644
index 00000000..d5b334e7
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifyInfo.cs
@@ -0,0 +1,20 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Service.DailyNote;
+
+internal readonly struct DailyNoteNotifyInfo
+{
+ public readonly string Title;
+ public readonly string AdaptiveIcon;
+ public readonly string AdaptiveHint;
+ public readonly string Hint;
+
+ public DailyNoteNotifyInfo(string title, string adaptiveIcon, string adaptiveHint, string hint)
+ {
+ Title = title;
+ AdaptiveIcon = adaptiveIcon;
+ AdaptiveHint = adaptiveHint;
+ Hint = hint;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteService.cs b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteService.cs
index 00441cfb..8e3ef6e9 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteService.cs
@@ -26,6 +26,7 @@ namespace Snap.Hutao.Service.DailyNote;
[Injection(InjectAs.Singleton, typeof(IDailyNoteService))]
internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient
{
+ private readonly DailyNoteNotificationOperation dailyNoteNotificationOperation;
private readonly IServiceProvider serviceProvider;
private readonly IDailyNoteDbService dailyNoteDbService;
private readonly IUserService userService;
@@ -106,7 +107,7 @@ internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient e.UserId == entry.UserId && e.Uid == entry.Uid)?.UpdateDailyNote(dailyNote);
// database
- await new DailyNoteNotificationOperation(serviceProvider, entry).SendAsync().ConfigureAwait(false);
+ await dailyNoteNotificationOperation.SendAsync(entry).ConfigureAwait(false);
entry.DailyNote = dailyNote;
await dailyNoteDbService.UpdateDailyNoteEntryAsync(entry).ConfigureAwait(false);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs
index 0f44636c..ae20b701 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs
@@ -9,6 +9,7 @@ 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 System.Runtime.InteropServices;
namespace Snap.Hutao.Service.GachaLog.Factory;
@@ -21,8 +22,8 @@ namespace Snap.Hutao.Service.GachaLog.Factory;
[Injection(InjectAs.Scoped, typeof(IGachaStatisticsFactory))]
internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
{
- private readonly IServiceProvider serviceProvider;
private readonly IMetadataService metadataService;
+ private readonly HomaGachaLogClient homaGachaLogClient;
private readonly ITaskContext taskContext;
private readonly AppOptions options;
@@ -33,32 +34,25 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
List gachaEvents = await metadataService.GetGachaEventsAsync().ConfigureAwait(false);
List historyWishBuilders = gachaEvents.SelectList(gachaEvent => new HistoryWishBuilder(gachaEvent, context));
- return CreateCore(serviceProvider, items, historyWishBuilders, context, options.IsEmptyHistoryWishVisible);
+ return CreateCore(taskContext, homaGachaLogClient, items, historyWishBuilders, context, options.IsEmptyHistoryWishVisible);
}
private static GachaStatistics CreateCore(
- IServiceProvider serviceProvider,
+ ITaskContext taskContext,
+ HomaGachaLogClient gachaLogClient,
List items,
List historyWishBuilders,
in GachaLogServiceMetadataContext context,
bool isEmptyHistoryWishVisible)
{
- TypedWishSummaryBuilder standardWishBuilder = new(
- serviceProvider,
- SH.ServiceGachaLogFactoryPermanentWishName,
- TypedWishSummaryBuilder.IsStandardWish,
- Web.Hutao.GachaLog.GachaDistributionType.Standard);
- TypedWishSummaryBuilder avatarWishBuilder = new(
- serviceProvider,
- SH.ServiceGachaLogFactoryAvatarWishName,
- TypedWishSummaryBuilder.IsAvatarEventWish,
- Web.Hutao.GachaLog.GachaDistributionType.AvatarEvent);
- TypedWishSummaryBuilder weaponWishBuilder = new(
- serviceProvider,
- SH.ServiceGachaLogFactoryWeaponWishName,
- TypedWishSummaryBuilder.IsWeaponEventWish,
- Web.Hutao.GachaLog.GachaDistributionType.WeaponEvent,
- 80);
+ TypedWishSummaryBuilderContext standardContext = TypedWishSummaryBuilderContext.StandardWish(taskContext, gachaLogClient);
+ TypedWishSummaryBuilder standardWishBuilder = new(standardContext);
+
+ TypedWishSummaryBuilderContext avatarContext = TypedWishSummaryBuilderContext.AvatarEventWish(taskContext, gachaLogClient);
+ TypedWishSummaryBuilder avatarWishBuilder = new(avatarContext);
+
+ TypedWishSummaryBuilderContext weaponContext = TypedWishSummaryBuilderContext.WeaponEventWish(taskContext, gachaLogClient);
+ TypedWishSummaryBuilder weaponWishBuilder = new(weaponContext);
Dictionary orangeAvatarCounter = new();
Dictionary purpleAvatarCounter = new();
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/PullPrediction.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/PullPrediction.cs
index 6b878435..dfe63e32 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/PullPrediction.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/PullPrediction.cs
@@ -14,38 +14,26 @@ namespace Snap.Hutao.Service.GachaLog.Factory;
///
internal sealed class PullPrediction
{
- private readonly IServiceProvider serviceProvider;
- private readonly ITaskContext taskContext;
private readonly TypedWishSummary typedWishSummary;
- private readonly GachaDistributionType distributionType;
+ private readonly TypedWishSummaryBuilderContext context;
- ///
- /// 构造一个新的抽数预计
- ///
- /// 服务提供器
- /// 类型化祈愿统计信息
- /// 分布类型
- public PullPrediction(IServiceProvider serviceProvider, TypedWishSummary typedWishSummary, GachaDistributionType distributionType)
+ public PullPrediction(TypedWishSummary typedWishSummary, in TypedWishSummaryBuilderContext context)
{
- this.serviceProvider = serviceProvider;
- taskContext = serviceProvider.GetRequiredService();
-
this.typedWishSummary = typedWishSummary;
- this.distributionType = distributionType;
+ this.context = context;
}
public async ValueTask PredictAsync(AsyncBarrier barrier)
{
- await taskContext.SwitchToBackgroundAsync();
- HomaGachaLogClient gachaLogClient = serviceProvider.GetRequiredService();
- Response response = await gachaLogClient.GetGachaDistributionAsync(distributionType).ConfigureAwait(false);
+ await context.TaskContext.SwitchToBackgroundAsync();
+ Response response = await context.GetGachaDistributionAsync().ConfigureAwait(false);
if (response.IsOk())
{
PredictResult result = PredictCore(response.Data.Distribution, typedWishSummary);
await barrier.SignalAndWaitAsync().ConfigureAwait(false);
- await taskContext.SwitchToMainThreadAsync();
+ await context.TaskContext.SwitchToMainThreadAsync();
typedWishSummary.ProbabilityOfNextPullIsOrange = result.ProbabilityOfNextPullIsOrange;
typedWishSummary.ProbabilityOfPredictedPullLeftToOrange = result.ProbabilityOfPredictedPullLeftToOrange;
typedWishSummary.PredictedPullLeftToOrange = result.PredictedPullLeftToOrange;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs
index 6163ae3c..36d3da52 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilder.cs
@@ -6,6 +6,7 @@ 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;
@@ -30,12 +31,7 @@ internal sealed class TypedWishSummaryBuilder
///
public static readonly Func IsWeaponEventWish = type => type is GachaConfigType.WeaponEventWish;
- private readonly IServiceProvider serviceProvider;
- private readonly string name;
- private readonly int guaranteeOrangeThreshold;
- private readonly int guaranteePurpleThreshold;
- private readonly Func typeEvaluator;
- private readonly Web.Hutao.GachaLog.GachaDistributionType distributionType;
+ private readonly TypedWishSummaryBuilderContext context;
private readonly List averageOrangePullTracker = new();
private readonly List averageUpOrangePullTracker = new();
@@ -54,20 +50,9 @@ internal sealed class TypedWishSummaryBuilder
private DateTimeOffset fromTimeTracker = DateTimeOffset.MaxValue;
private DateTimeOffset toTimeTracker = DateTimeOffset.MinValue;
- public TypedWishSummaryBuilder(
- IServiceProvider serviceProvider,
- string name,
- Func typeEvaluator,
- Web.Hutao.GachaLog.GachaDistributionType distributionType,
- int guaranteeOrangeThreshold = 90,
- int guaranteePurpleThreshold = 10)
+ public TypedWishSummaryBuilder(in TypedWishSummaryBuilderContext context)
{
- this.serviceProvider = serviceProvider;
- this.name = name;
- this.typeEvaluator = typeEvaluator;
- this.guaranteeOrangeThreshold = guaranteeOrangeThreshold;
- this.guaranteePurpleThreshold = guaranteePurpleThreshold;
- this.distributionType = distributionType;
+ this.context = context;
}
///
@@ -78,7 +63,7 @@ internal sealed class TypedWishSummaryBuilder
/// 是否为Up物品
public void Track(GachaItem item, ISummaryItemSource source, bool isUp)
{
- if (typeEvaluator(item.GachaType))
+ if (context.TypeEvaluator(item.GachaType))
{
++lastOrangePullTracker;
++lastPurplePullTracker;
@@ -129,13 +114,13 @@ internal sealed class TypedWishSummaryBuilder
public TypedWishSummary ToTypedWishSummary(AsyncBarrier barrier)
{
- summaryItems.CompleteAdding(guaranteeOrangeThreshold);
+ summaryItems.CompleteAdding(context.GuaranteeOrangeThreshold);
double totalCount = totalCountTracker;
TypedWishSummary summary = new()
{
// base
- Name = name,
+ Name = context.Name,
From = fromTimeTracker,
To = toTimeTracker,
TotalCount = totalCountTracker,
@@ -144,9 +129,9 @@ internal sealed class TypedWishSummaryBuilder
MaxOrangePull = maxOrangePullTracker,
MinOrangePull = minOrangePullTracker,
LastOrangePull = lastOrangePullTracker,
- GuaranteeOrangeThreshold = guaranteeOrangeThreshold,
+ GuaranteeOrangeThreshold = context.GuaranteeOrangeThreshold,
LastPurplePull = lastPurplePullTracker,
- GuaranteePurpleThreshold = guaranteePurpleThreshold,
+ GuaranteePurpleThreshold = context.GuaranteePurpleThreshold,
TotalOrangePull = totalOrangePullTracker,
TotalPurplePull = totalPurplePullTracker,
TotalBluePull = totalBluePullTracker,
@@ -158,7 +143,7 @@ internal sealed class TypedWishSummaryBuilder
OrangeList = summaryItems,
};
- new PullPrediction(serviceProvider, summary, distributionType).PredictAsync(barrier).SafeForget();
+ new PullPrediction(summary, context).PredictAsync(barrier).SafeForget();
return summary;
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilderContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilderContext.cs
new file mode 100644
index 00000000..cd778b05
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/TypedWishSummaryBuilderContext.cs
@@ -0,0 +1,62 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// 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;
+
+namespace Snap.Hutao.Service.GachaLog.Factory;
+
+internal readonly struct TypedWishSummaryBuilderContext
+{
+ public readonly ITaskContext TaskContext;
+ public readonly HomaGachaLogClient GachaLogClient;
+ public readonly string Name;
+ public readonly int GuaranteeOrangeThreshold;
+ public readonly int GuaranteePurpleThreshold;
+ public readonly Func TypeEvaluator;
+ public readonly GachaDistributionType DistributionType;
+
+ private static readonly Func IsStandardWish = type => type is GachaConfigType.StandardWish;
+ private static readonly Func IsAvatarEventWish = type => type is GachaConfigType.AvatarEventWish or GachaConfigType.AvatarEventWish2;
+ private static readonly Func IsWeaponEventWish = type => type is GachaConfigType.WeaponEventWish;
+
+ public TypedWishSummaryBuilderContext(
+ ITaskContext taskContext,
+ HomaGachaLogClient gachaLogClient,
+ string name,
+ int guaranteeOrangeThreshold,
+ int guaranteePurpleThreshold,
+ Func typeEvaluator,
+ GachaDistributionType distributionType)
+ {
+ TaskContext = taskContext;
+ GachaLogClient = gachaLogClient;
+ Name = name;
+ GuaranteeOrangeThreshold = guaranteeOrangeThreshold;
+ GuaranteePurpleThreshold = guaranteePurpleThreshold;
+ TypeEvaluator = typeEvaluator;
+ DistributionType = distributionType;
+ }
+
+ public static TypedWishSummaryBuilderContext StandardWish(ITaskContext taskContext, HomaGachaLogClient gachaLogClient)
+ {
+ return new(taskContext, gachaLogClient, SH.ServiceGachaLogFactoryPermanentWishName, 90, 10, IsStandardWish, GachaDistributionType.Standard);
+ }
+
+ public static TypedWishSummaryBuilderContext AvatarEventWish(ITaskContext taskContext, HomaGachaLogClient gachaLogClient)
+ {
+ return new(taskContext, gachaLogClient, SH.ServiceGachaLogFactoryAvatarWishName, 90, 10, IsAvatarEventWish, GachaDistributionType.AvatarEvent);
+ }
+
+ public static TypedWishSummaryBuilderContext WeaponEventWish(ITaskContext taskContext, HomaGachaLogClient gachaLogClient)
+ {
+ return new(taskContext, gachaLogClient, SH.ServiceGachaLogFactoryWeaponWishName, 80, 10, IsWeaponEventWish, GachaDistributionType.WeaponEvent);
+ }
+
+ public ValueTask> GetGachaDistributionAsync()
+ {
+ return GachaLogClient.GetGachaDistributionAsync(DistributionType);
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchiveOperation.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchiveOperation.cs
index f11e08f3..6bf5dc77 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchiveOperation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaArchiveOperation.cs
@@ -11,22 +11,15 @@ namespace Snap.Hutao.Service.GachaLog;
///
internal static class GachaArchiveOperation
{
- public static void GetOrAdd(IServiceProvider serviceProvider, string uid, ObservableCollection archives, [NotNull] out GachaArchive? archive)
+ public static void GetOrAdd(IGachaLogDbService gachaLogDbService, ITaskContext taskContext, string uid, ObservableCollection archives, [NotNull] out GachaArchive? archive)
{
archive = archives.SingleOrDefault(a => a.Uid == uid);
if (archive is null)
{
GachaArchive created = GachaArchive.From(uid);
- using (IServiceScope scope = serviceProvider.CreateScope())
- {
- IGachaLogDbService gachaLogDbService = scope.ServiceProvider.GetRequiredService();
- gachaLogDbService.AddGachaArchive(created);
-
- ITaskContext taskContext = scope.ServiceProvider.GetRequiredService();
- taskContext.InvokeOnMainThread(() => archives.Add(created));
- }
-
+ gachaLogDbService.AddGachaArchive(created);
+ taskContext.InvokeOnMainThread(() => archives.Add(created));
archive = created;
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaItemSaveContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaItemSaveContext.cs
index 0258fb8a..d62231a8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaItemSaveContext.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaItemSaveContext.cs
@@ -29,20 +29,27 @@ internal readonly struct GachaItemSaveContext
///
/// 数据集
///
- public readonly DbSet GachaItems;
+ public readonly IGachaLogDbService GachaLogDbService;
- ///
- /// 构造一个新的祈愿物品
- ///
- /// 待添加物品
- /// 是否懒惰
- /// 结尾 Id
- /// 数据集
- public GachaItemSaveContext(List itemsToAdd, bool isLazy, long endId, DbSet gachaItems)
+ public GachaItemSaveContext(List itemsToAdd, bool isLazy, long endId, IGachaLogDbService gachaLogDbService)
{
ItemsToAdd = itemsToAdd;
IsLazy = isLazy;
EndId = endId;
- GachaItems = gachaItems;
+ GachaLogDbService = gachaLogDbService;
+ }
+
+ public void SaveItems(GachaArchive archive)
+ {
+ if (ItemsToAdd.Count > 0)
+ {
+ // 全量刷新
+ if (!IsLazy)
+ {
+ GachaLogDbService.DeleteNewerGachaItemsByArchiveIdAndEndId(archive.InnerId, EndId);
+ }
+
+ GachaLogDbService.AddGachaItems(ItemsToAdd);
+ }
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogDbService.cs
index 50892f99..b963b45d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogDbService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogDbService.cs
@@ -13,7 +13,7 @@ using System.Collections.ObjectModel;
namespace Snap.Hutao.Service.GachaLog;
[ConstructorGenerated]
-[Injection(InjectAs.Scoped, typeof(IGachaLogDbService))]
+[Injection(InjectAs.Singleton, typeof(IGachaLogDbService))]
internal sealed partial class GachaLogDbService : IGachaLogDbService
{
private readonly IServiceProvider serviceProvider;
@@ -30,7 +30,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
}
catch (SqliteException ex)
{
- string message = string.Format(SH.ServiceGachaLogArchiveCollectionUserdataCorruptedMessage, ex.Message);
+ string message = SH.ServiceGachaLogArchiveCollectionUserdataCorruptedMessage.Format(ex.Message);
throw ThrowHelper.UserdataCorrupted(message, ex);
}
}
@@ -202,4 +202,26 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
await appDbContext.GachaItems.AddRangeAndSaveAsync(items).ConfigureAwait(false);
}
}
-}
+
+ public void AddGachaItems(List items)
+ {
+ using (IServiceScope scope = serviceProvider.CreateScope())
+ {
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+ appDbContext.GachaItems.AddRangeAndSave(items);
+ }
+ }
+
+ public void DeleteNewerGachaItemsByArchiveIdAndEndId(Guid archiveId, long endId)
+ {
+ using (IServiceScope scope = serviceProvider.CreateScope())
+ {
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+ appDbContext.GachaItems
+ .AsNoTracking()
+ .Where(i => i.ArchiveId == archiveId)
+ .Where(i => i.Id >= endId)
+ .ExecuteDelete();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs
index ad34ec84..2aaaa057 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchContext.cs
@@ -49,19 +49,15 @@ internal struct GachaLogFetchContext
///
public GachaConfigType CurrentType;
- private readonly IServiceProvider serviceProvider;
private readonly GachaLogServiceMetadataContext serviceContext;
+ private readonly IGachaLogDbService gachaLogDbService;
+ private readonly ITaskContext taskContext;
private readonly bool isLazy;
- ///
- /// 构造一个新的祈愿记录获取上下文
- ///
- /// 服务提供器
- /// 祈愿服务上下文
- /// 是否为懒惰模式
- public GachaLogFetchContext(IServiceProvider serviceProvider, in GachaLogServiceMetadataContext serviceContext, bool isLazy)
+ public GachaLogFetchContext(IGachaLogDbService gachaLogDbService, ITaskContext taskContext, in GachaLogServiceMetadataContext serviceContext, bool isLazy)
{
- this.serviceProvider = serviceProvider;
+ this.gachaLogDbService = gachaLogDbService;
+ this.taskContext = taskContext;
this.serviceContext = serviceContext;
this.isLazy = isLazy;
}
@@ -99,7 +95,7 @@ internal struct GachaLogFetchContext
{
if (TargetArchive is null)
{
- GachaArchiveOperation.GetOrAdd(serviceProvider, item.Uid, archives, out TargetArchive);
+ GachaArchiveOperation.GetOrAdd(gachaLogDbService, taskContext, item.Uid, archives, out TargetArchive);
}
DbEndId ??= gachaLogDbService.GetNewestGachaItemIdByArchiveIdAndQueryType(TargetArchive.InnerId, CurrentType);
@@ -131,7 +127,8 @@ internal struct GachaLogFetchContext
/// 物品
public void AddItem(GachaLogItem item)
{
- ItemsToAdd.Add(GachaItem.From(TargetArchive!.InnerId, item, serviceContext.GetItemId(item)));
+ ArgumentNullException.ThrowIfNull(TargetArchive);
+ ItemsToAdd.Add(GachaItem.From(TargetArchive.InnerId, item, serviceContext.GetItemId(item)));
FetchStatus.Items.Add(serviceContext.GetItemByNameAndType(item.Name, item.ItemType));
QueryOptions.EndId = item.Id;
}
@@ -141,16 +138,11 @@ internal struct GachaLogFetchContext
///
public readonly void SaveItems()
{
- using (IServiceScope scope = serviceProvider.CreateScope())
+ // While no item is fetched, archive can be null.
+ if (TargetArchive is not null)
{
- AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
-
- // While no item is fetched, archive can be null.
- if (TargetArchive is not null)
- {
- GachaItemSaveContext saveContext = new(ItemsToAdd, isLazy, QueryOptions.EndId, appDbContext.GachaItems);
- TargetArchive.SaveItems(saveContext);
- }
+ GachaItemSaveContext saveContext = new(ItemsToAdd, isLazy, QueryOptions.EndId, gachaLogDbService);
+ saveContext.SaveItems(TargetArchive);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogHutaoCloudService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogHutaoCloudService.cs
index aac86540..cd9cf702 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogHutaoCloudService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogHutaoCloudService.cs
@@ -25,9 +25,9 @@ namespace Snap.Hutao.Service.GachaLog;
[Injection(InjectAs.Scoped, typeof(IGachaLogHutaoCloudService))]
internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudService
{
+ private readonly IMetadataService metadataService;
private readonly HomaGachaLogClient homaGachaLogClient;
private readonly IGachaLogDbService gachaLogDbService;
- private readonly IServiceProvider serviceProvider;
///
public ValueTask>> GetGachaEntriesAsync(CancellationToken token = default)
@@ -92,7 +92,6 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
Response response = await homaGachaLogClient.GetGachaEventStatisticsAsync(token).ConfigureAwait(false);
if (response.IsOk())
{
- IMetadataService metadataService = serviceProvider.GetRequiredService();
if (await metadataService.InitializeAsync().ConfigureAwait(false))
{
Dictionary idAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false);
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs
index 008502a6..3796c577 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs
@@ -29,7 +29,6 @@ internal sealed partial class GachaLogService : IGachaLogService
private readonly IGachaStatisticsFactory gachaStatisticsFactory;
private readonly IUIGFExportService gachaLogExportService;
private readonly IUIGFImportService gachaLogImportService;
- private readonly IServiceProvider serviceProvider;
private readonly IMetadataService metadataService;
private readonly ILogger logger;
private readonly GachaInfoClient gachaInfoClient;
@@ -160,7 +159,7 @@ internal sealed partial class GachaLogService : IGachaLogService
private async ValueTask> FetchGachaLogsAsync(GachaLogQuery query, bool isLazy, IProgress progress, CancellationToken token)
{
ArgumentNullException.ThrowIfNull(ArchiveCollection);
- GachaLogFetchContext fetchContext = new(serviceProvider, context, isLazy);
+ GachaLogFetchContext fetchContext = new(gachaLogDbService, taskContext, context, isLazy);
foreach (GachaConfigType configType in GachaLog.QueryTypes)
{
@@ -172,7 +171,7 @@ internal sealed partial class GachaLogService : IGachaLogService
.GetGachaLogPageAsync(fetchContext.QueryOptions, token)
.ConfigureAwait(false);
- if (response.TryGetData(out GachaLogPage? page, serviceProvider))
+ if (response.TryGetData(out GachaLogPage? page))
{
List items = page.List;
fetchContext.ResetForProcessingPage();
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceMetadataContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceMetadataContext.cs
index e56e7cd4..0a9edc5a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceMetadataContext.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogServiceMetadataContext.cs
@@ -121,12 +121,12 @@ internal readonly struct GachaLogServiceMetadataContext
{
if (item.ItemType == SH.ModelInterchangeUIGFItemTypeAvatar)
{
- return NameAvatarMap!.GetValueOrDefault(item.Name)?.Id ?? 0;
+ return NameAvatarMap.GetValueOrDefault(item.Name)?.Id ?? 0;
}
if (item.ItemType == SH.ModelInterchangeUIGFItemTypeWeapon)
{
- return NameWeaponMap!.GetValueOrDefault(item.Name)?.Id ?? 0;
+ return NameWeaponMap.GetValueOrDefault(item.Name)?.Id ?? 0;
}
return 0U;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogDbService.cs
index 61790b3b..4940c1d6 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogDbService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogDbService.cs
@@ -13,10 +13,14 @@ internal interface IGachaLogDbService
ValueTask AddGachaArchiveAsync(GachaArchive archive);
+ void AddGachaItems(List items);
+
ValueTask AddGachaItemsAsync(List items);
ValueTask DeleteGachaArchiveByIdAsync(Guid archiveId);
+ void DeleteNewerGachaItemsByArchiveIdAndEndId(Guid archiveId, long endId);
+
ValueTask GetGachaArchiveByUidAsync(string uid, CancellationToken token);
ObservableCollection GetGachaArchiveCollection();
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuery.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuery.cs
index 95c6347c..231800af 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuery.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuery.cs
@@ -31,7 +31,7 @@ internal readonly struct GachaLogQuery
public GachaLogQuery(string query)
{
Query = query;
- IsOversea = query.Contains("hoyoverse.com");
+ IsOversea = query.Contains("hoyoverse.com", StringComparison.OrdinalIgnoreCase);
Message = string.Empty;
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs
index 32298d6d..f0b791ff 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryManualInputProvider.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.View.Dialog;
using Snap.Hutao.Web.Request.QueryString;
@@ -15,34 +16,29 @@ namespace Snap.Hutao.Service.GachaLog.QueryProvider;
[Injection(InjectAs.Transient)]
internal sealed partial class GachaLogQueryManualInputProvider : IGachaLogQueryProvider
{
- private readonly IServiceProvider serviceProvider;
+ private readonly IContentDialogFactory contentDialogFactory;
private readonly MetadataOptions metadataOptions;
- private readonly ITaskContext taskContext;
///
public async ValueTask> GetQueryAsync()
{
- // ContentDialog must be created by main thread.
- await taskContext.SwitchToMainThreadAsync();
- GachaLogUrlDialog dialog = serviceProvider.CreateInstance();
+ GachaLogUrlDialog dialog = await contentDialogFactory.CreateInstanceAsync().ConfigureAwait(false);
(bool isOk, string queryString) = await dialog.GetInputUrlAsync().ConfigureAwait(false);
if (isOk)
{
QueryString query = QueryString.Parse(queryString);
- string queryLanguageCode = query["lang"];
if (query["auth_appid"] == "webview_gacha")
{
+ string queryLanguageCode = query["lang"];
if (metadataOptions.IsCurrentLocale(queryLanguageCode))
{
return new(true, new(queryString));
}
else
{
- string message = string.Format(
- SH.ServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale,
- queryLanguageCode,
- metadataOptions.LanguageCode);
+ string message = SH.ServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale
+ .Format(queryLanguageCode, metadataOptions.LanguageCode);
return new(false, message);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryProviderFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryProviderFactory.cs
index e26a43b4..55752039 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryProviderFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryProviderFactory.cs
@@ -7,6 +7,7 @@ namespace Snap.Hutao.Service.GachaLog.QueryProvider;
[Injection(InjectAs.Transient, typeof(IGachaLogQueryProviderFactory))]
internal sealed partial class GachaLogQueryProviderFactory : IGachaLogQueryProviderFactory
{
+ [SuppressMessage("", "SH301")]
private readonly IServiceProvider serviceProvider;
public IGachaLogQueryProvider Create(RefreshOption option)
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs
index 9953a746..6a7e74fd 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQueryWebCacheProvider.cs
@@ -35,7 +35,9 @@ internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProv
? GameConstants.GenshinImpactData
: GameConstants.YuanShenData;
- DirectoryInfo webCacheFolder = new(Path.Combine(Path.GetDirectoryName(path)!, dataFolder, "webCaches"));
+ string? directory = Path.GetDirectoryName(path);
+ ArgumentNullException.ThrowIfNull(directory);
+ DirectoryInfo webCacheFolder = new(Path.Combine(directory, dataFolder, "webCaches"));
Regex versionRegex = VersionRegex();
DirectoryInfo? lastestVersionCacheFolder = webCacheFolder
.EnumerateDirectories()
@@ -51,53 +53,45 @@ internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProv
{
(bool isOk, string path) = await gameService.GetGamePathAsync().ConfigureAwait(false);
- if (isOk && (!string.IsNullOrEmpty(path)))
- {
- string cacheFile = GetCacheFile(path);
-
- using (TempFile? tempFile = TempFile.CopyFrom(cacheFile))
- {
- if (tempFile.TryGetValue(out TempFile file))
- {
- using (FileStream fileStream = new(file.Path, FileMode.Open, FileAccess.Read, FileShare.Read))
- {
- using (MemoryStream memoryStream = new())
- {
- await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
- string? result = Match(memoryStream, cacheFile.Contains(GameConstants.GenshinImpactData));
-
- if (!string.IsNullOrEmpty(result))
- {
- QueryString query = QueryString.Parse(result.TrimEnd("#/log"));
- string queryLanguageCode = query["lang"];
- if (metadataOptions.IsCurrentLocale(queryLanguageCode))
- {
- return new(true, new(result));
- }
- else
- {
- string message = string.Format(
- SH.ServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale,
- queryLanguageCode,
- metadataOptions.LanguageCode);
- return new(false, message);
- }
- }
- else
- {
- return new(false, SH.ServiceGachaLogUrlProviderCacheUrlNotFound);
- }
- }
- }
- }
-
- return new(false, string.Format(Regex.Unescape(SH.ServiceGachaLogUrlProviderCachePathNotFound), cacheFile));
- }
- }
- else
+ if (!isOk || string.IsNullOrEmpty(path))
{
return new(false, SH.ServiceGachaLogUrlProviderCachePathInvalid);
}
+
+ string cacheFile = GetCacheFile(path);
+ using (TempFile? tempFile = TempFile.CopyFrom(cacheFile))
+ {
+ if (!tempFile.TryGetValue(out TempFile file))
+ {
+ return new(false, Regex.Unescape(SH.ServiceGachaLogUrlProviderCachePathNotFound).Format(cacheFile));
+ }
+
+ using (FileStream fileStream = new(file.Path, FileMode.Open, FileAccess.Read, FileShare.Read))
+ {
+ using (MemoryStream memoryStream = new())
+ {
+ await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
+ string? result = Match(memoryStream, cacheFile.Contains(GameConstants.GenshinImpactData, StringComparison.Ordinal));
+
+ if (string.IsNullOrEmpty(result))
+ {
+ return new(false, SH.ServiceGachaLogUrlProviderCacheUrlNotFound);
+ }
+
+ QueryString query = QueryString.Parse(result.TrimEnd("#/log"));
+ string queryLanguageCode = query["lang"];
+
+ if (metadataOptions.IsCurrentLocale(queryLanguageCode))
+ {
+ return new(true, new(result));
+ }
+
+ string message = SH.ServiceGachaLogUrlProviderUrlLanguageNotMatchCurrentLocale
+ .Format(queryLanguageCode, metadataOptions.LanguageCode);
+ return new(false, message);
+ }
+ }
+ }
}
private static string? Match(MemoryStream stream, bool isOversea)
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFExportService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFExportService.cs
index e2463449..21d420b6 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFExportService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFExportService.cs
@@ -1,9 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Core;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.InterChange.GachaLog;
+using Snap.Hutao.Service.Metadata;
namespace Snap.Hutao.Service.GachaLog;
@@ -14,8 +16,9 @@ namespace Snap.Hutao.Service.GachaLog;
[Injection(InjectAs.Scoped, typeof(IUIGFExportService))]
internal sealed partial class UIGFExportService : IUIGFExportService
{
- private readonly IServiceProvider serviceProvider;
private readonly IGachaLogDbService gachaLogDbService;
+ private readonly RuntimeOptions runtimeOptions;
+ private readonly MetadataOptions metadataOptions;
private readonly ITaskContext taskContext;
///
@@ -29,7 +32,7 @@ internal sealed partial class UIGFExportService : IUIGFExportService
UIGF uigf = new()
{
- Info = UIGFInfo.From(serviceProvider, archive.Uid),
+ Info = UIGFInfo.From(runtimeOptions, metadataOptions, archive.Uid),
List = list,
};
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs
index 7b9e9471..76a7c99d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UIGFImportService.cs
@@ -18,7 +18,6 @@ internal sealed partial class UIGFImportService : IUIGFImportService
{
private readonly ILogger logger;
private readonly MetadataOptions metadataOptions;
- private readonly IServiceProvider serviceProvider;
private readonly IGachaLogDbService gachaLogDbService;
private readonly ITaskContext taskContext;
@@ -29,11 +28,11 @@ internal sealed partial class UIGFImportService : IUIGFImportService
if (!metadataOptions.IsCurrentLocale(uigf.Info.Language))
{
- string message = string.Format(SH.ServiceGachaUIGFImportLanguageNotMatch, uigf.Info.Language, metadataOptions.LanguageCode);
+ string message = SH.ServiceGachaUIGFImportLanguageNotMatch.Format(uigf.Info.Language, metadataOptions.LanguageCode);
ThrowHelper.InvalidOperation(message, null);
}
- GachaArchiveOperation.GetOrAdd(serviceProvider, uigf.Info.Uid, archives, out GachaArchive? archive);
+ GachaArchiveOperation.GetOrAdd(gachaLogDbService, taskContext, uigf.Info.Uid, archives, out GachaArchive? archive);
Guid archiveId = archive.InnerId;
long trimId = gachaLogDbService.GetOldestGachaItemIdByArchiveId(archiveId);
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameFileOperationException.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameFileOperationException.cs
index bdf7c5ed..59d3a000 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameFileOperationException.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameFileOperationException.cs
@@ -15,7 +15,7 @@ internal sealed class GameFileOperationException : Exception
/// 消息
/// 内部错误
public GameFileOperationException(string message, Exception innerException)
- : base(string.Format(SH.ServiceGameFileOperationExceptionMessage, message), innerException)
+ : base(SH.ServiceGameFileOperationExceptionMessage.Format(message), innerException)
{
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
index 6565294a..2ce14a2f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
@@ -82,7 +82,7 @@ internal sealed partial class GameService : IGameService
}
else
{
- return new(false, null!);
+ return new(false, default!);
}
}
@@ -112,9 +112,11 @@ internal sealed partial class GameService : IGameService
public bool SetChannelOptions(LaunchScheme scheme)
{
string gamePath = appOptions.GamePath;
- string configPath = Path.Combine(Path.GetDirectoryName(gamePath)!, ConfigFileName);
+ string? directory = Path.GetDirectoryName(gamePath);
+ ArgumentException.ThrowIfNullOrEmpty(directory);
+ string configPath = Path.Combine(directory, ConfigFileName);
- List elements = null!;
+ List elements = default!;
try
{
using (FileStream readStream = File.OpenRead(configPath))
@@ -124,11 +126,11 @@ internal sealed partial class GameService : IGameService
}
catch (FileNotFoundException ex)
{
- ThrowHelper.GameFileOperation(string.Format(SH.ServiceGameSetMultiChannelConfigFileNotFound, configPath), ex);
+ ThrowHelper.GameFileOperation(SH.ServiceGameSetMultiChannelConfigFileNotFound.Format(configPath), ex);
}
catch (DirectoryNotFoundException ex)
{
- ThrowHelper.GameFileOperation(string.Format(SH.ServiceGameSetMultiChannelConfigFileNotFound, configPath), ex);
+ ThrowHelper.GameFileOperation(SH.ServiceGameSetMultiChannelConfigFileNotFound.Format(configPath), ex);
}
catch (UnauthorizedAccessException ex)
{
@@ -168,7 +170,8 @@ internal sealed partial class GameService : IGameService
public async ValueTask EnsureGameResourceAsync(LaunchScheme launchScheme, IProgress progress)
{
string gamePath = appOptions.GamePath;
- string gameFolder = Path.GetDirectoryName(gamePath)!;
+ string? gameFolder = Path.GetDirectoryName(gamePath);
+ ArgumentException.ThrowIfNullOrEmpty(gameFolder);
string gameFileName = Path.GetFileName(gamePath);
progress.Report(new(SH.ServiceGameEnsureGameResourceQueryResourceInformation));
@@ -241,39 +244,36 @@ internal sealed partial class GameService : IGameService
}
string gamePath = appOptions.GamePath;
- if (string.IsNullOrWhiteSpace(gamePath))
+ ArgumentNullException.ThrowIfNullOrEmpty(gamePath);
+
+ using (Process game = ProcessInterop.InitializeGameProcess(launchOptions, gamePath))
{
- // TODO: throw exception
- return;
- }
-
- Process game = ProcessInterop.InitializeGameProcess(launchOptions, gamePath);
-
- try
- {
- bool isFirstInstance = Interlocked.Increment(ref runningGamesCounter) == 1;
-
- game.Start();
-
- bool isAdvancedOptionsAllowed = runtimeOptions.IsElevated && appOptions.IsAdvancedLaunchOptionsEnabled;
- if (isAdvancedOptionsAllowed && launchOptions.MultipleInstances && !isFirstInstance)
+ try
{
- ProcessInterop.DisableProtection(game, gamePath);
- }
+ bool isFirstInstance = Interlocked.Increment(ref runningGamesCounter) == 1;
- if (isAdvancedOptionsAllowed && launchOptions.UnlockFps)
- {
- await ProcessInterop.UnlockFpsAsync(serviceProvider, game, default).ConfigureAwait(false);
+ game.Start();
+
+ bool isAdvancedOptionsAllowed = runtimeOptions.IsElevated && appOptions.IsAdvancedLaunchOptionsEnabled;
+ if (isAdvancedOptionsAllowed && launchOptions.MultipleInstances && !isFirstInstance)
+ {
+ ProcessInterop.DisableProtection(game, gamePath);
+ }
+
+ if (isAdvancedOptionsAllowed && launchOptions.UnlockFps)
+ {
+ await ProcessInterop.UnlockFpsAsync(serviceProvider, game, default).ConfigureAwait(false);
+ }
+ else
+ {
+ await game.WaitForExitAsync().ConfigureAwait(false);
+ }
}
- else
+ finally
{
- await game.WaitForExitAsync().ConfigureAwait(false);
+ Interlocked.Decrement(ref runningGamesCounter);
}
}
- finally
- {
- Interlocked.Decrement(ref runningGamesCounter);
- }
}
///
@@ -373,7 +373,8 @@ internal sealed partial class GameService : IGameService
public async ValueTask RemoveGameAccountAsync(GameAccount gameAccount)
{
await taskContext.SwitchToMainThreadAsync();
- gameAccounts!.Remove(gameAccount);
+ ArgumentNullException.ThrowIfNull(gameAccounts);
+ gameAccounts.Remove(gameAccount);
await taskContext.SwitchToBackgroundAsync();
await gameDbService.DeleteGameAccountByIdAsync(gameAccount.InnerId).ConfigureAwait(false);
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs
index baf237b3..b2813d54 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs
@@ -5,6 +5,7 @@ using Microsoft.UI.Windowing;
using Snap.Hutao.Model;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service.Abstraction;
+using System.Globalization;
using Windows.Graphics;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi;
@@ -140,12 +141,12 @@ internal sealed class LaunchOptions : DbStoreOptions
[AllowNull]
public NameValue Monitor
{
- get => GetOption(ref monitor, SettingEntry.LaunchMonitor, index => Monitors[int.Parse(index) - 1], Monitors[0]);
+ get => GetOption(ref monitor, SettingEntry.LaunchMonitor, index => Monitors[int.Parse(index, CultureInfo.InvariantCulture) - 1], Monitors[0]);
set
{
if (value is not null)
{
- SetOption(ref monitor, SettingEntry.LaunchMonitor, value, selected => selected.Value.ToString());
+ SetOption(ref monitor, SettingEntry.LaunchMonitor, value, selected => selected.Value.ToString(CultureInfo.InvariantCulture));
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/GameLocatorFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/GameLocatorFactory.cs
index bcd7ef91..69872349 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/GameLocatorFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/GameLocatorFactory.cs
@@ -7,6 +7,7 @@ namespace Snap.Hutao.Service.Game.Locator;
[Injection(InjectAs.Transient, typeof(IGameLocatorFactory))]
internal sealed partial class GameLocatorFactory : IGameLocatorFactory
{
+ [SuppressMessage("", "SH301")]
private readonly IServiceProvider serviceProvider;
public IGameLocator Create(GameLocationSource source)
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/ManualGameLocator.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/ManualGameLocator.cs
index d685bee8..7f281662 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/ManualGameLocator.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/ManualGameLocator.cs
@@ -39,6 +39,6 @@ internal sealed partial class ManualGameLocator : IGameLocator
}
}
- return new(false, null!);
+ return new(false, default!);
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/RegistryLauncherLocator.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/RegistryLauncherLocator.cs
index e34cd8e1..3ec14227 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/RegistryLauncherLocator.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/RegistryLauncherLocator.cs
@@ -32,7 +32,8 @@ internal sealed partial class RegistryLauncherLocator : IGameLocator
else
{
string? path = Path.GetDirectoryName(result.Value);
- string configPath = Path.Combine(path!, GameConstants.ConfigFileName);
+ ArgumentException.ThrowIfNullOrEmpty(path);
+ string configPath = Path.Combine(path, GameConstants.ConfigFileName);
string? escapedPath;
using (FileStream stream = File.OpenRead(configPath))
{
@@ -64,12 +65,12 @@ internal sealed partial class RegistryLauncherLocator : IGameLocator
}
else
{
- return new(false, null!);
+ return new(false, default!);
}
}
else
{
- return new(false, null!);
+ return new(false, default!);
}
}
}
@@ -80,10 +81,10 @@ internal sealed partial class RegistryLauncherLocator : IGameLocator
// 不包含中文
// Some one's folder might begin with 'u'
- if (!hex4Result.Contains(@"\u"))
+ if (!hex4Result.Contains(@"\u", StringComparison.Ordinal))
{
// fix path with \
- hex4Result = hex4Result.Replace(@"\", @"\\");
+ hex4Result = hex4Result.Replace(@"\", @"\\", StringComparison.Ordinal);
}
return Regex.Unescape(hex4Result);
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs
index 29dfbf0f..7321a6f5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs
@@ -116,7 +116,9 @@ internal sealed partial class PackageConverter
if (entry.Length != 0)
{
string targetPath = Path.Combine(gameFolder, entry.FullName);
- Directory.CreateDirectory(Path.GetDirectoryName(targetPath)!);
+ string? directory = Path.GetDirectoryName(targetPath);
+ ArgumentException.ThrowIfNullOrEmpty(directory);
+ Directory.CreateDirectory(directory);
entry.ExtractToFile(targetPath, true);
}
}
@@ -180,7 +182,8 @@ internal sealed partial class PackageConverter
Regex dataFolderRegex = DataFolderRegex();
while (await reader.ReadLineAsync().ConfigureAwait(false) is { } row && !string.IsNullOrEmpty(row))
{
- VersionItem item = JsonSerializer.Deserialize(row, options)!;
+ VersionItem? item = JsonSerializer.Deserialize(row, options);
+ ArgumentNullException.ThrowIfNull(item);
item.RelativePath = dataFolderRegex.Replace(item.RelativePath, "{0}");
results.Add(item.RelativePath, item);
}
@@ -231,7 +234,7 @@ internal sealed partial class PackageConverter
private async ValueTask SkipOrDownloadAsync(ItemOperationInfo info, PackageConvertContext context, IProgress progress)
{
// 还原正确的远程地址
- string remoteName = string.Format(info.Remote.RelativePath, context.ToDataFolderName);
+ string remoteName = info.Remote.RelativePath.Format(context.ToDataFolderName);
string cacheFile = context.GetServerCacheTargetFilePath(remoteName);
if (File.Exists(cacheFile))
@@ -276,7 +279,7 @@ internal sealed partial class PackageConverter
{
// System.IO.IOException: The response ended prematurely.
// System.IO.IOException: Received an unexpected EOF or 0 bytes from the transport stream.
- ThrowHelper.PackageConvert(string.Format(SH.ServiceGamePackageRequestScatteredFileFailed, remoteName), ex);
+ ThrowHelper.PackageConvert(SH.ServiceGamePackageRequestScatteredFileFailed.Format(remoteName), ex);
}
}
}
@@ -312,17 +315,21 @@ internal sealed partial class PackageConverter
if (backup)
{
- string localFileName = string.Format(info.Local.RelativePath, context.FromDataFolder);
+ string localFileName = info.Local.RelativePath.Format(context.FromDataFolder);
string localFilePath = context.GetGameFolderFilePath(localFileName);
- Directory.CreateDirectory(Path.GetDirectoryName(localFilePath)!);
+ string? directory = Path.GetDirectoryName(localFilePath);
+ ArgumentException.ThrowIfNullOrEmpty(directory);
+ Directory.CreateDirectory(directory);
File.Move(localFilePath, context.GetServerCacheBackupFilePath(localFileName), true);
}
if (target)
{
- string targetFileName = string.Format(info.Remote.RelativePath, context.ToDataFolder);
+ string targetFileName = info.Remote.RelativePath.Format(context.ToDataFolder);
string targetFilePath = context.GetGameFolderFilePath(targetFileName);
- Directory.CreateDirectory(Path.GetDirectoryName(targetFilePath)!);
+ string? directory = Path.GetDirectoryName(targetFilePath);
+ ArgumentException.ThrowIfNullOrEmpty(directory);
+ Directory.CreateDirectory(directory);
File.Move(context.GetServerCacheTargetFilePath(targetFileName), targetFilePath, true);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/RegistryInterop.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/RegistryInterop.cs
index f4ec80eb..f4325979 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/RegistryInterop.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/RegistryInterop.cs
@@ -88,7 +88,8 @@ internal static class RegistryInterop
private static string GetPowerShellLocation()
{
- string paths = Environment.GetEnvironmentVariable("Path")!;
+ string? paths = Environment.GetEnvironmentVariable("Path");
+ ArgumentException.ThrowIfNullOrEmpty(paths);
foreach (StringSegment path in new StringTokenizer(paths, ';'.ToArray()))
{
@@ -101,6 +102,6 @@ internal static class RegistryInterop
}
}
- throw ThrowHelper.RuntimeEnvironment(SH.ServiceGameRegisteryInteropPowershellNotFound, null!);
+ throw ThrowHelper.RuntimeEnvironment(SH.ServiceGameRegisteryInteropPowershellNotFound, default!);
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs
index f172b865..47360271 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoCache.cs
@@ -184,7 +184,7 @@ internal sealed partial class HutaoCache : IHutaoCache
AvatarAppearanceRanks = avatarAppearanceRanksRaw.SortByDescending(r => r.Floor).SelectList(rank => new AvatarRankView
{
- Floor = string.Format(SH.ModelBindingHutaoComplexRankFloor, rank.Floor),
+ Floor = SH.ModelBindingHutaoComplexRankFloor.Format(rank.Floor),
Avatars = rank.Ranks.SortByDescending(r => r.Rate).SelectList(rank => new AvatarView(idAvatarMap[rank.Item], rank.Rate)),
});
}
@@ -201,7 +201,7 @@ internal sealed partial class HutaoCache : IHutaoCache
AvatarUsageRanks = avatarUsageRanksRaw.SortByDescending(r => r.Floor).SelectList(rank => new AvatarRankView
{
- Floor = string.Format(SH.ModelBindingHutaoComplexRankFloor, rank.Floor),
+ Floor = SH.ModelBindingHutaoComplexRankFloor.Format(rank.Floor),
Avatars = rank.Ranks.SortByDescending(r => r.Rate).SelectList(rank => new AvatarView(idAvatarMap[rank.Item], rank.Rate)),
});
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs
index ecd589be..5f5c6814 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs
@@ -19,9 +19,10 @@ namespace Snap.Hutao.Service.Hutao;
[Injection(InjectAs.Scoped, typeof(IHutaoService))]
internal sealed partial class HutaoService : IHutaoService
{
- private static readonly TimeSpan CacheExpireTime = TimeSpan.FromHours(4);
+ private readonly TimeSpan cacheExpireTime = TimeSpan.FromHours(4);
+
+ private readonly IObjectCacheDbService objectCacheDbService;
private readonly HomaSpiralAbyssClient homaClient;
- private readonly IServiceProvider serviceProvider;
private readonly JsonSerializerOptions options;
private readonly IMemoryCache memoryCache;
@@ -67,59 +68,30 @@ internal sealed partial class HutaoService : IHutaoService
return FromCacheOrWebAsync(nameof(TeamAppearance), homaClient.GetTeamCombinationsAsync);
}
- private async ValueTask FromCacheOrWebAsync(string typeName, Func>> taskFunc)
- where T : new()
+ private async ValueTask FromCacheOrWebAsync(string typeName, Func>> taskFunc)
+ where T : class, new()
{
string key = $"{nameof(HutaoService)}.Cache.{typeName}";
if (memoryCache.TryGetValue(key, out object? cache))
{
- return (T)cache!;
+ T? t = cache as T;
+ ArgumentNullException.ThrowIfNull(t);
+ return t;
}
- using (IServiceScope scope = serviceProvider.CreateScope())
+ if (await objectCacheDbService.GetObjectOrDefaultAsync(key).ConfigureAwait(false) is { } value)
{
- AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
-
- if (appDbContext.ObjectCache.SingleOrDefault(e => e.Key == key) is { } entry)
- {
- if (entry.IsExpired)
- {
- await appDbContext.ObjectCache.RemoveAndSaveAsync(entry).ConfigureAwait(false);
- }
- else
- {
- T value = JsonSerializer.Deserialize(entry.Value!, options)!;
- return memoryCache.Set(key, value, TimeSpan.FromMinutes(30));
- }
- }
+ return memoryCache.Set(key, value, cacheExpireTime);
}
Response webResponse = await taskFunc(default).ConfigureAwait(false);
T? data = webResponse.Data;
- try
+ if (data is not null)
{
- if (data is not null)
- {
- using (IServiceScope scope = serviceProvider.CreateScope())
- {
- AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
-
- await appDbContext.ObjectCache.AddAndSaveAsync(new()
- {
- Key = key,
- ExpireTime = DateTimeOffset.Now.Add(CacheExpireTime),
- Value = JsonSerializer.Serialize(data, options),
- }).ConfigureAwait(false);
- }
- }
- }
- catch (Microsoft.EntityFrameworkCore.DbUpdateException)
- {
- // DbUpdateException: An error occurred while saving the entity changes.
- // TODO: Not ignore it.
+ await objectCacheDbService.AddObjectCacheAsync(key, cacheExpireTime, data).ConfigureAwait(false);
}
- return memoryCache.Set(key, data ?? new(), CacheExpireTime);
+ return memoryCache.Set(key, data ?? new(), cacheExpireTime);
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptions.cs
index 2dc82229..e6e510aa 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserOptions.cs
@@ -86,7 +86,7 @@ internal sealed class HutaoUserOptions : ObservableObject, IOptions DateTimeOffset.Now;
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IObjectCacheDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IObjectCacheDbService.cs
new file mode 100644
index 00000000..fafaf6f6
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IObjectCacheDbService.cs
@@ -0,0 +1,13 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Service.Hutao;
+
+internal interface IObjectCacheDbService
+{
+ ValueTask AddObjectCacheAsync(string key, TimeSpan expire, T data)
+ where T : class;
+
+ ValueTask GetObjectOrDefaultAsync(string key)
+ where T : class;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/ObjectCacheDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/ObjectCacheDbService.cs
new file mode 100644
index 00000000..cb9e8631
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/ObjectCacheDbService.cs
@@ -0,0 +1,68 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Caching.Memory;
+using Snap.Hutao.Core.Database;
+using Snap.Hutao.Core.ExceptionService;
+using Snap.Hutao.Model.Entity;
+using Snap.Hutao.Model.Entity.Database;
+using Snap.Hutao.Web.Hutao;
+using Snap.Hutao.Web.Hutao.Model;
+using Snap.Hutao.Web.Response;
+
+namespace Snap.Hutao.Service.Hutao;
+
+[ConstructorGenerated]
+[Injection(InjectAs.Singleton, typeof(IObjectCacheDbService))]
+internal sealed partial class ObjectCacheDbService : IObjectCacheDbService
+{
+ private readonly IServiceProvider serviceProvider;
+ private readonly JsonSerializerOptions jsonSerializerOptions;
+
+ public async ValueTask AddObjectCacheAsync(string key, TimeSpan expire, T data)
+ where T : class
+ {
+ using (IServiceScope scope = serviceProvider.CreateScope())
+ {
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+
+ await appDbContext.ObjectCache.AddAndSaveAsync(new()
+ {
+ Key = key,
+ ExpireTime = DateTimeOffset.Now.Add(expire),
+ Value = JsonSerializer.Serialize(data, jsonSerializerOptions),
+ }).ConfigureAwait(false);
+ }
+ }
+
+ public async ValueTask GetObjectOrDefaultAsync(string key)
+ where T : class
+ {
+ try
+ {
+ using (IServiceScope scope = serviceProvider.CreateScope())
+ {
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+
+ if (await appDbContext.ObjectCache.SingleOrDefaultAsync(e => e.Key == key).ConfigureAwait(false) is { } entry)
+ {
+ if (!entry.IsExpired)
+ {
+ ArgumentNullException.ThrowIfNull(entry.Value);
+ T? value = JsonSerializer.Deserialize(entry.Value, jsonSerializerOptions);
+ return value;
+ }
+
+ await appDbContext.ObjectCache.RemoveAndSaveAsync(entry).ConfigureAwait(false);
+ }
+ }
+ }
+ catch (DbUpdateException ex)
+ {
+ ThrowHelper.DatabaseCorrupted($"无法存储 Key:{key} 对应的值到数据库缓存", ex);
+ }
+
+ return default!;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Constants.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Constants.cs
index 662ddc54..482bdc56 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Constants.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Constants.cs
@@ -8,6 +8,7 @@ namespace Snap.Hutao.Service.Metadata;
///
internal partial class MetadataService
{
+#pragma warning disable CA1823
private const string FileNameAchievement = "Achievement";
private const string FileNameAchievementGoal = "AchievementGoal";
private const string FileNameAvatar = "Avatar";
@@ -30,4 +31,5 @@ internal partial class MetadataService
private const string FileNameWeapon = "Weapon";
private const string FileNameWeaponCurve = "WeaponCurve";
private const string FileNameWeaponPromote = "WeaponPromote";
+#pragma warning restore
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs
index 7af3a781..b2218f00 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs
@@ -132,7 +132,7 @@ internal sealed class NavigationService : INavigationService, INavigationInitial
case NavigationResult.AlreadyNavigatedTo:
{
- if (frame!.Content is ScopedPage scopedPage)
+ if (frame is { Content: ScopedPage scopedPage })
{
await scopedPage.NotifyRecipientAsync((INavigationData)data).ConfigureAwait(false);
}
@@ -158,11 +158,9 @@ internal sealed class NavigationService : INavigationService, INavigationInitial
{
taskContext.InvokeOnMainThread(() =>
{
- bool canGoBack = frame?.CanGoBack ?? false;
-
- if (canGoBack)
+ if (frame is { CanGoBack: true })
{
- frame!.GoBack();
+ frame.GoBack();
SyncSelectedNavigationViewItemWith(frame.Content.GetType());
}
});
@@ -234,6 +232,7 @@ internal sealed class NavigationService : INavigationService, INavigationInitial
private void OnPaneStateChanged(NavigationView sender, object args)
{
- LocalSetting.Set(SettingKeys.IsNavPaneOpen, NavigationView!.IsPaneOpen);
+ ArgumentNullException.ThrowIfNull(NavigationView);
+ LocalSetting.Set(SettingKeys.IsNavPaneOpen, NavigationView.IsPaneOpen);
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs
index a504fb62..1f69dae3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Notification/InfoBarService.cs
@@ -125,7 +125,8 @@ internal sealed class InfoBarService : IInfoBarService
};
infoBar.Closed += infobarClosedEventHandler;
- collection!.Add(infoBar);
+ ArgumentNullException.ThrowIfNull(collection);
+ collection.Add(infoBar);
if (delay > 0)
{
@@ -137,7 +138,8 @@ internal sealed class InfoBarService : IInfoBarService
private void OnInfoBarClosed(InfoBar sender, InfoBarClosedEventArgs args)
{
- taskContext.InvokeOnMainThread(() => collection!.Remove(sender));
+ ArgumentNullException.ThrowIfNull(collection);
+ taskContext.InvokeOnMainThread(() => collection.Remove(sender));
sender.Closed -= infobarClosedEventHandler;
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordDbService.cs
index 64880fc8..7e5833e2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordDbService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordDbService.cs
@@ -10,7 +10,7 @@ using System.Collections.ObjectModel;
namespace Snap.Hutao.Service.SpiralAbyss;
[ConstructorGenerated]
-[Injection(InjectAs.Scoped, typeof(ISpiralAbyssRecordDbService))]
+[Injection(InjectAs.Singleton, typeof(ISpiralAbyssRecordDbService))]
internal sealed partial class SpiralAbyssRecordDbService : ISpiralAbyssRecordDbService
{
private readonly IServiceProvider serviceProvider;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordService.cs b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordService.cs
index 4015061d..ba71507f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/SpiralAbyss/SpiralAbyssRecordService.cs
@@ -18,8 +18,8 @@ namespace Snap.Hutao.Service.SpiralAbyss;
[Injection(InjectAs.Scoped, typeof(ISpiralAbyssRecordService))]
internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordService
{
- private readonly IServiceProvider serviceProvider;
private readonly ITaskContext taskContext;
+ private readonly IOverseaSupportFactory gameRecordClientFactory;
private readonly ISpiralAbyssRecordDbService spiralAbyssRecordDbService;
private string? uid;
@@ -50,8 +50,7 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi
private async ValueTask RefreshSpiralAbyssCoreAsync(UserAndUid userAndUid, SpiralAbyssSchedule schedule)
{
- Response response = await serviceProvider
- .GetRequiredService>()
+ Response response = await gameRecordClientFactory
.Create(userAndUid.User.IsOversea)
.GetSpiralAbyssAsync(userAndUid, schedule)
.ConfigureAwait(false);
@@ -60,7 +59,8 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi
{
Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss webSpiralAbyss = response.Data;
- if (spiralAbysses!.SingleOrDefault(s => s.ScheduleId == webSpiralAbyss.ScheduleId) is { } existEntry)
+ ArgumentNullException.ThrowIfNull(spiralAbysses);
+ if (spiralAbysses.SingleOrDefault(s => s.ScheduleId == webSpiralAbyss.ScheduleId) is { } existEntry)
{
await taskContext.SwitchToMainThreadAsync();
existEntry.UpdateSpiralAbyss(webSpiralAbyss);
@@ -73,7 +73,7 @@ internal sealed partial class SpiralAbyssRecordService : ISpiralAbyssRecordServi
SpiralAbyssEntry newEntry = SpiralAbyssEntry.From(userAndUid.Uid.Value, webSpiralAbyss);
await taskContext.SwitchToMainThreadAsync();
- spiralAbysses!.Insert(0, newEntry);
+ spiralAbysses.Insert(0, newEntry);
await taskContext.SwitchToBackgroundAsync();
await spiralAbyssRecordDbService.AddSpiralAbyssEntryAsync(newEntry).ConfigureAwait(false);
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
index 4c880bad..d2b81738 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
@@ -47,7 +47,8 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe
{
// Sync cache
await taskContext.SwitchToMainThreadAsync();
- userCollection!.Remove(user);
+ ArgumentNullException.ThrowIfNull(userCollection);
+ userCollection.Remove(user);
userAndUidCollection?.RemoveWhere(r => r.User.Mid == user.Entity.Mid);
// Sync database
@@ -132,7 +133,8 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe
}
// 检查 mid 对应用户是否存在
- if (TryGetUser(userCollection!, mid, out BindingUser? user))
+ ArgumentNullException.ThrowIfNull(userCollection);
+ if (TryGetUser(userCollection, mid, out BindingUser? user))
{
if (cookie.TryGetSToken(isOversea, out Cookie? stoken))
{
@@ -172,7 +174,7 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe
user.CookieToken ??= new();
// Sync ui and database
- user.CookieToken[Cookie.COOKIE_TOKEN] = cookieToken!;
+ user.CookieToken[Cookie.COOKIE_TOKEN] = cookieToken;
await userDbService.UpdateUserAsync(user.Entity).ConfigureAwait(false);
return true;
@@ -216,7 +218,8 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe
// Sync database
await taskContext.SwitchToBackgroundAsync();
await userDbService.AddUserAsync(newUser.Entity).ConfigureAwait(false);
- return new(UserOptionResult.Added, newUser.UserInfo!.Uid);
+ ArgumentNullException.ThrowIfNull(newUser.UserInfo);
+ return new(UserOptionResult.Added, newUser.UserInfo.Uid);
}
else
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
index 2dea564b..c644ca09 100644
--- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
+++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
@@ -288,7 +288,6 @@
-
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/BottomTextSmallControl.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Control/BottomTextSmallControl.xaml.cs
index 8e324dc8..2d2e8718 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Control/BottomTextSmallControl.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Control/BottomTextSmallControl.xaml.cs
@@ -16,6 +16,6 @@ internal sealed partial class BottomTextSmallControl : UserControl
{
public BottomTextSmallControl()
{
- this.InitializeComponent();
+ InitializeComponent();
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml.cs
index 91a12546..55647e7c 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml.cs
@@ -24,6 +24,7 @@ internal sealed partial class CultivatePromotionDeltaDialog : ContentDialog
///
/// 服务提供器
/// 选项
+ [SuppressMessage("", "SH002")]
public CultivatePromotionDeltaDialog(IServiceProvider serviceProvider, CalculableOptions options)
{
InitializeComponent();
@@ -40,37 +41,35 @@ internal sealed partial class CultivatePromotionDeltaDialog : ContentDialog
/// 异步获取提升差异
///
/// 提升差异
- public async Task> GetPromotionDeltaAsync()
+ public async ValueTask> GetPromotionDeltaAsync()
{
await taskContext.SwitchToMainThreadAsync();
ContentDialogResult result = await ShowAsync();
- if (result == ContentDialogResult.Primary)
- {
- AvatarPromotionDelta delta = new()
- {
- AvatarId = Avatar?.AvatarId ?? 0,
- AvatarLevelCurrent = Avatar?.LevelCurrent ?? 0,
- AvatarLevelTarget = Avatar?.LevelTarget ?? 0,
- SkillList = Avatar?.Skills.SelectList(s => new PromotionDelta()
- {
- Id = s.GroupId,
- LevelCurrent = s.LevelCurrent,
- LevelTarget = s.LevelTarget,
- }),
- Weapon = Weapon is null ? null : new PromotionDelta()
- {
- Id = Weapon.WeaponId,
- LevelCurrent = Weapon.LevelCurrent,
- LevelTarget = Weapon.LevelTarget,
- },
- };
-
- return new(true, delta);
- }
- else
+ if (result != ContentDialogResult.Primary)
{
return new(false, default!);
}
+
+ AvatarPromotionDelta delta = new()
+ {
+ AvatarId = Avatar?.AvatarId ?? 0,
+ AvatarLevelCurrent = Avatar?.LevelCurrent ?? 0,
+ AvatarLevelTarget = Avatar?.LevelTarget ?? 0,
+ SkillList = Avatar?.Skills.SelectList(s => new PromotionDelta()
+ {
+ Id = s.GroupId,
+ LevelCurrent = s.LevelCurrent,
+ LevelTarget = s.LevelTarget,
+ }),
+ Weapon = Weapon is null ? null : new PromotionDelta()
+ {
+ Id = Weapon.WeaponId,
+ LevelCurrent = Weapon.LevelCurrent,
+ LevelTarget = Weapon.LevelTarget,
+ },
+ };
+
+ return new(true, delta);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogImportDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogImportDialog.xaml.cs
index f6a6f619..ca82e98e 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogImportDialog.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogImportDialog.xaml.cs
@@ -35,7 +35,7 @@ internal sealed partial class GachaLogImportDialog : ContentDialog
/// 异步获取导入选项
///
/// 是否导入
- public async Task GetShouldImportAsync()
+ public async ValueTask GetShouldImportAsync()
{
await taskContext.SwitchToMainThreadAsync();
return await ShowAsync() == ContentDialogResult.Primary;
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogRefreshProgressDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogRefreshProgressDialog.xaml.cs
index 5117b29e..e5ff858b 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogRefreshProgressDialog.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogRefreshProgressDialog.xaml.cs
@@ -38,7 +38,7 @@ internal sealed partial class GachaLogRefreshProgressDialog : ContentDialog
// TODO: test new binding approach
GachaItemsPresenter.Header = state.AuthKeyTimeout
? SH.ViewDialogGachaLogRefreshProgressAuthkeyTimeout
- : string.Format(SH.ViewDialogGachaLogRefreshProgressDescription, state.ConfigType.GetLocalizedDescription());
+ : SH.ViewDialogGachaLogRefreshProgressDescription.Format(state.ConfigType.GetLocalizedDescription());
// Binding not working here.
GachaItemsPresenter.Items.Clear();
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogUrlDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogUrlDialog.xaml.cs
index 9036c10f..d1bdad21 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogUrlDialog.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogUrlDialog.xaml.cs
@@ -29,7 +29,7 @@ internal sealed partial class GachaLogUrlDialog : ContentDialog
/// 获取输入的Url
///
/// 输入的结果
- public async Task> GetInputUrlAsync()
+ public async ValueTask> GetInputUrlAsync()
{
await taskContext.SwitchToMainThreadAsync();
ContentDialogResult result = await ShowAsync();
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/LaunchGameAccountNameDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/LaunchGameAccountNameDialog.xaml.cs
index feb12843..a11b04a0 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/LaunchGameAccountNameDialog.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/LaunchGameAccountNameDialog.xaml.cs
@@ -29,7 +29,7 @@ internal sealed partial class LaunchGameAccountNameDialog : ContentDialog
/// 获取输入的Cookie
///
/// 输入的结果
- public async Task> GetInputNameAsync()
+ public async ValueTask> GetInputNameAsync()
{
await taskContext.SwitchToMainThreadAsync();
ContentDialogResult result = await ShowAsync();
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/SignInWebViewDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/SignInWebViewDialog.xaml.cs
index 40611b1b..5468b5aa 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/SignInWebViewDialog.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/SignInWebViewDialog.xaml.cs
@@ -36,7 +36,7 @@ internal sealed partial class SignInWebViewDialog : ContentDialog
InitializeAsync().SafeForget();
}
- private async Task InitializeAsync()
+ private async ValueTask InitializeAsync()
{
await WebView.EnsureCoreWebView2Async();
CoreWebView2 coreWebView2 = WebView.CoreWebView2;
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/UserDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/UserDialog.xaml.cs
index 093e6811..5bf0cbdd 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/UserDialog.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/UserDialog.xaml.cs
@@ -29,7 +29,7 @@ internal sealed partial class UserDialog : ContentDialog
/// 获取输入的Cookie
///
/// 输入的结果
- public async Task> GetInputCookieAsync()
+ public async ValueTask> GetInputCookieAsync()
{
await taskContext.SwitchToMainThreadAsync();
ContentDialogResult result = await ShowAsync();
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/LoginHoyoverseUserPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/LoginHoyoverseUserPage.xaml.cs
index f660a987..51bd1d4a 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/LoginHoyoverseUserPage.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/LoginHoyoverseUserPage.xaml.cs
@@ -28,7 +28,7 @@ internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Control
InitializeComponent();
}
- private static async Task GetUidFromCookieAsync(IServiceProvider serviceProvider, Cookie cookie, CancellationToken token = default)
+ private static async ValueTask GetUidFromCookieAsync(IServiceProvider serviceProvider, Cookie cookie, CancellationToken token = default)
{
JsonSerializerOptions options = serviceProvider.GetRequiredService();
ILogger logger = serviceProvider.GetRequiredService>();
@@ -40,9 +40,9 @@ internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Control
.TryCatchGetFromJsonAsync>(ApiOsEndpoints.WebApiOsAccountLoginByCookie, options, logger, token)
.ConfigureAwait(false);
- if (resp != null)
+ if (resp is not null)
{
- return resp.Data.AccountInfo.AccountId.ToString();
+ return $"{resp.Data.AccountInfo.AccountId}";
}
return string.Empty;
diff --git a/src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml.cs
index ab24d69f..28118bba 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml.cs
@@ -36,7 +36,7 @@ internal sealed partial class TitleView : UserControl
#else
SH.AppNameAndVersion;
#endif
- return string.Format(format, hutaoOptions.Version);
+ return format.Format(hutaoOptions.Version);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementImporter.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementImporter.cs
index dbf77df0..b91fb822 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementImporter.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementImporter.cs
@@ -23,9 +23,11 @@ namespace Snap.Hutao.ViewModel.Achievement;
[Injection(InjectAs.Scoped)]
internal sealed partial class AchievementImporter
{
+ private readonly IContentDialogFactory contentDialogFactory;
private readonly IAchievementService achievementService;
- private readonly IServiceProvider serviceProvider;
+ private readonly IClipboardInterop clipboardInterop;
private readonly IInfoBarService infoBarService;
+ private readonly IPickerFactory pickerFactory;
private readonly JsonSerializerOptions options;
private readonly ITaskContext taskContext;
@@ -62,8 +64,7 @@ internal sealed partial class AchievementImporter
{
if (achievementService.CurrentArchive is { } archive)
{
- ValueResult pickerResult = await serviceProvider
- .GetRequiredService()
+ ValueResult pickerResult = await pickerFactory
.GetFileOpenPicker(PickerLocationId.Desktop, SH.FilePickerImportCommit, ".json")
.TryPickSingleFileAsync()
.ConfigureAwait(false);
@@ -94,10 +95,7 @@ internal sealed partial class AchievementImporter
{
try
{
- return await serviceProvider
- .GetRequiredService()
- .DeserializeFromJsonAsync()
- .ConfigureAwait(false);
+ return await clipboardInterop.DeserializeFromJsonAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -110,16 +108,13 @@ internal sealed partial class AchievementImporter
{
if (uiaf.IsCurrentVersionSupported())
{
- // ContentDialog must be created by main thread.
- await taskContext.SwitchToMainThreadAsync();
- AchievementImportDialog importDialog = serviceProvider.CreateInstance(uiaf);
+ AchievementImportDialog importDialog = await contentDialogFactory.CreateInstanceAsync(uiaf).ConfigureAwait(false);
(bool isOk, ImportStrategy strategy) = await importDialog.GetImportStrategyAsync().ConfigureAwait(false);
if (isOk)
{
await taskContext.SwitchToMainThreadAsync();
- ContentDialog dialog = await serviceProvider
- .GetRequiredService()
+ ContentDialog dialog = await contentDialogFactory
.CreateForIndeterminateProgressAsync(SH.ViewModelAchievementImportProgress)
.ConfigureAwait(false);
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs
index 115511ef..76ef8dea 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Achievement/AchievementViewModel.cs
@@ -28,13 +28,13 @@ namespace Snap.Hutao.ViewModel.Achievement;
[Injection(InjectAs.Scoped)]
internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INavigationRecipient
{
- private static readonly SortDescription UncompletedItemsFirstSortDescription = new(nameof(AchievementView.IsChecked), SortDirection.Ascending);
- private static readonly SortDescription CompletionTimeSortDescription = new(nameof(AchievementView.Time), SortDirection.Descending);
+ private readonly SortDescription uncompletedItemsFirstSortDescription = new(nameof(AchievementView.IsChecked), SortDirection.Ascending);
+ private readonly SortDescription completionTimeSortDescription = new(nameof(AchievementView.Time), SortDirection.Descending);
private readonly IContentDialogFactory contentDialogFactory;
+ private readonly IPickerFactory pickerFactory;
private readonly AchievementImporter achievementImporter;
private readonly IAchievementService achievementService;
- private readonly IServiceProvider serviceProvider;
private readonly IMetadataService metadataService;
private readonly IInfoBarService infoBarService;
private readonly JsonSerializerOptions options;
@@ -189,9 +189,7 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
{
if (Archives is null)
{
- // ContentDialog must be created by main thread.
- await taskContext.SwitchToMainThreadAsync();
- AchievementArchiveCreateDialog dialog = serviceProvider.CreateInstance();
+ AchievementArchiveCreateDialog dialog = await contentDialogFactory.CreateInstanceAsync().ConfigureAwait(false);
(bool isOk, string name) = await dialog.GetInputAsync().ConfigureAwait(false);
if (isOk)
@@ -203,13 +201,13 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
case ArchiveAddResult.Added:
await taskContext.SwitchToMainThreadAsync();
SelectedArchive = achievementService.CurrentArchive;
- infoBarService.Success(string.Format(SH.ViewModelAchievementArchiveAdded, name));
+ infoBarService.Success(SH.ViewModelAchievementArchiveAdded.Format(name));
break;
case ArchiveAddResult.InvalidName:
infoBarService.Warning(SH.ViewModelAchievementArchiveInvalidName);
break;
case ArchiveAddResult.AlreadyExists:
- infoBarService.Warning(string.Format(SH.ViewModelAchievementArchiveAlreadyExists, name));
+ infoBarService.Warning(SH.ViewModelAchievementArchiveAlreadyExists.Format(name));
break;
default:
throw Must.NeverHappen();
@@ -223,7 +221,7 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
{
if (Archives is not null && SelectedArchive is not null)
{
- string title = string.Format(SH.ViewModelAchievementRemoveArchiveTitle, SelectedArchive.Name);
+ string title = SH.ViewModelAchievementRemoveArchiveTitle.Format(SelectedArchive.Name);
string content = SH.ViewModelAchievementRemoveArchiveContent;
ContentDialogResult result = await contentDialogFactory
.CreateForConfirmCancelAsync(title, content)
@@ -260,8 +258,7 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
[SH.ViewModelAchievementExportFileType] = ".json".Enumerate().ToList(),
};
- FileSavePicker picker = serviceProvider
- .GetRequiredService()
+ FileSavePicker picker = pickerFactory
.GetFileSavePicker(PickerLocationId.Desktop, fileName, SH.FilePickerExportCommit, fileTypes);
(bool isPickerOk, ValueFile file) = await picker.TryPickSaveFileAsync().ConfigureAwait(false);
@@ -343,8 +340,8 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
{
if (IsUncompletedItemsFirst)
{
- Achievements.SortDescriptions.Add(UncompletedItemsFirstSortDescription);
- Achievements.SortDescriptions.Add(CompletionTimeSortDescription);
+ Achievements.SortDescriptions.Add(uncompletedItemsFirstSortDescription);
+ Achievements.SortDescriptions.Add(completionTimeSortDescription);
}
else
{
@@ -387,7 +384,8 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
Achievements.Filter = obj =>
{
AchievementView view = (AchievementView)obj;
- return view.Inner.Title.Contains(search) || view.Inner.Description.Contains(search);
+ return view.Inner.Title.Contains(search, StringComparison.CurrentCultureIgnoreCase)
+ || view.Inner.Description.Contains(search, StringComparison.CurrentCultureIgnoreCase);
};
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarProperty.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarProperty.cs
index 57744a0d..83370f98 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarProperty.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarProperty.cs
@@ -46,7 +46,7 @@ internal sealed class AvatarProperty : INameIcon
{
Name = name;
Value = value;
- Icon = PropertyIcons.GetValueOrDefault(property)!;
+ Icon = PropertyIcons.GetValueOrDefault(property);
AddValue = addValue;
}
@@ -58,6 +58,7 @@ internal sealed class AvatarProperty : INameIcon
///
/// 图标
///
+ [AllowNull]
public Uri Icon { get; }
///
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs
index e7f8d63a..135c2162 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarPropertyViewModel.cs
@@ -38,10 +38,16 @@ namespace Snap.Hutao.ViewModel.AvatarProperty;
[Injection(InjectAs.Scoped)]
internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, IRecipient
{
- private readonly IServiceProvider serviceProvider;
+ private readonly IContentDialogFactory contentDialogFactory;
+ private readonly IAppResourceProvider appResourceProvider;
+ private readonly ICultivationService cultivationService;
+ private readonly IAvatarInfoService avatarInfoService;
+ private readonly IClipboardInterop clipboardInterop;
+ private readonly CalculatorClient calculatorClient;
private readonly ITaskContext taskContext;
private readonly IUserService userService;
private readonly IInfoBarService infoBarService;
+
private Summary? summary;
private AvatarView? selectedAvatar;
@@ -109,15 +115,13 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
ValueResult summaryResult;
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
- ContentDialog dialog = await serviceProvider
- .GetRequiredService()
+ ContentDialog dialog = await contentDialogFactory
.CreateForIndeterminateProgressAsync(SH.ViewModelAvatarPropertyFetch)
.ConfigureAwait(false);
using (await dialog.BlockAsync(taskContext).ConfigureAwait(false))
{
- summaryResult = await serviceProvider
- .GetRequiredService()
+ summaryResult = await avatarInfoService
.GetSummaryAsync(userAndUid, option, token)
.ConfigureAwait(false);
}
@@ -139,7 +143,8 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
break;
case RefreshResult.StatusCodeNotSucceed:
- infoBarService.Warning(summary!.Message);
+ ArgumentNullException.ThrowIfNull(summary);
+ infoBarService.Warning(summary.Message);
break;
case RefreshResult.ShowcaseNotOpen:
@@ -167,49 +172,50 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
}
// ContentDialog must be created by main thread.
- await taskContext.SwitchToMainThreadAsync();
CalculableOptions options = new(avatar.ToCalculable(), avatar.Weapon.ToCalculable());
- CultivatePromotionDeltaDialog dialog = serviceProvider.CreateInstance(options);
+ CultivatePromotionDeltaDialog dialog = await contentDialogFactory.CreateInstanceAsync(options).ConfigureAwait(false);
(bool isOk, CalculatorAvatarPromotionDelta delta) = await dialog.GetPromotionDeltaAsync().ConfigureAwait(false);
- if (isOk)
+ if (!isOk)
{
- Response consumptionResponse = await serviceProvider
- .GetRequiredService()
- .ComputeAsync(userService.Current.Entity, delta)
+ return;
+ }
+
+ Response consumptionResponse = await calculatorClient
+ .ComputeAsync(userService.Current.Entity, delta)
+ .ConfigureAwait(false);
+
+ if (!consumptionResponse.IsOk())
+ {
+ return;
+ }
+
+ CalculatorConsumption consumption = consumptionResponse.Data;
+
+ List items = CalculatorItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume);
+ bool avatarSaved = await cultivationService
+ .SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items)
+ .ConfigureAwait(false);
+
+ try
+ {
+ // take a hot path if avatar is not saved.
+ bool avatarAndWeaponSaved = avatarSaved && await cultivationService
+ .SaveConsumptionAsync(CultivateType.Weapon, avatar.Weapon.Id, consumption.WeaponConsume.EmptyIfNull())
.ConfigureAwait(false);
- if (consumptionResponse.IsOk())
+ if (avatarAndWeaponSaved)
{
- ICultivationService cultivationService = serviceProvider.GetRequiredService();
- CalculatorConsumption consumption = consumptionResponse.Data;
-
- List items = CalculatorItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume);
- bool avatarSaved = await cultivationService
- .SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items)
- .ConfigureAwait(false);
-
- try
- {
- // take a hot path if avatar is not saved.
- bool avatarAndWeaponSaved = avatarSaved && await cultivationService
- .SaveConsumptionAsync(CultivateType.Weapon, avatar.Weapon.Id, consumption.WeaponConsume.EmptyIfNull())
- .ConfigureAwait(false);
-
- if (avatarAndWeaponSaved)
- {
- infoBarService.Success(SH.ViewModelCultivationEntryAddSuccess);
- }
- else
- {
- infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning);
- }
- }
- catch (Core.ExceptionService.UserdataCorruptedException ex)
- {
- infoBarService.Error(ex, SH.ViewModelCultivationAddWarning);
- }
+ infoBarService.Success(SH.ViewModelCultivationEntryAddSuccess);
}
+ else
+ {
+ infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning);
+ }
+ }
+ catch (Core.ExceptionService.UserdataCorruptedException ex)
+ {
+ infoBarService.Error(ex, SH.ViewModelCultivationAddWarning);
}
}
else
@@ -231,7 +237,7 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
bool clipboardOpened = false;
using (SoftwareBitmap softwareBitmap = SoftwareBitmap.CreateCopyFromBuffer(buffer, BitmapPixelFormat.Bgra8, bitmap.PixelWidth, bitmap.PixelHeight))
{
- Bgra32 tint = serviceProvider.GetRequiredService().GetResource("CompatBackgroundColor");
+ Bgra32 tint = appResourceProvider.GetResource("CompatBackgroundColor");
softwareBitmap.NormalBlend(tint);
using (InMemoryRandomAccessStream memory = new())
{
@@ -239,7 +245,7 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
encoder.SetSoftwareBitmap(softwareBitmap);
await encoder.FlushAsync();
- clipboardOpened = serviceProvider.GetRequiredService().SetBitmap(memory);
+ clipboardOpened = clipboardInterop.SetBitmap(memory);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/WeaponView.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/WeaponView.cs
index 50c3ebf3..40d9f156 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/WeaponView.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/WeaponView.cs
@@ -25,7 +25,7 @@ internal sealed class WeaponView : Equip, ICalculableSource
///
/// 精炼属性
///
- public string AffixLevel { get => string.Format(SH.ModelBindingAvatarPropertyWeaponAffixFormat, AffixLevelNumber); }
+ public string AffixLevel { get => SH.ModelBindingAvatarPropertyWeaponAffixFormat.Format(AffixLevelNumber); }
///
/// 精炼名称
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/Team.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/Team.cs
index e3b7a5e2..457cf592 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/Team.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/Team.cs
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Primitives;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Web.Hutao.Model;
+using System.Globalization;
namespace Snap.Hutao.ViewModel.Complex;
@@ -25,11 +26,11 @@ internal sealed class Team : List
// TODO use Collection Literials
foreach (StringSegment item in new StringTokenizer(team.Item, new char[] { ',' }))
{
- uint id = uint.Parse(item.AsSpan());
+ uint id = uint.Parse(item.AsSpan(), CultureInfo.InvariantCulture);
Add(new(idAvatarMap[id]));
}
- Rate = string.Format(SH.ModelBindingHutaoTeamUpCountFormat, team.Rate);
+ Rate = SH.ModelBindingHutaoTeamUpCountFormat.Format(team.Rate);
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/TeamAppearanceView.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/TeamAppearanceView.cs
index d2f1acd9..be1eb0b8 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/TeamAppearanceView.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Complex/TeamAppearanceView.cs
@@ -20,7 +20,7 @@ internal sealed class TeamAppearanceView
/// 映射
public TeamAppearanceView(TeamAppearance teamRank, Dictionary idAvatarMap)
{
- Floor = string.Format(SH.ModelBindingHutaoComplexRankFloor, teamRank.Floor);
+ Floor = SH.ModelBindingHutaoComplexRankFloor.Format(teamRank.Floor);
Up = teamRank.Up.SelectList(teamRate => new Team(teamRate, idAvatarMap));
Down = teamRank.Down.SelectList(teamRate => new Team(teamRate, idAvatarMap));
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs
index 3e172735..b75e0305 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Cultivation/CultivationViewModel.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Metadata.Item;
using Snap.Hutao.Model.Primitive;
@@ -23,10 +24,12 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
{
private readonly ConcurrentCancellationTokenSource statisticsCancellationTokenSource = new();
+ private readonly IContentDialogFactory contentDialogFactory;
private readonly ICultivationService cultivationService;
private readonly ILogger logger;
- private readonly IServiceProvider serviceProvider;
+ private readonly INavigationService navigationService;
private readonly IMetadataService metadataService;
+ private readonly IInfoBarService infoBarService;
private readonly ITaskContext taskContext;
private ObservableCollection? projects;
@@ -89,15 +92,12 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
[Command("AddProjectCommand")]
private async Task AddProjectAsync()
{
- // ContentDialog must be created by main thread.
- await taskContext.SwitchToMainThreadAsync();
- CultivateProjectDialog dialog = serviceProvider.CreateInstance();
+ CultivateProjectDialog dialog = await contentDialogFactory.CreateInstanceAsync().ConfigureAwait(false);
(bool isOk, CultivateProject project) = await dialog.CreateProjectAsync().ConfigureAwait(false);
if (isOk)
{
ProjectAddResult result = await cultivationService.TryAddProjectAsync(project).ConfigureAwait(false);
- IInfoBarService infoBarService = serviceProvider.GetRequiredService();
switch (result)
{
@@ -214,10 +214,7 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
{
Type? pageType = Type.GetType(typeString);
ArgumentNullException.ThrowIfNull(pageType);
-
- serviceProvider
- .GetRequiredService()
- .Navigate(pageType, INavigationAwaiter.Default, true);
+ navigationService.Navigate(pageType, INavigationAwaiter.Default, true);
}
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNote/DailyNoteViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNote/DailyNoteViewModel.cs
index 33d4233f..7e9f37c5 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNote/DailyNoteViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNote/DailyNoteViewModel.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service.DailyNote;
using Snap.Hutao.Service.Notification;
@@ -20,8 +21,8 @@ namespace Snap.Hutao.ViewModel.DailyNote;
[Injection(InjectAs.Scoped)]
internal sealed partial class DailyNoteViewModel : Abstraction.ViewModel
{
+ private readonly IContentDialogFactory contentDialogFactory;
private readonly IDailyNoteService dailyNoteService;
- private readonly IServiceProvider serviceProvider;
private readonly IInfoBarService infoBarService;
private readonly DailyNoteOptions options;
private readonly ITaskContext taskContext;
@@ -113,7 +114,8 @@ internal sealed partial class DailyNoteViewModel : Abstraction.ViewModel
{
// ContentDialog must be created by main thread.
await taskContext.SwitchToMainThreadAsync();
- await serviceProvider.CreateInstance(entry).ShowAsync();
+ DailyNoteNotificationDialog dialog = await contentDialogFactory.CreateInstanceAsync(entry).ConfigureAwait(true);
+ await dialog.ShowAsync();
await taskContext.SwitchToBackgroundAsync();
await dailyNoteService.UpdateDailyNoteAsync(entry).ConfigureAwait(false);
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs
index a4a680d9..ceb29fbe 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs
@@ -26,10 +26,10 @@ namespace Snap.Hutao.ViewModel.GachaLog;
[Injection(InjectAs.Scoped)]
internal sealed partial class GachaLogViewModel : Abstraction.ViewModel
{
+ private readonly IGachaLogQueryProviderFactory gachaLogQueryProviderFactory;
private readonly IContentDialogFactory contentDialogFactory;
private readonly HutaoCloudStatisticsViewModel hutaoCloudStatisticsViewModel;
private readonly HutaoCloudViewModel hutaoCloudViewModel;
- private readonly IServiceProvider serviceProvider;
private readonly IGachaLogService gachaLogService;
private readonly IInfoBarService infoBarService;
private readonly JsonSerializerOptions options;
@@ -150,7 +150,7 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel
private async ValueTask RefreshInternalAsync(RefreshOption option)
{
- IGachaLogQueryProvider provider = serviceProvider.GetRequiredService().Create(option);
+ IGachaLogQueryProvider provider = gachaLogQueryProviderFactory.Create(option);
(bool isOk, GachaLogQuery query) = await provider.GetQueryAsync().ConfigureAwait(false);
@@ -158,10 +158,7 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel
{
RefreshStrategy strategy = IsAggressiveRefresh ? RefreshStrategy.AggressiveMerge : RefreshStrategy.LazyMerge;
- // ContentDialog must be created by main thread.
- await taskContext.SwitchToMainThreadAsync();
-
- GachaLogRefreshProgressDialog dialog = serviceProvider.CreateInstance();
+ GachaLogRefreshProgressDialog dialog = await contentDialogFactory.CreateInstanceAsync().ConfigureAwait(false);
ContentDialogHideToken hideToken = await dialog.BlockAsync(taskContext).ConfigureAwait(false);
Progress progress = new(dialog.OnReport);
bool authkeyValid;
@@ -268,7 +265,7 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel
if (Archives is not null && SelectedArchive is not null)
{
ContentDialogResult result = await contentDialogFactory
- .CreateForConfirmCancelAsync(string.Format(SH.ViewModelGachaLogRemoveArchiveTitle, SelectedArchive.Uid), SH.ViewModelGachaLogRemoveArchiveDescription)
+ .CreateForConfirmCancelAsync(SH.ViewModelGachaLogRemoveArchiveTitle.Format(SelectedArchive.Uid), SH.ViewModelGachaLogRemoveArchiveDescription)
.ConfigureAwait(false);
if (result == ContentDialogResult.Primary)
@@ -356,9 +353,7 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel
{
if (uigf.IsCurrentVersionSupported(out UIGFVersion version))
{
- // ContentDialog must be created by main thread.
- await taskContext.SwitchToMainThreadAsync();
- GachaLogImportDialog importDialog = serviceProvider.CreateInstance(uigf);
+ GachaLogImportDialog importDialog = await contentDialogFactory.CreateInstanceAsync(uigf).ConfigureAwait(false);
if (await importDialog.GetShouldImportAsync().ConfigureAwait(false))
{
if (CanImport(version, uigf))
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs
index 2d06ffb3..e9eecf5d 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs
@@ -22,10 +22,10 @@ namespace Snap.Hutao.ViewModel.GachaLog;
[Injection(InjectAs.Scoped)]
internal sealed partial class HutaoCloudViewModel : Abstraction.ViewModel
{
+ private readonly INavigationService navigationService;
private readonly IContentDialogFactory contentDialogFactory;
private readonly IGachaLogHutaoCloudService hutaoCloudService;
private readonly IHutaoUserService hutaoUserService;
- private readonly IServiceProvider serviceProvider;
private readonly IInfoBarService infoBarService;
private readonly ITaskContext taskContext;
private readonly HutaoUserOptions options;
@@ -131,9 +131,7 @@ internal sealed partial class HutaoCloudViewModel : Abstraction.ViewModel
[Command("NavigateToSpiralAbyssRecordCommand")]
private void NavigateToSpiralAbyssRecord()
{
- serviceProvider
- .GetRequiredService()
- .Navigate(INavigationAwaiter.Default);
+ navigationService.Navigate(INavigationAwaiter.Default);
}
private async ValueTask RefreshUidCollectionAsync()
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/TypedWishSummary.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/TypedWishSummary.cs
index ed0c1381..4cdd8616 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/TypedWishSummary.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/TypedWishSummary.cs
@@ -23,7 +23,7 @@ internal sealed partial class TypedWishSummary : Wish
///
public string MaxOrangePullFormatted
{
- get => string.Format(SH.ModelBindingGachaTypedWishSummaryMaxOrangePullFormat, MaxOrangePull);
+ get => SH.ModelBindingGachaTypedWishSummaryMaxOrangePullFormat.Format(MaxOrangePull);
}
///
@@ -31,7 +31,7 @@ internal sealed partial class TypedWishSummary : Wish
///
public string MinOrangePullFormatted
{
- get => string.Format(SH.ModelBindingGachaTypedWishSummaryMinOrangePullFormat, MinOrangePull);
+ get => SH.ModelBindingGachaTypedWishSummaryMinOrangePullFormat.Format(MinOrangePull);
}
///
@@ -83,7 +83,7 @@ internal sealed partial class TypedWishSummary : Wish
///
public string AverageOrangePullFormatted
{
- get => string.Format(SH.ModelBindingGachaTypedWishSummaryAveragePullFormat, AverageOrangePull);
+ get => SH.ModelBindingGachaTypedWishSummaryAveragePullFormat.Format(AverageOrangePull);
}
///
@@ -96,7 +96,7 @@ internal sealed partial class TypedWishSummary : Wish
///
public string AverageUpOrangePullFormatted
{
- get => string.Format(SH.ModelBindingGachaTypedWishSummaryAveragePullFormat, AverageUpOrangePull);
+ get => SH.ModelBindingGachaTypedWishSummaryAveragePullFormat.Format(AverageUpOrangePull);
}
///
@@ -104,7 +104,7 @@ internal sealed partial class TypedWishSummary : Wish
///
public string PredictedPullLeftToOrangeFormatted
{
- get => string.Format(SH.ViewModelGachaLogPredictedPullLeftToOrange, PredictedPullLeftToOrange, ProbabilityOfPredictedPullLeftToOrange);
+ get => SH.ViewModelGachaLogPredictedPullLeftToOrange.Format(PredictedPullLeftToOrange, ProbabilityOfPredictedPullLeftToOrange);
}
///
@@ -112,7 +112,7 @@ internal sealed partial class TypedWishSummary : Wish
///
public string ProbabilityOfNextPullIsOrangeFormatted
{
- get => string.Format(SH.ViewModelGachaLogProbabilityOfNextPullIsOrange, ProbabilityOfNextPullIsOrange);
+ get => SH.ViewModelGachaLogProbabilityOfNextPullIsOrange.Format(ProbabilityOfNextPullIsOrange);
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/Wish.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/Wish.cs
index 47b23e45..efaf1989 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/Wish.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/Wish.cs
@@ -40,7 +40,7 @@ internal abstract class Wish
///
public string TotalCountFormatted
{
- get => string.Format(SH.ModelBindingGachaWishBaseTotalCountFormat, TotalCount);
+ get => SH.ModelBindingGachaWishBaseTotalCountFormat.Format(TotalCount);
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs
index 86b5bfb3..1c8c3792 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Game/LaunchGameViewModel.cs
@@ -6,6 +6,7 @@ using Snap.Hutao.Control.Extension;
using Snap.Hutao.Core;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.LifeCycle;
+using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service;
using Snap.Hutao.Service.Game;
@@ -33,10 +34,12 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
///
public const string DesiredUid = nameof(DesiredUid);
- private readonly IServiceProvider serviceProvider;
+ private readonly IContentDialogFactory contentDialogFactory;
+ private readonly INavigationService navigationService;
private readonly IInfoBarService infoBarService;
private readonly LaunchOptions launchOptions;
private readonly RuntimeOptions hutaoOptions;
+ private readonly ResourceClient resourceClient;
private readonly IUserService userService;
private readonly ITaskContext taskContext;
private readonly IGameService gameService;
@@ -102,8 +105,6 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
protected override async ValueTask InitializeUIAsync()
{
- IInfoBarService infoBarService = serviceProvider.GetRequiredService();
-
if (File.Exists(AppOptions.GamePath))
{
try
@@ -127,7 +128,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
}
else
{
- infoBarService.Warning(string.Format(SH.ViewModelLaunchGameMultiChannelReadFail, options.ConfigFilePath));
+ infoBarService.Warning(SH.ViewModelLaunchGameMultiChannelReadFail.Format(options.ConfigFilePath));
}
ObservableCollection accounts = gameService.GameAccountCollection;
@@ -153,7 +154,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
{
infoBarService.Warning(SH.ViewModelLaunchGamePathInvalid);
await taskContext.SwitchToMainThreadAsync();
- await serviceProvider.GetRequiredService()
+ await navigationService
.NavigateAsync(INavigationAwaiter.Default, true)
.ConfigureAwait(false);
}
@@ -164,8 +165,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
private async ValueTask UpdateGameResourceAsync(LaunchScheme scheme)
{
await taskContext.SwitchToBackgroundAsync();
- Web.Response.Response response = await serviceProvider
- .GetRequiredService()
+ Web.Response.Response response = await resourceClient
.GetResourceAsync(scheme)
.ConfigureAwait(false);
@@ -179,8 +179,6 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
[Command("LaunchCommand", AllowConcurrentExecutions = true)]
private async Task LaunchAsync()
{
- IInfoBarService infoBarService = serviceProvider.GetRequiredService();
-
if (SelectedScheme is not null)
{
try
@@ -188,8 +186,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
if (gameService.SetChannelOptions(SelectedScheme))
{
// Channel changed, we need to change local file.
- await taskContext.SwitchToMainThreadAsync();
- LaunchGamePackageConvertDialog dialog = serviceProvider.CreateInstance();
+ LaunchGamePackageConvertDialog dialog = await contentDialogFactory.CreateInstanceAsync().ConfigureAwait(false);
Progress progress = new(state => dialog.State = state.Clone());
using (await dialog.BlockAsync(taskContext).ConfigureAwait(false))
{
@@ -232,7 +229,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
}
catch (UserdataCorruptedException ex)
{
- serviceProvider.GetRequiredService().Error(ex);
+ infoBarService.Error(ex);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Guide/DownloadSummary.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Guide/DownloadSummary.cs
index 26aad1a1..c94c215a 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Guide/DownloadSummary.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Guide/DownloadSummary.cs
@@ -24,6 +24,7 @@ internal sealed class DownloadSummary : ObservableObject
{
private readonly IServiceProvider serviceProvider;
private readonly ITaskContext taskContext;
+ private readonly IImageCache imageCache;
private readonly HttpClient httpClient;
private readonly string fileName;
private readonly string fileUrl;
@@ -41,6 +42,7 @@ internal sealed class DownloadSummary : ObservableObject
{
taskContext = serviceProvider.GetRequiredService();
httpClient = serviceProvider.GetRequiredService();
+ imageCache = serviceProvider.GetRequiredService();
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService();
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(hutaoOptions.UserAgent);
@@ -72,7 +74,7 @@ internal sealed class DownloadSummary : ObservableObject
/// 异步下载并解压
///
/// 任务
- public async Task DownloadAndExtractAsync()
+ public async ValueTask DownloadAndExtractAsync()
{
ILogger logger = serviceProvider.GetRequiredService>();
try
@@ -116,13 +118,14 @@ internal sealed class DownloadSummary : ObservableObject
private void ExtractFiles(Stream stream)
{
- IImageCacheFilePathOperation imageCache = serviceProvider.GetRequiredService().As()!;
+ IImageCacheFilePathOperation? imageCacheFilePathOperation = imageCache.As();
+ ArgumentNullException.ThrowIfNull(imageCacheFilePathOperation);
using (ZipArchive archive = new(stream))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
- string destPath = imageCache.GetFileFromCategoryAndName(fileName, entry.FullName);
+ string destPath = imageCacheFilePathOperation.GetFileFromCategoryAndName(fileName, entry.FullName);
entry.ExtractToFile(destPath, true);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs
index 3240eb6f..cac37f8c 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Home/AnnouncementViewModel.cs
@@ -77,7 +77,7 @@ internal sealed partial class AnnouncementViewModel : Abstraction.ViewModel
}
else if (rand == 1)
{
- GreetingText = string.Format(SH.ViewPageHomeGreetingTextCommon2, LocalSetting.Get(SettingKeys.LaunchTimes, 0));
+ GreetingText = SH.ViewPageHomeGreetingTextCommon2.Format(LocalSetting.Get(SettingKeys.LaunchTimes, 0));
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/HutaoPassportViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/HutaoPassportViewModel.cs
index 3833ad71..92de3945 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/HutaoPassportViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/HutaoPassportViewModel.cs
@@ -22,7 +22,6 @@ internal sealed partial class HutaoPassportViewModel : Abstraction.ViewModel
private readonly HomaPassportClient homaPassportClient;
private readonly INavigationService navigationService;
private readonly HutaoUserOptions hutaoUserOptions;
- private readonly IServiceProvider serviceProvider;
private readonly IInfoBarService infoBarService;
private readonly ITaskContext taskContext;
@@ -142,7 +141,7 @@ internal sealed partial class HutaoPassportViewModel : Abstraction.ViewModel
}
Response response = await homaPassportClient.VerifyAsync(UserName, isResetPassword).ConfigureAwait(false);
- serviceProvider.GetRequiredService().Information(response.Message);
+ infoBarService.Information(response.Message);
}
private void SaveUserNameAndPassword()
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/BattleView.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/BattleView.cs
index f4dabb70..efb8f465 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/BattleView.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/BattleView.cs
@@ -19,7 +19,7 @@ internal sealed class BattleView
/// Id角色映射
public BattleView(Battle battle, Dictionary idAvatarMap)
{
- Time = DateTimeOffset.FromUnixTimeSeconds(battle.Timestamp).ToLocalTime().ToString("yyyy.MM.dd HH:mm:ss");
+ Time = $"{DateTimeOffset.FromUnixTimeSeconds(battle.Timestamp).ToLocalTime():yyyy.MM.dd HH:mm:ss}";
Avatars = battle.Avatars.SelectList(a => AvatarView.From(idAvatarMap[a.Id]));
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/FloorView.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/FloorView.cs
index 697c4c88..369cc5f1 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/FloorView.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/FloorView.cs
@@ -18,7 +18,7 @@ internal sealed class FloorView
/// Id角色映射
public FloorView(Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.Floor floor, Dictionary idAvatarMap)
{
- Index = string.Format(SH.ModelBindingHutaoComplexRankFloor, floor.Index);
+ Index = SH.ModelBindingHutaoComplexRankFloor.Format(floor.Index);
SettleTime = $"{DateTimeOffset.FromUnixTimeSeconds(floor.SettleTime).ToLocalTime():yyyy.MM.dd HH:mm:ss}";
Star = floor.Star;
Levels = floor.Levels.SelectList(l => new LevelView(l, idAvatarMap));
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/LevelView.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/LevelView.cs
index 1178f7d0..ce475b48 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/LevelView.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/LevelView.cs
@@ -18,7 +18,7 @@ internal sealed class LevelView
/// Id角色映射
public LevelView(Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.Level level, Dictionary idAvatarMap)
{
- Index = string.Format(SH.ModelBindingHutaoComplexRankLevel, level.Index);
+ Index = SH.ModelBindingHutaoComplexRankLevel.Format(level.Index);
Star = level.Star;
Battles = level.Battles.SelectList(b => new BattleView(b, idAvatarMap));
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssRecordViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssRecordViewModel.cs
index c4d267e7..69c3cc1a 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssRecordViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssRecordViewModel.cs
@@ -26,7 +26,7 @@ namespace Snap.Hutao.ViewModel.SpiralAbyss;
internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel, IRecipient
{
private readonly ISpiralAbyssRecordService spiralAbyssRecordService;
- private readonly IServiceProvider serviceProvider;
+ private readonly HomaSpiralAbyssClient spiralAbyssClient;
private readonly IMetadataService metadataService;
private readonly IInfoBarService infoBarService;
private readonly ITaskContext taskContext;
@@ -148,15 +148,12 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel
[Command("UploadSpiralAbyssRecordCommand")]
private async Task UploadSpiralAbyssRecordAsync()
{
- HomaSpiralAbyssClient homaClient = serviceProvider.GetRequiredService();
- IInfoBarService infoBarService = serviceProvider.GetRequiredService();
-
if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
{
- SimpleRecord? record = await homaClient.GetPlayerRecordAsync(userAndUid).ConfigureAwait(false);
+ SimpleRecord? record = await spiralAbyssClient.GetPlayerRecordAsync(userAndUid).ConfigureAwait(false);
if (record is not null)
{
- Web.Response.Response response = await homaClient.UploadRecordAsync(record).ConfigureAwait(false);
+ Web.Response.Response response = await spiralAbyssClient.UploadRecordAsync(record).ConfigureAwait(false);
if (response.IsOk())
{
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserAndUid.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserAndUid.cs
index cef30cd0..2e8190d9 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserAndUid.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserAndUid.cs
@@ -36,6 +36,7 @@ internal sealed class UserAndUid : IMappingFrom
public PlayerUid Uid { get; private set; }
+ [SuppressMessage("", "SH002")]
public static UserAndUid From(EntityUser user, PlayerUid role)
{
return new(user, role);
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs
index 2003ace6..71f81482 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs
@@ -77,7 +77,7 @@ internal sealed partial class UserViewModel : ObservableObject
SelectedUser = Users.Single();
}
- infoBarService.Success(string.Format(SH.ViewModelUserAdded, uid));
+ infoBarService.Success(SH.ViewModelUserAdded.Format(uid));
break;
case UserOptionResult.Incomplete:
infoBarService.Information(SH.ViewModelUserIncomplete);
@@ -86,7 +86,7 @@ internal sealed partial class UserViewModel : ObservableObject
infoBarService.Information(SH.ViewModelUserInvalid);
break;
case UserOptionResult.Updated:
- infoBarService.Success(string.Format(SH.ViewModelUserUpdated, uid));
+ infoBarService.Success(SH.ViewModelUserUpdated.Format(uid));
break;
default:
throw Must.NeverHappen();
@@ -173,7 +173,7 @@ internal sealed partial class UserViewModel : ObservableObject
try
{
await userService.RemoveUserAsync(user).ConfigureAwait(false);
- infoBarService.Success(string.Format(SH.ViewModelUserRemoved, user.UserInfo?.Nickname));
+ infoBarService.Success(SH.ViewModelUserRemoved.Format(user.UserInfo?.Nickname));
}
catch (UserdataCorruptedException ex)
{
@@ -198,7 +198,7 @@ internal sealed partial class UserViewModel : ObservableObject
serviceProvider.GetRequiredService().SetText(cookieString);
ArgumentNullException.ThrowIfNull(user.UserInfo);
- infoBarService.Success(string.Format(SH.ViewModelUserCookieCopied, user.UserInfo.Nickname));
+ infoBarService.Success(SH.ViewModelUserCookieCopied.Format(user.UserInfo.Nickname));
}
catch (Exception ex)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs
index d603d067..808abab5 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using CommunityToolkit.WinUI.UI;
+using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Entity.Primitive;
using Snap.Hutao.Model.Intrinsic;
@@ -34,11 +35,13 @@ namespace Snap.Hutao.ViewModel.Wiki;
[Injection(InjectAs.Scoped)]
internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
{
- private readonly IServiceProvider serviceProvider;
+ private readonly IContentDialogFactory contentDialogFactory;
+ private readonly ICultivationService cultivationService;
private readonly IMetadataService metadataService;
private readonly ITaskContext taskContext;
private readonly IHutaoCache hutaoCache;
private readonly IInfoBarService infoBarService;
+ private readonly CalculateClient calculateClient;
private readonly IUserService userService;
private AdvancedCollectionView? avatars;
@@ -136,7 +139,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
// ContentDialog must be created by main thread.
await taskContext.SwitchToMainThreadAsync();
CalculableOptions options = new(avatar.ToCalculable(), null);
- CultivatePromotionDeltaDialog dialog = serviceProvider.CreateInstance(options);
+ CultivatePromotionDeltaDialog dialog = await contentDialogFactory.CreateInstanceAsync(options).ConfigureAwait(false);
(bool isOk, CalculateAvatarPromotionDelta delta) = await dialog.GetPromotionDeltaAsync().ConfigureAwait(false);
if (!isOk)
@@ -144,8 +147,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
return;
}
- Response consumptionResponse = await serviceProvider
- .GetRequiredService()
+ Response consumptionResponse = await calculateClient
.ComputeAsync(userService.Current.Entity, delta)
.ConfigureAwait(false);
@@ -158,8 +160,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
List items = CalculateItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume);
try
{
- bool saved = await serviceProvider
- .GetRequiredService()
+ bool saved = await cultivationService
.SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items)
.ConfigureAwait(false);
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs
index 32a19dea..105114a1 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using CommunityToolkit.WinUI.UI;
+using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Entity.Primitive;
using Snap.Hutao.Model.Intrinsic;
@@ -30,8 +31,10 @@ namespace Snap.Hutao.ViewModel.Wiki;
[Injection(InjectAs.Scoped)]
internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
{
+ private readonly IContentDialogFactory contentDialogFactory;
+ private readonly CalculateClient calculateClient;
+ private readonly ICultivationService cultivationService;
private readonly ITaskContext taskContext;
- private readonly IServiceProvider serviceProvider;
private readonly IMetadataService metadataService;
private readonly IHutaoCache hutaoCache;
private readonly IInfoBarService infoBarService;
@@ -120,10 +123,8 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
return;
}
- // ContentDialog must be created by main thread.
- await taskContext.SwitchToMainThreadAsync();
CalculableOptions options = new(null, weapon.ToCalculable());
- CultivatePromotionDeltaDialog dialog = serviceProvider.CreateInstance(options);
+ CultivatePromotionDeltaDialog dialog = await contentDialogFactory.CreateInstanceAsync(options).ConfigureAwait(false);
(bool isOk, CalculateAvatarPromotionDelta delta) = await dialog.GetPromotionDeltaAsync().ConfigureAwait(false);
if (!isOk)
@@ -131,8 +132,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
return;
}
- Response consumptionResponse = await serviceProvider
- .GetRequiredService()
+ Response consumptionResponse = await calculateClient
.ComputeAsync(userService.Current.Entity, delta)
.ConfigureAwait(false);
@@ -144,8 +144,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
CalculateConsumption consumption = consumptionResponse.Data;
try
{
- bool saved = await serviceProvider
- .GetRequiredService()
+ bool saved = await cultivationService
.SaveConsumptionAsync(CultivateType.Weapon, weapon.Id, consumption.WeaponConsume.EmptyIfNull())
.ConfigureAwait(false);
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/CoreWebView2Extension.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/CoreWebView2Extension.cs
index 00c32196..2edfda4e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/CoreWebView2Extension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/CoreWebView2Extension.cs
@@ -47,17 +47,17 @@ internal static class CoreWebView2Extension
{
CoreWebView2CookieManager cookieManager = webView.CookieManager;
- if (cookieToken != null)
+ if (cookieToken is not null)
{
cookieManager.AddMihoyoCookie(Cookie.ACCOUNT_ID, cookieToken, isOversea).AddMihoyoCookie(Cookie.COOKIE_TOKEN, cookieToken, isOversea);
}
- if (lToken != null)
+ if (lToken is not null)
{
cookieManager.AddMihoyoCookie(Cookie.LTUID, lToken, isOversea).AddMihoyoCookie(Cookie.LTOKEN, lToken, isOversea);
}
- if (sToken != null)
+ if (sToken is not null)
{
cookieManager.AddMihoyoCookie(Cookie.STUID, sToken, isOversea).AddMihoyoCookie(Cookie.STOKEN, sToken, isOversea);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs
index 44838cf6..78c6e9c7 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs
@@ -20,7 +20,6 @@ namespace Snap.Hutao.Web.Bridge;
///
[HighQuality]
[SuppressMessage("", "CA1001")]
-[SuppressMessage("", "SA1600")]
internal class MiHoYoJSInterface
{
private const string InitializeJsInterfaceScript2 = """
@@ -65,7 +64,7 @@ internal class MiHoYoJSInterface
///
/// 参数
/// 响应
- public virtual async Task GetActionTicketAsync(JsParam jsParam)
+ public virtual async ValueTask GetActionTicketAsync(JsParam jsParam)
{
return await serviceProvider
.GetRequiredService()
@@ -98,14 +97,13 @@ internal class MiHoYoJSInterface
/// 响应
public virtual JsResult> GetCookieInfo(JsParam param)
{
- User user = serviceProvider.GetRequiredService().Current!;
-
+ ArgumentNullException.ThrowIfNull(userAndUid.User.LToken);
return new()
{
Data = new()
{
- [Cookie.LTUID] = user.LToken![Cookie.LTUID],
- [Cookie.LTOKEN] = user.LToken[Cookie.LTOKEN],
+ [Cookie.LTUID] = userAndUid.User.LToken[Cookie.LTUID],
+ [Cookie.LTOKEN] = userAndUid.User.LToken[Cookie.LTOKEN],
[Cookie.LOGIN_TICKET] = string.Empty,
},
};
@@ -116,6 +114,7 @@ internal class MiHoYoJSInterface
///
/// 参数
/// 响应
+ [SuppressMessage("", "CA1308")]
public virtual JsResult> GetDynamicSecrectV1(JsParam param)
{
string salt = HoyolabOptions.Salts[SaltType.LK2];
@@ -146,9 +145,9 @@ internal class MiHoYoJSInterface
///
/// 参数
/// 响应
+ [SuppressMessage("", "CA1308")]
public virtual JsResult> GetDynamicSecrectV2(JsParam param)
{
- // TODO: Salt X4 for hoyolab user
string salt = HoyolabOptions.Salts[SaltType.X4];
long t = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
int r = GetRandom();
@@ -193,7 +192,7 @@ internal class MiHoYoJSInterface
///
/// 参数
/// 响应
- public virtual async Task>> GetCookieTokenAsync(JsParam param)
+ public virtual async ValueTask>> GetCookieTokenAsync(JsParam param)
{
IUserService userService = serviceProvider.GetRequiredService();
User user = userService.Current!;
@@ -212,7 +211,7 @@ internal class MiHoYoJSInterface
///
/// 参数
/// 响应
- public virtual async Task ClosePageAsync(JsParam param)
+ public virtual async ValueTask ClosePageAsync(JsParam param)
{
await taskContext.SwitchToMainThreadAsync();
if (webView.CanGoBack)
@@ -247,7 +246,7 @@ internal class MiHoYoJSInterface
return new() { Data = new() { ["statusBarHeight"] = 0 } };
}
- public virtual async Task PushPageAsync(JsParam param)
+ public virtual async ValueTask PushPageAsync(JsParam param)
{
await taskContext.SwitchToMainThreadAsync();
webView.Navigate(param.Payload.Page);
@@ -267,15 +266,16 @@ internal class MiHoYoJSInterface
{
Data = new()
{
+ // TODO: replace with metadata options value
["language"] = appOptions.PreviousCulture.Name.ToLowerInvariant(),
["timeZone"] = "GMT+8",
},
};
}
- public virtual Task ShowAlertDialogAsync(JsParam param)
+ public virtual ValueTask ShowAlertDialogAsync(JsParam param)
{
- return Task.FromException(new NotImplementedException());
+ return ValueTask.FromException(new NotSupportedException());
}
public virtual IJsResult? StartRealPersonValidation(JsParam param)
@@ -308,7 +308,7 @@ internal class MiHoYoJSInterface
throw new NotImplementedException();
}
- public virtual Task GetNotificationSettingsAsync(JsParam param)
+ public virtual ValueTask GetNotificationSettingsAsync(JsParam param)
{
throw new NotImplementedException();
}
@@ -318,7 +318,7 @@ internal class MiHoYoJSInterface
throw new NotImplementedException();
}
- private async Task ExecuteCallbackScriptAsync(string callback, string? payload = null)
+ private async ValueTask ExecuteCallbackScriptAsync(string callback, string? payload = null)
{
if (string.IsNullOrEmpty(callback))
{
@@ -377,7 +377,7 @@ internal class MiHoYoJSInterface
return default;
}
- private async Task TryGetJsResultFromJsParamAsync(JsParam param)
+ private async ValueTask TryGetJsResultFromJsParamAsync(JsParam param)
{
try
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/DynamicSecrect2Playload.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/DynamicSecrect2Playload.cs
index be1836e4..0cb8669b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/DynamicSecrect2Playload.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/DynamicSecrect2Playload.cs
@@ -25,6 +25,7 @@ internal sealed class DynamicSecrect2Playload
/// 获取排序后的的查询参数
///
/// 查询参数
+ [SuppressMessage("", "CA1308")]
public string GetQueryParam()
{
// TODO : improve here.
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/JsParam.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/JsParam.cs
index 8a69814f..d03d92c8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/JsParam.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/JsParam.cs
@@ -61,7 +61,7 @@ internal sealed class JsParam
return new JsParam()
{
Method = jsParam.Method,
- Payload = jsParam.Payload.HasValue ? jsParam.Payload.Value.Deserialize()! : default!,
+ Payload = JsonSerializer.Deserialize(jsParam.Payload ?? default) ?? default!,
Callback = jsParam.Callback,
};
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs
index 12208cb7..b442b5f0 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs
@@ -32,7 +32,7 @@ internal sealed partial class EnkaClient
/// 玩家Uid
/// 取消令牌
/// Enka API 响应
- public Task GetForwardDataAsync(in PlayerUid playerUid, CancellationToken token = default)
+ public ValueTask GetForwardDataAsync(in PlayerUid playerUid, CancellationToken token = default)
{
return TryGetEnkaResponseCoreAsync(string.Format(EnkaAPIHutaoForward, playerUid.Value), token);
}
@@ -43,12 +43,12 @@ internal sealed partial class EnkaClient
/// 玩家Uid
/// 取消令牌
/// Enka API 响应
- public Task GetDataAsync(in PlayerUid playerUid, CancellationToken token = default)
+ public ValueTask GetDataAsync(in PlayerUid playerUid, CancellationToken token = default)
{
- return TryGetEnkaResponseCoreAsync(string.Format(EnkaAPI, playerUid.Value), token);
+ return TryGetEnkaResponseCoreAsync(EnkaAPI.Format(playerUid.Value), token);
}
- private async Task TryGetEnkaResponseCoreAsync(string url, CancellationToken token = default)
+ private async ValueTask TryGetEnkaResponseCoreAsync(string url, CancellationToken token = default)
{
try
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/EnkaResponse.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/EnkaResponse.cs
index 7c99346a..717b5200 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/EnkaResponse.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/EnkaResponse.cs
@@ -34,7 +34,7 @@ internal sealed class EnkaResponse
public bool IsValid
{
[MemberNotNullWhen(true, nameof(PlayerInfo), nameof(AvatarInfoList))]
- get => PlayerInfo != null && AvatarInfoList != null;
+ get => PlayerInfo is not null && AvatarInfoList is not null;
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Geetest/GeetestClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Geetest/GeetestClient.cs
index 44bc5e0b..980a58e1 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Geetest/GeetestClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Geetest/GeetestClient.cs
@@ -23,7 +23,7 @@ internal sealed partial class GeetestClient
///
/// gt
/// 类型
- public async Task?> GetTypeAsync(string gt)
+ public async ValueTask?> GetTypeAsync(string gt)
{
string raw = await httpClient.GetStringAsync(ApiEndpoints.GeetestGetType(gt)).ConfigureAwait(false);
raw = raw[0] == '(' ? raw[1..^1] : raw; // remove surrounded ( )
@@ -37,7 +37,7 @@ internal sealed partial class GeetestClient
/// gt
/// 验证流水号
/// 验证方式
- public async Task?> GetAjaxAsync(string gt, string challenge)
+ public async ValueTask?> GetAjaxAsync(string gt, string challenge)
{
string raw = await httpClient.GetStringAsync(ApiEndpoints.GeetestAjax(gt, challenge)).ConfigureAwait(false);
raw = raw[0] == '(' ? raw[1..^1] : raw; // remove surrounded ( )
@@ -50,7 +50,7 @@ internal sealed partial class GeetestClient
///
/// 验证注册
/// 验证方式
- public async Task?> GetAjaxAsync(VerificationRegistration registration)
+ public async ValueTask?> GetAjaxAsync(VerificationRegistration registration)
{
try
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/App/Account/AccountClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/App/Account/AccountClient.cs
index 0e728633..7b956654 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/App/Account/AccountClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/App/Account/AccountClient.cs
@@ -32,7 +32,7 @@ internal sealed partial class AccountClient
/// 取消令牌
/// 用户角色信息
[ApiInformation(Cookie = CookieType.SToken, Salt = SaltType.K2)]
- public async Task> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default)
+ public async ValueTask> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default)
{
Response? resp = await httpClient
.SetUser(user, CookieType.SToken)
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/IUserClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/IUserClient.cs
index d3c8abb6..3024b485 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/IUserClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/IUserClient.cs
@@ -16,5 +16,5 @@ internal interface IUserClient
/// 用户
/// 取消令牌
/// 详细信息
- Task> GetUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default);
+ ValueTask> GetUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default);
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs
index b8f65591..e369cea1 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs
@@ -29,15 +29,16 @@ internal sealed partial class UserClient : IUserClient
/// 取消令牌
/// 详细信息
[ApiInformation(Cookie = CookieType.SToken, Salt = SaltType.K2)]
- public async Task> GetUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default)
+ public async ValueTask> GetUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default)
{
+ ArgumentException.ThrowIfNullOrEmpty(user.Aid);
Response? resp = await httpClient
// .SetUser(user, CookieType.SToken)
.SetReferer(ApiEndpoints.BbsReferer)
// .UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.K2, true)
- .TryCatchGetFromJsonAsync>(ApiEndpoints.UserFullInfoQuery(user.Aid!), options, logger, token)
+ .TryCatchGetFromJsonAsync>(ApiEndpoints.UserFullInfoQuery(user.Aid), options, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClientOversea.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClientOversea.cs
index feacd16c..e05008f4 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClientOversea.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClientOversea.cs
@@ -28,11 +28,12 @@ internal sealed partial class UserClientOversea : IUserClient
/// 取消令牌
/// 详细信息
[ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.None)]
- public async Task> GetUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default)
+ public async ValueTask> GetUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default)
{
+ ArgumentException.ThrowIfNullOrEmpty(user.Aid);
Response? resp = await httpClient
.SetUser(user, CookieType.LToken)
- .TryCatchGetFromJsonAsync>(ApiOsEndpoints.UserFullInfoQuery(user.Aid!), options, logger, token)
+ .TryCatchGetFromJsonAsync>(ApiOsEndpoints.UserFullInfoQuery(user.Aid), options, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Images.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Images.cs
deleted file mode 100644
index 9e0d1fb6..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Images.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-namespace Snap.Hutao.Web.Hoyolab;
-
-internal static class Images
-{
- public const string UIItemIcon204 = $"https://smms.app/image/x9psnPrcbYoCl6U";
- public const string UIItemIcon210 = $"https://smms.app/image/n4gwxlFGPTX2j8p";
- public const string UIItemIcon220021 = $"https://smms.app/image/kbh1a2YVXpxWuez";
- public const string UIIconInteeExplore1 = $"https://smms.app/image/zJ4UYqKiD6uQlLc";
- public const string UIMarkQuestEventsProce = $"https://smms.app/image/DQyTF3rv4aA8MZV";
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaGachaLogClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaGachaLogClient.cs
index 7a76a380..4c8a860c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaGachaLogClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaGachaLogClient.cs
@@ -55,7 +55,7 @@ internal sealed class HomaGachaLogClient
/// 分布类型
/// 取消令牌
/// 祈愿分布
- public async Task> GetGachaDistributionAsync(GachaDistributionType distributionType, CancellationToken token = default)
+ public async ValueTask> GetGachaDistributionAsync(GachaDistributionType distributionType, CancellationToken token = default)
{
Response? resp = await httpClient
.TryCatchGetFromJsonAsync>(HutaoEndpoints.GachaLogStatisticsDistribution(distributionType), options, logger, token)
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Converter/ReliquarySetsConverter.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Converter/ReliquarySetsConverter.cs
index 5b87e9eb..422c9289 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Converter/ReliquarySetsConverter.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Converter/ReliquarySetsConverter.cs
@@ -21,7 +21,7 @@ internal sealed class ReliquarySetsConverter : JsonConverter
List sets = new();
foreach (StringSegment segment in new StringTokenizer(source, Separator.ToArray()))
{
- if (segment.HasValue)
+ if (segment is { HasValue: true, Length: >0 })
{
sets.Add(new(segment.Value));
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Response/Response.cs b/src/Snap.Hutao/Snap.Hutao/Web/Response/Response.cs
index e9cfc3e4..c9889a2f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Response/Response.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Response/Response.cs
@@ -152,13 +152,7 @@ internal sealed class Response : Response, IJsResult
}
}
- ///
- /// 尝试获取数据
- ///
- /// 数据
- /// 服务提供器 默认 Ioc.Default
- /// 返回代码是否指示成功
- public bool TryGetData([NotNullWhen(true)] out TData? data, IServiceProvider? serviceProvider = null)
+ public bool TryGetData([NotNullWhen(true)] out TData? data, IInfoBarService? infoBarService = null, IServiceProvider? serviceProvider = null)
{
if (ReturnCode == 0)
{
@@ -169,7 +163,8 @@ internal sealed class Response : Response, IJsResult
else
{
serviceProvider ??= Ioc.Default;
- serviceProvider.GetRequiredService().Error(ToString());
+ infoBarService ??= serviceProvider.GetRequiredService();
+ infoBarService.Error(ToString());
data = default;
return false;
}