mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
add database intergrations
This commit is contained in:
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Snap.Hutao.Core;
|
|
||||||
using Snap.Hutao.Web.Request;
|
|
||||||
|
|
||||||
namespace Snap.Hutao;
|
namespace Snap.Hutao;
|
||||||
|
|
||||||
@@ -40,29 +38,13 @@ public partial class App : Application
|
|||||||
|
|
||||||
private static void InitializeDependencyInjection()
|
private static void InitializeDependencyInjection()
|
||||||
{
|
{
|
||||||
// prepare DI
|
|
||||||
IServiceProvider services = new ServiceCollection()
|
IServiceProvider services = new ServiceCollection()
|
||||||
.AddLogging(builder => builder.AddDebug())
|
.AddLogging(builder => builder.AddDebug())
|
||||||
|
.AddHttpClients()
|
||||||
// http json
|
.AddDefaultJsonSerializerOptions()
|
||||||
.AddHttpClient<HttpJson>()
|
|
||||||
.ConfigureHttpClient(client =>
|
|
||||||
{
|
|
||||||
client.Timeout = Timeout.InfiniteTimeSpan;
|
|
||||||
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) Snap Hutao");
|
|
||||||
})
|
|
||||||
.Services
|
|
||||||
|
|
||||||
// requester & auth reuqester
|
|
||||||
.AddHttpClient<Requester>(nameof(Requester))
|
|
||||||
.AddTypedClient<AuthRequester>()
|
|
||||||
.ConfigureHttpClient(client => client.Timeout = Timeout.InfiniteTimeSpan)
|
|
||||||
.Services
|
|
||||||
|
|
||||||
// inject app wide services
|
|
||||||
.AddInjections(typeof(App))
|
.AddInjections(typeof(App))
|
||||||
.BuildServiceProvider();
|
.BuildServiceProvider();
|
||||||
|
|
||||||
Ioc.Default.ConfigureServices(services);
|
Ioc.Default.ConfigureServices(services);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs
Normal file
21
src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Context.Database;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用程序数据库上下文
|
||||||
|
/// </summary>
|
||||||
|
internal class AppDbContext : DbContext
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的应用程序数据库上下文
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">选项</param>
|
||||||
|
public AppDbContext(DbContextOptions<AppDbContext> options)
|
||||||
|
: base(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Context.FileSystem.Location;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Context.FileSystem;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 文件系统上下文
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TLocation">路径位置类型</typeparam>
|
||||||
|
internal abstract class FileSystemContext
|
||||||
|
{
|
||||||
|
private readonly IFileSystemLocation location;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化文件系统上下文
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="location">指定的文件系统位置</param>
|
||||||
|
public FileSystemContext(IFileSystemLocation location)
|
||||||
|
{
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查根目录
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>是否创建了路径</returns>
|
||||||
|
public bool EnsureDirectory()
|
||||||
|
{
|
||||||
|
string folder = location.GetPath();
|
||||||
|
if (!Directory.Exists(folder))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(folder);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查文件是否存在
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="file">文件名称</param>
|
||||||
|
/// <returns>是否存在</returns>
|
||||||
|
public bool FileExists(string file)
|
||||||
|
{
|
||||||
|
return File.Exists(Locate(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查文件是否存在
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="folder">文件夹名称</param>
|
||||||
|
/// <param name="file">文件名称</param>
|
||||||
|
/// <returns>是否存在</returns>
|
||||||
|
public bool FileExists(string folder, string file)
|
||||||
|
{
|
||||||
|
return File.Exists(Locate(folder, file));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查文件是否存在
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="folder">文件夹名称</param>
|
||||||
|
/// <returns>是否存在</returns>
|
||||||
|
public bool FolderExists(string folder)
|
||||||
|
{
|
||||||
|
return Directory.Exists(Locate(folder));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 定位根目录中的文件或文件夹
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileOrFolder">文件或文件夹</param>
|
||||||
|
/// <returns>绝对路径</returns>
|
||||||
|
public string Locate(string fileOrFolder)
|
||||||
|
{
|
||||||
|
return Path.GetFullPath(fileOrFolder, location.GetPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 定位根目录下子文件夹中的文件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="folder">文件夹</param>
|
||||||
|
/// <param name="file">文件</param>
|
||||||
|
/// <returns>绝对路径</returns>
|
||||||
|
public string Locate(string folder, string file)
|
||||||
|
{
|
||||||
|
return Path.GetFullPath(Path.Combine(folder, file), location.GetPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将文件移动到指定的子目录
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="file">文件</param>
|
||||||
|
/// <param name="folder">文件夹</param>
|
||||||
|
/// <param name="overwrite">是否覆盖</param>
|
||||||
|
/// <returns>是否成功 当文件不存在时会失败</returns>
|
||||||
|
public bool MoveToFolderOrIgnore(string file, string folder, bool overwrite = true)
|
||||||
|
{
|
||||||
|
string target = Locate(folder, file);
|
||||||
|
file = Locate(file);
|
||||||
|
|
||||||
|
if (File.Exists(file))
|
||||||
|
{
|
||||||
|
File.Move(file, target, overwrite);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建文件,若已存在文件,则不会创建
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="file">文件</param>
|
||||||
|
public void CreateFileOrIgnore(string file)
|
||||||
|
{
|
||||||
|
file = Locate(file);
|
||||||
|
if (!File.Exists(file))
|
||||||
|
{
|
||||||
|
File.Create(file).Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建文件夹,若已存在文件,则不会创建
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="folder">文件夹</param>
|
||||||
|
public void CreateFolderOrIgnore(string folder)
|
||||||
|
{
|
||||||
|
folder = Locate(folder);
|
||||||
|
if (!Directory.Exists(folder))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 尝试删除文件夹
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="folder">文件夹</param>
|
||||||
|
public void DeleteFolderOrIgnore(string folder)
|
||||||
|
{
|
||||||
|
folder = Locate(folder);
|
||||||
|
if (Directory.Exists(folder))
|
||||||
|
{
|
||||||
|
Directory.Delete(folder, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Context.FileSystem.Location;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 文件系统位置
|
||||||
|
/// </summary>
|
||||||
|
public interface IFileSystemLocation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取路径
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>路径</returns>
|
||||||
|
string GetPath();
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Context.FileSystem.Location;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 我的文档位置
|
||||||
|
/// </summary>
|
||||||
|
[Injection(InjectAs.Transient)]
|
||||||
|
public class MyDocument : IFileSystemLocation
|
||||||
|
{
|
||||||
|
private string? path;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string GetPath()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
string myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||||||
|
path = Path.GetFullPath(Path.Combine(myDocument, "Hutao"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Context.FileSystem.Location;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Context.FileSystem;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 我的文档上下文
|
||||||
|
/// </summary>
|
||||||
|
[Injection(InjectAs.Transient)]
|
||||||
|
internal class MyDocumentContext : FileSystemContext
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="FileSystemContext"/>
|
||||||
|
public MyDocumentContext(MyDocument myDocument)
|
||||||
|
: base(myDocument)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs
Normal file
48
src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using Snap.Hutao.Extension;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using Windows.ApplicationModel;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 核心环境参数
|
||||||
|
/// </summary>
|
||||||
|
internal static class CoreEnvironment
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 当前版本
|
||||||
|
/// </summary>
|
||||||
|
public static readonly Version Version;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设备Id
|
||||||
|
/// </summary>
|
||||||
|
public static readonly string DeviceId;
|
||||||
|
|
||||||
|
private const string CryptographyKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography";
|
||||||
|
private const string MachineGuidValue = "MachineGuid";
|
||||||
|
|
||||||
|
static CoreEnvironment()
|
||||||
|
{
|
||||||
|
Version = Package.Current.Id.Version.ToVersion();
|
||||||
|
DeviceId = GetDeviceId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取设备的UUID
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>设备的UUID</returns>
|
||||||
|
private static string GetDeviceId()
|
||||||
|
{
|
||||||
|
string userName = Environment.UserName;
|
||||||
|
object? machineGuid = Registry.GetValue(CryptographyKey, MachineGuidValue, userName);
|
||||||
|
byte[] bytes = Encoding.UTF8.GetBytes($"{userName}{machineGuid}");
|
||||||
|
byte[] hash = MD5.HashData(bytes);
|
||||||
|
return Convert.ToHexString(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.Json.Converter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实现日期的转换
|
||||||
|
/// </summary>
|
||||||
|
internal class DateTimeConverter : JsonConverter<DateTime>
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (reader.GetString() is string dataTimeString)
|
||||||
|
{
|
||||||
|
return DateTime.Parse(dataTimeString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return default(DateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core;
|
namespace Snap.Hutao.Core.Json;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Http Json 处理
|
/// Http Json 处理
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// 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 Newtonsoft.Json;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core;
|
namespace Snap.Hutao.Core.Json;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Json操作
|
/// Json操作
|
||||||
@@ -12,20 +12,17 @@ namespace Snap.Hutao.Core;
|
|||||||
[Injection(InjectAs.Transient)]
|
[Injection(InjectAs.Transient)]
|
||||||
public class Json
|
public class Json
|
||||||
{
|
{
|
||||||
|
private readonly JsonSerializerOptions jsonSerializerOptions;
|
||||||
private readonly ILogger logger;
|
private readonly ILogger logger;
|
||||||
|
|
||||||
private readonly JsonSerializerSettings jsonSerializerSettings = new()
|
|
||||||
{
|
|
||||||
DateFormatString = "yyyy'-'MM'-'dd' 'HH':'mm':'ss.FFFFFFFK",
|
|
||||||
Formatting = Formatting.Indented,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化一个新的 Json操作 实例
|
/// 初始化一个新的 Json操作 实例
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="jsonSerializerOptions">配置</param>
|
||||||
/// <param name="logger">日志器</param>
|
/// <param name="logger">日志器</param>
|
||||||
public Json(ILogger<Json> logger)
|
public Json(JsonSerializerOptions jsonSerializerOptions, ILogger<Json> logger)
|
||||||
{
|
{
|
||||||
|
this.jsonSerializerOptions = jsonSerializerOptions;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,19 +31,20 @@ public class Json
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">要反序列化的对象的类型</typeparam>
|
/// <typeparam name="T">要反序列化的对象的类型</typeparam>
|
||||||
/// <param name="value">要反序列化的JSON</param>
|
/// <param name="value">要反序列化的JSON</param>
|
||||||
/// <returns>Json字符串中的反序列化对象, 如果反序列化失败会抛出异常</returns>
|
/// <returns>Json字符串中的反序列化对象, 如果反序列化失败会返回 <see langword="default"/></returns>
|
||||||
public T? ToObject<T>(string value)
|
public T? ToObject<T>(string value)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return JsonConvert.DeserializeObject<T>(value);
|
T? result = JsonSerializer.Deserialize<T>(value);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogError("反序列化Json时遇到问题:{ex}", ex);
|
logger.LogError("反序列化Json时遇到问题\n{ex}", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
return default(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -69,7 +67,7 @@ public class Json
|
|||||||
/// <returns>对象的JSON字符串表示形式</returns>
|
/// <returns>对象的JSON字符串表示形式</returns>
|
||||||
public string Stringify(object? value)
|
public string Stringify(object? value)
|
||||||
{
|
{
|
||||||
return JsonConvert.SerializeObject(value, jsonSerializerSettings);
|
return JsonSerializer.Serialize(value, jsonSerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
113
src/Snap.Hutao/Snap.Hutao/Core/Setting/LocalSetting.cs
Normal file
113
src/Snap.Hutao/Snap.Hutao/Core/Setting/LocalSetting.cs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Windows.Storage;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.Setting;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 本地设置
|
||||||
|
/// </summary>
|
||||||
|
internal static class LocalSetting
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 由于 <see cref="Windows.Foundation.Collections.IPropertySet"/> 没有启用 nullable,
|
||||||
|
/// 在处理引用类型时需要格外小心
|
||||||
|
/// 将值类型的操作与引用类型区分开,可以提升一定的性能
|
||||||
|
/// </summary>
|
||||||
|
private static readonly ApplicationDataContainer Container;
|
||||||
|
|
||||||
|
static LocalSetting()
|
||||||
|
{
|
||||||
|
Container = ApplicationData.Current.LocalSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取设置项的值
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">设置项的类型</typeparam>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>获取的值</returns>
|
||||||
|
public static T? Get<T>(string key, T? defaultValue = default)
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
if (Container.Values.TryGetValue(key, out object? value))
|
||||||
|
{
|
||||||
|
return value is null ? defaultValue : value as T;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Set(key, defaultValue);
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取设置项的值
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">设置项的类型</typeparam>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>获取的值</returns>
|
||||||
|
public static T GetValueType<T>(string key, T defaultValue = default)
|
||||||
|
where T : struct
|
||||||
|
{
|
||||||
|
if (Container.Values.TryGetValue(key, out object? value))
|
||||||
|
{
|
||||||
|
if (value is null)
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 无法避免的拆箱操作
|
||||||
|
return (T)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Set(key, defaultValue);
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置设置项的值
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">设置项的类型</typeparam>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="value">值</param>
|
||||||
|
public static void Set<T>(string key, T? value)
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Container.Values[key] = value;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置设置项的值
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">设置项的类型</typeparam>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="value">值</param>
|
||||||
|
public static void SetValueType<T>(string key, T value)
|
||||||
|
where T : struct
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Container.Values[key] = value;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/Snap.Hutao/Snap.Hutao/Core/Setting/SettingKeys.cs
Normal file
15
src/Snap.Hutao/Snap.Hutao/Core/Setting/SettingKeys.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.Setting;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置键
|
||||||
|
/// </summary>
|
||||||
|
internal static class SettingKeys
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 上次打开时App的版本
|
||||||
|
/// </summary>
|
||||||
|
public static readonly string LastAppVersion = "LastAppVersion";
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft;
|
using Microsoft;
|
||||||
|
using Snap.Hutao.Model;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.Threading;
|
namespace Snap.Hutao.Core.Threading;
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ namespace Snap.Hutao.Core.Threading;
|
|||||||
public class Watcher : Observable
|
public class Watcher : Observable
|
||||||
{
|
{
|
||||||
private readonly bool isReusable;
|
private readonly bool isReusable;
|
||||||
|
|
||||||
private bool hasUsed;
|
private bool hasUsed;
|
||||||
private bool isWorking;
|
private bool isWorking;
|
||||||
private bool isCompleted;
|
private bool isCompleted;
|
||||||
@@ -48,33 +50,33 @@ public class Watcher : Observable
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 对某个操作进行监视,
|
/// 对某个操作进行监视,
|
||||||
/// 无法防止代码重入
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>一个可释放的对象,用于在操作完成时自动提示监视器工作已经完成</returns>
|
/// <returns>一个可释放的对象,用于在操作完成时自动提示监视器工作已经完成</returns>
|
||||||
/// <exception cref="InvalidOperationException">重用了一个不可重用的监视器</exception>
|
/// <exception cref="InvalidOperationException">重用了一个不可重用的监视器</exception>
|
||||||
public IDisposable Watch()
|
public IDisposable Watch()
|
||||||
{
|
{
|
||||||
|
Verify.Operation(!IsWorking, $"此 {nameof(Watcher)} 已经处于检查状态");
|
||||||
Verify.Operation(isReusable || !hasUsed, $"此 {nameof(Watcher)} 不允许多次使用");
|
Verify.Operation(isReusable || !hasUsed, $"此 {nameof(Watcher)} 不允许多次使用");
|
||||||
|
|
||||||
hasUsed = true;
|
hasUsed = true;
|
||||||
IsWorking = true;
|
IsWorking = true;
|
||||||
|
|
||||||
return new WorkDisposable(this);
|
return new WatchDisposable(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct WorkDisposable : IDisposable
|
private struct WatchDisposable : IDisposable
|
||||||
{
|
{
|
||||||
private readonly Watcher work;
|
private readonly Watcher watcher;
|
||||||
|
|
||||||
public WorkDisposable(Watcher work)
|
public WatchDisposable(Watcher watcher)
|
||||||
{
|
{
|
||||||
this.work = work;
|
this.watcher = watcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
work.IsWorking = false;
|
watcher.IsWorking = false;
|
||||||
work.IsCompleted = true;
|
watcher.IsCompleted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Windows.ApplicationModel;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Extension;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 包版本扩展
|
||||||
|
/// </summary>
|
||||||
|
public static class PackageVersionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 将包版本转换为版本
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="packageVersion">包版本</param>
|
||||||
|
/// <returns>版本</returns>
|
||||||
|
public static Version ToVersion(this PackageVersion packageVersion)
|
||||||
|
{
|
||||||
|
return new Version(packageVersion.Major, packageVersion.Minor, packageVersion.Build, packageVersion.Revision);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Windows.Storage.Pickers;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Factory.Abstraction;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 文件选择器工厂
|
||||||
|
/// </summary>
|
||||||
|
internal interface IPickerFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取 经过初始化的 <see cref="FileOpenPicker"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>经过初始化的 <see cref="FileOpenPicker"/></returns>
|
||||||
|
FileOpenPicker GetFileOpenPicker();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取 经过初始化的 <see cref="FileSavePicker"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>经过初始化的 <see cref="FileSavePicker"/></returns>
|
||||||
|
FileSavePicker GetFileSavePicker();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取 经过初始化的 <see cref="FolderPicker"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>经过初始化的 <see cref="FolderPicker"/></returns>
|
||||||
|
FolderPicker GetFolderPicker();
|
||||||
|
}
|
||||||
54
src/Snap.Hutao/Snap.Hutao/Factory/PickerFactory.cs
Normal file
54
src/Snap.Hutao/Snap.Hutao/Factory/PickerFactory.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Factory.Abstraction;
|
||||||
|
using Windows.Storage.Pickers;
|
||||||
|
using WinRT.Interop;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Factory;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IPickerFactory"/>
|
||||||
|
[Injection(InjectAs.Transient)]
|
||||||
|
internal class PickerFactory : IPickerFactory
|
||||||
|
{
|
||||||
|
private readonly MainWindow mainWindow;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的文件选择器工厂
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mainWindow">主窗体的引用注入</param>
|
||||||
|
public PickerFactory(MainWindow mainWindow)
|
||||||
|
{
|
||||||
|
this.mainWindow = mainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public FileOpenPicker GetFileOpenPicker()
|
||||||
|
{
|
||||||
|
return GetInitializedPicker<FileOpenPicker>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public FileSavePicker GetFileSavePicker()
|
||||||
|
{
|
||||||
|
return GetInitializedPicker<FileSavePicker>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public FolderPicker GetFolderPicker()
|
||||||
|
{
|
||||||
|
return GetInitializedPicker<FolderPicker>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private T GetInitializedPicker<T>()
|
||||||
|
where T : new()
|
||||||
|
{
|
||||||
|
// Create a folder picker.
|
||||||
|
T picker = new();
|
||||||
|
|
||||||
|
IntPtr hWnd = WindowNative.GetWindowHandle(mainWindow);
|
||||||
|
InitializeWithWindow.Initialize(picker, hWnd);
|
||||||
|
|
||||||
|
return picker;
|
||||||
|
}
|
||||||
|
}
|
||||||
109
src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs
Normal file
109
src/Snap.Hutao/Snap.Hutao/IocConfiguration.cs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Snap.Hutao.Context.Database;
|
||||||
|
using Snap.Hutao.Context.FileSystem;
|
||||||
|
using Snap.Hutao.Core;
|
||||||
|
using Snap.Hutao.Core.Json;
|
||||||
|
using Snap.Hutao.Core.Setting;
|
||||||
|
using Snap.Hutao.Web.Request;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Snap.Hutao;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="Ioc"/> 配置
|
||||||
|
/// </summary>
|
||||||
|
internal static class IocConfiguration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 <see cref="System.Net.Http.HttpClient"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">集合</param>
|
||||||
|
/// <returns>可继续操作的集合</returns>
|
||||||
|
public static IServiceCollection AddHttpClients(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
// http json
|
||||||
|
services
|
||||||
|
.AddHttpClient<HttpJson>()
|
||||||
|
.ConfigureHttpClient(client =>
|
||||||
|
{
|
||||||
|
client.Timeout = Timeout.InfiniteTimeSpan;
|
||||||
|
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) Snap Hutao");
|
||||||
|
});
|
||||||
|
|
||||||
|
// requester & auth reuqester
|
||||||
|
services
|
||||||
|
.AddHttpClient<Requester>(nameof(Requester))
|
||||||
|
.AddTypedClient<AuthRequester>()
|
||||||
|
.ConfigureHttpClient(client => client.Timeout = Timeout.InfiniteTimeSpan);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加默认的 <see cref="JsonSerializer"/> 配置
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">集合</param>
|
||||||
|
/// <returns>可继续操作的集合</returns>
|
||||||
|
public static IServiceCollection AddDefaultJsonSerializerOptions(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
// default json options, global configuration
|
||||||
|
return services
|
||||||
|
.AddSingleton(new JsonSerializerOptions()
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
PropertyNameCaseInsensitive = true,
|
||||||
|
WriteIndented = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加数据库
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">集合</param>
|
||||||
|
/// <returns>可继续操作的集合</returns>
|
||||||
|
public static IServiceCollection AddDatebase(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
MyDocumentContext myDocument = new(new());
|
||||||
|
myDocument.EnsureDirectory();
|
||||||
|
|
||||||
|
string dbFile = myDocument.Locate("Userdata.db");
|
||||||
|
string sqlConnectionString = $"Data Source={dbFile}";
|
||||||
|
|
||||||
|
bool shouldMigrate = false;
|
||||||
|
|
||||||
|
if (!myDocument.FileExists(dbFile))
|
||||||
|
{
|
||||||
|
shouldMigrate = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string? versionString = LocalSetting.Get<string>(SettingKeys.LastAppVersion);
|
||||||
|
if (Version.TryParse(versionString, out Version? lastVersion))
|
||||||
|
{
|
||||||
|
if (lastVersion < CoreEnvironment.Version)
|
||||||
|
{
|
||||||
|
shouldMigrate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldMigrate)
|
||||||
|
{
|
||||||
|
// temporarily create a context
|
||||||
|
using (AppDbContext context = new(new DbContextOptionsBuilder<AppDbContext>().UseSqlite(sqlConnectionString).Options))
|
||||||
|
{
|
||||||
|
context.Database.Migrate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalSetting.Set(SettingKeys.LastAppVersion, CoreEnvironment.Version.ToString());
|
||||||
|
|
||||||
|
return services
|
||||||
|
.AddPooledDbContextFactory<AppDbContext>(builder => builder.UseSqlite(sqlConnectionString));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ namespace Snap.Hutao;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 主窗体
|
/// 主窗体
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Injection(InjectAs.Transient)]
|
[Injection(InjectAs.Singleton)]
|
||||||
public sealed partial class MainWindow : Window
|
public sealed partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core;
|
namespace Snap.Hutao.Model;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 简单的实现了 <see cref="INotifyPropertyChanged"/> 接口
|
/// 简单的实现了 <see cref="INotifyPropertyChanged"/> 接口
|
||||||
@@ -61,13 +61,6 @@ public interface IInfoBarService
|
|||||||
/// <param name="container">信息条的目标容器</param>
|
/// <param name="container">信息条的目标容器</param>
|
||||||
void Initialize(StackPanel container);
|
void Initialize(StackPanel container);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 显示特定的信息条
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="infoBar">信息条</param>
|
|
||||||
/// <param name="delay">关闭延迟</param>
|
|
||||||
void Show(InfoBar infoBar, int delay = 0);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 显示成功信息
|
/// 显示成功信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -61,5 +61,5 @@ public interface INavigationService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pageType">同步的页面类型</param>
|
/// <param name="pageType">同步的页面类型</param>
|
||||||
/// <returns>是否同步成功</returns>
|
/// <returns>是否同步成功</returns>
|
||||||
bool SyncTabWith(Type pageType);
|
bool SyncSelectedNavigationViewItemWith(Type pageType);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.VisualStudio.Threading;
|
|
||||||
using Snap.Hutao.Service.Abstraction;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service;
|
namespace Snap.Hutao.Service;
|
||||||
@@ -79,25 +78,25 @@ internal class InfoBarService : IInfoBarService
|
|||||||
PrepareInfoBarAndShow(InfoBarSeverity.Error, ex.GetType().Name, $"{title}\n{ex.Message}", delay);
|
PrepareInfoBarAndShow(InfoBarSeverity.Error, ex.GetType().Name, $"{title}\n{ex.Message}", delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Show(InfoBar infoBar, int delay = 0)
|
|
||||||
{
|
|
||||||
Must.NotNull(infoBarStack!).DispatcherQueue.TryEnqueue(ShowInfoBarOnUIThreadAsync(infoBarStack, infoBar, delay).Forget);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ShowInfoBarOnUIThreadAsync(StackPanel stack, InfoBar infoBar, int delay)
|
|
||||||
{
|
|
||||||
infoBar.Closed += OnInfoBarClosed;
|
|
||||||
stack.Children.Add(infoBar);
|
|
||||||
|
|
||||||
if (delay > 0)
|
|
||||||
{
|
|
||||||
await Task.Delay(delay);
|
|
||||||
infoBar.IsOpen = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay)
|
private void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay)
|
||||||
|
{
|
||||||
|
if (infoBarStack is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
infoBarStack.DispatcherQueue.TryEnqueue(() => PrepareInfoBarAndShowInternal(severity, title, message, delay));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 此方法应在主线程上运行
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="severity">严重程度</param>
|
||||||
|
/// <param name="title">标题</param>
|
||||||
|
/// <param name="message">消息</param>
|
||||||
|
/// <param name="delay">关闭延迟</param>
|
||||||
|
[SuppressMessage("", "VSTHRD100", Justification ="只能通过 async void 方法使控件在主线程创建")]
|
||||||
|
private async void PrepareInfoBarAndShowInternal(InfoBarSeverity severity, string? title, string? message, int delay)
|
||||||
{
|
{
|
||||||
InfoBar infoBar = new()
|
InfoBar infoBar = new()
|
||||||
{
|
{
|
||||||
@@ -107,7 +106,14 @@ internal class InfoBarService : IInfoBarService
|
|||||||
IsOpen = true,
|
IsOpen = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
Show(infoBar, delay);
|
infoBar.Closed += OnInfoBarClosed;
|
||||||
|
Must.NotNull(infoBarStack!)!.Children.Add(infoBar);
|
||||||
|
|
||||||
|
if (delay > 0)
|
||||||
|
{
|
||||||
|
await Task.Delay(delay);
|
||||||
|
infoBar.IsOpen = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnInfoBarClosed(InfoBar sender, InfoBarClosedEventArgs args)
|
private void OnInfoBarClosed(InfoBar sender, InfoBarClosedEventArgs args)
|
||||||
|
|||||||
@@ -66,9 +66,9 @@ internal class NavigationService : INavigationService
|
|||||||
public bool HasEverNavigated { get; set; }
|
public bool HasEverNavigated { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool SyncTabWith(Type pageType)
|
public bool SyncSelectedNavigationViewItemWith(Type? pageType)
|
||||||
{
|
{
|
||||||
if (NavigationView is null)
|
if (NavigationView is null || pageType is null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -98,7 +98,7 @@ internal class NavigationService : INavigationService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = isSyncTabRequested && SyncTabWith(pageType);
|
_ = isSyncTabRequested && SyncSelectedNavigationViewItemWith(pageType);
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
try
|
try
|
||||||
@@ -151,6 +151,7 @@ internal class NavigationService : INavigationService
|
|||||||
if (Frame != null && Frame.CanGoBack)
|
if (Frame != null && Frame.CanGoBack)
|
||||||
{
|
{
|
||||||
Frame.GoBack();
|
Frame.GoBack();
|
||||||
|
SyncSelectedNavigationViewItemWith(Frame.Content.GetType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||||
<NeutralLanguage>zh-CN</NeutralLanguage>
|
<NeutralLanguage>zh-CN</NeutralLanguage>
|
||||||
<DefaultLanguage>zh-CN</DefaultLanguage>
|
<DefaultLanguage>zh-cn</DefaultLanguage>
|
||||||
|
|
||||||
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
|
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
|
||||||
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
|
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
|
||||||
@@ -48,14 +48,16 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="7.1.2" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI" Version="7.1.2" />
|
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Animations" Version="7.1.2" />
|
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Behaviors" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.UI.Behaviors" Version="7.1.2" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
|
||||||
<PackageReference Include="Microsoft.AppCenter.Analytics" Version="4.5.1" />
|
<PackageReference Include="Microsoft.AppCenter.Analytics" Version="4.5.1" />
|
||||||
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.5.1" />
|
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.5.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.4" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.4">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.1.46" />
|
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.1.46" />
|
||||||
@@ -63,10 +65,7 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.0.46" />
|
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.0.3" />
|
|
||||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22000.197" />
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22000.197" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.406">
|
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.406">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
@@ -83,9 +82,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Model\" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\SettingsUI\SettingsUI.csproj" />
|
<ProjectReference Include="..\..\SettingsUI\SettingsUI.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Data;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.View.Converter;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This class converts a boolean value into an other object.
|
|
||||||
/// Can be used to convert true/false to visibility, a couple of colors, couple of images, etc.
|
|
||||||
/// </summary>
|
|
||||||
public class BoolToObjectConverter : DependencyObject, IValueConverter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Identifies the <see cref="TrueValue"/> property.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly DependencyProperty TrueValueProperty =
|
|
||||||
DependencyProperty.Register(nameof(TrueValue), typeof(object), typeof(BoolToObjectConverter), new PropertyMetadata(null));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Identifies the <see cref="FalseValue"/> property.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly DependencyProperty FalseValueProperty =
|
|
||||||
DependencyProperty.Register(nameof(FalseValue), typeof(object), typeof(BoolToObjectConverter), new PropertyMetadata(null));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the value to be returned when the boolean is true
|
|
||||||
/// </summary>
|
|
||||||
public object TrueValue
|
|
||||||
{
|
|
||||||
get => GetValue(TrueValueProperty);
|
|
||||||
set => SetValue(TrueValueProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the value to be returned when the boolean is false
|
|
||||||
/// </summary>
|
|
||||||
public object FalseValue
|
|
||||||
{
|
|
||||||
get => GetValue(FalseValueProperty);
|
|
||||||
set => SetValue(FalseValueProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert a boolean value to an other object.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The source data being passed to the target.</param>
|
|
||||||
/// <param name="targetType">The type of the target property, as a type reference.</param>
|
|
||||||
/// <param name="parameter">An optional parameter to be used to invert the converter logic.</param>
|
|
||||||
/// <param name="language">The language of the conversion.</param>
|
|
||||||
/// <returns>The value to be passed to the target dependency property.</returns>
|
|
||||||
public object Convert(object value, Type targetType, object parameter, string language)
|
|
||||||
{
|
|
||||||
bool boolValue = value is bool valid && valid;
|
|
||||||
|
|
||||||
// Negate if needed
|
|
||||||
if (ConvertHelper.TryParseBool(parameter))
|
|
||||||
{
|
|
||||||
boolValue = !boolValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ConvertHelper.Convert(boolValue ? TrueValue : FalseValue, targetType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert back the value to a boolean
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>If the <paramref name="value"/> parameter is a reference type, <see cref="TrueValue"/> must match its reference to return true.</remarks>
|
|
||||||
/// <param name="value">The target data being passed to the source.</param>
|
|
||||||
/// <param name="targetType">The type of the target property, as a type reference (System.Type for Microsoft .NET, a TypeName helper struct for Visual C++ component extensions (C++/CX)).</param>
|
|
||||||
/// <param name="parameter">An optional parameter to be used to invert the converter logic.</param>
|
|
||||||
/// <param name="language">The language of the conversion.</param>
|
|
||||||
/// <returns>The value to be passed to the source object.</returns>
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
|
||||||
{
|
|
||||||
bool result = Equals(value, ConvertHelper.Convert(TrueValue, value.GetType()));
|
|
||||||
|
|
||||||
if (ConvertHelper.TryParseBool(parameter))
|
|
||||||
{
|
|
||||||
result = !result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.View.Converter;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This class converts a boolean value into a Visibility enumeration.
|
|
||||||
/// </summary>
|
|
||||||
public class BoolToVisibilityConverter : BoolToObjectConverter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="BoolToVisibilityConverter"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public BoolToVisibilityConverter()
|
|
||||||
{
|
|
||||||
TrueValue = Visibility.Visible;
|
|
||||||
FalseValue = Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// 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 CommunityToolkit.WinUI.UI.Converters;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
|
||||||
namespace Snap.Hutao.View.Converter;
|
namespace Snap.Hutao.View.Converter;
|
||||||
|
|||||||
@@ -10,26 +10,28 @@
|
|||||||
<Grid>
|
<Grid>
|
||||||
<NavigationView
|
<NavigationView
|
||||||
x:Name="NavView"
|
x:Name="NavView"
|
||||||
|
CompactPaneLength="48"
|
||||||
OpenPaneLength="172"
|
OpenPaneLength="172"
|
||||||
CompactModeThresholdWidth="360"
|
CompactModeThresholdWidth="128"
|
||||||
ExpandedModeThresholdWidth="720"
|
ExpandedModeThresholdWidth="720"
|
||||||
IsBackEnabled="{x:Bind ContentFrame.CanGoBack}">
|
IsPaneOpen="True"
|
||||||
|
IsBackEnabled="{Binding ElementName=ContentFrame,Path=CanGoBack}">
|
||||||
|
<!-- x:Bind can't get property update here seems like a WinUI 3 bug-->
|
||||||
<NavigationView.MenuItems>
|
<NavigationView.MenuItems>
|
||||||
<NavigationViewItem
|
|
||||||
Content="活动"
|
<NavigationViewItem Content="活动" helper:NavHelper.NavigateTo="page:AnnouncementPage">
|
||||||
helper:NavHelper.NavigateTo="page:AnnouncementPage">
|
|
||||||
<NavigationViewItem.Icon>
|
<NavigationViewItem.Icon>
|
||||||
<FontIcon Glyph=""/>
|
<FontIcon Glyph=""/>
|
||||||
</NavigationViewItem.Icon>
|
</NavigationViewItem.Icon>
|
||||||
</NavigationViewItem>
|
</NavigationViewItem>
|
||||||
|
|
||||||
</NavigationView.MenuItems>
|
</NavigationView.MenuItems>
|
||||||
|
|
||||||
<Frame x:Name="ContentFrame">
|
<Frame x:Name="ContentFrame">
|
||||||
<Frame.ContentTransitions>
|
<Frame.ContentTransitions>
|
||||||
<TransitionCollection>
|
<NavigationThemeTransition>
|
||||||
<NavigationThemeTransition>
|
<DrillInNavigationTransitionInfo/>
|
||||||
<DrillInNavigationTransitionInfo/>
|
</NavigationThemeTransition>
|
||||||
</NavigationThemeTransition>
|
|
||||||
</TransitionCollection>
|
|
||||||
</Frame.ContentTransitions>
|
</Frame.ContentTransitions>
|
||||||
</Frame>
|
</Frame>
|
||||||
</NavigationView>
|
</NavigationView>
|
||||||
@@ -61,9 +63,6 @@
|
|||||||
TintColor="#34424d"
|
TintColor="#34424d"
|
||||||
FallbackColor="#34424d"/>
|
FallbackColor="#34424d"/>
|
||||||
</StackPanel.Resources>
|
</StackPanel.Resources>
|
||||||
<StackPanel.OpacityTransition>
|
|
||||||
<ScalarTransition/>
|
|
||||||
</StackPanel.OpacityTransition>
|
|
||||||
<StackPanel.Transitions>
|
<StackPanel.Transitions>
|
||||||
<TransitionCollection>
|
<TransitionCollection>
|
||||||
<AddDeleteThemeTransition/>
|
<AddDeleteThemeTransition/>
|
||||||
|
|||||||
@@ -21,10 +21,12 @@ public sealed partial class MainView : UserControl
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
||||||
|
infoBarService.Initialize(InfoBarStack);
|
||||||
|
|
||||||
navigationService = Ioc.Default.GetRequiredService<INavigationService>();
|
navigationService = Ioc.Default.GetRequiredService<INavigationService>();
|
||||||
navigationService.Initialize(NavView, ContentFrame);
|
navigationService.Initialize(NavView, ContentFrame);
|
||||||
|
|
||||||
infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
navigationService.Navigate<Page.AnnouncementPage>();
|
||||||
infoBarService.Initialize(InfoBarStack);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Border
|
<Border
|
||||||
CornerRadius="{StaticResource CompatCornerRadius}"
|
CornerRadius="{StaticResource CompatCornerRadius}"
|
||||||
Background="{StaticResource SystemControlPageBackgroundAltHighBrush}"
|
Background="{ThemeResource SystemControlPageBackgroundAltHighBrush}"
|
||||||
cwui:UIElementExtensions.ClipToBounds="True">
|
cwui:UIElementExtensions.ClipToBounds="True">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@@ -105,31 +105,20 @@
|
|||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Bottom"
|
||||||
Visibility="{Binding ShouldShowTimeDescription,Converter={StaticResource BoolToVisibilityConverter}}">
|
Visibility="{Binding ShouldShowTimeDescription,Converter={StaticResource BoolToVisibilityConverter}}">
|
||||||
<Border.Background>
|
<!--<Border.Background>
|
||||||
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
|
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
|
||||||
<GradientStop Color="#00000000"/>
|
<GradientStop Color="#00000000"/>
|
||||||
<GradientStop Offset="1" Color="#A0000000"/>
|
<GradientStop Offset="1" Color="#A0000000"/>
|
||||||
</LinearGradientBrush>
|
</LinearGradientBrush>
|
||||||
</Border.Background>
|
</Border.Background>-->
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
Height="1"
|
MinHeight="2"
|
||||||
MinHeight="1"
|
|
||||||
Value="{Binding TimePercent,Mode=OneWay}"
|
Value="{Binding TimePercent,Mode=OneWay}"
|
||||||
CornerRadius="0"
|
CornerRadius="0"
|
||||||
Maximum="1"
|
Maximum="1"
|
||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Bottom"
|
||||||
Background="Transparent"/>
|
Background="Transparent"/>
|
||||||
</Border>
|
</Border>
|
||||||
<Border
|
|
||||||
Padding="8,4"
|
|
||||||
Visibility="{Binding ShouldShowTimeDescription,Converter={StaticResource BoolToVisibilityConverter}}">
|
|
||||||
<TextBlock
|
|
||||||
Opacity="0.6"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
VerticalAlignment="Bottom"
|
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
|
||||||
Text="{Binding TimeDescription}" />
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<!--General Description-->
|
<!--General Description-->
|
||||||
<Border
|
<Border
|
||||||
@@ -170,13 +159,29 @@
|
|||||||
TextTrimming="WordEllipsis"
|
TextTrimming="WordEllipsis"
|
||||||
Margin="4,6,0,0"
|
Margin="4,6,0,0"
|
||||||
Opacity="0.6"/>
|
Opacity="0.6"/>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
FontSize="10"
|
||||||
|
Opacity="0.4"
|
||||||
|
Margin="4,4,0,4"
|
||||||
|
Text="{Binding TimeFormatted}"
|
||||||
|
TextWrapping="NoWrap"/>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="1"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
FontSize="10"
|
||||||
|
Opacity="0.8"
|
||||||
|
Margin="4,4,4,4"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Text="{Binding TimeDescription}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
|
||||||
FontSize="10"
|
|
||||||
Opacity="0.4"
|
|
||||||
Margin="4,4,0,4"
|
|
||||||
Text="{Binding TimeFormatted}"/>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -17,9 +17,8 @@
|
|||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Margin="32,0,24,0">
|
Margin="32,0,24,0">
|
||||||
|
|
||||||
<controls:SettingsGroup
|
<controls:SettingsGroup Header="关于 胡桃">
|
||||||
Header="关于 胡桃">
|
|
||||||
<controls:SettingExpander>
|
<controls:SettingExpander>
|
||||||
<controls:SettingExpander.Header>
|
<controls:SettingExpander.Header>
|
||||||
<controls:Setting
|
<controls:Setting
|
||||||
|
|||||||
@@ -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 Snap.Hutao.ViewModel;
|
||||||
|
|
||||||
namespace Snap.Hutao.View.Page;
|
namespace Snap.Hutao.View.Page;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -13,6 +15,7 @@ public sealed partial class SettingPage : Microsoft.UI.Xaml.Controls.Page
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public SettingPage()
|
public SettingPage()
|
||||||
{
|
{
|
||||||
|
DataContext = Ioc.Default.GetRequiredService<SettingViewModel>();
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs
Normal file
19
src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.ViewModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 测试视图模型
|
||||||
|
/// </summary>
|
||||||
|
[Injection(InjectAs.Transient)]
|
||||||
|
internal class SettingViewModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的测试视图模型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="asyncRelayCommandFactory">异步命令工厂</param>
|
||||||
|
public SettingViewModel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +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 Newtonsoft.Json;
|
using Snap.Hutao.Core.Json.Converter;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
|
||||||
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||||
@@ -13,42 +14,6 @@ public class Announcement : AnnouncementContent
|
|||||||
{
|
{
|
||||||
private double timePercent;
|
private double timePercent;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 类型标签
|
|
||||||
/// </summary>
|
|
||||||
[JsonProperty("type_label")]
|
|
||||||
public string? TypeLabel { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 标签文本
|
|
||||||
/// </summary>
|
|
||||||
[JsonProperty("tag_label")]
|
|
||||||
public string? TagLabel { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 标签图标
|
|
||||||
/// </summary>
|
|
||||||
[JsonProperty("tag_icon")]
|
|
||||||
public string? TagIcon { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 登录提醒
|
|
||||||
/// </summary>
|
|
||||||
[JsonProperty("login_alert")]
|
|
||||||
public int LoginAlert { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 开始时间
|
|
||||||
/// </summary>
|
|
||||||
[JsonProperty("start_time")]
|
|
||||||
public DateTime StartTime { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 结束时间
|
|
||||||
/// </summary>
|
|
||||||
[JsonProperty("end_time")]
|
|
||||||
public DateTime EndTime { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 启动展示窗口的命令
|
/// 启动展示窗口的命令
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -131,45 +96,83 @@ public class Announcement : AnnouncementContent
|
|||||||
get => $"{StartTime:yyyy.MM.dd HH:mm} - {EndTime:yyyy.MM.dd HH:mm}";
|
get => $"{StartTime:yyyy.MM.dd HH:mm} - {EndTime:yyyy.MM.dd HH:mm}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 类型标签
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("type_label")]
|
||||||
|
public string? TypeLabel { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标签文本
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("tag_label")]
|
||||||
|
public string? TagLabel { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标签图标
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("tag_icon")]
|
||||||
|
public string? TagIcon { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 登录提醒
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("login_alert")]
|
||||||
|
public int LoginAlert { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开始时间
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("start_time")]
|
||||||
|
[JsonConverter(typeof(DateTimeConverter))]
|
||||||
|
public DateTime StartTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结束时间
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("end_time")]
|
||||||
|
[JsonConverter(typeof(DateTimeConverter))]
|
||||||
|
public DateTime EndTime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 类型
|
/// 类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("type")]
|
[JsonPropertyName("type")]
|
||||||
public int Type { get; set; }
|
public int Type { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 提醒
|
/// 提醒
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("remind")]
|
[JsonPropertyName("remind")]
|
||||||
public int Remind { get; set; }
|
public int Remind { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 通知
|
/// 通知
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("alert")]
|
[JsonPropertyName("alert")]
|
||||||
public int Alert { get; set; }
|
public int Alert { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 标签开始时间
|
/// 标签开始时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("tag_start_time")]
|
[JsonPropertyName("tag_start_time")]
|
||||||
public string? TagStartTime { get; set; }
|
public string? TagStartTime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 标签结束时间
|
/// 标签结束时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("tag_end_time")]
|
[JsonPropertyName("tag_end_time")]
|
||||||
public string? TagEndTime { get; set; }
|
public string? TagEndTime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 提醒版本
|
/// 提醒版本
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("remind_ver")]
|
[JsonPropertyName("remind_ver")]
|
||||||
public int RemindVer { get; set; }
|
public int RemindVer { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否含有内容
|
/// 是否含有内容
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("has_content")]
|
[JsonPropertyName("has_content")]
|
||||||
public bool HasContent { get; set; }
|
public bool HasContent { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// 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 Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||||
|
|
||||||
@@ -13,37 +13,37 @@ public class AnnouncementContent
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 公告Id
|
/// 公告Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("ann_id")]
|
[JsonPropertyName("ann_id")]
|
||||||
public int AnnId { get; set; }
|
public int AnnId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 公告标题
|
/// 公告标题
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("title")]
|
[JsonPropertyName("title")]
|
||||||
public string? Title { get; set; }
|
public string? Title { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 副标题
|
/// 副标题
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("subtitle")]
|
[JsonPropertyName("subtitle")]
|
||||||
public string? Subtitle { get; set; }
|
public string? Subtitle { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 横幅Url
|
/// 横幅Url
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("banner")]
|
[JsonPropertyName("banner")]
|
||||||
public string? Banner { get; set; }
|
public string? Banner { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 内容字符串
|
/// 内容字符串
|
||||||
/// 可能包含了一些html格式
|
/// 可能包含了一些html格式
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("content")]
|
[JsonPropertyName("content")]
|
||||||
public string? Content { get; set; }
|
public string? Content { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 语言
|
/// 语言
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("lang")]
|
[JsonPropertyName("lang")]
|
||||||
public string? Lang { get; set; }
|
public string? Lang { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +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 Newtonsoft.Json;
|
|
||||||
using Snap.Hutao.Web.Response;
|
using Snap.Hutao.Web.Response;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||||
|
|
||||||
@@ -14,12 +14,12 @@ public class AnnouncementListWrapper : ListWrapper<Announcement>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 类型Id
|
/// 类型Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("type_id")]
|
[JsonPropertyName("type_id")]
|
||||||
public int TypeId { get; set; }
|
public int TypeId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 类型标签
|
/// 类型标签
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("type_label")]
|
[JsonPropertyName("type_label")]
|
||||||
public string? TypeLabel { get; set; }
|
public string? TypeLabel { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// 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 Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||||
|
|
||||||
@@ -13,18 +13,18 @@ public class AnnouncementType
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Id
|
/// Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 名称
|
/// 名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("name")]
|
[JsonPropertyName("name")]
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 国际化名称
|
/// 国际化名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("mi18n_name")]
|
[JsonPropertyName("mi18n_name")]
|
||||||
public string? MI18NName { get; set; }
|
public string? MI18NName { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,9 +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 Newtonsoft.Json;
|
|
||||||
using Snap.Hutao.Web.Response;
|
using Snap.Hutao.Web.Response;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||||
|
|
||||||
@@ -15,36 +15,36 @@ public class AnnouncementWrapper : ListWrapper<AnnouncementListWrapper>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 总数
|
/// 总数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("total")]
|
[JsonPropertyName("total")]
|
||||||
public int Total { get; set; }
|
public int Total { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 类型列表
|
/// 类型列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("type_list")]
|
[JsonPropertyName("type_list")]
|
||||||
public List<AnnouncementType>? TypeList { get; set; }
|
public List<AnnouncementType>? TypeList { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 提醒
|
/// 提醒
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("alert")]
|
[JsonPropertyName("alert")]
|
||||||
public bool Alert { get; set; }
|
public bool Alert { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 提醒Id
|
/// 提醒Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("alert_id")]
|
[JsonPropertyName("alert_id")]
|
||||||
public int AlertId { get; set; }
|
public int AlertId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 时区
|
/// 时区
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("timezone")]
|
[JsonPropertyName("timezone")]
|
||||||
public int TimeZone { get; set; }
|
public int TimeZone { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 时间戳
|
/// 时间戳
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("t")]
|
[JsonPropertyName("t")]
|
||||||
public long TimeStamp { get; set; }
|
public string? TimeStamp { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,7 +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 Snap.Hutao.Core;
|
using Snap.Hutao.Core.Json;
|
||||||
|
using Snap.Hutao.Service.Abstraction;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace Snap.Hutao.Web.Request;
|
namespace Snap.Hutao.Web.Request;
|
||||||
@@ -16,9 +17,10 @@ public class AuthRequester : Requester
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="httpClient">Http 客户端</param>
|
/// <param name="httpClient">Http 客户端</param>
|
||||||
/// <param name="json">Json 处理器</param>
|
/// <param name="json">Json 处理器</param>
|
||||||
|
/// <param name="infoBarService">信息条服务</param>
|
||||||
/// <param name="logger">消息器</param>
|
/// <param name="logger">消息器</param>
|
||||||
public AuthRequester(HttpClient httpClient, Json json, ILogger<Requester> logger)
|
public AuthRequester(HttpClient httpClient, Json json, IInfoBarService infoBarService, ILogger<Requester> logger)
|
||||||
: base(httpClient, json, logger)
|
: base(httpClient, json, infoBarService, logger)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +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 Snap.Hutao.Core;
|
using Snap.Hutao.Core.Json;
|
||||||
|
using Snap.Hutao.Service.Abstraction;
|
||||||
using Snap.Hutao.Web.Response;
|
using Snap.Hutao.Web.Response;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -16,6 +17,7 @@ public class Requester
|
|||||||
{
|
{
|
||||||
private readonly HttpClient httpClient;
|
private readonly HttpClient httpClient;
|
||||||
private readonly Json json;
|
private readonly Json json;
|
||||||
|
private readonly IInfoBarService infoBarService;
|
||||||
private readonly ILogger<Requester> logger;
|
private readonly ILogger<Requester> logger;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -23,11 +25,13 @@ public class Requester
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="httpClient">Http 客户端</param>
|
/// <param name="httpClient">Http 客户端</param>
|
||||||
/// <param name="json">Json 处理器</param>
|
/// <param name="json">Json 处理器</param>
|
||||||
|
/// <param name="infoBarService">信息条服务</param>
|
||||||
/// <param name="logger">消息器</param>
|
/// <param name="logger">消息器</param>
|
||||||
public Requester(HttpClient httpClient, Json json, ILogger<Requester> logger)
|
public Requester(HttpClient httpClient, Json json, IInfoBarService infoBarService, ILogger<Requester> logger)
|
||||||
{
|
{
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
this.json = json;
|
this.json = json;
|
||||||
|
this.infoBarService = infoBarService;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +41,7 @@ public class Requester
|
|||||||
public RequestOptions Headers { get; set; } = new RequestOptions();
|
public RequestOptions Headers { get; set; } = new RequestOptions();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 内部使用的 <see cref="HttpClient"/>
|
/// 内部使用的 <see cref="System.Net.Http.HttpClient"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected HttpClient HttpClient { get => httpClient; }
|
protected HttpClient HttpClient { get => httpClient; }
|
||||||
|
|
||||||
@@ -50,32 +54,15 @@ public class Requester
|
|||||||
/// <returns>响应</returns>
|
/// <returns>响应</returns>
|
||||||
public async Task<Response<TResult>?> GetAsync<TResult>(string? url, CancellationToken cancellationToken = default)
|
public async Task<Response<TResult>?> GetAsync<TResult>(string? url, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
logger.LogInformation("GET {urlbase}", url?.Split('?')[0]);
|
if (url is null)
|
||||||
return url is null
|
{
|
||||||
? null
|
return Response<TResult>.CreateForEmptyUrl();
|
||||||
: await RequestAsync<TResult>(
|
}
|
||||||
client => new RequestInfo(url, () => client.GetAsync(url, cancellationToken)),
|
|
||||||
cancellationToken)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
Task<HttpResponseMessage> GetMethod(HttpClient client, CancellationToken token) => client.GetAsync(url, token);
|
||||||
/// GET 操作
|
|
||||||
/// </summary>
|
return await RequestAsync<TResult>(GetMethod, cancellationToken)
|
||||||
/// <typeparam name="TResult">返回的类类型</typeparam>
|
.ConfigureAwait(false);
|
||||||
/// <param name="url">地址</param>
|
|
||||||
/// <param name="encoding">编码</param>
|
|
||||||
/// <param name="cancellationToken">取消令牌</param>
|
|
||||||
/// <returns>响应</returns>
|
|
||||||
public async Task<Response<TResult>?> GetAsync<TResult>(string? url, Encoding encoding, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
logger.LogInformation("GET {urlbase}", url?.Split('?')[0]);
|
|
||||||
return url is null
|
|
||||||
? null
|
|
||||||
: await RequestAsync<TResult>(
|
|
||||||
client => new RequestInfo(url, () => client.GetAsync(url, cancellationToken), encoding),
|
|
||||||
cancellationToken)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -88,13 +75,17 @@ public class Requester
|
|||||||
/// <returns>响应</returns>
|
/// <returns>响应</returns>
|
||||||
public async Task<Response<TResult>?> PostAsync<TResult>(string? url, object data, CancellationToken cancellationToken = default)
|
public async Task<Response<TResult>?> PostAsync<TResult>(string? url, object data, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
if (url is null)
|
||||||
|
{
|
||||||
|
return Response<TResult>.CreateForEmptyUrl();
|
||||||
|
}
|
||||||
|
|
||||||
string dataString = json.Stringify(data);
|
string dataString = json.Stringify(data);
|
||||||
logger.LogInformation("POST {urlbase} with\n{dataString}", url?.Split('?')[0], dataString);
|
HttpContent content = new StringContent(dataString);
|
||||||
return url is null
|
|
||||||
? null
|
Task<HttpResponseMessage> PostMethod(HttpClient client, CancellationToken token) => client.PostAsync(url, content, token);
|
||||||
: await RequestAsync<TResult>(
|
|
||||||
client => new RequestInfo(url, () => client.PostAsync(url, new StringContent(dataString), cancellationToken)),
|
return await RequestAsync<TResult>(PostMethod, cancellationToken)
|
||||||
cancellationToken)
|
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,13 +100,17 @@ public class Requester
|
|||||||
/// <returns>响应</returns>
|
/// <returns>响应</returns>
|
||||||
public async Task<Response<TResult>?> PostAsync<TResult>(string? url, object data, string contentType, CancellationToken cancellationToken = default)
|
public async Task<Response<TResult>?> PostAsync<TResult>(string? url, object data, string contentType, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
if (url is null)
|
||||||
|
{
|
||||||
|
return Response<TResult>.CreateForEmptyUrl();
|
||||||
|
}
|
||||||
|
|
||||||
string dataString = json.Stringify(data);
|
string dataString = json.Stringify(data);
|
||||||
logger.LogInformation("POST {urlbase} with\n{dataString}", url?.Split('?')[0], dataString);
|
HttpContent content = new StringContent(dataString, Encoding.UTF8, contentType);
|
||||||
return url is null
|
|
||||||
? null
|
Task<HttpResponseMessage> PostMethod(HttpClient client, CancellationToken token) => client.PostAsync(url, content, token);
|
||||||
: await RequestAsync<TResult>(
|
|
||||||
client => new RequestInfo(url, () => client.PostAsync(url, new StringContent(dataString, Encoding.UTF8, contentType), cancellationToken)),
|
return await RequestAsync<TResult>(PostMethod, cancellationToken)
|
||||||
cancellationToken)
|
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,53 +150,38 @@ public class Requester
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Response<TResult>?> RequestAsync<TResult>(Func<HttpClient, RequestInfo> requestFunc, CancellationToken cancellationToken = default)
|
private async Task<Response<TResult>?> RequestAsync<TResult>(
|
||||||
|
Func<HttpClient, CancellationToken, Task<HttpResponseMessage>> requestFunc,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
PrepareHttpClient();
|
PrepareHttpClient();
|
||||||
RequestInfo? info = requestFunc(HttpClient);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await info.RequestAsyncFunc.Invoke()
|
HttpResponseMessage response = await requestFunc
|
||||||
|
.Invoke(HttpClient, cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
string contentString = await response.Content.ReadAsStringAsync(cancellationToken)
|
string contentString = await response.Content
|
||||||
|
.ReadAsStringAsync(cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
if (info.Encoding is not null)
|
Response<TResult>? resp = json.ToObject<Response<TResult>>(contentString);
|
||||||
|
if (resp?.ToString() is string representable)
|
||||||
{
|
{
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(contentString);
|
infoBarService.Information(representable);
|
||||||
info.Encoding.GetString(bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.LogInformation("Response String :{contentString}", contentString);
|
return resp;
|
||||||
|
|
||||||
return json.ToObject<Response<TResult>>(contentString);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogError(ex, "请求时遇到问题");
|
logger.LogError(ex, "请求时遇到问题");
|
||||||
return Response<TResult>.CreateFail($"{ex.Message}");
|
return Response<TResult>.CreateForException($"{ex.Message}");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
logger.LogInformation("Request Completed");
|
logger.LogInformation("Request Completed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private record RequestInfo
|
|
||||||
{
|
|
||||||
public RequestInfo(string url, Func<Task<HttpResponseMessage>> httpResponseMessage, Encoding? encoding = null)
|
|
||||||
{
|
|
||||||
Url = url;
|
|
||||||
RequestAsyncFunc = httpResponseMessage;
|
|
||||||
Encoding = encoding;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Url { get; set; }
|
|
||||||
|
|
||||||
public Func<Task<HttpResponseMessage>> RequestAsyncFunc { get; set; }
|
|
||||||
|
|
||||||
public Encoding? Encoding { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -8,10 +8,15 @@ namespace Snap.Hutao.Web.Response;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public enum KnownReturnCode
|
public enum KnownReturnCode
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Url为 空
|
||||||
|
/// </summary>
|
||||||
|
UrlIsEmpty = -2000000001,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 内部错误
|
/// 内部错误
|
||||||
/// </summary>
|
/// </summary>
|
||||||
InternalFailure = int.MinValue,
|
InternalFailure = -2000000000,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 已经签到过了
|
/// 已经签到过了
|
||||||
|
|||||||
@@ -1,8 +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 Newtonsoft.Json;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Snap.Hutao.Web.Response;
|
namespace Snap.Hutao.Web.Response;
|
||||||
|
|
||||||
@@ -15,5 +15,6 @@ public class ListWrapper<T>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 列表
|
/// 列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("list")] public List<T>? List { get; set; }
|
[JsonPropertyName("list")]
|
||||||
|
public List<T>? List { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// 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 Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Snap.Hutao.Web.Response;
|
namespace Snap.Hutao.Web.Response;
|
||||||
|
|
||||||
@@ -13,13 +13,13 @@ public class Response
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 返回代码
|
/// 返回代码
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("retcode")]
|
[JsonPropertyName("retcode")]
|
||||||
public int ReturnCode { get; set; }
|
public int ReturnCode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 消息
|
/// 消息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("message")]
|
[JsonPropertyName("message")]
|
||||||
public string? Message { get; set; }
|
public string? Message { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -37,7 +37,7 @@ public class Response
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="message">消息</param>
|
/// <param name="message">消息</param>
|
||||||
/// <returns>响应</returns>
|
/// <returns>响应</returns>
|
||||||
public static Response CreateFail(string message)
|
public static Response CreateForException(string message)
|
||||||
{
|
{
|
||||||
return new Response()
|
return new Response()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// 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 Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Snap.Hutao.Web.Response;
|
namespace Snap.Hutao.Web.Response;
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ public class Response<TData> : Response
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据
|
/// 数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("data")]
|
[JsonPropertyName("data")]
|
||||||
public TData? Data { get; set; }
|
public TData? Data { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -22,7 +22,7 @@ public class Response<TData> : Response
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="message">消息</param>
|
/// <param name="message">消息</param>
|
||||||
/// <returns>响应</returns>
|
/// <returns>响应</returns>
|
||||||
public static new Response<TData> CreateFail(string message)
|
public static new Response<TData> CreateForException(string message)
|
||||||
{
|
{
|
||||||
return new Response<TData>()
|
return new Response<TData>()
|
||||||
{
|
{
|
||||||
@@ -30,4 +30,17 @@ public class Response<TData> : Response
|
|||||||
Message = message,
|
Message = message,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个空Url的响应
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>响应</returns>
|
||||||
|
public static Response<TData> CreateForEmptyUrl()
|
||||||
|
{
|
||||||
|
return new Response<TData>()
|
||||||
|
{
|
||||||
|
ReturnCode = (int)KnownReturnCode.UrlIsEmpty,
|
||||||
|
Message = "请求的 Url 不应为空",
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user