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.UI.Xaml;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Web.Request;
|
||||
|
||||
namespace Snap.Hutao;
|
||||
|
||||
@@ -40,29 +38,13 @@ public partial class App : Application
|
||||
|
||||
private static void InitializeDependencyInjection()
|
||||
{
|
||||
// prepare DI
|
||||
IServiceProvider services = new ServiceCollection()
|
||||
.AddLogging(builder => builder.AddDebug())
|
||||
|
||||
// http json
|
||||
.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
|
||||
.AddHttpClients()
|
||||
.AddDefaultJsonSerializerOptions()
|
||||
.AddInjections(typeof(App))
|
||||
.BuildServiceProvider();
|
||||
|
||||
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;
|
||||
|
||||
namespace Snap.Hutao.Core;
|
||||
namespace Snap.Hutao.Core.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Http Json 处理
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Snap.Hutao.Core;
|
||||
namespace Snap.Hutao.Core.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Json操作
|
||||
@@ -12,20 +12,17 @@ namespace Snap.Hutao.Core;
|
||||
[Injection(InjectAs.Transient)]
|
||||
public class Json
|
||||
{
|
||||
private readonly JsonSerializerOptions jsonSerializerOptions;
|
||||
private readonly ILogger logger;
|
||||
|
||||
private readonly JsonSerializerSettings jsonSerializerSettings = new()
|
||||
{
|
||||
DateFormatString = "yyyy'-'MM'-'dd' 'HH':'mm':'ss.FFFFFFFK",
|
||||
Formatting = Formatting.Indented,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 初始化一个新的 Json操作 实例
|
||||
/// </summary>
|
||||
/// <param name="jsonSerializerOptions">配置</param>
|
||||
/// <param name="logger">日志器</param>
|
||||
public Json(ILogger<Json> logger)
|
||||
public Json(JsonSerializerOptions jsonSerializerOptions, ILogger<Json> logger)
|
||||
{
|
||||
this.jsonSerializerOptions = jsonSerializerOptions;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@@ -34,19 +31,20 @@ public class Json
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要反序列化的对象的类型</typeparam>
|
||||
/// <param name="value">要反序列化的JSON</param>
|
||||
/// <returns>Json字符串中的反序列化对象, 如果反序列化失败会抛出异常</returns>
|
||||
/// <returns>Json字符串中的反序列化对象, 如果反序列化失败会返回 <see langword="default"/></returns>
|
||||
public T? ToObject<T>(string value)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(value);
|
||||
T? result = JsonSerializer.Deserialize<T>(value);
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError("反序列化Json时遇到问题:{ex}", ex);
|
||||
logger.LogError("反序列化Json时遇到问题\n{ex}", ex);
|
||||
}
|
||||
|
||||
return default;
|
||||
return default(T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -69,7 +67,7 @@ public class Json
|
||||
/// <returns>对象的JSON字符串表示形式</returns>
|
||||
public string Stringify(object? value)
|
||||
{
|
||||
return JsonConvert.SerializeObject(value, jsonSerializerSettings);
|
||||
return JsonSerializer.Serialize(value, jsonSerializerOptions);
|
||||
}
|
||||
|
||||
/// <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.
|
||||
|
||||
using Microsoft;
|
||||
using Snap.Hutao.Model;
|
||||
|
||||
namespace Snap.Hutao.Core.Threading;
|
||||
|
||||
@@ -13,6 +14,7 @@ namespace Snap.Hutao.Core.Threading;
|
||||
public class Watcher : Observable
|
||||
{
|
||||
private readonly bool isReusable;
|
||||
|
||||
private bool hasUsed;
|
||||
private bool isWorking;
|
||||
private bool isCompleted;
|
||||
@@ -48,33 +50,33 @@ public class Watcher : Observable
|
||||
|
||||
/// <summary>
|
||||
/// 对某个操作进行监视,
|
||||
/// 无法防止代码重入
|
||||
/// </summary>
|
||||
/// <returns>一个可释放的对象,用于在操作完成时自动提示监视器工作已经完成</returns>
|
||||
/// <exception cref="InvalidOperationException">重用了一个不可重用的监视器</exception>
|
||||
public IDisposable Watch()
|
||||
{
|
||||
Verify.Operation(!IsWorking, $"此 {nameof(Watcher)} 已经处于检查状态");
|
||||
Verify.Operation(isReusable || !hasUsed, $"此 {nameof(Watcher)} 不允许多次使用");
|
||||
|
||||
hasUsed = 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()
|
||||
{
|
||||
work.IsWorking = false;
|
||||
work.IsCompleted = true;
|
||||
watcher.IsWorking = false;
|
||||
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>
|
||||
[Injection(InjectAs.Transient)]
|
||||
[Injection(InjectAs.Singleton)]
|
||||
public sealed partial class MainWindow : Window
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Snap.Hutao.Core;
|
||||
namespace Snap.Hutao.Model;
|
||||
|
||||
/// <summary>
|
||||
/// 简单的实现了 <see cref="INotifyPropertyChanged"/> 接口
|
||||
@@ -61,13 +61,6 @@ public interface IInfoBarService
|
||||
/// <param name="container">信息条的目标容器</param>
|
||||
void Initialize(StackPanel container);
|
||||
|
||||
/// <summary>
|
||||
/// 显示特定的信息条
|
||||
/// </summary>
|
||||
/// <param name="infoBar">信息条</param>
|
||||
/// <param name="delay">关闭延迟</param>
|
||||
void Show(InfoBar infoBar, int delay = 0);
|
||||
|
||||
/// <summary>
|
||||
/// 显示成功信息
|
||||
/// </summary>
|
||||
|
||||
@@ -61,5 +61,5 @@ public interface INavigationService
|
||||
/// </summary>
|
||||
/// <param name="pageType">同步的页面类型</param>
|
||||
/// <returns>是否同步成功</returns>
|
||||
bool SyncTabWith(Type pageType);
|
||||
bool SyncSelectedNavigationViewItemWith(Type pageType);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
|
||||
namespace Snap.Hutao.Service;
|
||||
@@ -79,25 +78,25 @@ internal class InfoBarService : IInfoBarService
|
||||
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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
@@ -107,7 +106,14 @@ internal class InfoBarService : IInfoBarService
|
||||
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)
|
||||
|
||||
@@ -66,9 +66,9 @@ internal class NavigationService : INavigationService
|
||||
public bool HasEverNavigated { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool SyncTabWith(Type pageType)
|
||||
public bool SyncSelectedNavigationViewItemWith(Type? pageType)
|
||||
{
|
||||
if (NavigationView is null)
|
||||
if (NavigationView is null || pageType is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ internal class NavigationService : INavigationService
|
||||
return false;
|
||||
}
|
||||
|
||||
_ = isSyncTabRequested && SyncTabWith(pageType);
|
||||
_ = isSyncTabRequested && SyncSelectedNavigationViewItemWith(pageType);
|
||||
|
||||
bool result = false;
|
||||
try
|
||||
@@ -151,6 +151,7 @@ internal class NavigationService : INavigationService
|
||||
if (Frame != null && Frame.CanGoBack)
|
||||
{
|
||||
Frame.GoBack();
|
||||
SyncSelectedNavigationViewItemWith(Frame.Content.GetType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||
<NeutralLanguage>zh-CN</NeutralLanguage>
|
||||
<DefaultLanguage>zh-CN</DefaultLanguage>
|
||||
<DefaultLanguage>zh-cn</DefaultLanguage>
|
||||
|
||||
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
|
||||
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
|
||||
@@ -48,14 +48,16 @@
|
||||
|
||||
<ItemGroup>
|
||||
<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.UI.Animations" 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="Microsoft.AppCenter.Analytics" 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.Logging.Debug" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.1.46" />
|
||||
@@ -63,10 +65,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</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="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.406">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -83,9 +82,6 @@
|
||||
<ItemGroup>
|
||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Model\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\SettingsUI\SettingsUI.csproj" />
|
||||
</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.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.UI.Converters;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Snap.Hutao.View.Converter;
|
||||
|
||||
@@ -10,26 +10,28 @@
|
||||
<Grid>
|
||||
<NavigationView
|
||||
x:Name="NavView"
|
||||
CompactPaneLength="48"
|
||||
OpenPaneLength="172"
|
||||
CompactModeThresholdWidth="360"
|
||||
CompactModeThresholdWidth="128"
|
||||
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>
|
||||
<NavigationViewItem
|
||||
Content="活动"
|
||||
helper:NavHelper.NavigateTo="page:AnnouncementPage">
|
||||
|
||||
<NavigationViewItem Content="活动" helper:NavHelper.NavigateTo="page:AnnouncementPage">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon Glyph=""/>
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
|
||||
</NavigationView.MenuItems>
|
||||
|
||||
<Frame x:Name="ContentFrame">
|
||||
<Frame.ContentTransitions>
|
||||
<TransitionCollection>
|
||||
<NavigationThemeTransition>
|
||||
<DrillInNavigationTransitionInfo/>
|
||||
</NavigationThemeTransition>
|
||||
</TransitionCollection>
|
||||
<NavigationThemeTransition>
|
||||
<DrillInNavigationTransitionInfo/>
|
||||
</NavigationThemeTransition>
|
||||
</Frame.ContentTransitions>
|
||||
</Frame>
|
||||
</NavigationView>
|
||||
@@ -61,9 +63,6 @@
|
||||
TintColor="#34424d"
|
||||
FallbackColor="#34424d"/>
|
||||
</StackPanel.Resources>
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition/>
|
||||
</StackPanel.OpacityTransition>
|
||||
<StackPanel.Transitions>
|
||||
<TransitionCollection>
|
||||
<AddDeleteThemeTransition/>
|
||||
|
||||
@@ -21,10 +21,12 @@ public sealed partial class MainView : UserControl
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
||||
infoBarService.Initialize(InfoBarStack);
|
||||
|
||||
navigationService = Ioc.Default.GetRequiredService<INavigationService>();
|
||||
navigationService.Initialize(NavView, ContentFrame);
|
||||
|
||||
infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
||||
infoBarService.Initialize(InfoBarStack);
|
||||
navigationService.Navigate<Page.AnnouncementPage>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<DataTemplate>
|
||||
<Border
|
||||
CornerRadius="{StaticResource CompatCornerRadius}"
|
||||
Background="{StaticResource SystemControlPageBackgroundAltHighBrush}"
|
||||
Background="{ThemeResource SystemControlPageBackgroundAltHighBrush}"
|
||||
cwui:UIElementExtensions.ClipToBounds="True">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
@@ -105,31 +105,20 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Visibility="{Binding ShouldShowTimeDescription,Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Border.Background>
|
||||
<!--<Border.Background>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
|
||||
<GradientStop Color="#00000000"/>
|
||||
<GradientStop Offset="1" Color="#A0000000"/>
|
||||
</LinearGradientBrush>
|
||||
</Border.Background>
|
||||
</Border.Background>-->
|
||||
<ProgressBar
|
||||
Height="1"
|
||||
MinHeight="1"
|
||||
MinHeight="2"
|
||||
Value="{Binding TimePercent,Mode=OneWay}"
|
||||
CornerRadius="0"
|
||||
Maximum="1"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="Transparent"/>
|
||||
</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>
|
||||
<!--General Description-->
|
||||
<Border
|
||||
@@ -170,13 +159,29 @@
|
||||
TextTrimming="WordEllipsis"
|
||||
Margin="4,6,0,0"
|
||||
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>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
@@ -17,9 +17,8 @@
|
||||
<ScrollViewer>
|
||||
<StackPanel
|
||||
Margin="32,0,24,0">
|
||||
|
||||
<controls:SettingsGroup
|
||||
Header="关于 胡桃">
|
||||
|
||||
<controls:SettingsGroup Header="关于 胡桃">
|
||||
<controls:SettingExpander>
|
||||
<controls:SettingExpander.Header>
|
||||
<controls:Setting
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.ViewModel;
|
||||
|
||||
namespace Snap.Hutao.View.Page;
|
||||
|
||||
/// <summary>
|
||||
@@ -13,6 +15,7 @@ public sealed partial class SettingPage : Microsoft.UI.Xaml.Controls.Page
|
||||
/// </summary>
|
||||
public SettingPage()
|
||||
{
|
||||
DataContext = Ioc.Default.GetRequiredService<SettingViewModel>();
|
||||
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.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Snap.Hutao.Core.Json.Converter;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||
@@ -13,42 +14,6 @@ public class Announcement : AnnouncementContent
|
||||
{
|
||||
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>
|
||||
@@ -131,45 +96,83 @@ public class Announcement : AnnouncementContent
|
||||
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>
|
||||
[JsonProperty("type")]
|
||||
[JsonPropertyName("type")]
|
||||
public int Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提醒
|
||||
/// </summary>
|
||||
[JsonProperty("remind")]
|
||||
[JsonPropertyName("remind")]
|
||||
public int Remind { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知
|
||||
/// </summary>
|
||||
[JsonProperty("alert")]
|
||||
[JsonPropertyName("alert")]
|
||||
public int Alert { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 标签开始时间
|
||||
/// </summary>
|
||||
[JsonProperty("tag_start_time")]
|
||||
[JsonPropertyName("tag_start_time")]
|
||||
public string? TagStartTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 标签结束时间
|
||||
/// </summary>
|
||||
[JsonProperty("tag_end_time")]
|
||||
[JsonPropertyName("tag_end_time")]
|
||||
public string? TagEndTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提醒版本
|
||||
/// </summary>
|
||||
[JsonProperty("remind_ver")]
|
||||
[JsonPropertyName("remind_ver")]
|
||||
public int RemindVer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否含有内容
|
||||
/// </summary>
|
||||
[JsonProperty("has_content")]
|
||||
[JsonPropertyName("has_content")]
|
||||
public bool HasContent { get; set; }
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||
|
||||
@@ -13,37 +13,37 @@ public class AnnouncementContent
|
||||
/// <summary>
|
||||
/// 公告Id
|
||||
/// </summary>
|
||||
[JsonProperty("ann_id")]
|
||||
[JsonPropertyName("ann_id")]
|
||||
public int AnnId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公告标题
|
||||
/// </summary>
|
||||
[JsonProperty("title")]
|
||||
[JsonPropertyName("title")]
|
||||
public string? Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 副标题
|
||||
/// </summary>
|
||||
[JsonProperty("subtitle")]
|
||||
[JsonPropertyName("subtitle")]
|
||||
public string? Subtitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 横幅Url
|
||||
/// </summary>
|
||||
[JsonProperty("banner")]
|
||||
[JsonPropertyName("banner")]
|
||||
public string? Banner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 内容字符串
|
||||
/// 可能包含了一些html格式
|
||||
/// </summary>
|
||||
[JsonProperty("content")]
|
||||
[JsonPropertyName("content")]
|
||||
public string? Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 语言
|
||||
/// </summary>
|
||||
[JsonProperty("lang")]
|
||||
[JsonPropertyName("lang")]
|
||||
public string? Lang { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||
|
||||
@@ -14,12 +14,12 @@ public class AnnouncementListWrapper : ListWrapper<Announcement>
|
||||
/// <summary>
|
||||
/// 类型Id
|
||||
/// </summary>
|
||||
[JsonProperty("type_id")]
|
||||
[JsonPropertyName("type_id")]
|
||||
public int TypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 类型标签
|
||||
/// </summary>
|
||||
[JsonProperty("type_label")]
|
||||
[JsonPropertyName("type_label")]
|
||||
public string? TypeLabel { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||
|
||||
@@ -13,18 +13,18 @@ public class AnnouncementType
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
[JsonProperty("name")]
|
||||
[JsonPropertyName("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 国际化名称
|
||||
/// </summary>
|
||||
[JsonProperty("mi18n_name")]
|
||||
[JsonPropertyName("mi18n_name")]
|
||||
public string? MI18NName { get; set; }
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||
|
||||
@@ -15,36 +15,36 @@ public class AnnouncementWrapper : ListWrapper<AnnouncementListWrapper>
|
||||
/// <summary>
|
||||
/// 总数
|
||||
/// </summary>
|
||||
[JsonProperty("total")]
|
||||
[JsonPropertyName("total")]
|
||||
public int Total { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 类型列表
|
||||
/// </summary>
|
||||
[JsonProperty("type_list")]
|
||||
[JsonPropertyName("type_list")]
|
||||
public List<AnnouncementType>? TypeList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提醒
|
||||
/// </summary>
|
||||
[JsonProperty("alert")]
|
||||
[JsonPropertyName("alert")]
|
||||
public bool Alert { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提醒Id
|
||||
/// </summary>
|
||||
[JsonProperty("alert_id")]
|
||||
[JsonPropertyName("alert_id")]
|
||||
public int AlertId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时区
|
||||
/// </summary>
|
||||
[JsonProperty("timezone")]
|
||||
[JsonPropertyName("timezone")]
|
||||
public int TimeZone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时间戳
|
||||
/// </summary>
|
||||
[JsonProperty("t")]
|
||||
public long TimeStamp { get; set; }
|
||||
[JsonPropertyName("t")]
|
||||
public string? TimeStamp { get; set; }
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Core.Json;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Snap.Hutao.Web.Request;
|
||||
@@ -16,9 +17,10 @@ public class AuthRequester : Requester
|
||||
/// </summary>
|
||||
/// <param name="httpClient">Http 客户端</param>
|
||||
/// <param name="json">Json 处理器</param>
|
||||
/// <param name="infoBarService">信息条服务</param>
|
||||
/// <param name="logger">消息器</param>
|
||||
public AuthRequester(HttpClient httpClient, Json json, ILogger<Requester> logger)
|
||||
: base(httpClient, json, logger)
|
||||
public AuthRequester(HttpClient httpClient, Json json, IInfoBarService infoBarService, ILogger<Requester> logger)
|
||||
: base(httpClient, json, infoBarService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// 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 System.Net.Http;
|
||||
using System.Text;
|
||||
@@ -16,6 +17,7 @@ public class Requester
|
||||
{
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly Json json;
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly ILogger<Requester> logger;
|
||||
|
||||
/// <summary>
|
||||
@@ -23,11 +25,13 @@ public class Requester
|
||||
/// </summary>
|
||||
/// <param name="httpClient">Http 客户端</param>
|
||||
/// <param name="json">Json 处理器</param>
|
||||
/// <param name="infoBarService">信息条服务</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.json = json;
|
||||
this.infoBarService = infoBarService;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@@ -37,7 +41,7 @@ public class Requester
|
||||
public RequestOptions Headers { get; set; } = new RequestOptions();
|
||||
|
||||
/// <summary>
|
||||
/// 内部使用的 <see cref="HttpClient"/>
|
||||
/// 内部使用的 <see cref="System.Net.Http.HttpClient"/>
|
||||
/// </summary>
|
||||
protected HttpClient HttpClient { get => httpClient; }
|
||||
|
||||
@@ -50,32 +54,15 @@ public class Requester
|
||||
/// <returns>响应</returns>
|
||||
public async Task<Response<TResult>?> GetAsync<TResult>(string? url, 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)),
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
if (url is null)
|
||||
{
|
||||
return Response<TResult>.CreateForEmptyUrl();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GET 操作
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">返回的类类型</typeparam>
|
||||
/// <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);
|
||||
Task<HttpResponseMessage> GetMethod(HttpClient client, CancellationToken token) => client.GetAsync(url, token);
|
||||
|
||||
return await RequestAsync<TResult>(GetMethod, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -88,13 +75,17 @@ public class Requester
|
||||
/// <returns>响应</returns>
|
||||
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);
|
||||
logger.LogInformation("POST {urlbase} with\n{dataString}", url?.Split('?')[0], dataString);
|
||||
return url is null
|
||||
? null
|
||||
: await RequestAsync<TResult>(
|
||||
client => new RequestInfo(url, () => client.PostAsync(url, new StringContent(dataString), cancellationToken)),
|
||||
cancellationToken)
|
||||
HttpContent content = new StringContent(dataString);
|
||||
|
||||
Task<HttpResponseMessage> PostMethod(HttpClient client, CancellationToken token) => client.PostAsync(url, content, token);
|
||||
|
||||
return await RequestAsync<TResult>(PostMethod, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -109,13 +100,17 @@ public class Requester
|
||||
/// <returns>响应</returns>
|
||||
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);
|
||||
logger.LogInformation("POST {urlbase} with\n{dataString}", url?.Split('?')[0], dataString);
|
||||
return url is null
|
||||
? null
|
||||
: await RequestAsync<TResult>(
|
||||
client => new RequestInfo(url, () => client.PostAsync(url, new StringContent(dataString, Encoding.UTF8, contentType), cancellationToken)),
|
||||
cancellationToken)
|
||||
HttpContent content = new StringContent(dataString, Encoding.UTF8, contentType);
|
||||
|
||||
Task<HttpResponseMessage> PostMethod(HttpClient client, CancellationToken token) => client.PostAsync(url, content, token);
|
||||
|
||||
return await RequestAsync<TResult>(PostMethod, cancellationToken)
|
||||
.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();
|
||||
RequestInfo? info = requestFunc(HttpClient);
|
||||
|
||||
try
|
||||
{
|
||||
HttpResponseMessage response = await info.RequestAsyncFunc.Invoke()
|
||||
HttpResponseMessage response = await requestFunc
|
||||
.Invoke(HttpClient, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
string contentString = await response.Content.ReadAsStringAsync(cancellationToken)
|
||||
string contentString = await response.Content
|
||||
.ReadAsStringAsync(cancellationToken)
|
||||
.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);
|
||||
info.Encoding.GetString(bytes);
|
||||
infoBarService.Information(representable);
|
||||
}
|
||||
|
||||
logger.LogInformation("Response String :{contentString}", contentString);
|
||||
|
||||
return json.ToObject<Response<TResult>>(contentString);
|
||||
return resp;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "请求时遇到问题");
|
||||
return Response<TResult>.CreateFail($"{ex.Message}");
|
||||
return Response<TResult>.CreateForException($"{ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
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>
|
||||
public enum KnownReturnCode
|
||||
{
|
||||
/// <summary>
|
||||
/// Url为 空
|
||||
/// </summary>
|
||||
UrlIsEmpty = -2000000001,
|
||||
|
||||
/// <summary>
|
||||
/// 内部错误
|
||||
/// </summary>
|
||||
InternalFailure = int.MinValue,
|
||||
InternalFailure = -2000000000,
|
||||
|
||||
/// <summary>
|
||||
/// 已经签到过了
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Snap.Hutao.Web.Response;
|
||||
|
||||
@@ -15,5 +15,6 @@ public class ListWrapper<T>
|
||||
/// <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.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Snap.Hutao.Web.Response;
|
||||
|
||||
@@ -13,13 +13,13 @@ public class Response
|
||||
/// <summary>
|
||||
/// 返回代码
|
||||
/// </summary>
|
||||
[JsonProperty("retcode")]
|
||||
[JsonPropertyName("retcode")]
|
||||
public int ReturnCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息
|
||||
/// </summary>
|
||||
[JsonProperty("message")]
|
||||
[JsonPropertyName("message")]
|
||||
public string? Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -37,7 +37,7 @@ public class Response
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <returns>响应</returns>
|
||||
public static Response CreateFail(string message)
|
||||
public static Response CreateForException(string message)
|
||||
{
|
||||
return new Response()
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Snap.Hutao.Web.Response;
|
||||
|
||||
@@ -14,7 +14,7 @@ public class Response<TData> : Response
|
||||
/// <summary>
|
||||
/// 数据
|
||||
/// </summary>
|
||||
[JsonProperty("data")]
|
||||
[JsonPropertyName("data")]
|
||||
public TData? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -22,7 +22,7 @@ public class Response<TData> : Response
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <returns>响应</returns>
|
||||
public static new Response<TData> CreateFail(string message)
|
||||
public static new Response<TData> CreateForException(string message)
|
||||
{
|
||||
return new Response<TData>()
|
||||
{
|
||||
@@ -30,4 +30,17 @@ public class Response<TData> : Response
|
||||
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