mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
26 Commits
test/dialo
...
qa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
409a223213 | ||
|
|
719d934222 | ||
|
|
e8eed46d82 | ||
|
|
ff9b553a19 | ||
|
|
95d64c2895 | ||
|
|
558551c8ad | ||
|
|
d05c196b7c | ||
|
|
502fb6dbed | ||
|
|
4fa5270070 | ||
|
|
94fda223fc | ||
|
|
18103b4deb | ||
|
|
16ac52e71d | ||
|
|
73825d391e | ||
|
|
3b2eeb84a7 | ||
|
|
3e8655fd55 | ||
|
|
fe38e14ae8 | ||
|
|
a174493819 | ||
|
|
3a57d55c62 | ||
|
|
99f35ca6db | ||
|
|
c423e8b72d | ||
|
|
7ff78def46 | ||
|
|
bc9018f4bf | ||
|
|
107963b7ac | ||
|
|
4e89406f2f | ||
|
|
8119de3fa9 | ||
|
|
7a8c233b10 |
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),我的问题没有在文档中得到解答
|
- label: 我已阅读 Snap Hutao 文档中的[常见问题](https://hut.ao/advanced/FAQ.html)和[常见程序异常](https://hut.ao/advanced/exceptions.html),我的问题没有在文档中得到解答
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- label: 我知道文档站的导航栏中有**搜索功能**,且已经搜索过相关关键词
|
- label: 我知道[文档站](https://hut.ao/zh/menu.html)的导航栏中有**搜索功能**,且已经搜索过相关关键词
|
||||||
required: true
|
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)的问题也不是一个别人已发布的**重复的**问题
|
- 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: 通过这个议题来向开发团队分享你的想法
|
description: 通过这个议题来向开发团队分享你的想法
|
||||||
title: "[Feat]: 在这里填写一个合适的标题"
|
title: "[Feat]: 在这里填写一个合适的标题"
|
||||||
labels: ["功能", "needs-triage", "priority:none"]
|
labels: ["feature request", "needs-triage", "priority:none"]
|
||||||
assignees:
|
assignees:
|
||||||
- Lightczx
|
- Lightczx
|
||||||
body:
|
body:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: Feature Request [English Form]
|
name: Feature Request [English Form]
|
||||||
description: Tell us about your thought
|
description: Tell us about your thought
|
||||||
title: "[Feat]: Place your title here"
|
title: "[Feat]: Place your title here"
|
||||||
labels: ["功能", "needs-triage", "priority:none"]
|
labels: ["feature request", "needs-triage", "priority:none"]
|
||||||
assignees:
|
assignees:
|
||||||
- Lightczx
|
- Lightczx
|
||||||
body:
|
body:
|
||||||
|
|||||||
8
.github/workflows/alpha.yml
vendored
8
.github/workflows/alpha.yml
vendored
@@ -67,9 +67,7 @@ jobs:
|
|||||||
> 普通用户请 [点击这里](https://github.com/DGP-Studio/Snap.Hutao/releases/latest/) 下载最新的稳定版本
|
> 普通用户请 [点击这里](https://github.com/DGP-Studio/Snap.Hutao/releases/latest/) 下载最新的稳定版本
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!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
|
echo $summary >> $Env:GITHUB_STEP_SUMMARY
|
||||||
@@ -114,9 +112,7 @@ jobs:
|
|||||||
> 普通用户请 [点击这里](https://github.com/DGP-Studio/Snap.Hutao/releases/latest/) 下载最新的稳定版本
|
> 普通用户请 [点击这里](https://github.com/DGP-Studio/Snap.Hutao/releases/latest/) 下载最新的稳定版本
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!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
|
echo $summary >> $Env:GITHUB_STEP_SUMMARY
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.Windows.AppLifecycle;
|
using Microsoft.Windows.AppLifecycle;
|
||||||
|
using Microsoft.Windows.AppNotifications;
|
||||||
using Snap.Hutao.Core;
|
using Snap.Hutao.Core;
|
||||||
using Snap.Hutao.Core.ExceptionService;
|
using Snap.Hutao.Core.ExceptionService;
|
||||||
using Snap.Hutao.Core.LifeCycle;
|
using Snap.Hutao.Core.LifeCycle;
|
||||||
@@ -68,6 +69,10 @@ public sealed partial class App : Application
|
|||||||
{
|
{
|
||||||
try
|
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();
|
AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
|
||||||
|
|
||||||
if (serviceProvider.GetRequiredService<PrivateNamedPipeClient>().TryRedirectActivationTo(activatedEventArgs))
|
if (serviceProvider.GetRequiredService<PrivateNamedPipeClient>().TryRedirectActivationTo(activatedEventArgs))
|
||||||
@@ -81,14 +86,7 @@ public sealed partial class App : Application
|
|||||||
LogDiagnosticInformation();
|
LogDiagnosticInformation();
|
||||||
|
|
||||||
// Manually invoke
|
// Manually invoke
|
||||||
HutaoActivationArguments hutaoArgs = HutaoActivationArguments.FromAppActivationArguments(activatedEventArgs);
|
activation.Activate(HutaoActivationArguments.FromAppActivationArguments(activatedEventArgs));
|
||||||
if (hutaoArgs.Kind is HutaoActivationKind.Toast)
|
|
||||||
{
|
|
||||||
Exit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
activation.Activate(hutaoArgs);
|
|
||||||
activation.PostInitialization();
|
activation.PostInitialization();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -7,23 +7,45 @@ using Microsoft.UI.Xaml.Controls;
|
|||||||
|
|
||||||
namespace Snap.Hutao.Control.Behavior;
|
namespace Snap.Hutao.Control.Behavior;
|
||||||
|
|
||||||
|
[SuppressMessage("", "CA1001")]
|
||||||
[DependencyProperty("MilliSecondsDelay", typeof(int))]
|
[DependencyProperty("MilliSecondsDelay", typeof(int))]
|
||||||
internal sealed partial class InfoBarDelayCloseBehavior : BehaviorBase<InfoBar>
|
internal sealed partial class InfoBarDelayCloseBehavior : BehaviorBase<InfoBar>
|
||||||
{
|
{
|
||||||
|
private readonly CancellationTokenSource closeTokenSource = new();
|
||||||
|
|
||||||
protected override void OnAssociatedObjectLoaded()
|
protected override void OnAssociatedObjectLoaded()
|
||||||
|
{
|
||||||
|
AssociatedObject.Closed += OnInfoBarClosed;
|
||||||
|
if (MilliSecondsDelay > 0)
|
||||||
{
|
{
|
||||||
DelayCoreAsync().SafeForget();
|
DelayCoreAsync().SafeForget();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async ValueTask DelayCoreAsync()
|
private async ValueTask DelayCoreAsync()
|
||||||
{
|
{
|
||||||
if (MilliSecondsDelay > 0)
|
try
|
||||||
{
|
{
|
||||||
await Delay.FromMilliSeconds(MilliSecondsDelay).ConfigureAwait(true);
|
await Task.Delay(MilliSecondsDelay, closeTokenSource.Token).ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (AssociatedObject is not null)
|
if (AssociatedObject is not null)
|
||||||
{
|
{
|
||||||
AssociatedObject.IsOpen = false;
|
AssociatedObject.IsOpen = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnInfoBarClosed(InfoBar infoBar, InfoBarClosedEventArgs args)
|
||||||
|
{
|
||||||
|
if (args.Reason is InfoBarCloseReason.CloseButton)
|
||||||
|
{
|
||||||
|
closeTokenSource.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
AssociatedObject.Closed -= OnInfoBarClosed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ namespace Snap.Hutao.Control.Image;
|
|||||||
[DependencyProperty("CachedName", typeof(string), "Unknown")]
|
[DependencyProperty("CachedName", typeof(string), "Unknown")]
|
||||||
internal sealed partial class CachedImage : Implementation.ImageEx
|
internal sealed partial class CachedImage : Implementation.ImageEx
|
||||||
{
|
{
|
||||||
private string? file;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的缓存图像
|
/// 构造一个新的缓存图像
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -43,7 +41,6 @@ internal sealed partial class CachedImage : Implementation.ImageEx
|
|||||||
HutaoException.ThrowIf(string.IsNullOrEmpty(imageUri.Host), SH.ControlImageCachedImageInvalidResourceUri);
|
HutaoException.ThrowIf(string.IsNullOrEmpty(imageUri.Host), SH.ControlImageCachedImageInvalidResourceUri);
|
||||||
string file = await imageCache.GetFileFromCacheAsync(imageUri).ConfigureAwait(true); // BitmapImage need to be created by main thread.
|
string file = await imageCache.GetFileFromCacheAsync(imageUri).ConfigureAwait(true); // BitmapImage need to be created by main thread.
|
||||||
CachedName = Path.GetFileName(file);
|
CachedName = Path.GetFileName(file);
|
||||||
this.file = file;
|
|
||||||
token.ThrowIfCancellationRequested(); // check token state to determine whether the operation should be canceled.
|
token.ThrowIfCancellationRequested(); // check token state to determine whether the operation should be canceled.
|
||||||
return file.ToUri();
|
return file.ToUri();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
x:Name="ContentGrid"
|
x:Name="ContentGrid"
|
||||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
x:Load="False">
|
x:Load="True">
|
||||||
<ContentPresenter.RenderTransform>
|
<ContentPresenter.RenderTransform>
|
||||||
<CompositeTransform/>
|
<CompositeTransform/>
|
||||||
</ContentPresenter.RenderTransform>
|
</ContentPresenter.RenderTransform>
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Snap.Hutao.Service.Notification;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Control.Selector;
|
||||||
|
|
||||||
|
internal sealed class InfoBarTemplateSelector : DataTemplateSelector
|
||||||
|
{
|
||||||
|
public DataTemplate ActionButtonEnabled { get; set; } = default!;
|
||||||
|
|
||||||
|
public DataTemplate ActionButtonDisabled { get; set; } = default!;
|
||||||
|
|
||||||
|
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||||
|
{
|
||||||
|
if (item is InfoBarOptions { ActionButtonContent: { }, ActionButtonCommand: { } })
|
||||||
|
{
|
||||||
|
return ActionButtonEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionButtonDisabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
<shmmc:AchievementIconConverter x:Key="AchievementIconConverter"/>
|
<shmmc:AchievementIconConverter x:Key="AchievementIconConverter"/>
|
||||||
<shmmc:AvatarCardConverter x:Key="AvatarCardConverter"/>
|
<shmmc:AvatarCardConverter x:Key="AvatarCardConverter"/>
|
||||||
<shmmc:AvatarIconConverter x:Key="AvatarIconConverter"/>
|
<shmmc:AvatarIconConverter x:Key="AvatarIconConverter"/>
|
||||||
|
<shmmc:AvatarIconCircleConverter x:Key="AvatarIconCircleConverter"/>
|
||||||
<shmmc:AvatarNameCardPicConverter x:Key="AvatarNameCardPicConverter"/>
|
<shmmc:AvatarNameCardPicConverter x:Key="AvatarNameCardPicConverter"/>
|
||||||
<shmmc:AvatarSideIconConverter x:Key="AvatarSideIconConverter"/>
|
<shmmc:AvatarSideIconConverter x:Key="AvatarSideIconConverter"/>
|
||||||
<shmmc:DescriptionsParametersDescriptor x:Key="DescParamDescriptor"/>
|
<shmmc:DescriptionsParametersDescriptor x:Key="DescParamDescriptor"/>
|
||||||
|
|||||||
@@ -2,4 +2,5 @@
|
|||||||
<CornerRadius x:Key="ControlCornerRadiusTop">4,4,0,0</CornerRadius>
|
<CornerRadius x:Key="ControlCornerRadiusTop">4,4,0,0</CornerRadius>
|
||||||
<CornerRadius x:Key="ControlCornerRadiusBottom">0,0,4,4</CornerRadius>
|
<CornerRadius x:Key="ControlCornerRadiusBottom">0,0,4,4</CornerRadius>
|
||||||
<CornerRadius x:Key="ControlCornerRadiusTopRightAndBottomLeft">0,4,0,4</CornerRadius>
|
<CornerRadius x:Key="ControlCornerRadiusTopRightAndBottomLeft">0,4,0,4</CornerRadius>
|
||||||
|
<CornerRadius x:Key="CornerRadiusAll16">16</CornerRadius>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -23,6 +23,9 @@
|
|||||||
<ItemsPanelTemplate x:Key="HorizontalStackPanelSpacing4Template">
|
<ItemsPanelTemplate x:Key="HorizontalStackPanelSpacing4Template">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="4"/>
|
<StackPanel Orientation="Horizontal" Spacing="4"/>
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
|
<ItemsPanelTemplate x:Key="HorizontalStackPanelSpacing6Template">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="6"/>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
<ItemsPanelTemplate x:Key="StackPanelSpacing4Template">
|
<ItemsPanelTemplate x:Key="StackPanelSpacing4Template">
|
||||||
<StackPanel Spacing="4"/>
|
<StackPanel Spacing="4"/>
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using CommunityToolkit.WinUI.Notifications;
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.Windows.AppNotifications;
|
||||||
using Snap.Hutao.Core.LifeCycle.InterProcess;
|
using Snap.Hutao.Core.LifeCycle.InterProcess;
|
||||||
using Snap.Hutao.Core.Setting;
|
using Snap.Hutao.Core.Setting;
|
||||||
using Snap.Hutao.Core.Shell;
|
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.HotKey;
|
||||||
using Snap.Hutao.Core.Windowing.NotifyIcon;
|
using Snap.Hutao.Core.Windowing.NotifyIcon;
|
||||||
using Snap.Hutao.Service;
|
using Snap.Hutao.Service;
|
||||||
using Snap.Hutao.Service.DailyNote;
|
|
||||||
using Snap.Hutao.Service.Discord;
|
using Snap.Hutao.Service.Discord;
|
||||||
using Snap.Hutao.Service.Hutao;
|
using Snap.Hutao.Service.Hutao;
|
||||||
using Snap.Hutao.Service.Job;
|
using Snap.Hutao.Service.Job;
|
||||||
@@ -37,12 +36,10 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
|||||||
public const string ImportUIAFFromClipboard = nameof(ImportUIAFFromClipboard);
|
public const string ImportUIAFFromClipboard = nameof(ImportUIAFFromClipboard);
|
||||||
|
|
||||||
private const string CategoryAchievement = "ACHIEVEMENT";
|
private const string CategoryAchievement = "ACHIEVEMENT";
|
||||||
private const string CategoryDailyNote = "DAILYNOTE";
|
|
||||||
private const string UrlActionImport = "/IMPORT";
|
private const string UrlActionImport = "/IMPORT";
|
||||||
private const string UrlActionRefresh = "/REFRESH";
|
|
||||||
|
|
||||||
private readonly IServiceProvider serviceProvider;
|
|
||||||
private readonly ICurrentXamlWindowReference currentWindowReference;
|
private readonly ICurrentXamlWindowReference currentWindowReference;
|
||||||
|
private readonly IServiceProvider serviceProvider;
|
||||||
private readonly ITaskContext taskContext;
|
private readonly ITaskContext taskContext;
|
||||||
|
|
||||||
private readonly SemaphoreSlim activateSemaphore = new(1);
|
private readonly SemaphoreSlim activateSemaphore = new(1);
|
||||||
@@ -50,7 +47,47 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Activate(HutaoActivationArguments args)
|
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/>
|
/// <inheritdoc/>
|
||||||
@@ -62,20 +99,23 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
|||||||
{
|
{
|
||||||
await taskContext.SwitchToBackgroundAsync();
|
await taskContext.SwitchToBackgroundAsync();
|
||||||
|
|
||||||
serviceProvider.GetRequiredService<PrivateNamedPipeServer>().RunAsync().SafeForget();
|
|
||||||
ToastNotificationManagerCompat.OnActivated += NotificationActivate;
|
|
||||||
|
|
||||||
using (await activateSemaphore.EnterAsync().ConfigureAwait(false))
|
using (await activateSemaphore.EnterAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
// TODO: Introduced in 1.10.2, remove in later version
|
// TODO: Introduced in 1.10.2, remove in later version
|
||||||
|
{
|
||||||
serviceProvider.GetRequiredService<IJumpListInterop>().ClearAsync().SafeForget();
|
serviceProvider.GetRequiredService<IJumpListInterop>().ClearAsync().SafeForget();
|
||||||
serviceProvider.GetRequiredService<IScheduleTaskInterop>().UnregisterAllTasks();
|
serviceProvider.GetRequiredService<IScheduleTaskInterop>().UnregisterAllTasks();
|
||||||
|
}
|
||||||
|
|
||||||
if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) < GuideState.Completed)
|
if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) < GuideState.Completed)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serviceProvider.GetRequiredService<PrivateNamedPipeServer>().RunAsync().SafeForget();
|
||||||
|
|
||||||
|
// RegisterHotKey should be called from main thread
|
||||||
|
await taskContext.SwitchToMainThreadAsync();
|
||||||
serviceProvider.GetRequiredService<HotKeyOptions>().RegisterAll();
|
serviceProvider.GetRequiredService<HotKeyOptions>().RegisterAll();
|
||||||
|
|
||||||
if (serviceProvider.GetRequiredService<AppOptions>().IsNotifyIconEnabled)
|
if (serviceProvider.GetRequiredService<AppOptions>().IsNotifyIconEnabled)
|
||||||
@@ -87,7 +127,18 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
|||||||
_ = serviceProvider.GetRequiredService<NotifyIconController>();
|
_ = 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))
|
string category = builder.Host.ToUpperInvariant();
|
||||||
|
string action = builder.Path.ToUpperInvariant();
|
||||||
|
|
||||||
|
// string parameter = builder.Query.ToUpperInvariant();
|
||||||
|
switch (category)
|
||||||
{
|
{
|
||||||
if (action == LaunchGame)
|
case CategoryAchievement:
|
||||||
{
|
{
|
||||||
_ = toastArgs.TryGetValue(Uid, out string? uid);
|
await WaitMainWindowOrCurrentAsync().ConfigureAwait(false);
|
||||||
HandleLaunchGameActionAsync(uid).SafeForget();
|
if (currentWindowReference.Window is not MainWindow)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ValueTask HandleActivationAsync(HutaoActivationArguments args)
|
break;
|
||||||
{
|
|
||||||
await taskContext.SwitchToBackgroundAsync();
|
|
||||||
|
|
||||||
if (activateSemaphore.CurrentCount > 0)
|
|
||||||
{
|
|
||||||
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:
|
default:
|
||||||
{
|
{
|
||||||
await HandleNormalLaunchActionAsync(args.IsRedirectTo).ConfigureAwait(false);
|
await HandleLaunchActivationAsync(isRedirectTo).ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private async ValueTask HandleNormalLaunchActionAsync(bool isRedirectTo)
|
private async ValueTask HandleLaunchActivationAsync(bool isRedirectTo)
|
||||||
{
|
{
|
||||||
if (!isRedirectTo)
|
if (!isRedirectTo)
|
||||||
{
|
{
|
||||||
@@ -213,6 +261,22 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
|||||||
await WaitMainWindowOrCurrentAsync().ConfigureAwait(false);
|
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()
|
private async ValueTask WaitMainWindowOrCurrentAsync()
|
||||||
{
|
{
|
||||||
if (currentWindowReference.Window is { } window)
|
if (currentWindowReference.Window is { } window)
|
||||||
@@ -229,100 +293,5 @@ internal sealed partial class AppActivation : IAppActivation, IAppActivationActi
|
|||||||
|
|
||||||
mainWindow.SwitchTo();
|
mainWindow.SwitchTo();
|
||||||
mainWindow.BringToForeground();
|
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.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.Windows.AppLifecycle;
|
using Microsoft.Windows.AppLifecycle;
|
||||||
|
using Microsoft.Windows.AppNotifications;
|
||||||
using Windows.ApplicationModel.Activation;
|
using Windows.ApplicationModel.Activation;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.LifeCycle;
|
namespace Snap.Hutao.Core.LifeCycle;
|
||||||
@@ -12,12 +13,6 @@ namespace Snap.Hutao.Core.LifeCycle;
|
|||||||
[HighQuality]
|
[HighQuality]
|
||||||
internal static class AppActivationArgumentsExtensions
|
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)
|
public static bool TryGetProtocolActivatedUri(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out Uri? uri)
|
||||||
{
|
{
|
||||||
uri = null;
|
uri = null;
|
||||||
@@ -30,15 +25,10 @@ internal static class AppActivationArgumentsExtensions
|
|||||||
return true;
|
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)
|
public static bool TryGetLaunchActivatedArguments(this AppActivationArguments activatedEventArgs, [NotNullWhen(true)] out string? arguments)
|
||||||
{
|
{
|
||||||
arguments = null;
|
arguments = null;
|
||||||
|
|
||||||
if (activatedEventArgs.Data is not ILaunchActivatedEventArgs launchArgs)
|
if (activatedEventArgs.Data is not ILaunchActivatedEventArgs launchArgs)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -47,4 +37,21 @@ internal static class AppActivationArgumentsExtensions
|
|||||||
arguments = launchArgs.Arguments.Trim();
|
arguments = launchArgs.Arguments.Trim();
|
||||||
return true;
|
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.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.Extensions.Primitives;
|
|
||||||
using Microsoft.Windows.AppLifecycle;
|
using Microsoft.Windows.AppLifecycle;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.LifeCycle;
|
namespace Snap.Hutao.Core.LifeCycle;
|
||||||
@@ -10,14 +9,16 @@ internal sealed class HutaoActivationArguments
|
|||||||
{
|
{
|
||||||
public bool IsRedirectTo { get; set; }
|
public bool IsRedirectTo { get; set; }
|
||||||
|
|
||||||
public bool IsToastActivated { get; set; }
|
|
||||||
|
|
||||||
public HutaoActivationKind Kind { get; set; }
|
public HutaoActivationKind Kind { get; set; }
|
||||||
|
|
||||||
public Uri? ProtocolActivatedUri { get; set; }
|
public Uri? ProtocolActivatedUri { get; set; }
|
||||||
|
|
||||||
public string? LaunchActivatedArguments { 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)
|
public static HutaoActivationArguments FromAppActivationArguments(AppActivationArguments args, bool isRedirected = false)
|
||||||
{
|
{
|
||||||
HutaoActivationArguments result = new()
|
HutaoActivationArguments result = new()
|
||||||
@@ -33,15 +34,6 @@ internal sealed class HutaoActivationArguments
|
|||||||
if (args.TryGetLaunchActivatedArguments(out string? arguments))
|
if (args.TryGetLaunchActivatedArguments(out string? arguments))
|
||||||
{
|
{
|
||||||
result.LaunchActivatedArguments = arguments;
|
result.LaunchActivatedArguments = arguments;
|
||||||
|
|
||||||
foreach (StringSegment segment in new StringTokenizer(arguments, [' ']))
|
|
||||||
{
|
|
||||||
if (segment.AsSpan().SequenceEqual("-ToastActivated"))
|
|
||||||
{
|
|
||||||
result.Kind = HutaoActivationKind.Toast;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -55,6 +47,19 @@ internal sealed class HutaoActivationArguments
|
|||||||
result.ProtocolActivatedUri = uri;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ internal enum HutaoActivationKind
|
|||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
Launch,
|
Launch,
|
||||||
Toast,
|
AppNotification,
|
||||||
Protocol,
|
Protocol,
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.Windows.AppNotifications;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.LifeCycle;
|
namespace Snap.Hutao.Core.LifeCycle;
|
||||||
|
|
||||||
internal interface IAppActivation
|
internal interface IAppActivation
|
||||||
{
|
{
|
||||||
void Activate(HutaoActivationArguments args);
|
void Activate(HutaoActivationArguments args);
|
||||||
|
|
||||||
|
void NotificationInvoked(AppNotificationManager manager, AppNotificationActivatedEventArgs args);
|
||||||
|
|
||||||
void PostInitialization();
|
void PostInitialization();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface IAppActivationActionHandlersAccess
|
|
||||||
{
|
|
||||||
ValueTask HandleLaunchGameActionAsync(string? uid = null);
|
|
||||||
}
|
|
||||||
@@ -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 PrivateNamedPipeMessageDispatcher messageDispatcher;
|
||||||
private readonly RuntimeOptions runtimeOptions;
|
private readonly RuntimeOptions runtimeOptions;
|
||||||
|
private readonly ILogger<PrivateNamedPipeServer> logger;
|
||||||
|
|
||||||
private readonly CancellationTokenSource serverTokenSource = new();
|
private readonly CancellationTokenSource serverTokenSource = new();
|
||||||
private readonly SemaphoreSlim serverSemaphore = new(1);
|
private readonly SemaphoreSlim serverSemaphore = new(1);
|
||||||
@@ -23,6 +24,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
|
|||||||
{
|
{
|
||||||
messageDispatcher = serviceProvider.GetRequiredService<PrivateNamedPipeMessageDispatcher>();
|
messageDispatcher = serviceProvider.GetRequiredService<PrivateNamedPipeMessageDispatcher>();
|
||||||
runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||||
|
logger = serviceProvider.GetRequiredService<ILogger<PrivateNamedPipeServer>>();
|
||||||
|
|
||||||
PipeSecurity? pipeSecurity = default;
|
PipeSecurity? pipeSecurity = default;
|
||||||
|
|
||||||
@@ -64,6 +66,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await serverStream.WaitForConnectionAsync(serverTokenSource.Token).ConfigureAwait(false);
|
await serverStream.WaitForConnectionAsync(serverTokenSource.Token).ConfigureAwait(false);
|
||||||
|
logger.LogInformation("Pipe session created");
|
||||||
RunPacketSession(serverStream, serverTokenSource.Token);
|
RunPacketSession(serverStream, serverTokenSource.Token);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
@@ -78,6 +81,7 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
|
|||||||
while (serverStream.IsConnected && !token.IsCancellationRequested)
|
while (serverStream.IsConnected && !token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
serverStream.ReadPacket(out PipePacketHeader header);
|
serverStream.ReadPacket(out PipePacketHeader header);
|
||||||
|
logger.LogInformation("Pipe packet: [Type:{Type}] [Command:{Command}]", header.Type, header.Command);
|
||||||
switch ((header.Type, header.Command))
|
switch ((header.Type, header.Command))
|
||||||
{
|
{
|
||||||
case (PipePacketType.Request, PipePacketCommand.RequestElevationStatus):
|
case (PipePacketType.Request, PipePacketCommand.RequestElevationStatus):
|
||||||
@@ -87,6 +91,11 @@ internal sealed partial class PrivateNamedPipeServer : IDisposable
|
|||||||
break;
|
break;
|
||||||
case (PipePacketType.Request, PipePacketCommand.RedirectActivation):
|
case (PipePacketType.Request, PipePacketCommand.RedirectActivation):
|
||||||
HutaoActivationArguments? hutaoArgs = serverStream.ReadJsonContent<HutaoActivationArguments>(in header);
|
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);
|
messageDispatcher.RedirectActivation(hutaoArgs);
|
||||||
break;
|
break;
|
||||||
case (PipePacketType.SessionTermination, _):
|
case (PipePacketType.SessionTermination, _):
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
using Snap.Hutao.Core.ExceptionService;
|
using Snap.Hutao.Core.ExceptionService;
|
||||||
using Snap.Hutao.Win32.Foundation;
|
using Snap.Hutao.Win32.Foundation;
|
||||||
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
|
||||||
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Windows.Storage;
|
|
||||||
using static Snap.Hutao.Win32.ConstValues;
|
using static Snap.Hutao.Win32.ConstValues;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.Windowing.NotifyIcon;
|
namespace Snap.Hutao.Core.Windowing.NotifyIcon;
|
||||||
@@ -26,9 +26,10 @@ internal sealed class NotifyIconController : IDisposable
|
|||||||
{
|
{
|
||||||
lazyMenu = new(() => new(serviceProvider));
|
lazyMenu = new(() => new(serviceProvider));
|
||||||
|
|
||||||
StorageFile iconFile = StorageFile.GetFileFromApplicationUriAsync("ms-appx:///Assets/Logo.ico".ToUri()).AsTask().GetAwaiter().GetResult();
|
RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
|
||||||
icon = new(iconFile.Path);
|
string iconPath = Path.Combine(runtimeOptions.InstalledLocation, "Assets/Logo.ico");
|
||||||
id = Unsafe.As<byte, Guid>(ref MemoryMarshal.GetArrayDataReference(MD5.HashData(Encoding.UTF8.GetBytes(iconFile.Path))));
|
icon = new(iconPath);
|
||||||
|
id = Unsafe.As<byte, Guid>(ref MemoryMarshal.GetArrayDataReference(MD5.HashData(Encoding.UTF8.GetBytes(iconPath))));
|
||||||
|
|
||||||
xamlHostWindow = new(serviceProvider);
|
xamlHostWindow = new(serviceProvider);
|
||||||
xamlHostWindow.MoveAndResize(default);
|
xamlHostWindow.MoveAndResize(default);
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ internal static class WindowExtension
|
|||||||
return WindowControllers.TryGetValue(window, out _);
|
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)
|
public static DesktopWindowXamlSource? GetDesktopWindowXamlSource(this Window window)
|
||||||
{
|
{
|
||||||
if (window.SystemBackdrop is SystemBackdropDesktopWindowXamlSourceAccess access)
|
if (window.SystemBackdrop is SystemBackdropDesktopWindowXamlSourceAccess access)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using CommunityToolkit.WinUI.Notifications;
|
|
||||||
using Microsoft.UI;
|
using Microsoft.UI;
|
||||||
using Microsoft.UI.Composition.SystemBackdrops;
|
using Microsoft.UI.Composition.SystemBackdrops;
|
||||||
using Microsoft.UI.Content;
|
using Microsoft.UI.Content;
|
||||||
@@ -9,11 +8,11 @@ using Microsoft.UI.Input;
|
|||||||
using Microsoft.UI.Windowing;
|
using Microsoft.UI.Windowing;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Media;
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
using Microsoft.Windows.AppNotifications.Builder;
|
||||||
using Snap.Hutao.Core.LifeCycle;
|
using Snap.Hutao.Core.LifeCycle;
|
||||||
using Snap.Hutao.Core.Setting;
|
using Snap.Hutao.Core.Setting;
|
||||||
using Snap.Hutao.Core.Windowing.Abstraction;
|
using Snap.Hutao.Core.Windowing.Abstraction;
|
||||||
using Snap.Hutao.Core.Windowing.NotifyIcon;
|
using Snap.Hutao.Core.Windowing.NotifyIcon;
|
||||||
using Snap.Hutao.Factory.ContentDialog;
|
|
||||||
using Snap.Hutao.Service;
|
using Snap.Hutao.Service;
|
||||||
using Snap.Hutao.Win32;
|
using Snap.Hutao.Win32;
|
||||||
using Snap.Hutao.Win32.Foundation;
|
using Snap.Hutao.Win32.Foundation;
|
||||||
@@ -100,17 +99,13 @@ internal sealed class XamlWindowController
|
|||||||
|
|
||||||
private void OnWindowClosed(object sender, WindowEventArgs args)
|
private void OnWindowClosed(object sender, WindowEventArgs args)
|
||||||
{
|
{
|
||||||
IContentDialogFactory contentDialogFactory = serviceProvider.GetRequiredService<IContentDialogFactory>();
|
serviceProvider.GetRequiredService<AppOptions>().PropertyChanged -= OnOptionsPropertyChanged;
|
||||||
contentDialogFactory.CloseCurrentDialog();
|
|
||||||
|
|
||||||
if (XamlLifetime.ApplicationLaunchedWithNotifyIcon && !XamlLifetime.ApplicationExiting)
|
if (XamlLifetime.ApplicationLaunchedWithNotifyIcon && !XamlLifetime.ApplicationExiting)
|
||||||
{
|
{
|
||||||
//args.Handled = true;
|
|
||||||
//window.Hide();
|
|
||||||
|
|
||||||
if (!IsNotifyIconVisible())
|
if (!IsNotifyIconVisible())
|
||||||
{
|
{
|
||||||
new ToastContentBuilder()
|
new AppNotificationBuilder()
|
||||||
.AddText(SH.CoreWindowingNotifyIconPromotedHint)
|
.AddText(SH.CoreWindowingNotifyIconPromotedHint)
|
||||||
.Show();
|
.Show();
|
||||||
}
|
}
|
||||||
@@ -123,8 +118,7 @@ internal sealed class XamlWindowController
|
|||||||
|
|
||||||
GC.Collect(GC.MaxGeneration);
|
GC.Collect(GC.MaxGeneration);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (window is IXamlWindowRectPersisted rectPersisted)
|
if (window is IXamlWindowRectPersisted rectPersisted)
|
||||||
{
|
{
|
||||||
SaveOrSkipWindowSize(rectPersisted);
|
SaveOrSkipWindowSize(rectPersisted);
|
||||||
@@ -132,7 +126,7 @@ internal sealed class XamlWindowController
|
|||||||
|
|
||||||
subclass?.Dispose();
|
subclass?.Dispose();
|
||||||
windowNonRudeHWND?.Dispose();
|
windowNonRudeHWND?.Dispose();
|
||||||
}
|
window.UninitializeController();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsNotifyIconVisible()
|
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 Microsoft.UI.Xaml.Controls;
|
||||||
using Snap.Hutao.Core.LifeCycle;
|
using Snap.Hutao.Core.LifeCycle;
|
||||||
using Snap.Hutao.Service;
|
using Snap.Hutao.Service;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace Snap.Hutao.Factory.ContentDialog;
|
namespace Snap.Hutao.Factory.ContentDialog;
|
||||||
|
|
||||||
@@ -18,12 +19,27 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
|||||||
private readonly ITaskContext taskContext;
|
private readonly ITaskContext taskContext;
|
||||||
private readonly AppOptions appOptions;
|
private readonly AppOptions appOptions;
|
||||||
|
|
||||||
private Microsoft.UI.Xaml.Controls.ContentDialog? currentDialog;
|
private readonly ConcurrentQueue<Func<Task>> dialogQueue = [];
|
||||||
|
private bool isDialogShowing;
|
||||||
|
|
||||||
|
public bool IsDialogShowing
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (currentWindowReference.Window is not { } window)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isDialogShowing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async ValueTask<ContentDialogResult> CreateForConfirmAsync(string title, string content)
|
public async ValueTask<ContentDialogResult> CreateForConfirmAsync(string title, string content)
|
||||||
{
|
{
|
||||||
await taskContext.SwitchToMainThreadAsync();
|
await taskContext.SwitchToMainThreadAsync();
|
||||||
|
|
||||||
Microsoft.UI.Xaml.Controls.ContentDialog dialog = new()
|
Microsoft.UI.Xaml.Controls.ContentDialog dialog = new()
|
||||||
{
|
{
|
||||||
XamlRoot = currentWindowReference.GetXamlRoot(),
|
XamlRoot = currentWindowReference.GetXamlRoot(),
|
||||||
@@ -34,8 +50,6 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
|||||||
RequestedTheme = appOptions.ElementTheme,
|
RequestedTheme = appOptions.ElementTheme,
|
||||||
};
|
};
|
||||||
|
|
||||||
dialog.Closed += OnContentDialogClosed;
|
|
||||||
currentDialog = dialog;
|
|
||||||
return await dialog.ShowAsync();
|
return await dialog.ShowAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,6 +57,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
|||||||
public async ValueTask<ContentDialogResult> CreateForConfirmCancelAsync(string title, string content, ContentDialogButton defaultButton = ContentDialogButton.Close)
|
public async ValueTask<ContentDialogResult> CreateForConfirmCancelAsync(string title, string content, ContentDialogButton defaultButton = ContentDialogButton.Close)
|
||||||
{
|
{
|
||||||
await taskContext.SwitchToMainThreadAsync();
|
await taskContext.SwitchToMainThreadAsync();
|
||||||
|
|
||||||
Microsoft.UI.Xaml.Controls.ContentDialog dialog = new()
|
Microsoft.UI.Xaml.Controls.ContentDialog dialog = new()
|
||||||
{
|
{
|
||||||
XamlRoot = currentWindowReference.GetXamlRoot(),
|
XamlRoot = currentWindowReference.GetXamlRoot(),
|
||||||
@@ -54,8 +69,6 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
|||||||
RequestedTheme = appOptions.ElementTheme,
|
RequestedTheme = appOptions.ElementTheme,
|
||||||
};
|
};
|
||||||
|
|
||||||
dialog.Closed += OnContentDialogClosed;
|
|
||||||
currentDialog = dialog;
|
|
||||||
return await dialog.ShowAsync();
|
return await dialog.ShowAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +76,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
|||||||
public async ValueTask<Microsoft.UI.Xaml.Controls.ContentDialog> CreateForIndeterminateProgressAsync(string title)
|
public async ValueTask<Microsoft.UI.Xaml.Controls.ContentDialog> CreateForIndeterminateProgressAsync(string title)
|
||||||
{
|
{
|
||||||
await taskContext.SwitchToMainThreadAsync();
|
await taskContext.SwitchToMainThreadAsync();
|
||||||
|
|
||||||
Microsoft.UI.Xaml.Controls.ContentDialog dialog = new()
|
Microsoft.UI.Xaml.Controls.ContentDialog dialog = new()
|
||||||
{
|
{
|
||||||
XamlRoot = currentWindowReference.GetXamlRoot(),
|
XamlRoot = currentWindowReference.GetXamlRoot(),
|
||||||
@@ -71,8 +85,6 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
|||||||
RequestedTheme = appOptions.ElementTheme,
|
RequestedTheme = appOptions.ElementTheme,
|
||||||
};
|
};
|
||||||
|
|
||||||
dialog.Closed += OnContentDialogClosed;
|
|
||||||
currentDialog = dialog;
|
|
||||||
return dialog;
|
return dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,12 +92,11 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
|||||||
where TContentDialog : Microsoft.UI.Xaml.Controls.ContentDialog
|
where TContentDialog : Microsoft.UI.Xaml.Controls.ContentDialog
|
||||||
{
|
{
|
||||||
await taskContext.SwitchToMainThreadAsync();
|
await taskContext.SwitchToMainThreadAsync();
|
||||||
|
|
||||||
TContentDialog contentDialog = serviceProvider.CreateInstance<TContentDialog>(parameters);
|
TContentDialog contentDialog = serviceProvider.CreateInstance<TContentDialog>(parameters);
|
||||||
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
|
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
|
||||||
contentDialog.RequestedTheme = appOptions.ElementTheme;
|
contentDialog.RequestedTheme = appOptions.ElementTheme;
|
||||||
|
|
||||||
contentDialog.Closed += OnContentDialogClosed;
|
|
||||||
currentDialog = contentDialog;
|
|
||||||
return contentDialog;
|
return contentDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,24 +107,50 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
|||||||
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
|
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
|
||||||
contentDialog.RequestedTheme = appOptions.ElementTheme;
|
contentDialog.RequestedTheme = appOptions.ElementTheme;
|
||||||
|
|
||||||
contentDialog.Closed += OnContentDialogClosed;
|
|
||||||
currentDialog = contentDialog;
|
|
||||||
return contentDialog;
|
return contentDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CloseCurrentDialog()
|
[SuppressMessage("", "SH003")]
|
||||||
|
public Task<ContentDialogResult> EnqueueAndShowAsync(Microsoft.UI.Xaml.Controls.ContentDialog contentDialog)
|
||||||
{
|
{
|
||||||
currentDialog?.Hide();
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask CloseCurrentDialogAsync()
|
return dialogShowCompletionSource.Task;
|
||||||
{
|
|
||||||
await taskContext.SwitchToMainThreadAsync();
|
|
||||||
currentDialog?.Hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnContentDialogClosed(Microsoft.UI.Xaml.Controls.ContentDialog sender, ContentDialogClosedEventArgs args)
|
Task ShowNextDialog()
|
||||||
{
|
{
|
||||||
currentDialog = null;
|
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]
|
[HighQuality]
|
||||||
internal interface IContentDialogFactory
|
internal interface IContentDialogFactory
|
||||||
{
|
{
|
||||||
|
bool IsDialogShowing { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 异步确认
|
/// 异步确认
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -41,7 +43,5 @@ internal interface IContentDialogFactory
|
|||||||
ValueTask<TContentDialog> CreateInstanceAsync<TContentDialog>(params object[] parameters)
|
ValueTask<TContentDialog> CreateInstanceAsync<TContentDialog>(params object[] parameters)
|
||||||
where TContentDialog : Microsoft.UI.Xaml.Controls.ContentDialog;
|
where TContentDialog : Microsoft.UI.Xaml.Controls.ContentDialog;
|
||||||
|
|
||||||
void CloseCurrentDialog();
|
Task<ContentDialogResult> EnqueueAndShowAsync(Microsoft.UI.Xaml.Controls.ContentDialog contentDialog);
|
||||||
|
|
||||||
ValueTask CloseCurrentDialogAsync();
|
|
||||||
}
|
}
|
||||||
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)
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#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 =>
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||||
{
|
{
|
||||||
@@ -466,6 +466,33 @@ namespace Snap.Hutao.Migrations
|
|||||||
b.ToTable("spiral_abysses");
|
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 =>
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("InnerId")
|
b.Property<Guid>("InnerId")
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ internal sealed class AppDbContext : DbContext
|
|||||||
|
|
||||||
public DbSet<SpiralAbyssEntry> SpiralAbysses { get; set; } = default!;
|
public DbSet<SpiralAbyssEntry> SpiralAbysses { get; set; } = default!;
|
||||||
|
|
||||||
|
public DbSet<UidProfilePicture> UidProfilePictures { get; set; } = default!;
|
||||||
|
|
||||||
public static AppDbContext Create(IServiceProvider serviceProvider, string sqlConnectionString)
|
public static AppDbContext Create(IServiceProvider serviceProvider, string sqlConnectionString)
|
||||||
{
|
{
|
||||||
DbContextOptions<AppDbContext> options = new DbContextOptionsBuilder<AppDbContext>()
|
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.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Model.Intrinsic;
|
||||||
using Snap.Hutao.Model.Primitive;
|
using Snap.Hutao.Model.Primitive;
|
||||||
|
|
||||||
namespace Snap.Hutao.Model.Metadata.Avatar;
|
namespace Snap.Hutao.Model.Metadata.Avatar;
|
||||||
@@ -12,4 +13,17 @@ internal sealed class ProfilePicture
|
|||||||
public string Icon { get; set; } = default!;
|
public string Icon { get; set; } = default!;
|
||||||
|
|
||||||
public string Name { 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
</desktop:Extension>
|
</desktop:Extension>
|
||||||
<com:Extension Category="windows.comServer">
|
<com:Extension Category="windows.comServer">
|
||||||
<com: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:Class Id="5760EC4D-F7E8-4666-A965-9886D7DFFE7D" DisplayName="Snap Hutao Toast Activator"/>
|
||||||
</com:ExeServer>
|
</com:ExeServer>
|
||||||
</com:ComServer>
|
</com:ComServer>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
</desktop:Extension>
|
</desktop:Extension>
|
||||||
<com:Extension Category="windows.comServer">
|
<com:Extension Category="windows.comServer">
|
||||||
<com: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:Class Id="F32B561D-752E-472B-A22C-85824D421E1A" DisplayName="Snap Hutao Dev Toast Activator"/>
|
||||||
</com:ExeServer>
|
</com:ExeServer>
|
||||||
</com:ComServer>
|
</com:ComServer>
|
||||||
|
|||||||
@@ -1373,13 +1373,13 @@
|
|||||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||||
<value>Delete user data permanently?</value>
|
<value>Delete user data permanently?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||||
<value>Log in Now</value>
|
<value>Log in Now</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
<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>
|
<value>No Hutao Passport logged in currently, uploading Abyss Records will not grant you Hutao Cloud privilege extension.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||||
<value>Continue to Upload</value>
|
<value>Continue to Upload</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||||
|
|||||||
@@ -1373,13 +1373,13 @@
|
|||||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||||
<value>是否永久删除用户数据</value>
|
<value>是否永久删除用户数据</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||||
<value>前往登录</value>
|
<value>前往登录</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||||
<value>继续上传</value>
|
<value>继续上传</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||||
|
|||||||
@@ -1373,13 +1373,13 @@
|
|||||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||||
<value>Hapus data pengguna secara permanen?</value>
|
<value>Hapus data pengguna secara permanen?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||||
<value>前往登录</value>
|
<value>前往登录</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||||
<value>继续上传</value>
|
<value>继续上传</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||||
|
|||||||
@@ -1373,13 +1373,13 @@
|
|||||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||||
<value>ユーザーデータを完全に削除しますか</value>
|
<value>ユーザーデータを完全に削除しますか</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||||
<value>前往登录</value>
|
<value>前往登录</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||||
<value>继续上传</value>
|
<value>继续上传</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||||
|
|||||||
@@ -1373,13 +1373,13 @@
|
|||||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||||
<value>사용자 데이터 영구 삭제</value>
|
<value>사용자 데이터 영구 삭제</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||||
<value>前往登录</value>
|
<value>前往登录</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||||
<value>继续上传</value>
|
<value>继续上传</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||||
|
|||||||
@@ -1373,13 +1373,13 @@
|
|||||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||||
<value>Excluir permanentemente os dados do usuário?</value>
|
<value>Excluir permanentemente os dados do usuário?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||||
<value>前往登录</value>
|
<value>前往登录</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||||
<value>继续上传</value>
|
<value>继续上传</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||||
|
|||||||
@@ -1373,13 +1373,13 @@
|
|||||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||||
<value>是否永久删除用户数据</value>
|
<value>是否永久删除用户数据</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||||
<value>前往登录</value>
|
<value>前往登录</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||||
<value>继续上传</value>
|
<value>继续上传</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||||
|
|||||||
@@ -1373,13 +1373,13 @@
|
|||||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||||
<value>是否永久删除用户数据</value>
|
<value>是否永久删除用户数据</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||||
<value>前往登录</value>
|
<value>前往登录</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||||
<value>Продолжить загрузку</value>
|
<value>Продолжить загрузку</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||||
|
|||||||
@@ -1373,13 +1373,13 @@
|
|||||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||||
<value>是否永久删除用户数据</value>
|
<value>是否永久删除用户数据</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||||
<value>前往登录</value>
|
<value>前往登录</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||||
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
<value>当前未登录胡桃账号,上传深渊数据无法获赠胡桃云时长</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||||
<value>继续上传</value>
|
<value>继续上传</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||||
|
|||||||
@@ -1373,13 +1373,13 @@
|
|||||||
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
|
||||||
<value>是否永久刪除用戶數據</value>
|
<value>是否永久刪除用戶數據</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
||||||
<value>前往登入畫面</value>
|
<value>前往登入畫面</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginHint" xml:space="preserve">
|
||||||
<value>當前未登入胡桃賬號,上傳深淵數據無法獲贈胡桃雲時長</value>
|
<value>當前未登入胡桃賬號,上傳深淵數據無法獲贈胡桃雲時長</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText" xml:space="preserve">
|
||||||
<value>繼續上傳</value>
|
<value>繼續上傳</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
<data name="ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle" xml:space="preserve">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using CommunityToolkit.WinUI.Notifications;
|
using Microsoft.Windows.AppNotifications;
|
||||||
using Snap.Hutao.Core;
|
using Snap.Hutao.Core;
|
||||||
using Snap.Hutao.Core.LifeCycle;
|
using Snap.Hutao.Core.LifeCycle;
|
||||||
using Snap.Hutao.Model.Entity;
|
using Snap.Hutao.Model.Entity;
|
||||||
@@ -20,7 +20,6 @@ namespace Snap.Hutao.Service.DailyNote;
|
|||||||
[Injection(InjectAs.Singleton)]
|
[Injection(InjectAs.Singleton)]
|
||||||
internal sealed partial class DailyNoteNotificationOperation
|
internal sealed partial class DailyNoteNotificationOperation
|
||||||
{
|
{
|
||||||
private const string ToastHeaderIdArgument = "DAILYNOTE";
|
|
||||||
private const string ToastAttributionUnknown = "Unknown";
|
private const string ToastAttributionUnknown = "Unknown";
|
||||||
|
|
||||||
private readonly ITaskContext taskContext;
|
private readonly ITaskContext taskContext;
|
||||||
@@ -52,57 +51,57 @@ internal sealed partial class DailyNoteNotificationOperation
|
|||||||
? entry.UserGameRole.ToString()
|
? entry.UserGameRole.ToString()
|
||||||
: await GetUserUidAsync(entry).ConfigureAwait(false);
|
: await GetUserUidAsync(entry).ConfigureAwait(false);
|
||||||
|
|
||||||
ToastContentBuilder builder = new ToastContentBuilder()
|
string reminder = options.IsReminderNotification ? @"scenario=""reminder""" : string.Empty;
|
||||||
.AddHeader(ToastHeaderIdArgument, SH.ServiceDailyNoteNotifierTitle, ToastHeaderIdArgument)
|
string content;
|
||||||
.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notifyInfos.Count > 2)
|
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)
|
content = $"""
|
||||||
if (UniversalApiContract.IsPresent(WindowsVersion.Windows10AnniversaryUpdate))
|
<text>{SH.ServiceDailyNoteNotifierMultiValueReached}</text>
|
||||||
{
|
<group>
|
||||||
AdaptiveGroup group = new();
|
{adaptiveSubgroups}
|
||||||
foreach (DailyNoteNotifyInfo info in notifyInfos)
|
</group>
|
||||||
{
|
""";
|
||||||
AdaptiveSubgroup subgroup = new()
|
|
||||||
{
|
|
||||||
HintWeight = 1,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
// new AdaptiveImage() { Source = info.AdaptiveIcon, HintRemoveMargin = true, },
|
|
||||||
new AdaptiveText() { Text = info.AdaptiveHint, HintAlign = AdaptiveTextAlign.Center, },
|
|
||||||
new AdaptiveText() { Text = info.Title, HintAlign = AdaptiveTextAlign.Center, HintStyle = AdaptiveTextStyle.CaptionSubtle, },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
group.Children.Add(subgroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.AddVisualChild(group);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (DailyNoteNotifyInfo info in notifyInfos)
|
content = string.Join(string.Empty, notifyInfos.Select(info => $"""
|
||||||
{
|
<text>{info.Hint}</text>
|
||||||
builder.AddText(info.Hint);
|
"""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
await taskContext.SwitchToMainThreadAsync();
|
||||||
builder.Show(toast => toast.SuppressPopup = ShouldSuppressPopup(options));
|
AppNotificationManager.Default.Show(notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ShouldSuppressPopup(DailyNoteOptions options)
|
private bool ShouldSuppressPopup(DailyNoteOptions options)
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ internal sealed partial class DailyNoteRefreshJobScheduler : IJobScheduler
|
|||||||
|
|
||||||
ITrigger dailyNoteTrigger = TriggerBuilder.Create()
|
ITrigger dailyNoteTrigger = TriggerBuilder.Create()
|
||||||
.WithIdentity(JobIdentity.DailyNoteRefreshTriggerName, JobIdentity.DailyNoteGroupName)
|
.WithIdentity(JobIdentity.DailyNoteRefreshTriggerName, JobIdentity.DailyNoteGroupName)
|
||||||
.StartNow()
|
|
||||||
.WithSimpleSchedule(builder => builder.WithIntervalInSeconds(interval).RepeatForever())
|
.WithSimpleSchedule(builder => builder.WithIntervalInSeconds(interval).RepeatForever())
|
||||||
|
.StartAt(DateTimeOffset.Now.AddSeconds(interval))
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
await scheduler.ScheduleJob(dailyNoteJob, dailyNoteTrigger).ConfigureAwait(false);
|
await scheduler.ScheduleJob(dailyNoteJob, dailyNoteTrigger).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -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
|
||||||
|
{
|
||||||
|
public List<ProfilePicture> ProfilePictures { get; set; }
|
||||||
|
}
|
||||||
@@ -38,6 +38,11 @@ internal static class MetadataServiceContextExtension
|
|||||||
listMaterialSource.Materials = await metadataService.GetMaterialListAsync(token).ConfigureAwait(false);
|
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)
|
if (context is IMetadataListReliquaryMainAffixLevelSource listReliquaryMainAffixLevelSource)
|
||||||
{
|
{
|
||||||
listReliquaryMainAffixLevelSource.ReliquaryMainAffixLevels = await metadataService.GetReliquaryMainAffixLevelListAsync(token).ConfigureAwait(false);
|
listReliquaryMainAffixLevelSource.ReliquaryMainAffixLevels = await metadataService.GetReliquaryMainAffixLevelListAsync(token).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ internal static class MetadataFileNames
|
|||||||
public const string FileNameMaterial = "Material";
|
public const string FileNameMaterial = "Material";
|
||||||
public const string FileNameMonster = "Monster";
|
public const string FileNameMonster = "Monster";
|
||||||
public const string FileNameMonsterCurve = "MonsterCurve";
|
public const string FileNameMonsterCurve = "MonsterCurve";
|
||||||
|
public const string FileNameProfilePicture = "ProfilePicture";
|
||||||
public const string FileNameReliquary = "Reliquary";
|
public const string FileNameReliquary = "Reliquary";
|
||||||
public const string FileNameReliquaryAffixWeight = "ReliquaryAffixWeight";
|
public const string FileNameReliquaryAffixWeight = "ReliquaryAffixWeight";
|
||||||
public const string FileNameReliquaryMainAffix = "ReliquaryMainAffix";
|
public const string FileNameReliquaryMainAffix = "ReliquaryMainAffix";
|
||||||
|
|||||||
@@ -70,6 +70,11 @@ internal static class MetadataServiceListExtension
|
|||||||
return metadataService.FromCacheOrFileAsync<List<GrowCurve>>(FileNameMonsterCurve, token);
|
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)
|
public static ValueTask<List<Reliquary>> GetReliquaryListAsync(this IMetadataService metadataService, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return metadataService.FromCacheOrFileAsync<List<Reliquary>>(FileNameReliquary, token);
|
return metadataService.FromCacheOrFileAsync<List<Reliquary>>(FileNameReliquary, token);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using CommunityToolkit.WinUI.Notifications;
|
using Microsoft.Windows.AppNotifications;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Notification;
|
namespace Snap.Hutao.Service.Notification;
|
||||||
|
|
||||||
@@ -16,7 +16,8 @@ internal sealed class ToastNotificationLifeTime : IToastNotificationLifeTime
|
|||||||
// 用于在程序退出时尝试清除所有的系统通知
|
// 用于在程序退出时尝试清除所有的系统通知
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ToastNotificationManagerCompat.History.Clear();
|
AppNotificationManager.Default.RemoveAllAsync().AsTask().GetAwaiter().GetResult();
|
||||||
|
AppNotificationManager.Default.Unregister();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Model.Entity;
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.User;
|
namespace Snap.Hutao.Service.User;
|
||||||
|
|
||||||
internal interface IUserInitializationService
|
internal interface IUserInitializationService
|
||||||
|
|||||||
@@ -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>
|
/// <param name="user">待移除的用户</param>
|
||||||
/// <returns>任务</returns>
|
/// <returns>任务</returns>
|
||||||
ValueTask RemoveUserAsync(BindingUser user);
|
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
|
internal sealed partial class UserInitializationService : IUserInitializationService
|
||||||
{
|
{
|
||||||
private readonly IUserFingerprintService userFingerprintService;
|
private readonly IUserFingerprintService userFingerprintService;
|
||||||
|
private readonly IProfilePictureService profilePictureService;
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
|
|
||||||
public async ValueTask<ViewModel.User.User> ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default)
|
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 userFingerprintService.TryInitializeAsync(user, token).ConfigureAwait(false);
|
||||||
|
await profilePictureService.TryInitializeAsync(user, token).ConfigureAwait(false);
|
||||||
|
|
||||||
return user.IsInitialized = true;
|
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))]
|
[Injection(InjectAs.Singleton, typeof(IUserService))]
|
||||||
internal sealed partial class UserService : IUserService, IUserServiceUnsafe
|
internal sealed partial class UserService : IUserService, IUserServiceUnsafe
|
||||||
{
|
{
|
||||||
|
private readonly IProfilePictureService profilePictureService;
|
||||||
private readonly IUserCollectionService userCollectionService;
|
private readonly IUserCollectionService userCollectionService;
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
private readonly IUserDbService userDbService;
|
private readonly IUserDbService userDbService;
|
||||||
@@ -121,4 +122,9 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole)
|
||||||
|
{
|
||||||
|
await profilePictureService.RefreshUserGameRoleAsync(userGameRole).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -312,10 +312,9 @@
|
|||||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.0.240109" />
|
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.0.240109" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.TokenizingTextBox" 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.Media" Version="8.0.240109" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
|
||||||
<PackageReference Include="Google.OrTools" Version="9.10.4067" />
|
<PackageReference Include="Google.OrTools" Version="9.10.4067" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.5" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.5">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.6">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
@@ -330,7 +329,7 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.8.8" />
|
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.8.8" />
|
||||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1" />
|
<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="QRCoder" Version="1.5.1" />
|
||||||
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.9.0" />
|
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.9.0" />
|
||||||
<PackageReference Include="Snap.Discord.GameSDK" Version="1.6.0" />
|
<PackageReference Include="Snap.Discord.GameSDK" Version="1.6.0" />
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
MinWidth="42"
|
MinWidth="42"
|
||||||
Command="{x:Bind GoBackCommand}"
|
Command="{x:Bind GoBackCommand}"
|
||||||
FontSize="12"
|
FontSize="12"
|
||||||
IsEnabled="{x:Bind WebView.CanGoBack, Mode=OneWay}"
|
IsEnabled="{x:Bind CanGoBack, Mode=OneWay}"
|
||||||
Style="{StaticResource NavigationBackButtonSmallStyle}"/>
|
Style="{StaticResource NavigationBackButtonSmallStyle}"/>
|
||||||
<Button
|
<Button
|
||||||
MinWidth="42"
|
MinWidth="42"
|
||||||
|
|||||||
@@ -17,12 +17,14 @@ namespace Snap.Hutao.View.Control;
|
|||||||
|
|
||||||
[DependencyProperty("SourceProvider", typeof(IWebViewerSource))]
|
[DependencyProperty("SourceProvider", typeof(IWebViewerSource))]
|
||||||
[DependencyProperty("DocumentTitle", typeof(string))]
|
[DependencyProperty("DocumentTitle", typeof(string))]
|
||||||
|
[DependencyProperty("CanGoBack", typeof(bool))]
|
||||||
internal partial class WebViewer : UserControl, IRecipient<UserChangedMessage>
|
internal partial class WebViewer : UserControl, IRecipient<UserChangedMessage>
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
private readonly IInfoBarService infoBarService;
|
private readonly IInfoBarService infoBarService;
|
||||||
private readonly RoutedEventHandler loadEventHandler;
|
private readonly RoutedEventHandler loadEventHandler;
|
||||||
private readonly TypedEventHandler<CoreWebView2, object> documentTitleChangedEventHander;
|
private readonly TypedEventHandler<CoreWebView2, object> documentTitleChangedEventHandler;
|
||||||
|
private readonly TypedEventHandler<CoreWebView2, object> historyChangedEventHandler;
|
||||||
|
|
||||||
private MiHoYoJSBridge? jsBridge;
|
private MiHoYoJSBridge? jsBridge;
|
||||||
private bool isInitializingOrInitialized;
|
private bool isInitializingOrInitialized;
|
||||||
@@ -36,7 +38,8 @@ internal partial class WebViewer : UserControl, IRecipient<UserChangedMessage>
|
|||||||
serviceProvider.GetRequiredService<IMessenger>().Register(this);
|
serviceProvider.GetRequiredService<IMessenger>().Register(this);
|
||||||
|
|
||||||
loadEventHandler = OnLoaded;
|
loadEventHandler = OnLoaded;
|
||||||
documentTitleChangedEventHander = OnDocumentTitleChanged;
|
documentTitleChangedEventHandler = OnDocumentTitleChanged;
|
||||||
|
historyChangedEventHandler = OnHistoryChanged;
|
||||||
|
|
||||||
Loaded += loadEventHandler;
|
Loaded += loadEventHandler;
|
||||||
}
|
}
|
||||||
@@ -57,16 +60,16 @@ internal partial class WebViewer : UserControl, IRecipient<UserChangedMessage>
|
|||||||
[Command("GoBackCommand")]
|
[Command("GoBackCommand")]
|
||||||
private void GoBack()
|
private void GoBack()
|
||||||
{
|
{
|
||||||
if (WebView.CanGoBack)
|
if (WebView.CoreWebView2.CanGoBack)
|
||||||
{
|
{
|
||||||
WebView.GoBack();
|
WebView.CoreWebView2.GoBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command("RefreshCommand")]
|
[Command("RefreshCommand")]
|
||||||
private void Refresh()
|
private void Refresh()
|
||||||
{
|
{
|
||||||
WebView.Reload();
|
WebView.CoreWebView2.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||||
@@ -82,7 +85,8 @@ internal partial class WebViewer : UserControl, IRecipient<UserChangedMessage>
|
|||||||
|
|
||||||
await WebView.EnsureCoreWebView2Async();
|
await WebView.EnsureCoreWebView2Async();
|
||||||
WebView.CoreWebView2.DisableDevToolsForReleaseBuild();
|
WebView.CoreWebView2.DisableDevToolsForReleaseBuild();
|
||||||
WebView.CoreWebView2.DocumentTitleChanged += documentTitleChangedEventHander;
|
WebView.CoreWebView2.DocumentTitleChanged += documentTitleChangedEventHandler;
|
||||||
|
WebView.CoreWebView2.HistoryChanged += historyChangedEventHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefreshWebview2Content();
|
RefreshWebview2Content();
|
||||||
@@ -93,6 +97,11 @@ internal partial class WebViewer : UserControl, IRecipient<UserChangedMessage>
|
|||||||
DocumentTitle = sender.DocumentTitle;
|
DocumentTitle = sender.DocumentTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnHistoryChanged(CoreWebView2 sender, object args)
|
||||||
|
{
|
||||||
|
CanGoBack = sender.CanGoBack;
|
||||||
|
}
|
||||||
|
|
||||||
private async void RefreshWebview2Content()
|
private async void RefreshWebview2Content()
|
||||||
{
|
{
|
||||||
User? user = serviceProvider.GetRequiredService<IUserService>().Current;
|
User? user = serviceProvider.GetRequiredService<IUserService>().Current;
|
||||||
@@ -147,6 +156,7 @@ internal partial class WebViewer : UserControl, IRecipient<UserChangedMessage>
|
|||||||
jsBridge = SourceProvider.CreateJSBridge(serviceProvider, coreWebView2, userAndUid);
|
jsBridge = SourceProvider.CreateJSBridge(serviceProvider, coreWebView2, userAndUid);
|
||||||
|
|
||||||
await navigator.NavigateAsync(source).ConfigureAwait(true);
|
await navigator.NavigateAsync(source).ConfigureAwait(true);
|
||||||
|
await coreWebView2.Profile.ClearBrowsingDataAsync(CoreWebView2BrowsingDataKinds.BrowsingHistory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,9 +6,10 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||||
Title="{shcm:ResourceString Name=ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle}"
|
Title="{shcm:ResourceString Name=ViewDialogSpiralAbyssUploadRecordHomaNotLoginTitle}"
|
||||||
CloseButtonText="{shcm:ResourceString Name=ViewDialogSpiralAbyssUploadRecordHomaNotLoginCloseButtonText}"
|
CloseButtonText="{shcm:ResourceString Name=ContentDialogCancelCloseButtonText}"
|
||||||
DefaultButton="Close"
|
DefaultButton="Primary"
|
||||||
PrimaryButtonText="{shcm:ResourceString Name=ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText}"
|
PrimaryButtonText="{shcm:ResourceString Name=ViewDialogSpiralAbyssUploadRecordHomaNotLoginPrimaryButtonText}"
|
||||||
|
SecondaryButtonText="{shcm:ResourceString Name=ViewDialogSpiralAbyssUploadRecordHomaNotLoginSecondaryButtonText}"
|
||||||
Style="{StaticResource DefaultContentDialogStyle}"
|
Style="{StaticResource DefaultContentDialogStyle}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
|||||||
@@ -7,18 +7,8 @@ namespace Snap.Hutao.View.Dialog;
|
|||||||
|
|
||||||
internal sealed partial class SpiralAbyssUploadRecordHomaNotLoginDialog : ContentDialog
|
internal sealed partial class SpiralAbyssUploadRecordHomaNotLoginDialog : ContentDialog
|
||||||
{
|
{
|
||||||
private readonly ITaskContext taskContext;
|
|
||||||
|
|
||||||
public SpiralAbyssUploadRecordHomaNotLoginDialog(IServiceProvider serviceProvider)
|
public SpiralAbyssUploadRecordHomaNotLoginDialog(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
taskContext = serviceProvider.GetRequiredService<ITaskContext>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask<bool> ConfirmAsync()
|
|
||||||
{
|
|
||||||
await taskContext.SwitchToMainThreadAsync();
|
|
||||||
return await ShowAsync() is ContentDialogResult.Primary;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,7 +301,7 @@
|
|||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<ItemsControl
|
<ItemsControl
|
||||||
Margin="0,0,-4,0"
|
Margin="0,0,-4,0"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Left"
|
||||||
ItemContainerTransitions="{StaticResource ListViewLikeThemeTransitions}"
|
ItemContainerTransitions="{StaticResource ListViewLikeThemeTransitions}"
|
||||||
ItemTemplate="{StaticResource DownloadSummaryTemplate}"
|
ItemTemplate="{StaticResource DownloadSummaryTemplate}"
|
||||||
ItemsPanel="{StaticResource WrapPanelSpacing0Template}"
|
ItemsPanel="{StaticResource WrapPanelSpacing0Template}"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||||
|
xmlns:shcs="using:Snap.Hutao.Control.Selector"
|
||||||
xmlns:shsn="using:Snap.Hutao.Service.Notification"
|
xmlns:shsn="using:Snap.Hutao.Service.Notification"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
@@ -63,25 +64,38 @@
|
|||||||
<DataTemplate x:Key="InfoBarTemplate" x:DataType="shsn:InfoBarOptions">
|
<DataTemplate x:Key="InfoBarTemplate" x:DataType="shsn:InfoBarOptions">
|
||||||
<InfoBar
|
<InfoBar
|
||||||
Title="{Binding Title}"
|
Title="{Binding Title}"
|
||||||
|
Closed="OnInfoBarClosed"
|
||||||
|
Content="{Binding Content}"
|
||||||
|
IsOpen="True"
|
||||||
|
Message="{Binding Message}"
|
||||||
|
Severity="{Binding Severity}">
|
||||||
|
<mxi:Interaction.Behaviors>
|
||||||
|
<shcb:InfoBarDelayCloseBehavior MilliSecondsDelay="{Binding MilliSecondsDelay}"/>
|
||||||
|
</mxi:Interaction.Behaviors>
|
||||||
|
</InfoBar>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate x:Key="InfoBarWithActionButtonTemplate" x:DataType="shsn:InfoBarOptions">
|
||||||
|
<InfoBar
|
||||||
|
Title="{Binding Title}"
|
||||||
|
Closed="OnInfoBarClosed"
|
||||||
Content="{Binding Content}"
|
Content="{Binding Content}"
|
||||||
IsOpen="True"
|
IsOpen="True"
|
||||||
Message="{Binding Message}"
|
Message="{Binding Message}"
|
||||||
Closed="OnInfoBarClosed"
|
|
||||||
Severity="{Binding Severity}">
|
Severity="{Binding Severity}">
|
||||||
<InfoBar.Transitions>
|
|
||||||
<AddDeleteThemeTransition/>
|
|
||||||
</InfoBar.Transitions>
|
|
||||||
<InfoBar.ActionButton>
|
<InfoBar.ActionButton>
|
||||||
<Button
|
<Button Command="{Binding ActionButtonCommand}" Content="{Binding ActionButtonContent}"/>
|
||||||
Command="{Binding ActionButtonCommand}"
|
|
||||||
Content="{Binding ActionButtonContent}"
|
|
||||||
Visibility="{Binding ActionButtonContent, Converter={StaticResource EmptyObjectToVisibilityConverter}}"/>
|
|
||||||
</InfoBar.ActionButton>
|
</InfoBar.ActionButton>
|
||||||
<mxi:Interaction.Behaviors>
|
<mxi:Interaction.Behaviors>
|
||||||
<shcb:InfoBarDelayCloseBehavior MilliSecondsDelay="{Binding MilliSecondsDelay}"/>
|
<shcb:InfoBarDelayCloseBehavior MilliSecondsDelay="{Binding MilliSecondsDelay}"/>
|
||||||
</mxi:Interaction.Behaviors>
|
</mxi:Interaction.Behaviors>
|
||||||
</InfoBar>
|
</InfoBar>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
|
<shcs:InfoBarTemplateSelector
|
||||||
|
x:Key="InfoBarTemplateSelector"
|
||||||
|
ActionButtonDisabled="{StaticResource InfoBarTemplate}"
|
||||||
|
ActionButtonEnabled="{StaticResource InfoBarWithActionButtonTemplate}"/>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
|
|
||||||
@@ -91,9 +105,13 @@
|
|||||||
Margin="32,48,32,32"
|
Margin="32,48,32,32"
|
||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Bottom"
|
||||||
ItemContainerTransitions="{StaticResource RepositionThemeTransitions}"
|
ItemContainerTransitions="{StaticResource RepositionThemeTransitions}"
|
||||||
ItemTemplate="{StaticResource InfoBarTemplate}"
|
ItemTemplateSelector="{StaticResource InfoBarTemplateSelector}"
|
||||||
ItemsSource="{x:Bind InfoBars}"
|
ItemsSource="{x:Bind InfoBars}"
|
||||||
Visibility="{x:Bind VisibilityButton.IsChecked, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}"/>
|
Visibility="{x:Bind VisibilityButton.IsChecked, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
|
||||||
|
<ItemsControl.Transitions>
|
||||||
|
<AddDeleteThemeTransition/>
|
||||||
|
</ItemsControl.Transitions>
|
||||||
|
</ItemsControl>
|
||||||
|
|
||||||
<Border
|
<Border
|
||||||
Margin="16"
|
Margin="16"
|
||||||
|
|||||||
@@ -17,10 +17,7 @@
|
|||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<mxi:Interaction.Behaviors>
|
<mxi:Interaction.Behaviors>
|
||||||
<shcb:PeriodicInvokeCommandOrOnActualThemeChangedBehavior
|
<shcb:PeriodicInvokeCommandOrOnActualThemeChangedBehavior Command="{Binding UpdateBackgroundCommand}" Period="0:5:0"/>
|
||||||
Command="{Binding UpdateBackgroundCommand}"
|
|
||||||
CommandParameter="{x:Bind BackgroundImagePresenter}"
|
|
||||||
Period="0:5:0"/>
|
|
||||||
</mxi:Interaction.Behaviors>
|
</mxi:Interaction.Behaviors>
|
||||||
|
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
|
|||||||
@@ -58,66 +58,57 @@
|
|||||||
ListValue="{x:Bind ListImageExportPanel}"/>
|
ListValue="{x:Bind ListImageExportPanel}"/>
|
||||||
|
|
||||||
<DataTemplate x:Key="AvatarGridViewSkillTemplate">
|
<DataTemplate x:Key="AvatarGridViewSkillTemplate">
|
||||||
<Border
|
<shvcont:BottomTextControl
|
||||||
Width="40"
|
Grid.Row="0"
|
||||||
Margin="0,0,6,0"
|
Grid.Column="0"
|
||||||
Style="{StaticResource BorderCardStyle}">
|
Margin="0"
|
||||||
<StackPanel>
|
Text="{Binding Level}">
|
||||||
<shci:MonoChrome
|
<shci:MonoChrome
|
||||||
Width="32"
|
Width="40"
|
||||||
Height="32"
|
Height="40"
|
||||||
Margin="4,4,4,0"
|
Margin="12"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
EnableLazyLoading="False"
|
EnableLazyLoading="False"
|
||||||
Source="{Binding Icon}"/>
|
Source="{Binding Icon}"/>
|
||||||
<TextBlock
|
</shvcont:BottomTextControl>
|
||||||
Margin="0,0,0,4"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
|
||||||
Text="{Binding Level}"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="AvatarGridViewTemplate">
|
<DataTemplate x:Key="AvatarGridViewTemplate">
|
||||||
<Grid ColumnSpacing="6" Style="{ThemeResource GridCardStyle}">
|
<Grid Style="{ThemeResource GridCardStyle}">
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition/>
|
|
||||||
<ColumnDefinition/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="auto"/>
|
|
||||||
<RowDefinition/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Border
|
<Border
|
||||||
Grid.RowSpan="2"
|
Grid.ColumnSpan="3"
|
||||||
Grid.ColumnSpan="2"
|
Margin="-6"
|
||||||
CornerRadius="{ThemeResource ControlCornerRadius}">
|
CornerRadius="{ThemeResource ControlCornerRadius}">
|
||||||
<shci:CachedImage
|
<shci:CachedImage
|
||||||
MaxWidth="145"
|
MaxWidth="368"
|
||||||
HorizontalAlignment="Right"
|
MaxHeight="101"
|
||||||
Opacity="0.5"
|
Opacity="0.5"
|
||||||
Source="{Binding NameCard}"
|
Source="{Binding NameCard}"
|
||||||
Stretch="UniformToFill"/>
|
Stretch="UniformToFill"/>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<shvcont:BottomTextControl
|
<Grid
|
||||||
Grid.Row="0"
|
Padding="6"
|
||||||
Grid.Column="0"
|
HorizontalAlignment="Left"
|
||||||
Margin="6,6,0,6"
|
ColumnSpacing="6">
|
||||||
Text="{Binding Level}">
|
<Grid.ColumnDefinitions>
|
||||||
<Grid>
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<shvcont:BottomTextControl Grid.Column="0" Text="{Binding Level}">
|
||||||
|
<Grid cw:UIElementExtensions.ClipToBounds="True" CornerRadius="{ThemeResource ControlCornerRadius}">
|
||||||
<shvcont:ItemIcon
|
<shvcont:ItemIcon
|
||||||
Width="61.5"
|
Width="64"
|
||||||
Height="61.5"
|
Height="64"
|
||||||
Icon="{Binding Icon}"
|
Icon="{Binding Icon}"
|
||||||
Quality="{Binding Quality}"/>
|
Quality="{Binding Quality}"/>
|
||||||
<Border
|
<Border
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
Background="#80000000"
|
Background="#80000000"
|
||||||
CornerRadius="0,6,0,6">
|
CornerRadius="0,0,0,6">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="6,0,6,2"
|
Margin="6,0,6,2"
|
||||||
Foreground="#FFFFFFFF"
|
Foreground="#FFFFFFFF"
|
||||||
@@ -125,22 +116,18 @@
|
|||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</shvcont:BottomTextControl>
|
</shvcont:BottomTextControl>
|
||||||
<shvcont:BottomTextControl
|
<shvcont:BottomTextControl Grid.Column="1" Text="{Binding Weapon.Level}">
|
||||||
Grid.Row="0"
|
<Grid cw:UIElementExtensions.ClipToBounds="True" CornerRadius="{ThemeResource ControlCornerRadius}">
|
||||||
Grid.Column="1"
|
|
||||||
Margin="0,6,6,6"
|
|
||||||
Text="{Binding Weapon.Level}">
|
|
||||||
<Grid>
|
|
||||||
<shvcont:ItemIcon
|
<shvcont:ItemIcon
|
||||||
Width="61.5"
|
Width="64"
|
||||||
Height="61.5"
|
Height="64"
|
||||||
Icon="{Binding Weapon.Icon}"
|
Icon="{Binding Weapon.Icon}"
|
||||||
Quality="{Binding Weapon.Quality}"/>
|
Quality="{Binding Weapon.Quality}"/>
|
||||||
<Border
|
<Border
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
Background="#80000000"
|
Background="#80000000"
|
||||||
CornerRadius="0,6,0,6">
|
CornerRadius="0,0,0,6">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="6,0,6,2"
|
Margin="6,0,6,2"
|
||||||
Foreground="#FFFFFFFF"
|
Foreground="#FFFFFFFF"
|
||||||
@@ -150,15 +137,13 @@
|
|||||||
</shvcont:BottomTextControl>
|
</shvcont:BottomTextControl>
|
||||||
|
|
||||||
<ItemsControl
|
<ItemsControl
|
||||||
Grid.Row="1"
|
Grid.Column="2"
|
||||||
Grid.Column="0"
|
|
||||||
Grid.ColumnSpan="2"
|
|
||||||
Margin="6,0,0,6"
|
|
||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Bottom"
|
||||||
ItemTemplate="{StaticResource AvatarGridViewSkillTemplate}"
|
ItemTemplate="{StaticResource AvatarGridViewSkillTemplate}"
|
||||||
ItemsPanel="{StaticResource HorizontalStackPanelSpacing0Template}"
|
ItemsPanel="{StaticResource HorizontalStackPanelSpacing6Template}"
|
||||||
ItemsSource="{Binding Skills}"/>
|
ItemsSource="{Binding Skills}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="AvatarListViewTemplate">
|
<DataTemplate x:Key="AvatarListViewTemplate">
|
||||||
|
|||||||
@@ -289,7 +289,7 @@
|
|||||||
<Grid>
|
<Grid>
|
||||||
<Pivot Style="{ThemeResource CardPivotStyle}" Visibility="{Binding CultivateEntries.Count, Converter={StaticResource Int32ToVisibilityConverter}}">
|
<Pivot Style="{ThemeResource CardPivotStyle}" Visibility="{Binding CultivateEntries.Count, Converter={StaticResource Int32ToVisibilityConverter}}">
|
||||||
<PivotItem Header="{shcm:ResourceString Name=ViewPageCultivationCultivateEntry}">
|
<PivotItem Header="{shcm:ResourceString Name=ViewPageCultivationCultivateEntry}">
|
||||||
<ScrollView Padding="0,0">
|
<ScrollViewer Padding="0,0">
|
||||||
<ItemsRepeater
|
<ItemsRepeater
|
||||||
Margin="16,16,16,0"
|
Margin="16,16,16,0"
|
||||||
ItemTemplate="{StaticResource CultivateEntryTemplate}"
|
ItemTemplate="{StaticResource CultivateEntryTemplate}"
|
||||||
@@ -301,12 +301,12 @@
|
|||||||
MinRowSpacing="-4"/>
|
MinRowSpacing="-4"/>
|
||||||
</ItemsRepeater.Layout>
|
</ItemsRepeater.Layout>
|
||||||
</ItemsRepeater>
|
</ItemsRepeater>
|
||||||
</ScrollView>
|
</ScrollViewer>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem Header="{shcm:ResourceString Name=ViewPageCultivationMaterialStatistics}">
|
<PivotItem Header="{shcm:ResourceString Name=ViewPageCultivationMaterialStatistics}">
|
||||||
<Border Margin="16" cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
<Border Margin="16" cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||||
<Border Style="{ThemeResource AcrylicBorderCardStyle}">
|
<Border Style="{ThemeResource AcrylicBorderCardStyle}">
|
||||||
<ScrollView Padding="16,0">
|
<ScrollViewer Padding="16,0">
|
||||||
<ItemsRepeater
|
<ItemsRepeater
|
||||||
Margin="0,16,0,0"
|
Margin="0,16,0,0"
|
||||||
ItemTemplate="{StaticResource StatisticsItemTemplate}"
|
ItemTemplate="{StaticResource StatisticsItemTemplate}"
|
||||||
@@ -320,7 +320,7 @@
|
|||||||
MinRowSpacing="-4"/>
|
MinRowSpacing="-4"/>
|
||||||
</ItemsRepeater.Layout>
|
</ItemsRepeater.Layout>
|
||||||
</ItemsRepeater>
|
</ItemsRepeater>
|
||||||
</ScrollView>
|
</ScrollViewer>
|
||||||
</Border>
|
</Border>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
@@ -379,7 +379,9 @@
|
|||||||
<PivotItem Header="{shcm:ResourceString Name=ViewPageCultivationInventoryItem}">
|
<PivotItem Header="{shcm:ResourceString Name=ViewPageCultivationInventoryItem}">
|
||||||
<Border Margin="16" cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
<Border Margin="16" cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||||
<Border Style="{ThemeResource AcrylicBorderCardStyle}">
|
<Border Style="{ThemeResource AcrylicBorderCardStyle}">
|
||||||
|
<!-- Add a ScrollView for smooth scrolling -->
|
||||||
<ScrollView HorizontalScrollBarVisibility="Hidden">
|
<ScrollView HorizontalScrollBarVisibility="Hidden">
|
||||||
|
<ScrollViewer HorizontalScrollBarVisibility="Disabled">
|
||||||
<ItemsRepeater
|
<ItemsRepeater
|
||||||
Margin="16"
|
Margin="16"
|
||||||
ItemTemplate="{StaticResource InventoryItemTemplate}"
|
ItemTemplate="{StaticResource InventoryItemTemplate}"
|
||||||
@@ -388,6 +390,7 @@
|
|||||||
<shcl:WrapLayout HorizontalSpacing="12" VerticalSpacing="12"/>
|
<shcl:WrapLayout HorizontalSpacing="12" VerticalSpacing="12"/>
|
||||||
</ItemsRepeater.Layout>
|
</ItemsRepeater.Layout>
|
||||||
</ItemsRepeater>
|
</ItemsRepeater>
|
||||||
|
</ScrollViewer>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</Border>
|
</Border>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -378,7 +378,6 @@
|
|||||||
IsExpanded="{Binding FallbackValue=False, Converter={StaticResource EmptyObjectToBoolConverter}, Mode=OneTime}"/>
|
IsExpanded="{Binding FallbackValue=False, Converter={StaticResource EmptyObjectToBoolConverter}, Mode=OneTime}"/>
|
||||||
</Border>
|
</Border>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<ItemsControl
|
<ItemsControl
|
||||||
Margin="0,0,0,0"
|
Margin="0,0,0,0"
|
||||||
ItemTemplate="{StaticResource GameResourceTemplate}"
|
ItemTemplate="{StaticResource GameResourceTemplate}"
|
||||||
|
|||||||
@@ -129,11 +129,6 @@
|
|||||||
Header="Rename Desktop TestFolder"
|
Header="Rename Desktop TestFolder"
|
||||||
IsClickEnabled="True"/>
|
IsClickEnabled="True"/>
|
||||||
|
|
||||||
<cwc:SettingsCard
|
|
||||||
Command="{Binding ReeeeeeecreateMainWindowCommand}"
|
|
||||||
Header="Infinitely Recreate Main Window"
|
|
||||||
IsClickEnabled="True"/>
|
|
||||||
|
|
||||||
<cwc:SettingsCard Header="Crash">
|
<cwc:SettingsCard Header="Crash">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<Button Command="{Binding ExceptionCommand}" Content="Activate"/>
|
<Button Command="{Binding ExceptionCommand}" Content="Activate"/>
|
||||||
|
|||||||
@@ -19,7 +19,66 @@
|
|||||||
<shc:BindingProxy x:Key="ViewModelBindingProxy" DataContext="{Binding}"/>
|
<shc:BindingProxy x:Key="ViewModelBindingProxy" DataContext="{Binding}"/>
|
||||||
|
|
||||||
<DataTemplate x:Key="UserGameRoleTemplate">
|
<DataTemplate x:Key="UserGameRoleTemplate">
|
||||||
<StackPanel Padding="0,6">
|
<Grid Padding="0,12" Background="Transparent">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<PersonPicture
|
||||||
|
Grid.Column="0"
|
||||||
|
Height="32"
|
||||||
|
Margin="2,0"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Background="#FFDAB79B"
|
||||||
|
ProfilePicture="{Binding ProfilePictureIcon, Converter={StaticResource AvatarIconCircleConverter}}"/>
|
||||||
|
<Button
|
||||||
|
Grid.Column="0"
|
||||||
|
Width="32"
|
||||||
|
Height="32"
|
||||||
|
Margin="2,0"
|
||||||
|
Padding="0"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderBrush="Transparent"
|
||||||
|
BorderThickness="0"
|
||||||
|
Command="{Binding RefreshProfilePictureCommand}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
CornerRadius="{ThemeResource CornerRadiusAll16}">
|
||||||
|
<Button.Resources>
|
||||||
|
<Storyboard x:Key="ShowRefreshIcon">
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RefreshIcon" Storyboard.TargetProperty="Visibility">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0">
|
||||||
|
<DiscreteObjectKeyFrame.Value>
|
||||||
|
<Visibility>Visible</Visibility>
|
||||||
|
</DiscreteObjectKeyFrame.Value>
|
||||||
|
</DiscreteObjectKeyFrame>
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
<Storyboard x:Key="HideRefreshIcon">
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RefreshIcon" Storyboard.TargetProperty="Visibility">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0">
|
||||||
|
<DiscreteObjectKeyFrame.Value>
|
||||||
|
<Visibility>Collapsed</Visibility>
|
||||||
|
</DiscreteObjectKeyFrame.Value>
|
||||||
|
</DiscreteObjectKeyFrame>
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
</Button.Resources>
|
||||||
|
<mxi:Interaction.Behaviors>
|
||||||
|
<mxic:EventTriggerBehavior EventName="PointerEntered">
|
||||||
|
<mxim:ControlStoryboardAction Storyboard="{StaticResource ShowRefreshIcon}"/>
|
||||||
|
</mxic:EventTriggerBehavior>
|
||||||
|
<mxic:EventTriggerBehavior EventName="PointerExited">
|
||||||
|
<mxim:ControlStoryboardAction Storyboard="{StaticResource HideRefreshIcon}"/>
|
||||||
|
</mxic:EventTriggerBehavior>
|
||||||
|
</mxi:Interaction.Behaviors>
|
||||||
|
<FontIcon
|
||||||
|
x:Name="RefreshIcon"
|
||||||
|
FontSize="12"
|
||||||
|
Glyph=""
|
||||||
|
Visibility="Collapsed"/>
|
||||||
|
</Button>
|
||||||
|
<StackPanel Grid.Column="1" Margin="12,0">
|
||||||
<TextBlock Text="{Binding Nickname}"/>
|
<TextBlock Text="{Binding Nickname}"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0,2,0,0"
|
Margin="0,2,0,0"
|
||||||
@@ -27,6 +86,7 @@
|
|||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
Text="{Binding Description}"/>
|
Text="{Binding Description}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="UserTemplate">
|
<DataTemplate x:Key="UserTemplate">
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ internal static class StaticResource
|
|||||||
{ "AchievementIcon", 0 },
|
{ "AchievementIcon", 0 },
|
||||||
{ "AvatarCard", 0 },
|
{ "AvatarCard", 0 },
|
||||||
{ "AvatarIcon", 0 },
|
{ "AvatarIcon", 0 },
|
||||||
|
{ "AvatarIconCircle", 0 },
|
||||||
{ "Bg", 0 },
|
{ "Bg", 0 },
|
||||||
{ "ChapterIcon", 0 },
|
{ "ChapterIcon", 0 },
|
||||||
{ "CodexMonster", 0 },
|
{ "CodexMonster", 0 },
|
||||||
@@ -50,6 +51,7 @@ internal static class StaticResource
|
|||||||
{ "AchievementIcon", 2 },
|
{ "AchievementIcon", 2 },
|
||||||
{ "AvatarCard", 2 },
|
{ "AvatarCard", 2 },
|
||||||
{ "AvatarIcon", 5 },
|
{ "AvatarIcon", 5 },
|
||||||
|
{ "AvatarIconCircle", 1 },
|
||||||
{ "Bg", 3 },
|
{ "Bg", 3 },
|
||||||
{ "ChapterIcon", 3 },
|
{ "ChapterIcon", 3 },
|
||||||
{ "CodexMonster", 0 },
|
{ "CodexMonster", 0 },
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ internal sealed partial class MainViewModel : Abstraction.ViewModel, IMainViewMo
|
|||||||
public void Initialize(IBackgroundImagePresenterAccessor accessor)
|
public void Initialize(IBackgroundImagePresenterAccessor accessor)
|
||||||
{
|
{
|
||||||
backgroundImagePresenter = accessor.BackgroundImagePresenter;
|
backgroundImagePresenter = accessor.BackgroundImagePresenter;
|
||||||
|
UpdateBackgroundAsync(true).SafeForget();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Receive(BackgroundImageTypeChangedMessage message)
|
public void Receive(BackgroundImageTypeChangedMessage message)
|
||||||
@@ -39,14 +40,14 @@ internal sealed partial class MainViewModel : Abstraction.ViewModel, IMainViewMo
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Command("UpdateBackgroundCommand")]
|
[Command("UpdateBackgroundCommand")]
|
||||||
private async Task UpdateBackgroundAsync()
|
private async Task UpdateBackgroundAsync(bool forceRefresh = false)
|
||||||
{
|
{
|
||||||
if (backgroundImagePresenter is null)
|
if (backgroundImagePresenter is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
(bool shouldRefresh, BackgroundImage? backgroundImage) = await backgroundImageService.GetNextBackgroundImageAsync(previousBackgroundImage).ConfigureAwait(false);
|
(bool shouldRefresh, BackgroundImage? backgroundImage) = await backgroundImageService.GetNextBackgroundImageAsync(forceRefresh ? default : previousBackgroundImage).ConfigureAwait(false);
|
||||||
|
|
||||||
if (shouldRefresh)
|
if (shouldRefresh)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Snap.Hutao.Factory.ContentDialog;
|
using Snap.Hutao.Factory.ContentDialog;
|
||||||
using Snap.Hutao.Message;
|
using Snap.Hutao.Message;
|
||||||
using Snap.Hutao.Service.Hutao;
|
using Snap.Hutao.Service.Hutao;
|
||||||
@@ -142,11 +143,20 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel
|
|||||||
.CreateInstanceAsync<SpiralAbyssUploadRecordHomaNotLoginDialog>()
|
.CreateInstanceAsync<SpiralAbyssUploadRecordHomaNotLoginDialog>()
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
if (!await dialog.ConfirmAsync().ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
await taskContext.SwitchToMainThreadAsync();
|
await taskContext.SwitchToMainThreadAsync();
|
||||||
|
ContentDialogResult result = await dialog.ShowAsync();
|
||||||
|
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case ContentDialogResult.Primary:
|
||||||
await navigationService.NavigateAsync<SettingPage>(INavigationAwaiter.Default, true).ConfigureAwait(false);
|
await navigationService.NavigateAsync<SettingPage>(INavigationAwaiter.Default, true).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case ContentDialogResult.Secondary:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ContentDialogResult.None:
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -172,23 +172,4 @@ internal sealed partial class TestViewModel : Abstraction.ViewModel
|
|||||||
string source = Path.Combine(desktop, "TestFolder");
|
string source = Path.Combine(desktop, "TestFolder");
|
||||||
DirectoryOperation.UnsafeRename(source, "TestFolder1");
|
DirectoryOperation.UnsafeRename(source, "TestFolder1");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command("ReeeeeeecreateMainWindowCommand")]
|
|
||||||
private async Task ReeeeeeecreateMainWindowAsync()
|
|
||||||
{
|
|
||||||
currentXamlWindowReference.Window?.Close();
|
|
||||||
currentXamlWindowReference.Window = null;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
|
|
||||||
mainWindow.SwitchTo();
|
|
||||||
mainWindow.BringToForeground();
|
|
||||||
|
|
||||||
await Delay.FromMilliSeconds(2000).ConfigureAwait(true);
|
|
||||||
|
|
||||||
mainWindow.Hide();
|
|
||||||
mainWindow.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,7 @@ using Snap.Hutao.View.Dialog;
|
|||||||
using Snap.Hutao.View.Page;
|
using Snap.Hutao.View.Page;
|
||||||
using Snap.Hutao.Web.Hoyolab;
|
using Snap.Hutao.Web.Hoyolab;
|
||||||
using Snap.Hutao.Web.Hoyolab.Passport;
|
using Snap.Hutao.Web.Hoyolab.Passport;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||||
using Snap.Hutao.Web.Response;
|
using Snap.Hutao.Web.Response;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using EntityUser = Snap.Hutao.Model.Entity.User;
|
using EntityUser = Snap.Hutao.Model.Entity.User;
|
||||||
|
|||||||
@@ -21,22 +21,33 @@ namespace Snap.Hutao.Web.Enka;
|
|||||||
internal sealed partial class EnkaClient
|
internal sealed partial class EnkaClient
|
||||||
{
|
{
|
||||||
private const string EnkaAPI = "https://enka.network/api/uid/{0}";
|
private const string EnkaAPI = "https://enka.network/api/uid/{0}";
|
||||||
|
private const string EnkaInfoAPI = "https://enka.network/api/uid/{0}?info";
|
||||||
|
|
||||||
private readonly IHttpRequestMessageBuilderFactory httpRequestMessageBuilderFactory;
|
private readonly IHttpRequestMessageBuilderFactory httpRequestMessageBuilderFactory;
|
||||||
private readonly JsonSerializerOptions options;
|
private readonly JsonSerializerOptions options;
|
||||||
private readonly HttpClient httpClient;
|
private readonly HttpClient httpClient;
|
||||||
|
|
||||||
|
public ValueTask<EnkaResponse?> GetForwardPlayerInfoAsync(in PlayerUid playerUid, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return TryGetEnkaResponseCoreAsync(HutaoEndpoints.EnkaPlayerInfo(playerUid), true, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask<EnkaResponse?> GetPlayerInfoAsync(in PlayerUid playerUid, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return TryGetEnkaResponseCoreAsync(string.Format(CultureInfo.CurrentCulture, EnkaInfoAPI, playerUid), false, token);
|
||||||
|
}
|
||||||
|
|
||||||
public ValueTask<EnkaResponse?> GetForwardDataAsync(in PlayerUid playerUid, CancellationToken token = default)
|
public ValueTask<EnkaResponse?> GetForwardDataAsync(in PlayerUid playerUid, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return TryGetEnkaResponseCoreAsync(HutaoEndpoints.Enka(playerUid), token);
|
return TryGetEnkaResponseCoreAsync(HutaoEndpoints.Enka(playerUid), true, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueTask<EnkaResponse?> GetDataAsync(in PlayerUid playerUid, CancellationToken token = default)
|
public ValueTask<EnkaResponse?> GetDataAsync(in PlayerUid playerUid, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return TryGetEnkaResponseCoreAsync(string.Format(CultureInfo.CurrentCulture, EnkaAPI, playerUid), token);
|
return TryGetEnkaResponseCoreAsync(string.Format(CultureInfo.CurrentCulture, EnkaAPI, playerUid), false, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ValueTask<EnkaResponse?> TryGetEnkaResponseCoreAsync(string url, CancellationToken token = default)
|
private async ValueTask<EnkaResponse?> TryGetEnkaResponseCoreAsync(string url, bool isForward, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -52,6 +63,16 @@ internal sealed partial class EnkaClient
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// We want to fallback to original API and retry when requesting our forward api
|
||||||
|
if (isForward)
|
||||||
|
{
|
||||||
|
string content = await response.Content.ReadAsStringAsync(token).ConfigureAwait(false);
|
||||||
|
if (content.Contains("nginx", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/yoimiya-kokomi/miao-plugin/pull/441
|
// https://github.com/yoimiya-kokomi/miao-plugin/pull/441
|
||||||
// Additionally, HTTP codes for UID requests:
|
// Additionally, HTTP codes for UID requests:
|
||||||
// 400 = wrong UID format
|
// 400 = wrong UID format
|
||||||
|
|||||||
@@ -13,4 +13,10 @@ internal sealed class ProfilePicture
|
|||||||
{
|
{
|
||||||
[JsonPropertyName("id")]
|
[JsonPropertyName("id")]
|
||||||
public ProfilePictureId Id { get; set; }
|
public ProfilePictureId Id { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("avatarId")]
|
||||||
|
public AvatarId AvatarId { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("costumeId")]
|
||||||
|
public CostumeId CostumeId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,21 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Snap.Hutao.Service.User;
|
||||||
|
|
||||||
namespace Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
namespace Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户游戏角色
|
/// 用户游戏角色
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HighQuality]
|
[HighQuality]
|
||||||
internal sealed class UserGameRole
|
internal sealed class UserGameRole : ObservableObject
|
||||||
{
|
{
|
||||||
|
private string? profilePictureIcon;
|
||||||
|
private ICommand? refreshProfilePictureCommand;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// hk4e_cn for Genshin Impact
|
/// hk4e_cn for Genshin Impact
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -65,6 +72,18 @@ internal sealed class UserGameRole
|
|||||||
get => $"{RegionName} | Lv.{Level}";
|
get => $"{RegionName} | Lv.{Level}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string? ProfilePictureIcon
|
||||||
|
{
|
||||||
|
get => profilePictureIcon;
|
||||||
|
set => SetProperty(ref profilePictureIcon, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand RefreshProfilePictureCommand
|
||||||
|
{
|
||||||
|
get => refreshProfilePictureCommand ??= new AsyncRelayCommand(RefreshProfilePictureAsync);
|
||||||
|
}
|
||||||
|
|
||||||
public static implicit operator PlayerUid(UserGameRole userGameRole)
|
public static implicit operator PlayerUid(UserGameRole userGameRole)
|
||||||
{
|
{
|
||||||
return new PlayerUid(userGameRole.GameUid, userGameRole.Region);
|
return new PlayerUid(userGameRole.GameUid, userGameRole.Region);
|
||||||
@@ -75,4 +94,10 @@ internal sealed class UserGameRole
|
|||||||
{
|
{
|
||||||
return $"{Nickname} | {RegionName} | Lv.{Level}";
|
return $"{Nickname} | {RegionName} | Lv.{Level}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("", "SH003")]
|
||||||
|
private async Task RefreshProfilePictureAsync()
|
||||||
|
{
|
||||||
|
await Ioc.Default.GetRequiredService<IUserService>().RefreshProfilePictureAsync(this).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -212,6 +212,11 @@ internal static class HutaoEndpoints
|
|||||||
return $"{ApiSnapGenshinEnka}/{uid}";
|
return $"{ApiSnapGenshinEnka}/{uid}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string EnkaPlayerInfo(in PlayerUid uid)
|
||||||
|
{
|
||||||
|
return $"{ApiSnapGenshinEnka}/{uid}/info";
|
||||||
|
}
|
||||||
|
|
||||||
public const string Ip = $"{ApiSnapGenshin}/ip";
|
public const string Ip = $"{ApiSnapGenshin}/ip";
|
||||||
|
|
||||||
#region Metadata
|
#region Metadata
|
||||||
|
|||||||
Reference in New Issue
Block a user