mirror of
https://github.com/BeyondDimension/SteamTools.git
synced 2026-10-26 12:09:32 +08:00
Compare commits
72
Commits
3.0.0-rc.14
...
3.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86122510b4 | ||
|
|
67f047735f | ||
|
|
8ee7e09ecf | ||
|
|
e464f285e2 | ||
|
|
68d1435ade | ||
|
|
d42f6ba07a | ||
|
|
662a5aca6f | ||
|
|
f422c76805 | ||
|
|
cd85ff7f17 | ||
|
|
bd30f0dcc2 | ||
|
|
5c4564931c | ||
|
|
923c553da8 | ||
|
|
de36e33c5b | ||
|
|
d95c797afa | ||
|
|
f217902424 | ||
|
|
d6e6bd6cc3 | ||
|
|
8b521ff7c2 | ||
|
|
ed061db526 | ||
|
|
880d88749f | ||
|
|
01dee6f61a | ||
|
|
3267624951 | ||
|
|
5bb8b2bde8 | ||
|
|
736e9c405b | ||
|
|
36619868e5 | ||
|
|
13ad0c7142 | ||
|
|
2155c6a91e | ||
|
|
40893ce6b1 | ||
|
|
023d5a98b6 | ||
|
|
e501f46aa7 | ||
|
|
c96c30fa40 | ||
|
|
b76f77c017 | ||
|
|
74b2c3b3e7 | ||
|
|
be6b58f7f0 | ||
|
|
651a229a24 | ||
|
|
6db92b60ce | ||
|
|
b25c682e55 | ||
|
|
c457037a09 | ||
|
|
c222ae2a57 | ||
|
|
e2a5e7ef11 | ||
|
|
32d3fa92ca | ||
|
|
db30652b22 | ||
|
|
cb00861532 | ||
|
|
64f60440a3 | ||
|
|
0465f6f578 | ||
|
|
a8701adbb4 | ||
|
|
d954e99f12 | ||
|
|
ab1dcbdcaf | ||
|
|
7e4319695f | ||
|
|
6a78e9aa71 | ||
|
|
ece1768b2a | ||
|
|
c749fad3be | ||
|
|
25afcb2b3f | ||
|
|
45683c78a8 | ||
|
|
a477cd87cf | ||
|
|
a2cf95f0c1 | ||
|
|
88ddc7a320 | ||
|
|
e5463f44d2 | ||
|
|
816d1c9b0c | ||
|
|
69296f9587 | ||
|
|
6577ed9fa0 | ||
|
|
a54e988441 | ||
|
|
db508a04f0 | ||
|
|
eb065d7c7e | ||
|
|
1b639291e4 | ||
|
|
c8810f7926 | ||
|
|
073b5885c6 | ||
|
|
ed6df52998 | ||
|
|
88b4aac622 | ||
|
|
9504413ff7 | ||
|
|
0f28de1e94 | ||
|
|
ad11654cb4 | ||
|
|
f43aa16c6a |
@@ -40,3 +40,9 @@
|
||||
[submodule "ref/appcenter-sdk-dotnet"]
|
||||
path = ref/appcenter-sdk-dotnet
|
||||
url = https://github.com/BeyondDimension/appcenter-sdk-dotnet.git
|
||||
[submodule "ref/Fusillade"]
|
||||
path = ref/Fusillade
|
||||
url = https://github.com/reactiveui/Fusillade
|
||||
[submodule "ref/Avalonia8"]
|
||||
path = ref/Avalonia8
|
||||
url = https://github.com/BeyondDimension/Avalonia8
|
||||
|
||||
+2
-3
@@ -71,13 +71,12 @@ See details [./doc/download-guide.en.md](./doc/download-guide.en.md)
|
||||
Read what we [milestones](https://github.com/BeyondDimension/SteamTools/milestones), and feel free to ask questions.
|
||||
|
||||
## ⌨️ Development Environment
|
||||
[Visual Studio 2022](https://visualstudio.microsoft.com/vs)
|
||||
[Visual Studio 2022 for Mac](https://visualstudio.microsoft.com/vs/mac)
|
||||
[Visual Studio 2026](https://visualstudio.microsoft.com/vs)
|
||||
[JetBrains Rider](https://www.jetbrains.com/rider)
|
||||
[Visual Studio Code](https://code.visualstudio.com)
|
||||
[OpenJDK 17](https://learn.microsoft.com/en-us/java/openjdk/download#openjdk-17)
|
||||
[Android Studio Electric Eel Or Higher](https://developer.android.com/studio)
|
||||
[Xcode 14 Or Higher](https://developer.apple.com/xcode)
|
||||
[Xcode 26 Or Higher](https://developer.apple.com/xcode)
|
||||
|
||||
## 🏗️ Project Structure
|
||||
See details [./src/README.md](./src/README.md)
|
||||
|
||||
@@ -93,28 +93,12 @@
|
||||
查看这个 [milestones](https://github.com/BeyondDimension/SteamTools/milestones) 来了解我们下一步的开发计划,并随时提出问题。
|
||||
|
||||
## ⌨️ 开发环境
|
||||
[Visual Studio 2022](https://visualstudio.microsoft.com/zh-hans/vs) 或 [Visual Studio 2022 for Mac](https://visualstudio.microsoft.com/zh-hans/vs/mac)
|
||||
- 系统要求
|
||||
- [Windows 11 版本 21H2 或更高版本:家庭版、专业版、专业教育版、专业工作站版、企业版和教育版](https://learn.microsoft.com/zh-cn/visualstudio/releases/2022/system-requirements)
|
||||
- [macOS Big Sur 11.0 或更高版本](https://learn.microsoft.com/zh-cn/visualstudio/releases/2022/mac-system-requirements)
|
||||
- 工作负荷
|
||||
- Web 和云
|
||||
- ASP.NET 和 Web 开发
|
||||
- 桌面应用和移动应用
|
||||
- 使用 .NET 的移动开发 / .NET Multi-platform App UI 开发
|
||||
- .NET 桌面开发
|
||||
- 通用 Windows 平台开发
|
||||
- 单个组件
|
||||
- GitHub Extension for Visual Studio(可选)
|
||||
- [Visual Studio Marketplace](https://marketplace.visualstudio.com)
|
||||
- [Avalonia for Visual Studio(可选)](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaVS)
|
||||
- [NUnit VS Templates(可选)](https://marketplace.visualstudio.com/items?itemName=NUnitDevelopers.NUnitTemplatesforVisualStudio)
|
||||
|
||||
[Visual Studio 2026](https://visualstudio.microsoft.com/zh-hans/vs)
|
||||
[JetBrains Rider](https://www.jetbrains.com/rider)
|
||||
[Visual Studio Code](https://code.visualstudio.com)
|
||||
[OpenJDK 17](https://learn.microsoft.com/zh-cn/java/openjdk/download#openjdk-17)
|
||||
[Android Studio Electric Eel 或更高版本](https://developer.android.google.cn/studio)
|
||||
[Xcode 14 或更高版本](https://developer.apple.com/xcode)
|
||||
[Xcode 26 或更高版本](https://developer.apple.com/xcode)
|
||||
|
||||
## 🏗️ [项目结构](./src/README.md)
|
||||
详见 [./src/README.md](./src/README.md)
|
||||
|
||||
-1372
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,178 @@
|
||||
<Solution>
|
||||
<Configurations>
|
||||
<Platform Name="Any CPU" />
|
||||
<Platform Name="ARM" />
|
||||
<Platform Name="ARM64" />
|
||||
<Platform Name="x64" />
|
||||
<Platform Name="x86" />
|
||||
</Configurations>
|
||||
<Folder Name="/0.Root/">
|
||||
<File Path=".github/workflows/CI.yml" />
|
||||
<File Path=".gitignore" />
|
||||
<File Path=".gitmodules" />
|
||||
<File Path="nuget.config" />
|
||||
<File Path="README.en.md" />
|
||||
<File Path="README.md" />
|
||||
<File Path="ref/DirectoryPackages/Directory.Packages.props" />
|
||||
<File Path="src/.editorconfig" />
|
||||
<File Path="src/Directory.Build.props" />
|
||||
</Folder>
|
||||
<Folder Name="/0.Root/Build/">
|
||||
<File Path="build/publish_apphost_linux_x64.sh" />
|
||||
<File Path="build/settings_v4_app.json" />
|
||||
</Folder>
|
||||
<Folder Name="/0.Root/Document/">
|
||||
<File Path="doc/file-system.md" />
|
||||
<File Path="doc/open-source-library.md" />
|
||||
<File Path="doc/openwrt.md" />
|
||||
<File Path="doc/publish.md" />
|
||||
<File Path="doc/release-keylol.md" />
|
||||
</Folder>
|
||||
<Folder Name="/0.Root/Document/Program File Structure/">
|
||||
<File Path="doc/program-file-structure/Linux.md" />
|
||||
<File Path="doc/program-file-structure/macOS.md" />
|
||||
<File Path="doc/program-file-structure/Windows.md" />
|
||||
</Folder>
|
||||
<Folder Name="/0.Root/Source/">
|
||||
<File Path="src/AssemblyInfo.Constants.cs" />
|
||||
<File Path="src/AssemblyInfo.cs" />
|
||||
<File Path="src/AssemblyInfo.Version.Max.cs" />
|
||||
<File Path="src/README.md" />
|
||||
<File Path="src/Utils.cs" />
|
||||
</Folder>
|
||||
<Folder Name="/0.Root/Source/ImplicitUsings/">
|
||||
<File Path="src/ImplicitUsings.ArchiSteamFarm.cs" />
|
||||
<File Path="src/ImplicitUsings.AspNetCore.cs" />
|
||||
<File Path="src/ImplicitUsings.AutoMapper.cs" />
|
||||
<File Path="src/ImplicitUsings.Avalonia.cs" />
|
||||
<File Path="src/ImplicitUsings.BCL.cs" />
|
||||
<File Path="src/ImplicitUsings.Common.cs" />
|
||||
<File Path="src/ImplicitUsings.Controllers.cs" />
|
||||
<File Path="src/ImplicitUsings.Data.cs" />
|
||||
<File Path="src/ImplicitUsings.EntityFrameworkCore.cs" />
|
||||
<File Path="src/ImplicitUsings.Identity.cs" />
|
||||
<File Path="src/ImplicitUsings.Jobs.cs" />
|
||||
<File Path="src/ImplicitUsings.JsonProperty.cs" />
|
||||
<File Path="src/ImplicitUsings.JWT.cs" />
|
||||
<File Path="src/ImplicitUsings.MessagePack.cs" />
|
||||
<File Path="src/ImplicitUsings.Models.cs" />
|
||||
<File Path="src/ImplicitUsings.MSEX.cs" />
|
||||
<File Path="src/ImplicitUsings.Nito.cs" />
|
||||
<File Path="src/ImplicitUsings.NLog.cs" />
|
||||
<File Path="src/ImplicitUsings.Plugins.cs" />
|
||||
<File Path="src/ImplicitUsings.Quartz.cs" />
|
||||
<File Path="src/ImplicitUsings.ReactiveUI.cs" />
|
||||
<File Path="src/ImplicitUsings.Repositories.cs" />
|
||||
<File Path="src/ImplicitUsings.Services.cs" />
|
||||
<File Path="src/ImplicitUsings.Settings.cs" />
|
||||
<File Path="src/ImplicitUsings.SQLite.cs" />
|
||||
<File Path="src/ImplicitUsings.Steam.cs" />
|
||||
<File Path="src/ImplicitUsings.UI.cs" />
|
||||
<File Path="src/ImplicitUsings.UI.ViewModels.cs" />
|
||||
<File Path="src/ImplicitUsings.UI.Views.cs" />
|
||||
<File Path="src/ImplicitUsings.UnitTest.cs" />
|
||||
<File Path="src/ImplicitUsings.WTTS.cs" />
|
||||
</Folder>
|
||||
<Folder Name="/0.Root/TFM/">
|
||||
<File Path="src/TFM_NETX.props" />
|
||||
<File Path="src/TFM_NETX_SINGLE.props" />
|
||||
<File Path="src/TFM_NETX_WINDOWS.props" />
|
||||
<File Path="src/TFM_NETX_WITH_ALL.props" />
|
||||
<File Path="src/TFM_NETX_WITH_DESKTOP.props" />
|
||||
<File Path="src/TFM_NETX_WITH_WINDOWS.props" />
|
||||
</Folder>
|
||||
<Folder Name="/1.AppHost/">
|
||||
<Project Path="src/BD.WTTS.Client.AppHost.Bridge/Steam++.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client.AppHost/BD.WTTS.Client.AppHost.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/2.Reference/" />
|
||||
<Folder Name="/2.Reference/AppCenter/">
|
||||
<Project Path="ref/appcenter-sdk-dotnet/SDK/AppCenter/BD.AppCenter.Any/BD.AppCenter.Any.csproj" />
|
||||
<Project Path="ref/appcenter-sdk-dotnet/SDK/AppCenterAnalytics/BD.AppCenter.Analytics.Any/BD.AppCenter.Analytics.Any.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/2.Reference/Avalonia/">
|
||||
<Project Path="ref/Avalonia.Image2/Avalonia.Gif/Avalonia.Gif.csproj" />
|
||||
<Project Path="ref/Avalonia.Image2/LibAPNG/LibAPNG.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/2.Reference/Common/">
|
||||
<Project Path="ref/Common/src/BD.Common.Area/BD.Common.Area.csproj" />
|
||||
<Project Path="ref/Common/src/BD.Common.Essentials.Maui.Runtime/BD.Common.Essentials.Maui.Runtime.csproj" />
|
||||
<Project Path="ref/Common/src/BD.Common.Essentials.Preferences.SQLite/BD.Common.Essentials.Preferences.SQLite.csproj" />
|
||||
<Project Path="ref/Common/src/BD.Common.Essentials.Primitives/BD.Common.Essentials.Primitives.csproj" />
|
||||
<Project Path="ref/Common/src/BD.Common.Essentials.Utils/BD.Common.Essentials.Utils.csproj" />
|
||||
<Project Path="ref/Common/src/BD.Common.Essentials/BD.Common.Essentials.csproj" />
|
||||
<Project Path="ref/Common/src/BD.Common.Mvvm.ReactiveUI/BD.Common.Mvvm.ReactiveUI.csproj" />
|
||||
<Project Path="ref/Common/src/BD.Common.Mvvm/BD.Common.Mvvm.csproj" />
|
||||
<Project Path="ref/Common/src/BD.Common.Pinyin.TinyPinyin/BD.Common.Pinyin.TinyPinyin.csproj" />
|
||||
<Project Path="ref/Common/src/BD.Common.Pinyin/BD.Common.Pinyin.csproj" />
|
||||
<Project Path="ref/Common/src/BD.Common.Primitives/BD.Common.Primitives.csproj" />
|
||||
<Project Path="ref/Common/src/BD.Common.Repositories.SQLitePCL/BD.Common.Repositories.SQLitePCL.csproj" />
|
||||
<Project Path="ref/Common/src/BD.Common.Security/BD.Common.Security.csproj" />
|
||||
<Project Path="ref/Common/src/BD.Common.Settings/BD.Common.Settings.csproj" />
|
||||
<Project Path="ref/Common/src/BD.Common/BD.Common.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/2.Reference/Steam/">
|
||||
<Project Path="ref/ArchiSteamFarm/ArchiSteamFarm/ArchiSteamFarm.csproj" />
|
||||
<Project Path="ref/Facepunch.Steamworks/Facepunch.Steamworks/Facepunch.Steamworks.Posix.csproj" />
|
||||
<Project Path="ref/Facepunch.Steamworks/Facepunch.Steamworks/Facepunch.Steamworks.Win64.csproj" />
|
||||
<Project Path="ref/Gameloop.Vdf/Gameloop.Vdf/Gameloop.Vdf.csproj" />
|
||||
<Project Path="ref/Steam4NET/Steam4NET/Steam4NET.csproj" />
|
||||
<Project Path="ref/SteamClient/ref/ValveKeyValue/ValveKeyValue/ValveKeyValue/ValveKeyValue.csproj" />
|
||||
<Project Path="ref/SteamClient/src/BD.SteamClient.Models.Protobuf/BD.SteamClient.Models.Protobuf.csproj" />
|
||||
<Project Path="ref/SteamClient/src/BD.SteamClient/BD.SteamClient.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/2.Reference/WinAuth/">
|
||||
<Project Path="ref/WinAuth/src/WinAuth/WinAuth.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/3.Test/">
|
||||
<Project Path="src/BD.WTTS.UnitTest/BD.WTTS.UnitTest.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/4.Shared/">
|
||||
<Project Path="src/BD.Avalonia8.Image2.Compat/BD.Avalonia8.Image2.Compat.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client.Avalonia/BD.WTTS.Client.Avalonia.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client.IPC/BD.WTTS.Client.IPC.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client/BD.WTTS.Client.csproj" />
|
||||
<Project Path="src/OSShuttingDownHelper/OSShuttingDownHelper.shproj" />
|
||||
</Folder>
|
||||
<Folder Name="/4.Shared/Avalonia.Ref/">
|
||||
<Project Path="src/Avalonia.Base.Internals/Avalonia.Base.Internals.csproj" />
|
||||
<Project Path="src/Avalonia.Base/Avalonia.Base.csproj" />
|
||||
<Project Path="src/Avalonia.Controls.Internals/Avalonia.Controls.Internals.csproj" />
|
||||
<Project Path="src/Avalonia.Desktop/Avalonia.Desktop.csproj" />
|
||||
<Project Path="src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj" />
|
||||
<Project Path="src/Avalonia.Native/Avalonia.Native.csproj" />
|
||||
<Project Path="src/Avalonia.Skia.Internals/Avalonia.Skia.Internals.csproj" />
|
||||
<Project Path="src/Avalonia.WebView2/Avalonia.WebView2.csproj" />
|
||||
<Project Path="src/Avalonia.Win32/Avalonia.Win32.csproj" />
|
||||
<Project Path="src/Avalonia.X11/Avalonia.X11.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/4.Shared/ClientSDK/">
|
||||
<Project Path="ref/WTTS.MicroServices.ClientSDK/ref/WTTS.Public/src/BD.WTTS.MicroServices.Primitives.Resources/BD.WTTS.MicroServices.Primitives.Resources.csproj" />
|
||||
<Project Path="ref/WTTS.MicroServices.ClientSDK/ref/WTTS.Public/src/BD.WTTS.Primitives.Resources/BD.WTTS.Primitives.Resources.csproj" />
|
||||
<Project Path="ref/WTTS.MicroServices.ClientSDK/src/BD.WTTS.MicroServices.ClientSDK/BD.WTTS.MicroServices.ClientSDK.csproj" />
|
||||
<Project Path="ref/WTTS.MicroServices.ClientSDK/src/BD.WTTS.MicroServices.Primitives.Models/BD.WTTS.MicroServices.Primitives.Models.csproj" />
|
||||
<Project Path="ref/WTTS.MicroServices.ClientSDK/src/BD.WTTS.MicroServices.Primitives.ViewModels/BD.WTTS.MicroServices.Primitives.ViewModels.csproj" />
|
||||
<Project Path="ref/WTTS.MicroServices.ClientSDK/src/BD.WTTS.MicroServices.Primitives/BD.WTTS.MicroServices.Primitives.csproj" />
|
||||
<Project Path="ref/WTTS.MicroServices.ClientSDK/src/BD.WTTS.Primitives.Models/BD.WTTS.Primitives.Models.csproj" />
|
||||
<Project Path="ref/WTTS.MicroServices.ClientSDK/src/BD.WTTS.Primitives.ViewModels/BD.WTTS.Primitives.ViewModels.csproj" />
|
||||
<Project Path="ref/WTTS.MicroServices.ClientSDK/src/BD.WTTS.Primitives/BD.WTTS.Primitives.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/5.Plugins/">
|
||||
<Project Path="src/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client.Plugins.Accelerator/BD.WTTS.Client.Plugins.Accelerator.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client.Plugins.ArchiSteamFarmPlus/BD.WTTS.Client.Plugins.ArchiSteamFarmPlus.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client.Plugins.Authenticator/BD.WTTS.Client.Plugins.Authenticator.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client.Plugins.GameAccount/BD.WTTS.Client.Plugins.GameAccount.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client.Plugins.GameList/BD.WTTS.Client.Plugins.GameList.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client.Plugins.GameTools/BD.WTTS.Client.Plugins.GameTools.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client.Plugins.SteamIdleCard/BD.WTTS.Client.Plugins.SteamIdleCard.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/6.Tools/">
|
||||
<Project Path="ref/Common/src/BD.Common.Settings.V4.SourceGenerator.Tools/BD.Common.Settings.V4.SourceGenerator.Tools.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client.Avalonia.Designer.HostApp/BD.WTTS.Client.Avalonia.Designer.HostApp.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client.Tools.HostsTest/BD.WTTS.Client.Tools.HostsTest.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client.Tools.OpenSourceLibraryList/BD.WTTS.Client.Tools.OpenSourceLibraryList.csproj" />
|
||||
<Project Path="src/BD.WTTS.Client.Tools.Publish/BD.WTTS.Client.Tools.Publish.csproj" />
|
||||
</Folder>
|
||||
<Project Path="src/BD.WTTS.Client.Avalonia.App/BD.WTTS.Client.Avalonia.App.csproj" />
|
||||
</Solution>
|
||||
+1
-1
Submodule ref/Avalonia.Image2 updated: b8fe54e56b...0f01b21e70
Submodule
+1
Submodule ref/Avalonia8 added at 1260f0054f
+1
-1
Submodule ref/Common updated: 9b75e1c591...799d7da319
+1
-1
Submodule ref/DirectoryPackages updated: 8f5b07fa7e...7c8ce9293b
+1
-1
Submodule ref/Facepunch.Steamworks updated: 4463739be5...0c8f17e363
Submodule
+1
Submodule ref/Fusillade added at e53e10aef3
+1
-1
Submodule ref/Gameloop.Vdf updated: 8e3d3dca40...68728bcec8
+1
-1
Submodule ref/Steam4NET updated: 04f4acd815...693c53d6f3
Submodule ref/SteamAchievementManager updated: 39dbdc9678...881a31e110
+1
-1
Submodule ref/SteamClient updated: 215ee80032...7dadf00b16
Submodule ref/WTTS.MicroServices.ClientSDK updated: 213b92d243...0ee0e4f407
+1
-1
Submodule ref/WinAuth updated: 4e698bc59d...79763b9908
+1
-1
Submodule ref/appcenter-sdk-dotnet updated: 72d75433df...49e120aec0
@@ -5,29 +5,31 @@ namespace BD.WTTS;
|
||||
/// </summary>
|
||||
public static partial class AssemblyInfo
|
||||
{
|
||||
const string Version2 = "3.0";
|
||||
const string Version2 = "3.1";
|
||||
|
||||
/// <summary>
|
||||
/// 语义化应用程序版本
|
||||
/// https://semver.org/lang/zh-CN/
|
||||
/// </summary>
|
||||
public const string Version = $"{Version2}.0";
|
||||
public const string Version = $"{Version2}.{VersionBuild}";
|
||||
|
||||
const string VersionBuild = "0";
|
||||
|
||||
/// <summary>
|
||||
/// 预览版本号,范围 1~9,只增不减
|
||||
/// </summary>
|
||||
const string ver_for_preview = "0";
|
||||
const string ver_for_preview = "5";
|
||||
|
||||
/// <summary>
|
||||
/// RC 版本号,范围 0~9,只增不减
|
||||
/// </summary>
|
||||
const string ver_for_rc = "14";
|
||||
const string ver_for_rc = "0";
|
||||
|
||||
public const string FileVersion = $"{Version2}.{ver_for_rc}2{ver_for_preview}.0";
|
||||
public const string FileVersion = $"{Version2}.2{ver_for_rc}2{ver_for_preview}.0"; // msix 需要保持第四位为0
|
||||
|
||||
//public const string InformationalVersion = Version;
|
||||
public const string InformationalVersion = Version;
|
||||
//public const string InformationalVersion = $"{Version}-preview.{ver_for_preview}";
|
||||
public const string InformationalVersion = $"{Version}-rc.{ver_for_rc}";
|
||||
//public const string InformationalVersion = $"{Version}-rc.{ver_for_rc}";
|
||||
|
||||
#if !APP_HOST
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net$(DotNet_Version)</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Description>提供动图控件,支持 Gif/Apng</Description>
|
||||
<AssemblyName>BD.Avalonia8.Image2</AssemblyName>
|
||||
<RootNamespace>BD.Avalonia8.Image2</RootNamespace>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
<GenerateAssemblyFileVersionAttribute>true</GenerateAssemblyFileVersionAttribute>
|
||||
<GenerateAssemblyInformationalVersionAttribute>true</GenerateAssemblyInformationalVersionAttribute>
|
||||
<GenerateAssemblyVersionAttribute>true</GenerateAssemblyVersionAttribute>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Avalonia -->
|
||||
<PropertyGroup>
|
||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(MSBuildThisFileDirectory)..\..\ref\Avalonia8\src\Sdk\Links\LibAPNG.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="..\ImplicitUsings.Common.cs" />
|
||||
<Compile Remove="..\AssemblyInfo.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)..\..\ref\Avalonia8\src\BD.Avalonia8.Image2\**\*.cs">
|
||||
<LinkBase></LinkBase>
|
||||
</Compile>
|
||||
<Compile Remove="$(MSBuildThisFileDirectory)..\..\ref\Avalonia8\src\BD.Avalonia8.Image2\bin\**\*.cs" />
|
||||
<Compile Remove="$(MSBuildThisFileDirectory)..\..\ref\Avalonia8\src\BD.Avalonia8.Image2\obj\**\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" />
|
||||
<PackageReference Include="ReactiveUI" />
|
||||
<PackageReference Include="ReactiveUI.Fody" />
|
||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\ref\Common\src\BD.Common.Mvvm\BD.Common.Mvvm.csproj" />
|
||||
<ProjectReference Include="..\..\ref\Common\src\BD.Common\BD.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,41 @@
|
||||
// Common\src\BD.Common8.Bcl\Extensions\StreamExtensions.cs
|
||||
|
||||
namespace System.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// 提供对 <see cref="Stream"/> 类型的扩展函数
|
||||
/// </summary>
|
||||
internal static partial class StreamExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int ReadInt32(this Stream stream)
|
||||
{
|
||||
Span<byte> data = stackalloc byte[sizeof(int)];
|
||||
stream.ReadExactly(data);
|
||||
return BitConverter.ToInt32(data);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ushort ReadUInt16(this Stream stream)
|
||||
{
|
||||
Span<byte> data = stackalloc byte[sizeof(ushort)];
|
||||
stream.ReadExactly(data);
|
||||
return BitConverter.ToUInt16(data);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ReadUInt32(this Stream stream)
|
||||
{
|
||||
Span<byte> data = stackalloc byte[sizeof(uint)];
|
||||
stream.ReadExactly(data);
|
||||
return BitConverter.ToUInt32(data);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteUInt32(this Stream stream, uint value)
|
||||
{
|
||||
Span<byte> data = stackalloc byte[sizeof(uint)];
|
||||
BitConverter.TryWriteBytes(data, value);
|
||||
stream.Write(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<ReactiveUI />
|
||||
</Weavers>
|
||||
@@ -0,0 +1,4 @@
|
||||
// steampp
|
||||
using Avalonia.Metadata;
|
||||
|
||||
[assembly: XmlnsDefinition("https://steampp.net/ui", "BD.Avalonia8.Image2")]
|
||||
@@ -0,0 +1,24 @@
|
||||
// <auto-generated/>
|
||||
global using Avalonia;
|
||||
global using Avalonia.Animation;
|
||||
global using Avalonia.Controls;
|
||||
global using Avalonia.Logging;
|
||||
global using Avalonia.Media;
|
||||
global using Avalonia.Media.Imaging;
|
||||
global using Avalonia.Metadata;
|
||||
global using Avalonia.Platform;
|
||||
global using Avalonia.Rendering.Composition;
|
||||
global using Avalonia.Threading;
|
||||
global using Avalonia.VisualTree;
|
||||
global using BD.Avalonia8.Image2;
|
||||
global using BD.Avalonia8.Image2.Decoding;
|
||||
global using System.Extensions;
|
||||
global using System.Numerics;
|
||||
global using System.Reactive.Linq;
|
||||
global using AvaBitmap = Avalonia.Media.Imaging.Bitmap;
|
||||
global using AvaPixelFormat = Avalonia.Platform.PixelFormat;
|
||||
global using AvaPoint = Avalonia.Point;
|
||||
global using AvaRect = Avalonia.Rect;
|
||||
global using AvaSize = Avalonia.Size;
|
||||
global using AvaVector = Avalonia.Vector;
|
||||
global using CommonImageSource = BD.Common.UI.ViewModels.ImageSource.ClipStream;
|
||||
@@ -0,0 +1,5 @@
|
||||
// attributes that are applied to this project.
|
||||
// Project-level suppressions either have no target or are given
|
||||
// a specific target and scoped to a namespace, type, member, etc.
|
||||
|
||||
[assembly: SuppressMessage("Style", "IDE0005:Using 指令是不需要的。", Justification = "<挂起>")]
|
||||
@@ -9,6 +9,6 @@
|
||||
<legacyCorruptedStateExceptionsPolicy enabled="true"/>
|
||||
</runtime>
|
||||
<appSettings>
|
||||
<add key="d" value="7.0.11"/>
|
||||
<add key="d" value="10.0.3"/>
|
||||
</appSettings>
|
||||
</configuration>
|
||||
@@ -10,6 +10,6 @@
|
||||
<legacyCorruptedStateExceptionsPolicy enabled="true"/>
|
||||
</runtime>
|
||||
<appSettings>
|
||||
<add key="d" value="9.0.0"/>
|
||||
<add key="d" value="10.0.3"/>
|
||||
</appSettings>
|
||||
</configuration>
|
||||
@@ -16,9 +16,9 @@ static unsafe partial class Program
|
||||
{
|
||||
static Program()
|
||||
{
|
||||
dotnet_version_major = "9";
|
||||
dotnet_version_major = "10";
|
||||
dotnet_version_minor = "0";
|
||||
dotnet_version_build = "0";
|
||||
dotnet_version_build = "3";
|
||||
dotnet_version = $"{dotnet_version_major}.{dotnet_version_minor}.{dotnet_version_build}";
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
<RootNamespace>BD.WTTS</RootNamespace>
|
||||
<DefineConstants>AVALONIA;$(DefineConstants)</DefineConstants>
|
||||
<ApplicationId>net.steampp.app</ApplicationId>
|
||||
<ServerGarbageCollection>false</ServerGarbageCollection>
|
||||
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
|
||||
<RetainVMGarbageCollection>false</RetainVMGarbageCollection>
|
||||
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -36,6 +33,18 @@
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- 👇 GC 配置 MSBuild 属性 -->
|
||||
<ServerGarbageCollection>false</ServerGarbageCollection>
|
||||
<RetainVMGarbageCollection>false</RetainVMGarbageCollection>
|
||||
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- 👇 GC 配置 runtimeconfig.json -->
|
||||
<RuntimeHostConfigurationOption Include="System.GC.HighMemoryPercent" Value="75" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\ImplicitUsings.MessagePack.cs">
|
||||
<LinkBase>Properties</LinkBase>
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Watt Toolkit</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.0.0</string>
|
||||
<string>3.1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3.0.1219.0</string>
|
||||
<string>3.1.2021.0</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
|
||||
@@ -295,33 +295,42 @@ sealed partial class Program : Startup
|
||||
|
||||
protected override ActiveUserRecordDTO GetActiveUserRecord()
|
||||
{
|
||||
#if !__MOBILE__ && !MAUI
|
||||
var app = UI.App.Instance;
|
||||
var window = app.GetFirstOrDefaultWindow();
|
||||
var screens = window?.Screens;
|
||||
#else
|
||||
var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
|
||||
var mainDisplayInfoH = mainDisplayInfo.Height.ToInt32(NumberToInt32Format.Ceiling);
|
||||
var mainDisplayInfoW = mainDisplayInfo.Width.ToInt32(NumberToInt32Format.Ceiling);
|
||||
#endif
|
||||
var result = new ActiveUserRecordDTO
|
||||
{
|
||||
#if __MOBILE__ || MAUI
|
||||
ScreenCount = 1,
|
||||
PrimaryScreenPixelDensity = mainDisplayInfo.Density,
|
||||
PrimaryScreenWidth = mainDisplayInfoW,
|
||||
PrimaryScreenHeight = mainDisplayInfoH,
|
||||
SumScreenWidth = mainDisplayInfoW,
|
||||
SumScreenHeight = mainDisplayInfoH,
|
||||
#else
|
||||
ScreenCount = screens?.ScreenCount ?? default,
|
||||
PrimaryScreenPixelDensity = screens?.Primary?.Scaling ?? default,
|
||||
PrimaryScreenWidth = screens?.Primary?.Bounds.Width ?? default,
|
||||
PrimaryScreenHeight = screens?.Primary?.Bounds.Height ?? default,
|
||||
SumScreenWidth = screens?.All.Sum(x => x.Bounds.Width) ?? default,
|
||||
SumScreenHeight = screens?.All.Sum(x => x.Bounds.Height) ?? default,
|
||||
#endif
|
||||
};
|
||||
SetScreen(result);
|
||||
static void SetScreen(ActiveUserRecordDTO m)
|
||||
{
|
||||
try
|
||||
{
|
||||
#if !__MOBILE__ && !MAUI
|
||||
var app = UI.App.Instance;
|
||||
var window = app.GetFirstOrDefaultWindow();
|
||||
var screens = window?.Screens;
|
||||
|
||||
m.ScreenCount = screens?.ScreenCount ?? default;
|
||||
m.PrimaryScreenPixelDensity = screens?.Primary?.Scaling ?? default;
|
||||
m.PrimaryScreenWidth = screens?.Primary?.Bounds.Width ?? default;
|
||||
m.PrimaryScreenHeight = screens?.Primary?.Bounds.Height ?? default;
|
||||
m.SumScreenWidth = screens?.All?.Sum(x => x.Bounds.Width) ?? default;
|
||||
m.SumScreenHeight = screens?.All?.Sum(x => x.Bounds.Height) ?? default;
|
||||
#else
|
||||
var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
|
||||
var mainDisplayInfoH = mainDisplayInfo.Height.ToInt32(NumberToInt32Format.Ceiling);
|
||||
var mainDisplayInfoW = mainDisplayInfo.Width.ToInt32(NumberToInt32Format.Ceiling);
|
||||
|
||||
m.ScreenCount = 1;
|
||||
m.PrimaryScreenPixelDensity = mainDisplayInfo.Density;
|
||||
m.PrimaryScreenWidth = mainDisplayInfoW;
|
||||
m.PrimaryScreenHeight = mainDisplayInfoH;
|
||||
m.SumScreenWidth = mainDisplayInfoW;
|
||||
m.SumScreenHeight = mainDisplayInfoH;
|
||||
#endif
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -73,36 +73,34 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BD.Common" />
|
||||
<PackageReference Include="BD.Common.Primitives" />
|
||||
<PackageReference Include="BD.Common.Essentials" />
|
||||
<PackageReference Include="Avalonia" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" />
|
||||
<PackageReference Include="ReactiveUI.Avalonia" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" />
|
||||
<PackageReference Include="Avalonia.Controls.ItemsRepeater" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" />
|
||||
<PackageReference Include="Xaml.Behaviors.Avalonia" />
|
||||
<PackageReference Include="Avalonia.AvaloniaEdit" />
|
||||
<PackageReference Include="AvaloniaEdit.TextMate" />
|
||||
<!--<PackageReference Include="Markdown.Avalonia" />-->
|
||||
<PackageReference Include="FluentAvaloniaUI" />
|
||||
<PackageReference Include="Avalonia.Controls.ColorPicker" />
|
||||
<PackageReference Include="LiveChartsCore.SkiaSharpView.Avalonia" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" />
|
||||
<!--ProjectReference Include="..\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
|
||||
<ProjectReference Include="..\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />-->
|
||||
<ProjectReference Include="..\Avalonia.Desktop\Avalonia.Desktop.csproj" />
|
||||
<ProjectReference Include="..\..\ref\Avalonia.Image2\Avalonia.Gif\Avalonia.Gif.csproj" />
|
||||
<ProjectReference Include="..\BD.Avalonia8.Image2.Compat\BD.Avalonia8.Image2.Compat.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
|
||||
<!-- Windows Only -->
|
||||
<ProjectReference Include="..\Avalonia.X11\Avalonia.X11.csproj" />
|
||||
<ProjectReference Include="..\Avalonia.Native\Avalonia.Native.csproj" />
|
||||
<PackageReference Include="Aigio.Avalonia.AppCenter" />
|
||||
<!--<PackageReference Include="Aigio.Avalonia.AppCenter" />
|
||||
<PackageReference Include="Aigio.Avalonia.AppCenter.Analytics" />
|
||||
<PackageReference Include="Aigio.Avalonia.AppCenter.Crashes" />
|
||||
<PackageReference Include="Aigio.Avalonia.AppCenter.Crashes" />-->
|
||||
<PackageReference Include="Avalonia.Win32" />
|
||||
<PackageReference Include="BD.Common.Essentials.Maui.Runtime" />
|
||||
<ProjectReference Include="..\..\ref\Common\src\BD.Common.Essentials.Maui.Runtime\BD.Common.Essentials.Maui.Runtime.csproj" />
|
||||
<PackageReference Include="JumpLists.Win32.Avalonia" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -111,7 +109,7 @@
|
||||
<ProjectReference Include="..\Avalonia.X11\Avalonia.X11.csproj" />
|
||||
<ProjectReference Include="..\Avalonia.Win32\Avalonia.Win32.csproj" />
|
||||
<PackageReference Include="Avalonia.Native" />
|
||||
<PackageReference Include="BD.Common.Essentials.Maui.Runtime" />
|
||||
<ProjectReference Include="..\..\ref\Common\src\BD.Common.Essentials.Maui.Runtime\BD.Common.Essentials.Maui.Runtime.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == ''">
|
||||
|
||||
@@ -45,7 +45,7 @@ public sealed partial class App : Application
|
||||
{
|
||||
if (ApplicationLifetime is ClassicDesktopStyleApplicationLifetime classicDesktopStyleApplicationLifetime)
|
||||
{
|
||||
window = classicDesktopStyleApplicationLifetime.Windows.FirstOrDefault(x => x != null);
|
||||
window = classicDesktopStyleApplicationLifetime.Windows?.FirstOrDefault(x => x != null);
|
||||
}
|
||||
}
|
||||
return window;
|
||||
|
||||
@@ -40,14 +40,6 @@
|
||||
<Setter Property="FontFamily" Value="{Binding Source={x:Static ms:UISettings.FontName}, Path=Value, Mode=OneWay, Converter={StaticResource NameToFontFamilyConverter}}" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="ui|FontIcon">
|
||||
<Setter Property="MinWidth" Value="16" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="ui|SymbolIcon">
|
||||
<Setter Property="MinWidth" Value="16" />
|
||||
</Style>
|
||||
|
||||
<!--<Style Selector="TextBlock">
|
||||
<Setter Property="FontFamily" Value="{Binding Source={x:Static ms:UISettings.FontName}, Path=Value, Mode=OneWay, Converter={StaticResource NameToFontFamilyConverter}}" />
|
||||
</Style>-->
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<UserControl
|
||||
d:DesignHeight="900"
|
||||
d:DesignWidth="1080"
|
||||
mc:Ignorable="d"
|
||||
x:Class="BD.WTTS.UI.Views.Pages.HomePage"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="spp:HomePageViewModel"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:col="using:System.Collections.Generic"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:s="https://steampp.net/services"
|
||||
xmlns:spp="https://steampp.net/ui"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
d:DesignHeight="900"
|
||||
d:DesignWidth="1080"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="spp:HomePageViewModel"
|
||||
mc:Ignorable="d">
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<!--<spp:PageBase.PreviewImage>
|
||||
<ui:ImageIconSource Source="{Binding IconKey, Mode=OneWay, Converter={StaticResource BitmapAssetValueConverter}}" />
|
||||
</spp:PageBase.PreviewImage>-->
|
||||
@@ -70,87 +70,87 @@
|
||||
<ScrollViewer Padding="20">
|
||||
<!--<spp:ContentLoader IsLoading="False">-->
|
||||
<StackPanel
|
||||
x:Name="HomeRoot"
|
||||
MaxWidth="1080"
|
||||
Spacing="20">
|
||||
Spacing="20"
|
||||
x:Name="HomeRoot">
|
||||
<spp:Snackbar
|
||||
VerticalAlignment="Top"
|
||||
IsClosable="False"
|
||||
IsOpen="True"
|
||||
IsVisible="{spp:OnPlatform !GA}"
|
||||
Message="当前使用的程序版本为测试版本,可能会有各种各样不可言明的 Bug,遇到问题请理性反馈,谢谢。"
|
||||
Severity="Warning" />
|
||||
Severity="Warning"
|
||||
VerticalAlignment="Top" />
|
||||
<spp:Snackbar
|
||||
VerticalAlignment="Top"
|
||||
IsClosable="False"
|
||||
IsOpen="True"
|
||||
IsVisible="{spp:OnPlatform !GA}"
|
||||
Message="如果您不喜欢当前主页为默认启动页面可以在【设置-界面设置-启动时默认打开页面】处更改" />
|
||||
Message="如果您不喜欢当前主页为默认启动页面可以在【设置-界面设置-启动时默认打开页面】处更改"
|
||||
VerticalAlignment="Top" />
|
||||
<spp:Snackbar
|
||||
Title="{Binding Path=Res.Notice_Tray_Title, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
VerticalAlignment="Top"
|
||||
IsClosable="False"
|
||||
IsOpen="True"
|
||||
IsVisible="{Binding !!!MainWindow2.PluginCount, Mode=OneWay, Source={x:Static s:IViewModelManager.Instance}}"
|
||||
Message="检测到你没有安装插件,请去下载插件即可使用更多功能。"
|
||||
Severity="Warning" />
|
||||
Severity="Warning"
|
||||
Title="{Binding Path=Res.Notice_Tray_Title, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
VerticalAlignment="Top" />
|
||||
|
||||
<WrapPanel Margin="5,0">
|
||||
<WrapPanel IsVisible="{Binding Source={x:Static s:SteamConnectService.Current}, Path=CurrentSteamUser, Mode=OneWay, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}">
|
||||
<spp:Image2
|
||||
Width="36"
|
||||
DecodeWidth="36"
|
||||
Height="36"
|
||||
Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
DecodeWidth="36"
|
||||
Source="{Binding Source={x:Static s:SteamConnectService.Current}, Path=CurrentSteamUser.AvatarMedium, Mode=OneWay}"
|
||||
Stretch="Uniform">
|
||||
Stretch="Uniform"
|
||||
VerticalAlignment="Center"
|
||||
Width="36">
|
||||
<spp:Image2.Clip>
|
||||
<EllipseGeometry Rect="0,0,36,36" />
|
||||
</spp:Image2.Clip>
|
||||
</spp:Image2>
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Source={x:Static s:SteamConnectService.Current}, Path=CurrentSteamUser.SteamNickName, Mode=OneWay}"
|
||||
Theme="{StaticResource SubtitleTextBlockStyle}" />
|
||||
Theme="{StaticResource SubtitleTextBlockStyle}"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding Source={x:Static s:SteamConnectService.Current}, Path=CurrentSteamUser.IPCountry, Mode=OneWay, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}"
|
||||
Text="{Binding Source={x:Static s:SteamConnectService.Current}, Path=CurrentSteamUser.IPCountry, Mode=OneWay, StringFormat={}({0})}"
|
||||
Theme="{StaticResource SubtitleTextBlockStyle}" />
|
||||
Theme="{StaticResource SubtitleTextBlockStyle}"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
VerticalAlignment="Center"
|
||||
Text=","
|
||||
Theme="{StaticResource SubtitleTextBlockStyle}" />
|
||||
Theme="{StaticResource SubtitleTextBlockStyle}"
|
||||
VerticalAlignment="Center" />
|
||||
</WrapPanel>
|
||||
<TextBlock VerticalAlignment="Center" Theme="{StaticResource SubtitleTextBlockStyle}">
|
||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}" VerticalAlignment="Center">
|
||||
<Run Text="{Binding Path=Res.Welcome_User_, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
</TextBlock>
|
||||
</WrapPanel>
|
||||
|
||||
<Grid Margin="0,-10,0,0" RowDefinitions="Auto,*">
|
||||
<spp:CarouselBanner
|
||||
MinHeight="350"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
ItemsSource="{Binding NavigationBanners}">
|
||||
ItemsSource="{Binding NavigationBanners}"
|
||||
MinHeight="350"
|
||||
VerticalContentAlignment="Stretch">
|
||||
<spp:CarouselBanner.ItemTemplate>
|
||||
<DataTemplate DataType="spp:AdvertisementDTO">
|
||||
<spp:AppItem
|
||||
Title="{Binding Describe}"
|
||||
Height="350"
|
||||
Classes="Banner Scale"
|
||||
ClickCommand="{Binding OpenBrowserCommand, Source={x:Static spp:App.Instance}}"
|
||||
ClickCommandParameter="{Binding Url}"
|
||||
Description="{Binding Name}"
|
||||
Height="350"
|
||||
Title="{Binding Describe}"
|
||||
ToolTip.Tip="{Binding Describe}">
|
||||
<spp:AppItem.Status>
|
||||
<Border
|
||||
Padding="20,10"
|
||||
Background="{DynamicResource AccentFillColorSelectedTextBackgroundBrush}"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="{StaticResource ControlCornerRadius}">
|
||||
CornerRadius="{StaticResource ControlCornerRadius}"
|
||||
Padding="20,10">
|
||||
<TextBlock FontSize="16" Text="阅读更多" />
|
||||
</Border>
|
||||
</spp:AppItem.Status>
|
||||
@@ -176,22 +176,22 @@
|
||||
Spacing="10">
|
||||
<DockPanel>
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="最新消息"
|
||||
Theme="{StaticResource TitleTextBlockStyle}" />
|
||||
Theme="{StaticResource TitleTextBlockStyle}"
|
||||
VerticalAlignment="Center" />
|
||||
<Button
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding OpenBrowserCommand, Source={x:Static spp:App.Instance}}"
|
||||
CommandParameter="{x:Static spp:HomePageViewModel.OfficialWebsite_Article}"
|
||||
Content="查看更多"
|
||||
DockPanel.Dock="Right"
|
||||
Theme="{StaticResource HyperlinkButton}" />
|
||||
HorizontalAlignment="Right"
|
||||
Theme="{StaticResource HyperlinkButton}"
|
||||
VerticalAlignment="Center" />
|
||||
</DockPanel>
|
||||
|
||||
<Grid Margin="0,0,0,20" ColumnDefinitions="*,10,*,10,*">
|
||||
<Grid ColumnDefinitions="*,10,*,10,*" Margin="0,0,0,20">
|
||||
<Grid.Styles>
|
||||
<Style x:DataType="spp:ArticleItemDTO" Selector="spp|AppItem">
|
||||
<Style Selector="spp|AppItem" x:DataType="spp:ArticleItemDTO">
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
<Setter Property="Title" Value="{Binding Title}" />
|
||||
<Setter Property="Description" Value="{Binding Introduction}" />
|
||||
@@ -225,13 +225,13 @@
|
||||
</Grid.Styles>
|
||||
<spp:AppItem Classes="Vertical Scale" DataContext="{Binding Articles[0]}" />
|
||||
<spp:AppItem
|
||||
Grid.Column="2"
|
||||
Classes="Vertical Scale"
|
||||
DataContext="{Binding Articles[1]}" />
|
||||
DataContext="{Binding Articles[1]}"
|
||||
Grid.Column="2" />
|
||||
<spp:AppItem
|
||||
Grid.Column="4"
|
||||
Classes="Vertical Scale"
|
||||
DataContext="{Binding Articles[2]}" />
|
||||
DataContext="{Binding Articles[2]}"
|
||||
Grid.Column="4" />
|
||||
</Grid>
|
||||
|
||||
<!--<spp:CarouselItems ItemsPerPage="3" ItemsSource="{Binding Articles}">
|
||||
@@ -265,34 +265,34 @@
|
||||
|
||||
<DockPanel>
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="精选"
|
||||
Theme="{StaticResource TitleTextBlockStyle}" />
|
||||
Theme="{StaticResource TitleTextBlockStyle}"
|
||||
VerticalAlignment="Center" />
|
||||
<Button
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding OpenBrowserCommand, Source={x:Static spp:App.Instance}}"
|
||||
CommandParameter="{x:Static spp:HomePageViewModel.WattGameUrl}"
|
||||
Content="访问 WattGame 商城"
|
||||
DockPanel.Dock="Right"
|
||||
Theme="{StaticResource HyperlinkButton}" />
|
||||
HorizontalAlignment="Right"
|
||||
Theme="{StaticResource HyperlinkButton}"
|
||||
VerticalAlignment="Center" />
|
||||
</DockPanel>
|
||||
|
||||
<spp:CarouselItems
|
||||
MinHeight="250"
|
||||
ItemsPerPage="4"
|
||||
ItemsSource="{Binding Shops}">
|
||||
ItemsSource="{Binding Shops}"
|
||||
MinHeight="250">
|
||||
<spp:CarouselItems.ItemTemplate>
|
||||
<DataTemplate x:DataType="spp:ShopRecommendGoodItem">
|
||||
<!--<Border spp:Animations.EnableAnimations="True">-->
|
||||
<spp:AppItem
|
||||
Title="{Binding Name}"
|
||||
MinHeight="250"
|
||||
MaxWidth="260"
|
||||
Classes="Vertical Scale"
|
||||
ClickCommand="{Binding OpenBrowserCommand, Source={x:Static spp:App.Instance}}"
|
||||
Cursor="Hand"
|
||||
MaxWidth="260"
|
||||
MinHeight="250"
|
||||
Tags="{Binding GoodsAllCategorysText}"
|
||||
Title="{Binding Name}"
|
||||
ToolTip.Tip="{Binding Brief}">
|
||||
<spp:AppItem.ClickCommandParameter>
|
||||
<MultiBinding Converter="{StaticResource StringFormatConverter}">
|
||||
@@ -310,31 +310,31 @@
|
||||
<Border Classes="Status">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
IsVisible="{Binding !!DiscountValue}"
|
||||
Text="{Binding Mktprice, Converter={StaticResource StringFormatConverter}, ConverterParameter={x:Static spp:CurrencyCode.CNY}}"
|
||||
TextDecorations="Strikethrough" />
|
||||
TextDecorations="Strikethrough"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="{Binding Price, Converter={StaticResource StringFormatConverter}, ConverterParameter={x:Static spp:CurrencyCode.CNY}}" />
|
||||
Text="{Binding Price, Converter={StaticResource StringFormatConverter}, ConverterParameter={x:Static spp:CurrencyCode.CNY}}"
|
||||
VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</spp:AppItem.Status>
|
||||
<spp:AppItem.ActionButton>
|
||||
<Border
|
||||
Height="24"
|
||||
Padding="8,0"
|
||||
Background="#4c6b22"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="4"
|
||||
IsVisible="{Binding !!DiscountValue}">
|
||||
Height="24"
|
||||
IsVisible="{Binding !!DiscountValue}"
|
||||
Padding="8,0">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Foreground="#beee11"
|
||||
Text="{Binding DiscountValue, StringFormat={}-{0}%}" />
|
||||
Text="{Binding DiscountValue, StringFormat={}-{0}%}"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
</spp:AppItem.ActionButton>
|
||||
</spp:AppItem>
|
||||
@@ -522,9 +522,9 @@
|
||||
<ItemsControl ItemsSource="{Binding MainWindow2.TabItems, Mode=OneWay, Source={x:Static s:IViewModelManager.Instance}}">
|
||||
<ItemsControl.Resources>
|
||||
<ControlTheme
|
||||
x:Key="NavButtonStyle"
|
||||
BasedOn="{StaticResource {x:Type Button}}"
|
||||
TargetType="Button">
|
||||
TargetType="Button"
|
||||
x:Key="NavButtonStyle">
|
||||
<Setter Property="Width" Value="120" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="Margin" Value="6" />
|
||||
@@ -543,28 +543,28 @@
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="spp:TabItemViewModel">
|
||||
<Border spp:Animations.EnableAnimations="True">
|
||||
<Border spp:Animations.EnableAnimations="False">
|
||||
<Button
|
||||
Command="{Binding $parent[spp:HomePage].((spp:HomePageViewModel)DataContext).NavgationToMenuPageCommand}"
|
||||
CommandParameter="{Binding}"
|
||||
Theme="{StaticResource NavButtonStyle}">
|
||||
<Grid RowDefinitions="3*,*">
|
||||
<Border
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
CornerRadius="4 4 0 0"
|
||||
RenderOptions.BitmapInterpolationMode="HighQuality">
|
||||
HorizontalAlignment="Stretch"
|
||||
RenderOptions.BitmapInterpolationMode="HighQuality"
|
||||
VerticalAlignment="Stretch">
|
||||
<spp:Image2
|
||||
Width="96"
|
||||
Height="96"
|
||||
Margin="0,10,0,0"
|
||||
Source="{Binding IconKey}"
|
||||
Stretch="Uniform" />
|
||||
Stretch="Uniform"
|
||||
Width="96" />
|
||||
</Border>
|
||||
<TextBlock
|
||||
FontWeight="SemiBold"
|
||||
Grid.Row="1"
|
||||
Margin="12"
|
||||
FontWeight="SemiBold"
|
||||
Text="{Binding Name}" />
|
||||
</Grid>
|
||||
</Button>
|
||||
@@ -703,9 +703,9 @@
|
||||
</TabControl>-->
|
||||
|
||||
<TextBlock
|
||||
Margin="0,30"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0,30"
|
||||
Text="不忘初心,方得始终。"
|
||||
Theme="{StaticResource SubtitleTextBlockStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
@@ -16,48 +16,48 @@
|
||||
x:CompileBindings="True"
|
||||
x:DataType="spp:MainWindowViewModel"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Styles>
|
||||
<Style Selector="ui|NavigationView.AppNav">
|
||||
<Setter Property="IsPaneToggleButtonVisible" Value="False" />
|
||||
<Setter Property="OpenPaneLength" Value="72" />
|
||||
<Setter Property="IsPaneOpen" Value="True" />
|
||||
</Style>
|
||||
<UserControl.Styles>
|
||||
<Style Selector="ui|NavigationView.AppNav">
|
||||
<Setter Property="IsPaneToggleButtonVisible" Value="False" />
|
||||
<Setter Property="OpenPaneLength" Value="72" />
|
||||
<Setter Property="IsPaneOpen" Value="True" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="ui|NavigationView.AppNav[IsBackButtonVisible=False] SplitView /template/ ContentPresenter#PART_PanePresenter">
|
||||
<Setter Property="Margin" Value="0 42 0 0" />
|
||||
</Style>
|
||||
<Style Selector="ui|NavigationView.AppNav[IsBackButtonVisible=True] SplitView /template/ ContentPresenter#PART_PanePresenter">
|
||||
<Setter Property="Margin" Value="0 2 0 0" />
|
||||
</Style>
|
||||
<Style Selector="ui|NavigationView.AppNav[IsBackButtonVisible=False] SplitView /template/ ContentPresenter#PART_PanePresenter">
|
||||
<Setter Property="Margin" Value="0 42 0 0" />
|
||||
</Style>
|
||||
<Style Selector="ui|NavigationView.AppNav[IsBackButtonVisible=True] SplitView /template/ ContentPresenter#PART_PanePresenter">
|
||||
<Setter Property="Margin" Value="0 2 0 0" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="ui|NavigationViewItem.AppNav">
|
||||
<Setter Property="Transitions">
|
||||
<Setter.Value>
|
||||
<Transitions>
|
||||
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.05" />
|
||||
</Transitions>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style Selector="ui|NavigationViewItem.AppNav">
|
||||
<Setter Property="Transitions">
|
||||
<Setter.Value>
|
||||
<Transitions>
|
||||
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.05" />
|
||||
</Transitions>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter">
|
||||
<Setter Property="Width" Value="72" />
|
||||
<Setter Property="MinHeight" Value="75" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
<Setter Property="CornerRadius" Value="{StaticResource ControlCornerRadius}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Border
|
||||
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter">
|
||||
<Setter Property="Width" Value="72" />
|
||||
<Setter Property="MinHeight" Value="75" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
<Setter Property="CornerRadius" Value="{StaticResource ControlCornerRadius}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Border
|
||||
Name="LayoutRoot"
|
||||
Margin="4,6"
|
||||
Background="{TemplateBinding Background}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
TemplatedControl.IsTemplateFocusTarget="True">
|
||||
<Panel>
|
||||
<Panel HorizontalAlignment="Left" VerticalAlignment="Center">
|
||||
<Panel>
|
||||
<Panel HorizontalAlignment="Left" VerticalAlignment="Center">
|
||||
|
||||
<Border
|
||||
<Border
|
||||
Name="SelectionIndicator"
|
||||
Width="3"
|
||||
Height="20"
|
||||
@@ -65,11 +65,11 @@
|
||||
Background="{DynamicResource NavigationViewSelectionIndicatorForeground}"
|
||||
CornerRadius="{StaticResource ControlCornerRadius}"
|
||||
Opacity="0" />
|
||||
</Panel>
|
||||
</Panel>
|
||||
|
||||
|
||||
<DockPanel>
|
||||
<ContentPresenter
|
||||
<DockPanel>
|
||||
<ContentPresenter
|
||||
Name="ContentPresenter"
|
||||
Grid.Row="1"
|
||||
Margin="0,-15,0,3"
|
||||
@@ -80,80 +80,80 @@
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
DockPanel.Dock="Bottom"
|
||||
FontSize="10">
|
||||
<ContentPresenter.Styles>
|
||||
<Style Selector="TextBlock">
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
</Style>
|
||||
</ContentPresenter.Styles>
|
||||
</ContentPresenter>
|
||||
<ContentPresenter.Styles>
|
||||
<Style Selector="TextBlock">
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
</Style>
|
||||
</ContentPresenter.Styles>
|
||||
</ContentPresenter>
|
||||
|
||||
<Viewbox
|
||||
<Viewbox
|
||||
Name="IconBox"
|
||||
Height="28"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<ContentPresenter Name="Icon" Content="{Binding TemplateSettings.Icon, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
</Viewbox>
|
||||
<ContentPresenter Name="Icon" Content="{Binding TemplateSettings.Icon, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
</Viewbox>
|
||||
|
||||
</DockPanel>
|
||||
</Panel>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</Style>
|
||||
</DockPanel>
|
||||
</Panel>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pointerover /template/ ContentPresenter#ContentPresenter">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
</Style>
|
||||
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pointerover /template/ ContentPresenter#Icon">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
</Style>
|
||||
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pointerover /template/ ContentPresenter#ContentPresenter">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
</Style>
|
||||
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pointerover /template/ ContentPresenter#Icon">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pressed /template/ ContentPresenter#ContentPresenter">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
</Style>
|
||||
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pressed /template/ ContentPresenter#Icon">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
</Style>
|
||||
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pressed /template/ ContentPresenter#ContentPresenter">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
</Style>
|
||||
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pressed /template/ ContentPresenter#Icon">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:selected /template/ ContentPresenter#ContentPresenter">
|
||||
<Setter Property="IsVisible" Value="False" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource AccentFillColorDefaultBrush}" />
|
||||
</Style>
|
||||
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:selected /template/ ContentPresenter#ContentPresenter">
|
||||
<Setter Property="IsVisible" Value="False" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource AccentFillColorDefaultBrush}" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:selected /template/ ContentPresenter#Icon">
|
||||
<Setter Property="Foreground" Value="{DynamicResource AccentFillColorDefaultBrush}" />
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:selected /template/ ContentPresenter#Icon">
|
||||
<Setter Property="Foreground" Value="{DynamicResource AccentFillColorDefaultBrush}" />
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
|
||||
<Grid RowDefinitions="Auto, *">
|
||||
<spp:TitleBar Name="TitleBarHost">
|
||||
<spp:TitleBar.ActionContent>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<DropDownButton
|
||||
<Grid RowDefinitions="Auto, *">
|
||||
<spp:TitleBar Name="TitleBarHost">
|
||||
<spp:TitleBar.ActionContent>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<DropDownButton
|
||||
Width="40"
|
||||
Height="40"
|
||||
HorizontalContentAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Command="{Binding Path=MarkNotificationsHasRead, Mode=OneWay, Source={x:Static s:NoticeService.Current}}"
|
||||
Theme="{DynamicResource TransparentButton}">
|
||||
<Panel>
|
||||
<ui:InfoBadge
|
||||
<Panel>
|
||||
<ui:InfoBadge
|
||||
Margin="0,-3,-3,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Classes="Attention Dot"
|
||||
IsVisible="{Binding Path=!!UnreadNotificationsCount, Mode=OneWay, Source={x:Static s:NoticeService.Current}}" />
|
||||
<Viewbox
|
||||
<Viewbox
|
||||
Width="22"
|
||||
Height="22"
|
||||
VerticalAlignment="Center">
|
||||
<ui:SymbolIcon Symbol="Alert" />
|
||||
</Viewbox>
|
||||
</Panel>
|
||||
<ui:SymbolIcon Symbol="Alert" />
|
||||
</Viewbox>
|
||||
</Panel>
|
||||
|
||||
<DropDownButton.Flyout>
|
||||
<!--<ui:FAMenuFlyout
|
||||
<DropDownButton.Flyout>
|
||||
<!--<ui:FAMenuFlyout
|
||||
FlyoutPresenterClasses="NoPadding"
|
||||
ItemsSource="{Binding Path=Notices, Mode=OneWay, Source={x:Static s:NoticeService.Current}}"
|
||||
Placement="BottomEdgeAlignedRight">
|
||||
@@ -166,79 +166,81 @@
|
||||
</DataTemplate>
|
||||
</ui:FAMenuFlyout.ItemTemplate>
|
||||
</ui:FAMenuFlyout>-->
|
||||
<Flyout FlyoutPresenterClasses="NoPadding" Placement="BottomEdgeAlignedRight">
|
||||
<spp:NoticeFlyout Margin="5" />
|
||||
</Flyout>
|
||||
</DropDownButton.Flyout>
|
||||
</DropDownButton>
|
||||
<Flyout FlyoutPresenterClasses="NoPadding" Placement="BottomEdgeAlignedRight">
|
||||
<spp:NoticeFlyout Margin="5" />
|
||||
</Flyout>
|
||||
</DropDownButton.Flyout>
|
||||
</DropDownButton>
|
||||
|
||||
<DropDownButton Padding="4" Theme="{DynamicResource TransparentButton}">
|
||||
<Border
|
||||
<DropDownButton Padding="4" Theme="{DynamicResource TransparentButton}">
|
||||
<Border
|
||||
Width="32"
|
||||
Height="32"
|
||||
Background="{DynamicResource ControlOnImageFillColorDefaultBrush}"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="30">
|
||||
<spp:Image2
|
||||
<spp:Image2
|
||||
DecodeWidth="36"
|
||||
FallbackSource="{StaticResource DefaultAvatar}"
|
||||
RenderOptions.BitmapInterpolationMode="HighQuality"
|
||||
KeepSourceStreamOpen="True"
|
||||
Source="{Binding Path=AvatarPath, Mode=OneWay, Source={x:Static s:UserService.Current}}" />
|
||||
</Border>
|
||||
<DropDownButton.Flyout>
|
||||
<MenuFlyout FlyoutPresenterClasses="NoPadding" Placement="BottomEdgeAlignedRight">
|
||||
<MenuItem
|
||||
</Border>
|
||||
<DropDownButton.Flyout>
|
||||
<MenuFlyout FlyoutPresenterClasses="NoPadding" Placement="BottomEdgeAlignedRight">
|
||||
<MenuItem
|
||||
Command="{Binding Path=ShowWindow, Source={x:Static s:UserService.Current}}"
|
||||
CommandParameter="{x:Static spp:AppEndPoint.LoginOrRegister}"
|
||||
Cursor="Hand"
|
||||
IsHitTestVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}}">
|
||||
<MenuItem.Header>
|
||||
<DockPanel>
|
||||
<DockPanel DockPanel.Dock="Top">
|
||||
<Border
|
||||
<MenuItem.Header>
|
||||
<DockPanel>
|
||||
<DockPanel DockPanel.Dock="Top">
|
||||
<Border
|
||||
Width="64"
|
||||
Height="64"
|
||||
Margin="0,0,8,0"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="64"
|
||||
DockPanel.Dock="Left">
|
||||
<spp:Image2
|
||||
<spp:Image2
|
||||
DecodeWidth="64"
|
||||
FallbackSource="{StaticResource DefaultAvatar}"
|
||||
RenderOptions.BitmapInterpolationMode="HighQuality"
|
||||
KeepSourceStreamOpen="True"
|
||||
Source="{Binding Path=AvatarPath, Mode=OneWay, Source={x:Static s:UserService.Current}}" />
|
||||
</Border>
|
||||
<Panel>
|
||||
<TextBlock
|
||||
</Border>
|
||||
<Panel>
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}}"
|
||||
Text="{Binding Path=Res.User_UnLogin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
|
||||
<StackPanel VerticalAlignment="Center" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}">
|
||||
<TextBlock Text="{Binding Path=User.NickName, Mode=OneWay, Source={x:Static s:UserService.Current}}" />
|
||||
<Border
|
||||
<StackPanel VerticalAlignment="Center" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}">
|
||||
<TextBlock Text="{Binding Path=User.NickName, Mode=OneWay, Source={x:Static s:UserService.Current}}" />
|
||||
<Border
|
||||
Padding="5,1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
CornerRadius="2"
|
||||
IsVisible="{Binding Path=User.UserType, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource EnumEqualValueConverter}, ConverterParameter={x:Static enum:UserType.Sponsor}}">
|
||||
<Border.Background>
|
||||
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
|
||||
<GradientStop Offset="0.0" Color="#453a94" />
|
||||
<GradientStop Offset="1.0" Color="#f43b47" />
|
||||
</LinearGradientBrush>
|
||||
</Border.Background>
|
||||
<TextBlock
|
||||
<Border.Background>
|
||||
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
|
||||
<GradientStop Offset="0.0" Color="#453a94" />
|
||||
<GradientStop Offset="1.0" Color="#f43b47" />
|
||||
</LinearGradientBrush>
|
||||
</Border.Background>
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
FontSize="10"
|
||||
Foreground="White"
|
||||
Text="{Binding Path=Res.User_Sponsor, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}❤️{0}}" />
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
</StackPanel>
|
||||
</Panel>
|
||||
</DockPanel>
|
||||
<!--<WrapPanel HorizontalAlignment="Center" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}">
|
||||
</StackPanel>
|
||||
</Panel>
|
||||
</DockPanel>
|
||||
<!--<WrapPanel HorizontalAlignment="Center" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}">
|
||||
<Button Margin="5,0" FontSize="10">
|
||||
<ToolTip.Tip>
|
||||
<MultiBinding StringFormat="{}{0} : {1} / {2}">
|
||||
@@ -273,33 +275,33 @@
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</WrapPanel>-->
|
||||
</DockPanel>
|
||||
</MenuItem.Header>
|
||||
</MenuItem>
|
||||
<MenuItem Header="-" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
|
||||
<MenuItem
|
||||
</DockPanel>
|
||||
</MenuItem.Header>
|
||||
</MenuItem>
|
||||
<MenuItem Header="-" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
|
||||
<MenuItem
|
||||
Command="{Binding Path=OpenAuthOfficialCommand, Source={x:Static s:UserService.Current}}"
|
||||
CommandParameter="https://steampp.net/account/center/info"
|
||||
Header="{Binding Path=Res.PersonalCenter, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
|
||||
<!--<MenuItem Header="{Binding Path=Res.MyOrder, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />-->
|
||||
<MenuItem
|
||||
<!--<MenuItem Header="{Binding Path=Res.MyOrder, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />-->
|
||||
<MenuItem
|
||||
Command="{Binding Path=OpenAuthOfficialCommand, Source={x:Static s:UserService.Current}}"
|
||||
CommandParameter="https://steampp.net/account/center/bind"
|
||||
Header="{Binding Path=Res.BindThirdPartyAccount, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
|
||||
<MenuItem
|
||||
<MenuItem
|
||||
Command="{Binding Path=OpenAuthOfficialCommand, Source={x:Static s:UserService.Current}}"
|
||||
CommandParameter="https://steampp.net/account/center/safe"
|
||||
Header="{Binding Path=Res.PersonalSafe, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
|
||||
<MenuItem Header="-" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
|
||||
<MenuItem
|
||||
<MenuItem Header="-" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
|
||||
<MenuItem
|
||||
Command="{Binding Path=SignOut, Mode=OneWay, Source={x:Static s:UserService.Current}}"
|
||||
Foreground="{DynamicResource SystemFillColorCriticalBrush}"
|
||||
Header="{Binding Path=Res.SignOut, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
|
||||
<!--<MenuItem
|
||||
<!--<MenuItem
|
||||
Command="{Binding Path=ShowWindow,
|
||||
Source={x:Static s:UserService.Current}}"
|
||||
CommandParameter="{x:Static spp:AppEndPoint.LoginOrRegister}"
|
||||
@@ -311,14 +313,14 @@
|
||||
Mode=OneWay,
|
||||
Source={x:Static s:UserService.Current},
|
||||
Converter={StaticResource IsNullConverter}}" />-->
|
||||
</MenuFlyout>
|
||||
</DropDownButton.Flyout>
|
||||
</DropDownButton>
|
||||
</StackPanel>
|
||||
</spp:TitleBar.ActionContent>
|
||||
</spp:TitleBar>
|
||||
</MenuFlyout>
|
||||
</DropDownButton.Flyout>
|
||||
</DropDownButton>
|
||||
</StackPanel>
|
||||
</spp:TitleBar.ActionContent>
|
||||
</spp:TitleBar>
|
||||
|
||||
<ui:NavigationView
|
||||
<ui:NavigationView
|
||||
Name="NavView"
|
||||
Grid.RowSpan="2"
|
||||
AlwaysShowHeader="False"
|
||||
@@ -330,35 +332,35 @@
|
||||
IsSettingsVisible="False"
|
||||
MenuItemsSource="{Binding TabItems}"
|
||||
PaneDisplayMode="Left">
|
||||
<ui:NavigationView.Resources>
|
||||
<!-- Override this -->
|
||||
<Thickness x:Key="NavigationViewContentMargin">0,50,0,0</Thickness>
|
||||
</ui:NavigationView.Resources>
|
||||
<ui:NavigationView.Styles>
|
||||
<Style Selector="Button#NavigationViewBackButton">
|
||||
<Setter Property="FontFamily" Value="{DynamicResource SymbolThemeFontFamily}" />
|
||||
<Setter Property="Width" Value="36" />
|
||||
<Setter Property="Height" Value="36" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="Content" Value="" />
|
||||
</Style>
|
||||
<Style Selector="Grid#PaneToggleButtonGrid">
|
||||
<Setter Property="Margin" Value="5 0 0 0" />
|
||||
<Setter Property="ToolTip.Tip" Value="{Binding Path=Res.Back, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
<Setter Property="ToolTip.Placement" Value="Bottom" />
|
||||
</Style>
|
||||
</ui:NavigationView.Styles>
|
||||
<ui:NavigationView.MenuItemTemplate>
|
||||
<DataTemplate DataType="spp:TabItemViewModel">
|
||||
<ui:NavigationViewItem Classes="AppNav" Content="{Binding Name}">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<!--<ui:FontIconSource FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="{Binding IconKey, Converter={StaticResource ResourceKeyValueConverter}, ConverterParameter=fonticon}" />-->
|
||||
<ui:ImageIconSource Source="{Binding IconKey, Converter={StaticResource BitmapAssetValueConverter}}" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
</DataTemplate>
|
||||
</ui:NavigationView.MenuItemTemplate>
|
||||
<ui:Frame Name="FrameView" />
|
||||
</ui:NavigationView>
|
||||
</Grid>
|
||||
<ui:NavigationView.Resources>
|
||||
<!-- Override this -->
|
||||
<Thickness x:Key="NavigationViewContentMargin">0,50,0,0</Thickness>
|
||||
</ui:NavigationView.Resources>
|
||||
<ui:NavigationView.Styles>
|
||||
<Style Selector="Button#NavigationViewBackButton">
|
||||
<Setter Property="FontFamily" Value="{DynamicResource SymbolThemeFontFamily}" />
|
||||
<Setter Property="Width" Value="36" />
|
||||
<Setter Property="Height" Value="36" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="Content" Value="" />
|
||||
</Style>
|
||||
<Style Selector="Grid#PaneToggleButtonGrid">
|
||||
<Setter Property="Margin" Value="5 0 0 0" />
|
||||
<Setter Property="ToolTip.Tip" Value="{Binding Path=Res.Back, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
<Setter Property="ToolTip.Placement" Value="Bottom" />
|
||||
</Style>
|
||||
</ui:NavigationView.Styles>
|
||||
<ui:NavigationView.MenuItemTemplate>
|
||||
<DataTemplate DataType="spp:TabItemViewModel">
|
||||
<ui:NavigationViewItem Classes="AppNav" Content="{Binding Name}">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<!--<ui:FontIconSource FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="{Binding IconKey, Converter={StaticResource ResourceKeyValueConverter}, ConverterParameter=fonticon}" />-->
|
||||
<ui:ImageIconSource Source="{Binding IconKey, Converter={StaticResource BitmapAssetValueConverter}}" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
</DataTemplate>
|
||||
</ui:NavigationView.MenuItemTemplate>
|
||||
<ui:Frame Name="FrameView" />
|
||||
</ui:NavigationView>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="spp:TabItemViewModel">
|
||||
<Border spp:Animations.EnableAnimations="True">
|
||||
<Border spp:Animations.EnableAnimations="False">
|
||||
<Button
|
||||
Command="{Binding $parent[spp:PluginStorePage].((spp:PluginStorePageViewModel)DataContext).NavgationToMenuPageCommand}"
|
||||
CommandParameter="{Binding}"
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<UserControl
|
||||
d:DesignHeight="680"
|
||||
d:DesignWidth="1080"
|
||||
mc:Ignorable="d"
|
||||
x:Class="BD.WTTS.UI.Views.Pages.Settings_UI"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="spp:SettingsPageViewModel"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:com="https://steampp.net/common"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:enum="using:BD.WTTS.Enums"
|
||||
@@ -11,11 +15,7 @@
|
||||
xmlns:s="https://steampp.net/services"
|
||||
xmlns:spp="https://steampp.net/ui"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
d:DesignHeight="680"
|
||||
d:DesignWidth="1080"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="spp:SettingsPageViewModel"
|
||||
mc:Ignorable="d">
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<StackPanel Spacing="{StaticResource SettingsSpacing}">
|
||||
<TextBlock
|
||||
Margin="0,0,0,10"
|
||||
@@ -82,125 +82,125 @@
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<RadioButton Content="{Binding Path=Res.Settings_UI_SystemDefault, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" IsChecked="{Binding Source={x:Static ms:UISettings.UseSystemThemeAccent}, Path=Value, Mode=TwoWay}" />
|
||||
<RadioButton
|
||||
x:Name="AccentCustom"
|
||||
Content="{Binding Path=Res.Settings_UI_Custom, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
IsChecked="{Binding Source={x:Static ms:UISettings.UseSystemThemeAccent}, Path=!Value, Mode=OneWay}" />
|
||||
IsChecked="{Binding Source={x:Static ms:UISettings.UseSystemThemeAccent}, Path=!Value, Mode=OneWay}"
|
||||
x:Name="AccentCustom" />
|
||||
</StackPanel>
|
||||
</spp:OptionsDisplayItem.ActionButton>
|
||||
<spp:OptionsDisplayItem.Content>
|
||||
<StackPanel Spacing="4">
|
||||
<DockPanel Margin="24,0,0,6" LastChildFill="False">
|
||||
<DockPanel LastChildFill="False" Margin="24,0,0,6">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
Text="{Binding Path=Res.Settings_UI_PreviewColor, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
Text="{Binding Path=Res.Settings_UI_PreviewColor, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
VerticalAlignment="Center" />
|
||||
|
||||
<Grid
|
||||
HorizontalAlignment="Left"
|
||||
ColumnDefinitions="*,*"
|
||||
DockPanel.Dock="Right"
|
||||
HorizontalAlignment="Left"
|
||||
RowDefinitions="*,*,*,*">
|
||||
<Border
|
||||
Background="{DynamicResource SystemAccentColor}"
|
||||
Grid.ColumnSpan="2"
|
||||
Height="40"
|
||||
Background="{DynamicResource SystemAccentColor}">
|
||||
Height="40">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource TextOnAccentFillColorPrimaryBrush}"
|
||||
Text="SystemAccentColor" />
|
||||
HorizontalAlignment="Center"
|
||||
Text="SystemAccentColor"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
Background="{DynamicResource SystemAccentColorLight1}"
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Width="90"
|
||||
Height="40"
|
||||
Background="{DynamicResource SystemAccentColorLight1}">
|
||||
Width="90">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource TextOnAccentFillColorPrimaryBrush}"
|
||||
Text="Light1" />
|
||||
HorizontalAlignment="Center"
|
||||
Text="Light1"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<Border
|
||||
Background="{DynamicResource SystemAccentColorLight2}"
|
||||
Grid.Column="0"
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Width="90"
|
||||
Height="40"
|
||||
Background="{DynamicResource SystemAccentColorLight2}">
|
||||
Width="90">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource TextOnAccentFillColorPrimaryBrush}"
|
||||
Text="Light2" />
|
||||
HorizontalAlignment="Center"
|
||||
Text="Light2"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<Border
|
||||
Grid.Row="3"
|
||||
Background="{DynamicResource SystemAccentColorLight3}"
|
||||
Grid.Column="0"
|
||||
Width="90"
|
||||
Grid.Row="3"
|
||||
Height="40"
|
||||
Background="{DynamicResource SystemAccentColorLight3}">
|
||||
Width="90">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource TextOnAccentFillColorPrimaryBrush}"
|
||||
Text="Light3" />
|
||||
HorizontalAlignment="Center"
|
||||
Text="Light3"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
Background="{DynamicResource SystemAccentColorDark1}"
|
||||
Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="90"
|
||||
Height="40"
|
||||
Background="{DynamicResource SystemAccentColorDark1}">
|
||||
Width="90">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource TextOnAccentFillColorPrimaryBrush}"
|
||||
Text="Dark1" />
|
||||
HorizontalAlignment="Center"
|
||||
Text="Dark1"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<Border
|
||||
Background="{DynamicResource SystemAccentColorDark2}"
|
||||
Grid.Column="1"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="90"
|
||||
Height="40"
|
||||
Background="{DynamicResource SystemAccentColorDark2}">
|
||||
Width="90">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource TextOnAccentFillColorPrimaryBrush}"
|
||||
Text="Dark2" />
|
||||
HorizontalAlignment="Center"
|
||||
Text="Dark2"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<Border
|
||||
Grid.Row="3"
|
||||
Background="{DynamicResource SystemAccentColorDark3}"
|
||||
Grid.Column="1"
|
||||
Width="90"
|
||||
Grid.Row="3"
|
||||
Height="40"
|
||||
Background="{DynamicResource SystemAccentColorDark3}">
|
||||
Width="90">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource TextOnAccentFillColorPrimaryBrush}"
|
||||
Text="Dark3" />
|
||||
HorizontalAlignment="Center"
|
||||
Text="Dark3"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
|
||||
<Border
|
||||
Background="{DynamicResource ApplicationPageBackgroundThemeBrush}"
|
||||
Height="1"
|
||||
Margin="-20"
|
||||
Background="{DynamicResource ApplicationPageBackgroundThemeBrush}" />
|
||||
Margin="-20" />
|
||||
|
||||
|
||||
<TextBlock Margin="24,24,0,0" Text="Windows 颜色" />
|
||||
|
||||
<ListBox
|
||||
Name="ColorListBox"
|
||||
MaxWidth="441"
|
||||
Margin="24,0,24,12"
|
||||
HorizontalAlignment="Left"
|
||||
AutoScrollToSelectedItem="False"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="24,0,24,12"
|
||||
MaxWidth="441"
|
||||
Name="ColorListBox"
|
||||
SelectedItem="{Binding Source={x:Static ms:UISettings.ThemeAccent}, Converter={StaticResource ColorStringToBrushConverter}, Path=Value, Mode=TwoWay}">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
@@ -218,22 +218,22 @@
|
||||
<ControlTemplate>
|
||||
<Panel>
|
||||
<Border
|
||||
Name="Root"
|
||||
BorderThickness="2"
|
||||
CornerRadius="{StaticResource ControlCornerRadius}">
|
||||
CornerRadius="{StaticResource ControlCornerRadius}"
|
||||
Name="Root">
|
||||
<Border.Background>
|
||||
<SolidColorBrush Color="{Binding}" />
|
||||
</Border.Background>
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
Name="Check"
|
||||
Width="20"
|
||||
Background="{DynamicResource FocusStrokeColorOuter}"
|
||||
Height="20"
|
||||
Margin="0,2,2,0"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0,2,2,0"
|
||||
Name="Check"
|
||||
VerticalAlignment="Top"
|
||||
Background="{DynamicResource FocusStrokeColorOuter}">
|
||||
Width="20">
|
||||
<ui:SymbolIcon
|
||||
FontSize="18"
|
||||
Foreground="{DynamicResource SystemAccentColor}"
|
||||
@@ -261,26 +261,17 @@
|
||||
</ListBox>
|
||||
|
||||
<Border
|
||||
Background="{DynamicResource ApplicationPageBackgroundThemeBrush}"
|
||||
Height="1"
|
||||
Margin="-20"
|
||||
Background="{DynamicResource ApplicationPageBackgroundThemeBrush}" />
|
||||
Margin="-20" />
|
||||
|
||||
<DockPanel Margin="24,6,0,0" LastChildFill="False">
|
||||
<DockPanel LastChildFill="False" Margin="24,6,0,0">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
Text="{Binding Path=Res.Settings_UI_CustomColor, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
Text="{Binding Path=Res.Settings_UI_CustomColor, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
VerticalAlignment="Center" />
|
||||
|
||||
<ui:ColorPickerButton
|
||||
DockPanel.Dock="Right"
|
||||
IsCompact="True"
|
||||
IsMoreButtonVisible="True"
|
||||
ShowAcceptDismissButtons="True"
|
||||
UseColorPalette="False"
|
||||
UseColorTriangle="False"
|
||||
UseColorWheel="False"
|
||||
UseSpectrum="True"
|
||||
Color="{Binding Source={x:Static ms:UISettings.ThemeAccent}, Converter={StaticResource ColorStringToBrushConverter}, Path=Value, Mode=TwoWay}" />
|
||||
<ColorPicker Color="{Binding Source={x:Static ms:UISettings.ThemeAccent}, Converter={StaticResource ColorStringToBrushConverter}, Path=Value, Mode=TwoWay}" DockPanel.Dock="Right" />
|
||||
</DockPanel>
|
||||
</StackPanel>
|
||||
</spp:OptionsDisplayItem.Content>
|
||||
@@ -298,7 +289,7 @@
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<DataTemplate.DataType>
|
||||
<x:Type x:TypeArguments="x:String, x:String" TypeName="generic:KeyValuePair" />
|
||||
<x:Type TypeName="generic:KeyValuePair" x:TypeArguments="x:String, x:String" />
|
||||
</DataTemplate.DataType>
|
||||
<TextBlock Text="{Binding Value, Mode=OneWay}" />
|
||||
</DataTemplate>
|
||||
@@ -321,7 +312,7 @@
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<DataTemplate.DataType>
|
||||
<x:Type x:TypeArguments="x:String, x:String" TypeName="generic:KeyValuePair" />
|
||||
<x:Type TypeName="generic:KeyValuePair" x:TypeArguments="x:String, x:String" />
|
||||
</DataTemplate.DataType>
|
||||
<TextBlock FontFamily="{Binding Value, Mode=OneWay, Converter={StaticResource NameToFontFamilyConverter}}" Text="{Binding Key, Mode=OneWay}" />
|
||||
</DataTemplate>
|
||||
@@ -360,8 +351,8 @@
|
||||
</spp:OptionsDisplayItem.Icon>
|
||||
<spp:OptionsDisplayItem.ActionButton>
|
||||
<Slider
|
||||
MinWidth="250"
|
||||
Maximum="1.0"
|
||||
MinWidth="250"
|
||||
Minimum="0"
|
||||
Value="{Binding Value, Source={x:Static ms:UISettings.WindowBackgroundOpacity}, Mode=TwoWay}" />
|
||||
</spp:OptionsDisplayItem.ActionButton>
|
||||
@@ -381,72 +372,72 @@
|
||||
|
||||
<spp:OptionsDisplayItem.Content>
|
||||
<StackPanel Spacing="4">
|
||||
<DockPanel Margin="24,0,0,6" LastChildFill="False">
|
||||
<DockPanel LastChildFill="False" Margin="24,0,0,6">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
Text="{Binding Path=Res.PreviewPictures, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
Text="{Binding Path=Res.PreviewPictures, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
VerticalAlignment="Center" />
|
||||
|
||||
<Image
|
||||
Width="250"
|
||||
DockPanel.Dock="Right"
|
||||
Height="150"
|
||||
HorizontalAlignment="Left"
|
||||
DockPanel.Dock="Right"
|
||||
RenderOptions.BitmapInterpolationMode="HighQuality"
|
||||
Source="{Binding Source={x:Static ms:UISettings.WindowBackgroundCustomImagePath}, Path=Value, Mode=OneWay, Converter={StaticResource BitmapAssetValueConverter}}"
|
||||
Stretch="{Binding Source={x:Static ms:UISettings.WindowBackgroundCustomImageStretch}, Path=Value, Mode=OneWay, Converter={StaticResource EnumConverter}, ConverterParameter={x:Type Stretch}}" />
|
||||
Stretch="{Binding Source={x:Static ms:UISettings.WindowBackgroundCustomImageStretch}, Path=Value, Mode=OneWay, Converter={StaticResource EnumConverter}, ConverterParameter={x:Type Stretch}}"
|
||||
Width="250" />
|
||||
</DockPanel>
|
||||
|
||||
<Border
|
||||
Background="{DynamicResource ApplicationPageBackgroundThemeBrush}"
|
||||
Height="1"
|
||||
Margin="-20"
|
||||
Background="{DynamicResource ApplicationPageBackgroundThemeBrush}" />
|
||||
Margin="-20" />
|
||||
|
||||
|
||||
<DockPanel Margin="24,6,0,6" LastChildFill="False">
|
||||
<DockPanel LastChildFill="False" Margin="24,6,0,6">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
Text="{Binding Path=Res.Settings_UI_BackgroundImageOpacity, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
Text="{Binding Path=Res.Settings_UI_BackgroundImageOpacity, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
VerticalAlignment="Center" />
|
||||
<Slider
|
||||
MinWidth="250"
|
||||
DockPanel.Dock="Right"
|
||||
Maximum="1.0"
|
||||
MinWidth="250"
|
||||
Minimum="0"
|
||||
Value="{Binding Value, Source={x:Static ms:UISettings.WindowBackgroundCustomImageOpacity}, Mode=TwoWay}" />
|
||||
</DockPanel>
|
||||
|
||||
<Border
|
||||
Background="{DynamicResource ApplicationPageBackgroundThemeBrush}"
|
||||
Height="1"
|
||||
Margin="-20"
|
||||
Background="{DynamicResource ApplicationPageBackgroundThemeBrush}" />
|
||||
Margin="-20" />
|
||||
|
||||
|
||||
<DockPanel Margin="24,6,0,6" LastChildFill="False">
|
||||
<DockPanel LastChildFill="False" Margin="24,6,0,6">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
Text="{Binding Path=Res.SelectPicture, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
Text="{Binding Path=Res.SelectPicture, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
VerticalAlignment="Center" />
|
||||
<Button
|
||||
MinWidth="150"
|
||||
Command="{Binding SelectImage_Click}"
|
||||
Content="{Binding Path=Res.Settings_UI_SelectImage, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
DockPanel.Dock="Right" />
|
||||
DockPanel.Dock="Right"
|
||||
MinWidth="150" />
|
||||
</DockPanel>
|
||||
|
||||
<Border
|
||||
Background="{DynamicResource ApplicationPageBackgroundThemeBrush}"
|
||||
Height="1"
|
||||
Margin="-20"
|
||||
Background="{DynamicResource ApplicationPageBackgroundThemeBrush}" />
|
||||
Margin="-20" />
|
||||
|
||||
<DockPanel Margin="24,6,0,0" LastChildFill="False">
|
||||
<DockPanel LastChildFill="False" Margin="24,6,0,0">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
Text="{Binding Path=Res.PictureZoom, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
Text="{Binding Path=Res.PictureZoom, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
VerticalAlignment="Center" />
|
||||
<ComboBox
|
||||
MinWidth="150"
|
||||
DockPanel.Dock="Right"
|
||||
MinWidth="150"
|
||||
SelectedItem="{Binding Source={x:Static ms:UISettings.WindowBackgroundCustomImageStretch}, Path=Value, Mode=TwoWay}">
|
||||
<ComboBox.Items>
|
||||
<spp:XamlMediaStretch>Fill</spp:XamlMediaStretch>
|
||||
@@ -472,21 +463,21 @@
|
||||
<ListBoxItem>
|
||||
<Grid ColumnDefinitions="Auto,*,Auto">
|
||||
<ui:ImageIcon
|
||||
Width="24"
|
||||
Height="24"
|
||||
Margin="8"
|
||||
Source="{Binding IconKey, Converter={StaticResource BitmapAssetValueConverter}}"
|
||||
VerticalAlignment="Center"
|
||||
Source="{Binding IconKey, Converter={StaticResource BitmapAssetValueConverter}}" />
|
||||
Width="24" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Name}" />
|
||||
Text="{Binding Name}"
|
||||
VerticalAlignment="Center" />
|
||||
<Viewbox
|
||||
Grid.Column="2"
|
||||
Width="18"
|
||||
Height="18"
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center">
|
||||
VerticalAlignment="Center"
|
||||
Width="18">
|
||||
<ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="{StaticResource Move}" />
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
|
||||
@@ -190,7 +190,7 @@ public sealed class AppSplashScreen : IApplicationSplashScreen
|
||||
});
|
||||
|
||||
AdvertiseService.Current.InitAdvertise();
|
||||
await NoticeService.Current.GetNewsAsync();
|
||||
NoticeService.Current.GetNews();
|
||||
|
||||
var mainWindow = App.Instance.MainWindow;
|
||||
mainWindow.ThrowIsNull();
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!-- PackageReference -->
|
||||
<PackageReference Include="BD.Common" />
|
||||
<ProjectReference Include="..\..\ref\Common\src\BD.Common\BD.Common.csproj" />
|
||||
<PackageReference Include="dotnetCampus.Ipc" />
|
||||
<PackageReference Include="NLog" />
|
||||
<PackageReference Include="NLog.Extensions.Logging" />
|
||||
|
||||
+8
-4
@@ -92,8 +92,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BD.Common" />
|
||||
<PackageReference Include="BD.Common.Repositories.SQLitePCL" />
|
||||
<!--<PackageReference Include="BD.Common" />
|
||||
<PackageReference Include="BD.Common.Repositories.SQLitePCL" />-->
|
||||
<ProjectReference Include="..\..\ref\Common\src\BD.Common.Repositories.SQLitePCL\BD.Common.Repositories.SQLitePCL.csproj" />
|
||||
<ProjectReference Include="..\..\ref\Common\src\BD.Common\BD.Common.csproj" />
|
||||
<PackageReference Include="NLog" />
|
||||
<PackageReference Include="System.Linq.Async" />
|
||||
<PackageReference Include="Ae.DNS.Client" />
|
||||
@@ -101,9 +103,9 @@
|
||||
<PackageReference Include="DnsClient" />
|
||||
<PackageReference Include="Yarp.ReverseProxy" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" />
|
||||
<PackageReference Include="Aigio.Any.AppCenter" />
|
||||
<!--<PackageReference Include="Aigio.Any.AppCenter" />
|
||||
<PackageReference Include="Aigio.Any.AppCenter.Analytics" />
|
||||
<PackageReference Include="Aigio.Any.AppCenter.Crashes" />
|
||||
<PackageReference Include="Aigio.Any.AppCenter.Crashes" />-->
|
||||
<PackageReference Include="System.Drawing.Common" />
|
||||
<PackageReference Include="System.Runtime.Caching" />
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
@@ -119,6 +121,8 @@
|
||||
<ProjectReference Include="..\BD.WTTS.Client.IPC\BD.WTTS.Client.IPC.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\OSShuttingDownHelper\OSShuttingDownHelper.projitems" Label="Shared" />
|
||||
|
||||
<!-- 此项目不使用平台 TFM !!! -->
|
||||
<Import Project="..\TFM_NETX.props" />
|
||||
|
||||
|
||||
+2
-1
@@ -35,6 +35,7 @@ public static class KestrelServerOptionsExtensions
|
||||
|
||||
options.Listen(IReverseProxyService.Constants.Instance.ProxyIp, httpProxyPort, listen =>
|
||||
{
|
||||
listen.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
|
||||
var proxyMiddleware = options.ApplicationServices.GetRequiredService<HttpProxyMiddleware>();
|
||||
var tunnelMiddleware = options.ApplicationServices.GetRequiredService<TunnelMiddleware>();
|
||||
|
||||
@@ -123,7 +124,7 @@ public static class KestrelServerOptionsExtensions
|
||||
var httpsPort = IReverseProxyConfig.HttpsPort;
|
||||
options.Listen(IReverseProxyService.Constants.Instance.ProxyIp, httpsPort, listen =>
|
||||
{
|
||||
listen.Protocols = HttpProtocols.Http1AndHttp2;
|
||||
listen.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
|
||||
listen.UseFlowAnalyze();
|
||||
listen.UseTls();
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using BD.Common.Repositories.Abstractions;
|
||||
using dotnetCampus.Ipc.CompilerServices.GeneratedProxies;
|
||||
using Windows.Win32;
|
||||
using KeyValuePair = BD.Common.Entities.KeyValuePair;
|
||||
|
||||
const string moduleName = AssemblyInfo.Accelerator;
|
||||
@@ -35,6 +36,12 @@ static bool IsProcessElevated_DEBUG_Only()
|
||||
return principal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (OSShuttingDownHelper.IsSystemShuttingDown())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var exitCode = await IPCSubProcessService.MainAsync(moduleName, pluginName, ConfigureServices, static ipcProvider =>
|
||||
@@ -77,7 +84,7 @@ static void ConfigureServices(IServiceCollection services)
|
||||
services.AddLogging(l =>
|
||||
{
|
||||
l.ClearProviders();
|
||||
l.AddNLog(LogManager.Configuration); // 添加 NLog 日志
|
||||
l.AddNLog(LogManager.Configuration!); // 添加 NLog 日志
|
||||
#if DEBUG
|
||||
l.AddConsole();
|
||||
#endif
|
||||
|
||||
+2
-1
@@ -36,7 +36,7 @@ sealed class ReverseProxyHttpClientHandler : DelegatingHandler
|
||||
|
||||
// 设置请求头 host,因为连接中已带端口号故 修改协议为 http
|
||||
request.Headers.Host = uri.Host;
|
||||
request.RequestUri = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri;
|
||||
//request.RequestUri = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri;
|
||||
|
||||
if (domainConfig.Timeout != null)
|
||||
{
|
||||
@@ -61,6 +61,7 @@ sealed class ReverseProxyHttpClientHandler : DelegatingHandler
|
||||
AutomaticDecompression = DecompressionMethods.None,
|
||||
ConnectCallback = ConnectCallback,
|
||||
EnableMultipleHttp2Connections = true,
|
||||
EnableMultipleHttp3Connections = true,
|
||||
RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8,
|
||||
ResponseHeaderEncodingSelector = (_, _) => Encoding.UTF8,
|
||||
};
|
||||
|
||||
+40
-18
@@ -30,6 +30,12 @@ sealed class HttpLocalRequestMiddleware
|
||||
/// <returns></returns>
|
||||
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
|
||||
{
|
||||
if (TryGetInjectScriptLocalId(context.Request.Path, out var injectScriptLid))
|
||||
{
|
||||
await HandleScriptRequestAsync(context, injectScriptLid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (HttpMethods.IsOptions(context.Request.Method) && context.Request.Headers.ContainsKey("Access-Control-Request-Private-Network"))
|
||||
{
|
||||
// https://wicg.github.io/private-network-access/
|
||||
@@ -60,34 +66,50 @@ sealed class HttpLocalRequestMiddleware
|
||||
await HandleHttpRequestAsync(context);
|
||||
return;
|
||||
default: //默认处理脚本匹配
|
||||
if (!int.TryParse(context.Request.Path.Value?.Trim('/'), out var lid) && lid <= 0)
|
||||
{
|
||||
await Handle404NotFoundAsync(context);
|
||||
return;
|
||||
}
|
||||
// TODO: Scripts
|
||||
if (reverseProxyConfig.TryGetScriptContent(lid, out string? content))
|
||||
{
|
||||
if (string.IsNullOrEmpty(content))
|
||||
{
|
||||
await Handle404NotFoundAsync(context);
|
||||
return;
|
||||
}
|
||||
context.Response.Headers.ContentType = "text/javascript;charset=UTF-8";
|
||||
await context.Response.WriteAsync(content);
|
||||
return;
|
||||
}
|
||||
else
|
||||
if (!int.TryParse(context.Request.Path.Value?.Trim('/'), out var lid) || lid <= 0)
|
||||
{
|
||||
await Handle404NotFoundAsync(context);
|
||||
return;
|
||||
}
|
||||
await HandleScriptRequestAsync(context, lid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await next(context);
|
||||
}
|
||||
|
||||
static bool TryGetInjectScriptLocalId(PathString path, out int localId)
|
||||
{
|
||||
localId = default;
|
||||
var pathValue = path.Value;
|
||||
if (string.IsNullOrEmpty(pathValue))
|
||||
return false;
|
||||
|
||||
var prefix = IReverseProxyService.Constants.InjectScriptPathPrefix;
|
||||
if (!pathValue.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
|
||||
var idPart = pathValue[prefix.Length..];
|
||||
if (!idPart.EndsWith(".js", StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
|
||||
idPart = idPart[..^3];
|
||||
return int.TryParse(idPart, out localId) && localId > 0;
|
||||
}
|
||||
|
||||
async Task HandleScriptRequestAsync(HttpContext context, int lid)
|
||||
{
|
||||
if (reverseProxyConfig.TryGetScriptContent(lid, out var content) && !string.IsNullOrEmpty(content))
|
||||
{
|
||||
context.Response.Headers.ContentType = "application/javascript;charset=UTF-8";
|
||||
await context.Response.WriteAsync(content);
|
||||
return;
|
||||
}
|
||||
|
||||
await Handle404NotFoundAsync(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理脚本所需要的 HTTP 请求
|
||||
/// </summary>
|
||||
|
||||
+118
@@ -90,4 +90,122 @@ partial class HttpReverseProxyMiddleware
|
||||
notfound: insertPosition = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool FindScriptInjectInsertPositionForGithub(byte[] buffer_, Encoding encoding, out ReadOnlyMemory<byte> buffer, out int insertPosition)
|
||||
{
|
||||
buffer = buffer_.AsMemory();
|
||||
|
||||
ReadOnlySpan<byte> mark = "<script"u8;
|
||||
var lastScriptWithSrcStart = -1;
|
||||
var span = buffer_.AsSpan();
|
||||
|
||||
if (span.Length >= mark.Length)
|
||||
{
|
||||
for (var i = 0; i <= span.Length - mark.Length; i++)
|
||||
{
|
||||
if (!EqualsAsciiIgnoreCase(span.Slice(i, mark.Length), mark))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var afterMarkIndex = i + mark.Length;
|
||||
if (afterMarkIndex < span.Length && IsHtmlAttributeNameChar(span[afterMarkIndex]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var tagEndOffset = span.Slice(afterMarkIndex).IndexOf((byte)'>');
|
||||
if (tagEndOffset < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var tagEndIndex = afterMarkIndex + tagEndOffset;
|
||||
var scriptTag = span.Slice(i, tagEndIndex - i + 1);
|
||||
if (ScriptTagHasSrcAttribute(scriptTag))
|
||||
{
|
||||
lastScriptWithSrcStart = i;
|
||||
}
|
||||
|
||||
i = tagEndIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastScriptWithSrcStart >= 0)
|
||||
{
|
||||
insertPosition = lastScriptWithSrcStart;
|
||||
return true;
|
||||
}
|
||||
|
||||
return FindScriptInjectInsertPosition(buffer_, encoding, out buffer, out insertPosition);
|
||||
}
|
||||
|
||||
static bool ScriptTagHasSrcAttribute(ReadOnlySpan<byte> scriptTag)
|
||||
{
|
||||
ReadOnlySpan<byte> src = "src"u8;
|
||||
|
||||
if (scriptTag.Length < src.Length)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i <= scriptTag.Length - src.Length; i++)
|
||||
{
|
||||
if (!EqualsAsciiIgnoreCase(scriptTag.Slice(i, src.Length), src))
|
||||
continue;
|
||||
|
||||
if (i > 0 && IsHtmlAttributeNameChar(scriptTag[i - 1]))
|
||||
continue;
|
||||
|
||||
var j = i + src.Length;
|
||||
while (j < scriptTag.Length && IsAsciiWhitespace(scriptTag[j]))
|
||||
{
|
||||
j++;
|
||||
}
|
||||
|
||||
if (j < scriptTag.Length && scriptTag[j] == (byte)'=')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool EqualsAsciiIgnoreCase(ReadOnlySpan<byte> left, ReadOnlySpan<byte> right)
|
||||
{
|
||||
if (left.Length != right.Length)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < left.Length; i++)
|
||||
{
|
||||
var l = ToUpperAscii(left[i]);
|
||||
var r = ToUpperAscii(right[i]);
|
||||
if (l != r)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static byte ToUpperAscii(byte value)
|
||||
{
|
||||
if (value is >= (byte)'a' and <= (byte)'z')
|
||||
return (byte)(value - 32);
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool IsAsciiWhitespace(byte value) =>
|
||||
value == (byte)' ' ||
|
||||
value == (byte)'\t' ||
|
||||
value == (byte)'\r' ||
|
||||
value == (byte)'\n' ||
|
||||
value == (byte)'\f';
|
||||
|
||||
static bool IsHtmlAttributeNameChar(byte value) =>
|
||||
(value is >= (byte)'a' and <= (byte)'z') ||
|
||||
(value is >= (byte)'A' and <= (byte)'Z') ||
|
||||
(value is >= (byte)'0' and <= (byte)'9') ||
|
||||
value == (byte)'-' ||
|
||||
value == (byte)'_' ||
|
||||
value == (byte)':' ||
|
||||
value == (byte)'.';
|
||||
}
|
||||
+53
-18
@@ -2,6 +2,7 @@
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Yarp.ReverseProxy.Forwarder;
|
||||
using HttpVersion = System.Net.HttpVersion;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace BD.WTTS.Services.Implementation;
|
||||
@@ -30,6 +31,27 @@ sealed partial class HttpReverseProxyMiddleware
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
static ArgumentOutOfRangeException GetUnknownHttpVersionException(string? actualValue, [CallerArgumentExpression(nameof(actualValue))] string? paramName = null) => new(
|
||||
$"""
|
||||
Version doesn't map to a known HTTP protocol. (Parameter '{paramName}')
|
||||
Actual value was {actualValue}.
|
||||
""");
|
||||
|
||||
static Version GetHttpVersion(string requestProtocol) =>
|
||||
(requestProtocol != null && requestProtocol.Length >= 6) ? requestProtocol[5] switch
|
||||
{
|
||||
// 参考 Microsoft.AspNetCore.Http.HttpProtocol.GetHttpProtocol
|
||||
'1' => requestProtocol.Length >= 8 ? requestProtocol[7] switch
|
||||
{
|
||||
'0' => HttpVersion.Version10,
|
||||
'1' => HttpVersion.Version11,
|
||||
_ => throw GetUnknownHttpVersionException(requestProtocol),
|
||||
} : throw GetUnknownHttpVersionException(requestProtocol),
|
||||
'2' => HttpVersion.Version20,
|
||||
'3' => HttpVersion.Version30,
|
||||
_ => throw GetUnknownHttpVersionException(requestProtocol),
|
||||
} : throw GetUnknownHttpVersionException(requestProtocol);
|
||||
|
||||
/// <summary>
|
||||
/// 处理请求
|
||||
/// </summary>
|
||||
@@ -60,12 +82,7 @@ sealed partial class HttpReverseProxyMiddleware
|
||||
var destinationPrefix = GetDestinationPrefix(context.Request.Scheme, context.Request.Host, null);
|
||||
var forwarderRequestConfig = new ForwarderRequestConfig()
|
||||
{
|
||||
Version = context.Request.Protocol switch
|
||||
{
|
||||
var protocol when protocol.StartsWith("HTTP/2") => System.Net.HttpVersion.Version20,
|
||||
var protocol when protocol.StartsWith("HTTP/3") => System.Net.HttpVersion.Version30,
|
||||
_ => System.Net.HttpVersion.Version11,
|
||||
},
|
||||
Version = GetHttpVersion(context.Request.Protocol),
|
||||
};
|
||||
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient, forwarderRequestConfig, HttpTransformer.Empty);
|
||||
if (error != ForwarderError.None)
|
||||
@@ -119,19 +136,23 @@ sealed partial class HttpReverseProxyMiddleware
|
||||
context.Request.Headers.UserAgent = domainConfig.UserAgent.Replace("${origin}", context.Request.Headers.UserAgent, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
var forwarderRequestConfig = new ForwarderRequestConfig()
|
||||
{
|
||||
Version = context.Request.Protocol switch
|
||||
{
|
||||
var protocol when protocol.StartsWith("HTTP/2") => System.Net.HttpVersion.Version20,
|
||||
var protocol when protocol.StartsWith("HTTP/3") => System.Net.HttpVersion.Version30,
|
||||
_ => System.Net.HttpVersion.Version11,
|
||||
},
|
||||
};
|
||||
ForwarderRequestConfig forwarderRequestConfig;
|
||||
|
||||
if (domainConfig.IsServerSideProxy)
|
||||
{
|
||||
SetWattHeaders(context, reverseProxyConfig.Service.ServerSideProxyToken);
|
||||
forwarderRequestConfig = new ForwarderRequestConfig()
|
||||
{
|
||||
Version = HttpVersion.Version30,
|
||||
VersionPolicy = HttpVersionPolicy.RequestVersionOrLower
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
forwarderRequestConfig = new ForwarderRequestConfig()
|
||||
{
|
||||
Version = GetHttpVersion(context.Request.Protocol),
|
||||
};
|
||||
}
|
||||
|
||||
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient, forwarderRequestConfig, HttpTransformer.Empty);
|
||||
@@ -306,7 +327,12 @@ sealed partial class HttpReverseProxyMiddleware
|
||||
}
|
||||
}
|
||||
|
||||
if (FindScriptInjectInsertPosition(buffer_, encoding, out var buffer, out var position))
|
||||
var isGithubHost = IsGithubHost(context.Request.Host.Host);
|
||||
var isFindPosition = isGithubHost
|
||||
? FindScriptInjectInsertPositionForGithub(buffer_, encoding, out var buffer, out var position)
|
||||
: FindScriptInjectInsertPosition(buffer_, encoding, out buffer, out position);
|
||||
|
||||
if (isFindPosition)
|
||||
{
|
||||
using var bodyWriter = new MemoryStream();
|
||||
using Stream? bodyCompress = GetStreamByContentCompression(bodyWriter, contentCompression, CompressionMode.Compress, true);
|
||||
@@ -329,8 +355,8 @@ sealed partial class HttpReverseProxyMiddleware
|
||||
// var html_start_string = encoding.GetString(html_start.Span);
|
||||
//#endif
|
||||
await bodyCoreWriter.WriteAsync(html_start);
|
||||
ReadOnlyMemory<byte> script_xml_start = "<script type=\"text/javascript\" src=\"https://local.steampp.net/"u8.ToArray();
|
||||
ReadOnlyMemory<byte> script_xml_end = "\"></script>"u8.ToArray();
|
||||
var script_xml_start = Encoding.ASCII.GetBytes($"<script type=\"text/javascript\" src=\"{IReverseProxyService.Constants.InjectScriptPathPrefix}");
|
||||
ReadOnlyMemory<byte> script_xml_end = ".js\"></script>"u8.ToArray();
|
||||
foreach (var script in scripts)
|
||||
{
|
||||
await bodyCoreWriter.WriteAsync(script_xml_start);
|
||||
@@ -377,6 +403,15 @@ sealed partial class HttpReverseProxyMiddleware
|
||||
context.Response.Body = originalBody;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsGithubHost(string? host)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(host))
|
||||
return false;
|
||||
|
||||
return host.Equals("github.com", StringComparison.OrdinalIgnoreCase) ||
|
||||
host.EndsWith(".github.com", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
static Stream? GetStreamByContentCompression(Stream stream, string contentCompression, CompressionMode mode, bool leaveOpen) => contentCompression switch
|
||||
|
||||
+2
-1
@@ -154,8 +154,9 @@ abstract class ReverseProxyServiceImpl : IReverseProxySettings
|
||||
{
|
||||
reverseProxySettings = Serializable.DMP2<ReverseProxySettings>(reverseProxySettings_);
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
OnException(e);
|
||||
return StartProxyResultCode.DeserializeReverseProxySettingsFail;
|
||||
}
|
||||
|
||||
|
||||
+52
-5
@@ -98,8 +98,58 @@ sealed partial class YarpReverseProxyServiceImpl : ReverseProxyServiceImpl, IRev
|
||||
|
||||
protected override Task<StartProxyResult> StartProxyImpl() => Task.FromResult(StartProxyCore());
|
||||
|
||||
readonly string[] allowedHosts = ["*",];
|
||||
|
||||
#if WINDOWS
|
||||
/// <summary>
|
||||
/// https://learn.microsoft.com/zh-cn/dotnet/core/extensions/globalization-icu#determine-if-your-app-is-using-icu
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
static bool ICUMode()
|
||||
{
|
||||
SortVersion sortVersion = CultureInfo.InvariantCulture.CompareInfo.Version;
|
||||
byte[] bytes = sortVersion.SortId.ToByteArray();
|
||||
int version = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
|
||||
return version != 0 && version == sortVersion.FullVersion;
|
||||
}
|
||||
|
||||
static bool IcuTest(IEnumerable<string> incoming)
|
||||
{
|
||||
if (!OperatingSystem.IsWindowsVersionAtLeast(10, 0, 18362) && ICUMode())
|
||||
{
|
||||
// https://learn.microsoft.com/zh-cn/dotnet/core/extensions/globalization-icu#icu-on-windows
|
||||
// 与 .NET 6 和 .NET 5 相比,.NET 7 及更高版本能够在早期 Windows 版本上加载 ICU
|
||||
// 通常问题出在 1703 ~ 1903 之间的 Windows 版本
|
||||
try
|
||||
{
|
||||
// https://github.com/dotnet/aspnetcore/blob/v9.0.2/src/Middleware/HostFiltering/src/MiddlewareConfigurationManager.cs#L67
|
||||
foreach (var entry in incoming)
|
||||
{
|
||||
// Punycode. Http.Sys requires you to register Unicode hosts, but the headers contain punycode.
|
||||
var host = new HostString(entry).ToUriComponent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
StartProxyResult StartProxyCore()
|
||||
{
|
||||
#if WINDOWS
|
||||
if (!IcuTest(allowedHosts))
|
||||
{
|
||||
return StartProxyResultCode.IcuTestFail;
|
||||
}
|
||||
#endif
|
||||
// https://github.com/dotnetcore/FastGithub/blob/2.1.4/FastGithub/Program.cs#L29
|
||||
try
|
||||
{
|
||||
@@ -113,13 +163,10 @@ sealed partial class YarpReverseProxyServiceImpl : ReverseProxyServiceImpl, IRev
|
||||
|
||||
builder.Logging.AddProvider(new LogConsoleService.Utf8StringLoggerProvider(AssemblyInfo.Accelerator));
|
||||
|
||||
builder.Services.Configure<HostFilteringOptions>(static o =>
|
||||
builder.Services.Configure<HostFilteringOptions>(o =>
|
||||
{
|
||||
o.AllowEmptyHosts = true;
|
||||
o.AllowedHosts = new List<string>
|
||||
{
|
||||
"*",
|
||||
};
|
||||
o.AllowedHosts = allowedHosts;
|
||||
});
|
||||
|
||||
builder.Host.UseNLog();
|
||||
|
||||
+12
-1
@@ -34,6 +34,11 @@ public partial interface IReverseProxyService : IDisposable
|
||||
|
||||
public const string LocalDomain = "local.steampp.net";
|
||||
|
||||
/// <summary>
|
||||
/// 同源脚本注入路径前缀,避免使用外域 script src 造成页面脚本 publicPath 污染。
|
||||
/// </summary>
|
||||
public const string InjectScriptPathPrefix = "/WattToolkit_Inject/";
|
||||
|
||||
public static IPAddress DefaultProxyIp => IPAddress.Any;
|
||||
}
|
||||
|
||||
@@ -179,7 +184,7 @@ public partial interface IReverseProxySettings
|
||||
string? ServerSideProxyToken { get; set; }
|
||||
}
|
||||
|
||||
[MP2Obj(SerializeLayout.Explicit)]
|
||||
[MP2Obj(GenerateType.VersionTolerant, SerializeLayout.Explicit)]
|
||||
public readonly partial record struct ReverseProxySettings(
|
||||
[property: MP2Key(0)]
|
||||
IReadOnlyCollection<AccelerateProjectDTO>? ProxyDomains,
|
||||
@@ -341,6 +346,12 @@ public enum StartProxyResultCode : byte
|
||||
/// 绑定端口错误
|
||||
/// </summary>
|
||||
BindPortError,
|
||||
|
||||
/// <summary>
|
||||
/// 早期 Windows 版本且缺少 Windows 更新补丁,Icu 存在 Bug 导致,要么更新操作系统,要么更改配置使用 NLS 重启程序
|
||||
/// <para>https://learn.microsoft.com/zh-cn/dotnet/core/extensions/globalization-icu#use-nls-instead-of-icu</para>
|
||||
/// </summary>
|
||||
IcuTestFail = 127, // 值与 Mobius 保持一致
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+5
-9
@@ -9,7 +9,7 @@ internal partial class NetworkTestService : INetworkTestService
|
||||
#region 常量
|
||||
|
||||
// 默认测试地址
|
||||
const string DEFAULT_TESTSERVER_HOSTNAME = "stun.syncthing.net";
|
||||
internal const string DEFAULT_TESTSERVER_HOSTNAME = "stun.miwifi.com";
|
||||
|
||||
const int DEFAULT_PORT = 3478;
|
||||
const int DEFAULT_TLSPORT = 5349;
|
||||
@@ -202,8 +202,6 @@ internal partial class NetworkTestService : INetworkTestService
|
||||
|
||||
#region RFC3489
|
||||
|
||||
StunClient3489? _stunClient;
|
||||
|
||||
/// <summary>
|
||||
/// 测试 STUN 客户端 基于 RFC3489
|
||||
/// </summary>
|
||||
@@ -217,19 +215,17 @@ internal partial class NetworkTestService : INetworkTestService
|
||||
string? testServerHostName = DEFAULT_TESTSERVER_HOSTNAME,
|
||||
int? testServerPort = DEFAULT_TESTSTUN3489_PORT,
|
||||
IPEndPoint? localIPEndPoint = null,
|
||||
bool force = false,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
testServerHostName ??= DEFAULT_TESTSERVER_HOSTNAME;
|
||||
testServerPort ??= DEFAULT_TESTSTUN3489_PORT;
|
||||
|
||||
if (_stunClient == null || force)
|
||||
_stunClient = await GetStunClient3489Async(testServerHostName, testServerPort.Value, localIPEndPoint);
|
||||
using var stunClient = await GetStunClient3489Async(testServerHostName, testServerPort.Value, localIPEndPoint);
|
||||
|
||||
try
|
||||
{
|
||||
await _stunClient.QueryAsync(cancellationToken);
|
||||
await stunClient.QueryAsync(cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException) { throw; }
|
||||
catch (Exception ex)
|
||||
@@ -238,7 +234,7 @@ internal partial class NetworkTestService : INetworkTestService
|
||||
return null;
|
||||
}
|
||||
|
||||
return _stunClient.State;
|
||||
return stunClient.State;
|
||||
}
|
||||
|
||||
private async ValueTask<StunClient3489> GetStunClient3489Async(string serverHostName, int port, IPEndPoint? localIPEndPoint = null)
|
||||
@@ -289,7 +285,7 @@ internal partial class NetworkTestService : INetworkTestService
|
||||
testServerHostName ??= DEFAULT_TESTSERVER_HOSTNAME;
|
||||
testServerPort ??= DEFAULT_TESTSTUN5389_PORT;
|
||||
|
||||
IStunClient5389 client = await GetStunClient5389Async(protocol, testServerHostName, testServerPort.Value, localIPEndPoint);
|
||||
using var client = await GetStunClient5389Async(protocol, testServerHostName, testServerPort.Value, localIPEndPoint);
|
||||
|
||||
if (client == null)
|
||||
return null;
|
||||
|
||||
@@ -91,14 +91,12 @@ internal interface IStunTestService
|
||||
/// <param name="testServerHostName">STUN 服务地址</param>
|
||||
/// <param name="testServerPort">STUN 服务端口</param>
|
||||
/// <param name="localIPEndPoint">本机IP</param>
|
||||
/// <param name="force">刷新测试 Client</param>
|
||||
/// <remarks></remarks>
|
||||
/// <returns></returns>
|
||||
Task<ClassicStunResult?> TestStunClient3489Async(
|
||||
string? testServerHostName = default,
|
||||
int? testServerPort = default,
|
||||
IPEndPoint? localIPEndPoint = default,
|
||||
bool force = false,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
|
||||
|
||||
@@ -629,45 +629,9 @@ public sealed partial class GameAcceleratorService
|
||||
};
|
||||
var install = Ioc.Get<IAcceleratorService>().XY_Install(GameAcceleratorSettings.WattAcceleratorDirPath.Value!);
|
||||
|
||||
td.Opened += (s, e) =>
|
||||
td.Opened += async (s, e) =>
|
||||
{
|
||||
DownloadCallbackAsync(td, install).Wait();
|
||||
//await foreach (var item in install)
|
||||
//{
|
||||
// if (item.HandleUI(out var content))
|
||||
// {
|
||||
// switch (content)
|
||||
// {
|
||||
// case < 100:
|
||||
// Dispatcher.UIThread.Post(() => { td.Content = $"正在下载 {item.Content}%"; });
|
||||
// td.SetProgressBarState(item.Content, TaskDialogProgressState.Normal);
|
||||
// break;
|
||||
// case 100:
|
||||
// td.SetProgressBarState(item.Content, TaskDialogProgressState.Indeterminate);
|
||||
// Dispatcher.UIThread.Post(() => { td.Content = $"下载完成,正在安装..."; });
|
||||
// break;
|
||||
// case (int)XunYouDownLoadCode.安装成功:
|
||||
// //处理成功
|
||||
// //Dispatcher.UIThread.Post(() => { td.Content = $"安装完成"; });
|
||||
// Dispatcher.UIThread.Post(() => { td.Hide(TaskDialogStandardResult.OK); });
|
||||
// td.Hide();
|
||||
// break;
|
||||
// case int n when n > 101 && n < (int)XunYouDownLoadCode.启动安装程序失败:
|
||||
// //处理失败
|
||||
// break;
|
||||
// // Code 和进度重叠 递进 1000 XunYouInstallOrStartCode.默认 XunYouInstallOrStartCode.已安装
|
||||
// case 1000:
|
||||
// Dispatcher.UIThread.Post(() => { td.Content = $"默认"; });
|
||||
// // XunYouInstallOrStartCode.默认
|
||||
// break;
|
||||
// case 1001:
|
||||
// Dispatcher.UIThread.Post(() => { td.Content = $"已安装"; });
|
||||
// // XunYouInstallOrStartCode.已安装
|
||||
// Dispatcher.UIThread.Post(() => { td.Hide(TaskDialogStandardResult.OK); });
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
await DownloadCallbackAsync(td, install);
|
||||
};
|
||||
|
||||
//_ = Task.Run(() => { XunYouSDK.InstallAsync(progress, Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "WattAccelerator")); });
|
||||
|
||||
@@ -74,6 +74,9 @@ partial class ProxyService
|
||||
bool useDoh = ProxySettings.UseDoh.Value;
|
||||
string? customDohAddres = ProxySettings.CustomDohAddres2.Value;
|
||||
|
||||
string? proxyToken = proxyDomains.Any_Nullable(s => s.ProxyType == ProxyType.ServerAccelerate || s.Items.Any_Nullable(x => x.ProxyType == ProxyType.ServerAccelerate)) ?
|
||||
await TryRequestServerSideProxyToken() : null;
|
||||
|
||||
if (ProxySettings.ProxyBeforeDNSCheck.Value)
|
||||
{
|
||||
if (useDoh)
|
||||
@@ -229,9 +232,6 @@ partial class ProxyService
|
||||
}
|
||||
}
|
||||
|
||||
string? proxyToken = proxyDomains.Any_Nullable(s => s.ProxyType == ProxyType.ServerAccelerate || s.Items.Any_Nullable(x => x.ProxyType == ProxyType.ServerAccelerate)) ?
|
||||
await TryRequestServerSideProxyToken() : null;
|
||||
|
||||
ReverseProxySettings reverseProxySettings = new(proxyDomains, scripts,
|
||||
isEnableScript, isOnlyWorkSteamBrowser, proxyPort,
|
||||
proxyIp, proxyMode, isProxyGOG, onlyEnableProxyScript,
|
||||
@@ -406,8 +406,10 @@ partial class ProxyService
|
||||
if (!string.IsNullOrEmpty(proxyTokenCache))
|
||||
return proxyTokenCache;
|
||||
|
||||
using CancellationTokenSource cts = new();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(4.99D)); // 5 秒超时
|
||||
var resp = await IMicroServiceClient.Instance.Account
|
||||
.GenerateServerSideProxyToken();
|
||||
.GenerateServerSideProxyToken(cts.Token);
|
||||
|
||||
if (resp != null && !string.IsNullOrEmpty(resp.Content?.AccessToken))
|
||||
{
|
||||
@@ -416,11 +418,14 @@ partial class ProxyService
|
||||
|
||||
return proxyTokenCache;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ex.LogAndShowT();
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -431,7 +436,7 @@ partial class ProxyService
|
||||
{
|
||||
public static implicit operator OperateProxyServiceResult(string error) => new(error);
|
||||
|
||||
public static implicit operator OperateProxyServiceResult(Exception exception) => new(null, exception);
|
||||
public static implicit operator OperateProxyServiceResult(Exception exception) => new(exception.Message, exception);
|
||||
|
||||
/// <summary>
|
||||
/// 将返回结果使用 Toast 显示并且返回当前应设置的代理状态
|
||||
|
||||
@@ -163,8 +163,35 @@ public sealed partial class ProxyService
|
||||
return [];
|
||||
var data = ProxyDomains.Items
|
||||
.Where(x => x.Items != null)
|
||||
.SelectMany(s => s.Items!.Where(w => w.ThreeStateEnable != false));
|
||||
//return data.Concat(data.SelectMany(s => GetProxyDomainsItems(s)));
|
||||
.SelectMany(s => s.Items!.Where(w => w.ThreeStateEnable != false))
|
||||
.Select(item =>
|
||||
{
|
||||
//过滤部分选中的子项
|
||||
if (item.Items.Any_Nullable())
|
||||
{
|
||||
return new AccelerateProjectDTO
|
||||
{
|
||||
Name = item.Name,
|
||||
Port = item.Port,
|
||||
MatchDomainNames = item.MatchDomainNames,
|
||||
ForwardDomainNames = item.ForwardDomainNames,
|
||||
IgnoreSSLCertVerification = item.IgnoreSSLCertVerification,
|
||||
FakeServerName = item.FakeServerName,
|
||||
ProxyType = item.ProxyType,
|
||||
ListenDomainNames = item.ListenDomainNames,
|
||||
Checked = item.Checked,
|
||||
Id = item.Id,
|
||||
Order = item.Order,
|
||||
FakeUserAgent = item.FakeUserAgent,
|
||||
Version = item.Version,
|
||||
ThreeStateEnable = item.ThreeStateEnable,
|
||||
// 递归过滤子项
|
||||
Items = item.Items.Where(x => x.ThreeStateEnable != false).ToList()
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ internal partial class GameAcceleratorSettingsContext : JsonSerializerContext
|
||||
=> instance ??= new GameAcceleratorSettingsContext(ISettings.GetDefaultOptions());
|
||||
}
|
||||
|
||||
[MPObj, MP2Obj(SerializeLayout.Explicit)]
|
||||
[MP2Obj(SerializeLayout.Explicit)]
|
||||
public sealed partial class GameAcceleratorSettings_ : ISettings, ISettings<GameAcceleratorSettings_>
|
||||
{
|
||||
public const string Name = nameof(GameAcceleratorSettings);
|
||||
|
||||
@@ -21,16 +21,17 @@ public sealed partial class AcceleratorPageViewModel
|
||||
{
|
||||
// Create new ProxyEnableDomain for 加速服务 page
|
||||
var enableGroupDomain = ProxyService.Current.ProxyDomainsList
|
||||
.Where(list => list.ThreeStateEnable == true || list.ThreeStateEnable == null)
|
||||
.Where(list => list.ThreeStateEnable != false)
|
||||
.Select(list => new ProxyDomainGroupViewModel
|
||||
{
|
||||
Name = list.Name,
|
||||
IconUrl = list.IconUrl ?? string.Empty,
|
||||
EnableProxyDomainVMs = new(
|
||||
list.Items!
|
||||
.Where(i => i.ThreeStateEnable == true)
|
||||
.Where(i => i.ThreeStateEnable != false)
|
||||
.Select(i => new ProxyDomainViewModel(i.Name, i.ProxyType, "https://" + i.ListenDomainNames.Split(";")[0],
|
||||
i.Items?
|
||||
.Where(x => x.ThreeStateEnable != false)
|
||||
.Select(c => new ProxyDomainViewModel(c.Name, c.ProxyType, "https://" + c.ListenDomainNames.Split(';')[0]))
|
||||
.ToList()))
|
||||
.ToList()),
|
||||
|
||||
+69
-54
@@ -31,10 +31,9 @@ public partial class NetworkCheckControlViewModel : ViewModelBase
|
||||
|
||||
public string[] STUNAddress { get; } =
|
||||
[
|
||||
"stun.syncthing.net",
|
||||
NetworkTestService.DEFAULT_TESTSERVER_HOSTNAME,
|
||||
"stun.hot-chilli.net",
|
||||
"stun.fitauto.ru",
|
||||
"stun.miwifi.com",
|
||||
];
|
||||
|
||||
[ObservableAsProperty]
|
||||
@@ -90,28 +89,7 @@ public partial class NetworkCheckControlViewModel : ViewModelBase
|
||||
{
|
||||
SelectedSTUNAddress = STUNAddress[0];
|
||||
|
||||
NATCheckCommand = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
var natCheckResult = await _networkTestService.TestStunClient3489Async(testServerHostName: SelectedSTUNAddress) ?? new ClassicStunResult { NatType = NatType.Unknown };
|
||||
var (netSucc, _) = await _networkTestService.TestOpenUrlAsync("https://www.baidu.com");
|
||||
|
||||
var publicEndPoint = natCheckResult.PublicEndPoint?.Address.ToString() ?? "Unknown";
|
||||
var localEndPoint = natCheckResult.LocalEndPoint?.Address.ToString() ?? "Unknown";
|
||||
|
||||
var (natLevel, natTypeTip) = natCheckResult.NatType switch
|
||||
{
|
||||
// Open
|
||||
NatType.OpenInternet or NatType.FullCone => ("开放 NAT", "您可与在其网络上具有任意 NAT 类型的用户玩多人游戏和发起多人游戏。"),
|
||||
// Moderate
|
||||
NatType.RestrictedCone or NatType.PortRestrictedCone or NatType.SymmetricUdpFirewall => ("中等 NAT", "您可与一些用户玩多人游戏;但是,并且通常你将不会被选为比赛的主持人。"),
|
||||
// Strict
|
||||
NatType.Symmetric or NatType.UdpBlocked => ("严格 NAT", "您只能与具有开放 NAT 类型的用户玩多人游戏。您不能被选为比赛的主持人。"),
|
||||
// Unknown
|
||||
NatType.Unknown or NatType.UnsupportedServer or _ => ("不可用 NAT", "如果 NAT 不可用,您将无法使用群聊天或连接到某些 Xbox 游戏的多人游戏。"),
|
||||
};
|
||||
|
||||
return new NATFetchResult(publicEndPoint, localEndPoint, natLevel, natTypeTip, netSucc);
|
||||
});
|
||||
NATCheckCommand = ReactiveCommand.CreateFromTask(NATCheckAsync);
|
||||
NATCheckCommand
|
||||
.IsExecuting
|
||||
.ToPropertyEx(this, x => x.IsNATChecking);
|
||||
@@ -145,36 +123,7 @@ public partial class NetworkCheckControlViewModel : ViewModelBase
|
||||
|
||||
var canDNSCheck = this.WhenAnyValue(x => x.DomainPendingTest)
|
||||
.Select(domain => domain == string.Empty || DomainRegExp().IsMatch(domain));
|
||||
DNSCheckCommand = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
var testDomain = DomainPendingTest == string.Empty ? DefaultTestDomain : DomainPendingTest;
|
||||
try
|
||||
{
|
||||
long delayMs;
|
||||
IPAddress[] address;
|
||||
if (ProxySettings.UseDoh)
|
||||
{
|
||||
var configDoh = ProxySettings.CustomDohAddres2.Value ?? ProxySettingsWindowViewModel.DohAddress.FirstOrDefault() ?? string.Empty;
|
||||
(delayMs, address) = await _networkTestService.TestDNSOverHttpsAsync(testDomain, configDoh);
|
||||
}
|
||||
else
|
||||
{
|
||||
var configDns = ProxySettings.ProxyMasterDns.Value ?? string.Empty;
|
||||
(delayMs, address) = await _networkTestService.TestDNSAsync(testDomain, configDns, 53);
|
||||
}
|
||||
if (address.Length == 0)
|
||||
throw new Exception("Parsing failed. Return empty ip address.");
|
||||
|
||||
DNSTestDelay = delayMs + "ms ";
|
||||
DNSTestResult = string.Empty + address.FirstOrDefault();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(nameof(AcceleratorPageViewModel), ex.ToString());
|
||||
DNSTestDelay = string.Empty;
|
||||
DNSTestResult = "error";
|
||||
}
|
||||
}, canDNSCheck);
|
||||
DNSCheckCommand = ReactiveCommand.CreateFromTask(DNSCheckAsync, canDNSCheck);
|
||||
DNSCheckCommand
|
||||
.IsExecuting
|
||||
.ToPropertyEx(this, x => x.IsDNSChecking);
|
||||
@@ -199,4 +148,70 @@ public partial class NetworkCheckControlViewModel : ViewModelBase
|
||||
|
||||
IPv6CheckCommand.Execute().Subscribe();
|
||||
}
|
||||
|
||||
async Task<NATFetchResult> NATCheckAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await NATCheckCoreAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new("", "", "", "", false);
|
||||
}
|
||||
}
|
||||
|
||||
async Task<NATFetchResult> NATCheckCoreAsync()
|
||||
{
|
||||
var natCheckResult = await _networkTestService.TestStunClient3489Async(testServerHostName: SelectedSTUNAddress) ?? new ClassicStunResult { NatType = NatType.Unknown };
|
||||
var (netSucc, _) = await _networkTestService.TestOpenUrlAsync("https://www.baidu.com");
|
||||
|
||||
var publicEndPoint = natCheckResult.PublicEndPoint?.Address.ToString() ?? "Unknown";
|
||||
var localEndPoint = natCheckResult.LocalEndPoint?.Address.ToString() ?? "Unknown";
|
||||
|
||||
var (natLevel, natTypeTip) = natCheckResult.NatType switch
|
||||
{
|
||||
// Open
|
||||
NatType.OpenInternet or NatType.FullCone => ("开放 NAT", "您可与在其网络上具有任意 NAT 类型的用户玩多人游戏和发起多人游戏。"),
|
||||
// Moderate
|
||||
NatType.RestrictedCone or NatType.PortRestrictedCone or NatType.SymmetricUdpFirewall => ("中等 NAT", "您可与一些用户玩多人游戏;但是,并且通常你将不会被选为比赛的主持人。"),
|
||||
// Strict
|
||||
NatType.Symmetric or NatType.UdpBlocked => ("严格 NAT", "您只能与具有开放 NAT 类型的用户玩多人游戏。您不能被选为比赛的主持人。"),
|
||||
// Unknown
|
||||
NatType.Unknown or NatType.UnsupportedServer or _ => ("不可用 NAT", "如果 NAT 不可用,您将无法使用群聊天或连接到某些 Xbox 游戏的多人游戏。"),
|
||||
};
|
||||
|
||||
return new NATFetchResult(publicEndPoint, localEndPoint, natLevel, natTypeTip, netSucc);
|
||||
}
|
||||
|
||||
async Task DNSCheckAsync()
|
||||
{
|
||||
var testDomain = DomainPendingTest == string.Empty ? DefaultTestDomain : DomainPendingTest;
|
||||
try
|
||||
{
|
||||
long delayMs;
|
||||
IPAddress[] address;
|
||||
if (ProxySettings.UseDoh)
|
||||
{
|
||||
var configDoh = ProxySettings.CustomDohAddres2.Value ?? ProxySettingsWindowViewModel.DohAddress.FirstOrDefault() ?? string.Empty;
|
||||
(delayMs, address) = await _networkTestService.TestDNSOverHttpsAsync(testDomain, configDoh);
|
||||
}
|
||||
else
|
||||
{
|
||||
var configDns = ProxySettings.ProxyMasterDns.Value ?? string.Empty;
|
||||
(delayMs, address) = await _networkTestService.TestDNSAsync(testDomain, configDns, 53);
|
||||
}
|
||||
if (address.Length == 0)
|
||||
throw new Exception("Parsing failed. Return empty ip address.");
|
||||
|
||||
DNSTestDelay = delayMs + "ms ";
|
||||
DNSTestResult = string.Empty + address.FirstOrDefault();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(nameof(AcceleratorPageViewModel), ex.ToString());
|
||||
DNSTestDelay = string.Empty;
|
||||
DNSTestResult = "error";
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
using BD.WTTS.Helpers;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Controls;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Controls;
|
||||
|
||||
|
||||
@@ -213,27 +213,31 @@
|
||||
Binding="{Binding PingValue}"
|
||||
ComparisonCondition="GreaterThan"
|
||||
Value="120">
|
||||
<ia:ChangePropertyAction
|
||||
PropertyName="Fill"
|
||||
TargetObject="PingEllipse"
|
||||
Value="#FF4F58" />
|
||||
<ia:ChangePropertyAction
|
||||
PropertyName="Foreground"
|
||||
TargetObject="PingText"
|
||||
Value="#FF4F58" />
|
||||
<ia:ChangePropertyAction PropertyName="Fill" TargetObject="PingEllipse">
|
||||
<ia:ChangePropertyAction.Value>
|
||||
<SolidColorBrush Color="#FF4F58" />
|
||||
</ia:ChangePropertyAction.Value>
|
||||
</ia:ChangePropertyAction>
|
||||
<ia:ChangePropertyAction PropertyName="Foreground" TargetObject="PingText">
|
||||
<ia:ChangePropertyAction.Value>
|
||||
<SolidColorBrush Color="#FF4F58" />
|
||||
</ia:ChangePropertyAction.Value>
|
||||
</ia:ChangePropertyAction>
|
||||
</ia:DataTriggerBehavior>
|
||||
<ia:DataTriggerBehavior
|
||||
Binding="{Binding PingValue}"
|
||||
ComparisonCondition="LessThanOrEqual"
|
||||
Value="120">
|
||||
<ia:ChangePropertyAction
|
||||
PropertyName="Fill"
|
||||
TargetObject="PingEllipse"
|
||||
Value="#47A42B" />
|
||||
<ia:ChangePropertyAction
|
||||
PropertyName="Foreground"
|
||||
TargetObject="PingText"
|
||||
Value="#47A42B" />
|
||||
<ia:ChangePropertyAction PropertyName="Fill" TargetObject="PingEllipse">
|
||||
<ia:ChangePropertyAction.Value>
|
||||
<SolidColorBrush Color="#47A42B" />
|
||||
</ia:ChangePropertyAction.Value>
|
||||
</ia:ChangePropertyAction>
|
||||
<ia:ChangePropertyAction PropertyName="Foreground" TargetObject="PingText">
|
||||
<ia:ChangePropertyAction.Value>
|
||||
<SolidColorBrush Color="#47A42B" />
|
||||
</ia:ChangePropertyAction.Value>
|
||||
</ia:ChangePropertyAction>
|
||||
</ia:DataTriggerBehavior>
|
||||
</i:Interaction.Behaviors>
|
||||
<TextBlock
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
using System.CommandLine;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Controls;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Controls;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Pages;
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
<Button Content="选择证书"/>
|
||||
</DockPanel>-->
|
||||
|
||||
<ui:SettingsExpanderItem Description="*该选项勾选后会导致程序的加速完全不生效,请确认是配合二级代理实现仅需要脚本功能后再启用">
|
||||
<ui:SettingsExpanderItem Description="*该选项勾选后会导致程序的加速失效,请确认是配合二级代理实现仅需要脚本功能后再启用">
|
||||
<TextBlock Text="{Binding Path=Res.CommunityFix_ProxyOnlyOnScript, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch Classes="LeftContent" IsChecked="{Binding Source={x:Static ms:ProxySettings.OnlyEnableProxyScript}, Path=Value, Mode=TwoWay}" />
|
||||
@@ -114,7 +114,7 @@
|
||||
|
||||
|
||||
<ui:SettingsExpander
|
||||
Description="*改动后需重启加速才能生效"
|
||||
Description="*请在有正确二级代理的情况下才考虑启用该选项,否则会导致程序的加速失效,改动后需重启加速才能生效"
|
||||
Header="{Binding Path=Res.Settings_Proxy_TwoLevelAgent, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
IsExpanded="True">
|
||||
<ui:SettingsExpander.Footer>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Pages;
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</ItemsRepeater.Layout>
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate DataType="spp:ScriptDTO">
|
||||
<Border spp:Animations.EnableAnimations="True">
|
||||
<Border spp:Animations.EnableAnimations="False">
|
||||
<spp:AppItem
|
||||
Title="{Binding Name}"
|
||||
MaxWidth="135"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Pages;
|
||||
|
||||
|
||||
-4
@@ -135,10 +135,6 @@
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Settings\" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\TFM_NETX_WITH_DESKTOP.props" />
|
||||
|
||||
</Project>
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
using BD.WTTS.UI.Views.Controls;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Pages;
|
||||
|
||||
@@ -98,10 +98,6 @@
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Services\" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--<Import Project="..\TFM_NETX_WITH_ALL.props" />-->
|
||||
<Import Project="..\TFM_NETX_WITH_DESKTOP.props" />
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Avalonia.Gif;
|
||||
using BD.Avalonia8.Image2;
|
||||
using BD.WTTS.UI.Views.Pages;
|
||||
using System.Threading;
|
||||
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
<Border
|
||||
Width="200"
|
||||
Margin="3"
|
||||
spp:Animations.EnableAnimations="True">
|
||||
spp:Animations.EnableAnimations="False">
|
||||
<spp:AppItem
|
||||
Title="{Binding Name}"
|
||||
Classes="Vertical"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Pages;
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
using BD.WTTS.UI.Views.Controls;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using FluentAvalonia.UI.Media.Animation;
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Pages;
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
using BD.WTTS.UI.Views.Controls;
|
||||
using KeyEventArgs = Avalonia.Input.KeyEventArgs;
|
||||
|
||||
|
||||
@@ -103,10 +103,4 @@
|
||||
|
||||
<Import Project="..\TFM_NETX_WITH_DESKTOP.props" />
|
||||
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<UserProperties />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
namespace BD.WTTS.Models;
|
||||
|
||||
[MessagePackObjectAttribute]
|
||||
[MP2Obj(SerializeLayout.Explicit)]
|
||||
public partial class PlatformSettings : BaseNotifyPropertyChanged
|
||||
{
|
||||
[MessagePack.Key(0)]
|
||||
[Reactive, MP2Key(0)]
|
||||
public string? PlatformPath { get; set; }
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"PlatformIds": [ "e", "epg", "epic", "epicgames" ],
|
||||
"ExesToEnd": [ "EpicGamesLauncher.exe" ],
|
||||
"LoginFiles": {
|
||||
"%LocalAppData%\\EpicGamesLauncher\\Saved\\Config\\Windows\\GameUserSettings.ini": "GameUserSettings.ini",
|
||||
"%LocalAppData%\\EpicGamesLauncher\\Saved\\Config\\WindowsEditor\\GameUserSettings.ini": "GameUserSettings.ini",
|
||||
"REG:HKCU\\Software\\Epic Games\\Unreal Engine\\Identifiers:AccountId": "UNIQUE_ID"
|
||||
},
|
||||
"ClearPaths": [ "SAME_AS_LOGIN_FILES" ],
|
||||
|
||||
@@ -211,7 +211,7 @@
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="spp:IAccount">
|
||||
<Border Margin="5" spp:Animations.EnableAnimations="True">
|
||||
<Border Margin="5" spp:Animations.EnableAnimations="False">
|
||||
<spp:AppItem
|
||||
x:Name="Item"
|
||||
Title="{Binding DisplayName}"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
using BD.SteamClient.Helpers;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
<spp:PageBase
|
||||
Description="{Binding Description, Source={x:Static s:Plugin.Instance}, Mode=OneWay}"
|
||||
Subtitle="{Binding Author, Source={x:Static s:Plugin.Instance}, Mode=OneWay, Converter={StaticResource StringFormatConverter}, ConverterParameter=Plugin_Author}"
|
||||
Title="{Binding Name, Source={x:Static s:Plugin.Instance}, Mode=OneWay}"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d"
|
||||
x:Class="BD.WTTS.UI.Views.Pages.GameAccountPage"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="spp:GameAccountPageViewModel"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ms="https://steampp.net/settings"
|
||||
xmlns:s="https://steampp.net/services"
|
||||
xmlns:spp="https://steampp.net/ui"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
Title="{Binding Name, Source={x:Static s:Plugin.Instance}, Mode=OneWay}"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="spp:GameAccountPageViewModel"
|
||||
Description="{Binding Description, Source={x:Static s:Plugin.Instance}, Mode=OneWay}"
|
||||
Subtitle="{Binding Author, Source={x:Static s:Plugin.Instance}, Mode=OneWay, Converter={StaticResource StringFormatConverter}, ConverterParameter=Plugin_Author}"
|
||||
mc:Ignorable="d">
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<spp:PageBase.PreviewImage>
|
||||
<ui:ImageIconSource Source="{Binding Icon, Source={x:Static s:Plugin.Instance}, Mode=OneWay, Converter={StaticResource BitmapAssetValueConverter}}" />
|
||||
</spp:PageBase.PreviewImage>
|
||||
@@ -34,97 +34,97 @@
|
||||
<spp:PageBase.ActionContent>
|
||||
<StackPanel Orientation="Horizontal" Spacing="2">
|
||||
<Button
|
||||
Padding="8,4"
|
||||
Command="{Binding LoginNewCommand}"
|
||||
DockPanel.Dock="Right"
|
||||
Padding="8,4"
|
||||
Theme="{StaticResource TransparentButton}">
|
||||
<Panel>
|
||||
<Viewbox
|
||||
Width="16"
|
||||
Height="16"
|
||||
HorizontalAlignment="Left">
|
||||
HorizontalAlignment="Left"
|
||||
Width="16">
|
||||
<ui:SymbolIcon Symbol="Add" />
|
||||
</Viewbox>
|
||||
<TextBlock
|
||||
Margin="28,4,0,4"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Path=Res.UserChange_LoginNewAccount, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
Margin="28,4,0,4"
|
||||
Text="{Binding Path=Res.UserChange_LoginNewAccount, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
VerticalAlignment="Center" />
|
||||
</Panel>
|
||||
</Button>
|
||||
<Button
|
||||
Padding="8,4"
|
||||
Command="{Binding SaveCurrentUserCommand}"
|
||||
DockPanel.Dock="Right"
|
||||
IsVisible="{Binding !IsSelectedSteam}"
|
||||
Padding="8,4"
|
||||
Theme="{StaticResource TransparentButton}">
|
||||
<Panel>
|
||||
<Viewbox
|
||||
Width="16"
|
||||
Height="16"
|
||||
HorizontalAlignment="Left">
|
||||
HorizontalAlignment="Left"
|
||||
Width="16">
|
||||
<ui:SymbolIcon Symbol="Save" />
|
||||
</Viewbox>
|
||||
<TextBlock
|
||||
Margin="28,4,0,4"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Path=Res.GameAccount_SaveCurrentAccount, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
Margin="28,4,0,4"
|
||||
Text="{Binding Path=Res.GameAccount_SaveCurrentAccount, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
VerticalAlignment="Center" />
|
||||
</Panel>
|
||||
</Button>
|
||||
<!-- <Button -->
|
||||
<!-- Command="{Binding ShareManageCommand}" -->
|
||||
<!-- DockPanel.Dock="Right" -->
|
||||
<!-- IsVisible="{Binding IsSelectedSteam}" -->
|
||||
<!-- Padding="8,4" -->
|
||||
<!-- Theme="{StaticResource TransparentButton}"> -->
|
||||
<!-- <Panel> -->
|
||||
<!-- <Viewbox -->
|
||||
<!-- Height="16" -->
|
||||
<!-- HorizontalAlignment="Left" -->
|
||||
<!-- Width="16"> -->
|
||||
<!-- <ui:SymbolIcon Symbol="ShareAndroid" /> -->
|
||||
<!-- </Viewbox> -->
|
||||
<!-- <TextBlock -->
|
||||
<!-- HorizontalAlignment="Left" -->
|
||||
<!-- Margin="28,4,0,4" -->
|
||||
<!-- Text="{Binding Path=Res.AccountChange_Title, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" -->
|
||||
<!-- VerticalAlignment="Center" /> -->
|
||||
<!-- </Panel> -->
|
||||
<!-- </Button> -->
|
||||
<Button
|
||||
Padding="8,4"
|
||||
Command="{Binding ShareManageCommand}"
|
||||
DockPanel.Dock="Right"
|
||||
IsVisible="{Binding IsSelectedSteam}"
|
||||
Theme="{StaticResource TransparentButton}">
|
||||
<Panel>
|
||||
<Viewbox
|
||||
Width="16"
|
||||
Height="16"
|
||||
HorizontalAlignment="Left">
|
||||
<ui:SymbolIcon Symbol="ShareAndroid" />
|
||||
</Viewbox>
|
||||
<TextBlock
|
||||
Margin="28,4,0,4"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Path=Res.AccountChange_Title, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
</Panel>
|
||||
</Button>
|
||||
<Button
|
||||
Padding="8,4"
|
||||
Command="{Binding RefreshCommand}"
|
||||
DockPanel.Dock="Right"
|
||||
Padding="8,4"
|
||||
Theme="{StaticResource TransparentButton}">
|
||||
<Panel>
|
||||
<Viewbox
|
||||
Width="16"
|
||||
Height="16"
|
||||
HorizontalAlignment="Left">
|
||||
HorizontalAlignment="Left"
|
||||
Width="16">
|
||||
<ui:SymbolIcon Symbol="Refresh" />
|
||||
</Viewbox>
|
||||
<TextBlock
|
||||
Margin="28,4,0,4"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Path=Res.Refresh, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
Margin="28,4,0,4"
|
||||
Text="{Binding Path=Res.Refresh, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
|
||||
VerticalAlignment="Center" />
|
||||
</Panel>
|
||||
</Button>
|
||||
<DropDownButton
|
||||
Padding="8"
|
||||
HorizontalContentAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Content="{StaticResource More}"
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
FontSize="20"
|
||||
Theme="{StaticResource TransparentButton}">
|
||||
HorizontalContentAlignment="Center"
|
||||
Padding="8"
|
||||
Theme="{StaticResource TransparentButton}"
|
||||
VerticalContentAlignment="Center">
|
||||
<DropDownButton.Flyout>
|
||||
<ui:FAMenuFlyout Placement="BottomEdgeAlignedRight">
|
||||
<ui:MenuFlyoutItem
|
||||
x:Name="PlatformSettingButton"
|
||||
IsVisible="{Binding !IsSelectedSteam}"
|
||||
Text="平台设置" />
|
||||
Text="平台设置"
|
||||
x:Name="PlatformSettingButton" />
|
||||
<!--<ui:MenuFlyoutItem x:Name="PluginSettingButton" Text="{Binding Path=Res.Settings_Plugin, Mode=OneWay,Source={x:Static s:ResourceService.Current}}" />-->
|
||||
<ui:ToggleMenuFlyoutItem
|
||||
IsChecked="{Binding Source={x:Static ms:GameAccountSettings.IsShowAccountName}, Path=Value, Mode=TwoWay}"
|
||||
@@ -154,23 +154,23 @@
|
||||
</StackPanel>
|
||||
</spp:PageBase.ActionContent>
|
||||
<ui:TabView
|
||||
Padding="0"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="Transparent"
|
||||
CloseButtonOverlayMode="OnPointerOver"
|
||||
IsAddTabButtonVisible="False"
|
||||
Padding="0"
|
||||
SelectedItem="{Binding SelectedPlatform}"
|
||||
SelectionChanged="TabView_SelectedItemChanged"
|
||||
TabCloseRequested="TabView_TabCloseRequested"
|
||||
TabItems="{Binding GamePlatforms}"
|
||||
TabWidthMode="Equal">
|
||||
TabWidthMode="Equal"
|
||||
VerticalAlignment="Stretch">
|
||||
<ui:TabView.TabStripFooter>
|
||||
<Button
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Content=""
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
Theme="{StaticResource TabViewButtonStyle}">
|
||||
HorizontalAlignment="Left"
|
||||
Theme="{StaticResource TabViewButtonStyle}"
|
||||
VerticalAlignment="Center">
|
||||
<Button.Flyout>
|
||||
<MenuFlyout Placement="BottomEdgeAlignedLeft">
|
||||
<MenuItem
|
||||
@@ -178,7 +178,7 @@
|
||||
IsVisible="{Binding !!AddGamePlatforms.Count}"
|
||||
ItemsSource="{Binding AddGamePlatforms}">
|
||||
<MenuItem.Styles>
|
||||
<Style x:DataType="spp:PlatformAccount" Selector="MenuItem">
|
||||
<Style Selector="MenuItem" x:DataType="spp:PlatformAccount">
|
||||
<Setter Property="Header" Value="{Binding FullName}" />
|
||||
<Setter Property="Icon">
|
||||
<Setter.Value>
|
||||
@@ -239,7 +239,7 @@
|
||||
</Button>
|
||||
</ui:TabView.TabStripFooter>
|
||||
<ui:TabView.Styles>
|
||||
<Style x:DataType="spp:PlatformAccount" Selector="ui|TabViewItem">
|
||||
<Style Selector="ui|TabViewItem" x:DataType="spp:PlatformAccount">
|
||||
<!--<Setter Property="IconSource">
|
||||
<ui:FontIconSource Glyph="{Binding Icon}" />
|
||||
</Setter>-->
|
||||
@@ -255,14 +255,14 @@
|
||||
<DataTemplate DataType="spp:PlatformAccount">
|
||||
<DockPanel>
|
||||
<PathIcon
|
||||
Width="16"
|
||||
Data="{Binding Icon, Converter={StaticResource ResourceKeyValueConverter}}"
|
||||
DockPanel.Dock="Left"
|
||||
Height="16"
|
||||
Margin="0,0,8,0"
|
||||
Data="{Binding Icon, Converter={StaticResource ResourceKeyValueConverter}}"
|
||||
DockPanel.Dock="Left" />
|
||||
Width="16" />
|
||||
<TextBlock DockPanel.Dock="Right" Text="{Binding FullName}" />
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
</ui:TabView.TabItemTemplate>
|
||||
</ui:TabView>
|
||||
</spp:PageBase>
|
||||
</spp:PageBase>
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Pages;
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
|
||||
#region 自定义图片保存
|
||||
//var mostRecentUser = ISteamService.Instance.GetRememberUserList().Where(s => s.MostRecent).FirstOrDefault();
|
||||
var mostRecentUser = SteamConnectService.Current.SteamUsers.Items.Where(s => s.MostRecent).FirstOrDefault();
|
||||
if (!(App.EditHeaderLogoStream is FileStream fs3 && fs3.Name == (await App.HeaderLogoStream)?.Name))
|
||||
if (!(App.EditHeaderLogoStream is FileStream fs3 && (await App.HeaderLogoStream) is FileStream headerLogoStream && fs3.Name == headerLogoStream.Name))
|
||||
{
|
||||
if (!CheckCurrentSteamUserStats(mostRecentUser))
|
||||
return;
|
||||
@@ -135,7 +135,7 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
|
||||
else
|
||||
this.RaisePropertyChanged(nameof(App.HeaderLogoStream));
|
||||
}
|
||||
if (!(App.EditLibraryLogoStream is FileStream fs && fs.Name == (await App.LibraryLogoStream)?.Name))
|
||||
if (!(App.EditLibraryLogoStream is FileStream fs && (await App.LibraryLogoStream) is FileStream libraryLogoStream && fs.Name == libraryLogoStream.Name))
|
||||
{
|
||||
if (!CheckCurrentSteamUserStats(mostRecentUser))
|
||||
return;
|
||||
@@ -148,7 +148,7 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
|
||||
else
|
||||
this.RaisePropertyChanged(nameof(App.LibraryLogoStream));
|
||||
}
|
||||
if (!(App.EditLibraryHeroStream is FileStream fs1 && fs1.Name == (await App.LibraryHeroStream)?.Name))
|
||||
if (!(App.EditLibraryHeroStream is FileStream fs1 && (await App.LibraryHeroStream) is FileStream libraryHeroStream && fs1.Name == libraryHeroStream.Name))
|
||||
{
|
||||
if (!CheckCurrentSteamUserStats(mostRecentUser))
|
||||
return;
|
||||
@@ -161,7 +161,7 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
|
||||
else
|
||||
this.RaisePropertyChanged(nameof(App.LibraryHeroStream));
|
||||
}
|
||||
if (!(App.EditLibraryGridStream is FileStream fs2 && fs2.Name == (await App.LibraryGridStream)?.Name))
|
||||
if (!(App.EditLibraryGridStream is FileStream fs2 && (await App.LibraryGridStream) is FileStream libraryGridStream && fs2.Name == libraryGridStream.Name))
|
||||
{
|
||||
if (!CheckCurrentSteamUserStats(mostRecentUser))
|
||||
return;
|
||||
@@ -246,16 +246,16 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
|
||||
switch (type)
|
||||
{
|
||||
case SteamGridItemType.Header:
|
||||
App.EditHeaderLogoStream = (await App.HeaderLogoStream)?.Stream;
|
||||
App.EditHeaderLogoStream = (await App.HeaderLogoStream);
|
||||
break;
|
||||
case SteamGridItemType.Grid:
|
||||
App.EditLibraryGridStream = (await App.LibraryGridStream)?.Stream;
|
||||
App.EditLibraryGridStream = (await App.LibraryGridStream);
|
||||
break;
|
||||
case SteamGridItemType.Hero:
|
||||
App.EditLibraryHeroStream = (await App.LibraryHeroStream)?.Stream;
|
||||
App.EditLibraryHeroStream = (await App.LibraryHeroStream);
|
||||
break;
|
||||
case SteamGridItemType.Logo:
|
||||
App.EditLibraryLogoStream = (await App.LibraryLogoStream)?.Stream;
|
||||
App.EditLibraryLogoStream = (await App.LibraryLogoStream);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -16,6 +16,9 @@ public sealed class EditAppsPageViewModel : ViewModelBase
|
||||
|
||||
public bool IsSteamEditedAppsEmpty => !SteamEditedApps.Any_Nullable();
|
||||
|
||||
[Reactive]
|
||||
public bool IsExportingBackup { get; set; }
|
||||
|
||||
public ICommand EditAppInfoClickCommand { get; }
|
||||
|
||||
public EditAppsPageViewModel()
|
||||
@@ -56,6 +59,114 @@ public sealed class EditAppsPageViewModel : ViewModelBase
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ExportSteamEditedAppsBackup()
|
||||
{
|
||||
if (IsExportingBackup)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SteamEditedApps.Any())
|
||||
{
|
||||
Toast.Show(ToastIcon.Warning, "没有可导出的已修改游戏数据。");
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await FilePicker2.SaveAsync(new PickOptions
|
||||
{
|
||||
PickerTitle = "导出编辑游戏数据备份",
|
||||
InitialFileName = $"steam-edited-apps-{DateTime.Now:yyyyMMddHHmmss}.stmbak",
|
||||
});
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IsExportingBackup = true;
|
||||
var exportData = await Task.Run(async () =>
|
||||
{
|
||||
var stmService = ISteamService.Instance;
|
||||
var applist = await stmService.GetAppInfos(true);
|
||||
var editApps = SteamConnectService.Current.SteamApps.Items
|
||||
.Where(s => s.IsEdited)
|
||||
.ToDictionary(s => s.AppId, s => s);
|
||||
var modifiedApps = new List<ModifiedApp>();
|
||||
|
||||
foreach (var app in applist)
|
||||
{
|
||||
if (editApps.TryGetValue(app.AppId, out var editApp))
|
||||
{
|
||||
app.SetEditProperty(editApp);
|
||||
modifiedApps.Add(new ModifiedApp(app));
|
||||
}
|
||||
}
|
||||
|
||||
return (ModifiedApps: modifiedApps, Bytes: Serializable.SMP(modifiedApps));
|
||||
});
|
||||
|
||||
if (!exportData.ModifiedApps.Any())
|
||||
{
|
||||
Toast.Show(ToastIcon.Error, "导出失败:没有可序列化的修改数据。");
|
||||
return;
|
||||
}
|
||||
|
||||
using var stream = result.OpenWrite();
|
||||
await stream.WriteAsync(exportData.Bytes, 0, exportData.Bytes.Length);
|
||||
await stream.FlushAsync();
|
||||
|
||||
Toast.Show(ToastIcon.Success, $"导出成功,共 {exportData.ModifiedApps.Count} 条修改数据。");
|
||||
}
|
||||
catch
|
||||
{
|
||||
Toast.Show(ToastIcon.Error, "导出失败,请检查文件权限后重试。");
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsExportingBackup = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ImportSteamEditedAppsBackup()
|
||||
{
|
||||
var file = await FilePicker2.PickAsync(new PickOptions
|
||||
{
|
||||
PickerTitle = "导入编辑游戏数据备份",
|
||||
});
|
||||
|
||||
if (file == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var stream = await file.OpenReadAsync();
|
||||
var modifiedApps = Serializable.DMP<List<ModifiedApp>>(stream);
|
||||
if (!modifiedApps.Any_Nullable())
|
||||
{
|
||||
Toast.Show(ToastIcon.Warning, "导入文件中没有可还原的数据。");
|
||||
return;
|
||||
}
|
||||
|
||||
var applyCount = SteamConnectService.Current.ApplyModifiedApps(modifiedApps);
|
||||
if (applyCount <= 0)
|
||||
{
|
||||
Toast.Show(ToastIcon.Warning, "导入完成,但未匹配到可还原的游戏。\n请确认账号或游戏库与备份来源一致。");
|
||||
return;
|
||||
}
|
||||
|
||||
this.RaisePropertyChanged(nameof(IsSteamEditedAppsEmpty));
|
||||
Toast.Show(ToastIcon.Success, $"导入成功,已还原 {applyCount} 个游戏的修改数据。");
|
||||
}
|
||||
catch
|
||||
{
|
||||
Toast.Show(ToastIcon.Error, "导入失败,文件格式可能不正确。");
|
||||
}
|
||||
}
|
||||
|
||||
//public async Task ClearSteamEditedApps()
|
||||
//{
|
||||
// if (await MessageBox.ShowAsync("确定要重置所有的已修改数据吗?(该操作不可还原)", AssemblyInfo.Trademark, MessageBox.Button.OKCancel) == MessageBox.Result.OK)
|
||||
|
||||
+26
-4
@@ -200,10 +200,32 @@ public sealed class AchievementAppPageViewModel : WindowViewModel
|
||||
continue;
|
||||
}
|
||||
|
||||
var rawType = stat["type_int"].Valid
|
||||
? stat["type_int"].AsInteger(0)
|
||||
: stat["type"].AsInteger(0);
|
||||
var type = (UserStatType)rawType;
|
||||
UserStatType type;
|
||||
|
||||
// schema in the new format
|
||||
var typeNode = stat["type"];
|
||||
if (typeNode.Valid == true && typeNode.Type == KeyValueType.String)
|
||||
{
|
||||
if (Enum.TryParse((string)typeNode.Value, true, out type) == false)
|
||||
{
|
||||
type = UserStatType.Invalid;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
type = UserStatType.Invalid;
|
||||
}
|
||||
|
||||
// schema in the old format
|
||||
if (type == UserStatType.Invalid)
|
||||
{
|
||||
var typeIntNode = stat["type_int"];
|
||||
var rawType = typeIntNode.Valid == true
|
||||
? typeIntNode.AsInteger(0)
|
||||
: typeNode.AsInteger(0);
|
||||
type = (UserStatType)rawType;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case UserStatType.Invalid:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Pages;
|
||||
|
||||
|
||||
@@ -221,6 +221,7 @@
|
||||
FileStream="{Binding App.EditHeaderLogoStream, Mode=TwoWay}">
|
||||
<spp:Image2
|
||||
DecodeHeight="260"
|
||||
KeepSourceStreamOpen="True"
|
||||
Source="{Binding App.EditHeaderLogoStream}"
|
||||
Stretch="Uniform" />
|
||||
</spp:CustomFilePicker>
|
||||
@@ -275,6 +276,7 @@
|
||||
FileStream="{Binding App.EditLibraryGridStream, Mode=TwoWay}">
|
||||
<spp:Image2
|
||||
DecodeHeight="260"
|
||||
KeepSourceStreamOpen="True"
|
||||
Source="{Binding App.EditLibraryGridStream}"
|
||||
Stretch="Uniform" />
|
||||
</spp:CustomFilePicker>
|
||||
@@ -329,6 +331,7 @@
|
||||
FileStream="{Binding App.EditLibraryHeroStream, Mode=TwoWay}">
|
||||
<spp:Image2
|
||||
DecodeHeight="260"
|
||||
KeepSourceStreamOpen="True"
|
||||
Source="{Binding App.EditLibraryHeroStream}"
|
||||
Stretch="Uniform" />
|
||||
</spp:CustomFilePicker>
|
||||
@@ -381,6 +384,7 @@
|
||||
FileStream="{Binding App.EditLibraryLogoStream, Mode=TwoWay}">
|
||||
<spp:Image2
|
||||
DecodeWidth="220"
|
||||
KeepSourceStreamOpen="True"
|
||||
Source="{Binding App.EditLibraryLogoStream}"
|
||||
Stretch="Uniform" />
|
||||
</spp:CustomFilePicker>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
using Avalonia.VisualTree;
|
||||
using BD.SteamClient.Enums.SteamGridDB;
|
||||
using BD.SteamClient.Models.SteamGridDB;
|
||||
|
||||
@@ -67,6 +67,18 @@
|
||||
Theme="{StaticResource TransparentButton}">
|
||||
<DropDownButton.Flyout>
|
||||
<ui:FAMenuFlyout Placement="BottomEdgeAlignedRight">
|
||||
<ui:MenuFlyoutItem
|
||||
Command="{Binding ExportSteamEditedAppsBackup}"
|
||||
IsEnabled="{Binding !IsExportingBackup}"
|
||||
Text="{Binding Path=Res.Export,
|
||||
Mode=OneWay,
|
||||
Source={x:Static s:ResourceService.Current}}" />
|
||||
<ui:MenuFlyoutItem
|
||||
Command="{Binding ImportSteamEditedAppsBackup}"
|
||||
Text="{Binding Path=Res.Import,
|
||||
Mode=OneWay,
|
||||
Source={x:Static s:ResourceService.Current}}" />
|
||||
<ui:MenuFlyoutSeparator />
|
||||
<!--<ui:MenuFlyoutItem
|
||||
Command="{Binding DeleteAllButton_Click}"
|
||||
IconSource="Delete"
|
||||
@@ -100,88 +112,76 @@
|
||||
IsLoading="false"
|
||||
IsShowNoResultText="{Binding !!!SteamEditedApps.Count, Mode=OneWay}"
|
||||
NoResultMessage="{Binding Path=Res.SaveEditedAppInfo_AppsEmpty, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
|
||||
<ItemsControl ItemsSource="{Binding SteamEditedApps}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<ui:ItemsRepeater ItemsSource="{Binding SteamEditedApps}">
|
||||
<ui:ItemsRepeater.Layout>
|
||||
<ui:UniformGridLayout
|
||||
ItemsJustification="Start"
|
||||
ItemsStretch="Uniform"
|
||||
MinColumnSpacing="10"
|
||||
MinItemHeight="310"
|
||||
MinItemWidth="150"
|
||||
MinRowSpacing="10" />
|
||||
</ui:ItemsRepeater.Layout>
|
||||
<ui:ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate DataType="spp:SteamApp">
|
||||
<Border Margin="5" spp:Animations.EnableAnimations="True">
|
||||
<spp:AppItem
|
||||
Title="{Binding DisplayName}"
|
||||
Height="270"
|
||||
MaxWidth="120"
|
||||
Classes="Vertical"
|
||||
ClickCommand="{Binding $parent[spp:PageBase].((spp:EditAppsPageViewModel)DataContext).EditAppInfoClickCommand}"
|
||||
ClickCommandParameter="{Binding}"
|
||||
Cursor="Hand">
|
||||
<spp:AppItem.Status>
|
||||
<Border Classes="Status">
|
||||
<TextBlock Text="{Binding Path=Res.Edit, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
<spp:AppItem
|
||||
Title="{Binding DisplayName}"
|
||||
Classes="Vertical"
|
||||
ClickCommand="{Binding $parent[spp:PageBase].((spp:EditAppsPageViewModel)DataContext).EditAppInfoClickCommand}"
|
||||
ClickCommandParameter="{Binding}"
|
||||
Cursor="Hand">
|
||||
<spp:AppItem.Status>
|
||||
<Border Classes="Status">
|
||||
<TextBlock Text="{Binding Path=Res.Edit, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
|
||||
</Border>
|
||||
</spp:AppItem.Status>
|
||||
<spp:AppItem.Image>
|
||||
<Panel Width="{Binding $parent[spp:AppItem].Bounds.Width}">
|
||||
<spp:Image2
|
||||
Name="AppImage"
|
||||
DecodeWidth="120"
|
||||
FallbackSource="avares://BD.WTTS.Client.Plugins.GameList/UI/Assets/defaultappimage.png"
|
||||
RenderOptions.BitmapInterpolationMode="HighQuality"
|
||||
Source="{Binding LibraryGridStream^}"
|
||||
Stretch="UniformToFill" />
|
||||
<TextBlock
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
IsVisible="{Binding #AppImage.IsFailed}"
|
||||
Text="{Binding DisplayName}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="WrapWithOverflow"
|
||||
Theme="{StaticResource BodyStrongTextBlockStyle}" />
|
||||
<Border
|
||||
Margin="10,0,10,-3"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="#3D4450"
|
||||
CornerRadius="3"
|
||||
IsVisible="{Binding IsInstalled}">
|
||||
<DockPanel Margin="10,3" HorizontalAlignment="Center">
|
||||
<TextBlock
|
||||
Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
IsVisible="{Binding InstalledDrive, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}"
|
||||
Text="{Binding InstalledDrive}" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
Text="{Binding SizeOnDisk, Converter={StaticResource StringFormatConverter}, ConverterParameter=size}"
|
||||
TextWrapping="Wrap" />
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</spp:AppItem.Status>
|
||||
<!--<spp:AppItem.ActionButton>
|
||||
<Button
|
||||
Command="{Binding $parent[spp:PageBase].((spp:EditAppsPageViewModel)DataContext).DeleteButtonCommand}"
|
||||
CommandParameter="{Binding}"
|
||||
Cursor="Hand"
|
||||
ToolTip.Tip="{Binding Path=Res.GameList_RemoveItemBtn, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
|
||||
<Viewbox>
|
||||
<ui:SymbolIcon Symbol="Delete" />
|
||||
</Viewbox>
|
||||
</Button>
|
||||
</spp:AppItem.ActionButton>-->
|
||||
<spp:AppItem.Image>
|
||||
<Panel MinWidth="{Binding $parent[spp:AppItem].MinWidth}" MaxWidth="{Binding $parent[spp:AppItem].MaxWidth}">
|
||||
<spp:Image2
|
||||
Name="AppImage"
|
||||
DecodeWidth="120"
|
||||
FallbackSource="avares://BD.WTTS.Client.Plugins.GameList/UI/Assets/defaultappimage.png"
|
||||
RenderOptions.BitmapInterpolationMode="HighQuality"
|
||||
Source="{Binding LibraryGridStream^}"
|
||||
Stretch="UniformToFill" />
|
||||
<TextBlock
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
IsVisible="{Binding #AppImage.IsFailed}"
|
||||
Text="{Binding DisplayName}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="WrapWithOverflow"
|
||||
Theme="{StaticResource BodyStrongTextBlockStyle}" />
|
||||
|
||||
<Border
|
||||
Margin="10,0,10,-3"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="#3D4450"
|
||||
CornerRadius="3"
|
||||
IsVisible="{Binding IsInstalled}">
|
||||
<DockPanel Margin="10,3" HorizontalAlignment="Center">
|
||||
<TextBlock
|
||||
Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
IsVisible="{Binding InstalledDrive, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}"
|
||||
Text="{Binding InstalledDrive}" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
Text="{Binding SizeOnDisk, Converter={StaticResource StringFormatConverter}, ConverterParameter=size}"
|
||||
TextWrapping="Wrap" />
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</Panel>
|
||||
</spp:AppItem.Image>
|
||||
</spp:AppItem>
|
||||
</Border>
|
||||
</Panel>
|
||||
</spp:AppItem.Image>
|
||||
</spp:AppItem>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ui:ItemsRepeater.ItemTemplate>
|
||||
</ui:ItemsRepeater>
|
||||
</spp:ContentLoader>
|
||||
</spp:PageBase>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Pages;
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="spp:SteamApp">
|
||||
<Border Margin="5" spp:Animations.EnableAnimations="True">
|
||||
<Border Margin="5" spp:Animations.EnableAnimations="False">
|
||||
<spp:AppItem
|
||||
Title="{Binding DisplayName}"
|
||||
Height="270"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Windows;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI.Avalonia;
|
||||
|
||||
namespace BD.WTTS.UI.Views.Windows;
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
</ControlTheme>
|
||||
</ScrollViewer.Resources>
|
||||
<WrapPanel>
|
||||
<Border spp:Animations.EnableAnimations="True">
|
||||
<Border spp:Animations.EnableAnimations="False">
|
||||
<Button
|
||||
Click="GameToolsPage_Click"
|
||||
IsEnabled="{spp:OnPlatform Windows}"
|
||||
@@ -130,7 +130,7 @@
|
||||
</Button>
|
||||
</Border>
|
||||
|
||||
<Border spp:Animations.EnableAnimations="True">
|
||||
<Border spp:Animations.EnableAnimations="False">
|
||||
<Button
|
||||
Click="GameToolsPage_Click"
|
||||
IsEnabled="{spp:OnPlatform Windows}"
|
||||
@@ -160,7 +160,7 @@
|
||||
</Button>
|
||||
</Border>
|
||||
|
||||
<Border spp:Animations.EnableAnimations="True">
|
||||
<Border spp:Animations.EnableAnimations="False">
|
||||
<Button
|
||||
Click="GameToolsPage_Click"
|
||||
IsEnabled="{spp:OnPlatform Windows}"
|
||||
|
||||
@@ -5,6 +5,9 @@ namespace BD.WTTS.Models;
|
||||
|
||||
public class IdleApp : ReactiveObject
|
||||
{
|
||||
private bool isBlacklisted;
|
||||
private bool isPrivateGame;
|
||||
|
||||
public SteamApp App { get; }
|
||||
|
||||
public Badge Badge { get; }
|
||||
@@ -45,6 +48,50 @@ public class IdleApp : ReactiveObject
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsBlacklisted
|
||||
{
|
||||
get => isBlacklisted;
|
||||
set
|
||||
{
|
||||
if (isBlacklisted != value)
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref isBlacklisted, value);
|
||||
this.RaisePropertyChanged(nameof(IsExcludedFromIdle));
|
||||
this.RaisePropertyChanged(nameof(ExcludedReasonText));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPrivateGame
|
||||
{
|
||||
get => isPrivateGame;
|
||||
set
|
||||
{
|
||||
if (isPrivateGame != value)
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref isPrivateGame, value);
|
||||
this.RaisePropertyChanged(nameof(IsExcludedFromIdle));
|
||||
this.RaisePropertyChanged(nameof(ExcludedReasonText));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsExcludedFromIdle => IsBlacklisted || IsPrivateGame;
|
||||
|
||||
public string ExcludedReasonText
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsPrivateGame && IsBlacklisted)
|
||||
return "私密游戏(不掉卡) + 已拉黑";
|
||||
if (IsPrivateGame)
|
||||
return "私密游戏(不掉卡)";
|
||||
if (IsBlacklisted)
|
||||
return "已拉黑";
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public IdleApp(Badge badge)
|
||||
{
|
||||
Badge = badge;
|
||||
|
||||
@@ -55,6 +55,11 @@ public partial interface ISteamIdleSettings
|
||||
/// </summary>
|
||||
double RefreshBadgesTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 挂卡黑名单游戏列表
|
||||
/// </summary>
|
||||
Dictionary<uint, string?>? BlacklistAppList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 挂卡状态更新时间的默认值
|
||||
/// </summary>
|
||||
@@ -90,4 +95,9 @@ public partial interface ISteamIdleSettings
|
||||
/// </summary>
|
||||
static readonly double DefaultRefreshBadgesTime = 6;
|
||||
|
||||
/// <summary>
|
||||
/// 挂卡黑名单游戏列表的默认值
|
||||
/// </summary>
|
||||
static readonly Dictionary<uint, string?> DefaultBlacklistAppList = [];
|
||||
|
||||
}
|
||||
|
||||
@@ -85,6 +85,12 @@ public sealed partial class SteamIdleSettings_ : ISteamIdleSettings, ISettings,
|
||||
[MPKey(6), MP2Key(6), JsonPropertyOrder(6)]
|
||||
public double RefreshBadgesTime { get; set; } = ISteamIdleSettings.DefaultRefreshBadgesTime;
|
||||
|
||||
/// <summary>
|
||||
/// 挂卡黑名单游戏列表
|
||||
/// </summary>
|
||||
[MPKey(7), MP2Key(7), JsonPropertyOrder(7)]
|
||||
public Dictionary<uint, string?>? BlacklistAppList { get; set; } = ISteamIdleSettings.DefaultBlacklistAppList;
|
||||
|
||||
}
|
||||
|
||||
public static partial class SteamIdleSettings
|
||||
@@ -131,4 +137,10 @@ public static partial class SteamIdleSettings
|
||||
public static SettingsStructProperty<double, SteamIdleSettings_> RefreshBadgesTime { get; }
|
||||
= new(DefaultRefreshBadgesTime);
|
||||
|
||||
/// <summary>
|
||||
/// 挂卡黑名单游戏列表
|
||||
/// </summary>
|
||||
public static SettingsProperty<uint, string?, Dictionary<uint, string?>, SteamIdleSettings_> BlacklistAppList { get; }
|
||||
= new(DefaultBlacklistAppList);
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user