diff --git a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj
index 0e4c1dd7..fd9da915 100644
--- a/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj
+++ b/src/Snap.Hutao/Snap.Hutao.SourceGeneration/Snap.Hutao.SourceGeneration.csproj
@@ -25,4 +25,4 @@
-
+
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/BindingProxy.cs b/src/Snap.Hutao/Snap.Hutao/Control/BindingProxy.cs
index 5b56719f..5e64644e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/BindingProxy.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/BindingProxy.cs
@@ -8,6 +8,8 @@ namespace Snap.Hutao.Control;
///
/// 绑定探针
/// 用于处理特定情况下需要穿透数据上下文的工作
+/// DependencyObject will dispose inner ReferenceTracker in any time
+/// when object is not used anymore.
///
public class BindingProxy : DependencyObject
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Extension/ContentDialogExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Control/Extension/ContentDialogExtension.cs
similarity index 96%
rename from src/Snap.Hutao/Snap.Hutao/Control/Extension/ContentDialogExtensions.cs
rename to src/Snap.Hutao/Snap.Hutao/Control/Extension/ContentDialogExtension.cs
index efd4fced..5eaab145 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Extension/ContentDialogExtensions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Extension/ContentDialogExtension.cs
@@ -8,7 +8,7 @@ namespace Snap.Hutao.Control.Extension;
///
/// 对话框扩展
///
-internal static class ContentDialogExtensions
+internal static class ContentDialogExtension
{
///
/// 阻止用户交互
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
index ef236fac..ab14ff26 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
@@ -32,7 +32,7 @@ public class CachedImage : ImageEx
try
{
- Verify.Operation(imageUri.Host != string.Empty, "无效的Uri");
+ Verify.Operation(imageUri.Host != string.Empty, SH.ControlImageCachedImageInvalidResourceUri);
string file = await imageCache.GetFileFromCacheAsync(imageUri).ConfigureAwait(true);
// check token state to determine whether the operation should be canceled.
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs
index ae71b53d..1c4c8d45 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using CommunityToolkit.WinUI.UI.Animations;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Hosting;
@@ -11,6 +12,7 @@ using Snap.Hutao.Extension;
using Snap.Hutao.Service.Abstraction;
using System.IO;
using System.Net.Http;
+using System.Reflection;
using System.Runtime.InteropServices;
namespace Snap.Hutao.Control.Image;
@@ -24,7 +26,7 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
private static readonly DependencyProperty SourceProperty = Property.Depend(nameof(Source), default(Uri), OnSourceChanged);
private static readonly ConcurrentCancellationTokenSource LoadingTokenSource = new();
- private readonly IImageCache imageCache;
+ private readonly IServiceProvider serviceProvider;
private SpriteVisual? spriteVisual;
private bool isShow = true;
@@ -34,8 +36,15 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
///
public CompositionImage()
{
- imageCache = Ioc.Default.GetRequiredService();
+ serviceProvider = Ioc.Default.GetRequiredService();
+
+ AllowFocusOnInteraction = false;
+ IsDoubleTapEnabled = false;
+ IsHitTestVisible = false;
+ IsHoldingEnabled = false;
+ IsRightTapEnabled = false;
IsTabStop = false;
+
SizeChanged += OnSizeChanged;
}
@@ -86,11 +95,11 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
if (exception is HttpRequestException httpRequestException)
{
- infoBarService.Error(httpRequestException, $"GET {uri}");
+ infoBarService.Error(httpRequestException, string.Format(SH.ControlImageCompositionImageHttpRequest, uri));
}
else
{
- infoBarService.Error(exception, $"应用 {nameof(CompositionImage)} 的源时发生异常");
+ infoBarService.Error(exception.GetBaseException(), SH.ControlImageCompositionImageSystemException);
}
}
@@ -124,26 +133,20 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
if (uri != null)
{
- if (uri.Scheme == "ms-appx")
- {
- imageSurface = LoadedImageSurface.StartLoadFromUri(uri);
- }
- else
- {
- string storageFile = await imageCache.GetFileFromCacheAsync(uri).ConfigureAwait(true);
+ IImageCache imageCache = serviceProvider.GetRequiredService();
+ string file = await imageCache.GetFileFromCacheAsync(uri).ConfigureAwait(true);
- try
- {
- imageSurface = await LoadImageSurfaceAsync(storageFile, token).ConfigureAwait(true);
- }
- catch (COMException)
- {
- imageCache.Remove(uri.Enumerate());
- }
- catch (IOException)
- {
- imageCache.Remove(uri.Enumerate());
- }
+ try
+ {
+ imageSurface = await LoadImageSurfaceAsync(file, token).ConfigureAwait(true);
+ }
+ catch (COMException)
+ {
+ imageCache.Remove(uri.Enumerate());
+ }
+ catch (IOException)
+ {
+ imageCache.Remove(uri.Enumerate());
}
if (imageSurface != null)
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/MonoChrome.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/MonoChrome.cs
index 4e982a4b..e1760298 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/MonoChrome.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/MonoChrome.cs
@@ -6,6 +6,7 @@ using Microsoft.UI;
using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
+using Snap.Hutao.Control.Theme;
namespace Snap.Hutao.Control.Image;
@@ -49,12 +50,7 @@ public class MonoChrome : CompositionImage
private void SetBackgroundColor(CompositionColorBrush backgroundBrush)
{
- ApplicationTheme theme = ActualTheme switch
- {
- ElementTheme.Light => ApplicationTheme.Light,
- ElementTheme.Dark => ApplicationTheme.Dark,
- _ => Ioc.Default.GetRequiredService().RequestedTheme,
- };
+ ApplicationTheme theme = ThemeHelper.ElementToApplication(ActualTheme);
backgroundBrush.Color = theme switch
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Markup/ResourceStringExtension.cs b/src/Snap.Hutao/Snap.Hutao/Control/Markup/ResourceStringExtension.cs
new file mode 100644
index 00000000..c7a7ca36
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Markup/ResourceStringExtension.cs
@@ -0,0 +1,36 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Microsoft.UI.Xaml.Markup;
+
+namespace Snap.Hutao.Control.Markup;
+
+///
+/// Xaml extension to return a value from resource file associated with a resource key
+///
+[MarkupExtensionReturnType(ReturnType = typeof(string))]
+public sealed class ResourceStringExtension : MarkupExtension
+{
+ ///
+ /// Gets or sets associated ID from resource strings.
+ ///
+ public string? Name { get; set; }
+
+ ///
+ /// Gets a string value from resource file associated with a resource key.
+ ///
+ /// Resource key name.
+ /// A string value from resource file associated with a resource key.
+ public static string GetValue(string name)
+ {
+ // This function is needed to accomodate compiled function usage without second paramater,
+ // which doesn't work with optional values.
+ return SH.ResourceManager.GetString(name)!;
+ }
+
+ ///
+ protected override object ProvideValue()
+ {
+ return GetValue(Name ?? string.Empty);
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Panel/AspectRatio.cs b/src/Snap.Hutao/Snap.Hutao/Control/Panel/AspectRatio.cs
index 71bcf6f1..b77d4be3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Panel/AspectRatio.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Panel/AspectRatio.cs
@@ -9,8 +9,10 @@ namespace Snap.Hutao.Control.Panel;
///
/// 纵横比控件
///
-internal class AspectRatio : Microsoft.UI.Xaml.Controls.ContentControl
+internal class AspectRatio : Microsoft.UI.Xaml.Controls.Control
{
+ private const double Epsilon = 2.2204460492503131e-016;
+
private static readonly DependencyProperty TargetWidthProperty = Property.Depend(nameof(TargetWidth), 1D);
private static readonly DependencyProperty TargetHeightProperty = Property.Depend(nameof(TargetHeight), 1D);
@@ -38,6 +40,11 @@ internal class AspectRatio : Microsoft.UI.Xaml.Controls.ContentControl
double ratio = TargetWidth / TargetHeight;
double ratioAvailable = availableSize.Width / availableSize.Height;
+ if (Math.Abs(ratioAvailable - ratio) < Epsilon)
+ {
+ return availableSize;
+ }
+
// 更宽
if (ratioAvailable > ratio)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Panel/PanelSelector.xaml b/src/Snap.Hutao/Snap.Hutao/Control/Panel/PanelSelector.xaml
index 24939cfb..0cf5046b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Panel/PanelSelector.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Panel/PanelSelector.xaml
@@ -21,12 +21,12 @@
Click="RadioMenuFlyoutItemClick"
Icon="{shcm:FontIcon Glyph=}"
Tag="List"
- Text="列表"/>
+ Text="{shcm:ResourceString Name=ControlPanelPanelSelectorDropdownListName}"/>
+ Text="{shcm:ResourceString Name=ControlPanelPanelSelectorDropdownGridName}"/>
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs b/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs
index 6be2322a..b2ef6480 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs
@@ -16,16 +16,34 @@ namespace Snap.Hutao.Control;
[SuppressMessage("", "CA1001")]
public class ScopedPage : Page
{
+ // Allow GC to Collect the IServiceScope
+ private static readonly WeakReference PreviousScopeReference = new(null!);
+
private readonly CancellationTokenSource viewCancellationTokenSource = new();
- private readonly IServiceScope serviceScope;
+ private readonly IServiceScope currentScope;
///
/// 构造一个新的页面
///
public ScopedPage()
{
- serviceScope = Ioc.Default.CreateScope();
- serviceScope.Track();
+ Unloaded += OnScopedPageUnloaded;
+ currentScope = Ioc.Default.CreateScope();
+ DisposePreviousScope();
+
+ // track current
+ PreviousScopeReference.SetTarget(currentScope);
+ }
+
+ ///
+ /// 释放上个范围
+ ///
+ public static void DisposePreviousScope()
+ {
+ if (PreviousScopeReference.TryGetTarget(out IServiceScope? scope))
+ {
+ scope.Dispose();
+ }
}
///
@@ -36,7 +54,7 @@ public class ScopedPage : Page
public void InitializeWith()
where TViewModel : class, IViewModel
{
- IViewModel viewModel = serviceScope.ServiceProvider.GetRequiredService();
+ IViewModel viewModel = currentScope.ServiceProvider.GetRequiredService();
viewModel.CancellationToken = viewCancellationTokenSource.Token;
DataContext = viewModel;
}
@@ -59,7 +77,6 @@ public class ScopedPage : Page
///
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
- base.OnNavigatingFrom(e);
using (viewCancellationTokenSource)
{
// Cancel all tasks executed by the view model
@@ -73,7 +90,7 @@ public class ScopedPage : Page
viewModel.IsViewDisposed = true;
// Dispose the scope
- serviceScope.Dispose();
+ currentScope.Dispose();
}
}
}
@@ -81,11 +98,15 @@ public class ScopedPage : Page
///
protected override void OnNavigatedTo(NavigationEventArgs e)
{
- base.OnNavigatedTo(e);
-
if (e.Parameter is INavigationData extra)
{
NotifyRecipentAsync(extra).SafeForget();
}
}
+
+ private void OnScopedPageUnloaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
+ {
+ DataContext = null;
+ Unloaded -= OnScopedPageUnloaded;
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Text/DescriptionTextBlock.cs b/src/Snap.Hutao/Snap.Hutao/Control/Text/DescriptionTextBlock.cs
index ea1e0ffe..35d69c02 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Text/DescriptionTextBlock.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Text/DescriptionTextBlock.cs
@@ -9,7 +9,7 @@ using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Documents;
using Microsoft.UI.Xaml.Media;
using Snap.Hutao.Control.Media;
-using Snap.Hutao.Core;
+using Snap.Hutao.Control.Theme;
using Windows.UI;
namespace Snap.Hutao.Control.Text;
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/ThemeHelper.cs b/src/Snap.Hutao/Snap.Hutao/Control/Theme/ThemeHelper.cs
similarity index 82%
rename from src/Snap.Hutao/Snap.Hutao/Core/ThemeHelper.cs
rename to src/Snap.Hutao/Snap.Hutao/Control/Theme/ThemeHelper.cs
index 8d5d09fe..d750f6f6 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/ThemeHelper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Theme/ThemeHelper.cs
@@ -4,7 +4,7 @@
using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Xaml;
-namespace Snap.Hutao.Core;
+namespace Snap.Hutao.Control.Theme;
///
/// 主题帮助工具类
@@ -42,6 +42,21 @@ public static class ThemeHelper
};
}
+ ///
+ /// 从 转换到
+ ///
+ /// 元素主题
+ /// 应用主题
+ public static ApplicationTheme ElementToApplication(ElementTheme applicationTheme)
+ {
+ return applicationTheme switch
+ {
+ ElementTheme.Light => ApplicationTheme.Light,
+ ElementTheme.Dark => ApplicationTheme.Dark,
+ _ => Ioc.Default.GetRequiredService().RequestedTheme,
+ };
+ }
+
///
/// 从 转换到
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs
index b64e09b1..fb5f90cc 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs
@@ -70,6 +70,11 @@ internal static class CoreEnvironment
///
public static readonly string FamilyName;
+ ///
+ /// 安装位置
+ ///
+ public static readonly string InstalledLocation;
+
///
/// 数据文件夹
///
@@ -98,9 +103,10 @@ internal static class CoreEnvironment
static CoreEnvironment()
{
- DataFolder = GetDocumentsHutaoPath();
+ DataFolder = GetDatafolderPath();
Version = Package.Current.Id.Version.ToVersion();
FamilyName = Package.Current.Id.FamilyName;
+ InstalledLocation = Package.Current.InstalledLocation.Path;
CommonUA = $"Snap Hutao/{Version}";
// simply assign a random guid
@@ -115,7 +121,7 @@ internal static class CoreEnvironment
return Convert.ToMd5HexString($"{userName}{machineGuid}");
}
- private static string GetDocumentsHutaoPath()
+ private static string GetDatafolderPath()
{
string myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
#if RELEASE
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocConfiguration.cs
index ca944b11..67ad19d0 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocConfiguration.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocConfiguration.cs
@@ -18,7 +18,7 @@ internal static class IocConfiguration
///
/// 集合
/// 可继续操作的集合
- public static IServiceCollection AddJsonSerializerOptions(this IServiceCollection services)
+ public static IServiceCollection AddJsonOptions(this IServiceCollection services)
{
return services.AddSingleton(CoreEnvironment.JsonOptions);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/ServiceScopeExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/ServiceScopeExtension.cs
deleted file mode 100644
index f52fd970..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/ServiceScopeExtension.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Snap.Hutao.Core.DependencyInjection;
-
-///
-/// 服务范围扩展
-///
-public static class ServiceScopeExtension
-{
- // Allow GC to Collect the IServiceScope
- private static readonly WeakReference ScopeReference = new(null!);
-
- ///
- /// 追踪服务范围
- ///
- /// 范围
- public static void Track(this IServiceScope scope)
- {
- DisposeLast();
- ScopeReference.SetTarget(scope);
- }
-
- ///
- /// 释放上个范围
- ///
- public static void DisposeLast()
- {
- if (ScopeReference.TryGetTarget(out IServiceScope? scope))
- {
- scope.Dispose();
- }
- }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/ThrowHelper.cs b/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/ThrowHelper.cs
new file mode 100644
index 00000000..426f0225
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/ThrowHelper.cs
@@ -0,0 +1,56 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Service.Game.Package;
+using System.Runtime.CompilerServices;
+
+namespace Snap.Hutao.Core.ExceptionService;
+
+///
+/// 帮助更好的抛出异常
+///
+[System.Diagnostics.StackTraceHidden]
+internal static class ThrowHelper
+{
+ ///
+ /// 操作取消
+ ///
+ /// 消息
+ /// 内部错误
+ /// 操作取消异常
+ /// nothing
+ [DoesNotReturn]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static OperationCanceledException OperationCanceled(string message, Exception? inner)
+ {
+ throw new OperationCanceledException(message, inner);
+ }
+
+ ///
+ /// 包转换错误
+ ///
+ /// 消息
+ /// 内部错误
+ /// nothing
+ /// 包转换错误异常
+ [DoesNotReturn]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static PackageConvertException PackageConvert(string message, Exception inner)
+ {
+ throw new PackageConvertException(message, inner);
+ }
+
+ ///
+ /// 用户数据损坏
+ ///
+ /// 消息
+ /// 内部错误
+ /// 数据损坏
+ /// nothing
+ [DoesNotReturn]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static UserdataCorruptedException UserdataCorrupted(string message, Exception inner)
+ {
+ throw new UserdataCorruptedException(message, inner);
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/UserdataCorruptedException.cs b/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/UserdataCorruptedException.cs
index bb4c8c31..217b3af7 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/UserdataCorruptedException.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/UserdataCorruptedException.cs
@@ -14,7 +14,7 @@ internal class UserdataCorruptedException : Exception
/// 消息
/// 内部错误
public UserdataCorruptedException(string message, Exception innerException)
- : base($"用户数据已损坏: {message}", innerException)
+ : base(string.Format(SH.CoreExceptionServiceUserdataCorruptedMessage, message), innerException)
{
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/BitsJob.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/BitsJob.cs
index 47d6db7f..df436b5d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/BitsJob.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/BitsJob.cs
@@ -30,7 +30,7 @@ internal class BitsJob : DisposableObject, IBackgroundCopyCallback
private readonly object lockObj = new();
private IBackgroundCopyJob? nativeJob;
- private System.Exception? jobException;
+ private Exception? jobException;
private BG_JOB_PROGRESS progress;
private BG_JOB_STATE state;
private bool isJobComplete;
@@ -79,7 +79,7 @@ internal class BitsJob : DisposableObject, IBackgroundCopyCallback
UpdateJobState();
CompleteOrCancel();
}
- catch (System.Exception ex)
+ catch (Exception ex)
{
log.LogInformation("Failed to job transfer: {message}", ex.Message);
}
@@ -101,7 +101,7 @@ internal class BitsJob : DisposableObject, IBackgroundCopyCallback
CompleteOrCancel();
log.LogInformation(jobException, "Job Exception:");
}
- catch (System.Exception ex)
+ catch (Exception ex)
{
log?.LogInformation("Failed to handle job error: {message}", ex.Message);
}
@@ -141,7 +141,7 @@ internal class BitsJob : DisposableObject, IBackgroundCopyCallback
CompleteOrCancel();
}
}
- catch (System.Exception ex)
+ catch (Exception ex)
{
log.LogInformation(ex, "message");
}
@@ -283,7 +283,7 @@ internal class BitsJob : DisposableObject, IBackgroundCopyCallback
{
action();
}
- catch (System.Exception ex)
+ catch (Exception ex)
{
log.LogInformation("{name} failed. {exception}", displayName, ex);
if (throwOnFailure)
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/BitsManager.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/BitsManager.cs
index 09f74588..af8c2ba7 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/BitsManager.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/BitsManager.cs
@@ -39,7 +39,8 @@ internal class BitsManager
public async Task> DownloadAsync(Uri uri, IProgress progress, CancellationToken token = default)
{
TempFile tempFile = new(true);
- bool result = await Task.Run(() => DownloadCore(uri, tempFile.Path, progress.Report, token), token).ConfigureAwait(false);
+ await ThreadHelper.SwitchToBackgroundAsync();
+ bool result = DownloadCore(uri, tempFile.Path, progress.Report, token);
return new(result, tempFile);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/ProgressUpdateStatus.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/ProgressUpdateStatus.cs
index 8c3ec78f..f8ab9af3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/ProgressUpdateStatus.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/ProgressUpdateStatus.cs
@@ -1,11 +1,14 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using System.Diagnostics;
+
namespace Snap.Hutao.Core.IO.Bits;
///
/// 进度更新状态
///
+[DebuggerDisplay("{BytesRead}/{TotalBytes}")]
public class ProgressUpdateStatus
{
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/DataTransfer/Clipboard.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/DataTransfer/Clipboard.cs
index 497733c5..ab54c4c5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/IO/DataTransfer/Clipboard.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/DataTransfer/Clipboard.cs
@@ -20,7 +20,6 @@ internal static class Clipboard
public static async Task DeserializeTextAsync(JsonSerializerOptions options)
where T : class
{
- await ThreadHelper.SwitchToMainThreadAsync();
DataPackageView view = Windows.ApplicationModel.DataTransfer.Clipboard.GetContent();
string json = await view.GetTextAsync();
return JsonSerializer.Deserialize(json, options);
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/PickerExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/PickerExtension.cs
index 33940299..0f0e734a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/IO/PickerExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/PickerExtension.cs
@@ -32,13 +32,7 @@ internal static class PickerExtension
}
else
{
- if (exception != null)
- {
- Ioc.Default
- .GetRequiredService()
- .Warning("无法打开文件选择器", $"请勿在管理员模式下使用此功能 {exception.Message}");
- }
-
+ InfoBarWaringPickerException(exception);
return new(false, null!);
}
}
@@ -64,14 +58,20 @@ internal static class PickerExtension
}
else
{
- if (exception != null)
- {
- Ioc.Default
- .GetRequiredService()
- .Warning("无法打开文件选择器", $"请勿在管理员模式下使用此功能 {exception.Message}");
- }
-
+ InfoBarWaringPickerException(exception);
return new(false, null!);
}
}
+
+ private static void InfoBarWaringPickerException(Exception? exception)
+ {
+ if (exception != null)
+ {
+ Ioc.Default
+ .GetRequiredService()
+ .Warning(
+ SH.CoreIOPickerExtensionPickerExceptionInfoBarTitle,
+ string.Format(SH.CoreIOPickerExtensionPickerExceptionInfoBarMessage, exception.Message));
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/JumpListHelper.cs b/src/Snap.Hutao/Snap.Hutao/Core/JumpListHelper.cs
index 9dab2b4e..f14a1daa 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/JumpListHelper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/JumpListHelper.cs
@@ -23,8 +23,8 @@ public static class JumpListHelper
list.Items.Clear();
- JumpListItem launchGameItem = JumpListItem.CreateWithArguments(Activation.LaunchGame, "启动游戏");
- launchGameItem.GroupName = "快捷操作";
+ JumpListItem launchGameItem = JumpListItem.CreateWithArguments(Activation.LaunchGame, SH.CoreJumpListHelperLaunchGameItemDisplayName);
+ launchGameItem.GroupName = SH.CoreJumpListHelperLaunchGameItemGroupName;
launchGameItem.Logo = new("ms-appx:///Resource/Icon/UI_GuideIcon_PlayMethod.png");
list.Items.Add(launchGameItem);
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/DatabaseLoggerFactoryExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/DatabaseLoggerFactoryExtensions.cs
deleted file mode 100644
index 91864ecd..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Core/Logging/DatabaseLoggerFactoryExtensions.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-
-namespace Snap.Hutao.Core.Logging;
-
-///
-/// Extension methods for the class.
-///
-public static class DatabaseLoggerFactoryExtensions
-{
- ///
- /// Adds a debug logger named 'Debug' to the factory.
- ///
- /// The extension method argument.
- /// 日志构造器
- public static ILoggingBuilder AddDatabase(this ILoggingBuilder builder)
- {
- builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton());
- return builder;
- }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/DatebaseLogger.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/DatebaseLogger.cs
deleted file mode 100644
index a6c9b3f3..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Core/Logging/DatebaseLogger.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-namespace Snap.Hutao.Core.Logging;
-
-///
-/// A logger that writes messages in the database table
-///
-internal sealed partial class DatebaseLogger : ILogger
-{
- private readonly string name;
- private readonly LogEntryQueue logEntryQueue;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The name of the logger.
- /// 日志队列
- public DatebaseLogger(string name, LogEntryQueue logEntryQueue)
- {
- this.name = name;
- this.logEntryQueue = logEntryQueue;
- }
-
- ///
- public IDisposable BeginScope(TState state)
- where TState : notnull
- {
- return new NullScope();
- }
-
- ///
- public bool IsEnabled(LogLevel logLevel)
- {
- return logLevel != LogLevel.None;
- }
-
- ///
- public void Log(LogLevel logLevel, EventId eventId, TState state, System.Exception? exception, Func formatter)
- {
- if (!IsEnabled(logLevel))
- {
- return;
- }
-
- string message = formatter(state, exception);
-
- if (string.IsNullOrEmpty(message))
- {
- return;
- }
-
- LogEntry entry = new()
- {
- Time = DateTimeOffset.Now,
- Category = name,
- LogLevel = logLevel,
- EventId = eventId.Id,
- Message = message,
- Exception = exception?.ToString(),
- };
-
- logEntryQueue.Enqueue(entry);
- }
-
- ///
- /// An empty scope without any logic
- ///
- private struct NullScope : IDisposable
- {
- public NullScope()
- {
- }
-
- ///
- public void Dispose()
- {
- }
- }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/DatebaseLoggerProvider.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/DatebaseLoggerProvider.cs
deleted file mode 100644
index 3aabd840..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Core/Logging/DatebaseLoggerProvider.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-namespace Snap.Hutao.Core.Logging;
-
-///
-/// The provider for the .
-///
-[ProviderAlias("Database")]
-public sealed class DatebaseLoggerProvider : ILoggerProvider
-{
- private readonly LogEntryQueue logEntryQueue = new();
-
- ///
- public ILogger CreateLogger(string name)
- {
- return new DatebaseLogger(name, logEntryQueue);
- }
-
- ///
- public void Dispose()
- {
- logEntryQueue.Dispose();
- }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogEntry.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogEntry.cs
deleted file mode 100644
index 6c43298f..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogEntry.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Snap.Hutao.Core.Logging;
-
-///
-/// 数据库日志入口点
-///
-[Table("logs")]
-public class LogEntry
-{
- ///
- /// 内部Id
- ///
- [Key]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public Guid InnerId { get; set; }
-
- ///
- /// 日志时间
- ///
- public DateTimeOffset Time { get; set; }
-
- ///
- /// 类别
- ///
- public string Category { get; set; } = default!;
-
- ///
- /// 日志等级
- ///
- public LogLevel LogLevel { get; set; }
-
- ///
- /// 事件Id
- ///
- public int EventId { get; set; }
-
- ///
- /// 消息
- ///
- public string Message { get; set; } = default!;
-
- ///
- /// 可能的异常
- ///
- public string? Exception { get; set; }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogEntryQueue.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogEntryQueue.cs
deleted file mode 100644
index 64468ddc..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogEntryQueue.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using Microsoft.EntityFrameworkCore;
-using Snap.Hutao.Model.Entity.Database;
-using System.Collections.Concurrent;
-using System.Diagnostics;
-
-namespace Snap.Hutao.Core.Logging;
-
-///
-/// 日志队列
-///
-public sealed class LogEntryQueue : IDisposable
-{
- private readonly ConcurrentQueue entryQueue = new();
- private readonly CancellationTokenSource disposeTokenSource = new();
- private readonly TaskCompletionSource writeDbCompletionSource = new();
- private readonly LogDbContext logDbContext;
-
- private bool disposed;
-
- ///
- /// 构造一个新的日志队列
- ///
- public LogEntryQueue()
- {
- logDbContext = InitializeDbContext();
-
- Task.Run(() => WritePendingLogsAsync(disposeTokenSource.Token)).SafeForget();
- }
-
- ///
- /// 将日志消息存入队列
- ///
- /// 日志
- public void Enqueue(LogEntry logEntry)
- {
- entryQueue.Enqueue(logEntry);
- }
-
- ///
- [SuppressMessage("", "VSTHRD002")]
- public void Dispose()
- {
- if (disposed)
- {
- return;
- }
-
- // notify the write task to complete.
- disposeTokenSource.Cancel();
-
- // Wait the db operation complete.
- writeDbCompletionSource.Task.GetAwaiter().GetResult();
-
- logDbContext.Dispose();
- disposed = true;
- }
-
- private static LogDbContext InitializeDbContext()
- {
- string logDbName = System.IO.Path.Combine(CoreEnvironment.DataFolder, "Log.db");
- LogDbContext logDbContext = LogDbContext.Create($"Data Source={logDbName}");
- if (logDbContext.Database.GetPendingMigrations().Any())
- {
- Debug.WriteLine("[Debug] Performing LogDbContext Migrations");
- logDbContext.Database.Migrate();
- }
-
- // only raw sql can pass
- logDbContext.Logs.Where(log => log.Exception == null).ExecuteDelete();
- return logDbContext;
- }
-
- private async Task WritePendingLogsAsync(CancellationToken token)
- {
- bool hasAdded = false;
- while (true)
- {
- if (entryQueue.TryDequeue(out LogEntry? logEntry))
- {
- logDbContext.Logs.Add(logEntry);
- hasAdded = true;
- }
- else
- {
- if (hasAdded)
- {
- logDbContext.SaveChanges();
- hasAdded = false;
- }
-
- if (token.IsCancellationRequested)
- {
- writeDbCompletionSource.TrySetResult();
- break;
- }
-
- try
- {
- await Task.Delay(5000, token).ConfigureAwait(false);
- }
- catch (TaskCanceledException)
- {
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/ScheduleTaskHelper.cs b/src/Snap.Hutao/Snap.Hutao/Core/ScheduleTaskHelper.cs
index 6256358d..3ea99141 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/ScheduleTaskHelper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/ScheduleTaskHelper.cs
@@ -31,7 +31,7 @@ internal static class ScheduleTaskHelper
}
TaskDefinition task = TaskService.Instance.NewTask();
- task.RegistrationInfo.Description = "胡桃实时便笺刷新任务 | 请勿编辑或删除。";
+ task.RegistrationInfo.Description = SH.CoreScheduleTaskHelperDailyNoteRefreshTaskDescription;
task.Triggers.Add(new TimeTrigger() { Repetition = new(TimeSpan.FromSeconds(interval), TimeSpan.Zero), });
task.Actions.Add("explorer", "hutao://DailyNote/Refresh");
TaskService.Instance.RootFolder.RegisterTaskDefinition(DailyNoteRefreshTaskName, task);
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/SemaphoreSlimExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/SemaphoreSlimExtension.cs
similarity index 86%
rename from src/Snap.Hutao/Snap.Hutao/Core/Threading/SemaphoreSlimExtensions.cs
rename to src/Snap.Hutao/Snap.Hutao/Core/Threading/SemaphoreSlimExtension.cs
index 9a7a17b4..82cd2ef0 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/SemaphoreSlimExtensions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/SemaphoreSlimExtension.cs
@@ -1,12 +1,14 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Core.ExceptionService;
+
namespace Snap.Hutao.Core.Threading;
///
/// 信号量扩展
///
-public static class SemaphoreSlimExtensions
+public static class SemaphoreSlimExtension
{
///
/// 异步进入信号量
@@ -22,7 +24,7 @@ public static class SemaphoreSlimExtensions
}
catch (ObjectDisposedException ex)
{
- throw new OperationCanceledException("信号量已经被释放,操作取消", ex);
+ ThrowHelper.OperationCanceled(SH.CoreThreadingSemaphoreSlimDisposed, ex);
}
return new SemaphoreSlimReleaser(semaphoreSlim);
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/TaskExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/TaskExtensions.cs
index 317f8a94..0185acc3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/TaskExtensions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/TaskExtensions.cs
@@ -22,7 +22,7 @@ public static class TaskExtensions
{
await task.ConfigureAwait(false);
}
- catch (System.Exception ex)
+ catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
@@ -39,11 +39,11 @@ public static class TaskExtensions
{
await task.ConfigureAwait(false);
}
- catch (TaskCanceledException)
+ catch (OperationCanceledException)
{
// Do nothing
}
- catch (System.Exception e)
+ catch (Exception e)
{
logger?.LogError(EventIds.TaskException, e, "{caller}:\r\n{exception}", nameof(SafeForget), e.GetBaseException());
}
@@ -55,17 +55,17 @@ public static class TaskExtensions
/// 任务
/// 日志器
/// 发生异常时调用
- public static async void SafeForget(this Task task, ILogger? logger = null, Action? onException = null)
+ public static async void SafeForget(this Task task, ILogger? logger = null, Action? onException = null)
{
try
{
await task.ConfigureAwait(false);
}
- catch (TaskCanceledException)
+ catch (OperationCanceledException)
{
// Do nothing
}
- catch (System.Exception e)
+ catch (Exception e)
{
logger?.LogError(EventIds.TaskException, e, "{caller}:\r\n{exception}", nameof(SafeForget), e.GetBaseException());
onException?.Invoke(e);
@@ -79,17 +79,17 @@ public static class TaskExtensions
/// 日志器
/// 任务取消时调用
/// 发生异常时调用
- public static async void SafeForget(this Task task, ILogger? logger = null, Action? onCanceled = null, Action? onException = null)
+ public static async void SafeForget(this Task task, ILogger? logger = null, Action? onCanceled = null, Action? onException = null)
{
try
{
await task.ConfigureAwait(false);
}
- catch (TaskCanceledException)
+ catch (OperationCanceledException)
{
onCanceled?.Invoke();
}
- catch (System.Exception e)
+ catch (Exception e)
{
logger?.LogError(EventIds.TaskException, e, "{caller}:\r\n{exception}", nameof(SafeForget), e.GetBaseException());
onException?.Invoke(e);
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/ThreadPoolSwitchOperation.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/ThreadPoolSwitchOperation.cs
index 5d8ee0c7..cf2551c2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/ThreadPoolSwitchOperation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/ThreadPoolSwitchOperation.cs
@@ -30,13 +30,13 @@ public readonly struct ThreadPoolSwitchOperation : IAwaitable
public void OnCompleted(Action continuation)
{
- QueueContinuation(continuation, flowContext: true);
+ QueueContinuation(continuation, true);
}
///
public void UnsafeOnCompleted(Action continuation)
{
- QueueContinuation(continuation, flowContext: false);
+ QueueContinuation(continuation, false);
}
private static void QueueContinuation(Action continuation, bool flowContext)
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs b/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs
index 429cabcf..9fa90389 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Validation/Must.cs
@@ -44,7 +44,7 @@ public static class Must
/// 上下文
/// Nothing. This method always throws.
[DoesNotReturn]
- public static System.Exception NeverHappen(string? context = null)
+ public static Exception NeverHappen(string? context = null)
{
throw new NotSupportedException(context);
}
@@ -62,21 +62,4 @@ public static class Must
{
return value ?? throw new ArgumentNullException(parameterName);
}
-
- ///
- /// Throws an if the specified parameter's value is IntPtr.Zero.
- ///
- /// The value of the argument.
- /// The name of the parameter to include in any thrown exception.
- /// The value of the parameter.
- /// Thrown if is .
- public static Windows.Win32.Foundation.HWND NotNull(Windows.Win32.Foundation.HWND value, [CallerArgumentExpression("value")] string? parameterName = null)
- {
- if (value == default)
- {
- throw new ArgumentNullException(parameterName);
- }
-
- return value;
- }
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs b/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs
index 1b192f5f..bca6f18d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs
@@ -16,7 +16,7 @@ internal abstract class WebView2Helper
{
private static bool hasEverDetected;
private static bool isSupported;
- private static string version = "未检测到 WebView2 运行时";
+ private static string version = SH.CoreWebView2HelperVersionUndetected;
///
/// 检测 WebView2 是否存在
@@ -36,7 +36,7 @@ internal abstract class WebView2Helper
catch (FileNotFoundException ex)
{
ILogger logger = Ioc.Default.GetRequiredService>();
- logger.LogError(EventIds.WebView2EnvironmentException, ex, "WebView2 运行时未安装");
+ logger.LogError(EventIds.WebView2EnvironmentException, ex, "WebView2 Runtime not installed.");
isSupported = false;
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs
index d84e756d..92cfb10f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs
@@ -124,8 +124,8 @@ internal sealed class ExtendedWindow : IRecipient
/// 确保系统调度队列控制器存在
///
- public void Ensure()
+ public unsafe void Ensure()
{
if (DispatcherQueue.GetForCurrentThread() != null)
{
@@ -152,7 +153,7 @@ public class SystemBackdrop
{
DispatcherQueueOptions options = new()
{
- DwSize = Marshal.SizeOf(),
+ DwSize = sizeof(DispatcherQueueOptions),
ThreadType = 2, // DQTYPE_THREAD_CURRENT
ApartmentType = 2, // DQTAT_COM_STA
};
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclassManager.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclassManager.cs
index 2295cadc..a0d45100 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclassManager.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclassManager.cs
@@ -37,7 +37,7 @@ internal class WindowSubclassManager : IDisposable
public WindowSubclassManager(TWindow window, HWND hwnd, bool isLegacyDragBar)
{
this.window = window;
- this.hwnd = Must.NotNull(hwnd);
+ this.hwnd = hwnd;
this.isLegacyDragBar = isLegacyDragBar;
}
@@ -45,7 +45,7 @@ internal class WindowSubclassManager : IDisposable
/// 尝试设置窗体子类
///
/// 是否设置成功
- public bool TrySetWindowSubclass()
+ public unsafe bool TrySetWindowSubclass()
{
windowProc = new(OnSubclassProcedure);
bool windowHooked = SetWindowSubclass(hwnd, windowProc, WindowSubclassId, 0);
diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/DateTimeOffsetExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/DateTimeOffsetExtension.cs
index f939b0d2..41aca282 100644
--- a/src/Snap.Hutao/Snap.Hutao/Extension/DateTimeOffsetExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Extension/DateTimeOffsetExtension.cs
@@ -8,22 +8,6 @@ namespace Snap.Hutao.Extension;
///
public static class DateTimeOffsetExtension
{
- ///
- /// Converts the current to a that represents the local time.
- ///
- /// 时间偏移
- /// 保留主时间部分
- /// A that represents the local time.
- public static DateTimeOffset ToLocalTime(this DateTimeOffset dateTimeOffset, bool keepTicks)
- {
- if (keepTicks)
- {
- dateTimeOffset -= TimeZoneInfo.Local.GetUtcOffset(DateTimeOffset.Now);
- }
-
- return dateTimeOffset.ToLocalTime();
- }
-
///
/// 从Unix时间戳转换
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/LoggerExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/LoggerExtension.cs
deleted file mode 100644
index 4db7cc49..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Extension/LoggerExtension.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-namespace Snap.Hutao.Extension;
-
-///
-/// 日志器扩展
-///
-[SuppressMessage("", "CA2254")]
-public static class LoggerExtension
-{
- ///
- public static T LogWarning(this ILogger logger, string message, params object?[] param)
- {
- logger.LogWarning(message, param);
- return default!;
- }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs
index 44444b05..dfa068fb 100644
--- a/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs
@@ -33,17 +33,4 @@ public static class StringBuilderExtensions
{
return condition ? sb.Append(value) : sb;
}
-
- ///
- /// 当条件符合时执行
- ///
- /// 字符串建造器
- /// 条件
- /// 条件符合时附加的字符串
- /// 条件不符合时附加的字符串
- /// 同一个字符串建造器
- public static StringBuilder AppendIfElse(this StringBuilder sb, bool condition, string? trueValue, string? falseValue)
- {
- return condition ? sb.Append(trueValue) : sb.Append(falseValue);
- }
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialogFactory.cs b/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialogFactory.cs
index b82941f4..fb7b2605 100644
--- a/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialogFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialogFactory.cs
@@ -60,7 +60,7 @@ internal class ContentDialogFactory : IContentDialogFactory
Title = title,
Content = content,
DefaultButton = ContentDialogButton.Primary,
- PrimaryButtonText = "确认",
+ PrimaryButtonText = SH.FactoryContentDialogFactoryConfirmPrimaryButtonText,
};
return dialog;
@@ -75,8 +75,8 @@ internal class ContentDialogFactory : IContentDialogFactory
Title = title,
Content = content,
DefaultButton = defaultButton,
- PrimaryButtonText = "确认",
- CloseButtonText = "取消",
+ PrimaryButtonText = SH.FactoryContentDialogFactoryConfirmPrimaryButtonText,
+ CloseButtonText = SH.FactoryContentDialogFactoryCancelCloseButtonText,
};
return dialog;
diff --git a/src/Snap.Hutao/Snap.Hutao/GlobalUsing.cs b/src/Snap.Hutao/Snap.Hutao/GlobalUsing.cs
index ff431253..0d09504f 100644
--- a/src/Snap.Hutao/Snap.Hutao/GlobalUsing.cs
+++ b/src/Snap.Hutao/Snap.Hutao/GlobalUsing.cs
@@ -13,6 +13,7 @@ global using Snap.Hutao.Core.DependencyInjection;
global using Snap.Hutao.Core.DependencyInjection.Annotation;
global using Snap.Hutao.Core.Threading;
global using Snap.Hutao.Core.Validation;
+global using Snap.Hutao.Resource.Localization;
// Runtime
global using System;
diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/20220720121521_Logs.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/20220720121521_Logs.Designer.cs
deleted file mode 100644
index 94414f13..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/20220720121521_Logs.Designer.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Snap.Hutao.Model.Entity.Database;
-
-#nullable disable
-
-namespace Snap.Hutao.Migrations.LogDb
-{
- [DbContext(typeof(LogDbContext))]
- [Migration("20220720121521_Logs")]
- partial class Logs
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder.HasAnnotation("ProductVersion", "6.0.7");
-
- modelBuilder.Entity("Snap.Hutao.Core.Logging.LogEntry", b =>
- {
- b.Property("InnerId")
- .ValueGeneratedOnAdd()
- .HasColumnType("TEXT");
-
- b.Property("Category")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property("EventId")
- .HasColumnType("INTEGER");
-
- b.Property("Exception")
- .HasColumnType("TEXT");
-
- b.Property("LogLevel")
- .HasColumnType("INTEGER");
-
- b.Property("Message")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.HasKey("InnerId");
-
- b.ToTable("logs");
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/20220720121521_Logs.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/20220720121521_Logs.cs
deleted file mode 100644
index e5e68191..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/20220720121521_Logs.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-using Microsoft.EntityFrameworkCore.Migrations;
-
-#nullable disable
-
-namespace Snap.Hutao.Migrations.LogDb
-{
- public partial class Logs : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "logs",
- columns: table => new
- {
- InnerId = table.Column(type: "TEXT", nullable: false),
- Category = table.Column(type: "TEXT", nullable: false),
- LogLevel = table.Column(type: "INTEGER", nullable: false),
- EventId = table.Column(type: "INTEGER", nullable: false),
- Message = table.Column(type: "TEXT", nullable: false),
- Exception = table.Column(type: "TEXT", nullable: true),
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_logs", x => x.InnerId);
- });
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "logs");
- }
- }
-}
diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/20220903071033_LogTime.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/20220903071033_LogTime.Designer.cs
deleted file mode 100644
index 4ff3e508..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/20220903071033_LogTime.Designer.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Snap.Hutao.Model.Entity.Database;
-
-#nullable disable
-
-namespace Snap.Hutao.Migrations.LogDb
-{
- [DbContext(typeof(LogDbContext))]
- [Migration("20220903071033_LogTime")]
- partial class LogTime
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder.HasAnnotation("ProductVersion", "6.0.8");
-
- modelBuilder.Entity("Snap.Hutao.Core.Logging.LogEntry", b =>
- {
- b.Property("InnerId")
- .ValueGeneratedOnAdd()
- .HasColumnType("TEXT");
-
- b.Property("Category")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property("EventId")
- .HasColumnType("INTEGER");
-
- b.Property("Exception")
- .HasColumnType("TEXT");
-
- b.Property("LogLevel")
- .HasColumnType("INTEGER");
-
- b.Property("Message")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property("Time")
- .HasColumnType("TEXT");
-
- b.HasKey("InnerId");
-
- b.ToTable("logs");
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/20220903071033_LogTime.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/20220903071033_LogTime.cs
deleted file mode 100644
index 74d1e26d..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/20220903071033_LogTime.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-using Microsoft.EntityFrameworkCore.Migrations;
-
-#nullable disable
-
-namespace Snap.Hutao.Migrations.LogDb
-{
- public partial class LogTime : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.AddColumn(
- name: "Time",
- table: "logs",
- type: "TEXT",
- nullable: false,
- defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropColumn(
- name: "Time",
- table: "logs");
- }
- }
-}
diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/LogDbContextModelSnapshot.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/LogDbContextModelSnapshot.cs
deleted file mode 100644
index a2955f48..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Migrations/LogDb/LogDbContextModelSnapshot.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Snap.Hutao.Model.Entity.Database;
-
-#nullable disable
-
-namespace Snap.Hutao.Migrations.LogDb
-{
- [DbContext(typeof(LogDbContext))]
- partial class LogDbContextModelSnapshot : ModelSnapshot
- {
- protected override void BuildModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder.HasAnnotation("ProductVersion", "6.0.8");
-
- modelBuilder.Entity("Snap.Hutao.Core.Logging.LogEntry", b =>
- {
- b.Property("InnerId")
- .ValueGeneratedOnAdd()
- .HasColumnType("TEXT");
-
- b.Property("Category")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property("EventId")
- .HasColumnType("INTEGER");
-
- b.Property("Exception")
- .HasColumnType("TEXT");
-
- b.Property("LogLevel")
- .HasColumnType("INTEGER");
-
- b.Property("Message")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property("Time")
- .HasColumnType("TEXT");
-
- b.HasKey("InnerId");
-
- b.ToTable("logs");
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/LaunchGame/LaunchScheme.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/LaunchGame/LaunchScheme.cs
index 0c5db92e..9b3b0a34 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/LaunchGame/LaunchScheme.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/LaunchGame/LaunchScheme.cs
@@ -15,9 +15,9 @@ public class LaunchScheme
///
public static readonly ImmutableList KnownSchemes = new List()
{
- new LaunchScheme("官方服 | 天空岛", "eYd89JmJ", "18", "1", "1"),
- new LaunchScheme("渠道服 | 世界树", "KAtdSsoQ", "17", "14", "0"),
- new LaunchScheme("国际服 | 部分支持", "gcStgarh", "10", "1", "0"),
+ new LaunchScheme("官方服", "eYd89JmJ", "18", "1", "1"),
+ new LaunchScheme("渠道服", "KAtdSsoQ", "17", "14", "0"),
+ new LaunchScheme("国际服", "gcStgarh", "10", "1", "0"),
}.ToImmutableList();
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/LogDbContext.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/LogDbContext.cs
deleted file mode 100644
index 79197c6b..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/LogDbContext.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using Microsoft.EntityFrameworkCore;
-using Snap.Hutao.Core.Logging;
-
-namespace Snap.Hutao.Model.Entity.Database;
-
-///
-/// 日志数据库上下文
-/// 由于写入日志的行为需要锁定数据库上下文
-/// 所以将日志单独分离出来进行读写
-///
-public class LogDbContext : DbContext
-{
- ///
- /// 创建一个新的
- ///
- /// 选项
- private LogDbContext(DbContextOptions options)
- : base(options)
- {
- }
-
- ///
- /// 日志记录
- ///
- public DbSet Logs { get; set; } = default!;
-
- ///
- /// 构造一个临时的日志数据库上下文
- ///
- /// 连接字符串
- /// 日志数据库上下文
- public static LogDbContext Create(string sqlConnectionString)
- {
- return new(new DbContextOptionsBuilder().UseSqlite(sqlConnectionString).Options);
- }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/LogDbContextDesignTimeFactory.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/LogDbContextDesignTimeFactory.cs
deleted file mode 100644
index 419b50be..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/LogDbContextDesignTimeFactory.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using Microsoft.EntityFrameworkCore.Design;
-using Snap.Hutao.Model.Entity.Database;
-
-namespace Snap.Hutao.Context.Database;
-
-///
-/// 此类只用于在生成迁移时提供数据库上下文
-///
-[EditorBrowsable(EditorBrowsableState.Never)]
-public class LogDbContextDesignTimeFactory : IDesignTimeDbContextFactory
-{
- ///
- [EditorBrowsable(EditorBrowsableState.Never)]
- public LogDbContext CreateDbContext(string[] args)
- {
- string logDbName = System.IO.Path.Combine(Core.CoreEnvironment.DataFolder, "Log.db");
- return LogDbContext.Create($"Data Source={logDbName}");
- }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Package.StoreAssociation.xml b/src/Snap.Hutao/Snap.Hutao/Package.StoreAssociation.xml
index 1386cb9a..509d52ab 100644
--- a/src/Snap.Hutao/Snap.Hutao/Package.StoreAssociation.xml
+++ b/src/Snap.Hutao/Snap.Hutao/Package.StoreAssociation.xml
@@ -5,6 +5,7 @@
MSA
http://www.w3.org/2001/04/xmlenc#sha256
+
diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
index 55dcece4..f032975c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
+++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
@@ -25,7 +25,8 @@
-
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/Program.cs b/src/Snap.Hutao/Snap.Hutao/Program.cs
index 74121a77..dcbb1de3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Program.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Program.cs
@@ -5,7 +5,6 @@ using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.Windows.AppLifecycle;
-using Snap.Hutao.Core.Logging;
using System.Runtime.InteropServices;
using WinRT;
@@ -33,7 +32,7 @@ public static partial class Program
// In a Desktop app this runs a message pump internally,
// and does not return until the application shuts down.
Application.Start(InitializeApp);
- ServiceScopeExtension.DisposeLast();
+ Control.ScopedPage.DisposePreviousScope();
}
AppInstance.GetCurrent().UnregisterKey();
@@ -54,11 +53,11 @@ public static partial class Program
ServiceProvider services = new ServiceCollection()
// Microsoft extension
- .AddLogging(builder => builder.AddDebug().AddDatabase())
+ .AddLogging(builder => builder.AddDebug())
.AddMemoryCache()
// Hutao extensions
- .AddJsonSerializerOptions()
+ .AddJsonOptions()
.AddDatebase()
.AddInjections()
.AddHttpClients()
@@ -66,7 +65,7 @@ public static partial class Program
// Discrete services
.AddSingleton(WeakReferenceMessenger.Default)
- .BuildServiceProvider();
+ .BuildServiceProvider(true);
Ioc.Default.ConfigureServices(services);
return services;
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
new file mode 100644
index 00000000..98725880
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
@@ -0,0 +1,648 @@
+//------------------------------------------------------------------------------
+//
+// 此代码由工具生成。
+// 运行时版本:4.0.30319.42000
+//
+// 对此文件的更改可能会导致不正确的行为,并且如果
+// 重新生成代码,这些更改将会丢失。
+//
+//------------------------------------------------------------------------------
+
+namespace Snap.Hutao.Resource.Localization {
+ using System;
+
+
+ ///
+ /// 一个强类型的资源类,用于查找本地化的字符串等。
+ ///
+ // 此类是由 StronglyTypedResourceBuilder
+ // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+ // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+ // (以 /str 作为命令选项),或重新生成 VS 项目。
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class SH {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal SH() {
+ }
+
+ ///
+ /// 返回此类使用的缓存的 ResourceManager 实例。
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Snap.Hutao.Resource.Localization.SH", typeof(SH).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// 重写当前线程的 CurrentUICulture 属性,对
+ /// 使用此强类型资源类的所有资源查找执行重写。
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// 查找类似 胡桃 Dev {0} 的本地化字符串。
+ ///
+ internal static string AppDevNameAndVersion {
+ get {
+ return ResourceManager.GetString("AppDevNameAndVersion", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 胡桃 {0} 的本地化字符串。
+ ///
+ internal static string AppNameAndVersion {
+ get {
+ return ResourceManager.GetString("AppNameAndVersion", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 无效的 Uri 的本地化字符串。
+ ///
+ internal static string ControlImageCachedImageInvalidResourceUri {
+ get {
+ return ResourceManager.GetString("ControlImageCachedImageInvalidResourceUri", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 HTTP GET {0} 的本地化字符串。
+ ///
+ internal static string ControlImageCompositionImageHttpRequest {
+ get {
+ return ResourceManager.GetString("ControlImageCompositionImageHttpRequest", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 应用 CompositionImage 的源时发生异常 的本地化字符串。
+ ///
+ internal static string ControlImageCompositionImageSystemException {
+ get {
+ return ResourceManager.GetString("ControlImageCompositionImageSystemException", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 网格 的本地化字符串。
+ ///
+ internal static string ControlPanelPanelSelectorDropdownGridName {
+ get {
+ return ResourceManager.GetString("ControlPanelPanelSelectorDropdownGridName", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 列表 的本地化字符串。
+ ///
+ internal static string ControlPanelPanelSelectorDropdownListName {
+ get {
+ return ResourceManager.GetString("ControlPanelPanelSelectorDropdownListName", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 用户数据已损坏: {0} 的本地化字符串。
+ ///
+ internal static string CoreExceptionServiceUserdataCorruptedMessage {
+ get {
+ return ResourceManager.GetString("CoreExceptionServiceUserdataCorruptedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 请勿在管理员模式下使用此功能 {0} 的本地化字符串。
+ ///
+ internal static string CoreIOPickerExtensionPickerExceptionInfoBarMessage {
+ get {
+ return ResourceManager.GetString("CoreIOPickerExtensionPickerExceptionInfoBarMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 无法打开文件选择器 的本地化字符串。
+ ///
+ internal static string CoreIOPickerExtensionPickerExceptionInfoBarTitle {
+ get {
+ return ResourceManager.GetString("CoreIOPickerExtensionPickerExceptionInfoBarTitle", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 启动游戏 的本地化字符串。
+ ///
+ internal static string CoreJumpListHelperLaunchGameItemDisplayName {
+ get {
+ return ResourceManager.GetString("CoreJumpListHelperLaunchGameItemDisplayName", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 快捷操作 的本地化字符串。
+ ///
+ internal static string CoreJumpListHelperLaunchGameItemGroupName {
+ get {
+ return ResourceManager.GetString("CoreJumpListHelperLaunchGameItemGroupName", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 胡桃实时便笺刷新任务 | 请勿编辑或删除。 的本地化字符串。
+ ///
+ internal static string CoreScheduleTaskHelperDailyNoteRefreshTaskDescription {
+ get {
+ return ResourceManager.GetString("CoreScheduleTaskHelperDailyNoteRefreshTaskDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 信号量已经被释放,操作取消 的本地化字符串。
+ ///
+ internal static string CoreThreadingSemaphoreSlimDisposed {
+ get {
+ return ResourceManager.GetString("CoreThreadingSemaphoreSlimDisposed", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 未检测到 WebView2 运行时 的本地化字符串。
+ ///
+ internal static string CoreWebView2HelperVersionUndetected {
+ get {
+ return ResourceManager.GetString("CoreWebView2HelperVersionUndetected", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 取消 的本地化字符串。
+ ///
+ internal static string FactoryContentDialogFactoryCancelCloseButtonText {
+ get {
+ return ResourceManager.GetString("FactoryContentDialogFactoryCancelCloseButtonText", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 确认 的本地化字符串。
+ ///
+ internal static string FactoryContentDialogFactoryConfirmPrimaryButtonText {
+ get {
+ return ResourceManager.GetString("FactoryContentDialogFactoryConfirmPrimaryButtonText", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 单个成就存档内发现多个相同的成就 Id 的本地化字符串。
+ ///
+ internal static string ServiceAchievementUserdataCorruptedInnerIdNotUnique {
+ get {
+ return ResourceManager.GetString("ServiceAchievementUserdataCorruptedInnerIdNotUnique", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 开始游戏 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierActionLaunchGameButton {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierActionLaunchGameButton", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 我知道了 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierActionLaunchGameDismiss {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierActionLaunchGameDismiss", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 请求异常 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierAttribution {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierAttribution", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 每日委托 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierDailyTask {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierDailyTask", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 奖励未领取 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierDailyTaskHint {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierDailyTaskHint", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 探索派遣 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierExpedition {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierExpedition", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 已完成 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierExpeditionAdaptiveHint {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierExpeditionAdaptiveHint", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 探索派遣已完成 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierExpeditionHint {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierExpeditionHint", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 洞天宝钱 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierHomeCoin {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierHomeCoin", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 当前洞天宝钱:{0} 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierHomeCoinCurrent {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierHomeCoinCurrent", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 多个提醒项达到设定值 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierMultiValueReached {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierMultiValueReached", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 原粹树脂 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierResin {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierResin", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 当前原粹树脂:{0} 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierResinCurrent {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierResinCurrent", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 实时便笺提醒 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierTitle {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierTitle", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 参量质变仪 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierTransformer {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierTransformer", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 准备完成 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierTransformerAdaptiveHint {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierTransformerAdaptiveHint", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 参量质变仪已准备完成 的本地化字符串。
+ ///
+ internal static string ServiceDailyNoteNotifierTransformerHint {
+ get {
+ return ResourceManager.GetString("ServiceDailyNoteNotifierTransformerHint", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 无法获取祈愿记录: {0} 的本地化字符串。
+ ///
+ internal static string ServiceGachaLogArchiveCollectionUserdataCorruptedMessage {
+ get {
+ return ResourceManager.GetString("ServiceGachaLogArchiveCollectionUserdataCorruptedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 无法获取祈愿记录 End Id 的本地化字符串。
+ ///
+ internal static string ServiceGachaLogEndIdUserdataCorruptedMessage {
+ get {
+ return ResourceManager.GetString("ServiceGachaLogEndIdUserdataCorruptedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 角色活动 的本地化字符串。
+ ///
+ internal static string ServiceGachaLogFactoryAvatarWishName {
+ get {
+ return ResourceManager.GetString("ServiceGachaLogFactoryAvatarWishName", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 奔行世间 的本地化字符串。
+ ///
+ internal static string ServiceGachaLogFactoryPermanentWishName {
+ get {
+ return ResourceManager.GetString("ServiceGachaLogFactoryPermanentWishName", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 神铸赋形 的本地化字符串。
+ ///
+ internal static string ServiceGachaLogFactoryWeaponWishName {
+ get {
+ return ResourceManager.GetString("ServiceGachaLogFactoryWeaponWishName", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 请求验证密钥失败 的本地化字符串。
+ ///
+ internal static string ServiceGachaLogUrlProviderAuthkeyRequestFailed {
+ get {
+ return ResourceManager.GetString("ServiceGachaLogUrlProviderAuthkeyRequestFailed", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 未正确提供原神路径,或当前设置的路径不正确 的本地化字符串。
+ ///
+ internal static string ServiceGachaLogUrlProviderCachePathInvalid {
+ get {
+ return ResourceManager.GetString("ServiceGachaLogUrlProviderCachePathInvalid", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 找不到原神内置浏览器缓存路径:\n{0} 的本地化字符串。
+ ///
+ internal static string ServiceGachaLogUrlProviderCachePathNotFound {
+ get {
+ return ResourceManager.GetString("ServiceGachaLogUrlProviderCachePathNotFound", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 未找到可用的 Url 的本地化字符串。
+ ///
+ internal static string ServiceGachaLogUrlProviderCacheUrlNotFound {
+ get {
+ return ResourceManager.GetString("ServiceGachaLogUrlProviderCacheUrlNotFound", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 提供的Url无效 的本地化字符串。
+ ///
+ internal static string ServiceGachaLogUrlProviderManualInputInvalid {
+ get {
+ return ResourceManager.GetString("ServiceGachaLogUrlProviderManualInputInvalid", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 存在多个匹配账号,请删除重复的账号 的本地化字符串。
+ ///
+ internal static string ServiceGameDetectGameAccountMultiMatched {
+ get {
+ return ResourceManager.GetString("ServiceGameDetectGameAccountMultiMatched", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 查询游戏资源信息 的本地化字符串。
+ ///
+ internal static string ServiceGameEnsureGameResourceQueryResourceInformation {
+ get {
+ return ResourceManager.GetString("ServiceGameEnsureGameResourceQueryResourceInformation", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 游戏文件操作失败: {0} 的本地化字符串。
+ ///
+ internal static string ServiceGameFileOperationExceptionMessage {
+ get {
+ return ResourceManager.GetString("ServiceGameFileOperationExceptionMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 选择游戏本体 的本地化字符串。
+ ///
+ internal static string ServiceGameLocatorFileOpenPickerCommitText {
+ get {
+ return ResourceManager.GetString("ServiceGameLocatorFileOpenPickerCommitText", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 找不到 Unity 日志文件 的本地化字符串。
+ ///
+ internal static string ServiceGameLocatorUnityLogFileNotFound {
+ get {
+ return ResourceManager.GetString("ServiceGameLocatorUnityLogFileNotFound", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 在 Unity 日志文件中找不到游戏路径 的本地化字符串。
+ ///
+ internal static string ServiceGameLocatorUnityLogGamePathNotFound {
+ get {
+ return ResourceManager.GetString("ServiceGameLocatorUnityLogGamePathNotFound", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 获取 Package Version 的本地化字符串。
+ ///
+ internal static string ServiceGamePackageRequestPackageVerion {
+ get {
+ return ResourceManager.GetString("ServiceGamePackageRequestPackageVerion", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 获取 Package Version 失败 的本地化字符串。
+ ///
+ internal static string ServiceGamePackageRequestPackageVerionFailed {
+ get {
+ return ResourceManager.GetString("ServiceGamePackageRequestPackageVerionFailed", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 无法找到游戏路径,请前往设置修改 的本地化字符串。
+ ///
+ internal static string ServiceGamePathLocateFailed {
+ get {
+ return ResourceManager.GetString("ServiceGamePathLocateFailed", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 找不到游戏配置文件 {0} 的本地化字符串。
+ ///
+ internal static string ServiceGameSetMultiChannelConfigFileNotFound {
+ get {
+ return ResourceManager.GetString("ServiceGameSetMultiChannelConfigFileNotFound", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 无法读取或保存配置文件,请以管理员模式重试 的本地化字符串。
+ ///
+ internal static string ServiceGameSetMultiChannelUnauthorizedAccess {
+ get {
+ return ResourceManager.GetString("ServiceGameSetMultiChannelUnauthorizedAccess", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 元数据服务尚未初始化,或初始化失败 的本地化字符串。
+ ///
+ internal static string ServiceMetadataNotInitialized {
+ get {
+ return ResourceManager.GetString("ServiceMetadataNotInitialized", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 元数据校验文件解析失败 的本地化字符串。
+ ///
+ internal static string ServiceMetadataParseFailed {
+ get {
+ return ResourceManager.GetString("ServiceMetadataParseFailed", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 元数据校验文件下载失败 的本地化字符串。
+ ///
+ internal static string ServiceMetadataRequestFailed {
+ get {
+ return ResourceManager.GetString("ServiceMetadataRequestFailed", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 尚未选择任何用户以及角色 的本地化字符串。
+ ///
+ internal static string ServiceUserAndRoleUnselected {
+ get {
+ return ResourceManager.GetString("ServiceUserAndRoleUnselected", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 多个用户记录为选中状态 的本地化字符串。
+ ///
+ internal static string ServiceUserCurrentMultiMatched {
+ get {
+ return ResourceManager.GetString("ServiceUserCurrentMultiMatched", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 用户 {0} 状态保存失败 的本地化字符串。
+ ///
+ internal static string ServiceUserCurrentUpdateAndSaveFailed {
+ get {
+ return ResourceManager.GetString("ServiceUserCurrentUpdateAndSaveFailed", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 输入的 Cookie 必须包含 Mid 的本地化字符串。
+ ///
+ internal static string ServiceUserProcessCookieNoMid {
+ get {
+ return ResourceManager.GetString("ServiceUserProcessCookieNoMid", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 输入的 Cookie 必须包含 Stoken 的本地化字符串。
+ ///
+ internal static string ServiceUserProcessCookieNoStoken {
+ get {
+ return ResourceManager.GetString("ServiceUserProcessCookieNoStoken", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 输入的 Cookie 无法获取用户信息 的本地化字符串。
+ ///
+ internal static string ServiceUserProcessCookieRequestUserInfoFailed {
+ get {
+ return ResourceManager.GetString("ServiceUserProcessCookieRequestUserInfoFailed", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
new file mode 100644
index 00000000..7f5046ac
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
@@ -0,0 +1,315 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 胡桃 Dev {0}
+
+
+ 胡桃 {0}
+
+
+ 无效的 Uri
+
+
+ HTTP GET {0}
+
+
+ 应用 CompositionImage 的源时发生异常
+
+
+ 网格
+
+
+ 列表
+
+
+ 用户数据已损坏: {0}
+
+
+ 请勿在管理员模式下使用此功能 {0}
+
+
+ 无法打开文件选择器
+
+
+ 启动游戏
+
+
+ 快捷操作
+
+
+ 胡桃实时便笺刷新任务 | 请勿编辑或删除。
+
+
+ 信号量已经被释放,操作取消
+
+
+ 未检测到 WebView2 运行时
+
+
+ 取消
+
+
+ 确认
+
+
+ 单个成就存档内发现多个相同的成就 Id
+
+
+ 开始游戏
+
+
+ 我知道了
+
+
+ 请求异常
+
+
+ 每日委托
+
+
+ 奖励未领取
+
+
+ 探索派遣
+
+
+ 已完成
+
+
+ 探索派遣已完成
+
+
+ 洞天宝钱
+
+
+ 当前洞天宝钱:{0}
+
+
+ 多个提醒项达到设定值
+
+
+ 原粹树脂
+
+
+ 当前原粹树脂:{0}
+
+
+ 实时便笺提醒
+
+
+ 参量质变仪
+
+
+ 准备完成
+
+
+ 参量质变仪已准备完成
+
+
+ 无法获取祈愿记录: {0}
+
+
+ 无法获取祈愿记录 End Id
+
+
+ 角色活动
+
+
+ 奔行世间
+
+
+ 神铸赋形
+
+
+ 请求验证密钥失败
+
+
+ 未正确提供原神路径,或当前设置的路径不正确
+
+
+ 找不到原神内置浏览器缓存路径:\n{0}
+
+
+ 未找到可用的 Url
+
+
+ 提供的Url无效
+
+
+ 存在多个匹配账号,请删除重复的账号
+
+
+ 查询游戏资源信息
+
+
+ 游戏文件操作失败: {0}
+
+
+ 选择游戏本体
+
+
+ 找不到 Unity 日志文件
+
+
+ 在 Unity 日志文件中找不到游戏路径
+
+
+ 获取 Package Version
+
+
+ 获取 Package Version 失败
+
+
+ 无法找到游戏路径,请前往设置修改
+
+
+ 找不到游戏配置文件 {0}
+
+
+ 无法读取或保存配置文件,请以管理员模式重试
+
+
+ 元数据服务尚未初始化,或初始化失败
+
+
+ 元数据校验文件解析失败
+
+
+ 元数据校验文件下载失败
+
+
+ 尚未选择任何用户以及角色
+
+
+ 多个用户记录为选中状态
+
+
+ 用户 {0} 状态保存失败
+
+
+ 输入的 Cookie 必须包含 Mid
+
+
+ 输入的 Cookie 必须包含 Stoken
+
+
+ 输入的 Cookie 无法获取用户信息
+
+
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs
index b4119679..95b359bd 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Achievement/AchievementService.cs
@@ -114,14 +114,14 @@ internal class AchievementService : IAchievementService
List results = new();
foreach (MetadataAchievement meta in metadata)
{
- EntityAchievement? entity;
+ EntityAchievement? entity = null;
try
{
entity = entities.SingleOrDefault(e => e.Id == meta.Id);
}
catch (InvalidOperationException ex)
{
- throw new UserdataCorruptedException("单个成就存档内发现多个相同的成就 Id", ex);
+ ThrowHelper.UserdataCorrupted(SH.ServiceAchievementUserdataCorruptedInnerIdNotUnique, ex);
}
entity ??= EntityAchievement.Create(archiveId, meta.Id);
@@ -154,7 +154,6 @@ internal class AchievementService : IAchievementService
///
public async Task ImportFromUIAFAsync(EntityArchive archive, List list, ImportStrategy strategy)
{
- // return Task.Run(() => ImportFromUIAF(archive, list, strategy));
await ThreadHelper.SwitchToBackgroundAsync();
Guid archiveId = archive.InnerId;
@@ -190,7 +189,7 @@ internal class AchievementService : IAchievementService
public void SaveAchievements(EntityArchive archive, IList achievements)
{
string name = archive.Name;
- logger.LogInformation(EventIds.Achievement, "Begin saving achievements for [{name}]", name);
+ logger.LogInformation("Begin saving achievements for [{name}]", name);
ValueStopwatch stopwatch = ValueStopwatch.StartNew();
IEnumerable newData = achievements
@@ -200,8 +199,8 @@ internal class AchievementService : IAchievementService
ImportResult result = achievementDbOperation.Overwrite(archive.InnerId, newData);
double time = stopwatch.GetElapsedTime().TotalMilliseconds;
- logger.LogInformation(EventIds.Achievement, "{add} added, {update} updated, {remove} removed", result.Add, result.Update, result.Remove);
- logger.LogInformation(EventIds.Achievement, "Save achievements for [{name}] completed in {time}ms", name, time);
+ logger.LogInformation("{add} added, {update} updated, {remove} removed", result.Add, result.Update, result.Remove);
+ logger.LogInformation("Save achievements for [{name}] completed in {time}ms", name, time);
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs b/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs
index e739fa78..7195f3d2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AnnouncementService.cs
@@ -57,7 +57,7 @@ internal partial class AnnouncementService : IAnnouncementService
Dictionary contentMap = contents
.ToDictionary(id => id.AnnId, content => content.Content);
- // 将活动公告置于上方
+ // 将活动公告置于前方
wrapper.List.Reverse();
// 将公告内容联入公告列表
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryImplementation.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryImplementation.cs
index 20428588..b137d83a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryImplementation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactoryImplementation.cs
@@ -35,11 +35,10 @@ internal class SummaryFactoryImplementation
return new()
{
Player = SummaryHelper.CreatePlayer(playerInfo),
- Avatars = avatarInfos.Where(a => !AvatarIds.IsPlayer(a.AvatarId)).Select(a =>
- {
- SummaryAvatarFactory summaryAvatarFactory = new(metadataContext, a);
- return summaryAvatarFactory.CreateAvatar();
- }).ToList(),
+ Avatars = avatarInfos
+ .Where(a => !AvatarIds.IsPlayer(a.AvatarId))
+ .Select(a => new SummaryAvatarFactory(metadataContext, a).CreateAvatar())
+ .ToList(),
};
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifier.cs b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifier.cs
index ce2c4919..a62c991e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifier.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifier.cs
@@ -54,10 +54,10 @@ internal class DailyNoteNotifier
if (!entry.ResinNotifySuppressed)
{
notifyInfos.Add(new(
- "原粹树脂",
+ SH.ServiceDailyNoteNotifierResin,
"ms-appx:///Resource/Icon/UI_ItemIcon_210_256.png",
$"{entry.DailyNote.CurrentResin}",
- $"当前原粹树脂:{entry.DailyNote.CurrentResin}"));
+ string.Format(SH.ServiceDailyNoteNotifierResinCurrent, entry.DailyNote.CurrentResin)));
entry.ResinNotifySuppressed = true;
}
}
@@ -71,10 +71,10 @@ internal class DailyNoteNotifier
if (!entry.HomeCoinNotifySuppressed)
{
notifyInfos.Add(new(
- "洞天宝钱",
+ SH.ServiceDailyNoteNotifierHomeCoin,
"ms-appx:///Resource/Icon/UI_ItemIcon_204.png",
$"{entry.DailyNote.CurrentHomeCoin}",
- $"当前洞天宝钱:{entry.DailyNote.CurrentHomeCoin}"));
+ string.Format(SH.ServiceDailyNoteNotifierHomeCoinCurrent, entry.DailyNote.CurrentHomeCoin)));
entry.HomeCoinNotifySuppressed = true;
}
}
@@ -88,9 +88,9 @@ internal class DailyNoteNotifier
if (!entry.DailyTaskNotifySuppressed)
{
notifyInfos.Add(new(
- "每日委托",
+ SH.ServiceDailyNoteNotifierDailyTask,
"ms-appx:///Resource/Icon/UI_MarkQuest_Events_Proce.png",
- $"奖励待领取",
+ SH.ServiceDailyNoteNotifierDailyTaskHint,
entry.DailyNote.ExtraTaskRewardDescription));
entry.DailyTaskNotifySuppressed = true;
}
@@ -105,10 +105,10 @@ internal class DailyNoteNotifier
if (!entry.TransformerNotifySuppressed)
{
notifyInfos.Add(new(
- "参量质变仪",
+ SH.ServiceDailyNoteNotifierTransformer,
"ms-appx:///Resource/Icon/UI_ItemIcon_220021.png",
- $"准备完成",
- "参量质变仪已准备完成"));
+ SH.ServiceDailyNoteNotifierTransformerAdaptiveHint,
+ SH.ServiceDailyNoteNotifierTransformerHint));
entry.TransformerNotifySuppressed = true;
}
}
@@ -122,10 +122,10 @@ internal class DailyNoteNotifier
if (!entry.ExpeditionNotifySuppressed)
{
notifyInfos.Add(new(
- "探索派遣",
- AvatarIconConverter.IconNameToUri("UI_AvatarIcon_Side_None.png").ToString(),
- $"已完成",
- "探索派遣已完成"));
+ SH.ServiceDailyNoteNotifierExpedition,
+ Web.HutaoEndpoints.UIAvatarIconSideNone.ToString(), // TODO: embed this
+ SH.ServiceDailyNoteNotifierExpeditionAdaptiveHint,
+ SH.ServiceDailyNoteNotifierExpeditionHint));
entry.ExpeditionNotifySuppressed = true;
}
}
@@ -150,7 +150,7 @@ internal class DailyNoteNotifier
.GetActionTicketByStokenAsync("game_role", entry.User)
.ConfigureAwait(false);
- string? attribution = "请求异常";
+ string? attribution = SH.ServiceDailyNoteNotifierAttribution;
if (actionTicketResponse.IsOk())
{
Response> rolesResponse = await scope.ServiceProvider
@@ -166,10 +166,13 @@ internal class DailyNoteNotifier
}
ToastContentBuilder builder = new ToastContentBuilder()
- .AddHeader("DAILYNOTE", "实时便笺提醒", "DAILYNOTE")
+ .AddHeader("DAILYNOTE", SH.ServiceDailyNoteNotifierTitle, "DAILYNOTE")
.AddAttributionText(attribution)
- .AddButton(new ToastButton().SetContent("开始游戏").AddArgument("Action", "LaunchGame").AddArgument("Uid", entry.Uid))
- .AddButton(new ToastButtonDismiss("我知道了"));
+ .AddButton(new ToastButton()
+ .SetContent(SH.ServiceDailyNoteNotifierActionLaunchGameButton)
+ .AddArgument("Action", Core.LifeCycle.Activation.LaunchGame)
+ .AddArgument("Uid", entry.Uid))
+ .AddButton(new ToastButtonDismiss(SH.ServiceDailyNoteNotifierActionLaunchGameDismiss));
if (appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteReminderNotify, SettingEntryHelper.FalseString).GetBoolean())
{
@@ -178,7 +181,7 @@ internal class DailyNoteNotifier
if (notifyInfos.Count > 2)
{
- builder.AddText("多个提醒项达到设定值");
+ builder.AddText(SH.ServiceDailyNoteNotifierMultiValueReached);
// Desktop and Mobile started supporting adaptive toasts in API contract 3 (Anniversary Update)
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 3))
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 4b08bb1d..71d60414 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/Factory/GachaStatisticsFactory.cs
@@ -65,9 +65,9 @@ internal class GachaStatisticsFactory : IGachaStatisticsFactory
Dictionary weaponMap,
bool isEmptyHistoryWishVisible)
{
- TypedWishSummaryBuilder permanentWishBuilder = new("奔行世间", TypedWishSummaryBuilder.IsPermanentWish, 90, 10);
- TypedWishSummaryBuilder avatarWishBuilder = new("角色活动", TypedWishSummaryBuilder.IsAvatarEventWish, 90, 10);
- TypedWishSummaryBuilder weaponWishBuilder = new("神铸赋形", TypedWishSummaryBuilder.IsWeaponEventWish, 80, 10);
+ TypedWishSummaryBuilder permanentWishBuilder = new(SH.ServiceGachaLogFactoryPermanentWishName, TypedWishSummaryBuilder.IsPermanentWish, 90, 10);
+ TypedWishSummaryBuilder avatarWishBuilder = new(SH.ServiceGachaLogFactoryAvatarWishName, TypedWishSummaryBuilder.IsAvatarEventWish, 90, 10);
+ TypedWishSummaryBuilder weaponWishBuilder = new(SH.ServiceGachaLogFactoryWeaponWishName, TypedWishSummaryBuilder.IsWeaponEventWish, 80, 10);
Dictionary orangeAvatarCounter = new();
Dictionary purpleAvatarCounter = new();
@@ -136,6 +136,7 @@ internal class GachaStatisticsFactory : IGachaStatisticsFactory
else
{
// ItemId place not correct.
+ // TODO: check items id when importing
Must.NeverHappen();
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs
index acd09fe9..951508d4 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs
@@ -6,6 +6,7 @@ using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.Diagnostics;
+using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Extension;
using Snap.Hutao.Model.Binding.Gacha;
@@ -119,12 +120,14 @@ internal class GachaLogService : IGachaLogService
await ThreadHelper.SwitchToMainThreadAsync();
try
{
- return archiveCollection ??= appDbContext.GachaArchives.AsNoTracking().ToObservableCollection();
+ archiveCollection ??= appDbContext.GachaArchives.AsNoTracking().ToObservableCollection();
}
catch (SqliteException ex)
{
- throw new Core.ExceptionService.UserdataCorruptedException($"无法获取祈愿记录: {ex.Message}", ex);
+ ThrowHelper.UserdataCorrupted(string.Format(SH.ServiceGachaLogArchiveCollectionUserdataCorruptedMessage, ex.Message), ex);
}
+
+ return archiveCollection;
}
///
@@ -349,7 +352,7 @@ internal class GachaLogService : IGachaLogService
}
catch (SqliteException ex)
{
- throw new Core.ExceptionService.UserdataCorruptedException("无法获取祈愿记录 End Id", ex);
+ ThrowHelper.UserdataCorrupted(SH.ServiceGachaLogEndIdUserdataCorruptedMessage, ex);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlManualInputProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlManualInputProvider.cs
index 3be7213f..850ba224 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlManualInputProvider.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlManualInputProvider.cs
@@ -29,7 +29,7 @@ internal class GachaLogUrlManualInputProvider : IGachaLogUrlProvider
}
else
{
- return new(false, "提供的Url无效");
+ return new(false, SH.ServiceGachaLogUrlProviderManualInputInvalid);
}
}
else
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs
index 68f481c1..c6c28269 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs
@@ -1,10 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Model.Binding.User;
using Snap.Hutao.Service.User;
-using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
+using Snap.Hutao.Web.Response;
namespace Snap.Hutao.Service.GachaLog;
@@ -34,32 +35,23 @@ internal class GachaLogUrlStokenProvider : IGachaLogUrlProvider
///
public async Task> GetQueryAsync()
{
- Model.Binding.User.User? user = userService.Current;
- if (user != null && user.SelectedUserGameRole != null)
+ if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
{
- if (user.Stoken != null)
- {
- PlayerUid uid = (PlayerUid)user.SelectedUserGameRole;
- GenAuthKeyData data = GenAuthKeyData.CreateForWebViewGacha(uid);
+ GenAuthKeyData data = GenAuthKeyData.CreateForWebViewGacha(userAndUid.Uid);
+ Response authkeyResponse = await bindingClient2.GenerateAuthenticationKeyAsync(userAndUid.User, data).ConfigureAwait(false);
- Web.Response.Response authkeyResponse = await bindingClient2.GenerateAuthenticationKeyAsync(user.Entity, data).ConfigureAwait(false);
- if (authkeyResponse.IsOk())
- {
- return new(true, GachaLogConfigration.AsQuery(data, authkeyResponse.Data));
- }
- else
- {
- return new(false, "请求验证密钥失败");
- }
+ if (authkeyResponse.IsOk())
+ {
+ return new(true, GachaLogConfigration.AsQuery(data, authkeyResponse.Data));
}
else
{
- return new(false, "当前用户的 Cookie 不包含 Stoken");
+ return new(false, SH.ServiceGachaLogUrlProviderAuthkeyRequestFailed);
}
}
else
{
- return new(false, "尚未选择要刷新的用户以及角色");
+ return new(false, SH.ServiceUserAndRoleUnselected);
}
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlWebCacheProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlWebCacheProvider.cs
index 1ea05d84..11239381 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlWebCacheProvider.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlWebCacheProvider.cs
@@ -56,7 +56,7 @@ internal class GachaLogUrlWebCacheProvider : IGachaLogUrlProvider
{
if (tempFile == null)
{
- return new(false, $"找不到原神内置浏览器缓存路径:\n{cacheFile}");
+ return new(false, string.Format(SH.ServiceGachaLogUrlProviderCachePathNotFound, cacheFile));
}
using (FileStream fileStream = new(tempFile.Path, FileMode.Open, FileAccess.Read, FileShare.Read))
@@ -65,14 +65,14 @@ internal class GachaLogUrlWebCacheProvider : IGachaLogUrlProvider
{
await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
string? result = Match(memoryStream, cacheFile.Contains(GameConstants.GenshinImpactData));
- return new(!string.IsNullOrEmpty(result), result ?? "未找到可用的 Url");
+ return new(!string.IsNullOrEmpty(result), result ?? SH.ServiceGachaLogUrlProviderCacheUrlNotFound);
}
}
}
}
else
{
- return new(false, "未正确提供原神路径,或当前设置的路径不正确");
+ return new(false, SH.ServiceGachaLogUrlProviderCachePathInvalid);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameFileOperationException.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameFileOperationException.cs
index b0f152f2..5b04800b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameFileOperationException.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameFileOperationException.cs
@@ -14,7 +14,7 @@ internal class GameFileOperationException : Exception
/// 消息
/// 内部错误
public GameFileOperationException(string message, Exception innerException)
- : base($"游戏文件操作失败: {message}", innerException)
+ : base(string.Format(SH.ServiceGameFileOperationExceptionMessage, 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 2e503f9b..1d294d1d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs
@@ -6,6 +6,7 @@ using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Core;
using Snap.Hutao.Core.Database;
+using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.IO.Ini;
using Snap.Hutao.Extension;
using Snap.Hutao.Model.Binding.LaunchGame;
@@ -92,7 +93,7 @@ internal class GameService : IGameService
}
else
{
- return new(false, "请启动游戏后再次尝试");
+ return new(false, SH.ServiceGamePathLocateFailed);
}
}
@@ -181,15 +182,15 @@ internal class GameService : IGameService
}
catch (FileNotFoundException ex)
{
- throw new GameFileOperationException($"找不到游戏配置文件 {configPath}", ex);
+ throw new GameFileOperationException(string.Format(SH.ServiceGameSetMultiChannelConfigFileNotFound, configPath), ex);
}
catch (DirectoryNotFoundException ex)
{
- throw new GameFileOperationException($"找不到游戏配置文件 {configPath}", ex);
+ throw new GameFileOperationException(string.Format(SH.ServiceGameSetMultiChannelConfigFileNotFound, configPath), ex);
}
catch (UnauthorizedAccessException ex)
{
- throw new GameFileOperationException($"无法读取或保存配置文件,请以管理员模式重试。", ex);
+ throw new GameFileOperationException(SH.ServiceGameSetMultiChannelUnauthorizedAccess, ex);
}
bool changed = false;
@@ -236,7 +237,7 @@ internal class GameService : IGameService
string gameFolder = Path.GetDirectoryName(gamePath)!;
string gameFileName = Path.GetFileName(gamePath);
- progress.Report(new("查询游戏资源信息"));
+ progress.Report(new(SH.ServiceGameEnsureGameResourceQueryResourceInformation));
Response response = await Ioc.Default
.GetRequiredService()
.GetResourceAsync(launchScheme)
@@ -321,6 +322,7 @@ internal class GameService : IGameService
}
// https://docs.unity.cn/cn/current/Manual/PlayerCommandLineArguments.html
+ // TODO: impl monitor option.
string commandLine = new CommandLineBuilder()
.AppendIf("-popupwindow", configuration.IsBorderless)
.Append("-screen-fullscreen", configuration.IsFullScreen ? 1 : 0)
@@ -343,31 +345,24 @@ internal class GameService : IGameService
using (await gameSemaphore.EnterAsync().ConfigureAwait(false))
{
- try
+ if (configuration.UnlockFPS)
{
- if (configuration.UnlockFPS)
- {
- IGameFpsUnlocker unlocker = new GameFpsUnlocker(game, configuration.TargetFPS);
+ IGameFpsUnlocker unlocker = new GameFpsUnlocker(game, configuration.TargetFPS);
- TimeSpan findModuleDelay = TimeSpan.FromMilliseconds(100);
- TimeSpan findModuleLimit = TimeSpan.FromMilliseconds(10000);
- TimeSpan adjustFpsDelay = TimeSpan.FromMilliseconds(2000);
- if (game.Start())
- {
- await unlocker.UnlockAsync(findModuleDelay, findModuleLimit, adjustFpsDelay).ConfigureAwait(false);
- }
- }
- else
+ TimeSpan findModuleDelay = TimeSpan.FromMilliseconds(100);
+ TimeSpan findModuleLimit = TimeSpan.FromMilliseconds(10000);
+ TimeSpan adjustFpsDelay = TimeSpan.FromMilliseconds(2000);
+ if (game.Start())
{
- if (game.Start())
- {
- await game.WaitForExitAsync().ConfigureAwait(false);
- }
+ await unlocker.UnlockAsync(findModuleDelay, findModuleLimit, adjustFpsDelay).ConfigureAwait(false);
}
}
- catch (Win32Exception)
+ else
{
- // 通常是用户取消了UAC
+ if (game.Start())
+ {
+ await game.WaitForExitAsync().ConfigureAwait(false);
+ }
}
}
}
@@ -380,14 +375,14 @@ internal class GameService : IGameService
string? registrySdk = RegistryInterop.Get();
if (!string.IsNullOrEmpty(registrySdk))
{
- GameAccount? account;
+ GameAccount? account = null;
try
{
account = gameAccounts.SingleOrDefault(a => a.MihoyoSDK == registrySdk);
}
catch (InvalidOperationException ex)
{
- throw new Core.ExceptionService.UserdataCorruptedException("已存在多个匹配账号,请先删除重复的账号", ex);
+ ThrowHelper.UserdataCorrupted(SH.ServiceGameDetectGameAccountMultiMatched, ex);
}
if (account == null)
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 5bd33c0e..f73659f8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/ManualGameLocator.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/ManualGameLocator.cs
@@ -36,7 +36,11 @@ internal class ManualGameLocator : IGameLocator
private async Task> LocateInternalAsync(List fileNames)
{
- FileOpenPicker picker = pickerFactory.GetFileOpenPicker(PickerLocationId.Desktop, "选择游戏本体", ".exe");
+ FileOpenPicker picker = pickerFactory.GetFileOpenPicker(
+ PickerLocationId.Desktop,
+ SH.ServiceGameLocatorFileOpenPickerCommitText,
+ ".exe");
+
(bool isPickerOk, FilePath file) = await picker.TryPickSingleFileAsync().ConfigureAwait(false);
if (isPickerOk)
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/UnityLogGameLocator.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/UnityLogGameLocator.cs
index 164d754b..3a3a8293 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/UnityLogGameLocator.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/UnityLogGameLocator.cs
@@ -20,6 +20,7 @@ internal partial class UnityLogGameLocator : IGameLocator
public async Task> LocateGamePathAsync()
{
await ThreadHelper.SwitchToBackgroundAsync();
+
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string logFilePathChinese = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\原神\output_log.txt");
string logFilePathOvsesea = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\Genshin Impact\output_log.txt");
@@ -36,7 +37,7 @@ internal partial class UnityLogGameLocator : IGameLocator
Match matchResult = WarmupFileLine().Match(content);
if (!matchResult.Success)
{
- return new(false, $"在 Unity 日志文件中找不到游戏路径");
+ return new(false, SH.ServiceGameLocatorUnityLogGamePathNotFound);
}
string entryName = matchResult.Groups[0].Value.Replace("_Data", ".exe");
@@ -45,7 +46,7 @@ internal partial class UnityLogGameLocator : IGameLocator
}
else
{
- return new(false, $"找不到 Unity 日志文件");
+ return new(false, SH.ServiceGameLocatorUnityLogFileNotFound);
}
}
}
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 eeb6c25e..1c03c6ea 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Package/PackageConverter.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
+using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.IO;
using Snap.Hutao.Model.Binding.LaunchGame;
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
@@ -49,8 +50,8 @@ internal class PackageConverter
Uri pkgVersionUri = new($"{scatteredFilesUrl}/pkg_version");
ConvertDirection direction = targetScheme.IsOversea ? ConvertDirection.ChineseToOversea : ConvertDirection.OverseaToChinese;
- progress.Report(new("获取 Package Version"));
- Dictionary remoteItems;
+ progress.Report(new(SH.ServiceGamePackageRequestPackageVerion));
+ Dictionary remoteItems = default!;
try
{
using (Stream remoteSteam = await httpClient.GetStreamAsync(pkgVersionUri).ConfigureAwait(false))
@@ -60,7 +61,7 @@ internal class PackageConverter
}
catch (IOException ex)
{
- throw new PackageConvertException("下载 Package Version 失败", ex);
+ ThrowHelper.PackageConvert(SH.ServiceGamePackageRequestPackageVerionFailed, ex);
}
Dictionary localItems;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs
index 00a70bee..9eb84d9e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs
@@ -33,7 +33,7 @@ internal class GameFpsUnlocker : IGameFpsUnlocker
/// 目标fps
public GameFpsUnlocker(Process gameProcess, int targetFPS)
{
- Must.Range(targetFPS >= 30 && targetFPS <= 2000, "目标FPS超过允许值");
+ Must.Range(targetFPS >= 30 && targetFPS <= 2000, "Target FPS threshold exceeded");
TargetFps = targetFPS;
this.gameProcess = gameProcess;
@@ -45,12 +45,12 @@ internal class GameFpsUnlocker : IGameFpsUnlocker
///
public async Task UnlockAsync(TimeSpan findModuleDelay, TimeSpan findModuleLimit, TimeSpan adjustFpsDelay)
{
- Verify.Operation(isValid, "此解锁器已经失效");
+ Verify.Operation(isValid, "This Unlocker is invalid");
MODULEENTRY32 unityPlayer = await FindModuleAsync(findModuleDelay, findModuleLimit).ConfigureAwait(false);
// Read UnityPlayer.dll
- TryReadModuleMemoryFindFpsAddress(unityPlayer);
+ UnsafeTryReadModuleMemoryFindFpsAddress(unityPlayer);
// When player switch between scenes, we have to re adjust the fps
// So we keep a loop here
@@ -73,7 +73,7 @@ internal class GameFpsUnlocker : IGameFpsUnlocker
return WriteProcessMemory(process.SafeHandle, (void*)baseAddress, lpBuffer, sizeof(int), null);
}
- private static unsafe MODULEENTRY32 FindModule(int processId, string moduleName)
+ private static unsafe MODULEENTRY32 UnsafeFindModule(int processId, string moduleName)
{
HANDLE snapshot = CreateToolhelp32Snapshot(CREATE_TOOLHELP_SNAPSHOT_FLAGS.TH32CS_SNAPMODULE, (uint)processId);
try
@@ -109,7 +109,7 @@ internal class GameFpsUnlocker : IGameFpsUnlocker
while (true)
{
- MODULEENTRY32 module = FindModule(gameProcess.Id, "UnityPlayer.dll");
+ MODULEENTRY32 module = UnsafeFindModule(gameProcess.Id, "UnityPlayer.dll");
if (!StructMarshal.IsDefault(module))
{
return module;
@@ -145,7 +145,7 @@ internal class GameFpsUnlocker : IGameFpsUnlocker
}
}
- private unsafe void TryReadModuleMemoryFindFpsAddress(MODULEENTRY32 unityPlayer)
+ private unsafe void UnsafeTryReadModuleMemoryFindFpsAddress(MODULEENTRY32 unityPlayer)
{
bool readOk = UnsafeReadModuleMemory(gameProcess, unityPlayer, out Span image);
Verify.Operation(readOk, "读取内存失败");
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlockerException.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlockerException.cs
deleted file mode 100644
index 1978a284..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlockerException.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using Snap.Hutao.Core.Diagnostics;
-using Snap.Hutao.Win32;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using Windows.Win32.Foundation;
-using Windows.Win32.System.Diagnostics.ToolHelp;
-using static Windows.Win32.PInvoke;
-
-namespace Snap.Hutao.Service.Game.Unlocker;
-
-///
-/// 游戏帧率解锁器异常
-///
-internal class GameFpsUnlockerException : Exception
-{
- ///
- /// 构造一个新的用户数据损坏异常
- ///
- /// 消息
- /// 内部错误
- public GameFpsUnlockerException(Exception innerException)
- : base($"解锁帧率失败: {innerException.Message}", innerException)
- {
- }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs
index abd90fc1..027bee4d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs
@@ -94,13 +94,13 @@ internal partial class MetadataService : IMetadataService, IMetadataServiceIniti
if (metaMd5Map is null)
{
- infoBarService.Error("元数据校验文件解析失败");
+ infoBarService.Error(SH.ServiceMetadataParseFailed);
return false;
}
}
catch (HttpRequestException ex)
{
- infoBarService.Error(ex, "元数据校验文件下载失败");
+ infoBarService.Error(ex, SH.ServiceMetadataRequestFailed);
return false;
}
@@ -140,7 +140,7 @@ internal partial class MetadataService : IMetadataService, IMetadataServiceIniti
if (!skip)
{
- logger.LogInformation(EventIds.MetadataFileMD5Check, "MD5 of {file} not matched, begin downloading", fileFullName);
+ logger.LogInformation("MD5 of {file} not matched, begin downloading", fileFullName);
await DownloadMetadataAsync(fileFullName, token).ConfigureAwait(false);
}
@@ -172,7 +172,7 @@ internal partial class MetadataService : IMetadataService, IMetadataServiceIniti
private async ValueTask FromCacheOrFileAsync(string fileName, CancellationToken token)
where T : class
{
- Verify.Operation(isInitialized, "元数据服务尚未初始化,或初始化失败");
+ Verify.Operation(isInitialized, SH.ServiceMetadataNotInitialized);
string cacheKey = $"{nameof(MetadataService)}.Cache.{fileName}";
if (memoryCache.TryGetValue(cacheKey, out object? value))
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs
index d61470cb..4db78e63 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/INavigationService.cs
@@ -48,6 +48,7 @@ public interface INavigationService
///
/// 导航到指定类型的页面
+ /// 若已经处于当前页面不会向页面发送消息
///
/// 指定的页面类型
/// 要传递的数据
@@ -58,6 +59,7 @@ public interface INavigationService
///
/// 异步的导航到指定类型的页面
+ /// 若已经处于当前页面则会向页面发送消息
///
/// 指定的页面类型
/// 要传递的数据
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs
index 52a2cf63..28abd4d6 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Navigation/NavigationService.cs
@@ -102,7 +102,7 @@ internal class NavigationService : INavigationService
if (currentType == pageType)
{
- logger.LogInformation(EventIds.NavigationHistory, "Navigate to {pageType} : succeed, already in", pageType);
+ logger.LogInformation("Navigate to {pageType} : succeed, already in", pageType);
return NavigationResult.AlreadyNavigatedTo;
}
@@ -112,11 +112,11 @@ internal class NavigationService : INavigationService
try
{
navigated = Frame?.Navigate(pageType, data) ?? false;
- logger.LogInformation(EventIds.NavigationHistory, "Navigate to {pageType} : {result}", pageType, navigated ? "succeed" : "failed");
+ logger.LogInformation("Navigate to {pageType} : {result}", pageType, navigated ? "succeed" : "failed");
}
catch (Exception ex)
{
- logger.LogError(EventIds.NavigationFailed, ex, "An error occurred while navigating to {pageType}", pageType);
+ logger.LogError(ex, "An error occurred while navigating to {pageType}", pageType);
infoBarService.Error(ex);
}
@@ -148,7 +148,7 @@ internal class NavigationService : INavigationService
}
catch (Exception ex)
{
- logger.LogError(EventIds.NavigationFailed, ex, "异步导航时发生异常");
+ logger.LogError(ex, "异步导航时发生异常");
return NavigationResult.Failed;
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
index fb8f89b0..95c62397 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
@@ -5,6 +5,7 @@ using CommunityToolkit.Mvvm.Messaging;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Core.Database;
+using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Extension;
using Snap.Hutao.Message;
using Snap.Hutao.Model.Entity.Database;
@@ -82,7 +83,7 @@ internal class UserService : IUserService
}
catch (InvalidOperationException ex)
{
- throw new Core.ExceptionService.UserdataCorruptedException($"用户 {currentUser.UserInfo?.Uid} 状态保存失败", ex);
+ ThrowHelper.UserdataCorrupted(string.Format(SH.ServiceUserCurrentUpdateAndSaveFailed, currentUser.UserInfo?.Uid), ex);
}
}
@@ -104,15 +105,10 @@ internal class UserService : IUserService
await ThreadHelper.SwitchToBackgroundAsync();
using (IServiceScope scope = scopeFactory.CreateScope())
{
- try
- {
- // Note: cascade deleted dailynotes
- await scope.ServiceProvider.GetRequiredService().Users.RemoveAndSaveAsync(user.Entity).ConfigureAwait(false);
- }
- catch (DbUpdateConcurrencyException ex)
- {
- throw new Core.ExceptionService.UserdataCorruptedException("用户已被其他功能删除", ex);
- }
+ AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
+ await appDbContext.Users
+ .ExecuteDeleteWhereAsync(u => u.InnerId == user.Entity.InnerId)
+ .ConfigureAwait(false);
}
messenger.Send(new UserRemovedMessage(user.Entity));
@@ -144,7 +140,7 @@ internal class UserService : IUserService
}
catch (InvalidOperationException ex)
{
- throw new Core.ExceptionService.UserdataCorruptedException("无法设置当前用户", ex);
+ throw new UserdataCorruptedException(SH.ServiceUserCurrentMultiMatched, ex);
}
}
@@ -201,7 +197,7 @@ internal class UserService : IUserService
if (mid == null)
{
- return new(UserOptionResult.Invalid, "输入的Cookie无法获取用户信息");
+ return new(UserOptionResult.Invalid, SH.ServiceUserProcessCookieNoMid);
}
// 检查 mid 对应用户是否存在
@@ -222,7 +218,7 @@ internal class UserService : IUserService
}
else
{
- return new(UserOptionResult.Invalid, "必须包含 Stoken");
+ return new(UserOptionResult.Invalid, SH.ServiceUserProcessCookieNoStoken);
}
}
}
@@ -266,7 +262,7 @@ internal class UserService : IUserService
}
else
{
- return new(UserOptionResult.Invalid, "输入的 Cookie 无法获取用户信息");
+ return new(UserOptionResult.Invalid, SH.ServiceUserProcessCookieRequestUserInfoFailed);
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
index e914cbcc..3fc0a68e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
+++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
@@ -174,13 +174,13 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -438,4 +438,20 @@
MSBuild:Compile
+
+
+
+
+
+ True
+ True
+ SH.resx
+
+
+
+
+ ResXFileCodeGenerator
+ SH.Designer.cs
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/AnnouncementContentViewer.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Control/AnnouncementContentViewer.xaml.cs
index 4d1ca8bb..3813892d 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Control/AnnouncementContentViewer.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Control/AnnouncementContentViewer.xaml.cs
@@ -5,7 +5,7 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.Control;
-using Snap.Hutao.Core;
+using Snap.Hutao.Control.Theme;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
using Windows.System;
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/HutaoDatabasePage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/HutaoDatabasePage.xaml
index bd0b434d..3cad88ba 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/HutaoDatabasePage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/HutaoDatabasePage.xaml
@@ -100,7 +100,6 @@
-
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml
index 9cf104fe..6d9c6d9e 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml
@@ -1,9 +1,10 @@
-
-
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml.cs
index ba0bc2b5..aa1d0702 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Navigation;
+using Snap.Hutao.Control;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.ViewModel;
@@ -10,25 +11,14 @@ namespace Snap.Hutao.View.Page;
///
/// 设置页面
///
-public sealed partial class SettingPage : Microsoft.UI.Xaml.Controls.Page
+public sealed partial class SettingPage : ScopedPage
{
///
/// 构造新的设置页面
///
public SettingPage()
{
- DataContext = Ioc.Default.GetRequiredService();
+ InitializeWith();
InitializeComponent();
}
-
- ///
- protected override void OnNavigatedTo(NavigationEventArgs e)
- {
- base.OnNavigatedTo(e);
-
- if (e.Parameter is INavigationData data)
- {
- data.NotifyNavigationCompleted();
- }
- }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
index 096fd6ea..99bfaa20 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
@@ -1,4 +1,4 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml.cs
index 8932e53b..954d4011 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Navigation;
+using Snap.Hutao.Control;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.ViewModel;
@@ -10,25 +11,14 @@ namespace Snap.Hutao.View.Page;
///
/// 角色资料页
///
-public sealed partial class WikiAvatarPage : Microsoft.UI.Xaml.Controls.Page
+public sealed partial class WikiAvatarPage : ScopedPage
{
///
/// 构造一个新的角色资料页
///
public WikiAvatarPage()
{
- DataContext = Ioc.Default.GetRequiredService();
+ InitializeWith();
InitializeComponent();
}
-
- ///
- protected override void OnNavigatedTo(NavigationEventArgs e)
- {
- base.OnNavigatedTo(e);
-
- if (e.Parameter is INavigationData data)
- {
- data.NotifyNavigationCompleted();
- }
- }
}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml.cs
index 3c0a5159..1fe0cd93 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml.cs
@@ -26,9 +26,9 @@ public sealed partial class TitleView : UserControl
public string Title
{
#if DEBUG
- get => $"胡桃 Dev Build";
+ get => string.Format(SH.AppDevNameAndVersion, Core.CoreEnvironment.Version);
#else
- get => $"胡桃 {Core.CoreEnvironment.Version}";
+ get => string.Format(SH.AppNameAndVersion, Core.CoreEnvironment.Version);
#endif
}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/WelcomeView.xaml b/src/Snap.Hutao/Snap.Hutao/View/WelcomeView.xaml
index 5088b8ce..3e5f2ef6 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/WelcomeView.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/WelcomeView.xaml
@@ -8,6 +8,7 @@
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shv="using:Snap.Hutao.ViewModel"
d:DataContext="{d:DesignInstance shv:WelcomeViewModel}"
+ Unloaded="OnUnloaded"
mc:Ignorable="d">
@@ -34,6 +35,7 @@
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/WelcomeView.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/WelcomeView.xaml.cs
index ad2f8abf..b20040f2 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/WelcomeView.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/WelcomeView.xaml.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.ViewModel;
@@ -11,12 +12,21 @@ namespace Snap.Hutao.View;
///
public sealed partial class WelcomeView : UserControl
{
+ private readonly IServiceScope serviceScope;
+
///
/// һµĻӭͼ
///
public WelcomeView()
{
InitializeComponent();
- DataContext = Ioc.Default.GetRequiredService();
+ serviceScope = Ioc.Default.CreateScope();
+ DataContext = serviceScope.ServiceProvider.GetRequiredService();
+ }
+
+ private void OnUnloaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
+ {
+ DataContext = null;
+ serviceScope.Dispose();
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs
index 70e35cf1..a7585160 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs
@@ -370,6 +370,12 @@ public class MiHoYoJSInterface
}
}
+ private IJsResult? LogUnhandledMessage([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string message, params object?[] param)
+ {
+ logger.LogWarning(message, param);
+ return default;
+ }
+
private async Task TryGetJsResultFromJsParamAsync(JsParam param)
{
try
@@ -391,7 +397,7 @@ public class MiHoYoJSInterface
"login" => null,
"pushPage" => await PushPageAsync(param).ConfigureAwait(false),
"showLoading" => null,
- _ => logger.LogWarning("Unhandled Message Type: {method}", param.Method),
+ _ => LogUnhandledMessage("Unhandled Message Type: {method}", param.Method),
};
}
catch (ObjectDisposedException)
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs
index 75618762..29b04df4 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs
@@ -116,6 +116,11 @@ internal static class HutaoEndpoints
///
public static readonly Uri UIItemIconNone = new(StaticFile("Bg", "UI_ItemIcon_None.png"));
+ ///
+ /// UI_AvatarIcon_Side_None
+ ///
+ public static readonly Uri UIAvatarIconSideNone = new(StaticFile("AvatarIcon", "UI_AvatarIcon_Side_None.png"));
+
///
/// 压缩包资源
///