diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs index b2bc6c45..352a682c 100644 --- a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs @@ -45,12 +45,19 @@ public partial class App : Application public static Window? Window { get => window; set => window = value; } /// - public static new App Current => (App)Application.Current; + public static new App Current + { + get => (App)Application.Current; + } /// /// /// - public static Windows.Storage.ApplicationData AppData => Windows.Storage.ApplicationData.Current; + [SuppressMessage("", "CA1822")] + public Windows.Storage.ApplicationData AppData + { + get => Windows.Storage.ApplicationData.Current; + } /// /// Invoked when the application is launched. @@ -60,12 +67,13 @@ public partial class App : Application protected override async void OnLaunched(LaunchActivatedEventArgs args) { AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs(); - AppInstance mainInstance = AppInstance.FindOrRegisterForKey("main"); + AppInstance firstInstance = AppInstance.FindOrRegisterForKey("main"); + firstInstance.Activated += OnActivated; - if (!mainInstance.IsCurrent) + if (!firstInstance.IsCurrent) { // Redirect the activation (and args) to the "main" instance, and exit. - await mainInstance.RedirectActivationToAsync(activatedEventArgs); + await firstInstance.RedirectActivationToAsync(activatedEventArgs); Process.GetCurrentProcess().Kill(); } else @@ -73,11 +81,6 @@ public partial class App : Application Window = Ioc.Default.GetRequiredService(); Window.Activate(); - if (activatedEventArgs.TryGetProtocolActivatedUri(out Uri? uri)) - { - Ioc.Default.GetRequiredService().Information(uri.ToString()); - } - logger.LogInformation(EventIds.CommonLog, "Cache folder : {folder}", AppData.TemporaryFolder.Path); Ioc.Default @@ -117,6 +120,14 @@ public partial class App : Application logger.LogError(EventIds.UnhandledException, e.Exception, "未经处理的异常"); } + private void OnActivated(object? sender, AppActivationArguments args) + { + if (args.TryGetProtocolActivatedUri(out Uri? uri)) + { + Ioc.Default.GetRequiredService().Information(uri.ToString()); + } + } + private void XamlBindingFailed(object sender, BindingFailedEventArgs e) { logger.LogCritical(EventIds.XamlBindingError, "XAML绑定失败: {message}", e.Message); diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs index 896cb4cb..e9943272 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs +++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs @@ -31,6 +31,7 @@ public class CachedImage : ImageEx try { + Verify.Operation(imageUri.Host != string.Empty, "可能是空绑定产生的 [ms-appx:///]"); StorageFile file = await imageCache.GetFileFromCacheAsync(imageUri); // check token state to determine whether the operation should be canceled. diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs b/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs index e04dff26..83d5c661 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using Snap.Hutao.Core.Logging; +using Snap.Hutao.Extension; using System.Collections.Generic; using System.IO; using System.Linq; @@ -236,25 +237,17 @@ public abstract class CacheBase return; } - await cacheFolderSemaphore.WaitAsync().ConfigureAwait(false); - - baseFolder ??= ApplicationData.Current.TemporaryFolder; - - if (string.IsNullOrWhiteSpace(cacheFolderName)) + using (await cacheFolderSemaphore.EnterAsync().ConfigureAwait(false)) { - cacheFolderName = GetType().Name; - } + baseFolder ??= ApplicationData.Current.TemporaryFolder; - try - { - cacheFolder = await baseFolder - .CreateFolderAsync(cacheFolderName, CreationCollisionOption.OpenIfExists) - .AsTask() - .ConfigureAwait(false); - } - finally - { - cacheFolderSemaphore.Release(); + if (string.IsNullOrWhiteSpace(cacheFolderName)) + { + cacheFolderName = GetType().Name; + } + + cacheFolder = await baseFolder.CreateFolderAsync(cacheFolderName, CreationCollisionOption.OpenIfExists) + .AsTask().ConfigureAwait(false); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Caching/IImageCache.cs b/src/Snap.Hutao/Snap.Hutao/Core/Caching/IImageCache.cs index 41c3c549..77dd6a93 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Caching/IImageCache.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Caching/IImageCache.cs @@ -1,7 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Microsoft.UI.Xaml.Media.Imaging; using System.Collections.Generic; using Windows.Storage; diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/DatebaseLoggerProvider.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/DatebaseLoggerProvider.cs index 7e566002..87dc972e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Logging/DatebaseLoggerProvider.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Logging/DatebaseLoggerProvider.cs @@ -13,11 +13,12 @@ namespace Snap.Hutao.Core.Logging; /// The provider for the . /// [ProviderAlias("Database")] -public class DatebaseLoggerProvider : ILoggerProvider +public sealed class DatebaseLoggerProvider : ILoggerProvider { + private static readonly object LogDbContextLock = new(); + // the provider is created per logger, we don't want to create to much private static volatile LogDbContext? logDbContext; - private static readonly object logDbContextLock = new(); private static LogDbContext LogDbContext { @@ -25,7 +26,7 @@ public class DatebaseLoggerProvider : ILoggerProvider { if (logDbContext == null) { - lock (logDbContextLock) + lock (LogDbContextLock) { // prevent re-entry call if (logDbContext == null) @@ -34,7 +35,7 @@ public class DatebaseLoggerProvider : ILoggerProvider logDbContext = LogDbContext.Create($"Data Source={myDocument.Locate("Log.db")}"); if (logDbContext.Database.GetPendingMigrations().Any()) { - Debug.WriteLine("Performing LogDbContext Migrations"); + Debug.WriteLine("[Debug] Performing LogDbContext Migrations"); logDbContext.Database.Migrate(); } @@ -51,11 +52,12 @@ public class DatebaseLoggerProvider : ILoggerProvider /// public ILogger CreateLogger(string name) { - return new DatebaseLogger(name, LogDbContext, logDbContextLock); + return new DatebaseLogger(name, LogDbContext, LogDbContextLock); } /// public void Dispose() { + LogDbContext.Dispose(); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/SemaphoreSlimExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Extension/SemaphoreSlimExtensions.cs new file mode 100644 index 00000000..b42b1315 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Extension/SemaphoreSlimExtensions.cs @@ -0,0 +1,36 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Extension; + +/// +/// 信号量扩展 +/// +public static class SemaphoreSlimExtensions +{ + /// + /// 异步进入信号量 + /// + /// 信号量 + /// 可释放的对象,用于释放信号量 + public static async Task EnterAsync(this SemaphoreSlim semaphoreSlim) + { + await semaphoreSlim.WaitAsync(); + return new SemaphoreSlimReleaser(semaphoreSlim); + } + + private struct SemaphoreSlimReleaser : IDisposable + { + private readonly SemaphoreSlim semaphoreSlim; + + public SemaphoreSlimReleaser(SemaphoreSlim semaphoreSlim) + { + this.semaphoreSlim = semaphoreSlim; + } + + public void Dispose() + { + semaphoreSlim.Release(); + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs index 8e7857dc..98338f20 100644 --- a/src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs +++ b/src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs @@ -51,7 +51,7 @@ internal static class IocConfiguration { if (context.Database.GetPendingMigrations().Any()) { - Debug.WriteLine("Performing AppDbContext Migrations"); + Debug.WriteLine("[Debug] Performing AppDbContext Migrations"); context.Database.Migrate(); } } diff --git a/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs index e26699ff..d4cbb5c4 100644 --- a/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs +++ b/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using Microsoft.Extensions.DependencyInjection; +using Snap.Hutao.Core.Caching; using Snap.Hutao.Service.Metadata; using Snap.Hutao.Web.Enka; using Snap.Hutao.Web.Hoyolab.Bbs.User; @@ -29,7 +30,7 @@ internal static class IocHttpClientConfiguration { // services services.AddHttpClient(DefaultConfiguration); - // services.AddHttpClient(DefaultConfiguration); + services.AddHttpClient(DefaultConfiguration).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() { MaxConnectionsPerServer = 20 }); // normal clients services.AddHttpClient(DefaultConfiguration); diff --git a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs index d276af60..aa9e9787 100644 --- a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs @@ -7,7 +7,6 @@ using Snap.Hutao.Control.HostBackdrop; using Snap.Hutao.Core.Logging; using Snap.Hutao.Core.Setting; using Snap.Hutao.Core.Win32; -using System.Runtime.InteropServices; using WinRT.Interop; namespace Snap.Hutao; diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Annotation/FormatMethod.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Annotation/FormatMethod.cs index 560ccd99..e54ca6a5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Annotation/FormatMethod.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Annotation/FormatMethod.cs @@ -1,4 +1,7 @@ -namespace Snap.Hutao.Model.Metadata.Annotation; +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Metadata.Annotation; /// /// 格式化方法 diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/DescParam.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/DescParam.cs index 52157c4b..983fca33 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/DescParam.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/DescParam.cs @@ -13,10 +13,10 @@ public class DescParam /// /// 描述 /// - public IEnumerable Descriptions { get; set; } = default!; + public IList Descriptions { get; set; } = default!; /// /// 参数 /// - public IEnumerable> Parameters { get; set; } = default!; -} \ No newline at end of file + public IList> Parameters { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs index a61e6604..c2570364 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/SkillDepot.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System.Collections.Generic; +using System.Linq; namespace Snap.Hutao.Model.Metadata.Avatar; @@ -13,7 +14,7 @@ public class SkillDepot /// /// 技能天赋 /// - public IEnumerable Skills { get; set; } = default!; + public IList Skills { get; set; } = default!; /// /// 大招 @@ -23,10 +24,30 @@ public class SkillDepot /// /// 固有天赋 /// - public IEnumerable Inherents { get; set; } = default!; + public IList Inherents { get; set; } = default!; + + /// + /// 全部天赋 + /// + public IList CompositeSkills => GetCompositeSkills().ToList(); /// /// 命之座 /// - public IEnumerable Talents { get; set; } = default!; + public IList Talents { get; set; } = default!; + + private IEnumerable GetCompositeSkills() + { + foreach (ProudableSkill skill in Skills) + { + yield return skill; + } + + yield return EnergySkill; + + foreach (ProudableSkill skill in Inherents) + { + yield return skill; + } + } } diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AvatarIconConverter.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AvatarIconConverter.cs index c3fa3dee..57055dd6 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AvatarIconConverter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AvatarIconConverter.cs @@ -23,4 +23,4 @@ internal class AvatarIconConverter : IValueConverter { throw Must.NeverHappen(); } -} +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/DescParamDescriptor.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/DescParamDescriptor.cs index 08ec486d..36ec798d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/DescParamDescriptor.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/DescParamDescriptor.cs @@ -17,25 +17,26 @@ internal class DescParamDescriptor : IValueConverter /// public object Convert(object value, Type targetType, object parameter, string language) { - DescParam descParam = (DescParam)value; - IEnumerable parsedDescriptions = descParam.Descriptions.Select(desc => - { - string[] parts = desc.Split('|', 2); - return new DescFormat(parts[0], parts[1]); - }); + DescParam rawDescParam = (DescParam)value; - IList> parameters = descParam.Parameters - .Select(param => + // Spilt rawDesc into two parts: desc and format + IList parsedDescriptions = rawDescParam.Descriptions + .Select(desc => { - IList parameters = GetFormattedParameters(parsedDescriptions, param.Parameters); - parameters.Insert(0, param.Level.ToString()); - return parameters; + string[] parts = desc.Split('|', 2); + return new DescFormat(parts[0], parts[1]); }) .ToList(); - List descList = parsedDescriptions.Select(p => p.Description).ToList(); - descList.Insert(0, "等级"); - return new DescParamInternal(descList, parameters); + IList> parameters = rawDescParam.Parameters + .Select(param => + { + IList parameters = GetFormattedParameters(parsedDescriptions, param.Parameters); + return new LevelParam() { Level = param.Level.ToString(), Parameters = parameters }; + }) + .ToList(); + + return parameters; } /// @@ -44,6 +45,22 @@ internal class DescParamDescriptor : IValueConverter throw Must.NeverHappen(); } + private static IList GetFormattedParameters(IList formats, IList param) + { + List results = new(); + + for (int index = 0; index < formats.Count; index++) + { + DescFormat descFormat = formats[index]; + + string format = descFormat.Format; + string resultFormatted = Regex.Replace(format, @"{param\d+.*?}", match => EvaluateMatch(match, param)); + results.Add(new ParameterInfo { Description = descFormat.Description, Parameter = resultFormatted }); + } + + return results; + } + private static string EvaluateMatch(Match match, IList param) { if (match.Success) @@ -74,19 +91,6 @@ internal class DescParamDescriptor : IValueConverter } } - private IList GetFormattedParameters(IEnumerable formats, IList param) - { - List results = new(); - foreach (DescFormat descFormat in formats) - { - string format = descFormat.Format; - string resultFormatted = Regex.Replace(format, @"{param\d+.*?}", match => EvaluateMatch(match, param)); - results.Add(resultFormatted); - } - - return results; - } - private class DescFormat { public DescFormat(string description, string format) @@ -99,17 +103,4 @@ internal class DescParamDescriptor : IValueConverter public string Format { get; set; } } - - private class DescParamInternal - { - public DescParamInternal(IList descriptions, IList> parameters) - { - Descriptions = descriptions; - Parameters = parameters; - } - - public IList Descriptions { get; set; } - - public IList> Parameters { get; set; } - } -} \ No newline at end of file +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/FightPropertyConverter.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/FightPropertyConverter.cs deleted file mode 100644 index c7530a5d..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/FightPropertyConverter.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Microsoft.UI.Xaml.Data; -using Snap.Hutao.Extension; -using Snap.Hutao.Model.Intrinsic; - -namespace Snap.Hutao.Model.Metadata.Converter; - -/// -/// 战斗属性转换器 -/// -internal class FightPropertyConverter : IValueConverter -{ - /// - public object Convert(object value, Type targetType, object parameter, string language) - { - return ((FightProperty)value).GetDescription(); - } - - /// - public object ConvertBack(object value, Type targetType, object parameter, string language) - { - throw Must.NeverHappen(); - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/PropertyInfoDescriptor.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/PropertyInfoDescriptor.cs new file mode 100644 index 00000000..d0433e83 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/PropertyInfoDescriptor.cs @@ -0,0 +1,61 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.UI.Xaml.Data; +using Snap.Hutao.Extension; +using Snap.Hutao.Model.Intrinsic; +using Snap.Hutao.Model.Metadata.Annotation; +using System.Collections.Generic; +using System.Linq; + +namespace Snap.Hutao.Model.Metadata.Converter; + +/// +/// 基础属性翻译器 +/// +internal class PropertyInfoDescriptor : IValueConverter +{ + /// + public object Convert(object value, Type targetType, object parameter, string language) + { + PropertyInfo rawDescParam = (PropertyInfo)value; + + IList> parameters = rawDescParam.Parameters + .Select(param => + { + IList parameters = GetFormattedParameters(param.Parameters, rawDescParam.Properties); + return new LevelParam() { Level = param.Level, Parameters = parameters }; + }) + .ToList(); + + return parameters; + } + + /// + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw Must.NeverHappen(); + } + + private static IList GetFormattedParameters(IList parameters, IList properties) + { + List results = new(); + + for (int index = 0; index < parameters.Count; index++) + { + double param = parameters[index]; + FormatMethod method = properties[index].GetFormat(); + + string valueFormatted = method switch + { + FormatMethod.Integer => Math.Round((double)param, MidpointRounding.AwayFromZero).ToString(), + FormatMethod.Percent => string.Format("{0:P1}", param), + _ => param.ToString(), + }; + + results.Add(new ParameterInfo { Description = properties[index].GetDescription(), Parameter = valueFormatted }); + } + + return results; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/FightPropertyValueFormatter.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/SkillIconConverter.cs similarity index 50% rename from src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/FightPropertyValueFormatter.cs rename to src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/SkillIconConverter.cs index ca241949..3117efe5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/FightPropertyValueFormatter.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/SkillIconConverter.cs @@ -2,27 +2,30 @@ // Licensed under the MIT license. using Microsoft.UI.Xaml.Data; -using Snap.Hutao.Model.Intrinsic; -using Snap.Hutao.Model.Metadata.Annotation; namespace Snap.Hutao.Model.Metadata.Converter; /// -/// 战斗属性数值格式化器 +/// 技能图标转换器 /// -internal class FightPropertyValueFormatter : IValueConverter +internal class SkillIconConverter : IValueConverter { + private const string SkillUrl = "https://static.snapgenshin.com/Skill/{0}.png"; + private const string TalentUrl = "https://static.snapgenshin.com/Talent/{0}.png"; + /// public object Convert(object value, Type targetType, object parameter, string language) { - FormatMethod method = ((FightProperty)parameter).GetFormat(); + string target = (string)value; - return method switch + if (target.StartsWith("UI_Talent_")) { - FormatMethod.Integer => Math.Round((double)value, MidpointRounding.AwayFromZero), - FormatMethod.Percent => string.Format("{0:P1}", value), - _ => value, - }; + return new Uri(string.Format(TalentUrl, target)); + } + else + { + return new Uri(string.Format(SkillUrl, target)); + } } /// diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/ParameterInfo.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/ParameterInfo.cs new file mode 100644 index 00000000..dc3fa7a8 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/ParameterInfo.cs @@ -0,0 +1,21 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Model.Metadata; + +/// +/// 包装一个描述参数 +/// 专用于绑定 +/// +public class ParameterInfo +{ + /// + /// 描述 + /// + public string Description { get; set; } = default!; + + /// + /// 参数 + /// + public string Parameter { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/PropertyInfo.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/PropertyInfo.cs index 3d31796d..5781fa82 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/PropertyInfo.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/PropertyInfo.cs @@ -14,10 +14,10 @@ public class PropertyInfo /// /// 提升的属性 /// - public IEnumerable Properties { get; set; } = default!; + public IList Properties { get; set; } = default!; /// /// 参数 /// - public IEnumerable> Parameters { get; set; } = default!; + public IList> Parameters { get; set; } = default!; } diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest index 30d59408..d9c235f7 100644 --- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest +++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest @@ -9,7 +9,7 @@ + Version="1.0.18.0" /> 胡桃 diff --git a/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json b/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json index 14b37087..dc55a580 100644 --- a/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json +++ b/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json @@ -1,7 +1,8 @@ { "profiles": { "Snap.Hutao (Package)": { - "commandName": "MsixPackage" + "commandName": "MsixPackage", + "nativeDebugging": false }, "Snap.Hutao (Unpackaged)": { "commandName": "Project" diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 1b8ae0f0..af073e01 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -34,7 +34,9 @@ + + @@ -104,6 +106,16 @@ + + + MSBuild:Compile + + + + + MSBuild:Compile + + MSBuild:Compile diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml b/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml new file mode 100644 index 00000000..a5f2e757 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml.cs new file mode 100644 index 00000000..8972472b --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/View/Control/DescParamComboBox.xaml.cs @@ -0,0 +1,61 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Snap.Hutao.Core; +using Snap.Hutao.Model.Metadata; +using System.Collections.Generic; +using System.Linq; + +namespace Snap.Hutao.View.Control; + +/// +/// 描述参数组合框 +/// +public sealed partial class DescParamComboBox : UserControl +{ + private static readonly DependencyProperty SourceProperty = Property + .Depend>>(nameof(Source), default!, OnSourceChanged); + + /// + /// 构造一个新的描述参数组合框 + /// + public DescParamComboBox() + { + InitializeComponent(); + } + + /// + /// 技能列表 + /// + public IList> Source + { + get { return (IList>)GetValue(SourceProperty); } + set { SetValue(SourceProperty, value); } + } + + private static void OnSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) + { + // Some of the {x:Bind} feature is not working properly, + // so we use this simple code behind approach to achieve selection function + if (sender is DescParamComboBox descParamComboBox) + { + if (args.NewValue != args.OldValue && args.NewValue is IList> list) + { + descParamComboBox.ItemHost.ItemsSource = list; + descParamComboBox.ItemHost.SelectedIndex = 0; + + descParamComboBox.DetailsHost.ItemsSource = list.FirstOrDefault()?.Parameters; + } + } + } + + private void ItemHostSelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (sender is ComboBox comboBox && comboBox.SelectedIndex >= 0) + { + DetailsHost.ItemsSource = Source[comboBox.SelectedIndex]?.Parameters; + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/SkillPivot.xaml b/src/Snap.Hutao/Snap.Hutao/View/Control/SkillPivot.xaml new file mode 100644 index 00000000..9502f10a --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/View/Control/SkillPivot.xaml @@ -0,0 +1,48 @@ + + + + + + + 0,0,16,0 + 0 + + + + + + + + + + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Control/SkillPivot.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Control/SkillPivot.xaml.cs new file mode 100644 index 00000000..c049bf72 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/View/Control/SkillPivot.xaml.cs @@ -0,0 +1,54 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Snap.Hutao.Core; +using System.Collections; + +namespace Snap.Hutao.View.Control; + +/// +/// 技能展柜 +/// +public sealed partial class SkillPivot : UserControl +{ + private static readonly DependencyProperty SkillsProperty = Property.Depend(nameof(Skills)); + private static readonly DependencyProperty SelectedProperty = Property.Depend(nameof(Selected)); + private static readonly DependencyProperty ItemTemplateProperty = Property.Depend(nameof(ItemTemplate)); + + /// + /// 创建一个新的技能展柜 + /// + public SkillPivot() + { + InitializeComponent(); + } + + /// + /// 技能列表 + /// + public IList Skills + { + get { return (IList)GetValue(SkillsProperty); } + set { SetValue(SkillsProperty, value); } + } + + /// + /// 选中的项 + /// + public object Selected + { + get { return GetValue(SelectedProperty); } + set { SetValue(SelectedProperty, value); } + } + + /// + /// 项目模板 + /// + public DataTemplate ItemTemplate + { + get { return (DataTemplate)GetValue(ItemTemplateProperty); } + set { SetValue(ItemTemplateProperty, value); } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs index f2fbf6d1..15a4bb68 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml.cs @@ -28,6 +28,7 @@ public sealed partial class MainView : UserControl navigationService = Ioc.Default.GetRequiredService(); navigationService.Initialize(NavView, ContentFrame); + navigationService.Navigate(INavigationAwaiter.Default, true); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs index 5a139e89..836b131d 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementContentPage.xaml.cs @@ -4,9 +4,10 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Navigation; using Microsoft.VisualStudio.Threading; -using Snap.Hutao.Core; +using Microsoft.Web.WebView2.Core; using Snap.Hutao.Extension; using Snap.Hutao.Service.Navigation; +using Windows.System; namespace Snap.Hutao.View.Page; @@ -55,31 +56,7 @@ openInWebview: function(url){ location.href = url }}"; } } - private async Task LoadAnnouncementAsync(INavigationData data) - { - try - { - await WebView.EnsureCoreWebView2Async(); - - await WebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(MihoyoSDKDefinition); - WebView.CoreWebView2.WebMessageReceived += (_, e) => Browser.Open(e.TryGetWebMessageAsString()); - } - catch (Exception ex) - { - data.NotifyNavigationException(ex); - return; - } - - WebView.NavigateToString(ReplaceForeground(targetContent, ActualTheme)); - data.NotifyNavigationCompleted(); - } - - private void PageActualThemeChanged(FrameworkElement sender, object args) - { - WebView.NavigateToString(ReplaceForeground(targetContent, ActualTheme)); - } - - private string? ReplaceForeground(string? rawContent, ElementTheme theme) + private static string? ReplaceForeground(string? rawContent, ElementTheme theme) { if (string.IsNullOrWhiteSpace(rawContent)) { @@ -105,4 +82,39 @@ openInWebview: function(url){ location.href = url }}"; // wrap a default color body around return $@"{rawContent}"; } + + private async Task LoadAnnouncementAsync(INavigationData data) + { + try + { + await WebView.EnsureCoreWebView2Async(); + + await WebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(MihoyoSDKDefinition); + WebView.CoreWebView2.WebMessageReceived += OnWebMessageReceived; + } + catch (Exception ex) + { + data.NotifyNavigationException(ex); + return; + } + + WebView.NavigateToString(ReplaceForeground(targetContent, ActualTheme)); + data.NotifyNavigationCompleted(); + } + + private void PageActualThemeChanged(FrameworkElement sender, object args) + { + WebView.NavigateToString(ReplaceForeground(targetContent, ActualTheme)); + } + + [SuppressMessage("", "VSTHRD100")] + private async void OnWebMessageReceived(CoreWebView2 coreWebView2, CoreWebView2WebMessageReceivedEventArgs args) + { + string url = args.TryGetWebMessageAsString(); + + if (Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out Uri? uri)) + { + await Launcher.LaunchUriAsync(uri).AsTask().ConfigureAwait(false); + } + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml index 0cbe1585..eeb0b57d 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml @@ -6,7 +6,6 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:cwuc="using:CommunityToolkit.WinUI.UI.Controls" xmlns:mxi="using:Microsoft.Xaml.Interactivity" - xmlns:shc="using:Snap.Hutao.Control" xmlns:shcb="using:Snap.Hutao.Control.Behavior" xmlns:shci="using:Snap.Hutao.Control.Image" xmlns:shct="using:Snap.Hutao.Control.Text" @@ -26,30 +25,14 @@ - - - - - + - - - - - - + + - - - + Source="{Binding Property,Converter={StaticResource PropertyDescriptor}}"/> + + + + + + + + @@ -180,12 +109,13 @@ + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Content="{Binding Selected,Mode=OneWay}" + ContentTemplate="{StaticResource PropertyDataTemplate}"/> - + + - + + - - - + - + @@ -448,8 +301,7 @@ HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Header="资料"> - + @@ -460,9 +312,7 @@ Margin="16,8,16,16" Description="{Binding Context}"> - + - - + + diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs index 9f4d79e1..0ada31bb 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs @@ -34,14 +34,14 @@ internal class ExperimentalFeaturesViewModel : ObservableObject /// public ICommand OpenDataFolderCommand { get; } - private Task OpenCacheFolderAsync() + private Task OpenCacheFolderAsync(CancellationToken token) { - return Launcher.LaunchFolderAsync(App.AppData.TemporaryFolder).AsTask(); + return Launcher.LaunchFolderAsync(App.Current.AppData.TemporaryFolder).AsTask(token); } - private async Task OpenDataFolderAsync() + private async Task OpenDataFolderAsync(CancellationToken token) { - StorageFolder folder = await KnownFolders.DocumentsLibrary.GetFolderAsync("Hutao").AsTask().ConfigureAwait(false); - await Launcher.LaunchFolderAsync(folder).AsTask().ConfigureAwait(false); + StorageFolder folder = await KnownFolders.DocumentsLibrary.GetFolderAsync("Hutao").AsTask(token).ConfigureAwait(false); + await Launcher.LaunchFolderAsync(folder).AsTask(token).ConfigureAwait(false); } } \ No newline at end of file