mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
fix view model scope
This commit is contained in:
@@ -22,6 +22,7 @@ public class InjectionGenerator : ISourceGenerator
|
||||
{
|
||||
private const string InjectAsSingletonName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Singleton";
|
||||
private const string InjectAsTransientName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Transient";
|
||||
private const string InjectAsScopedName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectAs.Scoped";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
@@ -97,8 +98,11 @@ internal static partial class ServiceCollectionExtensions
|
||||
case InjectAsTransientName:
|
||||
lineBuilder.Append(@" services.AddTransient(");
|
||||
break;
|
||||
case InjectAsScopedName:
|
||||
lineBuilder.Append(@" services.AddScoped(");
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"非法的InjectAs值: [{injectAsName}]");
|
||||
throw new InvalidOperationException($"非法的 InjectAs 值: [{injectAsName}]");
|
||||
}
|
||||
|
||||
if (arguments.Length == 2)
|
||||
|
||||
@@ -28,9 +28,6 @@ public class ScopedPage : Page
|
||||
serviceScope = Ioc.Default.CreateScope();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IServiceScope.ServiceProvider"/>
|
||||
public IServiceProvider ServiceProvider { get => serviceScope.ServiceProvider; }
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
@@ -38,7 +35,7 @@ public class ScopedPage : Page
|
||||
public void InitializeWith<TViewModel>()
|
||||
where TViewModel : class, ISupportCancellation
|
||||
{
|
||||
ISupportCancellation viewModel = ServiceProvider.GetRequiredService<TViewModel>();
|
||||
ISupportCancellation viewModel = serviceScope.ServiceProvider.GetRequiredService<TViewModel>();
|
||||
viewModel.CancellationToken = viewLoadingCancellationTokenSource.Token;
|
||||
DataContext = viewModel;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ public abstract class CacheBase<T>
|
||||
{
|
||||
private readonly SemaphoreSlim cacheFolderSemaphore = new(1);
|
||||
private readonly ILogger logger;
|
||||
|
||||
// violate di rule
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
private StorageFolder? baseFolder;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Snap.Hutao.Extension;
|
||||
|
||||
namespace Snap.Hutao.Core.Database;
|
||||
|
||||
@@ -17,7 +16,6 @@ internal class DbCurrent<TEntity, TMessage>
|
||||
where TEntity : class, ISelectable
|
||||
where TMessage : Message.ValueChangedMessage<TEntity>, new()
|
||||
{
|
||||
private readonly DbContext dbContext;
|
||||
private readonly DbSet<TEntity> dbSet;
|
||||
private readonly IMessenger messenger;
|
||||
|
||||
@@ -31,7 +29,6 @@ internal class DbCurrent<TEntity, TMessage>
|
||||
///
|
||||
public DbCurrent(DbSet<TEntity> dbSet, IMessenger messenger)
|
||||
{
|
||||
this.dbContext = dbSet.Context();
|
||||
this.dbSet = dbSet;
|
||||
this.messenger = messenger;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace Snap.Hutao.Extension;
|
||||
namespace Snap.Hutao.Core.Database;
|
||||
|
||||
/// <summary>
|
||||
/// 数据库集合上下文
|
||||
@@ -50,6 +50,34 @@ public static class DbSetExtension
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加并保存
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">实体类型</typeparam>
|
||||
/// <param name="dbSet">数据库集</param>
|
||||
/// <param name="entity">实体</param>
|
||||
/// <returns>影响条数</returns>
|
||||
public static int AddAndSave<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||
where TEntity : class
|
||||
{
|
||||
dbSet.Add(entity);
|
||||
return dbSet.Context().SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除并保存
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">实体类型</typeparam>
|
||||
/// <param name="dbSet">数据库集</param>
|
||||
/// <param name="entity">实体</param>
|
||||
/// <returns>影响条数</returns>
|
||||
public static int RemoveAndSave<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||
where TEntity : class
|
||||
{
|
||||
dbSet.Remove(entity);
|
||||
return dbSet.Context().SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新并保存
|
||||
/// </summary>
|
||||
@@ -17,4 +17,9 @@ public enum InjectAs
|
||||
/// 指示应注册为短期对象
|
||||
/// </summary>
|
||||
Transient,
|
||||
|
||||
/// <summary>
|
||||
/// 指示应注册为范围对象
|
||||
/// </summary>
|
||||
Scoped,
|
||||
}
|
||||
|
||||
@@ -21,4 +21,4 @@ internal class SeparatorCommaInt32EnumerableConverter : JsonConverter<IEnumerabl
|
||||
{
|
||||
writer.WriteStringValue(string.Join(',', value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,6 @@ namespace Snap.Hutao.Core.Setting;
|
||||
/// </summary>
|
||||
internal static class SettingKeys
|
||||
{
|
||||
/// <summary>
|
||||
/// 上次打开时App的版本
|
||||
/// </summary>
|
||||
public const string LastAppVersion = "LastAppVersion";
|
||||
|
||||
/// <summary>
|
||||
/// 窗体左侧
|
||||
/// </summary>
|
||||
|
||||
@@ -38,18 +38,6 @@ public static class Must
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 任务异常
|
||||
/// </summary>
|
||||
/// <param name="message">异常消息</param>
|
||||
/// <returns>异常的任务</returns>
|
||||
[SuppressMessage("", "VSTHRD200")]
|
||||
public static Task Fault(string message)
|
||||
{
|
||||
InvalidOperationException exception = new(message);
|
||||
return Task.FromException(exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 任务异常
|
||||
/// </summary>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Snap.Hutao.Model.Binding;
|
||||
|
||||
/// <summary>
|
||||
/// 用于视图绑定的成就
|
||||
/// </summary>
|
||||
public class Achievement : Observable
|
||||
public class Achievement : ObservableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// 满进度占位符
|
||||
@@ -28,7 +30,7 @@ public class Achievement : Observable
|
||||
this.inner = inner;
|
||||
this.entity = entity;
|
||||
|
||||
// Property should only be set when is user checking.
|
||||
// Property should only be set when it's user checking.
|
||||
isChecked = (int)entity.Status >= 2;
|
||||
}
|
||||
|
||||
@@ -50,7 +52,7 @@ public class Achievement : Observable
|
||||
get => isChecked;
|
||||
set
|
||||
{
|
||||
Set(ref isChecked, value);
|
||||
SetProperty(ref isChecked, value);
|
||||
|
||||
// Only update state when checked
|
||||
if (value)
|
||||
@@ -67,6 +69,6 @@ public class Achievement : Observable
|
||||
/// </summary>
|
||||
public string Time
|
||||
{
|
||||
get => entity.Time.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
get => entity.Time.ToString("yyyy.MM.dd HH:mm:ss");
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
using Snap.Hutao.Web.Hoyolab.Bbs.User;
|
||||
@@ -12,7 +13,7 @@ namespace Snap.Hutao.Model.Binding;
|
||||
/// <summary>
|
||||
/// 用于视图绑定的用户
|
||||
/// </summary>
|
||||
public class User : Observable
|
||||
public class User : ObservableObject
|
||||
{
|
||||
private readonly EntityUser inner;
|
||||
|
||||
@@ -44,7 +45,7 @@ public class User : Observable
|
||||
public UserGameRole? SelectedUserGameRole
|
||||
{
|
||||
get => selectedUserGameRole;
|
||||
private set => Set(ref selectedUserGameRole, value);
|
||||
private set => SetProperty(ref selectedUserGameRole, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityUser.IsSelected"/>
|
||||
@@ -58,7 +59,19 @@ public class User : Observable
|
||||
public Cookie Cookie
|
||||
{
|
||||
get => inner.Cookie;
|
||||
set => inner.Cookie = value;
|
||||
set
|
||||
{
|
||||
inner.Cookie = value;
|
||||
OnPropertyChanged(nameof(HasSToken));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否拥有 SToken
|
||||
/// </summary>
|
||||
public bool HasSToken
|
||||
{
|
||||
get => inner.Cookie.ContainsSToken();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -71,6 +84,17 @@ public class User : Observable
|
||||
/// </summary>
|
||||
public bool IsInitialized { get => isInitialized; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新SToken
|
||||
/// </summary>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <param name="cookie">cookie</param>
|
||||
internal void UpdateSToken(string uid, Cookie cookie)
|
||||
{
|
||||
Cookie.InsertSToken(uid, cookie);
|
||||
OnPropertyChanged(nameof(HasSToken));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从数据库恢复用户
|
||||
/// </summary>
|
||||
@@ -79,11 +103,7 @@ public class User : Observable
|
||||
/// <param name="userGameRoleClient">角色客户端</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>用户是否初始化完成,若Cookie失效会返回 <see langword="false"/> </returns>
|
||||
internal static async Task<User?> ResumeAsync(
|
||||
EntityUser inner,
|
||||
UserClient userClient,
|
||||
BindingClient userGameRoleClient,
|
||||
CancellationToken token = default)
|
||||
internal static async Task<User?> ResumeAsync(EntityUser inner, UserClient userClient, BindingClient userGameRoleClient, CancellationToken token = default)
|
||||
{
|
||||
User user = new(inner);
|
||||
bool successful = await user.InitializeCoreAsync(userClient, userGameRoleClient, token).ConfigureAwait(false);
|
||||
@@ -98,36 +118,20 @@ public class User : Observable
|
||||
/// <param name="userGameRoleClient">角色客户端</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>用户是否初始化完成,若Cookie失效会返回 <see langword="null"/> </returns>
|
||||
internal static async Task<User?> CreateAsync(
|
||||
Cookie cookie,
|
||||
UserClient userClient,
|
||||
BindingClient userGameRoleClient,
|
||||
CancellationToken token = default)
|
||||
internal static async Task<User?> CreateAsync(Cookie cookie, UserClient userClient, BindingClient userGameRoleClient, CancellationToken token = default)
|
||||
{
|
||||
User user = new(EntityUser.Create(cookie));
|
||||
bool successful = await user.InitializeCoreAsync(userClient, userGameRoleClient, token).ConfigureAwait(false);
|
||||
return successful ? user : null;
|
||||
}
|
||||
|
||||
private async Task<bool> InitializeCoreAsync(
|
||||
UserClient userClient,
|
||||
BindingClient userGameRoleClient,
|
||||
CancellationToken token = default)
|
||||
private async Task<bool> InitializeCoreAsync(UserClient userClient, BindingClient userGameRoleClient, CancellationToken token = default)
|
||||
{
|
||||
if (isInitialized)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
await InitializeUserInfoAndUserGameRolesAsync(userClient, userGameRoleClient, token).ConfigureAwait(false);
|
||||
|
||||
isInitialized = true;
|
||||
|
||||
return UserInfo != null && UserGameRoles.Any();
|
||||
}
|
||||
|
||||
private async Task InitializeUserInfoAndUserGameRolesAsync(UserClient userClient, BindingClient userGameRoleClient, CancellationToken token)
|
||||
{
|
||||
UserInfo = await userClient
|
||||
.GetUserFullInfoAsync(this, token)
|
||||
.ConfigureAwait(false);
|
||||
@@ -137,5 +141,9 @@ public class User : Observable
|
||||
.ConfigureAwait(false);
|
||||
|
||||
SelectedUserGameRole = UserGameRoles.FirstOrFirstOrDefault(role => role.IsChosen);
|
||||
|
||||
isInitialized = true;
|
||||
|
||||
return UserInfo != null && UserGameRoles.Any();
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Snap.Hutao.Model;
|
||||
|
||||
/// <summary>
|
||||
/// 简单的实现了 <see cref="INotifyPropertyChanged"/> 接口
|
||||
/// </summary>
|
||||
public abstract class Observable : INotifyPropertyChanged
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 设置字段的值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">字段类型</typeparam>
|
||||
/// <param name="storage">现有值</param>
|
||||
/// <param name="value">新的值</param>
|
||||
/// <param name="propertyName">属性名称</param>
|
||||
/// <returns>项是否更新</returns>
|
||||
protected bool Set<T>([NotNullIfNotNull("value")] ref T storage, T value, [CallerMemberName] string propertyName = default!)
|
||||
{
|
||||
if (Equals(storage, value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
storage = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发 <see cref="PropertyChanged"/>
|
||||
/// </summary>
|
||||
/// <param name="propertyName">属性名称</param>
|
||||
protected void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
65
src/Snap.Hutao/Snap.Hutao/Model/Primitive/AvatarId.cs
Normal file
65
src/Snap.Hutao/Snap.Hutao/Model/Primitive/AvatarId.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 角色Id
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(AvatarIdConverter))]
|
||||
public readonly struct AvatarId : IEquatable<AvatarId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AvatarId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public AvatarId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(AvatarId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator AvatarId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(AvatarId left, AvatarId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(AvatarId left, AvatarId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(AvatarId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is AvatarId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// 角色Id转换器
|
||||
/// </summary>
|
||||
internal class AvatarIdConverter : JsonConverter<AvatarId>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override AvatarId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return reader.GetInt32();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, AvatarId value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteNumberValue(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// 武器Id转换器
|
||||
/// </summary>
|
||||
internal class WeaponIdConverter : JsonConverter<WeaponId>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override WeaponId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return reader.GetInt32();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, WeaponId value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteNumberValue(value);
|
||||
}
|
||||
}
|
||||
65
src/Snap.Hutao/Snap.Hutao/Model/Primitive/WeaponId.cs
Normal file
65
src/Snap.Hutao/Snap.Hutao/Model/Primitive/WeaponId.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 武器Id
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(WeaponIdConverter))]
|
||||
public readonly struct WeaponId : IEquatable<WeaponId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WeaponId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public WeaponId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(WeaponId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator WeaponId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(WeaponId left, WeaponId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(WeaponId left, WeaponId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(WeaponId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is WeaponId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Snap.Hutao.Model;
|
||||
|
||||
/// <summary>
|
||||
@@ -8,7 +10,7 @@ namespace Snap.Hutao.Model;
|
||||
/// 默认为选中状态
|
||||
/// </summary>
|
||||
/// <typeparam name="T">值的类型</typeparam>
|
||||
public class Selectable<T> : Observable
|
||||
public class Selectable<T> : ObservableObject
|
||||
where T : class
|
||||
{
|
||||
private readonly Action? selectedChanged;
|
||||
@@ -35,7 +37,7 @@ public class Selectable<T> : Observable
|
||||
get => isSelected;
|
||||
set
|
||||
{
|
||||
Set(ref isSelected, value);
|
||||
SetProperty(ref isSelected, value);
|
||||
selectedChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
@@ -43,5 +45,5 @@ public class Selectable<T> : Observable
|
||||
/// <summary>
|
||||
/// 存放的对象
|
||||
/// </summary>
|
||||
public T Value { get => value; set => Set(ref this.value, value); }
|
||||
public T Value { get => value; set => SetProperty(ref this.value, value); }
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Context.Database;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.InterChange.Achievement;
|
||||
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
|
||||
|
||||
@@ -63,7 +64,7 @@ public class AchievementDbOperation
|
||||
|
||||
if (entity == null && uiaf != null)
|
||||
{
|
||||
AddEntity(EntityAchievement.Create(archiveId, uiaf));
|
||||
appDbContext.Achievements.AddAndSave(EntityAchievement.Create(archiveId, uiaf));
|
||||
add++;
|
||||
continue;
|
||||
}
|
||||
@@ -85,8 +86,8 @@ public class AchievementDbOperation
|
||||
|
||||
if (aggressive)
|
||||
{
|
||||
RemoveEntity(entity);
|
||||
AddEntity(EntityAchievement.Create(archiveId, uiaf));
|
||||
appDbContext.Achievements.RemoveAndSave(entity);
|
||||
appDbContext.Achievements.AddAndSave(EntityAchievement.Create(archiveId, uiaf));
|
||||
update++;
|
||||
}
|
||||
}
|
||||
@@ -96,7 +97,7 @@ public class AchievementDbOperation
|
||||
moveEntity = false;
|
||||
moveUIAF = true;
|
||||
|
||||
AddEntity(EntityAchievement.Create(archiveId, uiaf));
|
||||
appDbContext.Achievements.AddAndSave(EntityAchievement.Create(archiveId, uiaf));
|
||||
add++;
|
||||
}
|
||||
}
|
||||
@@ -115,7 +116,7 @@ public class AchievementDbOperation
|
||||
/// <returns>导入结果</returns>
|
||||
public ImportResult Overwrite(Guid archiveId, IEnumerable<EntityAchievement> items)
|
||||
{
|
||||
IQueryable<EntityAchievement> oldData = appDbContext.Achievements
|
||||
IOrderedQueryable<EntityAchievement> oldData = appDbContext.Achievements
|
||||
.Where(a => a.ArchiveId == archiveId)
|
||||
.OrderBy(a => a.Id);
|
||||
|
||||
@@ -142,13 +143,13 @@ public class AchievementDbOperation
|
||||
|
||||
if (oldEntity == null && newEntity != null)
|
||||
{
|
||||
AddEntity(newEntity);
|
||||
appDbContext.Achievements.AddAndSave(newEntity);
|
||||
add++;
|
||||
continue;
|
||||
}
|
||||
else if (oldEntity != null && newEntity == null)
|
||||
{
|
||||
RemoveEntity(oldEntity);
|
||||
appDbContext.Achievements.RemoveAndSave(oldEntity);
|
||||
remove++;
|
||||
continue;
|
||||
}
|
||||
@@ -157,7 +158,7 @@ public class AchievementDbOperation
|
||||
{
|
||||
moveOld = true;
|
||||
moveNew = false;
|
||||
RemoveEntity(oldEntity);
|
||||
appDbContext.Achievements.RemoveAndSave(oldEntity);
|
||||
remove++;
|
||||
}
|
||||
else if (oldEntity.Id == newEntity.Id)
|
||||
@@ -172,8 +173,8 @@ public class AchievementDbOperation
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveEntity(oldEntity);
|
||||
AddEntity(newEntity);
|
||||
appDbContext.Achievements.RemoveAndSave(oldEntity);
|
||||
appDbContext.Achievements.AddAndSave(newEntity);
|
||||
update++;
|
||||
}
|
||||
}
|
||||
@@ -182,7 +183,7 @@ public class AchievementDbOperation
|
||||
// entity.Id > uiaf.Id
|
||||
moveOld = false;
|
||||
moveNew = true;
|
||||
AddEntity(newEntity);
|
||||
appDbContext.Achievements.AddAndSave(newEntity);
|
||||
add++;
|
||||
}
|
||||
}
|
||||
@@ -196,16 +197,4 @@ public class AchievementDbOperation
|
||||
|
||||
return new(add, update, remove);
|
||||
}
|
||||
|
||||
private void AddEntity(EntityAchievement entity)
|
||||
{
|
||||
appDbContext.Achievements.Add(entity);
|
||||
appDbContext.SaveChanges();
|
||||
}
|
||||
|
||||
private void RemoveEntity(EntityAchievement entity)
|
||||
{
|
||||
appDbContext.Achievements.Remove(entity);
|
||||
appDbContext.SaveChanges();
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ namespace Snap.Hutao.Service.Achievement;
|
||||
/// <summary>
|
||||
/// 成就服务
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient, typeof(IAchievementService))]
|
||||
[Injection(InjectAs.Scoped, typeof(IAchievementService))]
|
||||
internal class AchievementService : IAchievementService
|
||||
{
|
||||
private readonly AppDbContext appDbContext;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Snap.Hutao.Service.AvatarInfo;
|
||||
/// <summary>
|
||||
/// 角色信息服务
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient, typeof(IAvatarInfoService))]
|
||||
[Injection(InjectAs.Scoped, typeof(IAvatarInfoService))]
|
||||
internal class AvatarInfoService : IAvatarInfoService
|
||||
{
|
||||
private readonly AppDbContext appDbContext;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Snap.Hutao.Service.GachaLog.Factory;
|
||||
/// <summary>
|
||||
/// 祈愿统计工厂
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient, typeof(IGachaStatisticsFactory))]
|
||||
[Injection(InjectAs.Scoped, typeof(IGachaStatisticsFactory))]
|
||||
internal class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
{
|
||||
private readonly IMetadataService metadataService;
|
||||
|
||||
@@ -14,7 +14,6 @@ using Snap.Hutao.Model.Binding.Gacha.Abstraction;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.InterChange.GachaLog;
|
||||
using Snap.Hutao.Model.Metadata.Abstraction;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Service.GachaLog.Factory;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
@@ -27,7 +26,7 @@ namespace Snap.Hutao.Service.GachaLog;
|
||||
/// <summary>
|
||||
/// 祈愿记录服务
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient, typeof(IGachaLogService))]
|
||||
[Injection(InjectAs.Scoped, typeof(IGachaLogService))]
|
||||
internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
{
|
||||
/// <summary>
|
||||
@@ -45,7 +44,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
private readonly IEnumerable<IGachaLogUrlProvider> urlProviders;
|
||||
private readonly GachaInfoClient gachaInfoClient;
|
||||
private readonly IMetadataService metadataService;
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly IGachaStatisticsFactory gachaStatisticsFactory;
|
||||
private readonly ILogger<GachaLogService> logger;
|
||||
private readonly DbCurrent<GachaArchive, Message.GachaArchiveChangedMessage> dbCurrent;
|
||||
@@ -66,7 +64,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
/// <param name="urlProviders">Url提供器集合</param>
|
||||
/// <param name="gachaInfoClient">祈愿记录客户端</param>
|
||||
/// <param name="metadataService">元数据服务</param>
|
||||
/// <param name="infoBarService">信息条服务</param>
|
||||
/// <param name="gachaStatisticsFactory">祈愿统计工厂</param>
|
||||
/// <param name="logger">日志器</param>
|
||||
/// <param name="messenger">消息器</param>
|
||||
@@ -75,7 +72,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
IEnumerable<IGachaLogUrlProvider> urlProviders,
|
||||
GachaInfoClient gachaInfoClient,
|
||||
IMetadataService metadataService,
|
||||
IInfoBarService infoBarService,
|
||||
IGachaStatisticsFactory gachaStatisticsFactory,
|
||||
ILogger<GachaLogService> logger,
|
||||
IMessenger messenger)
|
||||
@@ -84,7 +80,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
this.urlProviders = urlProviders;
|
||||
this.gachaInfoClient = gachaInfoClient;
|
||||
this.metadataService = metadataService;
|
||||
this.infoBarService = infoBarService;
|
||||
this.logger = logger;
|
||||
this.gachaStatisticsFactory = gachaStatisticsFactory;
|
||||
|
||||
@@ -135,8 +130,8 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
nameAvatarMap = await metadataService.GetNameToAvatarMapAsync(token).ConfigureAwait(false);
|
||||
nameWeaponMap = await metadataService.GetNameToWeaponMapAsync(token).ConfigureAwait(false);
|
||||
|
||||
idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false);
|
||||
idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false);
|
||||
idAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false);
|
||||
idWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false);
|
||||
|
||||
IsInitialized = true;
|
||||
}
|
||||
@@ -214,8 +209,8 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
archiveCollection.Remove(archive);
|
||||
|
||||
// Sync database
|
||||
appDbContext.GachaArchives.Remove(archive);
|
||||
return appDbContext.SaveChangesAsync();
|
||||
appDbContext.GachaArchives.RemoveAndSave(archive);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static Task RandomDelayAsync(CancellationToken token)
|
||||
@@ -232,7 +227,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
long trimId = appDbContext.GachaItems
|
||||
.Where(i => i.ArchiveId == archiveId)
|
||||
.OrderBy(i => i.Id)
|
||||
.Take(1)
|
||||
.FirstOrDefault()?.Id ?? long.MaxValue;
|
||||
|
||||
IEnumerable<GachaItem> toAdd = list
|
||||
@@ -326,8 +320,7 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
if (archive == null)
|
||||
{
|
||||
GachaArchive created = GachaArchive.Create(uid);
|
||||
appDbContext.GachaArchives.Add(created);
|
||||
appDbContext.SaveChanges();
|
||||
appDbContext.GachaArchives.AddAndSave(created);
|
||||
|
||||
archive = appDbContext.GachaArchives.Single(a => a.Uid == uid);
|
||||
GachaArchive temp = archive;
|
||||
@@ -346,10 +339,9 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
.Where(i => i.ArchiveId == archive.InnerId)
|
||||
.Where(i => i.QueryType == configType)
|
||||
.OrderByDescending(i => i.Id)
|
||||
.Take(1)
|
||||
.FirstOrDefault();
|
||||
|
||||
// MaxBy should be supported by .NET 7
|
||||
// TODO MaxBy should be supported by .NET 7
|
||||
// .MaxBy(i => i.Id);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Snap.Hutao.Context.Database;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Core.Threading;
|
||||
using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.Game.Locator;
|
||||
|
||||
|
||||
@@ -153,8 +153,8 @@ internal class UserService : IUserService
|
||||
// 检查 stoken 是否存在
|
||||
if (cookie.ContainsSToken())
|
||||
{
|
||||
// insert stoken directly
|
||||
userWithSameUid.Cookie.InsertSToken(uid, cookie);
|
||||
// insert stoken
|
||||
userWithSameUid.UpdateSToken(uid, cookie);
|
||||
appDbContext.Users.Update(userWithSameUid.Entity);
|
||||
appDbContext.SaveChanges();
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Snap.Hutao.ViewModel;
|
||||
/// <summary>
|
||||
/// 成就视图模型
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient)]
|
||||
[Injection(InjectAs.Scoped)]
|
||||
internal class AchievementViewModel
|
||||
: ObservableObject,
|
||||
ISupportCancellation,
|
||||
@@ -42,10 +42,10 @@ internal class AchievementViewModel
|
||||
new(nameof(Model.Binding.Achievement.IsChecked), SortDirection.Ascending);
|
||||
|
||||
private readonly IMetadataService metadataService;
|
||||
private readonly IAchievementService achievementService;
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly JsonSerializerOptions options;
|
||||
private readonly IPickerFactory pickerFactory;
|
||||
|
||||
private readonly IAchievementService achievementService;
|
||||
|
||||
private readonly TaskCompletionSource<bool> openUICompletionSource = new();
|
||||
|
||||
@@ -67,7 +67,7 @@ internal class AchievementViewModel
|
||||
/// <param name="infoBarService">信息条服务</param>
|
||||
/// <param name="options">Json序列化选项</param>
|
||||
/// <param name="asyncRelayCommandFactory">异步命令工厂</param>
|
||||
/// <param name="pickerFactory">文件选择器工厂</param>
|
||||
/// <param name="scopeFactory">范围工厂</param>
|
||||
/// <param name="messenger">消息器</param>
|
||||
public AchievementViewModel(
|
||||
IMetadataService metadataService,
|
||||
@@ -75,14 +75,12 @@ internal class AchievementViewModel
|
||||
IInfoBarService infoBarService,
|
||||
JsonSerializerOptions options,
|
||||
IAsyncRelayCommandFactory asyncRelayCommandFactory,
|
||||
IPickerFactory pickerFactory,
|
||||
IMessenger messenger)
|
||||
{
|
||||
this.metadataService = metadataService;
|
||||
this.achievementService = achievementService;
|
||||
this.infoBarService = infoBarService;
|
||||
this.options = options;
|
||||
this.pickerFactory = pickerFactory;
|
||||
|
||||
OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync);
|
||||
ImportUIAFFromClipboardCommand = asyncRelayCommandFactory.Create(ImportUIAFFromClipboardAsync);
|
||||
@@ -416,6 +414,7 @@ internal class AchievementViewModel
|
||||
return;
|
||||
}
|
||||
|
||||
IPickerFactory pickerFactory = Ioc.Default.GetRequiredService<IPickerFactory>();
|
||||
FileOpenPicker picker = pickerFactory.GetFileOpenPicker();
|
||||
picker.SuggestedStartLocation = PickerLocationId.Desktop;
|
||||
picker.CommitButtonText = "导入";
|
||||
|
||||
@@ -16,12 +16,10 @@ namespace Snap.Hutao.ViewModel;
|
||||
/// <summary>
|
||||
/// 公告视图模型
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient)]
|
||||
[Injection(InjectAs.Scoped)]
|
||||
internal class AnnouncementViewModel : ObservableObject, ISupportCancellation
|
||||
{
|
||||
private readonly IAnnouncementService announcementService;
|
||||
private readonly INavigationService navigationService;
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly ILogger<AnnouncementViewModel> logger;
|
||||
|
||||
private AnnouncementWrapper? announcement;
|
||||
@@ -36,14 +34,10 @@ internal class AnnouncementViewModel : ObservableObject, ISupportCancellation
|
||||
/// <param name="logger">日志器</param>
|
||||
public AnnouncementViewModel(
|
||||
IAnnouncementService announcementService,
|
||||
INavigationService navigationService,
|
||||
IAsyncRelayCommandFactory asyncRelayCommandFactory,
|
||||
IInfoBarService infoBarService,
|
||||
ILogger<AnnouncementViewModel> logger)
|
||||
{
|
||||
this.announcementService = announcementService;
|
||||
this.navigationService = navigationService;
|
||||
this.infoBarService = infoBarService;
|
||||
this.logger = logger;
|
||||
|
||||
OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync);
|
||||
@@ -88,10 +82,12 @@ internal class AnnouncementViewModel : ObservableObject, ISupportCancellation
|
||||
{
|
||||
if (WebView2Helper.IsSupported)
|
||||
{
|
||||
INavigationService navigationService = Ioc.Default.GetRequiredService<INavigationService>();
|
||||
navigationService.Navigate<AnnouncementContentPage>(data: new NavigationExtra(content));
|
||||
}
|
||||
else
|
||||
{
|
||||
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
||||
infoBarService.Warning("尚未安装 WebView2 运行时。");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Snap.Hutao.ViewModel;
|
||||
/// <summary>
|
||||
/// 角色属性视图模型
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient)]
|
||||
[Injection(InjectAs.Scoped)]
|
||||
internal class AvatarPropertyViewModel : ObservableObject, ISupportCancellation
|
||||
{
|
||||
private readonly IUserService userService;
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Snap.Hutao.ViewModel;
|
||||
/// <summary>
|
||||
/// 实验性功能视图模型
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient)]
|
||||
[Injection(InjectAs.Scoped)]
|
||||
internal class ExperimentalFeaturesViewModel : ObservableObject
|
||||
{
|
||||
private readonly IFileSystemLocation hutaoLocation;
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Snap.Hutao.ViewModel;
|
||||
/// <summary>
|
||||
/// 祈愿记录视图模型
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient)]
|
||||
[Injection(InjectAs.Scoped)]
|
||||
internal class GachaLogViewModel : ObservableObject, ISupportCancellation
|
||||
{
|
||||
private readonly IGachaLogService gachaLogService;
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Snap.Hutao.ViewModel;
|
||||
/// <summary>
|
||||
/// 胡桃数据库视图模型
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient)]
|
||||
[Injection(InjectAs.Scoped)]
|
||||
internal class HutaoDatabaseViewModel : ObservableObject, ISupportCancellation
|
||||
{
|
||||
private readonly IHtaoCache hutaoCache;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Context.Database;
|
||||
using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
|
||||
namespace Snap.Hutao.ViewModel;
|
||||
@@ -11,7 +11,7 @@ namespace Snap.Hutao.ViewModel;
|
||||
/// <summary>
|
||||
/// 测试视图模型
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient)]
|
||||
[Injection(InjectAs.Scoped)]
|
||||
internal class SettingViewModel : ObservableObject
|
||||
{
|
||||
private readonly AppDbContext appDbContext;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Snap.Hutao.ViewModel;
|
||||
/// <summary>
|
||||
/// 用户视图模型
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient)]
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal class UserViewModel : ObservableObject
|
||||
{
|
||||
private readonly IUserService userService;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Snap.Hutao.ViewModel;
|
||||
/// <summary>
|
||||
/// 角色资料视图模型
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient)]
|
||||
[Injection(InjectAs.Scoped)]
|
||||
internal class WikiAvatarViewModel : ObservableObject
|
||||
{
|
||||
private readonly IMetadataService metadataService;
|
||||
|
||||
Reference in New Issue
Block a user