diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
index 787ecdfa..0b917cbd 100644
--- a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
@@ -44,7 +44,10 @@ public partial class App : Application
public static Window? Window { get => window; set => window = value; }
///
- public static new App Current => (App)Application.Current;
+ public static new App Current
+ {
+ get => (App)Application.Current;
+ }
///
/// Invoked when the application is launched.
diff --git a/src/Snap.Hutao/Snap.Hutao/Context/Database/LogDbContextDesignTimeFactory.cs b/src/Snap.Hutao/Snap.Hutao/Context/Database/LogDbContextDesignTimeFactory.cs
index add72b09..532fd516 100644
--- a/src/Snap.Hutao/Snap.Hutao/Context/Database/LogDbContextDesignTimeFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Context/Database/LogDbContextDesignTimeFactory.cs
@@ -1,11 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Snap.Hutao.Context.FileSystem;
-using Snap.Hutao.Core.Logging;
-using Snap.Hutao.Model.Entity;
namespace Snap.Hutao.Context.Database;
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Behavior/InvokeCommandOnLoadedBehavior.cs b/src/Snap.Hutao/Snap.Hutao/Control/Behavior/InvokeCommandOnLoadedBehavior.cs
index 14016952..d5520122 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Behavior/InvokeCommandOnLoadedBehavior.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Behavior/InvokeCommandOnLoadedBehavior.cs
@@ -20,8 +20,8 @@ internal class InvokeCommandOnLoadedBehavior : BehaviorBase
///
public ICommand Command
{
- get { return (ICommand)GetValue(CommandProperty); }
- set { SetValue(CommandProperty, value); }
+ get => (ICommand)GetValue(CommandProperty);
+ set => SetValue(CommandProperty, value);
}
///
@@ -30,8 +30,8 @@ internal class InvokeCommandOnLoadedBehavior : BehaviorBase
[MaybeNull]
public object CommandParameter
{
- get { return GetValue(CommandParameterProperty); }
- set { SetValue(CommandParameterProperty, value); }
+ get => GetValue(CommandParameterProperty);
+ set => SetValue(CommandParameterProperty, value);
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
index 954a6284..7d443117 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CachedImage.cs
@@ -33,7 +33,7 @@ public class CachedImage : ImageEx
BitmapImage? image;
try
{
- image = await imageCache.GetFromCacheAsync(imageUri, true, token);
+ image = await imageCache.GetFromCacheAsync(imageUri, true);
}
catch (TaskCanceledException)
{
@@ -54,7 +54,7 @@ public class CachedImage : ImageEx
}
else
{
- return Must.NotNull(image);
+ return Must.NotNull(image!);
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs
index dba607d7..801e836e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs
@@ -1,15 +1,14 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using CommunityToolkit.WinUI.Helpers;
using CommunityToolkit.WinUI.UI.Animations;
using Microsoft.UI;
using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Hosting;
using Microsoft.UI.Xaml.Media;
-using Snap.Hutao.Context.FileSystem;
using Snap.Hutao.Core;
+using Snap.Hutao.Core.Caching;
using Snap.Hutao.Core.Threading;
using Snap.Hutao.Extension;
using Snap.Hutao.Service.Abstraction;
@@ -64,6 +63,11 @@ public class Gradient : Microsoft.UI.Xaml.Controls.Control
.Error(exception, "应用渐变背景时发生异常");
}
+ private static Task GetCachedFileAsync(string url)
+ {
+ return Ioc.Default.GetRequiredService().GetFileFromCacheAsync(new(url));
+ }
+
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.NewSize != e.PreviousSize && spriteVisual is not null)
@@ -76,22 +80,16 @@ public class Gradient : Microsoft.UI.Xaml.Controls.Control
{
if (spriteVisual is not null)
{
- double width = ActualWidth;
- double height = Math.Clamp(width * imageAspectRatio, 0, MaxHeight);
-
- spriteVisual.Size = new Vector2((float)width, (float)height);
- Height = height;
+ Height = (double)Math.Clamp(ActualWidth / imageAspectRatio, 0, MaxHeight);
+ spriteVisual.Size = ActualSize;
}
}
private async Task ApplyImageAsync(string url, CancellationToken token)
{
- await AnimationBuilder
- .Create()
- .Opacity(0d)
- .StartAsync(this, token);
+ await AnimationBuilder.Create().Opacity(0d).StartAsync(this, token);
- StorageFile storageFile = await GetCachedFileAsync(url, token);
+ StorageFile? storageFile = Must.NotNull((await GetCachedFileAsync(url))!);
Compositor compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
@@ -104,7 +102,6 @@ public class Gradient : Microsoft.UI.Xaml.Controls.Control
CompositionEffectBrush gradientEffectBrush = compositor.CompositeBlendEffectBrush(backgroundBrush, foregroundBrush);
CompositionEffectBrush opacityMaskEffectBrush = compositor.CompositeLuminanceToAlphaEffectBrush(gradientEffectBrush);
- compositor.CreateMaskBrush();
CompositionEffectBrush alphaMaskEffectBrush = compositor.CompositeAlphaMaskEffectBrush(imageSurfaceBrush, opacityMaskEffectBrush);
spriteVisual = compositor.CompositeSpriteVisual(alphaMaskEffectBrush);
@@ -112,40 +109,7 @@ public class Gradient : Microsoft.UI.Xaml.Controls.Control
ElementCompositionPreview.SetElementChildVisual(this, spriteVisual);
- await AnimationBuilder
- .Create()
- .Opacity(1d)
- .StartAsync(this, token);
- }
-
- private async Task GetCachedFileAsync(string url, CancellationToken token)
- {
- string fileName = CacheContext.GetCacheFileName(url);
- CacheContext cacheContext = Ioc.Default.GetRequiredService();
- StorageFolder imageCacheFolder = await CacheContext
- .GetFolderAsync(nameof(Core.Caching.ImageCache), token)
- .ConfigureAwait(false);
-
- StorageFile storageFile;
- if (!cacheContext.FileExists(nameof(Core.Caching.ImageCache), fileName))
- {
- storageFile = await imageCacheFolder
- .CreateFileAsync(fileName)
- .AsTask(token)
- .ConfigureAwait(false);
- await StreamHelper
- .GetHttpStreamToStorageFileAsync(new(url), storageFile)
- .ConfigureAwait(false);
- }
- else
- {
- storageFile = await imageCacheFolder
- .GetFileAsync(fileName)
- .AsTask(token)
- .ConfigureAwait(false);
- }
-
- return storageFile;
+ await AnimationBuilder.Create().Opacity(1d).StartAsync(this, token);
}
private async Task LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
@@ -153,7 +117,7 @@ public class Gradient : Microsoft.UI.Xaml.Controls.Control
using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token))
{
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream).AsTask(token);
- imageAspectRatio = (double)decoder.PixelHeight / decoder.PixelWidth;
+ imageAspectRatio = decoder.PixelWidth / (double)decoder.PixelHeight;
return LoadedImageSurface.StartLoadFromStream(imageStream);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs b/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs
index 7f163761..26f277de 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Caching/CacheBase.cs
@@ -2,537 +2,397 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using Snap.Hutao.Core.Logging;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
+using System.Text;
using Windows.Storage;
+using Windows.Storage.FileProperties;
-namespace Snap.Hutao.Core.Caching
+namespace Snap.Hutao.Core.Caching;
+
+///
+/// Provides methods and tools to cache files in a folder
+/// 经过简化
+///
+/// Generic type as supplied by consumer of the class
+public abstract class CacheBase
+ where T : class
{
+ private readonly SemaphoreSlim cacheFolderSemaphore = new(1);
+ private readonly ConcurrentDictionary> concurrentTasks = new();
+ private readonly ILogger logger;
+ private readonly HttpClient httpClient;
+
+ private StorageFolder? baseFolder;
+ private string? cacheFolderName;
+ private StorageFolder? cacheFolder;
+
///
- /// Provides methods and tools to cache files in a folder
+ /// Initializes a new instance of the class.
///
- /// Generic type as supplied by consumer of the class
- public abstract class CacheBase
+ /// 日志器
+ /// http客户端
+ protected CacheBase(ILogger logger, HttpClient httpClient)
{
- private class ConcurrentRequest
- {
- public Task Task { get; set; }
+ this.logger = logger;
+ this.httpClient = httpClient;
- public bool EnsureCachedCopy { get; set; }
- }
+ CacheDuration = TimeSpan.FromDays(30);
+ RetryCount = 3;
+ }
- private readonly SemaphoreSlim _cacheFolderSemaphore = new(1);
- private StorageFolder _baseFolder = null;
- private string _cacheFolderName = null;
+ ///
+ /// Gets or sets the life duration of every cache entry.
+ ///
+ public TimeSpan CacheDuration { get; set; }
- private StorageFolder _cacheFolder = null;
- private InMemoryStorage _inMemoryFileStorage = null;
+ ///
+ /// Gets or sets the number of retries trying to ensure the file is cached.
+ ///
+ public uint RetryCount { get; set; }
- private ConcurrentDictionary _concurrentTasks = new ConcurrentDictionary();
+ ///
+ /// Clears all files in the cache
+ ///
+ /// awaitable task
+ public async Task ClearAsync()
+ {
+ StorageFolder folder = await GetCacheFolderAsync().ConfigureAwait(false);
+ IReadOnlyList files = await folder.GetFilesAsync().AsTask().ConfigureAwait(false);
- private HttpClient _httpClient = null;
+ await InternalClearAsync(files).ConfigureAwait(false);
+ }
- ///
- /// Initializes a new instance of the class.
- ///
- protected CacheBase()
- {
- CacheDuration = TimeSpan.FromDays(1);
- _inMemoryFileStorage = new InMemoryStorage();
- RetryCount = 1;
- }
+ ///
+ /// Removes cached files that have expired
+ ///
+ /// Optional timespan to compute whether file has expired or not. If no value is supplied, is used.
+ /// awaitable task
+ public async Task RemoveExpiredAsync(TimeSpan? duration = null)
+ {
+ TimeSpan expiryDuration = duration ?? CacheDuration;
- ///
- /// Gets or sets the life duration of every cache entry.
- ///
- public TimeSpan CacheDuration { get; set; }
+ StorageFolder? folder = await GetCacheFolderAsync().ConfigureAwait(false);
+ IReadOnlyList? files = await folder.GetFilesAsync().AsTask().ConfigureAwait(false);
- ///
- /// Gets or sets the number of retries trying to ensure the file is cached.
- ///
- public uint RetryCount { get; set; }
+ List? filesToDelete = new();
- ///
- /// Gets or sets max in-memory item storage count
- ///
- public int MaxMemoryCacheCount
- {
- get
- {
- return _inMemoryFileStorage.MaxItemCount;
- }
-
- set
- {
- _inMemoryFileStorage.MaxItemCount = value;
- }
- }
-
- ///
- /// Gets instance of
- ///
- protected HttpClient HttpClient
- {
- get
- {
- if (_httpClient == null)
- {
- var messageHandler = new HttpClientHandler() { MaxConnectionsPerServer = 20 };
-
- _httpClient = new HttpClient(messageHandler);
- }
-
- return _httpClient;
- }
- }
-
- ///
- /// Initializes FileCache and provides root folder and cache folder name
- ///
- /// Folder that is used as root for cache
- /// Cache folder name
- /// instance of
- /// awaitable task
- public virtual async Task InitializeAsync(StorageFolder folder = null, string folderName = null, HttpMessageHandler httpMessageHandler = null)
- {
- _baseFolder = folder;
- _cacheFolderName = folderName;
-
- _cacheFolder = await GetCacheFolderAsync().ConfigureAwait(false);
-
- if (httpMessageHandler != null)
- {
- _httpClient = new HttpClient(httpMessageHandler);
- }
- }
-
- ///
- /// Clears all files in the cache
- ///
- /// awaitable task
- public async Task ClearAsync()
- {
- var folder = await GetCacheFolderAsync().ConfigureAwait(false);
- var files = await folder.GetFilesAsync().AsTask().ConfigureAwait(false);
-
- await InternalClearAsync(files).ConfigureAwait(false);
-
- _inMemoryFileStorage.Clear();
- }
-
- ///
- /// Clears file if it has expired
- ///
- /// timespan to compute whether file has expired or not
- /// awaitable task
- public Task ClearAsync(TimeSpan duration)
- {
- return RemoveExpiredAsync(duration);
- }
-
- ///
- /// Removes cached files that have expired
- ///
- /// Optional timespan to compute whether file has expired or not. If no value is supplied, is used.
- /// awaitable task
- public async Task RemoveExpiredAsync(TimeSpan? duration = null)
- {
- TimeSpan expiryDuration = duration ?? CacheDuration;
-
- var folder = await GetCacheFolderAsync().ConfigureAwait(false);
- var files = await folder.GetFilesAsync().AsTask().ConfigureAwait(false);
-
- var filesToDelete = new List();
-
- foreach (var file in files)
- {
- if (file == null)
- {
- continue;
- }
-
- if (await IsFileOutOfDateAsync(file, expiryDuration, false).ConfigureAwait(false))
- {
- filesToDelete.Add(file);
- }
- }
-
- await InternalClearAsync(filesToDelete).ConfigureAwait(false);
-
- _inMemoryFileStorage.Clear(expiryDuration);
- }
-
- ///
- /// Removed items based on uri list passed
- ///
- /// Enumerable uri list
- /// awaitable Task
- public async Task RemoveAsync(IEnumerable uriForCachedItems)
- {
- if (uriForCachedItems == null || !uriForCachedItems.Any())
- {
- return;
- }
-
- var folder = await GetCacheFolderAsync().ConfigureAwait(false);
- var files = await folder.GetFilesAsync().AsTask().ConfigureAwait(false);
-
- var filesToDelete = new List();
- var keys = new List();
-
- Dictionary hashDictionary = new Dictionary();
-
- foreach (var file in files)
- {
- hashDictionary.Add(file.Name, file);
- }
-
- foreach (var uri in uriForCachedItems)
- {
- string fileName = GetCacheFileName(uri);
- if (hashDictionary.TryGetValue(fileName, out var file))
- {
- filesToDelete.Add(file);
- keys.Add(fileName);
- }
- }
-
- await InternalClearAsync(filesToDelete).ConfigureAwait(false);
-
- _inMemoryFileStorage.Remove(keys);
- }
-
- ///
- /// Assures that item represented by Uri is cached.
- ///
- /// Uri of the item
- /// Indicates whether or not exception should be thrown if item cannot be cached
- /// Indicates if item should be loaded into the in-memory storage
- /// instance of
- /// Awaitable Task
- public Task PreCacheAsync(Uri uri, bool throwOnError = false, bool storeToMemoryCache = false, CancellationToken cancellationToken = default(CancellationToken))
- {
- return GetItemAsync(uri, throwOnError, !storeToMemoryCache, cancellationToken, null);
- }
-
- ///
- /// Retrieves item represented by Uri from the cache. If the item is not found in the cache, it will try to downloaded and saved before returning it to the caller.
- ///
- /// Uri of the item.
- /// Indicates whether or not exception should be thrown if item cannot be found / downloaded.
- /// instance of
- /// key value pairs used when initializing instance of generic type
- /// an instance of Generic type
- public Task GetFromCacheAsync(Uri uri, bool throwOnError = false, CancellationToken cancellationToken = default(CancellationToken), List> initializerKeyValues = null)
- {
- return GetItemAsync(uri, throwOnError, false, cancellationToken, initializerKeyValues);
- }
-
- ///
- /// Gets the StorageFile containing cached item for given Uri
- ///
- /// Uri of the item.
- /// a StorageFile
- public async Task GetFileFromCacheAsync(Uri uri)
- {
- var folder = await GetCacheFolderAsync().ConfigureAwait(false);
-
- string fileName = GetCacheFileName(uri);
-
- var item = await folder.TryGetItemAsync(fileName).AsTask().ConfigureAwait(false);
-
- return item as StorageFile;
- }
-
- ///
- /// Retrieves item represented by Uri from the in-memory cache if it exists and is not out of date. If item is not found or is out of date, default instance of the generic type is returned.
- ///
- /// Uri of the item.
- /// an instance of Generic type
- public T GetFromMemoryCache(Uri uri)
- {
- T instance = default(T);
-
- string fileName = GetCacheFileName(uri);
-
- if (_inMemoryFileStorage.MaxItemCount > 0)
- {
- var msi = _inMemoryFileStorage.GetItem(fileName, CacheDuration);
- if (msi != null)
- {
- instance = msi.Item;
- }
- }
-
- return instance;
- }
-
- ///
- /// Cache specific hooks to process items from HTTP response
- ///
- /// input stream
- /// key value pairs used when initializing instance of generic type
- /// awaitable task
- protected abstract Task InitializeTypeAsync(Stream stream, List> initializerKeyValues = null);
-
- ///
- /// Cache specific hooks to process items from HTTP response
- ///
- /// storage file
- /// key value pairs used when initializing instance of generic type
- /// awaitable task
- protected abstract Task InitializeTypeAsync(StorageFile baseFile, List> initializerKeyValues = null);
-
- ///
- /// Override-able method that checks whether file is valid or not.
- ///
- /// storage file
- /// cache duration
- /// option to mark uninitialized file as expired
- /// bool indicate whether file has expired or not
- protected virtual async Task IsFileOutOfDateAsync(StorageFile file, TimeSpan duration, bool treatNullFileAsOutOfDate = true)
+ foreach (StorageFile? file in files)
{
if (file == null)
{
- return treatNullFileAsOutOfDate;
+ continue;
}
- var properties = await file.GetBasicPropertiesAsync().AsTask().ConfigureAwait(false);
-
- return properties.Size == 0 || DateTime.Now.Subtract(properties.DateModified.DateTime) > duration;
- }
-
- private static string GetCacheFileName(Uri uri)
- {
- return CreateHash64(uri.ToString()).ToString();
- }
-
- private static ulong CreateHash64(string str)
- {
- byte[] utf8 = global::System.Text.Encoding.UTF8.GetBytes(str);
-
- ulong value = (ulong)utf8.Length;
- for (int n = 0; n < utf8.Length; n++)
+ if (await IsFileOutOfDateAsync(file, expiryDuration, false).ConfigureAwait(false))
{
- value += (ulong)utf8[n] << ((n * 5) % 56);
+ filesToDelete.Add(file);
}
-
- return value;
}
- private async Task GetItemAsync(Uri uri, bool throwOnError, bool preCacheOnly, CancellationToken cancellationToken, List> initializerKeyValues)
- {
- T instance = default(T);
+ await InternalClearAsync(filesToDelete).ConfigureAwait(false);
+ }
+ ///
+ /// Removed items based on uri list passed
+ ///
+ /// Enumerable uri list
+ /// awaitable Task
+ public async Task RemoveAsync(IEnumerable uriForCachedItems)
+ {
+ if (uriForCachedItems == null || !uriForCachedItems.Any())
+ {
+ return;
+ }
+
+ StorageFolder folder = await GetCacheFolderAsync().ConfigureAwait(false);
+ IReadOnlyList files = await folder.GetFilesAsync().AsTask().ConfigureAwait(false);
+
+ List filesToDelete = new();
+ List keys = new();
+
+ Dictionary hashDictionary = new();
+
+ foreach (StorageFile file in files)
+ {
+ hashDictionary.Add(file.Name, file);
+ }
+
+ foreach (Uri uri in uriForCachedItems)
+ {
string fileName = GetCacheFileName(uri);
- _concurrentTasks.TryGetValue(fileName, out var request);
-
- // if similar request exists check if it was preCacheOnly and validate that current request isn't preCacheOnly
- if (request != null && request.EnsureCachedCopy && !preCacheOnly)
+ if (hashDictionary.TryGetValue(fileName, out StorageFile? file))
{
- await request.Task.ConfigureAwait(false);
- request = null;
+ filesToDelete.Add(file);
+ keys.Add(fileName);
}
+ }
- if (request == null)
+ await InternalClearAsync(filesToDelete).ConfigureAwait(false);
+ }
+
+ ///
+ /// Retrieves item represented by Uri from the cache. If the item is not found in the cache, it will try to downloaded and saved before returning it to the caller.
+ ///
+ /// Uri of the item.
+ /// Indicates whether or not exception should be thrown if item cannot be found / downloaded.
+ /// an instance of Generic type
+ public Task GetFromCacheAsync(Uri uri, bool throwOnError = false)
+ {
+ return GetItemAsync(uri, throwOnError);
+ }
+
+ ///
+ /// Gets the StorageFile containing cached item for given Uri
+ ///
+ /// Uri of the item.
+ /// a StorageFile
+ public async Task GetFileFromCacheAsync(Uri uri)
+ {
+ StorageFolder folder = await GetCacheFolderAsync().ConfigureAwait(false);
+
+ string fileName = GetCacheFileName(uri);
+
+ IStorageItem? item = await folder.TryGetItemAsync(fileName).AsTask().ConfigureAwait(false);
+
+ if (item == null)
+ {
+ StorageFile? baseFile = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting).AsTask().ConfigureAwait(false);
+ await DownloadFileAsync(uri, baseFile).ConfigureAwait(false);
+ item = await folder.TryGetItemAsync(fileName).AsTask().ConfigureAwait(false);
+ }
+
+ return item as StorageFile;
+ }
+
+ ///
+ /// Cache specific hooks to process items from HTTP response
+ ///
+ /// input stream
+ /// awaitable task
+ protected abstract Task InitializeTypeAsync(Stream stream);
+
+ ///
+ /// Cache specific hooks to process items from HTTP response
+ ///
+ /// storage file
+ /// awaitable task
+ protected abstract Task InitializeTypeAsync(StorageFile baseFile);
+
+ ///
+ /// Override-able method that checks whether file is valid or not.
+ ///
+ /// storage file
+ /// cache duration
+ /// option to mark uninitialized file as expired
+ /// bool indicate whether file has expired or not
+ protected virtual async Task IsFileOutOfDateAsync(StorageFile file, TimeSpan duration, bool treatNullFileAsOutOfDate = true)
+ {
+ if (file == null)
+ {
+ return treatNullFileAsOutOfDate;
+ }
+
+ BasicProperties? properties = await file.GetBasicPropertiesAsync().AsTask().ConfigureAwait(false);
+
+ return properties.Size == 0 || DateTime.Now.Subtract(properties.DateModified.DateTime) > duration;
+ }
+
+ private static string GetCacheFileName(Uri uri)
+ {
+ return CreateHash64(uri.ToString()).ToString();
+ }
+
+ private static ulong CreateHash64(string str)
+ {
+ byte[] utf8 = Encoding.UTF8.GetBytes(str);
+
+ ulong value = (ulong)utf8.Length;
+ for (int n = 0; n < utf8.Length; n++)
+ {
+ value += (ulong)utf8[n] << ((n * 5) % 56);
+ }
+
+ return value;
+ }
+
+ private async Task GetItemAsync(Uri uri, bool throwOnError)
+ {
+ T? instance = default(T);
+
+ string fileName = GetCacheFileName(uri);
+ concurrentTasks.TryGetValue(fileName, out Task? request);
+
+ // complete previous task first
+ if (request != null)
+ {
+ await request.ConfigureAwait(false);
+ request = null;
+ }
+
+ if (request == null)
+ {
+ request = GetFromCacheOrDownloadAsync(uri, fileName);
+
+ concurrentTasks[fileName] = request;
+ }
+
+ try
+ {
+ instance = await request.ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(EventIds.CacheException, ex, "Exception happened when caching.");
+ if (throwOnError)
{
- request = new ConcurrentRequest()
- {
- Task = GetFromCacheOrDownloadAsync(uri, fileName, preCacheOnly, cancellationToken, initializerKeyValues),
- EnsureCachedCopy = preCacheOnly
- };
-
- _concurrentTasks[fileName] = request;
+ throw;
}
+ }
+ finally
+ {
+ concurrentTasks.TryRemove(fileName, out _);
+ }
+ return instance;
+ }
+
+ private async Task GetFromCacheOrDownloadAsync(Uri uri, string fileName)
+ {
+ T? instance = default;
+
+ StorageFolder folder = await GetCacheFolderAsync().ConfigureAwait(false);
+ StorageFile? baseFile = await folder.TryGetItemAsync(fileName).AsTask().ConfigureAwait(false) as StorageFile;
+
+ bool downloadDataFile = baseFile == null || await IsFileOutOfDateAsync(baseFile, CacheDuration).ConfigureAwait(false);
+ baseFile ??= await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting).AsTask().ConfigureAwait(false);
+
+ if (downloadDataFile)
+ {
+ uint retries = 0;
try
{
- instance = await request.Task.ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- global::System.Diagnostics.Debug.WriteLine(ex.Message);
- if (throwOnError)
+ while (retries < RetryCount)
{
- throw;
- }
- }
- finally
- {
- _concurrentTasks.TryRemove(fileName, out _);
- }
-
- return instance;
- }
-
- private async Task GetFromCacheOrDownloadAsync(Uri uri, string fileName, bool preCacheOnly, CancellationToken cancellationToken, List> initializerKeyValues)
- {
- T instance = default(T);
-
- if (_inMemoryFileStorage.MaxItemCount > 0)
- {
- var msi = _inMemoryFileStorage.GetItem(fileName, CacheDuration);
- if (msi != null)
- {
- instance = msi.Item;
- }
- }
-
- if (instance != null)
- {
- return instance;
- }
-
- var folder = await GetCacheFolderAsync().ConfigureAwait(false);
- var baseFile = await folder.TryGetItemAsync(fileName).AsTask().ConfigureAwait(false) as StorageFile;
-
- bool downloadDataFile = baseFile == null || await IsFileOutOfDateAsync(baseFile, CacheDuration).ConfigureAwait(false);
-
- if (baseFile == null)
- {
- baseFile = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting).AsTask().ConfigureAwait(false);
- }
-
- if (downloadDataFile)
- {
- uint retries = 0;
- try
- {
- while (retries < RetryCount)
+ try
{
- try
- {
- instance = await DownloadFileAsync(uri, baseFile, preCacheOnly, cancellationToken, initializerKeyValues).ConfigureAwait(false);
+ instance = await DownloadFileAsync(uri, baseFile).ConfigureAwait(false);
- if (instance != null)
- {
- break;
- }
- }
- catch (FileNotFoundException)
+ if (instance != null)
{
+ break;
}
-
- retries++;
}
- }
- catch (Exception)
- {
- await baseFile.DeleteAsync().AsTask().ConfigureAwait(false);
- throw; // re-throwing the exception changes the stack trace. just throw
- }
- }
-
- if (EqualityComparer.Default.Equals(instance, default(T)) && !preCacheOnly)
- {
- instance = await InitializeTypeAsync(baseFile, initializerKeyValues).ConfigureAwait(false);
-
- if (_inMemoryFileStorage.MaxItemCount > 0)
- {
- var properties = await baseFile.GetBasicPropertiesAsync().AsTask().ConfigureAwait(false);
-
- var msi = new InMemoryStorageItem(fileName, properties.DateModified.DateTime, instance);
- _inMemoryFileStorage.SetItem(msi);
- }
- }
-
- return instance;
- }
-
- private async Task DownloadFileAsync(Uri uri, StorageFile baseFile, bool preCacheOnly, CancellationToken cancellationToken, List> initializerKeyValues)
- {
- T instance = default(T);
-
- using (MemoryStream ms = new MemoryStream())
- {
- using (var stream = await HttpClient.GetStreamAsync(uri))
- {
- stream.CopyTo(ms);
- ms.Flush();
-
- ms.Position = 0;
-
- using (var fs = await baseFile.OpenStreamForWriteAsync())
+ catch (FileNotFoundException)
{
- ms.CopyTo(fs);
-
- fs.Flush();
-
- ms.Position = 0;
}
- }
- // if its pre-cache we aren't looking to load items in memory
- if (!preCacheOnly)
- {
- instance = await InitializeTypeAsync(ms, initializerKeyValues).ConfigureAwait(false);
+ retries++;
}
}
-
- return instance;
- }
-
- private async Task InternalClearAsync(IEnumerable files)
- {
- foreach (var file in files)
+ catch (Exception)
{
- try
- {
- await file.DeleteAsync().AsTask().ConfigureAwait(false);
- }
- catch
- {
- // Just ignore errors for now}
- }
+ await baseFile.DeleteAsync().AsTask().ConfigureAwait(false);
+ throw; // re-throwing the exception changes the stack trace. just throw
}
}
- ///
- /// Initializes with default values if user has not initialized explicitly
- ///
- /// awaitable task
- private async Task ForceInitialiseAsync()
+ if (EqualityComparer.Default.Equals(instance, default(T)))
{
- if (_cacheFolder != null)
+ instance = await InitializeTypeAsync(baseFile).ConfigureAwait(false);
+ }
+
+ return instance;
+ }
+
+ private async Task DownloadFileAsync(Uri uri, StorageFile baseFile)
+ {
+ T? instance = default;
+
+ using (MemoryStream memory = new())
+ {
+ using (Stream httpStream = await httpClient.GetStreamAsync(uri))
{
- return;
+ await httpStream.CopyToAsync(memory);
+ await memory.FlushAsync();
+
+ memory.Position = 0;
+
+ using (Stream fs = await baseFile.OpenStreamForWriteAsync())
+ {
+ await memory.CopyToAsync(fs);
+ await fs.FlushAsync();
+
+ memory.Position = 0;
+ }
}
- await _cacheFolderSemaphore.WaitAsync().ConfigureAwait(false);
+ instance = await InitializeTypeAsync(memory).ConfigureAwait(false);
+ }
- _inMemoryFileStorage = new InMemoryStorage();
-
- if (_baseFolder == null)
- {
- _baseFolder = ApplicationData.Current.TemporaryFolder;
- }
-
- if (string.IsNullOrWhiteSpace(_cacheFolderName))
- {
- _cacheFolderName = GetType().Name;
- }
+ return instance;
+ }
+ [SuppressMessage("", "CA1822")]
+ private async Task InternalClearAsync(IEnumerable files)
+ {
+ foreach (StorageFile file in files)
+ {
try
{
- _cacheFolder = await _baseFolder.CreateFolderAsync(_cacheFolderName, CreationCollisionOption.OpenIfExists).AsTask().ConfigureAwait(false);
+ await file.DeleteAsync().AsTask().ConfigureAwait(false);
}
- finally
+ catch
{
- _cacheFolderSemaphore.Release();
+ // Just ignore errors for now
}
}
-
- private async Task GetCacheFolderAsync()
- {
- if (_cacheFolder == null)
- {
- await ForceInitialiseAsync().ConfigureAwait(false);
- }
-
- return _cacheFolder;
- }
+ }
+
+ ///
+ /// Initializes with default values if user has not initialized explicitly
+ ///
+ /// awaitable task
+ private async Task InitializeInternalAsync()
+ {
+ if (cacheFolder != null)
+ {
+ return;
+ }
+
+ await cacheFolderSemaphore.WaitAsync().ConfigureAwait(false);
+
+ baseFolder ??= ApplicationData.Current.TemporaryFolder;
+
+ if (string.IsNullOrWhiteSpace(cacheFolderName))
+ {
+ cacheFolderName = GetType().Name;
+ }
+
+ try
+ {
+ cacheFolder = await baseFolder
+ .CreateFolderAsync(cacheFolderName, CreationCollisionOption.OpenIfExists)
+ .AsTask()
+ .ConfigureAwait(false);
+ }
+ finally
+ {
+ cacheFolderSemaphore.Release();
+ }
+ }
+
+ private async Task GetCacheFolderAsync()
+ {
+ if (cacheFolder == null)
+ {
+ await InitializeInternalAsync().ConfigureAwait(false);
+ }
+
+ return Must.NotNull(cacheFolder!);
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Caching/IImageCache.cs b/src/Snap.Hutao/Snap.Hutao/Core/Caching/IImageCache.cs
index 6bd710eb..a0383a45 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Caching/IImageCache.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Caching/IImageCache.cs
@@ -3,6 +3,7 @@
using Microsoft.UI.Xaml.Media.Imaging;
using System.Collections.Generic;
+using Windows.Storage;
namespace Snap.Hutao.Core.Caching;
@@ -12,16 +13,20 @@ namespace Snap.Hutao.Core.Caching;
/// 缓存类型
internal interface IImageCache
{
+ ///
+ /// Gets the StorageFile containing cached item for given Uri
+ ///
+ /// Uri of the item.
+ /// a StorageFile
+ Task GetFileFromCacheAsync(Uri uri);
+
///
/// Retrieves item represented by Uri from the cache. If the item is not found in the cache, it will try to downloaded and saved before returning it to the caller.
///
/// Uri of the item.
/// Indicates whether or not exception should be thrown if item cannot be found / downloaded.
- /// instance of
- /// key value pairs used when initializing instance of generic type
/// an instance of Generic type
- [SuppressMessage("", "CA1068")]
- Task GetFromCacheAsync(Uri uri, bool throwOnError = false, CancellationToken cancellationToken = default(CancellationToken), List> initializerKeyValues = null!);
+ Task GetFromCacheAsync(Uri uri, bool throwOnError = false);
///
/// Removed items based on uri list passed
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs b/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs
index df5971d1..4a27b7d2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs
@@ -2,12 +2,11 @@
// Licensed under the MIT license.
using CommunityToolkit.WinUI;
-using CommunityToolkit.WinUI.UI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml.Media.Imaging;
using System.Collections.Generic;
using System.IO;
-using System.Reflection;
+using System.Net.Http;
using Windows.Storage;
using Windows.Storage.FileProperties;
@@ -30,13 +29,12 @@ public class ImageCache : CacheBase, IImageCache
///
/// Initializes a new instance of the class.
///
+ /// 日志器
/// http客户端
- public ImageCache()
+ public ImageCache(ILogger logger, HttpClient httpClient)
+ : base(logger, httpClient)
{
DispatcherQueue = Program.UIDispatcherQueue;
-
- CacheDuration = TimeSpan.FromDays(30);
- RetryCount = 3;
}
///
@@ -48,9 +46,8 @@ public class ImageCache : CacheBase, IImageCache
/// Cache specific hooks to process items from HTTP response
///
/// input stream
- /// key value pairs used when initializing instance of generic type
/// awaitable task
- protected override Task InitializeTypeAsync(Stream stream, List> initializerKeyValues = null!)
+ protected override Task InitializeTypeAsync(Stream stream)
{
if (stream.Length == 0)
{
@@ -61,24 +58,6 @@ public class ImageCache : CacheBase, IImageCache
{
BitmapImage image = new();
- if (initializerKeyValues != null && initializerKeyValues.Count > 0)
- {
- foreach (KeyValuePair kvp in initializerKeyValues)
- {
- if (string.IsNullOrWhiteSpace(kvp.Key))
- {
- continue;
- }
-
- PropertyInfo? propInfo = image.GetType().GetProperty(kvp.Key, BindingFlags.Public | BindingFlags.Instance);
-
- if (propInfo != null && propInfo.CanWrite)
- {
- propInfo.SetValue(image, kvp.Value);
- }
- }
- }
-
// This action will run on the UI thread, no need to care which thread to continue with
await image.SetSourceAsync(stream.AsRandomAccessStream()).AsTask().ConfigureAwait(false);
@@ -90,13 +69,12 @@ public class ImageCache : CacheBase, IImageCache
/// Cache specific hooks to process items from HTTP response
///
/// storage file
- /// key value pairs used when initializing instance of generic type
/// awaitable task
- protected override async Task InitializeTypeAsync(StorageFile baseFile, List> initializerKeyValues = null!)
+ protected override async Task InitializeTypeAsync(StorageFile baseFile)
{
using (Stream stream = await baseFile.OpenStreamForReadAsync().ConfigureAwait(false))
{
- return await InitializeTypeAsync(stream, initializerKeyValues).ConfigureAwait(false);
+ return await InitializeTypeAsync(stream).ConfigureAwait(false);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/ValueStopwatch.cs b/src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/ValueStopwatch.cs
index 432da6ab..0a6ec2c7 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/ValueStopwatch.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Diagnostics/ValueStopwatch.cs
@@ -22,13 +22,19 @@ internal struct ValueStopwatch
///
/// 是否处于活动状态
///
- public bool IsActive => startTimestamp != 0;
+ public bool IsActive
+ {
+ get => startTimestamp != 0;
+ }
///
/// 触发一个新的停表
///
/// 一个新的停表实例
- public static ValueStopwatch StartNew() => new(Stopwatch.GetTimestamp());
+ public static ValueStopwatch StartNew()
+ {
+ return new(Stopwatch.GetTimestamp());
+ }
///
/// 获取经过的时间
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/EventIds.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/EventIds.cs
index bb7dffb7..274bc70f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Logging/EventIds.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Logging/EventIds.cs
@@ -30,6 +30,11 @@ internal static class EventIds
///
public static readonly EventId WebView2EnvironmentException = 100003;
+ ///
+ /// 缓存异常
+ ///
+ public static readonly EventId CacheException = 100004;
+
// 服务
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogEntry.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogEntry.cs
index 48465702..cbc4b4d6 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogEntry.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogEntry.cs
@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using Snap.Hutao.Context.Database;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs b/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs
index c15e2989..ea134c12 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/WebView2Helper.cs
@@ -50,5 +50,8 @@ internal abstract class WebView2Helper
///
/// WebView2的版本
///
- public static string Version => version;
+ public static string Version
+ {
+ get => version;
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Win32/WINDOWPLACEMENT.cs b/src/Snap.Hutao/Snap.Hutao/Core/Win32/WINDOWPLACEMENT.cs
index 033a0950..0d10be88 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Win32/WINDOWPLACEMENT.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Win32/WINDOWPLACEMENT.cs
@@ -57,4 +57,22 @@ internal struct WINDOWPLACEMENT
return result;
}
}
+
+ ///
+ /// 构造一个新的
+ ///
+ /// 最大点
+ /// 正常位置
+ /// 显示命令
+ /// 窗体位置
+ public static WINDOWPLACEMENT Create(POINT max, RECT normal, ShowWindowCommand command)
+ {
+ WINDOWPLACEMENT result = Default;
+
+ result.MaxPosition = max;
+ result.NormalPosition = normal;
+ result.ShowCmd = command;
+
+ return result;
+ }
}
diff --git a/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs
index 186047aa..e26699ff 100644
--- a/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs
+++ b/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Microsoft.Extensions.DependencyInjection;
-using Snap.Hutao.Core.Caching;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Web.Enka;
using Snap.Hutao.Web.Hoyolab.Bbs.User;
diff --git a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs
index 15c5e74b..b7d5fa0c 100644
--- a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs
@@ -67,13 +67,14 @@ public sealed partial class MainWindow : Window
RECT rect = RetriveWindowRect();
if (!rect.Size.IsEmpty)
{
- WINDOWPLACEMENT windowPlacement = new()
- {
- Length = Marshal.SizeOf(),
- MaxPosition = new POINT(-1, -1),
- NormalPosition = rect,
- ShowCmd = ShowWindowCommand.Normal,
- };
+ WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Create(new POINT(-1, -1), rect, ShowWindowCommand.Normal);
+ //WINDOWPLACEMENT windowPlacement = new()
+ //{
+ // Length = Marshal.SizeOf(),
+ // MaxPosition = new POINT(-1, -1),
+ // NormalPosition = rect,
+ // ShowCmd = ShowWindowCommand.Normal,
+ //};
User32.SetWindowPlacement(handle, ref windowPlacement);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Program.cs b/src/Snap.Hutao/Snap.Hutao/Program.cs
index 0ec7dffd..f13b5d71 100644
--- a/src/Snap.Hutao/Snap.Hutao/Program.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Program.cs
@@ -18,7 +18,10 @@ public static class Program
///
/// 主线程调度器队列
///
- public static DispatcherQueue UIDispatcherQueue => Must.NotNull(dispatcherQueue!);
+ public static DispatcherQueue UIDispatcherQueue
+ {
+ get => Must.NotNull(dispatcherQueue!);
+ }
[DllImport("Microsoft.ui.xaml.dll")]
private static extern void XamlCheckProcessRequirements();
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs
index f3a76568..5aeee539 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.cs
@@ -5,7 +5,6 @@ using Microsoft.Extensions.Caching.Memory;
using Snap.Hutao.Context.FileSystem;
using Snap.Hutao.Core.Abstraction;
using Snap.Hutao.Core.Logging;
-using Snap.Hutao.Extension;
using Snap.Hutao.Model.Metadata.Achievement;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Reliquary;
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Helper/NavHelper.cs b/src/Snap.Hutao/Snap.Hutao/View/Helper/NavHelper.cs
index 59b8516d..18a20073 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Helper/NavHelper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Helper/NavHelper.cs
@@ -4,7 +4,6 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Core;
-using Snap.Hutao.Service.Navigation;
namespace Snap.Hutao.View.Helper;
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs
index 335e41ae..6d13a137 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs
@@ -36,10 +36,7 @@ public struct PlayerUid
///
public string Region
{
- get
- {
- return region ??= EvaluateRegion(Value[0]);
- }
+ get => region ??= EvaluateRegion(Value[0]);
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/Damage.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/Damage.cs
index b0e5aa4b..2e9e9415 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/Damage.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/Damage.cs
@@ -1,8 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using System.Collections.Generic;
-
namespace Snap.Hutao.Web.Hutao.Model.Post;
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/PlayerRecord.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/PlayerRecord.cs
index 84fe59f2..20ad843c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/PlayerRecord.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/Post/PlayerRecord.cs
@@ -84,11 +84,11 @@ public class PlayerRecord
return hutaoClient.UploadRecordAsync(this, token);
}
- private static Damage? GetDamage(List ranks)
+ private static Damage? GetDamage(List ranks)
{
if (ranks.Count > 0)
{
- RankInfo rank = ranks[0];
+ Rank rank = ranks[0];
return new Damage(rank.AvatarId, rank.Value);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankInfo.cs
index 09756eeb..9aba85d5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankInfo.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankInfo.cs
@@ -1,10 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using Snap.Hutao.Web.Hutao.Model.Converter;
-using System.Collections.Generic;
-using System.Text.Json.Serialization;
-
namespace Snap.Hutao.Web.Hutao.Model;
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankInfoWrapper.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankInfoWrapper.cs
index ec8d46f6..a559c6d2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankInfoWrapper.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Model/RankInfoWrapper.cs
@@ -1,10 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using Snap.Hutao.Web.Hutao.Model.Converter;
-using System.Collections.Generic;
-using System.Text.Json.Serialization;
-
namespace Snap.Hutao.Web.Hutao.Model;
///