Compare commits

..

697 Commits

Author SHA1 Message Date
DismissedLight
a464cc1f33 1.7.0 package 2023-09-01 20:07:05 +08:00
DismissedLight
8a447813d7 Merge pull request #877 from DGP-Studio/develop
1.7.0 Stable
2023-09-01 19:52:57 +08:00
Masterain
ec0cbc614d Merge pull request #874 from DGP-Studio/l10n_develop
New Crowdin updates
2023-09-01 02:54:34 -07:00
Masterain
4c5e6984d7 New translations SH.resx (Japanese) 2023-09-01 02:53:41 -07:00
Masterain
c5bbbbdfe3 New translations SH.resx (English) 2023-09-01 02:15:39 -07:00
Lightczx
9f9f2a9d14 Update GachaLog.png 2023-09-01 13:50:27 +08:00
Masterain
a7640fdcb3 New translations SH.resx (English) 2023-08-31 16:46:17 -07:00
DismissedLight
2bec008381 fix announcement padding 2023-08-31 23:50:36 +08:00
DismissedLight
3ada42c927 announcement 2023-08-31 23:47:10 +08:00
Lightczx
bbed07b2ea add announcement 2023-08-31 17:29:50 +08:00
Lightczx
d90ce0afe0 improve achievement & gachalog large dataset load speed 2023-08-31 10:23:25 +08:00
Lightczx
4e0d83726e segment abstraction 2023-08-30 15:51:59 +08:00
Masterain
2b53ffd4d2 Merge pull request #867 from DGP-Studio/l10n_develop
New Crowdin updates
2023-08-30 00:47:50 -07:00
Masterain
84c1fbd13c New translations SH.resx (English) 2023-08-30 00:06:31 -07:00
Masterain
4077c85be4 New translations SH.resx (Chinese Traditional) 2023-08-30 00:06:30 -07:00
Lightczx
948987e31a fix gachalog and cultivation view dispose pipeline 2023-08-30 15:05:51 +08:00
Lightczx
686d2bd3b8 attempt to fix cultivation selection 2023-08-30 14:34:44 +08:00
Masterain
552fb131ed New translations SH.resx (Japanese) 2023-08-29 21:46:19 -07:00
Masterain
cb1fb96cf8 New translations SH.resx (English) 2023-08-29 21:46:18 -07:00
Masterain
7e37d0a008 New translations SH.resx (Chinese Traditional) 2023-08-29 21:46:17 -07:00
Masterain
3abe1d25cd New translations SH.resx (Korean) 2023-08-29 21:46:16 -07:00
Lightczx
00c13ec333 fix gacha statistic pull prediction view 2023-08-30 12:37:46 +08:00
Masterain
c3246da5dd New translations SH.resx (English) 2023-08-29 10:36:54 -07:00
Masterain
79ade22f94 New translations SH.resx (Chinese Traditional) 2023-08-29 08:15:48 -07:00
DismissedLight
9d196c7c14 dailynote text no wrap 2023-08-29 22:47:09 +08:00
Masterain
3949e32324 New translations SH.resx (Japanese) 2023-08-29 07:13:32 -07:00
Masterain
c04b473494 New translations SH.resx (English) 2023-08-29 07:13:31 -07:00
Masterain
a8437b90fc New translations SH.resx (Chinese Traditional) 2023-08-29 07:13:29 -07:00
Masterain
c667623c06 New translations SH.resx (Korean) 2023-08-29 07:13:28 -07:00
DismissedLight
e62a386ac8 fix minor bugs 2023-08-29 21:23:26 +08:00
Masterain
7407bd44ce New translations SH.resx (English) 2023-08-29 04:05:20 -07:00
Masterain
aaea81022f New translations SH.resx (Chinese Traditional) 2023-08-29 04:05:19 -07:00
Masterain
d99b7b46c7 New translations SH.resx (Korean) 2023-08-29 04:05:17 -07:00
Lightczx
a000b3a10e fix guide language text 2023-08-29 17:07:58 +08:00
Lightczx
d06cd53ac4 correct reward time 2023-08-29 17:06:09 +08:00
Masterain
0b8c3fd225 Merge pull request #862 from DGP-Studio/l10n_develop
New Crowdin updates
2023-08-29 02:05:29 -07:00
Masterain
6c267b51d3 New translations SH.resx (English) 2023-08-29 02:02:10 -07:00
Masterain
0140c85d07 New translations SH.resx (Chinese Traditional) 2023-08-29 02:02:09 -07:00
Masterain
9129ff88fd New translations SH.resx (English) 2023-08-29 01:33:17 -07:00
Masterain
a123325c71 New translations SH.resx (Chinese Traditional) 2023-08-29 01:33:16 -07:00
Masterain
cb9e4c5c04 New translations SH.resx (Korean) 2023-08-29 01:33:14 -07:00
Masterain
24a186e254 New translations SH.resx (Japanese) 2023-08-29 01:33:13 -07:00
Lightczx
604c51e064 fix wiki avatar & weapon select item in view 2023-08-29 15:31:20 +08:00
Lightczx
088f915a54 1.7.0 RC 3 2023-08-29 15:12:14 +08:00
Lightczx
5fdbcfc2e8 update dependencies 2023-08-29 11:06:03 +08:00
Lightczx
bcaeb86fe7 Merge branch 'feature/guide' into develop 2023-08-29 10:46:53 +08:00
Lightczx
c83db8695f guide complete 2023-08-29 10:45:53 +08:00
DismissedLight
c2319cac68 add missing localizible string 2023-08-29 09:10:17 +08:00
DismissedLight
cd3ce6d338 fix #815 2023-08-29 09:10:17 +08:00
Masterain
0bdc5d6c54 New translations SH.resx (English) 2023-08-29 09:10:17 +08:00
Masterain
f007f9b193 New translations SH.resx (English) 2023-08-29 09:10:17 +08:00
Masterain
90ea0f7276 New translations SH.resx (English) 2023-08-29 09:10:17 +08:00
Masterain
af5c4c258d New translations SH.resx (English) 2023-08-29 09:10:17 +08:00
Masterain
eaa50df837 New translations SH.resx (Chinese Traditional) 2023-08-29 09:10:17 +08:00
Masterain
04ea760fba New translations SH.resx (Korean) 2023-08-29 09:10:17 +08:00
Masterain
57c0016839 New translations SH.resx (Japanese) 2023-08-29 09:10:17 +08:00
Masterain
3f019e0d23 New translations SH.resx (English) 2023-08-29 09:10:17 +08:00
Masterain
09e94f5ccc New translations SH.resx (English) 2023-08-29 09:10:17 +08:00
Masterain
e5f295fae1 New translations SH.resx (English) 2023-08-29 09:10:17 +08:00
Masterain
ae8da285ab New translations SH.resx (Chinese Traditional) 2023-08-29 09:10:17 +08:00
Masterain
50b244f76c New translations SH.resx (Korean) 2023-08-29 09:10:17 +08:00
Masterain
171add072c New translations SH.resx (Japanese) 2023-08-29 09:10:17 +08:00
Masterain
16ea8ea5fb New translations SH.resx (English) 2023-08-29 09:10:17 +08:00
Masterain
18bce39e2a New translations SH.resx (English) 2023-08-29 09:10:17 +08:00
Masterain
d8712c45b4 New translations SH.resx (Chinese Traditional) 2023-08-29 09:10:17 +08:00
Masterain
325800602c New translations SH.resx (Korean) 2023-08-29 09:10:17 +08:00
Masterain
71bbf20c12 New translations SH.resx (Japanese) 2023-08-29 09:10:16 +08:00
Masterain
a55ef96a79 New translations SH.resx (Korean) 2023-08-29 09:10:16 +08:00
Masterain
f27308c12d New translations SH.resx (Japanese) 2023-08-29 09:10:16 +08:00
Masterain
a8877d93b8 New translations SH.resx (English) 2023-08-29 09:10:16 +08:00
Masterain
4605df8f03 New translations SH.resx (Chinese Traditional) 2023-08-29 09:10:16 +08:00
Masterain
3a26c7a3ed New translations SH.resx (Korean) 2023-08-29 09:10:16 +08:00
Masterain
e648080d8b New translations SH.resx (Japanese) 2023-08-29 09:10:16 +08:00
Masterain
60585f90ed New translations SH.resx (English) 2023-08-29 09:10:16 +08:00
Masterain
f104823d04 New translations SH.resx (English) 2023-08-29 09:10:16 +08:00
Masterain
29c6e20738 New translations SH.resx (English) 2023-08-29 09:10:16 +08:00
Masterain
f922bd6c2f New translations SH.resx (Chinese Traditional) 2023-08-29 09:10:16 +08:00
Masterain
76b2921847 New translations SH.resx (Korean) 2023-08-29 09:10:16 +08:00
Masterain
6a41ace38d New translations SH.resx (Japanese) 2023-08-29 09:10:16 +08:00
Masterain
40cd084304 New translations SH.resx (English) 2023-08-29 09:10:16 +08:00
Masterain
f84e5f2f1b New translations SH.resx (Chinese Traditional) 2023-08-29 09:10:16 +08:00
Masterain
b9d7987605 New translations SH.resx (Korean) 2023-08-29 09:10:16 +08:00
Masterain
ddf272d8f4 New translations SH.resx (Japanese) 2023-08-29 09:10:16 +08:00
Masterain
7c7b959916 New translations SH.resx (English) 2023-08-29 09:10:16 +08:00
Masterain
260aa8797f New translations SH.resx (Chinese Traditional) 2023-08-29 09:10:16 +08:00
Masterain
e551072e52 New translations SH.resx (Korean) 2023-08-29 09:10:16 +08:00
Masterain
f3a271dd48 New translations SH.resx (Japanese) 2023-08-29 09:10:16 +08:00
Masterain
f8d804eb4e New translations SH.resx (English) 2023-08-29 09:10:16 +08:00
Masterain
4271bdb23b New translations SH.resx (Chinese Traditional) 2023-08-29 09:10:16 +08:00
Masterain
0f96a6d8da New translations SH.resx (Korean) 2023-08-29 09:10:16 +08:00
Masterain
f9185b519e New translations SH.resx (Japanese) 2023-08-29 09:10:16 +08:00
Masterain
59f5095432 New translations SH.resx (English) 2023-08-29 09:10:16 +08:00
Masterain
19d4b8f25d New translations SH.resx (English) 2023-08-29 09:10:16 +08:00
Masterain
0026add54b New translations SH.resx (Chinese Traditional) 2023-08-29 09:10:16 +08:00
Masterain
0c6789c71d New translations SH.resx (Korean) 2023-08-29 09:10:16 +08:00
Masterain
1d45c75584 New translations SH.resx (Japanese) 2023-08-29 09:10:15 +08:00
Masterain
b4d58405e0 New translations SH.resx (Japanese) 2023-08-29 09:10:15 +08:00
Masterain
85ee37926e New translations SH.resx (English) 2023-08-29 09:10:15 +08:00
Masterain
3b411c6949 New translations SH.resx (Chinese Traditional) 2023-08-29 09:10:15 +08:00
Masterain
361cd850da New translations SH.resx (English) 2023-08-29 09:10:15 +08:00
Masterain
3ea59a0352 New translations SH.resx (Chinese Traditional) 2023-08-29 09:10:15 +08:00
Masterain
9abe39e806 New translations SH.resx (Korean) 2023-08-29 09:10:15 +08:00
Masterain
47fcd6f851 New translations SH.resx (Japanese) 2023-08-29 09:10:15 +08:00
Masterain
8f83853a8f New translations SH.resx (English) 2023-08-29 09:10:15 +08:00
Masterain
f5300745b0 New translations SH.resx (Chinese Traditional) 2023-08-29 09:10:15 +08:00
Masterain
43f65db1b6 New translations SH.resx (Korean) 2023-08-29 09:10:15 +08:00
Masterain
d2d1e73891 New translations SH.resx (Japanese) 2023-08-29 09:10:15 +08:00
Masterain
4bf1d8513d New translations SH.resx (English) 2023-08-29 09:10:15 +08:00
Masterain
6006e79923 New translations SH.resx (Chinese Traditional) 2023-08-29 09:10:15 +08:00
Masterain
2cd5d0c622 New translations SH.resx (Korean) 2023-08-29 09:10:15 +08:00
Masterain
f2d6a2627c New translations SH.resx (Japanese) 2023-08-29 09:10:15 +08:00
Masterain
7b6c340348 New translations SH.resx (Japanese) 2023-08-29 09:10:15 +08:00
Masterain
5e5f5398fb New translations SH.resx (English) 2023-08-29 09:10:15 +08:00
Masterain
bb042a1498 New translations SH.resx (English) 2023-08-29 09:10:15 +08:00
Masterain
9db1fc360b New translations SH.resx (Chinese Traditional) 2023-08-29 09:10:15 +08:00
Masterain
4a3676d549 New translations SH.resx (Korean) 2023-08-29 09:10:15 +08:00
Masterain
fdbf7ecf90 New translations SH.resx (Japanese) 2023-08-29 09:10:15 +08:00
Masterain
a9712fa6df New translations SH.resx (English) 2023-08-28 09:34:09 -07:00
Masterain
a3e105db7b New translations SH.resx (English) 2023-08-28 07:58:06 -07:00
Masterain
94fe2016f8 New translations SH.resx (Chinese Traditional) 2023-08-28 07:58:05 -07:00
Masterain
baab021a42 New translations SH.resx (Korean) 2023-08-28 07:58:04 -07:00
Masterain
5c3365a7c6 New translations SH.resx (Japanese) 2023-08-28 07:58:03 -07:00
DismissedLight
a5a85ac0fc add missing localizible string 2023-08-28 22:40:46 +08:00
DismissedLight
967f01108c fix #815 2023-08-28 22:30:09 +08:00
Lightczx
605a48f8f2 guide view 2023-08-28 17:25:25 +08:00
Masterain
058484459b Merge pull request #845 from DGP-Studio/l10n_develop
New Crowdin updates
2023-08-28 01:06:11 -07:00
Lightczx
32a780f460 remove redundant icons 2023-08-28 11:17:25 +08:00
Lightczx
d50117af3d fix cultivation icons 2023-08-28 09:32:55 +08:00
Masterain
a8406cf5a4 New translations SH.resx (English) 2023-08-27 13:44:13 -07:00
Masterain
67052b5f24 New translations SH.resx (English) 2023-08-27 12:39:33 -07:00
Masterain
beff98c8c5 New translations SH.resx (English) 2023-08-27 09:23:50 -07:00
DismissedLight
7756e348a8 add new buff & time info for spiral abyss 2023-08-27 23:26:47 +08:00
Masterain
b3fabf92e8 New translations SH.resx (English) 2023-08-27 08:14:34 -07:00
Masterain
3ced5ee962 New translations SH.resx (Chinese Traditional) 2023-08-27 08:14:33 -07:00
Masterain
fc3424e59e New translations SH.resx (Korean) 2023-08-27 08:14:32 -07:00
Masterain
5e7e310bf5 New translations SH.resx (Japanese) 2023-08-27 08:14:31 -07:00
DismissedLight
92afa4b23c add ability to customize geetest url 2023-08-27 22:20:28 +08:00
DismissedLight
82b00ba458 Merge pull request #858 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/develop/packages-b2d8e37c27
Bump the packages group in /src/Snap.Hutao with 2 updates
2023-08-27 20:15:35 +08:00
dependabot[bot]
3f798b86a4 Bump the packages group in /src/Snap.Hutao with 2 updates
Bumps the packages group in /src/Snap.Hutao with 2 updates: [Microsoft.CodeAnalysis.CSharp](https://github.com/dotnet/roslyn) and [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest).


Updates `Microsoft.CodeAnalysis.CSharp` from 4.6.0 to 4.7.0
- [Release notes](https://github.com/dotnet/roslyn/releases)
- [Changelog](https://github.com/dotnet/roslyn/blob/main/docs/Breaking%20API%20Changes.md)
- [Commits](https://github.com/dotnet/roslyn/commits)

Updates `Microsoft.NET.Test.Sdk` from 17.7.0 to 17.7.1
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.7.0...v17.7.1)

---
updated-dependencies:
- dependency-name: Microsoft.CodeAnalysis.CSharp
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: packages
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: packages
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-27 12:14:34 +00:00
DismissedLight
49fa5846f8 Merge pull request #857 from DGP-Studio/develop
1.7.0 RC 2
2023-08-27 20:14:10 +08:00
DismissedLight
1e73c1ba96 fix #855 2023-08-27 20:10:31 +08:00
DismissedLight
f37ed74238 fix response message on -100 2023-08-27 19:38:08 +08:00
Masterain
b57faac904 New translations SH.resx (English) 2023-08-27 04:21:41 -07:00
Masterain
4f7be56376 New translations SH.resx (English) 2023-08-27 03:10:55 -07:00
Masterain
81574f678c New translations SH.resx (English) 2023-08-27 02:10:59 -07:00
Masterain
188fd831e4 New translations SH.resx (Chinese Traditional) 2023-08-27 02:10:58 -07:00
Masterain
8ff369bd11 New translations SH.resx (Korean) 2023-08-27 02:10:57 -07:00
Masterain
51871f9795 New translations SH.resx (Japanese) 2023-08-27 02:10:56 -07:00
DismissedLight
8ca6251d33 add refresh time for avatar property 2023-08-27 17:02:23 +08:00
DismissedLight
00f083608e refine navigation icons 2023-08-27 15:57:39 +08:00
DismissedLight
14c75d7c04 fix dailynote activation exiting 2023-08-26 21:50:50 +08:00
DismissedLight
eb557afd18 add color for enhanced count 2023-08-26 19:55:02 +08:00
Masterain
2ac428d848 New translations SH.resx (English) 2023-08-26 02:32:02 -07:00
DismissedLight
1d8ee4cee5 remove redundant attribute 2023-08-26 17:08:18 +08:00
Masterain
335dbc9dc7 New translations SH.resx (English) 2023-08-26 01:28:59 -07:00
Masterain
c87555e86f New translations SH.resx (Chinese Traditional) 2023-08-26 01:28:58 -07:00
Masterain
809ea5768c New translations SH.resx (Korean) 2023-08-26 01:28:57 -07:00
Masterain
7b035053a1 New translations SH.resx (Japanese) 2023-08-26 01:28:56 -07:00
DismissedLight
d872639c19 Merge branch 'main' into develop 2023-08-26 16:16:34 +08:00
DismissedLight
e969267c1e port back signin reward feature 2023-08-26 16:13:49 +08:00
Masterain
a874ddc078 Update azure-pipelines.yml 2023-08-25 22:37:52 -07:00
DismissedLight
853473ebf7 fix avatar property page issue 2023-08-26 11:05:03 +08:00
Masterain
08a1363a13 New translations SH.resx (Korean) 2023-08-25 19:47:31 -07:00
Masterain
d8fdcbcf2f New translations SH.resx (Japanese) 2023-08-25 19:47:30 -07:00
DismissedLight
0cdcb1c444 refine user panel UI 2023-08-26 10:36:05 +08:00
Masterain
548ecea28b New translations SH.resx (English) 2023-08-25 09:58:38 -07:00
Masterain
f14933a797 New translations SH.resx (Chinese Traditional) 2023-08-25 09:58:37 -07:00
Masterain
8b53bb6a89 New translations SH.resx (Korean) 2023-08-25 09:58:36 -07:00
Masterain
99a91166ee New translations SH.resx (Japanese) 2023-08-25 09:58:35 -07:00
DismissedLight
0de4aff03a fix dailynote refresh time notify 2023-08-26 00:14:18 +08:00
Masterain
7ef88d390c New translations SH.resx (English) 2023-08-25 08:50:03 -07:00
Masterain
fe8648e241 New translations SH.resx (English) 2023-08-25 03:28:44 -07:00
Masterain
5f000629e8 New translations SH.resx (English) 2023-08-25 02:31:35 -07:00
Masterain
604708298a New translations SH.resx (Chinese Traditional) 2023-08-25 02:31:34 -07:00
Masterain
cc529df60c New translations SH.resx (Korean) 2023-08-25 02:31:32 -07:00
Masterain
195ddb20a0 New translations SH.resx (Japanese) 2023-08-25 02:31:31 -07:00
Lightczx
020c8c38a8 add signin client back 2023-08-25 17:27:57 +08:00
Lightczx
800ecb07d7 change how we format reliquary sub property 2023-08-25 15:15:21 +08:00
Lightczx
486e7fffd2 add enhanced count for composed reliquary sub affix 2023-08-25 14:35:09 +08:00
Masterain
119bacea7d New translations SH.resx (English) 2023-08-24 20:49:48 -07:00
Masterain
900730935e New translations SH.resx (Chinese Traditional) 2023-08-24 20:49:47 -07:00
Masterain
cd37fc761f New translations SH.resx (Korean) 2023-08-24 20:49:46 -07:00
Masterain
79d8a63016 New translations SH.resx (Japanese) 2023-08-24 20:49:44 -07:00
Lightczx
0fd018f42e UIGF import denial on invalid itemid 2023-08-25 11:47:53 +08:00
Lightczx
c1cc4961a0 try fix dailynote notification user sync 2023-08-25 11:36:07 +08:00
Masterain
243217d1fa New translations SH.resx (English) 2023-08-24 08:04:53 -07:00
Masterain
c4e3e993fd New translations SH.resx (Chinese Traditional) 2023-08-24 08:04:52 -07:00
Masterain
4e4770dc23 New translations SH.resx (Korean) 2023-08-24 08:04:51 -07:00
Masterain
a74d9e5c4c New translations SH.resx (Japanese) 2023-08-24 08:04:49 -07:00
DismissedLight
b7642847cd fix refresh time update 2023-08-24 22:55:28 +08:00
DismissedLight
5f37b6bd6d add setting for dailynote auto refresh 2023-08-24 22:46:36 +08:00
Masterain
9158aca48d New translations SH.resx (English) 2023-08-24 06:52:10 -07:00
Masterain
591be8473c New translations SH.resx (Chinese Traditional) 2023-08-24 06:52:09 -07:00
Masterain
409ac022ea New translations SH.resx (Korean) 2023-08-24 06:52:07 -07:00
Masterain
ff6f3c50c4 New translations SH.resx (Japanese) 2023-08-24 06:52:06 -07:00
DismissedLight
b3de8b11a1 add dailynote refresh time hint 2023-08-24 21:22:52 +08:00
Lightczx
5a952d8b41 refreshtime for dailynote 2023-08-24 17:30:38 +08:00
Lightczx
39db32a48b fix #656 listviewbase selected item sync 2023-08-24 16:19:17 +08:00
Lightczx
67d17cf23f fix #596 incorrect announcement time 2023-08-24 15:16:57 +08:00
Lightczx
2df292f1b8 style tag zero width negative look ahead 2023-08-24 14:06:47 +08:00
Lightczx
b09a16079c source generation code style 2023-08-24 11:13:07 +08:00
Lightczx
8bd831a16d code style 2023-08-24 10:56:36 +08:00
Masterain
097d5ab9ef New translations SH.resx (English) 2023-08-23 13:28:33 -07:00
Masterain
73071e6e81 New translations SH.resx (English) 2023-08-23 08:10:59 -07:00
Masterain
7da2b21db7 New translations SH.resx (Chinese Traditional) 2023-08-23 08:10:57 -07:00
Masterain
7b78b501a7 New translations SH.resx (Korean) 2023-08-23 08:10:56 -07:00
Masterain
55d5847980 New translations SH.resx (Japanese) 2023-08-23 08:10:55 -07:00
DismissedLight
9259f173b5 refine dailynote UI & fix transformer not obtained desc 2023-08-23 22:54:54 +08:00
DismissedLight
b6080c45c2 fix #823 2023-08-23 20:21:32 +08:00
DismissedLight
704113cacd geetest verify 2023-08-22 23:32:03 +08:00
DismissedLight
f4f3242546 fix game resouce latest package no path 2023-08-21 23:16:23 +08:00
DismissedLight
23feb78f05 fix user collection initialization race condition 2023-08-21 22:51:33 +08:00
Lightczx
e347694a0b Update dependabot.yml 2023-08-21 15:52:20 +08:00
Masterain
3748f721bf New translations SH.resx (Japanese) 2023-08-21 00:36:52 -07:00
Masterain
96f458b42e New translations SH.resx (English) 2023-08-20 09:41:01 -07:00
Masterain
2b313e6c0a New translations SH.resx (Chinese Traditional) 2023-08-20 08:07:44 -07:00
Masterain
ed90664170 New translations SH.resx (English) 2023-08-20 07:06:38 -07:00
Masterain
eb75ce75ce New translations SH.resx (Chinese Traditional) 2023-08-20 07:06:37 -07:00
Masterain
4188dd1f9e New translations SH.resx (Korean) 2023-08-20 07:06:36 -07:00
Masterain
08b82dda07 New translations SH.resx (Japanese) 2023-08-20 07:06:35 -07:00
DismissedLight
f2fea5a0e6 fix game package convert 2023-08-20 21:50:39 +08:00
DismissedLight
368d0bfbd7 fix avatarproperty ordering 2023-08-19 20:56:27 +08:00
DismissedLight
baee72a2fa fix gachastatistics pullprediction infobar spam 2023-08-19 20:33:32 +08:00
DismissedLight
6f1c7b250e fix achievement archive creation 2023-08-19 20:23:30 +08:00
DismissedLight
75938c3ede fix gachalog initialization & improve loading speed 2023-08-19 20:07:02 +08:00
DismissedLight
79cf2839e6 fix gachalog import crash 2023-08-19 18:21:24 +08:00
DismissedLight
cb06949e60 fix gachalog refresh crash 2023-08-19 18:16:42 +08:00
DismissedLight
282eb228d9 improve cachedimage performance 2023-08-19 17:15:46 +08:00
DismissedLight
674002ee8d fix #841 2023-08-19 16:40:22 +08:00
Masterain
faa00d7c5f New translations SH.resx (English) 2023-08-18 02:47:04 -07:00
Masterain
638c9ff732 New translations SH.resx (Chinese Traditional) 2023-08-18 02:47:03 -07:00
Masterain
dabe80900d New translations SH.resx (Korean) 2023-08-18 02:47:01 -07:00
Masterain
a8b7a7f850 New translations SH.resx (Japanese) 2023-08-18 02:47:00 -07:00
Lightczx
7e5a536119 remove incorrect i18n resource 2023-08-18 16:43:41 +08:00
Lightczx
0ea85ae26b Prepare Announcement Regex 2023-08-18 16:41:11 +08:00
Masterain
d3beccc304 New translations SH.resx (English) 2023-08-18 01:41:10 -07:00
Masterain
facfec4831 New translations SH.resx (Chinese Traditional) 2023-08-18 01:41:09 -07:00
Masterain
d6eea91f2c New translations SH.resx (Korean) 2023-08-18 01:41:08 -07:00
Masterain
a5f3821d43 New translations SH.resx (Japanese) 2023-08-18 01:41:07 -07:00
Masterain
6d5485d4ca New translations SH.resx (Japanese) 2023-08-17 22:19:21 -07:00
Lightczx
1e92edca63 Improve AnnouncementContentViewer DarkMode speed 2023-08-18 11:48:06 +08:00
Masterain
055e040238 Update azure-pipelines.yml 2023-08-17 00:42:49 -07:00
Masterain
96f6921120 Update azure-pipelines.yml 2023-08-17 00:25:16 -07:00
Masterain
ddbb2611d7 New translations SH.resx (English) 2023-08-16 23:45:40 -07:00
Masterain
617961d0d2 New translations SH.resx (English) 2023-08-16 21:27:16 -07:00
Masterain
cf1c3eb056 New translations SH.resx (Chinese Traditional) 2023-08-16 21:27:15 -07:00
Masterain
882d9fc655 New translations SH.resx (Korean) 2023-08-16 21:27:14 -07:00
Masterain
6bfad4571a New translations SH.resx (Japanese) 2023-08-16 21:27:13 -07:00
DismissedLight
bc3aea5515 Merge pull request #836 from DGP-Studio/develop
Update readme.md
2023-08-17 00:35:15 +08:00
DismissedLight
c1c7cda390 update readme.md 2023-08-17 00:33:50 +08:00
DismissedLight
0bd8c01fdd refactor done 2023-08-17 00:05:04 +08:00
Lightczx
c4e4ffebd6 refactor viewmodel 2023-08-16 17:19:43 +08:00
Lightczx
d08d2b406f refactor welcome view model 2023-08-15 17:26:38 +08:00
Lightczx
4c50479e96 refactor SettingViewModel 2023-08-14 17:30:08 +08:00
Lightczx
f1cea8e5da Update azure-pipelines.yml 2023-08-14 17:09:03 +08:00
DismissedLight
3225f8105a Merge pull request #831 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/main-dependencies-ddb7c159a1
Bump the main-dependencies group in /src/Snap.Hutao with 3 updates
2023-08-14 16:07:37 +08:00
dependabot[bot]
565817c14a Bump the main-dependencies group in /src/Snap.Hutao with 3 updates
Bumps the main-dependencies group in /src/Snap.Hutao with 3 updates: [Microsoft.EntityFrameworkCore.Sqlite](https://github.com/dotnet/efcore), [Microsoft.EntityFrameworkCore.Tools](https://github.com/dotnet/efcore) and [Microsoft.VisualStudio.Threading.Analyzers](https://github.com/microsoft/vs-threading).


Updates `Microsoft.EntityFrameworkCore.Sqlite` from 7.0.9 to 7.0.10
- [Release notes](https://github.com/dotnet/efcore/releases)
- [Commits](https://github.com/dotnet/efcore/compare/v7.0.9...v7.0.10)

Updates `Microsoft.EntityFrameworkCore.Tools` from 7.0.9 to 7.0.10
- [Release notes](https://github.com/dotnet/efcore/releases)
- [Commits](https://github.com/dotnet/efcore/compare/v7.0.9...v7.0.10)

Updates `Microsoft.VisualStudio.Threading.Analyzers` from 17.6.40 to 17.7.30
- [Release notes](https://github.com/microsoft/vs-threading/releases)
- [Commits](https://github.com/microsoft/vs-threading/commits)

---
updated-dependencies:
- dependency-name: Microsoft.EntityFrameworkCore.Sqlite
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: main-dependencies
- dependency-name: Microsoft.EntityFrameworkCore.Tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: main-dependencies
- dependency-name: Microsoft.VisualStudio.Threading.Analyzers
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: main-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-14 07:35:33 +00:00
DismissedLight
ade6613fe1 refactor dialogs 2023-08-12 16:24:12 +08:00
Lightczx
71fcbc367c refactor string cultureinfo 2023-08-11 16:12:11 +08:00
DismissedLight
a23043fb6d update controls style 2023-08-10 23:38:35 +08:00
Lightczx
d9169df3b8 refactor wiki viewmodel 2023-08-10 17:25:37 +08:00
DismissedLight
420bf6ff41 update community toolkit dependencies 2023-08-09 23:37:38 +08:00
Lightczx
5b269c7b3d refactor viewmodel 2023-08-09 17:06:19 +08:00
DismissedLight
9387b955ee card refine 2023-08-08 23:50:53 +08:00
DismissedLight
f8173208f4 Merge pull request #828 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/main-dependencies-5bd43afe2e
Bump the main-dependencies group in /src/Snap.Hutao with 1 update
2023-08-08 23:03:10 +08:00
Lightczx
3368ae1b25 refresh view 2023-08-08 20:40:48 +08:00
Lightczx
5706e877e7 fix dependency property 2023-08-07 23:45:34 +08:00
Lightczx
3cf0cd1c9a refactor viewmodels 2023-08-07 17:28:39 +08:00
dependabot[bot]
312aa5f71a Bump the main-dependencies group in /src/Snap.Hutao with 1 update
Bumps the main-dependencies group in /src/Snap.Hutao with 1 update: [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest).

- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.6.3...v17.7.0)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: main-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-07 07:44:18 +00:00
Lightczx
4dca174019 update announcement regex 2023-08-07 08:18:38 +08:00
Lightczx
fd52334c13 Really fast 2023-08-03 23:39:35 +08:00
Lightczx
0ee875d28d user service left 2023-08-02 21:58:46 +08:00
DismissedLight
1c46412324 Merge pull request #821 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/main-dependencies-b6424fe871
Bump the main-dependencies group in /src/Snap.Hutao with 1 update
2023-08-02 20:21:36 +08:00
Lightczx
a69ae12e4f fix #825 2023-08-01 22:20:50 +08:00
dependabot[bot]
d738c60d3c Bump the main-dependencies group in /src/Snap.Hutao with 1 update
Bumps the main-dependencies group in /src/Snap.Hutao with 1 update: [Microsoft.WindowsAppSDK](https://github.com/microsoft/windowsappsdk).

- [Release notes](https://github.com/microsoft/windowsappsdk/releases)
- [Commits](https://github.com/microsoft/windowsappsdk/commits)

---
updated-dependencies:
- dependency-name: Microsoft.WindowsAppSDK
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: main-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-31 08:02:16 +00:00
Lightczx
4f6c2905d2 Update GameService.cs 2023-07-30 22:37:40 +08:00
Lightczx
4c337a79b9 package convert fixed 2023-07-30 21:54:10 +08:00
Lightczx
a661530025 fix package converter missing directory 2023-07-30 21:51:16 +08:00
Lightczx
d3b72ecb98 fix CI 2023-07-30 21:33:35 +08:00
Lightczx
69dc8355ad fix #811 2023-07-30 21:32:15 +08:00
Lightczx
2c45274cd3 fix #819 2023-07-30 16:30:56 +08:00
Lightczx
93ee1a3386 fix CI 2023-07-30 12:47:22 +08:00
Lightczx
c5ab707b66 remove manual dependency property 2023-07-30 00:35:41 +08:00
Lightczx
4226598442 refactor gacha service 3 2023-07-28 14:57:51 +08:00
Lightczx
5f38c370c1 delay task 2023-07-27 22:59:31 +08:00
Lightczx
de9abcfad4 refactor gacha service 2 2023-07-27 22:26:19 +08:00
Lightczx
53044b0dda refactor gacha service 2023-07-27 17:23:28 +08:00
Lightczx
e843c84374 refactor dailynote service and partial gacha service 2023-07-26 23:46:11 +08:00
Lightczx
8525aeafac refactor 2023-07-26 08:13:27 +08:00
DismissedLight
d665ba22e5 refactor cultivation service 2023-07-20 16:49:36 +08:00
DismissedLight
d03f8185b8 refactor services 2023-07-20 15:43:51 +08:00
Lightczx
2ad87d25df refactor achievementservice 2023-07-20 08:08:29 +08:00
DismissedLight
01fdcda729 refactor model 2023-07-19 16:44:38 +08:00
Lightczx
f531684e6a refactor naming 2023-07-18 23:20:02 +08:00
DismissedLight
cd066e1462 mapping abstraction 2023-07-18 16:54:42 +08:00
DismissedLight
e9ee31a604 refactor controls 2023-07-17 17:14:15 +08:00
DismissedLight
f2fc5e443c Merge pull request #810 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/MSTest.TestFramework-3.1.1
Bump MSTest.TestFramework from 3.0.4 to 3.1.1 in /src/Snap.Hutao
2023-07-17 15:50:05 +08:00
DismissedLight
4d2b54d49d Merge pull request #809 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/Microsoft.EntityFrameworkCore.Tools-7.0.9
Bump Microsoft.EntityFrameworkCore.Tools from 7.0.8 to 7.0.9 in /src/Snap.Hutao
2023-07-17 15:49:47 +08:00
dependabot[bot]
bea9d5caf2 Bump Microsoft.EntityFrameworkCore.Tools in /src/Snap.Hutao
Bumps [Microsoft.EntityFrameworkCore.Tools](https://github.com/dotnet/efcore) from 7.0.8 to 7.0.9.
- [Release notes](https://github.com/dotnet/efcore/releases)
- [Commits](https://github.com/dotnet/efcore/compare/v7.0.8...v7.0.9)

---
updated-dependencies:
- dependency-name: Microsoft.EntityFrameworkCore.Tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 07:49:04 +00:00
dependabot[bot]
2fbf904987 Bump MSTest.TestFramework from 3.0.4 to 3.1.1 in /src/Snap.Hutao
Bumps [MSTest.TestFramework](https://github.com/microsoft/testfx) from 3.0.4 to 3.1.1.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v3.0.4...v3.1.1)

---
updated-dependencies:
- dependency-name: MSTest.TestFramework
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 07:48:57 +00:00
DismissedLight
6cd7e14ac9 Merge pull request #808 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/Microsoft.EntityFrameworkCore.Sqlite-7.0.9
Bump Microsoft.EntityFrameworkCore.Sqlite from 7.0.8 to 7.0.9 in /src/Snap.Hutao
2023-07-17 15:48:30 +08:00
DismissedLight
7826e019d7 Merge pull request #807 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/MSTest.TestAdapter-3.1.1
Bump MSTest.TestAdapter from 3.0.4 to 3.1.1 in /src/Snap.Hutao
2023-07-17 15:48:03 +08:00
dependabot[bot]
e3e8400978 Bump Microsoft.EntityFrameworkCore.Sqlite in /src/Snap.Hutao
Bumps [Microsoft.EntityFrameworkCore.Sqlite](https://github.com/dotnet/efcore) from 7.0.8 to 7.0.9.
- [Release notes](https://github.com/dotnet/efcore/releases)
- [Commits](https://github.com/dotnet/efcore/compare/v7.0.8...v7.0.9)

---
updated-dependencies:
- dependency-name: Microsoft.EntityFrameworkCore.Sqlite
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 07:39:37 +00:00
dependabot[bot]
500003f9c2 Bump MSTest.TestAdapter from 3.0.4 to 3.1.1 in /src/Snap.Hutao
Bumps [MSTest.TestAdapter](https://github.com/microsoft/testfx) from 3.0.4 to 3.1.1.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v3.0.4...v3.1.1)

---
updated-dependencies:
- dependency-name: MSTest.TestAdapter
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 07:39:33 +00:00
Lightczx
147312c289 Update ObjectExtension.cs 2023-07-16 21:56:07 +08:00
Lightczx
e90f76ead8 delete unused resources 2023-07-16 18:13:35 +08:00
Lightczx
8631933ef4 fix #804 2023-07-16 16:37:29 +08:00
Lightczx
c5dada3f72 guide window 2023-07-15 22:54:50 +08:00
DismissedLight
9aaeb327b6 Merge pull request #799 from DGP-Studio/l10n_main
New Crowdin updates
2023-07-13 19:22:44 +08:00
Masterain
75e771c75e New translations SH.resx (English) 2023-07-13 04:11:51 -07:00
Masterain
d4515936bd New translations SH.resx (Chinese Traditional) 2023-07-13 04:11:49 -07:00
Masterain
d9f2261129 New translations SH.resx (Korean) 2023-07-13 04:11:48 -07:00
Masterain
f0d4ea9a10 New translations SH.resx (Japanese) 2023-07-13 04:11:47 -07:00
Lightczx
bdcbba3237 update text 2023-07-13 19:09:58 +08:00
Masterain
a931a661cd New translations SH.resx (English) 2023-07-13 04:09:52 -07:00
Masterain
0fb2991085 New translations SH.resx (Chinese Traditional) 2023-07-13 04:09:50 -07:00
Masterain
cda04c6aa7 New translations SH.resx (Korean) 2023-07-13 04:09:49 -07:00
Masterain
b23659bf80 New translations SH.resx (Japanese) 2023-07-13 04:09:48 -07:00
DismissedLight
2a3f119fb0 update translation tips 2023-07-13 16:48:09 +08:00
DismissedLight
30e888ffb2 Add Gacha pull prediction 2023-07-13 15:39:10 +08:00
Lightczx
27b79659a4 Add animation to hutao cloud gacha statistics view 2023-07-12 22:18:37 +08:00
DismissedLight
4cef096cb5 refine code 2023-07-12 17:27:17 +08:00
DismissedLight
e065ba0964 Add GI 4.0 materials for Cultivation 2023-07-12 16:41:21 +08:00
DismissedLight
4c0d86fd13 Add InventoryItems localization support 2023-07-12 16:20:17 +08:00
DismissedLight
70ab81bb0f fix #796 2023-07-12 13:21:48 +08:00
DismissedLight
72be37834d Add compatibility with GI 4.0 2023-07-12 10:33:29 +08:00
DismissedLight
f1a2a828c8 Update HutaoStatisticsCard.xaml 2023-07-10 16:36:56 +08:00
DismissedLight
3eaaf0e4e2 Add Hutao GachaEvent Statistics 2023-07-10 15:13:41 +08:00
Lightczx
41d7441eb3 Merge branch 'main' of https://github.com/DGP-Studio/Snap.Hutao 2023-07-10 09:31:53 +08:00
Lightczx
173a1343c1 Hutao GachaEvent Statistics 1 2023-07-10 09:31:16 +08:00
DismissedLight
0fc0413612 remove unnecessary DS handler in UserClient 2023-07-10 09:30:32 +08:00
DismissedLight
1fc1f9b08d Update .gitignore 2023-07-10 09:25:23 +08:00
Lightczx
133a1a532e code style 2023-07-07 22:50:07 +08:00
Lightczx
f4fa08a939 fix developer hint visibility 2023-07-07 22:22:43 +08:00
Lightczx
5020621c46 add gachalogserviceexpiredat in hutaocloudservice 2023-07-07 11:13:47 +08:00
Lightczx
4d526bb363 DependencyProperty generation 2023-07-07 10:11:54 +08:00
DismissedLight
b37d5331a2 Merge pull request #784 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/Microsoft.NET.Test.Sdk-17.6.3
Bump Microsoft.NET.Test.Sdk from 17.6.2 to 17.6.3 in /src/Snap.Hutao
2023-07-03 15:41:55 +08:00
dependabot[bot]
451486e3fc Bump Microsoft.NET.Test.Sdk from 17.6.2 to 17.6.3 in /src/Snap.Hutao
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.6.2 to 17.6.3.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.6.2...v17.6.3)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-03 07:18:55 +00:00
Lightczx
bbacb038cb fix CHS cannot load correct metadata 2023-06-27 20:43:22 +08:00
DismissedLight
7aba05d9c8 Merge pull request #781 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/Microsoft.EntityFrameworkCore.Tools-7.0.8
Bump Microsoft.EntityFrameworkCore.Tools from 7.0.7 to 7.0.8 in /src/Snap.Hutao
2023-06-26 17:12:58 +08:00
dependabot[bot]
fbcd43c0af Bump Microsoft.EntityFrameworkCore.Tools in /src/Snap.Hutao
Bumps [Microsoft.EntityFrameworkCore.Tools](https://github.com/dotnet/efcore) from 7.0.7 to 7.0.8.
- [Release notes](https://github.com/dotnet/efcore/releases)
- [Commits](https://github.com/dotnet/efcore/compare/v7.0.7...v7.0.8)

---
updated-dependencies:
- dependency-name: Microsoft.EntityFrameworkCore.Tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-26 09:12:40 +00:00
DismissedLight
51314da123 Merge pull request #782 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/Microsoft.EntityFrameworkCore.Sqlite-7.0.8
Bump Microsoft.EntityFrameworkCore.Sqlite from 7.0.7 to 7.0.8 in /src/Snap.Hutao
2023-06-26 17:12:20 +08:00
DismissedLight
8ed2e8cc72 Merge pull request #783 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/StyleCop.Analyzers.Unstable-1.2.0.507
Bump StyleCop.Analyzers.Unstable from 1.2.0.435 to 1.2.0.507 in /src/Snap.Hutao
2023-06-26 17:12:03 +08:00
dependabot[bot]
7ab10d7824 Bump StyleCop.Analyzers.Unstable in /src/Snap.Hutao
Bumps [StyleCop.Analyzers.Unstable](https://github.com/DotNetAnalyzers/StyleCopAnalyzers) from 1.2.0.435 to 1.2.0.507.
- [Release notes](https://github.com/DotNetAnalyzers/StyleCopAnalyzers/releases)
- [Changelog](https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/KnownChanges.md)
- [Commits](https://github.com/DotNetAnalyzers/StyleCopAnalyzers/commits)

---
updated-dependencies:
- dependency-name: StyleCop.Analyzers.Unstable
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-26 08:05:16 +00:00
dependabot[bot]
ca5f84911b Bump Microsoft.EntityFrameworkCore.Sqlite in /src/Snap.Hutao
Bumps [Microsoft.EntityFrameworkCore.Sqlite](https://github.com/dotnet/efcore) from 7.0.7 to 7.0.8.
- [Release notes](https://github.com/dotnet/efcore/releases)
- [Commits](https://github.com/dotnet/efcore/compare/v7.0.7...v7.0.8)

---
updated-dependencies:
- dependency-name: Microsoft.EntityFrameworkCore.Sqlite
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-26 08:04:49 +00:00
DismissedLight
500efa60ed Merge pull request #779 from DGP-Studio/l10n_main
New Crowdin updates
2023-06-21 22:01:21 +08:00
Masterain
650e5d8a6e New translations SH.resx (Japanese) 2023-06-21 07:00:22 -07:00
Masterain
79a9128434 New translations SH.resx (English) 2023-06-21 07:00:21 -07:00
Masterain
c7c883bb11 New translations SH.resx (Chinese Traditional) 2023-06-21 07:00:20 -07:00
Masterain
3ee98810f1 New translations SH.resx (Korean) 2023-06-21 07:00:18 -07:00
Lightczx
f09c924a61 reduce NoModuleFound chance 2023-06-18 20:02:09 +08:00
DismissedLight
86d9a381c4 Merge pull request #772 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/Microsoft.NET.Test.Sdk-17.6.2
Bump Microsoft.NET.Test.Sdk from 17.5.0 to 17.6.2 in /src/Snap.Hutao
2023-06-16 16:30:38 +08:00
dependabot[bot]
b2d141af11 Bump Microsoft.NET.Test.Sdk from 17.5.0 to 17.6.2 in /src/Snap.Hutao
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.5.0 to 17.6.2.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.5.0...v17.6.2)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-16 08:30:07 +00:00
DismissedLight
15865eb746 Merge pull request #771 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/MSTest.TestFramework-3.0.4
Bump MSTest.TestFramework from 3.0.2 to 3.0.4 in /src/Snap.Hutao
2023-06-16 16:29:58 +08:00
dependabot[bot]
242b9aa036 Bump MSTest.TestFramework from 3.0.2 to 3.0.4 in /src/Snap.Hutao
Bumps [MSTest.TestFramework](https://github.com/microsoft/testfx) from 3.0.2 to 3.0.4.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v3.0.2...v3.0.4)

---
updated-dependencies:
- dependency-name: MSTest.TestFramework
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-16 08:29:51 +00:00
DismissedLight
7b81cc7a9c Merge pull request #770 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/Microsoft.CodeAnalysis.CSharp-4.6.0
Bump Microsoft.CodeAnalysis.CSharp from 4.5.0 to 4.6.0 in /src/Snap.Hutao
2023-06-16 16:29:41 +08:00
DismissedLight
2060f20707 Merge pull request #769 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/coverlet.collector-6.0.0
Bump coverlet.collector from 3.2.0 to 6.0.0 in /src/Snap.Hutao
2023-06-16 16:29:27 +08:00
DismissedLight
27a6c4f1fb Merge pull request #768 from DGP-Studio/dependabot/nuget/src/Snap.Hutao/MSTest.TestAdapter-3.0.4
Bump MSTest.TestAdapter from 3.0.2 to 3.0.4 in /src/Snap.Hutao
2023-06-16 16:29:14 +08:00
dependabot[bot]
6427fe23ef Bump Microsoft.CodeAnalysis.CSharp in /src/Snap.Hutao
Bumps [Microsoft.CodeAnalysis.CSharp](https://github.com/dotnet/roslyn) from 4.5.0 to 4.6.0.
- [Release notes](https://github.com/dotnet/roslyn/releases)
- [Changelog](https://github.com/dotnet/roslyn/blob/main/docs/Breaking%20API%20Changes.md)
- [Commits](https://github.com/dotnet/roslyn/commits)

---
updated-dependencies:
- dependency-name: Microsoft.CodeAnalysis.CSharp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-16 08:22:18 +00:00
dependabot[bot]
6cf516f883 Bump coverlet.collector from 3.2.0 to 6.0.0 in /src/Snap.Hutao
Bumps [coverlet.collector](https://github.com/coverlet-coverage/coverlet) from 3.2.0 to 6.0.0.
- [Release notes](https://github.com/coverlet-coverage/coverlet/releases)
- [Commits](https://github.com/coverlet-coverage/coverlet/compare/v3.2.0...v6.0.0)

---
updated-dependencies:
- dependency-name: coverlet.collector
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-16 08:22:06 +00:00
dependabot[bot]
080674f648 Bump MSTest.TestAdapter from 3.0.2 to 3.0.4 in /src/Snap.Hutao
Bumps [MSTest.TestAdapter](https://github.com/microsoft/testfx) from 3.0.2 to 3.0.4.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v3.0.2...v3.0.4)

---
updated-dependencies:
- dependency-name: MSTest.TestAdapter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-16 08:22:01 +00:00
Lightczx
09834ada6b fix dependabot 2023-06-16 16:20:43 +08:00
Lightczx
efbc282a14 separate tests 2023-06-11 14:17:41 +08:00
Lightczx
e59a1c5444 1.6.5 rc1 2023-06-10 19:01:15 +08:00
Lightczx
356c157ab2 Support new language: Japanese 2023-06-08 22:14:37 +08:00
DismissedLight
be83ebfae1 Merge pull request #757 from DGP-Studio/l10n_main
New Crowdin updates
2023-06-08 21:56:03 +08:00
Lightczx
f94cbed525 improve #749 2023-06-08 21:53:04 +08:00
Lightczx
1199d2c737 fix #745 2023-06-08 21:44:55 +08:00
Lightczx
126e866a02 use metadata reliquary affix weight 2023-06-08 20:37:01 +08:00
Masterain
fbc0c02af0 New translations SH.resx (Japanese) 2023-06-08 02:28:01 -07:00
Masterain
e646e26908 New translations SH.resx (Japanese) 2023-06-08 01:46:30 -07:00
Masterain
4a8bf8182c New translations SH.resx (Chinese Traditional) 2023-06-08 01:46:28 -07:00
Masterain
86423a86c5 New translations SH.resx (Japanese) 2023-06-07 11:07:24 -07:00
Masterain
0766361441 New translations SH.resx (English) 2023-06-07 11:07:23 -07:00
Masterain
ded93dda75 New translations SH.resx (Chinese Traditional) 2023-06-07 11:07:22 -07:00
Masterain
f437c6615c New translations SH.resx (Korean) 2023-06-07 11:07:21 -07:00
Lightczx
e663c2f353 refine UI 2023-06-07 22:21:55 +08:00
Lightczx
90b396703a Update GameFpsUnlocker.cs 2023-06-07 15:54:08 +08:00
Lightczx
be4fa571cd fix unlock fps 2023-06-07 15:52:51 +08:00
DismissedLight
13e43a1c23 Merge pull request #751 from DGP-Studio/feat/metadata2
Migrating to metadata2
2023-06-05 11:19:58 +08:00
Lightczx
2d9165599b migrate to metadata2 2023-06-05 11:17:47 +08:00
Lightczx
5b35167908 optimize Win32 calls 2023-05-29 20:47:57 +08:00
Masterain
099c5d3f6d Update SECURITY.md 2023-05-29 00:28:50 -07:00
DismissedLight
5f15c05e3d Merge pull request #736 from DGP-Studio/l10n_main
New Crowdin updates
2023-05-27 14:33:54 +08:00
Masterain
de95fd6419 New translations SH.resx (English) 2023-05-26 23:17:16 -07:00
Masterain
bd0901a8ab New translations SH.resx (Chinese Traditional) 2023-05-26 23:17:15 -07:00
Masterain
84fa2dadcc New translations SH.resx (Korean) 2023-05-26 23:17:14 -07:00
DismissedLight
e204b65afc Merge pull request #738 from Masterain98/main
fix #737 Localization Update
2023-05-27 09:47:55 +08:00
Masterain98
9c71c4ffcc Automatic Config File Push 2023-05-27 00:13:32 +00:00
Masterain
62694be22b Localization update 2023-05-26 14:20:00 -07:00
Masterain
20f16ceb3e New translations SH.resx (English) 2023-05-26 06:04:04 -07:00
Masterain
a1e8f90710 New translations SH.resx (Chinese Traditional) 2023-05-26 06:04:03 -07:00
Masterain
0d9baa2cac New translations SH.resx (Korean) 2023-05-26 06:04:02 -07:00
DismissedLight
96b3e6d092 Merge pull request #734 from DGP-Studio/fix-unlock-fps
Fix unlock fps
2023-05-26 16:22:14 +08:00
Lightczx
4178f1b89b fix unlock fps 2023-05-26 16:20:12 +08:00
Lightczx
e44a20f202 Try to fix unlock fps 2023-05-25 20:50:37 +08:00
Masterain98
a12874d114 Automatic Config File Push 2023-05-25 00:12:55 +00:00
Lightczx
c7e3b18d62 fix 3.7 metadata crash 2023-05-23 13:13:41 +08:00
Lightczx
b607bbf819 Update .gitignore 2023-05-21 21:59:49 +08:00
Lightczx
5b16313b65 Delete desktop.ini 2023-05-21 21:58:40 +08:00
Lightczx
577aed0f80 Typo 2023-05-21 21:27:10 +08:00
Masterain98
39a3e6eb37 Automatic Config File Push 2023-05-20 04:14:35 +00:00
Lightczx
96f503d30c Introducing Elevation Badge 2023-05-17 20:58:40 +08:00
Lightczx
f104554661 remove installer 2023-05-16 22:45:09 +08:00
DismissedLight
451823ebf9 Merge pull request #723 from Xhichn/Dev
Improve HoYoLAB API request
2023-05-16 16:21:39 +08:00
Lightczx
7346c5eb4e typo 2023-05-16 16:20:41 +08:00
Lightczx
73f0e356c4 move InitializeCulture 2023-05-16 16:14:49 +08:00
Lightczx
168bed4b2c shell interop 2023-05-16 15:32:44 +08:00
Lightczx
d634eb6818 Unify Encoding to UIT-8 with BOM 2023-05-15 15:43:03 +08:00
DismissedLight
0cb9e59b8a Merge pull request #725 from DGP-Studio/feat/uigf_v2.3
fully support UIGF v2.3
2023-05-15 12:38:57 +08:00
Lightczx
ca10afa25a fully support UIGF v2.3 2023-05-15 12:37:46 +08:00
Lightczx
5fe38f305b refine game service 2023-05-14 22:43:50 +08:00
Lightczx
46f58730dc fix # 722 2023-05-14 22:20:45 +08:00
Xhichn
d144859d94 Merge branch 'main' of https://github.com/DGP-Studio/Snap.Hutao 2023-05-14 21:55:18 +08:00
Xhichn
40439aea8a Removed some unused endpoints 2023-05-14 21:52:46 +08:00
Lightczx
d54828ff55 fix #720 2023-05-14 21:15:22 +08:00
Xhichn
7e0c0fa5bf Change hoyolab api host 2023-05-14 17:49:03 +08:00
Xhichn
8312c7c88b Update hoyolab requests 2023-05-14 12:24:27 +08:00
Lightczx
456d87003f fix #709 2023-05-13 22:37:11 +08:00
Lightczx
7a63013dfd fix #719 2023-05-13 20:47:49 +08:00
Lightczx
15c3019576 fix jumplist launch 2023-05-11 22:13:06 +08:00
Lightczx
c15e948659 增加米奇妙妙工具 2023-05-11 18:17:10 +08:00
Lightczx
c5a4226662 fix scheme displayname 2023-05-10 22:12:02 +08:00
DismissedLight
0f9b906b7b Merge pull request #712 from DGP-Studio/l10n_main
New Crowdin updates
2023-05-10 21:26:39 +08:00
Masterain
f55dc038e2 New translations SH.resx (English) 2023-05-10 06:26:16 -07:00
Masterain
8540c1eebc New translations SH.resx (English) 2023-05-10 06:24:15 -07:00
Masterain
8f71994574 New translations SH.resx (Chinese Traditional) 2023-05-10 06:24:14 -07:00
Masterain
fe3e835211 New translations SH.resx (Korean) 2023-05-10 06:24:13 -07:00
Lightczx
6b5477ba44 1.16.3 release candidate 1 2023-05-10 21:19:26 +08:00
Lightczx
e147b8773f support dynamically change game fps 2023-05-10 19:52:22 +08:00
Masterain98
b386a35f07 Automatic Config File Push 2023-05-10 05:47:11 +00:00
Lightczx
42a19239e6 fix launch scheme detection 2023-05-10 13:44:09 +08:00
Masterain98
404cd9d705 Automatic Config File Push 2023-05-10 00:17:39 +00:00
Lightczx
33935dabc2 code style 2023-05-06 22:43:09 +08:00
Lightczx
5e04c51456 Address several db sync issues 2023-05-06 15:34:40 +08:00
Lightczx
50c5bb44ff auto dependency injection 2023-05-06 13:05:19 +08:00
Lightczx
60641c3f57 fix GetCharacterAsync 2023-05-01 15:47:58 +08:00
Lightczx
40753177cb Merge branch 'main' of https://github.com/DGP-Studio/Snap.Hutao 2023-05-01 10:29:14 +08:00
Lightczx
ba2f2d5708 fix gacha fetch 2023-05-01 10:29:11 +08:00
Masterain
6f5a65c0e3 Update azure-pipelines.yml 2023-04-30 13:05:13 -07:00
Lightczx
20f353d9eb fix avatar property 2023-04-30 21:05:07 +08:00
DismissedLight
1a1a865b8d Merge pull request #700 from DGP-Studio/refactor/application-model
Refactor Application Model
2023-04-30 20:28:10 +08:00
Lightczx
7b3b1f1317 remove all ThreadHelper usage 2023-04-30 20:26:07 +08:00
Lightczx
0891c3f521 fixup all services 2023-04-29 20:54:17 +08:00
Lightczx
9657df36c2 Generate Enum Localization 2023-04-26 21:43:21 +08:00
Lightczx
9a54df4163 remove CoreEnvironment 2023-04-24 19:34:58 +08:00
Lightczx
8ac410fb4d miyoushe 2.49.1 2023-04-23 13:44:09 +08:00
Lightczx
538a076b69 increase boot speed 2023-04-20 19:11:40 +08:00
Lightczx
7db14a9c2a config utf-8 bom 2023-04-20 17:32:33 +08:00
Masterain
0e710f92d2 fix pipelines env 2023-04-20 02:24:08 -07:00
Masterain
fec2dc6c99 Use dedicated build sever 2023-04-20 02:19:24 -07:00
Lightczx
bdb40aca6a daily note card 2023-04-20 15:54:57 +08:00
Masterain
c90f147564 i10n refinement 2023-04-19 20:04:43 -07:00
DismissedLight
2b36b01145 Merge pull request #690 from Xhichn/SignIn
Feat: Daily check-in support for hoyolab user
2023-04-19 21:28:07 +08:00
Lightczx
d876f269fd typo 2023-04-19 21:27:35 +08:00
Lightczx
729f6717e1 Update GachaStatisticsCard.xaml 2023-04-19 19:06:27 +08:00
Xhichn
b5371a9656 Use seperated js interface for hoyolab check-in 2023-04-19 18:23:45 +08:00
Lightczx
04dae7ccd8 achievement card 2023-04-19 18:22:30 +08:00
Xhichn
a5fcfca609 Feat: Daily check-in support for hoyolab user 2023-04-19 13:18:01 +08:00
Lightczx
aee5271a2d more ref-like parameters 2023-04-18 21:57:13 +08:00
Lightczx
89fe93b3eb use ref-like keyword to accelerate runtime speed 2023-04-18 18:54:43 +08:00
Lightczx
dc38def97c fixup cookie creation key collision 2023-04-18 11:24:56 +08:00
Lightczx
2432b1ec5d fix #675 2023-04-17 12:41:06 +08:00
Lightczx
97c7671595 use hoyoverse instead of hoyolab to login 2023-04-17 12:00:48 +08:00
Lightczx
b8895d8250 update to was 1.3 and fix upload spiralabyss 2023-04-14 15:48:00 +08:00
Lightczx
cb882ab062 Update GachaLogViewModelSlim.cs 2023-04-12 22:36:01 +08:00
Lightczx
27c7875c26 refactor gachalog service 2023-04-12 22:34:49 +08:00
Lightczx
3cf505d9b2 Merge branch 'main' of https://github.com/DGP-Studio/Snap.Hutao 2023-04-11 18:42:47 +08:00
Lightczx
4407166005 temp state 2023-04-11 18:42:44 +08:00
DismissedLight
7b6e63a932 Merge pull request #671 from DGP-Studio/l10n_main
New Crowdin updates
2023-04-11 18:41:48 +08:00
Masterain
4a452c205e New translations SH.resx (English) 2023-04-11 01:37:14 -07:00
Masterain
4dd2ba3c6d New translations SH.resx (English) 2023-04-11 01:28:25 -07:00
Masterain
1e7155a902 New translations SH.resx (Chinese Traditional) 2023-04-11 01:28:24 -07:00
Masterain
129a1a7fa8 New translations SH.resx (Korean) 2023-04-11 01:28:24 -07:00
Masterain
f37f4fe37d Update SH.resx 2023-04-11 01:27:38 -07:00
DismissedLight
0df746e4c6 Merge pull request #670 from Masterain98/main
WIKI Pages Localization
2023-04-11 15:39:26 +08:00
Masterain
285f788015 fix typo 2023-04-11 00:37:54 -07:00
Masterain
d0525dd814 WIKI pages localization 2023-04-11 00:25:38 -07:00
Masterain
4372eb0ded =w= 2023-04-10 18:09:04 -07:00
Masterain98
15e6964340 Automatic Config File Push 2023-04-11 00:58:30 +00:00
Lightczx
e5c5c1c95c improve cloud service experience 2023-04-08 22:09:07 +08:00
DismissedLight
a4f555ea40 Merge pull request #663 from DGP-Studio/l10n_main
New Crowdin updates
2023-04-08 16:19:14 +08:00
Masterain
1ea0a14926 New translations SH.resx (English) 2023-04-08 01:17:53 -07:00
Masterain
a26b6fc528 New translations SH.resx (Chinese Traditional) 2023-04-08 01:17:52 -07:00
Masterain
e4523d540c New translations SH.resx (Korean) 2023-04-08 01:17:51 -07:00
Lightczx
6a309321f6 source generate reliquary score weight 2023-04-08 16:17:16 +08:00
Lightczx
512b1257a8 fix DbCurrent 2023-04-07 18:44:08 +08:00
Lightczx
d5b668cb8d fix cloud gacha download crash 2023-04-07 16:41:57 +08:00
Lightczx
32177491da gacha log upload 2023-04-07 15:08:20 +08:00
Lightczx
e5012d9051 improve welcome download experience 2023-04-06 15:12:51 +08:00
Chen Hill
cad1182ade Remove BITS 2023-04-05 13:03:33 +08:00
DismissedLight
e58d982e72 Merge pull request #652 from Xhichn/main
Fix hoyolab userinfo request
2023-04-05 11:12:01 +08:00
xhichn
9749c6c342 Fix hoyolab userinfo request 2023-04-05 10:02:08 +08:00
DismissedLight
179b78ca83 Launch Game QoL 2023-04-04 18:44:19 +08:00
DismissedLight
79118cdb4d fix #649 2023-04-04 16:53:01 +08:00
DismissedLight
35e7aaef4e fix HTTPS usage 2023-04-03 21:17:52 +08:00
DismissedLight
97f5904efa fix cn login 2023-03-31 13:09:03 +08:00
DismissedLight
50112cb3f6 Merge pull request #634 from Masterain98/main
Update PWA assets
2023-03-30 13:12:19 +08:00
DismissedLight
3dd7a8c85f fix HoYoLAB webview login 2023-03-30 13:07:15 +08:00
Masterain
ca06fe7b02 Remove badge logo assets 2023-03-29 17:43:57 -07:00
DismissedLight
04e9f7db72 Merge pull request #637 from DGP-Studio/l10n_main
New Crowdin updates
2023-03-29 18:41:33 +08:00
Masterain
7facf43332 New translations SH.resx (English) 2023-03-29 03:40:37 -07:00
Masterain
bc7cf93137 New translations SH.resx (Chinese Traditional) 2023-03-29 03:40:36 -07:00
Masterain
877dd93bad New translations SH.resx (Korean) 2023-03-29 03:40:35 -07:00
DismissedLight
98b3f2d202 add hutao passport 2023-03-29 17:35:43 +08:00
DismissedLight
0a7bd55dab remove using statements 2023-03-28 21:26:18 +08:00
DismissedLight
e9a8e906da Merge pull request #635 from DGP-Studio/l10n_main
New Crowdin updates
2023-03-28 18:06:03 +08:00
Masterain
93523c8832 New translations SH.resx (English) 2023-03-28 02:50:46 -07:00
Masterain
541455e51d New translations SH.resx (Chinese Traditional) 2023-03-28 02:50:45 -07:00
Masterain
cdc48a1a4f New translations SH.resx (Korean) 2023-03-28 02:50:43 -07:00
DismissedLight
ab65a62c11 fix hoyolab user creation 2023-03-28 16:43:37 +08:00
Masterain
9e90cfb1c8 Update PWA assets 2023-03-27 23:09:01 -07:00
DismissedLight
b1ace71648 fixup dependency injection 2023-03-27 20:08:00 +08:00
DismissedLight
015c731df0 Merge pull request #608 from Xhichn/main
Add basic support for hoyoverse account
2023-03-27 19:03:53 +08:00
DismissedLight
8ea4411e2a phase final 2023-03-27 19:02:09 +08:00
DismissedLight
f90b828bb4 phase 3 2023-03-27 18:39:02 +08:00
DismissedLight
97cbe7cf55 phase 2 2023-03-27 16:17:36 +08:00
DismissedLight
09abb46159 fixup some code style phase 1 2023-03-25 21:26:17 +08:00
Xhichn
523374ed3d Upload spiral abyss record for hoyolab user & clean 2023-03-24 21:44:05 +08:00
Xhichn
9decb67cff Merge branch 'DGP-Studio:main' into main 2023-03-24 20:24:37 +08:00
DismissedLight
af26c06cfc Merge pull request #623 from Masterain98/main
Update Project Information
2023-03-23 22:42:49 +08:00
DismissedLight
b550b91cc9 update banner 2023-03-23 22:24:19 +08:00
DismissedLight
2ba6377088 update bug report template 2023-03-23 12:47:01 +08:00
DismissedLight
18f8137f41 fix cookie token 2023-03-22 18:24:48 +08:00
Xhichn
32ae92e49d Login from hoyolab account page to get stoken 2023-03-22 18:13:55 +08:00
Xhichn
805fd31bf8 Login from hoyolab account website to get stoken 2023-03-22 17:44:56 +08:00
Masterain
b65e0c94f0 Update Project Information 2023-03-22 02:07:06 -07:00
DismissedLight
7e3d7e9076 remove MemoryExtension [skip ci] 2023-03-21 13:16:09 +08:00
DismissedLight
2c162d1fef fix #616 [skip ci] 2023-03-20 12:36:49 +08:00
DismissedLight
bfacf42d71 refactor user initialization 2023-03-19 21:57:49 +08:00
DismissedLight
eeee171b78 fix #611 2023-03-19 20:45:36 +08:00
DismissedLight
9861a3df37 improve fps unlocker 2023-03-19 12:27:17 +08:00
Masterain
84b9a9de23 Update azure-pipelines.yml for Azure Pipelines 2023-03-18 02:52:38 -07:00
DismissedLight
11828bd280 Merge branch 'main' of https://github.com/DGP-Studio/Snap.Hutao 2023-03-18 16:42:46 +08:00
DismissedLight
b29d66d6b6 fix ci build 2023-03-18 16:42:35 +08:00
Masterain
a25d1ba4ce Update azure-pipelines.yml for Azure Pipelines 2023-03-18 01:30:31 -07:00
DismissedLight
5505927ca1 fix nuget sources 2023-03-18 16:00:58 +08:00
DismissedLight
d3a60451a4 Merge branch 'main' of https://github.com/DGP-Studio/Snap.Hutao 2023-03-18 14:45:40 +08:00
DismissedLight
915635a843 fix material 2023-03-18 14:44:24 +08:00
Masterain
918e84be61 Update azure-pipelines.yml for Azure Pipelines 2023-03-17 19:56:09 -07:00
DismissedLight
a8a3354217 Add IsOverSea on User Entity 2023-03-17 23:20:47 +08:00
Xhichn
b6f6c5d54b Merge branch 'DGP-Studio:main' into main 2023-03-16 20:15:43 +08:00
DismissedLight
a9e0e3db39 Update README.md [skip ci] 2023-03-16 18:52:49 +08:00
DismissedLight
ece3f2cd08 migrate to CommunityToolkit.Labs.WinUI.SettingsControls 2023-03-16 18:22:30 +08:00
Xhichn
7612a2e7c0 Merge branch 'DGP-Studio:main' into main 2023-03-15 23:40:21 +08:00
Xhichn
eee34ec4c7 Merge branch 'main' of https://github.com/Xhichn/Snap.Hutao 2023-03-15 23:38:58 +08:00
Xhichn
e24a7436b4 Add daily reward claim support for hoyolab user & fix typo 2023-03-15 23:38:42 +08:00
Xhichn
a543bf3091 Fix dailynote notification for hoyoverse account, block some unsupported operations 2023-03-15 22:39:42 +08:00
DismissedLight
1d88360528 fix app options 2023-03-15 20:13:24 +08:00
DismissedLight
f21d04fadc Merge pull request #604 from GashByte/main
add 'Enabled Advanced' option to setting page
2023-03-15 19:15:02 +08:00
x3zF Love U
0cd6bf95a9 change the field name of 'EnabledAdvanced' 2023-03-15 19:12:41 +08:00
x3zF Love U
4de01d2f62 add 'Enabled Advanced' option to setting page 2023-03-15 19:04:55 +08:00
DismissedLight
05d226ad15 move viewmodels 2023-03-15 18:28:14 +08:00
Xhichn
eabf2a9ea8 Merge branch 'DGP-Studio:main' into main 2023-03-15 14:56:38 +08:00
DismissedLight
e796afbbb0 Merge pull request #603 from HolographicHat/main
Use exclusive access instead of renaming
2023-03-15 14:27:36 +08:00
Xhichn
d6099f10ad Merge branch 'DGP-Studio:main' into main 2023-03-15 13:49:00 +08:00
Xhichn
f837b39ecf Refresh gacha log by stoken is unsupported for oversea players currently 2023-03-15 13:43:03 +08:00
HolographicHat
770cabce81 Update ProcessInterop.cs 2023-03-15 00:23:14 +08:00
HolographicHat
cec4b2a23a Use exclusive access instead of renaming 2023-03-14 15:53:28 +00:00
DismissedLight
c60748626a Create .github/dependabot.yml 2023-03-14 16:33:03 +08:00
Xhichn
51d8ee5d6e Add some auth endpoints 2023-03-14 16:29:51 +08:00
Xhichn
64c020a5fb Support get avatars info from hoyolab dev calc & add some apis 2023-03-14 16:02:10 +08:00
Xhichn
934fb75fa5 Merge branch 'Globalization' 2023-03-13 21:26:18 +08:00
DismissedLight
85b9fc1a08 fix setting restart 2023-03-13 21:01:55 +08:00
Xhichn
314f86966a Remove unnecessary cookie method 2023-03-13 19:36:28 +08:00
DismissedLight
5aba2eab97 support multi-clienting 2023-03-13 19:13:35 +08:00
Xhichn
fdf2311f0c Support website login for adding hoyoverse account 2023-03-13 12:36:38 +08:00
Xhichn
b0b3553d0c Support daily notes for global server player 2023-03-13 12:16:38 +08:00
Xhichn
388cdf1848 Support sync from hoyolab my characters 2023-03-13 11:41:18 +08:00
xhichn
22f4f411ea Support spiral abyss info request for global server players 2023-03-12 23:26:00 +08:00
xhichn
b89c66fd6b Support adding hoyoverse accounts by cookie input 2023-03-12 23:25:25 +08:00
xhichn
0295d4fc22 Add client config and DS salt for oversea server request 2023-03-12 23:21:52 +08:00
xhichn
d85811ee99 Add some hoyolab endpoints 2023-03-12 23:20:09 +08:00
DismissedLight
dc8d7ac913 Merge pull request #598 from GashByte/main
Add 'Mult-Start' & fix a start game bug
2023-03-12 22:32:23 +08:00
x3zF Love U
a832ea96ea repair 'LaunchMultipleInstancesGameAsync' 2023-03-12 22:24:29 +08:00
x3zF Love U
29582efaee reduce the Task waiting time of mult-start 2023-03-12 18:15:40 +08:00
x3zF Love U
6caeb1d238 Add 'Mult-Start' & fix a start game bug 2023-03-12 16:32:59 +08:00
DismissedLight
439a8dd475 Merge pull request #594 from GashByte/main
add 'Copy' Button to 'SettingPage - Device Id'
2023-03-11 16:04:56 +08:00
x3zF Love U
60c7e65abb add 'Copy' Button to 'SettingPage - Device Id' 2023-03-11 15:42:21 +08:00
DismissedLight
5c0984b064 rescale assets [skip ci] 2023-03-11 13:19:45 +08:00
DismissedLight
9b8cce30a7 fix package logo 2023-03-11 13:02:26 +08:00
DismissedLight
463a842cb1 update assets 2023-03-10 21:48:14 +08:00
DismissedLight
8ee2908633 launch screenshot folder 2023-03-10 20:32:49 +08:00
DismissedLight
c71ecd89e3 support game resource download switch 2023-03-07 16:28:00 +08:00
DismissedLight
128b985609 fix game resource convert 2023-03-06 11:29:53 +08:00
DismissedLight
b1a03662d9 launch game impl switch monitor 2023-03-05 18:47:36 +08:00
DismissedLight
706fb3404b refactor localization 2023-03-03 16:49:27 +08:00
DismissedLight
63e1273d6a Merge pull request #582 from DGP-Studio/l10n_main
New Crowdin updates
2023-03-03 15:33:45 +08:00
Masterain
400a3b99ae New translations SH.resx (English) 2023-03-02 23:31:00 -08:00
Masterain
c0c1774db8 New translations SH.resx (Chinese Traditional) 2023-03-02 23:30:59 -08:00
Masterain
099fbf4052 New translations SH.resx (Korean) 2023-03-02 23:30:58 -08:00
Masterain
5c4405e545 Update Crowdin configuration file 2023-03-02 23:30:30 -08:00
DismissedLight
ac68579d6a fix #567 2023-02-28 12:35:49 +08:00
DismissedLight
f3387bb8c8 fix #566 2023-02-28 11:52:43 +08:00
DismissedLight
e50c1b9184 monster wiki fullfilled 2023-02-25 19:12:38 +08:00
DismissedLight
54535cd822 Merge branch 'main' of https://github.com/DGP-Studio/Snap.Hutao 2023-02-24 13:54:47 +08:00
DismissedLight
ece2737633 avatar & weapon level slider 2023-02-24 13:54:43 +08:00
Masterain
4b012424b9 Update bug-report.yml 2023-02-23 11:22:24 -08:00
DismissedLight
6d66af6c84 Merge branch 'main' of https://github.com/DGP-Studio/Snap.Hutao 2023-02-20 19:39:21 +08:00
DismissedLight
027874d4cf adjust avatar reliquary score weight 2023-02-20 19:38:48 +08:00
Masterain
3001936ab3 Update README.md
- optimze image size
- update i18n URL
2023-02-20 01:03:29 -08:00
DismissedLight
400e097fa7 support language switch 2023-02-20 16:04:23 +08:00
DismissedLight
ffce055d75 Merge pull request #539 from DGP-Studio/l10n_main
New Crowdin updates
2023-02-20 14:40:11 +08:00
Masterain
a820c41ad7 New translations SH.resx (Korean) 2023-02-19 22:34:43 -08:00
Masterain
485010c895 New translations SH.resx (English) 2023-02-19 22:34:42 -08:00
Masterain
5feddf566e New translations SH.resx (Chinese Traditional) 2023-02-19 22:34:41 -08:00
Masterain
cdfe306b16 New translations SH.resx (Russian) 2023-02-19 22:34:40 -08:00
Masterain
189c61ddea New translations SH.resx (Japanese) 2023-02-19 22:34:39 -08:00
Masterain
0ac7d6e94d Update PublishDistribution.yml 2023-02-18 02:18:40 -08:00
DismissedLight
042e3b5747 fix #527 2023-02-18 12:51:42 +08:00
DismissedLight
0372f1a8e3 fix #525 2023-02-18 12:50:10 +08:00
DismissedLight
08a630fd43 Merge branch 'main' of https://github.com/DGP-Studio/Snap.Hutao 2023-02-17 13:50:07 +08:00
DismissedLight
3781cad896 make all type internal 2023-02-17 13:49:37 +08:00
Masterain
78bc2f09d5 Create SECURITY.md 2023-02-15 02:17:43 -08:00
DismissedLight
8c52921b3e refactor codebase 2023-02-14 14:30:59 +08:00
DismissedLight
605aecb216 remove strings 2023-02-13 14:51:18 +08:00
DismissedLight
e3e124d52f Merge pull request #500 from DGP-Studio/l10n_main
New Crowdin updates
2023-02-13 14:48:40 +08:00
Masterain
0866e1947b New translations SH.resx (English) 2023-02-12 22:47:53 -08:00
Masterain
dbcb7dd879 New translations SH.resx (Chinese Traditional) 2023-02-12 22:47:52 -08:00
Masterain
44687dd87b New translations SH.resx (English) 2023-02-12 21:59:02 -08:00
Masterain
1a209f6c8d New translations SH.resx (Chinese Traditional) 2023-02-12 21:59:01 -08:00
Masterain
8633b78725 New translations SH.resx (Russian) 2023-02-12 21:59:00 -08:00
Masterain
2e20701c6c New translations SH.resx (Japanese) 2023-02-12 21:58:59 -08:00
DismissedLight
9c4d4cda1e fix localization 2023-02-13 13:51:15 +08:00
DismissedLight
a32481980b code style [skip ci] 2023-02-12 16:18:17 +08:00
DismissedLight
b5577e76a5 add hint for #493 2023-02-12 16:15:30 +08:00
DismissedLight
6c2ff9b3c9 fix process name detection 2023-02-11 18:32:00 +08:00
DismissedLight
818365b816 fix #486 2023-02-11 12:32:00 +08:00
DismissedLight
d7dd8c6f0d code style 2023-02-10 16:07:01 +08:00
DismissedLight
faad104e0e fix import thread issue 2023-02-10 11:57:55 +08:00
DismissedLight
2f6ee75f80 Merge pull request #479 from DGP-Studio/l10n_main
New Crowdin updates
2023-02-09 20:32:35 +08:00
Masterain
34f319bdac New translations SH.resx (English) 2023-02-09 04:32:02 -08:00
Masterain
f242808768 New translations SH.resx (Chinese Traditional) 2023-02-09 04:32:01 -08:00
DismissedLight
98f18f91d8 use WScript.Shell to run scheduled tasks 2023-02-09 19:23:24 +08:00
Masterain
0fd1f6959a New translations SH.resx (English) 2023-02-08 20:27:42 -08:00
Masterain
ba46ed64db New translations SH.resx (Chinese Traditional) 2023-02-08 20:27:41 -08:00
Masterain
0fb8312605 New translations SH.resx (Russian) 2023-02-08 20:27:40 -08:00
Masterain
b722554950 New translations SH.resx (Japanese) 2023-02-08 20:27:39 -08:00
DismissedLight
165c33ef2c fix translation 2023-02-09 12:26:42 +08:00
Masterain
54bb3d634b New translations SH.resx (English) 2023-02-08 20:26:02 -08:00
Masterain
629975480a New translations SH.resx (Chinese Traditional) 2023-02-08 20:26:01 -08:00
Masterain
5a36448c23 New translations SH.resx (Russian) 2023-02-08 20:26:00 -08:00
Masterain
80a6aaab46 New translations SH.resx (Japanese) 2023-02-08 20:25:58 -08:00
Masterain
6c83cd3da5 Update azure-pipelines.yml for Azure Pipelines 2023-02-07 21:54:23 -08:00
DismissedLight
e60a04a2bc impl #117 2023-02-08 12:28:31 +08:00
DismissedLight
aec483510f fix #460 2023-02-08 10:07:10 +08:00
DismissedLight
c245fe654e add gacha import validation 2023-02-07 16:57:53 +08:00
DismissedLight
898d95bb1d add more globalization strings 2023-02-07 15:36:50 +08:00
DismissedLight
1df22e5b75 fix L10n issues 2023-02-07 14:44:36 +08:00
DismissedLight
332e09fef0 fix #439 2023-02-07 13:41:53 +08:00
DismissedLight
2a77daf2ca Merge branch 'main' of https://github.com/DGP-Studio/Snap.Hutao 2023-02-07 10:21:51 +08:00
DismissedLight
8a47ea8727 fix enka api 2023-02-07 10:21:36 +08:00
DismissedLight
b3937ac810 Merge pull request #453 from DGP-Studio/l10n_main
New Crowdin updates
2023-02-06 16:49:06 +08:00
Masterain
ed5c52dc63 New translations SH.resx (English) 2023-02-06 00:44:03 -08:00
Masterain
461d139602 New translations SH.resx (English) 2023-02-05 22:49:55 -08:00
Masterain
164ec2af33 New translations SH.resx (Chinese Traditional) 2023-02-05 22:49:54 -08:00
Masterain
e30523c621 New translations SH.resx (Russian) 2023-02-05 22:49:53 -08:00
Masterain
11d0405102 New translations SH.resx (Japanese) 2023-02-05 22:49:52 -08:00
DismissedLight
a1c0b4f830 fix zh-cn showcase 2 [skip ci] 2023-02-06 14:46:04 +08:00
Masterain
e476ed5960 New translations SH.resx (English) 2023-02-05 22:21:50 -08:00
Masterain
ffc999360d New translations SH.resx (Chinese Traditional) 2023-02-05 22:21:49 -08:00
Masterain
84058011c7 New translations SH.resx (Russian) 2023-02-05 22:21:48 -08:00
Masterain
c18e0c40c5 New translations SH.resx (Japanese) 2023-02-05 22:21:48 -08:00
DismissedLight
ad78515094 fix zh-cn task [skip ci] 2023-02-06 14:20:10 +08:00
Masterain
38367a090d New translations SH.resx (English) 2023-02-05 22:12:29 -08:00
Masterain
ce30f609fb New translations SH.resx (Chinese Traditional) 2023-02-05 22:12:28 -08:00
Masterain
f4b9cc7c48 New translations SH.resx (Russian) 2023-02-05 22:12:27 -08:00
Masterain
7c2212f44c New translations SH.resx (Japanese) 2023-02-05 22:12:26 -08:00
DismissedLight
95eddef457 fix zh-cn showcase 2023-02-06 14:11:43 +08:00
Masterain
02447bc966 New translations SH.resx (English) 2023-02-05 21:49:52 -08:00
Masterain
fb88e33d16 New translations SH.resx (Chinese Traditional) 2023-02-05 21:49:51 -08:00
Masterain
5fa36416ef New translations SH.resx (Russian) 2023-02-05 21:49:50 -08:00
Masterain
7076caaa5d New translations SH.resx (Japanese) 2023-02-05 21:49:49 -08:00
DismissedLight
b7b1155cfc adjust achievement UI 2023-02-06 13:45:41 +08:00
Masterain
6351f2b460 Update SH.resx
- fix typo in based language
2023-02-05 20:09:28 -08:00
DismissedLight
35ac2f33ba Merge branch 'main' of https://github.com/DGP-Studio/Snap.Hutao 2023-02-05 21:06:45 +08:00
DismissedLight
f8ff1988bb update readme 2023-02-05 21:06:41 +08:00
Masterain
907d70ba71 Update azure-pipelines.yml for Azure Pipelines
[skip ci]
2023-02-05 05:04:47 -08:00
Masterain
a5bdc17712 Update azure-pipelines.yml
[skip ci]
2023-02-05 04:50:00 -08:00
Masterain
f078d92f33 Update Crowdin configuration file 2023-02-05 04:46:19 -08:00
DismissedLight
f2d4f0f1d3 locale start 2023-02-05 19:52:00 +08:00
DismissedLight
fcde9b21ae fix #442 2023-02-03 20:05:32 +08:00
DismissedLight
24f09861fd locale zh-cn phase 2 2023-02-02 20:48:48 +08:00
DismissedLight
47708adc83 update readme 2023-02-02 16:35:48 +08:00
DismissedLight
79a254235a Merge branch 'main' of https://github.com/DGP-Studio/Snap.Hutao 2023-02-01 20:28:40 +08:00
DismissedLight
d9bcb3b16b locale zh-cn 2023-02-01 20:28:32 +08:00
Masterain
cf7dd548a2 Update network-issue.yml 2023-01-30 17:34:07 -08:00
DismissedLight
04deeb7086 Create FUNDING.yml 2023-01-30 19:26:25 +08:00
DismissedLight
9fb2da41cd store migration 2023-01-30 16:22:54 +08:00
DismissedLight
bb01f3a3cb fix package convert issue 2023-01-30 10:43:05 +08:00
DismissedLight
f7f2d9c867 fix #406 2023-01-28 20:03:37 +08:00
DismissedLight
01b7e58b3e fix convert cache 2023-01-27 16:51:43 +08:00
DismissedLight
2518ae0b90 package convert impl 2023-01-27 11:22:25 +08:00
DismissedLight
7d4a8cdcd9 fix empty statistics [skip ci] 2023-01-23 13:06:56 +08:00
DismissedLight
623893e00e remove visual transition gap in gacha log initialization 2023-01-23 12:58:00 +08:00
Masterain
0d34c81bcf Merge pull request #388 from wordlesswind/patch-1
Update version information and fix broken links
2023-01-22 01:07:49 -08:00
清靈語
5f3d0126b3 Update version information 2023-01-22 12:51:57 +08:00
1243 changed files with 60044 additions and 22497 deletions

13
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: https://afdian.net/a/DismissedLight

View File

@@ -15,17 +15,15 @@ body:
description: |-
请确保你已完整执行检查清单,否则你的 Issue 可能会被忽略
options:
- label: 完整阅读[胡桃工具箱文档](https://hut.ao/FAQ/most-frequent-questions.html),并认为我的问题没有在文档中得到解答
required: true
- label: 并未完整阅读[胡桃工具箱文档](https://hut.ao/advanced/FAQ.html)
- label: 我不知道文档站的导航栏中有**搜索功能**,也没有搜索过相关关键词
- label: 我使用的操作系统是[受支持的版本](https://hut.ao/quick-start.html#%E6%9C%80%E4%BD%8E%E7%B3%BB%E7%BB%9F%E8%A6%81%E6%B1%82)
required: true
- label: 我使用的操作系统是[受支持的版本](https://hut.ao/quick-start.html#%E6%9C%80%E4%BD%8E%E7%B3%BB%E7%BB%9F%E8%A6%81%E6%B1%82)
- label: 确认没有其他人已经提出相同或类似的问题
required: true
- label: 我会在下方的表单中附上充足的信息以帮助开发人员确定问题
required: true
- label: 我没有**通过搜索功能**确认其他人提出相同或类似的问题
- label: 我不明白上述的勾选项是**一个有助于快速排查问题的检查清单**,而是随手确认的选项
- type: input
id: winver
@@ -33,7 +31,7 @@ body:
label: Windows 版本
description: |
`Win+R` 输入 `winver` 回车后在打开的窗口第二行可以找到
placeholder: 22000.556
placeholder: 22621.1105
validations:
required: true
@@ -42,7 +40,7 @@ body:
attributes:
label: Snap Hutao 版本
description: 在应用标题,应用程序的设置界面中靠下的位置可以找到
placeholder: 1.1.0
placeholder: 1.4.15.0
validations:
required: true
@@ -96,14 +94,4 @@ body:
description: 详细的描述你期望发生的行为,突出与目前(可能不正确的)行为的不同
validations:
required: false
- type: textarea
id: logs
attributes:
label: 相关的崩溃日志
description: |
在资源管理器中直接输入`%userprofile%/Documents/Hutao`即可进入文件夹
如果应用程序崩溃了,请将`log.db` 文件上传,文件包含了敏感信息,谨慎上传
如果这个表单是关于导入祈愿记录的问题,请包含你导入的`Json`文件
> **务必不要上传`user.db`文件,该文件包含你的帐号敏感信息**

View File

@@ -14,7 +14,7 @@ body:
id: back
attributes:
label: 背景与动机
description: 添加此功能的理由
description: 添加此功能的理由,如果你想要实现多个功能,请分别发起多个单独的 Issue
validations:
required: true

View File

@@ -21,7 +21,7 @@ body:
**在填写下面的问题之前请先使用我们的网络诊断工具**
**这个工具将会生成一份报告,请将这份报告拖入下面的框中,让其与你的工单一起被上传提交**
- 你可以点击下面的链接以下载网络诊断工具:
- [胡桃资源站](https://d.hut.ao/d/tools/network-diagnosis-tool.exe)
- [胡桃资源站](https://d.hut.ao/d/tools/network-diagnosis-hutao.exe)
- [GitHub](https://github.com/Masterain98/network-diagnosis-tool/releases/latest/download/network-diagnosis-hutao.exe)
validations:
required: true

16
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "nuget"
directory: "/src/Snap.Hutao" # Snap.Hutao.csproj
target-branch: "develop"
schedule:
interval: "weekly"
groups:
packages:
patterns:
- "*"

View File

@@ -23,7 +23,7 @@ jobs:
with:
repository: "DGP-Studio/Snap.Hutao"
latest: true
fileName: "*.zip"
fileName: "*.msix"
out-file-path: ./release-download
# Upload to Drive

8
.gitignore vendored
View File

@@ -1,8 +1,12 @@
desktop.ini
*.csproj.user
*.pubxml
*.DotSettings.user
.vs/
.idea/
src/Snap.Hutao/_ReSharper.Caches
src/Snap.Hutao/Snap.Hutao/bin/
src/Snap.Hutao/Snap.Hutao/obj/
@@ -15,5 +19,5 @@ src/Snap.Hutao/Snap.Hutao.Installer/Properties/PublishProfiles/FolderProfile.pub
src/Snap.Hutao/Snap.Hutao.SourceGeneration/bin/
src/Snap.Hutao/Snap.Hutao.SourceGeneration/obj/
src/Snap.Hutao/Snap.Hutao.Win32/bin/
src/Snap.Hutao/Snap.Hutao.Win32/obj/
src/Snap.Hutao/Snap.Hutao.Test/bin/
src/Snap.Hutao/Snap.Hutao.Test/obj/

13
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,13 @@
# Code of Conduct
> Snap Hutao is adapting the following rules to keep the community safety.
When participating in our open source community, we want all members to respect and support each other. To ensure the comfort and safety of our community members, we have established the following code of conduct:
1. Respect diversity and inclusivity. We welcome people from different countries, regions, genders, sexual orientations, abilities, religions, and cultural backgrounds to participate in our community, and we encourage respect for all differences.
2. Prohibit discrimination and harassment. We do not tolerate any form of discrimination, harassment, personal attacks, or insults. This includes but is not limited to race, gender, sexual orientation, age, religion, nationality, cultural background, physical and mental health status.
3. Respect privacy and personal information. We protect the privacy and personal information of community members and prohibit the public disclosure of any private information. If you need to disclose certain information, please make sure you have obtained the relevant person's permission.
4. Keep honesty and transparency. We expect community members to maintain honesty and transparency and not intentionally mislead or deceive others.
5. Respect community rules and other members. We encourage community members to follow community rules and guidelines and maintain a polite and respectful attitude towards other members. If you find that other members are violating community rules, please report it to community administrators or organizers in a timely manner.
The above is our community's code of conduct, and we expect all community members to abide by these rules. We will actively address behaviors that violate these rules. We believe that through mutual respect and support, we can build a friendly, inclusive, and beneficial open source community.

20
NuGet.Config Normal file
View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="Microsoft CsWin32" value="https://pkgs.dev.azure.com/azure-public/winsdk/_packaging/CI/nuget/v3/index.json" />
<add key="CommunityToolkit-MainLatest" value="https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-MainLatest/nuget/v3/index.json" />
<add key="CommunityToolkit-Labs" value="https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json" />
</packageSources>
<packageRestore>
<add key="enabled" value="True" />
<add key="automatic" value="True" />
</packageRestore>
<bindingRedirects>
<add key="skip" value="False" />
</bindingRedirects>
<packageManagement>
<add key="format" value="1" />
<add key="disabled" value="False" />
</packageManagement>
</configuration>

View File

@@ -1,33 +1,50 @@
# [Snap.Hutao](https://hut.ao)
![](https://repository-images.githubusercontent.com/482734649/c47a0a8e-868d-4d07-a66f-6d4473abfe46)
![](https://repository-images.githubusercontent.com/482734649/5f8cf574-2ef0-43e9-aa8d-6cf094b54dd9)
胡桃工具箱是一个 Windows 平台的开源的原神工具箱,旨在帮助玩家获得更好的游戏体验; 它是对官方移动端工具的一种非破坏性功能扩展,为不习惯在移动端进行原神游戏的 PC 玩家提供一个在 Windows 平台下获得接近移动端功能权利的途径
> 唷,找本堂主有何贵干呀?
Snap Hutao is an open-source Genshin Impact toolbox on Windows platform, aim to provide a better gaming experience for players. It's an nondestructive feature extension from Genshin Impact's official mobile application, to provide similar feature on desktop, to allow PC gamers gain deserved benefits from mobile platforms.
![Snap.Hutao](https://repobeats.axiom.co/api/embed/f029553fbe0c60689b1710476ec8512452163fc9.svg)
## 下载使用 / Download
# 特别感谢
[<img src="https://get.microsoft.com/images/zh-cn%20light.svg" width="30%" height="30%">](https://apps.microsoft.com/store/detail/snap-hutao/9PH4NXJ2JN52)
## 贡献 / Contribute
* [向我们提交 PR / Make Pull Requests](https://github.com/DGP-Studio/Snap.Hutao/pulls)
* [在 Crowdin 上进行本地化 / Translate project on Crowdin](https://translate.hut.ao/)
* [为我们更新文档 / Enhance our Document ](https://github.com/DGP-Studio/Snap.Hutao.Docs)
## 特别感谢 / Special Thanks
* [HolographicHat](https://github.com/HolographicHat)
* [UIGF organization](https://uigf.org)
### 特定的原神项目
### 特定的原神项目 / Specific Genshin-related Project
* [biuuu/genshin-wish-export](https://github.com/biuuu/genshin-wish-export)
* [xunkong/xunkong](https://github.com/xunkong/xunkong)
* [YuehaiTeam/cocogoat](https://github.com/YuehaiTeam/cocogoat)
### 使用的技术栈
### 使用的技术栈 / Tech Stack
* [CommunityToolkit/dotnet](https://github.com/CommunityToolkit/dotnet)
* [CommunityToolkit/WindowsCommunityToolkit](https://github.com/CommunityToolkit/WindowsCommunityToolkit)
* [CommunityToolkit/Labs-Windows](https://github.com/CommunityToolkit/Labs-Windows)
* [CommunityToolkit/Windows](https://github.com/CommunityToolkit/Windows)
* [dahall/taskscheduler](https://github.com/dahall/taskscheduler)
* [dotnet/efcore](https://github.com/dotnet/efcore)
* [dotnet/runtime](https://github.com/dotnet/runtime)
* [DotNetAnalyzers/StyleCopAnalyzers](https://github.com/DotNetAnalyzers/StyleCopAnalyzers)
* [microsoft/CsWin32](https://github.com/microsoft/CsWin32)
* [microsoft/vs-threading](https://github.com/microsoft/vs-threading)
* [microsoft/vs-validation](https://github.com/microsoft/vs-validation)
* [microsoft/WindowsAppSDK](https://github.com/microsoft/WindowsAppSDK)
* [microsoft/microsoft-ui-xaml](https://github.com/microsoft/microsoft-ui-xaml)
* [WinUICommunity/SettingsUI](https://github.com/WinUICommunity/SettingsUI)
### 支撑项目 / Supporter Project
* [Snap.Hutao.Server](https://github.com/DGP-Studio/Snap.Hutao.Server)
* [Snap.Metadata](https://github.com/DGP-Studio/Snap.Metadata)
## 开发 / Development
![Snap.Hutao](https://repobeats.axiom.co/api/embed/f029553fbe0c60689b1710476ec8512452163fc9.svg)
![Branch Rule](res/branch.png)

12
SECURITY.md Normal file
View File

@@ -0,0 +1,12 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| >=1.6.0 | :white_check_mark: |
| <1.6.0 | :x: |
## Reporting a Vulnerability
Please [open an issue](https://github.com/DGP-Studio/Snap.Hutao/issues/new/choose)

View File

@@ -11,12 +11,14 @@ trigger:
branches:
include:
- main
- develop
paths:
exclude:
- README.md
- azure-pipelines.yml
- .github/ISSUE_TEMPLATE/*.yml
- .github/workflows/*.yml
- src/Snap.Hutao/Snap.Hutao/Resource/Localization/*.resx
pr:
branches:
include:
@@ -27,10 +29,12 @@ pr:
- azure-pipelines.yml
- .github/ISSUE_TEMPLATE/*.yml
- .github/workflows/*.yml
- src/Snap.Hutao/Snap.Hutao/Resource/Localization/*.resx
pool:
vmImage: 'windows-2022'
name: Default
demands: agent.name -equals Hutao-Server
variables:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
@@ -63,7 +67,8 @@ steps:
inputs:
command: 'restore'
restoreSolution: '$(solution)'
feedsToUse: 'select'
feedsToUse: 'config'
nugetConfigPath: '$(Build.SourcesDirectory)/NuGet.Config'
- task: MsixPackaging@1
displayName: Build binary package
@@ -81,7 +86,7 @@ steps:
- task: MagicChunks@2
inputs:
sourcePath: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64\AppxManifest.xml'
sourcePath: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.19041.0\win10-x64\AppxManifest.xml'
fileType: 'Xml'
targetPathType: 'source'
transformationType: 'json'
@@ -102,7 +107,7 @@ steps:
mkdir Assets
mkdir Resource
workingDirectory: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64'
workingDirectory: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.19041.0\win10-x64'
- task: CopyFiles@2
@@ -110,33 +115,36 @@ steps:
inputs:
SourceFolder: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\Assets'
Contents: '**'
TargetFolder: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64\Assets'
TargetFolder: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.19041.0\win10-x64\Assets'
- task: CopyFiles@2
displayName: Copy Resource Folder
inputs:
SourceFolder: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\Resource'
Contents: '**'
TargetFolder: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64\Resource'
TargetFolder: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.19041.0\win10-x64\Resource'
- task: CmdLine@2
displayName: Build MSIX
inputs:
script: '"C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x64\makeappx.exe" pack /d $(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64 /p $(Build.ArtifactStagingDirectory)/Snap.Hutao.Alpha-$(build_date).$(rev_number).msix'
script: '"C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\makeappx.exe" pack /d $(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.19041.0\win10-x64 /p $(Build.ArtifactStagingDirectory)/Snap.Hutao.Alpha-$(build_date).$(rev_number).msix'
- task: MsixSigning@1
name: signMsix
displayName: Sign MSIX package
inputs:
package: '$(Build.ArtifactStagingDirectory)/Snap.Hutao.Alpha-$(build_date).$(rev_number).msix'
certificate: 'DGP_Studio_CI.pfx'
passwordVariable: 'pw'
condition: succeeded()
- task: PublishPipelineArtifact@1
displayName: 'Upload Output'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/'
artifact: 'Output'
publishLocation: 'pipeline'
#- task: PublishPipelineArtifact@1
# displayName: 'Upload Output'
# inputs:
# targetPath: '$(Build.ArtifactStagingDirectory)/'
# artifact: 'Output'
# publishLocation: 'pipeline'
- task: DownloadSecureFile@1
name: cerFile
@@ -145,7 +153,6 @@ steps:
secureFile: 'Snap.Hutao.CI.cer'
- task: GitHubRelease@1
condition: or(eq(variables['Build.Reason'], 'Manual'), eq(variables['Build.Reason'], 'IndividualCI'), eq(variables['Build.Reason'], 'BatchedCI'))
inputs:
gitHubConnection: 'github.com_Masterain'
repositoryName: 'DGP-Studio/Snap.Hutao'
@@ -168,14 +175,17 @@ steps:
changeLogCompareToRelease: 'lastFullRelease'
changeLogType: 'commitBased'
- task: DownloadSecureFile@1
name: RcloneConfigFile
displayName: Download Rclone Config
inputs:
secureFile: 'rclone.conf'
- task: rclone@1
displayName: Upload CI via Rclone
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
inputs:
arguments: 'copy $(Build.ArtifactStagingDirectory)/Snap.Hutao.Alpha-$(build_date).$(rev_number).msix downloadDGPCN:/releases/Alpha/'
configPath: '$(RcloneConfigFile.secureFilePath)'
configPath: 'C:\agent\_work\_tasks\rclone.conf'
- task: rclone@1
displayName: Upload PR CI via Rclone
condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest'))
inputs:
arguments: 'copy $(Build.ArtifactStagingDirectory)/Snap.Hutao.Alpha-$(build_date).$(rev_number).msix downloadDGPCN:/releases/PR/'
configPath: 'C:\agent\_work\_tasks\rclone.conf'

3
crowdin.yml Normal file
View File

@@ -0,0 +1,3 @@
files:
- source: /src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
translation: /src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.%osx_locale%.resx

View File

@@ -1,6 +0,0 @@
[.ShellClassInfo]
IconResource=D:\Develop\Projects\Snap.Hutao\src\Snap.Hutao\Snap.Hutao\Assets\Logo.ico,0
[ViewState]
Mode=
Vid=
FolderType=Generic

Binary file not shown.

BIN
res/HutaoIcon2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 785 KiB

BIN
res/HutaoIcon2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
res/HutaoIconSource.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
res/HutaoRepoBanner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 699 KiB

BIN
res/HutaoRepoBanner.psd Normal file

Binary file not shown.

3
res/README.md Normal file
View File

@@ -0,0 +1,3 @@
本文件夹中的所有图片,均由 [DGP Studio](https://github.com/DGP-Studio) 委托 [Bilibili 画画的芦苇](https://space.bilibili.com/274422134) 绘制
Copyright © 2023 DGP Studio, All Rights Reserved.

BIN
res/branch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

File diff suppressed because one or more lines are too long

View File

@@ -10,7 +10,7 @@ csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = false:silent
csharp_style_expression_bodied_indexers = false:silent
csharp_style_expression_bodied_accessors = when_on_single_line:silent
csharp_style_expression_bodied_accessors = when_on_single_line:suggestion
csharp_style_expression_bodied_lambdas = when_on_single_line:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_conditional_delegate_call = true:suggestion
@@ -57,6 +57,7 @@ dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
dotnet_diagnostic.CA1000.severity = suggestion
[*.cs]
#### 命名样式 ####
@@ -162,11 +163,166 @@ dotnet_diagnostic.CA1309.severity = suggestion
dotnet_diagnostic.CA1805.severity = suggestion
# VSTHRD111: Use ConfigureAwait(bool)
dotnet_diagnostic.VSTHRD111.severity = suggestion
dotnet_diagnostic.VSTHRD111.severity = silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_readonly_struct = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
# SA1600: Elements should be documented
dotnet_diagnostic.SA1600.severity = none
dotnet_diagnostic.SA1601.severity = silent
dotnet_diagnostic.SA1602.severity = silent
# CA1008: 枚举应具有零值
dotnet_diagnostic.CA1008.severity = suggestion
# CA1010: 还应实现泛型接口
dotnet_diagnostic.CA1010.severity = suggestion
# CA1012: 抽象类型不应具有公共构造函数
dotnet_diagnostic.CA1012.severity = suggestion
# CA1024: 在适用处使用属性
dotnet_diagnostic.CA1024.severity = suggestion
# CA1034: 嵌套类型应不可见
dotnet_diagnostic.CA1034.severity = suggestion
# CA1036: 重写可比较类型中的方法
dotnet_diagnostic.CA1036.severity = suggestion
# CA1040: 避免使用空接口
dotnet_diagnostic.CA1040.severity = suggestion
# CA1044: 属性不应是只写的
dotnet_diagnostic.CA1044.severity = suggestion
# CA1043: 将整型或字符串参数用于索引器
dotnet_diagnostic.CA1043.severity = suggestion
# CA1046: 不要对引用类型重载相等运算符
dotnet_diagnostic.CA1046.severity = suggestion
# CA1051: 不要声明可见实例字段
dotnet_diagnostic.CA1051.severity = suggestion
# CA1052: 静态容器类型应为 Static 或 NotInheritable
dotnet_diagnostic.CA1052.severity = suggestion
# CA1058: 类型不应扩展某些基类型
dotnet_diagnostic.CA1058.severity = suggestion
# CA1063: 正确实现 IDisposable
dotnet_diagnostic.CA1063.severity = suggestion
# CA1065: 不要在意外的位置引发异常
dotnet_diagnostic.CA1065.severity = suggestion
# CA1066: 重写 Object.Equals 时实现 IEquatable
dotnet_diagnostic.CA1066.severity = suggestion
# CA1304: 指定 CultureInfo
dotnet_diagnostic.CA1304.severity = suggestion
# CA1305: 指定 IFormatProvider
dotnet_diagnostic.CA1305.severity = suggestion
# CA1307: 为了清晰起见,请指定 StringComparison
dotnet_diagnostic.CA1307.severity = suggestion
# CA1310: 为了确保正确,请指定 StringComparison
dotnet_diagnostic.CA1310.severity = suggestion
# CA1308: 将字符串规范化为大写
dotnet_diagnostic.CA1308.severity = suggestion
# CA1501: 避免过度继承
dotnet_diagnostic.CA1501.severity = suggestion
# CA1502: 避免过度复杂性
dotnet_diagnostic.CA1502.severity = suggestion
# CA1505: 避免使用无法维护的代码
dotnet_diagnostic.CA1505.severity = suggestion
# CA1506: 避免过度的类耦合
dotnet_diagnostic.CA1506.severity = suggestion
# CA1508: 避免死条件代码
dotnet_diagnostic.CA1508.severity = suggestion
# CA1810: 以内联方式初始化引用类型的静态字段
dotnet_diagnostic.CA1810.severity = suggestion
# CA1813: 避免使用非密封特性
dotnet_diagnostic.CA1813.severity = suggestion
# CA1814: 与多维数组相比,首选使用交错数组
dotnet_diagnostic.CA1814.severity = suggestion
# CA1819: 属性不应返回数组
dotnet_diagnostic.CA1819.severity = suggestion
# CA1820: 使用字符串长度测试是否有空字符串
dotnet_diagnostic.CA1820.severity = suggestion
# CA1823: 避免未使用的私有字段
dotnet_diagnostic.CA1823.severity = suggestion
# CA1849: 当在异步方法中时,调用异步方法
dotnet_diagnostic.CA1849.severity = suggestion
# CA1852: 密封内部类型
dotnet_diagnostic.CA1852.severity = suggestion
# CA2000: 丢失范围之前释放对象
dotnet_diagnostic.CA2000.severity = suggestion
# CA2002: 不要锁定具有弱标识的对象
dotnet_diagnostic.CA2002.severity = suggestion
# CA2007: 考虑对等待的任务调用 ConfigureAwait
dotnet_diagnostic.CA2007.severity = suggestion
# CA2008: 不要在未传递 TaskScheduler 的情况下创建任务
dotnet_diagnostic.CA2008.severity = suggestion
# CA2100: 检查 SQL 查询是否存在安全漏洞
dotnet_diagnostic.CA2100.severity = suggestion
# CA2109: 检查可见的事件处理程序
dotnet_diagnostic.CA2109.severity = suggestion
# CA2119: 密封满足私有接口的方法
dotnet_diagnostic.CA2119.severity = suggestion
# CA2153: 不要捕获损坏状态异常
dotnet_diagnostic.CA2153.severity = suggestion
# CA2201: 不要引发保留的异常类型
dotnet_diagnostic.CA2201.severity = suggestion
# CA2207: 以内联方式初始化值类型的静态字段
dotnet_diagnostic.CA2207.severity = suggestion
# CA2213: 应释放可释放的字段
dotnet_diagnostic.CA2213.severity = suggestion
# CA2214: 不要在构造函数中调用可重写的方法
dotnet_diagnostic.CA2214.severity = suggestion
# CA2215: Dispose 方法应调用基类释放
dotnet_diagnostic.CA2215.severity = suggestion
# CA2216: 可释放类型应声明终结器
dotnet_diagnostic.CA2216.severity = suggestion
# CA2227: 集合属性应为只读
dotnet_diagnostic.CA2227.severity = suggestion
# CA2251: 使用 “string.Equals”
dotnet_diagnostic.CA2251.severity = suggestion
[*.vb]
#### 命名样式 ####

View File

@@ -1,73 +0,0 @@
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
namespace Snap.Hutao.Installer;
internal class Program
{
private const string AppxKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Appx";
private const string ValueName = "AllowDevelopmentWithoutDevLicense";
public static async Task Main(string[] args)
{
_ = args;
string ps1File = Path.Combine(AppContext.BaseDirectory, "Install.ps1");
if (!File.Exists(ps1File))
{
Console.WriteLine("未检测到 Install.ps1 文件");
Console.WriteLine("请勿移动该安装程序,按下任意键退出...");
Console.ReadKey();
return;
}
try
{
//以管理策略打开开发者模式
Registry.SetValue(AppxKey, ValueName, 1, RegistryValueKind.DWord);
}
catch (Exception)
{
Console.WriteLine("开发者模式未开启,请手动开启,参阅下方链接");
Console.WriteLine("https://learn.microsoft.com/zh-CN/windows/apps/get-started/developer-mode-features-and-debugging");
}
await InstallAsync(ps1File).ConfigureAwait(false);
Console.WriteLine();
Console.WriteLine("官方文档与使用教程");
Console.WriteLine("https://hut.ao");
Console.WriteLine();
Console.WriteLine("在开始菜单中启动 Snap.Hutao ,按下任意键退出...");
Console.ReadKey();
}
private static async Task InstallAsync(string ps1File)
{
Console.WriteLine("请注意 PowerShell 中的提示");
Process ps = new()
{
StartInfo = new ProcessStartInfo()
{
FileName = "powershell.exe",
Arguments = $"-ExecutionPolicy Unrestricted \"{ps1File}\"",
UseShellExecute = true,
}
};
try
{
ps.Start();
await ps.WaitForExitAsync();
Console.WriteLine("安装脚本运行完成");
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}

View File

@@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishTrimmed>true</PublishTrimmed>
<ApplicationManifest>app.manifest</ApplicationManifest>
<DebugType>embedded</DebugType>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
</Project>

View File

@@ -1,79 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC 清单选项
如果想要更改 Windows 用户帐户控制级别,请使用
以下节点之一替换 requestedExecutionLevel 节点。
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
如果你的应用程序需要此虚拟化来实现向后兼容性,则移除此
元素。
-->
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
Windows 版本的列表。取消评论适当的元素,
Windows 将自动选择最兼容的环境。 -->
<!-- Windows Vista -->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
<!-- Windows 7 -->
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
<!-- Windows 8 -->
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
<!-- Windows 8.1 -->
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<!-- 指示该应用程序可感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI无需
选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应
在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。
将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
<!--
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
-->
<!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->
</assembly>

View File

@@ -0,0 +1,98 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Snap.Hutao.SourceGeneration.Primitive;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
namespace Snap.Hutao.SourceGeneration.Automation;
[Generator(LanguageNames.CSharp)]
internal sealed class CommandGenerator : IIncrementalGenerator
{
public const string AttributeName = "Snap.Hutao.Core.Annotation.CommandAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValueProvider<ImmutableArray<GeneratorSyntaxContext2<IMethodSymbol>>> commands =
context.SyntaxProvider.CreateSyntaxProvider(FilterAttributedMethods, CommandMethod)
.Where(GeneratorSyntaxContext2<IMethodSymbol>.NotNull)
.Collect();
context.RegisterImplementationSourceOutput(commands, GenerateCommandImplementations);
}
private static bool FilterAttributedMethods(SyntaxNode node, CancellationToken token)
{
return node is MethodDeclarationSyntax methodDeclarationSyntax
&& methodDeclarationSyntax.Parent is ClassDeclarationSyntax classDeclarationSyntax
&& classDeclarationSyntax.Modifiers.Count > 1
&& methodDeclarationSyntax.HasAttributeLists();
}
private static GeneratorSyntaxContext2<IMethodSymbol> CommandMethod(GeneratorSyntaxContext context, CancellationToken token)
{
if (context.TryGetDeclaredSymbol(token, out IMethodSymbol? methodSymbol))
{
ImmutableArray<AttributeData> attributes = methodSymbol.GetAttributes();
if (attributes.Any(data => data.AttributeClass!.ToDisplayString() == AttributeName))
{
return new(context, methodSymbol, attributes);
}
}
return default;
}
private static void GenerateCommandImplementations(SourceProductionContext production, ImmutableArray<GeneratorSyntaxContext2<IMethodSymbol>> context2s)
{
foreach (GeneratorSyntaxContext2<IMethodSymbol> context2 in context2s.DistinctBy(c => c.Symbol.ToDisplayString()))
{
GenerateCommandImplementation(production, context2);
}
}
private static void GenerateCommandImplementation(SourceProductionContext production, GeneratorSyntaxContext2<IMethodSymbol> context2)
{
INamedTypeSymbol classSymbol = context2.Symbol.ContainingType;
AttributeData commandInfo = context2.SingleAttribute(AttributeName);
string commandName = (string)commandInfo.ConstructorArguments[0].Value!;
string commandType = context2.Symbol.ReturnType.IsOrInheritsFrom("System.Threading.Tasks.Task")
? "AsyncRelayCommand"
: "RelayCommand";
string genericParameter = context2.Symbol.Parameters.ElementAtOrDefault(0) is IParameterSymbol parameter
? $"<{parameter.Type.ToDisplayString(SymbolDisplayFormats.FullyQualifiedNonNullableFormat)}>"
: string.Empty;
string concurrentExecution = commandInfo.HasNamedArgumentWith<bool>("AllowConcurrentExecutions", value => value)
? ", AsyncRelayCommandOptions.AllowConcurrentExecutions"
: string.Empty;
string className = classSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
string code = $$"""
using CommunityToolkit.Mvvm.Input;
namespace {{classSymbol.ContainingNamespace}};
partial class {{className}}
{
private ICommand _{{commandName}};
public ICommand {{commandName}}
{
get => _{{commandName}} ??= new {{commandType}}{{genericParameter}}({{context2.Symbol.Name}}{{concurrentExecution}});
}
}
""";
string normalizedClassName = classSymbol.ToDisplayString().Replace('<', '{').Replace('>', '}');
production.AddSource($"{normalizedClassName}.{commandName}.g.cs", code);
}
}

View File

@@ -0,0 +1,193 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Snap.Hutao.SourceGeneration.Primitive;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading;
namespace Snap.Hutao.SourceGeneration.Automation;
[Generator(LanguageNames.CSharp)]
internal sealed class ConstructorGenerator : IIncrementalGenerator
{
private const string AttributeName = "Snap.Hutao.Core.Annotation.ConstructorGeneratedAttribute";
private const string CompilerGenerated = "System.Runtime.CompilerServices.CompilerGeneratedAttribute";
//private static readonly DiagnosticDescriptor genericTypeNotSupportedDescriptor = new("SH102", "Generic type is not supported to generate .ctor", "Type [{0}] is not supported", "Quality", DiagnosticSeverity.Error, true);
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValueProvider<ImmutableArray<GeneratorSyntaxContext2>> injectionClasses =
context.SyntaxProvider.CreateSyntaxProvider(FilterAttributedClasses, ConstructorGeneratedClass)
.Where(GeneratorSyntaxContext2.NotNull)
.Collect();
context.RegisterSourceOutput(injectionClasses, GenerateConstructorImplementations);
}
private static bool FilterAttributedClasses(SyntaxNode node, CancellationToken token)
{
return node is ClassDeclarationSyntax classDeclarationSyntax
&& classDeclarationSyntax.Modifiers.Count > 1
&& classDeclarationSyntax.HasAttributeLists();
}
private static GeneratorSyntaxContext2 ConstructorGeneratedClass(GeneratorSyntaxContext context, CancellationToken token)
{
if (context.TryGetDeclaredSymbol(token, out INamedTypeSymbol? classSymbol))
{
ImmutableArray<AttributeData> attributes = classSymbol.GetAttributes();
if (attributes.Any(data => data.AttributeClass!.ToDisplayString() == AttributeName))
{
return new(context, classSymbol, attributes);
}
}
return default;
}
private static void GenerateConstructorImplementations(SourceProductionContext production, ImmutableArray<GeneratorSyntaxContext2> context2s)
{
foreach (GeneratorSyntaxContext2 context2 in context2s.DistinctBy(c => c.Symbol.ToDisplayString()))
{
GenerateConstructorImplementation(production, context2);
}
}
private static void GenerateConstructorImplementation(SourceProductionContext production, GeneratorSyntaxContext2 context2)
{
AttributeData constructorInfo = context2.SingleAttribute(AttributeName);
bool resolveHttpClient = constructorInfo.HasNamedArgumentWith<bool>("ResolveHttpClient", value => value);
bool callBaseConstructor = constructorInfo.HasNamedArgumentWith<bool>("CallBaseConstructor", value => value);
string httpclient = resolveHttpClient ? ", System.Net.Http.HttpClient httpClient" : string.Empty;
FieldValueAssignmentOptions options = new(resolveHttpClient, callBaseConstructor);
StringBuilder sourceBuilder = new StringBuilder().Append($$"""
namespace {{context2.Symbol.ContainingNamespace}};
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(ConstructorGenerator)}}", "1.0.0.0")]
partial class {{context2.Symbol.ToDisplayString(SymbolDisplayFormats.QualifiedNonNullableFormat)}}
{
public {{context2.Symbol.Name}}(System.IServiceProvider serviceProvider{{httpclient}}){{(options.CallBaseConstructor ? " : base(serviceProvider)" : string.Empty)}}
{
""");
FillUpWithFieldValueAssignment(sourceBuilder, context2, options);
sourceBuilder.Append("""
}
}
""");
string normalizedClassName = context2.Symbol.ToDisplayString().Replace('<', '{').Replace('>', '}');
production.AddSource($"{normalizedClassName}.ctor.g.cs", sourceBuilder.ToString());
}
private static void FillUpWithFieldValueAssignment(StringBuilder builder, GeneratorSyntaxContext2 context2, FieldValueAssignmentOptions options)
{
IEnumerable<IFieldSymbol> fields = context2.Symbol.GetMembers()
.Where(m => m.Kind == SymbolKind.Field)
.OfType<IFieldSymbol>();
foreach (IFieldSymbol fieldSymbol in fields)
{
if (fieldSymbol.Name.AsSpan()[0] is '<')
{
continue;
}
bool shoudSkip = false;
foreach (SyntaxReference syntaxReference in fieldSymbol.DeclaringSyntaxReferences)
{
if (syntaxReference.GetSyntax() is VariableDeclaratorSyntax declarator)
{
if (declarator.Initializer is not null)
{
// Skip field with initializer
builder.Append(" // Skip field with initializer: ").AppendLine(fieldSymbol.Name);
shoudSkip = true;
break;
}
}
}
if (shoudSkip)
{
continue;
}
if (fieldSymbol.IsReadOnly && !fieldSymbol.IsStatic)
{
switch (fieldSymbol.Type.ToDisplayString())
{
case "System.IServiceProvider":
builder
.Append(" this.")
.Append(fieldSymbol.Name)
.AppendLine(" = serviceProvider;");
break;
case "System.Net.Http.HttpClient":
if (options.ResolveHttpClient)
{
builder
.Append(" this.")
.Append(fieldSymbol.Name)
.AppendLine(" = httpClient;");
}
else
{
builder
.Append(" this.")
.Append(fieldSymbol.Name)
.Append(" = serviceProvider.GetRequiredService<System.Net.Http.IHttpClientFactory>().CreateClient(nameof(")
.Append(context2.Symbol.Name)
.AppendLine("));");
}
break;
default:
builder
.Append(" this.")
.Append(fieldSymbol.Name)
.Append(" = serviceProvider.GetRequiredService<")
.Append(fieldSymbol.Type)
.AppendLine(">();");
break;
}
}
}
foreach (INamedTypeSymbol interfaceSymbol in context2.Symbol.Interfaces)
{
if (interfaceSymbol.Name == "IRecipient")
{
builder
.Append(" CommunityToolkit.Mvvm.Messaging.IMessengerExtensions.Register<")
.Append(interfaceSymbol.TypeArguments[0])
.AppendLine(">(serviceProvider.GetRequiredService<CommunityToolkit.Mvvm.Messaging.IMessenger>(), this);");
}
}
}
private readonly struct FieldValueAssignmentOptions
{
public readonly bool ResolveHttpClient;
public readonly bool CallBaseConstructor;
public FieldValueAssignmentOptions(bool resolveHttpClient, bool callBaseConstructor)
{
ResolveHttpClient = resolveHttpClient;
CallBaseConstructor = callBaseConstructor;
}
}
}

View File

@@ -0,0 +1,149 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Snap.Hutao.SourceGeneration.Primitive;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
namespace Snap.Hutao.SourceGeneration.Automation;
[Generator(LanguageNames.CSharp)]
internal sealed class DependencyPropertyGenerator : IIncrementalGenerator
{
private const string AttributeName = "Snap.Hutao.Core.Annotation.DependencyPropertyAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValueProvider<ImmutableArray<GeneratorSyntaxContext2>> commands =
context.SyntaxProvider.CreateSyntaxProvider(FilterAttributedClasses, CommandMethod)
.Where(GeneratorSyntaxContext2.NotNull)
.Collect();
context.RegisterImplementationSourceOutput(commands, GenerateDependencyPropertyImplementations);
}
private static bool FilterAttributedClasses(SyntaxNode node, CancellationToken token)
{
return node is ClassDeclarationSyntax classDeclarationSyntax
&& classDeclarationSyntax.Modifiers.Count > 1
&& classDeclarationSyntax.HasAttributeLists();
}
private static GeneratorSyntaxContext2 CommandMethod(GeneratorSyntaxContext context, CancellationToken token)
{
if (context.TryGetDeclaredSymbol(token, out INamedTypeSymbol? methodSymbol))
{
ImmutableArray<AttributeData> attributes = methodSymbol.GetAttributes();
if (attributes.Any(data => data.AttributeClass!.ToDisplayString() == AttributeName))
{
return new(context, methodSymbol, attributes);
}
}
return default;
}
private static void GenerateDependencyPropertyImplementations(SourceProductionContext production, ImmutableArray<GeneratorSyntaxContext2> context2s)
{
foreach (GeneratorSyntaxContext2 context2 in context2s.DistinctBy(c => c.Symbol.ToDisplayString()))
{
GenerateDependencyPropertyImplementation(production, context2);
}
}
private static void GenerateDependencyPropertyImplementation(SourceProductionContext production, GeneratorSyntaxContext2 context2)
{
foreach (AttributeData propertyInfo in context2.Attributes.Where(attr => attr.AttributeClass!.ToDisplayString() == AttributeName))
{
string owner = context2.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
Dictionary<string, TypedConstant> namedArguments = propertyInfo.NamedArguments.ToDictionary();
bool isAttached = namedArguments.TryGetValue("IsAttached", out TypedConstant constant) && (bool)constant.Value!;
string register = isAttached ? "RegisterAttached" : "Register";
ImmutableArray<TypedConstant> arguments = propertyInfo.ConstructorArguments;
string propertyName = (string)arguments[0].Value!;
string propertyType = arguments[1].Value!.ToString();
string defaultValue = GetLiteralString(arguments.ElementAtOrDefault(2)) ?? "default";
string propertyChangedCallback = arguments.ElementAtOrDefault(3) is { IsNull: false } arg3 ? $", {arg3.Value}" : string.Empty;
string code;
if (isAttached)
{
string objType = namedArguments.TryGetValue("AttachedType", out TypedConstant attachedType)
? attachedType.Value!.ToString()
: "object";
code = $$"""
using Microsoft.UI.Xaml;
namespace {{context2.Symbol.ContainingNamespace}};
partial class {{owner}}
{
private static readonly DependencyProperty {{propertyName}}Property =
DependencyProperty.RegisterAttached("{{propertyName}}", typeof({{propertyType}}), typeof({{owner}}), new PropertyMetadata(({{propertyType}}){{defaultValue}}{{propertyChangedCallback}}));
public static {{propertyType}} Get{{propertyName}}({{objType}} obj)
{
return ({{propertyType}})obj?.GetValue({{propertyName}}Property);
}
public static void Set{{propertyName}}({{objType}} obj, {{propertyType}} value)
{
obj.SetValue({{propertyName}}Property, value);
}
}
""";
}
else
{
code = $$"""
using Microsoft.UI.Xaml;
namespace {{context2.Symbol.ContainingNamespace}};
partial class {{owner}}
{
private static readonly DependencyProperty {{propertyName}}Property =
DependencyProperty.Register(nameof({{propertyName}}), typeof({{propertyType}}), typeof({{owner}}), new PropertyMetadata(({{propertyType}}){{defaultValue}}{{propertyChangedCallback}}));
public {{propertyType}} {{propertyName}}
{
get => ({{propertyType}})GetValue({{propertyName}}Property);
set => SetValue({{propertyName}}Property, value);
}
}
""";
}
string normalizedClassName = context2.Symbol.ToDisplayString().Replace('<', '{').Replace('>', '}');
production.AddSource($"{normalizedClassName}.{propertyName}.g.cs", code);
}
}
private static string? GetLiteralString(TypedConstant typedConstant)
{
if (typedConstant.IsNull)
{
return default;
}
if (typedConstant.Value is bool boolValue)
{
return boolValue ? "true" : "false";
}
string result = typedConstant.Value!.ToString();
if (string.IsNullOrEmpty(result))
{
return default;
}
return result;
}
}

View File

@@ -0,0 +1,15 @@
namespace System.Diagnostics.CodeAnalysis;
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class NotNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}

View File

@@ -1,178 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
namespace Snap.Hutao.SourceGeneration.DedendencyInjection;
/// <summary>
/// 注入HttpClient代码生成器
/// 旨在使用源生成器提高注入效率
/// 防止在运行时动态查找注入类型
/// </summary>
[Generator]
public class HttpClientGenerator : ISourceGenerator
{
private const string DefaultName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientConfigration.Default";
private const string XRpcName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientConfigration.XRpc";
private const string XRpc2Name = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientConfigration.XRpc2";
private const string PrimaryHttpMessageHandlerAttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.PrimaryHttpMessageHandlerAttribute";
private const string DynamicSecretAttributeName = "Snap.Hutao.Web.Hoyolab.DynamicSecret.UseDynamicSecretAttribute";
/// <inheritdoc/>
public void Initialize(GeneratorInitializationContext context)
{
// Register a syntax receiver that will be created for each generation pass
context.RegisterForSyntaxNotifications(() => new HttpClientSyntaxContextReceiver());
}
/// <inheritdoc/>
public void Execute(GeneratorExecutionContext context)
{
// retrieve the populated receiver
if (context.SyntaxContextReceiver is not HttpClientSyntaxContextReceiver receiver)
{
return;
}
string toolName = this.GetGeneratorType().FullName;
StringBuilder sourceCodeBuilder = new();
sourceCodeBuilder.Append($@"// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
// This class is generated by Snap.Hutao.SourceGeneration
using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Web.Hoyolab.DynamicSecret;
using System.Net.Http;
namespace Snap.Hutao.Core.DependencyInjection;
internal static partial class IocHttpClientConfiguration
{{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute(""{toolName}"",""1.0.0.0"")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static partial IServiceCollection AddHttpClients(this IServiceCollection services)
{{");
FillWithInjectionServices(receiver, sourceCodeBuilder);
sourceCodeBuilder.Append(@"
return services;
}
}");
context.AddSource("IocHttpClientConfiguration.g.cs", SourceText.From(sourceCodeBuilder.ToString(), Encoding.UTF8));
}
private static void FillWithInjectionServices(HttpClientSyntaxContextReceiver receiver, StringBuilder sourceCodeBuilder)
{
List<string> lines = new();
StringBuilder lineBuilder = new();
foreach (INamedTypeSymbol classSymbol in receiver.Classes)
{
lineBuilder.Clear().Append("\r\n");
lineBuilder.Append(@" services.AddHttpClient<");
lineBuilder.Append($"{classSymbol.ToDisplayString()}>(");
AttributeData httpClientInfo = classSymbol
.GetAttributes()
.Single(attr => attr.AttributeClass!.ToDisplayString() == HttpClientSyntaxContextReceiver.AttributeName);
ImmutableArray<TypedConstant> arguments = httpClientInfo.ConstructorArguments;
TypedConstant injectAs = arguments[0];
string injectAsName = injectAs.ToCSharpString();
switch (injectAsName)
{
case DefaultName:
lineBuilder.Append(@"DefaultConfiguration)");
break;
case XRpcName:
lineBuilder.Append(@"XRpcConfiguration)");
break;
case XRpc2Name:
lineBuilder.Append(@"XRpc2Configuration)");
break;
default:
throw new InvalidOperationException($"非法的HttpClientConfigration值: [{injectAsName}]");
}
AttributeData? handlerInfo = classSymbol
.GetAttributes()
.SingleOrDefault(attr => attr.AttributeClass!.ToDisplayString() == PrimaryHttpMessageHandlerAttributeName);
if (handlerInfo != null)
{
ImmutableArray<KeyValuePair<string, TypedConstant>> properties = handlerInfo.NamedArguments;
lineBuilder.Append(@".ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() {");
foreach (KeyValuePair<string, TypedConstant> property in properties)
{
lineBuilder.Append(" ");
lineBuilder.Append(property.Key);
lineBuilder.Append(" = ");
lineBuilder.Append(property.Value.ToCSharpString());
lineBuilder.Append(",");
}
lineBuilder.Append(" })");
}
if (classSymbol.GetAttributes().Any(attr => attr.AttributeClass!.ToDisplayString() == DynamicSecretAttributeName))
{
lineBuilder.Append(".AddHttpMessageHandler<DynamicSecretHandler>()");
}
lineBuilder.Append(";");
lines.Add(lineBuilder.ToString());
}
foreach (string line in lines.OrderBy(x => x))
{
sourceCodeBuilder.Append(line);
}
}
private class HttpClientSyntaxContextReceiver : ISyntaxContextReceiver
{
/// <summary>
/// 注入特性的名称
/// </summary>
public const string AttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientAttribute";
/// <summary>
/// 所有需要注入的类型符号
/// </summary>
public List<INamedTypeSymbol> Classes { get; } = new();
/// <inheritdoc/>
public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
{
// any class with at least one attribute is a candidate for injection generation
if (context.Node is ClassDeclarationSyntax classDeclarationSyntax && classDeclarationSyntax.AttributeLists.Count > 0)
{
// get as named type symbol
if (context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax) is INamedTypeSymbol classSymbol)
{
if (classSymbol.GetAttributes().Any(ad => ad.AttributeClass!.ToDisplayString() == AttributeName))
{
Classes.Add(classSymbol);
}
}
}
}
}
}

View File

@@ -1,156 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
namespace Snap.Hutao.SourceGeneration.DedendencyInjection;
/// <summary>
/// 注入代码生成器
/// 旨在使用源生成器提高注入效率
/// 防止在运行时动态查找注入类型
/// </summary>
[Generator]
public class InjectionGenerator : ISourceGenerator
{
private const string InjectAsSingletonName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Singleton";
private const string InjectAsTransientName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Transient";
private const string InjectAsScopedName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Scoped";
/// <inheritdoc/>
public void Initialize(GeneratorInitializationContext context)
{
// Register a syntax receiver that will be created for each generation pass
context.RegisterForSyntaxNotifications(() => new InjectionSyntaxContextReceiver());
}
/// <inheritdoc/>
public void Execute(GeneratorExecutionContext context)
{
// retrieve the populated receiver
if (context.SyntaxContextReceiver is not InjectionSyntaxContextReceiver receiver)
{
return;
}
string toolName = this.GetGeneratorType().FullName;
StringBuilder sourceCodeBuilder = new();
sourceCodeBuilder.Append($@"// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
// This class is generated by Snap.Hutao.SourceGeneration
using Microsoft.Extensions.DependencyInjection;
namespace Snap.Hutao.Core.DependencyInjection;
internal static partial class ServiceCollectionExtension
{{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute(""{toolName}"",""1.0.0.0"")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static partial IServiceCollection AddInjections(this IServiceCollection services)
{{");
FillWithInjectionServices(receiver, sourceCodeBuilder);
sourceCodeBuilder.Append(@"
return services;
}
}");
context.AddSource("ServiceCollectionExtension.g.cs", SourceText.From(sourceCodeBuilder.ToString(), Encoding.UTF8));
}
private static void FillWithInjectionServices(InjectionSyntaxContextReceiver receiver, StringBuilder sourceCodeBuilder)
{
List<string> lines = new();
StringBuilder lineBuilder = new();
foreach (INamedTypeSymbol classSymbol in receiver.Classes)
{
IEnumerable<AttributeData> datas = classSymbol
.GetAttributes()
.Where(attr => attr.AttributeClass!.ToDisplayString() == InjectionSyntaxContextReceiver.AttributeName);
foreach (AttributeData injectionInfo in datas)
{
lineBuilder
.Clear()
.Append("\r\n");
ImmutableArray<TypedConstant> arguments = injectionInfo.ConstructorArguments;
TypedConstant injectAs = arguments[0];
string injectAsName = injectAs.ToCSharpString();
switch (injectAsName)
{
case InjectAsSingletonName:
lineBuilder.Append(@" services.AddSingleton(");
break;
case InjectAsTransientName:
lineBuilder.Append(@" services.AddTransient(");
break;
case InjectAsScopedName:
lineBuilder.Append(@" services.AddScoped(");
break;
default:
throw new InvalidOperationException($"非法的 InjectAs 值: [{injectAsName}]");
}
if (arguments.Length == 2)
{
TypedConstant interfaceType = arguments[1];
lineBuilder.Append($"{interfaceType.ToCSharpString()}, ");
}
lineBuilder.Append($"typeof({classSymbol.ToDisplayString()}));");
lines.Add(lineBuilder.ToString());
}
}
foreach (string line in lines.OrderBy(x => x))
{
sourceCodeBuilder.Append(line);
}
}
private class InjectionSyntaxContextReceiver : ISyntaxContextReceiver
{
/// <summary>
/// 注入特性的名称
/// </summary>
public const string AttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectionAttribute";
/// <summary>
/// 所有需要注入的类型符号
/// </summary>
public List<INamedTypeSymbol> Classes { get; } = new();
/// <inheritdoc/>
public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
{
// any class with at least one attribute is a candidate for injection generation
if (context.Node is ClassDeclarationSyntax classDeclarationSyntax && classDeclarationSyntax.AttributeLists.Count > 0)
{
// get as named type symbol
if (context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax) is INamedTypeSymbol classSymbol)
{
if (classSymbol.GetAttributes().Any(ad => ad.AttributeClass!.ToDisplayString() == AttributeName))
{
Classes.Add(classSymbol);
}
}
}
}
}
}

View File

@@ -0,0 +1,152 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Snap.Hutao.SourceGeneration.Primitive;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading;
namespace Snap.Hutao.SourceGeneration.DependencyInjection;
[Generator(LanguageNames.CSharp)]
internal sealed class HttpClientGenerator : IIncrementalGenerator
{
private const string AttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientAttribute";
private const string HttpClientConfiguration = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientConfiguration.";
private const string PrimaryHttpMessageHandlerAttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.PrimaryHttpMessageHandlerAttribute";
private const string UseDynamicSecretAttributeName = "Snap.Hutao.Web.Hoyolab.DynamicSecret.UseDynamicSecretAttribute";
private const string CRLF = "\r\n";
private static readonly DiagnosticDescriptor injectionShouldOmitDescriptor = new("SH201", "Injection 特性可以省略", "HttpClient 特性已将 {0} 注册为 Transient 服务", "Quality", DiagnosticSeverity.Warning, true);
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValueProvider<ImmutableArray<GeneratorSyntaxContext2>> injectionClasses = context.SyntaxProvider
.CreateSyntaxProvider(FilterAttributedClasses, HttpClientClass)
.Where(GeneratorSyntaxContext2.NotNull)
.Collect();
context.RegisterImplementationSourceOutput(injectionClasses, GenerateAddHttpClientsImplementation);
}
private static bool FilterAttributedClasses(SyntaxNode node, CancellationToken token)
{
return node is ClassDeclarationSyntax classDeclarationSyntax
&& classDeclarationSyntax.HasAttributeLists();
}
private static GeneratorSyntaxContext2 HttpClientClass(GeneratorSyntaxContext context, CancellationToken token)
{
if (context.TryGetDeclaredSymbol(token, out INamedTypeSymbol? classSymbol))
{
ImmutableArray<AttributeData> attributes = classSymbol.GetAttributes();
if (attributes.Any(data => data.AttributeClass!.ToDisplayString() == AttributeName))
{
return new(context, classSymbol, attributes);
}
}
return default;
}
private static void GenerateAddHttpClientsImplementation(SourceProductionContext context, ImmutableArray<GeneratorSyntaxContext2> context2s)
{
StringBuilder sourceBuilder = new StringBuilder().Append($$"""
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Web.Hoyolab.DynamicSecret;
using System.Net.Http;
namespace Snap.Hutao.Core.DependencyInjection;
internal static partial class IocHttpClientConfiguration
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(HttpClientGenerator)}}", "1.0.0.0")]
public static partial IServiceCollection AddHttpClients(this IServiceCollection services)
{
""");
FillUpWithAddHttpClient(sourceBuilder, context, context2s);
sourceBuilder.Append("""
return services;
}
}
""");
context.AddSource("IocHttpClientConfiguration.g.cs", sourceBuilder.ToString());
}
private static void FillUpWithAddHttpClient(StringBuilder sourceBuilder, SourceProductionContext production, ImmutableArray<GeneratorSyntaxContext2> contexts)
{
List<string> lines = new();
StringBuilder lineBuilder = new();
foreach (GeneratorSyntaxContext2 context in contexts.DistinctBy(c => c.Symbol.ToDisplayString()))
{
if (context.SingleOrDefaultAttribute(InjectionGenerator.AttributeName) is AttributeData injectionData)
{
if (injectionData.ConstructorArguments[0].ToCSharpString() == InjectionGenerator.InjectAsTransientName)
{
if (injectionData.ConstructorArguments.Length < 2)
{
production.ReportDiagnostic(Diagnostic.Create(injectionShouldOmitDescriptor, context.Context.Node.GetLocation(), context.Context.Node));
}
}
}
lineBuilder.Clear().Append(CRLF);
lineBuilder.Append(@" services.AddHttpClient<");
AttributeData httpClientData = context.SingleAttribute(AttributeName);
ImmutableArray<TypedConstant> arguments = httpClientData.ConstructorArguments;
if (arguments.Length == 2)
{
lineBuilder.Append($"{arguments[1].Value}, ");
}
lineBuilder.Append($"{context.Symbol.ToDisplayString()}>(");
lineBuilder.Append(arguments[0].ToCSharpString().Substring(HttpClientConfiguration.Length)).Append("Configuration)");
if (context.SingleOrDefaultAttribute(PrimaryHttpMessageHandlerAttributeName) is AttributeData handlerData)
{
ImmutableArray<KeyValuePair<string, TypedConstant>> properties = handlerData.NamedArguments;
lineBuilder.Append(@".ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() {");
foreach (KeyValuePair<string, TypedConstant> property in properties)
{
lineBuilder.Append(' ');
lineBuilder.Append(property.Key);
lineBuilder.Append(" = ");
lineBuilder.Append(property.Value.ToCSharpString());
lineBuilder.Append(',');
}
lineBuilder.Append(" })");
}
if (context.HasAttributeWithName(UseDynamicSecretAttributeName))
{
lineBuilder.Append(".AddHttpMessageHandler<DynamicSecretHandler>()");
}
lineBuilder.Append(';');
lines.Add(lineBuilder.ToString());
}
foreach (string line in lines.OrderBy(x => x))
{
sourceBuilder.Append(line);
}
}
}

View File

@@ -0,0 +1,126 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Snap.Hutao.SourceGeneration.Primitive;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading;
namespace Snap.Hutao.SourceGeneration.DependencyInjection;
[Generator(LanguageNames.CSharp)]
internal sealed class InjectionGenerator : IIncrementalGenerator
{
public const string AttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectionAttribute";
public const string InjectAsSingletonName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Singleton";
public const string InjectAsTransientName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Transient";
public const string InjectAsScopedName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Scoped";
private static readonly DiagnosticDescriptor invalidInjectionDescriptor = new("SH101", "无效的 InjectAs 枚举值", "尚未支持生成 {0} 配置", "Quality", DiagnosticSeverity.Error, true);
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValueProvider<ImmutableArray<GeneratorSyntaxContext2>> injectionClasses = context.SyntaxProvider
.CreateSyntaxProvider(FilterAttributedClasses, HttpClientClass)
.Where(GeneratorSyntaxContext2.NotNull)
.Collect();
context.RegisterImplementationSourceOutput(injectionClasses, GenerateAddInjectionsImplementation);
}
private static bool FilterAttributedClasses(SyntaxNode node, CancellationToken token)
{
return node is ClassDeclarationSyntax classDeclarationSyntax
&& classDeclarationSyntax.HasAttributeLists();
}
private static GeneratorSyntaxContext2 HttpClientClass(GeneratorSyntaxContext context, CancellationToken token)
{
if (context.TryGetDeclaredSymbol(token, out INamedTypeSymbol? classSymbol))
{
ImmutableArray<AttributeData> attributes = classSymbol.GetAttributes();
if (attributes.Any(data => data.AttributeClass!.ToDisplayString() == AttributeName))
{
return new(context, classSymbol, attributes);
}
}
return default;
}
private static void GenerateAddInjectionsImplementation(SourceProductionContext context, ImmutableArray<GeneratorSyntaxContext2> context2s)
{
StringBuilder sourceBuilder = new StringBuilder().Append($$"""
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Core.DependencyInjection;
internal static partial class ServiceCollectionExtension
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(InjectionGenerator)}}", "1.0.0.0")]
public static partial IServiceCollection AddInjections(this IServiceCollection services)
{
""");
FillUpWithAddServices(sourceBuilder, context, context2s);
sourceBuilder.Append("""
return services;
}
}
""");
context.AddSource("ServiceCollectionExtension.g.cs", sourceBuilder.ToString());
}
private static void FillUpWithAddServices(StringBuilder sourceBuilder, SourceProductionContext production, ImmutableArray<GeneratorSyntaxContext2> contexts)
{
List<string> lines = new();
StringBuilder lineBuilder = new();
foreach (GeneratorSyntaxContext2 context in contexts.DistinctBy(c => c.Symbol.ToDisplayString()))
{
lineBuilder.Clear().AppendLine();
AttributeData injectionInfo = context.SingleAttribute(AttributeName);
ImmutableArray<TypedConstant> arguments = injectionInfo.ConstructorArguments;
string injectAsName = arguments[0].ToCSharpString();
switch (injectAsName)
{
case InjectAsSingletonName:
lineBuilder.Append(" services.AddSingleton<");
break;
case InjectAsTransientName:
lineBuilder.Append(" services.AddTransient<");
break;
case InjectAsScopedName:
lineBuilder.Append(" services.AddScoped<");
break;
default:
production.ReportDiagnostic(Diagnostic.Create(invalidInjectionDescriptor, context.Context.Node.GetLocation(), injectAsName));
break;
}
if (arguments.Length == 2)
{
lineBuilder.Append($"{arguments[1].Value}, ");
}
lineBuilder.Append($"{context.Symbol.ToDisplayString()}>();");
lines.Add(lineBuilder.ToString());
}
foreach (string line in lines.OrderBy(x => x))
{
sourceBuilder.Append(line);
}
}
}

View File

@@ -0,0 +1,78 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Snap.Hutao.SourceGeneration.Primitive;
using System.Collections.Immutable;
using System.Linq;
namespace Snap.Hutao.SourceGeneration.DependencyInjection;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class ServiceAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor NonSingletonUseServiceProviderDescriptor = new("SH301", "Non Singleton service should avoid direct use of IServiceProvider", "Non Singleton service should avoid direct use of IServiceProvider", "Quality", DiagnosticSeverity.Info, true);
private static readonly DiagnosticDescriptor SingletonServiceCaptureNonSingletonServiceDescriptor = new("SH302", "Singleton service should avoid keep reference of non singleton service", "Singleton service should avoid keep reference of non singleton service", "Quality", DiagnosticSeverity.Info, true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get => new DiagnosticDescriptor[]
{
NonSingletonUseServiceProviderDescriptor,
SingletonServiceCaptureNonSingletonServiceDescriptor,
}.ToImmutableArray();
}
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(CompilationStart);
}
private static void CompilationStart(CompilationStartAnalysisContext context)
{
context.RegisterSyntaxNodeAction(HandleNonSingletonUseServiceProvider, SyntaxKind.ClassDeclaration);
context.RegisterSyntaxNodeAction(HandleSingletonServiceCaptureNonSingletonService, SyntaxKind.ClassDeclaration);
}
private static void HandleNonSingletonUseServiceProvider(SyntaxNodeAnalysisContext context)
{
ClassDeclarationSyntax classDeclarationSyntax = (ClassDeclarationSyntax)context.Node;
if (classDeclarationSyntax.HasAttributeLists())
{
INamedTypeSymbol? classSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax);
if (classSymbol is not null)
{
foreach (AttributeData attributeData in classSymbol.GetAttributes())
{
if (attributeData.AttributeClass!.ToDisplayString() is InjectionGenerator.AttributeName)
{
string serviceType = attributeData.ConstructorArguments[0].ToCSharpString();
if (serviceType is InjectionGenerator.InjectAsTransientName or InjectionGenerator.InjectAsScopedName)
{
HandleNonSingletonUseServiceProviderActual(context, classSymbol);
}
}
}
}
}
}
private static void HandleNonSingletonUseServiceProviderActual(SyntaxNodeAnalysisContext context, INamedTypeSymbol classSymbol)
{
ISymbol? symbol = classSymbol.GetMembers().Where(m => m is IFieldSymbol f && f.Type.ToDisplayString() == "System.IServiceProvider").SingleOrDefault();
if (symbol is not null)
{
Diagnostic diagnostic = Diagnostic.Create(NonSingletonUseServiceProviderDescriptor, symbol.Locations.FirstOrDefault());
context.ReportDiagnostic(diagnostic);
}
}
private static void HandleSingletonServiceCaptureNonSingletonService(SyntaxNodeAnalysisContext context)
{
//classSymbol.GetMembers().Where(m => m is IFieldSymbol { IsReadOnly: true, DeclaredAccessibility: Accessibility.Private } f);
}
}

View File

@@ -0,0 +1,134 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Snap.Hutao.SourceGeneration.Primitive;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading;
namespace Snap.Hutao.SourceGeneration.Enum;
[Generator(LanguageNames.CSharp)]
internal class LocalizedEnumGenerator : IIncrementalGenerator
{
private const string AttributeName = "Snap.Hutao.Resource.Localization.LocalizationAttribute";
private const string LocalizationKeyName = "Snap.Hutao.Resource.Localization.LocalizationKeyAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValuesProvider<GeneratorSyntaxContext2> localizationEnums = context.SyntaxProvider
.CreateSyntaxProvider(FilterAttributedEnums, LocalizationEnum)
.Where(GeneratorSyntaxContext2.NotNull);
context.RegisterSourceOutput(localizationEnums, GenerateGetLocalizedDescriptionImplementation);
}
private static bool FilterAttributedEnums(SyntaxNode node, CancellationToken token)
{
return node is EnumDeclarationSyntax enumDeclarationSyntax
&& enumDeclarationSyntax.HasAttributeLists();
}
private static GeneratorSyntaxContext2 LocalizationEnum(GeneratorSyntaxContext context, CancellationToken token)
{
if (context.SemanticModel.GetDeclaredSymbol(context.Node, token) is INamedTypeSymbol enumSymbol)
{
ImmutableArray<AttributeData> attributes = enumSymbol.GetAttributes();
if (attributes.Any(data => data.AttributeClass!.ToDisplayString() == AttributeName))
{
return new(context, enumSymbol, attributes);
}
}
return default;
}
private static void GenerateGetLocalizedDescriptionImplementation(SourceProductionContext context, GeneratorSyntaxContext2 context2)
{
StringBuilder sourceBuilder = new StringBuilder().Append($$"""
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Resource.Localization;
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(LocalizedEnumGenerator)}}", "1.0.0.0")]
internal static class {{context2.Symbol.Name}}Extension
{
/// <summary>
/// 获取本地化的描述
/// </summary>
/// <param name="value">枚举值</param>
/// <returns>本地化的描述</returns>
public static string GetLocalizedDescription(this {{context2.Symbol}} value)
{
string key = value switch
{
""");
FillUpWithSwitchBranches(sourceBuilder, context2);
sourceBuilder.Append($$"""
_ => string.Empty,
};
if (string.IsNullOrEmpty(key))
{
return Enum.GetName(value);
}
else
{
return SH.ResourceManager.GetString(key);
}
}
/// <summary>
/// 获取本地化的描述
/// </summary>
/// <param name="value">枚举值</param>
/// <returns>本地化的描述</returns>
[return:MaybeNull]
public static string GetLocalizedDescriptionOrDefault(this {{context2.Symbol}} value)
{
string key = value switch
{
""");
FillUpWithSwitchBranches(sourceBuilder, context2);
sourceBuilder.Append($$"""
_ => string.Empty,
};
return SH.ResourceManager.GetString(key);
}
}
""");
context.AddSource($"{context2.Symbol.Name}Extension.g.cs", sourceBuilder.ToString());
}
private static void FillUpWithSwitchBranches(StringBuilder sourceBuilder, GeneratorSyntaxContext2 context)
{
IEnumerable<IFieldSymbol> fields = context.Symbol.GetMembers()
.Where(m => m.Kind == SymbolKind.Field)
.Cast<IFieldSymbol>();
foreach (IFieldSymbol fieldSymbol in fields)
{
AttributeData? localizationKeyInfo = fieldSymbol.GetAttributes()
.SingleOrDefault(data => data.AttributeClass!.ToDisplayString() == LocalizationKeyName);
if (localizationKeyInfo != null)
{
sourceBuilder
.Append(" ")
.Append(fieldSymbol)
.Append(" => \"")
.Append(localizationKeyInfo.ConstructorArguments[0].Value)
.AppendLine("\",");
}
}
}
}

View File

@@ -0,0 +1,8 @@
// This file is used by Code Analysis to maintain SuppressMessage
// 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.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("", "RS2008")]

View File

@@ -0,0 +1,196 @@
using Microsoft.CodeAnalysis;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
namespace Snap.Hutao.SourceGeneration.Identity;
[Generator(LanguageNames.CSharp)]
internal sealed class IdentityGenerator : IIncrementalGenerator
{
private const string FileName = "IdentityStructs.json";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValueProvider<ImmutableArray<AdditionalText>> provider = context.AdditionalTextsProvider.Where(MatchFileName).Collect();
context.RegisterImplementationSourceOutput(provider, GenerateIdentityStructs);
}
private static bool MatchFileName(AdditionalText text)
{
return Path.GetFileName(text.Path) == FileName;
}
private static void GenerateIdentityStructs(SourceProductionContext context, ImmutableArray<AdditionalText> texts)
{
AdditionalText jsonFile = texts.Single();
string identityJson = jsonFile.GetText(context.CancellationToken)!.ToString();
List<IdentityStructMetadata> identities = identityJson.FromJson<List<IdentityStructMetadata>>()!;
if (identities.Any())
{
foreach (IdentityStructMetadata identityStruct in identities)
{
GenerateIdentityStruct(context, identityStruct);
}
}
}
private static void GenerateIdentityStruct(SourceProductionContext context, IdentityStructMetadata metadata)
{
string name = metadata.Name;
StringBuilder sourceBuilder = new StringBuilder().AppendLine($$"""
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive.Converter;
using System.Numerics;
namespace Snap.Hutao.Model.Primitive;
/// <summary>
/// {{metadata.Documentation}}
/// </summary>
[JsonConverter(typeof(IdentityConverter<{{name}}>))]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(IdentityGenerator)}}","1.0.0.0")]
internal readonly partial struct {{name}}
{
/// <summary>
/// 值
/// </summary>
public readonly uint Value;
/// <summary>
/// Initializes a new instance of the <see cref="{{name}}"/> struct.
/// </summary>
/// <param name="value">value</param>
public {{name}}(uint value)
{
Value = value;
}
public static implicit operator uint({{name}} value)
{
return value.Value;
}
public static implicit operator {{name}}(uint value)
{
return new(value);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
""");
if (metadata.Equatable)
{
sourceBuilder.AppendLine($$"""
internal readonly partial struct {{name}} : IEquatable<{{name}}>
{
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is {{name}} other && Equals(other);
}
/// <inheritdoc/>
public bool Equals({{name}} other)
{
return Value == other.Value;
}
}
""");
}
if (metadata.EqualityOperators)
{
sourceBuilder.AppendLine($$"""
internal readonly partial struct {{name}} : IEqualityOperators<{{name}}, {{name}}, bool>, IEqualityOperators<{{name}}, uint, bool>
{
public static bool operator ==({{name}} left, {{name}} right)
{
return left.Value == right.Value;
}
public static bool operator ==({{name}} left, uint right)
{
return left.Value == right;
}
public static bool operator !=({{name}} left, {{name}} right)
{
return !(left == right);
}
public static bool operator !=({{name}} left, uint right)
{
return !(left == right);
}
}
""");
}
if (metadata.AdditionOperators)
{
sourceBuilder.AppendLine($$"""
internal readonly partial struct {{name}} : IAdditionOperators<{{name}}, {{name}}, {{name}}>, IAdditionOperators<{{name}}, uint, {{name}}>
{
public static {{name}} operator +({{name}} left, {{name}} right)
{
return left.Value + right.Value;
}
public static {{name}} operator +({{name}} left, uint right)
{
return left.Value + right;
}
}
""");
}
if (metadata.IncrementOperators)
{
sourceBuilder.AppendLine($$"""
internal readonly partial struct {{name}} : IIncrementOperators<{{name}}>
{
public static unsafe {{name}} operator ++({{name}} value)
{
++*(uint*)&value;
return value;
}
}
""");
}
context.AddSource($"{name}.g.cs", sourceBuilder.ToString());
}
private sealed class IdentityStructMetadata
{
public string Name { get; set; } = default!;
public string? Documentation { get; set; }
public bool Equatable { get; set; }
public bool EqualityOperators { get; set; }
public bool AdditionOperators { get; set; }
public bool IncrementOperators { get; set; }
}
}

View File

@@ -0,0 +1,439 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
namespace Snap.Hutao.SourceGeneration;
// Really simple JSON parser in ~300 lines
// - Attempts to parse JSON files with minimal GC allocation
// - Nice and simple "[1,2,3]".FromJson<List<int>>() API
// - Classes and structs can be parsed too!
// class Foo { public int Value; }
// "{\"Value\":10}".FromJson<Foo>()
// - Can parse JSON without type information into Dictionary<string,object> and List<object> e.g.
// "[1,2,3]".FromJson<object>().GetType() == typeof(List<object>)
// "{\"Value\":10}".FromJson<object>().GetType() == typeof(Dictionary<string,object>)
// - No JIT Emit support to support AOT compilation on iOS
// - Attempts are made to NOT throw an exception if the JSON is corrupted or invalid: returns null instead.
// - Only public fields and property setters on classes/structs will be written to
//
// Limitations:
// - No JIT Emit support to parse structures quickly
// - Limited to parsing <2GB JSON files (due to int.MaxValue)
// - Parsing of abstract classes or interfaces is NOT supported and will throw an exception.
public static class JsonParser
{
[ThreadStatic]
private static Stack<List<string>>? splitArrayPool;
[ThreadStatic]
private static StringBuilder? stringBuilder;
[ThreadStatic]
private static Dictionary<Type, Dictionary<string, FieldInfo>>? fieldInfoCache;
[ThreadStatic]
private static Dictionary<Type, Dictionary<string, PropertyInfo>>? propertyInfoCache;
public static T? FromJson<T>(this string json)
{
// Initialize, if needed, the ThreadStatic variables
propertyInfoCache ??= new Dictionary<Type, Dictionary<string, PropertyInfo>>();
fieldInfoCache ??= new Dictionary<Type, Dictionary<string, FieldInfo>>();
stringBuilder ??= new StringBuilder();
splitArrayPool ??= new Stack<List<string>>();
// Remove all whitespace not within strings to make parsing simpler
stringBuilder.Length = 0;
for (int i = 0; i < json.Length; i++)
{
char c = json[i];
if (c == '"')
{
i = AppendUntilStringEnd(true, i, json);
continue;
}
if (char.IsWhiteSpace(c))
{
continue;
}
stringBuilder.Append(c);
}
// Parse the thing!
return (T?)ParseValue(typeof(T), stringBuilder.ToString());
}
private static int AppendUntilStringEnd(bool appendEscapeCharacter, int startIdx, string json)
{
stringBuilder!.Append(json[startIdx]);
for (int i = startIdx + 1; i < json.Length; i++)
{
if (json[i] == '\\')
{
if (appendEscapeCharacter)
{
stringBuilder.Append(json[i]);
}
stringBuilder.Append(json[i + 1]);
i++;//Skip next character as it is escaped
}
else if (json[i] == '"')
{
stringBuilder.Append(json[i]);
return i;
}
else
{
stringBuilder.Append(json[i]);
}
}
return json.Length - 1;
}
// Splits { <value>:<value>, <value>:<value> } and [ <value>, <value> ] into a list of <value> strings
private static List<string> Split(string json)
{
List<string> splitArray = splitArrayPool!.Count > 0 ? splitArrayPool.Pop() : new List<string>();
splitArray.Clear();
if (json.Length == 2)
{
return splitArray;
}
int parseDepth = 0;
stringBuilder!.Length = 0;
for (int i = 1; i < json.Length - 1; i++)
{
switch (json[i])
{
case '[':
case '{':
parseDepth++;
break;
case ']':
case '}':
parseDepth--;
break;
case '"':
i = AppendUntilStringEnd(true, i, json);
continue;
case ',':
case ':':
if (parseDepth == 0)
{
splitArray.Add(stringBuilder.ToString());
stringBuilder.Length = 0;
continue;
}
break;
}
stringBuilder.Append(json[i]);
}
splitArray.Add(stringBuilder.ToString());
return splitArray;
}
internal static object? ParseValue(Type type, string json)
{
if (type == typeof(string))
{
if (json.Length <= 2)
{
return string.Empty;
}
StringBuilder parseStringBuilder = new(json.Length);
for (int i = 1; i < json.Length - 1; ++i)
{
if (json[i] == '\\' && i + 1 < json.Length - 1)
{
int j = "\"\\nrtbf/".IndexOf(json[i + 1]);
if (j >= 0)
{
parseStringBuilder.Append("\"\\\n\r\t\b\f/"[j]);
++i;
continue;
}
if (json[i + 1] == 'u' && i + 5 < json.Length - 1)
{
if (uint.TryParse(json.Substring(i + 2, 4), System.Globalization.NumberStyles.AllowHexSpecifier, null, out uint c))
{
parseStringBuilder.Append((char)c);
i += 5;
continue;
}
}
}
parseStringBuilder.Append(json[i]);
}
return parseStringBuilder.ToString();
}
if (type.IsPrimitive)
{
object result = Convert.ChangeType(json, type, System.Globalization.CultureInfo.InvariantCulture);
return result;
}
if (type == typeof(decimal))
{
decimal.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out decimal result);
return result;
}
if (type == typeof(DateTime))
{
DateTime.TryParse(json.Replace("\"", ""), System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out DateTime result);
return result;
}
if (json == "null")
{
return null;
}
if (type.IsEnum)
{
if (json[0] == '"')
{
json = json.Substring(1, json.Length - 2);
}
try
{
return System.Enum.Parse(type, json, false);
}
catch
{
return 0;
}
}
if (type.IsArray)
{
Type arrayType = type.GetElementType();
if (json[0] != '[' || json[json.Length - 1] != ']')
{
return null;
}
List<string> elems = Split(json);
Array newArray = Array.CreateInstance(arrayType, elems.Count);
for (int i = 0; i < elems.Count; i++)
{
newArray.SetValue(ParseValue(arrayType, elems[i]), i);
}
splitArrayPool!.Push(elems);
return newArray;
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
{
Type listType = type.GetGenericArguments()[0];
if (json[0] != '[' || json[json.Length - 1] != ']')
{
return null;
}
List<string> elems = Split(json);
IList list = (IList)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count });
for (int i = 0; i < elems.Count; i++)
{
list.Add(ParseValue(listType, elems[i]));
}
splitArrayPool!.Push(elems);
return list;
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
{
Type keyType, valueType;
{
Type[] args = type.GetGenericArguments();
keyType = args[0];
valueType = args[1];
}
//Refuse to parse dictionary keys that aren't of type string
if (keyType != typeof(string))
{
return null;
}
//Must be a valid dictionary element
if (json[0] != '{' || json[json.Length - 1] != '}')
{
return null;
}
//The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON
List<string> elems = Split(json);
if (elems.Count % 2 != 0)
{
return null;
}
IDictionary dictionary = (IDictionary)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count / 2 });
for (int i = 0; i < elems.Count; i += 2)
{
if (elems[i].Length <= 2)
{
continue;
}
string keyValue = elems[i].Substring(1, elems[i].Length - 2);
object? val = ParseValue(valueType, elems[i + 1]);
dictionary[keyValue] = val;
}
return dictionary;
}
if (type == typeof(object))
{
return ParseAnonymousValue(json);
}
if (json[0] == '{' && json[json.Length - 1] == '}')
{
return ParseObject(type, json);
}
return null;
}
private static object? ParseAnonymousValue(string json)
{
if (json.Length == 0)
{
return null;
}
if (json[0] == '{' && json[json.Length - 1] == '}')
{
List<string> elems = Split(json);
if (elems.Count % 2 != 0)
{
return null;
}
Dictionary<string, object?> dict = new(elems.Count / 2);
for (int i = 0; i < elems.Count; i += 2)
{
dict[elems[i].Substring(1, elems[i].Length - 2)] = ParseAnonymousValue(elems[i + 1]);
}
return dict;
}
if (json[0] == '[' && json[json.Length - 1] == ']')
{
List<string> items = Split(json);
List<object?> finalList = new(items.Count);
for (int i = 0; i < items.Count; i++)
{
finalList.Add(ParseAnonymousValue(items[i]));
}
return finalList;
}
if (json[0] == '"' && json[json.Length - 1] == '"')
{
string str = json.Substring(1, json.Length - 2);
return str.Replace("\\", string.Empty);
}
if (char.IsDigit(json[0]) || json[0] == '-')
{
if (json.Contains("."))
{
double.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out double result);
return result;
}
else
{
int.TryParse(json, out int result);
return result;
}
}
if (json == "true")
{
return true;
}
if (json == "false")
{
return false;
}
// handles json == "null" as well as invalid JSON
return null;
}
private static Dictionary<string, T> CreateMemberNameDictionary<T>(T[] members) where T : MemberInfo
{
Dictionary<string, T> nameToMember = new(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < members.Length; i++)
{
T member = members[i];
if (member.IsDefined(typeof(IgnoreDataMemberAttribute), true))
{
continue;
}
string name = member.Name;
if (member.IsDefined(typeof(DataMemberAttribute), true))
{
DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true);
if (!string.IsNullOrEmpty(dataMemberAttribute.Name))
{
name = dataMemberAttribute.Name;
}
}
nameToMember.Add(name, member);
}
return nameToMember;
}
private static object ParseObject(Type type, string json)
{
object instance = FormatterServices.GetUninitializedObject(type);
// The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON
List<string> elems = Split(json);
if (elems.Count % 2 != 0)
{
return instance;
}
if (!fieldInfoCache!.TryGetValue(type, out Dictionary<string, FieldInfo> nameToField))
{
nameToField = CreateMemberNameDictionary(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
fieldInfoCache.Add(type, nameToField);
}
if (!propertyInfoCache!.TryGetValue(type, out Dictionary<string, PropertyInfo> nameToProperty))
{
nameToProperty = CreateMemberNameDictionary(type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
propertyInfoCache.Add(type, nameToProperty);
}
for (int i = 0; i < elems.Count; i += 2)
{
if (elems[i].Length <= 2)
{
continue;
}
string key = elems[i].Substring(1, elems[i].Length - 2);
string value = elems[i + 1];
if (nameToField.TryGetValue(key, out FieldInfo fieldInfo))
{
fieldInfo.SetValue(instance, ParseValue(fieldInfo.FieldType, value));
}
else if (nameToProperty.TryGetValue(key, out PropertyInfo propertyInfo))
{
propertyInfo.SetValue(instance, ParseValue(propertyInfo.PropertyType, value), null);
}
}
return instance;
}
}

View File

@@ -0,0 +1,16 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using System;
using System.Linq;
namespace Snap.Hutao.SourceGeneration.Primitive;
internal static class AttributeDataExtension
{
public static bool HasNamedArgumentWith<TValue>(this AttributeData data, string key, Func<TValue, bool> predicate)
{
return data.NamedArguments.Any(a => a.Key == key && predicate((TValue)a.Value.Value!));
}
}

View File

@@ -0,0 +1,41 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Snap.Hutao.SourceGeneration.Primitive;
internal static class EnumerableExtension
{
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
return DistinctByIterator(source, keySelector);
}
private static IEnumerable<TSource> DistinctByIterator<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
using IEnumerator<TSource> enumerator = source.GetEnumerator();
if (enumerator.MoveNext())
{
HashSet<TKey> set = new();
do
{
TSource element = enumerator.Current;
if (set.Add(keySelector(element)))
{
yield return element;
}
}
while (enumerator.MoveNext());
}
}
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source)
{
return source.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
}

View File

@@ -0,0 +1,77 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using System.Collections.Immutable;
using System.Linq;
namespace Snap.Hutao.SourceGeneration.Primitive;
internal readonly struct GeneratorSyntaxContext2
{
public readonly GeneratorSyntaxContext Context;
public readonly INamedTypeSymbol Symbol;
public readonly ImmutableArray<AttributeData> Attributes;
public readonly bool HasValue = false;
public GeneratorSyntaxContext2(GeneratorSyntaxContext context, INamedTypeSymbol symbol, ImmutableArray<AttributeData> attributes)
{
Context = context;
Symbol = symbol;
Attributes = attributes;
HasValue = true;
}
public static bool NotNull(GeneratorSyntaxContext2 context)
{
return context.HasValue;
}
public bool HasAttributeWithName(string name)
{
return Attributes.Any(attr => attr.AttributeClass!.ToDisplayString() == name);
}
public AttributeData SingleAttribute(string name)
{
return Attributes.Single(attribute => attribute.AttributeClass!.ToDisplayString() == name);
}
public AttributeData? SingleOrDefaultAttribute(string name)
{
return Attributes.SingleOrDefault(attribute => attribute.AttributeClass!.ToDisplayString() == name);
}
public TSyntaxNode Node<TSyntaxNode>()
where TSyntaxNode : SyntaxNode
{
return (TSyntaxNode)Context.Node;
}
}
internal readonly struct GeneratorSyntaxContext2<TSymbol>
where TSymbol : ISymbol
{
public readonly GeneratorSyntaxContext Context;
public readonly TSymbol Symbol;
public readonly ImmutableArray<AttributeData> Attributes;
public readonly bool HasValue = false;
public GeneratorSyntaxContext2(GeneratorSyntaxContext context, TSymbol symbol, ImmutableArray<AttributeData> attributes)
{
Context = context;
Symbol = symbol;
Attributes = attributes;
HasValue = true;
}
public static bool NotNull(GeneratorSyntaxContext2<TSymbol> context)
{
return context.HasValue;
}
public AttributeData SingleAttribute(string name)
{
return Attributes.Single(attribute => attribute.AttributeClass!.ToDisplayString() == name);
}
}

View File

@@ -0,0 +1,18 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Diagnostics.CodeAnalysis;
namespace Snap.Hutao.SourceGeneration.Primitive;
internal static class GeneratorSyntaxContextExtension
{
public static bool TryGetDeclaredSymbol<TSymbol>(this GeneratorSyntaxContext context, System.Threading.CancellationToken token, [NotNullWhen(true)] out TSymbol? symbol)
where TSymbol : class, ISymbol
{
symbol = context.SemanticModel.GetDeclaredSymbol(context.Node, token) as TSymbol;
return symbol != null;
}
}

View File

@@ -0,0 +1,21 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
namespace Snap.Hutao.SourceGeneration.Primitive;
internal static class SymbolDisplayFormats
{
public static SymbolDisplayFormat FullyQualifiedNonNullableFormat { get; } = new(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
public static SymbolDisplayFormat QualifiedNonNullableFormat { get; } = new(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
}

View File

@@ -0,0 +1,20 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Snap.Hutao.SourceGeneration.Primitive;
internal static class SyntaxExtension
{
/// <summary>
/// Checks whether a given <see cref="MemberDeclarationSyntax"/> has or could potentially have any attribute lists.
/// </summary>
/// <param name="declaration">The input <see cref="MemberDeclarationSyntax"/> to check.</param>
/// <returns>Whether <paramref name="declaration"/> has or potentially has any attribute lists.</returns>
public static bool HasAttributeLists<TSyntax>(this TSyntax declaration)
where TSyntax : MemberDeclarationSyntax
{
return declaration.AttributeLists.Count > 0;
}
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
namespace Snap.Hutao.SourceGeneration.Primitive;
internal static class TypeSymbolExtension
{
/// <summary>
/// Checks whether or not a given <see cref="ITypeSymbol"/> has or inherits from a specified type.
/// </summary>
/// <param name="typeSymbol">The target <see cref="ITypeSymbol"/> instance to check.</param>
/// <param name="name">The full name of the type to check for inheritance.</param>
/// <returns>Whether or not <paramref name="typeSymbol"/> is or inherits from <paramref name="name"/>.</returns>
public static bool IsOrInheritsFrom(this ITypeSymbol typeSymbol, string name)
{
for (ITypeSymbol? currentType = typeSymbol; currentType is not null; currentType = currentType.BaseType)
{
if (currentType.ToDisplayString() == name)
{
return true;
}
}
return false;
}
}

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
@@ -7,6 +7,7 @@
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<Configurations>Debug;Release;Debug As Fake Elevated</Configurations>
</PropertyGroup>
<ItemGroup>
@@ -18,11 +19,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4-beta1.22518.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" PrivateAssets="all" Version="3.3.4" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
</ItemGroup>
</Project>
</Project>

View File

@@ -0,0 +1,312 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Snap.Hutao.SourceGeneration.Primitive;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace Snap.Hutao.SourceGeneration;
/// <summary>
/// 通用分析器
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class UniversalAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor typeInternalDescriptor = new("SH001", "Type should be internal", "Type [{0}] should be internal", "Quality", DiagnosticSeverity.Info, true);
private static readonly DiagnosticDescriptor readOnlyStructRefDescriptor = new("SH002", "ReadOnly struct should be passed with ref-like key word", "ReadOnly Struct [{0}] should be passed with ref-like key word", "Quality", DiagnosticSeverity.Info, true);
private static readonly DiagnosticDescriptor useValueTaskIfPossibleDescriptor = new("SH003", "Use ValueTask instead of Task whenever possible", "Use ValueTask instead of Task", "Quality", DiagnosticSeverity.Info, true);
private static readonly DiagnosticDescriptor useIsNotNullPatternMatchingDescriptor = new("SH004", "Use \"is not null\" instead of \"!= null\" whenever possible", "Use \"is not null\" instead of \"!= null\"", "Quality", DiagnosticSeverity.Info, true);
private static readonly DiagnosticDescriptor useIsNullPatternMatchingDescriptor = new("SH005", "Use \"is null\" instead of \"== null\" whenever possible", "Use \"is null\" instead of \"== null\"", "Quality", DiagnosticSeverity.Info, true);
private static readonly DiagnosticDescriptor useIsPatternRecursiveMatchingDescriptor = new("SH006", "Use \"is { } obj\" whenever possible", "Use \"is {{ }} {0}\"", "Quality", DiagnosticSeverity.Info, true);
private static readonly DiagnosticDescriptor useArgumentNullExceptionThrowIfNullDescriptor = new("SH007", "Use \"ArgumentNullException.ThrowIfNull()\" instead of \"!\"", "Use \"ArgumentNullException.ThrowIfNull()\"", "Quality", DiagnosticSeverity.Info, true);
private static readonly ImmutableHashSet<string> RefLikeKeySkipTypes = new HashSet<string>()
{
"System.Threading.CancellationToken",
"System.Guid"
}.ToImmutableHashSet();
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get
{
return new DiagnosticDescriptor[]
{
typeInternalDescriptor,
readOnlyStructRefDescriptor,
useValueTaskIfPossibleDescriptor,
useIsNotNullPatternMatchingDescriptor,
useIsNullPatternMatchingDescriptor,
useIsPatternRecursiveMatchingDescriptor,
useArgumentNullExceptionThrowIfNullDescriptor
}.ToImmutableArray();
}
}
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(CompilationStart);
}
private static void CompilationStart(CompilationStartAnalysisContext context)
{
SyntaxKind[] types =
{
SyntaxKind.ClassDeclaration,
SyntaxKind.InterfaceDeclaration,
SyntaxKind.StructDeclaration,
SyntaxKind.EnumDeclaration,
};
context.RegisterSyntaxNodeAction(HandleTypeShouldBeInternal, types);
context.RegisterSyntaxNodeAction(HandleMethodParameterShouldUseRefLikeKeyword, SyntaxKind.MethodDeclaration);
context.RegisterSyntaxNodeAction(HandleMethodReturnTypeShouldUseValueTaskInsteadOfTask, SyntaxKind.MethodDeclaration);
context.RegisterSyntaxNodeAction(HandleConstructorParameterShouldUseRefLikeKeyword, SyntaxKind.ConstructorDeclaration);
SyntaxKind[] expressions =
{
SyntaxKind.EqualsExpression,
SyntaxKind.NotEqualsExpression,
};
context.RegisterSyntaxNodeAction(HandleEqualsAndNotEqualsExpressionShouldUsePatternMatching, expressions);
context.RegisterSyntaxNodeAction(HandleIsPatternShouldUseRecursivePattern, SyntaxKind.IsPatternExpression);
context.RegisterSyntaxNodeAction(HandleArgumentNullExceptionThrowIfNull, SyntaxKind.SuppressNullableWarningExpression);
// TODO add analyzer for unnecessary IServiceProvider registration
// TODO add analyzer for Singlton service use Scoped or Transient services
}
private static void HandleTypeShouldBeInternal(SyntaxNodeAnalysisContext context)
{
BaseTypeDeclarationSyntax syntax = (BaseTypeDeclarationSyntax)context.Node;
bool privateExists = false;
bool internalExists = false;
bool fileExists = false;
foreach (SyntaxToken token in syntax.Modifiers)
{
if (token.IsKind(SyntaxKind.PrivateKeyword))
{
privateExists = true;
}
if (token.IsKind(SyntaxKind.InternalKeyword))
{
internalExists = true;
}
if (token.IsKind(SyntaxKind.FileKeyword))
{
fileExists = true;
}
}
if (!privateExists && !internalExists && !fileExists)
{
Location location = syntax.Identifier.GetLocation();
Diagnostic diagnostic = Diagnostic.Create(typeInternalDescriptor, location, syntax.Identifier);
context.ReportDiagnostic(diagnostic);
}
}
private static void HandleMethodReturnTypeShouldUseValueTaskInsteadOfTask(SyntaxNodeAnalysisContext context)
{
MethodDeclarationSyntax methodSyntax = (MethodDeclarationSyntax)context.Node;
IMethodSymbol methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodSyntax)!;
// 跳过重载方法
if (methodSyntax.Modifiers.Any(token => token.IsKind(SyntaxKind.OverrideKeyword)))
{
return;
}
// ICommand can only use Task or Task<T>
if (methodSymbol.GetAttributes().Any(attr => attr.AttributeClass!.ToDisplayString() == Automation.CommandGenerator.AttributeName))
{
return;
}
if (methodSymbol.ReturnType.IsOrInheritsFrom("System.Threading.Tasks.Task"))
{
Location location = methodSyntax.ReturnType.GetLocation();
Diagnostic diagnostic = Diagnostic.Create(useValueTaskIfPossibleDescriptor, location);
context.ReportDiagnostic(diagnostic);
}
}
private static void HandleMethodParameterShouldUseRefLikeKeyword(SyntaxNodeAnalysisContext context)
{
MethodDeclarationSyntax methodSyntax = (MethodDeclarationSyntax)context.Node;
// 跳过方法定义 如 接口
if (methodSyntax.Body == null)
{
return;
}
IMethodSymbol methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodSyntax)!;
if (methodSymbol.ReturnType.IsOrInheritsFrom("System.Threading.Tasks.Task"))
{
return;
}
if (methodSymbol.ReturnType.IsOrInheritsFrom("System.Threading.Tasks.ValueTask"))
{
return;
}
foreach (SyntaxToken token in methodSyntax.Modifiers)
{
// 跳过异步方法,因为异步方法无法使用 ref/in/out
if (token.IsKind(SyntaxKind.AsyncKeyword))
{
return;
}
// 跳过重载方法
if (token.IsKind(SyntaxKind.OverrideKeyword))
{
return;
}
}
foreach (ParameterSyntax parameter in methodSyntax.ParameterList.Parameters)
{
if (context.SemanticModel.GetDeclaredSymbol(parameter) is IParameterSymbol symbol)
{
if (IsBuiltInType(symbol.Type))
{
continue;
}
if (RefLikeKeySkipTypes.Contains(symbol.Type.ToDisplayString()))
{
continue;
}
if (symbol.Type.IsReadOnly && symbol.RefKind == RefKind.None)
{
Location location = parameter.GetLocation();
Diagnostic diagnostic = Diagnostic.Create(readOnlyStructRefDescriptor, location, symbol.Type);
context.ReportDiagnostic(diagnostic);
}
}
}
}
private static void HandleConstructorParameterShouldUseRefLikeKeyword(SyntaxNodeAnalysisContext context)
{
ConstructorDeclarationSyntax constructorSyntax = (ConstructorDeclarationSyntax)context.Node;
foreach (ParameterSyntax parameter in constructorSyntax.ParameterList.Parameters)
{
if (context.SemanticModel.GetDeclaredSymbol(parameter) is IParameterSymbol symbol)
{
if (IsBuiltInType(symbol.Type))
{
continue;
}
// 跳过 CancellationToken
if (symbol.Type.ToDisplayString() == "System.Threading.CancellationToken")
{
continue;
}
if (symbol.Type.IsReadOnly && symbol.RefKind == RefKind.None)
{
Location location = parameter.GetLocation();
Diagnostic diagnostic = Diagnostic.Create(readOnlyStructRefDescriptor, location, symbol.Type);
context.ReportDiagnostic(diagnostic);
}
}
}
}
public static void HandleEqualsAndNotEqualsExpressionShouldUsePatternMatching(SyntaxNodeAnalysisContext context)
{
BinaryExpressionSyntax syntax = (BinaryExpressionSyntax)context.Node;
if (syntax.IsKind(SyntaxKind.NotEqualsExpression) && syntax.Right.IsKind(SyntaxKind.NullLiteralExpression))
{
Location location = syntax.OperatorToken.GetLocation();
Diagnostic diagnostic = Diagnostic.Create(useIsNotNullPatternMatchingDescriptor, location);
context.ReportDiagnostic(diagnostic);
}
else if (syntax.IsKind(SyntaxKind.EqualsExpression) && syntax.Right.IsKind(SyntaxKind.NullLiteralExpression))
{
Location location = syntax.OperatorToken.GetLocation();
Diagnostic diagnostic = Diagnostic.Create(useIsNullPatternMatchingDescriptor, location);
context.ReportDiagnostic(diagnostic);
}
}
private static void HandleIsPatternShouldUseRecursivePattern(SyntaxNodeAnalysisContext context)
{
IsPatternExpressionSyntax syntax = (IsPatternExpressionSyntax)context.Node;
if (syntax.Pattern is DeclarationPatternSyntax declaration)
{
ITypeSymbol? leftType = context.SemanticModel.GetTypeInfo(syntax.Expression).ConvertedType;
ITypeSymbol? rightType = context.SemanticModel.GetTypeInfo(declaration).ConvertedType;
if (SymbolEqualityComparer.Default.Equals(leftType, rightType))
{
Location location = declaration.GetLocation();
Diagnostic diagnostic = Diagnostic.Create(useIsPatternRecursiveMatchingDescriptor, location, declaration.Designation);
context.ReportDiagnostic(diagnostic);
}
}
}
private static void HandleArgumentNullExceptionThrowIfNull(SyntaxNodeAnalysisContext context)
{
PostfixUnaryExpressionSyntax syntax = (PostfixUnaryExpressionSyntax)context.Node;
if (syntax.Operand is LiteralExpressionSyntax literal)
{
if (literal.IsKind(SyntaxKind.DefaultLiteralExpression))
{
return;
}
}
if (syntax.Operand is DefaultExpressionSyntax expression)
{
return;
}
Location location = syntax.GetLocation();
Diagnostic diagnostic = Diagnostic.Create(useArgumentNullExceptionThrowIfNullDescriptor, location);
context.ReportDiagnostic(diagnostic);
}
private static bool IsBuiltInType(ITypeSymbol symbol)
{
return symbol.SpecialType switch
{
SpecialType.System_Boolean => true,
SpecialType.System_Char => true,
SpecialType.System_SByte => true,
SpecialType.System_Byte => true,
SpecialType.System_Int16 => true,
SpecialType.System_UInt16 => true,
SpecialType.System_Int32 => true,
SpecialType.System_UInt32 => true,
SpecialType.System_Int64 => true,
SpecialType.System_UInt64 => true,
SpecialType.System_Decimal => true,
SpecialType.System_Single => true,
SpecialType.System_Double => true,
SpecialType.System_IntPtr => true,
SpecialType.System_UIntPtr => true,
_ => false,
};
}
}

View File

@@ -0,0 +1,89 @@
using Microsoft.Extensions.DependencyInjection;
using System;
namespace Snap.Hutao.Test;
[TestClass]
public sealed class DependencyInjectionTest
{
private readonly IServiceProvider services = new ServiceCollection()
.AddSingleton<IService, ServiceA>()
.AddSingleton<IService, ServiceB>()
.AddScoped<IScopedService, ServiceA>()
.AddTransient(typeof(IGenericService<>), typeof(GenericService<>))
.BuildServiceProvider();
[TestMethod]
public void OriginalTypeCannotResolved()
{
Assert.IsNull(services.GetService<ServiceA>());
Assert.IsNull(services.GetService<ServiceB>());
}
[TestMethod]
public void GenericServicesCanBeResolved()
{
IServiceProvider services = new ServiceCollection()
.AddTransient(typeof(IGenericService<>), typeof(GenericService<>))
.BuildServiceProvider();
Assert.IsNotNull(services.GetService<IGenericService<int>>());
}
[TestMethod]
public void ScopedServiceInitializeMultipleTimesInScope()
{
using (IServiceScope scope = services.CreateScope())
{
IScopedService service1 = scope.ServiceProvider.GetRequiredService<IScopedService>();
IScopedService service2 = scope.ServiceProvider.GetRequiredService<IScopedService>();
Assert.AreNotEqual(service1.Id, service2.Id);
}
}
private interface IService
{
Guid Id { get; }
}
private interface IScopedService
{
Guid Id { get; }
}
private sealed class ServiceA : IService, IScopedService
{
public Guid Id
{
get => Guid.NewGuid();
}
}
private sealed class ServiceB : IService
{
public Guid Id
{
get => throw new NotImplementedException();
}
}
private interface IGenericService<T>
{
}
private sealed class GenericService<T> : IGenericService<T>
{
}
private sealed class NonInjectedServiceA
{
}
private sealed class NonInjectedServiceB
{
[ActivatorUtilitiesConstructor]
public NonInjectedServiceB(NonInjectedServiceA? serviceA)
{
}
}
}

View File

@@ -0,0 +1 @@
global using Microsoft.VisualStudio.TestTools.UnitTesting;

View File

@@ -0,0 +1,67 @@
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Snap.Hutao.Test;
[TestClass]
public class JsonSerializeTest
{
private const string SmapleObjectJson = """
{
"A" :1
}
""";
private const string SmapleEmptyStringObjectJson = """
{
"A" : ""
}
""";
private const string SmapleNumberKeyDictionaryJson = """
{
"111" : "12",
"222" : "34"
}
""";
[TestMethod]
public void DelegatePropertyCanSerialize()
{
Sample sample = JsonSerializer.Deserialize<Sample>(SmapleObjectJson)!;
Assert.AreEqual(sample.B, 1);
}
[TestMethod]
[ExpectedException(typeof(JsonException))]
public void EmptyStringCannotSerializeAsNumber()
{
StringNumberSample sample = JsonSerializer.Deserialize<StringNumberSample>(SmapleEmptyStringObjectJson)!;
Assert.AreEqual(sample.A, 0);
}
[TestMethod]
public void NumberStringKeyCanSerializeAsKey()
{
JsonSerializerOptions options = new()
{
NumberHandling = JsonNumberHandling.AllowReadingFromString,
};
Dictionary<int,string> sample = JsonSerializer.Deserialize<Dictionary<int, string>>(SmapleNumberKeyDictionaryJson, options)!;
Assert.AreEqual(sample[111], "12");
}
private sealed class Sample
{
public int A { get => B; set => B = value; }
public int B { get; set; }
}
private sealed class StringNumberSample
{
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
public int A { get; set; }
}
}

View File

@@ -0,0 +1,35 @@
using System;
namespace Snap.Hutao.Test.RuntimeBehavior;
[TestClass]
public sealed class EnumRuntimeBehaviorTest
{
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void EnumParseCanNotHandleEmptyString()
{
Enum.Parse<EnumA>(string.Empty);
}
[TestMethod]
public void EnumParseCanHandleNumberString()
{
EnumA a = Enum.Parse<EnumA>("2");
Assert.AreEqual(a, EnumA.ValueB);
}
[TestMethod]
public void EnumToStringDecimal()
{
Assert.AreEqual("2", EnumA.ValueB.ToString("D"));
}
private enum EnumA
{
None = 0,
ValueA = 1,
ValueB = 2,
ValueC = 3,
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
namespace Snap.Hutao.Test.RuntimeBehavior;
[TestClass]
public sealed class ForEachRuntimeBehaviorTest
{
[TestMethod]
public void ListOfStringCanEnumerateAsReadOnlySpanOfChar()
{
List<string> strings = new()
{
"a", "b", "c"
};
int count = 0;
foreach (ReadOnlySpan<char> chars in strings)
{
Assert.IsTrue(chars.Length == 1);
++count;
}
Assert.AreEqual(3, count);
}
}

View File

@@ -0,0 +1,15 @@
using System;
namespace Snap.Hutao.Test.RuntimeBehavior;
[TestClass]
public sealed class PropertyRuntimeBehaviorTest
{
[TestMethod]
public void GetTwiceOnPropertyResultsNotSame()
{
Assert.AreNotEqual(UUID, UUID);
}
public static Guid UUID { get => Guid.NewGuid(); }
}

View File

@@ -0,0 +1,17 @@
using System;
namespace Snap.Hutao.Test.RuntimeBehavior;
[TestClass]
public sealed class RangeRuntimeBehaviorTest
{
[TestMethod]
public void RangeTrimLastOne()
{
int[] array = { 1, 2, 3, 4 };
int[] test = { 1, 2, 3 };
int[] result = array[..^1];
Assert.AreEqual(3, result.Length);
Assert.IsTrue(MemoryExtensions.SequenceEqual<int>(test, result));
}
}

View File

@@ -0,0 +1,35 @@
using System;
namespace Snap.Hutao.Test.RuntimeBehavior;
[TestClass]
public sealed class StringRuntimeBehaviorTest
{
[TestMethod]
public unsafe void NullStringFixedIsNullPointer()
{
string testStr = null!;
fixed (char* pStr = testStr)
{
Assert.IsTrue(pStr == null);
}
}
[TestMethod]
public unsafe void EmptyStringFixedIsNullTerminator()
{
string testStr = string.Empty;
fixed (char* pStr = testStr)
{
Assert.IsTrue(*pStr == '\0');
}
}
[TestMethod]
public unsafe void EmptyStringAsSpanIsZeroLength()
{
string testStr = string.Empty;
ReadOnlySpan<char> testSpan = testStr;
Assert.IsTrue(testSpan.Length == 0);
}
}

View File

@@ -0,0 +1,16 @@
namespace Snap.Hutao.Test.RuntimeBehavior;
[TestClass]
public sealed class UnsafeRuntimeBehaviorTest
{
[TestMethod]
public unsafe void UInt32AllSetIs()
{
byte[] bytes = { 0xFF, 0xFF, 0xFF, 0xFF, };
fixed (byte* pBytes = bytes)
{
Assert.AreEqual(uint.MaxValue, *(uint*)pBytes);
}
}
}

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<Configurations>Debug;Release;Debug As Fake Elevated</Configurations>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -12,10 +12,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.SourceGeneration", "Snap.Hutao.SourceGeneration\Snap.Hutao.SourceGeneration.csproj", "{8B96721E-5604-47D2-9B72-06FEBAD0CE00}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.Installer", "Snap.Hutao.Installer\Snap.Hutao.Installer.csproj", "{CEC01691-F65E-4874-9AE2-F571369A7631}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.Test", "Snap.Hutao.Test\Snap.Hutao.Test.csproj", "{D691BA9F-904C-4229-87A5-E14F2EFF2F64}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug As Fake Elevated|Any CPU = Debug As Fake Elevated|Any CPU
Debug As Fake Elevated|arm64 = Debug As Fake Elevated|arm64
Debug As Fake Elevated|x64 = Debug As Fake Elevated|x64
Debug As Fake Elevated|x86 = Debug As Fake Elevated|x86
Debug|Any CPU = Debug|Any CPU
Debug|arm64 = Debug|arm64
Debug|x64 = Debug|x64
@@ -26,6 +30,18 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|Any CPU.ActiveCfg = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|Any CPU.Build.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|Any CPU.Deploy.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|arm64.ActiveCfg = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|arm64.Build.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|arm64.Deploy.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x64.ActiveCfg = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x64.Build.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x64.Deploy.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x86.ActiveCfg = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x86.Build.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug As Fake Elevated|x86.Deploy.0 = Debug As Fake Elevated|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug|Any CPU.ActiveCfg = Debug|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug|Any CPU.Build.0 = Debug|x64
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Debug|Any CPU.Deploy.0 = Debug|x64
@@ -50,8 +66,16 @@ Global
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.ActiveCfg = Release|x86
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.Build.0 = Release|x86
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.Deploy.0 = Release|x86
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|Any CPU.ActiveCfg = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|Any CPU.Build.0 = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|arm64.ActiveCfg = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|arm64.Build.0 = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|x64.ActiveCfg = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|x64.Build.0 = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|x86.ActiveCfg = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug As Fake Elevated|x86.Build.0 = Debug As Fake Elevated|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|Any CPU.ActiveCfg = Debug|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|Any CPU.Build.0 = Debug|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|arm64.ActiveCfg = Debug|Any CPU
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|arm64.Build.0 = Debug|Any CPU
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|x64.ActiveCfg = Debug|x64
@@ -66,27 +90,40 @@ Global
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x64.Build.0 = Release|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x86.ActiveCfg = Release|Any CPU
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x86.Build.0 = Release|Any CPU
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|arm64.ActiveCfg = Debug|Any CPU
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|arm64.Build.0 = Debug|Any CPU
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|x64.ActiveCfg = Debug|x64
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|x64.Build.0 = Debug|x64
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|x86.ActiveCfg = Debug|Any CPU
{CEC01691-F65E-4874-9AE2-F571369A7631}.Debug|x86.Build.0 = Debug|Any CPU
{CEC01691-F65E-4874-9AE2-F571369A7631}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CEC01691-F65E-4874-9AE2-F571369A7631}.Release|Any CPU.Build.0 = Release|Any CPU
{CEC01691-F65E-4874-9AE2-F571369A7631}.Release|arm64.ActiveCfg = Release|Any CPU
{CEC01691-F65E-4874-9AE2-F571369A7631}.Release|arm64.Build.0 = Release|Any CPU
{CEC01691-F65E-4874-9AE2-F571369A7631}.Release|x64.ActiveCfg = Release|Any CPU
{CEC01691-F65E-4874-9AE2-F571369A7631}.Release|x64.Build.0 = Release|Any CPU
{CEC01691-F65E-4874-9AE2-F571369A7631}.Release|x86.ActiveCfg = Release|Any CPU
{CEC01691-F65E-4874-9AE2-F571369A7631}.Release|x86.Build.0 = Release|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|Any CPU.ActiveCfg = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|Any CPU.Build.0 = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|arm64.ActiveCfg = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|arm64.Build.0 = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|x64.ActiveCfg = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|x64.Build.0 = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|x86.ActiveCfg = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug As Fake Elevated|x86.Build.0 = Debug As Fake Elevated|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug|arm64.ActiveCfg = Debug|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug|arm64.Build.0 = Debug|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug|x64.ActiveCfg = Debug|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug|x64.Build.0 = Debug|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug|x86.ActiveCfg = Debug|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Debug|x86.Build.0 = Debug|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Release|Any CPU.Build.0 = Release|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Release|arm64.ActiveCfg = Release|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Release|arm64.Build.0 = Release|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Release|x64.ActiveCfg = Release|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Release|x64.Build.0 = Release|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Release|x86.ActiveCfg = Release|Any CPU
{D691BA9F-904C-4229-87A5-E14F2EFF2F64}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
RESX_Rules = {"EnabledRules":["StringFormat","WhiteSpaceLead","WhiteSpaceTail","PunctuationLead"]}
RESX_ShowErrorsInErrorList = False
RESX_SortFileContentOnSave = True
SolutionGuid = {E4449B1C-0E6A-4D19-955E-1CA491656ABA}
RESX_NeutralResourcesLanguage = zh-CN
RESX_AutoApplyExistingTranslations = False
EndGlobalSection
EndGlobal

View File

@@ -9,7 +9,7 @@
},
"pathSegment": {
"add": {
".*": [ ".cs" ]
".*": [ ".cs", ".resx" ]
}
},
"fileSuffixToExtension": {
@@ -19,11 +19,12 @@
},
"fileToFile": {
"add": {
".filenesting.json": [ "App.xaml.cs" ],
"app.manifest": [ "App.xaml.cs" ],
"Package.appxmanifest": [ "App.xaml.cs" ],
"GlobalUsing.cs": [ "Program.cs" ],
".filenesting.json": [ "Program.cs" ],
".editorconfig": [ "Program.cs" ]
"Package.appxmanifest": [ "App.xaml" ],
"Package.StoreAssociation.xml": [ "App.xaml" ],
".editorconfig": [ "Program.cs" ],
"GlobalUsing.cs": [ "Program.cs" ]
}
}
}

View File

@@ -2,15 +2,16 @@
x:Class="Snap.Hutao.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cwcw="using:CommunityToolkit.WinUI.Controls"
xmlns:cwuc="using:CommunityToolkit.WinUI.UI.Converters"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:shci="using:Snap.Hutao.Control.Image"
xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter"
xmlns:shvc="using:Snap.Hutao.View.Converter">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<muxc:XamlControlsResources/>
<ResourceDictionary Source="ms-appx:///SettingsUI/Themes/Generic.xaml"/>
<ResourceDictionary Source="Control/Theme/FontStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
@@ -30,8 +31,9 @@
<!-- Page Transparent Background -->
<StaticResource x:Key="ApplicationPageBackgroundThemeBrush" ResourceKey="ControlFillColorTransparentBrush"/>
<!-- InfoBar Resource -->
<Thickness x:Key="InfoBarIconMargin">6,16,16,16</Thickness>
<Thickness x:Key="InfoBarContentRootPadding">16,0,0,0</Thickness>
<Thickness x:Key="InfoBarIconMargin">19,16,19,16</Thickness>
<Thickness x:Key="InfoBarContentRootPadding">0,0,0,0</Thickness>
<x:Double x:Key="InfoBarIconFontSize">20</x:Double>
<!-- Pivot Resource -->
<x:Double x:Key="PivotHeaderItemFontSize">16</x:Double>
<Thickness x:Key="PivotHeaderItemMargin">16,0,0,0</Thickness>
@@ -42,18 +44,60 @@
<CornerRadius x:Key="CompatCornerRadiusRight">0,6,6,0</CornerRadius>
<CornerRadius x:Key="CompatCornerRadiusBottom">0,0,6,6</CornerRadius>
<CornerRadius x:Key="CompatCornerRadiusSmall">2</CornerRadius>
<!-- OpenPaneLength -->
<!-- Length -->
<GridLength x:Key="CompatGridLength2">288</GridLength>
<x:Double x:Key="CompatSplitViewOpenPaneLength">212</x:Double>
<x:Double x:Key="CompatSplitViewOpenPaneLength2">268</x:Double>
<GridLength x:Key="CompatGridLength2">268</GridLength>
<x:Double x:Key="CompatSplitViewOpenPaneLength2">304</x:Double>
<x:Double x:Key="CompatSplitViewOpenPaneLength3">320</x:Double>
<x:Double x:Key="HomeAdaptiveCardHeight">180</x:Double>
<x:Double x:Key="ContentDialogMinHeight">64</x:Double>
<x:Double x:Key="LargeAppBarButtonWidth">100</x:Double>
<!-- ProgressBar -->
<x:Double x:Key="LargeBackgroundProgressBarOpacity">0.2</x:Double>
<!-- Brushes -->
<SolidColorBrush x:Key="AvatarPropertyAddValueBrush" Color="{ThemeResource AvatarPropertyAddValueColor}"/>
<SolidColorBrush x:Key="BlueBrush" Color="#FF5180CB"/>
<SolidColorBrush x:Key="PurpleBrush" Color="#FFA156E0"/>
<SolidColorBrush x:Key="OrangeBrush" Color="#FFBC6932"/>
<SolidColorBrush x:Key="GuaranteePullBrush" Color="#FF0063FF"/>
<SolidColorBrush x:Key="UpPullBrush" Color="#FFFFA400"/>
<!-- Settings -->
<x:Double x:Key="SettingsCardSpacing">3</x:Double>
<x:Double x:Key="SettingsCardMinHeight">0</x:Double>
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
<x:Double x:Key="SettingsCardWrapNoIconThreshold">0</x:Double>
<Style
x:Key="SettingsSectionHeaderTextBlockStyle"
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
TargetType="TextBlock">
<Style.Setters>
<Setter Property="Margin" Value="1,29,0,5"/>
</Style.Setters>
</Style>
<Style
x:Key="SettingsContentComboBoxStyle"
BasedOn="{StaticResource DefaultComboBoxStyle}"
TargetType="ComboBox">
<Setter Property="MinWidth" Value="120"/>
</Style>
<!-- Uris -->
<x:String x:Key="DocumentLink_MhyAccountSwitch">https://hut.ao/features/mhy-account-switch.html#%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96-cookie</x:String>
<x:String x:Key="DocumentLink_MhyAccountSwitch">https://hut.ao/features/mhy-account-switch.html</x:String>
<x:String x:Key="DocumentLink_BugReport">https://hut.ao/statements/bug-report.html</x:String>
<x:String x:Key="DocumentLink_Translate">https://translate.hut.ao</x:String>
<x:String x:Key="DocumentLink_Home">https://hut.ao</x:String>
<x:String x:Key="HolographicHat_GetToken_Release">https://github.com/HolographicHat/GetToken/releases/latest</x:String>
<x:String x:Key="Sponsor_Afadian">https://afdian.net/a/DismissedLight</x:String>
<!-- Images -->
<x:String x:Key="UI_ItemIcon_None">https://static.snapgenshin.com/Bg/UI_ItemIcon_None.png</x:String>
<x:String x:Key="UI_MarkTower">https://static.snapgenshin.com/Bg/UI_MarkTower.png</x:String>
<x:String x:Key="UI_Icon_Intee_Explore_1">https://static.snapgenshin.com/Bg/UI_Icon_Intee_Explore_1.png</x:String>
<x:String x:Key="UI_MarkQuest_Events_Proce">https://static.snapgenshin.com/Bg/UI_MarkQuest_Events_Proce.png</x:String>
<x:String x:Key="UI_ItemIcon_201">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_201.png</x:String>
<x:String x:Key="UI_ItemIcon_204">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_204.png</x:String>
<x:String x:Key="UI_ItemIcon_210">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_210.png</x:String>
<x:String x:Key="UI_ItemIcon_220021">https://static.snapgenshin.com/ItemIcon/UI_ItemIcon_220021.png</x:String>
<x:String x:Key="UI_ImgSign_ItemIcon">https://static.snapgenshin.com/Bg/UI_ImgSign_ItemIcon.png</x:String>
<x:String x:Key="UI_AvatarIcon_Costume_Card">https://static.snapgenshin.com/AvatarCard/UI_AvatarIcon_Costume_Card.png</x:String>
<x:String x:Key="UI_EmotionIcon25">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon25.png</x:String>
@@ -61,15 +105,26 @@
<x:String x:Key="UI_EmotionIcon250">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon250.png</x:String>
<x:String x:Key="UI_EmotionIcon272">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon272.png</x:String>
<x:String x:Key="UI_EmotionIcon293">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon293.png</x:String>
<!-- FontIcon Content -->
<x:String x:Key="FontIconContentAdd">&#xE710;</x:String>
<x:String x:Key="FontIconContentSetting">&#xE713;</x:String>
<x:String x:Key="FontIconContentRefresh">&#xE72C;</x:String>
<x:String x:Key="FontIconContentDelete">&#xE74D;</x:String>
<x:String x:Key="FontIconContentFolder">&#xE8B7;</x:String>
<x:String x:Key="FontIconContentCheckList">&#xE9D5;</x:String>
<x:String x:Key="FontIconContentAsteriskBadge12">&#xEDAD;</x:String>
<x:String x:Key="FontIconContentZipFolder">&#xF012;</x:String>
<!-- Converters -->
<cwuc:BoolNegationConverter x:Key="BoolNegationConverter"/>
<cwuc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<cwuc:FileSizeToFriendlyStringConverter x:Key="FileSizeToFriendlyStringConverter"/>
<shmmc:AchievementIconConverter x:Key="AchievementIconConverter"/>
<shmmc:AvatarCardConverter x:Key="AvatarCardConverter"/>
<shmmc:AvatarIconConverter x:Key="AvatarIconConverter"/>
<shmmc:AvatarNameCardPicConverter x:Key="AvatarNameCardPicConverter"/>
<shmmc:AvatarSideIconConverter x:Key="AvatarSideIconConverter"/>
<shmmc:DescParamDescriptor x:Key="DescParamDescriptor"/>
<shmmc:DescriptionsParametersDescriptor x:Key="DescParamDescriptor"/>
<shmmc:ElementNameIconConverter x:Key="ElementNameIconConverter"/>
<shmmc:EmotionIconConverter x:Key="EmotionIconConverter"/>
<shmmc:EquipIconConverter x:Key="EquipIconConverter"/>
@@ -77,7 +132,8 @@
<shmmc:GachaAvatarIconConverter x:Key="GachaAvatarIconConverter"/>
<shmmc:GachaEquipIconConverter x:Key="GachaEquipIconConverter"/>
<shmmc:ItemIconConverter x:Key="ItemIconConverter"/>
<shmmc:PropertyInfoDescriptor x:Key="PropertyDescriptor"/>
<shmmc:MonsterIconConverter x:Key="MonsterIconConverter"/>
<shmmc:PropertiesParametersDescriptor x:Key="PropertyDescriptor"/>
<shmmc:QualityColorConverter x:Key="QualityColorConverter"/>
<shmmc:WeaponTypeIconConverter x:Key="WeaponTypeIconConverter"/>
<shvc:BoolToVisibilityRevertConverter x:Key="BoolToVisibilityRevertConverter"/>
@@ -87,8 +143,9 @@
<shvc:EmptyObjectToVisibilityRevertConverter x:Key="EmptyObjectToVisibilityRevertConverter"/>
<shvc:Int32ToVisibilityConverter x:Key="Int32ToVisibilityConverter"/>
<shvc:Int32ToVisibilityRevertConverter x:Key="Int32ToVisibilityRevertConverter"/>
<!-- Styles -->
<shvc:StringBoolConverter x:Key="StringBoolConverter"/>
<!-- Styles -->
<Style
x:Key="LargeGridViewItemStyle"
BasedOn="{StaticResource DefaultGridViewItemStyle}"
@@ -111,13 +168,103 @@
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="{StaticResource CompatCornerRadius}"/>
</Style>
<Style x:Key="BorderGridStyle" TargetType="Grid">
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="{StaticResource CompatCornerRadius}"/>
</Style>
<Style TargetType="shci:CachedImage">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{ThemeResource ApplicationForegroundThemeBrush}"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="LazyLoadingThreshold" Value="300"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="shci:CachedImage">
<Grid
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Image
Name="PlaceholderImage"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Opacity="1.0"
Source="{TemplateBinding PlaceholderSource}"
Stretch="{TemplateBinding PlaceholderStretch}"/>
<Image
Name="Image"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
NineGrid="{TemplateBinding NineGrid}"
Opacity="0.0"
Stretch="{TemplateBinding Stretch}"/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Failed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Image" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderImage" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Loading">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Image" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderImage" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Loaded">
<Storyboard>
<DoubleAnimation
AutoReverse="False"
BeginTime="0"
Storyboard.TargetName="Image"
Storyboard.TargetProperty="Opacity"
From="0"
To="1"
Duration="0:0:0.5"/>
<DoubleAnimation
AutoReverse="False"
BeginTime="0"
Storyboard.TargetName="PlaceholderImage"
Storyboard.TargetProperty="Opacity"
From="1"
To="0"
Duration="0:0:0.5"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Unloaded"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ItemsPanelTemplate -->
<ItemsPanelTemplate x:Key="ItemsStackPanelTemplate">
<ItemsStackPanel/>
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="WrapPanelTemplate">
<cwcw:WrapPanel/>
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="HorizontalStackPanelTemplate">
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
<!-- Transitions -->
<TransitionCollection x:Key="ReorderThemeTransitions">
<ReorderThemeTransition/>
</TransitionCollection>
</ResourceDictionary>
</Application.Resources>
</Application>
</Application>

View File

@@ -1,38 +1,44 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Notifications;
using Microsoft.UI.Xaml;
using Microsoft.Windows.AppLifecycle;
using Snap.Hutao.Core;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.LifeCycle;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Core.Shell;
using System.Diagnostics;
using Windows.Storage;
namespace Snap.Hutao;
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// This class must be public
/// </summary>
[HighQuality]
[Injection(InjectAs.Singleton)]
public partial class App : Application
[SuppressMessage("", "SH001")]
public sealed partial class App : Application
{
private const string AppInstanceKey = "main";
private readonly IServiceProvider serviceProvider;
private readonly IActivation activation;
private readonly ILogger<App> logger;
/// <summary>
/// Initializes the singleton application object.
/// </summary>
/// <param name="logger">日志器</param>
/// <param name="appCenter">App Center</param>
public App(ILogger<App> logger)
/// <param name="serviceProvider">服务提供器</param>
public App(IServiceProvider serviceProvider)
{
// load app resource
// Load app resource
InitializeComponent();
this.logger = logger;
_ = new ExceptionRecorder(this, logger);
activation = serviceProvider.GetRequiredService<IActivation>();
logger = serviceProvider.GetRequiredService<ILogger<App>>();
serviceProvider.GetRequiredService<ExceptionRecorder>().Record(this);
this.serviceProvider = serviceProvider;
}
/// <inheritdoc/>
@@ -41,19 +47,16 @@ public partial class App : Application
try
{
AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
AppInstance firstInstance = AppInstance.FindOrRegisterForKey("main");
AppInstance firstInstance = AppInstance.FindOrRegisterForKey(AppInstanceKey);
if (firstInstance.IsCurrent)
{
// manually invoke
Activation.NonRedirectToActivate(firstInstance, activatedEventArgs);
firstInstance.Activated += Activation.Activate;
ToastNotificationManagerCompat.OnActivated += Activation.NotificationActivate;
activation.NonRedirectToActivate(firstInstance, activatedEventArgs);
activation.InitializeWith(firstInstance);
logger.LogInformation(EventIds.CommonLog, "Snap Hutao | {name} : {version}", CoreEnvironment.FamilyName, CoreEnvironment.Version);
logger.LogInformation(EventIds.CommonLog, "Cache folder : {folder}", ApplicationData.Current.LocalCacheFolder.Path);
JumpListHelper.ConfigureAsync().SafeForget(logger);
LogDiagnosticInformation();
serviceProvider.GetRequiredService<IJumpListInterop>().ConfigureAsync().SafeForget();
}
else
{
@@ -62,10 +65,19 @@ public partial class App : Application
Process.GetCurrentProcess().Kill();
}
}
catch (Exception)
catch
{
// AppInstance.GetCurrent() calls failed
Process.GetCurrentProcess().Kill();
}
}
private void LogDiagnosticInformation()
{
RuntimeOptions hutaoOptions = serviceProvider.GetRequiredService<RuntimeOptions>();
logger.LogInformation("FamilyName: {name}", hutaoOptions.FamilyName);
logger.LogInformation("Version: {version}", hutaoOptions.Version);
logger.LogInformation("LocalCache: {folder}", hutaoOptions.LocalCache);
}
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao;
/// <summary>
/// 应用程序资源提供器
/// </summary>
[Injection(InjectAs.Transient, typeof(IAppResourceProvider))]
internal sealed class AppResourceProvider : IAppResourceProvider
{
private readonly App app;
/// <summary>
/// 构造一个新的应用程序资源提供器
/// </summary>
/// <param name="app">应用</param>
public AppResourceProvider(App app)
{
this.app = app;
}
/// <inheritdoc/>
public T GetResource<T>(string name)
{
return (T)app.Resources[name];
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 500 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Some files were not shown because too many files have changed in this diff Show More