mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
26 Commits
fix/1379
...
fix/launch
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51c4e66472 | ||
|
|
48875195bf | ||
|
|
a179e0e838 | ||
|
|
00c3e94e97 | ||
|
|
eec7224c07 | ||
|
|
11620816ec | ||
|
|
c5b75d6f82 | ||
|
|
a9295c0a37 | ||
|
|
29cd690032 | ||
|
|
78d8539ae2 | ||
|
|
7dec87586b | ||
|
|
d169f355f3 | ||
|
|
32b1b698df | ||
|
|
d6c7df1593 | ||
|
|
904fdf7fc9 | ||
|
|
b3e4ebb5d3 | ||
|
|
09b9af4575 | ||
|
|
8930548f44 | ||
|
|
34dbcc6f5d | ||
|
|
75c25cec53 | ||
|
|
22251ca937 | ||
|
|
57c9531db8 | ||
|
|
fd73743159 | ||
|
|
6594d9032d | ||
|
|
ae1b452697 | ||
|
|
bc3df782e4 |
16
.github/workflows/alpha.yml
vendored
16
.github/workflows/alpha.yml
vendored
@@ -13,6 +13,15 @@ on:
|
||||
- '**.md'
|
||||
- 'LICENSE'
|
||||
- '**.yml'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '.gitattributes'
|
||||
- '.github/**'
|
||||
- '.gitignore'
|
||||
- '.gitmodules'
|
||||
- '**.md'
|
||||
- 'LICENSE'
|
||||
- '**.yml'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -34,20 +43,21 @@ jobs:
|
||||
VERSION_API_TOKEN: ${{ secrets.VERSION_API_TOKEN }}
|
||||
|
||||
- name: Sign Msix
|
||||
if: success() && github.event_name != 'pull_request'
|
||||
shell: pwsh
|
||||
run: |
|
||||
[System.Convert]::FromBase64String("${{ secrets.CERTIFICATE }}") | Set-Content -AsByteStream temp.pfx
|
||||
signtool.exe sign /debug /v /a /fd SHA256 /f temp.pfx /p ${{ secrets.PW }} ${{ github.workspace }}\src\output\Snap.Hutao.Alpha-${{ steps.cake.outputs.version }}.msix
|
||||
|
||||
- name: Upload signed msix
|
||||
if: success()
|
||||
uses: actions/upload-artifact@v3
|
||||
if: success() && github.event_name != 'pull_request'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Snap.Hutao.Alpha-${{ steps.cake.outputs.version }}
|
||||
path: ${{ github.workspace }}/src/output/Snap.Hutao.Alpha-${{ steps.cake.outputs.version }}.msix
|
||||
|
||||
- name: Add summary
|
||||
if: success()
|
||||
if: success() && github.event_name != 'pull_request'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$summary = "
|
||||
|
||||
36
build.cake
36
build.cake
@@ -1,5 +1,5 @@
|
||||
#tool "nuget:?package=nuget.commandline&version=6.5.0"
|
||||
#addin nuget:?package=Cake.Http&version=3.0.2
|
||||
#tool "nuget:?package=nuget.commandline&version=6.9.1"
|
||||
#addin nuget:?package=Cake.Http&version=4.0.0
|
||||
|
||||
var target = Argument("target", "Build");
|
||||
var configuration = Argument("configuration", "Release");
|
||||
@@ -33,18 +33,28 @@ if (GitHubActions.IsRunningOnGitHubActions)
|
||||
repoDir = GitHubActions.Environment.Workflow.Workspace.FullPath;
|
||||
outputPath = System.IO.Path.Combine(repoDir, "src", "output");
|
||||
|
||||
var versionAuth = HasEnvironmentVariable("VERSION_API_TOKEN") ? EnvironmentVariable("VERSION_API_TOKEN") : throw new Exception("Cannot find VERSION_API_TOKEN");
|
||||
version = HttpGet(
|
||||
"https://internal.snapgenshin.cn/BuildIntergration/RequestNewVersion",
|
||||
new HttpSettings
|
||||
{
|
||||
Headers = new Dictionary<string, string>
|
||||
{
|
||||
if (GitHubActions.Environment.PullRequest.IsPullRequest)
|
||||
{
|
||||
version = System.DateTime.Now.ToString("yyyy.M.d.0");
|
||||
|
||||
Information("Is Pull Request. Skip version.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var versionAuth = HasEnvironmentVariable("VERSION_API_TOKEN") ? EnvironmentVariable("VERSION_API_TOKEN") : throw new Exception("Cannot find VERSION_API_TOKEN");
|
||||
version = HttpGet(
|
||||
"https://internal.snapgenshin.cn/BuildIntergration/RequestNewVersion",
|
||||
new HttpSettings
|
||||
{
|
||||
Headers = new Dictionary<string, string>
|
||||
{
|
||||
{ "Authorization", versionAuth }
|
||||
}
|
||||
}
|
||||
);
|
||||
Information($"Version: {version}");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Information($"Version: {version}");
|
||||
}
|
||||
|
||||
GitHubActions.Commands.SetOutputParameter("version", version);
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.2.0" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.2.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.2.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.2.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Control.Brush;
|
||||
|
||||
internal sealed class ColorSegmentCollection : List<IColorSegment>
|
||||
{
|
||||
}
|
||||
@@ -9,5 +9,5 @@ internal interface IColorSegment
|
||||
{
|
||||
Color Color { get; }
|
||||
|
||||
double Value { get; }
|
||||
double Value { get; set; }
|
||||
}
|
||||
@@ -5,40 +5,53 @@ using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Shapes;
|
||||
using System.Collections.Specialized;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Control.Brush;
|
||||
|
||||
[DependencyProperty("Source", typeof(List<IColorSegment>), default!, nameof(OnSourceChanged))]
|
||||
[DependencyProperty("Source", typeof(ColorSegmentCollection), default!, nameof(OnSourceChanged))]
|
||||
internal sealed partial class SegmentedBar : ContentControl
|
||||
{
|
||||
private readonly LinearGradientBrush brush = new() { StartPoint = new(0, 0), EndPoint = new(1, 0), };
|
||||
|
||||
public SegmentedBar()
|
||||
{
|
||||
HorizontalContentAlignment = HorizontalAlignment.Stretch;
|
||||
VerticalContentAlignment = VerticalAlignment.Stretch;
|
||||
|
||||
Content = new Rectangle()
|
||||
{
|
||||
Fill = brush,
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
VerticalAlignment = VerticalAlignment.Stretch,
|
||||
};
|
||||
}
|
||||
|
||||
private static void OnSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
SegmentedBar segmentedBar = (SegmentedBar)obj;
|
||||
UpdateLinearGradientBrush((SegmentedBar)obj);
|
||||
}
|
||||
|
||||
private static void UpdateLinearGradientBrush(SegmentedBar segmentedBar)
|
||||
{
|
||||
GradientStopCollection collection = segmentedBar.brush.GradientStops;
|
||||
collection.Clear();
|
||||
|
||||
if (args.NewValue as List<IColorSegment> is [_, ..] list)
|
||||
ColorSegmentCollection segmentCollection = segmentedBar.Source;
|
||||
|
||||
double total = segmentCollection.Sum(seg => seg.Value);
|
||||
if (total is 0D)
|
||||
{
|
||||
double total = list.Sum(seg => seg.Value);
|
||||
double offset = 0;
|
||||
foreach (ref readonly IColorSegment segment in CollectionsMarshal.AsSpan(list))
|
||||
{
|
||||
collection.Add(new() { Color = segment.Color, Offset = offset, });
|
||||
offset += segment.Value / total;
|
||||
collection.Add(new() { Color = segment.Color, Offset = offset, });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
double offset = 0;
|
||||
foreach (ref readonly IColorSegment segment in CollectionsMarshal.AsSpan(segmentCollection))
|
||||
{
|
||||
collection.Add(new() { Color = segment.Color, Offset = offset, });
|
||||
offset += segment.Value / total;
|
||||
collection.Add(new() { Color = segment.Color, Offset = offset, });
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/Snap.Hutao/Snap.Hutao/Control/Theme/KnownColors.cs
Normal file
14
src/Snap.Hutao/Snap.Hutao/Control/Theme/KnownColors.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Win32;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Snap.Hutao.Control.Theme;
|
||||
|
||||
internal static class KnownColors
|
||||
{
|
||||
public static readonly Color Orange = StructMarshal.Color(0xFFBC6932);
|
||||
public static readonly Color Purple = StructMarshal.Color(0xFFA156E0);
|
||||
public static readonly Color Blue = StructMarshal.Color(0xFF5180CB);
|
||||
}
|
||||
@@ -42,10 +42,20 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
|
||||
private string? baseFolder;
|
||||
private string? cacheFolder;
|
||||
|
||||
private string CacheFolder
|
||||
{
|
||||
get => LazyInitializer.EnsureInitialized(ref cacheFolder, () =>
|
||||
{
|
||||
baseFolder ??= serviceProvider.GetRequiredService<RuntimeOptions>().LocalCache;
|
||||
DirectoryInfo info = Directory.CreateDirectory(Path.Combine(baseFolder, CacheFolderName));
|
||||
return info.FullName;
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveInvalid()
|
||||
{
|
||||
RemoveInternal(Directory.GetFiles(GetCacheFolder()).Where(file => IsFileInvalid(file, false)));
|
||||
RemoveCore(Directory.GetFiles(CacheFolder).Where(file => IsFileInvalid(file, false)));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -62,7 +72,7 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
|
||||
return;
|
||||
}
|
||||
|
||||
string folder = GetCacheFolder();
|
||||
string folder = CacheFolder;
|
||||
string[] files = Directory.GetFiles(folder);
|
||||
|
||||
List<string> filesToDelete = [];
|
||||
@@ -75,16 +85,16 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
|
||||
}
|
||||
}
|
||||
|
||||
RemoveInternal(filesToDelete);
|
||||
RemoveCore(filesToDelete);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<ValueFile> GetFileFromCacheAsync(Uri uri)
|
||||
{
|
||||
string fileName = GetCacheFileName(uri);
|
||||
string filePath = Path.Combine(GetCacheFolder(), fileName);
|
||||
string filePath = Path.Combine(CacheFolder, fileName);
|
||||
|
||||
if (File.Exists(filePath) && new FileInfo(filePath).Length != 0)
|
||||
if (!IsFileInvalid(filePath))
|
||||
{
|
||||
return filePath;
|
||||
}
|
||||
@@ -94,10 +104,12 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
|
||||
{
|
||||
if (concurrentTasks.TryAdd(fileName, taskCompletionSource.Task))
|
||||
{
|
||||
logger.LogDebug("Begin downloading image file from '{Uri}' to '{File}'", uri, filePath);
|
||||
await DownloadFileAsync(uri, filePath).ConfigureAwait(false);
|
||||
}
|
||||
else if (concurrentTasks.TryGetValue(fileName, out Task? task))
|
||||
{
|
||||
logger.LogDebug("Waiting for a queued image download task to complete for '{Uri}'", uri);
|
||||
await task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -115,7 +127,7 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
|
||||
public ValueFile GetFileFromCategoryAndName(string category, string fileName)
|
||||
{
|
||||
Uri dummyUri = Web.HutaoEndpoints.StaticRaw(category, fileName).ToUri();
|
||||
return Path.Combine(GetCacheFolder(), GetCacheFileName(dummyUri));
|
||||
return Path.Combine(CacheFolder, GetCacheFileName(dummyUri));
|
||||
}
|
||||
|
||||
private static string GetCacheFileName(Uri uri)
|
||||
@@ -137,17 +149,18 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
|
||||
return fileInfo.Length == 0;
|
||||
}
|
||||
|
||||
private void RemoveInternal(IEnumerable<string> filePaths)
|
||||
private void RemoveCore(IEnumerable<string> filePaths)
|
||||
{
|
||||
foreach (string filePath in filePaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(filePath);
|
||||
logger.LogInformation("Remove cached image succeed:{File}", filePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "Remove Cache Image Failed:{File}", filePath);
|
||||
logger.LogWarning(ex, "Remove cached image failed:{File}", filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,14 +168,17 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
|
||||
[SuppressMessage("", "SH003")]
|
||||
private async Task DownloadFileAsync(Uri uri, string baseFile)
|
||||
{
|
||||
logger.LogInformation("Begin downloading for {Uri}", uri);
|
||||
|
||||
int retryCount = 0;
|
||||
HttpClient httpClient = httpClientFactory.CreateClient(nameof(ImageCache));
|
||||
while (retryCount < 3)
|
||||
{
|
||||
using (HttpResponseMessage message = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
|
||||
{
|
||||
if (message.RequestMessage is { RequestUri: { } target } && target != uri)
|
||||
{
|
||||
logger.LogDebug("The Request '{Source}' has been redirected to '{Target}'", uri, target);
|
||||
}
|
||||
|
||||
if (message.IsSuccessStatusCode)
|
||||
{
|
||||
using (Stream httpStream = await message.Content.ReadAsStreamAsync().ConfigureAwait(false))
|
||||
@@ -181,7 +197,7 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
|
||||
{
|
||||
retryCount++;
|
||||
TimeSpan delay = message.Headers.RetryAfter?.Delta ?? retryCountToDelay[retryCount];
|
||||
logger.LogInformation("Retry {Uri} after {Delay}.", uri, delay);
|
||||
logger.LogInformation("Retry download '{Uri}' after {Delay}.", uri, delay);
|
||||
await Task.Delay(delay).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
@@ -192,18 +208,4 @@ internal sealed partial class ImageCache : IImageCache, IImageCacheFilePathOpera
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetCacheFolder()
|
||||
{
|
||||
if (cacheFolder is not null)
|
||||
{
|
||||
return cacheFolder;
|
||||
}
|
||||
|
||||
baseFolder ??= serviceProvider.GetRequiredService<RuntimeOptions>().LocalCache;
|
||||
DirectoryInfo info = Directory.CreateDirectory(Path.Combine(baseFolder, CacheFolderName));
|
||||
cacheFolder = info.FullName;
|
||||
|
||||
return cacheFolder;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Snap.Hutao.Core.Database;
|
||||
internal static class QueryableExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// source.Where(predicate).ExecuteDelete()
|
||||
/// <code>source.Where(predicate).ExecuteDelete()</code>
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">源类型</typeparam>
|
||||
/// <param name="source">源</param>
|
||||
@@ -27,7 +27,7 @@ internal static class QueryableExtension
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// source.Where(predicate).ExecuteDeleteAsync(token)
|
||||
/// <code>source.Where(predicate).ExecuteDeleteAsync(token)</code>
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">源类型</typeparam>
|
||||
/// <param name="source">源</param>
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Snap.Hutao.Core.DependencyInjection.Abstraction;
|
||||
/// <summary>
|
||||
/// 可转换类型服务
|
||||
/// </summary>
|
||||
[Obsolete("Not useful anymore")]
|
||||
internal interface ICastService
|
||||
{
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.DependencyInjection.Abstraction;
|
||||
|
||||
/// <summary>
|
||||
/// 有名称的对象
|
||||
/// 指示该对象可通过名称区分
|
||||
/// </summary>
|
||||
[Obsolete("无意义的接口")]
|
||||
[HighQuality]
|
||||
internal interface INamedService
|
||||
{
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.DependencyInjection.Abstraction;
|
||||
|
||||
/// <summary>
|
||||
/// 海外服/HoYoLAB 可区分
|
||||
/// </summary>
|
||||
[Obsolete("Use IOverseaSupportFactory instead")]
|
||||
internal interface IOverseaSupport
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否为 海外服/HoYoLAB
|
||||
/// </summary>
|
||||
public bool IsOversea { get; }
|
||||
}
|
||||
@@ -17,6 +17,7 @@ internal static class CastServiceExtension
|
||||
/// <typeparam name="T">目标转换类型</typeparam>
|
||||
/// <param name="service">对象</param>
|
||||
/// <returns>转换类型后的对象</returns>
|
||||
[Obsolete("Not useful anymore")]
|
||||
public static T? As<T>(this ICastService service)
|
||||
where T : class
|
||||
{
|
||||
|
||||
@@ -51,8 +51,13 @@ internal static class DependencyInjection
|
||||
CultureOptions cultureOptions = serviceProvider.GetRequiredService<CultureOptions>();
|
||||
cultureOptions.SystemCulture = CultureInfo.CurrentCulture;
|
||||
|
||||
ILogger<CultureOptions> logger = serviceProvider.GetRequiredService<ILogger<CultureOptions>>();
|
||||
logger.LogDebug("System Culture: {System}", cultureOptions.SystemCulture);
|
||||
|
||||
CultureInfo cultureInfo = cultureOptions.CurrentCulture;
|
||||
|
||||
logger.LogDebug("Current Culture: {Current}", cultureInfo);
|
||||
|
||||
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
|
||||
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
|
||||
CultureInfo.CurrentCulture = cultureInfo;
|
||||
@@ -63,6 +68,7 @@ internal static class DependencyInjection
|
||||
SH.Culture = cultureInfo;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void InitializeConsoleWindow(this IServiceProvider serviceProvider)
|
||||
{
|
||||
_ = serviceProvider.GetRequiredService<ConsoleWindowLifeTime>();
|
||||
|
||||
@@ -47,17 +47,13 @@ internal static class IocConfiguration
|
||||
{
|
||||
if (context.Database.GetPendingMigrations().Any())
|
||||
{
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debug.WriteLine("[Database] Performing AppDbContext Migrations");
|
||||
#endif
|
||||
context.Database.Migrate();
|
||||
}
|
||||
}
|
||||
|
||||
builder
|
||||
#if DEBUG
|
||||
.EnableSensitiveDataLogging()
|
||||
#endif
|
||||
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
|
||||
.UseSqlite(sqlConnectionString);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,6 @@ internal readonly struct MeasureExecutionToken : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
logger.LogInformation("{Caller} toke {Time} ms", callerName, stopwatch.GetElapsedTime().TotalMilliseconds);
|
||||
logger.LogDebug("{Caller} toke {Time} ms", callerName, stopwatch.GetElapsedTime().TotalMilliseconds);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ namespace Snap.Hutao.Core.ExceptionService;
|
||||
/// 数据库损坏异常
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Obsolete("Use HutaoException instead")]
|
||||
internal sealed class DatabaseCorruptedException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Snap.Hutao.Core.ExceptionService;
|
||||
/// 用户的计算机中的某些设置不符合要求
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Obsolete("Use HutaoException instead")]
|
||||
internal sealed class RuntimeEnvironmentException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Snap.Hutao.Core.ExceptionService;
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[System.Diagnostics.StackTraceHidden]
|
||||
[Obsolete("Use HutaoException instead")]
|
||||
internal static class ThrowHelper
|
||||
{
|
||||
[DoesNotReturn]
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Snap.Hutao.Core.ExceptionService;
|
||||
/// 用户数据损坏异常
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Obsolete("Use HutaoException instead")]
|
||||
internal sealed class UserdataCorruptedException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -29,17 +29,19 @@ internal sealed unsafe class LoopbackManager : ObservableObject
|
||||
INET_FIREWALL_APP_CONTAINER* pContainers = default;
|
||||
try
|
||||
{
|
||||
WIN32_ERROR error = NetworkIsolationEnumAppContainers(NETISO_FLAG.NETISO_FLAG_MAX, out uint acCount, out pContainers);
|
||||
Marshal.ThrowExceptionForHR(HRESULT_FROM_WIN32(error));
|
||||
for (uint i = 0; i < acCount; i++)
|
||||
{
|
||||
INET_FIREWALL_APP_CONTAINER* pContainer = pContainers + i;
|
||||
ReadOnlySpan<char> appContainerName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(pContainer->appContainerName);
|
||||
if (appContainerName.Equals(runtimeOptions.FamilyName, StringComparison.Ordinal))
|
||||
WIN32_ERROR error = NetworkIsolationEnumAppContainers(NETISO_FLAG.NETISO_FLAG_MAX, out uint acCount, out pContainers);
|
||||
Marshal.ThrowExceptionForHR(HRESULT_FROM_WIN32(error));
|
||||
for (uint i = 0; i < acCount; i++)
|
||||
{
|
||||
ConvertSidToStringSidW(pContainer->appContainerSid, out PWSTR stringSid);
|
||||
hutaoContainerStringSID = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(stringSid).ToString();
|
||||
break;
|
||||
INET_FIREWALL_APP_CONTAINER* pContainer = pContainers + i;
|
||||
ReadOnlySpan<char> appContainerName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(pContainer->appContainerName);
|
||||
if (appContainerName.Equals(runtimeOptions.FamilyName, StringComparison.Ordinal))
|
||||
{
|
||||
ConvertSidToStringSidW(pContainer->appContainerSid, out PWSTR stringSid);
|
||||
hutaoContainerStringSID = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(stringSid).ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,16 +51,18 @@ internal sealed unsafe class LoopbackManager : ObservableObject
|
||||
_ = NetworkIsolationFreeAppContainers(pContainers);
|
||||
}
|
||||
|
||||
WIN32_ERROR error2 = NetworkIsolationGetAppContainerConfig(out uint accCount, out SID_AND_ATTRIBUTES* pSids);
|
||||
Marshal.ThrowExceptionForHR(HRESULT_FROM_WIN32(error2));
|
||||
for (uint i = 0; i < accCount; i++)
|
||||
{
|
||||
ConvertSidToStringSidW((pSids + i)->Sid, out PWSTR stringSid);
|
||||
ReadOnlySpan<char> stringSidSpan = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(stringSid);
|
||||
if (stringSidSpan.Equals(hutaoContainerStringSID, StringComparison.Ordinal))
|
||||
WIN32_ERROR error = NetworkIsolationGetAppContainerConfig(out uint accCount, out SID_AND_ATTRIBUTES* pSids);
|
||||
Marshal.ThrowExceptionForHR(HRESULT_FROM_WIN32(error));
|
||||
for (uint i = 0; i < accCount; i++)
|
||||
{
|
||||
IsLoopbackEnabled = true;
|
||||
break;
|
||||
ConvertSidToStringSidW((pSids + i)->Sid, out PWSTR stringSid);
|
||||
ReadOnlySpan<char> stringSidSpan = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(stringSid);
|
||||
if (stringSidSpan.Equals(hutaoContainerStringSID, StringComparison.Ordinal))
|
||||
{
|
||||
IsLoopbackEnabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
627
src/Snap.Hutao/Snap.Hutao/Migrations/20240219020258_AddPerferredUidOnUser.Designer.cs
generated
Normal file
627
src/Snap.Hutao/Snap.Hutao/Migrations/20240219020258_AddPerferredUidOnUser.Designer.cs
generated
Normal file
@@ -0,0 +1,627 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20240219020258_AddPerferredUidOnUser")]
|
||||
partial class AddPerferredUidOnUser
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.2");
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ArchiveId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Current")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("achievements");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("achievement_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("CalculatorRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("GameRecordRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Info")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("ShowcaseRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("avatar_infos");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("cultivate_entries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("AvatarLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("AvatarLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("EntryId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("SkillALevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillALevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillELevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillELevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillQLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillQLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("WeaponLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("WeaponLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("EntryId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("cultivate_entry_level_informations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Count")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("EntryId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsFinished")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("EntryId");
|
||||
|
||||
b.ToTable("cultivate_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AttachedUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("cultivate_projects");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DailyNote")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("DailyTaskNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("DailyTaskNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ExpeditionNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ExpeditionNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HomeCoinNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HomeCoinNotifyThreshold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("RefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("ResinNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ResinNotifyThreshold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("TransformerNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("TransformerNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("daily_notes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("gacha_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ArchiveId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("GachaType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("QueryType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("gacha_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AttachUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("MihoyoSDK")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("game_accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Count")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AppendPropIdList")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MainPropId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_reliquaries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("PromoteLevel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_weapons");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("ExpireTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("object_cache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("ScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SpiralAbyss")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("spiral_abysses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Aid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CookieToken")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("CookieTokenLastUpdateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Fingerprint")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("FingerprintLastUpdateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsOversea")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("LToken")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("Ltoken");
|
||||
|
||||
b.Property<string>("Mid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PreferredUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SToken")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("Stoken");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive")
|
||||
.WithMany()
|
||||
.HasForeignKey("ArchiveId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Archive");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||
.WithOne("LevelInformation")
|
||||
.HasForeignKey("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", "EntryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Entry");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||
.WithMany()
|
||||
.HasForeignKey("EntryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Entry");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive")
|
||||
.WithMany()
|
||||
.HasForeignKey("ArchiveId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Archive");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.Navigation("LevelInformation");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddPerferredUidOnUser : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PreferredUid",
|
||||
table: "users",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PreferredUid",
|
||||
table: "users");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Snap.Hutao.Migrations
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.1");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.2");
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
{
|
||||
@@ -503,6 +503,9 @@ namespace Snap.Hutao.Migrations
|
||||
b.Property<string>("Mid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PreferredUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SToken")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("Stoken");
|
||||
|
||||
@@ -13,6 +13,7 @@ internal sealed partial class SettingEntry
|
||||
public const string Culture = "Culture";
|
||||
|
||||
public const string SystemBackdropType = "SystemBackdropType";
|
||||
public const string BackgroundImageType = "BackgroundImageType";
|
||||
|
||||
public const string AnnouncementRegion = "AnnouncementRegion";
|
||||
|
||||
@@ -42,6 +43,7 @@ internal sealed partial class SettingEntry
|
||||
public const string LaunchIsUseCloudThirdPartyMobile = "Launch.IsUseCloudThirdPartyMobile";
|
||||
public const string LaunchIsWindowsHDREnabled = "Launch.IsWindowsHDREnabled";
|
||||
public const string LaunchUseStarwardPlayTimeStatistics = "Launch.UseStarwardPlayTimeStatistics";
|
||||
public const string LaunchUseBetterGenshinImpactAutomation = "Launch.UseBetterGenshinImpactAutomation";
|
||||
public const string LaunchSetDiscordActivityWhenPlaying = "Launch.SetDiscordActivityWhenPlaying";
|
||||
|
||||
[Obsolete("不再支持多开")]
|
||||
@@ -49,4 +51,4 @@ internal sealed partial class SettingEntry
|
||||
|
||||
[Obsolete("不再使用 PowerShell")]
|
||||
public const string PowerShellPath = "PowerShellPath";
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,8 @@ internal sealed class User : ISelectable, IReorderable, IMappingFrom<User, Cooki
|
||||
|
||||
public int Index { get; set; }
|
||||
|
||||
public string? PreferredUid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的用户
|
||||
/// </summary>
|
||||
|
||||
@@ -824,6 +824,9 @@
|
||||
<data name="ServiceDailyNoteNotifierTransformerHint" xml:space="preserve">
|
||||
<value>参量质变仪已准备完成</value>
|
||||
</data>
|
||||
<data name="ServiceDiscordActivityElevationRequiredHint" xml:space="preserve">
|
||||
<value>权限不足,将无法为您设置 Discord Activity 状态</value>
|
||||
</data>
|
||||
<data name="ServiceDiscordGameActivityDetails" xml:space="preserve">
|
||||
<value>正在提瓦特大陆中探索</value>
|
||||
</data>
|
||||
@@ -2189,6 +2192,12 @@
|
||||
<data name="ViewPageLaunchGameArgumentsHeader" xml:space="preserve">
|
||||
<value>启动参数</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameBetterGIDescription" xml:space="preserve">
|
||||
<value>在游戏启动后尝试启动并使用 Better GI 进行自动化任务</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameBetterGIHeader" xml:space="preserve">
|
||||
<value>Better GI</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
|
||||
<value>常规</value>
|
||||
</data>
|
||||
|
||||
@@ -5,6 +5,7 @@ using Snap.Hutao.Core.Windowing;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Service.BackgroundImage;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
|
||||
namespace Snap.Hutao.Service;
|
||||
@@ -15,6 +16,7 @@ internal sealed partial class AppOptions : DbStoreOptions
|
||||
{
|
||||
private bool? isEmptyHistoryWishVisible;
|
||||
private BackdropType? backdropType;
|
||||
private BackgroundImageType? backgroundImageType;
|
||||
private Region? region;
|
||||
private string? geetestCustomCompositeUrl;
|
||||
|
||||
@@ -28,8 +30,14 @@ internal sealed partial class AppOptions : DbStoreOptions
|
||||
|
||||
public BackdropType BackdropType
|
||||
{
|
||||
get => GetOption(ref backdropType, SettingEntry.SystemBackdropType, v => Enum.Parse<BackdropType>(v), BackdropType.Mica).Value;
|
||||
set => SetOption(ref backdropType, SettingEntry.SystemBackdropType, value, value => value.ToStringOrEmpty());
|
||||
get => GetOption(ref backdropType, SettingEntry.SystemBackdropType, EnumParse<BackdropType>, BackdropType.Mica).Value;
|
||||
set => SetOption(ref backdropType, SettingEntry.SystemBackdropType, value, EnumToStringOrEmpty);
|
||||
}
|
||||
|
||||
public BackgroundImageType BackgroundImageType
|
||||
{
|
||||
get => GetOption(ref backgroundImageType, SettingEntry.BackgroundImageType, EnumParse<BackgroundImageType>, BackgroundImageType.HutaoOfficialLauncher).Value;
|
||||
set => SetOption(ref backgroundImageType, SettingEntry.BackgroundImageType, value, EnumToStringOrEmpty);
|
||||
}
|
||||
|
||||
public Lazy<List<NameValue<Region>>> LazyRegions { get; } = new(KnownRegions.Get);
|
||||
@@ -45,4 +53,16 @@ internal sealed partial class AppOptions : DbStoreOptions
|
||||
get => GetOption(ref geetestCustomCompositeUrl, SettingEntry.GeetestCustomCompositeUrl);
|
||||
set => SetOption(ref geetestCustomCompositeUrl, SettingEntry.GeetestCustomCompositeUrl, value);
|
||||
}
|
||||
|
||||
private static T? EnumParse<T>(string input)
|
||||
where T : struct, Enum
|
||||
{
|
||||
return Enum.Parse<T>(input);
|
||||
}
|
||||
|
||||
private static string EnumToStringOrEmpty<T>(T? input)
|
||||
where T : struct, Enum
|
||||
{
|
||||
return input.ToStringOrEmpty();
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ namespace Snap.Hutao.Service.BackgroundImage;
|
||||
|
||||
internal sealed class BackgroundImage
|
||||
{
|
||||
public string Path { get; set; } = default!;
|
||||
|
||||
public BitmapImage ImageSource { get; set; } = default!;
|
||||
|
||||
public Color AccentColor { get; set; }
|
||||
|
||||
@@ -38,8 +38,7 @@ internal sealed partial class BackgroundImageService : IBackgroundImageService
|
||||
string path = System.Random.Shared.GetItems(backgroundSet.ToArray(), 1)[0];
|
||||
backgroundSet.Remove(path);
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
if (string.Equals(path, previous?.ImageSource.UriSource.ToString(), StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(path, previous?.Path, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new(false, default!);
|
||||
}
|
||||
@@ -54,6 +53,7 @@ internal sealed partial class BackgroundImageService : IBackgroundImageService
|
||||
|
||||
BackgroundImage background = new()
|
||||
{
|
||||
Path = path,
|
||||
ImageSource = new(path.ToUri()),
|
||||
AccentColor = accentColor,
|
||||
Luminance = accentColor.Luminance,
|
||||
@@ -65,7 +65,7 @@ internal sealed partial class BackgroundImageService : IBackgroundImageService
|
||||
|
||||
private async ValueTask<HashSet<string>> SkipOrInitBackgroundAsync()
|
||||
{
|
||||
if (backgroundPathSet is null || backgroundPathSet.Count <= 0)
|
||||
if (backgroundPathSet is not { Count: > 0 })
|
||||
{
|
||||
string backgroundFolder = runtimeOptions.GetDataFolderBackgroundFolder();
|
||||
Directory.CreateDirectory(backgroundFolder);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.BackgroundImage;
|
||||
|
||||
internal enum BackgroundImageType
|
||||
{
|
||||
None,
|
||||
LocalFolder,
|
||||
HutaoBing,
|
||||
HutaoDaily,
|
||||
HutaoOfficialLauncher,
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
using Snap.Hutao.Model.Entity.Primitive;
|
||||
using Snap.Hutao.Model.Metadata.Item;
|
||||
using Snap.Hutao.Service.Inventroy;
|
||||
using Snap.Hutao.Service.Inventory;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
using Snap.Hutao.ViewModel.Cultivation;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
@@ -132,13 +132,6 @@ internal static class DiscordController
|
||||
return;
|
||||
}
|
||||
|
||||
// Actually requires a discord client to be running on Windows platform.
|
||||
// If not, the following creation code will throw.
|
||||
if (System.Diagnostics.Process.GetProcessesByName("Discord").Length <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (SyncRoot)
|
||||
{
|
||||
DiscordCreateParams @params = default;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Service.Notification;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Snap.Hutao.Service.Discord;
|
||||
|
||||
@@ -9,22 +11,69 @@ namespace Snap.Hutao.Service.Discord;
|
||||
[Injection(InjectAs.Singleton, typeof(IDiscordService))]
|
||||
internal sealed partial class DiscordService : IDiscordService, IDisposable
|
||||
{
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
|
||||
private bool isInitialized;
|
||||
|
||||
public async ValueTask SetPlayingActivityAsync(bool isOversea)
|
||||
{
|
||||
_ = isOversea
|
||||
? await DiscordController.SetPlayingGenshinImpactAsync().ConfigureAwait(false)
|
||||
: await DiscordController.SetPlayingYuanShenAsync().ConfigureAwait(false);
|
||||
if (IsSupported())
|
||||
{
|
||||
_ = isOversea
|
||||
? await DiscordController.SetPlayingGenshinImpactAsync().ConfigureAwait(false)
|
||||
: await DiscordController.SetPlayingYuanShenAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask SetNormalActivityAsync()
|
||||
{
|
||||
_ = await DiscordController.SetDefaultActivityAsync(runtimeOptions.AppLaunchTime).ConfigureAwait(false);
|
||||
if (IsSupported())
|
||||
{
|
||||
_ = await DiscordController.SetDefaultActivityAsync(runtimeOptions.AppLaunchTime).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DiscordController.Stop();
|
||||
}
|
||||
|
||||
private bool IsSupported()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Actually requires a discord client to be running on Windows platform.
|
||||
// If not, discord core creation code will throw.
|
||||
Process[] discordProcesses = Process.GetProcessesByName("Discord");
|
||||
|
||||
if (discordProcesses.Length <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (Process process in discordProcesses)
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = process.Handle;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
infoBarService.Warning(SH.ServiceDiscordActivityElevationRequiredHint);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
isInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
private bool? isWindowsHDREnabled;
|
||||
private AspectRatio? selectedAspectRatio;
|
||||
private bool? useStarwardPlayTimeStatistics;
|
||||
private bool? useBetterGenshinImpactAutomation;
|
||||
private bool? setDiscordActivityWhenPlaying;
|
||||
|
||||
public LaunchOptions(IServiceProvider serviceProvider)
|
||||
@@ -219,7 +220,7 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
get => selectedAspectRatio;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref selectedAspectRatio, value) && value is AspectRatio aspectRatio)
|
||||
if (SetProperty(ref selectedAspectRatio, value) && value is { } aspectRatio)
|
||||
{
|
||||
(ScreenWidth, ScreenHeight) = ((int)aspectRatio.Width, (int)aspectRatio.Height);
|
||||
}
|
||||
@@ -232,6 +233,12 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
set => SetOption(ref useStarwardPlayTimeStatistics, SettingEntry.LaunchUseStarwardPlayTimeStatistics, value);
|
||||
}
|
||||
|
||||
public bool UseBetterGenshinImpactAutomation
|
||||
{
|
||||
get => GetOption(ref useBetterGenshinImpactAutomation, SettingEntry.LaunchUseBetterGenshinImpactAutomation, false);
|
||||
set => SetOption(ref useBetterGenshinImpactAutomation, SettingEntry.LaunchUseBetterGenshinImpactAutomation, value);
|
||||
}
|
||||
|
||||
public bool SetDiscordActivityWhenPlaying
|
||||
{
|
||||
get => GetOption(ref setDiscordActivityWhenPlaying, SettingEntry.LaunchSetDiscordActivityWhenPlaying, true);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Windows.System;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Launching.Handler;
|
||||
|
||||
internal sealed class LaunchExecutionBetterGenshinImpactAutomationHandlder : ILaunchExecutionDelegateHandler
|
||||
{
|
||||
public async ValueTask OnExecutionAsync(LaunchExecutionContext context, LaunchExecutionDelegate next)
|
||||
{
|
||||
if (context.Options.UseBetterGenshinImpactAutomation)
|
||||
{
|
||||
context.Logger.LogInformation("Using BetterGI to automate gameplay");
|
||||
await LaunchBetterGenshinImpactAsync(context).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async ValueTask LaunchBetterGenshinImpactAsync(LaunchExecutionContext context)
|
||||
{
|
||||
Uri betterGenshinImpactUri = "bettergi://start".ToUri();
|
||||
if (await Launcher.QueryUriSupportAsync(betterGenshinImpactUri, LaunchQuerySupportType.Uri) is LaunchQuerySupportStatus.Available)
|
||||
{
|
||||
context.Logger.LogInformation("Waiting game window to be ready");
|
||||
context.Process.WaitForInputIdle();
|
||||
|
||||
context.Logger.LogInformation("Launching BetterGI");
|
||||
await Launcher.LaunchUriAsync(betterGenshinImpactUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ internal sealed class LaunchExecutionStarwardPlayTimeStatisticsHandler : ILaunch
|
||||
{
|
||||
if (context.Options.UseStarwardPlayTimeStatistics)
|
||||
{
|
||||
context.Logger.LogInformation("Using starward to count game time");
|
||||
context.Logger.LogInformation("Using Starward to count game time");
|
||||
await LaunchStarwardForPlayTimeStatisticsAsync(context).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ internal sealed class LaunchExecutionStarwardPlayTimeStatisticsHandler : ILaunch
|
||||
Uri starwardPlayTimeUri = $"starward://playtime/{gameBiz}".ToUri();
|
||||
if (await Launcher.QueryUriSupportAsync(starwardPlayTimeUri, LaunchQuerySupportType.Uri) is LaunchQuerySupportStatus.Available)
|
||||
{
|
||||
context.Logger.LogInformation("Launching starward");
|
||||
context.Logger.LogInformation("Launching Starward");
|
||||
await Launcher.LaunchUriAsync(starwardPlayTimeUri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ internal sealed class LaunchExecutionUnlockFpsHandler : ILaunchExecutionDelegate
|
||||
|
||||
// The Unlocker can't unlock the process
|
||||
context.Process.Kill();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ internal sealed class LaunchExecutionInvoker
|
||||
handlers.Enqueue(new LaunchExecutionSetDiscordActivityHandler());
|
||||
handlers.Enqueue(new LaunchExecutionGameProcessStartHandler());
|
||||
handlers.Enqueue(new LaunchExecutionStarwardPlayTimeStatisticsHandler());
|
||||
handlers.Enqueue(new LaunchExecutionBetterGenshinImpactAutomationHandlder());
|
||||
handlers.Enqueue(new LaunchExecutionUnlockFpsHandler());
|
||||
handlers.Enqueue(new LaunchExecutionGameProcessExitHandler());
|
||||
}
|
||||
@@ -40,9 +41,9 @@ internal sealed class LaunchExecutionInvoker
|
||||
if (handlers.TryDequeue(out ILaunchExecutionDelegateHandler? handler))
|
||||
{
|
||||
string typeName = TypeNameHelper.GetTypeDisplayName(handler, false);
|
||||
context.Logger.LogInformation("Handler[{Handler}] begin execution", typeName);
|
||||
context.Logger.LogInformation("Handler [{Handler}] begin execution", typeName);
|
||||
await handler.OnExecutionAsync(context, () => InvokeHandlerAsync(context)).ConfigureAwait(false);
|
||||
context.Logger.LogInformation("Handler[{Handler}] end execution", typeName);
|
||||
context.Logger.LogInformation("Handler [{Handler}] end execution", typeName);
|
||||
}
|
||||
|
||||
return context;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
|
||||
namespace Snap.Hutao.Service.Inventroy;
|
||||
namespace Snap.Hutao.Service.Inventory;
|
||||
|
||||
internal interface IInventoryDbService
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Inventroy;
|
||||
namespace Snap.Hutao.Service.Inventory;
|
||||
|
||||
internal interface IInventoryService
|
||||
{
|
||||
@@ -6,7 +6,7 @@ using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
|
||||
namespace Snap.Hutao.Service.Inventroy;
|
||||
namespace Snap.Hutao.Service.Inventory;
|
||||
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton, typeof(IInventoryDbService))]
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Inventroy;
|
||||
namespace Snap.Hutao.Service.Inventory;
|
||||
|
||||
[Injection(InjectAs.Transient)]
|
||||
internal sealed class InventoryService : IInventoryService
|
||||
@@ -90,8 +90,6 @@ internal sealed partial class UserInitializationService : IUserInitializationSer
|
||||
|
||||
await userFingerprintService.TryInitializeAsync(user, token).ConfigureAwait(false);
|
||||
|
||||
// Should not raise propery changed event here
|
||||
user.SetSelectedUserGameRole(user.UserGameRoles.FirstOrFirstOrDefault(role => role.IsChosen), false);
|
||||
return user.IsInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -300,8 +300,8 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.0.240109" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.0.240109" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.1">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -316,7 +316,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.8.8" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.231219000" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.240211001" />
|
||||
<PackageReference Include="QRCoder" Version="1.4.3" />
|
||||
<PackageReference Include="Snap.Discord.GameSDK" Version="1.6.0" />
|
||||
<PackageReference Include="Snap.Hutao.Deployment.Runtime" Version="1.15.3">
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
xmlns:cwconv="using:CommunityToolkit.WinUI.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:shcb="using:Snap.Hutao.Control.Brush"
|
||||
xmlns:shch="using:Snap.Hutao.Control.Helper"
|
||||
xmlns:shci="using:Snap.Hutao.Control.Image"
|
||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||
@@ -221,101 +222,172 @@
|
||||
IsPredictPullAvailable="{Binding IsPredictPullAvailable}"
|
||||
SelectedIndex="0"/>
|
||||
<cwcont:SwitchPresenter
|
||||
Height="85"
|
||||
Height="96"
|
||||
Padding="0,12"
|
||||
Value="{x:Bind StatisticsSegmented.SelectedIndex, Mode=OneWay}">
|
||||
<cwcont:Case Value="{shcm:Int32 Value=0}">
|
||||
<StackPanel Spacing="2">
|
||||
<Grid>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardOrangeAveragePullText}"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding AverageOrangePullFormatted}"/>
|
||||
<Grid ColumnSpacing="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Grid.Column="0" Style="{ThemeResource BorderCardStyle}">
|
||||
<Viewbox Margin="8,0" StretchDirection="DownOnly">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextBlock
|
||||
Opacity="0.8"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardOrangeAveragePullText}"/>
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{Binding AverageOrangePullFormatted}"/>
|
||||
</StackPanel>
|
||||
</Viewbox>
|
||||
</Border>
|
||||
<Border Grid.Column="1" Style="{ThemeResource BorderCardStyle}">
|
||||
<Viewbox Margin="8,0" StretchDirection="DownOnly">
|
||||
<Grid>
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="{x:Bind ShowUpPull, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<TextBlock
|
||||
Opacity="0.8"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardUpAveragePullText}"/>
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{Binding AverageUpOrangePullFormatted}"/>
|
||||
</StackPanel>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="N/A"
|
||||
Visibility="{x:Bind ShowUpPull, Converter={StaticResource BoolToVisibilityRevertConverter}}"/>
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
</Border>
|
||||
|
||||
<Grid Grid.Column="2" RowSpacing="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.Row="0" Style="{ThemeResource BorderCardStyle}">
|
||||
<Viewbox Margin="8,0" StretchDirection="DownOnly">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding MaxOrangePullFormatted}"/>
|
||||
</Viewbox>
|
||||
</Border>
|
||||
<Border Grid.Row="1" Style="{ThemeResource BorderCardStyle}">
|
||||
<Viewbox Margin="8,0" StretchDirection="DownOnly">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding MinOrangePullFormatted}"/>
|
||||
</Viewbox>
|
||||
</Border>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<!-- 高度占位符 -->
|
||||
<TextBlock/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardUpAveragePullText}"
|
||||
Visibility="{x:Bind ShowUpPull, Converter={StaticResource BoolToVisibilityConverter}}"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding AverageUpOrangePullFormatted}"
|
||||
Visibility="{x:Bind ShowUpPull, Converter={StaticResource BoolToVisibilityConverter}}"/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding MaxOrangePullFormatted}"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding MinOrangePullFormatted}"/>
|
||||
</Grid>
|
||||
<Grid/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</cwcont:Case>
|
||||
<cwcont:Case Value="{shcm:Int32 Value=1}">
|
||||
<StackPanel Spacing="2">
|
||||
<Grid>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Foreground="{StaticResource OrangeColorBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardOrangeText}"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
FontFamily="{StaticResource CascadiaMonoAndMiSans}"
|
||||
Foreground="{StaticResource OrangeColorBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding TotalOrangeFormatted}"/>
|
||||
<Grid RowSpacing="4">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid ColumnSpacing="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.Column="0" Style="{ThemeResource GridCardStyle}">
|
||||
<StackPanel
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock
|
||||
Foreground="{StaticResource OrangeColorBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardOrangeText}"/>
|
||||
<Viewbox StretchDirection="DownOnly">
|
||||
<TextBlock Foreground="{StaticResource OrangeColorBrush}" Text="{Binding TotalOrangeFormatted}"/>
|
||||
</Viewbox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid Grid.Column="1" Style="{ThemeResource GridCardStyle}">
|
||||
<StackPanel
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock
|
||||
Foreground="{StaticResource PurpleColorBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardPurpleText}"/>
|
||||
<Viewbox StretchDirection="DownOnly">
|
||||
<TextBlock Foreground="{StaticResource PurpleColorBrush}" Text="{Binding TotalPurpleFormatted}"/>
|
||||
</Viewbox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid Grid.Column="2" Style="{ThemeResource GridCardStyle}">
|
||||
<StackPanel
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock
|
||||
Foreground="{StaticResource BlueColorBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardBlueText}"/>
|
||||
<Viewbox StretchDirection="DownOnly">
|
||||
<TextBlock Foreground="{StaticResource BlueColorBrush}" Text="{Binding TotalBlueFormatted}"/>
|
||||
</Viewbox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Foreground="{StaticResource PurpleColorBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardPurpleText}"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
FontFamily="{StaticResource CascadiaMonoAndMiSans}"
|
||||
Foreground="{StaticResource PurpleColorBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding TotalPurpleFormatted}"/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Foreground="{StaticResource BlueColorBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardBlueText}"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
FontFamily="{StaticResource CascadiaMonoAndMiSans}"
|
||||
Foreground="{StaticResource BlueColorBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding TotalBlueFormatted}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<shcb:SegmentedBar
|
||||
Grid.Row="1"
|
||||
Height="2"
|
||||
Margin="2,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Opacity="0.7"
|
||||
Source="{Binding PullPercentSegmentSource}"/>
|
||||
</Grid>
|
||||
|
||||
</cwcont:Case>
|
||||
<cwcont:Case Value="{shcm:Int32 Value=2}">
|
||||
<StackPanel Spacing="2">
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{Binding PredictedPullLeftToOrangeFormatted}"/>
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{Binding ProbabilityOfNextPullIsOrangeFormatted}"/>
|
||||
</StackPanel>
|
||||
<Grid RowSpacing="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.Row="0" Style="{ThemeResource BorderCardStyle}">
|
||||
<Viewbox StretchDirection="DownOnly">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding PredictedPullLeftToOrangeFormatted}"/>
|
||||
</Viewbox>
|
||||
</Border>
|
||||
<Border Grid.Row="1" Style="{ThemeResource BorderCardStyle}">
|
||||
<Viewbox StretchDirection="DownOnly">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding ProbabilityOfNextPullIsOrangeFormatted}"/>
|
||||
</Viewbox>
|
||||
</Border>
|
||||
</Grid>
|
||||
</cwcont:Case>
|
||||
</cwcont:SwitchPresenter>
|
||||
<MenuFlyoutSeparator Margin="-12,0"/>
|
||||
</StackPanel>
|
||||
|
||||
</Expander>
|
||||
|
||||
<cwcont:SwitchPresenter
|
||||
|
||||
@@ -149,20 +149,22 @@
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="AnnouncementPivotItemContentTemplate">
|
||||
<ItemsRepeater
|
||||
Margin="16,16,16,16"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemTemplate="{StaticResource AnnouncementTemplate}"
|
||||
ItemsSource="{Binding List}">
|
||||
<ItemsRepeater.Layout>
|
||||
<UniformGridLayout
|
||||
ItemsJustification="Start"
|
||||
ItemsStretch="Fill"
|
||||
MinColumnSpacing="12"
|
||||
MinItemWidth="300"
|
||||
MinRowSpacing="12"/>
|
||||
</ItemsRepeater.Layout>
|
||||
</ItemsRepeater>
|
||||
<ScrollViewer>
|
||||
<ItemsRepeater
|
||||
Margin="16,16,16,16"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemTemplate="{StaticResource AnnouncementTemplate}"
|
||||
ItemsSource="{Binding List}">
|
||||
<ItemsRepeater.Layout>
|
||||
<UniformGridLayout
|
||||
ItemsJustification="Start"
|
||||
ItemsStretch="Fill"
|
||||
MinColumnSpacing="12"
|
||||
MinItemWidth="300"
|
||||
MinRowSpacing="12"/>
|
||||
</ItemsRepeater.Layout>
|
||||
</ItemsRepeater>
|
||||
</ScrollViewer>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="HutaoAnnouncementTemplate">
|
||||
|
||||
@@ -346,6 +346,12 @@
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
||||
<ToggleSwitch MinWidth="{ThemeResource SettingsCardContentControlMinWidth}" IsOn="{Binding LaunchOptions.UseStarwardPlayTimeStatistics, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageLaunchGameBetterGIDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameBetterGIHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
||||
<ToggleSwitch MinWidth="{ThemeResource SettingsCardContentControlMinWidth}" IsOn="{Binding LaunchOptions.UseBetterGenshinImpactAutomation, Mode=TwoWay}"/>
|
||||
</cwc:SettingsCard>
|
||||
<cwc:SettingsCard
|
||||
Description="{shcm:ResourceString Name=ViewPageLaunchGameDiscordActivityDescription}"
|
||||
Header="{shcm:ResourceString Name=ViewPageLaunchGameDiscordActivityHeader}"
|
||||
|
||||
@@ -221,7 +221,7 @@
|
||||
<shct:DescriptionTextBlock
|
||||
Margin="0,8,0,16"
|
||||
Description="{Binding Description}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"/>
|
||||
TextStyle="{StaticResource CaptionTextBlockStyle}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -291,7 +291,7 @@
|
||||
ProfilePicture="{Binding UserInfo.AvatarUrl, Mode=OneWay}"/>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="10,0,0,0"
|
||||
Margin="12,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding UserInfo.Nickname}"/>
|
||||
<TextBlock
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Control.Brush;
|
||||
using Snap.Hutao.Control.Theme;
|
||||
|
||||
namespace Snap.Hutao.ViewModel.GachaLog;
|
||||
|
||||
@@ -83,6 +85,16 @@ internal sealed partial class TypedWishSummary : Wish
|
||||
get => $"{TotalBluePull} [{TotalBluePercent,6:p2}]";
|
||||
}
|
||||
|
||||
public ColorSegmentCollection PullPercentSegmentSource
|
||||
{
|
||||
get =>
|
||||
[
|
||||
new ColorSegment(KnownColors.Orange, TotalOrangePull),
|
||||
new ColorSegment(KnownColors.Purple, TotalPurplePull),
|
||||
new ColorSegment(KnownColors.Blue, TotalBluePull),
|
||||
];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 平均五星抽数
|
||||
/// </summary>
|
||||
|
||||
@@ -167,13 +167,13 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
|
||||
if (await dialog.ConfirmAsync(SH.ViewPageSettingIsAdvancedLaunchOptionsEnabledHeader).ConfigureAwait(true))
|
||||
{
|
||||
launchOptions.IsAdvancedLaunchOptionsEnabled = true;
|
||||
OnPropertyChanged(nameof(IsAllocConsoleDebugModeEnabled));
|
||||
OnPropertyChanged(nameof(IsAdvancedLaunchOptionsEnabled));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
launchOptions.IsAdvancedLaunchOptionsEnabled = false;
|
||||
OnPropertyChanged(nameof(IsAllocConsoleDebugModeEnabled));
|
||||
OnPropertyChanged(nameof(IsAdvancedLaunchOptionsEnabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using CommunityToolkit.Mvvm.Messaging;
|
||||
using Snap.Hutao.Core.Abstraction;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
using Snap.Hutao.Web.Hoyolab.Bbs.User;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
@@ -20,18 +21,14 @@ namespace Snap.Hutao.ViewModel.User;
|
||||
internal sealed class User : ObservableObject, IEntityOnly<EntityUser>, IMappingFrom<User, EntityUser, IServiceProvider>, ISelectable
|
||||
{
|
||||
private readonly EntityUser inner;
|
||||
private readonly IMessenger messenger;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
private UserGameRole? selectedUserGameRole;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的绑定视图用户
|
||||
/// </summary>
|
||||
/// <param name="user">用户实体</param>
|
||||
private User(EntityUser user, IServiceProvider serviceProvider)
|
||||
{
|
||||
inner = user;
|
||||
messenger = serviceProvider.GetRequiredService<IMessenger>();
|
||||
this.serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public bool IsInitialized { get; set; }
|
||||
@@ -99,6 +96,8 @@ internal sealed class User : ObservableObject, IEntityOnly<EntityUser>, IMapping
|
||||
|
||||
public bool NeedDbUpdateAfterResume { get; set; }
|
||||
|
||||
public string? PreferredUid { get => inner.PreferredUid; }
|
||||
|
||||
public static User From(EntityUser user, IServiceProvider provider)
|
||||
{
|
||||
return new(user, provider);
|
||||
@@ -106,9 +105,21 @@ internal sealed class User : ObservableObject, IEntityOnly<EntityUser>, IMapping
|
||||
|
||||
public void SetSelectedUserGameRole(UserGameRole? value, bool raiseMessage = true)
|
||||
{
|
||||
if (SetProperty(ref selectedUserGameRole, value, nameof(SelectedUserGameRole)) && raiseMessage)
|
||||
if (SetProperty(ref selectedUserGameRole, value, nameof(SelectedUserGameRole)))
|
||||
{
|
||||
messenger.Send(Message.UserChangedMessage.CreateOnlyRoleChanged(this));
|
||||
if (value is not null && inner.PreferredUid != value.GameUid)
|
||||
{
|
||||
inner.PreferredUid = value.GameUid;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
scope.ServiceProvider.GetRequiredService<AppDbContext>().Users.UpdateAndSave(inner);
|
||||
}
|
||||
}
|
||||
|
||||
if (raiseMessage)
|
||||
{
|
||||
serviceProvider.GetRequiredService<IMessenger>().Send(Message.UserChangedMessage.CreateOnlyRoleChanged(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,10 +53,18 @@ internal sealed partial class UserViewModel : ObservableObject
|
||||
get => selectedUser ??= userService.Current;
|
||||
set
|
||||
{
|
||||
if (value is { SelectedUserGameRole: null })
|
||||
if (value is not null)
|
||||
{
|
||||
// Pre select the chosen role to avoid multiple UserChangedMessage
|
||||
value.SetSelectedUserGameRole(value.UserGameRoles.FirstOrFirstOrDefault(role => role.IsChosen), false);
|
||||
// Should not raise propery changed event below
|
||||
if (value.PreferredUid is not null)
|
||||
{
|
||||
value.SetSelectedUserGameRole(value.UserGameRoles.FirstOrDefault(role => role.GameUid == value.PreferredUid), false);
|
||||
}
|
||||
|
||||
if (value.SelectedUserGameRole is null)
|
||||
{
|
||||
value.SetSelectedUserGameRole(value.UserGameRoles.FirstOrFirstOrDefault(role => role.IsChosen), false);
|
||||
}
|
||||
}
|
||||
|
||||
if (SetProperty(ref selectedUser, value))
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Web;
|
||||
|
||||
namespace Snap.Hutao.Web.Hutao.Algolia;
|
||||
|
||||
internal sealed class AlgoliaHierarchy
|
||||
@@ -35,42 +37,42 @@ internal sealed class AlgoliaHierarchy
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return Lvl1;
|
||||
yield return HttpUtility.HtmlDecode(Lvl1);
|
||||
|
||||
if (string.IsNullOrEmpty(Lvl2))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return Lvl2;
|
||||
yield return HttpUtility.HtmlDecode(Lvl2);
|
||||
|
||||
if (string.IsNullOrEmpty(Lvl3))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return Lvl3;
|
||||
yield return HttpUtility.HtmlDecode(Lvl3);
|
||||
|
||||
if (string.IsNullOrEmpty(Lvl4))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return Lvl4;
|
||||
yield return HttpUtility.HtmlDecode(Lvl4);
|
||||
|
||||
if (string.IsNullOrEmpty(Lvl5))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return Lvl5;
|
||||
yield return HttpUtility.HtmlDecode(Lvl5);
|
||||
|
||||
if (string.IsNullOrEmpty(Lvl6))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return Lvl6;
|
||||
yield return HttpUtility.HtmlDecode(Lvl6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||
using Snap.Hutao.Web.Request.Builder;
|
||||
using Snap.Hutao.Web.Request.Builder.Abstraction;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Snap.Hutao.Web.Hutao.Wallpaper;
|
||||
|
||||
[HttpClient(HttpClientConfiguration.Default)]
|
||||
[ConstructorGenerated(ResolveHttpClient = true)]
|
||||
internal sealed partial class HutaoWallpaperClient
|
||||
{
|
||||
private readonly IHttpRequestMessageBuilderFactory httpRequestMessageBuilderFactory;
|
||||
private readonly ILogger<HutaoWallpaperClient> logger;
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
public ValueTask<Response<Wallpaper>> GetBingWallpaperAsync(CancellationToken token = default)
|
||||
{
|
||||
return GetWallpaperAsync(HutaoEndpoints.WallpaperBing, token);
|
||||
}
|
||||
|
||||
public ValueTask<Response<Wallpaper>> GetLauncherWallpaperAsync(CancellationToken token = default)
|
||||
{
|
||||
return GetWallpaperAsync(HutaoEndpoints.WallpaperGenshinLauncher, token);
|
||||
}
|
||||
|
||||
public ValueTask<Response<Wallpaper>> GetTodayWallpaperAsync(CancellationToken token = default)
|
||||
{
|
||||
return GetWallpaperAsync(HutaoEndpoints.WallpaperToday, token);
|
||||
}
|
||||
|
||||
private async ValueTask<Response<Wallpaper>> GetWallpaperAsync(string url, CancellationToken token = default)
|
||||
{
|
||||
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
|
||||
.SetRequestUri(url)
|
||||
.Get();
|
||||
|
||||
Response<Wallpaper>? resp = await builder.TryCatchSendAsync<Response<Wallpaper>>(httpClient, logger, token).ConfigureAwait(false);
|
||||
return Web.Response.Response.DefaultIfNull(resp);
|
||||
}
|
||||
}
|
||||
19
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Wallpaper/Wallpaper.cs
Normal file
19
src/Snap.Hutao/Snap.Hutao/Web/Hutao/Wallpaper/Wallpaper.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Web.Hutao.Wallpaper;
|
||||
|
||||
internal sealed class Wallpaper
|
||||
{
|
||||
[JsonPropertyName("url")]
|
||||
public string Url { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("source_url")]
|
||||
public string SourceUrl { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("author")]
|
||||
public string Author { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("uploader")]
|
||||
public string Uploader { get; set; } = default!;
|
||||
}
|
||||
@@ -10,8 +10,9 @@ namespace Snap.Hutao.Web;
|
||||
/// 胡桃 API 端点
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[SuppressMessage("", "SA1201")]
|
||||
[SuppressMessage("", "SA1124")]
|
||||
[SuppressMessage("", "SA1201")]
|
||||
[SuppressMessage("", "SA1203")]
|
||||
internal static class HutaoEndpoints
|
||||
{
|
||||
#region HomaAPI
|
||||
@@ -271,6 +272,15 @@ internal static class HutaoEndpoints
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Wallpaper
|
||||
|
||||
public const string WallpaperBing = $"{ApiSnapGenshin}/wallpaper/bing";
|
||||
|
||||
public const string WallpaperGenshinLauncher = $"{ApiSnapGenshin}/wallpaper/genshin-launcher";
|
||||
|
||||
public const string WallpaperToday = $"{ApiSnapGenshin}/wallpaper/today";
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
private const string ApiSnapGenshin = "https://api.snapgenshin.com";
|
||||
|
||||
Reference in New Issue
Block a user