mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
update avatar page style
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
# Snap.Hutao
|
# Snap.Hutao
|
||||||
Snap Genshin but WinUI3
|
唷,找本堂主有何贵干呀?
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.VisualStudio.Threading;
|
||||||
using Microsoft.Windows.AppLifecycle;
|
using Microsoft.Windows.AppLifecycle;
|
||||||
using Snap.Hutao.Core.Logging;
|
using Snap.Hutao.Core.Logging;
|
||||||
using Snap.Hutao.Extension;
|
using Snap.Hutao.Extension;
|
||||||
@@ -68,6 +69,7 @@ public partial class App : Application
|
|||||||
Window = Ioc.Default.GetRequiredService<MainWindow>();
|
Window = Ioc.Default.GetRequiredService<MainWindow>();
|
||||||
Window.Activate();
|
Window.Activate();
|
||||||
|
|
||||||
|
logger.LogInformation("Image cache folder : {folder}", Windows.Storage.ApplicationData.Current.TemporaryFolder.Path);
|
||||||
if (Ioc.Default.GetRequiredService<IMetadataService>() is IMetadataInitializer initializer)
|
if (Ioc.Default.GetRequiredService<IMetadataService>() is IMetadataInitializer initializer)
|
||||||
{
|
{
|
||||||
initializer.InitializeInternalAsync().SafeForget();
|
initializer.InitializeInternalAsync().SafeForget();
|
||||||
|
|||||||
@@ -17,11 +17,6 @@ public class AppDbContextDesignTimeFactory : IDesignTimeDbContextFactory<AppDbCo
|
|||||||
public AppDbContext CreateDbContext(string[] args)
|
public AppDbContext CreateDbContext(string[] args)
|
||||||
{
|
{
|
||||||
MyDocumentContext myDocument = new(new());
|
MyDocumentContext myDocument = new(new());
|
||||||
myDocument.EnsureDirectory();
|
return AppDbContext.Create($"Data Source={myDocument.Locate("Userdata.db")}");
|
||||||
|
|
||||||
string dbFile = myDocument.Locate("Userdata.db");
|
|
||||||
string sqlConnectionString = $"Data Source={dbFile}";
|
|
||||||
|
|
||||||
return AppDbContext.Create(sqlConnectionString);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ internal abstract class FileSystemContext
|
|||||||
public FileSystemContext(IFileSystemLocation location)
|
public FileSystemContext(IFileSystemLocation location)
|
||||||
{
|
{
|
||||||
this.location = location;
|
this.location = location;
|
||||||
|
EnsureDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -62,22 +63,6 @@ internal abstract class FileSystemContext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查根目录
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>是否创建了路径</returns>
|
|
||||||
public bool EnsureDirectory()
|
|
||||||
{
|
|
||||||
string folder = location.GetPath();
|
|
||||||
if (!Directory.Exists(folder))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(folder);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 检查文件是否存在
|
/// 检查文件是否存在
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -170,4 +155,20 @@ internal abstract class FileSystemContext
|
|||||||
{
|
{
|
||||||
return File.Create(Locate(file));
|
return File.Create(Locate(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查根目录
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>是否创建了路径</returns>
|
||||||
|
private bool EnsureDirectory()
|
||||||
|
{
|
||||||
|
string folder = location.GetPath();
|
||||||
|
if (!Directory.Exists(folder))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(folder);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.HostBackdrop;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 回退行为
|
|
||||||
/// </summary>
|
|
||||||
public enum BackbdropFallBackBehavior
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 回退到无
|
|
||||||
/// </summary>
|
|
||||||
None,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 回退到亚克力
|
|
||||||
/// </summary>
|
|
||||||
Acrylic,
|
|
||||||
}
|
|
||||||
@@ -16,10 +16,9 @@ namespace Snap.Hutao.Control.HostBackdrop;
|
|||||||
public class SystemBackdrop
|
public class SystemBackdrop
|
||||||
{
|
{
|
||||||
private readonly Window window;
|
private readonly Window window;
|
||||||
private readonly BackbdropFallBackBehavior fallBackBehavior;
|
|
||||||
|
|
||||||
private WindowsSystemDispatcherQueueHelper? dispatcherQueueHelper;
|
private WindowsSystemDispatcherQueueHelper? dispatcherQueueHelper;
|
||||||
private ISystemBackdropControllerWithTargets? backdropController;
|
private MicaController? backdropController;
|
||||||
private SystemBackdropConfiguration? configurationSource;
|
private SystemBackdropConfiguration? configurationSource;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -27,16 +26,14 @@ public class SystemBackdrop
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="window">窗体</param>
|
/// <param name="window">窗体</param>
|
||||||
/// <param name="fallBackBehavior">回退行为</param>
|
/// <param name="fallBackBehavior">回退行为</param>
|
||||||
public SystemBackdrop(Window window, BackbdropFallBackBehavior fallBackBehavior = BackbdropFallBackBehavior.Acrylic)
|
public SystemBackdrop(Window window)
|
||||||
{
|
{
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.fallBackBehavior = fallBackBehavior;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum BackDropType
|
private enum BackDropType
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
Acrylic,
|
|
||||||
Mica,
|
Mica,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,9 +43,7 @@ public class SystemBackdrop
|
|||||||
/// <returns>是否设置成功</returns>
|
/// <returns>是否设置成功</returns>
|
||||||
public bool TrySetBackdrop()
|
public bool TrySetBackdrop()
|
||||||
{
|
{
|
||||||
BackDropType targetBackDropType = ResolveBackdropType();
|
if (!MicaController.IsSupported())
|
||||||
|
|
||||||
if (targetBackDropType == BackDropType.None)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -67,12 +62,7 @@ public class SystemBackdrop
|
|||||||
configurationSource.IsInputActive = true;
|
configurationSource.IsInputActive = true;
|
||||||
SetConfigurationSourceTheme();
|
SetConfigurationSourceTheme();
|
||||||
|
|
||||||
backdropController = targetBackDropType switch
|
backdropController = new MicaController();
|
||||||
{
|
|
||||||
BackDropType.Mica => new MicaController(),
|
|
||||||
BackDropType.Acrylic => new DesktopAcrylicController(),
|
|
||||||
_ => throw Must.NeverHappen(),
|
|
||||||
};
|
|
||||||
|
|
||||||
ICompositionSupportsSystemBackdrop target = window.As<ICompositionSupportsSystemBackdrop>();
|
ICompositionSupportsSystemBackdrop target = window.As<ICompositionSupportsSystemBackdrop>();
|
||||||
backdropController.AddSystemBackdropTarget(target);
|
backdropController.AddSystemBackdropTarget(target);
|
||||||
@@ -82,27 +72,6 @@ public class SystemBackdrop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BackDropType ResolveBackdropType()
|
|
||||||
{
|
|
||||||
BackDropType targetBackDropType = BackDropType.None;
|
|
||||||
if (MicaController.IsSupported())
|
|
||||||
{
|
|
||||||
targetBackDropType = BackDropType.Mica;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (fallBackBehavior == BackbdropFallBackBehavior.Acrylic)
|
|
||||||
{
|
|
||||||
if (DesktopAcrylicController.IsSupported())
|
|
||||||
{
|
|
||||||
targetBackDropType = BackDropType.Acrylic;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return targetBackDropType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WindowActivated(object sender, WindowActivatedEventArgs args)
|
private void WindowActivated(object sender, WindowActivatedEventArgs args)
|
||||||
{
|
{
|
||||||
Must.NotNull(configurationSource!);
|
Must.NotNull(configurationSource!);
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ namespace Snap.Hutao.Control;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class BindingProxy : DependencyObject
|
public class BindingProxy : DependencyObject
|
||||||
{
|
{
|
||||||
private static readonly DependencyProperty DataProperty = Property<BindingProxy>.Depend<object>(nameof(DataContext));
|
private static readonly DependencyProperty DataContextProperty = Property<BindingProxy>.Depend<object>(nameof(DataContext));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据上下文
|
/// 数据上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object? DataContext
|
public object? DataContext
|
||||||
{
|
{
|
||||||
get => GetValue(DataProperty);
|
get => GetValue(DataContextProperty);
|
||||||
set => SetValue(DataProperty, value);
|
set => SetValue(DataContextProperty, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,16 +32,21 @@ public class CachedImage : ImageEx
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override async Task<ImageSource> ProvideCachedResourceAsync(Uri imageUri, CancellationToken token)
|
protected override async Task<ImageSource> ProvideCachedResourceAsync(Uri imageUri, CancellationToken token)
|
||||||
{
|
{
|
||||||
BitmapImage image;
|
BitmapImage? image;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
image = await ImageCache.Instance.GetFromCacheAsync(imageUri, true, token);
|
image = await ImageCache.Instance.GetFromCacheAsync(imageUri, true, token);
|
||||||
}
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
// task was explicitly canceled
|
||||||
|
throw;
|
||||||
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// maybe the image is corrupted remove it and re-download
|
// maybe the image is corrupted, remove it.
|
||||||
await ImageCache.Instance.RemoveAsync(imageUri.Enumerate());
|
await ImageCache.Instance.RemoveAsync(imageUri.Enumerate());
|
||||||
image = await ImageCache.Instance.GetFromCacheAsync(imageUri, false, token);
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check token state to determine whether the operation should be canceled.
|
// check token state to determine whether the operation should be canceled.
|
||||||
|
|||||||
101
src/Snap.Hutao/Snap.Hutao/Control/Text/DescriptionTextBlock.cs
Normal file
101
src/Snap.Hutao/Snap.Hutao/Control/Text/DescriptionTextBlock.cs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
// some part of this file came from:
|
||||||
|
// https://github.com/xunkong/desktop/tree/main/src/Desktop/Desktop/Pages/CharacterInfoPage.xaml.cs
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Documents;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
using Snap.Hutao.Core;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Windows.UI;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Control.Text;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 专用于呈现描述文本的文本块
|
||||||
|
/// </summary>
|
||||||
|
public class DescriptionTextBlock : ContentControl
|
||||||
|
{
|
||||||
|
private static readonly DependencyProperty DescriptionProperty =
|
||||||
|
Property<DescriptionTextBlock>.Depend(nameof(Description), string.Empty, OnDescriptionChanged);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的呈现描述文本的文本块
|
||||||
|
/// </summary>
|
||||||
|
public DescriptionTextBlock()
|
||||||
|
{
|
||||||
|
Content = new TextBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 可绑定的描述文本
|
||||||
|
/// </summary>
|
||||||
|
public string Description
|
||||||
|
{
|
||||||
|
get => (string)GetValue(DescriptionProperty);
|
||||||
|
set => SetValue(DescriptionProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnDescriptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
TextBlock text = (TextBlock)((DescriptionTextBlock)d).Content;
|
||||||
|
text.Inlines.Clear();
|
||||||
|
|
||||||
|
string[] lines = ((string)e.NewValue).Split('\n');
|
||||||
|
foreach (string line in lines)
|
||||||
|
{
|
||||||
|
MatchCollection matches = Regex.Matches(line, @"<color=([^>]+)>([^<]+)</color>");
|
||||||
|
string left, right = line;
|
||||||
|
|
||||||
|
foreach (Match match in matches)
|
||||||
|
{
|
||||||
|
string matched = match.Groups[0].Value;
|
||||||
|
int matchPosition = right.IndexOf(matched);
|
||||||
|
left = right[..matchPosition];
|
||||||
|
right = right[(matchPosition + matched.Length)..];
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(left))
|
||||||
|
{
|
||||||
|
text.Inlines.Add(new Run { Text = left });
|
||||||
|
}
|
||||||
|
|
||||||
|
string hexColor = match.Groups[1].Value;
|
||||||
|
string content = match.Groups[2].Value;
|
||||||
|
|
||||||
|
text.Inlines.Add(new Run { Text = content, Foreground = GetSolidColorBrush(hexColor[..7]) });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(right))
|
||||||
|
{
|
||||||
|
if (right.Contains("<i>"))
|
||||||
|
{
|
||||||
|
string italic = right.Replace("<i>", string.Empty).Replace("</i>", string.Empty);
|
||||||
|
text.Inlines.Add(new Run { Text = italic, FontStyle = Windows.UI.Text.FontStyle.Italic });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text.Inlines.Add(new Run { Text = right });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text.Inlines.Add(new LineBreak());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.Inlines.LastOrDefault() is LineBreak newline)
|
||||||
|
{
|
||||||
|
text.Inlines.Remove(newline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SolidColorBrush GetSolidColorBrush(string hex)
|
||||||
|
{
|
||||||
|
hex = hex.Replace("#", string.Empty);
|
||||||
|
byte r = (byte)Convert.ToUInt32(hex.Substring(0, 2), 16);
|
||||||
|
byte g = (byte)Convert.ToUInt32(hex.Substring(2, 2), 16);
|
||||||
|
byte b = (byte)Convert.ToUInt32(hex.Substring(4, 2), 16);
|
||||||
|
return new SolidColorBrush(Color.FromArgb(255, r, g, b));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ namespace Snap.Hutao.Extension;
|
|||||||
public static class EnumerableExtensions
|
public static class EnumerableExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将源转换为仅包含单个元素的集合
|
/// 将源转换为仅包含单个元素的枚举
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TSource">源的类型</typeparam>
|
/// <typeparam name="TSource">源的类型</typeparam>
|
||||||
/// <param name="source">源</param>
|
/// <param name="source">源</param>
|
||||||
@@ -79,51 +79,6 @@ public static class EnumerableExtensions
|
|||||||
return source.FirstOrDefault(predicate) ?? source.FirstOrDefault();
|
return source.FirstOrDefault(predicate) ?? source.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将二维可枚举对象一维化
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TSource">源类型</typeparam>
|
|
||||||
/// <param name="source">源</param>
|
|
||||||
/// <returns>扁平的对象</returns>
|
|
||||||
public static IEnumerable<TSource> Flatten<TSource>(this IEnumerable<IEnumerable<TSource>> source)
|
|
||||||
{
|
|
||||||
return source.SelectMany(x => x);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 对集合中的每个物品执行指定的操作
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TSource">集合类型</typeparam>
|
|
||||||
/// <param name="source">集合</param>
|
|
||||||
/// <param name="action">指定的操作</param>
|
|
||||||
/// <returns>修改后的集合</returns>
|
|
||||||
public static IEnumerable<TSource> ForEach<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
|
|
||||||
{
|
|
||||||
foreach (TSource item in source)
|
|
||||||
{
|
|
||||||
action(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 对集合中的每个物品执行指定的操作
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TSource">集合类型</typeparam>
|
|
||||||
/// <param name="source">集合</param>
|
|
||||||
/// <param name="func">指定的操作</param>
|
|
||||||
/// <returns>修改后的集合</returns>
|
|
||||||
public static async Task<IEnumerable<TSource>> ForEachAsync<TSource>(this IEnumerable<TSource> source, Func<TSource, Task> func)
|
|
||||||
{
|
|
||||||
foreach (TSource item in source)
|
|
||||||
{
|
|
||||||
await func(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表示一个对 <see cref="TItem"/> 类型的计数器
|
/// 表示一个对 <see cref="TItem"/> 类型的计数器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using WinRT.Interop;
|
|||||||
namespace Snap.Hutao.Factory;
|
namespace Snap.Hutao.Factory;
|
||||||
|
|
||||||
/// <inheritdoc cref="IPickerFactory"/>
|
/// <inheritdoc cref="IPickerFactory"/>
|
||||||
[Injection(InjectAs.Transient)]
|
[Injection(InjectAs.Transient, typeof(IPickerFactory))]
|
||||||
internal class PickerFactory : IPickerFactory
|
internal class PickerFactory : IPickerFactory
|
||||||
{
|
{
|
||||||
private readonly MainWindow mainWindow;
|
private readonly MainWindow mainWindow;
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ internal static class IocConfiguration
|
|||||||
public static IServiceCollection AddDatebase(this IServiceCollection services)
|
public static IServiceCollection AddDatebase(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
MyDocumentContext myDocument = new(new());
|
MyDocumentContext myDocument = new(new());
|
||||||
myDocument.EnsureDirectory();
|
|
||||||
|
|
||||||
string dbFile = myDocument.Locate("Userdata.db");
|
string dbFile = myDocument.Locate("Userdata.db");
|
||||||
string sqlConnectionString = $"Data Source={dbFile}";
|
string sqlConnectionString = $"Data Source={dbFile}";
|
||||||
|
|||||||
@@ -66,12 +66,6 @@ public class User : Observable
|
|||||||
private set => Set(ref selectedUserGameRole, value);
|
private set => Set(ref selectedUserGameRole, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 移除命令
|
|
||||||
/// </summary>
|
|
||||||
[NotMapped]
|
|
||||||
public ICommand? RemoveCommand { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 复制Cookie命令
|
/// 复制Cookie命令
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -126,7 +120,6 @@ public class User : Observable
|
|||||||
}
|
}
|
||||||
|
|
||||||
CopyCookieCommand = new RelayCommand(CopyCookie);
|
CopyCookieCommand = new RelayCommand(CopyCookie);
|
||||||
Must.NotNull(RemoveCommand!);
|
|
||||||
|
|
||||||
UserInfo = await userClient
|
UserInfo = await userClient
|
||||||
.GetUserFullInfoAsync(this, token)
|
.GetUserFullInfoAsync(this, token)
|
||||||
|
|||||||
@@ -18,5 +18,5 @@ public class DescParam
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 参数
|
/// 参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<LevelParam<int>> Parameters { get; set; } = default!;
|
public IEnumerable<LevelParam<int, double>> Parameters { get; set; } = default!;
|
||||||
}
|
}
|
||||||
@@ -40,26 +40,27 @@ public class FetterInfo
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int BirthDay { get; set; }
|
public int BirthDay { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 格式化的生日日期
|
||||||
|
/// </summary>
|
||||||
|
public string BirthFormatted
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return $"{BirthMonth} 月 {BirthDay} 日";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 神之眼属性-前
|
/// 神之眼属性-前
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string VisionBefore { get; set; } = default!;
|
public string VisionBefore { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 神之眼属性-后
|
|
||||||
/// </summary>
|
|
||||||
public string VisionAfter { get; set; } = default!;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 命座-前
|
/// 命座-前
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ConstellationBefore { get; set; } = default!;
|
public string ConstellationBefore { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 命座-后
|
|
||||||
/// </summary>
|
|
||||||
public string ConstellationAfter { get; set; } = default!;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 中文CV
|
/// 中文CV
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Snap.Hutao.Model.Metadata.Converter;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 角色头像转换器
|
/// 角色头像转换器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class IconConverter : IValueConverter
|
internal class AvatarIconConverter : IValueConverter
|
||||||
{
|
{
|
||||||
private const string BaseUrl = "https://upload-bbs.mihoyo.com/game_record/genshin/character_icon/{0}.png";
|
private const string BaseUrl = "https://upload-bbs.mihoyo.com/game_record/genshin/character_icon/{0}.png";
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ namespace Snap.Hutao.Model.Metadata.Converter;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 角色侧面头像转换器
|
/// 角色侧面头像转换器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class SideIconConverter : IValueConverter
|
internal class AvatarSideIconConverter : IValueConverter
|
||||||
{
|
{
|
||||||
private const string BaseUrl = "https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/{0}.png";
|
private const string BaseUrl = "https://upload-bbs.mihoyo.com/game_record/genshin/character_side_icon/{0}.png";
|
||||||
|
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using Snap.Hutao.Model.Metadata.Avatar;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 描述参数解析器
|
||||||
|
/// </summary>
|
||||||
|
internal class DescParamDescriptor : IValueConverter
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
DescParam descParam = (DescParam)value;
|
||||||
|
IEnumerable<DescFormat> parsedDescriptions = descParam.Descriptions.Select(desc =>
|
||||||
|
{
|
||||||
|
string[] parts = desc.Split('|', 2);
|
||||||
|
return new DescFormat(parts[0], parts[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
IList<IList<string>> parameters = descParam.Parameters
|
||||||
|
.Select(param =>
|
||||||
|
{
|
||||||
|
IList<string> parameters = GetFormattedParameters(parsedDescriptions, param.Parameters);
|
||||||
|
parameters.Insert(0, param.Level.ToString());
|
||||||
|
return parameters;
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
List<string> descList = parsedDescriptions.Select(p => p.Description).ToList();
|
||||||
|
descList.Insert(0, "等级");
|
||||||
|
return new DescParamInternal(descList, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
throw Must.NeverHappen();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IList<string> GetFormattedParameters(IEnumerable<DescFormat> formats, IList<double> param)
|
||||||
|
{
|
||||||
|
List<string> 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 string EvaluateMatch(Match match, IList<double> param)
|
||||||
|
{
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
string[] parts = match.Value[1..^1].Split(':', 2);
|
||||||
|
|
||||||
|
int index = int.Parse(parts[0][5..]) - 1;
|
||||||
|
if (parts[1] == "I")
|
||||||
|
{
|
||||||
|
return ((int)param[index]).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts[1] == "F1P")
|
||||||
|
{
|
||||||
|
return string.Format("{0:P1}", param[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts[1] == "F2P")
|
||||||
|
{
|
||||||
|
return string.Format("{0:P2}", param[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Format($"{{0:{parts[1]}}}", param[index]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DescFormat
|
||||||
|
{
|
||||||
|
public DescFormat(string description, string format)
|
||||||
|
{
|
||||||
|
Description = description;
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
public string Format { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DescParamInternal
|
||||||
|
{
|
||||||
|
public DescParamInternal(IList<string> descriptions, IList<IList<string>> parameters)
|
||||||
|
{
|
||||||
|
Descriptions = descriptions;
|
||||||
|
Parameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IList<string> Descriptions { get; set; }
|
||||||
|
|
||||||
|
public IList<IList<string>> Parameters { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 元素名称图标转换器
|
||||||
|
/// </summary>
|
||||||
|
internal class ElementNameIconConverter : IValueConverter
|
||||||
|
{
|
||||||
|
private const string BaseUrl = "https://static.snapgenshin.com/IconElement/UI_Icon_Element_{0}.png";
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
string element = (string)value switch
|
||||||
|
{
|
||||||
|
"雷" => "Electric",
|
||||||
|
"火" => "Fire",
|
||||||
|
"草" => "Grass",
|
||||||
|
"冰" => "Ice",
|
||||||
|
"岩" => "Rock",
|
||||||
|
"水" => "Water",
|
||||||
|
"风" => "Wind",
|
||||||
|
_ => throw Must.NeverHappen(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Uri(string.Format(BaseUrl, element));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
throw Must.NeverHappen();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,4 +30,4 @@ internal class QualityConverter : IValueConverter
|
|||||||
{
|
{
|
||||||
throw Must.NeverHappen();
|
throw Must.NeverHappen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Data;
|
||||||
|
using Snap.Hutao.Model.Intrinsic;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Metadata.Converter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 元素名称图标转换器
|
||||||
|
/// </summary>
|
||||||
|
internal class WeaponTypeIconConverter : IValueConverter
|
||||||
|
{
|
||||||
|
private const string BaseUrl = "https://static.snapgenshin.com/Skill/Skill_A_{0}.png";
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public object Convert(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
string element = (WeaponType)value switch
|
||||||
|
{
|
||||||
|
WeaponType.WEAPON_SWORD_ONE_HAND => "01",
|
||||||
|
WeaponType.WEAPON_BOW => "02",
|
||||||
|
WeaponType.WEAPON_POLE => "03",
|
||||||
|
WeaponType.WEAPON_CLAYMORE => "04",
|
||||||
|
WeaponType.WEAPON_CATALYST => "Catalyst_MD",
|
||||||
|
_ => throw Must.NeverHappen(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Uri(string.Format(BaseUrl, element));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||||
|
{
|
||||||
|
throw Must.NeverHappen();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,8 @@ namespace Snap.Hutao.Model.Metadata;
|
|||||||
/// 等级与参数
|
/// 等级与参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TLevel">等级的类型</typeparam>
|
/// <typeparam name="TLevel">等级的类型</typeparam>
|
||||||
public class LevelParam<TLevel>
|
/// <typeparam name="TParam">参数的类型</typeparam>
|
||||||
|
public class LevelParam<TLevel, TParam>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 等级
|
/// 等级
|
||||||
@@ -19,5 +20,5 @@ public class LevelParam<TLevel>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 参数
|
/// 参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<double> Parameters { get; set; } = default!;
|
public IList<TParam> Parameters { get; set; } = default!;
|
||||||
}
|
}
|
||||||
@@ -19,5 +19,5 @@ public class PropertyInfo
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 参数
|
/// 参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<LevelParam<string>> Parameters { get; set; } = default!;
|
public IEnumerable<LevelParam<string, double>> Parameters { get; set; } = default!;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<Identity
|
<Identity
|
||||||
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
|
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
|
||||||
Publisher="CN=DGP Studio"
|
Publisher="CN=DGP Studio"
|
||||||
Version="1.0.10.0" />
|
Version="1.0.11.0" />
|
||||||
|
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>胡桃</DisplayName>
|
<DisplayName>胡桃</DisplayName>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public static class Program
|
|||||||
|
|
||||||
Application.Start(p =>
|
Application.Start(p =>
|
||||||
{
|
{
|
||||||
DispatcherQueueSynchronizationContext context = new(DispatcherQueue.GetForCurrentThread());
|
SynchronizationContext context = new DispatcherQueueSynchronizationContext(DispatcherQueue.GetForCurrentThread());
|
||||||
SynchronizationContext.SetSynchronizationContext(context);
|
SynchronizationContext.SetSynchronizationContext(context);
|
||||||
_ = new App();
|
_ = new App();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -69,11 +69,4 @@ internal interface IMetadataService
|
|||||||
/// <param name="token">取消令牌</param>
|
/// <param name="token">取消令牌</param>
|
||||||
/// <returns>武器列表</returns>
|
/// <returns>武器列表</returns>
|
||||||
ValueTask<IEnumerable<Weapon>> GetWeaponsAsync(CancellationToken token = default);
|
ValueTask<IEnumerable<Weapon>> GetWeaponsAsync(CancellationToken token = default);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 异步更新元数据
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="token">取消令牌</param>
|
|
||||||
/// <returns>更新是否完成</returns>
|
|
||||||
Task<bool> UpdateMetadataAsync(CancellationToken token = default);
|
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,7 @@ internal class MetadataService : IMetadataService, IMetadataInitializer, ISuppor
|
|||||||
{
|
{
|
||||||
private const string MetaAPIHost = "http://hutao-metadata.snapgenshin.com";
|
private const string MetaAPIHost = "http://hutao-metadata.snapgenshin.com";
|
||||||
private const string MetaFileName = "Meta.json";
|
private const string MetaFileName = "Meta.json";
|
||||||
|
|
||||||
private readonly IInfoBarService infoBarService;
|
private readonly IInfoBarService infoBarService;
|
||||||
private readonly HttpClient httpClient;
|
private readonly HttpClient httpClient;
|
||||||
private readonly FileSystemContext metadataContext;
|
private readonly FileSystemContext metadataContext;
|
||||||
@@ -78,40 +79,14 @@ internal class MetadataService : IMetadataService, IMetadataInitializer, ISuppor
|
|||||||
public async Task InitializeInternalAsync(CancellationToken token = default)
|
public async Task InitializeInternalAsync(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
logger.LogInformation("元数据初始化开始");
|
logger.LogInformation("元数据初始化开始");
|
||||||
metadataContext.EnsureDirectory();
|
|
||||||
|
|
||||||
IsInitialized = await UpdateMetadataAsync(token)
|
IsInitialized = await TryUpdateMetadataAsync(token)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
initializeCompletionSource.SetResult();
|
initializeCompletionSource.SetResult();
|
||||||
logger.LogInformation("元数据初始化完成");
|
logger.LogInformation("元数据初始化完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public async Task<bool> UpdateMetadataAsync(CancellationToken token = default)
|
|
||||||
{
|
|
||||||
IDictionary<string, string>? metaMd5Map = await httpClient
|
|
||||||
.GetFromJsonAsync<IDictionary<string, string>>($"{MetaAPIHost}/{MetaFileName}", options, token)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (metaMd5Map is null)
|
|
||||||
{
|
|
||||||
infoBarService.Error("元数据校验文件解析失败");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await CheckMetadataAsync(metaMd5Map, token).ConfigureAwait(false);
|
|
||||||
|
|
||||||
using (FileStream metaFileStream = metadataContext.Create(MetaFileName))
|
|
||||||
{
|
|
||||||
await JsonSerializer
|
|
||||||
.SerializeAsync(metaFileStream, metaMd5Map, options, token)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ValueTask<IEnumerable<AchievementGoal>> GetAchievementGoalsAsync(CancellationToken token = default)
|
public ValueTask<IEnumerable<AchievementGoal>> GetAchievementGoalsAsync(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
@@ -154,34 +129,30 @@ internal class MetadataService : IMetadataService, IMetadataInitializer, ISuppor
|
|||||||
return GetMetadataAsync<IEnumerable<Weapon>>("Weapon", token);
|
return GetMetadataAsync<IEnumerable<Weapon>>("Weapon", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ValueTask<T> GetMetadataAsync<T>(string fileName, CancellationToken token)
|
private async Task<bool> TryUpdateMetadataAsync(CancellationToken token = default)
|
||||||
where T : class
|
|
||||||
{
|
{
|
||||||
Verify.Operation(IsInitialized, "元数据服务尚未初始化,或初始化失败");
|
// download meta check file
|
||||||
string cacheKey = $"{nameof(MetadataService)}.Cache.{fileName}";
|
IDictionary<string, string>? metaMd5Map = await httpClient
|
||||||
|
.GetFromJsonAsync<IDictionary<string, string>>($"{MetaAPIHost}/{MetaFileName}", options, token)
|
||||||
if (memoryCache.TryGetValue(cacheKey, out object? value))
|
|
||||||
{
|
|
||||||
return Must.NotNull((value as T)!);
|
|
||||||
}
|
|
||||||
|
|
||||||
T? result = await JsonSerializer
|
|
||||||
.DeserializeAsync<T>(metadataContext.OpenRead($"{fileName}.json"), options, token)
|
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
return memoryCache.Set(cacheKey, Must.NotNull(result!));
|
if (metaMd5Map is null)
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<string> GetFileMd5Async(string fileFullName, CancellationToken token)
|
|
||||||
{
|
|
||||||
using (FileStream stream = metadataContext.OpenRead(fileFullName))
|
|
||||||
{
|
{
|
||||||
byte[] bytes = await MD5.Create()
|
infoBarService.Error("元数据校验文件解析失败");
|
||||||
.ComputeHashAsync(stream, token)
|
return false;
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
return Convert.ToHexString(bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await CheckMetadataAsync(metaMd5Map, token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// save metadataFile
|
||||||
|
using (FileStream metaFileStream = metadataContext.Create(MetaFileName))
|
||||||
|
{
|
||||||
|
await JsonSerializer
|
||||||
|
.SerializeAsync(metaFileStream, metaMd5Map, options, token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -193,6 +164,7 @@ internal class MetadataService : IMetadataService, IMetadataInitializer, ISuppor
|
|||||||
/// <returns>令牌</returns>
|
/// <returns>令牌</returns>
|
||||||
private async Task CheckMetadataAsync(IDictionary<string, string> metaMd5Map, CancellationToken token)
|
private async Task CheckMetadataAsync(IDictionary<string, string> metaMd5Map, CancellationToken token)
|
||||||
{
|
{
|
||||||
|
// TODO: Make this foreach async to imporve speed
|
||||||
// enumerate files and compare md5
|
// enumerate files and compare md5
|
||||||
foreach ((string fileName, string md5) in metaMd5Map)
|
foreach ((string fileName, string md5) in metaMd5Map)
|
||||||
{
|
{
|
||||||
@@ -215,6 +187,18 @@ internal class MetadataService : IMetadataService, IMetadataInitializer, ISuppor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<string> GetFileMd5Async(string fileFullName, CancellationToken token)
|
||||||
|
{
|
||||||
|
using (FileStream stream = metadataContext.OpenRead(fileFullName))
|
||||||
|
{
|
||||||
|
byte[] bytes = await MD5.Create()
|
||||||
|
.ComputeHashAsync(stream, token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return Convert.ToHexString(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task DownloadMetadataAsync(string fileFullName, CancellationToken token)
|
private async Task DownloadMetadataAsync(string fileFullName, CancellationToken token)
|
||||||
{
|
{
|
||||||
Stream sourceStream = await httpClient
|
Stream sourceStream = await httpClient
|
||||||
@@ -238,4 +222,22 @@ internal class MetadataService : IMetadataService, IMetadataInitializer, ISuppor
|
|||||||
|
|
||||||
logger.LogInformation("{file} 下载完成", fileFullName);
|
logger.LogInformation("{file} 下载完成", fileFullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async ValueTask<T> GetMetadataAsync<T>(string fileName, CancellationToken token)
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
Verify.Operation(IsInitialized, "元数据服务尚未初始化,或初始化失败");
|
||||||
|
string cacheKey = $"{nameof(MetadataService)}.Cache.{fileName}";
|
||||||
|
|
||||||
|
if (memoryCache.TryGetValue(cacheKey, out object? value))
|
||||||
|
{
|
||||||
|
return Must.NotNull((value as T)!);
|
||||||
|
}
|
||||||
|
|
||||||
|
T? result = await JsonSerializer
|
||||||
|
.DeserializeAsync<T>(metadataContext.OpenRead($"{fileName}.json"), options, token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return memoryCache.Set(cacheKey, Must.NotNull(result!));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ namespace Snap.Hutao.Service.Navigation;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 为 <see cref="NavigationExtra"/> 提供抽象接口
|
/// 为 <see cref="NavigationExtra"/> 提供抽象接口
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface INavigationExtra
|
public interface INavigationData
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据
|
/// 数据
|
||||||
@@ -6,7 +6,7 @@ namespace Snap.Hutao.Service.Navigation;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 导航额外信息
|
/// 导航额外信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class NavigationExtra : INavigationExtra, INavigationAwaiter
|
public class NavigationExtra : INavigationData, INavigationAwaiter
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 任务完成源
|
/// 任务完成源
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Snap.Hutao.Core.Setting;
|
|||||||
using Snap.Hutao.Service.Abstraction;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
using Snap.Hutao.View.Helper;
|
using Snap.Hutao.View.Helper;
|
||||||
using Snap.Hutao.View.Page;
|
using Snap.Hutao.View.Page;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Navigation;
|
namespace Snap.Hutao.Service.Navigation;
|
||||||
@@ -85,8 +86,7 @@ internal class NavigationService : INavigationService
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NavigationViewItem? target = NavigationView.MenuItems
|
NavigationViewItem? target = EnumerateMenuItems(NavigationView.MenuItems)
|
||||||
.OfType<NavigationViewItem>()
|
|
||||||
.SingleOrDefault(menuItem => NavHelper.GetNavigateTo(menuItem) == pageType);
|
.SingleOrDefault(menuItem => NavHelper.GetNavigateTo(menuItem) == pageType);
|
||||||
|
|
||||||
NavigationView.SelectedItem = target;
|
NavigationView.SelectedItem = target;
|
||||||
@@ -163,6 +163,24 @@ internal class NavigationService : INavigationService
|
|||||||
NavigationView.IsPaneOpen = LocalSetting.GetValueType(SettingKeys.IsNavPaneOpen, true);
|
NavigationView.IsPaneOpen = LocalSetting.GetValueType(SettingKeys.IsNavPaneOpen, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 遍历所有子菜单项
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="items">项列表</param>
|
||||||
|
/// <returns>枚举器</returns>
|
||||||
|
private IEnumerable<NavigationViewItem> EnumerateMenuItems(IList<object> items)
|
||||||
|
{
|
||||||
|
foreach (NavigationViewItem item in items.OfType<NavigationViewItem>())
|
||||||
|
{
|
||||||
|
yield return item;
|
||||||
|
|
||||||
|
foreach (NavigationViewItem subItem in EnumerateMenuItems(item.MenuItems))
|
||||||
|
{
|
||||||
|
yield return subItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
|
private void OnItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
|
||||||
{
|
{
|
||||||
Selected = NavigationView?.SelectedItem as NavigationViewItem;
|
Selected = NavigationView?.SelectedItem as NavigationViewItem;
|
||||||
@@ -170,8 +188,12 @@ internal class NavigationService : INavigationService
|
|||||||
? typeof(SettingPage)
|
? typeof(SettingPage)
|
||||||
: NavHelper.GetNavigateTo(Selected);
|
: NavHelper.GetNavigateTo(Selected);
|
||||||
|
|
||||||
INavigationAwaiter navigationAwaiter = new NavigationExtra(NavHelper.GetExtraData(Selected));
|
// ignore item that doesn't have nav type specified
|
||||||
Navigate(Must.NotNull(targetType!), navigationAwaiter, false);
|
if (targetType != null)
|
||||||
|
{
|
||||||
|
INavigationAwaiter navigationAwaiter = new NavigationExtra(NavHelper.GetExtraData(Selected));
|
||||||
|
Navigate(targetType, navigationAwaiter, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args)
|
private void OnBackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public interface IUserService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="removeCommand">移除用户命令</param>
|
/// <param name="removeCommand">移除用户命令</param>
|
||||||
/// <returns>准备完成的用户信息枚举</returns>
|
/// <returns>准备完成的用户信息枚举</returns>
|
||||||
Task<ObservableCollection<User>> GetInitializedUsersAsync(ICommand removeCommand);
|
Task<ObservableCollection<User>> GetInitializedUsersAsync();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 异步添加用户
|
/// 异步添加用户
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ internal class UserService : IUserService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<ObservableCollection<User>> GetInitializedUsersAsync(ICommand removeCommand)
|
public async Task<ObservableCollection<User>> GetInitializedUsersAsync()
|
||||||
{
|
{
|
||||||
if (cachedUsers == null)
|
if (cachedUsers == null)
|
||||||
{
|
{
|
||||||
@@ -133,7 +133,6 @@ internal class UserService : IUserService
|
|||||||
|
|
||||||
foreach (User user in cachedUsers)
|
foreach (User user in cachedUsers)
|
||||||
{
|
{
|
||||||
user.RemoveCommand = removeCommand;
|
|
||||||
await user
|
await user
|
||||||
.InitializeAsync(userClient, userGameRoleClient)
|
.InitializeAsync(userClient, userGameRoleClient)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|||||||
@@ -41,7 +41,6 @@
|
|||||||
<None Remove="View\Page\AnnouncementContentPage.xaml" />
|
<None Remove="View\Page\AnnouncementContentPage.xaml" />
|
||||||
<None Remove="View\Page\AnnouncementPage.xaml" />
|
<None Remove="View\Page\AnnouncementPage.xaml" />
|
||||||
<None Remove="View\Page\SettingPage.xaml" />
|
<None Remove="View\Page\SettingPage.xaml" />
|
||||||
<None Remove="View\Page\WelcomePage.xaml" />
|
|
||||||
<None Remove="View\Page\WikiAvatarPage.xaml" />
|
<None Remove="View\Page\WikiAvatarPage.xaml" />
|
||||||
<None Remove="View\TitleView.xaml" />
|
<None Remove="View\TitleView.xaml" />
|
||||||
<None Remove="View\UserView.xaml" />
|
<None Remove="View\UserView.xaml" />
|
||||||
@@ -154,11 +153,6 @@
|
|||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Page Update="View\Page\WelcomePage.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Update="View\Dialog\UserDialog.xaml">
|
<Page Update="View\Dialog\UserDialog.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
xmlns:shci="using:Snap.Hutao.Control.Image"
|
xmlns:shci="using:Snap.Hutao.Control.Image"
|
||||||
xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter"
|
xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Width="64"
|
Width="80"
|
||||||
Height="64">
|
Height="80">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<shmmc:IconConverter x:Key="IconConverter"/>
|
<shmmc:AvatarIconConverter x:Key="IconConverter"/>
|
||||||
<shmmc:QualityConverter x:Key="QualityConverter"/>
|
<shmmc:QualityConverter x:Key="QualityConverter"/>
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<Grid>
|
<Grid>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Snap.Hutao.View.Helper;
|
|||||||
public sealed class NavHelper
|
public sealed class NavHelper
|
||||||
{
|
{
|
||||||
private static readonly DependencyProperty NavigateToProperty = Property<NavHelper>.Attach<Type>("NavigateTo");
|
private static readonly DependencyProperty NavigateToProperty = Property<NavHelper>.Attach<Type>("NavigateTo");
|
||||||
private static readonly DependencyProperty ExtraDataProperty = Property<NavHelper>.Attach<INavigationExtra>("ExtraData");
|
private static readonly DependencyProperty ExtraDataProperty = Property<NavHelper>.Attach<INavigationData>("ExtraData");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取导航项的目标页面类型
|
/// 获取导航项的目标页面类型
|
||||||
@@ -41,9 +41,9 @@ public sealed class NavHelper
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">待获取的导航项</param>
|
/// <param name="item">待获取的导航项</param>
|
||||||
/// <returns>目标页面类型的额外数据</returns>
|
/// <returns>目标页面类型的额外数据</returns>
|
||||||
public static INavigationExtra? GetExtraData(NavigationViewItem? item)
|
public static INavigationData? GetExtraData(NavigationViewItem? item)
|
||||||
{
|
{
|
||||||
return item?.GetValue(ExtraDataProperty) as INavigationExtra;
|
return item?.GetValue(ExtraDataProperty) as INavigationData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -51,7 +51,7 @@ public sealed class NavHelper
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">待设置的导航项</param>
|
/// <param name="item">待设置的导航项</param>
|
||||||
/// <param name="value">新的目标页面类型</param>
|
/// <param name="value">新的目标页面类型</param>
|
||||||
public static void SetExtraData(NavigationViewItem item, INavigationExtra value)
|
public static void SetExtraData(NavigationViewItem item, INavigationData value)
|
||||||
{
|
{
|
||||||
item.SetValue(ExtraDataProperty, value);
|
item.SetValue(ExtraDataProperty, value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,13 +12,12 @@
|
|||||||
<Thickness x:Key="NavigationViewContentMargin">0,48,0,0</Thickness>
|
<Thickness x:Key="NavigationViewContentMargin">0,48,0,0</Thickness>
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<Grid>
|
<Grid>
|
||||||
<!-- x:Bind can't get property update here seems like a WinUI 3 bug-->
|
|
||||||
<NavigationView
|
<NavigationView
|
||||||
x:Name="NavView"
|
x:Name="NavView"
|
||||||
CompactPaneLength="48"
|
CompactPaneLength="48"
|
||||||
OpenPaneLength="244"
|
OpenPaneLength="244"
|
||||||
CompactModeThresholdWidth="128"
|
CompactModeThresholdWidth="16"
|
||||||
ExpandedModeThresholdWidth="720"
|
ExpandedModeThresholdWidth="16"
|
||||||
IsPaneOpen="True"
|
IsPaneOpen="True"
|
||||||
IsBackEnabled="{Binding ElementName=ContentFrame,Path=CanGoBack}">
|
IsBackEnabled="{Binding ElementName=ContentFrame,Path=CanGoBack}">
|
||||||
|
|
||||||
@@ -26,7 +25,8 @@
|
|||||||
|
|
||||||
<NavigationViewItem Content="活动" helper:NavHelper.NavigateTo="page:AnnouncementPage">
|
<NavigationViewItem Content="活动" helper:NavHelper.NavigateTo="page:AnnouncementPage">
|
||||||
<NavigationViewItem.Icon>
|
<NavigationViewItem.Icon>
|
||||||
<BitmapIcon UriSource="ms-appx:///Resource/Icon/UI_BtnIcon_ActivityEntry.png"/>
|
<BitmapIcon
|
||||||
|
UriSource="ms-appx:///Resource/Icon/UI_BtnIcon_ActivityEntry.png"/>
|
||||||
</NavigationViewItem.Icon>
|
</NavigationViewItem.Icon>
|
||||||
</NavigationViewItem>
|
</NavigationViewItem>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
using Snap.Hutao.Control.Cancellable;
|
using Snap.Hutao.Control.Cancellable;
|
||||||
|
using Snap.Hutao.Service.Navigation;
|
||||||
using Snap.Hutao.ViewModel;
|
using Snap.Hutao.ViewModel;
|
||||||
|
|
||||||
namespace Snap.Hutao.View.Page;
|
namespace Snap.Hutao.View.Page;
|
||||||
@@ -19,4 +21,15 @@ public sealed partial class AchievementPage : CancellablePage
|
|||||||
InitializeWith<AchievementViewModel>();
|
InitializeWith<AchievementViewModel>();
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnNavigatedTo(e);
|
||||||
|
|
||||||
|
if (e.Parameter is INavigationData extra)
|
||||||
|
{
|
||||||
|
extra.NotifyNavigationCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,14 +36,14 @@ openInWebview: function(url){ location.href = url }}";
|
|||||||
{
|
{
|
||||||
base.OnNavigatedTo(e);
|
base.OnNavigatedTo(e);
|
||||||
|
|
||||||
if (e.Parameter is INavigationExtra extra)
|
if (e.Parameter is INavigationData extra)
|
||||||
{
|
{
|
||||||
targetContent = extra.Data as string;
|
targetContent = extra.Data as string;
|
||||||
LoadAnnouncementAsync(extra).SafeForget();
|
LoadAnnouncementAsync(extra).SafeForget();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadAnnouncementAsync(INavigationExtra extra)
|
private async Task LoadAnnouncementAsync(INavigationData extra)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,9 +27,7 @@
|
|||||||
<shvc:BoolToVisibilityRevertConverter x:Key="BoolToVisibilityRevertConverter"/>
|
<shvc:BoolToVisibilityRevertConverter x:Key="BoolToVisibilityRevertConverter"/>
|
||||||
</shcc:CancellablePage.Resources>
|
</shcc:CancellablePage.Resources>
|
||||||
<Grid>
|
<Grid>
|
||||||
<ScrollViewer
|
<ScrollViewer Padding="0,0,4,0">
|
||||||
Padding="0,0,4,0"
|
|
||||||
Visibility="{Binding OpeningUI.IsWorking,Converter={StaticResource BoolToVisibilityRevertConverter}}">
|
|
||||||
<ItemsControl
|
<ItemsControl
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
ItemsSource="{Binding Announcement.List}"
|
ItemsSource="{Binding Announcement.List}"
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
using Snap.Hutao.Control.Cancellable;
|
using Snap.Hutao.Control.Cancellable;
|
||||||
|
using Snap.Hutao.Service.Navigation;
|
||||||
using Snap.Hutao.ViewModel;
|
using Snap.Hutao.ViewModel;
|
||||||
|
|
||||||
namespace Snap.Hutao.View.Page;
|
namespace Snap.Hutao.View.Page;
|
||||||
@@ -19,4 +21,15 @@ public sealed partial class AnnouncementPage : CancellablePage
|
|||||||
InitializeWith<AnnouncementViewModel>();
|
InitializeWith<AnnouncementViewModel>();
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnNavigatedTo(e);
|
||||||
|
|
||||||
|
if (e.Parameter is INavigationData extra)
|
||||||
|
{
|
||||||
|
extra.NotifyNavigationCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
|
using Snap.Hutao.Service.Navigation;
|
||||||
using Snap.Hutao.ViewModel;
|
using Snap.Hutao.ViewModel;
|
||||||
|
|
||||||
namespace Snap.Hutao.View.Page;
|
namespace Snap.Hutao.View.Page;
|
||||||
@@ -18,4 +20,15 @@ public sealed partial class SettingPage : Microsoft.UI.Xaml.Controls.Page
|
|||||||
DataContext = Ioc.Default.GetRequiredService<SettingViewModel>();
|
DataContext = Ioc.Default.GetRequiredService<SettingViewModel>();
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnNavigatedTo(e);
|
||||||
|
|
||||||
|
if (e.Parameter is INavigationData extra)
|
||||||
|
{
|
||||||
|
extra.NotifyNavigationCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
<Page
|
|
||||||
x:Class="Snap.Hutao.View.Page.WelcomePage"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:local="using:Snap.Hutao.View.Page"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
|
||||||
|
|
||||||
<Grid Padding="12">
|
|
||||||
<TextBlock
|
|
||||||
Text="欢迎使用胡桃"
|
|
||||||
Style="{StaticResource TitleTextBlockStyle}"/>
|
|
||||||
</Grid>
|
|
||||||
</Page>
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Snap.Hutao.View.Page;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 欢迎页
|
|
||||||
/// </summary>
|
|
||||||
public sealed partial class WelcomePage : Microsoft.UI.Xaml.Controls.Page
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 构造一个新的欢迎页
|
|
||||||
/// </summary>
|
|
||||||
public WelcomePage()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,8 @@
|
|||||||
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
|
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
|
||||||
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||||
xmlns:shc="using:Snap.Hutao.Control"
|
xmlns:shc="using:Snap.Hutao.Control"
|
||||||
|
xmlns:shci="using:Snap.Hutao.Control.Image"
|
||||||
|
xmlns:shct="using:Snap.Hutao.Control.Text"
|
||||||
xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter"
|
xmlns:shmmc="using:Snap.Hutao.Model.Metadata.Converter"
|
||||||
xmlns:shvc="using:Snap.Hutao.View.Control"
|
xmlns:shvc="using:Snap.Hutao.View.Control"
|
||||||
xmlns:shv="using:Snap.Hutao.ViewModel"
|
xmlns:shv="using:Snap.Hutao.ViewModel"
|
||||||
@@ -20,42 +22,166 @@
|
|||||||
</mxic:EventTriggerBehavior>
|
</mxic:EventTriggerBehavior>
|
||||||
</mxi:Interaction.Behaviors>
|
</mxi:Interaction.Behaviors>
|
||||||
<Page.Resources>
|
<Page.Resources>
|
||||||
<shmmc:IconConverter x:Key="IconConverter"/>
|
<shmmc:AvatarIconConverter x:Key="AvatarIconConverter"/>
|
||||||
|
<shmmc:AvatarSideIconConverter x:Key="AvatarSideIconConverter"/>
|
||||||
|
<shmmc:ElementNameIconConverter x:Key="ElementNameIconConverter"/>
|
||||||
|
<shmmc:WeaponTypeIconConverter x:Key="WeaponTypeIconConverter"/>
|
||||||
|
|
||||||
<shmmc:FightPropertyConverter x:Key="FightPropertyConverter"/>
|
<shmmc:FightPropertyConverter x:Key="FightPropertyConverter"/>
|
||||||
<shmmc:FightPropertyValueFormatter x:Key="FightPropertyValueFormatter"/>
|
<shmmc:FightPropertyValueFormatter x:Key="FightPropertyValueFormatter"/>
|
||||||
|
|
||||||
|
<shmmc:DescParamDescriptor x:Key="DescParamDescriptor"/>
|
||||||
|
|
||||||
<shc:BindingProxy
|
<shc:BindingProxy
|
||||||
x:Key="FightPropertyBindingProxy"
|
x:Key="FightPropertyBindingProxy"
|
||||||
DataContext="{Binding Selected.Property.Properties}"/>
|
DataContext="{Binding Selected.Property.Properties}"/>
|
||||||
|
|
||||||
<SolidColorBrush
|
<DataTemplate x:Key="SkillDataTemplate">
|
||||||
x:Key="SystemControlPageBackgroundChromeLowBrush"
|
<Expander
|
||||||
Color="{StaticResource SystemChromeLowColor}"
|
Margin="16,16,0,0"
|
||||||
Opacity="0.2"/>
|
Header="{Binding Name}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Stretch">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="auto"/>
|
||||||
|
<RowDefinition/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<shct:DescriptionTextBlock
|
||||||
|
Margin="16,16,16,0"
|
||||||
|
Description="{Binding Description}">
|
||||||
|
<shct:DescriptionTextBlock.Resources>
|
||||||
|
<Style
|
||||||
|
TargetType="TextBlock"
|
||||||
|
BasedOn="{StaticResource BodyTextBlockStyle}">
|
||||||
|
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||||
|
</Style>
|
||||||
|
</shct:DescriptionTextBlock.Resources>
|
||||||
|
</shct:DescriptionTextBlock>
|
||||||
|
<ScrollViewer
|
||||||
|
Grid.Row="1"
|
||||||
|
VerticalScrollMode="Disabled"
|
||||||
|
HorizontalScrollBarVisibility="Auto">
|
||||||
|
<Grid
|
||||||
|
Margin="16"
|
||||||
|
DataContext="{Binding Proud,Converter={StaticResource DescParamDescriptor}}">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="auto"/>
|
||||||
|
<RowDefinition/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<ItemsControl
|
||||||
|
Grid.Row="0"
|
||||||
|
ItemsSource="{Binding Descriptions}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<cwuc:UniformGrid
|
||||||
|
ColumnSpacing="16"
|
||||||
|
Columns="{Binding Descriptions.Count}"/>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding}"
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
Style="{StaticResource BaseTextBlockStyle}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
|
||||||
|
<ItemsControl
|
||||||
|
Margin="0,16,0,0"
|
||||||
|
Grid.Row="2"
|
||||||
|
ItemsSource="{Binding Parameters}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<ItemsControl ItemsSource="{Binding}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<cwuc:UniformGrid
|
||||||
|
ColumnSpacing="16"
|
||||||
|
Columns="{Binding Count}"/>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding}"
|
||||||
|
TextTrimming="CharacterEllipsis"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</Grid>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
</Expander>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate x:Key="InherentDataTemplate">
|
||||||
|
<Expander
|
||||||
|
Margin="16,16,0,0"
|
||||||
|
Header="{Binding Name}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Stretch">
|
||||||
|
<shct:DescriptionTextBlock
|
||||||
|
Margin="16"
|
||||||
|
Description="{Binding Description}">
|
||||||
|
<shct:DescriptionTextBlock.Resources>
|
||||||
|
<Style
|
||||||
|
TargetType="TextBlock"
|
||||||
|
BasedOn="{StaticResource BodyTextBlockStyle}">
|
||||||
|
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||||
|
</Style>
|
||||||
|
</shct:DescriptionTextBlock.Resources>
|
||||||
|
</shct:DescriptionTextBlock>
|
||||||
|
</Expander>
|
||||||
|
</DataTemplate>
|
||||||
</Page.Resources>
|
</Page.Resources>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<SplitView
|
<SplitView
|
||||||
IsPaneOpen="True"
|
IsPaneOpen="True"
|
||||||
DisplayMode="Inline"
|
DisplayMode="Inline"
|
||||||
OpenPaneLength="374">
|
OpenPaneLength="200">
|
||||||
|
<SplitView.PaneBackground>
|
||||||
|
<SolidColorBrush Color="{StaticResource CardBackgroundFillColorSecondary}"/>
|
||||||
|
</SplitView.PaneBackground>
|
||||||
<SplitView.Pane>
|
<SplitView.Pane>
|
||||||
<GridView
|
<ListView
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
ItemsSource="{Binding Avatars}"
|
ItemsSource="{Binding Avatars}"
|
||||||
Padding="16,16,0,12"
|
|
||||||
SelectedItem="{Binding Selected,Mode=TwoWay}">
|
SelectedItem="{Binding Selected,Mode=TwoWay}">
|
||||||
<GridView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<shvc:ItemIcon
|
<Grid>
|
||||||
Quality="{Binding Quality,Mode=OneWay}"
|
<Grid.ColumnDefinitions>
|
||||||
Icon="{Binding Icon,Converter={StaticResource IconConverter},Mode=OneWay}"/>
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<shci:CachedImage
|
||||||
|
Grid.Column="0"
|
||||||
|
Width="48"
|
||||||
|
Height="48"
|
||||||
|
Margin="0,0,12,12"
|
||||||
|
Source="{Binding SideIcon,Converter={StaticResource AvatarSideIconConverter},Mode=OneWay}"/>
|
||||||
|
<TextBlock
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="12,0,0,0"
|
||||||
|
Text="{Binding Name}"/>
|
||||||
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</GridView.ItemTemplate>
|
</ListView.ItemTemplate>
|
||||||
</GridView>
|
</ListView>
|
||||||
</SplitView.Pane>
|
</SplitView.Pane>
|
||||||
<SplitView.Content>
|
<SplitView.Content>
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel Margin="0,0,16,16">
|
<StackPanel Margin="0,0,16,16">
|
||||||
<!--头图-->
|
<!--简介-->
|
||||||
<Grid
|
<Grid
|
||||||
Margin="16,16,0,16"
|
Margin="16,16,0,16"
|
||||||
VerticalAlignment="Top">
|
VerticalAlignment="Top">
|
||||||
@@ -63,23 +189,130 @@
|
|||||||
<ColumnDefinition Width="auto"/>
|
<ColumnDefinition Width="auto"/>
|
||||||
<ColumnDefinition/>
|
<ColumnDefinition/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<shvc:ItemIcon
|
<StackPanel Grid.Column="0">
|
||||||
Grid.Column="0"
|
<Grid Margin="0,0,0,16" >
|
||||||
Height="100"
|
<Grid.ColumnDefinitions>
|
||||||
Width="100"
|
<ColumnDefinition/>
|
||||||
Quality="{Binding Selected.Quality,Mode=OneWay}"
|
<ColumnDefinition/>
|
||||||
Icon="{Binding Selected.Icon,Converter={StaticResource IconConverter},Mode=OneWay}"/>
|
</Grid.ColumnDefinitions>
|
||||||
|
<shci:CachedImage
|
||||||
|
Grid.Column="0"
|
||||||
|
Width="27.2"
|
||||||
|
Height="27.2"
|
||||||
|
Source="{Binding Selected.FetterInfo.VisionBefore,Converter={StaticResource ElementNameIconConverter}}"/>
|
||||||
|
<shci:CachedImage
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="27.2"
|
||||||
|
Height="27.2"
|
||||||
|
Source="{Binding Selected.Weapon,Converter={StaticResource WeaponTypeIconConverter}}"/>
|
||||||
|
</Grid>
|
||||||
|
<shvc:ItemIcon
|
||||||
|
Height="100"
|
||||||
|
Width="100"
|
||||||
|
Quality="{Binding Selected.Quality,Mode=OneWay}"
|
||||||
|
Icon="{Binding Selected.Icon,Converter={StaticResource AvatarIconConverter},Mode=OneWay}"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Margin="16,0,0,0"
|
Margin="16,0,0,0"
|
||||||
Grid.Column="1">
|
Grid.Column="1">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding Selected.Name}"
|
||||||
|
Style="{StaticResource SubtitleTextBlockStyle}"/>
|
||||||
|
<TextBlock
|
||||||
|
Margin="24,0,0,0"
|
||||||
|
Text="{Binding Selected.FetterInfo.Title}"
|
||||||
|
Style="{StaticResource SubtitleTextBlockStyle}"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{Binding Selected.Name}"
|
Margin="0,12,0,0"
|
||||||
Style="{StaticResource SubtitleTextBlockStyle}"/>
|
Text="{Binding Selected.FetterInfo.Detail}"
|
||||||
<TextBlock
|
TextWrapping="NoWrap"
|
||||||
Margin="0,6,0,0"
|
|
||||||
Text="{Binding Selected.Description}"
|
|
||||||
MaxLines="2"
|
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"/>
|
Style="{StaticResource CaptionTextBlockStyle}"/>
|
||||||
|
|
||||||
|
<cwuc:UniformGrid
|
||||||
|
Columns="4"
|
||||||
|
ColumnSpacing="12"
|
||||||
|
Margin="0,12,0,0">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
Text="所属"
|
||||||
|
Style="{StaticResource BaseTextBlockStyle}"/>
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,6,0,0"
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
Text="{Binding Selected.FetterInfo.Native}"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
Text="命之座"
|
||||||
|
Style="{StaticResource BaseTextBlockStyle}"/>
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,6,0,0"
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
Text="{Binding Selected.FetterInfo.ConstellationBefore}"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
Text="生日"
|
||||||
|
Style="{StaticResource BaseTextBlockStyle}"/>
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,6,0,0"
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
Text="{Binding Selected.FetterInfo.BirthFormatted}"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</cwuc:UniformGrid>
|
||||||
|
|
||||||
|
<cwuc:UniformGrid
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
ColumnSpacing="12"
|
||||||
|
Columns="4">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
Text="汉语 CV"
|
||||||
|
Style="{StaticResource BaseTextBlockStyle}"/>
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,6,0,0"
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
Text="{Binding Selected.FetterInfo.CvChinese}"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
Text="日语 CV"
|
||||||
|
Style="{StaticResource BaseTextBlockStyle}"/>
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,6,0,0"
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
Text="{Binding Selected.FetterInfo.CvJapanese}"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
Text="英语 CV"
|
||||||
|
Style="{StaticResource BaseTextBlockStyle}"/>
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,6,0,0"
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
Text="{Binding Selected.FetterInfo.CvEnglish}"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
Text="韩语 CV"
|
||||||
|
Style="{StaticResource BaseTextBlockStyle}"/>
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,6,0,0"
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
Text="{Binding Selected.FetterInfo.CvKorean}"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</cwuc:UniformGrid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<!--属性-->
|
<!--属性-->
|
||||||
@@ -88,75 +321,209 @@
|
|||||||
Header="基础数值"
|
Header="基础数值"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
HorizontalContentAlignment="Stretch">
|
HorizontalContentAlignment="Stretch">
|
||||||
<Grid Margin="16">
|
<ScrollViewer
|
||||||
|
VerticalScrollMode="Disabled"
|
||||||
|
HorizontalScrollBarVisibility="Auto">
|
||||||
|
<Grid Margin="16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="auto"/>
|
||||||
|
<RowDefinition/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<cwuc:UniformGrid
|
||||||
|
Grid.Row="0"
|
||||||
|
Columns="5"
|
||||||
|
ColumnSpacing="16"
|
||||||
|
DataContext="{Binding Selected.Property}">
|
||||||
|
<TextBlock
|
||||||
|
Style="{StaticResource BaseTextBlockStyle}"
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
Text="等级"/>
|
||||||
|
<TextBlock
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
Style="{StaticResource BaseTextBlockStyle}"
|
||||||
|
Text="{Binding Properties[0], Converter={StaticResource FightPropertyConverter}}"/>
|
||||||
|
<TextBlock
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
Style="{StaticResource BaseTextBlockStyle}"
|
||||||
|
Text="{Binding Properties[1], Converter={StaticResource FightPropertyConverter}}"/>
|
||||||
|
<TextBlock
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
Style="{StaticResource BaseTextBlockStyle}"
|
||||||
|
Text="{Binding Properties[2], Converter={StaticResource FightPropertyConverter}}"/>
|
||||||
|
<TextBlock
|
||||||
|
TextWrapping="NoWrap"
|
||||||
|
Style="{StaticResource BaseTextBlockStyle}"
|
||||||
|
Text="{Binding Properties[3], Converter={StaticResource FightPropertyConverter}}"/>
|
||||||
|
</cwuc:UniformGrid>
|
||||||
|
<ItemsControl
|
||||||
|
Margin="0,16,0,0"
|
||||||
|
Grid.Row="1"
|
||||||
|
ItemsSource="{Binding Selected.Property.Parameters}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<cwuc:UniformGrid
|
||||||
|
Columns="5"
|
||||||
|
ColumnSpacing="16">
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding Level}"/>
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding Parameters[0],
|
||||||
|
Converter={StaticResource FightPropertyValueFormatter},
|
||||||
|
ConverterParameter={Binding DataContext[0],Source={StaticResource FightPropertyBindingProxy}}}"/>
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding Parameters[1],
|
||||||
|
Converter={StaticResource FightPropertyValueFormatter},
|
||||||
|
ConverterParameter={Binding DataContext[1],Source={StaticResource FightPropertyBindingProxy}}}"/>
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding Parameters[2],
|
||||||
|
Converter={StaticResource FightPropertyValueFormatter},
|
||||||
|
ConverterParameter={Binding DataContext[2],Source={StaticResource FightPropertyBindingProxy}}}"/>
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding Parameters[3],
|
||||||
|
Converter={StaticResource FightPropertyValueFormatter},
|
||||||
|
ConverterParameter={Binding DataContext[3],Source={StaticResource FightPropertyBindingProxy}}}"/>
|
||||||
|
</cwuc:UniformGrid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</Grid>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
</Expander>
|
||||||
|
<TextBlock Text="天赋" Style="{StaticResource BaseTextBlockStyle}" Margin="16,16,0,0"/>
|
||||||
|
<ItemsControl
|
||||||
|
ItemsSource="{Binding Selected.SkillDepot.Skills}"
|
||||||
|
ItemTemplate="{StaticResource SkillDataTemplate}"/>
|
||||||
|
|
||||||
|
<!--元素爆发-->
|
||||||
|
<Expander
|
||||||
|
Margin="16,16,0,0"
|
||||||
|
Header="{Binding Name}"
|
||||||
|
DataContext="{Binding Selected.SkillDepot.EnergySkill}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Stretch">
|
||||||
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="auto"/>
|
<RowDefinition Height="auto"/>
|
||||||
<RowDefinition/>
|
<RowDefinition/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<cwuc:UniformGrid
|
<shct:DescriptionTextBlock
|
||||||
Grid.Row="0"
|
Margin="16,16,16,0"
|
||||||
Columns="5"
|
Description="{Binding Description}">
|
||||||
DataContext="{Binding Selected.Property}"
|
<shct:DescriptionTextBlock.Resources>
|
||||||
x:Name="PropertyGrid">
|
<Style
|
||||||
<TextBlock
|
TargetType="TextBlock"
|
||||||
Style="{StaticResource BaseTextBlockStyle}"
|
BasedOn="{StaticResource BodyTextBlockStyle}">
|
||||||
TextWrapping="NoWrap"
|
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||||
Text="等级"/>
|
</Style>
|
||||||
<TextBlock
|
</shct:DescriptionTextBlock.Resources>
|
||||||
TextWrapping="NoWrap"
|
</shct:DescriptionTextBlock>
|
||||||
Style="{StaticResource BaseTextBlockStyle}"
|
<ScrollViewer
|
||||||
Text="{Binding Properties[0], Converter={StaticResource FightPropertyConverter}}"/>
|
|
||||||
<TextBlock
|
|
||||||
TextWrapping="NoWrap"
|
|
||||||
Style="{StaticResource BaseTextBlockStyle}"
|
|
||||||
Text="{Binding Properties[1], Converter={StaticResource FightPropertyConverter}}"/>
|
|
||||||
<TextBlock
|
|
||||||
TextWrapping="NoWrap"
|
|
||||||
Style="{StaticResource BaseTextBlockStyle}"
|
|
||||||
Text="{Binding Properties[2], Converter={StaticResource FightPropertyConverter}}"/>
|
|
||||||
<TextBlock
|
|
||||||
TextWrapping="NoWrap"
|
|
||||||
Style="{StaticResource BaseTextBlockStyle}"
|
|
||||||
Text="{Binding Properties[3], Converter={StaticResource FightPropertyConverter}}"/>
|
|
||||||
</cwuc:UniformGrid>
|
|
||||||
<ItemsControl
|
|
||||||
Margin="0,6,0,0"
|
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
ItemsSource="{Binding Selected.Property.Parameters}">
|
VerticalScrollMode="Disabled"
|
||||||
<ItemsControl.ItemTemplate>
|
HorizontalScrollBarVisibility="Auto">
|
||||||
<DataTemplate>
|
<Grid>
|
||||||
<cwuc:UniformGrid
|
<Grid
|
||||||
Columns="5">
|
Grid.Row="1"
|
||||||
<TextBlock
|
Margin="16"
|
||||||
Text="{Binding Level}"/>
|
DataContext="{Binding Proud,Converter={StaticResource DescParamDescriptor}}">
|
||||||
<TextBlock
|
<Grid.RowDefinitions>
|
||||||
Text="{Binding Parameters[0],
|
<RowDefinition Height="auto"/>
|
||||||
Converter={StaticResource FightPropertyValueFormatter},
|
<RowDefinition/>
|
||||||
ConverterParameter={Binding DataContext[0],Source={StaticResource FightPropertyBindingProxy}}}"/>
|
</Grid.RowDefinitions>
|
||||||
<TextBlock
|
<ItemsControl
|
||||||
Text="{Binding Parameters[1],
|
Grid.Row="0"
|
||||||
Converter={StaticResource FightPropertyValueFormatter},
|
ItemsSource="{Binding Descriptions}">
|
||||||
ConverterParameter={Binding DataContext[1],Source={StaticResource FightPropertyBindingProxy}}}"/>
|
<ItemsControl.ItemsPanel>
|
||||||
<TextBlock
|
<ItemsPanelTemplate>
|
||||||
Text="{Binding Parameters[2],
|
<cwuc:UniformGrid
|
||||||
Converter={StaticResource FightPropertyValueFormatter},
|
ColumnSpacing="16"
|
||||||
ConverterParameter={Binding DataContext[2],Source={StaticResource FightPropertyBindingProxy}}}"/>
|
Columns="{Binding Descriptions.Count}"/>
|
||||||
<TextBlock
|
</ItemsPanelTemplate>
|
||||||
Text="{Binding Parameters[3],
|
</ItemsControl.ItemsPanel>
|
||||||
Converter={StaticResource FightPropertyValueFormatter},
|
<ItemsControl.ItemTemplate>
|
||||||
ConverterParameter={Binding DataContext[3],Source={StaticResource FightPropertyBindingProxy}}}"/>
|
<DataTemplate>
|
||||||
</cwuc:UniformGrid>
|
<TextBlock
|
||||||
</DataTemplate>
|
Text="{Binding}"
|
||||||
</ItemsControl.ItemTemplate>
|
TextWrapping="NoWrap"
|
||||||
</ItemsControl>
|
TextTrimming="CharacterEllipsis"
|
||||||
|
Style="{StaticResource BaseTextBlockStyle}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
|
||||||
|
<ItemsControl
|
||||||
|
Margin="0,6,0,0"
|
||||||
|
Grid.Row="2"
|
||||||
|
ItemsSource="{Binding Parameters}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<ItemsControl ItemsSource="{Binding}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<cwuc:UniformGrid
|
||||||
|
ColumnSpacing="16"
|
||||||
|
Columns="{Binding Count}"/>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding}"
|
||||||
|
TextTrimming="CharacterEllipsis"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Expander>
|
</Expander>
|
||||||
|
|
||||||
|
<ItemsControl
|
||||||
|
ItemsSource="{Binding Selected.SkillDepot.Inherents}"
|
||||||
|
ItemTemplate="{StaticResource InherentDataTemplate}"/>
|
||||||
|
|
||||||
|
<TextBlock Text="命之座" Style="{StaticResource BaseTextBlockStyle}" Margin="16,16,0,0"/>
|
||||||
|
|
||||||
|
<ItemsControl
|
||||||
|
ItemsSource="{Binding Selected.SkillDepot.Talents}"
|
||||||
|
ItemTemplate="{StaticResource InherentDataTemplate}"/>
|
||||||
|
|
||||||
|
<TextBlock Text="其他" Style="{StaticResource BaseTextBlockStyle}" Margin="16,16,0,0"/>
|
||||||
|
<!--衣装-->
|
||||||
|
<Expander
|
||||||
|
Margin="16,16,0,0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Stretch"
|
||||||
|
Header="衣装">
|
||||||
|
<ItemsControl
|
||||||
|
ItemsSource="{Binding Selected.Costumes}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<StackPanel Margin="0,0,0,-4">
|
||||||
|
<TextBlock
|
||||||
|
Margin="16,16,16,0"
|
||||||
|
Text="{Binding Name}"/>
|
||||||
|
<TextBlock
|
||||||
|
Margin="16,8,16,16"
|
||||||
|
Text="{Binding Description}"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"/>
|
||||||
|
<MenuFlyoutSeparator Margin="16,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</Expander>
|
||||||
<!--资料-->
|
<!--资料-->
|
||||||
<Expander
|
<Expander
|
||||||
Margin="16,16,0,0"
|
Margin="16,16,0,0"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
HorizontalContentAlignment="Stretch"
|
HorizontalContentAlignment="Stretch"
|
||||||
Header="语音">
|
Header="资料">
|
||||||
<ItemsControl
|
<ItemsControl
|
||||||
ItemsSource="{Binding Selected.FetterInfo.Fetters}">
|
ItemsSource="{Binding Selected.FetterInfo.Fetters}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
@@ -165,10 +532,15 @@
|
|||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="16,16,16,0"
|
Margin="16,16,16,0"
|
||||||
Text="{Binding Title}"/>
|
Text="{Binding Title}"/>
|
||||||
<TextBlock
|
<shct:DescriptionTextBlock
|
||||||
Margin="16,8,16,16"
|
Margin="16,8,16,16"
|
||||||
Text="{Binding Context}"
|
Description="{Binding Context}">
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"/>
|
<shct:DescriptionTextBlock.Resources>
|
||||||
|
<Style
|
||||||
|
TargetType="TextBlock"
|
||||||
|
BasedOn="{StaticResource CaptionTextBlockStyle}"/>
|
||||||
|
</shct:DescriptionTextBlock.Resources>
|
||||||
|
</shct:DescriptionTextBlock>
|
||||||
<MenuFlyoutSeparator Margin="16,0"/>
|
<MenuFlyoutSeparator Margin="16,0"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@@ -204,4 +576,4 @@
|
|||||||
</SplitView.Content>
|
</SplitView.Content>
|
||||||
</SplitView>
|
</SplitView>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
|
using Snap.Hutao.Service.Navigation;
|
||||||
using Snap.Hutao.ViewModel;
|
using Snap.Hutao.ViewModel;
|
||||||
|
|
||||||
namespace Snap.Hutao.View.Page;
|
namespace Snap.Hutao.View.Page;
|
||||||
@@ -18,4 +20,15 @@ public sealed partial class WikiAvatarPage : Microsoft.UI.Xaml.Controls.Page
|
|||||||
DataContext = Ioc.Default.GetRequiredService<WikiAvatarViewModel>();
|
DataContext = Ioc.Default.GetRequiredService<WikiAvatarViewModel>();
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnNavigatedTo(e);
|
||||||
|
|
||||||
|
if (e.Parameter is INavigationData extra)
|
||||||
|
{
|
||||||
|
extra.NotifyNavigationCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
<UserControl
|
<UserControl
|
||||||
x:Class="Snap.Hutao.View.UserView"
|
x:Class="Snap.Hutao.View.UserView"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
|
||||||
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
|
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
|
||||||
xmlns:mxim="using:Microsoft.Xaml.Interactions.Media"
|
xmlns:mxim="using:Microsoft.Xaml.Interactions.Media"
|
||||||
|
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||||
|
xmlns:shc="using:Snap.Hutao.Control"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<mxi:Interaction.Behaviors>
|
<mxi:Interaction.Behaviors>
|
||||||
<mxic:EventTriggerBehavior EventName="Loaded">
|
<mxic:EventTriggerBehavior EventName="Loaded">
|
||||||
@@ -26,8 +27,9 @@
|
|||||||
<PersonPicture
|
<PersonPicture
|
||||||
ProfilePicture="{Binding SelectedUser.UserInfo.AvatarUrl,Mode=OneWay}"
|
ProfilePicture="{Binding SelectedUser.UserInfo.AvatarUrl,Mode=OneWay}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
Margin="4,0,4,0"
|
Margin="6,2"
|
||||||
Height="40"/>
|
Height="36"
|
||||||
|
Width="36"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Margin="0,0,0,2"
|
Margin="0,0,0,2"
|
||||||
@@ -42,6 +44,11 @@
|
|||||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="4">
|
Margin="4">
|
||||||
|
<Button.Resources>
|
||||||
|
<shc:BindingProxy
|
||||||
|
x:Key="ViewModelBindingProxy"
|
||||||
|
DataContext="{Binding}"/>
|
||||||
|
</Button.Resources>
|
||||||
<Button.Flyout>
|
<Button.Flyout>
|
||||||
<Flyout
|
<Flyout
|
||||||
Placement="TopEdgeAlignedRight"
|
Placement="TopEdgeAlignedRight"
|
||||||
@@ -124,7 +131,8 @@
|
|||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
BorderBrush="{x:Null}"
|
BorderBrush="{x:Null}"
|
||||||
Margin="12,0,0,0"
|
Margin="12,0,0,0"
|
||||||
Command="{Binding CopyCookieCommand}"
|
Command="{Binding DataContext.CopyCookieCommand,Source={StaticResource ViewModelBindingProxy}}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
ToolTipService.ToolTip="复制 Cookie"/>
|
ToolTipService.ToolTip="复制 Cookie"/>
|
||||||
<Button
|
<Button
|
||||||
Content=""
|
Content=""
|
||||||
@@ -135,7 +143,7 @@
|
|||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
BorderBrush="{x:Null}"
|
BorderBrush="{x:Null}"
|
||||||
Margin="6,0,0,0"
|
Margin="6,0,0,0"
|
||||||
Command="{Binding RemoveCommand}"
|
Command="{Binding DataContext.RemoveUserCommand,Source={StaticResource ViewModelBindingProxy}}"
|
||||||
CommandParameter="{Binding}"
|
CommandParameter="{Binding}"
|
||||||
ToolTipService.ToolTip="移除用户"/>
|
ToolTipService.ToolTip="移除用户"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
|||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using Snap.Hutao.Control.Cancellable;
|
using Snap.Hutao.Control.Cancellable;
|
||||||
using Snap.Hutao.Core;
|
using Snap.Hutao.Core;
|
||||||
using Snap.Hutao.Core.Threading;
|
|
||||||
using Snap.Hutao.Factory.Abstraction;
|
using Snap.Hutao.Factory.Abstraction;
|
||||||
using Snap.Hutao.Service.Abstraction;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
using Snap.Hutao.Service.Navigation;
|
using Snap.Hutao.Service.Navigation;
|
||||||
@@ -64,11 +63,6 @@ internal class AnnouncementViewModel : ObservableObject, ISupportCancellation
|
|||||||
set => SetProperty(ref announcement, value);
|
set => SetProperty(ref announcement, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 打开界面监视器
|
|
||||||
/// </summary>
|
|
||||||
public Watcher OpeningUI { get; } = new(false);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 打开界面触发的命令
|
/// 打开界面触发的命令
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -81,23 +75,18 @@ internal class AnnouncementViewModel : ObservableObject, ISupportCancellation
|
|||||||
|
|
||||||
private async Task OpenUIAsync()
|
private async Task OpenUIAsync()
|
||||||
{
|
{
|
||||||
using (OpeningUI.Watch())
|
try
|
||||||
{
|
{
|
||||||
try
|
Announcement = await announcementService.GetAnnouncementsAsync(OpenAnnouncementUICommand, CancellationToken);
|
||||||
{
|
}
|
||||||
Announcement = await announcementService.GetAnnouncementsAsync(OpenAnnouncementUICommand, CancellationToken);
|
catch (TaskCanceledException)
|
||||||
}
|
{
|
||||||
catch (TaskCanceledException)
|
logger.LogInformation($"{nameof(OpenUIAsync)} cancelled");
|
||||||
{
|
|
||||||
logger.LogInformation($"{nameof(OpenUIAsync)} cancelled");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenAnnouncementUI(string? content)
|
private void OpenAnnouncementUI(string? content)
|
||||||
{
|
{
|
||||||
logger.LogInformation($"{nameof(OpenAnnouncementUICommand)} Triggered");
|
|
||||||
|
|
||||||
if (WebView2Helper.IsSupported)
|
if (WebView2Helper.IsSupported)
|
||||||
{
|
{
|
||||||
navigationService.Navigate<AnnouncementContentPage>(data: new NavigationExtra(content));
|
navigationService.Navigate<AnnouncementContentPage>(data: new NavigationExtra(content));
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ internal class SettingViewModel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的测试视图模型
|
/// 构造一个新的测试视图模型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="asyncRelayCommandFactory">异步命令工厂</param>
|
|
||||||
public SettingViewModel()
|
public SettingViewModel()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using Snap.Hutao.Core.Threading;
|
using Snap.Hutao.Core.Threading;
|
||||||
using Snap.Hutao.Factory.Abstraction;
|
using Snap.Hutao.Factory.Abstraction;
|
||||||
using Snap.Hutao.Model.Entity;
|
using Snap.Hutao.Model.Entity;
|
||||||
@@ -10,6 +11,7 @@ using Snap.Hutao.View.Dialog;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Windows.ApplicationModel.DataTransfer;
|
||||||
|
|
||||||
namespace Snap.Hutao.ViewModel;
|
namespace Snap.Hutao.ViewModel;
|
||||||
|
|
||||||
@@ -23,7 +25,6 @@ internal class UserViewModel : ObservableObject
|
|||||||
|
|
||||||
private readonly IUserService userService;
|
private readonly IUserService userService;
|
||||||
private readonly IInfoBarService infoBarService;
|
private readonly IInfoBarService infoBarService;
|
||||||
private readonly ICommand removeUserCommandCache;
|
|
||||||
|
|
||||||
private User? selectedUser;
|
private User? selectedUser;
|
||||||
private ObservableCollection<User>? userInfos;
|
private ObservableCollection<User>? userInfos;
|
||||||
@@ -41,8 +42,8 @@ internal class UserViewModel : ObservableObject
|
|||||||
|
|
||||||
OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync);
|
OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync);
|
||||||
AddUserCommand = asyncRelayCommandFactory.Create(AddUserAsync);
|
AddUserCommand = asyncRelayCommandFactory.Create(AddUserAsync);
|
||||||
|
RemoveUserCommand = asyncRelayCommandFactory.Create<User>(RemoveUserAsync);
|
||||||
removeUserCommandCache = asyncRelayCommandFactory.Create<User>(RemoveUserAsync);
|
CopyCookieCommand = new RelayCommand<User>(CopyCookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -75,6 +76,16 @@ internal class UserViewModel : ObservableObject
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ICommand AddUserCommand { get; }
|
public ICommand AddUserCommand { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除用户命令
|
||||||
|
/// </summary>
|
||||||
|
public ICommand RemoveUserCommand { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 复制Cookie命令
|
||||||
|
/// </summary>
|
||||||
|
public ICommand CopyCookieCommand { get; }
|
||||||
|
|
||||||
private static bool TryValidateCookie(IDictionary<string, string> map, [NotNullWhen(true)] out IDictionary<string, string>? filteredCookie)
|
private static bool TryValidateCookie(IDictionary<string, string> map, [NotNullWhen(true)] out IDictionary<string, string>? filteredCookie)
|
||||||
{
|
{
|
||||||
int validFlag = 4;
|
int validFlag = 4;
|
||||||
@@ -104,7 +115,7 @@ internal class UserViewModel : ObservableObject
|
|||||||
|
|
||||||
private async Task OpenUIAsync()
|
private async Task OpenUIAsync()
|
||||||
{
|
{
|
||||||
Users = await userService.GetInitializedUsersAsync(removeUserCommandCache);
|
Users = await userService.GetInitializedUsersAsync();
|
||||||
SelectedUser = userService.CurrentUser;
|
SelectedUser = userService.CurrentUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,11 +132,7 @@ internal class UserViewModel : ObservableObject
|
|||||||
if (TryValidateCookie(cookieMap, out IDictionary<string, string>? filteredCookie))
|
if (TryValidateCookie(cookieMap, out IDictionary<string, string>? filteredCookie))
|
||||||
{
|
{
|
||||||
string simplifiedCookie = string.Join(';', filteredCookie.Select(kvp => $"{kvp.Key}={kvp.Value}"));
|
string simplifiedCookie = string.Join(';', filteredCookie.Select(kvp => $"{kvp.Key}={kvp.Value}"));
|
||||||
User user = new()
|
User user = new() { Cookie = simplifiedCookie };
|
||||||
{
|
|
||||||
Cookie = simplifiedCookie,
|
|
||||||
RemoveCommand = removeUserCommandCache,
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (await userService.TryAddUserAsync(user, filteredCookie[AccountIdKey]))
|
switch (await userService.TryAddUserAsync(user, filteredCookie[AccountIdKey]))
|
||||||
{
|
{
|
||||||
@@ -160,4 +167,26 @@ internal class UserViewModel : ObservableObject
|
|||||||
infoBarService.Success($"用户 [{user.UserInfo!.Nickname}] 成功移除");
|
infoBarService.Success($"用户 [{user.UserInfo!.Nickname}] 成功移除");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CopyCookie(User? user)
|
||||||
|
{
|
||||||
|
if (User.IsNone(user))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DataPackage content = new();
|
||||||
|
content.SetText(Must.NotNull(user.Cookie!));
|
||||||
|
Clipboard.SetContent(content);
|
||||||
|
|
||||||
|
infoBarService.Success($"{user.UserInfo!.Nickname} 的 Cookie 复制成功");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
infoBarService.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,6 @@ using Snap.Hutao.Web.Hutao.Model;
|
|||||||
using Snap.Hutao.Web.Hutao.Model.Post;
|
using Snap.Hutao.Web.Hutao.Model.Post;
|
||||||
using Snap.Hutao.Web.Response;
|
using Snap.Hutao.Web.Response;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
|
|
||||||
@@ -325,46 +324,6 @@ internal class HutaoClient : ISupportAsyncInitialization
|
|||||||
return await response.Content.ReadFromJsonAsync<Response<string>>(jsonSerializerOptions, token);
|
return await response.Content.ReadFromJsonAsync<Response<string>>(jsonSerializerOptions, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 异步上传物品所有物品
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="characters">角色详细信息</param>
|
|
||||||
/// <param name="token">取消令牌</param>
|
|
||||||
/// <returns>响应</returns>
|
|
||||||
[Obsolete("不再强制要求上传物品")]
|
|
||||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
||||||
internal async Task<Response<string>?> UploadItemsAsync(List<Character> characters, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
Verify.Operation(IsInitialized, "必须在初始化后才能调用其他方法");
|
|
||||||
|
|
||||||
IEnumerable<Item> avatars = characters
|
|
||||||
.Select(avatar => new Item(avatar.Id, avatar.Name, avatar.Icon))
|
|
||||||
.DistinctBy(item => item.Id);
|
|
||||||
|
|
||||||
IEnumerable<Item> weapons = characters
|
|
||||||
.Select(avatar => avatar.Weapon)
|
|
||||||
.Select(weapon => new Item(weapon.Id, weapon.Name, weapon.Icon))
|
|
||||||
.DistinctBy(item => item.Id);
|
|
||||||
|
|
||||||
IEnumerable<Item> reliquaries = characters
|
|
||||||
.Select(avatars => avatars.Reliquaries)
|
|
||||||
.Flatten()
|
|
||||||
.Where(relic => relic.Position == ReliquaryPosition.FlowerOfLife)
|
|
||||||
.DistinctBy(relic => relic.Id)
|
|
||||||
.Select(relic => new Item(relic.ReliquarySet.Id, relic.ReliquarySet.Name, relic.Icon));
|
|
||||||
|
|
||||||
GenshinItemWrapper? data = new(avatars, weapons, reliquaries);
|
|
||||||
JsonSerializerOptions? option = Ioc.Default.GetService<JsonSerializerOptions>();
|
|
||||||
|
|
||||||
HttpResponseMessage? response = await httpClient
|
|
||||||
.PostAsJsonAsync($"{HutaoAPI}/GenshinItem/Upload", data, jsonSerializerOptions, token)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
return await response.Content
|
|
||||||
.ReadFromJsonAsync<Response<string>>(jsonSerializerOptions, token)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Auth
|
private class Auth
|
||||||
{
|
{
|
||||||
public Auth(string appid, string secret)
|
public Auth(string appid, string secret)
|
||||||
|
|||||||
Reference in New Issue
Block a user