mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
[skip ci] broken
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Snap.Hutao.Core.Database.Abstraction;
|
||||
using Snap.Hutao.Model;
|
||||
@@ -52,6 +53,10 @@ internal sealed class AdvancedDbCollectionView<TEntity> : AdvancedCollectionView
|
||||
dbContext.Set<TEntity>().UpdateAndSave(currentItem);
|
||||
}
|
||||
}
|
||||
|
||||
serviceProvider
|
||||
.GetRequiredService<IMessenger>()
|
||||
.Send(new AdvancedDbCollectionViewCurrentChangedMessage<TEntity>(currentItem));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,5 +105,9 @@ internal sealed class AdvancedDbCollectionView<TEntityAccess, TEntity> : Advance
|
||||
dbContext.Set<TEntity>().UpdateAndSave(currentItem.Entity);
|
||||
}
|
||||
}
|
||||
|
||||
serviceProvider
|
||||
.GetRequiredService<IMessenger>()
|
||||
.Send(new AdvancedDbCollectionViewCurrentChangedMessage<TEntityAccess>(currentItem));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Database;
|
||||
|
||||
internal sealed class AdvancedDbCollectionViewCurrentChangedMessage<TItem>
|
||||
where TItem : class
|
||||
{
|
||||
public AdvancedDbCollectionViewCurrentChangedMessage(TItem? currentItem)
|
||||
{
|
||||
CurrentItem = currentItem;
|
||||
}
|
||||
|
||||
public TItem? CurrentItem { get; }
|
||||
}
|
||||
@@ -44,7 +44,7 @@ internal static class DependencyInjection
|
||||
.AddConfiguredHttpClients()
|
||||
|
||||
// Discrete services
|
||||
.AddSingleton<IMessenger, WeakReferenceMessenger>()
|
||||
.AddSingleton<IMessenger, StrongReferenceMessenger>()
|
||||
.BuildServiceProvider(true);
|
||||
|
||||
Ioc.Default.ConfigureServices(serviceProvider);
|
||||
|
||||
@@ -26,7 +26,7 @@ internal sealed partial class HttpProxyUsingSystemProxy : ObservableObject, IWeb
|
||||
UpdateInnerProxy();
|
||||
|
||||
watcher = new(ProxySettingPath, OnSystemProxySettingsChanged);
|
||||
watcher.Start();
|
||||
watcher.Start(serviceProvider.GetRequiredService<ILogger<HttpProxyUsingSystemProxy>>());
|
||||
}
|
||||
|
||||
public string CurrentProxyUri
|
||||
|
||||
@@ -24,7 +24,9 @@ internal static class TaskExtension
|
||||
return new(task);
|
||||
}
|
||||
|
||||
#if NET9_0_OR_GREATER
|
||||
[Obsolete("SafeForget without logger is not recommended.")]
|
||||
#endif
|
||||
public static async void SafeForget(this Task task)
|
||||
{
|
||||
try
|
||||
@@ -101,7 +103,9 @@ internal static class TaskExtension
|
||||
}
|
||||
}
|
||||
|
||||
#if NET9_0_OR_GREATER
|
||||
[Obsolete("SafeForget without logger is not recommended.")]
|
||||
#endif
|
||||
public static async void SafeForget(this ValueTask task)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Message;
|
||||
|
||||
[Obsolete]
|
||||
internal sealed class BackgroundImageTypeChangedMessage;
|
||||
@@ -1,15 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
|
||||
namespace Snap.Hutao.Message;
|
||||
|
||||
/// <summary>
|
||||
/// 养成计划切换消息
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Obsolete]
|
||||
internal sealed class CultivateProjectChangedMessage : ValueChangedMessage<CultivateProject>
|
||||
{
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
|
||||
namespace Snap.Hutao.Message;
|
||||
|
||||
/// <summary>
|
||||
/// 祈愿记录存档切换消息
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Obsolete]
|
||||
internal sealed class GachaArchiveChangedMessage : ValueChangedMessage<GachaArchive>
|
||||
{
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Message;
|
||||
|
||||
internal enum UserChangeFlag
|
||||
{
|
||||
// This flag is impossible to appear alone
|
||||
UserChanged = 0b0001,
|
||||
RoleChanged = 0b0010,
|
||||
UserAndRoleChanged = UserChanged | RoleChanged,
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.ViewModel.User;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace Snap.Hutao.Message;
|
||||
|
||||
/// <summary>
|
||||
/// 用户切换消息
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[DebuggerDisplay("{DebuggerDisplay(),nq}")]
|
||||
[Obsolete]
|
||||
internal sealed class UserChangedMessage : ValueChangedMessage<User>
|
||||
{
|
||||
// defaults to the UserAndRoleChanged when we raise this message in ScopedDbCurrent
|
||||
public UserChangeFlag Flag { get; private set; } = UserChangeFlag.UserAndRoleChanged;
|
||||
|
||||
public bool IsOnlyRoleChanged { get => Flag == UserChangeFlag.RoleChanged; }
|
||||
|
||||
public static UserChangedMessage Create(User oldValue, User newValue, UserChangeFlag flag)
|
||||
{
|
||||
return new UserChangedMessage
|
||||
{
|
||||
OldValue = oldValue,
|
||||
NewValue = newValue,
|
||||
Flag = flag,
|
||||
};
|
||||
}
|
||||
|
||||
public static UserChangedMessage CreateOnlyRoleChanged(User value)
|
||||
{
|
||||
return Create(value, value, UserChangeFlag.RoleChanged);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
private string DebuggerDisplay()
|
||||
{
|
||||
StringBuilder stringBuilder = new();
|
||||
stringBuilder
|
||||
.Append("Name:")
|
||||
.Append(OldValue?.UserInfo?.Nickname)
|
||||
.Append("|Role[")
|
||||
.Append(OldValue?.UserGameRoles?.Count)
|
||||
.Append("]:<")
|
||||
.Append(OldValue?.SelectedUserGameRole)
|
||||
.Append("> -> Name:")
|
||||
.Append(NewValue?.UserInfo?.Nickname)
|
||||
.Append("|Role[")
|
||||
.Append(NewValue?.UserGameRoles?.Count)
|
||||
.Append("]:<")
|
||||
.Append(NewValue?.SelectedUserGameRole)
|
||||
.Append('>');
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
|
||||
namespace Snap.Hutao.Message;
|
||||
|
||||
/// <summary>
|
||||
/// 用户删除消息
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Obsolete]
|
||||
internal sealed class UserRemovedMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的用户删除消息
|
||||
/// </summary>
|
||||
/// <param name="user">用户</param>
|
||||
public UserRemovedMessage(User user)
|
||||
{
|
||||
RemovedUserId = user.InnerId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除的用户Id
|
||||
/// </summary>
|
||||
public Guid RemovedUserId { get; }
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Message;
|
||||
|
||||
/// <summary>
|
||||
/// 值变化消息
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">值的类型</typeparam>
|
||||
[HighQuality]
|
||||
[Obsolete]
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
|
||||
internal abstract class ValueChangedMessage<TValue>
|
||||
where TValue : class
|
||||
{
|
||||
/// <summary>
|
||||
/// 动态访问
|
||||
/// </summary>
|
||||
public ValueChangedMessage()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的值变化消息
|
||||
/// </summary>
|
||||
/// <param name="oldValue">旧值</param>
|
||||
/// <param name="newValue">新值</param>
|
||||
protected ValueChangedMessage(TValue? oldValue, TValue? newValue)
|
||||
{
|
||||
OldValue = oldValue;
|
||||
NewValue = newValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 旧的值
|
||||
/// </summary>
|
||||
public TValue? OldValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 新的值
|
||||
/// </summary>
|
||||
public TValue? NewValue { get; set; }
|
||||
|
||||
public Guid UniqueMessageId { get; } = Guid.NewGuid();
|
||||
}
|
||||
@@ -64,24 +64,15 @@ internal sealed partial class AchievementDbService : IAchievementDbService
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask OverwriteAchievementAsync(EntityAchievement achievement, CancellationToken token = default)
|
||||
{
|
||||
await this.DeleteByInnerIdAsync(achievement, token).ConfigureAwait(false);
|
||||
if (achievement.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
|
||||
{
|
||||
await this.AddAsync(achievement, token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<AchievementArchive> GetAchievementArchiveCollection()
|
||||
{
|
||||
return this.ObservableCollection<AchievementArchive>();
|
||||
}
|
||||
|
||||
public async ValueTask RemoveAchievementArchiveAsync(AchievementArchive archive, CancellationToken token = default)
|
||||
public void RemoveAchievementArchive(AchievementArchive archive)
|
||||
{
|
||||
// It will cascade delete the achievements.
|
||||
await this.DeleteAsync(archive, token).ConfigureAwait(false);
|
||||
this.Delete(archive);
|
||||
}
|
||||
|
||||
public List<EntityAchievement> GetAchievementListByArchiveId(Guid archiveId)
|
||||
|
||||
@@ -77,7 +77,7 @@ internal sealed partial class AchievementService : IAchievementService
|
||||
|
||||
// Sync database
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
await achievementDbService.RemoveAchievementArchiveAsync(archive).ConfigureAwait(false);
|
||||
achievementDbService.RemoveAchievementArchive(archive);
|
||||
}
|
||||
|
||||
public async ValueTask<ImportResult> ImportFromUIAFAsync(AchievementArchive archive, List<UIAFItem> list, ImportStrategyKind strategy)
|
||||
|
||||
@@ -10,8 +10,6 @@ namespace Snap.Hutao.Service.Achievement;
|
||||
|
||||
internal interface IAchievementDbService : IAppDbService<Model.Entity.AchievementArchive>, IAppDbService<EntityAchievement>
|
||||
{
|
||||
ValueTask RemoveAchievementArchiveAsync(Model.Entity.AchievementArchive archive, CancellationToken token = default);
|
||||
|
||||
ObservableCollection<Model.Entity.AchievementArchive> GetAchievementArchiveCollection();
|
||||
|
||||
List<Model.Entity.AchievementArchive> GetAchievementArchiveList();
|
||||
@@ -30,5 +28,5 @@ internal interface IAchievementDbService : IAppDbService<Model.Entity.Achievemen
|
||||
|
||||
void OverwriteAchievement(EntityAchievement achievement);
|
||||
|
||||
ValueTask OverwriteAchievementAsync(EntityAchievement achievement, CancellationToken token = default);
|
||||
void RemoveAchievementArchive(Model.Entity.AchievementArchive archive);
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Snap.Hutao.Core.DependencyInjection.Abstraction;
|
||||
using Snap.Hutao.Message;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
@@ -18,13 +16,9 @@ using WebDailyNote = Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote.DailyNot
|
||||
|
||||
namespace Snap.Hutao.Service.DailyNote;
|
||||
|
||||
/// <summary>
|
||||
/// 实时便笺服务
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton, typeof(IDailyNoteService))]
|
||||
internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient<UserRemovedMessage>
|
||||
internal sealed partial class DailyNoteService : IDailyNoteService
|
||||
{
|
||||
private readonly DailyNoteNotificationOperation dailyNoteNotificationOperation;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
@@ -176,4 +170,4 @@ internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient<U
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.InterChange.GachaLog;
|
||||
using Snap.Hutao.Service.GachaLog.QueryProvider;
|
||||
using Snap.Hutao.ViewModel.GachaLog;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Snap.Hutao.Service.GachaLog;
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using Snap.Hutao.Core.IO;
|
||||
using Snap.Hutao.Service.Game;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -64,6 +63,7 @@ internal sealed partial class GachaLogQueryWebCacheProvider : IGachaLogQueryProv
|
||||
return new(false, GachaLogQuery.Invalid(SH.FormatServiceGachaLogUrlProviderCachePathNotFound(cacheFile)));
|
||||
}
|
||||
|
||||
// TODO: prevent allocation there
|
||||
using (FileStream fileStream = new(file.Path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
using (MemoryStream memoryStream = new())
|
||||
|
||||
@@ -12,11 +12,9 @@ namespace Snap.Hutao.Service.User;
|
||||
|
||||
internal interface IUserCollectionService
|
||||
{
|
||||
BindingUser? CurrentUser { get; set; }
|
||||
|
||||
ValueTask<ObservableCollection<UserAndUid>> GetUserAndUidCollectionAsync();
|
||||
|
||||
ValueTask<ObservableReorderableDbCollection<BindingUser, EntityUser>> GetUserCollectionAsync();
|
||||
ValueTask<AdvancedDbCollectionView<BindingUser, EntityUser>> GetUserCollectionAsync();
|
||||
|
||||
UserGameRole? GetUserGameRoleByUid(string uid);
|
||||
|
||||
|
||||
@@ -2,56 +2,20 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.ViewModel.User;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
using System.Collections.ObjectModel;
|
||||
using BindingUser = Snap.Hutao.ViewModel.User.User;
|
||||
using EntityUser = Snap.Hutao.Model.Entity.User;
|
||||
|
||||
namespace Snap.Hutao.Service.User;
|
||||
|
||||
/// <summary>
|
||||
/// 用户服务
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal interface IUserService
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置当前用户
|
||||
/// </summary>
|
||||
BindingUser? Current { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取角色与用户集合
|
||||
/// </summary>
|
||||
/// <returns>角色与用户集合</returns>
|
||||
ValueTask<ObservableCollection<UserAndUid>> GetRoleCollectionAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化用户服务及所有用户
|
||||
/// 异步获取同步的用户信息集合
|
||||
/// 对集合的操作应通过服务抽象完成
|
||||
/// 此操作不能取消
|
||||
/// </summary>
|
||||
/// <returns>准备完成的用户信息集合</returns>
|
||||
ValueTask<ObservableReorderableDbCollection<BindingUser, EntityUser>> GetUserCollectionAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 获取角色信息
|
||||
/// </summary>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <returns>对应的角色信息</returns>
|
||||
UserGameRole? GetUserGameRoleByUid(string uid);
|
||||
ValueTask<AdvancedDbCollectionView<BindingUser, EntityUser>> GetUserCollectionAsync();
|
||||
|
||||
ValueTask<ValueResult<UserOptionResult, string>> ProcessInputCookieAsync(InputCookie inputCookie);
|
||||
|
||||
ValueTask<bool> RefreshCookieTokenAsync(Model.Entity.User user);
|
||||
ValueTask<bool> RefreshCookieTokenAsync(EntityUser user);
|
||||
|
||||
/// <summary>
|
||||
/// 异步移除用户
|
||||
/// </summary>
|
||||
/// <param name="user">待移除的用户</param>
|
||||
/// <returns>任务</returns>
|
||||
ValueTask RemoveUserAsync(BindingUser user);
|
||||
|
||||
ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole);
|
||||
|
||||
@@ -5,5 +5,5 @@ namespace Snap.Hutao.Service.User;
|
||||
|
||||
internal interface IUserServiceUnsafe
|
||||
{
|
||||
ValueTask UnsafeRemoveUsersAsync();
|
||||
ValueTask UnsafeRemoveAllUsersAsync();
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Message;
|
||||
using Snap.Hutao.ViewModel.User;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -16,7 +15,6 @@ namespace Snap.Hutao.Service.User;
|
||||
[Injection(InjectAs.Singleton, typeof(IUserCollectionService))]
|
||||
internal sealed partial class UserCollectionService : IUserCollectionService, IDisposable
|
||||
{
|
||||
private readonly ScopedDbCurrent<BindingUser, EntityUser, UserChangedMessage> dbCurrent;
|
||||
private readonly IUserInitializationService userInitializationService;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly IUserDbService userDbService;
|
||||
@@ -25,37 +23,21 @@ internal sealed partial class UserCollectionService : IUserCollectionService, ID
|
||||
|
||||
private readonly SemaphoreSlim throttler = new(1);
|
||||
|
||||
private ObservableReorderableDbCollection<BindingUser, EntityUser>? userCollection;
|
||||
private Dictionary<string, BindingUser>? midUserMap;
|
||||
private AdvancedDbCollectionView<BindingUser, EntityUser>? users;
|
||||
|
||||
private ObservableCollection<UserAndUid>? userAndUidCollection;
|
||||
private Dictionary<string, UserGameRole>? uidUserGameRoleMap;
|
||||
|
||||
public BindingUser? CurrentUser
|
||||
{
|
||||
get => dbCurrent.Current;
|
||||
set => dbCurrent.Current = value;
|
||||
}
|
||||
|
||||
public async ValueTask<ObservableReorderableDbCollection<BindingUser, EntityUser>> GetUserCollectionAsync()
|
||||
public async ValueTask<AdvancedDbCollectionView<BindingUser, EntityUser>> GetUserCollectionAsync()
|
||||
{
|
||||
// Force run in background thread, otherwise will cause reentrance
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
using (await throttler.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
if (userCollection is null)
|
||||
if (users is null)
|
||||
{
|
||||
List<EntityUser> entities = await userDbService.GetUserListAsync().ConfigureAwait(false);
|
||||
List<BindingUser> users = await entities.SelectListAsync(userInitializationService.ResumeUserAsync).ConfigureAwait(false);
|
||||
|
||||
midUserMap = [];
|
||||
foreach (BindingUser user in users)
|
||||
{
|
||||
if (user.Entity.Mid is not null)
|
||||
{
|
||||
midUserMap[user.Entity.Mid] = user;
|
||||
}
|
||||
|
||||
if (user.NeedDbUpdateAfterResume)
|
||||
{
|
||||
await userDbService.UpdateUserAsync(user.Entity).ConfigureAwait(false);
|
||||
@@ -63,25 +45,12 @@ internal sealed partial class UserCollectionService : IUserCollectionService, ID
|
||||
}
|
||||
}
|
||||
|
||||
userCollection = users.ToObservableReorderableDbCollection<BindingUser, EntityUser>(serviceProvider);
|
||||
|
||||
try
|
||||
{
|
||||
CurrentUser = users.SelectedOrDefault();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
foreach (BindingUser user in users)
|
||||
{
|
||||
user.IsSelected = false;
|
||||
}
|
||||
|
||||
await userDbService.ClearUserSelectionAsync().ConfigureAwait(false);
|
||||
}
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
this.users = new(users.ToObservableReorderableDbCollection<BindingUser, EntityUser>(serviceProvider), serviceProvider);
|
||||
}
|
||||
}
|
||||
|
||||
return userCollection;
|
||||
return users;
|
||||
}
|
||||
|
||||
public async ValueTask<ObservableCollection<UserAndUid>> GetUserAndUidCollectionAsync()
|
||||
@@ -112,8 +81,8 @@ internal sealed partial class UserCollectionService : IUserCollectionService, ID
|
||||
{
|
||||
// Sync cache
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
ArgumentNullException.ThrowIfNull(userCollection);
|
||||
userCollection.Remove(user);
|
||||
ArgumentNullException.ThrowIfNull(users);
|
||||
users.Remove(user);
|
||||
userAndUidCollection?.RemoveWhere(r => r.User.Mid == user.Entity.Mid);
|
||||
if (user.Entity.Mid is not null)
|
||||
{
|
||||
@@ -159,11 +128,11 @@ internal sealed partial class UserCollectionService : IUserCollectionService, ID
|
||||
}
|
||||
|
||||
await GetUserCollectionAsync().ConfigureAwait(false);
|
||||
ArgumentNullException.ThrowIfNull(userCollection);
|
||||
ArgumentNullException.ThrowIfNull(users);
|
||||
|
||||
// Sync cache
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
userCollection.Add(newUser); // Database synced in the collection
|
||||
users.Add(newUser); // Database synced in the collection
|
||||
if (newUser.Entity.Mid is not null)
|
||||
{
|
||||
midUserMap?.Add(newUser.Entity.Mid, newUser);
|
||||
|
||||
@@ -9,14 +9,12 @@ using Snap.Hutao.Web.Hoyolab.Passport;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.Collections.ObjectModel;
|
||||
using Windows.Foundation;
|
||||
using BindingUser = Snap.Hutao.ViewModel.User.User;
|
||||
using EntityUser = Snap.Hutao.Model.Entity.User;
|
||||
|
||||
namespace Snap.Hutao.Service.User;
|
||||
|
||||
/// <summary>
|
||||
/// 用户服务
|
||||
/// </summary>
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton, typeof(IUserService))]
|
||||
internal sealed partial class UserService : IUserService, IUserServiceUnsafe
|
||||
@@ -27,24 +25,18 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe
|
||||
private readonly IUserDbService userDbService;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
public BindingUser? Current
|
||||
{
|
||||
get => userCollectionService.CurrentUser;
|
||||
set => userCollectionService.CurrentUser = value;
|
||||
}
|
||||
|
||||
public ValueTask RemoveUserAsync(BindingUser user)
|
||||
{
|
||||
return userCollectionService.RemoveUserAsync(user);
|
||||
}
|
||||
|
||||
public async ValueTask UnsafeRemoveUsersAsync()
|
||||
public async ValueTask UnsafeRemoveAllUsersAsync()
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
await userDbService.RemoveUsersAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public ValueTask<ObservableReorderableDbCollection<BindingUser, EntityUser>> GetUserCollectionAsync()
|
||||
public ValueTask<AdvancedDbCollectionView<BindingUser, EntityUser>> GetUserCollectionAsync()
|
||||
{
|
||||
return userCollectionService.GetUserCollectionAsync();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
mc:Ignorable="d">
|
||||
|
||||
<mxi:Interaction.Behaviors>
|
||||
<shuxb:InvokeCommandOnLoadedBehavior Command="{Binding LoadCommand}"/>
|
||||
<shuxb:PeriodicInvokeCommandOrOnActualThemeChangedBehavior Command="{Binding UpdateBackgroundCommand}" Period="0:5:0"/>
|
||||
</mxi:Interaction.Behaviors>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
using Snap.Hutao.UI.Xaml.View.Page;
|
||||
@@ -27,6 +28,8 @@ internal sealed partial class MainView : UserControl
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
this.Unloaded += OnUnloaded;
|
||||
|
||||
(DataContext as MainViewModel)?.Initialize(new BackgroundImagePresenterAccessor(BackgroundImagePresenter));
|
||||
|
||||
navigationService = serviceProvider.GetRequiredService<INavigationService>();
|
||||
@@ -38,6 +41,11 @@ internal sealed partial class MainView : UserControl
|
||||
navigationService.Navigate<AnnouncementPage>(INavigationAwaiter.Default, true);
|
||||
}
|
||||
|
||||
private void OnUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
(DataContext as MainViewModel)?.Uninitialize();
|
||||
}
|
||||
|
||||
private class NavigationViewAccessor : INavigationViewAccessor
|
||||
{
|
||||
public NavigationViewAccessor(NavigationView navigationView, Frame frame)
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Snap.Hutao.ViewModel;
|
||||
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton)]
|
||||
internal sealed partial class MainViewModel : Abstraction.ViewModel, IMainViewModelInitialization, IRecipient<BackgroundImageTypeChangedMessage>
|
||||
internal sealed partial class MainViewModel : Abstraction.ViewModel, IMainViewModelInitialization
|
||||
{
|
||||
private readonly IBackgroundImageService backgroundImageService;
|
||||
private readonly ILogger<MainViewModel> logger;
|
||||
@@ -34,9 +34,23 @@ internal sealed partial class MainViewModel : Abstraction.ViewModel, IMainViewMo
|
||||
UpdateBackgroundAsync(true).SafeForget();
|
||||
}
|
||||
|
||||
public void Receive(BackgroundImageTypeChangedMessage message)
|
||||
protected override ValueTask<bool> InitializeOverrideAsync()
|
||||
{
|
||||
UpdateBackgroundAsync().SafeForget();
|
||||
appOptions.PropertyChanged += OnAppOptionsPropertyChanged;
|
||||
return ValueTask.FromResult(true);
|
||||
}
|
||||
|
||||
protected override void UninitializeOverride()
|
||||
{
|
||||
appOptions.PropertyChanged -= OnAppOptionsPropertyChanged;
|
||||
}
|
||||
|
||||
private void OnAppOptionsPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(AppOptions.BackgroundImageType))
|
||||
{
|
||||
UpdateBackgroundAsync().SafeForget();
|
||||
}
|
||||
}
|
||||
|
||||
[Command("UpdateBackgroundCommand")]
|
||||
|
||||
@@ -120,7 +120,6 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
|
||||
if (SetProperty(ref selectedBackgroundImageType, value) && value is not null)
|
||||
{
|
||||
AppOptions.BackgroundImageType = value.Value;
|
||||
messenger.Send(new Message.BackgroundImageTypeChangedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,7 +346,7 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
|
||||
|
||||
if (result is ContentDialogResult.Primary)
|
||||
{
|
||||
await @unsafe.UnsafeRemoveUsersAsync().ConfigureAwait(false);
|
||||
await @unsafe.UnsafeRemoveAllUsersAsync().ConfigureAwait(false);
|
||||
AppInstance.Restart(string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Core.Database.Abstraction;
|
||||
using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
using Snap.Hutao.UI.Xaml.Data;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
using Snap.Hutao.Web.Hoyolab.Bbs.User;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
@@ -15,11 +16,11 @@ using EntityUser = Snap.Hutao.Model.Entity.User;
|
||||
|
||||
namespace Snap.Hutao.ViewModel.User;
|
||||
|
||||
/// <summary>
|
||||
/// 用于视图绑定的用户
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal sealed class User : ObservableObject, IEntityAccess<EntityUser>, IMappingFrom<User, EntityUser, IServiceProvider>, ISelectable
|
||||
internal sealed class User : ObservableObject,
|
||||
IEntityAccess<EntityUser>,
|
||||
IMappingFrom<User, EntityUser, IServiceProvider>,
|
||||
ISelectable,
|
||||
IAdvancedCollectionViewItem
|
||||
{
|
||||
private readonly EntityUser inner;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
@@ -34,19 +35,10 @@ internal sealed class User : ObservableObject, IEntityAccess<EntityUser>, IMappi
|
||||
|
||||
public bool IsInitialized { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户信息
|
||||
/// </summary>
|
||||
public UserInfo? UserInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户信息
|
||||
/// </summary>
|
||||
public List<UserGameRole> UserGameRoles { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 用户信息
|
||||
/// </summary>
|
||||
public UserGameRole? SelectedUserGameRole
|
||||
{
|
||||
get => selectedUserGameRole;
|
||||
@@ -57,42 +49,32 @@ internal sealed class User : ObservableObject, IEntityAccess<EntityUser>, IMappi
|
||||
|
||||
public Guid InnerId { get => inner.InnerId; }
|
||||
|
||||
/// <inheritdoc cref="EntityUser.IsSelected"/>
|
||||
public bool IsSelected
|
||||
{
|
||||
get => inner.IsSelected;
|
||||
set => inner.IsSelected = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityUser.CookieToken"/>
|
||||
public Cookie? CookieToken
|
||||
{
|
||||
get => inner.CookieToken;
|
||||
set => inner.CookieToken = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityUser.LToken"/>
|
||||
public Cookie? LToken
|
||||
{
|
||||
get => inner.LToken;
|
||||
set => inner.LToken = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityUser.SToken"/>
|
||||
public Cookie? SToken
|
||||
{
|
||||
get => inner.SToken;
|
||||
set => inner.SToken = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为国际服
|
||||
/// </summary>
|
||||
public bool IsOversea { get => Entity.IsOversea; }
|
||||
|
||||
/// <summary>
|
||||
/// 内部的用户实体
|
||||
/// </summary>
|
||||
public EntityUser Entity { get => inner; }
|
||||
|
||||
public bool NeedDbUpdateAfterResume { get; set; }
|
||||
@@ -123,4 +105,12 @@ internal sealed class User : ObservableObject, IEntityAccess<EntityUser>, IMappi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public object? GetPropertyValue(string name)
|
||||
{
|
||||
return name switch
|
||||
{
|
||||
_ => default,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -103,6 +103,8 @@ internal class MiHoYoJSBridgeFacade
|
||||
|
||||
public event Action? ClosePageRequested;
|
||||
|
||||
protected ILogger Logger { get => logger; }
|
||||
|
||||
public void Detach()
|
||||
{
|
||||
coreWebView2.WebMessageReceived -= OnWebMessageReceived;
|
||||
|
||||
@@ -6,10 +6,6 @@ using Snap.Hutao.ViewModel.User;
|
||||
|
||||
namespace Snap.Hutao.Web.Bridge;
|
||||
|
||||
/// <summary>
|
||||
/// HoYoLAB 签到页面JS桥
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal sealed class SignInJSBridgeOversea : MiHoYoJSBridgeFacade
|
||||
{
|
||||
// 移除 请旋转手机 提示所在的HTML元素
|
||||
@@ -30,6 +26,6 @@ internal sealed class SignInJSBridgeOversea : MiHoYoJSBridgeFacade
|
||||
|
||||
protected override void DOMContentLoaded(CoreWebView2 coreWebView2)
|
||||
{
|
||||
coreWebView2.ExecuteScriptAsync(RemoveRotationWarningScript).AsTask().SafeForget();
|
||||
coreWebView2.ExecuteScriptAsync(RemoveRotationWarningScript).AsTask().SafeForget(Logger);
|
||||
}
|
||||
}
|
||||
@@ -47,10 +47,10 @@ internal sealed partial class RegistryWatcher : IDisposable
|
||||
this.valueChangedCallback = valueChangedCallback;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
public void Start(ILogger logger)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(disposed, this);
|
||||
WatchAsync(cancellationTokenSource.Token).SafeForget();
|
||||
WatchAsync(cancellationTokenSource.Token).SafeForget(logger);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
Reference in New Issue
Block a user