mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9205d51cd5 | ||
|
|
a66a0b8a23 | ||
|
|
17c53dce4c | ||
|
|
5049aa9cb6 | ||
|
|
aac1e62fd2 | ||
|
|
5c1f861956 | ||
|
|
9667917559 | ||
|
|
9a3183e917 |
65
.github/ISSUE_TEMPLATE/artifact-rating-rules.yml
vendored
Normal file
65
.github/ISSUE_TEMPLATE/artifact-rating-rules.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
name: Artifact Rating Rules
|
||||||
|
description: 圣遗物评分细则建议
|
||||||
|
title: "[Artifact Rating] 请在这里填写角色名称"
|
||||||
|
labels: area-AvatarInfo
|
||||||
|
assignees: Lightczx
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
请按下方的要求填写完整的问题表单
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: your-suggested-rule
|
||||||
|
attributes:
|
||||||
|
label: 评分细则
|
||||||
|
description: |
|
||||||
|
请修改下方表格中的**角色名称**和**各属性权重**,并在表格后添加合适的说明
|
||||||
|
你可以点击预览按钮(preview)来查看表格最终会显示出的内容
|
||||||
|
value: |
|
||||||
|
|项目|评分权重(0-100)|
|
||||||
|
|-----|-----|
|
||||||
|
|角色名称| 旅行者 |
|
||||||
|
|生命值| 10 |
|
||||||
|
|攻击力| 10 |
|
||||||
|
|防御力| 10 |
|
||||||
|
|暴击率| 10 |
|
||||||
|
|暴击伤害| 10 |
|
||||||
|
|元素精通| 10 |
|
||||||
|
|充能效率| 10 |
|
||||||
|
|治疗加成| 10 |
|
||||||
|
|元素伤害| 10 |
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: no-duplicated-dropdown
|
||||||
|
attributes:
|
||||||
|
label: 我确认当前没有其它的该角色的圣遗物评分细则建议
|
||||||
|
description: 如果有,你应该在已有的工单内回复以提出你的建议
|
||||||
|
options:
|
||||||
|
- 否
|
||||||
|
- 是
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: title-filled-dropdown
|
||||||
|
attributes:
|
||||||
|
label: 我确认已设置合适的标题
|
||||||
|
options:
|
||||||
|
- 否
|
||||||
|
- 是
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: all-filled-dropdown
|
||||||
|
attributes:
|
||||||
|
label: 我确认已完整填写表格
|
||||||
|
options:
|
||||||
|
- 否
|
||||||
|
- 是
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
22
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
22
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -54,18 +54,18 @@ body:
|
|||||||
如果应用程序崩溃了,请将`log.db` 文件上传,文件包含了敏感信息,谨慎上传
|
如果应用程序崩溃了,请将`log.db` 文件上传,文件包含了敏感信息,谨慎上传
|
||||||
如果这个表单是关于导入祈愿记录的问题,请包含你导入的`Json`文件
|
如果这个表单是关于导入祈愿记录的问题,请包含你导入的`Json`文件
|
||||||
**务必不要上传`user.db`文件,该文件包含你的帐号敏感信息**
|
**务必不要上传`user.db`文件,该文件包含你的帐号敏感信息**
|
||||||
render: shell
|
|
||||||
|
|
||||||
- type: checkboxes
|
- type: dropdown
|
||||||
id: confirm-issue
|
id: confirm-issue
|
||||||
attributes:
|
attributes:
|
||||||
label: 我确认已在表单中附上了充足的补充说明以帮助开发人员确定问题
|
label: 我确认已在表单中附上了充足的补充说明以帮助开发人员确定问题
|
||||||
description: 补充说明包括但不限于:日志文件、抛出的错误信息、截图和录屏
|
description: 补充说明包括但不限于:日志文件、抛出的错误信息、截图和录屏
|
||||||
options:
|
options:
|
||||||
- label: 是
|
- 是
|
||||||
required: true
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
- type: checkboxes
|
- type: dropdown
|
||||||
id: confirm-no-duplicated-issue
|
id: confirm-no-duplicated-issue
|
||||||
attributes:
|
attributes:
|
||||||
label: 我确认没有他人提出相同或类似的问题
|
label: 我确认没有他人提出相同或类似的问题
|
||||||
@@ -75,15 +75,17 @@ body:
|
|||||||
你应该在原始 Issue 中通过回复添加有助于解决问题的信息,而不是创建重复的问题;
|
你应该在原始 Issue 中通过回复添加有助于解决问题的信息,而不是创建重复的问题;
|
||||||
**没有帮助的重复问题可能会被直接关闭**
|
**没有帮助的重复问题可能会被直接关闭**
|
||||||
options:
|
options:
|
||||||
- label: 是
|
- 是
|
||||||
required: true
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
- type: checkboxes
|
- type: dropdown
|
||||||
id: confirm-docs
|
id: confirm-docs
|
||||||
attributes:
|
attributes:
|
||||||
label: 我确认该问题没有在文档中解释
|
label: 我确认该问题没有在文档中解释
|
||||||
description: Snap Hutao 官方文档:[https://hut.ao](https://hut.ao)
|
description: Snap Hutao 官方文档:[https://hut.ao](https://hut.ao)
|
||||||
options:
|
options:
|
||||||
- label: 是
|
- 是
|
||||||
required: true
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net6.0-windows10.0.17763.0</TargetFrameworks>
|
<TargetFrameworks>net7.0-windows10.0.18362.0</TargetFrameworks>
|
||||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
<TargetPlatformMinVersion>10.0.18362.0</TargetPlatformMinVersion>
|
||||||
<RootNamespace>SettingsUI</RootNamespace>
|
<RootNamespace>SettingsUI</RootNamespace>
|
||||||
<Platforms>x64</Platforms>
|
<Platforms>x64</Platforms>
|
||||||
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.1.4" />
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221109.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ internal class Program
|
|||||||
|
|
||||||
public static async Task Main(string[] args)
|
public static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
|
_ = args;
|
||||||
string ps1File = Path.Combine(AppContext.BaseDirectory, "Install.ps1");
|
string ps1File = Path.Combine(AppContext.BaseDirectory, "Install.ps1");
|
||||||
|
|
||||||
if (!File.Exists(ps1File))
|
if (!File.Exists(ps1File))
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net6.0-windows</TargetFramework>
|
<TargetFramework>net7.0-windows</TargetFramework>
|
||||||
<ImplicitUsings>disable</ImplicitUsings>
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<PublishTrimmed>true</PublishTrimmed>
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
<DebugType>embedded</DebugType>
|
<DebugType>embedded</DebugType>
|
||||||
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
|
|
||||||
namespace Snap.Hutao.Core.DependencyInjection;
|
namespace Snap.Hutao.Core.DependencyInjection;
|
||||||
|
|
||||||
internal static partial class ServiceCollectionExtensions
|
internal static partial class ServiceCollectionExtension
|
||||||
{{
|
{{
|
||||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute(""{toolName}"",""1.0.0.0"")]
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute(""{toolName}"",""1.0.0.0"")]
|
||||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
@@ -65,7 +65,7 @@ internal static partial class ServiceCollectionExtensions
|
|||||||
}
|
}
|
||||||
}");
|
}");
|
||||||
|
|
||||||
context.AddSource("ServiceCollectionExtensions.g.cs", SourceText.From(sourceCodeBuilder.ToString(), Encoding.UTF8));
|
context.AddSource("ServiceCollectionExtension.g.cs", SourceText.From(sourceCodeBuilder.ToString(), Encoding.UTF8));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void FillWithInjectionServices(InjectionSyntaxContextReceiver receiver, StringBuilder sourceCodeBuilder)
|
private static void FillWithInjectionServices(InjectionSyntaxContextReceiver receiver, StringBuilder sourceCodeBuilder)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SettingsUI", "SettingsUI\Se
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.SourceGeneration", "Snap.Hutao.SourceGeneration\Snap.Hutao.SourceGeneration.csproj", "{8B96721E-5604-47D2-9B72-06FEBAD0CE00}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.SourceGeneration", "Snap.Hutao.SourceGeneration\Snap.Hutao.SourceGeneration.csproj", "{8B96721E-5604-47D2-9B72-06FEBAD0CE00}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Snap.Hutao.Installer", "Snap.Hutao.Installer\Snap.Hutao.Installer.csproj", "{CEC01691-F65E-4874-9AE2-F571369A7631}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.Installer", "Snap.Hutao.Installer\Snap.Hutao.Installer.csproj", "{CEC01691-F65E-4874-9AE2-F571369A7631}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -88,8 +88,8 @@ Global
|
|||||||
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|arm64.ActiveCfg = Debug|Any CPU
|
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|arm64.ActiveCfg = Debug|Any CPU
|
||||||
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|arm64.Build.0 = Debug|Any CPU
|
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|arm64.Build.0 = Debug|Any CPU
|
||||||
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|x64.ActiveCfg = Debug|Any CPU
|
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|x64.Build.0 = Debug|Any CPU
|
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|x64.Build.0 = Debug|x64
|
||||||
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|x86.ActiveCfg = Debug|Any CPU
|
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|x86.Build.0 = Debug|Any CPU
|
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
{CEC01691-F65E-4874-9AE2-F571369A7631}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{CEC01691-F65E-4874-9AE2-F571369A7631}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
|||||||
31
src/Snap.Hutao/Snap.Hutao/.filenesting.json
Normal file
31
src/Snap.Hutao/Snap.Hutao/.filenesting.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"help": "https://go.microsoft.com/fwlink/?linkid=866610",
|
||||||
|
"dependentFileProviders": {
|
||||||
|
"add": {
|
||||||
|
"extensionToExtension": {
|
||||||
|
"add": {
|
||||||
|
".json": [ ".txt" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pathSegment": {
|
||||||
|
"add": {
|
||||||
|
".*": [ ".cs" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fileSuffixToExtension": {
|
||||||
|
"add": {
|
||||||
|
"DesignTimeFactory.cs": [".cs"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fileToFile": {
|
||||||
|
"add": {
|
||||||
|
"app.manifest": [ "App.xaml.cs" ],
|
||||||
|
"Package.appxmanifest": [ "App.xaml.cs" ],
|
||||||
|
"GlobalUsing.cs": [ "Program.cs" ],
|
||||||
|
".filenesting.json": [ "Program.cs" ],
|
||||||
|
".editorconfig": [ "Program.cs" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using CommunityToolkit.WinUI.Notifications;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.Windows.AppLifecycle;
|
using Microsoft.Windows.AppLifecycle;
|
||||||
using Snap.Hutao.Core;
|
using Snap.Hutao.Core;
|
||||||
using Snap.Hutao.Core.Exception;
|
using Snap.Hutao.Core.Exception;
|
||||||
using Snap.Hutao.Core.LifeCycle;
|
using Snap.Hutao.Core.LifeCycle;
|
||||||
using Snap.Hutao.Core.Logging;
|
using Snap.Hutao.Core.Logging;
|
||||||
using Snap.Hutao.Extension;
|
|
||||||
using Snap.Hutao.Service.Metadata;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Windows.Storage;
|
using Windows.Storage;
|
||||||
|
|
||||||
@@ -48,17 +47,12 @@ public partial class App : Application
|
|||||||
// manually invoke
|
// manually invoke
|
||||||
Activation.Activate(firstInstance, activatedEventArgs);
|
Activation.Activate(firstInstance, activatedEventArgs);
|
||||||
firstInstance.Activated += Activation.Activate;
|
firstInstance.Activated += Activation.Activate;
|
||||||
|
ToastNotificationManagerCompat.OnActivated += Activation.NotificationActivate;
|
||||||
|
|
||||||
logger.LogInformation(EventIds.CommonLog, "Snap Hutao : {version}", CoreEnvironment.Version);
|
logger.LogInformation(EventIds.CommonLog, "Snap Hutao | {name} : {version}", CoreEnvironment.FamilyName, CoreEnvironment.Version);
|
||||||
logger.LogInformation(EventIds.CommonLog, "Cache folder : {folder}", ApplicationData.Current.TemporaryFolder.Path);
|
logger.LogInformation(EventIds.CommonLog, "Cache folder : {folder}", ApplicationData.Current.TemporaryFolder.Path);
|
||||||
|
|
||||||
JumpListHelper.ConfigAsync().SafeForget(logger);
|
JumpListHelper.ConfigAsync().SafeForget(logger);
|
||||||
|
|
||||||
Ioc.Default
|
|
||||||
.GetRequiredService<IMetadataService>()
|
|
||||||
.ImplictAs<IMetadataInitializer>()?
|
|
||||||
.InitializeInternalAsync()
|
|
||||||
.SafeForget(logger);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
BIN
src/Snap.Hutao/Snap.Hutao/Assets/Logo.ico
Normal file
BIN
src/Snap.Hutao/Snap.Hutao/Assets/Logo.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
@@ -61,6 +61,11 @@ public class AppDbContext : DbContext
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DbSet<GameAccount> GameAccounts { get; set; } = default!;
|
public DbSet<GameAccount> GameAccounts { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实时便笺
|
||||||
|
/// </summary>
|
||||||
|
public DbSet<DailyNoteEntry> DailyNotes { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个临时的应用程序数据库上下文
|
/// 构造一个临时的应用程序数据库上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -76,6 +81,7 @@ public class AppDbContext : DbContext
|
|||||||
{
|
{
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.ApplyConfiguration(new AvatarInfoConfiguration())
|
.ApplyConfiguration(new AvatarInfoConfiguration())
|
||||||
.ApplyConfiguration(new UserConfiguration());
|
.ApplyConfiguration(new UserConfiguration())
|
||||||
|
.ApplyConfiguration(new DailyNoteEntryConfiguration());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.Extension;
|
namespace Snap.Hutao.Control.Extension;
|
||||||
|
|
||||||
@@ -37,7 +36,7 @@ internal static class ContentDialogExtensions
|
|||||||
return new ContentDialogHider(contentDialog);
|
return new ContentDialogHider(contentDialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct ContentDialogHider : IAsyncDisposable
|
private readonly struct ContentDialogHider : IAsyncDisposable
|
||||||
{
|
{
|
||||||
private readonly ContentDialog contentDialog;
|
private readonly ContentDialog contentDialog;
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ using Microsoft.UI.Xaml.Hosting;
|
|||||||
using Microsoft.UI.Xaml.Media;
|
using Microsoft.UI.Xaml.Media;
|
||||||
using Snap.Hutao.Core;
|
using Snap.Hutao.Core;
|
||||||
using Snap.Hutao.Core.Caching;
|
using Snap.Hutao.Core.Caching;
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Snap.Hutao.Extension;
|
using Snap.Hutao.Extension;
|
||||||
using Snap.Hutao.Service.Abstraction;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -67,7 +66,7 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
|||||||
/// <returns>加载的图像表面</returns>
|
/// <returns>加载的图像表面</returns>
|
||||||
protected virtual async Task<LoadedImageSurface> LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
|
protected virtual async Task<LoadedImageSurface> LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
|
||||||
{
|
{
|
||||||
using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token))
|
using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token).ConfigureAwait(true))
|
||||||
{
|
{
|
||||||
return LoadedImageSurface.StartLoadFromStream(imageStream);
|
return LoadedImageSurface.StartLoadFromStream(imageStream);
|
||||||
}
|
}
|
||||||
@@ -119,7 +118,7 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
|||||||
|
|
||||||
private async Task ApplyImageInternalAsync(Uri? uri, CancellationToken token)
|
private async Task ApplyImageInternalAsync(Uri? uri, CancellationToken token)
|
||||||
{
|
{
|
||||||
await HideAsync(token);
|
await HideAsync(token).ConfigureAwait(true);
|
||||||
|
|
||||||
LoadedImageSurface? imageSurface = null;
|
LoadedImageSurface? imageSurface = null;
|
||||||
Compositor compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
|
Compositor compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
|
||||||
@@ -132,15 +131,15 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StorageFile storageFile = await imageCache.GetFileFromCacheAsync(uri);
|
StorageFile storageFile = await imageCache.GetFileFromCacheAsync(uri).ConfigureAwait(true);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
imageSurface = await LoadImageSurfaceAsync(storageFile, token);
|
imageSurface = await LoadImageSurfaceAsync(storageFile, token).ConfigureAwait(true);
|
||||||
}
|
}
|
||||||
catch (COMException)
|
catch (COMException)
|
||||||
{
|
{
|
||||||
await imageCache.RemoveAsync(uri.Enumerate());
|
await imageCache.RemoveAsync(uri.Enumerate()).ConfigureAwait(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +149,7 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
|||||||
OnUpdateVisual(spriteVisual);
|
OnUpdateVisual(spriteVisual);
|
||||||
|
|
||||||
ElementCompositionPreview.SetElementChildVisual(this, spriteVisual);
|
ElementCompositionPreview.SetElementChildVisual(this, spriteVisual);
|
||||||
await ShowAsync(token);
|
await ShowAsync(token).ConfigureAwait(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +158,7 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
|||||||
{
|
{
|
||||||
if (!isShow)
|
if (!isShow)
|
||||||
{
|
{
|
||||||
await AnimationBuilder.Create().Opacity(1d).StartAsync(this, token);
|
await AnimationBuilder.Create().Opacity(1d).StartAsync(this, token).ConfigureAwait(true);
|
||||||
isShow = true;
|
isShow = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,7 +167,7 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
|||||||
{
|
{
|
||||||
if (isShow)
|
if (isShow)
|
||||||
{
|
{
|
||||||
await AnimationBuilder.Create().Opacity(0d).StartAsync(this, token);
|
await AnimationBuilder.Create().Opacity(0d).StartAsync(this, token).ConfigureAwait(true);
|
||||||
isShow = false;
|
isShow = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ public class Gradient : CompositionImage
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override async Task<LoadedImageSurface> LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
|
protected override async Task<LoadedImageSurface> LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
|
||||||
{
|
{
|
||||||
using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token))
|
using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token).ConfigureAwait(true))
|
||||||
{
|
{
|
||||||
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream).AsTask(token);
|
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream).AsTask(token).ConfigureAwait(true);
|
||||||
imageAspectRatio = decoder.PixelWidth / (double)decoder.PixelHeight;
|
imageAspectRatio = decoder.PixelWidth / (double)decoder.PixelHeight;
|
||||||
|
|
||||||
return LoadedImageSurface.StartLoadFromStream(imageStream);
|
return LoadedImageSurface.StartLoadFromStream(imageStream);
|
||||||
|
|||||||
@@ -11,10 +11,8 @@ namespace Snap.Hutao.Control;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表示支持取消加载的异步页面
|
/// 表示支持取消加载的异步页面
|
||||||
/// 在被导航到其他页面前触发取消异步通知
|
/// 在被导航到其他页面前触发取消异步通知
|
||||||
/// <para/>
|
|
||||||
/// InitializeWith{T}();
|
|
||||||
/// InitializeComponent();
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SuppressMessage("", "CA1001")]
|
||||||
public class ScopedPage : Page
|
public class ScopedPage : Page
|
||||||
{
|
{
|
||||||
private readonly CancellationTokenSource viewLoadingCancellationTokenSource = new();
|
private readonly CancellationTokenSource viewLoadingCancellationTokenSource = new();
|
||||||
@@ -26,6 +24,7 @@ public class ScopedPage : Page
|
|||||||
public ScopedPage()
|
public ScopedPage()
|
||||||
{
|
{
|
||||||
serviceScope = Ioc.Default.CreateScope();
|
serviceScope = Ioc.Default.CreateScope();
|
||||||
|
serviceScope.Track();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -64,6 +63,7 @@ public class ScopedPage : Page
|
|||||||
|
|
||||||
// Try dispose scope when page is not presented
|
// Try dispose scope when page is not presented
|
||||||
serviceScope.Dispose();
|
serviceScope.Dispose();
|
||||||
|
viewLoadingCancellationTokenSource.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Logging;
|
using Snap.Hutao.Core.Logging;
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
@@ -18,6 +17,7 @@ namespace Snap.Hutao.Core.Caching;
|
|||||||
/// 经过简化
|
/// 经过简化
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Generic type as supplied by consumer of the class</typeparam>
|
/// <typeparam name="T">Generic type as supplied by consumer of the class</typeparam>
|
||||||
|
[SuppressMessage("", "CA1001")]
|
||||||
public abstract class CacheBase<T>
|
public abstract class CacheBase<T>
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,9 +2,8 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
using Snap.Hutao.Core.Convert;
|
||||||
using Snap.Hutao.Extension;
|
using Snap.Hutao.Extension;
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using Windows.ApplicationModel;
|
using Windows.ApplicationModel;
|
||||||
|
|
||||||
@@ -15,12 +14,12 @@ namespace Snap.Hutao.Core;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class CoreEnvironment
|
internal static class CoreEnvironment
|
||||||
{
|
{
|
||||||
// 计算过程:https://gist.github.com/Lightczx/373c5940b36e24b25362728b52dec4fd
|
// 计算过程:https://github.com/UIGF-org/Hoyolab.Salt
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 动态密钥1的盐
|
/// 动态密钥1的盐
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string DynamicSecret1Salt = "yUZ3s0Sna1IrSNfk29Vo6vRapdOyqyhB";
|
public const string DynamicSecret1Salt = "jEpJb9rRARU2rXDA9qYbZ3selxkuct9a";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 动态密钥2的盐
|
/// 动态密钥2的盐
|
||||||
@@ -35,7 +34,7 @@ internal static class CoreEnvironment
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 米游社 Rpc 版本
|
/// 米游社 Rpc 版本
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string HoyolabXrpcVersion = "2.38.1";
|
public const string HoyolabXrpcVersion = "2.40.1";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 标准UA
|
/// 标准UA
|
||||||
@@ -57,6 +56,11 @@ internal static class CoreEnvironment
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly string HutaoDeviceId;
|
public static readonly string HutaoDeviceId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 包家族名称
|
||||||
|
/// </summary>
|
||||||
|
public static readonly string FamilyName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 默认的Json序列化选项
|
/// 默认的Json序列化选项
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -74,6 +78,7 @@ internal static class CoreEnvironment
|
|||||||
static CoreEnvironment()
|
static CoreEnvironment()
|
||||||
{
|
{
|
||||||
Version = Package.Current.Id.Version.ToVersion();
|
Version = Package.Current.Id.Version.ToVersion();
|
||||||
|
FamilyName = Package.Current.Id.FamilyName;
|
||||||
CommonUA = $"Snap Hutao/{Version}";
|
CommonUA = $"Snap Hutao/{Version}";
|
||||||
|
|
||||||
// simply assign a random guid
|
// simply assign a random guid
|
||||||
@@ -85,8 +90,6 @@ internal static class CoreEnvironment
|
|||||||
{
|
{
|
||||||
string userName = Environment.UserName;
|
string userName = Environment.UserName;
|
||||||
object? machineGuid = Registry.GetValue(CryptographyKey, MachineGuidValue, userName);
|
object? machineGuid = Registry.GetValue(CryptographyKey, MachineGuidValue, userName);
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes($"{userName}{machineGuid}");
|
return Md5Convert.ToHexString($"{userName}{machineGuid}");
|
||||||
byte[] hash = MD5.Create().ComputeHash(bytes);
|
|
||||||
return System.Convert.ToHexString(hash);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Snap.Hutao.Model.Entity;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.Database;
|
namespace Snap.Hutao.Core.Database;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace Snap.Hutao.Core.DependencyInjection;
|
|||||||
/// 服务管理器
|
/// 服务管理器
|
||||||
/// 依赖注入的核心管理类
|
/// 依赖注入的核心管理类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static partial class ServiceCollectionExtensions
|
internal static partial class ServiceCollectionExtension
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 向容器注册服务
|
/// 向容器注册服务
|
||||||
@@ -17,4 +17,4 @@ internal static partial class ServiceCollectionExtensions
|
|||||||
/// <param name="services">容器</param>
|
/// <param name="services">容器</param>
|
||||||
/// <returns>可继续操作的服务集合</returns>
|
/// <returns>可继续操作的服务集合</returns>
|
||||||
public static partial IServiceCollection AddInjections(this IServiceCollection services);
|
public static partial IServiceCollection AddInjections(this IServiceCollection services);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.DependencyInjection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 服务范围扩展
|
||||||
|
/// </summary>
|
||||||
|
public static class ServiceScopeExtension
|
||||||
|
{
|
||||||
|
private static IServiceScope? scopeReference;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 追踪服务范围
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scope">范围</param>
|
||||||
|
public static void Track(this IServiceScope scope)
|
||||||
|
{
|
||||||
|
DisposeLast();
|
||||||
|
scopeReference = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 释放上个范围
|
||||||
|
/// </summary>
|
||||||
|
public static void DisposeLast()
|
||||||
|
{
|
||||||
|
scopeReference?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ namespace Snap.Hutao.Core.Diagnostics;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 值类型的<see cref="Stopwatch"/>
|
/// 值类型的<see cref="Stopwatch"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal struct ValueStopwatch
|
internal readonly struct ValueStopwatch
|
||||||
{
|
{
|
||||||
private static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
|
private static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Windows.ApplicationModel.DataTransfer;
|
using Windows.ApplicationModel.DataTransfer;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.IO.DataTransfer;
|
namespace Snap.Hutao.Core.IO.DataTransfer;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Windows.Storage;
|
using Windows.Storage;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using CommunityToolkit.WinUI.Notifications;
|
||||||
using Microsoft.Windows.AppLifecycle;
|
using Microsoft.Windows.AppLifecycle;
|
||||||
using Snap.Hutao.Core.Threading;
|
using Snap.Hutao.Extension;
|
||||||
using Snap.Hutao.Service.Abstraction;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
|
using Snap.Hutao.Service.DailyNote;
|
||||||
|
using Snap.Hutao.Service.Metadata;
|
||||||
using Snap.Hutao.Service.Navigation;
|
using Snap.Hutao.Service.Navigation;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
|
|
||||||
@@ -43,7 +46,29 @@ internal static class Activation
|
|||||||
public static void Activate(object? sender, AppActivationArguments args)
|
public static void Activate(object? sender, AppActivationArguments args)
|
||||||
{
|
{
|
||||||
_ = sender;
|
_ = sender;
|
||||||
HandleActivationAsync(args).SafeForget();
|
if (!ToastNotificationManagerCompat.WasCurrentProcessToastActivated())
|
||||||
|
{
|
||||||
|
HandleActivationAsync(args).SafeForget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 响应通知激活事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">参数</param>
|
||||||
|
public static void NotificationActivate(ToastNotificationActivatedEventArgsCompat args)
|
||||||
|
{
|
||||||
|
ToastArguments toastArgs = ToastArguments.Parse(args.Argument);
|
||||||
|
_ = toastArgs;
|
||||||
|
|
||||||
|
if (toastArgs.TryGetValue("Action", out string? action))
|
||||||
|
{
|
||||||
|
if (action == LaunchGame)
|
||||||
|
{
|
||||||
|
_ = toastArgs.TryGetValue("Uid", out string? uid);
|
||||||
|
HandleLaunchGameActionAsync(uid).SafeForget();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -63,43 +88,6 @@ internal static class Activation
|
|||||||
|
|
||||||
private static async Task HandleActivationCoreAsync(AppActivationArguments args)
|
private static async Task HandleActivationCoreAsync(AppActivationArguments args)
|
||||||
{
|
{
|
||||||
string argument = string.Empty;
|
|
||||||
|
|
||||||
if (args.Kind == ExtendedActivationKind.Launch)
|
|
||||||
{
|
|
||||||
if (args.TryGetLaunchActivatedArgument(out string? arguments))
|
|
||||||
{
|
|
||||||
argument = arguments;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (argument)
|
|
||||||
{
|
|
||||||
case "":
|
|
||||||
{
|
|
||||||
_ = Ioc.Default.GetRequiredService<MainWindow>();
|
|
||||||
await Ioc.Default.GetRequiredService<IInfoBarService>().WaitInitializationAsync().ConfigureAwait(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case LaunchGame:
|
|
||||||
{
|
|
||||||
await ThreadHelper.SwitchToMainThreadAsync();
|
|
||||||
if (!MainWindow.IsPresent)
|
|
||||||
{
|
|
||||||
_ = Ioc.Default.GetRequiredService<LaunchGameWindow>();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await Ioc.Default
|
|
||||||
.GetRequiredService<INavigationService>()
|
|
||||||
.NavigateAsync<View.Page.LaunchGamePage>(INavigationAwaiter.Default, true).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.Kind == ExtendedActivationKind.Protocol)
|
if (args.Kind == ExtendedActivationKind.Protocol)
|
||||||
{
|
{
|
||||||
if (args.TryGetProtocolActivatedUri(out Uri? uri))
|
if (args.TryGetProtocolActivatedUri(out Uri? uri))
|
||||||
@@ -108,22 +96,61 @@ internal static class Activation
|
|||||||
await HandleUrlActivationAsync(uri).ConfigureAwait(false);
|
await HandleUrlActivationAsync(uri).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (args.Kind == ExtendedActivationKind.Launch)
|
||||||
|
{
|
||||||
|
if (args.TryGetLaunchActivatedArgument(out string? arguments))
|
||||||
|
{
|
||||||
|
switch (arguments)
|
||||||
|
{
|
||||||
|
case "":
|
||||||
|
{
|
||||||
|
await WaitMainWindowAsync().ConfigureAwait(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LaunchGame:
|
||||||
|
{
|
||||||
|
await HandleLaunchGameActionAsync().ConfigureAwait(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task WaitMainWindowAsync()
|
||||||
|
{
|
||||||
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
|
_ = Ioc.Default.GetRequiredService<MainWindow>();
|
||||||
|
await Ioc.Default.GetRequiredService<IInfoBarService>().WaitInitializationAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
Ioc.Default
|
||||||
|
.GetRequiredService<IMetadataService>()
|
||||||
|
.ImplictAs<IMetadataInitializer>()?
|
||||||
|
.InitializeInternalAsync()
|
||||||
|
.SafeForget();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task HandleUrlActivationAsync(Uri uri)
|
private static async Task HandleUrlActivationAsync(Uri uri)
|
||||||
{
|
{
|
||||||
UriBuilder builder = new(uri);
|
UriBuilder builder = new(uri);
|
||||||
Must.Argument(builder.Scheme == "hutao", "uri 的协议不正确");
|
|
||||||
|
|
||||||
string category = builder.Host.ToLowerInvariant();
|
string category = builder.Host.ToLowerInvariant();
|
||||||
string action = builder.Path.ToLowerInvariant();
|
string action = builder.Path.ToLowerInvariant();
|
||||||
string rawParameter = builder.Query.ToLowerInvariant();
|
string parameter = builder.Query.ToLowerInvariant();
|
||||||
|
|
||||||
switch (category)
|
switch (category)
|
||||||
{
|
{
|
||||||
case "achievement":
|
case "achievement":
|
||||||
{
|
{
|
||||||
await HandleAchievementActionAsync(action, rawParameter).ConfigureAwait(false);
|
await WaitMainWindowAsync().ConfigureAwait(false);
|
||||||
|
await HandleAchievementActionAsync(action, parameter).ConfigureAwait(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "dailynote":
|
||||||
|
{
|
||||||
|
await HandleDailyNoteActionAsync(action, parameter).ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,6 +158,7 @@ internal static class Activation
|
|||||||
|
|
||||||
private static async Task HandleAchievementActionAsync(string action, string parameter)
|
private static async Task HandleAchievementActionAsync(string action, string parameter)
|
||||||
{
|
{
|
||||||
|
_ = parameter;
|
||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case "/import":
|
case "/import":
|
||||||
@@ -146,4 +174,37 @@ internal static class Activation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task HandleDailyNoteActionAsync(string action, string parameter)
|
||||||
|
{
|
||||||
|
_ = parameter;
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case "/refresh":
|
||||||
|
{
|
||||||
|
await Ioc.Default
|
||||||
|
.GetRequiredService<IDailyNoteService>()
|
||||||
|
.RefreshDailyNotesAsync(true)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task HandleLaunchGameActionAsync(string? uid = null)
|
||||||
|
{
|
||||||
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
|
|
||||||
|
// TODO auto switch to account
|
||||||
|
if (!MainWindow.IsPresent)
|
||||||
|
{
|
||||||
|
_ = Ioc.Default.GetRequiredService<LaunchGameWindow>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Ioc.Default
|
||||||
|
.GetRequiredService<INavigationService>()
|
||||||
|
.NavigateAsync<View.Page.LaunchGamePage>(INavigationAwaiter.Default, true).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ internal sealed partial class DatebaseLogger : ILogger
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IDisposable BeginScope<TState>(TState state)
|
public IDisposable BeginScope<TState>(TState state)
|
||||||
|
where TState : notnull
|
||||||
{
|
{
|
||||||
return new NullScope();
|
return new NullScope();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Context.Database;
|
||||||
using Snap.Hutao.Context.FileSystem;
|
using Snap.Hutao.Context.FileSystem;
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Core;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 进程帮助类
|
|
||||||
/// </summary>
|
|
||||||
public static class ProcessHelper
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 启动进程
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">路径</param>
|
|
||||||
/// <param name="useShellExecute">使用shell</param>
|
|
||||||
/// <returns>进程</returns>
|
|
||||||
public static Process? Start(string path, bool useShellExecute = true)
|
|
||||||
{
|
|
||||||
ProcessStartInfo processInfo = new(path)
|
|
||||||
{
|
|
||||||
UseShellExecute = useShellExecute,
|
|
||||||
};
|
|
||||||
return Process.Start(processInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 启动进程
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">路径</param>
|
|
||||||
/// <param name="arguments">命令行参数</param>
|
|
||||||
/// <param name="useShellExecute">使用shell</param>
|
|
||||||
/// <returns>进程</returns>
|
|
||||||
public static Process? Start(string path, string arguments, bool useShellExecute = true)
|
|
||||||
{
|
|
||||||
ProcessStartInfo processInfo = new(path)
|
|
||||||
{
|
|
||||||
UseShellExecute = useShellExecute,
|
|
||||||
Arguments = arguments,
|
|
||||||
};
|
|
||||||
return Process.Start(processInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
48
src/Snap.Hutao/Snap.Hutao/Core/TaskSchedulerHelper.cs
Normal file
48
src/Snap.Hutao/Snap.Hutao/Core/TaskSchedulerHelper.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.Win32.TaskScheduler;
|
||||||
|
using SchedulerTask = Microsoft.Win32.TaskScheduler.Task;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 任务计划器服务
|
||||||
|
/// </summary>
|
||||||
|
internal static class TaskSchedulerHelper
|
||||||
|
{
|
||||||
|
private const string DailyNoteRefreshTaskName = "SnapHutaoDailyNoteRefreshTask";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册实时便笺刷新任务
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="interval">间隔(秒)</param>
|
||||||
|
/// <returns>是否注册或修改成功</returns>
|
||||||
|
public static bool RegisterForDailyNoteRefresh(int interval)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TimeSpan intervalTime = TimeSpan.FromSeconds(interval);
|
||||||
|
if (TaskService.Instance.GetTask(DailyNoteRefreshTaskName) is SchedulerTask targetTask)
|
||||||
|
{
|
||||||
|
TimeTrigger? trigger = targetTask.Definition.Triggers[0] as TimeTrigger;
|
||||||
|
trigger!.Repetition.Interval = intervalTime;
|
||||||
|
targetTask.RegisterChanges();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TaskDefinition task = TaskService.Instance.NewTask();
|
||||||
|
task.RegistrationInfo.Description = "胡桃实时便笺刷新任务 | 请勿编辑或删除。";
|
||||||
|
task.Triggers.Add(new TimeTrigger() { Repetition = new(intervalTime, TimeSpan.Zero), });
|
||||||
|
task.Actions.Add("explorer", "hutao://DailyNote/Refresh");
|
||||||
|
TaskService.Instance.RootFolder.RegisterTaskDefinition(DailyNoteRefreshTaskName, task);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ public sealed class CancellationTokenTaskCompletionSource : IDisposable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tcs = new TaskCompletionSource();
|
TaskCompletionSource tcs = new TaskCompletionSource();
|
||||||
registration = cancellationToken.Register(() => tcs.TrySetResult(), useSynchronizationContext: false);
|
registration = cancellationToken.Register(() => tcs.TrySetResult(), useSynchronizationContext: false);
|
||||||
Task = tcs.Task;
|
Task = tcs.Task;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,16 +14,8 @@ internal class ThreadAccessAttribute : Attribute
|
|||||||
/// 指示方法的进入线程访问状态
|
/// 指示方法的进入线程访问状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="enter">进入状态</param>
|
/// <param name="enter">进入状态</param>
|
||||||
|
[SuppressMessage("", "IDE0060")]
|
||||||
public ThreadAccessAttribute(ThreadAccessState enter)
|
public ThreadAccessAttribute(ThreadAccessState enter)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 指示方法的进入退出线程访问状态
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="enter">进入状态</param>
|
|
||||||
/// <param name="leave">离开状态</param>
|
|
||||||
public ThreadAccessAttribute(ThreadAccessState enter, ThreadAccessState leave)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ namespace Snap.Hutao.Core.Threading;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 调度器队列切换操作
|
/// 调度器队列切换操作
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct DispatherQueueSwitchOperation : IAwaitable<DispatherQueueSwitchOperation>, IAwaiter
|
public readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQueueSwitchOperation>, IAwaiter
|
||||||
{
|
{
|
||||||
private readonly DispatcherQueue dispatherQueue;
|
private readonly DispatcherQueue dispatherQueue;
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public static class SemaphoreSlimExtensions
|
|||||||
return new SemaphoreSlimReleaser(semaphoreSlim);
|
return new SemaphoreSlimReleaser(semaphoreSlim);
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct SemaphoreSlimReleaser : IDisposable
|
private readonly struct SemaphoreSlimReleaser : IDisposable
|
||||||
{
|
{
|
||||||
private readonly SemaphoreSlim semaphoreSlim;
|
private readonly SemaphoreSlim semaphoreSlim;
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public static class TaskExtensions
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await task;
|
await task.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (System.Exception ex)
|
catch (System.Exception ex)
|
||||||
{
|
{
|
||||||
@@ -37,7 +37,7 @@ public static class TaskExtensions
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await task;
|
await task.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (TaskCanceledException)
|
||||||
{
|
{
|
||||||
@@ -59,7 +59,7 @@ public static class TaskExtensions
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await task;
|
await task.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (TaskCanceledException)
|
||||||
{
|
{
|
||||||
@@ -83,7 +83,7 @@ public static class TaskExtensions
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await task;
|
await task.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (TaskCanceledException)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.UI.Windowing;
|
using Microsoft.UI.Windowing;
|
||||||
|
using Snap.Hutao.Win32;
|
||||||
using Windows.Graphics;
|
using Windows.Graphics;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.Windowing;
|
namespace Snap.Hutao.Core.Windowing;
|
||||||
@@ -18,9 +19,6 @@ public static class AppWindowExtensions
|
|||||||
/// <returns>呈现矩形</returns>
|
/// <returns>呈现矩形</returns>
|
||||||
public static RectInt32 GetRect(this AppWindow appWindow)
|
public static RectInt32 GetRect(this AppWindow appWindow)
|
||||||
{
|
{
|
||||||
PointInt32 postion = appWindow.Position;
|
return StructMarshal.RectInt32(appWindow.Position, appWindow.Size);
|
||||||
SizeInt32 size = appWindow.Size;
|
|
||||||
|
|
||||||
return new RectInt32(postion.X, postion.Y, size.Width, size.Height);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,25 +1,30 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
namespace Snap.Hutao.Model.Metadata.Achievement;
|
namespace Snap.Hutao.Core.Windowing;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 成就触发器类型
|
/// 背景类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum AchievementTriggerType
|
public enum BackdropType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 任务
|
/// 无
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Quest = 1,
|
None = 0,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 子任务
|
/// 亚克力
|
||||||
/// </summary>
|
/// </summary>
|
||||||
SubQuest = 2,
|
Acrylic,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 日常任务
|
/// 云母
|
||||||
/// </summary>
|
/// </summary>
|
||||||
DailyTask = 3,
|
Mica,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 变种云母
|
||||||
|
/// </summary>
|
||||||
|
MicaAlt,
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,16 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Microsoft.UI;
|
using Microsoft.UI;
|
||||||
using Microsoft.UI.Windowing;
|
using Microsoft.UI.Windowing;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Snap.Hutao.Core.Logging;
|
using Snap.Hutao.Core.Logging;
|
||||||
using Snap.Hutao.Extension;
|
using Snap.Hutao.Extension;
|
||||||
|
using Snap.Hutao.Message;
|
||||||
using Snap.Hutao.Win32;
|
using Snap.Hutao.Win32;
|
||||||
|
using System.IO;
|
||||||
|
using Windows.ApplicationModel;
|
||||||
using Windows.Graphics;
|
using Windows.Graphics;
|
||||||
using Windows.UI;
|
using Windows.UI;
|
||||||
using Windows.Win32.Foundation;
|
using Windows.Win32.Foundation;
|
||||||
@@ -15,11 +19,11 @@ using WinRT.Interop;
|
|||||||
namespace Snap.Hutao.Core.Windowing;
|
namespace Snap.Hutao.Core.Windowing;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 窗口管理器
|
/// 扩展窗口
|
||||||
/// 主要包含了针对窗体的 P/Inoke 逻辑
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TWindow">窗体类型</typeparam>
|
/// <typeparam name="TWindow">窗体类型</typeparam>
|
||||||
internal sealed class ExtendedWindow<TWindow>
|
[SuppressMessage("", "CA1001")]
|
||||||
|
internal sealed class ExtendedWindow<TWindow> : IRecipient<BackdropTypeChangedMessage>
|
||||||
where TWindow : Window, IExtendedWindowSource
|
where TWindow : Window, IExtendedWindowSource
|
||||||
{
|
{
|
||||||
private readonly HWND handle;
|
private readonly HWND handle;
|
||||||
@@ -33,8 +37,10 @@ internal sealed class ExtendedWindow<TWindow>
|
|||||||
|
|
||||||
private readonly bool useLegacyDragBar;
|
private readonly bool useLegacyDragBar;
|
||||||
|
|
||||||
|
private SystemBackdrop? systemBackdrop;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的窗口状态管理器
|
/// 构造一个新的扩展窗口
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="window">窗口</param>
|
/// <param name="window">窗口</param>
|
||||||
/// <param name="titleBar">充当标题栏的元素</param>
|
/// <param name="titleBar">充当标题栏的元素</param>
|
||||||
@@ -65,6 +71,17 @@ internal sealed class ExtendedWindow<TWindow>
|
|||||||
return new(window, window.TitleBar);
|
return new(window, window.TitleBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Receive(BackdropTypeChangedMessage message)
|
||||||
|
{
|
||||||
|
if (systemBackdrop != null)
|
||||||
|
{
|
||||||
|
systemBackdrop.BackdropType = message.BackdropType;
|
||||||
|
bool micaApplied = systemBackdrop.TryApply();
|
||||||
|
logger.LogInformation(EventIds.BackdropState, "Apply {name} : {result}", nameof(SystemBackdrop), micaApplied ? "succeed" : "failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void UpdateTitleButtonColor(AppWindowTitleBar appTitleBar)
|
private static void UpdateTitleButtonColor(AppWindowTitleBar appTitleBar)
|
||||||
{
|
{
|
||||||
appTitleBar.ButtonBackgroundColor = Colors.Transparent;
|
appTitleBar.ButtonBackgroundColor = Colors.Transparent;
|
||||||
@@ -102,7 +119,7 @@ internal sealed class ExtendedWindow<TWindow>
|
|||||||
private void InitializeWindow()
|
private void InitializeWindow()
|
||||||
{
|
{
|
||||||
appWindow.Title = "胡桃";
|
appWindow.Title = "胡桃";
|
||||||
|
appWindow.SetIcon(Path.Combine(Package.Current.InstalledLocation.Path, "Assets/Logos/Logo.ico"));
|
||||||
ExtendsContentIntoTitleBar();
|
ExtendsContentIntoTitleBar();
|
||||||
|
|
||||||
Persistence.RecoverOrInit(appWindow, window.PersistSize, window.InitSize);
|
Persistence.RecoverOrInit(appWindow, window.PersistSize, window.InitSize);
|
||||||
@@ -113,12 +130,14 @@ internal sealed class ExtendedWindow<TWindow>
|
|||||||
|
|
||||||
appWindow.Show(true);
|
appWindow.Show(true);
|
||||||
|
|
||||||
bool micaApplied = new SystemBackdrop(window).TryApply();
|
systemBackdrop = new(window);
|
||||||
|
bool micaApplied = systemBackdrop.TryApply();
|
||||||
logger.LogInformation(EventIds.BackdropState, "Apply {name} : {result}", nameof(SystemBackdrop), micaApplied ? "succeed" : "failed");
|
logger.LogInformation(EventIds.BackdropState, "Apply {name} : {result}", nameof(SystemBackdrop), micaApplied ? "succeed" : "failed");
|
||||||
|
|
||||||
bool subClassApplied = subclassManager.TrySetWindowSubclass();
|
bool subClassApplied = subclassManager.TrySetWindowSubclass();
|
||||||
logger.LogInformation(EventIds.SubClassing, "Apply {name} : {result}", nameof(WindowSubclassManager<TWindow>), subClassApplied ? "succeed" : "failed");
|
logger.LogInformation(EventIds.SubClassing, "Apply {name} : {result}", nameof(WindowSubclassManager<TWindow>), subClassApplied ? "succeed" : "failed");
|
||||||
|
|
||||||
|
Ioc.Default.GetRequiredService<IMessenger>().Register(this);
|
||||||
window.Closed += OnWindowClosed;
|
window.Closed += OnWindowClosed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,16 +172,17 @@ internal sealed class ExtendedWindow<TWindow>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateDragRectangles(AppWindowTitleBar appTitleBar)
|
private unsafe void UpdateDragRectangles(AppWindowTitleBar appTitleBar)
|
||||||
{
|
{
|
||||||
double scale = Persistence.GetScaleForWindow(handle);
|
double scale = Persistence.GetScaleForWindow(handle);
|
||||||
|
|
||||||
// 48 is the navigation button leftInset
|
// 48 is the navigation button leftInset
|
||||||
RectInt32 dragRect = new RectInt32(48, 0, (int)titleBar.ActualWidth, (int)titleBar.ActualHeight).Scale(scale);
|
RectInt32 dragRect = StructMarshal.RectInt32(new(48, 0), titleBar.ActualSize).Scale(scale);
|
||||||
appTitleBar.SetDragRectangles(dragRect.Enumerate().ToArray());
|
appTitleBar.SetDragRectangles(dragRect.Enumerate().ToArray());
|
||||||
|
|
||||||
// workaround for https://github.com/microsoft/WindowsAppSDK/issues/2976
|
// workaround for https://github.com/microsoft/WindowsAppSDK/issues/2976
|
||||||
// add this to set the same window size after every time drag rectangles are set
|
SizeInt32 size = appWindow.ClientSize;
|
||||||
appWindow.ResizeClient(appWindow.ClientSize);
|
size.Height -= 38;
|
||||||
|
appWindow.ResizeClient(size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,11 @@ internal static class Persistence
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TransformToCenterScreen(ref rect);
|
unsafe
|
||||||
|
{
|
||||||
|
TransformToCenterScreen(&rect);
|
||||||
|
}
|
||||||
|
|
||||||
appWindow.MoveAndResize(rect);
|
appWindow.MoveAndResize(rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,13 +73,13 @@ internal static class Persistence
|
|||||||
return new((int)(size.Width * scale), (int)(size.Height * scale));
|
return new((int)(size.Width * scale), (int)(size.Height * scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TransformToCenterScreen(ref RectInt32 rect)
|
private static unsafe void TransformToCenterScreen(RectInt32* rect)
|
||||||
{
|
{
|
||||||
DisplayArea displayArea = DisplayArea.GetFromRect(rect, DisplayAreaFallback.Primary);
|
DisplayArea displayArea = DisplayArea.GetFromRect(*rect, DisplayAreaFallback.Primary);
|
||||||
RectInt32 workAreaRect = displayArea.WorkArea;
|
RectInt32 workAreaRect = displayArea.WorkArea;
|
||||||
|
|
||||||
rect.X = workAreaRect.X + ((workAreaRect.Width - rect.Width) / 2);
|
rect->X = workAreaRect.X + ((workAreaRect.Width - rect->Width) / 2);
|
||||||
rect.Y = workAreaRect.Y + ((workAreaRect.Height - rect.Height) / 2);
|
rect->Y = workAreaRect.Y + ((workAreaRect.Height - rect->Height) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.UI.Composition;
|
using Microsoft.UI.Composition;
|
||||||
using Microsoft.UI.Composition.SystemBackdrops;
|
using Microsoft.UI.Composition.SystemBackdrops;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
using Snap.Hutao.Context.Database;
|
||||||
|
using Snap.Hutao.Core.Database;
|
||||||
|
using Snap.Hutao.Model.Entity;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Windows.System;
|
using Windows.System;
|
||||||
using WinRT;
|
using WinRT;
|
||||||
@@ -18,7 +22,7 @@ public class SystemBackdrop
|
|||||||
private readonly Window window;
|
private readonly Window window;
|
||||||
|
|
||||||
private DispatcherQueueHelper? dispatcherQueueHelper;
|
private DispatcherQueueHelper? dispatcherQueueHelper;
|
||||||
private MicaController? backdropController;
|
private ISystemBackdropControllerWithTargets? backdropController;
|
||||||
private SystemBackdropConfiguration? configuration;
|
private SystemBackdropConfiguration? configuration;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -28,38 +32,68 @@ public class SystemBackdrop
|
|||||||
public SystemBackdrop(Window window)
|
public SystemBackdrop(Window window)
|
||||||
{
|
{
|
||||||
this.window = window;
|
this.window = window;
|
||||||
|
using (IServiceScope scope = Ioc.Default.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
SettingEntry entry = appDbContext.Settings.SingleOrAdd(SettingEntry.SystemBackdropType, BackdropType.Mica.ToString());
|
||||||
|
BackdropType = Enum.Parse<BackdropType>(entry.Value!);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 背景类型
|
||||||
|
/// </summary>
|
||||||
|
public BackdropType BackdropType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 尝试设置背景
|
/// 尝试设置背景
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>是否设置成功</returns>
|
/// <returns>是否设置成功</returns>
|
||||||
public bool TryApply()
|
public bool TryApply()
|
||||||
{
|
{
|
||||||
if (!MicaController.IsSupported())
|
bool isSupport = BackdropType switch
|
||||||
|
{
|
||||||
|
BackdropType.Acrylic => DesktopAcrylicController.IsSupported(),
|
||||||
|
BackdropType.Mica or BackdropType.MicaAlt => MicaController.IsSupported(),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isSupport)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dispatcherQueueHelper = new();
|
// Previous one
|
||||||
dispatcherQueueHelper.Ensure();
|
if (backdropController != null)
|
||||||
|
{
|
||||||
|
backdropController.RemoveAllSystemBackdropTargets();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dispatcherQueueHelper = new();
|
||||||
|
dispatcherQueueHelper.Ensure();
|
||||||
|
}
|
||||||
|
|
||||||
// Hooking up the policy object
|
// Hooking up the policy object
|
||||||
configuration = new();
|
configuration = new()
|
||||||
|
{
|
||||||
|
IsInputActive = true, // Initial configuration state.
|
||||||
|
};
|
||||||
|
SetConfigurationSourceTheme(configuration);
|
||||||
|
|
||||||
window.Activated += OnWindowActivated;
|
window.Activated += OnWindowActivated;
|
||||||
window.Closed += OnWindowClosed;
|
window.Closed += OnWindowClosed;
|
||||||
((FrameworkElement)window.Content).ActualThemeChanged += OnWindowThemeChanged;
|
((FrameworkElement)window.Content).ActualThemeChanged += OnWindowThemeChanged;
|
||||||
|
|
||||||
// Initial configuration state.
|
backdropController = BackdropType switch
|
||||||
configuration.IsInputActive = true;
|
|
||||||
SetConfigurationSourceTheme(configuration);
|
|
||||||
|
|
||||||
backdropController = new()
|
|
||||||
{
|
{
|
||||||
// Mica Alt
|
BackdropType.Acrylic => new DesktopAcrylicController(),
|
||||||
Kind = MicaKind.BaseAlt,
|
BackdropType.Mica => new MicaController() { Kind = MicaKind.Base },
|
||||||
|
BackdropType.MicaAlt => new MicaController() { Kind = MicaKind.BaseAlt },
|
||||||
|
_ => throw Must.NeverHappen(),
|
||||||
};
|
};
|
||||||
|
|
||||||
backdropController.AddSystemBackdropTarget(window.As<ICompositionSupportsSystemBackdrop>());
|
backdropController.AddSystemBackdropTarget(window.As<ICompositionSupportsSystemBackdrop>());
|
||||||
backdropController.SetSystemBackdropConfiguration(configuration);
|
backdropController.SetSystemBackdropConfiguration(configuration);
|
||||||
|
|
||||||
@@ -69,7 +103,7 @@ public class SystemBackdrop
|
|||||||
|
|
||||||
private void OnWindowActivated(object sender, WindowActivatedEventArgs args)
|
private void OnWindowActivated(object sender, WindowActivatedEventArgs args)
|
||||||
{
|
{
|
||||||
Must.NotNull(configuration!).IsInputActive = args.WindowActivationState != WindowActivationState.Deactivated;
|
configuration!.IsInputActive = args.WindowActivationState != WindowActivationState.Deactivated;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnWindowClosed(object sender, WindowEventArgs args)
|
private void OnWindowClosed(object sender, WindowEventArgs args)
|
||||||
|
|||||||
@@ -52,9 +52,10 @@ internal class WindowSubclassManager<TWindow> : IDisposable
|
|||||||
|
|
||||||
bool titleBarHooked = true;
|
bool titleBarHooked = true;
|
||||||
|
|
||||||
// only hook up drag bar proc when use legacy Window.ExtendsContentIntoTitleBar
|
// only hook up drag bar proc when not use legacy Window.ExtendsContentIntoTitleBar
|
||||||
if (isLegacyDragBar)
|
if (isLegacyDragBar)
|
||||||
{
|
{
|
||||||
|
titleBarHooked = false;
|
||||||
hwndDragBar = FindWindowEx(hwnd, default, "DRAG_BAR_WINDOW_CLASS", string.Empty);
|
hwndDragBar = FindWindowEx(hwnd, default, "DRAG_BAR_WINDOW_CLASS", string.Empty);
|
||||||
|
|
||||||
if (!hwndDragBar.IsNull)
|
if (!hwndDragBar.IsNull)
|
||||||
@@ -90,6 +91,12 @@ internal class WindowSubclassManager<TWindow> : IDisposable
|
|||||||
window.ProcessMinMaxInfo((MINMAXINFO*)lParam.Value, scalingFactor);
|
window.ProcessMinMaxInfo((MINMAXINFO*)lParam.Value, scalingFactor);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case WM_NCRBUTTONDOWN:
|
||||||
|
case WM_NCRBUTTONUP:
|
||||||
|
{
|
||||||
|
return new(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Extension;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="Collection{T}"/> 部分
|
||||||
|
/// </summary>
|
||||||
|
public static partial class EnumerableExtension
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 移除集合中满足条件的项
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">集合项类型</typeparam>
|
||||||
|
/// <param name="collection">集合</param>
|
||||||
|
/// <param name="shouldRemovePredicate">是否应当移除</param>
|
||||||
|
/// <returns>移除的个数</returns>
|
||||||
|
public static int RemoveWhere<T>(this Collection<T> collection, Func<T, bool> shouldRemovePredicate)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
foreach (T item in collection.ToList())
|
||||||
|
{
|
||||||
|
if (shouldRemovePredicate.Invoke(item))
|
||||||
|
{
|
||||||
|
collection.Remove(item);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Extension;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="Dictionary{TKey, TValue}"/> 部分
|
||||||
|
/// </summary>
|
||||||
|
public static partial class EnumerableExtension
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取值或默认值
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TKey">键类型</typeparam>
|
||||||
|
/// <typeparam name="TValue">值类型</typeparam>
|
||||||
|
/// <param name="dictionary">字典</param>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>结果值</returns>
|
||||||
|
public static TValue? GetValueOrDefault2<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue? defaultValue = default)
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
if (dictionary.TryGetValue(key, out TValue? value))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 增加计数
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TKey">键类型</typeparam>
|
||||||
|
/// <param name="dict">字典</param>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
public static void Increase<TKey>(this Dictionary<TKey, int> dict, TKey key)
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
++CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 增加计数
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TKey">键类型</typeparam>
|
||||||
|
/// <param name="dict">字典</param>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="value">增加的值</param>
|
||||||
|
public static void Increase<TKey>(this Dictionary<TKey, int> dict, TKey key, int value)
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
// ref the value, so that we can manipulate it outside the dict.
|
||||||
|
ref int current = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _);
|
||||||
|
current += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 增加计数
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TKey">键类型</typeparam>
|
||||||
|
/// <param name="dict">字典</param>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <returns>是否存在键值</returns>
|
||||||
|
public static bool TryIncrease<TKey>(this Dictionary<TKey, int> dict, TKey key)
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
ref int value = ref CollectionsMarshal.GetValueRefOrNullRef(dict, key);
|
||||||
|
if (!Unsafe.IsNullRef(ref value))
|
||||||
|
{
|
||||||
|
++value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Extension;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="List{T}"/> 部分
|
||||||
|
/// </summary>
|
||||||
|
public static partial class EnumerableExtension
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="Enumerable.Average(IEnumerable{int})"/>
|
||||||
|
public static double AverageNoThrow(this List<int> source)
|
||||||
|
{
|
||||||
|
Span<int> span = CollectionsMarshal.AsSpan(source);
|
||||||
|
|
||||||
|
if (span.IsEmpty)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
long sum = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
sum += span[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (double)sum / span.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 如果传入列表不为空则原路返回,
|
||||||
|
/// 如果传入列表为空返回一个空的列表
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSource">源类型</typeparam>
|
||||||
|
/// <param name="source">源</param>
|
||||||
|
/// <returns>源列表或空列表</returns>
|
||||||
|
public static List<TSource> EmptyIfNull<TSource>(this List<TSource>? source)
|
||||||
|
{
|
||||||
|
return source ?? new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除表中首个满足条件的项
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">项的类型</typeparam>
|
||||||
|
/// <param name="list">表</param>
|
||||||
|
/// <param name="shouldRemovePredicate">是否应当移除</param>
|
||||||
|
/// <returns>是否移除了元素</returns>
|
||||||
|
public static bool RemoveFirstWhere<T>(this IList<T> list, Func<T, bool> shouldRemovePredicate)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < list.Count; i++)
|
||||||
|
{
|
||||||
|
if (shouldRemovePredicate.Invoke(list[i]))
|
||||||
|
{
|
||||||
|
list.RemoveAt(i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Extension;
|
namespace Snap.Hutao.Extension;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -11,26 +8,6 @@ namespace Snap.Hutao.Extension;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class EnumerableExtension
|
public static partial class EnumerableExtension
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="Enumerable.Average(IEnumerable{int})"/>
|
|
||||||
public static double AverageNoThrow(this List<int> source)
|
|
||||||
{
|
|
||||||
Span<int> span = CollectionsMarshal.AsSpan(source);
|
|
||||||
|
|
||||||
if (span.IsEmpty)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
long sum = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < span.Length; i++)
|
|
||||||
{
|
|
||||||
sum += span[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return (double)sum / span.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 计数
|
/// 计数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -63,18 +40,6 @@ public static partial class EnumerableExtension
|
|||||||
return source ?? Enumerable.Empty<TSource>();
|
return source ?? Enumerable.Empty<TSource>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 如果传入列表不为空则原路返回,
|
|
||||||
/// 如果传入列表为空返回一个空的列表
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TSource">源类型</typeparam>
|
|
||||||
/// <param name="source">源</param>
|
|
||||||
/// <returns>源列表或空列表</returns>
|
|
||||||
public static List<TSource> EmptyIfNull<TSource>(this List<TSource>? source)
|
|
||||||
{
|
|
||||||
return source ?? new();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将源转换为仅包含单个元素的枚举
|
/// 将源转换为仅包含单个元素的枚举
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -99,94 +64,6 @@ public static partial class EnumerableExtension
|
|||||||
return source.FirstOrDefault(predicate) ?? source.FirstOrDefault();
|
return source.FirstOrDefault(predicate) ?? source.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取值或默认值
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">键类型</typeparam>
|
|
||||||
/// <typeparam name="TValue">值类型</typeparam>
|
|
||||||
/// <param name="dictionary">字典</param>
|
|
||||||
/// <param name="key">键</param>
|
|
||||||
/// <param name="defaultValue">默认值</param>
|
|
||||||
/// <returns>结果值</returns>
|
|
||||||
public static TValue? GetValueOrDefault2<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue? defaultValue = default)
|
|
||||||
where TKey : notnull
|
|
||||||
{
|
|
||||||
if (dictionary.TryGetValue(key, out TValue? value))
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 增加计数
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">键类型</typeparam>
|
|
||||||
/// <param name="dict">字典</param>
|
|
||||||
/// <param name="key">键</param>
|
|
||||||
public static void Increase<TKey>(this Dictionary<TKey, int> dict, TKey key)
|
|
||||||
where TKey : notnull
|
|
||||||
{
|
|
||||||
++CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 增加计数
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">键类型</typeparam>
|
|
||||||
/// <param name="dict">字典</param>
|
|
||||||
/// <param name="key">键</param>
|
|
||||||
/// <param name="value">增加的值</param>
|
|
||||||
public static void Increase<TKey>(this Dictionary<TKey, int> dict, TKey key, int value)
|
|
||||||
where TKey : notnull
|
|
||||||
{
|
|
||||||
// ref the value, so that we can manipulate it outside the dict.
|
|
||||||
ref int current = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _);
|
|
||||||
current += value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 增加计数
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">键类型</typeparam>
|
|
||||||
/// <param name="dict">字典</param>
|
|
||||||
/// <param name="key">键</param>
|
|
||||||
/// <returns>是否存在键值</returns>
|
|
||||||
public static bool TryIncrease<TKey>(this Dictionary<TKey, int> dict, TKey key)
|
|
||||||
where TKey : notnull
|
|
||||||
{
|
|
||||||
ref int value = ref CollectionsMarshal.GetValueRefOrNullRef(dict, key);
|
|
||||||
if (!Unsafe.IsNullRef(ref value))
|
|
||||||
{
|
|
||||||
++value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 移除表中首个满足条件的项
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">项的类型</typeparam>
|
|
||||||
/// <param name="list">表</param>
|
|
||||||
/// <param name="shouldRemovePredicate">是否应当移除</param>
|
|
||||||
/// <returns>是否移除了元素</returns>
|
|
||||||
public static bool RemoveFirstWhere<T>(this IList<T> list, Func<T, bool> shouldRemovePredicate)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < list.Count; i++)
|
|
||||||
{
|
|
||||||
if (shouldRemovePredicate.Invoke(list[i]))
|
|
||||||
{
|
|
||||||
list.RemoveAt(i);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="Enumerable.ToDictionary{TSource, TKey}(IEnumerable{TSource}, Func{TSource, TKey})"/>
|
/// <inheritdoc cref="Enumerable.ToDictionary{TSource, TKey}(IEnumerable{TSource}, Func{TSource, TKey})"/>
|
||||||
public static Dictionary<TKey, TSource> ToDictionaryOverride<TKey, TSource>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
|
public static Dictionary<TKey, TSource> ToDictionaryOverride<TKey, TSource>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
|
||||||
where TKey : notnull
|
where TKey : notnull
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Extension;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="StringBuilder"/> 扩展方法
|
||||||
|
/// </summary>
|
||||||
|
public static class StringBuilderExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 当条件符合时执行 <see cref="StringBuilder.Append(string?)"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sb">字符串建造器</param>
|
||||||
|
/// <param name="condition">条件</param>
|
||||||
|
/// <param name="value">附加的字符串</param>
|
||||||
|
/// <returns>同一个字符串建造器</returns>
|
||||||
|
public static StringBuilder AppendIf(this StringBuilder sb, bool condition, string? value)
|
||||||
|
{
|
||||||
|
return condition ? sb.Append(value) : sb;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ public sealed partial class LaunchGameWindow : Window, IDisposable, IExtendedWin
|
|||||||
|
|
||||||
scope = scopeFactory.CreateScope();
|
scope = scopeFactory.CreateScope();
|
||||||
RootGrid.DataContext = scope.ServiceProvider.GetRequiredService<LaunchGameViewModel>();
|
RootGrid.DataContext = scope.ServiceProvider.GetRequiredService<LaunchGameViewModel>();
|
||||||
|
Closed += (s, e) => Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Core.Windowing;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Message;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 背景类型改变消息
|
||||||
|
/// </summary>
|
||||||
|
internal class BackdropTypeChangedMessage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的背景类型改变消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="backdropType">背景类型</param>
|
||||||
|
public BackdropTypeChangedMessage(BackdropType backdropType)
|
||||||
|
{
|
||||||
|
BackdropType = backdropType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 背景类型
|
||||||
|
/// </summary>
|
||||||
|
public BackdropType BackdropType { get; set; }
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Model.Binding;
|
using Snap.Hutao.Model.Binding.User;
|
||||||
|
|
||||||
namespace Snap.Hutao.Message;
|
namespace Snap.Hutao.Message;
|
||||||
|
|
||||||
|
|||||||
26
src/Snap.Hutao/Snap.Hutao/Message/UserRemovedMessage.cs
Normal file
26
src/Snap.Hutao/Snap.Hutao/Message/UserRemovedMessage.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Model.Entity;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Message;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户删除消息
|
||||||
|
/// </summary>
|
||||||
|
internal class UserRemovedMessage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的用户删除消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">用户</param>
|
||||||
|
public UserRemovedMessage(User user)
|
||||||
|
{
|
||||||
|
RemovedUserId = user.InnerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除的用户Id
|
||||||
|
/// </summary>
|
||||||
|
public Guid RemovedUserId { get; }
|
||||||
|
}
|
||||||
282
src/Snap.Hutao/Snap.Hutao/Migrations/20221108081525_DailyNoteEntry.Designer.cs
generated
Normal file
282
src/Snap.Hutao/Snap.Hutao/Migrations/20221108081525_DailyNoteEntry.Designer.cs
generated
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Snap.Hutao.Context.Database;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20221108081525_DailyNoteEntry")]
|
||||||
|
partial class DailyNoteEntry
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "6.0.10");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("ArchiveId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Current")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Time")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.HasIndex("ArchiveId");
|
||||||
|
|
||||||
|
b.ToTable("achievements");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsSelected")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.ToTable("achievement_archives");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Info")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Uid")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.ToTable("avatar_infos");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.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<bool>("ResinNotifySuppressed")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ResinNotifyThreshold")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("ShowInHomeWidget")
|
||||||
|
.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<int>("ItemId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("QueryType")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Time")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.HasIndex("ArchiveId");
|
||||||
|
|
||||||
|
b.ToTable("gacha_items");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("AttachUid")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("MihoyoSDK")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.ToTable("game_accounts");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.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.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Cookie")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsSelected")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
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.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");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Migrations
|
||||||
|
{
|
||||||
|
public partial class DailyNoteEntry : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "daily_notes",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
InnerId = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||||
|
UserId = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||||
|
Uid = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
DailyNote = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
ResinNotifyThreshold = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
ResinNotifySuppressed = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
HomeCoinNotifyThreshold = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
HomeCoinNotifySuppressed = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
TransformerNotify = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
TransformerNotifySuppressed = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
DailyTaskNotify = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
DailyTaskNotifySuppressed = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
ExpeditionNotify = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
ExpeditionNotifySuppressed = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
ShowInHomeWidget = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_daily_notes", x => x.InnerId);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_daily_notes_users_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "users",
|
||||||
|
principalColumn: "InnerId",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_daily_notes_UserId",
|
||||||
|
table: "daily_notes",
|
||||||
|
column: "UserId");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "daily_notes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -82,6 +82,62 @@ namespace Snap.Hutao.Migrations
|
|||||||
b.ToTable("avatar_infos");
|
b.ToTable("avatar_infos");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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<bool>("ResinNotifySuppressed")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ResinNotifyThreshold")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("ShowInHomeWidget")
|
||||||
|
.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 =>
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("InnerId")
|
b.Property<Guid>("InnerId")
|
||||||
@@ -197,6 +253,17 @@ namespace Snap.Hutao.Migrations
|
|||||||
b.Navigation("Archive");
|
b.Navigation("Archive");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 =>
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive")
|
b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive")
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Snap.Hutao.Model.Binding.AvatarProperty;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 词条评分
|
/// 词条评分
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct AffixScore
|
public readonly struct AffixScore
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的圣遗物评分
|
/// 构造一个新的圣遗物评分
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ public class LaunchScheme
|
|||||||
/// <param name="channel">通道</param>
|
/// <param name="channel">通道</param>
|
||||||
/// <param name="cps">通道描述字符串</param>
|
/// <param name="cps">通道描述字符串</param>
|
||||||
/// <param name="subChannel">子通道</param>
|
/// <param name="subChannel">子通道</param>
|
||||||
public LaunchScheme(string name, string channel, string subChannel)
|
/// <param name="launcherId">启动器Id</param>
|
||||||
|
public LaunchScheme(string name, string channel, string subChannel, string launcherId)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Channel = channel;
|
Channel = channel;
|
||||||
SubChannel = subChannel;
|
SubChannel = subChannel;
|
||||||
|
LauncherId = launcherId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -39,4 +41,9 @@ public class LaunchScheme
|
|||||||
/// 子通道
|
/// 子通道
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SubChannel { get; set; }
|
public string SubChannel { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动器Id
|
||||||
|
/// </summary>
|
||||||
|
public string LauncherId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ using Snap.Hutao.Web.Hoyolab.Bbs.User;
|
|||||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||||
using EntityUser = Snap.Hutao.Model.Entity.User;
|
using EntityUser = Snap.Hutao.Model.Entity.User;
|
||||||
|
|
||||||
namespace Snap.Hutao.Model.Binding;
|
namespace Snap.Hutao.Model.Binding.User;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用于视图绑定的用户
|
/// 用于视图绑定的用户
|
||||||
@@ -146,4 +146,4 @@ public class User : ObservableObject
|
|||||||
|
|
||||||
return UserInfo != null && UserGameRoles.Any();
|
return UserInfo != null && UserGameRoles.Any();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
34
src/Snap.Hutao/Snap.Hutao/Model/Binding/User/UserAndRole.cs
Normal file
34
src/Snap.Hutao/Snap.Hutao/Model/Binding/User/UserAndRole.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||||
|
using EntityUser = Snap.Hutao.Model.Entity.User;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Binding.User;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色与实体用户
|
||||||
|
/// </summary>
|
||||||
|
public class UserAndRole
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的实体用户与角色
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">实体用户</param>
|
||||||
|
/// <param name="role">角色</param>
|
||||||
|
public UserAndRole(EntityUser user, UserGameRole role)
|
||||||
|
{
|
||||||
|
User = user;
|
||||||
|
Role = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实体用户
|
||||||
|
/// </summary>
|
||||||
|
public EntityUser User { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色
|
||||||
|
/// </summary>
|
||||||
|
public UserGameRole Role { get; private set; }
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Extension;
|
|
||||||
using Snap.Hutao.Model.InterChange.Achievement;
|
using Snap.Hutao.Model.InterChange.Achievement;
|
||||||
using Snap.Hutao.Model.Intrinsic;
|
using Snap.Hutao.Model.Intrinsic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
@@ -84,7 +83,7 @@ public class Achievement : IEquatable<Achievement>
|
|||||||
Id = uiaf.Id,
|
Id = uiaf.Id,
|
||||||
Current = uiaf.Current,
|
Current = uiaf.Current,
|
||||||
Status = uiaf.Status, // Hot fix | 1.0.30 | Status not set when create database entity
|
Status = uiaf.Status, // Hot fix | 1.0.30 | Status not set when create database entity
|
||||||
Time = DateTimeOffset.FromUnixTimeSeconds(uiaf.Timestamp).ToLocalTime(true),
|
Time = DateTimeOffset.FromUnixTimeSeconds(uiaf.Timestamp).ToLocalTime(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Entity.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实时便笺入口配置
|
||||||
|
/// </summary>
|
||||||
|
internal class DailyNoteEntryConfiguration : IEntityTypeConfiguration<DailyNoteEntry>
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Configure(EntityTypeBuilder<DailyNoteEntry> builder)
|
||||||
|
{
|
||||||
|
builder.Property(e => e.DailyNote)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasConversion<JsonTextValueConverter<Web.Hoyolab.Takumi.GameRecord.DailyNote.DailyNote>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
135
src/Snap.Hutao/Snap.Hutao/Model/Entity/DailyNoteEntry.cs
Normal file
135
src/Snap.Hutao/Snap.Hutao/Model/Entity/DailyNoteEntry.cs
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Model.Binding.User;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Entity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实时便笺入口
|
||||||
|
/// </summary>
|
||||||
|
[Table("daily_notes")]
|
||||||
|
public class DailyNoteEntry : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 内部Id
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public Guid InnerId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户Id
|
||||||
|
/// </summary>
|
||||||
|
[ForeignKey(nameof(UserId))]
|
||||||
|
public Guid UserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户
|
||||||
|
/// </summary>
|
||||||
|
public User User { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uid
|
||||||
|
/// </summary>
|
||||||
|
public string Uid { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家角色
|
||||||
|
/// </summary>
|
||||||
|
[NotMapped]
|
||||||
|
public UserGameRole? UserGameRole { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Json!!! 实时便笺
|
||||||
|
/// </summary>
|
||||||
|
public DailyNote? DailyNote { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 树脂提醒阈值
|
||||||
|
/// </summary>
|
||||||
|
public int ResinNotifyThreshold { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用于判断树脂是否继续提醒
|
||||||
|
/// </summary>
|
||||||
|
public bool ResinNotifySuppressed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 洞天宝钱提醒阈值
|
||||||
|
/// </summary>
|
||||||
|
public int HomeCoinNotifyThreshold { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用于判断洞天宝钱是否继续提醒
|
||||||
|
/// </summary>
|
||||||
|
public bool HomeCoinNotifySuppressed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 参量质变仪提醒
|
||||||
|
/// </summary>
|
||||||
|
public bool TransformerNotify { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用于判断参量质变仪是否继续提醒
|
||||||
|
/// </summary>
|
||||||
|
public bool TransformerNotifySuppressed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 每日委托提醒
|
||||||
|
/// </summary>
|
||||||
|
public bool DailyTaskNotify { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用于判断每日委托是否继续提醒
|
||||||
|
/// </summary>
|
||||||
|
public bool DailyTaskNotifySuppressed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 探索派遣提醒
|
||||||
|
/// </summary>
|
||||||
|
public bool ExpeditionNotify { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用于判断探索派遣是否继续提醒
|
||||||
|
/// </summary>
|
||||||
|
public bool ExpeditionNotifySuppressed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否在主页显示小组件
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowInHomeWidget { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的实时便笺
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userAndRole">用户与角色</param>
|
||||||
|
/// <returns>新的实时便笺</returns>
|
||||||
|
public static DailyNoteEntry Create(UserAndRole userAndRole)
|
||||||
|
{
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
UserId = userAndRole.User.InnerId,
|
||||||
|
Uid = userAndRole.Role.GameUid,
|
||||||
|
ResinNotifyThreshold = 160,
|
||||||
|
HomeCoinNotifyThreshold = 2400,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新实时便笺
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dailyNote">新的值</param>
|
||||||
|
public void UpdateDailyNote(DailyNote? dailyNote)
|
||||||
|
{
|
||||||
|
DailyNote = dailyNote;
|
||||||
|
PropertyChanged?.Invoke(this, new(nameof(DailyNote)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ public class GachaItem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 存档
|
/// 存档
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ForeignKey(nameof(ArchiveId))]
|
||||||
public GachaArchive Archive { get; set; } = default!;
|
public GachaArchive Archive { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -10,8 +10,11 @@ namespace Snap.Hutao.Model.Entity;
|
|||||||
/// 设置入口
|
/// 设置入口
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Table("settings")]
|
[Table("settings")]
|
||||||
|
[SuppressMessage("", "SA1124")]
|
||||||
public class SettingEntry
|
public class SettingEntry
|
||||||
{
|
{
|
||||||
|
#region EntryNames
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 游戏路径
|
/// 游戏路径
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -22,6 +25,21 @@ public class SettingEntry
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string IsEmptyHistoryWishVisible = "IsEmptyHistoryWishVisible";
|
public const string IsEmptyHistoryWishVisible = "IsEmptyHistoryWishVisible";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 窗口背景类型
|
||||||
|
/// </summary>
|
||||||
|
public const string SystemBackdropType = "SystemBackdropType";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实时便笺刷新时间
|
||||||
|
/// </summary>
|
||||||
|
public const string DailyNoteRefreshSeconds = "DailyNote.RefreshSeconds";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实时便笺提醒式通知
|
||||||
|
/// </summary>
|
||||||
|
public const string DailyNoteReminderNotify = "DailyNote.ReminderNotify";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 启动游戏 全屏
|
/// 启动游戏 全屏
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -51,6 +69,7 @@ public class SettingEntry
|
|||||||
/// 启动游戏 目标帧率
|
/// 启动游戏 目标帧率
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string LaunchTargetFps = "Launch.TargetFps";
|
public const string LaunchTargetFps = "Launch.TargetFps";
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的设置入口
|
/// 构造一个新的设置入口
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using MiniExcelLibs.Attributes;
|
|
||||||
using Snap.Hutao.Core.Json.Converter;
|
using Snap.Hutao.Core.Json.Converter;
|
||||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||||
|
|
||||||
@@ -15,7 +14,6 @@ public class UIGFItem : GachaLogItem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 额外祈愿映射
|
/// 额外祈愿映射
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ExcelColumn(Name = "uigf_gacha_type")]
|
|
||||||
[JsonPropertyName("uigf_gacha_type")]
|
[JsonPropertyName("uigf_gacha_type")]
|
||||||
[JsonConverter(typeof(EnumStringValueConverter<GachaConfigType>))]
|
[JsonConverter(typeof(EnumStringValueConverter<GachaConfigType>))]
|
||||||
public GachaConfigType UIGFGachaType { get; set; } = default!;
|
public GachaConfigType UIGFGachaType { get; set; } = default!;
|
||||||
|
|||||||
@@ -43,11 +43,6 @@ public class Achievement
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int Progress { get; set; }
|
public int Progress { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 触发器
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<AchievementTrigger>? Triggers { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 图标
|
/// 图标
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Model.Metadata.Achievement;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 成就触发器
|
|
||||||
/// </summary>
|
|
||||||
public class AchievementTrigger
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 触发器类型
|
|
||||||
/// </summary>
|
|
||||||
public AchievementTriggerType Type { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Id
|
|
||||||
/// </summary>
|
|
||||||
public string Id { get; set; } = default!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 标题
|
|
||||||
/// </summary>
|
|
||||||
public string Title { get; set; } = default!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 描述
|
|
||||||
/// </summary>
|
|
||||||
public string Description { get; set; } = default!;
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,7 @@ namespace Snap.Hutao.Model.Metadata.Converter;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 描述参数解析器
|
/// 描述参数解析器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class DescParamDescriptor : ValueConverterBase<DescParam, IList<LevelParam<string, ParameterInfo>>>
|
internal sealed partial class DescParamDescriptor : ValueConverterBase<DescParam, IList<LevelParam<string, ParameterInfo>>>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取特定等级的解释
|
/// 获取特定等级的解释
|
||||||
@@ -56,13 +56,16 @@ internal sealed class DescParamDescriptor : ValueConverterBase<DescParam, IList<
|
|||||||
DescFormat descFormat = formats[index];
|
DescFormat descFormat = formats[index];
|
||||||
|
|
||||||
string format = descFormat.Format;
|
string format = descFormat.Format;
|
||||||
string resultFormatted = Regex.Replace(format, @"{param\d+.*?}", match => EvaluateMatch(match, param));
|
string resultFormatted = ParamRegex().Replace(format, match => EvaluateMatch(match, param));
|
||||||
results.Add(new ParameterInfo { Description = descFormat.Description, Parameter = resultFormatted });
|
results.Add(new ParameterInfo { Description = descFormat.Description, Parameter = resultFormatted });
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[GeneratedRegex("{param\\d+.*?}")]
|
||||||
|
private static partial Regex ParamRegex();
|
||||||
|
|
||||||
private static string EvaluateMatch(Match match, IList<double> param)
|
private static string EvaluateMatch(Match match, IList<double> param)
|
||||||
{
|
{
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Model;
|
namespace Snap.Hutao.Model;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -2,15 +2,17 @@
|
|||||||
|
|
||||||
<Package
|
<Package
|
||||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||||
|
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
|
||||||
|
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||||
xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"
|
xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"
|
||||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
|
||||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||||
IgnorableNamespaces="uap desktop6 rescap">
|
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||||
|
IgnorableNamespaces="com uap desktop desktop6 rescap">
|
||||||
|
|
||||||
<Identity
|
<Identity
|
||||||
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
|
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
|
||||||
Publisher="CN=DGP Studio"
|
Publisher="CN=DGP Studio"
|
||||||
Version="1.1.21.0" />
|
Version="1.2.0.0" />
|
||||||
|
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>胡桃</DisplayName>
|
<DisplayName>胡桃</DisplayName>
|
||||||
@@ -22,7 +24,7 @@
|
|||||||
|
|
||||||
<Dependencies>
|
<Dependencies>
|
||||||
<!--<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />-->
|
<!--<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />-->
|
||||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.22000.0" />
|
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22000.0" />
|
||||||
</Dependencies>
|
</Dependencies>
|
||||||
|
|
||||||
<Resources>
|
<Resources>
|
||||||
@@ -43,6 +45,16 @@
|
|||||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||||
</uap:VisualElements>
|
</uap:VisualElements>
|
||||||
<Extensions>
|
<Extensions>
|
||||||
|
<desktop:Extension Category="windows.toastNotificationActivation">
|
||||||
|
<desktop:ToastNotificationActivation ToastActivatorCLSID="5760ec4d-f7e8-4666-a965-9886d7dffe7d"/>
|
||||||
|
</desktop:Extension>
|
||||||
|
<com:Extension Category="windows.comServer">
|
||||||
|
<com:ComServer>
|
||||||
|
<com:ExeServer Executable="Snap.Hutao.exe" Arguments="-ToastActivated" DisplayName="Snap Hutao Toast Activator">
|
||||||
|
<com:Class Id="5760ec4d-f7e8-4666-a965-9886d7dffe7d" DisplayName="Snap Hutao Toast Activator"/>
|
||||||
|
</com:ExeServer>
|
||||||
|
</com:ComServer>
|
||||||
|
</com:Extension>
|
||||||
<uap:Extension Category="windows.protocol">
|
<uap:Extension Category="windows.protocol">
|
||||||
<uap:Protocol Name="hutao">
|
<uap:Protocol Name="hutao">
|
||||||
<uap:DisplayName>胡桃</uap:DisplayName>
|
<uap:DisplayName>胡桃</uap:DisplayName>
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ public static partial class Program
|
|||||||
[SuppressMessage("", "SA1401")]
|
[SuppressMessage("", "SA1401")]
|
||||||
internal static volatile DispatcherQueue? DispatcherQueue;
|
internal static volatile DispatcherQueue? DispatcherQueue;
|
||||||
|
|
||||||
[DllImport("Microsoft.ui.xaml.dll")]
|
[LibraryImport("Microsoft.ui.xaml.dll")]
|
||||||
private static extern void XamlCheckProcessRequirements();
|
private static partial void XamlCheckProcessRequirements();
|
||||||
|
|
||||||
[STAThread]
|
[STAThread]
|
||||||
private static void Main(string[] args)
|
private static void Main(string[] args)
|
||||||
@@ -41,6 +41,7 @@ public static partial class Program
|
|||||||
// In a Desktop app this runs a message pump internally,
|
// In a Desktop app this runs a message pump internally,
|
||||||
// and does not return until the application shuts down.
|
// and does not return until the application shuts down.
|
||||||
Application.Start(InitializeApp);
|
Application.Start(InitializeApp);
|
||||||
|
ServiceScopeExtension.DisposeLast();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
"profiles": {
|
"profiles": {
|
||||||
"Snap.Hutao (Package)": {
|
"Snap.Hutao (Package)": {
|
||||||
"commandName": "MsixPackage",
|
"commandName": "MsixPackage",
|
||||||
"nativeDebugging": false
|
"nativeDebugging": false,
|
||||||
|
"doNotLaunchApp": false
|
||||||
},
|
},
|
||||||
"Snap.Hutao (Unpackaged)": {
|
"Snap.Hutao (Unpackaged)": {
|
||||||
"commandName": "Project"
|
"commandName": "Project"
|
||||||
|
|||||||
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_ItemIcon_204.png
Normal file
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_ItemIcon_204.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_ItemIcon_210_256.png
Normal file
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_ItemIcon_210_256.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_ItemIcon_220021.png
Normal file
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_ItemIcon_220021.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_MarkTower.png
Normal file
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_MarkTower.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
@@ -6,7 +6,6 @@ using Snap.Hutao.Context.Database;
|
|||||||
using Snap.Hutao.Core.Database;
|
using Snap.Hutao.Core.Database;
|
||||||
using Snap.Hutao.Core.Diagnostics;
|
using Snap.Hutao.Core.Diagnostics;
|
||||||
using Snap.Hutao.Core.Logging;
|
using Snap.Hutao.Core.Logging;
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Snap.Hutao.Model.InterChange.Achievement;
|
using Snap.Hutao.Model.InterChange.Achievement;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using BindingAchievement = Snap.Hutao.Model.Binding.Achievement;
|
using BindingAchievement = Snap.Hutao.Model.Binding.Achievement;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Snap.Hutao.Service.Achievement;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 导入结果
|
/// 导入结果
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct ImportResult
|
public readonly struct ImportResult
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 新增数
|
/// 新增数
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Snap.Hutao.Service;
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[Injection(InjectAs.Transient, typeof(IAnnouncementService))]
|
[Injection(InjectAs.Transient, typeof(IAnnouncementService))]
|
||||||
internal class AnnouncementService : IAnnouncementService
|
internal partial class AnnouncementService : IAnnouncementService
|
||||||
{
|
{
|
||||||
private static readonly string CacheKey = $"{nameof(AnnouncementService)}.Cache.{nameof(AnnouncementWrapper)}";
|
private static readonly string CacheKey = $"{nameof(AnnouncementService)}.Cache.{nameof(AnnouncementWrapper)}";
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ internal class AnnouncementService : IAnnouncementService
|
|||||||
// 缓存中存在记录,直接返回
|
// 缓存中存在记录,直接返回
|
||||||
if (memoryCache.TryGetValue(CacheKey, out object? cache))
|
if (memoryCache.TryGetValue(CacheKey, out object? cache))
|
||||||
{
|
{
|
||||||
return Must.NotNull((AnnouncementWrapper)cache);
|
return Must.NotNull((AnnouncementWrapper)cache!);
|
||||||
}
|
}
|
||||||
|
|
||||||
AnnouncementWrapper? wrapper = await announcementClient
|
AnnouncementWrapper? wrapper = await announcementClient
|
||||||
@@ -60,8 +60,7 @@ internal class AnnouncementService : IAnnouncementService
|
|||||||
|
|
||||||
private static void JoinAnnouncements(Dictionary<int, string> contentMap, List<AnnouncementListWrapper> announcementListWrappers)
|
private static void JoinAnnouncements(Dictionary<int, string> contentMap, List<AnnouncementListWrapper> announcementListWrappers)
|
||||||
{
|
{
|
||||||
// 匹配特殊的时间格式: <t>(.*?)</t>
|
Regex timeTagRegrex = XmlTagRegex();
|
||||||
Regex timeTagRegrex = new("<t.*?>(.*?)</t>", RegexOptions.Multiline);
|
|
||||||
|
|
||||||
announcementListWrappers.ForEach(listWrapper =>
|
announcementListWrappers.ForEach(listWrapper =>
|
||||||
{
|
{
|
||||||
@@ -77,4 +76,11 @@ internal class AnnouncementService : IAnnouncementService
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 匹配特殊的时间格式: <t>(.*?)</t>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>正则</returns>
|
||||||
|
[GeneratedRegex("<t.*?>(.*?)</t>", RegexOptions.Multiline)]
|
||||||
|
private static partial Regex XmlTagRegex();
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,6 @@ using Snap.Hutao.Context.Database;
|
|||||||
using Snap.Hutao.Core.Database;
|
using Snap.Hutao.Core.Database;
|
||||||
using Snap.Hutao.Core.Diagnostics;
|
using Snap.Hutao.Core.Diagnostics;
|
||||||
using Snap.Hutao.Core.Logging;
|
using Snap.Hutao.Core.Logging;
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Snap.Hutao.Model.Binding.AvatarProperty;
|
using Snap.Hutao.Model.Binding.AvatarProperty;
|
||||||
using Snap.Hutao.Model.Metadata;
|
using Snap.Hutao.Model.Metadata;
|
||||||
using Snap.Hutao.Service.AvatarInfo.Factory;
|
using Snap.Hutao.Service.AvatarInfo.Factory;
|
||||||
|
|||||||
@@ -83,5 +83,6 @@ internal static partial class ReliquaryWeightConfiguration
|
|||||||
new(AvatarIds.Nilou, 100, 75, 0, 100, 100, 0, 55, 0, "反应流") { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
|
new(AvatarIds.Nilou, 100, 75, 0, 100, 100, 0, 55, 0, "反应流") { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
|
||||||
new(AvatarIds.Cyno, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
|
new(AvatarIds.Cyno, 0, 75, 0, 100, 100, 75, 55, 0) { { FightProperty.FIGHT_PROP_ELEC_ADD_HURT, 100 } },
|
||||||
new(AvatarIds.Candace, 100, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
|
new(AvatarIds.Candace, 100, 75, 0, 100, 100, 0, 55, 0) { { FightProperty.FIGHT_PROP_WATER_ADD_HURT, 100 } },
|
||||||
|
new(AvatarIds.Nahida, 0, 55, 0, 100, 100, 100, 55, 0) { { FightProperty.FIGHT_PROP_GRASS_ADD_HURT, 100 } },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -147,13 +147,12 @@ internal static class SummaryHelper
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 物伤
|
// 物伤
|
||||||
if (fightPropMap.ContainsKey(FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT))
|
if (fightPropMap.TryGetValue(FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, out double addValue))
|
||||||
{
|
{
|
||||||
double value = fightPropMap[FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT];
|
if (addValue > 0)
|
||||||
if (value > 0)
|
|
||||||
{
|
{
|
||||||
string description = FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT.GetDescription();
|
string description = FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT.GetDescription();
|
||||||
Pair2<string, string, string?> physicalBonusPair2 = new(description, PropertyInfoDescriptor.FormatValue(FormatMethod.Percent, value), null);
|
Pair2<string, string, string?> physicalBonusPair2 = new(description, PropertyInfoDescriptor.FormatValue(FormatMethod.Percent, addValue), null);
|
||||||
properties.Add(physicalBonusPair2);
|
properties.Add(physicalBonusPair2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Snap.Hutao.Model.Binding.AvatarProperty;
|
using Snap.Hutao.Model.Binding.AvatarProperty;
|
||||||
using Snap.Hutao.Web.Hoyolab;
|
using Snap.Hutao.Web.Hoyolab;
|
||||||
|
|
||||||
|
|||||||
252
src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteService.cs
Normal file
252
src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteService.cs
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using CommunityToolkit.WinUI.Notifications;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Snap.Hutao.Context.Database;
|
||||||
|
using Snap.Hutao.Core.Database;
|
||||||
|
using Snap.Hutao.Extension;
|
||||||
|
using Snap.Hutao.Message;
|
||||||
|
using Snap.Hutao.Model.Binding.User;
|
||||||
|
using Snap.Hutao.Model.Entity;
|
||||||
|
using Snap.Hutao.Service.User;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using WebDailyNote = Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote.DailyNote;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Service.DailyNote;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实时便笺服务
|
||||||
|
/// </summary>
|
||||||
|
[Injection(InjectAs.Singleton, typeof(IDailyNoteService))]
|
||||||
|
internal class DailyNoteService : IDailyNoteService, IRecipient<UserRemovedMessage>
|
||||||
|
{
|
||||||
|
private readonly IServiceScopeFactory scopeFactory;
|
||||||
|
private readonly IUserService userService;
|
||||||
|
private ObservableCollection<DailyNoteEntry>? entries;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的实时便笺服务
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scopeFactory">范围工厂</param>
|
||||||
|
/// <param name="userService">用户服务</param>
|
||||||
|
/// <param name="messenger">消息器</param>
|
||||||
|
public DailyNoteService(IServiceScopeFactory scopeFactory, IUserService userService, IMessenger messenger)
|
||||||
|
{
|
||||||
|
this.scopeFactory = scopeFactory;
|
||||||
|
this.userService = userService;
|
||||||
|
|
||||||
|
messenger.Register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Receive(UserRemovedMessage message)
|
||||||
|
{
|
||||||
|
entries?.RemoveWhere(n => n.UserId == message.RemovedUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task AddDailyNoteAsync(UserAndRole role)
|
||||||
|
{
|
||||||
|
string roleUid = role.Role.GameUid;
|
||||||
|
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
GameRecordClient gameRecordClient = scope.ServiceProvider.GetRequiredService<GameRecordClient>();
|
||||||
|
|
||||||
|
if (!appDbContext.DailyNotes.Any(n => n.Uid == roleUid))
|
||||||
|
{
|
||||||
|
DailyNoteEntry newEntry = DailyNoteEntry.Create(role);
|
||||||
|
newEntry.DailyNote = await gameRecordClient.GetDialyNoteAsync(role.User, newEntry.Uid).ConfigureAwait(false);
|
||||||
|
appDbContext.DailyNotes.AddAndSave(newEntry);
|
||||||
|
|
||||||
|
newEntry.UserGameRole = userService.GetUserGameRoleByUid(roleUid);
|
||||||
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
|
entries?.Add(newEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<ObservableCollection<DailyNoteEntry>> GetDailyNoteEntriesAsync()
|
||||||
|
{
|
||||||
|
if (entries == null)
|
||||||
|
{
|
||||||
|
await RefreshDailyNotesAsync(false).ConfigureAwait(false);
|
||||||
|
|
||||||
|
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
List<DailyNoteEntry> entryList = appDbContext.DailyNotes.ToList();
|
||||||
|
entryList.ForEach(entry => { entry.UserGameRole = userService.GetUserGameRoleByUid(entry.Uid); });
|
||||||
|
entries = new(appDbContext.DailyNotes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async ValueTask RefreshDailyNotesAsync(bool notify)
|
||||||
|
{
|
||||||
|
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
GameRecordClient gameRecordClient = scope.ServiceProvider.GetRequiredService<GameRecordClient>();
|
||||||
|
BindingClient bindingClient = scope.ServiceProvider.GetRequiredService<BindingClient>();
|
||||||
|
|
||||||
|
foreach (DailyNoteEntry entry in appDbContext.DailyNotes.Include(n => n.User))
|
||||||
|
{
|
||||||
|
WebDailyNote? dailyNote = await gameRecordClient.GetDialyNoteAsync(entry.User, entry.Uid).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// database
|
||||||
|
entry.DailyNote = dailyNote;
|
||||||
|
|
||||||
|
// cache
|
||||||
|
Guid userId = entry.UserId;
|
||||||
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
|
entries?.Single(e => e.UserId == userId).UpdateDailyNote(dailyNote);
|
||||||
|
|
||||||
|
if (notify)
|
||||||
|
{
|
||||||
|
await NotifyDailyNoteAsync(bindingClient, entry).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await appDbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void RemoveDailyNote(DailyNoteEntry entry)
|
||||||
|
{
|
||||||
|
entries!.Remove(entry);
|
||||||
|
|
||||||
|
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
appDbContext.DailyNotes.RemoveAndSave(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ValueTask NotifyDailyNoteAsync(BindingClient client, DailyNoteEntry entry)
|
||||||
|
{
|
||||||
|
if (entry.DailyNote == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<string> hints = new();
|
||||||
|
|
||||||
|
// NotifySuppressed judge
|
||||||
|
{
|
||||||
|
if (entry.DailyNote.CurrentResin >= entry.ResinNotifyThreshold)
|
||||||
|
{
|
||||||
|
if (!entry.ResinNotifySuppressed)
|
||||||
|
{
|
||||||
|
hints.Add($"当前原粹树脂:{entry.DailyNote.CurrentResin}");
|
||||||
|
entry.ResinNotifySuppressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry.ResinNotifySuppressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.DailyNote.CurrentHomeCoin >= entry.HomeCoinNotifyThreshold)
|
||||||
|
{
|
||||||
|
if (!entry.HomeCoinNotifySuppressed)
|
||||||
|
{
|
||||||
|
hints.Add($"当前洞天宝钱:{entry.DailyNote.CurrentHomeCoin}");
|
||||||
|
entry.HomeCoinNotifySuppressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry.HomeCoinNotifySuppressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.DailyTaskNotify && !entry.DailyNote.IsExtraTaskRewardReceived)
|
||||||
|
{
|
||||||
|
if (!entry.DailyTaskNotifySuppressed)
|
||||||
|
{
|
||||||
|
hints.Add(entry.DailyNote.ExtraTaskRewardDescription);
|
||||||
|
entry.DailyTaskNotifySuppressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry.DailyTaskNotifySuppressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.TransformerNotify && entry.DailyNote.Transformer.Obtained && entry.DailyNote.Transformer.RecoveryTime.Reached)
|
||||||
|
{
|
||||||
|
if (!entry.TransformerNotifySuppressed)
|
||||||
|
{
|
||||||
|
hints.Add("参量质变仪已准备完成");
|
||||||
|
entry.TransformerNotifySuppressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry.TransformerNotifySuppressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.ExpeditionNotify && entry.DailyNote.Expeditions.All(e => e.Status == ExpeditionStatus.Finished))
|
||||||
|
{
|
||||||
|
if (!entry.ExpeditionNotifySuppressed)
|
||||||
|
{
|
||||||
|
hints.Add("探索派遣已完成");
|
||||||
|
entry.ExpeditionNotifySuppressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry.ExpeditionNotifySuppressed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hints.Count <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<UserGameRole> roles = await client.GetUserGameRolesAsync(entry.User).ConfigureAwait(false);
|
||||||
|
string attribution = roles.SingleOrDefault(r => r.GameUid == entry.Uid)?.ToString() ?? "未知角色";
|
||||||
|
|
||||||
|
ToastContentBuilder builder = new ToastContentBuilder()
|
||||||
|
.AddHeader("DAILYNOTE", "实时便笺提醒", "DAILYNOTE")
|
||||||
|
.AddAttributionText(attribution)
|
||||||
|
.AddButton(new ToastButton().SetContent("开始游戏").AddArgument("Action", "LaunchGame").AddArgument("Uid", entry.Uid))
|
||||||
|
.AddButton(new ToastButtonDismiss("我知道了"));
|
||||||
|
|
||||||
|
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
if (appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteReminderNotify, false.ToString()).GetBoolean())
|
||||||
|
{
|
||||||
|
builder.SetToastScenario(ToastScenario.Reminder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hints.Count > 2)
|
||||||
|
{
|
||||||
|
builder.AddText("多个提醒项达到设定值");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (string hint in hints)
|
||||||
|
{
|
||||||
|
builder.AddText(hint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
|
builder.Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Model.Binding.User;
|
||||||
|
using Snap.Hutao.Model.Entity;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Service.DailyNote;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实时便笺服务
|
||||||
|
/// </summary>
|
||||||
|
public interface IDailyNoteService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 添加实时便笺
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="role">角色</param>
|
||||||
|
/// <returns>任务</returns>
|
||||||
|
Task AddDailyNoteAsync(UserAndRole role);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取实时便笺列表
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>实时便笺列表</returns>
|
||||||
|
Task<ObservableCollection<DailyNoteEntry>> GetDailyNoteEntriesAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步刷新实时便笺
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="notify">是否通知</param>
|
||||||
|
/// <returns>任务</returns>
|
||||||
|
ValueTask RefreshDailyNotesAsync(bool notify);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除指定的实时便笺
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entry">指定的实时便笺</param>
|
||||||
|
void RemoveDailyNote(DailyNoteEntry entry);
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ using Snap.Hutao.Core.Abstraction;
|
|||||||
using Snap.Hutao.Core.Database;
|
using Snap.Hutao.Core.Database;
|
||||||
using Snap.Hutao.Core.Diagnostics;
|
using Snap.Hutao.Core.Diagnostics;
|
||||||
using Snap.Hutao.Core.Logging;
|
using Snap.Hutao.Core.Logging;
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Snap.Hutao.Extension;
|
using Snap.Hutao.Extension;
|
||||||
using Snap.Hutao.Model.Binding.Gacha;
|
using Snap.Hutao.Model.Binding.Gacha;
|
||||||
using Snap.Hutao.Model.Binding.Gacha.Abstraction;
|
using Snap.Hutao.Model.Binding.Gacha.Abstraction;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Snap.Hutao.View.Dialog;
|
using Snap.Hutao.View.Dialog;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.GachaLog;
|
namespace Snap.Hutao.Service.GachaLog;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Snap.Hutao.Service.User;
|
using Snap.Hutao.Service.User;
|
||||||
using Snap.Hutao.Web.Hoyolab;
|
using Snap.Hutao.Web.Hoyolab;
|
||||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||||
@@ -35,7 +34,7 @@ internal class GachaLogUrlStokenProvider : IGachaLogUrlProvider
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<ValueResult<bool, string>> GetQueryAsync()
|
public async Task<ValueResult<bool, string>> GetQueryAsync()
|
||||||
{
|
{
|
||||||
Model.Binding.User? user = userService.Current;
|
Model.Binding.User.User? user = userService.Current;
|
||||||
if (user != null && user.SelectedUserGameRole != null)
|
if (user != null && user.SelectedUserGameRole != null)
|
||||||
{
|
{
|
||||||
if (user.Cookie!.ContainsSToken())
|
if (user.Cookie!.ContainsSToken())
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.IO;
|
using Snap.Hutao.Core.IO;
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Snap.Hutao.Extension;
|
|
||||||
using Snap.Hutao.Service.Game;
|
using Snap.Hutao.Service.Game;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Abstraction;
|
using Snap.Hutao.Core.Abstraction;
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.GachaLog;
|
namespace Snap.Hutao.Service.GachaLog;
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ using Snap.Hutao.Context.Database;
|
|||||||
using Snap.Hutao.Core;
|
using Snap.Hutao.Core;
|
||||||
using Snap.Hutao.Core.Database;
|
using Snap.Hutao.Core.Database;
|
||||||
using Snap.Hutao.Core.IO.Ini;
|
using Snap.Hutao.Core.IO.Ini;
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Snap.Hutao.Model.Binding.LaunchGame;
|
using Snap.Hutao.Model.Binding.LaunchGame;
|
||||||
using Snap.Hutao.Model.Entity;
|
using Snap.Hutao.Model.Entity;
|
||||||
using Snap.Hutao.Service.Game.Locator;
|
using Snap.Hutao.Service.Game.Locator;
|
||||||
@@ -24,6 +23,7 @@ namespace Snap.Hutao.Service.Game;
|
|||||||
/// 游戏服务
|
/// 游戏服务
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Injection(InjectAs.Singleton, typeof(IGameService))]
|
[Injection(InjectAs.Singleton, typeof(IGameService))]
|
||||||
|
[SuppressMessage("", "CA1001")]
|
||||||
internal class GameService : IGameService
|
internal class GameService : IGameService
|
||||||
{
|
{
|
||||||
private const string GamePathKey = $"{nameof(GameService)}.Cache.{SettingEntry.GamePath}";
|
private const string GamePathKey = $"{nameof(GameService)}.Cache.{SettingEntry.GamePath}";
|
||||||
@@ -168,25 +168,38 @@ internal class GameService : IGameService
|
|||||||
elements = IniSerializer.Deserialize(readStream).ToList();
|
elements = IniSerializer.Deserialize(readStream).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
foreach (IniElement element in elements)
|
foreach (IniElement element in elements)
|
||||||
{
|
{
|
||||||
if (element is IniParameter parameter)
|
if (element is IniParameter parameter)
|
||||||
{
|
{
|
||||||
if (parameter.Key == "channel")
|
if (parameter.Key == "channel")
|
||||||
{
|
{
|
||||||
parameter.Value = scheme.Channel;
|
if (parameter.Value != scheme.Channel)
|
||||||
|
{
|
||||||
|
parameter.Value = scheme.Channel;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parameter.Key == "sub_channel")
|
if (parameter.Key == "sub_channel")
|
||||||
{
|
{
|
||||||
parameter.Value = scheme.SubChannel;
|
if (parameter.Value != scheme.SubChannel)
|
||||||
|
{
|
||||||
|
parameter.Value = scheme.SubChannel;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using (FileStream writeStream = File.Create(configPath))
|
if (changed)
|
||||||
{
|
{
|
||||||
IniSerializer.Serialize(writeStream, elements);
|
using (FileStream writeStream = File.Create(configPath))
|
||||||
|
{
|
||||||
|
IniSerializer.Serialize(writeStream, elements);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,6 +353,7 @@ internal class GameService : IGameService
|
|||||||
|
|
||||||
if (isOk)
|
if (isOk)
|
||||||
{
|
{
|
||||||
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
gameAccount.UpdateName(name);
|
gameAccount.UpdateName(name);
|
||||||
|
|
||||||
// sync database
|
// sync database
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Snap.Hutao.Model.Binding.LaunchGame;
|
using Snap.Hutao.Model.Binding.LaunchGame;
|
||||||
using Snap.Hutao.Model.Entity;
|
using Snap.Hutao.Model.Entity;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Abstraction;
|
using Snap.Hutao.Core.Abstraction;
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Game.Locator;
|
namespace Snap.Hutao.Service.Game.Locator;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Snap.Hutao.Factory.Abstraction;
|
using Snap.Hutao.Factory.Abstraction;
|
||||||
using Windows.Storage;
|
using Windows.Storage;
|
||||||
using Windows.Storage.Pickers;
|
using Windows.Storage.Pickers;
|
||||||
@@ -44,7 +43,7 @@ internal class ManualGameLocator : IGameLocator
|
|||||||
{
|
{
|
||||||
FileOpenPicker picker = pickerFactory.GetFileOpenPicker();
|
FileOpenPicker picker = pickerFactory.GetFileOpenPicker();
|
||||||
picker.FileTypeFilter.Add(".exe");
|
picker.FileTypeFilter.Add(".exe");
|
||||||
picker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
|
picker.SuggestedStartLocation = PickerLocationId.Desktop;
|
||||||
|
|
||||||
// System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT E_FAIL has been returned from a call to a COM component.
|
// System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT E_FAIL has been returned from a call to a COM component.
|
||||||
// Not sure what's going on here.
|
// Not sure what's going on here.
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using Snap.Hutao.Core.IO.Ini;
|
using Snap.Hutao.Core.IO.Ini;
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
@@ -13,7 +12,7 @@ namespace Snap.Hutao.Service.Game.Locator;
|
|||||||
/// 注册表启动器位置定位器
|
/// 注册表启动器位置定位器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Injection(InjectAs.Transient, typeof(IGameLocator))]
|
[Injection(InjectAs.Transient, typeof(IGameLocator))]
|
||||||
internal class RegistryLauncherLocator : IGameLocator
|
internal partial class RegistryLauncherLocator : IGameLocator
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public string Name { get => nameof(RegistryLauncherLocator); }
|
public string Name { get => nameof(RegistryLauncherLocator); }
|
||||||
@@ -76,7 +75,7 @@ internal class RegistryLauncherLocator : IGameLocator
|
|||||||
|
|
||||||
private static string Unescape(string str)
|
private static string Unescape(string str)
|
||||||
{
|
{
|
||||||
string? hex4Result = Regex.Replace(str, @"\\x([0-9a-f]{4})", @"\u$1");
|
string? hex4Result = Utf16Regex().Replace(str, @"\u$1");
|
||||||
|
|
||||||
// 不包含中文
|
// 不包含中文
|
||||||
if (!hex4Result.Contains(@"\u"))
|
if (!hex4Result.Contains(@"\u"))
|
||||||
@@ -87,4 +86,7 @@ internal class RegistryLauncherLocator : IGameLocator
|
|||||||
|
|
||||||
return Regex.Unescape(hex4Result);
|
return Regex.Unescape(hex4Result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[GeneratedRegex("\\\\x([0-9a-f]{4})")]
|
||||||
|
private static partial Regex Utf16Regex();
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ internal class HutaoService : IHutaoService
|
|||||||
string key = $"{nameof(HutaoService)}.Cache.{typeName}";
|
string key = $"{nameof(HutaoService)}.Cache.{typeName}";
|
||||||
if (memoryCache.TryGetValue(key, out object? cache))
|
if (memoryCache.TryGetValue(key, out object? cache))
|
||||||
{
|
{
|
||||||
return (T)cache;
|
return (T)cache!;
|
||||||
}
|
}
|
||||||
|
|
||||||
T web = await taskFunc(default).ConfigureAwait(false);
|
T web = await taskFunc(default).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Snap.Hutao.Service.Abstraction;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service;
|
namespace Snap.Hutao.Service;
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ internal partial class MetadataService : IMetadataService, IMetadataInitializer,
|
|||||||
{
|
{
|
||||||
using (StreamWriter streamWriter = new(metadataContext.Create(fileFullName)))
|
using (StreamWriter streamWriter = new(metadataContext.Create(fileFullName)))
|
||||||
{
|
{
|
||||||
while (await streamReader.ReadLineAsync().ConfigureAwait(false) is string line)
|
while (await streamReader.ReadLineAsync(token).ConfigureAwait(false) is string line)
|
||||||
{
|
{
|
||||||
Func<string?, Task> write = streamReader.EndOfStream ? streamWriter.WriteAsync : streamWriter.WriteLineAsync;
|
Func<string?, Task> write = streamReader.EndOfStream ? streamWriter.WriteAsync : streamWriter.WriteLineAsync;
|
||||||
await write(line).ConfigureAwait(false);
|
await write(line).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Snap.Hutao.Web.Hoyolab;
|
using Snap.Hutao.Web.Hoyolab;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using BindingUser = Snap.Hutao.Model.Binding.User;
|
using BindingUser = Snap.Hutao.Model.Binding.User.User;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.User;
|
namespace Snap.Hutao.Service.User;
|
||||||
|
|
||||||
@@ -18,6 +18,12 @@ public interface IUserService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
BindingUser? Current { get; set; }
|
BindingUser? Current { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步获取角色与用户集合
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>角色与用户集合</returns>
|
||||||
|
Task<ObservableCollection<Model.Binding.User.UserAndRole>> GetRoleCollectionAsync();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化用户服务及所有用户
|
/// 初始化用户服务及所有用户
|
||||||
/// 异步获取同步的用户信息集合
|
/// 异步获取同步的用户信息集合
|
||||||
@@ -27,6 +33,13 @@ public interface IUserService
|
|||||||
/// <returns>准备完成的用户信息集合</returns>
|
/// <returns>准备完成的用户信息集合</returns>
|
||||||
Task<ObservableCollection<BindingUser>> GetUserCollectionAsync();
|
Task<ObservableCollection<BindingUser>> GetUserCollectionAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取角色信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">uid</param>
|
||||||
|
/// <returns>对应的角色信息</returns>
|
||||||
|
UserGameRole? GetUserGameRoleByUid(string uid);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 尝试异步处理输入的Cookie
|
/// 尝试异步处理输入的Cookie
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using BindingUser = Snap.Hutao.Model.Binding.User;
|
using BindingUser = Snap.Hutao.Model.Binding.User.User;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.User;
|
namespace Snap.Hutao.Service.User;
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ using CommunityToolkit.Mvvm.Messaging;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Context.Database;
|
||||||
using Snap.Hutao.Core.Database;
|
using Snap.Hutao.Core.Database;
|
||||||
using Snap.Hutao.Core.Threading;
|
using Snap.Hutao.Extension;
|
||||||
|
using Snap.Hutao.Message;
|
||||||
using Snap.Hutao.Web.Hoyolab;
|
using Snap.Hutao.Web.Hoyolab;
|
||||||
using Snap.Hutao.Web.Hoyolab.Bbs.User;
|
using Snap.Hutao.Web.Hoyolab.Bbs.User;
|
||||||
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
|
|
||||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using BindingUser = Snap.Hutao.Model.Binding.User;
|
using BindingUser = Snap.Hutao.Model.Binding.User.User;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.User;
|
namespace Snap.Hutao.Service.User;
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@ internal class UserService : IUserService
|
|||||||
|
|
||||||
private BindingUser? currentUser;
|
private BindingUser? currentUser;
|
||||||
private ObservableCollection<BindingUser>? userCollection;
|
private ObservableCollection<BindingUser>? userCollection;
|
||||||
|
private ObservableCollection<Model.Binding.User.UserAndRole>? roleCollection;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的用户服务
|
/// 构造一个新的用户服务
|
||||||
@@ -86,17 +87,21 @@ internal class UserService : IUserService
|
|||||||
public async Task RemoveUserAsync(BindingUser user)
|
public async Task RemoveUserAsync(BindingUser user)
|
||||||
{
|
{
|
||||||
await Task.Yield();
|
await Task.Yield();
|
||||||
Must.NotNull(userCollection!);
|
|
||||||
|
|
||||||
// Sync cache
|
// Sync cache
|
||||||
userCollection.Remove(user);
|
userCollection!.Remove(user);
|
||||||
|
roleCollection!.RemoveWhere(r => r.User.InnerId == user.Entity.InnerId);
|
||||||
|
|
||||||
// Sync database
|
// Sync database
|
||||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
|
||||||
|
// Note: cascade deleted dailynotes
|
||||||
appDbContext.Users.RemoveAndSave(user.Entity);
|
appDbContext.Users.RemoveAndSave(user.Entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
messenger.Send(new UserRemovedMessage(user.Entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -137,6 +142,38 @@ internal class UserService : IUserService
|
|||||||
return userCollection;
|
return userCollection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<ObservableCollection<Model.Binding.User.UserAndRole>> GetRoleCollectionAsync()
|
||||||
|
{
|
||||||
|
if (roleCollection == null)
|
||||||
|
{
|
||||||
|
List<Model.Binding.User.UserAndRole> userAndRoles = new();
|
||||||
|
ObservableCollection<BindingUser> observableUsers = await GetUserCollectionAsync().ConfigureAwait(false);
|
||||||
|
foreach (BindingUser user in observableUsers.ToList())
|
||||||
|
{
|
||||||
|
foreach (UserGameRole role in user.UserGameRoles)
|
||||||
|
{
|
||||||
|
userAndRoles.Add(new(user.Entity, role));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
roleCollection = new(userAndRoles);
|
||||||
|
}
|
||||||
|
|
||||||
|
return roleCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public UserGameRole? GetUserGameRoleByUid(string uid)
|
||||||
|
{
|
||||||
|
if (roleCollection != null)
|
||||||
|
{
|
||||||
|
return roleCollection.Single(r => r.Role.GameUid == uid).Role;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<ValueResult<UserOptionResult, string>> ProcessInputCookieAsync(Cookie cookie)
|
public async Task<ValueResult<UserOptionResult, string>> ProcessInputCookieAsync(Cookie cookie)
|
||||||
{
|
{
|
||||||
@@ -148,7 +185,7 @@ internal class UserService : IUserService
|
|||||||
{
|
{
|
||||||
// 检查 login ticket 是否存在
|
// 检查 login ticket 是否存在
|
||||||
// 若存在则尝试升级至 stoken
|
// 若存在则尝试升级至 stoken
|
||||||
await TryAddMultiTokenAsync(cookie, uid).ConfigureAwait(false);
|
await cookie.TryAddMultiTokenAsync(uid).ConfigureAwait(false);
|
||||||
|
|
||||||
// 检查 uid 对应用户是否存在
|
// 检查 uid 对应用户是否存在
|
||||||
if (UserHelper.TryGetUserByUid(userCollection, uid, out BindingUser? userWithSameUid))
|
if (UserHelper.TryGetUserByUid(userCollection, uid, out BindingUser? userWithSameUid))
|
||||||
@@ -180,32 +217,14 @@ internal class UserService : IUserService
|
|||||||
}
|
}
|
||||||
else if (cookie.ContainsLTokenAndCookieToken())
|
else if (cookie.ContainsLTokenAndCookieToken())
|
||||||
{
|
{
|
||||||
return await TryCreateUserAndAddAsync(userCollection, cookie).ConfigureAwait(false);
|
return await TryCreateUserAndAddAsync(cookie).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new(UserOptionResult.Incomplete, null!);
|
return new(UserOptionResult.Incomplete, null!);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task TryAddMultiTokenAsync(Cookie cookie, string uid)
|
private async Task<ValueResult<UserOptionResult, string>> TryCreateUserAndAddAsync(Cookie cookie)
|
||||||
{
|
|
||||||
if (cookie.TryGetLoginTicket(out string? loginTicket))
|
|
||||||
{
|
|
||||||
// get multitoken
|
|
||||||
Dictionary<string, string> multiToken = await Ioc.Default
|
|
||||||
.GetRequiredService<AuthClient>()
|
|
||||||
.GetMultiTokenByLoginTicketAsync(loginTicket, uid, default)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (multiToken.Count >= 2)
|
|
||||||
{
|
|
||||||
cookie.InsertMultiToken(uid, multiToken);
|
|
||||||
cookie.RemoveLoginTicket();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<ValueResult<UserOptionResult, string>> TryCreateUserAndAddAsync(ObservableCollection<BindingUser> users, Cookie cookie)
|
|
||||||
{
|
{
|
||||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
@@ -217,8 +236,19 @@ internal class UserService : IUserService
|
|||||||
if (newUser != null)
|
if (newUser != null)
|
||||||
{
|
{
|
||||||
// Sync cache
|
// Sync cache
|
||||||
await ThreadHelper.SwitchToMainThreadAsync();
|
if (userCollection != null)
|
||||||
users.Add(newUser);
|
{
|
||||||
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
|
userCollection!.Add(newUser);
|
||||||
|
|
||||||
|
if (roleCollection != null)
|
||||||
|
{
|
||||||
|
foreach (UserGameRole role in newUser.UserGameRoles)
|
||||||
|
{
|
||||||
|
roleCollection.Add(new(newUser.Entity, role));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sync database
|
// Sync database
|
||||||
appDbContext.Users.AddAndSave(newUser.Entity);
|
appDbContext.Users.AddAndSave(newUser.Entity);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0-windows10.0.18362.0</TargetFramework>
|
<TargetFramework>net7.0-windows10.0.18362.0</TargetFramework>
|
||||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||||
<RootNamespace>Snap.Hutao</RootNamespace>
|
<RootNamespace>Snap.Hutao</RootNamespace>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
|
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
|
||||||
<PackageCertificateThumbprint>F8C2255969BEA4A681CED102771BF807856AEC02</PackageCertificateThumbprint>
|
<PackageCertificateThumbprint>F8C2255969BEA4A681CED102771BF807856AEC02</PackageCertificateThumbprint>
|
||||||
<AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm>
|
<AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm>
|
||||||
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
|
<AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision>
|
||||||
<AppxSymbolPackageEnabled>True</AppxSymbolPackageEnabled>
|
<AppxSymbolPackageEnabled>True</AppxSymbolPackageEnabled>
|
||||||
<GenerateTestArtifacts>True</GenerateTestArtifacts>
|
<GenerateTestArtifacts>True</GenerateTestArtifacts>
|
||||||
<AppxBundle>Never</AppxBundle>
|
<AppxBundle>Never</AppxBundle>
|
||||||
@@ -30,6 +30,7 @@
|
|||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<DebugType>embedded</DebugType>
|
<DebugType>embedded</DebugType>
|
||||||
|
<ApplicationIcon>Assets\Logo.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -48,7 +49,12 @@
|
|||||||
<None Remove="Resource\Icon\UI_Icon_Locked.png" />
|
<None Remove="Resource\Icon\UI_Icon_Locked.png" />
|
||||||
<None Remove="Resource\Icon\UI_Icon_None.png" />
|
<None Remove="Resource\Icon\UI_Icon_None.png" />
|
||||||
<None Remove="Resource\Icon\UI_ItemIcon_201.png" />
|
<None Remove="Resource\Icon\UI_ItemIcon_201.png" />
|
||||||
|
<None Remove="Resource\Icon\UI_ItemIcon_204.png" />
|
||||||
<None Remove="Resource\Icon\UI_ItemIcon_210.png" />
|
<None Remove="Resource\Icon\UI_ItemIcon_210.png" />
|
||||||
|
<None Remove="Resource\Icon\UI_ItemIcon_210_256.png" />
|
||||||
|
<None Remove="Resource\Icon\UI_ItemIcon_220021.png" />
|
||||||
|
<None Remove="Resource\Icon\UI_MarkQuest_Events_Proce.png" />
|
||||||
|
<None Remove="Resource\Icon\UI_MarkTower.png" />
|
||||||
<None Remove="Resource\Segoe Fluent Icons.ttf" />
|
<None Remove="Resource\Segoe Fluent Icons.ttf" />
|
||||||
<None Remove="stylecop.json" />
|
<None Remove="stylecop.json" />
|
||||||
<None Remove="View\Control\BottomTextControl.xaml" />
|
<None Remove="View\Control\BottomTextControl.xaml" />
|
||||||
@@ -59,6 +65,7 @@
|
|||||||
<None Remove="View\Dialog\AchievementArchiveCreateDialog.xaml" />
|
<None Remove="View\Dialog\AchievementArchiveCreateDialog.xaml" />
|
||||||
<None Remove="View\Dialog\AchievementImportDialog.xaml" />
|
<None Remove="View\Dialog\AchievementImportDialog.xaml" />
|
||||||
<None Remove="View\Dialog\AvatarInfoQueryDialog.xaml" />
|
<None Remove="View\Dialog\AvatarInfoQueryDialog.xaml" />
|
||||||
|
<None Remove="View\Dialog\DailyNoteNotificationDialog.xaml" />
|
||||||
<None Remove="View\Dialog\GachaLogImportDialog.xaml" />
|
<None Remove="View\Dialog\GachaLogImportDialog.xaml" />
|
||||||
<None Remove="View\Dialog\GachaLogRefreshProgressDialog.xaml" />
|
<None Remove="View\Dialog\GachaLogRefreshProgressDialog.xaml" />
|
||||||
<None Remove="View\Dialog\GachaLogUrlDialog.xaml" />
|
<None Remove="View\Dialog\GachaLogUrlDialog.xaml" />
|
||||||
@@ -107,36 +114,43 @@
|
|||||||
<Content Include="Resource\Icon\UI_Icon_Locked.png" />
|
<Content Include="Resource\Icon\UI_Icon_Locked.png" />
|
||||||
<Content Include="Resource\Icon\UI_Icon_None.png" />
|
<Content Include="Resource\Icon\UI_Icon_None.png" />
|
||||||
<Content Include="Resource\Icon\UI_ItemIcon_201.png" />
|
<Content Include="Resource\Icon\UI_ItemIcon_201.png" />
|
||||||
|
<Content Include="Resource\Icon\UI_ItemIcon_204.png" />
|
||||||
<Content Include="Resource\Icon\UI_ItemIcon_210.png" />
|
<Content Include="Resource\Icon\UI_ItemIcon_210.png" />
|
||||||
|
<Content Include="Resource\Icon\UI_ItemIcon_210_256.png" />
|
||||||
|
<Content Include="Resource\Icon\UI_ItemIcon_220021.png" />
|
||||||
|
<Content Include="Resource\Icon\UI_MarkQuest_Events_Proce.png" />
|
||||||
|
<Content Include="Resource\Icon\UI_MarkTower.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.0.0" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0-preview1" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Behaviors" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.UI.Behaviors" Version="7.1.2" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0" />
|
||||||
<!-- The PrivateAssets & IncludeAssets of Microsoft.EntityFrameworkCore.Tools should be remove to prevent multiple deps files-->
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.0">
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.10" />
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.3.44">
|
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.4.27">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.0.64" />
|
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.0.64" />
|
||||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.63-beta">
|
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.104-beta">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.25211-preview" />
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.25231-preview" />
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.220930.4-preview2" />
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221109.1" />
|
||||||
<PackageReference Include="MiniExcel" Version="1.28.0" />
|
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.435">
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="TaskScheduler" Version="2.10.1" />
|
||||||
<Manifest Include="$(ApplicationManifest)" />
|
<Manifest Include="$(ApplicationManifest)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@@ -154,6 +168,11 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="View\Dialog\DailyNoteNotificationDialog.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Update="View\Page\DailyNotePage.xaml">
|
<Page Update="View\Page\DailyNotePage.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ public sealed partial class BottomTextControl : ContentControl
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public UIElement TopContent
|
public UIElement TopContent
|
||||||
{
|
{
|
||||||
get { return (UIElement)GetValue(TopContentProperty); }
|
get => (UIElement)GetValue(TopContentProperty);
|
||||||
set { SetValue(TopContentProperty, value); }
|
set => SetValue(TopContentProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -39,8 +39,8 @@ public sealed partial class BottomTextControl : ContentControl
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Text
|
public string Text
|
||||||
{
|
{
|
||||||
get { return (string)GetValue(TextProperty); }
|
get => (string)GetValue(TextProperty);
|
||||||
set { SetValue(TextProperty, value); }
|
set => SetValue(TextProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnTextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs dp)
|
private static void OnTextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs dp)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user