mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
89 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07c52019f4 | ||
|
|
77cb2fc603 | ||
|
|
b5c16e2dae | ||
|
|
29c954b032 | ||
|
|
8df5d5d6eb | ||
|
|
827d944987 | ||
|
|
4c47f3c08b | ||
|
|
6540cc4577 | ||
|
|
df22d30a96 | ||
|
|
5ea9dd533f | ||
|
|
744c1079e1 | ||
|
|
1539863415 | ||
|
|
3527e43118 | ||
|
|
8388def548 | ||
|
|
d0bfbfa505 | ||
|
|
fa640d27f0 | ||
|
|
389c1417f7 | ||
|
|
6dcacb1bf4 | ||
|
|
4ffd09cce8 | ||
|
|
0dcbac3ee1 | ||
|
|
b1ea5332fc | ||
|
|
b34dab0f99 | ||
|
|
791e517e39 | ||
|
|
4408e3994e | ||
|
|
32cbbefe1a | ||
|
|
d754c0d117 | ||
|
|
4c75295f2c | ||
|
|
34e5312d75 | ||
|
|
465d6b631e | ||
|
|
0d2d1b8115 | ||
|
|
981949651e | ||
|
|
874dac1119 | ||
|
|
3f0694b28e | ||
|
|
6b166b6aed | ||
|
|
b351231c84 | ||
|
|
0603b24466 | ||
|
|
f97ad4eac0 | ||
|
|
28fc4558be | ||
|
|
ea3391b112 | ||
|
|
ec95e42d7d | ||
|
|
e9f12aeb09 | ||
|
|
04850dd136 | ||
|
|
4782d61ed0 | ||
|
|
28ade90926 | ||
|
|
dde97b6489 | ||
|
|
44fe729e1a | ||
|
|
2a1d814cc5 | ||
|
|
c1ee37bd8f | ||
|
|
65b81f0ad8 | ||
|
|
91fea88623 | ||
|
|
026c68229a | ||
|
|
91b2db886f | ||
|
|
101d316525 | ||
|
|
59d62f931d | ||
|
|
f0bb19bc07 | ||
|
|
c0b05e2c2f | ||
|
|
fc02f833a0 | ||
|
|
7b8ebd86b1 | ||
|
|
47b24286b1 | ||
|
|
0e3e3b9e4a | ||
|
|
85d7b22e11 | ||
|
|
7caeb17788 | ||
|
|
b11b90e9f1 | ||
|
|
58643a60b5 | ||
|
|
a29b487c26 | ||
|
|
1bd6023e0a | ||
|
|
579173d464 | ||
|
|
9ed53e8c34 | ||
|
|
830556a043 | ||
|
|
7ba27e184f | ||
|
|
9aa6a2b57b | ||
|
|
5773902f4a | ||
|
|
06c5bcad3e | ||
|
|
c0165c57fd | ||
|
|
4e57520115 | ||
|
|
17c3480dae | ||
|
|
7d5faadbb5 | ||
|
|
b7df968ea7 | ||
|
|
5f6cc46774 | ||
|
|
a34b5a5101 | ||
|
|
457e3c4af2 | ||
|
|
26143079b3 | ||
|
|
20ad9aec60 | ||
|
|
61dd098d95 | ||
|
|
e7233fbf2a | ||
|
|
8de526274a | ||
|
|
5b1fb6e1dd | ||
|
|
1584fd1428 | ||
|
|
65179a340f |
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
@@ -1,8 +1,8 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
github: [DGP-Studio]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
open_collective: snaphutao
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: 问题反馈
|
||||
description: 告诉我们你的问题
|
||||
description: 通过这个议题向开发团队反馈你发现的程序中的问题
|
||||
title: "[Bug]: 在这里填写一个合适的标题"
|
||||
labels: ["BUG"]
|
||||
labels: ["BUG", "priority:none"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -14,7 +14,7 @@ body:
|
||||
attributes:
|
||||
label: 检查清单
|
||||
description: |-
|
||||
请确保你已完整执行检查清单,否则你的 Issue 可能会被忽略
|
||||
请确保你已完整执行检查清单,否则你的议题可能会被忽略
|
||||
options:
|
||||
- label: 我已阅读 Snap Hutao 文档中的[常见问题](https://hut.ao/advanced/FAQ.html)和[常见程序异常](https://hut.ao/advanced/exceptions.html),我的问题没有在文档中得到解答
|
||||
required: true
|
||||
@@ -51,6 +51,7 @@ body:
|
||||
description: |
|
||||
在胡桃工具箱的设置界面,你可以找到并复制你的设备 ID
|
||||
如果你的问题涉及程序崩溃,请填写该项,这将有助于我们定位问题
|
||||
如果你的程序已经无法启动,请下载并运行[此PowerShell脚本](https://github.com/DGP-Studio/ISSUE_TEMPLATES/releases/download/get_device_id/GetHutaoDeviceId.ps1),它将显示你的设备 ID
|
||||
validations:
|
||||
required: false
|
||||
|
||||
27
.github/ISSUE_TEMPLATE/CHS-feature-request.yml
vendored
Normal file
27
.github/ISSUE_TEMPLATE/CHS-feature-request.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: 功能请求
|
||||
description: 通过这个议题来向开发团队分享你的想法
|
||||
title: "[Feat]: 在这里填写一个合适的标题"
|
||||
labels: ["功能", "priority:none"]
|
||||
assignees:
|
||||
- Lightczx
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
请按下方的要求填写完整的问题表单。
|
||||
|
||||
- type: textarea
|
||||
id: back
|
||||
attributes:
|
||||
label: 背景与动机
|
||||
description: 添加此功能的理由,如果你想要实现多个功能,请分别发起多个单独的议题
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: req
|
||||
attributes:
|
||||
label: 想要实现或优化的功能
|
||||
description: 详细的描述一下你想要的功能,描述的越具体,采纳的可能性越高
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,5 +1,5 @@
|
||||
name: 网络问题
|
||||
description: 当网络问题影响到你的程序使用时
|
||||
description: 通过这个议题来反馈网络问题
|
||||
title: "[Network]: 在这里填写一个合适的标题"
|
||||
labels: ["area-Network"]
|
||||
assignees:
|
||||
@@ -19,10 +19,10 @@ body:
|
||||
description: |
|
||||
停下!
|
||||
**在填写下面的问题之前请先使用我们的网络诊断工具**
|
||||
**这个工具将会生成一份报告,请将这份报告拖入下面的框中,让其与你的工单一起被上传提交**
|
||||
**这个工具将会生成一份报告并加密压缩,请将这份报告拖入下面的框中,让其与你的工单一起被上传提交**
|
||||
- 你可以点击下面的链接以下载网络诊断工具:
|
||||
- [胡桃资源站](https://d.hut.ao/d/tools/network-diagnosis-hutao.exe)
|
||||
- [GitHub](https://github.com/Masterain98/network-diagnosis-tool/releases/latest/download/network-diagnosis-hutao.exe)
|
||||
- [GitHub](https://github.com/Masterain98/network-diagnosis-tool/releases/latest/download/SH-Network-Diagnosis.exe)
|
||||
- [极狐 GitLab](https://jihulab.com/DGP-Studio/network-diagnosis-tool/-/jobs/11144011/artifacts/raw/SH-Network-Diagnosis.exe?inline=false)
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -60,7 +60,6 @@ body:
|
||||
- 完全无法连接服务器
|
||||
- 连接速度慢
|
||||
- 获取到了不正确的页面或数据
|
||||
- 客户端提示 429 Error
|
||||
- 客户端图片下载错误
|
||||
- 客户端图片预下载错误
|
||||
- 其它
|
||||
@@ -74,5 +73,12 @@ body:
|
||||
description: 如果你在上一项中选择了`其它`或者你有更多信息需要提供,请在这里写下来
|
||||
validations:
|
||||
required: false
|
||||
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist-final
|
||||
attributes:
|
||||
label: 最后一步
|
||||
description: 检查你提交的议题
|
||||
options:
|
||||
- label: 我已经在该议题中上传了包含网络诊断报告的加密压缩包
|
||||
required: true
|
||||
@@ -1,7 +1,7 @@
|
||||
name: BUG Report [English Form]
|
||||
description: Tell us what issue you get
|
||||
title: "[ENG][Bug]: Place your Issue Title Here"
|
||||
labels: ["BUG"]
|
||||
labels: ["BUG", "priority:none"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -50,7 +50,8 @@ body:
|
||||
label: Device ID
|
||||
description: |
|
||||
In Snap Hutao's settings page, you can find and copy your device ID
|
||||
If your issue is about program crash, please fill this so we can dump the log and locate the source easier
|
||||
If your issue is about program crash, please fill this so we can dump the log and locate the source easier
|
||||
If your program cannot startup, please download and run [this PowerShell script](https://github.com/DGP-Studio/ISSUE_TEMPLATES/releases/download/get_device_id/GetHutaoDeviceId.ps1), it will shows your device ID.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
27
.github/ISSUE_TEMPLATE/ENG-feature-request.yml
vendored
Normal file
27
.github/ISSUE_TEMPLATE/ENG-feature-request.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Feature Request [English Form]
|
||||
description: Tell us about your thought
|
||||
title: "[Feat]: Place your title here"
|
||||
labels: ["功能", "priority:none"]
|
||||
assignees:
|
||||
- Lightczx
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please fill the form below
|
||||
|
||||
- type: textarea
|
||||
id: back
|
||||
attributes:
|
||||
label: Background & Motivation
|
||||
description: Reason why this feature is needed. If multiple features is requested, please open multiple issues for each of them.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: req
|
||||
attributes:
|
||||
label: Detail of the Feature
|
||||
description: Descripbe the feaure in detail. The more detailed and convincing the desciprtion the more likyly feature will be accepted.
|
||||
validations:
|
||||
required: true
|
||||
79
.github/ISSUE_TEMPLATE/ENG-network-issue.yml
vendored
Normal file
79
.github/ISSUE_TEMPLATE/ENG-network-issue.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
name: Network Issue [English Form]
|
||||
description: Submit this issue form when network issue affect your client experience
|
||||
title: "[Network]: Place your title here"
|
||||
labels: ["area-Network"]
|
||||
assignees:
|
||||
- Lightczx
|
||||
- Masterain98
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**Please use one sentence to briefly describe your issue as title above**
|
||||
**Please follow the instruction below to fill the form, so we can locate the issue quickly**
|
||||
|
||||
- type: textarea
|
||||
id: network-diagnosis-report
|
||||
attributes:
|
||||
label: Submit Your Network Diagnosis Report
|
||||
description: |
|
||||
STOP HERE!
|
||||
**Please run our network diagnosis tool before filling this form**
|
||||
**The diagnosis tool will generate a report and add it into a password-protected archive. Drag the `.zip` archive to the box below so it can be uploaded.**
|
||||
- Use the following link to download the Network Diagnosis Tool:
|
||||
- [GitHub](https://github.com/Masterain98/network-diagnosis-tool/releases/latest/download/SH-Network-Diagnosis.exe)
|
||||
- [JIHu GitLab](https://jihulab.com/DGP-Studio/network-diagnosis-tool/-/jobs/11144011/artifacts/raw/SH-Network-Diagnosis.exe?inline=false)
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: user-geo-location
|
||||
attributes:
|
||||
label: Your Geographical Location
|
||||
description: |
|
||||
Description accurate to country
|
||||
placeholder: USA
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: user-isp
|
||||
attributes:
|
||||
label: Your ISP Name
|
||||
description: |
|
||||
Name of your Internet service provider
|
||||
placeholder: AT&T
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: user-issue-category
|
||||
attributes:
|
||||
label: Issue Category
|
||||
description: Select an issue category
|
||||
options:
|
||||
- Cannot connect to server completely
|
||||
- Slow spped
|
||||
- Fetched wrong page or data
|
||||
- Image download error in the client
|
||||
- Image set pre-download error (client welcome wizard process)
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: Your Issue (cont.)
|
||||
description: If you selected `Other` in previous dropdown, please explain your issue in detail here.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist-final
|
||||
attributes:
|
||||
label: One Last Step
|
||||
description: Check your issue form
|
||||
options:
|
||||
- label: I confirm I have attached the network diagnosis report archive in the issue
|
||||
required: true
|
||||
65
.github/ISSUE_TEMPLATE/artifact-rating-rules.yml
vendored
65
.github/ISSUE_TEMPLATE/artifact-rating-rules.yml
vendored
@@ -1,65 +0,0 @@
|
||||
name: 圣遗物评分细则建议
|
||||
description: 为圣遗物评分规则提供你的想法
|
||||
title: "[Artifact Rating] 请在这里填写角色名称"
|
||||
labels: area-AvatarInfo
|
||||
assignees: Lightczx
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
请按下方的要求填写完整的问题表单
|
||||
|
||||
- type: textarea
|
||||
id: your-suggested-rule
|
||||
attributes:
|
||||
label: 评分细则
|
||||
description: |
|
||||
请修改下方表格中的**角色名称**和**各属性权重**,并在表格后添加合适的说明
|
||||
你可以点击预览按钮(preview)来查看表格最终会显示出的内容
|
||||
value: |
|
||||
|项目|评分权重(0-100)|
|
||||
|-----|-----|
|
||||
|角色名称| 旅行者 |
|
||||
|生命值| 10 |
|
||||
|攻击力| 10 |
|
||||
|防御力| 10 |
|
||||
|暴击率| 10 |
|
||||
|暴击伤害| 10 |
|
||||
|元素精通| 10 |
|
||||
|充能效率| 10 |
|
||||
|治疗加成| 10 |
|
||||
|元素伤害| 10 |
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: no-duplicated-dropdown
|
||||
attributes:
|
||||
label: 我确认当前没有其它的该角色的圣遗物评分细则建议
|
||||
description: 如果有,你应该在已有的工单内回复以提出你的建议
|
||||
options:
|
||||
- 否
|
||||
- 是
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: title-filled-dropdown
|
||||
attributes:
|
||||
label: 我确认已设置合适的标题
|
||||
options:
|
||||
- 否
|
||||
- 是
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: all-filled-dropdown
|
||||
attributes:
|
||||
label: 我确认已完整填写表格
|
||||
options:
|
||||
- 否
|
||||
- 是
|
||||
validations:
|
||||
required: true
|
||||
28
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
28
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -1,28 +0,0 @@
|
||||
name: Feature Request 功能请求
|
||||
description: Tell us about your thought 告诉我们你的想法
|
||||
title: "[Feat]: Place your title here 在这里填写一个合适的标题"
|
||||
labels: ["功能"]
|
||||
assignees:
|
||||
- Lightczx
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please fill the form below
|
||||
请按下方的要求填写完整的问题表单。
|
||||
|
||||
- type: textarea
|
||||
id: back
|
||||
attributes:
|
||||
label: Background & Motivation 背景与动机
|
||||
description: Reason why this feature is needed. If multiple features is requested, please open multiple issues for each of them. 添加此功能的理由,如果你想要实现多个功能,请分别发起多个单独的 Issue
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: req
|
||||
attributes:
|
||||
label: Detail of the Feature 想要实现或优化的功能
|
||||
description: Descripbe the feaure in detail. The more detailed and convincing the desciprtion the more likyly feature will be accepted. 详细的描述一下你想要的功能,描述的越具体,采纳的可能性越高
|
||||
validations:
|
||||
required: true
|
||||
26
.github/workflows/qodana_code_quality.yml
vendored
26
.github/workflows/qodana_code_quality.yml
vendored
@@ -1,26 +0,0 @@
|
||||
name: Qodana
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.yml'
|
||||
- '**.resx'
|
||||
|
||||
jobs:
|
||||
qodana:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: 'Qodana Scan'
|
||||
uses: JetBrains/qodana-action@v2023.2
|
||||
with:
|
||||
pr-mode: false
|
||||
env:
|
||||
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,6 +10,7 @@ src/Snap.Hutao/_ReSharper.Caches
|
||||
|
||||
src/Snap.Hutao/Snap.Hutao/bin/
|
||||
src/Snap.Hutao/Snap.Hutao/obj/
|
||||
src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
|
||||
src/Snap.Hutao/Snap.Hutao/Snap.Hutao_TemporaryKey.pfx
|
||||
|
||||
src/Snap.Hutao/Snap.Hutao.Win32/bin/
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||

|
||||
|
||||
胡桃工具箱是一个 Windows 平台的开源的原神工具箱,旨在帮助玩家获得更好的游戏体验; 它是对官方移动端工具的一种非破坏性功能扩展,为不习惯在移动端进行原神游戏的 PC 玩家提供一个在 Windows 平台下获得接近移动端功能权利的途径
|
||||
胡桃工具箱是一款以 MIT 协议开源的原神工具箱,专为现代化 Windows 平台设计,旨在改善桌面端玩家的游戏体验。通过将既有的官方资源与开发团队设计的全新 功能相结合,它提供了一套完整且实用的工具集,且无需依赖任何移动设备。它不对游戏客户端进行任何破坏性修改以确保工具箱的安全性
|
||||
|
||||
Snap Hutao is an open-source Genshin Impact toolbox on Windows platform, aim to provide a better gaming experience for players. It's an nondestructive feature extension from Genshin Impact's official mobile application, to provide similar feature on desktop, to allow PC gamers gain deserved benefits from mobile platforms.
|
||||
Snap Hutao is an open-source Genshin Impact toolkit under MIT license, designed for modern Windows platform to improve the gaming experience for desktop players. By combining existing official resources with new features designed by the development team, it provides a complete and useful set of tools without the need to rely on mobile devices. Snap Hutao does not take any destructive modification to the game client to ensure the security of the toolkit.
|
||||
|
||||
## 下载使用 / Download
|
||||
|
||||
|
||||
33
qodana.yaml
33
qodana.yaml
@@ -1,33 +0,0 @@
|
||||
#-------------------------------------------------------------------------------#
|
||||
# Qodana analysis is configured by qodana.yaml file #
|
||||
# https://www.jetbrains.com/help/qodana/qodana-yaml.html #
|
||||
#-------------------------------------------------------------------------------#
|
||||
version: "1.0"
|
||||
|
||||
#Specify inspection profile for code analysis
|
||||
profile:
|
||||
name: qodana.starter
|
||||
|
||||
#Enable inspections
|
||||
#include:
|
||||
# - name: <SomeEnabledInspectionId>
|
||||
|
||||
#Disable inspections
|
||||
exclude:
|
||||
- name: Test
|
||||
paths:
|
||||
- Snap.Hutao.Test
|
||||
- Snap.Hutao.SourceGeneration
|
||||
- name: All
|
||||
paths:
|
||||
- Snap.Hutao.SourceGeneration
|
||||
- Snap.Hutao.Test
|
||||
#Execute shell command before Qodana execution (Applied in CI/CD pipeline)
|
||||
#bootstrap: sh ./prepare-qodana.sh
|
||||
|
||||
#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline)
|
||||
#plugins:
|
||||
# - id: <plugin.id> #(plugin id can be found at https://plugins.jetbrains.com)
|
||||
|
||||
#Specify Qodana linter for analysis (Applied in CI/CD pipeline)
|
||||
linter: jetbrains/qodana-dotnet:2023.2-eap
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
@@ -10,7 +11,18 @@ namespace Snap.Hutao.SourceGeneration.Automation;
|
||||
[Generator(LanguageNames.CSharp)]
|
||||
internal sealed class SaltConstantGenerator : IIncrementalGenerator
|
||||
{
|
||||
private static readonly HttpClient httpClient = new();
|
||||
private static readonly HttpClient httpClient;
|
||||
private static readonly Lazy<Response<SaltLatest>> lazySaltInfo;
|
||||
|
||||
static SaltConstantGenerator()
|
||||
{
|
||||
httpClient = new();
|
||||
lazySaltInfo = new Lazy<Response<SaltLatest>>(() =>
|
||||
{
|
||||
string body = httpClient.GetStringAsync("https://internal.snapgenshin.cn/Archive/Salt/Latest").GetAwaiter().GetResult();
|
||||
return JsonParser.FromJson<Response<SaltLatest>>(body)!;
|
||||
});
|
||||
}
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
@@ -19,8 +31,7 @@ internal sealed class SaltConstantGenerator : IIncrementalGenerator
|
||||
|
||||
private static void GenerateSaltContstants(IncrementalGeneratorPostInitializationContext context)
|
||||
{
|
||||
string body = httpClient.GetStringAsync("https://internal.snapgenshin.cn/Archive/Salt/Latest").GetAwaiter().GetResult();
|
||||
Response<SaltLatest> saltInfo = JsonParser.FromJson<Response<SaltLatest>>(body)!;
|
||||
Response<SaltLatest> saltInfo = lazySaltInfo.Value;
|
||||
string code = $$"""
|
||||
namespace Snap.Hutao.Web.Hoyolab;
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ internal class LocalizedEnumGenerator : IIncrementalGenerator
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
namespace Snap.Hutao.Resource.Localization;
|
||||
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(LocalizedEnumGenerator)}}", "1.0.0.0")]
|
||||
@@ -79,7 +81,7 @@ internal class LocalizedEnumGenerator : IIncrementalGenerator
|
||||
}
|
||||
else
|
||||
{
|
||||
return SH.ResourceManager.GetString(key);
|
||||
return SH.ResourceManager.GetString(key, CultureInfo.CurrentCulture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +104,7 @@ internal class LocalizedEnumGenerator : IIncrementalGenerator
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
return SH.ResourceManager.GetString(key);
|
||||
return SH.ResourceManager.GetString(key, CultureInfo.CurrentCulture);
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
607
src/Snap.Hutao/Snap.Hutao.SourceGeneration/Resx/ResxGenerator.cs
Normal file
607
src/Snap.Hutao/Snap.Hutao.SourceGeneration/Resx/ResxGenerator.cs
Normal file
@@ -0,0 +1,607 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
|
||||
namespace Snap.Hutao.SourceGeneration.Resx;
|
||||
|
||||
[Generator]
|
||||
public sealed class ResxGenerator : IIncrementalGenerator
|
||||
{
|
||||
private static readonly DiagnosticDescriptor InvalidResx = new("SH401", "Couldn't parse Resx file", "Couldn't parse Resx file '{0}'", "ResxGenerator", DiagnosticSeverity.Warning, true);
|
||||
private static readonly DiagnosticDescriptor InvalidPropertiesForNamespace = new("SH402", "Couldn't compute namespace", "Couldn't compute namespace for file '{0}'", "ResxGenerator", DiagnosticSeverity.Warning, true);
|
||||
private static readonly DiagnosticDescriptor InvalidPropertiesForResourceName = new("SH403", "Couldn't compute resource name", "Couldn't compute resource name for file '{0}'", "ResxGenerator", DiagnosticSeverity.Warning, true);
|
||||
private static readonly DiagnosticDescriptor InconsistentProperties = new("SH404", "Inconsistent properties", "Property '{0}' values for '{1}' are inconsistent", "ResxGenerator", DiagnosticSeverity.Warning, true);
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
IncrementalValueProvider<(string? AssemblyName, bool SupportNullableReferenceTypes)> compilationProvider = context.CompilationProvider
|
||||
.Select(static (compilation, cancellationToken) => (compilation.AssemblyName, SupportNullableReferenceTypes: compilation.GetTypeByMetadataName("System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute") is not null));
|
||||
|
||||
IncrementalValueProvider<ImmutableArray<AdditionalText>> resxProvider = context.AdditionalTextsProvider
|
||||
.Where(text => text.Path.EndsWith(".resx", StringComparison.OrdinalIgnoreCase))
|
||||
.Collect();
|
||||
|
||||
context.RegisterSourceOutput(
|
||||
source: context.AnalyzerConfigOptionsProvider.Combine(compilationProvider.Combine(resxProvider)),
|
||||
action: (ctx, source) => Execute(ctx, source.Left, source.Right.Left.AssemblyName, source.Right.Left.SupportNullableReferenceTypes, source.Right.Right));
|
||||
}
|
||||
|
||||
private static void Execute(SourceProductionContext context, AnalyzerConfigOptionsProvider options, string? assemblyName, bool supportNullableReferenceTypes, ImmutableArray<AdditionalText> files)
|
||||
{
|
||||
// Group additional file by resource kind ((a.resx, a.en.resx, a.en-us.resx), (b.resx, b.en-us.resx))
|
||||
List<IGrouping<string, AdditionalText>> resxGroups = files
|
||||
.GroupBy(file => GetResourceName(file.Path), StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(x => x.Key, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
|
||||
foreach (IGrouping<string, AdditionalText>? resxGroug in resxGroups)
|
||||
{
|
||||
string? rootNamespaceConfiguration = GetMetadataValue(context, options, "RootNamespace", resxGroug);
|
||||
string? projectDirConfiguration = GetMetadataValue(context, options, "ProjectDir", resxGroug);
|
||||
string? namespaceConfiguration = GetMetadataValue(context, options, "Namespace", "DefaultResourcesNamespace", resxGroug);
|
||||
string? resourceNameConfiguration = GetMetadataValue(context, options, "ResourceName", globalName: null, resxGroug);
|
||||
string? classNameConfiguration = GetMetadataValue(context, options, "ClassName", globalName: null, resxGroug);
|
||||
|
||||
string rootNamespace = rootNamespaceConfiguration ?? assemblyName ?? "";
|
||||
string projectDir = projectDirConfiguration ?? assemblyName ?? "";
|
||||
string? defaultResourceName = ComputeResourceName(rootNamespace, projectDir, resxGroug.Key);
|
||||
string? defaultNamespace = ComputeNamespace(rootNamespace, projectDir, resxGroug.Key);
|
||||
|
||||
string? ns = namespaceConfiguration ?? defaultNamespace;
|
||||
string? resourceName = resourceNameConfiguration ?? defaultResourceName;
|
||||
string className = classNameConfiguration ?? ToCSharpNameIdentifier(Path.GetFileName(resxGroug.Key));
|
||||
|
||||
if (ns == null)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(InvalidPropertiesForNamespace, location: null, resxGroug.First().Path));
|
||||
}
|
||||
|
||||
if (resourceName == null)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(InvalidPropertiesForResourceName, location: null, resxGroug.First().Path));
|
||||
}
|
||||
|
||||
List<ResxEntry>? entries = LoadResourceFiles(context, resxGroug);
|
||||
|
||||
string content = $"""
|
||||
// Debug info:
|
||||
// key: {resxGroug.Key}
|
||||
// files: {string.Join(", ", resxGroug.Select(f => f.Path))}
|
||||
// RootNamespace (metadata): {rootNamespaceConfiguration}
|
||||
// ProjectDir (metadata): {projectDirConfiguration}
|
||||
// Namespace / DefaultResourcesNamespace (metadata): {namespaceConfiguration}
|
||||
// ResourceName (metadata): {resourceNameConfiguration}
|
||||
// ClassName (metadata): {classNameConfiguration}
|
||||
// AssemblyName: {assemblyName}
|
||||
// RootNamespace (computed): {rootNamespace}
|
||||
// ProjectDir (computed): {projectDir}
|
||||
// defaultNamespace: {defaultNamespace}
|
||||
// defaultResourceName: {defaultResourceName}
|
||||
// Namespace: {ns}
|
||||
// ResourceName: {resourceName}
|
||||
// ClassName: {className}
|
||||
""";
|
||||
|
||||
if (resourceName != null && entries != null)
|
||||
{
|
||||
content += GenerateCode(ns, className, resourceName, entries, supportNullableReferenceTypes);
|
||||
}
|
||||
|
||||
context.AddSource($"{Path.GetFileName(resxGroug.Key)}.resx.g.cs", SourceText.From(content, Encoding.UTF8));
|
||||
}
|
||||
}
|
||||
|
||||
private static string GenerateCode(string? ns, string className, string resourceName, List<ResxEntry> entries, bool enableNullableAttributes)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("#nullable enable");
|
||||
|
||||
if (ns != null)
|
||||
{
|
||||
sb.AppendLine($$"""
|
||||
|
||||
namespace {{ns}};
|
||||
|
||||
""");
|
||||
}
|
||||
|
||||
sb.AppendLine($$"""
|
||||
internal partial class {{className}}
|
||||
{
|
||||
private static global::System.Resources.ResourceManager? resourceMan;
|
||||
|
||||
public {{className}}()
|
||||
{
|
||||
}
|
||||
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (resourceMan is null)
|
||||
{
|
||||
resourceMan = new global::System.Resources.ResourceManager("{{resourceName}}", typeof({{className}}).Assembly);
|
||||
}
|
||||
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Globalization.CultureInfo? Culture { get; set; }
|
||||
|
||||
[return:global::System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("defaultValue")]
|
||||
public static object? GetObject(global::System.Globalization.CultureInfo? culture, string name, object? defaultValue)
|
||||
{
|
||||
culture ??= Culture;
|
||||
object? obj = ResourceManager.GetObject(name, culture);
|
||||
if (obj == null)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static object? GetObject(global::System.Globalization.CultureInfo? culture, string name)
|
||||
{
|
||||
return GetObject(culture: culture, name: name, defaultValue: null);
|
||||
}
|
||||
|
||||
public static object? GetObject(string name)
|
||||
{
|
||||
return GetObject(culture: null, name: name, defaultValue: null);
|
||||
}
|
||||
|
||||
[return:global::System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("defaultValue")]
|
||||
public static object? GetObject(string name, object? defaultValue)
|
||||
{
|
||||
return GetObject(culture: null, name: name, defaultValue: defaultValue);
|
||||
}
|
||||
|
||||
public static global::System.IO.Stream? GetStream(string name)
|
||||
{
|
||||
return GetStream(culture: null, name: name);
|
||||
}
|
||||
|
||||
public static global::System.IO.Stream? GetStream(global::System.Globalization.CultureInfo? culture, string name)
|
||||
{
|
||||
culture ??= Culture;
|
||||
return ResourceManager.GetStream(name, culture);
|
||||
}
|
||||
|
||||
public static string? GetString(global::System.Globalization.CultureInfo? culture, string name)
|
||||
{
|
||||
return GetString(culture: culture, name: name, args: null);
|
||||
}
|
||||
|
||||
public static string? GetString(global::System.Globalization.CultureInfo? culture, string name, params object?[]? args)
|
||||
{
|
||||
culture ??= Culture;
|
||||
string? str = ResourceManager.GetString(name, culture);
|
||||
if (str == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (args != null)
|
||||
{
|
||||
return string.Format(culture, str, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
public static string? GetString(string name, params object?[]? args)
|
||||
{
|
||||
return GetString(culture: null, name: name, args: args);
|
||||
}
|
||||
|
||||
[return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("defaultValue")]
|
||||
public static string? GetString(string name, string? defaultValue)
|
||||
{
|
||||
return GetStringOrDefault(culture: null, name: name, defaultValue: defaultValue, args: null);
|
||||
}
|
||||
|
||||
public static string? GetString(string name)
|
||||
{
|
||||
return GetStringOrDefault(culture: null, name: name, defaultValue: null, args: null);
|
||||
}
|
||||
|
||||
[return:global::System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("defaultValue")]
|
||||
public static string? GetStringOrDefault(global::System.Globalization.CultureInfo? culture, string name, string? defaultValue)
|
||||
{
|
||||
return GetStringOrDefault(culture: culture, name: name, defaultValue: defaultValue, args: null);
|
||||
}
|
||||
|
||||
[return:global::System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("defaultValue")]
|
||||
public static string? GetStringOrDefault(global::System.Globalization.CultureInfo? culture, string name, string? defaultValue, params object?[]? args)
|
||||
{
|
||||
culture ??= Culture;
|
||||
string? str = ResourceManager.GetString(name, culture);
|
||||
if (str == null)
|
||||
{
|
||||
if (defaultValue == null || args == null)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Format(culture, defaultValue, args);
|
||||
}
|
||||
}
|
||||
|
||||
if (args != null)
|
||||
{
|
||||
return string.Format(culture, str, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
[return:global::System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("defaultValue")]
|
||||
public static string? GetStringOrDefault(string name, string? defaultValue, params object?[]? args)
|
||||
{
|
||||
return GetStringOrDefault(culture: null, name: name, defaultValue: defaultValue, args: args);
|
||||
}
|
||||
|
||||
[return:global::System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("defaultValue")]
|
||||
public static string? GetStringOrDefault(string name, string? defaultValue)
|
||||
{
|
||||
return GetStringOrDefault(culture: null, name: name, defaultValue: defaultValue, args: null);
|
||||
}
|
||||
""");
|
||||
|
||||
foreach (ResxEntry? entry in entries.OrderBy(e => e.Name, StringComparer.Ordinal))
|
||||
{
|
||||
if (string.IsNullOrEmpty(entry.Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.IsText)
|
||||
{
|
||||
XElement summary = new("summary", new XElement("para", $"Looks up a localized string for \"{entry.Name}\"."));
|
||||
if (!string.IsNullOrWhiteSpace(entry.Comment))
|
||||
{
|
||||
summary.Add(new XElement("para", entry.Comment));
|
||||
}
|
||||
|
||||
if (!entry.IsFileRef)
|
||||
{
|
||||
summary.Add(new XElement("para", $"Value: \"{entry.Value}\"."));
|
||||
}
|
||||
|
||||
string comment = summary.ToString().Replace("\r\n", "\r\n /// ", StringComparison.Ordinal);
|
||||
|
||||
sb.AppendLine($$"""
|
||||
/// {{comment}}
|
||||
public static string {{ToCSharpNameIdentifier(entry.Name!)}}
|
||||
{
|
||||
get => GetString("{{entry.Name}}")!;
|
||||
}
|
||||
|
||||
""");
|
||||
|
||||
if (entry.Value != null)
|
||||
{
|
||||
int args = Regex.Matches(entry.Value, "\\{(?<num>[0-9]+)(\\:[^}]*)?\\}", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant)
|
||||
.Cast<Match>()
|
||||
.Select(m => int.Parse(m.Groups["num"].Value, CultureInfo.InvariantCulture))
|
||||
.Distinct()
|
||||
.DefaultIfEmpty(-1)
|
||||
.Max();
|
||||
|
||||
if (args >= 0)
|
||||
{
|
||||
string inParams = string.Join(", ", Enumerable.Range(0, args + 1).Select(arg => "object? arg" + arg.ToString(CultureInfo.InvariantCulture)));
|
||||
string callParams = string.Join(", ", Enumerable.Range(0, args + 1).Select(arg => "arg" + arg.ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
sb.AppendLine($$"""
|
||||
/// {{comment}}
|
||||
public static string Format{{ToCSharpNameIdentifier(entry.Name!)}}(global::System.Globalization.CultureInfo? provider, {{inParams}})
|
||||
{
|
||||
return GetString(provider, "{{entry.Name}}", {{callParams}})!;
|
||||
}
|
||||
|
||||
/// {{comment}}
|
||||
public static string Format{{ToCSharpNameIdentifier(entry.Name!)}}({{inParams}})
|
||||
{
|
||||
return GetString("{{entry.Name}}", {{callParams}})!;
|
||||
}
|
||||
|
||||
""");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($$"""
|
||||
public static global::{{entry.FullTypeName}}? {{ToCSharpNameIdentifier(entry.Name!)}}
|
||||
{
|
||||
get => (global::{{entry.FullTypeName}}?)GetObject("{{entry.Name}}");
|
||||
}
|
||||
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine($$"""
|
||||
}
|
||||
|
||||
internal partial class {{className}}Names
|
||||
{
|
||||
""");
|
||||
|
||||
foreach (ResxEntry entry in entries)
|
||||
{
|
||||
if (string.IsNullOrEmpty(entry.Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sb.AppendLine($$"""
|
||||
public const string {{ToCSharpNameIdentifier(entry.Name!)}} = "entry.Name";
|
||||
""");
|
||||
}
|
||||
|
||||
sb.AppendLine("}");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string? ComputeResourceName(string rootNamespace, string projectDir, string resourcePath)
|
||||
{
|
||||
string fullProjectDir = EnsureEndSeparator(Path.GetFullPath(projectDir));
|
||||
string fullResourcePath = Path.GetFullPath(resourcePath);
|
||||
|
||||
if (fullProjectDir == fullResourcePath)
|
||||
{
|
||||
return rootNamespace;
|
||||
}
|
||||
|
||||
if (fullResourcePath.StartsWith(fullProjectDir, StringComparison.Ordinal))
|
||||
{
|
||||
string relativePath = fullResourcePath.Substring(fullProjectDir.Length);
|
||||
return rootNamespace + '.' + relativePath.Replace('/', '.').Replace('\\', '.');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string? ComputeNamespace(string rootNamespace, string projectDir, string resourcePath)
|
||||
{
|
||||
string fullProjectDir = EnsureEndSeparator(Path.GetFullPath(projectDir));
|
||||
string fullResourcePath = EnsureEndSeparator(Path.GetDirectoryName(Path.GetFullPath(resourcePath))!);
|
||||
|
||||
if (fullProjectDir == fullResourcePath)
|
||||
{
|
||||
return rootNamespace;
|
||||
}
|
||||
|
||||
if (fullResourcePath.StartsWith(fullProjectDir, StringComparison.Ordinal))
|
||||
{
|
||||
string relativePath = fullResourcePath.Substring(fullProjectDir.Length);
|
||||
return rootNamespace + '.' + relativePath.Replace('/', '.').Replace('\\', '.').TrimEnd('.');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<ResxEntry>? LoadResourceFiles(SourceProductionContext context, IGrouping<string, AdditionalText> resxGroug)
|
||||
{
|
||||
List<ResxEntry> entries = new();
|
||||
foreach (AdditionalText? entry in resxGroug.OrderBy(file => file.Path, StringComparer.Ordinal))
|
||||
{
|
||||
SourceText? content = entry.GetText(context.CancellationToken);
|
||||
if (content == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
XDocument document = XDocument.Parse(content.ToString());
|
||||
foreach (XElement? element in document.XPathSelectElements("/root/data"))
|
||||
{
|
||||
string? name = element.Attribute("name")?.Value;
|
||||
string? type = element.Attribute("type")?.Value;
|
||||
string? comment = element.Attribute("comment")?.Value;
|
||||
string? value = element.Element("value")?.Value;
|
||||
|
||||
ResxEntry existingEntry = entries.Find(e => e.Name == name);
|
||||
if (existingEntry != null)
|
||||
{
|
||||
existingEntry.Comment ??= comment;
|
||||
}
|
||||
else
|
||||
{
|
||||
entries.Add(new ResxEntry { Name = name, Value = value, Comment = comment, Type = type });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(InvalidResx, location: null, entry.Path));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
private static string? GetMetadataValue(SourceProductionContext context, AnalyzerConfigOptionsProvider analyzerConfigOptionsProvider, string name, IEnumerable<AdditionalText> additionalFiles)
|
||||
{
|
||||
return GetMetadataValue(context, analyzerConfigOptionsProvider, name, name, additionalFiles);
|
||||
}
|
||||
|
||||
private static string? GetMetadataValue(SourceProductionContext context, AnalyzerConfigOptionsProvider analyzerConfigOptionsProvider, string name, string? globalName, IEnumerable<AdditionalText> additionalFiles)
|
||||
{
|
||||
string? result = null;
|
||||
foreach (AdditionalText file in additionalFiles)
|
||||
{
|
||||
if (analyzerConfigOptionsProvider.GetOptions(file).TryGetValue("build_metadata.AdditionalFiles." + name, out string? value))
|
||||
{
|
||||
if (result != null && value != result)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(InconsistentProperties, location: null, name, file.Path));
|
||||
return null;
|
||||
}
|
||||
|
||||
result = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (globalName != null && analyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property." + globalName, out string? globalValue) && !string.IsNullOrEmpty(globalValue))
|
||||
{
|
||||
return globalValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string ToCSharpNameIdentifier(string name)
|
||||
{
|
||||
// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#identifiers
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.globalization.unicodecategory?view=net-5.0
|
||||
StringBuilder sb = new();
|
||||
foreach (char c in name)
|
||||
{
|
||||
UnicodeCategory category = char.GetUnicodeCategory(c);
|
||||
switch (category)
|
||||
{
|
||||
case UnicodeCategory.UppercaseLetter:
|
||||
case UnicodeCategory.LowercaseLetter:
|
||||
case UnicodeCategory.TitlecaseLetter:
|
||||
case UnicodeCategory.ModifierLetter:
|
||||
case UnicodeCategory.OtherLetter:
|
||||
case UnicodeCategory.LetterNumber:
|
||||
sb.Append(c);
|
||||
break;
|
||||
|
||||
case UnicodeCategory.DecimalDigitNumber:
|
||||
case UnicodeCategory.ConnectorPunctuation:
|
||||
case UnicodeCategory.Format:
|
||||
if (sb.Length == 0)
|
||||
{
|
||||
sb.Append('_');
|
||||
}
|
||||
sb.Append(c);
|
||||
break;
|
||||
|
||||
default:
|
||||
sb.Append('_');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string EnsureEndSeparator(string path)
|
||||
{
|
||||
if (path[path.Length - 1] == Path.DirectorySeparatorChar)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
return path + Path.DirectorySeparatorChar;
|
||||
}
|
||||
|
||||
private static string GetResourceName(string path)
|
||||
{
|
||||
string pathWithoutExtension = Path.Combine(Path.GetDirectoryName(path)!, Path.GetFileNameWithoutExtension(path));
|
||||
int indexOf = pathWithoutExtension.LastIndexOf('.');
|
||||
if (indexOf < 0)
|
||||
{
|
||||
return pathWithoutExtension;
|
||||
}
|
||||
|
||||
return Regex.IsMatch(pathWithoutExtension.Substring(indexOf + 1), "^[a-zA-Z]{2}(-[a-zA-Z]{2})?$", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant, TimeSpan.FromSeconds(1))
|
||||
? pathWithoutExtension.Substring(0, indexOf)
|
||||
: pathWithoutExtension;
|
||||
}
|
||||
|
||||
private sealed class ResxEntry
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Value { get; set; }
|
||||
public string? Comment { get; set; }
|
||||
public string? Type { get; set; }
|
||||
|
||||
public bool IsText
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Type == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Value != null)
|
||||
{
|
||||
string[] parts = Value.Split(';');
|
||||
if (parts.Length > 1)
|
||||
{
|
||||
string type = parts[1];
|
||||
if (type.StartsWith("System.String,", StringComparison.Ordinal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public string? FullTypeName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsText)
|
||||
{
|
||||
return "string";
|
||||
}
|
||||
|
||||
if (Value != null)
|
||||
{
|
||||
string[] parts = Value.Split(';');
|
||||
if (parts.Length > 1)
|
||||
{
|
||||
string type = parts[1];
|
||||
return type.Split(',')[0];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFileRef
|
||||
{
|
||||
get => Type != null && Type.StartsWith("System.Resources.ResXFileRef,", StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Snap.Hutao.SourceGeneration.Resx;
|
||||
|
||||
internal static class StringExtensions
|
||||
{
|
||||
public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
|
||||
int previousIndex = 0;
|
||||
int index = str.IndexOf(oldValue, comparison);
|
||||
while (index is not -1)
|
||||
{
|
||||
sb.Append(str, previousIndex, index - previousIndex);
|
||||
sb.Append(newValue);
|
||||
index += oldValue.Length;
|
||||
|
||||
previousIndex = index;
|
||||
index = str.IndexOf(oldValue, index, comparison);
|
||||
}
|
||||
|
||||
sb.Append(str, previousIndex, str.Length - previousIndex);
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/CsWin32/main/src/Microsoft.Windows.CsWin32/settings.schema.json",
|
||||
"allowMarshaling": true,
|
||||
"useSafeHandles": false,
|
||||
"emitSingleFile": true
|
||||
"useSafeHandles": false
|
||||
}
|
||||
@@ -1,159 +1,36 @@
|
||||
<Application
|
||||
x:Class="Snap.Hutao.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cwcont="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:cwconv="using:CommunityToolkit.WinUI.Converters"
|
||||
xmlns:cwm="using:CommunityToolkit.WinUI.Media"
|
||||
xmlns:shci="using:Snap.Hutao.Control.Image"
|
||||
xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter"
|
||||
xmlns:shvc="using:Snap.Hutao.View.Converter">
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources/>
|
||||
<ResourceDictionary Source="Control/Theme/FontStyle.xaml"/>
|
||||
<ResourceDictionary Source="Control/Theme/InfoBarOverride.xaml"/>
|
||||
<ResourceDictionary Source="Control/Theme/PageOverride.xaml"/>
|
||||
<ResourceDictionary Source="Control/Theme/PivotOverride.xaml"/>
|
||||
<ResourceDictionary Source="Control/Theme/SettingsStyle.xaml"/>
|
||||
<ResourceDictionary Source="Control/Theme/TransitionCollection.xaml"/>
|
||||
<ResourceDictionary Source="Control/Theme/WindowOverride.xaml"/>
|
||||
<ResourceDictionary Source="Control/Loading.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Loading.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Image/CachedImage.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/Card.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/Color.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/Converter.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/CornerRadius.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/FontStyle.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/Glyph.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/InfoBarOverride.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/ItemsPanelTemplate.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/NumericValue.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/PageOverride.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/PivotOverride.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/SettingsStyle.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/TransitionCollection.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/Uri.xaml"/>
|
||||
<ResourceDictionary Source="ms-appx:///Control/Theme/WindowOverride.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<Color x:Key="AvatarPropertyAddValueColor">#FF74BF00</Color>
|
||||
<Color x:Key="CompatBackgroundColor">#FFF4F4F4</Color>
|
||||
<SolidColorBrush x:Key="DarkOnlyOverlayMaskBrush" Color="#00000000"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<Color x:Key="AvatarPropertyAddValueColor">#FF90E800</Color>
|
||||
<Color x:Key="CompatBackgroundColor">#FF242424</Color>
|
||||
<SolidColorBrush x:Key="DarkOnlyOverlayMaskBrush" Color="#60000000"/>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<!-- CornerRadius -->
|
||||
<CornerRadius x:Key="ControlCornerRadiusTop">4,4,0,0</CornerRadius>
|
||||
<CornerRadius x:Key="ControlCornerRadiusBottom">0,0,4,4</CornerRadius>
|
||||
<CornerRadius x:Key="CompatCornerRadius">6</CornerRadius>
|
||||
<CornerRadius x:Key="CompatCornerRadiusTop">6,6,0,0</CornerRadius>
|
||||
<CornerRadius x:Key="CompatCornerRadiusRight">0,6,6,0</CornerRadius>
|
||||
<CornerRadius x:Key="CompatCornerRadiusBottom">0,0,6,6</CornerRadius>
|
||||
<CornerRadius x:Key="CompatCornerRadiusSmall">2</CornerRadius>
|
||||
<!-- Length -->
|
||||
<GridLength x:Key="CompatGridLength2">288</GridLength>
|
||||
<x:Double x:Key="CompatSplitViewOpenPaneLength">212</x:Double>
|
||||
<x:Double x:Key="CompatSplitViewOpenPaneLength2">304</x:Double>
|
||||
<x:Double x:Key="CompatSplitViewOpenPaneLength3">320</x:Double>
|
||||
<x:Double x:Key="HomeAdaptiveCardHeight">180</x:Double>
|
||||
<x:Double x:Key="ContentDialogMinHeight">64</x:Double>
|
||||
<x:Double x:Key="LargeAppBarButtonWidth">100</x:Double>
|
||||
<!-- ProgressBar -->
|
||||
<x:Double x:Key="LargeBackgroundProgressBarOpacity">0.2</x:Double>
|
||||
<!-- Brushes -->
|
||||
<SolidColorBrush x:Key="AvatarPropertyAddValueBrush" Color="{ThemeResource AvatarPropertyAddValueColor}"/>
|
||||
<SolidColorBrush x:Key="BlueBrush" Color="#FF5180CB"/>
|
||||
<SolidColorBrush x:Key="PurpleBrush" Color="#FFA156E0"/>
|
||||
<SolidColorBrush x:Key="OrangeBrush" Color="#FFBC6932"/>
|
||||
<SolidColorBrush x:Key="GuaranteePullBrush" Color="#FF0063FF"/>
|
||||
<SolidColorBrush x:Key="UpPullBrush" Color="#FFFFA400"/>
|
||||
|
||||
<!-- Uris -->
|
||||
<x:String x:Key="DocumentLink_MhyAccountSwitch">https://hut.ao/features/mhy-account-switch.html</x:String>
|
||||
<x:String x:Key="DocumentLink_BugReport">https://hut.ao/statements/bug-report.html</x:String>
|
||||
<x:String x:Key="DocumentLink_Translate">https://translate.hut.ao</x:String>
|
||||
<x:String x:Key="DocumentLink_Home">https://hut.ao</x:String>
|
||||
<x:String x:Key="HolographicHat_GetToken_Release">https://github.com/HolographicHat/GetToken/releases/latest</x:String>
|
||||
<x:String x:Key="Sponsor_Afadian">https://afdian.net/a/DismissedLight</x:String>
|
||||
|
||||
<!-- Images -->
|
||||
<x:String x:Key="UI_ItemIcon_None">https://static.snapgenshin.com/Bg/UI_ItemIcon_None.png</x:String>
|
||||
<x:String x:Key="UI_MarkTower">https://static.snapgenshin.com/Bg/UI_MarkTower.png</x:String>
|
||||
<x:String x:Key="UI_Icon_Intee_Explore_1">https://static.snapgenshin.com/Bg/UI_Icon_Intee_Explore_1.png</x:String>
|
||||
<x:String x:Key="UI_MarkQuest_Events_Proce">https://static.snapgenshin.com/Bg/UI_MarkQuest_Events_Proce.png</x:String>
|
||||
<x:String x:Key="UI_ItemIcon_201">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_201.png</x:String>
|
||||
<x:String x:Key="UI_ItemIcon_204">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_204.png</x:String>
|
||||
<x:String x:Key="UI_ItemIcon_210">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_210.png</x:String>
|
||||
<x:String x:Key="UI_ItemIcon_220021">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_220021.png</x:String>
|
||||
<x:String x:Key="UI_ImgSign_ItemIcon">https://static.snapgenshin.com/Bg/UI_ImgSign_ItemIcon.png</x:String>
|
||||
<x:String x:Key="UI_AvatarIcon_Costume_Card">https://static.snapgenshin.com/AvatarCard/UI_AvatarIcon_Costume_Card.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon25">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon25.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon71">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon71.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon250">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon250.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon272">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon272.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon293">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon293.png</x:String>
|
||||
|
||||
<!-- FontIcon Content -->
|
||||
<x:String x:Key="FontIconContentAdd"></x:String>
|
||||
<x:String x:Key="FontIconContentSetting"></x:String>
|
||||
<x:String x:Key="FontIconContentRefresh"></x:String>
|
||||
<x:String x:Key="FontIconContentDelete"></x:String>
|
||||
<x:String x:Key="FontIconContentFolder"></x:String>
|
||||
<x:String x:Key="FontIconContentCheckList"></x:String>
|
||||
<x:String x:Key="FontIconContentAsteriskBadge12"></x:String>
|
||||
<x:String x:Key="FontIconContentZipFolder"></x:String>
|
||||
<!-- Converters -->
|
||||
<cwconv:BoolNegationConverter x:Key="BoolNegationConverter"/>
|
||||
<cwconv:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
|
||||
<cwconv:FileSizeToFriendlyStringConverter x:Key="FileSizeToFriendlyStringConverter"/>
|
||||
<shmmc:AchievementIconConverter x:Key="AchievementIconConverter"/>
|
||||
<shmmc:AvatarCardConverter x:Key="AvatarCardConverter"/>
|
||||
<shmmc:AvatarIconConverter x:Key="AvatarIconConverter"/>
|
||||
<shmmc:AvatarNameCardPicConverter x:Key="AvatarNameCardPicConverter"/>
|
||||
<shmmc:AvatarSideIconConverter x:Key="AvatarSideIconConverter"/>
|
||||
<shmmc:DescriptionsParametersDescriptor x:Key="DescParamDescriptor"/>
|
||||
<shmmc:ElementNameIconConverter x:Key="ElementNameIconConverter"/>
|
||||
<shmmc:EmotionIconConverter x:Key="EmotionIconConverter"/>
|
||||
<shmmc:EquipIconConverter x:Key="EquipIconConverter"/>
|
||||
<shmmc:GachaAvatarImgConverter x:Key="GachaAvatarImgConverter"/>
|
||||
<shmmc:GachaAvatarIconConverter x:Key="GachaAvatarIconConverter"/>
|
||||
<shmmc:GachaEquipIconConverter x:Key="GachaEquipIconConverter"/>
|
||||
<shmmc:ItemIconConverter x:Key="ItemIconConverter"/>
|
||||
<shmmc:MonsterIconConverter x:Key="MonsterIconConverter"/>
|
||||
<shmmc:PropertiesParametersDescriptor x:Key="PropertyDescriptor"/>
|
||||
<shmmc:QualityColorConverter x:Key="QualityColorConverter"/>
|
||||
<shmmc:WeaponTypeIconConverter x:Key="WeaponTypeIconConverter"/>
|
||||
<shvc:BoolToVisibilityRevertConverter x:Key="BoolToVisibilityRevertConverter"/>
|
||||
<shvc:EmptyObjectToBoolConverter x:Key="EmptyObjectToBoolConverter"/>
|
||||
<shvc:EmptyObjectToBoolRevertConverter x:Key="EmptyObjectToBoolRevertConverter"/>
|
||||
<shvc:EmptyObjectToVisibilityConverter x:Key="EmptyObjectToVisibilityConverter"/>
|
||||
<shvc:EmptyObjectToVisibilityRevertConverter x:Key="EmptyObjectToVisibilityRevertConverter"/>
|
||||
<shvc:Int32ToVisibilityConverter x:Key="Int32ToVisibilityConverter"/>
|
||||
<shvc:Int32ToVisibilityRevertConverter x:Key="Int32ToVisibilityRevertConverter"/>
|
||||
<shvc:StringBoolConverter x:Key="StringBoolConverter"/>
|
||||
|
||||
<!-- Styles -->
|
||||
<Style
|
||||
x:Key="LargeGridViewItemStyle"
|
||||
BasedOn="{StaticResource DefaultGridViewItemStyle}"
|
||||
TargetType="GridViewItem">
|
||||
<Setter Property="Margin" Value="0,0,12,12"/>
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="SettingButtonStyle"
|
||||
BasedOn="{StaticResource DefaultButtonStyle}"
|
||||
TargetType="Button">
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}"/>
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||
<Setter Property="Padding" Value="16,6,16,6"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
</Style>
|
||||
<Style x:Key="BorderCardStyle" TargetType="Border">
|
||||
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||
</Style>
|
||||
<Style x:Key="BorderGridStyle" TargetType="Grid">
|
||||
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||
</Style>
|
||||
<Style
|
||||
x:Name="NoneSelectionListViewItemStyle"
|
||||
BasedOn="{StaticResource DefaultListViewItemStyle}"
|
||||
@@ -168,108 +45,6 @@
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="Margin" Value="0,0,2,4"/>
|
||||
</Style>
|
||||
<Style TargetType="shci:CachedImage">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Foreground" Value="{ThemeResource ApplicationForegroundThemeBrush}"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="LazyLoadingThreshold" Value="256"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="shci:CachedImage">
|
||||
<Grid
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<Image
|
||||
Name="PlaceholderImage"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
Opacity="1.0"
|
||||
Source="{TemplateBinding PlaceholderSource}"
|
||||
Stretch="{TemplateBinding PlaceholderStretch}"/>
|
||||
<Image
|
||||
Name="Image"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
NineGrid="{TemplateBinding NineGrid}"
|
||||
Opacity="0.0"
|
||||
Stretch="{TemplateBinding Stretch}"/>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Failed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Image" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderImage" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Loading">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Image" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderImage" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Loaded">
|
||||
<Storyboard>
|
||||
<DoubleAnimation
|
||||
AutoReverse="False"
|
||||
BeginTime="0"
|
||||
Storyboard.TargetName="Image"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
From="0"
|
||||
To="1"
|
||||
Duration="0:0:0.5"/>
|
||||
<DoubleAnimation
|
||||
AutoReverse="False"
|
||||
BeginTime="0"
|
||||
Storyboard.TargetName="PlaceholderImage"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
From="1"
|
||||
To="0"
|
||||
Duration="0:0:0.5"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Unloaded"/>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<!-- ItemsPanelTemplate -->
|
||||
<ItemsPanelTemplate x:Key="ItemsStackPanelTemplate">
|
||||
<ItemsStackPanel/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="WrapPanelSpacing0Template">
|
||||
<cwcont:WrapPanel/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="WrapPanelSpacing4Template">
|
||||
<cwcont:WrapPanel HorizontalSpacing="4" VerticalSpacing="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="HorizontalStackPanelTemplate">
|
||||
<StackPanel Orientation="Horizontal"/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="UniformGridColumns5Spacing4Template">
|
||||
<cwcont:UniformGrid
|
||||
ColumnSpacing="4"
|
||||
Columns="5"
|
||||
RowSpacing="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
|
||||
|
||||
<cwm:AttachedCardShadow
|
||||
x:Key="CompatCardShadow"
|
||||
Opacity="0.1"
|
||||
Offset="0,4,0"/>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Behaviors;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Snap.Hutao.Control.Behavior;
|
||||
|
||||
/// <summary>
|
||||
/// 按给定比例自动调整高度的行为
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[DependencyProperty("TargetWidth", typeof(double), 1.0D)]
|
||||
[DependencyProperty("TargetHeight", typeof(double), 1.0D)]
|
||||
internal sealed partial class AutoHeightBehavior : BehaviorBase<FrameworkElement>
|
||||
{
|
||||
private readonly SizeChangedEventHandler sizeChangedEventHandler;
|
||||
|
||||
public AutoHeightBehavior()
|
||||
{
|
||||
sizeChangedEventHandler = OnSizeChanged;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool Initialize()
|
||||
{
|
||||
UpdateElement();
|
||||
AssociatedObject.SizeChanged += sizeChangedEventHandler;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool Uninitialize()
|
||||
{
|
||||
AssociatedObject.SizeChanged -= sizeChangedEventHandler;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
UpdateElement();
|
||||
}
|
||||
|
||||
private void UpdateElement()
|
||||
{
|
||||
AssociatedObject.Height = AssociatedObject.ActualWidth * (TargetHeight / TargetWidth);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Behaviors;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Snap.Hutao.Control.Behavior;
|
||||
|
||||
/// <summary>
|
||||
/// 按给定比例自动调整高度的行为
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[DependencyProperty("TargetWidth", typeof(double), 1.0D)]
|
||||
[DependencyProperty("TargetHeight", typeof(double), 1.0D)]
|
||||
internal sealed partial class AutoWidthBehavior : BehaviorBase<FrameworkElement>
|
||||
{
|
||||
private readonly SizeChangedEventHandler sizeChangedEventHandler;
|
||||
|
||||
public AutoWidthBehavior()
|
||||
{
|
||||
sizeChangedEventHandler = OnSizeChanged;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool Initialize()
|
||||
{
|
||||
UpdateElement();
|
||||
AssociatedObject.SizeChanged += sizeChangedEventHandler;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool Uninitialize()
|
||||
{
|
||||
AssociatedObject.SizeChanged -= sizeChangedEventHandler;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
UpdateElement();
|
||||
}
|
||||
|
||||
private void UpdateElement()
|
||||
{
|
||||
AssociatedObject.Width = AssociatedObject.Height * (TargetWidth / TargetHeight);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI;
|
||||
using CommunityToolkit.WinUI.Behaviors;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
@@ -12,7 +13,7 @@ internal sealed class SelectedItemInViewBehavior : BehaviorBase<ListViewBase>
|
||||
{
|
||||
if (AssociatedObject.SelectedItem is { } item)
|
||||
{
|
||||
AssociatedObject.ScrollIntoView(item);
|
||||
AssociatedObject.SmoothScrollIntoViewWithItemAsync(item, ScrollItemPlacement.Center).SafeForget();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -10,8 +10,8 @@ internal struct ContentDialogHideToken : IDisposable, IAsyncDisposable
|
||||
private readonly ContentDialog contentDialog;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
private bool disposed = false;
|
||||
private bool disposing = false;
|
||||
private bool disposed = false;
|
||||
|
||||
public ContentDialogHideToken(ContentDialog contentDialog, ITaskContext taskContext)
|
||||
{
|
||||
|
||||
82
src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.xaml
Normal file
82
src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.xaml
Normal file
@@ -0,0 +1,82 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:shci="using:Snap.Hutao.Control.Image">
|
||||
<Style TargetType="shci:CachedImage">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Foreground" Value="{ThemeResource ApplicationForegroundThemeBrush}"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="LazyLoadingThreshold" Value="256"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="shci:CachedImage">
|
||||
<Grid
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<Image
|
||||
Name="PlaceholderImage"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
Opacity="1.0"
|
||||
Source="{TemplateBinding PlaceholderSource}"
|
||||
Stretch="{TemplateBinding PlaceholderStretch}"/>
|
||||
<Image
|
||||
Name="Image"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
NineGrid="{TemplateBinding NineGrid}"
|
||||
Opacity="0.0"
|
||||
Stretch="{TemplateBinding Stretch}"/>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Failed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Image" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderImage" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Loading">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Image" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderImage" Storyboard.TargetProperty="Opacity">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Loaded">
|
||||
<Storyboard>
|
||||
<DoubleAnimation
|
||||
AutoReverse="False"
|
||||
BeginTime="0"
|
||||
Storyboard.TargetName="Image"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
From="0"
|
||||
To="1"
|
||||
Duration="0:0:0.5"/>
|
||||
<DoubleAnimation
|
||||
AutoReverse="False"
|
||||
BeginTime="0"
|
||||
Storyboard.TargetName="PlaceholderImage"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
From="1"
|
||||
To="0"
|
||||
Duration="0:0:0.5"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Unloaded"/>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -164,11 +164,26 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co
|
||||
private async ValueTask<LoadedImageSurface> LoadImageSurfaceAsync(string file, CancellationToken token)
|
||||
{
|
||||
surfaceLoadTaskCompletionSource = new();
|
||||
LoadedImageSurface surface = LoadedImageSurface.StartLoadFromUri(file.ToUri());
|
||||
surface.LoadCompleted += loadedImageSourceLoadCompletedEventHandler;
|
||||
await surfaceLoadTaskCompletionSource.Task.ConfigureAwait(true);
|
||||
LoadImageSurfaceCompleted(surface);
|
||||
return surface;
|
||||
LoadedImageSurface? surface = default;
|
||||
try
|
||||
{
|
||||
surface = LoadedImageSurface.StartLoadFromUri(file.ToUri());
|
||||
surface.LoadCompleted += loadedImageSourceLoadCompletedEventHandler;
|
||||
if (surface.DecodedPhysicalSize.Size() <= 0D)
|
||||
{
|
||||
await surfaceLoadTaskCompletionSource.Task.ConfigureAwait(true);
|
||||
}
|
||||
|
||||
LoadImageSurfaceCompleted(surface);
|
||||
return surface;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (surface is not null)
|
||||
{
|
||||
surface.LoadCompleted -= loadedImageSourceLoadCompletedEventHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask ShowAsync(CancellationToken token)
|
||||
@@ -216,7 +231,6 @@ internal abstract partial class CompositionImage : Microsoft.UI.Xaml.Controls.Co
|
||||
private void OnLoadImageSurfaceLoadCompleted(LoadedImageSurface surface, LoadedImageSourceLoadCompletedEventArgs e)
|
||||
{
|
||||
surfaceLoadTaskCompletionSource?.TrySetResult();
|
||||
surface.LoadCompleted -= loadedImageSourceLoadCompletedEventHandler;
|
||||
}
|
||||
|
||||
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
|
||||
21
src/Snap.Hutao/Snap.Hutao/Control/Theme/Card.xaml
Normal file
21
src/Snap.Hutao/Snap.Hutao/Control/Theme/Card.xaml
Normal file
@@ -0,0 +1,21 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cwm="using:CommunityToolkit.WinUI.Media">
|
||||
<Style x:Key="BorderCardStyle" TargetType="Border">
|
||||
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||
</Style>
|
||||
<Style x:Key="GridCardStyle" TargetType="Grid">
|
||||
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||
</Style>
|
||||
<cwm:AttachedCardShadow
|
||||
x:Key="CompatCardShadow"
|
||||
Opacity="0.1"
|
||||
Offset="0,4,0"/>
|
||||
</ResourceDictionary>
|
||||
31
src/Snap.Hutao/Snap.Hutao/Control/Theme/Color.xaml
Normal file
31
src/Snap.Hutao/Snap.Hutao/Control/Theme/Color.xaml
Normal file
@@ -0,0 +1,31 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<Color x:Key="AvatarPropertyAddValueColor">#FF74BF00</Color>
|
||||
<Color x:Key="CompatBackgroundColor">#FFF4F4F4</Color>
|
||||
<Color x:Key="DarkOnlyOverlayMaskColor">#00000000</Color>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<Color x:Key="AvatarPropertyAddValueColor">#FF90E800</Color>
|
||||
<Color x:Key="CompatBackgroundColor">#FF242424</Color>
|
||||
<Color x:Key="DarkOnlyOverlayMaskColor">#60000000</Color>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<Color x:Key="BlueColor">#FF5180CB</Color>
|
||||
<Color x:Key="PurpleColor">#FFA156E0</Color>
|
||||
<Color x:Key="OrangeColor">#FFBC6932</Color>
|
||||
|
||||
<Color x:Key="GuaranteePullColor">#FF0063FF</Color>
|
||||
<Color x:Key="UpPullColor">#FFFFA400</Color>
|
||||
|
||||
<SolidColorBrush x:Key="BlueColorBrush" Color="{ThemeResource BlueColor}"/>
|
||||
<SolidColorBrush x:Key="PurpleColorBrush" Color="{ThemeResource PurpleColor}"/>
|
||||
<SolidColorBrush x:Key="OrangeColorBrush" Color="{ThemeResource OrangeColor}"/>
|
||||
|
||||
<SolidColorBrush x:Key="GuaranteePullCoolorBrush" Color="{ThemeResource GuaranteePullColor}"/>
|
||||
<SolidColorBrush x:Key="UpPullColorBrush" Color="{ThemeResource UpPullColor}"/>
|
||||
|
||||
<SolidColorBrush x:Key="DarkOnlyOverlayMaskColorBrush" Color="{ThemeResource DarkOnlyOverlayMaskColor}"/>
|
||||
<SolidColorBrush x:Key="AvatarPropertyAddValueColorBrush" Color="{ThemeResource AvatarPropertyAddValueColor}"/>
|
||||
</ResourceDictionary>
|
||||
35
src/Snap.Hutao/Snap.Hutao/Control/Theme/Converter.xaml
Normal file
35
src/Snap.Hutao/Snap.Hutao/Control/Theme/Converter.xaml
Normal file
@@ -0,0 +1,35 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cwc="using:CommunityToolkit.WinUI.Converters"
|
||||
xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter"
|
||||
xmlns:shvc="using:Snap.Hutao.View.Converter">
|
||||
<cwc:BoolNegationConverter x:Key="BoolNegationConverter"/>
|
||||
<cwc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
|
||||
<cwc:FileSizeToFriendlyStringConverter x:Key="FileSizeToFriendlyStringConverter"/>
|
||||
<shmmc:AchievementIconConverter x:Key="AchievementIconConverter"/>
|
||||
<shmmc:AvatarCardConverter x:Key="AvatarCardConverter"/>
|
||||
<shmmc:AvatarIconConverter x:Key="AvatarIconConverter"/>
|
||||
<shmmc:AvatarNameCardPicConverter x:Key="AvatarNameCardPicConverter"/>
|
||||
<shmmc:AvatarSideIconConverter x:Key="AvatarSideIconConverter"/>
|
||||
<shmmc:DescriptionsParametersDescriptor x:Key="DescParamDescriptor"/>
|
||||
<shmmc:ElementNameIconConverter x:Key="ElementNameIconConverter"/>
|
||||
<shmmc:EmotionIconConverter x:Key="EmotionIconConverter"/>
|
||||
<shmmc:EquipIconConverter x:Key="EquipIconConverter"/>
|
||||
<shmmc:GachaAvatarImgConverter x:Key="GachaAvatarImgConverter"/>
|
||||
<shmmc:GachaAvatarIconConverter x:Key="GachaAvatarIconConverter"/>
|
||||
<shmmc:GachaEquipIconConverter x:Key="GachaEquipIconConverter"/>
|
||||
<shmmc:ItemIconConverter x:Key="ItemIconConverter"/>
|
||||
<shmmc:MonsterIconConverter x:Key="MonsterIconConverter"/>
|
||||
<shmmc:PropertiesParametersDescriptor x:Key="PropertyDescriptor"/>
|
||||
<shmmc:QualityColorConverter x:Key="QualityColorConverter"/>
|
||||
<shmmc:WeaponTypeIconConverter x:Key="WeaponTypeIconConverter"/>
|
||||
<shvc:BoolToVisibilityRevertConverter x:Key="BoolToVisibilityRevertConverter"/>
|
||||
<shvc:EmptyObjectToBoolConverter x:Key="EmptyObjectToBoolConverter"/>
|
||||
<shvc:EmptyObjectToBoolRevertConverter x:Key="EmptyObjectToBoolRevertConverter"/>
|
||||
<shvc:EmptyObjectToVisibilityConverter x:Key="EmptyObjectToVisibilityConverter"/>
|
||||
<shvc:EmptyObjectToVisibilityRevertConverter x:Key="EmptyObjectToVisibilityRevertConverter"/>
|
||||
<shvc:Int32ToVisibilityConverter x:Key="Int32ToVisibilityConverter"/>
|
||||
<shvc:Int32ToVisibilityRevertConverter x:Key="Int32ToVisibilityRevertConverter"/>
|
||||
<shvc:StringBoolConverter x:Key="StringBoolConverter"/>
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,4 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<CornerRadius x:Key="ControlCornerRadiusTop">4,4,0,0</CornerRadius>
|
||||
<CornerRadius x:Key="ControlCornerRadiusBottom">0,0,4,4</CornerRadius>
|
||||
</ResourceDictionary>
|
||||
@@ -1,7 +1,7 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:shch="using:Snap.Hutao.Control.Helper">
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<FontFamily x:Key="MiSans">ms-appx:///Resource/Font/MiSans-Regular.ttf#MiSans</FontFamily>
|
||||
<FontFamily x:Key="CascadiaMonoAndMiSans">ms-appx:///Resource/Font/CascadiaMono.ttf#Cascadia Mono, ms-appx:///Resource/Font/MiSans-Regular.ttf#MiSans</FontFamily>
|
||||
|
||||
|
||||
10
src/Snap.Hutao/Snap.Hutao/Control/Theme/Glyph.xaml
Normal file
10
src/Snap.Hutao/Snap.Hutao/Control/Theme/Glyph.xaml
Normal file
@@ -0,0 +1,10 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<x:String x:Key="FontIconContentAdd"></x:String>
|
||||
<x:String x:Key="FontIconContentSetting"></x:String>
|
||||
<x:String x:Key="FontIconContentRefresh"></x:String>
|
||||
<x:String x:Key="FontIconContentDelete"></x:String>
|
||||
<x:String x:Key="FontIconContentFolder"></x:String>
|
||||
<x:String x:Key="FontIconContentCheckList"></x:String>
|
||||
<x:String x:Key="FontIconContentAsteriskBadge12"></x:String>
|
||||
<x:String x:Key="FontIconContentZipFolder"></x:String>
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,23 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cwcont="using:CommunityToolkit.WinUI.Controls">
|
||||
<ItemsPanelTemplate x:Key="ItemsStackPanelTemplate">
|
||||
<ItemsStackPanel/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="WrapPanelSpacing0Template">
|
||||
<cwcont:WrapPanel/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="WrapPanelSpacing4Template">
|
||||
<cwcont:WrapPanel HorizontalSpacing="4" VerticalSpacing="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="HorizontalStackPanelTemplate">
|
||||
<StackPanel Orientation="Horizontal"/>
|
||||
</ItemsPanelTemplate>
|
||||
<ItemsPanelTemplate x:Key="UniformGridColumns5Spacing4Template">
|
||||
<cwcont:UniformGrid
|
||||
ColumnSpacing="4"
|
||||
Columns="5"
|
||||
RowSpacing="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ResourceDictionary>
|
||||
14
src/Snap.Hutao/Snap.Hutao/Control/Theme/NumericValue.xaml
Normal file
14
src/Snap.Hutao/Snap.Hutao/Control/Theme/NumericValue.xaml
Normal file
@@ -0,0 +1,14 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<!-- Length -->
|
||||
<GridLength x:Key="CompatGridLength2">288</GridLength>
|
||||
|
||||
<x:Double x:Key="CompatSplitViewOpenPaneLength">212</x:Double>
|
||||
<x:Double x:Key="CompatSplitViewOpenPaneLength2">304</x:Double>
|
||||
<x:Double x:Key="CompatSplitViewOpenPaneLength3">320</x:Double>
|
||||
<x:Double x:Key="HomeAdaptiveCardHeight">180</x:Double>
|
||||
<x:Double x:Key="ContentDialogMinHeight">64</x:Double>
|
||||
<x:Double x:Key="LargeAppBarButtonWidth">100</x:Double>
|
||||
|
||||
<!-- ProgressBar -->
|
||||
<x:Double x:Key="LargeBackgroundProgressBarOpacity">0.2</x:Double>
|
||||
</ResourceDictionary>
|
||||
@@ -2,5 +2,5 @@
|
||||
<!-- Page Transparent Background -->
|
||||
<StaticResource x:Key="ApplicationPageBackgroundThemeBrush" ResourceKey="ControlFillColorTransparentBrush"/>
|
||||
<!-- https://github.com/microsoft/microsoft-ui-xaml/issues/4811 -->
|
||||
<x:Int32 x:Key="__Discard">0</x:Int32>
|
||||
<x:Int32 x:Key="__DiscardPageOverride">0</x:Int32>
|
||||
</ResourceDictionary>
|
||||
@@ -4,6 +4,9 @@
|
||||
<x:Double x:Key="SettingsCardMinHeight">0</x:Double>
|
||||
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
|
||||
<x:Double x:Key="SettingsCardWrapNoIconThreshold">0</x:Double>
|
||||
|
||||
<x:Double x:Key="SettingsCardContentControlMinWidth">120</x:Double>
|
||||
|
||||
<Style
|
||||
x:Key="SettingsSectionHeaderTextBlockStyle"
|
||||
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
|
||||
@@ -16,6 +19,17 @@
|
||||
x:Key="SettingsContentComboBoxStyle"
|
||||
BasedOn="{StaticResource DefaultComboBoxStyle}"
|
||||
TargetType="ComboBox">
|
||||
<Setter Property="MinWidth" Value="120"/>
|
||||
<Setter Property="MinWidth" Value="{ThemeResource SettingsCardContentControlMinWidth}"/>
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="SettingButtonStyle"
|
||||
BasedOn="{StaticResource DefaultButtonStyle}"
|
||||
TargetType="Button">
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||
<Setter Property="Padding" Value="16,6,16,6"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Setter Property="MinWidth" Value="{ThemeResource SettingsCardContentControlMinWidth}"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
|
||||
34
src/Snap.Hutao/Snap.Hutao/Control/Theme/Uri.xaml
Normal file
34
src/Snap.Hutao/Snap.Hutao/Control/Theme/Uri.xaml
Normal file
@@ -0,0 +1,34 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<!-- DocumentLink -->
|
||||
<x:String x:Key="DocumentLink_BugReport">https://hut.ao/statements/bug-report.html</x:String>
|
||||
<x:String x:Key="DocumentLink_Home">https://hut.ao</x:String>
|
||||
<x:String x:Key="DocumentLink_MhyAccountSwitch">https://hut.ao/features/mhy-account-switch.html</x:String>
|
||||
<x:String x:Key="DocumentLink_Translate">https://translate.hut.ao</x:String>
|
||||
|
||||
<!-- Other -->
|
||||
<x:String x:Key="HolographicHat_GetToken_Release">https://github.com/HolographicHat/GetToken/releases/latest</x:String>
|
||||
<x:String x:Key="Sponsor_Afadian">https://afdian.net/a/DismissedLight</x:String>
|
||||
|
||||
<!-- AvatarCard -->
|
||||
<x:String x:Key="UI_AvatarIcon_Costume_Card">https://static.snapgenshin.com/AvatarCard/UI_AvatarIcon_Costume_Card.png</x:String>
|
||||
|
||||
<!-- Bg -->
|
||||
<x:String x:Key="UI_Icon_Intee_Explore_1">https://static.snapgenshin.com/Bg/UI_Icon_Intee_Explore_1.png</x:String>
|
||||
<x:String x:Key="UI_ImgSign_ItemIcon">https://static.snapgenshin.com/Bg/UI_ImgSign_ItemIcon.png</x:String>
|
||||
<x:String x:Key="UI_ItemIcon_None">https://static.snapgenshin.com/Bg/UI_ItemIcon_None.png</x:String>
|
||||
<x:String x:Key="UI_MarkQuest_Events_Proce">https://static.snapgenshin.com/Bg/UI_MarkQuest_Events_Proce.png</x:String>
|
||||
<x:String x:Key="UI_MarkTower">https://static.snapgenshin.com/Bg/UI_MarkTower.png</x:String>
|
||||
|
||||
<!-- ItemIcon -->
|
||||
<x:String x:Key="UI_ItemIcon_201">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_201.png</x:String>
|
||||
<x:String x:Key="UI_ItemIcon_204">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_204.png</x:String>
|
||||
<x:String x:Key="UI_ItemIcon_210">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_210.png</x:String>
|
||||
<x:String x:Key="UI_ItemIcon_220021">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_220021.png</x:String>
|
||||
|
||||
<!-- EmotionIcon -->
|
||||
<x:String x:Key="UI_EmotionIcon25">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon25.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon71">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon71.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon250">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon250.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon272">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon272.png</x:String>
|
||||
<x:String x:Key="UI_EmotionIcon293">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon293.png</x:String>
|
||||
</ResourceDictionary>
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Windows.ApplicationModel.Resources;
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using Snap.Hutao.Service;
|
||||
using System.Globalization;
|
||||
|
||||
@@ -14,9 +14,7 @@ namespace Snap.Hutao.Core.ExceptionService;
|
||||
internal sealed partial class ExceptionRecorder
|
||||
{
|
||||
private readonly ILogger<ExceptionRecorder> logger;
|
||||
#if RELEASE
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 记录应用程序异常
|
||||
@@ -29,15 +27,17 @@ internal sealed partial class ExceptionRecorder
|
||||
app.DebugSettings.XamlResourceReferenceFailed += OnXamlResourceReferenceFailed;
|
||||
}
|
||||
|
||||
[SuppressMessage("", "CA2012")]
|
||||
private void OnAppUnhandledException(object? sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||
{
|
||||
#if RELEASE
|
||||
serviceProvider
|
||||
ValueTask<string?> task = serviceProvider
|
||||
.GetRequiredService<Web.Hutao.Log.HomaLogUploadClient>()
|
||||
.UploadLogAsync(e.Exception)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
#endif
|
||||
.UploadLogAsync(e.Exception);
|
||||
|
||||
if (!task.IsCompleted)
|
||||
{
|
||||
task.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
logger.LogError("未经处理的全局异常:\r\n{Detail}", ExceptionFormat.Format(e.Exception));
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ internal sealed class RuntimeEnvironmentException : Exception
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="innerException">内部错误</param>
|
||||
public RuntimeEnvironmentException(string message, Exception innerException)
|
||||
: base($"{message}\n{innerException.Message}", innerException)
|
||||
public RuntimeEnvironmentException(string message, Exception? innerException)
|
||||
: base($"{message}\n{innerException?.Message}", innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -14,27 +14,27 @@ namespace Snap.Hutao.Core.ExceptionService;
|
||||
[System.Diagnostics.StackTraceHidden]
|
||||
internal static class ThrowHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 操作取消
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="inner">内部错误</param>
|
||||
/// <returns>nothing</returns>
|
||||
/// <exception cref="OperationCanceledException">操作取消异常</exception>
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static OperationCanceledException OperationCanceled(string message, Exception? inner = default)
|
||||
public static ArgumentException Argument(string message, string? paramName)
|
||||
{
|
||||
throw new OperationCanceledException(message, inner);
|
||||
throw new ArgumentException(message, paramName);
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static DatabaseCorruptedException DatabaseCorrupted(string message, Exception? inner)
|
||||
{
|
||||
throw new DatabaseCorruptedException(message, inner);
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static GameFileOperationException GameFileOperation(string message, Exception? inner)
|
||||
{
|
||||
throw new GameFileOperationException(message, inner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 无效操作
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="inner">内部错误</param>
|
||||
/// <returns>nothing</returns>
|
||||
/// <exception cref="InvalidOperationException">无效操作异常</exception>
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static InvalidOperationException InvalidOperation(string message, Exception? inner = default)
|
||||
@@ -42,71 +42,38 @@ internal static class ThrowHelper
|
||||
throw new InvalidOperationException(message, inner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 游戏文件操作失败
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="inner">内部错误</param>
|
||||
/// <returns>nothing</returns>
|
||||
/// <exception cref="GameFileOperationException">文件操作失败</exception>
|
||||
public static GameFileOperationException GameFileOperation(string message, Exception inner)
|
||||
{
|
||||
throw new GameFileOperationException(message, inner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 包转换错误
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="inner">内部错误</param>
|
||||
/// <returns>nothing</returns>
|
||||
/// <exception cref="PackageConvertException">包转换错误异常</exception>
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static PackageConvertException PackageConvert(string message, Exception inner)
|
||||
{
|
||||
throw new PackageConvertException(message, inner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户数据损坏
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="inner">内部错误</param>
|
||||
/// <returns>nothing</returns>
|
||||
/// <exception cref="UserdataCorruptedException">数据损坏</exception>
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static UserdataCorruptedException UserdataCorrupted(string message, Exception inner)
|
||||
{
|
||||
throw new UserdataCorruptedException(message, inner);
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static DatabaseCorruptedException DatabaseCorrupted(string message, Exception inner)
|
||||
{
|
||||
throw new DatabaseCorruptedException(message, inner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行环境异常
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="inner">内部错误</param>
|
||||
/// <returns>nothing</returns>
|
||||
/// <exception cref="RuntimeEnvironmentException">环境异常</exception>
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static RuntimeEnvironmentException RuntimeEnvironment(string message, Exception inner)
|
||||
{
|
||||
throw new RuntimeEnvironmentException(message, inner);
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static NotSupportedException NotSupported()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static OperationCanceledException OperationCanceled(string message, Exception? inner = default)
|
||||
{
|
||||
throw new OperationCanceledException(message, inner);
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static PackageConvertException PackageConvert(string message, Exception? inner)
|
||||
{
|
||||
throw new PackageConvertException(message, inner);
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static RuntimeEnvironmentException RuntimeEnvironment(string message, Exception? inner)
|
||||
{
|
||||
throw new RuntimeEnvironmentException(message, inner);
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static UserdataCorruptedException UserdataCorrupted(string message, Exception? inner)
|
||||
{
|
||||
throw new UserdataCorruptedException(message, inner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Snap.Hutao.Core.LifeCycle;
|
||||
|
||||
internal static class CurrentWindowReferenceExtension
|
||||
{
|
||||
public static XamlRoot GetXamlRoot(this ICurrentWindowReference reference)
|
||||
{
|
||||
return reference.Window.Content.XamlRoot;
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,4 @@ namespace Snap.Hutao.Core.LifeCycle;
|
||||
internal interface ICurrentWindowReference
|
||||
{
|
||||
public Window Window { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,14 @@ internal sealed partial class ShellLinkInterop : IShellLinkInterop
|
||||
string target = Path.Combine(desktop, $"{SH.AppNameAndVersion.Format(runtimeOptions.Version)}.lnk");
|
||||
|
||||
IPersistFile persistFile = (IPersistFile)shellLink;
|
||||
persistFile.Save(target, false);
|
||||
try
|
||||
{
|
||||
persistFile.Save(target, false);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Dispatching;
|
||||
using System.Runtime.ExceptionServices;
|
||||
|
||||
namespace Snap.Hutao.Core.Threading;
|
||||
|
||||
@@ -18,15 +19,35 @@ internal static class DispatcherQueueExtension
|
||||
/// <param name="action">执行的回调</param>
|
||||
public static void Invoke(this DispatcherQueue dispatcherQueue, Action action)
|
||||
{
|
||||
using (ManualResetEventSlim blockEvent = new())
|
||||
if (dispatcherQueue.HasThreadAccess)
|
||||
{
|
||||
action();
|
||||
return;
|
||||
}
|
||||
|
||||
ExceptionDispatchInfo? exceptionDispatchInfo = null;
|
||||
using (ManualResetEventSlim blockEvent = new(false))
|
||||
{
|
||||
dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
action();
|
||||
blockEvent.Set();
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
blockEvent.Set();
|
||||
}
|
||||
});
|
||||
|
||||
blockEvent.Wait();
|
||||
#pragma warning disable CA1508
|
||||
exceptionDispatchInfo?.Throw();
|
||||
#pragma warning restore CA1508
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Dispatching;
|
||||
|
||||
namespace Snap.Hutao.Core.Threading;
|
||||
|
||||
internal sealed class DispatcherQueueSynchronizationContextSendSupport : SynchronizationContext
|
||||
{
|
||||
private readonly DispatcherQueue dispatcherQueue;
|
||||
|
||||
public DispatcherQueueSynchronizationContextSendSupport(DispatcherQueue dispatcherQueue)
|
||||
{
|
||||
this.dispatcherQueue = dispatcherQueue;
|
||||
}
|
||||
|
||||
public override void Post(SendOrPostCallback d, object? state)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(d);
|
||||
dispatcherQueue.TryEnqueue(() => d(state));
|
||||
}
|
||||
|
||||
public override void Send(SendOrPostCallback d, object? state)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(d);
|
||||
dispatcherQueue.Invoke(() => d(state));
|
||||
}
|
||||
|
||||
public override SynchronizationContext CreateCopy()
|
||||
{
|
||||
return new DispatcherQueueSynchronizationContextSendSupport(dispatcherQueue);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace Snap.Hutao.Core.Threading;
|
||||
[Injection(InjectAs.Singleton, typeof(ITaskContext))]
|
||||
internal sealed class TaskContext : ITaskContext
|
||||
{
|
||||
private readonly DispatcherQueueSynchronizationContext dispatcherQueueSynchronizationContext;
|
||||
private readonly DispatcherQueueSynchronizationContextSendSupport synchronizationContext;
|
||||
private readonly DispatcherQueue dispatcherQueue;
|
||||
|
||||
/// <summary>
|
||||
@@ -20,8 +20,8 @@ internal sealed class TaskContext : ITaskContext
|
||||
public TaskContext()
|
||||
{
|
||||
dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
dispatcherQueueSynchronizationContext = new(dispatcherQueue);
|
||||
SynchronizationContext.SetSynchronizationContext(dispatcherQueueSynchronizationContext);
|
||||
synchronizationContext = new(dispatcherQueue);
|
||||
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -39,18 +39,11 @@ internal sealed class TaskContext : ITaskContext
|
||||
/// <inheritdoc/>
|
||||
public void InvokeOnMainThread(Action action)
|
||||
{
|
||||
if (dispatcherQueue.HasThreadAccess)
|
||||
{
|
||||
action();
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatcherQueue.Invoke(action);
|
||||
}
|
||||
dispatcherQueue.Invoke(action);
|
||||
}
|
||||
|
||||
public IProgress<T> CreateProgressForMainThread<T>(Action<T> handler)
|
||||
{
|
||||
return new DispatcherQueueProgress<T>(handler, dispatcherQueueSynchronizationContext);
|
||||
return new DispatcherQueueProgress<T>(handler, synchronizationContext);
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ internal sealed class HotKeyController : IHotKeyController
|
||||
|
||||
public void OnHotKeyPressed(in HotKeyParameter parameter)
|
||||
{
|
||||
if (parameter is { Key: VIRTUAL_KEY.VK_F8, Modifier: 0 })
|
||||
if (parameter is { Key: VIRTUAL_KEY.VK_F8, NativeModifier: 0 })
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,11 @@ namespace Snap.Hutao.Core.Windowing.HotKey;
|
||||
|
||||
internal readonly struct HotKeyParameter
|
||||
{
|
||||
public readonly ushort Modifier;
|
||||
public readonly ushort NativeModifier;
|
||||
public readonly VIRTUAL_KEY Key;
|
||||
|
||||
public readonly HOT_KEY_MODIFIERS Modifier
|
||||
{
|
||||
get => (HOT_KEY_MODIFIERS)NativeModifier;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing;
|
||||
|
||||
internal interface IMinMaxInfoHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// 处理最大最小信息
|
||||
/// </summary>
|
||||
/// <param name="info">信息</param>
|
||||
/// <param name="scalingFactor">缩放比</param>
|
||||
unsafe void HandleMinMaxInfo(ref MINMAXINFO info, double scalingFactor);
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Snap.Hutao.Core.Windowing;
|
||||
|
||||
/// <summary>
|
||||
@@ -14,11 +12,4 @@ internal interface IWindowOptionsSource
|
||||
/// 窗体选项
|
||||
/// </summary>
|
||||
WindowOptions WindowOptions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 处理最大最小信息
|
||||
/// </summary>
|
||||
/// <param name="pInfo">信息指针</param>
|
||||
/// <param name="scalingFactor">缩放比</param>
|
||||
unsafe void ProcessMinMaxInfo(MINMAXINFO* pInfo, double scalingFactor);
|
||||
}
|
||||
@@ -39,9 +39,12 @@ internal sealed class WindowController
|
||||
|
||||
private static void TransformToCenterScreen(ref RectInt32 rect)
|
||||
{
|
||||
DisplayArea displayArea = DisplayArea.GetFromRect(rect, DisplayAreaFallback.Primary);
|
||||
DisplayArea displayArea = DisplayArea.GetFromRect(rect, DisplayAreaFallback.Nearest);
|
||||
RectInt32 workAreaRect = displayArea.WorkArea;
|
||||
|
||||
rect.Width = Math.Min(workAreaRect.Width, rect.Width);
|
||||
rect.Height = Math.Min(workAreaRect.Height, rect.Height);
|
||||
|
||||
rect.X = workAreaRect.X + ((workAreaRect.Width - rect.Width) / 2);
|
||||
rect.Y = workAreaRect.Y + ((workAreaRect.Height - rect.Height) / 2);
|
||||
}
|
||||
@@ -76,15 +79,15 @@ internal sealed class WindowController
|
||||
{
|
||||
// Set first launch size
|
||||
double scale = options.GetWindowScale();
|
||||
SizeInt32 transformedSize = options.InitSize.Scale(scale);
|
||||
RectInt32 rect = StructMarshal.RectInt32(transformedSize);
|
||||
SizeInt32 scaledSize = options.InitSize.Scale(scale);
|
||||
RectInt32 rect = StructMarshal.RectInt32(scaledSize);
|
||||
|
||||
if (options.PersistSize)
|
||||
{
|
||||
RectInt32 persistedRect = (CompactRect)LocalSetting.Get(SettingKeys.WindowRect, (CompactRect)rect);
|
||||
if (persistedRect.Size() >= options.InitSize.Size())
|
||||
{
|
||||
rect = persistedRect;
|
||||
rect = persistedRect.Scale(scale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +108,8 @@ internal sealed class WindowController
|
||||
// prevent save value when we are maximized.
|
||||
if (!windowPlacement.showCmd.HasFlag(SHOW_WINDOW_CMD.SW_SHOWMAXIMIZED))
|
||||
{
|
||||
LocalSetting.Set(SettingKeys.WindowRect, (CompactRect)window.AppWindow.GetRect());
|
||||
double scale = 1 / options.GetWindowScale();
|
||||
LocalSetting.Set(SettingKeys.WindowRect, (CompactRect)window.AppWindow.GetRect().Scale(scale));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,8 +117,10 @@ internal sealed class WindowController
|
||||
{
|
||||
if (e.PropertyName == nameof(AppOptions.BackdropType))
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(sender);
|
||||
UpdateSystemBackdrop(((AppOptions)sender).BackdropType);
|
||||
if (sender is AppOptions options)
|
||||
{
|
||||
UpdateSystemBackdrop(options.BackdropType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Snap.Hutao.Core.Windowing.HotKey;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.Shell;
|
||||
@@ -71,17 +72,16 @@ internal sealed class WindowSubclass : IDisposable
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
hotKeyController.Unregister(options.Hwnd);
|
||||
|
||||
RemoveWindowSubclass(options.Hwnd, windowProc, WindowSubclassId);
|
||||
windowProc = null;
|
||||
|
||||
if (!options.UseLegacyDragBarImplementation)
|
||||
if (options.UseLegacyDragBarImplementation)
|
||||
{
|
||||
return;
|
||||
RemoveWindowSubclass(options.Hwnd, legacyDragBarProc, DragBarSubclassId);
|
||||
legacyDragBarProc = null;
|
||||
}
|
||||
|
||||
hotKeyController.Unregister(options.Hwnd);
|
||||
RemoveWindowSubclass(options.Hwnd, legacyDragBarProc, DragBarSubclassId);
|
||||
legacyDragBarProc = null;
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SH002")]
|
||||
@@ -91,16 +91,18 @@ internal sealed class WindowSubclass : IDisposable
|
||||
{
|
||||
case WM_GETMINMAXINFO:
|
||||
{
|
||||
uint dpi = GetDpiForWindow(hwnd);
|
||||
double scalingFactor = Math.Round(dpi / 96D, 2, MidpointRounding.AwayFromZero);
|
||||
((IWindowOptionsSource)window).ProcessMinMaxInfo((MINMAXINFO*)lParam.Value, scalingFactor);
|
||||
if (window is IMinMaxInfoHandler handler)
|
||||
{
|
||||
handler.HandleMinMaxInfo(ref *(MINMAXINFO*)lParam.Value, options.GetWindowScale());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_NCRBUTTONDOWN:
|
||||
case WM_NCRBUTTONUP:
|
||||
{
|
||||
return (LRESULT)(nint)WM_NULL;
|
||||
return default;
|
||||
}
|
||||
|
||||
case WM_HOTKEY:
|
||||
|
||||
@@ -62,4 +62,9 @@ internal static class StructExtension
|
||||
{
|
||||
return sizeInt32.Width * sizeInt32.Height;
|
||||
}
|
||||
|
||||
public static double Size(this Windows.Foundation.Size size)
|
||||
{
|
||||
return size.Width * size.Height;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
ContentDialog dialog = new()
|
||||
{
|
||||
XamlRoot = currentWindowReference.Window.Content.XamlRoot,
|
||||
XamlRoot = currentWindowReference.GetXamlRoot(),
|
||||
Title = title,
|
||||
Content = content,
|
||||
DefaultButton = ContentDialogButton.Primary,
|
||||
@@ -39,7 +39,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
ContentDialog dialog = new()
|
||||
{
|
||||
XamlRoot = currentWindowReference.Window.Content.XamlRoot,
|
||||
XamlRoot = currentWindowReference.GetXamlRoot(),
|
||||
Title = title,
|
||||
Content = content,
|
||||
DefaultButton = defaultButton,
|
||||
@@ -56,7 +56,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
ContentDialog dialog = new()
|
||||
{
|
||||
XamlRoot = currentWindowReference.Window.Content.XamlRoot,
|
||||
XamlRoot = currentWindowReference.GetXamlRoot(),
|
||||
Title = title,
|
||||
Content = new ProgressBar() { IsIndeterminate = true },
|
||||
};
|
||||
@@ -68,12 +68,16 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory
|
||||
where TContentDialog : ContentDialog
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
return serviceProvider.CreateInstance<TContentDialog>(parameters);
|
||||
TContentDialog contentDialog = serviceProvider.CreateInstance<TContentDialog>(parameters);
|
||||
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
|
||||
return contentDialog;
|
||||
}
|
||||
|
||||
public TContentDialog CreateInstance<TContentDialog>(params object[] parameters)
|
||||
where TContentDialog : ContentDialog
|
||||
{
|
||||
return serviceProvider.CreateInstance<TContentDialog>(parameters);
|
||||
TContentDialog contentDialog = serviceProvider.CreateInstance<TContentDialog>(parameters);
|
||||
contentDialog.XamlRoot = currentWindowReference.GetXamlRoot();
|
||||
return contentDialog;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
using Snap.Hutao.Core.Windowing;
|
||||
using Snap.Hutao.Factory.Abstraction;
|
||||
using Windows.Storage.Pickers;
|
||||
using WinRT.Interop;
|
||||
@@ -16,7 +18,7 @@ internal sealed partial class PickerFactory : IPickerFactory
|
||||
{
|
||||
private const string AnyType = "*";
|
||||
|
||||
private readonly MainWindow mainWindow;
|
||||
private readonly ICurrentWindowReference currentWindow;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public FileOpenPicker GetFileOpenPicker(PickerLocationId location, string commitButton, params string[] fileTypes)
|
||||
@@ -78,7 +80,10 @@ internal sealed partial class PickerFactory : IPickerFactory
|
||||
{
|
||||
// Create a folder picker.
|
||||
T picker = new();
|
||||
InitializeWithWindow.Initialize(picker, mainWindow.WindowOptions.Hwnd);
|
||||
if (currentWindow.Window is IWindowOptionsSource optionsSource)
|
||||
{
|
||||
InitializeWithWindow.Initialize(picker, optionsSource.WindowOptions.Hwnd);
|
||||
}
|
||||
|
||||
return picker;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Snap.Hutao;
|
||||
/// 指引窗口
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed partial class GuideWindow : Window, IWindowOptionsSource
|
||||
internal sealed partial class GuideWindow : Window, IWindowOptionsSource, IMinMaxInfoHandler
|
||||
{
|
||||
private const int MinWidth = 1000;
|
||||
private const int MinHeight = 600;
|
||||
@@ -30,11 +30,11 @@ internal sealed partial class GuideWindow : Window, IWindowOptionsSource
|
||||
|
||||
WindowOptions IWindowOptionsSource.WindowOptions { get => windowOptions; }
|
||||
|
||||
public unsafe void ProcessMinMaxInfo(MINMAXINFO* pInfo, double scalingFactor)
|
||||
public unsafe void HandleMinMaxInfo(ref MINMAXINFO info, double scalingFactor)
|
||||
{
|
||||
pInfo->ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, pInfo->ptMinTrackSize.X);
|
||||
pInfo->ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, pInfo->ptMinTrackSize.Y);
|
||||
pInfo->ptMaxTrackSize.X = (int)Math.Min(MaxWidth * scalingFactor, pInfo->ptMaxTrackSize.X);
|
||||
pInfo->ptMaxTrackSize.Y = (int)Math.Min(MaxHeight * scalingFactor, pInfo->ptMaxTrackSize.Y);
|
||||
info.ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, info.ptMinTrackSize.X);
|
||||
info.ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, info.ptMinTrackSize.Y);
|
||||
info.ptMaxTrackSize.X = (int)Math.Min(MaxWidth * scalingFactor, info.ptMaxTrackSize.X);
|
||||
info.ptMaxTrackSize.Y = (int)Math.Min(MaxHeight * scalingFactor, info.ptMaxTrackSize.Y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Snap.Hutao;
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed partial class LaunchGameWindow : Window, IDisposable, IWindowOptionsSource
|
||||
internal sealed partial class LaunchGameWindow : Window, IDisposable, IWindowOptionsSource, IMinMaxInfoHandler
|
||||
{
|
||||
private const int MinWidth = 240;
|
||||
private const int MinHeight = 240;
|
||||
@@ -48,11 +48,11 @@ internal sealed partial class LaunchGameWindow : Window, IDisposable, IWindowOpt
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public unsafe void ProcessMinMaxInfo(MINMAXINFO* pInfo, double scalingFactor)
|
||||
public unsafe void HandleMinMaxInfo(ref MINMAXINFO info, double scalingFactor)
|
||||
{
|
||||
pInfo->ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, pInfo->ptMinTrackSize.X);
|
||||
pInfo->ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, pInfo->ptMinTrackSize.Y);
|
||||
pInfo->ptMaxTrackSize.X = (int)Math.Min(MaxWidth * scalingFactor, pInfo->ptMaxTrackSize.X);
|
||||
pInfo->ptMaxTrackSize.Y = (int)Math.Min(MaxHeight * scalingFactor, pInfo->ptMaxTrackSize.Y);
|
||||
info.ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, info.ptMinTrackSize.X);
|
||||
info.ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, info.ptMinTrackSize.Y);
|
||||
info.ptMaxTrackSize.X = (int)Math.Min(MaxWidth * scalingFactor, info.ptMaxTrackSize.X);
|
||||
info.ptMaxTrackSize.Y = (int)Math.Min(MaxHeight * scalingFactor, info.ptMaxTrackSize.Y);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Snap.Hutao;
|
||||
[HighQuality]
|
||||
[Injection(InjectAs.Singleton)]
|
||||
[SuppressMessage("", "CA1001")]
|
||||
internal sealed partial class MainWindow : Window, IWindowOptionsSource
|
||||
internal sealed partial class MainWindow : Window, IWindowOptionsSource, IMinMaxInfoHandler
|
||||
{
|
||||
private const int MinWidth = 848;
|
||||
private const int MinHeight = 524;
|
||||
@@ -46,10 +46,10 @@ internal sealed partial class MainWindow : Window, IWindowOptionsSource
|
||||
public WindowOptions WindowOptions { get => windowOptions; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public unsafe void ProcessMinMaxInfo(MINMAXINFO* pInfo, double scalingFactor)
|
||||
public unsafe void HandleMinMaxInfo(ref MINMAXINFO pInfo, double scalingFactor)
|
||||
{
|
||||
pInfo->ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, pInfo->ptMinTrackSize.X);
|
||||
pInfo->ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, pInfo->ptMinTrackSize.Y);
|
||||
pInfo.ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, pInfo.ptMinTrackSize.X);
|
||||
pInfo.ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, pInfo.ptMinTrackSize.Y);
|
||||
}
|
||||
|
||||
private void OnClosed(object sender, WindowEventArgs args)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Model.InterChange.GachaLog;
|
||||
|
||||
/// <summary>
|
||||
@@ -49,20 +51,35 @@ internal sealed class UIGF
|
||||
/// <summary>
|
||||
/// 列表物品是否正常
|
||||
/// </summary>
|
||||
/// <param name="itemId">首个出错的Id</param>
|
||||
/// <param name="id">首个出错的Id</param>
|
||||
/// <returns>是否正常</returns>
|
||||
public bool IsMajor2Minor2OrLowerListValid([NotNullWhen(false)] out long itemId)
|
||||
public bool IsMajor2Minor2OrLowerListValid([NotNullWhen(false)] out long id)
|
||||
{
|
||||
foreach (UIGFItem item in List)
|
||||
foreach (ref readonly UIGFItem item in CollectionsMarshal.AsSpan(List))
|
||||
{
|
||||
if (item.ItemType != SH.ModelInterchangeUIGFItemTypeAvatar && item.ItemType != SH.ModelInterchangeUIGFItemTypeWeapon)
|
||||
{
|
||||
itemId = item.Id;
|
||||
id = item.Id;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
itemId = 0;
|
||||
id = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsMajor2Minor3OrHigherListValid([NotNullWhen(false)] out long id)
|
||||
{
|
||||
foreach (ref readonly UIGFItem item in CollectionsMarshal.AsSpan(List))
|
||||
{
|
||||
if (string.IsNullOrEmpty(item.ItemId))
|
||||
{
|
||||
id = item.Id;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
id = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -91,6 +91,8 @@ internal static class AvatarIds
|
||||
public static readonly AvatarId Freminet = 10000085;
|
||||
public static readonly AvatarId Wriothesley = 10000086;
|
||||
public static readonly AvatarId Neuvillette = 10000087;
|
||||
public static readonly AvatarId Charlotte = 10000088;
|
||||
public static readonly AvatarId Furina = 10000089;
|
||||
|
||||
/// <summary>
|
||||
/// 检查该角色是否为主角
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="60568DGPStudio.SnapHutao"
|
||||
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
||||
Version="1.7.7.0" />
|
||||
Version="1.7.14.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Snap Hutao</DisplayName>
|
||||
|
||||
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_Icon_Gift.png
Normal file
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Icon/UI_Icon_Gift.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 913 B |
File diff suppressed because it is too large
Load Diff
@@ -506,6 +506,78 @@
|
||||
<data name="MustSelectUserAndUid" xml:space="preserve">
|
||||
<value>You must select a user and a role first</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInsufficientRecordSlot" xml:space="preserve">
|
||||
<value>Reached max allowed number of wish history archives on Snap Hutao Cloud</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInsufficientTime" xml:space="preserve">
|
||||
<value>No valid wish history backup service privilege</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInvalidGachaLogData" xml:space="preserve">
|
||||
<value>Wish history data contains invalid item, unable to upload to Snap Hutao Cloud</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceServerDatabaseError" xml:space="preserve">
|
||||
<value>Found abnormal data, unable to upload to Snap Hutao Cloud. Please do not upload across accounts or you can attempt to delete cloud data and try again.</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceEmailHasNotRegistered" xml:space="preserve">
|
||||
<value>Current email adress is not registered</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceEmailHasRegistered" xml:space="preserve">
|
||||
<value>Current emaill address is registered</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceInternalException" xml:space="preserve">
|
||||
<value>Register failed, server error, please contact developer to fix it</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceUnregisterFailed" xml:space="preserve">
|
||||
<value>User does not exist, failed to delete account</value>
|
||||
</data>
|
||||
<data name="ServerPassportUserInfoNotExist" xml:space="preserve">
|
||||
<value>User does not exist, failed to fetch user's data</value>
|
||||
</data>
|
||||
<data name="ServerPassportUsernameOrPassportIncorrect" xml:space="preserve">
|
||||
<value>Wrong username or password</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyFailed" xml:space="preserve">
|
||||
<value>Verification failed</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyRequestSuccess" xml:space="preserve">
|
||||
<value>The verification code has been sent to your e-mail.</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyTooFrequent" xml:space="preserve">
|
||||
<value>Validation request is too frequent. Please try again in 1 minute.</value>
|
||||
</data>
|
||||
<data name="ServerRecordBannedUid" xml:space="preserve">
|
||||
<value>Failed to upload Sprial Abyss record, current UID is banned by Hutao Database</value>
|
||||
</data>
|
||||
<data name="ServerRecordComputingStatistics" xml:space="preserve">
|
||||
<value>Failed to upload Sprial Abyss record, server is calculating statistical data</value>
|
||||
</data>
|
||||
<data name="ServerRecordComputingStatistics2" xml:space="preserve">
|
||||
<value>Failed to fetch data, server is calculating statistical data</value>
|
||||
</data>
|
||||
<data name="ServerRecordInternalException" xml:space="preserve">
|
||||
<value>Failed to upload Sprial Abyss record, server error, please contact developer to fix it</value>
|
||||
</data>
|
||||
<data name="ServerRecordInvalidData" xml:space="preserve">
|
||||
<value>Failed to upload Sprial Abyss record, invalid data detected</value>
|
||||
</data>
|
||||
<data name="ServerRecordInvalidUid" xml:space="preserve">
|
||||
<value>Invalid UID</value>
|
||||
</data>
|
||||
<data name="ServerRecordNotCurrentSchedule" xml:space="preserve">
|
||||
<value>Failed to upload Spiral Abyss record. It is not data for the current schedule.</value>
|
||||
</data>
|
||||
<data name="ServerRecordPreviousRequestNotCompleted" xml:space="preserve">
|
||||
<value>Failed to upload Sprial Abyss record. The record for the current Uid is still being processed. Please do not repeat the operation.</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessAndGachaLogServiceTimeExtended" xml:space="preserve">
|
||||
<value>Uploaded Spiral Abyss record successfully. Received a privilege extension for Snap Hutao Cloud service.</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessButNoPassport" xml:space="preserve">
|
||||
<value>Uploaded abyss record successfully. No Snap Hutao Cloud privilege received as no Snap Hutao account logged in.</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessButNotFirstTimeAtCurrentSchedule" xml:space="preserve">
|
||||
<value>Uploaded abyss record successfully. No Snap Hutao Cloud privilege received as there is not first upload of current schedule.</value>
|
||||
</data>
|
||||
<data name="ServiceAchievementImportResultFormat" xml:space="preserve">
|
||||
<value>New: {0} Achievements | Updated: {1} Achievements | Delete: {2} Achievements</value>
|
||||
</data>
|
||||
@@ -959,6 +1031,9 @@
|
||||
<data name="ViewControlStatisticsSegmentedItemContentStatistics" xml:space="preserve">
|
||||
<value>Statistics</value>
|
||||
</data>
|
||||
<data name="ViewControlWebViewerCoreWebView2ProfileQueryInterfaceFailed" xml:space="preserve">
|
||||
<value>The current version of WebView2 does not support management configuration, continue to use may cause abnormalities, please upgrade as soon as possible</value>
|
||||
</data>
|
||||
<data name="ViewCultivationHeader" xml:space="preserve">
|
||||
<value>Dev Plan</value>
|
||||
</data>
|
||||
@@ -1040,6 +1115,12 @@
|
||||
<data name="ViewDialogDailyNoteNotificationTransformerNotify" xml:space="preserve">
|
||||
<value>Parametric Transformer Notification</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteWebhookUrlInputPlaceholder" xml:space="preserve">
|
||||
<value>Input URL</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
|
||||
<value>Realtime Note Webhook URL</value>
|
||||
</data>
|
||||
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
|
||||
<value>Import wish history</value>
|
||||
</data>
|
||||
@@ -1085,6 +1166,18 @@
|
||||
<data name="ViewDialogGeetestCustomUrlTitle" xml:space="preserve">
|
||||
<value>Configure Geetest CAPTCHA Verficaition API</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportLoginTitle" xml:space="preserve">
|
||||
<value>Login to Snap Hutao Passport</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportRegisterTitle" xml:space="preserve">
|
||||
<value>Signup Snap Hutao Passport</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportResetPasswordTitle" xml:space="preserve">
|
||||
<value>Reset Password of Snap Hutao Passport</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportUnregisterTitle" xml:space="preserve">
|
||||
<value>Delete Snap Hutao Passport</value>
|
||||
</data>
|
||||
<data name="ViewDialogImportExportApp" xml:space="preserve">
|
||||
<value>Export App</value>
|
||||
</data>
|
||||
@@ -1268,6 +1361,9 @@
|
||||
<data name="ViewModelCultivationProjectInvalidName" xml:space="preserve">
|
||||
<value>Can't add plan with invalid name</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteConfigWebhookUrlComplete" xml:space="preserve">
|
||||
<value>Realtime Note Webhook URL successfully configured</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
|
||||
<value>HoYoLab account does not support Realtime Notes verification</value>
|
||||
</data>
|
||||
@@ -1407,11 +1503,14 @@
|
||||
<value>Failed to create desktop shortcut</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingGeetestCustomUrlSucceed" xml:space="preserve">
|
||||
<value>无感验证复合 Url 配置成功</value>
|
||||
<value>CAPTCHA Verification composite URL successfully configured</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingSetDataFolderSuccess" xml:space="preserve">
|
||||
<value>Set data directory successfully. Restart to apply changes.</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
|
||||
<value>Failed to save game path</value>
|
||||
</data>
|
||||
<data name="ViewModelUserAdded" xml:space="preserve">
|
||||
<value>User [{0}] added successfully</value>
|
||||
</data>
|
||||
@@ -1589,6 +1688,18 @@
|
||||
<data name="ViewPageDailyNoteAddEntryToolTip" xml:space="preserve">
|
||||
<value>Add</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteAttendanceStatusInfo" xml:space="preserve">
|
||||
<value>Encounter Points Status</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteConfigWebhookDescription" xml:space="preserve">
|
||||
<value>Push data to specific webhook after refreshing Realtime Note</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteConfigWebhookHeader" xml:space="preserve">
|
||||
<value>Config Webhook</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteDataInteropHeader" xml:space="preserve">
|
||||
<value>Data Interoperability</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteNotificationHeader" xml:space="preserve">
|
||||
<value>Notification</value>
|
||||
</data>
|
||||
@@ -1829,6 +1940,9 @@
|
||||
<data name="ViewPageHutaoPassportResetPasswordHeader" xml:space="preserve">
|
||||
<value>Reset Password</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportResetPasswordHint" xml:space="preserve">
|
||||
<value>Delete Snap Hutao Passport will cause your data to lose without any recovery option</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportUserNameHint" xml:space="preserve">
|
||||
<value>Enter your email</value>
|
||||
</data>
|
||||
@@ -1877,12 +1991,21 @@
|
||||
<data name="ViewPageLaunchGameAppearanceScreenWidthHeader" xml:space="preserve">
|
||||
<value>Width</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameArgumentsDescription" xml:space="preserve">
|
||||
<value>Modify its default behavior at game startup</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameArgumentsHeader" xml:space="preserve">
|
||||
<value>Start-up Arguments</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
|
||||
<value>General</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameConfigurationSaveHint" xml:space="preserve">
|
||||
<value>All options will be saved only after the game is launched successfully.</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameFileHeader" xml:space="preserve">
|
||||
<value>File</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||
<value>Run the software on the selected display</value>
|
||||
</data>
|
||||
@@ -1898,6 +2021,12 @@
|
||||
<data name="ViewPageLaunchGameOptionsHeader" xml:space="preserve">
|
||||
<value>Game Options</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameProcessHeader" xml:space="preserve">
|
||||
<value>Progress</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameRegistryHeader" xml:space="preserve">
|
||||
<value>Registry</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceDiffHeader" xml:space="preserve">
|
||||
<value>OTA Package</value>
|
||||
</data>
|
||||
@@ -1938,7 +2067,7 @@
|
||||
<value>Server</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameSwitchSchemeWarning" xml:space="preserve">
|
||||
<value>版本更新前需要提前转换至与启动器匹配的服务器</value>
|
||||
<value>You need to convert to a server that matches the launcher before updating the version</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameUnlockFpsDescription" xml:space="preserve">
|
||||
<value>Please turn off V-Sync in the game settings. You may need a high-performance graphic card to support a high frame rate limit.</value>
|
||||
@@ -2096,8 +2225,50 @@
|
||||
<data name="ViewpageSettingHomeHeader" xml:space="preserve">
|
||||
<value>Home</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportDangerZoneDescription" xml:space="preserve">
|
||||
<value>Proceed with caution</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportDangerZoneHeader" xml:space="preserve">
|
||||
<value>Danger Zone</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportGachaLogExpiredAtHeader" xml:space="preserve">
|
||||
<value>Snap Hutao Cloud Expiring in</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportHeader" xml:space="preserve">
|
||||
<value>Snap Hutao Account</value>
|
||||
<value>Snap Hutao Passport</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLicensedDeveloperDescription" xml:space="preserve">
|
||||
<value>You are unlimited in any Snap Hutao Cloud features</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLicensedDeveloperHeader" xml:space="preserve">
|
||||
<value>Certificated Developer</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLoginAction" xml:space="preserve">
|
||||
<value>Sign in</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLogoutAction" xml:space="preserve">
|
||||
<value>Sign out</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportMaintainerDescription" xml:space="preserve">
|
||||
<value>You are unlimited in any testing feature</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportMaintainerHeader" xml:space="preserve">
|
||||
<value>Snap Hutao developer and maintainer</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRedeemCodeDescription" xml:space="preserve">
|
||||
<value>We sometimes give away Snap Hutao Cloud redemption codes to some users</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRedeemCodeHeader" xml:space="preserve">
|
||||
<value>Use Redemption Code</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRegisterAction" xml:space="preserve">
|
||||
<value>Register</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportResetPasswordAction" xml:space="preserve">
|
||||
<value>Change Password</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportUnregisterAction" xml:space="preserve">
|
||||
<value>Delete Account</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingIsAdvancedLaunchOptionsEnabledDescription" xml:space="preserve">
|
||||
<value>After a full reading of the Genshin Impact and Snap Hutao user agreements, I choose to enable「Game Launcher - Advanced Features」.</value>
|
||||
@@ -2414,6 +2585,27 @@
|
||||
<data name="WebAnnouncementTimeHoursEndFormat" xml:space="preserve">
|
||||
<value>End in {0} hours</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
|
||||
<value>Copied to clipboard</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusFinishedNonReward" xml:space="preserve">
|
||||
<value>Finished</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusForbid" xml:space="preserve">
|
||||
<value>Forbid to Claim</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusInvalid" xml:space="preserve">
|
||||
<value>Invalid</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusTakenAward" xml:space="preserve">
|
||||
<value>Claimed</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusUnfinished" xml:space="preserve">
|
||||
<value>Unfinished</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusWaitTaken" xml:space="preserve">
|
||||
<value>Ready to claim</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteExpeditionRemainHoursFormat" xml:space="preserve">
|
||||
<value>{0} hrs</value>
|
||||
</data>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -506,6 +506,78 @@
|
||||
<data name="MustSelectUserAndUid" xml:space="preserve">
|
||||
<value>유저와 UID를 선택하세요</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInsufficientRecordSlot" xml:space="preserve">
|
||||
<value>胡桃云保存的祈愿记录存档数已达当前账号上限</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInsufficientTime" xml:space="preserve">
|
||||
<value>未开通祈愿记录上传服务或已到期</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInvalidGachaLogData" xml:space="preserve">
|
||||
<value>祈愿数据存在无效的物品,无法保存至胡桃云</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceServerDatabaseError" xml:space="preserve">
|
||||
<value>数据异常,无法保存至云端,请勿跨账号上传或尝试删除云端数据后重试</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceEmailHasNotRegistered" xml:space="preserve">
|
||||
<value>当前邮箱尚未注册</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceEmailHasRegistered" xml:space="preserve">
|
||||
<value>当前邮箱已被注册</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceInternalException" xml:space="preserve">
|
||||
<value>注册失败,服务器异常,请尽快联系开发者解决</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceUnregisterFailed" xml:space="preserve">
|
||||
<value>用户不存在,注销失败</value>
|
||||
</data>
|
||||
<data name="ServerPassportUserInfoNotExist" xml:space="preserve">
|
||||
<value>用户不存在,获取用户信息失败</value>
|
||||
</data>
|
||||
<data name="ServerPassportUsernameOrPassportIncorrect" xml:space="preserve">
|
||||
<value>用户名或密码错误</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyFailed" xml:space="preserve">
|
||||
<value>验证失败</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyRequestSuccess" xml:space="preserve">
|
||||
<value>验证码已发送至邮箱</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyTooFrequent" xml:space="preserve">
|
||||
<value>验证请求过快,请 1 分钟后再试</value>
|
||||
</data>
|
||||
<data name="ServerRecordBannedUid" xml:space="preserve">
|
||||
<value>上传深渊记录失败,当前 Uid 已被胡桃数据库封禁</value>
|
||||
</data>
|
||||
<data name="ServerRecordComputingStatistics" xml:space="preserve">
|
||||
<value>上传深渊记录失败,正在计算统计数据</value>
|
||||
</data>
|
||||
<data name="ServerRecordComputingStatistics2" xml:space="preserve">
|
||||
<value>获取数据失败,正在计算统计数据</value>
|
||||
</data>
|
||||
<data name="ServerRecordInternalException" xml:space="preserve">
|
||||
<value>上传深渊记录失败,服务器异常,请尽快联系开发者解决</value>
|
||||
</data>
|
||||
<data name="ServerRecordInvalidData" xml:space="preserve">
|
||||
<value>上传深渊记录失败,存在无效的数据</value>
|
||||
</data>
|
||||
<data name="ServerRecordInvalidUid" xml:space="preserve">
|
||||
<value>无效的 Uid</value>
|
||||
</data>
|
||||
<data name="ServerRecordNotCurrentSchedule" xml:space="preserve">
|
||||
<value>上传深渊记录失败,不是本期数据</value>
|
||||
</data>
|
||||
<data name="ServerRecordPreviousRequestNotCompleted" xml:space="preserve">
|
||||
<value>上传深渊记录失败,当前 Uid 的记录仍在处理中,请勿重复操作</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessAndGachaLogServiceTimeExtended" xml:space="preserve">
|
||||
<value>上传深渊记录成功,获赠祈愿记录上传服务时长</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessButNoPassport" xml:space="preserve">
|
||||
<value>上传深渊记录成功,但未登录胡桃账号,无法获赠祈愿记录上传服务时长</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessButNotFirstTimeAtCurrentSchedule" xml:space="preserve">
|
||||
<value>上传深渊记录成功,但不是本期首次提交,无法获赠祈愿记录上传服务时长</value>
|
||||
</data>
|
||||
<data name="ServiceAchievementImportResultFormat" xml:space="preserve">
|
||||
<value>新增:{0} 个成就 | 更新:{1} 个成就 | 删除:{2} 个成就</value>
|
||||
</data>
|
||||
@@ -959,6 +1031,9 @@
|
||||
<data name="ViewControlStatisticsSegmentedItemContentStatistics" xml:space="preserve">
|
||||
<value>统计</value>
|
||||
</data>
|
||||
<data name="ViewControlWebViewerCoreWebView2ProfileQueryInterfaceFailed" xml:space="preserve">
|
||||
<value>当前 WebView2 版本不支持管理配置,继续使用可能会导致异常,请尽快升级</value>
|
||||
</data>
|
||||
<data name="ViewCultivationHeader" xml:space="preserve">
|
||||
<value>육성 계획</value>
|
||||
</data>
|
||||
@@ -1040,6 +1115,12 @@
|
||||
<data name="ViewDialogDailyNoteNotificationTransformerNotify" xml:space="preserve">
|
||||
<value>매개 변수 변환기 알림</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteWebhookUrlInputPlaceholder" xml:space="preserve">
|
||||
<value>请输入 Url</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
|
||||
<value>实时便笺 Webhook Url</value>
|
||||
</data>
|
||||
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
|
||||
<value>기원 기록 가져오기</value>
|
||||
</data>
|
||||
@@ -1085,6 +1166,18 @@
|
||||
<data name="ViewDialogGeetestCustomUrlTitle" xml:space="preserve">
|
||||
<value>配置无感验证接口</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportLoginTitle" xml:space="preserve">
|
||||
<value>登录胡桃通行证</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportRegisterTitle" xml:space="preserve">
|
||||
<value>注册胡桃通行证</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportResetPasswordTitle" xml:space="preserve">
|
||||
<value>重置胡桃通行证密码</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportUnregisterTitle" xml:space="preserve">
|
||||
<value>注销胡桃通行证账号</value>
|
||||
</data>
|
||||
<data name="ViewDialogImportExportApp" xml:space="preserve">
|
||||
<value>앱 내보내기</value>
|
||||
</data>
|
||||
@@ -1268,6 +1361,9 @@
|
||||
<data name="ViewModelCultivationProjectInvalidName" xml:space="preserve">
|
||||
<value>잘못된 이름을 가진 일정은 추가할 수 없습니다</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteConfigWebhookUrlComplete" xml:space="preserve">
|
||||
<value>实时便笺 Webhook Url 配置成功</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
|
||||
<value>HoYoLab 계정은 실시간 메모 확인 기능을 지원하지 않습니다</value>
|
||||
</data>
|
||||
@@ -1412,6 +1508,9 @@
|
||||
<data name="ViewModelSettingSetDataFolderSuccess" xml:space="preserve">
|
||||
<value>데이터 경로를 설정했습니다. 변경 사항을 적용하기 위해 재시작합니다</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
|
||||
<value>保存游戏路径失败</value>
|
||||
</data>
|
||||
<data name="ViewModelUserAdded" xml:space="preserve">
|
||||
<value>사용자 [{0}]가 정상적으로 추가되었습니다</value>
|
||||
</data>
|
||||
@@ -1589,6 +1688,18 @@
|
||||
<data name="ViewPageDailyNoteAddEntryToolTip" xml:space="preserve">
|
||||
<value>추가</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteAttendanceStatusInfo" xml:space="preserve">
|
||||
<value>历练点获取详情</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteConfigWebhookDescription" xml:space="preserve">
|
||||
<value>在实时便笺刷新后推送到指定的 Webhook</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteConfigWebhookHeader" xml:space="preserve">
|
||||
<value>配置 Webhook</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteDataInteropHeader" xml:space="preserve">
|
||||
<value>数据互操作</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteNotificationHeader" xml:space="preserve">
|
||||
<value>알림</value>
|
||||
</data>
|
||||
@@ -1829,6 +1940,9 @@
|
||||
<data name="ViewPageHutaoPassportResetPasswordHeader" xml:space="preserve">
|
||||
<value>비밀번호 재설정</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportResetPasswordHint" xml:space="preserve">
|
||||
<value>注销账号的数据将永远丢失,无法恢复</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportUserNameHint" xml:space="preserve">
|
||||
<value>이메일을 입력하세요</value>
|
||||
</data>
|
||||
@@ -1877,12 +1991,21 @@
|
||||
<data name="ViewPageLaunchGameAppearanceScreenWidthHeader" xml:space="preserve">
|
||||
<value>너비</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameArgumentsDescription" xml:space="preserve">
|
||||
<value>在游戏启动时修改其默认行为</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameArgumentsHeader" xml:space="preserve">
|
||||
<value>启动参数</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
|
||||
<value>보통</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameConfigurationSaveHint" xml:space="preserve">
|
||||
<value>모든 설정은 게임을 성공적으로 실행한 후에 저장됩니다</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameFileHeader" xml:space="preserve">
|
||||
<value>文件</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||
<value>지정한 모니터에서 실행</value>
|
||||
</data>
|
||||
@@ -1898,6 +2021,12 @@
|
||||
<data name="ViewPageLaunchGameOptionsHeader" xml:space="preserve">
|
||||
<value>게임 설정</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameProcessHeader" xml:space="preserve">
|
||||
<value>进程</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameRegistryHeader" xml:space="preserve">
|
||||
<value>注册表</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceDiffHeader" xml:space="preserve">
|
||||
<value>증분 패키지</value>
|
||||
</data>
|
||||
@@ -2096,8 +2225,50 @@
|
||||
<data name="ViewpageSettingHomeHeader" xml:space="preserve">
|
||||
<value>主页</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportDangerZoneDescription" xml:space="preserve">
|
||||
<value>三思而后行</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportDangerZoneHeader" xml:space="preserve">
|
||||
<value>危险操作</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportGachaLogExpiredAtHeader" xml:space="preserve">
|
||||
<value>胡桃云服务到期时间</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportHeader" xml:space="preserve">
|
||||
<value>호두 계정</value>
|
||||
<value>胡桃通行证账号</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLicensedDeveloperDescription" xml:space="preserve">
|
||||
<value>您可以无限制使用任何基于胡桃云服务的功能</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLicensedDeveloperHeader" xml:space="preserve">
|
||||
<value>已认证的合作开发者</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLoginAction" xml:space="preserve">
|
||||
<value>登录</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLogoutAction" xml:space="preserve">
|
||||
<value>退出登录</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportMaintainerDescription" xml:space="preserve">
|
||||
<value>您可以无限制的使用任何测试功能</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportMaintainerHeader" xml:space="preserve">
|
||||
<value>胡桃开发/运维</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRedeemCodeDescription" xml:space="preserve">
|
||||
<value>我们有时会向某些用户赠送胡桃云兑换码</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRedeemCodeHeader" xml:space="preserve">
|
||||
<value>使用兑换码</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRegisterAction" xml:space="preserve">
|
||||
<value>注册</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportResetPasswordAction" xml:space="preserve">
|
||||
<value>修改密码</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportUnregisterAction" xml:space="preserve">
|
||||
<value>注销账号</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingIsAdvancedLaunchOptionsEnabledDescription" xml:space="preserve">
|
||||
<value>원신과 호두의 사용자 계약을 완전히 읽은 후, 「게임 고급 기능」을 사용</value>
|
||||
@@ -2414,6 +2585,27 @@
|
||||
<data name="WebAnnouncementTimeHoursEndFormat" xml:space="preserve">
|
||||
<value>{0}시간 후 종료</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
|
||||
<value>已复制到剪贴板</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusFinishedNonReward" xml:space="preserve">
|
||||
<value>已完成</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusForbid" xml:space="preserve">
|
||||
<value>禁止领取</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusInvalid" xml:space="preserve">
|
||||
<value>无效</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusTakenAward" xml:space="preserve">
|
||||
<value>已领取</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusUnfinished" xml:space="preserve">
|
||||
<value>尚未完成</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusWaitTaken" xml:space="preserve">
|
||||
<value>等待领取</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteExpeditionRemainHoursFormat" xml:space="preserve">
|
||||
<value>{0}시간</value>
|
||||
</data>
|
||||
|
||||
@@ -506,6 +506,78 @@
|
||||
<data name="MustSelectUserAndUid" xml:space="preserve">
|
||||
<value>必须先选择一个用户与角色</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInsufficientRecordSlot" xml:space="preserve">
|
||||
<value>胡桃云保存的祈愿记录存档数已达当前账号上限</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInsufficientTime" xml:space="preserve">
|
||||
<value>未开通祈愿记录上传服务或已到期</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInvalidGachaLogData" xml:space="preserve">
|
||||
<value>祈愿数据存在无效的物品,无法保存至胡桃云</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceServerDatabaseError" xml:space="preserve">
|
||||
<value>数据异常,无法保存至云端,请勿跨账号上传或尝试删除云端数据后重试</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceEmailHasNotRegistered" xml:space="preserve">
|
||||
<value>当前邮箱尚未注册</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceEmailHasRegistered" xml:space="preserve">
|
||||
<value>当前邮箱已被注册</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceInternalException" xml:space="preserve">
|
||||
<value>注册失败,服务器异常,请尽快联系开发者解决</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceUnregisterFailed" xml:space="preserve">
|
||||
<value>用户不存在,注销失败</value>
|
||||
</data>
|
||||
<data name="ServerPassportUserInfoNotExist" xml:space="preserve">
|
||||
<value>用户不存在,获取用户信息失败</value>
|
||||
</data>
|
||||
<data name="ServerPassportUsernameOrPassportIncorrect" xml:space="preserve">
|
||||
<value>用户名或密码错误</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyFailed" xml:space="preserve">
|
||||
<value>验证失败</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyRequestSuccess" xml:space="preserve">
|
||||
<value>验证码已发送至邮箱</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyTooFrequent" xml:space="preserve">
|
||||
<value>验证请求过快,请 1 分钟后再试</value>
|
||||
</data>
|
||||
<data name="ServerRecordBannedUid" xml:space="preserve">
|
||||
<value>上传深渊记录失败,当前 Uid 已被胡桃数据库封禁</value>
|
||||
</data>
|
||||
<data name="ServerRecordComputingStatistics" xml:space="preserve">
|
||||
<value>上传深渊记录失败,正在计算统计数据</value>
|
||||
</data>
|
||||
<data name="ServerRecordComputingStatistics2" xml:space="preserve">
|
||||
<value>获取数据失败,正在计算统计数据</value>
|
||||
</data>
|
||||
<data name="ServerRecordInternalException" xml:space="preserve">
|
||||
<value>上传深渊记录失败,服务器异常,请尽快联系开发者解决</value>
|
||||
</data>
|
||||
<data name="ServerRecordInvalidData" xml:space="preserve">
|
||||
<value>上传深渊记录失败,存在无效的数据</value>
|
||||
</data>
|
||||
<data name="ServerRecordInvalidUid" xml:space="preserve">
|
||||
<value>无效的 Uid</value>
|
||||
</data>
|
||||
<data name="ServerRecordNotCurrentSchedule" xml:space="preserve">
|
||||
<value>上传深渊记录失败,不是本期数据</value>
|
||||
</data>
|
||||
<data name="ServerRecordPreviousRequestNotCompleted" xml:space="preserve">
|
||||
<value>上传深渊记录失败,当前 Uid 的记录仍在处理中,请勿重复操作</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessAndGachaLogServiceTimeExtended" xml:space="preserve">
|
||||
<value>上传深渊记录成功,获赠祈愿记录上传服务时长</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessButNoPassport" xml:space="preserve">
|
||||
<value>上传深渊记录成功,但未登录胡桃账号,无法获赠祈愿记录上传服务时长</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessButNotFirstTimeAtCurrentSchedule" xml:space="preserve">
|
||||
<value>上传深渊记录成功,但不是本期首次提交,无法获赠祈愿记录上传服务时长</value>
|
||||
</data>
|
||||
<data name="ServiceAchievementImportResultFormat" xml:space="preserve">
|
||||
<value>新增:{0} 个成就 | 更新:{1} 个成就 | 删除:{2} 个成就</value>
|
||||
</data>
|
||||
@@ -959,6 +1031,9 @@
|
||||
<data name="ViewControlStatisticsSegmentedItemContentStatistics" xml:space="preserve">
|
||||
<value>统计</value>
|
||||
</data>
|
||||
<data name="ViewControlWebViewerCoreWebView2ProfileQueryInterfaceFailed" xml:space="preserve">
|
||||
<value>当前 WebView2 版本不支持管理配置,继续使用可能会导致异常,请尽快升级</value>
|
||||
</data>
|
||||
<data name="ViewCultivationHeader" xml:space="preserve">
|
||||
<value>养成计划</value>
|
||||
</data>
|
||||
@@ -1040,6 +1115,12 @@
|
||||
<data name="ViewDialogDailyNoteNotificationTransformerNotify" xml:space="preserve">
|
||||
<value>参量质变仪提醒</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteWebhookUrlInputPlaceholder" xml:space="preserve">
|
||||
<value>请输入 Url</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
|
||||
<value>实时便笺 Webhook Url</value>
|
||||
</data>
|
||||
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
|
||||
<value>导入祈愿记录</value>
|
||||
</data>
|
||||
@@ -1085,6 +1166,18 @@
|
||||
<data name="ViewDialogGeetestCustomUrlTitle" xml:space="preserve">
|
||||
<value>配置无感验证接口</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportLoginTitle" xml:space="preserve">
|
||||
<value>登录胡桃通行证</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportRegisterTitle" xml:space="preserve">
|
||||
<value>注册胡桃通行证</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportResetPasswordTitle" xml:space="preserve">
|
||||
<value>重置胡桃通行证密码</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportUnregisterTitle" xml:space="preserve">
|
||||
<value>注销胡桃通行证账号</value>
|
||||
</data>
|
||||
<data name="ViewDialogImportExportApp" xml:space="preserve">
|
||||
<value>导出 App</value>
|
||||
</data>
|
||||
@@ -1268,6 +1361,9 @@
|
||||
<data name="ViewModelCultivationProjectInvalidName" xml:space="preserve">
|
||||
<value>不能添加名称无效的计划</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteConfigWebhookUrlComplete" xml:space="preserve">
|
||||
<value>实时便笺 Webhook Url 配置成功</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
|
||||
<value>HoYoLab 账号不支持验证实时便笺</value>
|
||||
</data>
|
||||
@@ -1412,6 +1508,9 @@
|
||||
<data name="ViewModelSettingSetDataFolderSuccess" xml:space="preserve">
|
||||
<value>设置数据目录成功,重启以应用更改</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
|
||||
<value>保存游戏路径失败</value>
|
||||
</data>
|
||||
<data name="ViewModelUserAdded" xml:space="preserve">
|
||||
<value>用户 [{0}] 添加成功</value>
|
||||
</data>
|
||||
@@ -1589,6 +1688,18 @@
|
||||
<data name="ViewPageDailyNoteAddEntryToolTip" xml:space="preserve">
|
||||
<value>添加</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteAttendanceStatusInfo" xml:space="preserve">
|
||||
<value>历练点获取详情</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteConfigWebhookDescription" xml:space="preserve">
|
||||
<value>在实时便笺刷新后推送到指定的 Webhook</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteConfigWebhookHeader" xml:space="preserve">
|
||||
<value>配置 Webhook</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteDataInteropHeader" xml:space="preserve">
|
||||
<value>数据互操作</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteNotificationHeader" xml:space="preserve">
|
||||
<value>通知</value>
|
||||
</data>
|
||||
@@ -1829,6 +1940,9 @@
|
||||
<data name="ViewPageHutaoPassportResetPasswordHeader" xml:space="preserve">
|
||||
<value>重置密码</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportResetPasswordHint" xml:space="preserve">
|
||||
<value>注销账号的数据将永远丢失,无法恢复</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportUserNameHint" xml:space="preserve">
|
||||
<value>请输入邮箱</value>
|
||||
</data>
|
||||
@@ -1877,12 +1991,21 @@
|
||||
<data name="ViewPageLaunchGameAppearanceScreenWidthHeader" xml:space="preserve">
|
||||
<value>宽度</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameArgumentsDescription" xml:space="preserve">
|
||||
<value>在游戏启动时修改其默认行为</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameArgumentsHeader" xml:space="preserve">
|
||||
<value>启动参数</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
|
||||
<value>常规</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameConfigurationSaveHint" xml:space="preserve">
|
||||
<value>所有选项仅会在启动游戏成功后保存</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameFileHeader" xml:space="preserve">
|
||||
<value>文件</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||
<value>在指定的显示器上运行</value>
|
||||
</data>
|
||||
@@ -1898,6 +2021,12 @@
|
||||
<data name="ViewPageLaunchGameOptionsHeader" xml:space="preserve">
|
||||
<value>游戏选项</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameProcessHeader" xml:space="preserve">
|
||||
<value>进程</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameRegistryHeader" xml:space="preserve">
|
||||
<value>注册表</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceDiffHeader" xml:space="preserve">
|
||||
<value>增量包</value>
|
||||
</data>
|
||||
@@ -2096,8 +2225,50 @@
|
||||
<data name="ViewpageSettingHomeHeader" xml:space="preserve">
|
||||
<value>主页</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportDangerZoneDescription" xml:space="preserve">
|
||||
<value>三思而后行</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportDangerZoneHeader" xml:space="preserve">
|
||||
<value>危险操作</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportGachaLogExpiredAtHeader" xml:space="preserve">
|
||||
<value>胡桃云服务到期时间</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportHeader" xml:space="preserve">
|
||||
<value>胡桃账号</value>
|
||||
<value>胡桃通行证账号</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLicensedDeveloperDescription" xml:space="preserve">
|
||||
<value>您可以无限制使用任何基于胡桃云服务的功能</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLicensedDeveloperHeader" xml:space="preserve">
|
||||
<value>已认证的合作开发者</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLoginAction" xml:space="preserve">
|
||||
<value>登录</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLogoutAction" xml:space="preserve">
|
||||
<value>退出登录</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportMaintainerDescription" xml:space="preserve">
|
||||
<value>您可以无限制的使用任何测试功能</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportMaintainerHeader" xml:space="preserve">
|
||||
<value>胡桃开发/运维</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRedeemCodeDescription" xml:space="preserve">
|
||||
<value>我们有时会向某些用户赠送胡桃云兑换码</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRedeemCodeHeader" xml:space="preserve">
|
||||
<value>使用兑换码</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRegisterAction" xml:space="preserve">
|
||||
<value>注册</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportResetPasswordAction" xml:space="preserve">
|
||||
<value>修改密码</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportUnregisterAction" xml:space="preserve">
|
||||
<value>注销账号</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingIsAdvancedLaunchOptionsEnabledDescription" xml:space="preserve">
|
||||
<value>在完整阅读原神和胡桃工具箱用户协议后,我选择启用「启动游戏-高级功能」</value>
|
||||
@@ -2414,6 +2585,27 @@
|
||||
<data name="WebAnnouncementTimeHoursEndFormat" xml:space="preserve">
|
||||
<value>{0} 小时后结束</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
|
||||
<value>已复制到剪贴板</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusFinishedNonReward" xml:space="preserve">
|
||||
<value>已完成</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusForbid" xml:space="preserve">
|
||||
<value>禁止领取</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusInvalid" xml:space="preserve">
|
||||
<value>无效</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusTakenAward" xml:space="preserve">
|
||||
<value>已领取</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusUnfinished" xml:space="preserve">
|
||||
<value>尚未完成</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusWaitTaken" xml:space="preserve">
|
||||
<value>等待领取</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteExpeditionRemainHoursFormat" xml:space="preserve">
|
||||
<value>{0} 时</value>
|
||||
</data>
|
||||
|
||||
@@ -506,6 +506,78 @@
|
||||
<data name="MustSelectUserAndUid" xml:space="preserve">
|
||||
<value>必須先選擇一個用戶與角色</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInsufficientRecordSlot" xml:space="preserve">
|
||||
<value>胡桃云保存的祈愿记录存档数已达当前账号上限</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInsufficientTime" xml:space="preserve">
|
||||
<value>未开通祈愿记录上传服务或已到期</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceInvalidGachaLogData" xml:space="preserve">
|
||||
<value>祈愿数据存在无效的物品,无法保存至胡桃云</value>
|
||||
</data>
|
||||
<data name="ServerGachaLogServiceServerDatabaseError" xml:space="preserve">
|
||||
<value>数据异常,无法保存至云端,请勿跨账号上传或尝试删除云端数据后重试</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceEmailHasNotRegistered" xml:space="preserve">
|
||||
<value>当前邮箱尚未注册</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceEmailHasRegistered" xml:space="preserve">
|
||||
<value>当前邮箱已被注册</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceInternalException" xml:space="preserve">
|
||||
<value>注册失败,服务器异常,请尽快联系开发者解决</value>
|
||||
</data>
|
||||
<data name="ServerPassportServiceUnregisterFailed" xml:space="preserve">
|
||||
<value>用户不存在,注销失败</value>
|
||||
</data>
|
||||
<data name="ServerPassportUserInfoNotExist" xml:space="preserve">
|
||||
<value>用户不存在,获取用户信息失败</value>
|
||||
</data>
|
||||
<data name="ServerPassportUsernameOrPassportIncorrect" xml:space="preserve">
|
||||
<value>用户名或密码错误</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyFailed" xml:space="preserve">
|
||||
<value>验证失败</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyRequestSuccess" xml:space="preserve">
|
||||
<value>验证码已发送至邮箱</value>
|
||||
</data>
|
||||
<data name="ServerPassportVerifyTooFrequent" xml:space="preserve">
|
||||
<value>验证请求过快,请 1 分钟后再试</value>
|
||||
</data>
|
||||
<data name="ServerRecordBannedUid" xml:space="preserve">
|
||||
<value>上传深渊记录失败,当前 Uid 已被胡桃数据库封禁</value>
|
||||
</data>
|
||||
<data name="ServerRecordComputingStatistics" xml:space="preserve">
|
||||
<value>上传深渊记录失败,正在计算统计数据</value>
|
||||
</data>
|
||||
<data name="ServerRecordComputingStatistics2" xml:space="preserve">
|
||||
<value>获取数据失败,正在计算统计数据</value>
|
||||
</data>
|
||||
<data name="ServerRecordInternalException" xml:space="preserve">
|
||||
<value>上传深渊记录失败,服务器异常,请尽快联系开发者解决</value>
|
||||
</data>
|
||||
<data name="ServerRecordInvalidData" xml:space="preserve">
|
||||
<value>上传深渊记录失败,存在无效的数据</value>
|
||||
</data>
|
||||
<data name="ServerRecordInvalidUid" xml:space="preserve">
|
||||
<value>无效的 Uid</value>
|
||||
</data>
|
||||
<data name="ServerRecordNotCurrentSchedule" xml:space="preserve">
|
||||
<value>上传深渊记录失败,不是本期数据</value>
|
||||
</data>
|
||||
<data name="ServerRecordPreviousRequestNotCompleted" xml:space="preserve">
|
||||
<value>上传深渊记录失败,当前 Uid 的记录仍在处理中,请勿重复操作</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessAndGachaLogServiceTimeExtended" xml:space="preserve">
|
||||
<value>上传深渊记录成功,获赠祈愿记录上传服务时长</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessButNoPassport" xml:space="preserve">
|
||||
<value>上传深渊记录成功,但未登录胡桃账号,无法获赠祈愿记录上传服务时长</value>
|
||||
</data>
|
||||
<data name="ServerRecordUploadSuccessButNotFirstTimeAtCurrentSchedule" xml:space="preserve">
|
||||
<value>上传深渊记录成功,但不是本期首次提交,无法获赠祈愿记录上传服务时长</value>
|
||||
</data>
|
||||
<data name="ServiceAchievementImportResultFormat" xml:space="preserve">
|
||||
<value>新增:{0} 個成就 | 更新:{1} 個成就 | 删除:{2} 個成就</value>
|
||||
</data>
|
||||
@@ -959,6 +1031,9 @@
|
||||
<data name="ViewControlStatisticsSegmentedItemContentStatistics" xml:space="preserve">
|
||||
<value>統計</value>
|
||||
</data>
|
||||
<data name="ViewControlWebViewerCoreWebView2ProfileQueryInterfaceFailed" xml:space="preserve">
|
||||
<value>当前 WebView2 版本不支持管理配置,继续使用可能会导致异常,请尽快升级</value>
|
||||
</data>
|
||||
<data name="ViewCultivationHeader" xml:space="preserve">
|
||||
<value>養成計劃</value>
|
||||
</data>
|
||||
@@ -1040,6 +1115,12 @@
|
||||
<data name="ViewDialogDailyNoteNotificationTransformerNotify" xml:space="preserve">
|
||||
<value>參數質變儀提醒</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteWebhookUrlInputPlaceholder" xml:space="preserve">
|
||||
<value>请输入 Url</value>
|
||||
</data>
|
||||
<data name="ViewDialogDailyNoteWebhookUrlTitle" xml:space="preserve">
|
||||
<value>实时便笺 Webhook Url</value>
|
||||
</data>
|
||||
<data name="ViewDialogGachaLogImportTitle" xml:space="preserve">
|
||||
<value>匯入祈願記錄</value>
|
||||
</data>
|
||||
@@ -1085,6 +1166,18 @@
|
||||
<data name="ViewDialogGeetestCustomUrlTitle" xml:space="preserve">
|
||||
<value>配置無感驗證接口</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportLoginTitle" xml:space="preserve">
|
||||
<value>登录胡桃通行证</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportRegisterTitle" xml:space="preserve">
|
||||
<value>注册胡桃通行证</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportResetPasswordTitle" xml:space="preserve">
|
||||
<value>重置胡桃通行证密码</value>
|
||||
</data>
|
||||
<data name="ViewDialogHutaoPassportUnregisterTitle" xml:space="preserve">
|
||||
<value>注销胡桃通行证账号</value>
|
||||
</data>
|
||||
<data name="ViewDialogImportExportApp" xml:space="preserve">
|
||||
<value>匯出 App</value>
|
||||
</data>
|
||||
@@ -1268,6 +1361,9 @@
|
||||
<data name="ViewModelCultivationProjectInvalidName" xml:space="preserve">
|
||||
<value>不能新增名稱無效的計劃</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteConfigWebhookUrlComplete" xml:space="preserve">
|
||||
<value>实时便笺 Webhook Url 配置成功</value>
|
||||
</data>
|
||||
<data name="ViewModelDailyNoteHoyolabVerificationUnsupported" xml:space="preserve">
|
||||
<value>HoYoLAB 賬號不支持驗證實时便箋</value>
|
||||
</data>
|
||||
@@ -1412,6 +1508,9 @@
|
||||
<data name="ViewModelSettingSetDataFolderSuccess" xml:space="preserve">
|
||||
<value>設置數據目錄成功,重啓以應用更改</value>
|
||||
</data>
|
||||
<data name="ViewModelSettingSetGamePathDatabaseFailedTitle" xml:space="preserve">
|
||||
<value>保存游戏路径失败</value>
|
||||
</data>
|
||||
<data name="ViewModelUserAdded" xml:space="preserve">
|
||||
<value>用戶 [{0}] 新增成功</value>
|
||||
</data>
|
||||
@@ -1589,6 +1688,18 @@
|
||||
<data name="ViewPageDailyNoteAddEntryToolTip" xml:space="preserve">
|
||||
<value>新增</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteAttendanceStatusInfo" xml:space="preserve">
|
||||
<value>历练点获取详情</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteConfigWebhookDescription" xml:space="preserve">
|
||||
<value>在实时便笺刷新后推送到指定的 Webhook</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteConfigWebhookHeader" xml:space="preserve">
|
||||
<value>配置 Webhook</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteDataInteropHeader" xml:space="preserve">
|
||||
<value>数据互操作</value>
|
||||
</data>
|
||||
<data name="ViewPageDailyNoteNotificationHeader" xml:space="preserve">
|
||||
<value>通知</value>
|
||||
</data>
|
||||
@@ -1829,6 +1940,9 @@
|
||||
<data name="ViewPageHutaoPassportResetPasswordHeader" xml:space="preserve">
|
||||
<value>重設密碼</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportResetPasswordHint" xml:space="preserve">
|
||||
<value>注销账号的数据将永远丢失,无法恢复</value>
|
||||
</data>
|
||||
<data name="ViewPageHutaoPassportUserNameHint" xml:space="preserve">
|
||||
<value>請輸入電郵地址</value>
|
||||
</data>
|
||||
@@ -1877,12 +1991,21 @@
|
||||
<data name="ViewPageLaunchGameAppearanceScreenWidthHeader" xml:space="preserve">
|
||||
<value>寬度</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameArgumentsDescription" xml:space="preserve">
|
||||
<value>在游戏启动时修改其默认行为</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameArgumentsHeader" xml:space="preserve">
|
||||
<value>启动参数</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameCommonHeader" xml:space="preserve">
|
||||
<value>一般</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameConfigurationSaveHint" xml:space="preserve">
|
||||
<value>所有選項盡會在啓動游戲成功後保存</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameFileHeader" xml:space="preserve">
|
||||
<value>文件</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameMonitorsDescription" xml:space="preserve">
|
||||
<value>在指定的屏幕上運行</value>
|
||||
</data>
|
||||
@@ -1898,6 +2021,12 @@
|
||||
<data name="ViewPageLaunchGameOptionsHeader" xml:space="preserve">
|
||||
<value>遊戲選項</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameProcessHeader" xml:space="preserve">
|
||||
<value>进程</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameRegistryHeader" xml:space="preserve">
|
||||
<value>注册表</value>
|
||||
</data>
|
||||
<data name="ViewPageLaunchGameResourceDiffHeader" xml:space="preserve">
|
||||
<value>增量包</value>
|
||||
</data>
|
||||
@@ -2096,8 +2225,50 @@
|
||||
<data name="ViewpageSettingHomeHeader" xml:space="preserve">
|
||||
<value>主頁</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportDangerZoneDescription" xml:space="preserve">
|
||||
<value>三思而后行</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportDangerZoneHeader" xml:space="preserve">
|
||||
<value>危险操作</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportGachaLogExpiredAtHeader" xml:space="preserve">
|
||||
<value>胡桃云服务到期时间</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportHeader" xml:space="preserve">
|
||||
<value>Snap Hutao 賬號</value>
|
||||
<value>胡桃通行证账号</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLicensedDeveloperDescription" xml:space="preserve">
|
||||
<value>您可以无限制使用任何基于胡桃云服务的功能</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLicensedDeveloperHeader" xml:space="preserve">
|
||||
<value>已认证的合作开发者</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLoginAction" xml:space="preserve">
|
||||
<value>登录</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportLogoutAction" xml:space="preserve">
|
||||
<value>退出登录</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportMaintainerDescription" xml:space="preserve">
|
||||
<value>您可以无限制的使用任何测试功能</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportMaintainerHeader" xml:space="preserve">
|
||||
<value>胡桃开发/运维</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRedeemCodeDescription" xml:space="preserve">
|
||||
<value>我们有时会向某些用户赠送胡桃云兑换码</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRedeemCodeHeader" xml:space="preserve">
|
||||
<value>使用兑换码</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportRegisterAction" xml:space="preserve">
|
||||
<value>注册</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportResetPasswordAction" xml:space="preserve">
|
||||
<value>修改密码</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingHutaoPassportUnregisterAction" xml:space="preserve">
|
||||
<value>注销账号</value>
|
||||
</data>
|
||||
<data name="ViewPageSettingIsAdvancedLaunchOptionsEnabledDescription" xml:space="preserve">
|
||||
<value>在完整閱讀原神和胡桃工具箱使用者協定後,我選擇啟用「啟動遊戲 - 高級功能」</value>
|
||||
@@ -2414,6 +2585,27 @@
|
||||
<data name="WebAnnouncementTimeHoursEndFormat" xml:space="preserve">
|
||||
<value>{0} 小時後結束</value>
|
||||
</data>
|
||||
<data name="WebBridgeShareCopyToClipboardSuccess" xml:space="preserve">
|
||||
<value>已复制到剪贴板</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusFinishedNonReward" xml:space="preserve">
|
||||
<value>已完成</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusForbid" xml:space="preserve">
|
||||
<value>禁止领取</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusInvalid" xml:space="preserve">
|
||||
<value>无效</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusTakenAward" xml:space="preserve">
|
||||
<value>已领取</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusUnfinished" xml:space="preserve">
|
||||
<value>尚未完成</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteAttendanceRewardStatusWaitTaken" xml:space="preserve">
|
||||
<value>等待领取</value>
|
||||
</data>
|
||||
<data name="WebDailyNoteExpeditionRemainHoursFormat" xml:space="preserve">
|
||||
<value>{0} 時</value>
|
||||
</data>
|
||||
|
||||
@@ -153,7 +153,7 @@ internal abstract partial class DbStoreOptions : ObservableObject, IOptions<DbSt
|
||||
/// <param name="key">键</param>
|
||||
/// <param name="value">值</param>
|
||||
/// <param name="propertyName">属性名称</param>
|
||||
protected void SetOption(ref string? storage, string key, string value, [CallerMemberName] string? propertyName = null)
|
||||
protected void SetOption(ref string? storage, string key, string? value, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
if (!SetProperty(ref storage, value, propertyName))
|
||||
{
|
||||
|
||||
@@ -105,8 +105,12 @@ internal static class SummaryHelper
|
||||
/// <returns>分数</returns>
|
||||
public static float GetPercentSubAffixScore(in ReliquarySubAffixId appendId)
|
||||
{
|
||||
// 圣遗物相同类型副词条强化档位一共为 4 档
|
||||
// 恰好为 70% 80% 90% 100%
|
||||
// 圣遗物相同类型副词条强化档位一共为 4/3/2 档
|
||||
// 五星 为 70% 80% 90% 100%
|
||||
// 四星 为 70% 80% 90% 100%
|
||||
// 三星 为 70% 80% 90% 100%
|
||||
// 二星 为 70% 85% 100%
|
||||
// 二星 为 80% 100%
|
||||
// 通过计算与最大属性的 Id 差来决定当前副词条的强化档位
|
||||
uint maxId = GetAffixMaxId(appendId);
|
||||
uint delta = maxId - appendId;
|
||||
@@ -119,7 +123,11 @@ internal static class SummaryHelper
|
||||
(5 or 4 or 3, 3) => 70F,
|
||||
|
||||
(2, 0) => 100F,
|
||||
(2, 1) => 80F,
|
||||
(2, 1) => 85F,
|
||||
(2, 2) => 70F,
|
||||
|
||||
(1, 0) => 100F,
|
||||
(1, 1) => 80F,
|
||||
|
||||
_ => throw Must.NeverHappen($"Unexpected AppendId: {appendId.Value} Delta: {delta}"),
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ internal sealed partial class DailyNoteOptions : DbStoreOptions
|
||||
private NameValue<int>? selectedRefreshTime;
|
||||
private bool? isReminderNotification;
|
||||
private bool? isSilentWhenPlayingGame;
|
||||
private string? webhookUrl;
|
||||
|
||||
/// <summary>
|
||||
/// 刷新时间
|
||||
@@ -76,7 +77,7 @@ internal sealed partial class DailyNoteOptions : DbStoreOptions
|
||||
{
|
||||
if (runtimeOptions.IsElevated)
|
||||
{
|
||||
// leave below untouched if we are running in elevated privilege
|
||||
// leave untouched when we are running in elevated privilege
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -87,7 +88,7 @@ internal sealed partial class DailyNoteOptions : DbStoreOptions
|
||||
{
|
||||
if (runtimeOptions.IsElevated)
|
||||
{
|
||||
// leave below untouched if we are running in elevated privilege
|
||||
// leave untouched when we are running in elevated privilege
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -122,4 +123,10 @@ internal sealed partial class DailyNoteOptions : DbStoreOptions
|
||||
get => GetOption(ref isSilentWhenPlayingGame, SettingEntry.DailyNoteSilentWhenPlayingGame);
|
||||
set => SetOption(ref isSilentWhenPlayingGame, SettingEntry.DailyNoteSilentWhenPlayingGame, value);
|
||||
}
|
||||
|
||||
public string? WebhookUrl
|
||||
{
|
||||
get => GetOption(ref webhookUrl, SettingEntry.DailyNoteSilentWhenPlayingGame);
|
||||
set => SetOption(ref webhookUrl, SettingEntry.DailyNoteSilentWhenPlayingGame, value);
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,15 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Snap.Hutao.Core.DependencyInjection.Abstraction;
|
||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||
using Snap.Hutao.Message;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.ViewModel.User;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||
using Snap.Hutao.Web.Request.Builder;
|
||||
using Snap.Hutao.Web.Request.Builder.Abstraction;
|
||||
using System.Collections.ObjectModel;
|
||||
using WebDailyNote = Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote.DailyNote;
|
||||
|
||||
@@ -108,6 +111,8 @@ internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient<U
|
||||
|
||||
private async ValueTask RefreshDailyNotesCoreAsync(bool forceRefresh)
|
||||
{
|
||||
DailyNoteWebhookOperation dailyNoteWebhookOperation = serviceProvider.GetRequiredService<DailyNoteWebhookOperation>();
|
||||
|
||||
foreach (DailyNoteEntry entry in await dailyNoteDbService.GetDailyNoteEntryIncludeUserListAsync().ConfigureAwait(false))
|
||||
{
|
||||
if (!forceRefresh && entry.DailyNote is not null)
|
||||
@@ -144,6 +149,7 @@ internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient<U
|
||||
// database
|
||||
entry.UpdateDailyNote(dailyNote);
|
||||
await dailyNoteDbService.UpdateDailyNoteEntryAsync(entry).ConfigureAwait(false);
|
||||
await dailyNoteWebhookOperation.TryPostDailyNoteToWebhookAsync(dailyNote).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||
using Snap.Hutao.Web.Request.Builder;
|
||||
using Snap.Hutao.Web.Request.Builder.Abstraction;
|
||||
using System.Net.Http;
|
||||
using WebDailyNote = Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote.DailyNote;
|
||||
|
||||
namespace Snap.Hutao.Service.DailyNote;
|
||||
|
||||
[ConstructorGenerated(ResolveHttpClient = true)]
|
||||
[HttpClient(HttpClientConfiguration.Default)]
|
||||
internal sealed partial class DailyNoteWebhookOperation
|
||||
{
|
||||
private readonly IHttpRequestMessageBuilderFactory httpRequestMessageBuilderFactory;
|
||||
private readonly ILogger<DailyNoteWebhookOperation> logger;
|
||||
private readonly DailyNoteOptions dailyNoteOptions;
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
public async ValueTask TryPostDailyNoteToWebhookAsync(WebDailyNote dailyNote, CancellationToken token = default)
|
||||
{
|
||||
string? targetUrl = dailyNoteOptions.WebhookUrl;
|
||||
if (string.IsNullOrEmpty(targetUrl) || !Uri.TryCreate(targetUrl, UriKind.Absolute, out Uri? targetUri))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
|
||||
.SetRequestUri(targetUri)
|
||||
.PostJson(dailyNote);
|
||||
|
||||
await builder.TryCatchSendAsync(httpClient, logger, token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
using Snap.Hutao.Web.Hutao;
|
||||
using Snap.Hutao.Web.Hutao.GachaLog;
|
||||
using Snap.Hutao.Web.Response;
|
||||
|
||||
@@ -54,7 +55,7 @@ internal readonly struct TypedWishSummaryBuilderContext
|
||||
return new(taskContext, gachaLogClient, SH.ServiceGachaLogFactoryWeaponWishName, 80, 10, IsWeaponEventWish, GachaDistributionType.WeaponEvent);
|
||||
}
|
||||
|
||||
public ValueTask<Response<GachaDistribution>> GetGachaDistributionAsync()
|
||||
public ValueTask<HutaoResponse<GachaDistribution>> GetGachaDistributionAsync()
|
||||
{
|
||||
return GachaLogClient.GetGachaDistributionAsync(DistributionType);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using Snap.Hutao.Service.GachaLog.Factory;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.ViewModel.GachaLog;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
using Snap.Hutao.Web.Hutao;
|
||||
using Snap.Hutao.Web.Hutao.GachaLog;
|
||||
using Snap.Hutao.Web.Response;
|
||||
|
||||
@@ -27,7 +28,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
|
||||
private readonly IGachaLogDbService gachaLogDbService;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Response<List<GachaEntry>>> GetGachaEntriesAsync(CancellationToken token = default)
|
||||
public ValueTask<HutaoResponse<List<GachaEntry>>> GetGachaEntriesAsync(CancellationToken token = default)
|
||||
{
|
||||
return homaGachaLogClient.GetGachaEntriesAsync(token);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.ViewModel.GachaLog;
|
||||
using Snap.Hutao.Web.Hutao;
|
||||
using Snap.Hutao.Web.Hutao.GachaLog;
|
||||
using Snap.Hutao.Web.Response;
|
||||
|
||||
@@ -28,7 +29,7 @@ internal interface IGachaLogHutaoCloudService
|
||||
/// <returns>祈愿统计信息</returns>
|
||||
ValueTask<ValueResult<bool, HutaoStatistics>> GetCurrentEventStatisticsAsync(CancellationToken token = default);
|
||||
|
||||
ValueTask<Response<List<GachaEntry>>> GetGachaEntriesAsync(CancellationToken token = default);
|
||||
ValueTask<HutaoResponse<List<GachaEntry>>> GetGachaEntriesAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取祈愿记录
|
||||
|
||||
@@ -35,10 +35,28 @@ internal sealed partial class UIGFImportService : IUIGFImportService
|
||||
// v2.3+ support any locale
|
||||
// v2.2 only support matched locale
|
||||
// v2.1 only support CHS
|
||||
if (version is UIGFVersion.Major2Minor2OrLower && !metadataOptions.IsCurrentLocale(uigf.Info.Language))
|
||||
if (version is UIGFVersion.Major2Minor2OrLower)
|
||||
{
|
||||
string message = SH.ServiceGachaUIGFImportLanguageNotMatch.Format(uigf.Info.Language, metadataOptions.LanguageCode);
|
||||
ThrowHelper.InvalidOperation(message, null);
|
||||
if (!metadataOptions.IsCurrentLocale(uigf.Info.Language))
|
||||
{
|
||||
string message = SH.ServiceGachaUIGFImportLanguageNotMatch.Format(uigf.Info.Language, metadataOptions.LanguageCode);
|
||||
ThrowHelper.InvalidOperation(message);
|
||||
}
|
||||
|
||||
if (!uigf.IsMajor2Minor2OrLowerListValid(out long id))
|
||||
{
|
||||
string message = SH.ServiceGachaLogUIGFImportItemInvalidFormat.Format(id);
|
||||
ThrowHelper.InvalidOperation(message);
|
||||
}
|
||||
}
|
||||
|
||||
if (version is UIGFVersion.Major2Minor3OrHigher)
|
||||
{
|
||||
if (!uigf.IsMajor2Minor3OrHigherListValid(out long id))
|
||||
{
|
||||
string message = SH.ServiceGachaLogUIGFImportItemInvalidFormat.Format(id);
|
||||
ThrowHelper.InvalidOperation(message);
|
||||
}
|
||||
}
|
||||
|
||||
GachaArchiveOperation.GetOrAdd(gachaLogDbService, taskContext, uigf.Info.Uid, archives, out GachaArchive? archive);
|
||||
|
||||
@@ -49,6 +49,13 @@ internal readonly struct ChannelOptions
|
||||
ConfigFilePath = configFilePath;
|
||||
}
|
||||
|
||||
public ChannelOptions(ChannelType channel, SubChannelType subChannel, bool isOversea)
|
||||
{
|
||||
Channel = channel;
|
||||
SubChannel = subChannel;
|
||||
IsOversea = isOversea;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置文件未找到
|
||||
/// </summary>
|
||||
@@ -65,4 +72,9 @@ internal readonly struct ChannelOptions
|
||||
{
|
||||
return $"[ChannelType:{Channel}] [SubChannel:{SubChannel}] [IsOversea: {IsOversea}]";
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Channel, SubChannel, IsOversea);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ internal sealed class GameFileOperationException : Exception
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="innerException">内部错误</param>
|
||||
public GameFileOperationException(string message, Exception innerException)
|
||||
public GameFileOperationException(string message, Exception? innerException)
|
||||
: base(SH.ServiceGameFileOperationExceptionMessage.Format(message), innerException)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Core.IO.Ini;
|
||||
using Snap.Hutao.Factory.Abstraction;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.Game.Locator;
|
||||
using Snap.Hutao.Service.Game.Package;
|
||||
@@ -25,6 +26,7 @@ namespace Snap.Hutao.Service.Game;
|
||||
[Injection(InjectAs.Singleton, typeof(IGameService))]
|
||||
internal sealed partial class GameService : IGameService
|
||||
{
|
||||
private readonly IContentDialogFactory contentDialogFactory;
|
||||
private readonly PackageConverter packageConverter;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly IGameDbService gameDbService;
|
||||
@@ -206,10 +208,7 @@ internal sealed partial class GameService : IGameService
|
||||
}
|
||||
}
|
||||
|
||||
if (!launchScheme.IsOversea)
|
||||
{
|
||||
await packageConverter.EnsureDeprecatedFilesAndSdkAsync(resource, gameFolder).ConfigureAwait(false);
|
||||
}
|
||||
await packageConverter.EnsureDeprecatedFilesAndSdkAsync(resource, gameFolder).ConfigureAwait(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -225,8 +224,8 @@ internal sealed partial class GameService : IGameService
|
||||
return false;
|
||||
}
|
||||
|
||||
return Process.GetProcessesByName(YuanShenProcessName) is [_, ..]
|
||||
|| Process.GetProcessesByName(GenshinImpactProcessName) is [_, ..];
|
||||
return Process.GetProcessesByName(YuanShenProcessName).Any()
|
||||
|| Process.GetProcessesByName(GenshinImpactProcessName).Any();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -245,6 +244,7 @@ internal sealed partial class GameService : IGameService
|
||||
{
|
||||
try
|
||||
{
|
||||
Interlocked.Increment(ref runningGamesCounter);
|
||||
game.Start();
|
||||
progress.Report(new(LaunchPhase.ProcessStarted, SH.ServiceGameLaunchPhaseProcessStarted));
|
||||
|
||||
@@ -281,7 +281,7 @@ internal sealed partial class GameService : IGameService
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask DetectGameAccountAsync()
|
||||
public async ValueTask<GameAccount?> DetectGameAccountAsync()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(gameAccounts);
|
||||
|
||||
@@ -302,7 +302,7 @@ internal sealed partial class GameService : IGameService
|
||||
{
|
||||
// ContentDialog must be created by main thread.
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
LaunchGameAccountNameDialog dialog = serviceProvider.CreateInstance<LaunchGameAccountNameDialog>();
|
||||
LaunchGameAccountNameDialog dialog = await contentDialogFactory.CreateInstanceAsync<LaunchGameAccountNameDialog>().ConfigureAwait(false);
|
||||
(bool isOk, string name) = await dialog.GetInputNameAsync().ConfigureAwait(false);
|
||||
|
||||
if (isOk)
|
||||
@@ -318,7 +318,11 @@ internal sealed partial class GameService : IGameService
|
||||
gameAccounts.Add(account);
|
||||
}
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -26,11 +26,7 @@ internal interface IGameService
|
||||
/// <param name="uid">uid</param>
|
||||
void AttachGameAccountToUid(GameAccount gameAccount, string uid);
|
||||
|
||||
/// <summary>
|
||||
/// 检测并尝试添加游戏内账户
|
||||
/// </summary>
|
||||
/// <returns>任务</returns>
|
||||
ValueTask DetectGameAccountAsync();
|
||||
ValueTask<GameAccount?> DetectGameAccountAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取游戏路径
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Snap.Hutao.Service.Game;
|
||||
|
||||
internal static class IgnoredInvalidChannelOptions
|
||||
{
|
||||
private static readonly ImmutableHashSet<ChannelOptions> InvalidOptions = new HashSet<ChannelOptions>()
|
||||
{
|
||||
new(ChannelType.Bili, SubChannelType.Official, true),
|
||||
}.ToImmutableHashSet();
|
||||
|
||||
public static bool Contains(in ChannelOptions options)
|
||||
{
|
||||
return InvalidOptions.Contains(options);
|
||||
}
|
||||
}
|
||||
@@ -53,13 +53,7 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
public bool IsFullScreen
|
||||
{
|
||||
get => GetOption(ref isFullScreen, SettingEntry.LaunchIsFullScreen);
|
||||
set
|
||||
{
|
||||
if (SetOption(ref isFullScreen, SettingEntry.LaunchIsFullScreen, value) && value)
|
||||
{
|
||||
IsBorderless = false;
|
||||
}
|
||||
}
|
||||
set => SetOption(ref isFullScreen, SettingEntry.LaunchIsFullScreen, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -68,14 +62,7 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
public bool IsBorderless
|
||||
{
|
||||
get => GetOption(ref isBorderless, SettingEntry.LaunchIsBorderless);
|
||||
set
|
||||
{
|
||||
if (SetOption(ref isBorderless, SettingEntry.LaunchIsBorderless, value) && value)
|
||||
{
|
||||
IsExclusive = false;
|
||||
IsFullScreen = false;
|
||||
}
|
||||
}
|
||||
set => SetOption(ref isBorderless, SettingEntry.LaunchIsBorderless, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -84,13 +71,7 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
public bool IsExclusive
|
||||
{
|
||||
get => GetOption(ref isExclusive, SettingEntry.LaunchIsExclusive);
|
||||
set
|
||||
{
|
||||
if (SetOption(ref isExclusive, SettingEntry.LaunchIsExclusive, value) && value)
|
||||
{
|
||||
IsFullScreen = true;
|
||||
}
|
||||
}
|
||||
set => SetOption(ref isExclusive, SettingEntry.LaunchIsExclusive, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -153,7 +134,7 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
private static void InitializeMonitors(List<NameValue<int>> monitors)
|
||||
{
|
||||
// This list can't use foreach
|
||||
// https://github.com/microsoft/microsoft-ui-xaml/issues/6454
|
||||
// https://github.com/microsoft/CsWinRT/issues/747
|
||||
IReadOnlyList<DisplayArea> displayAreas = DisplayArea.FindAll();
|
||||
for (int i = 0; i < displayAreas.Count; i++)
|
||||
{
|
||||
@@ -165,12 +146,15 @@ internal sealed class LaunchOptions : DbStoreOptions
|
||||
|
||||
private static void InitializeScreenFps(out int fps)
|
||||
{
|
||||
HDC hDC = GetDC(HWND.Null);
|
||||
fps = GetDeviceCaps(hDC, GET_DEVICE_CAPS_INDEX.VREFRESH);
|
||||
if (ReleaseDC(HWND.Null, hDC) == 0)
|
||||
HDC hDC = default;
|
||||
try
|
||||
{
|
||||
// not released
|
||||
throw new Win32Exception();
|
||||
hDC = GetDC(HWND.Null);
|
||||
fps = GetDeviceCaps(hDC, GET_DEVICE_CAPS_INDEX.VREFRESH);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ = ReleaseDC(HWND.Null, hDC);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,6 +103,15 @@ internal sealed partial class LaunchScheme
|
||||
IsOversea = true,
|
||||
};
|
||||
|
||||
private static readonly LaunchScheme ServerGlobalChannelOfficialSubChannelOfficial = new()
|
||||
{
|
||||
LauncherId = SdkStaticLauncherGlobalId,
|
||||
Key = SdkStaticLauncherGlobalKey,
|
||||
Channel = ChannelType.Official,
|
||||
SubChannel = SubChannelType.Official,
|
||||
IsOversea = true,
|
||||
};
|
||||
|
||||
private static readonly LaunchScheme ServerGlobalChannelOfficialSubChannelEpic = new()
|
||||
{
|
||||
LauncherId = SdkStaticLauncherGlobalId,
|
||||
@@ -143,6 +152,7 @@ internal sealed partial class LaunchScheme
|
||||
// 国际服
|
||||
ServerGlobalChannelDefaultSubChannelDefaultCompatOnly,
|
||||
ServerGlobalChannelOfficialSubChannelDefault,
|
||||
ServerGlobalChannelOfficialSubChannelOfficial,
|
||||
ServerGlobalChannelOfficialSubChannelEpic,
|
||||
ServerGlobalChannelOfficialSubChannelGoogle,
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Snap.Hutao.Service.Game.Package;
|
||||
internal sealed class PackageConvertException : Exception
|
||||
{
|
||||
/// <inheritdoc cref="Exception(string?, Exception?)"/>
|
||||
public PackageConvertException(string message, Exception innerException)
|
||||
public PackageConvertException(string message, Exception? innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -26,7 +26,10 @@ internal static class ProcessInterop
|
||||
/// <returns>初始化后的游戏进程</returns>
|
||||
public static Process InitializeGameProcess(LaunchOptions options, string gamePath)
|
||||
{
|
||||
Must.Argument(!(options.IsBorderless && options.IsExclusive), "无边框与独占全屏选项无法同时生效");
|
||||
|
||||
// https://docs.unity.cn/cn/current/Manual/PlayerCommandLineArguments.html
|
||||
// https://docs.unity3d.com/2017.4/Documentation/Manual/CommandLineArguments.html
|
||||
string commandLine = new CommandLineBuilder()
|
||||
.AppendIf("-popupwindow", options.IsBorderless)
|
||||
.AppendIf("-window-mode", options.IsExclusive, "exclusive")
|
||||
|
||||
@@ -72,10 +72,7 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
|
||||
{
|
||||
ulong temp = 0;
|
||||
bool result = ReadProcessMemory((HANDLE)process.Handle, (void*)baseAddress, (byte*)&temp, 8);
|
||||
if (!result)
|
||||
{
|
||||
ThrowHelper.InvalidOperation(SH.ServiceGameUnlockerReadProcessMemoryPointerAddressFailed, null);
|
||||
}
|
||||
Verify.Operation(result, SH.ServiceGameUnlockerReadProcessMemoryPointerAddressFailed);
|
||||
|
||||
value = (nuint)temp;
|
||||
return result;
|
||||
@@ -235,7 +232,7 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
|
||||
|
||||
using (localMemory)
|
||||
{
|
||||
int offset = IndexOfPattern(localMemory.Span[(int)moduleEntryInfo.UnityPlayer.Size..]);
|
||||
int offset = IndexOfPattern(localMemory.AsSpan()[(int)moduleEntryInfo.UnityPlayer.Size..]);
|
||||
Must.Range(offset >= 0, SH.ServiceGameUnlockerInterestedPatternNotFound);
|
||||
|
||||
byte* pLocalMemory = (byte*)localMemory.Pointer;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Snap.Hutao.Web.Hutao;
|
||||
using Snap.Hutao.Web.Hutao.SpiralAbyss;
|
||||
using Snap.Hutao.Web.Response;
|
||||
|
||||
@@ -64,7 +65,7 @@ internal sealed partial class HutaoSpiralAbyssService : IHutaoSpiralAbyssService
|
||||
return FromCacheOrWebAsync(nameof(TeamAppearance), homaClient.GetTeamCombinationsAsync);
|
||||
}
|
||||
|
||||
private async ValueTask<T> FromCacheOrWebAsync<T>(string typeName, Func<CancellationToken, ValueTask<Response<T>>> taskFunc)
|
||||
private async ValueTask<T> FromCacheOrWebAsync<T>(string typeName, Func<CancellationToken, ValueTask<HutaoResponse<T>>> taskFunc)
|
||||
where T : class, new()
|
||||
{
|
||||
string key = $"{nameof(HutaoSpiralAbyssService)}.Cache.{typeName}";
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Snap.Hutao.Web.Hutao;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -21,6 +22,7 @@ internal sealed class HutaoUserOptions : ObservableObject, IOptions<HutaoUserOpt
|
||||
private bool isHutaoCloudServiceAllowed;
|
||||
private bool isLicensedDeveloper;
|
||||
private string? gachaLogExpireAt;
|
||||
private string? gachaLogExpireAtSlim;
|
||||
private bool isMaintainer;
|
||||
|
||||
/// <summary>
|
||||
@@ -55,20 +57,37 @@ internal sealed class HutaoUserOptions : ObservableObject, IOptions<HutaoUserOpt
|
||||
/// </summary>
|
||||
public string? GachaLogExpireAt { get => gachaLogExpireAt; set => SetProperty(ref gachaLogExpireAt, value); }
|
||||
|
||||
public string? GachaLogExpireAtSlim { get => gachaLogExpireAtSlim; set => SetProperty(ref gachaLogExpireAtSlim, value); }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public HutaoUserOptions Value { get => this; }
|
||||
|
||||
/// <summary>
|
||||
/// 登录
|
||||
/// </summary>
|
||||
/// <param name="userName">用户名</param>
|
||||
/// <param name="token">令牌</param>
|
||||
public void LoginSucceed(string userName, string? token)
|
||||
public async ValueTask<bool> PostLoginSucceedAsync(HomaPassportClient passportClient, ITaskContext taskContext, string username, string? token)
|
||||
{
|
||||
UserName = userName;
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
UserName = username;
|
||||
this.token = token;
|
||||
IsLoggedIn = true;
|
||||
initializedTaskCompletionSource.TrySetResult();
|
||||
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
Web.Response.Response<UserInfo> userInfoResponse = await passportClient.GetUserInfoAsync(default).ConfigureAwait(false);
|
||||
if (userInfoResponse.IsOk())
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
UpdateUserInfo(userInfoResponse.Data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void LogoutOrUnregister()
|
||||
{
|
||||
UserName = null;
|
||||
token = null;
|
||||
IsLoggedIn = false;
|
||||
ClearUserInfo();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,6 +113,7 @@ internal sealed class HutaoUserOptions : ObservableObject, IOptions<HutaoUserOpt
|
||||
IsLicensedDeveloper = userInfo.IsLicensedDeveloper;
|
||||
IsMaintainer = userInfo.IsMaintainer;
|
||||
GachaLogExpireAt = Regex.Unescape(SH.ServiceHutaoUserGachaLogExpiredAt).Format(userInfo.GachaLogExpireAt);
|
||||
GachaLogExpireAtSlim = $"{userInfo.GachaLogExpireAt:yyyy.MM.dd HH:mm:ss}";
|
||||
IsCloudServiceAllowed = IsLicensedDeveloper || userInfo.GachaLogExpireAt > DateTimeOffset.Now;
|
||||
}
|
||||
|
||||
@@ -102,4 +122,13 @@ internal sealed class HutaoUserOptions : ObservableObject, IOptions<HutaoUserOpt
|
||||
await initializedTaskCompletionSource.Task.ConfigureAwait(false);
|
||||
return token;
|
||||
}
|
||||
|
||||
private void ClearUserInfo()
|
||||
{
|
||||
IsLicensedDeveloper = false;
|
||||
IsMaintainer = false;
|
||||
GachaLogExpireAt = null;
|
||||
GachaLogExpireAtSlim = null;
|
||||
IsCloudServiceAllowed = false;
|
||||
}
|
||||
}
|
||||
@@ -44,15 +44,8 @@ internal sealed partial class HutaoUserService : IHutaoUserService, IHutaoUserSe
|
||||
|
||||
if (response.IsOk())
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
options.LoginSucceed(userName, response.Data);
|
||||
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
Web.Response.Response<UserInfo> userInfoResponse = await passportClient.GetUserInfoAsync(token).ConfigureAwait(false);
|
||||
if (userInfoResponse.IsOk())
|
||||
if (await options.PostLoginSucceedAsync(passportClient, taskContext, userName, response.Data).ConfigureAwait(false))
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
options.UpdateUserInfo(userInfoResponse.Data);
|
||||
isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ internal sealed partial class DocumentationProvider : IDocumentationProvider
|
||||
[typeof(CultivationPage)] = "https://hut.ao/features/develop-plan.html",
|
||||
[typeof(DailyNotePage)] = "https://hut.ao/features/real-time-notes.html",
|
||||
[typeof(GachaLogPage)] = "https://hut.ao/features/wish-export.html",
|
||||
[typeof(HutaoPassportPage)] = "https://hut.ao/zh/features/hutao-settings.html#%E8%83%A1%E6%A1%83%E5%B8%90%E5%8F%B7",
|
||||
[typeof(LaunchGamePage)] = "https://hut.ao/features/game-launcher.html",
|
||||
[typeof(LoginHoyoverseUserPage)] = "https://hut.ao/features/mhy-account-switch.html",
|
||||
[typeof(LoginMihoyoUserPage)] = "https://hut.ao/features/mhy-account-switch.html",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
|
||||
@@ -72,14 +72,23 @@
|
||||
<None Remove="Assets\Wide310x150Logo.scale-200.png" />
|
||||
<None Remove="Assets\Wide310x150Logo.scale-400.png" />
|
||||
<None Remove="CodeMetricsConfig.txt" />
|
||||
<None Remove="Control\Image\CachedImage.xaml" />
|
||||
<None Remove="Control\Loading.xaml" />
|
||||
<None Remove="Control\Panel\PanelSelector.xaml" />
|
||||
<None Remove="Control\Theme\Card.xaml" />
|
||||
<None Remove="Control\Theme\Color.xaml" />
|
||||
<None Remove="Control\Theme\Converter.xaml" />
|
||||
<None Remove="Control\Theme\CornerRadius.xaml" />
|
||||
<None Remove="Control\Theme\FontStyle.xaml" />
|
||||
<None Remove="Control\Theme\Glyph.xaml" />
|
||||
<None Remove="Control\Theme\InfoBarOverride.xaml" />
|
||||
<None Remove="Control\Theme\ItemsPanelTemplate.xaml" />
|
||||
<None Remove="Control\Theme\NumericValue.xaml" />
|
||||
<None Remove="Control\Theme\PageOverride.xaml" />
|
||||
<None Remove="Control\Theme\PivotOverride.xaml" />
|
||||
<None Remove="Control\Theme\SettingsStyle.xaml" />
|
||||
<None Remove="Control\Theme\TransitionCollection.xaml" />
|
||||
<None Remove="Control\Theme\Uri.xaml" />
|
||||
<None Remove="Control\Theme\WindowOverride.xaml" />
|
||||
<None Remove="GuideWindow.xaml" />
|
||||
<None Remove="IdentityStructs.json" />
|
||||
@@ -91,6 +100,7 @@
|
||||
<None Remove="Resource\Icon\UI_AchievementIcon_3_3.png" />
|
||||
<None Remove="Resource\Icon\UI_GachaShowPanel_Bg_Weapon.png" />
|
||||
<None Remove="Resource\Icon\UI_Icon_Fetter.png" />
|
||||
<None Remove="Resource\Icon\UI_Icon_Gift.png" />
|
||||
<None Remove="Resource\Icon\UI_Icon_Locked.png" />
|
||||
<None Remove="Resource\Icon\UI_Icon_Tower_Star.png" />
|
||||
<None Remove="Resource\Icon\UI_ItemIcon_201.png" />
|
||||
@@ -135,10 +145,15 @@
|
||||
<None Remove="View\Dialog\CultivatePromotionDeltaBatchDialog.xaml" />
|
||||
<None Remove="View\Dialog\CultivatePromotionDeltaDialog.xaml" />
|
||||
<None Remove="View\Dialog\DailyNoteNotificationDialog.xaml" />
|
||||
<None Remove="View\Dialog\DailyNoteWebhookDialog.xaml" />
|
||||
<None Remove="View\Dialog\GachaLogImportDialog.xaml" />
|
||||
<None Remove="View\Dialog\GachaLogRefreshProgressDialog.xaml" />
|
||||
<None Remove="View\Dialog\GachaLogUrlDialog.xaml" />
|
||||
<None Remove="View\Dialog\GeetestCustomUrlDialog.xaml" />
|
||||
<None Remove="View\Dialog\HutaoPassportLoginDialog.xaml" />
|
||||
<None Remove="View\Dialog\HutaoPassportRegisterDialog.xaml" />
|
||||
<None Remove="View\Dialog\HutaoPassportResetPasswordDialog.xaml" />
|
||||
<None Remove="View\Dialog\HutaoPassportUnregisterDialog.xaml" />
|
||||
<None Remove="View\Dialog\LaunchGameAccountNameDialog.xaml" />
|
||||
<None Remove="View\Dialog\LaunchGamePackageConvertDialog.xaml" />
|
||||
<None Remove="View\Dialog\UserDialog.xaml" />
|
||||
@@ -152,7 +167,6 @@
|
||||
<None Remove="View\Page\CultivationPage.xaml" />
|
||||
<None Remove="View\Page\DailyNotePage.xaml" />
|
||||
<None Remove="View\Page\GachaLogPage.xaml" />
|
||||
<None Remove="View\Page\HutaoPassportPage.xaml" />
|
||||
<None Remove="View\Page\LaunchGamePage.xaml" />
|
||||
<None Remove="View\Page\LoginMihoyoUserPage.xaml" />
|
||||
<None Remove="View\Page\SettingPage.xaml" />
|
||||
@@ -217,6 +231,7 @@
|
||||
<Content Include="Resource\Icon\UI_AchievementIcon_3_3.png" />
|
||||
<Content Include="Resource\Icon\UI_GachaShowPanel_Bg_Weapon.png" />
|
||||
<Content Include="Resource\Icon\UI_Icon_Fetter.png" />
|
||||
<Content Include="Resource\Icon\UI_Icon_Gift.png" />
|
||||
<Content Include="Resource\Icon\UI_Icon_Locked.png" />
|
||||
<Content Include="Resource\Icon\UI_Icon_Tower_Star.png" />
|
||||
<Content Include="Resource\Icon\UI_ItemIcon_201.png" />
|
||||
@@ -239,12 +254,12 @@
|
||||
<!-- Packages -->
|
||||
<ItemGroup>
|
||||
<!-- https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json -->
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.Controls.DataTable" Version="0.1.230809" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.MarqueeText" Version="0.1.230809" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.Shimmer" Version="0.1.230809" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.TokenView" Version="0.1.230809" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.TransitionHelper" Version="0.1.230809" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2-build.1" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.Controls.DataTable" Version="0.1.230830" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.MarqueeText" Version="0.1.230830" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.Shimmer" Version="0.1.230830" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.TokenView" Version="0.1.230830" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.TransitionHelper" Version="0.1.230830" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.0.230907" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Collections" Version="8.0.230907" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.HeaderedControls" Version="8.0.230907" />
|
||||
@@ -253,8 +268,8 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.0.230907" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.0.230907" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.11" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.11">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.13" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.13">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -262,7 +277,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.6.11" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.756" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.230913002" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.231008000" />
|
||||
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.507">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -287,20 +302,93 @@
|
||||
<ItemGroup>
|
||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Resource\Localization\SH.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>SH.resx</DependentUpon>
|
||||
</Compile>
|
||||
<AdditionalFiles Include="Resource\Localization\SH.resx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="View\Dialog\HutaoPassportUnregisterDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="View\Dialog\HutaoPassportResetPasswordDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="View\Dialog\HutaoPassportRegisterDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="View\Dialog\HutaoPassportLoginDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resource\Localization\SH.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>SH.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<Page Update="View\Dialog\DailyNoteWebhookDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="Control\Theme\NumericValue.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="Control\Theme\CornerRadius.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="Control\Theme\Color.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="Control\Theme\Card.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="Control\Image\CachedImage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="Control\Theme\ItemsPanelTemplate.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="Control\Theme\Converter.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="Control\Theme\Glyph.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="Control\Theme\Uri.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -428,12 +516,6 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="View\Page\HutaoPassportPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Pages -->
|
||||
<ItemGroup>
|
||||
|
||||
@@ -66,16 +66,16 @@
|
||||
Description="{Binding LastOrangePull}"
|
||||
Header="{shcm:ResourceString Name=ViewControlStatisticsCardOrangeText}"
|
||||
Maximum="{Binding GuaranteeOrangeThreshold}"
|
||||
ProgressForeground="{StaticResource OrangeBrush}"
|
||||
TextForeground="{StaticResource OrangeBrush}"
|
||||
ProgressForeground="{StaticResource OrangeColorBrush}"
|
||||
TextForeground="{StaticResource OrangeColorBrush}"
|
||||
Value="{Binding LastOrangePull}"/>
|
||||
<shvcp:CardProgressBar
|
||||
Grid.Column="0"
|
||||
Description="{Binding LastPurplePull}"
|
||||
Header="{shcm:ResourceString Name=ViewControlStatisticsCardPurpleText}"
|
||||
Maximum="{Binding GuaranteePurpleThreshold}"
|
||||
ProgressForeground="{StaticResource PurpleBrush}"
|
||||
TextForeground="{StaticResource PurpleBrush}"
|
||||
ProgressForeground="{StaticResource PurpleColorBrush}"
|
||||
TextForeground="{StaticResource PurpleColorBrush}"
|
||||
Value="{Binding LastPurplePull}"/>
|
||||
</StackPanel>
|
||||
|
||||
@@ -98,16 +98,16 @@
|
||||
Description="{Binding LastOrangePull}"
|
||||
Header="{shcm:ResourceString Name=ViewControlStatisticsCardOrangeText}"
|
||||
Maximum="{Binding GuaranteeOrangeThreshold}"
|
||||
ProgressForeground="{StaticResource OrangeBrush}"
|
||||
TextForeground="{StaticResource OrangeBrush}"
|
||||
ProgressForeground="{StaticResource OrangeColorBrush}"
|
||||
TextForeground="{StaticResource OrangeColorBrush}"
|
||||
Value="{Binding LastOrangePull}"/>
|
||||
<shvcp:CardProgressBar
|
||||
Grid.Column="0"
|
||||
Description="{Binding LastPurplePull}"
|
||||
Header="{shcm:ResourceString Name=ViewControlStatisticsCardPurpleText}"
|
||||
Maximum="{Binding GuaranteePurpleThreshold}"
|
||||
ProgressForeground="{StaticResource PurpleBrush}"
|
||||
TextForeground="{StaticResource PurpleBrush}"
|
||||
ProgressForeground="{StaticResource PurpleColorBrush}"
|
||||
TextForeground="{StaticResource PurpleColorBrush}"
|
||||
Value="{Binding LastPurplePull}"/>
|
||||
</StackPanel>
|
||||
|
||||
@@ -129,16 +129,16 @@
|
||||
Description="{Binding LastOrangePull}"
|
||||
Header="{shcm:ResourceString Name=ViewControlStatisticsCardOrangeText}"
|
||||
Maximum="{Binding GuaranteeOrangeThreshold}"
|
||||
ProgressForeground="{StaticResource OrangeBrush}"
|
||||
TextForeground="{StaticResource OrangeBrush}"
|
||||
ProgressForeground="{StaticResource OrangeColorBrush}"
|
||||
TextForeground="{StaticResource OrangeColorBrush}"
|
||||
Value="{Binding LastOrangePull}"/>
|
||||
<shvcp:CardProgressBar
|
||||
Grid.Column="0"
|
||||
Description="{Binding LastPurplePull}"
|
||||
Header="{shcm:ResourceString Name=ViewControlStatisticsCardPurpleText}"
|
||||
Maximum="{Binding GuaranteePurpleThreshold}"
|
||||
ProgressForeground="{StaticResource PurpleBrush}"
|
||||
TextForeground="{StaticResource PurpleBrush}"
|
||||
ProgressForeground="{StaticResource PurpleColorBrush}"
|
||||
TextForeground="{StaticResource PurpleColorBrush}"
|
||||
Value="{Binding LastPurplePull}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:shch="using:Snap.Hutao.Control.Helper"
|
||||
xmlns:shci="using:Snap.Hutao.Control.Image"
|
||||
Style="{StaticResource BorderGridStyle}"
|
||||
Style="{ThemeResource GridCardStyle}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
MinHeight="40"
|
||||
Style="{StaticResource BorderGridStyle}"
|
||||
Style="{ThemeResource GridCardStyle}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Markup;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Snap.Hutao.Control.Theme;
|
||||
using Snap.Hutao.Web.Bridge;
|
||||
@@ -50,6 +51,7 @@ internal sealed partial class AnnouncementContentViewer : UserControl
|
||||
/// </summary>
|
||||
public AnnouncementContentViewer()
|
||||
{
|
||||
Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00000000");
|
||||
InitializeComponent();
|
||||
|
||||
loadEventHandler = OnLoaded;
|
||||
@@ -131,9 +133,10 @@ internal sealed partial class AnnouncementContentViewer : UserControl
|
||||
|
||||
private void OnUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WebView.CoreWebView2.WebMessageReceived -= webMessageReceivedHandler;
|
||||
Loaded -= loadEventHandler;
|
||||
Unloaded -= unloadEventHandler;
|
||||
if (WebView is { CoreWebView2: CoreWebView2 coreWebView2 })
|
||||
{
|
||||
coreWebView2.WebMessageReceived -= webMessageReceivedHandler;
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask LoadAnnouncementAsync()
|
||||
@@ -141,7 +144,7 @@ internal sealed partial class AnnouncementContentViewer : UserControl
|
||||
try
|
||||
{
|
||||
await WebView.EnsureCoreWebView2Async();
|
||||
WebView.CoreWebView2.DisableDevToolsOnReleaseBuild();
|
||||
WebView.CoreWebView2.DisableDevToolsForReleaseBuild();
|
||||
WebView.CoreWebView2.WebMessageReceived += webMessageReceivedHandler;
|
||||
|
||||
await WebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(MihoyoSDKDefinition);
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
</DataTemplate>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid Style="{StaticResource BorderGridStyle}">
|
||||
<Grid Style="{ThemeResource GridCardStyle}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition/>
|
||||
@@ -36,7 +36,7 @@
|
||||
Grid.Row="0"
|
||||
cw:UIElementExtensions.ClipToBounds="True"
|
||||
CornerRadius="{StaticResource ControlCornerRadiusTop}"
|
||||
Style="{StaticResource BorderGridStyle}">
|
||||
Style="{ThemeResource GridCardStyle}">
|
||||
<cwc:ConstrainedBox
|
||||
Margin="-1"
|
||||
HorizontalAlignment="Center"
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<shvconv:Int32ToGradientColorConverter x:Key="Int32ToGradientColorConverter" MaximumValue="{Binding GuaranteeOrangeThreshold}"/>
|
||||
|
||||
<DataTemplate x:Key="OrangeListTemplate" d:DataType="shvg:SummaryItem">
|
||||
<Grid Margin="0" Style="{StaticResource BorderGridStyle}">
|
||||
<Grid Margin="0" Style="{ThemeResource GridCardStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock Text="{Binding TimeFormatted}"/>
|
||||
</ToolTipService.ToolTip>
|
||||
@@ -59,13 +59,13 @@
|
||||
<TextBlock
|
||||
Margin="0,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{StaticResource GuaranteePullBrush}"
|
||||
Foreground="{StaticResource GuaranteePullCoolorBrush}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardGuaranteeText}"
|
||||
Visibility="{Binding IsGuarantee, Converter={StaticResource BoolToVisibilityConverter}}"/>
|
||||
<TextBlock
|
||||
Margin="0,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{StaticResource UpPullBrush}"
|
||||
Foreground="{StaticResource UpPullColorBrush}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardUpText}"
|
||||
Visibility="{Binding IsUp, Converter={StaticResource BoolToVisibilityConverter}}"/>
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
</DataTemplate>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid Style="{StaticResource BorderGridStyle}">
|
||||
<Grid Style="{ThemeResource GridCardStyle}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
@@ -181,8 +181,8 @@
|
||||
Description="{Binding LastOrangePull}"
|
||||
Header="{shcm:ResourceString Name=ViewControlStatisticsCardToLastOrangeText}"
|
||||
Maximum="{Binding GuaranteeOrangeThreshold}"
|
||||
ProgressForeground="{StaticResource OrangeBrush}"
|
||||
TextForeground="{StaticResource OrangeBrush}"
|
||||
ProgressForeground="{StaticResource OrangeColorBrush}"
|
||||
TextForeground="{StaticResource OrangeColorBrush}"
|
||||
Value="{Binding LastOrangePull}"/>
|
||||
<shvcp:CardProgressBar
|
||||
Grid.Column="1"
|
||||
@@ -190,12 +190,28 @@
|
||||
Description="{Binding LastPurplePull}"
|
||||
Header="{shcm:ResourceString Name=ViewControlStatisticsCardToLastPurpleText}"
|
||||
Maximum="{Binding GuaranteePurpleThreshold}"
|
||||
ProgressForeground="{StaticResource PurpleBrush}"
|
||||
TextForeground="{StaticResource PurpleBrush}"
|
||||
ProgressForeground="{StaticResource PurpleColorBrush}"
|
||||
TextForeground="{StaticResource PurpleColorBrush}"
|
||||
Value="{Binding LastPurplePull}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<MenuFlyoutSeparator Margin="-12,0"/>
|
||||
<Grid Margin="-12,-2" ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<MenuFlyoutSeparator Grid.Column="0"/>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Opacity="0.6"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding TimeSpanFormatted}"/>
|
||||
<MenuFlyoutSeparator Grid.Column="2"/>
|
||||
</Grid>
|
||||
|
||||
<shvcont:StatisticsSegmented
|
||||
x:Name="StatisticsSegmented"
|
||||
Margin="0,12,0,0"
|
||||
@@ -209,15 +225,20 @@
|
||||
<cwcont:Case Value="{shcm:Int32 Value=0}">
|
||||
<StackPanel Spacing="2">
|
||||
<Grid>
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{shcm:ResourceString Name=ViewControlStatisticsCardOrangeAveragePullText}"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardOrangeAveragePullText}"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding AverageOrangePullFormatted}"/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<!-- 高度占位符 -->
|
||||
<TextBlock/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardUpAveragePullText}"
|
||||
Visibility="{x:Bind ShowUpPull, Converter={StaticResource BoolToVisibilityConverter}}"/>
|
||||
@@ -237,43 +258,47 @@
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding MinOrangePullFormatted}"/>
|
||||
</Grid>
|
||||
<Grid/>
|
||||
</StackPanel>
|
||||
</cwcont:Case>
|
||||
<cwcont:Case Value="{shcm:Int32 Value=1}">
|
||||
<StackPanel Spacing="2">
|
||||
<Grid>
|
||||
<TextBlock
|
||||
Foreground="{StaticResource OrangeBrush}"
|
||||
HorizontalAlignment="Left"
|
||||
Foreground="{StaticResource OrangeColorBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardOrangeText}"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
FontFamily="{StaticResource CascadiaMonoAndMiSans}"
|
||||
Foreground="{StaticResource OrangeBrush}"
|
||||
Foreground="{StaticResource OrangeColorBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding TotalOrangeFormatted}"/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextBlock
|
||||
Foreground="{StaticResource PurpleBrush}"
|
||||
HorizontalAlignment="Left"
|
||||
Foreground="{StaticResource PurpleColorBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardPurpleText}"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
FontFamily="{StaticResource CascadiaMonoAndMiSans}"
|
||||
Foreground="{StaticResource PurpleBrush}"
|
||||
Foreground="{StaticResource PurpleColorBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding TotalPurpleFormatted}"/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<TextBlock
|
||||
Foreground="{StaticResource BlueBrush}"
|
||||
HorizontalAlignment="Left"
|
||||
Foreground="{StaticResource BlueColorBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{shcm:ResourceString Name=ViewControlStatisticsCardBlueText}"/>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
FontFamily="{StaticResource CascadiaMonoAndMiSans}"
|
||||
Foreground="{StaticResource BlueBrush}"
|
||||
Foreground="{StaticResource BlueColorBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{Binding TotalBlueFormatted}"/>
|
||||
</Grid>
|
||||
|
||||
@@ -10,6 +10,8 @@ using Snap.Hutao.Service.Notification;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.ViewModel.User;
|
||||
using Snap.Hutao.Web.Bridge;
|
||||
using WinRT;
|
||||
using WinRT.Interop;
|
||||
|
||||
namespace Snap.Hutao.View.Control;
|
||||
|
||||
@@ -17,23 +19,23 @@ namespace Snap.Hutao.View.Control;
|
||||
internal partial class WebViewer : UserControl, IRecipient<UserChangedMessage>
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly RoutedEventHandler loadEventHandler;
|
||||
private readonly RoutedEventHandler unloadEventHandler;
|
||||
|
||||
[SuppressMessage("", "IDE0052")]
|
||||
private MiHoYoJSInterface? jsInterface;
|
||||
private bool isInitializingOrInitialized;
|
||||
|
||||
public WebViewer()
|
||||
{
|
||||
Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00000000");
|
||||
InitializeComponent();
|
||||
serviceProvider = Ioc.Default;
|
||||
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
|
||||
serviceProvider.GetRequiredService<IMessenger>().Register(this);
|
||||
|
||||
loadEventHandler = OnLoaded;
|
||||
unloadEventHandler = OnUnloaded;
|
||||
|
||||
Loaded += loadEventHandler;
|
||||
Unloaded += unloadEventHandler;
|
||||
}
|
||||
|
||||
public void Receive(UserChangedMessage message)
|
||||
@@ -47,17 +49,17 @@ internal partial class WebViewer : UserControl, IRecipient<UserChangedMessage>
|
||||
InitializeAsync().SafeForget();
|
||||
}
|
||||
|
||||
private void OnUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
jsInterface = null;
|
||||
Loaded -= loadEventHandler;
|
||||
Unloaded -= unloadEventHandler;
|
||||
}
|
||||
|
||||
private async ValueTask InitializeAsync()
|
||||
{
|
||||
if (isInitializingOrInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
isInitializingOrInitialized = true;
|
||||
|
||||
await WebView.EnsureCoreWebView2Async();
|
||||
WebView.CoreWebView2.DisableDevToolsOnReleaseBuild();
|
||||
WebView.CoreWebView2.DisableDevToolsForReleaseBuild();
|
||||
RefreshWebview2Content();
|
||||
}
|
||||
|
||||
@@ -86,19 +88,31 @@ internal partial class WebViewer : UserControl, IRecipient<UserChangedMessage>
|
||||
string source = SourceProvider.GetSource(userAndUid);
|
||||
if (!string.IsNullOrEmpty(source))
|
||||
{
|
||||
await coreWebView2.DeleteCookiesAsync(".mihoyo.com").ConfigureAwait(true);
|
||||
coreWebView2.SetCookie(user.CookieToken, user.LToken, user.SToken);
|
||||
_ = userAndUid.User.IsOversea ? coreWebView2.SetMobileOverseaUserAgent() : coreWebView2.SetMobileUserAgent();
|
||||
jsInterface = SourceProvider.CreateJsInterface(serviceProvider, coreWebView2, userAndUid);
|
||||
try
|
||||
{
|
||||
await coreWebView2.Profile.ClearBrowsingDataAsync();
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
infoBarService.Warning(SH.ViewControlWebViewerCoreWebView2ProfileQueryInterfaceFailed);
|
||||
await coreWebView2.DeleteCookiesAsync(userAndUid.IsOversea).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
CoreWebView2Navigator navigator = new(coreWebView2);
|
||||
await navigator.NavigateAsync("about:blank").ConfigureAwait(true);
|
||||
|
||||
coreWebView2
|
||||
.SetCookie(user.CookieToken, user.LToken, userAndUid.IsOversea)
|
||||
.SetMobileUserAgent(userAndUid.IsOversea);
|
||||
jsInterface?.Detach();
|
||||
jsInterface = SourceProvider.CreateJsInterface(serviceProvider, coreWebView2, userAndUid);
|
||||
|
||||
await navigator.NavigateAsync(source).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.MustSelectUserAndUid);
|
||||
infoBarService.Warning(SH.MustSelectUserAndUid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user