mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6971042dc | ||
|
|
87f1f2c46b | ||
|
|
c576d8f7c4 | ||
|
|
a0c1241b32 | ||
|
|
a3ab24554a | ||
|
|
9ae45a4cc4 | ||
|
|
f700faae14 | ||
|
|
57b51ed5ee | ||
|
|
5dfb7fbb63 | ||
|
|
046823245c | ||
|
|
0497d89559 | ||
|
|
9d364a291c | ||
|
|
c342147809 | ||
|
|
a86caaf229 | ||
|
|
d0b07f1308 | ||
|
|
409a223213 | ||
|
|
75ea2b807f | ||
|
|
719d934222 | ||
|
|
e8eed46d82 | ||
|
|
ff9b553a19 | ||
|
|
95d64c2895 | ||
|
|
558551c8ad | ||
|
|
d05c196b7c | ||
|
|
502fb6dbed | ||
|
|
4fa5270070 | ||
|
|
94fda223fc | ||
|
|
18103b4deb | ||
|
|
16ac52e71d | ||
|
|
73825d391e | ||
|
|
3b2eeb84a7 | ||
|
|
3e8655fd55 | ||
|
|
fe38e14ae8 | ||
|
|
a174493819 | ||
|
|
3a57d55c62 | ||
|
|
99f35ca6db | ||
|
|
c423e8b72d | ||
|
|
7ff78def46 | ||
|
|
bc9018f4bf | ||
|
|
3513268ad9 | ||
|
|
107963b7ac | ||
|
|
4e89406f2f | ||
|
|
850ea7ed4b | ||
|
|
fd59b471cb |
2
.github/ISSUE_TEMPLATE/CHS-bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/CHS-bug-report.yml
vendored
@@ -19,7 +19,7 @@ body:
|
||||
- label: 我已阅读 Snap Hutao 文档中的[常见问题](https://hut.ao/advanced/FAQ.html)和[常见程序异常](https://hut.ao/advanced/exceptions.html),我的问题没有在文档中得到解答
|
||||
required: true
|
||||
|
||||
- label: 我知道文档站的导航栏中有**搜索功能**,且已经搜索过相关关键词
|
||||
- label: 我知道[文档站](https://hut.ao/zh/menu.html)的导航栏中有**搜索功能**,且已经搜索过相关关键词
|
||||
required: true
|
||||
|
||||
- label: 我的问题不是[已完成](https://github.com/DGP-Studio/Snap.Hutao/issues?q=is%3Aopen+is%3Aissue+label%3A%E5%B7%B2%E5%AE%8C%E6%88%90)的问题也不是一个别人已发布的**重复的**问题
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: 功能请求
|
||||
name: 功能请求
|
||||
description: 通过这个议题来向开发团队分享你的想法
|
||||
title: "[Feat]: 在这里填写一个合适的标题"
|
||||
labels: ["功能", "needs-triage", "priority:none"]
|
||||
labels: ["feature request", "needs-triage", "priority:none"]
|
||||
assignees:
|
||||
- Lightczx
|
||||
body:
|
||||
@@ -24,4 +24,4 @@ body:
|
||||
label: 想要实现或优化的功能
|
||||
description: 详细的描述一下你想要的功能,描述的越具体,采纳的可能性越高
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Feature Request [English Form]
|
||||
description: Tell us about your thought
|
||||
title: "[Feat]: Place your title here"
|
||||
labels: ["功能", "needs-triage", "priority:none"]
|
||||
labels: ["feature request", "needs-triage", "priority:none"]
|
||||
assignees:
|
||||
- Lightczx
|
||||
body:
|
||||
@@ -22,6 +22,6 @@ body:
|
||||
id: req
|
||||
attributes:
|
||||
label: Detail of the Feature
|
||||
description: Descripbe the feaure in detail. The more detailed and convincing the desciprtion the more likyly feature will be accepted.
|
||||
description: Descripbe the feaure in detail. The more detailed and convincing the desciprtion the more likyly feature will be accepted.
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
|
||||
12
.github/workflows/alpha.yml
vendored
12
.github/workflows/alpha.yml
vendored
@@ -64,12 +64,10 @@ jobs:
|
||||
> 该版本是由 CI 程序自动打包生成的 `Alpha` 测试版本,**仅供开发者测试使用**
|
||||
|
||||
> [!TIP]
|
||||
> 普通用户请[点击这里](https://github.com/DGP-Studio/Snap.Hutao/releases/latest/)下载最新的稳定版本
|
||||
> 普通用户请 [点击这里](https://github.com/DGP-Studio/Snap.Hutao/releases/latest/) 下载最新的稳定版本
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 请注意,从 Snap Hutao Alpha 2023.12.21.3 开始,我们将使用全新的 CI 证书,原有的 Snap.Hutao.CI.cer 将在几天后过期停止使用。
|
||||
>
|
||||
> 请安装 [DGP_Studio_CA.crt](https://github.com/DGP-Automation/Hutao-Auto-Release/releases/download/certificate-ca/DGP_Studio_CA.crt) 到 `受信任的根证书颁发机构` 以安装测试版安装包
|
||||
> 请先安装 **[DGP_Studio_CA.crt](https://github.com/DGP-Automation/Hutao-Auto-Release/releases/download/certificate-ca/DGP_Studio_CA.crt)** 到 **受信任的根证书颁发机构** 以安装测试版安装包
|
||||
"
|
||||
|
||||
echo $summary >> $Env:GITHUB_STEP_SUMMARY
|
||||
@@ -111,12 +109,10 @@ jobs:
|
||||
> 该版本是由 CI 程序自动打包生成的 `Alpha` 测试版本,**仅供开发者测试使用**
|
||||
|
||||
> [!TIP]
|
||||
> 普通用户请[点击这里](https://github.com/DGP-Studio/Snap.Hutao/releases/latest/)下载最新的稳定版本
|
||||
> 普通用户请 [点击这里](https://github.com/DGP-Studio/Snap.Hutao/releases/latest/) 下载最新的稳定版本
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 请注意,从 Snap Hutao Alpha 2023.12.21.3 开始,我们将使用全新的 CI 证书,原有的 Snap.Hutao.CI.cer 将在几天后过期停止使用。
|
||||
>
|
||||
> 请安装 [DGP_Studio_CA.crt](https://github.com/DGP-Automation/Hutao-Auto-Release/releases/download/certificate-ca/DGP_Studio_CA.crt) 到 `受信任的根证书颁发机构` 以安装测试版安装包
|
||||
> 请先安装 **[DGP_Studio_CA.crt](https://github.com/DGP-Automation/Hutao-Auto-Release/releases/download/certificate-ca/DGP_Studio_CA.crt)** 到 **受信任的根证书颁发机构** 以安装测试版安装包
|
||||
"
|
||||
|
||||
echo $summary >> $Env:GITHUB_STEP_SUMMARY
|
||||
|
||||
@@ -72,9 +72,9 @@ Snap Hutao uses [Crowdin](https://translate.hut.ao/) as a client text translatio
|
||||
Snap Hutao is currently using sponsored software from the following service providers.
|
||||
|
||||
| [](https://www.netlify.com/) | [](https://crowdin.com/) | [](https://gitlab.cn/) |
|
||||
|:-:|:-:|:-:|
|
||||
|[](https://about.signpath.io)|[](https://1password.com/)|[](https://www.digitalocean.com)|
|
||||
|[](https://www.jetbrains.com/opensource/)|||
|
||||
| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
|
||||
| [](https://about.signpath.io) | [](https://1password.com/) | [](https://www.digitalocean.com) |
|
||||
| [](https://hi.ducalis.io/) | [](https://www.jetbrains.com/opensource/) | |
|
||||
|
||||
- Netlify provides document and home page hosting service for Snap Hutao
|
||||
|
||||
@@ -88,6 +88,8 @@ Snap Hutao is currently using sponsored software from the following service prov
|
||||
|
||||
- DigitalOcean provides reliable cloud database for Snap Hutao database backup
|
||||
|
||||
- [Ducalis.io](https://hi.ducalis.io/) provides Snap Hutao project with a complete decision-making toolkit for project management
|
||||
|
||||
- Jetbrains provides powerful IDE for Snap Hutao infrastructure services coding
|
||||
|
||||
## 开发 / Development
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/Uri.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/WindowOverride.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///View/Card/Primitive/CardProgressBar.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///View/Control/RateDeltaTextBlockStyle.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.Windows.AppLifecycle;
|
||||
using Microsoft.Windows.AppNotifications;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
@@ -68,6 +69,10 @@ public sealed partial class App : Application
|
||||
{
|
||||
try
|
||||
{
|
||||
// Important: You must call AppNotificationManager::Default().Register
|
||||
// before calling AppInstance.GetCurrent.GetActivatedEventArgs.
|
||||
AppNotificationManager.Default.NotificationInvoked += activation.NotificationInvoked;
|
||||
AppNotificationManager.Default.Register();
|
||||
AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
|
||||
|
||||
if (serviceProvider.GetRequiredService<PrivateNamedPipeClient>().TryRedirectActivationTo(activatedEventArgs))
|
||||
@@ -81,14 +86,7 @@ public sealed partial class App : Application
|
||||
LogDiagnosticInformation();
|
||||
|
||||
// Manually invoke
|
||||
HutaoActivationArguments hutaoArgs = HutaoActivationArguments.FromAppActivationArguments(activatedEventArgs);
|
||||
if (hutaoArgs.Kind is HutaoActivationKind.Toast)
|
||||
{
|
||||
Exit();
|
||||
return;
|
||||
}
|
||||
|
||||
activation.Activate(hutaoArgs);
|
||||
activation.Activate(HutaoActivationArguments.FromAppActivationArguments(activatedEventArgs));
|
||||
activation.PostInitialization();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -21,8 +21,6 @@ namespace Snap.Hutao.Control.Image;
|
||||
[DependencyProperty("CachedName", typeof(string), "Unknown")]
|
||||
internal sealed partial class CachedImage : Implementation.ImageEx
|
||||
{
|
||||
private string? file;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的缓存图像
|
||||
/// </summary>
|
||||
@@ -43,7 +41,6 @@ internal sealed partial class CachedImage : Implementation.ImageEx
|
||||
HutaoException.ThrowIf(string.IsNullOrEmpty(imageUri.Host), SH.ControlImageCachedImageInvalidResourceUri);
|
||||
string file = await imageCache.GetFileFromCacheAsync(imageUri).ConfigureAwait(true); // BitmapImage need to be created by main thread.
|
||||
CachedName = Path.GetFileName(file);
|
||||
this.file = file;
|
||||
token.ThrowIfCancellationRequested(); // check token state to determine whether the operation should be canceled.
|
||||
return file.ToUri();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
x:Name="ContentGrid"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
x:Load="False">
|
||||
x:Load="True">
|
||||
<ContentPresenter.RenderTransform>
|
||||
<CompositeTransform/>
|
||||
</ContentPresenter.RenderTransform>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<shmmc:AchievementIconConverter x:Key="AchievementIconConverter"/>
|
||||
<shmmc:AvatarCardConverter x:Key="AvatarCardConverter"/>
|
||||
<shmmc:AvatarIconConverter x:Key="AvatarIconConverter"/>
|
||||
<shmmc:AvatarIconCircleConverter x:Key="AvatarIconCircleConverter"/>
|
||||
<shmmc:AvatarNameCardPicConverter x:Key="AvatarNameCardPicConverter"/>
|
||||
<shmmc:AvatarSideIconConverter x:Key="AvatarSideIconConverter"/>
|
||||
<shmmc:DescriptionsParametersDescriptor x:Key="DescParamDescriptor"/>
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
<CornerRadius x:Key="ControlCornerRadiusTop">4,4,0,0</CornerRadius>
|
||||
<CornerRadius x:Key="ControlCornerRadiusBottom">0,0,4,4</CornerRadius>
|
||||
<CornerRadius x:Key="ControlCornerRadiusTopRightAndBottomLeft">0,4,0,4</CornerRadius>
|
||||
<CornerRadius x:Key="CornerRadiusAll16">16</CornerRadius>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
<ItemsPanelTemplate x:Key="HorizontalStackPanelSpacing4Template">
|
||||
<StackPanel Orientation="Horizontal" Spacing="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="HorizontalStackPanelSpacing6Template">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6"/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="StackPanelSpacing4Template">
|
||||
<StackPanel Spacing="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Notifications;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.Windows.AppNotifications;
|
||||
using Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Core.Shell;
|
||||
@@ -11,7 +11,6 @@ using Snap.Hutao.Core.Windowing;
|
||||
using Snap.Hutao.Core.Windowing.HotKey;
|
||||
using Snap.Hutao.Core.Windowing.NotifyIcon;
|
||||
using Snap.Hutao.Service;
|
||||
using Snap.Hutao.Service.DailyNote;
|
||||
using Snap.Hutao.Service.Discord;
|
||||
using Snap.Hutao.Service.Hutao;
|
||||
using Snap.Hutao.Service.Job;
|
||||
@@ -37,12 +36,10 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
||||
public const string ImportUIAFFromClipboard = nameof(ImportUIAFFromClipboard);
|
||||
|
||||
private const string CategoryAchievement = "ACHIEVEMENT";
|
||||
private const string CategoryDailyNote = "DAILYNOTE";
|
||||
private const string UrlActionImport = "/IMPORT";
|
||||
private const string UrlActionRefresh = "/REFRESH";
|
||||
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly ICurrentXamlWindowReference currentWindowReference;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
private readonly SemaphoreSlim activateSemaphore = new(1);
|
||||
@@ -50,7 +47,47 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
||||
/// <inheritdoc/>
|
||||
public void Activate(HutaoActivationArguments args)
|
||||
{
|
||||
HandleActivationAsync(args).SafeForget();
|
||||
HandleActivationExclusiveAsync(args).SafeForget();
|
||||
|
||||
async ValueTask HandleActivationExclusiveAsync(HutaoActivationArguments args)
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
|
||||
if (activateSemaphore.CurrentCount > 0)
|
||||
{
|
||||
using (await activateSemaphore.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
switch (args.Kind)
|
||||
{
|
||||
case HutaoActivationKind.Protocol:
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(args.ProtocolActivatedUri);
|
||||
await HandleProtocolActivationAsync(args.ProtocolActivatedUri, args.IsRedirectTo).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
case HutaoActivationKind.Launch:
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(args.LaunchActivatedArguments);
|
||||
await HandleLaunchActivationAsync(args.IsRedirectTo).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
case HutaoActivationKind.AppNotification:
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(args.AppNotificationActivatedArguments);
|
||||
await HandleAppNotificationActivationAsync(args.AppNotificationActivatedArguments, args.IsRedirectTo).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void NotificationInvoked(AppNotificationManager manager, AppNotificationActivatedEventArgs args)
|
||||
{
|
||||
HandleAppNotificationActivationAsync(args.Arguments, false).SafeForget();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -62,20 +99,23 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
|
||||
serviceProvider.GetRequiredService<PrivateNamedPipeServer>().RunAsync().SafeForget();
|
||||
ToastNotificationManagerCompat.OnActivated += NotificationActivate;
|
||||
|
||||
using (await activateSemaphore.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
// TODO: Introduced in 1.10.2, remove in later version
|
||||
serviceProvider.GetRequiredService<IJumpListInterop>().ClearAsync().SafeForget();
|
||||
serviceProvider.GetRequiredService<IScheduleTaskInterop>().UnregisterAllTasks();
|
||||
{
|
||||
serviceProvider.GetRequiredService<IJumpListInterop>().ClearAsync().SafeForget();
|
||||
serviceProvider.GetRequiredService<IScheduleTaskInterop>().UnregisterAllTasks();
|
||||
}
|
||||
|
||||
if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) < GuideState.Completed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
serviceProvider.GetRequiredService<PrivateNamedPipeServer>().RunAsync().SafeForget();
|
||||
|
||||
// RegisterHotKey should be called from main thread
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
serviceProvider.GetRequiredService<HotKeyOptions>().RegisterAll();
|
||||
|
||||
if (serviceProvider.GetRequiredService<AppOptions>().IsNotifyIconEnabled)
|
||||
@@ -87,7 +127,18 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
||||
_ = serviceProvider.GetRequiredService<NotifyIconController>();
|
||||
}
|
||||
|
||||
serviceProvider.GetRequiredService<IQuartzService>().StartAsync(default).SafeForget();
|
||||
serviceProvider.GetRequiredService<IDiscordService>().SetNormalActivityAsync().SafeForget();
|
||||
serviceProvider.GetRequiredService<IQuartzService>().StartAsync().SafeForget();
|
||||
|
||||
if (serviceProvider.GetRequiredService<IMetadataService>() is IMetadataServiceInitialization metadataServiceInitialization)
|
||||
{
|
||||
metadataServiceInitialization.InitializeInternalAsync().SafeForget();
|
||||
}
|
||||
|
||||
if (serviceProvider.GetRequiredService<IHutaoUserService>() is IHutaoUserServiceInitialization hutaoUserServiceInitialization)
|
||||
{
|
||||
hutaoUserServiceInitialization.InitializeInternalAsync().SafeForget();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,55 +184,52 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
||||
}
|
||||
}
|
||||
|
||||
private void NotificationActivate(ToastNotificationActivatedEventArgsCompat args)
|
||||
private async ValueTask HandleProtocolActivationAsync(Uri uri, bool isRedirectTo)
|
||||
{
|
||||
ToastArguments toastArgs = ToastArguments.Parse(args.Argument);
|
||||
UriBuilder builder = new(uri);
|
||||
|
||||
if (toastArgs.TryGetValue(Action, out string? action))
|
||||
{
|
||||
if (action == LaunchGame)
|
||||
{
|
||||
_ = toastArgs.TryGetValue(Uid, out string? uid);
|
||||
HandleLaunchGameActionAsync(uid).SafeForget();
|
||||
}
|
||||
}
|
||||
}
|
||||
string category = builder.Host.ToUpperInvariant();
|
||||
string action = builder.Path.ToUpperInvariant();
|
||||
|
||||
private async ValueTask HandleActivationAsync(HutaoActivationArguments args)
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
|
||||
if (activateSemaphore.CurrentCount > 0)
|
||||
// string parameter = builder.Query.ToUpperInvariant();
|
||||
switch (category)
|
||||
{
|
||||
using (await activateSemaphore.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
await HandleActivationCoreAsync(args).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask HandleActivationCoreAsync(HutaoActivationArguments args)
|
||||
{
|
||||
if (args.Kind is HutaoActivationKind.Protocol)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(args.ProtocolActivatedUri);
|
||||
await HandleUrlActivationAsync(args.ProtocolActivatedUri, args.IsRedirectTo).ConfigureAwait(false);
|
||||
}
|
||||
else if (args.Kind is HutaoActivationKind.Launch)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(args.LaunchActivatedArguments);
|
||||
switch (args.LaunchActivatedArguments)
|
||||
{
|
||||
default:
|
||||
case CategoryAchievement:
|
||||
{
|
||||
await WaitMainWindowOrCurrentAsync().ConfigureAwait(false);
|
||||
if (currentWindowReference.Window is not MainWindow)
|
||||
{
|
||||
await HandleNormalLaunchActionAsync(args.IsRedirectTo).ConfigureAwait(false);
|
||||
break;
|
||||
// TODO: Send notification to hint?
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case UrlActionImport:
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
INavigationAwaiter navigationAwaiter = new NavigationExtra(ImportUIAFFromClipboard);
|
||||
await serviceProvider
|
||||
.GetRequiredService<INavigationService>()
|
||||
.NavigateAsync<View.Page.AchievementPage>(navigationAwaiter, true)
|
||||
.ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
await HandleLaunchActivationAsync(isRedirectTo).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask HandleNormalLaunchActionAsync(bool isRedirectTo)
|
||||
private async ValueTask HandleLaunchActivationAsync(bool isRedirectTo)
|
||||
{
|
||||
if (!isRedirectTo)
|
||||
{
|
||||
@@ -213,6 +261,22 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
||||
await WaitMainWindowOrCurrentAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async ValueTask HandleAppNotificationActivationAsync(IDictionary<string, string> arguments, bool isRedirectTo)
|
||||
{
|
||||
if (arguments.TryGetValue(Action, out string? action))
|
||||
{
|
||||
if (action == LaunchGame)
|
||||
{
|
||||
_ = arguments.TryGetValue(Uid, out string? uid);
|
||||
await HandleLaunchGameActionAsync(uid).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await HandleLaunchActivationAsync(isRedirectTo).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask WaitMainWindowOrCurrentAsync()
|
||||
{
|
||||
if (currentWindowReference.Window is { } window)
|
||||
@@ -229,100 +293,5 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
||||
|
||||
mainWindow.SwitchTo();
|
||||
mainWindow.BringToForeground();
|
||||
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
|
||||
if (serviceProvider.GetRequiredService<IMetadataService>() is IMetadataServiceInitialization metadataServiceInitialization)
|
||||
{
|
||||
metadataServiceInitialization.InitializeInternalAsync().SafeForget();
|
||||
}
|
||||
|
||||
if (serviceProvider.GetRequiredService<IHutaoUserService>() is IHutaoUserServiceInitialization hutaoUserServiceInitialization)
|
||||
{
|
||||
hutaoUserServiceInitialization.InitializeInternalAsync().SafeForget();
|
||||
}
|
||||
|
||||
serviceProvider.GetRequiredService<IDiscordService>().SetNormalActivityAsync().SafeForget();
|
||||
}
|
||||
|
||||
private async ValueTask HandleUrlActivationAsync(Uri uri, bool isRedirectTo)
|
||||
{
|
||||
UriBuilder builder = new(uri);
|
||||
|
||||
string category = builder.Host.ToUpperInvariant();
|
||||
string action = builder.Path.ToUpperInvariant();
|
||||
string parameter = builder.Query.ToUpperInvariant();
|
||||
|
||||
switch (category)
|
||||
{
|
||||
case CategoryAchievement:
|
||||
{
|
||||
await WaitMainWindowOrCurrentAsync().ConfigureAwait(false);
|
||||
await HandleAchievementActionAsync(action, parameter, isRedirectTo).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
case CategoryDailyNote:
|
||||
{
|
||||
await HandleDailyNoteActionAsync(action, parameter, isRedirectTo).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
await HandleNormalLaunchActionAsync(isRedirectTo).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask HandleAchievementActionAsync(string action, string parameter, bool isRedirectTo)
|
||||
{
|
||||
_ = parameter;
|
||||
_ = isRedirectTo;
|
||||
switch (action)
|
||||
{
|
||||
case UrlActionImport:
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
INavigationAwaiter navigationAwaiter = new NavigationExtra(ImportUIAFFromClipboard);
|
||||
await serviceProvider
|
||||
.GetRequiredService<INavigationService>()
|
||||
.NavigateAsync<View.Page.AchievementPage>(navigationAwaiter, true)
|
||||
.ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask HandleDailyNoteActionAsync(string action, string parameter, bool isRedirectTo)
|
||||
{
|
||||
_ = parameter;
|
||||
switch (action)
|
||||
{
|
||||
case UrlActionRefresh:
|
||||
{
|
||||
try
|
||||
{
|
||||
await serviceProvider
|
||||
.GetRequiredService<IDailyNoteService>()
|
||||
.RefreshDailyNotesAsync()
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
// Check if it's redirected.
|
||||
if (!isRedirectTo)
|
||||
{
|
||||
// It's a direct open process, should exit immediately.
|
||||
Process.GetCurrentProcess().Kill();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Windows.AppLifecycle;
|
||||
using Microsoft.Windows.AppNotifications;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle;
|
||||
@@ -12,12 +13,6 @@ namespace Snap.Hutao.Core.LifeCycle;
|
||||
[HighQuality]
|
||||
internal static class AppActivationArgumentsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 尝试获取协议启动的Uri
|
||||
/// </summary>
|
||||
/// <param name="activatedEventArgs">应用程序激活参数</param>
|
||||
/// <param name="uri">协议Uri</param>
|
||||
/// <returns>是否存在协议Uri</returns>
|
||||
public static bool TryGetProtocolActivatedUri(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out Uri? uri)
|
||||
{
|
||||
uri = null;
|
||||
@@ -30,15 +25,10 @@ internal static class AppActivationArgumentsExtensions
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试获取启动的参数
|
||||
/// </summary>
|
||||
/// <param name="activatedEventArgs">应用程序激活参数</param>
|
||||
/// <param name="arguments">参数</param>
|
||||
/// <returns>是否存在参数</returns>
|
||||
public static bool TryGetLaunchActivatedArguments(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out string? arguments)
|
||||
{
|
||||
arguments = null;
|
||||
|
||||
if (activatedEventArgs.Data is not ILaunchActivatedEventArgs launchArgs)
|
||||
{
|
||||
return false;
|
||||
@@ -47,4 +37,21 @@ internal static class AppActivationArgumentsExtensions
|
||||
arguments = launchArgs.Arguments.Trim();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryGetAppNotificationActivatedArguments(this AppActivationArguments activatedEventArgs, out string? argument, [NotNullWhen(true)] out IDictionary<string, string>? arguments, [NotNullWhen(true)] out IDictionary<string, string>? userInput)
|
||||
{
|
||||
argument = null;
|
||||
arguments = null;
|
||||
userInput = null;
|
||||
|
||||
if (activatedEventArgs.Data is not AppNotificationActivatedEventArgs appNotificationArgs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
argument = appNotificationArgs.Argument;
|
||||
arguments = appNotificationArgs.Arguments;
|
||||
userInput = appNotificationArgs.UserInput;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Windows.AppLifecycle;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle;
|
||||
@@ -10,14 +9,16 @@ internal sealed class HutaoActivationArguments
|
||||
{
|
||||
public bool IsRedirectTo { get; set; }
|
||||
|
||||
public bool IsToastActivated { get; set; }
|
||||
|
||||
public HutaoActivationKind Kind { get; set; }
|
||||
|
||||
public Uri? ProtocolActivatedUri { get; set; }
|
||||
|
||||
public string? LaunchActivatedArguments { get; set; }
|
||||
|
||||
public IDictionary<string, string>? AppNotificationActivatedArguments { get; set; }
|
||||
|
||||
public IDictionary<string, string>? AppNotificationActivatedUserInput { get; set; }
|
||||
|
||||
public static HutaoActivationArguments FromAppActivationArguments(AppActivationArguments args, bool isRedirected = false)
|
||||
{
|
||||
HutaoActivationArguments result = new()
|
||||
@@ -33,15 +34,6 @@ internal sealed class HutaoActivationArguments
|
||||
if (args.TryGetLaunchActivatedArguments(out string? arguments))
|
||||
{
|
||||
result.LaunchActivatedArguments = arguments;
|
||||
|
||||
foreach (StringSegment segment in new StringTokenizer(arguments, [' ']))
|
||||
{
|
||||
if (segment.AsSpan().SequenceEqual("-ToastActivated"))
|
||||
{
|
||||
result.Kind = HutaoActivationKind.Toast;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -55,6 +47,19 @@ internal sealed class HutaoActivationArguments
|
||||
result.ProtocolActivatedUri = uri;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ExtendedActivationKind.AppNotification:
|
||||
{
|
||||
result.Kind = HutaoActivationKind.AppNotification;
|
||||
if (args.TryGetAppNotificationActivatedArguments(out string? argument, out IDictionary<string, string>? arguments, out IDictionary<string, string>? userInput))
|
||||
{
|
||||
result.LaunchActivatedArguments = argument;
|
||||
result.AppNotificationActivatedArguments = arguments;
|
||||
result.AppNotificationActivatedUserInput = userInput;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@ internal enum HutaoActivationKind
|
||||
{
|
||||
None,
|
||||
Launch,
|
||||
Toast,
|
||||
AppNotification,
|
||||
Protocol,
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Windows.AppNotifications;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle;
|
||||
|
||||
internal interface IAppActivation
|
||||
{
|
||||
void Activate(HutaoActivationArguments args);
|
||||
|
||||
void PostInitialization();
|
||||
}
|
||||
void NotificationInvoked(AppNotificationManager manager, AppNotificationActivatedEventArgs args);
|
||||
|
||||
internal interface IAppActivationActionHandlersAccess
|
||||
{
|
||||
ValueTask HandleLaunchGameActionAsync(string? uid = null);
|
||||
void PostInitialization();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle;
|
||||
|
||||
internal interface IAppActivationActionHandlersAccess
|
||||
{
|
||||
ValueTask HandleLaunchGameActionAsync(string? uid = null);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
|
||||
{
|
||||
private readonly PrivateNamedPipeMessageDispatcher messageDispatcher;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
private readonly ILogger<PrivateNamedPipeServer> logger;
|
||||
|
||||
private readonly CancellationTokenSource serverTokenSource = new();
|
||||
private readonly SemaphoreSlim serverSemaphore = new(1);
|
||||
@@ -23,6 +24,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
|
||||
{
|
||||
messageDispatcher = serviceProvider.GetRequiredService<PrivateNamedPipeMessageDispatcher>();
|
||||
runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
logger = serviceProvider.GetRequiredService<ILogger<PrivateNamedPipeServer>>();
|
||||
|
||||
PipeSecurity? pipeSecurity = default;
|
||||
|
||||
@@ -64,6 +66,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
|
||||
try
|
||||
{
|
||||
await serverStream.WaitForConnectionAsync(serverTokenSource.Token).ConfigureAwait(false);
|
||||
logger.LogInformation("Pipe session created");
|
||||
RunPacketSession(serverStream, serverTokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -78,6 +81,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
|
||||
while (serverStream.IsConnected && !token.IsCancellationRequested)
|
||||
{
|
||||
serverStream.ReadPacket(out PipePacketHeader header);
|
||||
logger.LogInformation("Pipe packet: [Type:{Type}] [Command:{Command}]", header.Type, header.Command);
|
||||
switch ((header.Type, header.Command))
|
||||
{
|
||||
case (PipePacketType.Request, PipePacketCommand.RequestElevationStatus):
|
||||
@@ -87,6 +91,11 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
|
||||
break;
|
||||
case (PipePacketType.Request, PipePacketCommand.RedirectActivation):
|
||||
HutaoActivationArguments? hutaoArgs = serverStream.ReadJsonContent<HutaoActivationArguments>(in header);
|
||||
if (hutaoArgs is not null)
|
||||
{
|
||||
logger.LogInformation("Redirect activation: [Kind:{Kind}] [Arguments:{Arguments}]", hutaoArgs.Kind, hutaoArgs.LaunchActivatedArguments);
|
||||
}
|
||||
|
||||
messageDispatcher.RedirectActivation(hutaoArgs);
|
||||
break;
|
||||
case (PipePacketType.SessionTermination, _):
|
||||
|
||||
@@ -25,7 +25,7 @@ internal class AsyncBarrier
|
||||
/// <param name="participants">The number of participants.</param>
|
||||
public AsyncBarrier(int participants)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(participants, 1, "Participants of AsyncBarrier can not be less than 1");
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(participants, "Participants of AsyncBarrier must be greater than 0");
|
||||
participantCount = participants;
|
||||
|
||||
// Allocate the stack so no resizing is necessary.
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Windows.Storage;
|
||||
using static Snap.Hutao.Win32.ConstValues;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing.NotifyIcon;
|
||||
@@ -26,9 +26,10 @@ internal sealed class NotifyIconController : IDisposable
|
||||
{
|
||||
lazyMenu = new(() => new(serviceProvider));
|
||||
|
||||
StorageFile iconFile = StorageFile.GetFileFromApplicationUriAsync("ms-appx:///Assets/Logo.ico".ToUri()).AsTask().GetAwaiter().GetResult();
|
||||
icon = new(iconFile.Path);
|
||||
id = Unsafe.As<byte, Guid>(ref MemoryMarshal.GetArrayDataReference(MD5.HashData(Encoding.UTF8.GetBytes(iconFile.Path))));
|
||||
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||
string iconPath = Path.Combine(runtimeOptions.InstalledLocation, "Assets/Logo.ico");
|
||||
icon = new(iconPath);
|
||||
id = Unsafe.As<byte, Guid>(ref MemoryMarshal.GetArrayDataReference(MD5.HashData(Encoding.UTF8.GetBytes(iconPath))));
|
||||
|
||||
xamlHostWindow = new(serviceProvider);
|
||||
xamlHostWindow.MoveAndResize(default);
|
||||
|
||||
@@ -31,6 +31,12 @@ internal static class WindowExtension
|
||||
return WindowControllers.TryGetValue(window, out _);
|
||||
}
|
||||
|
||||
public static void UninitializeController<TWindow>(this TWindow window)
|
||||
where TWindow : Window
|
||||
{
|
||||
WindowControllers.Remove(window);
|
||||
}
|
||||
|
||||
public static DesktopWindowXamlSource? GetDesktopWindowXamlSource(this Window window)
|
||||
{
|
||||
if (window.SystemBackdrop is SystemBackdropDesktopWindowXamlSourceAccess access)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Notifications;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Composition.SystemBackdrops;
|
||||
using Microsoft.UI.Content;
|
||||
@@ -9,6 +8,7 @@ using Microsoft.UI.Input;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.Windows.AppNotifications.Builder;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Core.Windowing.Abstraction;
|
||||
@@ -99,14 +99,13 @@ internal sealed class XamlWindowController
|
||||
|
||||
private void OnWindowClosed(object sender, WindowEventArgs args)
|
||||
{
|
||||
serviceProvider.GetRequiredService<AppOptions>().PropertyChanged -= OnOptionsPropertyChanged;
|
||||
|
||||
if (XamlLifetime.ApplicationLaunchedWithNotifyIcon && !XamlLifetime.ApplicationExiting)
|
||||
{
|
||||
args.Handled = true;
|
||||
window.Hide();
|
||||
|
||||
if (!IsNotifyIconVisible())
|
||||
{
|
||||
new ToastContentBuilder()
|
||||
new AppNotificationBuilder()
|
||||
.AddText(SH.CoreWindowingNotifyIconPromotedHint)
|
||||
.Show();
|
||||
}
|
||||
@@ -119,16 +118,15 @@ internal sealed class XamlWindowController
|
||||
|
||||
GC.Collect(GC.MaxGeneration);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (window is IXamlWindowRectPersisted rectPersisted)
|
||||
{
|
||||
SaveOrSkipWindowSize(rectPersisted);
|
||||
}
|
||||
|
||||
subclass?.Dispose();
|
||||
windowNonRudeHWND?.Dispose();
|
||||
if (window is IXamlWindowRectPersisted rectPersisted)
|
||||
{
|
||||
SaveOrSkipWindowSize(rectPersisted);
|
||||
}
|
||||
|
||||
subclass?.Dispose();
|
||||
windowNonRudeHWND?.Dispose();
|
||||
window.UninitializeController();
|
||||
}
|
||||
|
||||
private bool IsNotifyIconVisible()
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Windows.AppNotifications;
|
||||
using Microsoft.Windows.AppNotifications.Builder;
|
||||
|
||||
namespace Snap.Hutao.Extension;
|
||||
|
||||
internal static class AppNotificationBuilderExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Build and show the notification
|
||||
/// </summary>
|
||||
/// <param name="builder">this</param>
|
||||
/// <param name="manager">Defaults to <see cref="AppNotificationManager.Default"/></param>
|
||||
public static void Show(this AppNotificationBuilder builder, AppNotificationManager? manager = default)
|
||||
{
|
||||
(manager ?? AppNotificationManager.Default).Show(builder.BuildNotification());
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
using Snap.Hutao.Service;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Snap.Hutao.Factory.ContentDialog;
|
||||
|
||||
@@ -18,10 +19,27 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly AppOptions appOptions;
|
||||
|
||||
private readonly ConcurrentQueue<Func<Task>> dialogQueue = [];
|
||||
private bool isDialogShowing;
|
||||
|
||||
public bool IsDialogShowing
|
||||
{
|
||||
get
|
||||
{
|
||||
if (currentWindowReference.Window is not { } window)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return isDialogShowing;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<ContentDialogResult> CreateForConfirmAsync(string title, string content)
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
Microsoft.UI.Xaml.Controls.ContentDialog dialog = new()
|
||||
{
|
||||
XamlRoot = currentWindowReference.GetXamlRoot(),
|
||||
@@ -39,6 +57,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
public async ValueTask<ContentDialogResult> CreateForConfirmCancelAsync(string title, string content, ContentDialogButton defaultButton = ContentDialogButton.Close)
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
Microsoft.UI.Xaml.Controls.ContentDialog dialog = new()
|
||||
{
|
||||
XamlRoot = currentWindowReference.GetXamlRoot(),
|
||||
@@ -57,6 +76,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
public async ValueTask<Microsoft.UI.Xaml.Controls.ContentDialog> CreateForIndeterminateProgressAsync(string title)
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
Microsoft.UI.Xaml.Controls.ContentDialog dialog = new()
|
||||
{
|
||||
XamlRoot = currentWindowReference.GetXamlRoot(),
|
||||
@@ -72,9 +92,11 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
where TContentDialog : Microsoft.UI.Xaml.Controls.ContentDialog
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
TContentDialog contentDialog = serviceProvider.CreateInstance<TContentDialog>(parameters);
|
||||
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
|
||||
contentDialog.RequestedTheme = appOptions.ElementTheme;
|
||||
|
||||
return contentDialog;
|
||||
}
|
||||
|
||||
@@ -84,6 +106,51 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
TContentDialog contentDialog = serviceProvider.CreateInstance<TContentDialog>(parameters);
|
||||
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
|
||||
contentDialog.RequestedTheme = appOptions.ElementTheme;
|
||||
|
||||
return contentDialog;
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SH003")]
|
||||
public Task<ContentDialogResult> EnqueueAndShowAsync(Microsoft.UI.Xaml.Controls.ContentDialog contentDialog)
|
||||
{
|
||||
TaskCompletionSource<ContentDialogResult> dialogShowCompletionSource = new();
|
||||
|
||||
dialogQueue.Enqueue(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
ContentDialogResult result = await contentDialog.ShowAsync();
|
||||
dialogShowCompletionSource.SetResult(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
dialogShowCompletionSource.SetException(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ShowNextDialog().SafeForget();
|
||||
}
|
||||
});
|
||||
|
||||
if (!isDialogShowing)
|
||||
{
|
||||
ShowNextDialog();
|
||||
}
|
||||
|
||||
return dialogShowCompletionSource.Task;
|
||||
|
||||
Task ShowNextDialog()
|
||||
{
|
||||
if (dialogQueue.TryDequeue(out Func<Task>? showNextDialogAsync))
|
||||
{
|
||||
isDialogShowing = true;
|
||||
return showNextDialogAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
isDialogShowing = false;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ namespace Snap.Hutao.Factory.ContentDialog;
|
||||
[HighQuality]
|
||||
internal interface IContentDialogFactory
|
||||
{
|
||||
bool IsDialogShowing { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 异步确认
|
||||
/// </summary>
|
||||
@@ -40,4 +42,6 @@ internal interface IContentDialogFactory
|
||||
|
||||
ValueTask<TContentDialog> CreateInstanceAsync<TContentDialog>(params object[] parameters)
|
||||
where TContentDialog : Microsoft.UI.Xaml.Controls.ContentDialog;
|
||||
|
||||
Task<ContentDialogResult> EnqueueAndShowAsync(Microsoft.UI.Xaml.Controls.ContentDialog contentDialog);
|
||||
}
|
||||
@@ -13,7 +13,7 @@ using Windows.Graphics;
|
||||
namespace Snap.Hutao;
|
||||
|
||||
[HighQuality]
|
||||
[Injection(InjectAs.Singleton)]
|
||||
[Injection(InjectAs.Transient)]
|
||||
internal sealed partial class LaunchGameWindow : Window,
|
||||
IDisposable,
|
||||
IXamlWindowExtendContentIntoTitleBar,
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Snap.Hutao;
|
||||
/// 主窗体
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Injection(InjectAs.Singleton)]
|
||||
[Injection(InjectAs.Transient)]
|
||||
internal sealed partial class MainWindow : Window,
|
||||
IXamlWindowExtendContentIntoTitleBar,
|
||||
IXamlWindowRectPersisted,
|
||||
|
||||
654
src/Snap.Hutao/Snap.Hutao/Migrations/20240616104646_UidProfilePicture.Designer.cs
generated
Normal file
654
src/Snap.Hutao/Snap.Hutao/Migrations/20240616104646_UidProfilePicture.Designer.cs
generated
Normal file
@@ -0,0 +1,654 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20240616104646_UidProfilePicture")]
|
||||
partial class UidProfilePicture
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.6");
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ArchiveId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Current")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("achievements");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("achievement_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("CalculatorRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("GameRecordRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Info")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("ShowcaseRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("avatar_infos");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("cultivate_entries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("AvatarLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("AvatarLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("EntryId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("SkillALevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillALevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillELevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillELevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillQLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillQLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("WeaponLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("WeaponLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("EntryId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("cultivate_entry_level_informations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Count")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("EntryId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsFinished")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("EntryId");
|
||||
|
||||
b.ToTable("cultivate_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AttachedUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("cultivate_projects");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DailyNote")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("DailyTaskNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("DailyTaskNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ExpeditionNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ExpeditionNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HomeCoinNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HomeCoinNotifyThreshold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("RefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("ResinNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ResinNotifyThreshold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("TransformerNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("TransformerNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("daily_notes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("gacha_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ArchiveId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("GachaType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("QueryType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("gacha_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AttachUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("MihoyoSDK")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("game_accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Count")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AppendPropIdList")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MainPropId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_reliquaries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("PromoteLevel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_weapons");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("ExpireTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("object_cache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("ScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SpiralAbyss")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("spiral_abysses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.UidProfilePicture", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("AvatarId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("CostumeId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ProfilePictureId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("RefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("uid_profile_pictures");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Aid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CookieToken")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("CookieTokenLastUpdateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Fingerprint")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("FingerprintLastUpdateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsOversea")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("LToken")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("Ltoken");
|
||||
|
||||
b.Property<string>("Mid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PreferredUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SToken")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("Stoken");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive")
|
||||
.WithMany()
|
||||
.HasForeignKey("ArchiveId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Archive");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||
.WithOne("LevelInformation")
|
||||
.HasForeignKey("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", "EntryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Entry");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||
.WithMany()
|
||||
.HasForeignKey("EntryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Entry");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive")
|
||||
.WithMany()
|
||||
.HasForeignKey("ArchiveId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Archive");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.Navigation("LevelInformation");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UidProfilePicture : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "uid_profile_pictures",
|
||||
columns: table => new
|
||||
{
|
||||
InnerId = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||
Uid = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ProfilePictureId = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
AvatarId = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
CostumeId = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
RefreshTime = table.Column<DateTimeOffset>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_uid_profile_pictures", x => x.InnerId);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "uid_profile_pictures");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Snap.Hutao.Migrations
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.2");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.6");
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
{
|
||||
@@ -466,6 +466,33 @@ namespace Snap.Hutao.Migrations
|
||||
b.ToTable("spiral_abysses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.UidProfilePicture", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("AvatarId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("CostumeId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ProfilePictureId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("RefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("uid_profile_pictures");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
|
||||
@@ -65,6 +65,8 @@ internal sealed class AppDbContext : DbContext
|
||||
|
||||
public DbSet<SpiralAbyssEntry> SpiralAbysses { get; set; } = default!;
|
||||
|
||||
public DbSet<UidProfilePicture> UidProfilePictures { get; set; } = default!;
|
||||
|
||||
public static AppDbContext Create(IServiceProvider serviceProvider, string sqlConnectionString)
|
||||
{
|
||||
DbContextOptions<AppDbContext> options = new DbContextOptionsBuilder<AppDbContext>()
|
||||
|
||||
41
src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs
Normal file
41
src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Abstraction;
|
||||
using Snap.Hutao.Web.Enka.Model;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Snap.Hutao.Model.Entity;
|
||||
|
||||
[Table("uid_profile_pictures")]
|
||||
internal sealed class UidProfilePicture : IMappingFrom<UidProfilePicture, PlayerUid, ProfilePicture>
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid InnerId { get; set; }
|
||||
|
||||
public string Uid { get; set; } = default!;
|
||||
|
||||
public uint ProfilePictureId { get; set; }
|
||||
|
||||
public uint AvatarId { get; set; }
|
||||
|
||||
public uint CostumeId { get; set; }
|
||||
|
||||
public DateTimeOffset RefreshTime { get; set; }
|
||||
|
||||
[SuppressMessage("", "SH002")]
|
||||
public static UidProfilePicture From(PlayerUid uid, ProfilePicture profilePicture)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Uid = uid.ToString(),
|
||||
ProfilePictureId = profilePicture.Id,
|
||||
AvatarId = profilePicture.AvatarId,
|
||||
CostumeId = profilePicture.CostumeId,
|
||||
RefreshTime = DateTimeOffset.Now,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
internal enum ProfilePictureUnlockType
|
||||
{
|
||||
None,
|
||||
Item,
|
||||
Avatar,
|
||||
Costume,
|
||||
ParentQuest,
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Avatar;
|
||||
@@ -12,4 +13,17 @@ internal sealed class ProfilePicture
|
||||
public string Icon { get; set; } = default!;
|
||||
|
||||
public string Name { get; set; } = default!;
|
||||
}
|
||||
|
||||
public ProfilePictureUnlockType UnlockType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ProfilePictureUnlockType.Item"/> -> <see cref="MaterialId"/>
|
||||
/// <br/>
|
||||
/// <see cref="ProfilePictureUnlockType.Avatar"/> -> <see cref="AvatarId"/>
|
||||
/// <br/>
|
||||
/// <see cref="ProfilePictureUnlockType.Costume"/> -> <see cref="CostumeId"/>
|
||||
/// <br/>
|
||||
/// <see cref="ProfilePictureUnlockType.ParentQuest"/> -> <see cref="QuestId"/>
|
||||
/// </summary>
|
||||
public uint UnlockParameter { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Control;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// 玩家头像转换器
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal sealed class AvatarIconCircleConverter : ValueConverter<string, Uri>
|
||||
{
|
||||
/// <summary>
|
||||
/// 名称转Uri
|
||||
/// </summary>
|
||||
/// <param name="name">名称</param>
|
||||
/// <returns>链接</returns>
|
||||
public static Uri IconNameToUri(string name)
|
||||
{
|
||||
return Web.HutaoEndpoints.StaticRaw("AvatarIconCircle", $"{name}.png").ToUri();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Uri Convert(string from)
|
||||
{
|
||||
return IconNameToUri(from);
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
<Identity
|
||||
Name="60568DGPStudio.SnapHutao"
|
||||
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
||||
Version="1.10.3.0" />
|
||||
Version="1.10.4.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Snap Hutao</DisplayName>
|
||||
@@ -50,7 +50,7 @@
|
||||
</desktop:Extension>
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
<com:ExeServer Executable="Snap.Hutao.exe" Arguments="-ToastActivated" DisplayName="Snap Hutao Toast Activator">
|
||||
<com:ExeServer Executable="Snap.Hutao.exe" Arguments="----AppNotificationActivated:" DisplayName="Snap Hutao Toast Activator">
|
||||
<com:Class Id="5760EC4D-F7E8-4666-A965-9886D7DFFE7D" DisplayName="Snap Hutao Toast Activator"/>
|
||||
</com:ExeServer>
|
||||
</com:ComServer>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<Identity
|
||||
Name="60568DGPStudio.SnapHutaoDev"
|
||||
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
||||
Version="1.10.3.0" />
|
||||
Version="1.10.4.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Snap Hutao Dev</DisplayName>
|
||||
@@ -50,7 +50,7 @@
|
||||
</desktop:Extension>
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
<com:ExeServer Executable="Snap.Hutao.exe" Arguments="-ToastActivated" DisplayName="Snap Hutao Dev Toast Activator">
|
||||
<com:ExeServer Executable="Snap.Hutao.exe" Arguments="----AppNotificationActivated:" DisplayName="Snap Hutao Toast Activator">
|
||||
<com:Class Id="F32B561D-752E-472B-A22C-85824D421E1A" DisplayName="Snap Hutao Dev Toast Activator"/>
|
||||
</com:ExeServer>
|
||||
</com:ComServer>
|
||||
|
||||
@@ -193,16 +193,16 @@
|
||||
<value>Register [{0}] hotkey [{1}] failed</value>
|
||||
</data>
|
||||
<data name="CoreWindowingNotifyIconExitLabel" xml:space="preserve">
|
||||
<value>退出</value>
|
||||
<value>Quit</value>
|
||||
</data>
|
||||
<data name="CoreWindowingNotifyIconLaunchGameLabel" xml:space="preserve">
|
||||
<value>Launch</value>
|
||||
</data>
|
||||
<data name="CoreWindowingNotifyIconPromotedHint" xml:space="preserve">
|
||||
<value>胡桃已进入后台运行</value>
|
||||
<value>Snap Hutao is running in the background</value>
|
||||
</data>
|
||||
<data name="CoreWindowingNotifyIconViewLabel" xml:space="preserve">
|
||||
<value>窗口</value>
|
||||
<value>Window</value>
|
||||
</data>
|
||||
<data name="CoreWindowThemeDark" xml:space="preserve">
|
||||
<value>Dark</value>
|
||||
@@ -552,7 +552,7 @@
|
||||
<value>Refinement {0}</value>
|
||||
</data>
|
||||
<data name="MustSelectUserAndUid" xml:space="preserve">
|
||||
<value>Must sign in to your MiHoYo BBS account and select a user</value>
|
||||
<value>Must sign in to MiYouShe/HoYoLAB and select a user and role</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceDeleteEntrySucceed" xml:space="preserve">
|
||||
<value>Deleted {1} wish records from UID: {0}</value>
|
||||
@@ -570,7 +570,7 @@
|
||||
<value>Found abnormal data, unable to upload to Snap Hutao Cloud. Please do not upload across accounts or you can attempt to delete cloud data and try again.</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceUploadEntrySucceed" xml:space="preserve">
|
||||
<value>Uploaded {1} wish records of UID: {0}, stored {2}</value>
|
||||
<value>Uploaded {1} wish records to UID: {0}, {2} records saved</value>
|
||||
</data>
|
||||
<data name="ServerPassportLoginRequired" xml:space="preserve">
|
||||
<value>Please login or register Snap Hutao account first</value>
|
||||
@@ -621,7 +621,7 @@
|
||||
<value>Verification request is too frequent. Please try again in 1 minute.</value>
|
||||
</data>
|
||||
<data name="ServerRecordBannedUid" xml:space="preserve">
|
||||
<value>Failed to upload Sprial Abyss record, current UID is banned by Hutao Database</value>
|
||||
<value>Upload Sprial Abyss record failed, current UID is banned by Hutao Database</value>
|
||||
</data>
|
||||
<data name="ServerRecordComputingStatistics" xml:space="preserve">
|
||||
<value>Failed to upload Sprial Abyss record, server is calculating statistical data</value>
|
||||
@@ -642,7 +642,7 @@
|
||||
<value>Failed to upload Spiral Abyss record. It is not data for the current schedule.</value>
|
||||
</data>
|
||||
<data name="ServerRecordPreviousRequestNotCompleted" xml:space="preserve">
|
||||
<value>Failed to upload Sprial Abyss record. The record for the current UID is still being processed. Please do not repeat the operation.</value>
|
||||
<value>Upload Sprial Abyss record failed. The record for the current UID is still being processed. Please do not repeat the operation.</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessAndGachaLogServiceTimeExtended" xml:space="preserve">
|
||||
<value>Uploaded Spiral Abyss record successfully. Received a privilege extension for Snap Hutao Cloud service.</value>
|
||||
@@ -1034,6 +1034,9 @@
|
||||
<data name="ServiceGameUnlockerReadProcessMemoryPointerAddressFailed" xml:space="preserve">
|
||||
<value>Error reading process modules' memory: could not read valid value in given address</value>
|
||||
</data>
|
||||
<data name="ServiceGameUnlockerWriteProcessMemoryFpsAddressFailed" xml:space="preserve">
|
||||
<value>Failed to unlock frame rate:x{0:X8}</value>
|
||||
</data>
|
||||
<data name="ServiceHutaoUserGachaLogExpiredAt" xml:space="preserve">
|
||||
<value>Wish history data backup service will expire at \n{0:yyyy.MM.dd HH:mm:ss}</value>
|
||||
</data>
|
||||
@@ -1373,13 +1376,13 @@
|
||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||
<value>Delete user data permanently?</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
||||
<value>Log in Now</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||
<value>No Hutao Passport logged in currently, uploading Abyss Records will not grant you Hutao Cloud privilege extension.</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<value>Log in Now</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||
<value>Continue to Upload</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||
@@ -1568,11 +1571,14 @@
|
||||
<data name="ViewModelCultivationProjectInvalidName" xml:space="preserve">
|
||||
<value>Can't add plan with invalid name</value>
|
||||
</data>
|
||||
<data name="ViewModelCultivationRefreshInventoryProgress" xml:space="preserve">
|
||||
<value>Syncing inventory items</value>
|
||||
</data>
|
||||
<data name="ViewModelCultivationRemoveProjectContent" xml:space="preserve">
|
||||
<value>此操作不可逆,此计划的养成物品与背包材料将会丢失</value>
|
||||
<value>This action is irreversible, items and materials in this plan will lose</value>
|
||||
</data>
|
||||
<data name="ViewModelCultivationRemoveProjectTitle" xml:space="preserve">
|
||||
<value>确认要删除当前计划吗?</value>
|
||||
<value>Are you sure to delete current plan?</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteConfigWebhookUrlComplete" xml:space="preserve">
|
||||
<value>Realtime Note Webhook URL successfully configured</value>
|
||||
@@ -1925,6 +1931,9 @@
|
||||
<data name="ViewPageCultivationNavigateAction" xml:space="preserve">
|
||||
<value>Go</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationRefreshInventory" xml:space="preserve">
|
||||
<value>Sync Inventory Items</value>
|
||||
</data>
|
||||
<data name="ViewPageCultivationRemoveEntry" xml:space="preserve">
|
||||
<value>Delete list</value>
|
||||
</data>
|
||||
@@ -1965,7 +1974,7 @@
|
||||
<value>Notification Settings</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteNotificationUnavailableHint" xml:space="preserve">
|
||||
<value>胡桃的通知权限已被关闭</value>
|
||||
<value>Snap Hutao notification permission has been disabled</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteRefresh" xml:space="preserve">
|
||||
<value>Refresh</value>
|
||||
@@ -1998,7 +2007,7 @@
|
||||
<value>Refresh</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteSettingRefreshNotifyIconDisabledHint" xml:space="preserve">
|
||||
<value>未启用通知区域图标,关闭窗口后自动刷新将不会执行</value>
|
||||
<value>Notification area incon is disabled, auto-refresh will be be executed after closing the window</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteSlientModeDescription" xml:space="preserve">
|
||||
<value>Do not disturb during Genshin gaming</value>
|
||||
@@ -2067,10 +2076,10 @@
|
||||
<value>Go to Afdian to Buy Related Services</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogHutaoCloudAfdianPurchaseHeader" xml:space="preserve">
|
||||
<value>Buy or Renew Cloud Service</value>
|
||||
<value>Buy / Renew Cloud Services</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogHutaoCloudDelete" xml:space="preserve">
|
||||
<value>Delete the Cloud Archive of This UID</value>
|
||||
<value>Delete cloud archive of this UID</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogHutaoCloudDeveloperHint" xml:space="preserve">
|
||||
<value>Lifetime developer license</value>
|
||||
@@ -2079,7 +2088,7 @@
|
||||
<value>Snap Hutao Cloud Service Expired</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogHutaoCloudRetrieve" xml:space="preserve">
|
||||
<value>Download the Cloud Archive of this UID</value>
|
||||
<value>Download cloud archive of this UID</value>
|
||||
</data>
|
||||
<data name="ViewPageGachaLogHutaoCloudSpiralAbyssActivityDescription" xml:space="preserve">
|
||||
<value>Free 3-day license after uploading current schedule Abyss record</value>
|
||||
@@ -2406,7 +2415,7 @@
|
||||
<value>Rename</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameSwitchSchemeDescription" xml:space="preserve">
|
||||
<value>Switch game server (CN/BiliBili/Global)</value>
|
||||
<value>Switch game server (CN/Bilibili/Global)</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameSwitchSchemeHeader" xml:space="preserve">
|
||||
<value>Server</value>
|
||||
@@ -2415,7 +2424,7 @@
|
||||
<value>You need to convert to a server that matches the launcher before updating the version</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameUnlockFpsDescription" xml:space="preserve">
|
||||
<value>Please turn off V-Sync in the game settings. You may need a high-performance graphic card to support a high frame rate limit.</value>
|
||||
<value>Please turn off [V-Sync] in your game graphic settings, need high performance GPU to support higher refresh rate gaming</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameUnlockFpsHeader" xml:space="preserve">
|
||||
<value>Unlock Frame Rate Limit</value>
|
||||
@@ -2433,7 +2442,7 @@
|
||||
<value>Windows HDR</value>
|
||||
</data>
|
||||
<data name="ViewPageLoginHoyoverseUserHint" xml:space="preserve">
|
||||
<value>Enter your HoYoLab UID</value>
|
||||
<value>Please input your HoYoLAB UID</value>
|
||||
</data>
|
||||
<data name="ViewPageLoginMihoyoUserDescription" xml:space="preserve">
|
||||
<value>You are using an embedded webview to login to your MiHoYo passport account, the client will fetch the Cookie data when you click on I'm Signed in button. All network communication initialed in this webview occurs only between your computer and MiHoYo official servers.</value>
|
||||
@@ -2511,7 +2520,7 @@
|
||||
<value>Unless the developer explicitly asks you to do this, you should not execute the operations below!</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingDataFolderDescription" xml:space="preserve">
|
||||
<value>User data and metadata are saved here</value>
|
||||
<value>User data/metadata are saved here</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingDataFolderHeader" xml:space="preserve">
|
||||
<value>Data Folder</value>
|
||||
@@ -2589,7 +2598,7 @@
|
||||
<value>Select the game server for which you want to get announcements</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHomeAnnouncementRegionHeader" xml:space="preserve">
|
||||
<value>Announcement Server</value>
|
||||
<value>Announcement Game Server Source</value>
|
||||
</data>
|
||||
<data name="ViewpageSettingHomeCardDescription" xml:space="preserve">
|
||||
<value>Manage cards on home dashboard</value>
|
||||
@@ -2646,7 +2655,7 @@
|
||||
<value>You are unlimited in any testing feature</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportMaintainerHeader" xml:space="preserve">
|
||||
<value>Snap Hutao developer and maintainer</value>
|
||||
<value>Snap Hutao Developer / Maintainer</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRedeemCodeDescription" xml:space="preserve">
|
||||
<value>We sometimes give away Snap Hutao Cloud redemption codes to some users</value>
|
||||
@@ -2679,10 +2688,10 @@
|
||||
<value>Shortcut Keys</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingNotifyIconDescription" xml:space="preserve">
|
||||
<value>在通知区域显示图标,以允许执行后台任务,重启后生效</value>
|
||||
<value>Show icon in notification area, to allow background task, restart the program to activate</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingNotifyIconHeader" xml:space="preserve">
|
||||
<value>通知区域图标</value>
|
||||
<value>Notification Area Icon</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingOfficialSiteNavigate" xml:space="preserve">
|
||||
<value>Official Website</value>
|
||||
@@ -2703,7 +2712,7 @@
|
||||
<value>Reset Image Resource</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsDescription" xml:space="preserve">
|
||||
<value>Add Unlock Frame Rate Limit Option in Game Launcher Process Section</value>
|
||||
<value>Add [Unlock Frame Rate Limit] in [Game Launcher - Process Linkage]</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingsAdvancedOptionsLaunchUnlockFpsHeader" xml:space="preserve">
|
||||
<value>Game Launcher - Unlock Frame Rate Limit</value>
|
||||
@@ -2751,7 +2760,7 @@
|
||||
<value>Contribute Translations</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingUnobtainedWishItemVisibleDescription" xml:space="preserve">
|
||||
<value>Display undrawn wish items in Character and Weapon tabs in Wish Export</value>
|
||||
<value>在「祈愿记录-角色」与「祈愿记录-武器」中显示未抽取到的祈愿物品</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingUnobtainedWishItemVisibleHeader" xml:space="preserve">
|
||||
<value>Undrawn Wish Items</value>
|
||||
@@ -2997,7 +3006,7 @@
|
||||
<value>Document</value>
|
||||
</data>
|
||||
<data name="ViewUserLoginMihoyoUserDisabledTooltip" xml:space="preserve">
|
||||
<value>由于米游社安全策略的相关更改,网页登录暂不可用</value>
|
||||
<value>Due to MiYouShe's security policy changes, login from web is unavailable</value>
|
||||
</data>
|
||||
<data name="ViewUserNoUserHint" xml:space="preserve">
|
||||
<value>Haven't logged in</value>
|
||||
@@ -3261,7 +3270,7 @@
|
||||
<value>[{1}] network request exception in [{0}] please try again later</value>
|
||||
</data>
|
||||
<data name="WebResponseSignInErrorHint" xml:space="preserve">
|
||||
<value>登录失败,请前往 HoYoLAB 初始化账号,原始消息:{0}</value>
|
||||
<value>Login failed, please visit HoYoLAB to initial your account, original message: {0}</value>
|
||||
</data>
|
||||
<data name="WindowIdentifyMonitorHeader" xml:space="preserve">
|
||||
<value>Monitor ID</value>
|
||||
|
||||
@@ -60,45 +60,45 @@
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -1373,13 +1373,13 @@
|
||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||
<value>是否永久删除用户数据</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<value>前往登录</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||
<value>继续上传</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||
|
||||
@@ -60,45 +60,45 @@
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -1373,13 +1373,13 @@
|
||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||
<value>Hapus data pengguna secara permanen?</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<value>前往登录</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||
<value>继续上传</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||
|
||||
@@ -60,45 +60,45 @@
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -1373,13 +1373,13 @@
|
||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||
<value>ユーザーデータを完全に削除しますか</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<value>前往登录</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||
<value>继续上传</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||
|
||||
@@ -60,45 +60,45 @@
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -1373,13 +1373,13 @@
|
||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||
<value>사용자 데이터 영구 삭제</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<value>前往登录</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||
<value>继续上传</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||
|
||||
@@ -60,45 +60,45 @@
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -1373,13 +1373,13 @@
|
||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||
<value>Excluir permanentemente os dados do usuário?</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<value>前往登录</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||
<value>继续上传</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||
|
||||
@@ -1034,6 +1034,9 @@
|
||||
<data name="ServiceGameUnlockerReadProcessMemoryPointerAddressFailed" xml:space="preserve">
|
||||
<value>在读取游戏进程内存时遇到问题:无法读取到指定地址的有效值</value>
|
||||
</data>
|
||||
<data name="ServiceGameUnlockerWriteProcessMemoryFpsAddressFailed" xml:space="preserve">
|
||||
<value>解锁帧率失败:0x{0:X8}</value>
|
||||
</data>
|
||||
<data name="ServiceHutaoUserGachaLogExpiredAt" xml:space="preserve">
|
||||
<value>祈愿记录上传服务有效期至\n{0:yyyy.MM.dd HH:mm:ss}</value>
|
||||
</data>
|
||||
@@ -1373,13 +1376,13 @@
|
||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||
<value>是否永久删除用户数据</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
||||
<value>前往登录</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<value>前往登录</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||
<value>继续上传</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||
|
||||
@@ -60,45 +60,45 @@
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -1373,13 +1373,13 @@
|
||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||
<value>是否永久删除用户数据</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<value>前往登录</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||
<value>Продолжить загрузку</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||
|
||||
@@ -60,45 +60,45 @@
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -190,7 +190,7 @@
|
||||
<value>Không phát hiện WebView2 Runtime</value>
|
||||
</data>
|
||||
<data name="CoreWindowHotkeyCombinationRegisterFailed" xml:space="preserve">
|
||||
<value/>
|
||||
<value />
|
||||
</data>
|
||||
<data name="CoreWindowingNotifyIconExitLabel" xml:space="preserve">
|
||||
<value>Thoát</value>
|
||||
@@ -292,7 +292,7 @@
|
||||
<value>刷新于 {0:MM.dd HH:mm:ss}</value>
|
||||
</data>
|
||||
<data name="ModelEntitySpiralAbyssScheduleFormat" xml:space="preserve">
|
||||
<value/>
|
||||
<value />
|
||||
</data>
|
||||
<data name="ModelInterchangeUIGFItemTypeAvatar" xml:space="preserve">
|
||||
<value>Tài khoản</value>
|
||||
@@ -395,7 +395,7 @@
|
||||
<value>4 Sao</value>
|
||||
</data>
|
||||
<data name="ModelIntrinsicItemQualityRed" xml:space="preserve">
|
||||
<value/>
|
||||
<value />
|
||||
</data>
|
||||
<data name="ModelIntrinsicItemQualityWhite" xml:space="preserve">
|
||||
<value>1 sao</value>
|
||||
@@ -669,7 +669,7 @@
|
||||
<value>Đã tìm thấy nhiều ID thành tựu giống hệt nhau trong kho lưu trữ thành tựu</value>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
||||
<value/>
|
||||
<value />
|
||||
<comment>Need EXACT same string in game</comment>
|
||||
</data>
|
||||
<data name="ServiceAvatarInfoPropertyBaseAtk" xml:space="preserve">
|
||||
@@ -1005,7 +1005,7 @@
|
||||
<value>获取 Package Version 失败</value>
|
||||
</data>
|
||||
<data name="ServiceGamePackageRequestScatteredFileFailed" xml:space="preserve">
|
||||
<value/>
|
||||
<value />
|
||||
</data>
|
||||
<data name="ServiceGamePathLocateFailed" xml:space="preserve">
|
||||
<value>无法找到游戏路径,请前往设置修改</value>
|
||||
@@ -1373,13 +1373,13 @@
|
||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||
<value>是否永久删除用户数据</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<value>前往登录</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||
<value>继续上传</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||
|
||||
@@ -60,45 +60,45 @@
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -1373,13 +1373,13 @@
|
||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||
<value>是否永久刪除用戶數據</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<value>前往登入畫面</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||
<value>當前未登入胡桃賬號,上傳深淵數據無法獲贈胡桃雲時長</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||
<value>繼續上傳</value>
|
||||
</data>
|
||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Notifications;
|
||||
using Microsoft.Windows.AppNotifications;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
@@ -20,7 +20,6 @@ namespace Snap.Hutao.Service.DailyNote;
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed partial class DailyNoteNotificationOperation
|
||||
{
|
||||
private const string ToastHeaderIdArgument = "DAILYNOTE";
|
||||
private const string ToastAttributionUnknown = "Unknown";
|
||||
|
||||
private readonly ITaskContext taskContext;
|
||||
@@ -52,63 +51,57 @@ internal sealed partial class DailyNoteNotificationOperation
|
||||
? entry.UserGameRole.ToString()
|
||||
: await GetUserUidAsync(entry).ConfigureAwait(false);
|
||||
|
||||
ToastContentBuilder builder = new ToastContentBuilder()
|
||||
.AddHeader(ToastHeaderIdArgument, SH.ServiceDailyNoteNotifierTitle, ToastHeaderIdArgument)
|
||||
.AddAttributionText(attribution)
|
||||
.AddButton(new ToastButton()
|
||||
.SetContent(SH.ServiceDailyNoteNotifierActionLaunchGameButton)
|
||||
.AddArgument(AppActivation.Action, AppActivation.LaunchGame)
|
||||
.AddArgument(AppActivation.Uid, entry.Uid))
|
||||
.AddButton(new ToastButtonDismiss(SH.ServiceDailyNoteNotifierActionLaunchGameDismiss));
|
||||
|
||||
if (options.IsReminderNotification)
|
||||
{
|
||||
builder.SetToastScenario(ToastScenario.Reminder);
|
||||
}
|
||||
string reminder = options.IsReminderNotification ? @"scenario=""reminder""" : string.Empty;
|
||||
string content;
|
||||
|
||||
if (notifyInfos.Count > 2)
|
||||
{
|
||||
builder.AddText(SH.ServiceDailyNoteNotifierMultiValueReached);
|
||||
string adaptiveSubgroups = string.Join(string.Empty, notifyInfos.Select(info => $"""
|
||||
<subgroup>
|
||||
<text hint-align="center">{info.AdaptiveHint}</text>
|
||||
<text hint-style="captionSubtle" hint-align="center">{info.Title}</text>
|
||||
</subgroup>
|
||||
"""));
|
||||
|
||||
// Desktop and Mobile started supporting adaptive toasts in API contract 3 (Anniversary Update)
|
||||
if (UniversalApiContract.IsPresent(WindowsVersion.Windows10AnniversaryUpdate))
|
||||
{
|
||||
AdaptiveGroup group = new();
|
||||
foreach (DailyNoteNotifyInfo info in notifyInfos)
|
||||
{
|
||||
AdaptiveSubgroup subgroup = new()
|
||||
{
|
||||
HintWeight = 1,
|
||||
Children =
|
||||
{
|
||||
// new AdaptiveImage() { Source = info.AdaptiveIcon, HintRemoveMargin = true, },
|
||||
new AdaptiveText() { Text = info.AdaptiveHint, HintAlign = AdaptiveTextAlign.Center, },
|
||||
new AdaptiveText() { Text = info.Title, HintAlign = AdaptiveTextAlign.Center, HintStyle = AdaptiveTextStyle.CaptionSubtle, },
|
||||
},
|
||||
};
|
||||
|
||||
group.Children.Add(subgroup);
|
||||
}
|
||||
|
||||
builder.AddVisualChild(group);
|
||||
}
|
||||
content = $"""
|
||||
<text>{SH.ServiceDailyNoteNotifierMultiValueReached}</text>
|
||||
<group>
|
||||
{adaptiveSubgroups}
|
||||
</group>
|
||||
""";
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (DailyNoteNotifyInfo info in notifyInfos)
|
||||
{
|
||||
builder.AddText(info.Hint);
|
||||
}
|
||||
content = string.Join(string.Empty, notifyInfos.Select(info => $"""
|
||||
<text>{info.Hint}</text>
|
||||
"""));
|
||||
}
|
||||
|
||||
string rawXml = $"""
|
||||
<toast {reminder}>
|
||||
<header title="{SH.ServiceDailyNoteNotifierTitle}" id="DAILYNOTE" arguments="DAILYNOTE"/>
|
||||
|
||||
<visual>
|
||||
<binding template="ToastGeneric">
|
||||
{content}
|
||||
<text placement="attribution">{attribution}</text>
|
||||
</binding>
|
||||
</visual>
|
||||
<actions>
|
||||
<action activationType="background" content="{SH.ServiceDailyNoteNotifierActionLaunchGameButton}" arguments="{AppActivation.Action}={AppActivation.LaunchGame};{AppActivation.Uid}={entry.Uid}"/>
|
||||
<action activationType="system" content="{SH.ServiceDailyNoteNotifierActionLaunchGameDismiss}" arguments="dismiss"/>
|
||||
</actions>
|
||||
</toast>
|
||||
""";
|
||||
AppNotification notification = new(rawXml);
|
||||
|
||||
if (options.IsSilentWhenPlayingGame && gameService.IsGameRunning())
|
||||
{
|
||||
notification.SuppressDisplay = true;
|
||||
}
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
builder.Show(toast => toast.SuppressPopup = ShouldSuppressPopup(options));
|
||||
}
|
||||
|
||||
private bool ShouldSuppressPopup(DailyNoteOptions options)
|
||||
{
|
||||
// Prevent notify when we are in game && silent mode.
|
||||
return options.IsSilentWhenPlayingGame && gameService.IsGameRunning();
|
||||
AppNotificationManager.Default.Show(notification);
|
||||
}
|
||||
|
||||
private async ValueTask<string> GetUserUidAsync(DailyNoteEntry entry)
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.Graphics.Direct3D11;
|
||||
using Windows.Graphics.Capture;
|
||||
using static Snap.Hutao.Win32.ConstValues;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Automation.ScreenCapture;
|
||||
|
||||
@@ -13,20 +11,6 @@ namespace Snap.Hutao.Service.Game.Automation.ScreenCapture;
|
||||
[Injection(InjectAs.Singleton, typeof(IGameScreenCaptureService))]
|
||||
internal sealed partial class GameScreenCaptureService : IGameScreenCaptureService
|
||||
{
|
||||
private const uint CreateDXGIFactoryFlag =
|
||||
#if DEBUG
|
||||
DXGI_CREATE_FACTORY_DEBUG;
|
||||
#else
|
||||
0;
|
||||
#endif
|
||||
|
||||
private const D3D11_CREATE_DEVICE_FLAG D3d11CreateDeviceFlag =
|
||||
D3D11_CREATE_DEVICE_FLAG.D3D11_CREATE_DEVICE_BGRA_SUPPORT
|
||||
#if DEBUG
|
||||
| D3D11_CREATE_DEVICE_FLAG.D3D11_CREATE_DEVICE_DEBUG
|
||||
#endif
|
||||
;
|
||||
|
||||
private readonly ILogger<GameScreenCaptureService> logger;
|
||||
|
||||
public bool IsSupported()
|
||||
|
||||
@@ -10,8 +10,10 @@ using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Service.Game.Configuration;
|
||||
using Snap.Hutao.Service.Game.Package;
|
||||
using Snap.Hutao.View.Dialog;
|
||||
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
|
||||
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher.Resource;
|
||||
using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect;
|
||||
using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.ChannelSDK;
|
||||
using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.DeprecatedFile;
|
||||
using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Package;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.IO;
|
||||
|
||||
@@ -43,7 +45,7 @@ internal sealed class LaunchExecutionEnsureGameResourceHandler : ILaunchExecutio
|
||||
return;
|
||||
}
|
||||
|
||||
// Backup config file, in order to prevent a incompatible launcher to delete it.
|
||||
// Backup config file, recover when a incompatible launcher deleted it.
|
||||
context.ServiceProvider.GetRequiredService<IGameConfigurationFileService>().Backup(gameFileSystem.GameConfigFilePath);
|
||||
|
||||
await context.TaskContext.SwitchToMainThreadAsync();
|
||||
@@ -96,13 +98,30 @@ internal sealed class LaunchExecutionEnsureGameResourceHandler : ILaunchExecutio
|
||||
|
||||
progress.Report(new(SH.ServiceGameEnsureGameResourceQueryResourceInformation));
|
||||
|
||||
ResourceClient resourceClient = context.ServiceProvider.GetRequiredService<ResourceClient>();
|
||||
Response<GameResource> response = await resourceClient.GetResourceAsync(context.Scheme).ConfigureAwait(false);
|
||||
HoyoPlayClient hoyoPlayClient = context.ServiceProvider.GetRequiredService<HoyoPlayClient>();
|
||||
|
||||
if (!response.TryGetDataWithoutUINotification(out GameResource? resource))
|
||||
// We perform these requests before package conversion to ensure resources index is intact.
|
||||
Response<GamePackagesWrapper> packagesResponse = await hoyoPlayClient.GetPackagesAsync(context.Scheme).ConfigureAwait(false);
|
||||
if (!packagesResponse.TryGetDataWithoutUINotification(out GamePackagesWrapper? gamePackages))
|
||||
{
|
||||
context.Result.Kind = LaunchExecutionResultKind.GameResourceIndexQueryInvalidResponse;
|
||||
context.Result.ErrorMessage = SH.FormatServiceGameLaunchExecutionGameResourceQueryIndexFailed(response);
|
||||
context.Result.ErrorMessage = SH.FormatServiceGameLaunchExecutionGameResourceQueryIndexFailed(packagesResponse);
|
||||
return false;
|
||||
}
|
||||
|
||||
Response<GameChannelSDKsWrapper> sdkResponse = await hoyoPlayClient.GetChannelSDKAsync(context.Scheme).ConfigureAwait(false);
|
||||
if (!sdkResponse.TryGetDataWithoutUINotification(out GameChannelSDKsWrapper? channelSDKs))
|
||||
{
|
||||
context.Result.Kind = LaunchExecutionResultKind.GameResourceIndexQueryInvalidResponse;
|
||||
context.Result.ErrorMessage = SH.FormatServiceGameLaunchExecutionGameResourceQueryIndexFailed(sdkResponse);
|
||||
return false;
|
||||
}
|
||||
|
||||
Response<DeprecatedFileConfigurationsWrapper> deprecatedFileResponse = await hoyoPlayClient.GetDeprecatedFileConfigurationsAsync(context.Scheme).ConfigureAwait(false);
|
||||
if (!deprecatedFileResponse.TryGetDataWithoutUINotification(out DeprecatedFileConfigurationsWrapper? deprecatedFileConfigs))
|
||||
{
|
||||
context.Result.Kind = LaunchExecutionResultKind.GameResourceIndexQueryInvalidResponse;
|
||||
context.Result.ErrorMessage = SH.FormatServiceGameLaunchExecutionGameResourceQueryIndexFailed(deprecatedFileResponse);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -110,7 +129,7 @@ internal sealed class LaunchExecutionEnsureGameResourceHandler : ILaunchExecutio
|
||||
|
||||
if (!context.Scheme.ExecutableMatches(gameFileName))
|
||||
{
|
||||
if (!await packageConverter.EnsureGameResourceAsync(context.Scheme, resource, gameFolder, progress).ConfigureAwait(false))
|
||||
if (!await packageConverter.EnsureGameResourceAsync(context.Scheme, gamePackages.GamePackages.Single(), gameFolder, progress).ConfigureAwait(false))
|
||||
{
|
||||
context.Result.Kind = LaunchExecutionResultKind.GameResourcePackageConvertInternalError;
|
||||
context.Result.ErrorMessage = SH.ViewModelLaunchGameEnsureGameResourceFail;
|
||||
@@ -124,7 +143,7 @@ internal sealed class LaunchExecutionEnsureGameResourceHandler : ILaunchExecutio
|
||||
context.Options.UpdateGamePathAndRefreshEntries(Path.Combine(gameFolder, executableName));
|
||||
}
|
||||
|
||||
await packageConverter.EnsureDeprecatedFilesAndSdkAsync(resource, gameFolder).ConfigureAwait(false);
|
||||
await packageConverter.EnsureDeprecatedFilesAndSdkAsync(channelSDKs.GameChannelSDKs.SingleOrDefault(), deprecatedFileConfigs.DeprecatedFileConfigurations.SingleOrDefault(), gameFolder).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ internal sealed class LaunchExecutionSetChannelOptionsHandler : ILaunchExecution
|
||||
string configPath = gameFileSystem.GameConfigFilePath;
|
||||
context.Logger.LogInformation("Game config file path: {ConfigPath}", configPath);
|
||||
|
||||
List<IniElement> elements = default!;
|
||||
List<IniElement> elements;
|
||||
try
|
||||
{
|
||||
elements = [.. IniSerializer.DeserializeFromFile(configPath)];
|
||||
|
||||
@@ -13,7 +13,6 @@ internal sealed class PackageConvertStatus
|
||||
public PackageConvertStatus(string name)
|
||||
{
|
||||
Name = name;
|
||||
Description = name;
|
||||
}
|
||||
|
||||
public PackageConvertStatus(string name, long bytesRead, long totalBytes)
|
||||
|
||||
@@ -8,7 +8,9 @@ using Snap.Hutao.Core.IO;
|
||||
using Snap.Hutao.Core.IO.Hashing;
|
||||
using Snap.Hutao.Core.IO.Http.Sharding;
|
||||
using Snap.Hutao.Service.Game.Scheme;
|
||||
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher.Resource;
|
||||
using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.ChannelSDK;
|
||||
using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.DeprecatedFile;
|
||||
using Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Package;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
@@ -34,9 +36,9 @@ internal sealed partial class PackageConverter
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly ILogger<PackageConverter> logger;
|
||||
|
||||
public async ValueTask<bool> EnsureGameResourceAsync(LaunchScheme targetScheme, GameResource gameResource, string gameFolder, IProgress<PackageConvertStatus> progress)
|
||||
public async ValueTask<bool> EnsureGameResourceAsync(LaunchScheme targetScheme, GamePackage gamePackage, string gameFolder, IProgress<PackageConvertStatus> progress)
|
||||
{
|
||||
// 以 国服 => 国际 为例
|
||||
// 以 国服 -> 国际服 为例
|
||||
// 1. 下载国际服的 pkg_version 文件,转换为索引字典
|
||||
// 获取本地对应 pkg_version 文件,转换为索引字典
|
||||
//
|
||||
@@ -56,7 +58,7 @@ internal sealed partial class PackageConverter
|
||||
// 替换操作等于 先备份国服文件,随后新增国际服文件
|
||||
|
||||
// 准备下载链接
|
||||
string scatteredFilesUrl = gameResource.Game.Latest.DecompressedPath;
|
||||
string scatteredFilesUrl = gamePackage.Main.Major.ResourceListUrl;
|
||||
string pkgVersionUrl = $"{scatteredFilesUrl}/{PackageVersion}";
|
||||
|
||||
PackageConverterFileSystemContext context = new(targetScheme.IsOversea, runtimeOptions.GetDataFolderServerCacheFolder(), gameFolder, scatteredFilesUrl);
|
||||
@@ -77,7 +79,7 @@ internal sealed partial class PackageConverter
|
||||
return await ReplaceGameResourceAsync(diffOperations, context, progress).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async ValueTask EnsureDeprecatedFilesAndSdkAsync(GameResource resource, string gameFolder)
|
||||
public async ValueTask EnsureDeprecatedFilesAndSdkAsync(GameChannelSDK? channelSDK, DeprecatedFilesWrapper? deprecatedFiles, string gameFolder)
|
||||
{
|
||||
string sdkDllBackup = Path.Combine(gameFolder, YuanShenData, "Plugins\\PCGameSDK.dll.backup");
|
||||
string sdkDll = Path.Combine(gameFolder, YuanShenData, "Plugins\\PCGameSDK.dll");
|
||||
@@ -86,9 +88,9 @@ internal sealed partial class PackageConverter
|
||||
string sdkVersion = Path.Combine(gameFolder, "sdk_pkg_version");
|
||||
|
||||
// Only bilibili's sdk is not null
|
||||
if (resource.Sdk is not null)
|
||||
if (channelSDK is not null)
|
||||
{
|
||||
using (Stream sdkWebStream = await httpClient.GetStreamAsync(resource.Sdk.Path).ConfigureAwait(false))
|
||||
using (Stream sdkWebStream = await httpClient.GetStreamAsync(channelSDK.ChannelSdkPackage.Url).ConfigureAwait(false))
|
||||
{
|
||||
ZipFile.ExtractToDirectory(sdkWebStream, gameFolder, true);
|
||||
}
|
||||
@@ -106,9 +108,9 @@ internal sealed partial class PackageConverter
|
||||
FileOperation.Move(sdkVersion, sdkVersionBackup, true);
|
||||
}
|
||||
|
||||
if (resource.DeprecatedFiles is not null)
|
||||
if (deprecatedFiles is not null)
|
||||
{
|
||||
foreach (NameMd5 file in resource.DeprecatedFiles)
|
||||
foreach (DeprecatedFile file in deprecatedFiles.DeprecatedFiles)
|
||||
{
|
||||
string filePath = Path.Combine(gameFolder, file.Name);
|
||||
FileOperation.Move(filePath, $"{filePath}.backup", true);
|
||||
|
||||
@@ -30,25 +30,13 @@ internal class LaunchScheme : IEquatable<ChannelOptions>
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通道
|
||||
/// </summary>
|
||||
public ChannelType Channel { get; private protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子通道
|
||||
/// </summary>
|
||||
public SubChannelType SubChannel { get; private protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// 启动器 Id
|
||||
/// </summary>
|
||||
public int LauncherId { get; private protected set; }
|
||||
public string LauncherId { get; private protected set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// API Key
|
||||
/// </summary>
|
||||
public string Key { get; private protected set; } = default!;
|
||||
public string GameId { get; private protected set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为海外
|
||||
|
||||
@@ -7,13 +7,13 @@ namespace Snap.Hutao.Service.Game.Scheme;
|
||||
|
||||
internal sealed class LaunchSchemeBilibili : LaunchScheme
|
||||
{
|
||||
private const int SdkStaticLauncherBilibiliId = 17;
|
||||
private const string SdkStaticLauncherBilibiliKey = "KAtdSsoQ";
|
||||
private const string HoyoPlayLauncherBilibiliId = "umfgRO5gh5";
|
||||
private const string HoyoPlayGameBilibiliId = "T2S0Gz4Dr2";
|
||||
|
||||
public LaunchSchemeBilibili(SubChannelType subChannel, bool isNotCompatOnly = true)
|
||||
{
|
||||
LauncherId = SdkStaticLauncherBilibiliId;
|
||||
Key = SdkStaticLauncherBilibiliKey;
|
||||
LauncherId = HoyoPlayLauncherBilibiliId;
|
||||
GameId = HoyoPlayGameBilibiliId;
|
||||
Channel = ChannelType.Bili;
|
||||
SubChannel = subChannel;
|
||||
IsOversea = false;
|
||||
|
||||
@@ -7,13 +7,13 @@ namespace Snap.Hutao.Service.Game.Scheme;
|
||||
|
||||
internal sealed class LaunchSchemeChinese : LaunchScheme
|
||||
{
|
||||
private const int SdkStaticLauncherChineseId = 18;
|
||||
private const string SdkStaticLauncherChineseKey = "eYd89JmJ";
|
||||
private const string HoyoPlayLauncherChineseId = "jGHBHlcOq1";
|
||||
private const string HoyoPlayGameChineseId = "1Z8W5NHUQb";
|
||||
|
||||
public LaunchSchemeChinese(ChannelType channel, SubChannelType subChannel, bool isNotCompatOnly = true)
|
||||
{
|
||||
LauncherId = SdkStaticLauncherChineseId;
|
||||
Key = SdkStaticLauncherChineseKey;
|
||||
LauncherId = HoyoPlayLauncherChineseId;
|
||||
GameId = HoyoPlayGameChineseId;
|
||||
Channel = channel;
|
||||
SubChannel = subChannel;
|
||||
IsOversea = false;
|
||||
|
||||
@@ -7,13 +7,13 @@ namespace Snap.Hutao.Service.Game.Scheme;
|
||||
|
||||
internal sealed class LaunchSchemeOversea : LaunchScheme
|
||||
{
|
||||
private const int SdkStaticLauncherOverseaId = 10;
|
||||
private const string SdkStaticLauncherOverseaKey = "gcStgarh";
|
||||
private const string HoyoPlayLauncherOverseaId = "VYTpXlbWo8";
|
||||
private const string HoyoPlayGameOverseaId = "gopR6Cufr3";
|
||||
|
||||
public LaunchSchemeOversea(ChannelType channel, SubChannelType subChannel, bool isNotCompatOnly = true)
|
||||
{
|
||||
LauncherId = SdkStaticLauncherOverseaId;
|
||||
Key = SdkStaticLauncherOverseaKey;
|
||||
LauncherId = HoyoPlayLauncherOverseaId;
|
||||
GameId = HoyoPlayGameOverseaId;
|
||||
Channel = channel;
|
||||
SubChannel = subChannel;
|
||||
IsOversea = true;
|
||||
|
||||
@@ -54,6 +54,12 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
|
||||
if (!context.GameProcess.HasExited && context.FpsAddress != 0U)
|
||||
{
|
||||
UnsafeWriteProcessMemory(context.GameProcess, context.FpsAddress, launchOptions.TargetFps);
|
||||
WIN32_ERROR error = GetLastError();
|
||||
if (error is not WIN32_ERROR.NO_ERROR)
|
||||
{
|
||||
context.Description = SH.FormatServiceGameUnlockerWriteProcessMemoryFpsAddressFailed(error);
|
||||
}
|
||||
|
||||
context.Report();
|
||||
}
|
||||
else
|
||||
|
||||
@@ -24,51 +24,52 @@ internal sealed partial class HutaoSpiralAbyssService : IHutaoSpiralAbyssService
|
||||
private readonly IMemoryCache memoryCache;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Overview> GetOverviewAsync()
|
||||
public ValueTask<Overview> GetOverviewAsync(bool last = false)
|
||||
{
|
||||
return FromCacheOrWebAsync(nameof(Overview), homaClient.GetOverviewAsync);
|
||||
return FromCacheOrWebAsync(nameof(Overview), last, homaClient.GetOverviewAsync);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<AvatarAppearanceRank>> GetAvatarAppearanceRanksAsync()
|
||||
public ValueTask<List<AvatarAppearanceRank>> GetAvatarAppearanceRanksAsync(bool last = false)
|
||||
{
|
||||
return FromCacheOrWebAsync(nameof(AvatarAppearanceRank), homaClient.GetAvatarAttendanceRatesAsync);
|
||||
return FromCacheOrWebAsync(nameof(AvatarAppearanceRank), last, homaClient.GetAvatarAttendanceRatesAsync);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<AvatarUsageRank>> GetAvatarUsageRanksAsync()
|
||||
public ValueTask<List<AvatarUsageRank>> GetAvatarUsageRanksAsync(bool last = false)
|
||||
{
|
||||
return FromCacheOrWebAsync(nameof(AvatarUsageRank), homaClient.GetAvatarUtilizationRatesAsync);
|
||||
return FromCacheOrWebAsync(nameof(AvatarUsageRank), last, homaClient.GetAvatarUtilizationRatesAsync);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<AvatarConstellationInfo>> GetAvatarConstellationInfosAsync()
|
||||
public ValueTask<List<AvatarConstellationInfo>> GetAvatarConstellationInfosAsync(bool last = false)
|
||||
{
|
||||
return FromCacheOrWebAsync(nameof(AvatarConstellationInfo), homaClient.GetAvatarHoldingRatesAsync);
|
||||
return FromCacheOrWebAsync(nameof(AvatarConstellationInfo), last, homaClient.GetAvatarHoldingRatesAsync);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<AvatarCollocation>> GetAvatarCollocationsAsync()
|
||||
public ValueTask<List<AvatarCollocation>> GetAvatarCollocationsAsync(bool last = false)
|
||||
{
|
||||
return FromCacheOrWebAsync(nameof(AvatarCollocation), homaClient.GetAvatarCollocationsAsync);
|
||||
return FromCacheOrWebAsync(nameof(AvatarCollocation), last, homaClient.GetAvatarCollocationsAsync);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<WeaponCollocation>> GetWeaponCollocationsAsync()
|
||||
public ValueTask<List<WeaponCollocation>> GetWeaponCollocationsAsync(bool last = false)
|
||||
{
|
||||
return FromCacheOrWebAsync(nameof(WeaponCollocation), homaClient.GetWeaponCollocationsAsync);
|
||||
return FromCacheOrWebAsync(nameof(WeaponCollocation), last, homaClient.GetWeaponCollocationsAsync);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<List<TeamAppearance>> GetTeamAppearancesAsync()
|
||||
public ValueTask<List<TeamAppearance>> GetTeamAppearancesAsync(bool last = false)
|
||||
{
|
||||
return FromCacheOrWebAsync(nameof(TeamAppearance), homaClient.GetTeamCombinationsAsync);
|
||||
return FromCacheOrWebAsync(nameof(TeamAppearance), last, homaClient.GetTeamCombinationsAsync);
|
||||
}
|
||||
|
||||
private async ValueTask<T> FromCacheOrWebAsync<T>(string typeName, Func<CancellationToken, ValueTask<HutaoResponse<T>>> taskFunc)
|
||||
private async ValueTask<T> FromCacheOrWebAsync<T>(string typeName, bool last, Func<bool, CancellationToken, ValueTask<HutaoResponse<T>>> taskFunc)
|
||||
where T : class, new()
|
||||
{
|
||||
string key = $"{nameof(HutaoSpiralAbyssService)}.Cache.{typeName}";
|
||||
string kind = last ? "Last" : "Current";
|
||||
string key = $"{nameof(HutaoSpiralAbyssService)}.Cache.{typeName}.{kind}";
|
||||
if (memoryCache.TryGetValue(key, out object? cache))
|
||||
{
|
||||
T? t = cache as T;
|
||||
@@ -81,7 +82,7 @@ internal sealed partial class HutaoSpiralAbyssService : IHutaoSpiralAbyssService
|
||||
return memoryCache.Set(key, value, cacheExpireTime);
|
||||
}
|
||||
|
||||
Response<T> webResponse = await taskFunc(default).ConfigureAwait(false);
|
||||
Response<T> webResponse = await taskFunc(last, default).ConfigureAwait(false);
|
||||
T? data = webResponse.Data;
|
||||
|
||||
if (data is not null)
|
||||
|
||||
@@ -9,6 +9,7 @@ using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.ViewModel.Complex;
|
||||
using Snap.Hutao.Web.Hutao.SpiralAbyss;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Snap.Hutao.Service.Hutao;
|
||||
|
||||
@@ -124,6 +125,32 @@ internal sealed partial class HutaoSpiralAbyssStatisticsCache : IHutaoSpiralAbys
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static IEnumerable<TResult> CurrentLeftJoinLast<TElement, TKey, TResult>(IEnumerable<TElement> current, IEnumerable<TElement>? last, Func<TElement, TKey> keySelector, Func<TElement, TElement?, TResult> resultSelector)
|
||||
where TKey : notnull
|
||||
{
|
||||
if (last is null)
|
||||
{
|
||||
foreach (TElement element in current)
|
||||
{
|
||||
yield return resultSelector(element, default);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Dictionary<TKey, TElement> lastMap = [];
|
||||
foreach (TElement element in last)
|
||||
{
|
||||
lastMap[keySelector(element)] = element;
|
||||
}
|
||||
|
||||
foreach (TElement element in current)
|
||||
{
|
||||
yield return resultSelector(element, lastMap.GetValueOrDefault(keySelector(element)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask<Dictionary<AvatarId, Avatar>> GetIdAvatarMapExtendedAsync()
|
||||
{
|
||||
if (idAvatarExtendedMap is null)
|
||||
@@ -137,86 +164,91 @@ internal sealed partial class HutaoSpiralAbyssStatisticsCache : IHutaoSpiralAbys
|
||||
|
||||
private async ValueTask AvatarCollocationsAsync(Dictionary<AvatarId, Avatar> idAvatarMap, Dictionary<WeaponId, Weapon> idWeaponMap, Dictionary<ExtendedEquipAffixId, Model.Metadata.Reliquary.ReliquarySet> idReliquarySetMap)
|
||||
{
|
||||
List<AvatarCollocation> avatarCollocationsRaw;
|
||||
List<AvatarCollocation> raw, rawLast;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
IHutaoSpiralAbyssService hutaoService = scope.ServiceProvider.GetRequiredService<IHutaoSpiralAbyssService>();
|
||||
avatarCollocationsRaw = await hutaoService.GetAvatarCollocationsAsync().ConfigureAwait(false);
|
||||
raw = await hutaoService.GetAvatarCollocationsAsync(false).ConfigureAwait(false);
|
||||
rawLast = await hutaoService.GetAvatarCollocationsAsync(true).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
AvatarCollocations = avatarCollocationsRaw.SelectList(co => new AvatarCollocationView()
|
||||
AvatarCollocations = CurrentLeftJoinLast(raw, rawLast, data => data.AvatarId, (raw, rawLast) => new AvatarCollocationView()
|
||||
{
|
||||
AvatarId = co.AvatarId,
|
||||
Avatars = co.Avatars.SelectList(a => new AvatarView(idAvatarMap[a.Item], a.Rate)),
|
||||
Weapons = co.Weapons.SelectList(w => new WeaponView(idWeaponMap[w.Item], w.Rate)),
|
||||
ReliquarySets = co.Reliquaries.SelectList(r => new ReliquarySetView(r, idReliquarySetMap)),
|
||||
AvatarId = raw.AvatarId,
|
||||
Avatars = CurrentLeftJoinLast(raw.Avatars, rawLast?.Avatars, data => data.Item, (avatar, avatarLast) => new AvatarView(idAvatarMap[avatar.Item], avatar.Rate, avatarLast?.Rate)).ToList(),
|
||||
Weapons = CurrentLeftJoinLast(raw.Weapons, rawLast?.Weapons, data => data.Item, (weapon, weaponLast) => new WeaponView(idWeaponMap[weapon.Item], weapon.Rate, weaponLast?.Rate)).ToList(),
|
||||
ReliquarySets = CurrentLeftJoinLast(raw.Reliquaries, rawLast?.Reliquaries, data => data.Item, (relic, relicLast) => new ReliquarySetView(idReliquarySetMap, relic, relicLast)).ToList(),
|
||||
}).ToDictionary(a => a.AvatarId);
|
||||
}
|
||||
|
||||
private async ValueTask WeaponCollocationsAsync(Dictionary<AvatarId, Avatar> idAvatarMap)
|
||||
{
|
||||
List<WeaponCollocation> weaponCollocationsRaw;
|
||||
List<WeaponCollocation> raw, rawLast;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
IHutaoSpiralAbyssService hutaoService = scope.ServiceProvider.GetRequiredService<IHutaoSpiralAbyssService>();
|
||||
weaponCollocationsRaw = await hutaoService.GetWeaponCollocationsAsync().ConfigureAwait(false);
|
||||
raw = await hutaoService.GetWeaponCollocationsAsync(false).ConfigureAwait(false);
|
||||
rawLast = await hutaoService.GetWeaponCollocationsAsync(true).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
WeaponCollocations = weaponCollocationsRaw.SelectList(co => new WeaponCollocationView()
|
||||
WeaponCollocations = CurrentLeftJoinLast(raw, rawLast, data => data.WeaponId, (raw, rawLast) => new WeaponCollocationView()
|
||||
{
|
||||
WeaponId = co.WeaponId,
|
||||
Avatars = co.Avatars.SelectList(a => new AvatarView(idAvatarMap[a.Item], a.Rate)),
|
||||
WeaponId = raw.WeaponId,
|
||||
Avatars = CurrentLeftJoinLast(raw.Avatars, rawLast?.Avatars, data => data.Item, (avatar, avatarLast) => new AvatarView(idAvatarMap[avatar.Item], avatar.Rate, avatarLast?.Rate)).ToList(),
|
||||
}).ToDictionary(w => w.WeaponId);
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SH003")]
|
||||
private async Task AvatarAppearanceRankAsync(Dictionary<AvatarId, Avatar> idAvatarMap)
|
||||
{
|
||||
List<AvatarAppearanceRank> avatarAppearanceRanksRaw;
|
||||
List<AvatarAppearanceRank> raw, rawLast;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
IHutaoSpiralAbyssService hutaoService = scope.ServiceProvider.GetRequiredService<IHutaoSpiralAbyssService>();
|
||||
avatarAppearanceRanksRaw = await hutaoService.GetAvatarAppearanceRanksAsync().ConfigureAwait(false);
|
||||
raw = await hutaoService.GetAvatarAppearanceRanksAsync(false).ConfigureAwait(false);
|
||||
rawLast = await hutaoService.GetAvatarAppearanceRanksAsync(true).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
AvatarAppearanceRanks = avatarAppearanceRanksRaw.SortByDescending(r => r.Floor).SelectList(rank => new AvatarRankView
|
||||
AvatarAppearanceRanks = CurrentLeftJoinLast(raw.SortByDescending(r => r.Floor), rawLast, data => data.Floor, (raw, rawLast) => new AvatarRankView
|
||||
{
|
||||
Floor = SH.FormatModelBindingHutaoComplexRankFloor(rank.Floor),
|
||||
Avatars = rank.Ranks.SortByDescending(r => r.Rate).SelectList(rank => new AvatarView(idAvatarMap[rank.Item], rank.Rate)),
|
||||
});
|
||||
Floor = SH.FormatModelBindingHutaoComplexRankFloor(raw.Floor),
|
||||
Avatars = CurrentLeftJoinLast(raw.Ranks.SortByDescending(r => r.Rate), rawLast?.Ranks, data => data.Item, (rank, rankLast) => new AvatarView(idAvatarMap[rank.Item], rank.Rate, rankLast?.Rate)).ToList(),
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SH003")]
|
||||
private async Task AvatarUsageRanksAsync(Dictionary<AvatarId, Avatar> idAvatarMap)
|
||||
{
|
||||
List<AvatarUsageRank> avatarUsageRanksRaw;
|
||||
List<AvatarUsageRank> raw, rawLast;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
IHutaoSpiralAbyssService hutaoService = scope.ServiceProvider.GetRequiredService<IHutaoSpiralAbyssService>();
|
||||
avatarUsageRanksRaw = await hutaoService.GetAvatarUsageRanksAsync().ConfigureAwait(false);
|
||||
raw = await hutaoService.GetAvatarUsageRanksAsync(false).ConfigureAwait(false);
|
||||
rawLast = await hutaoService.GetAvatarUsageRanksAsync(true).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
AvatarUsageRanks = avatarUsageRanksRaw.SortByDescending(r => r.Floor).SelectList(rank => new AvatarRankView
|
||||
AvatarUsageRanks = CurrentLeftJoinLast(raw.SortByDescending(r => r.Floor), rawLast, data => data.Floor, (raw, rawLast) => new AvatarRankView
|
||||
{
|
||||
Floor = SH.FormatModelBindingHutaoComplexRankFloor(rank.Floor),
|
||||
Avatars = rank.Ranks.SortByDescending(r => r.Rate).SelectList(rank => new AvatarView(idAvatarMap[rank.Item], rank.Rate)),
|
||||
});
|
||||
Floor = SH.FormatModelBindingHutaoComplexRankFloor(raw.Floor),
|
||||
Avatars = CurrentLeftJoinLast(raw.Ranks.SortByDescending(r => r.Rate), rawLast?.Ranks, data => data.Item, (rank, rankLast) => new AvatarView(idAvatarMap[rank.Item], rank.Rate, rankLast?.Rate)).ToList(),
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SH003")]
|
||||
private async Task AvatarConstellationInfosAsync(Dictionary<AvatarId, Avatar> idAvatarMap)
|
||||
{
|
||||
List<AvatarConstellationInfo> avatarConstellationInfosRaw;
|
||||
List<AvatarConstellationInfo> raw, rawLast;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
IHutaoSpiralAbyssService hutaoService = scope.ServiceProvider.GetRequiredService<IHutaoSpiralAbyssService>();
|
||||
avatarConstellationInfosRaw = await hutaoService.GetAvatarConstellationInfosAsync().ConfigureAwait(false);
|
||||
raw = await hutaoService.GetAvatarConstellationInfosAsync(false).ConfigureAwait(false);
|
||||
rawLast = await hutaoService.GetAvatarConstellationInfosAsync(true).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
AvatarConstellationInfos = avatarConstellationInfosRaw.SortBy(i => i.HoldingRate).SelectList(info =>
|
||||
AvatarConstellationInfos = CurrentLeftJoinLast(raw.SortBy(i => i.HoldingRate), rawLast, data => data.AvatarId, (raw, rawLast) => new AvatarConstellationInfoView(idAvatarMap[raw.AvatarId], raw.HoldingRate, rawLast?.HoldingRate)
|
||||
{
|
||||
return new AvatarConstellationInfoView(idAvatarMap[info.AvatarId], info.HoldingRate, info.Constellations.SelectList(x => x.Rate));
|
||||
});
|
||||
Rates = CurrentLeftJoinLast(raw.Constellations, rawLast?.Constellations, data => data.Item, (rate, rataLast) => new RateAndDelta(rate.Rate, rataLast?.Rate)).ToList(),
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SH003")]
|
||||
|
||||
@@ -11,45 +11,17 @@ namespace Snap.Hutao.Service.Hutao;
|
||||
[HighQuality]
|
||||
internal interface IHutaoSpiralAbyssService
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步获取角色上场率
|
||||
/// </summary>
|
||||
/// <returns>角色上场率</returns>
|
||||
ValueTask<List<AvatarAppearanceRank>> GetAvatarAppearanceRanksAsync();
|
||||
ValueTask<List<AvatarAppearanceRank>> GetAvatarAppearanceRanksAsync(bool last = false);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取角色搭配
|
||||
/// </summary>
|
||||
/// <returns>角色搭配</returns>
|
||||
ValueTask<List<AvatarCollocation>> GetAvatarCollocationsAsync();
|
||||
ValueTask<List<AvatarCollocation>> GetAvatarCollocationsAsync(bool last = false);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取角色持有率信息
|
||||
/// </summary>
|
||||
/// <returns>角色持有率信息</returns>
|
||||
ValueTask<List<AvatarConstellationInfo>> GetAvatarConstellationInfosAsync();
|
||||
ValueTask<List<AvatarConstellationInfo>> GetAvatarConstellationInfosAsync(bool last = false);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取角色使用率
|
||||
/// </summary>
|
||||
/// <returns>角色使用率</returns>
|
||||
ValueTask<List<AvatarUsageRank>> GetAvatarUsageRanksAsync();
|
||||
ValueTask<List<AvatarUsageRank>> GetAvatarUsageRanksAsync(bool last = false);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取统计数据
|
||||
/// </summary>
|
||||
/// <returns>统计数据</returns>
|
||||
ValueTask<Overview> GetOverviewAsync();
|
||||
ValueTask<Overview> GetOverviewAsync(bool last = false);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取队伍上场
|
||||
/// </summary>
|
||||
/// <returns>队伍上场</returns>
|
||||
ValueTask<List<TeamAppearance>> GetTeamAppearancesAsync();
|
||||
ValueTask<List<TeamAppearance>> GetTeamAppearancesAsync(bool last = false);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取武器搭配
|
||||
/// </summary>
|
||||
/// <returns>武器搭配</returns>
|
||||
ValueTask<List<WeaponCollocation>> GetWeaponCollocationsAsync();
|
||||
ValueTask<List<WeaponCollocation>> GetWeaponCollocationsAsync(bool last = false);
|
||||
}
|
||||
@@ -25,8 +25,8 @@ internal sealed partial class DailyNoteRefreshJobScheduler : IJobScheduler
|
||||
|
||||
ITrigger dailyNoteTrigger = TriggerBuilder.Create()
|
||||
.WithIdentity(JobIdentity.DailyNoteRefreshTriggerName, JobIdentity.DailyNoteGroupName)
|
||||
.StartNow()
|
||||
.WithSimpleSchedule(builder => builder.WithIntervalInSeconds(interval).RepeatForever())
|
||||
.StartAt(DateTimeOffset.Now.AddSeconds(interval))
|
||||
.Build();
|
||||
|
||||
await scheduler.ScheduleJob(dailyNoteJob, dailyNoteTrigger).ConfigureAwait(false);
|
||||
|
||||
@@ -7,5 +7,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataDictionaryIdAchievementSource
|
||||
{
|
||||
public Dictionary<AchievementId, Model.Metadata.Achievement.Achievement> IdAchievementMap { get; set; }
|
||||
Dictionary<AchievementId, Model.Metadata.Achievement.Achievement> IdAchievementMap { get; set; }
|
||||
}
|
||||
@@ -7,5 +7,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataDictionaryIdAvatarSource
|
||||
{
|
||||
public Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> IdAvatarMap { get; set; }
|
||||
Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> IdAvatarMap { get; set; }
|
||||
}
|
||||
@@ -8,5 +8,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataDictionaryIdMaterialSource
|
||||
{
|
||||
public Dictionary<MaterialId, Material> IdMaterialMap { get; set; }
|
||||
Dictionary<MaterialId, Material> IdMaterialMap { get; set; }
|
||||
}
|
||||
@@ -8,5 +8,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataDictionaryIdReliquaryAffixWeightSource
|
||||
{
|
||||
public Dictionary<AvatarId, ReliquaryAffixWeight> IdReliquaryAffixWeightMap { get; set; }
|
||||
Dictionary<AvatarId, ReliquaryAffixWeight> IdReliquaryAffixWeightMap { get; set; }
|
||||
}
|
||||
@@ -8,5 +8,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataDictionaryIdReliquaryMainPropertySource
|
||||
{
|
||||
public Dictionary<ReliquaryMainAffixId, FightProperty> IdReliquaryMainPropertyMap { get; set; }
|
||||
Dictionary<ReliquaryMainAffixId, FightProperty> IdReliquaryMainPropertyMap { get; set; }
|
||||
}
|
||||
@@ -8,5 +8,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataDictionaryIdReliquarySource
|
||||
{
|
||||
public Dictionary<ReliquaryId, Reliquary> IdReliquaryMap { get; set; }
|
||||
Dictionary<ReliquaryId, Reliquary> IdReliquaryMap { get; set; }
|
||||
}
|
||||
@@ -8,5 +8,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataDictionaryIdReliquarySubAffixSource
|
||||
{
|
||||
public Dictionary<ReliquarySubAffixId, ReliquarySubAffix> IdReliquarySubAffixMap { get; set; }
|
||||
Dictionary<ReliquarySubAffixId, ReliquarySubAffix> IdReliquarySubAffixMap { get; set; }
|
||||
}
|
||||
@@ -7,5 +7,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataDictionaryIdWeaponSource
|
||||
{
|
||||
public Dictionary<WeaponId, Model.Metadata.Weapon.Weapon> IdWeaponMap { get; set; }
|
||||
Dictionary<WeaponId, Model.Metadata.Weapon.Weapon> IdWeaponMap { get; set; }
|
||||
}
|
||||
@@ -5,5 +5,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataDictionaryNameAvatarSource
|
||||
{
|
||||
public Dictionary<string, Model.Metadata.Avatar.Avatar> NameAvatarMap { get; set; }
|
||||
Dictionary<string, Model.Metadata.Avatar.Avatar> NameAvatarMap { get; set; }
|
||||
}
|
||||
@@ -5,5 +5,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataDictionaryNameWeaponSource
|
||||
{
|
||||
public Dictionary<string, Model.Metadata.Weapon.Weapon> NameWeaponMap { get; set; }
|
||||
Dictionary<string, Model.Metadata.Weapon.Weapon> NameWeaponMap { get; set; }
|
||||
}
|
||||
@@ -5,5 +5,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataListAchievementSource
|
||||
{
|
||||
public List<Model.Metadata.Achievement.Achievement> Achievements { get; set; }
|
||||
List<Model.Metadata.Achievement.Achievement> Achievements { get; set; }
|
||||
}
|
||||
@@ -7,5 +7,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataListChapterSource
|
||||
{
|
||||
public List<Chapter> Chapters { get; set; }
|
||||
List<Chapter> Chapters { get; set; }
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataListGachaEventSource
|
||||
{
|
||||
public List<GachaEvent> GachaEvents { get; set; }
|
||||
List<GachaEvent> GachaEvents { get; set; }
|
||||
}
|
||||
@@ -7,5 +7,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataListMaterialSource
|
||||
{
|
||||
public List<Material> Materials { get; set; }
|
||||
List<Material> Materials { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataListProfilePictureSource
|
||||
{
|
||||
List<ProfilePicture> ProfilePictures { get; set; }
|
||||
}
|
||||
@@ -7,5 +7,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataListReliquaryMainAffixLevelSource
|
||||
{
|
||||
public List<ReliquaryMainAffixLevel> ReliquaryMainAffixLevels { get; set; }
|
||||
List<ReliquaryMainAffixLevel> ReliquaryMainAffixLevels { get; set; }
|
||||
}
|
||||
@@ -7,5 +7,5 @@ namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataListReliquarySource
|
||||
{
|
||||
public List<Reliquary> Reliquaries { get; set; }
|
||||
List<Reliquary> Reliquaries { get; set; }
|
||||
}
|
||||
@@ -38,6 +38,11 @@ internal static class MetadataServiceContextExtension
|
||||
listMaterialSource.Materials = await metadataService.GetMaterialListAsync(token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (context is IMetadataListProfilePictureSource dictionaryIdProfilePictureSource)
|
||||
{
|
||||
dictionaryIdProfilePictureSource.ProfilePictures = await metadataService.GetProfilePictureListAsync(token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (context is IMetadataListReliquaryMainAffixLevelSource listReliquaryMainAffixLevelSource)
|
||||
{
|
||||
listReliquaryMainAffixLevelSource.ReliquaryMainAffixLevels = await metadataService.GetReliquaryMainAffixLevelListAsync(token).ConfigureAwait(false);
|
||||
|
||||
@@ -16,6 +16,7 @@ internal static class MetadataFileNames
|
||||
public const string FileNameMaterial = "Material";
|
||||
public const string FileNameMonster = "Monster";
|
||||
public const string FileNameMonsterCurve = "MonsterCurve";
|
||||
public const string FileNameProfilePicture = "ProfilePicture";
|
||||
public const string FileNameReliquary = "Reliquary";
|
||||
public const string FileNameReliquaryAffixWeight = "ReliquaryAffixWeight";
|
||||
public const string FileNameReliquaryMainAffix = "ReliquaryMainAffix";
|
||||
|
||||
@@ -70,6 +70,11 @@ internal static class MetadataServiceListExtension
|
||||
return metadataService.FromCacheOrFileAsync<List<GrowCurve>>(FileNameMonsterCurve, token);
|
||||
}
|
||||
|
||||
public static ValueTask<List<ProfilePicture>> GetProfilePictureListAsync(this IMetadataService metadataService, CancellationToken token = default)
|
||||
{
|
||||
return metadataService.FromCacheOrFileAsync<List<ProfilePicture>>(FileNameProfilePicture, token);
|
||||
}
|
||||
|
||||
public static ValueTask<List<Reliquary>> GetReliquaryListAsync(this IMetadataService metadataService, CancellationToken token = default)
|
||||
{
|
||||
return metadataService.FromCacheOrFileAsync<List<Reliquary>>(FileNameReliquary, token);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Notifications;
|
||||
using Microsoft.Windows.AppNotifications;
|
||||
|
||||
namespace Snap.Hutao.Service.Notification;
|
||||
|
||||
@@ -16,7 +16,8 @@ internal sealed class ToastNotificationLifeTime : IToastNotificationLifeTime
|
||||
// 用于在程序退出时尝试清除所有的系统通知
|
||||
try
|
||||
{
|
||||
ToastNotificationManagerCompat.History.Clear();
|
||||
AppNotificationManager.Default.RemoveAllAsync().AsTask().GetAwaiter().GetResult();
|
||||
AppNotificationManager.Default.Unregister();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
|
||||
namespace Snap.Hutao.Service.User;
|
||||
|
||||
internal interface IProfilePictureService
|
||||
{
|
||||
ValueTask TryInitializeAsync(ViewModel.User.User user, CancellationToken token = default(CancellationToken));
|
||||
|
||||
ValueTask RefreshUserGameRoleAsync(UserGameRole userGameRole, CancellationToken token = default(CancellationToken));
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
|
||||
namespace Snap.Hutao.Service.User;
|
||||
|
||||
internal interface IUidProfilePictureDbService : IAppDbService<UidProfilePicture>
|
||||
{
|
||||
ValueTask<UidProfilePicture?> SingleUidProfilePictureOrDefaultByUidAsync(string uid, CancellationToken token = default);
|
||||
|
||||
ValueTask UpdateUidProfilePictureAsync(UidProfilePicture profilePicture, CancellationToken token = default);
|
||||
|
||||
ValueTask DeleteUidProfilePictureByUidAsync(string uid, CancellationToken token = default);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
namespace Snap.Hutao.Service.User;
|
||||
|
||||
internal interface IUserMetadataContext : IMetadataContext,
|
||||
IMetadataListProfilePictureSource;
|
||||
@@ -53,4 +53,6 @@ internal interface IUserService
|
||||
/// <param name="user">待移除的用户</param>
|
||||
/// <returns>任务</returns>
|
||||
ValueTask RemoveUserAsync(BindingUser user);
|
||||
|
||||
ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole);
|
||||
}
|
||||
109
src/Snap.Hutao/Snap.Hutao/Service/User/ProfilePictureService.cs
Normal file
109
src/Snap.Hutao/Snap.Hutao/Service/User/ProfilePictureService.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
using Snap.Hutao.Web.Enka;
|
||||
using Snap.Hutao.Web.Enka.Model;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
|
||||
namespace Snap.Hutao.Service.User;
|
||||
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton, typeof(IProfilePictureService))]
|
||||
internal sealed partial class ProfilePictureService : IProfilePictureService
|
||||
{
|
||||
private readonly IUidProfilePictureDbService uidProfilePictureDbService;
|
||||
private readonly IMetadataService metadataService;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
public async ValueTask TryInitializeAsync(ViewModel.User.User user, CancellationToken token = default)
|
||||
{
|
||||
foreach (UserGameRole userGameRole in user.UserGameRoles)
|
||||
{
|
||||
if (await uidProfilePictureDbService.SingleUidProfilePictureOrDefaultByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false) is { } profilePicture)
|
||||
{
|
||||
if (await TryUpdateUserGameRoleAsync(userGameRole, profilePicture, token).ConfigureAwait(false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Force update
|
||||
await RefreshUserGameRoleAsync(userGameRole, token: token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask RefreshUserGameRoleAsync(UserGameRole userGameRole, CancellationToken token = default)
|
||||
{
|
||||
EnkaResponse? enkaResponse;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
EnkaClient enkaClient = scope.ServiceProvider
|
||||
.GetRequiredService<EnkaClient>();
|
||||
|
||||
enkaResponse = await enkaClient.GetForwardPlayerInfoAsync(userGameRole, token).ConfigureAwait(false)
|
||||
?? await enkaClient.GetPlayerInfoAsync(userGameRole, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (enkaResponse is { PlayerInfo: { } playerInfo })
|
||||
{
|
||||
UidProfilePicture profilePicture = UidProfilePicture.From(userGameRole, playerInfo.ProfilePicture);
|
||||
|
||||
await uidProfilePictureDbService.DeleteUidProfilePictureByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false);
|
||||
await uidProfilePictureDbService.UpdateUidProfilePictureAsync(profilePicture, token).ConfigureAwait(false);
|
||||
|
||||
await TryUpdateUserGameRoleAsync(userGameRole, profilePicture, token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask<bool> TryUpdateUserGameRoleAsync(UserGameRole userGameRole, UidProfilePicture cache, CancellationToken token = default)
|
||||
{
|
||||
if (cache.RefreshTime.AddDays(15) < DateTimeOffset.Now)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!await metadataService.InitializeAsync().ConfigureAwait(false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UserMetadataContext context = await metadataService.GetContextAsync<UserMetadataContext>(token).ConfigureAwait(false);
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
// Most common to most rare
|
||||
if (cache.ProfilePictureId is not 0U)
|
||||
{
|
||||
userGameRole.ProfilePictureIcon = context.ProfilePictures
|
||||
.Single(p => p.Id == cache.ProfilePictureId)
|
||||
.Icon;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cache.AvatarId is not 0U)
|
||||
{
|
||||
userGameRole.ProfilePictureIcon = context.ProfilePictures
|
||||
.Single(p => p.UnlockType is ProfilePictureUnlockType.Avatar && p.UnlockParameter == cache.AvatarId)
|
||||
.Icon;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cache.CostumeId is not 0U)
|
||||
{
|
||||
userGameRole.ProfilePictureIcon = context.ProfilePictures
|
||||
.Single(p => p.UnlockType is ProfilePictureUnlockType.Costume && p.UnlockParameter == cache.CostumeId)
|
||||
.Icon;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
|
||||
namespace Snap.Hutao.Service.User;
|
||||
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton, typeof(IUidProfilePictureDbService))]
|
||||
internal sealed partial class UidProfilePictureDbService : IUidProfilePictureDbService
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public IServiceProvider ServiceProvider { get => serviceProvider; }
|
||||
|
||||
public ValueTask<UidProfilePicture?> SingleUidProfilePictureOrDefaultByUidAsync(string uid, CancellationToken token = default)
|
||||
{
|
||||
return this.QueryAsync(query => query.SingleOrDefaultAsync(n => n.Uid == uid));
|
||||
}
|
||||
|
||||
public async ValueTask UpdateUidProfilePictureAsync(UidProfilePicture profilePicture, CancellationToken token = default)
|
||||
{
|
||||
await this.UpdateAsync(profilePicture, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async ValueTask DeleteUidProfilePictureByUidAsync(string uid, CancellationToken token = default)
|
||||
{
|
||||
await this.DeleteAsync(profilePicture => profilePicture.Uid == uid, token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ namespace Snap.Hutao.Service.User;
|
||||
internal sealed partial class UserInitializationService : IUserInitializationService
|
||||
{
|
||||
private readonly IUserFingerprintService userFingerprintService;
|
||||
private readonly IProfilePictureService profilePictureService;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public async ValueTask<ViewModel.User.User> ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default)
|
||||
@@ -90,6 +91,7 @@ internal sealed partial class UserInitializationService : IUserInitializationSer
|
||||
}
|
||||
|
||||
await userFingerprintService.TryInitializeAsync(user, token).ConfigureAwait(false);
|
||||
await profilePictureService.TryInitializeAsync(user, token).ConfigureAwait(false);
|
||||
|
||||
return user.IsInitialized = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
|
||||
namespace Snap.Hutao.Service.User;
|
||||
|
||||
internal class UserMetadataContext : IUserMetadataContext
|
||||
{
|
||||
public List<ProfilePicture> ProfilePictures { get; set; } = default!;
|
||||
}
|
||||
@@ -21,6 +21,7 @@ namespace Snap.Hutao.Service.User;
|
||||
[Injection(InjectAs.Singleton, typeof(IUserService))]
|
||||
internal sealed partial class UserService : IUserService, IUserServiceUnsafe
|
||||
{
|
||||
private readonly IProfilePictureService profilePictureService;
|
||||
private readonly IUserCollectionService userCollectionService;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly IUserDbService userDbService;
|
||||
@@ -121,4 +122,9 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole)
|
||||
{
|
||||
await profilePictureService.RefreshUserGameRoleAsync(userGameRole).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -160,6 +160,7 @@
|
||||
<None Remove="View\Control\LaunchGameResourceExpander.xaml" />
|
||||
<None Remove="View\Control\LoadingView.xaml" />
|
||||
<None Remove="View\Control\LoadingViewSlim.xaml" />
|
||||
<None Remove="View\Control\RateDeltaTextBlockStyle.xaml" />
|
||||
<None Remove="View\Control\SkillPivot.xaml" />
|
||||
<None Remove="View\Control\SegmentedOverride.xaml" />
|
||||
<None Remove="View\Control\StatisticsCard.xaml" />
|
||||
@@ -312,10 +313,9 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.0.240109" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.TokenizingTextBox" Version="8.0.240109" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.0.240109" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
||||
<PackageReference Include="Google.OrTools" Version="9.10.4067" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.5">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -330,7 +330,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.8.8" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240428000" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240607001" />
|
||||
<PackageReference Include="QRCoder" Version="1.5.1" />
|
||||
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.9.0" />
|
||||
<PackageReference Include="Snap.Discord.GameSDK" Version="1.6.0" />
|
||||
@@ -363,6 +363,11 @@
|
||||
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnablePreviewMsixTooling)'=='true'">
|
||||
<ProjectCapability Include="Msix" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Control\RateDeltaTextBlockStyle.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Service\Game\Automation\ScreenCapture\GameScreenCaptureDebugPreviewWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
d:DataContext="{d:DesignInstance shva:AchievementViewModelSlim}"
|
||||
Background="Transparent"
|
||||
BorderBrush="{x:Null}"
|
||||
BorderThickness="0"
|
||||
Command="{Binding NavigateCommand}"
|
||||
Style="{ThemeResource DefaultButtonStyle}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
d:DataContext="{d:DesignInstance shvd:DailyNoteViewModelSlim}"
|
||||
Background="Transparent"
|
||||
BorderBrush="{x:Null}"
|
||||
BorderThickness="0"
|
||||
Command="{Binding NavigateCommand}"
|
||||
Style="{ThemeResource DefaultButtonStyle}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
d:DataContext="{d:DesignInstance shvg:GachaLogViewModelSlim}"
|
||||
Background="Transparent"
|
||||
BorderBrush="{x:Null}"
|
||||
BorderThickness="0"
|
||||
Command="{Binding NavigateCommand}"
|
||||
Style="{ThemeResource DefaultButtonStyle}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
d:DataContext="{d:DesignInstance shvg:LaunchGameViewModelSlim}"
|
||||
Background="Transparent"
|
||||
BorderBrush="{x:Null}"
|
||||
BorderThickness="0"
|
||||
Command="{Binding LaunchCommand}"
|
||||
Style="{ThemeResource DefaultButtonStyle}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||
xmlns:shwhshlr="using:Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher.Resource"
|
||||
xmlns:shwhhpc="using:Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect"
|
||||
xmlns:shwhhpcp="using:Snap.Hutao.Web.Hoyolab.HoyoPlay.Connect.Package"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
d:DataContext="{d:DesignInstance shwhshlr:Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher.Resource.Package}"
|
||||
d:DataContext="{d:DesignInstance shwhhpcp:Package}"
|
||||
BorderThickness="0"
|
||||
IsExpanded="True"
|
||||
ItemsSource="{Binding VoicePacks}"
|
||||
ItemsSource="{Binding AllPackages}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<cwc:SettingsExpander.Resources>
|
||||
@@ -24,7 +26,7 @@
|
||||
</cwc:SettingsExpander.Resources>
|
||||
|
||||
<cwc:SettingsExpander.ItemTemplate>
|
||||
<DataTemplate x:DataType="shwhshlr:VoicePackage">
|
||||
<DataTemplate x:DataType="shwhhpc:PackageSegment">
|
||||
<cwc:SettingsCard
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
ActionIcon="{shcm:FontIcon Glyph={StaticResource FontIconContentCopy}}"
|
||||
@@ -35,70 +37,26 @@
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="0,4,0,0"
|
||||
Orientation="Horizontal">
|
||||
Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="{StaticResource FontIconContentZipFolder}"/>
|
||||
<TextBlock
|
||||
Width="80"
|
||||
Margin="8,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding PackageSize, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
|
||||
Text="{Binding Size, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
|
||||
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="{StaticResource FontIconContentFolder}"/>
|
||||
<TextBlock
|
||||
Width="80"
|
||||
Margin="8,0,0,0"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding Size, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
|
||||
Text="{Binding DecompressedSize, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
|
||||
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="{StaticResource FontIconContentAsteriskBadge12}"/>
|
||||
<TextBlock
|
||||
Margin="8,0,0,0"
|
||||
IsTextSelectionEnabled="True"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding Md5}"/>
|
||||
Text="{Binding MD5}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard.Description>
|
||||
</cwc:SettingsCard>
|
||||
</DataTemplate>
|
||||
</cwc:SettingsExpander.ItemTemplate>
|
||||
|
||||
<cwc:SettingsExpander.ItemsHeader>
|
||||
<cwc:SettingsCard
|
||||
MinHeight="52"
|
||||
Padding="{ThemeResource SettingsExpanderItemHasIconPadding}"
|
||||
ActionIcon="{shcm:FontIcon Glyph={StaticResource FontIconContentCopy}}"
|
||||
Background="{ThemeResource InfoBarInformationalSeverityBackgroundBrush}"
|
||||
BorderThickness="0"
|
||||
Command="{Binding CopyPathCommand}"
|
||||
CornerRadius="0"
|
||||
Header="{Binding DisplayName}"
|
||||
IsClickEnabled="True">
|
||||
<cwc:SettingsCard.Description>
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="0,4,0,0"
|
||||
Orientation="Horizontal">
|
||||
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="{StaticResource FontIconContentZipFolder}"/>
|
||||
<TextBlock
|
||||
Width="80"
|
||||
Margin="8,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding PackageSize, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
|
||||
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="{StaticResource FontIconContentFolder}"/>
|
||||
<TextBlock
|
||||
Width="80"
|
||||
Margin="8,0,0,0"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding Size, Converter={StaticResource FileSizeToFriendlyStringConverter}}"/>
|
||||
<FontIcon FontSize="{StaticResource CaptionTextBlockFontSize}" Glyph="{StaticResource FontIconContentAsteriskBadge12}"/>
|
||||
<TextBlock
|
||||
Margin="8,0,0,0"
|
||||
IsTextSelectionEnabled="True"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding Md5}"/>
|
||||
</StackPanel>
|
||||
</cwc:SettingsCard.Description>
|
||||
</cwc:SettingsCard>
|
||||
</cwc:SettingsExpander.ItemsHeader>
|
||||
|
||||
</cwc:SettingsExpander>
|
||||
|
||||
49
src/Snap.Hutao/Snap.Hutao/View/Control/RateDeltaTextBlock.cs
Normal file
49
src/Snap.Hutao/Snap.Hutao/View/Control/RateDeltaTextBlock.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Snap.Hutao.View.Control;
|
||||
|
||||
[TemplateVisualState(Name = "PositiveValue", GroupName = "CommonStates")]
|
||||
[TemplateVisualState(Name = "NegativeValue", GroupName = "CommonStates")]
|
||||
[DependencyProperty("Text", typeof(string), default(string), nameof(OnTextPropertyChanged))]
|
||||
[DependencyProperty("TextStyle", typeof(Style))]
|
||||
internal sealed partial class RateDeltaTextBlock : ContentControl
|
||||
{
|
||||
public RateDeltaTextBlock()
|
||||
{
|
||||
DefaultStyleKey = typeof(RateDeltaTextBlock);
|
||||
DefaultStyleResourceUri = "ms-appx:///View/Control/RateDeltaTextBlockStyle.xaml".ToUri();
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
RateDeltaTextBlock control = (RateDeltaTextBlock)d;
|
||||
control.UpdateState();
|
||||
}
|
||||
|
||||
private void UpdateState()
|
||||
{
|
||||
if (Text is string { Length: > 0 } text)
|
||||
{
|
||||
_ = text.AsSpan()[0] switch
|
||||
{
|
||||
'+' => VisualStateManager.GoToState(this, "PositiveValue", true),
|
||||
'-' => VisualStateManager.GoToState(this, "NegativeValue", true),
|
||||
_ => VisualStateManager.GoToState(this, "NoValue", true),
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStateManager.GoToState(this, "NoValue", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user