code style

This commit is contained in:
DismissedLight
2023-02-10 16:07:01 +08:00
parent faad104e0e
commit d7dd8c6f0d
47 changed files with 353 additions and 287 deletions

View File

@@ -7,7 +7,6 @@ using Microsoft.Windows.AppLifecycle;
using Snap.Hutao.Core;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.LifeCycle;
using Snap.Hutao.Core.Logging;
using System.Diagnostics;
using Windows.Storage;

View File

@@ -21,6 +21,7 @@ internal static class ContentDialogExtension
contentDialog.ShowAsync().AsTask().SafeForget();
// E_ASYNC_OPERATION_NOT_STARTED 0x80000019
// Only a single ContentDialog can be open at any time.
return new ContentDialogHider(contentDialog);
}

View File

@@ -12,7 +12,6 @@ using Snap.Hutao.Extension;
using Snap.Hutao.Service.Abstraction;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Snap.Hutao.Control.Image;

View File

@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Snap.Hutao.Core.Annotation;
namespace Snap.Hutao.Core.Annotation;
/// <summary>
/// 本地化键

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.ExceptionService;
using System.IO;
namespace Snap.Hutao.Core.IO;
@@ -16,7 +17,14 @@ internal sealed class TempFile : IDisposable
/// <param name="delete">是否在创建时删除文件</param>
public TempFile(bool delete = false)
{
Path = System.IO.Path.GetTempFileName();
try
{
Path = System.IO.Path.GetTempFileName();
}
catch (UnauthorizedAccessException ex)
{
throw ThrowHelper.RuntimeEnvironment(SH.CoreIOTempFileCreateFail, ex);
}
if (delete)
{

View File

@@ -109,7 +109,7 @@ internal static class Activation
private static async Task HandleActivationCoreAsync(AppActivationArguments args, bool isRedirected)
{
if (args.Kind == ExtendedActivationKind.Protocol)
if (args.Kind == ExtendedActivationKind.Protocol)
{
if (args.TryGetProtocolActivatedUri(out Uri? uri))
{

View File

@@ -10,7 +10,6 @@ using Snap.Hutao.Extension;
using Snap.Hutao.Message;
using Snap.Hutao.Win32;
using System.IO;
using Windows.ApplicationModel;
using Windows.Graphics;
using Windows.UI;
using Windows.Win32.Foundation;

View File

@@ -29,7 +29,7 @@ public class SpiralAbyssEntry : ObservableObject
/// 计划名称
/// </summary>
[NotMapped]
public string Schedule { get => $"第 {ScheduleId} 期"; }
public string Schedule { get => string.Format(SH.ModelEntitySpiralAbyssScheduleFormat, ScheduleId); }
/// <summary>
/// Uid

View File

@@ -195,6 +195,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 权限不足,创建临时文件失败 的本地化字符串。
/// </summary>
internal static string CoreIOTempFileCreateFail {
get {
return ResourceManager.GetString("CoreIOTempFileCreateFail", resourceCulture);
}
}
/// <summary>
/// 查找类似 启动游戏 的本地化字符串。
/// </summary>
@@ -339,6 +348,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 第 {0} 期 的本地化字符串。
/// </summary>
internal static string ModelEntitySpiralAbyssScheduleFormat {
get {
return ResourceManager.GetString("ModelEntitySpiralAbyssScheduleFormat", resourceCulture);
}
}
/// <summary>
/// 查找类似 必须先选择一个用户与角色 的本地化字符串。
/// </summary>
@@ -357,6 +375,24 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 保存养成计划状态失败 的本地化字符串。
/// </summary>
internal static string ServiceCultivationProjectCurrentUserdataCourrpted {
get {
return ResourceManager.GetString("ServiceCultivationProjectCurrentUserdataCourrpted", resourceCulture);
}
}
/// <summary>
/// 查找类似 存在多个选中的养成计划 的本地化字符串。
/// </summary>
internal static string ServiceCultivationProjectCurrentUserdataCourrpted2 {
get {
return ResourceManager.GetString("ServiceCultivationProjectCurrentUserdataCourrpted2", resourceCulture);
}
}
/// <summary>
/// 查找类似 开始游戏 的本地化字符串。
/// </summary>
@@ -1437,6 +1473,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 养成计划添加失败 的本地化字符串。
/// </summary>
internal static string ViewModelCultivationAddWarning {
get {
return ResourceManager.GetString("ViewModelCultivationAddWarning", resourceCulture);
}
}
/// <summary>
/// 查找类似 已成功添加至当前养成计划 的本地化字符串。
/// </summary>

View File

@@ -162,6 +162,9 @@
<data name="CoreIOPickerExtensionPickerExceptionInfoBarTitle" xml:space="preserve">
<value>无法打开文件选择器</value>
</data>
<data name="CoreIOTempFileCreateFail" xml:space="preserve">
<value>权限不足,创建临时文件失败</value>
</data>
<data name="CoreJumpListHelperLaunchGameItemDisplayName" xml:space="preserve">
<value>启动游戏</value>
</data>
@@ -210,12 +213,21 @@
<data name="ModelBingGachaWishBaseTotalCountFormat" xml:space="preserve">
<value>{0} 抽</value>
</data>
<data name="ModelEntitySpiralAbyssScheduleFormat" xml:space="preserve">
<value>第 {0} 期</value>
</data>
<data name="MustSelectUserAndUid" xml:space="preserve">
<value>必须先选择一个用户与角色</value>
</data>
<data name="ServiceAchievementUserdataCorruptedInnerIdNotUnique" xml:space="preserve">
<value>单个成就存档内发现多个相同的成就 Id</value>
</data>
<data name="ServiceCultivationProjectCurrentUserdataCourrpted" xml:space="preserve">
<value>保存养成计划状态失败</value>
</data>
<data name="ServiceCultivationProjectCurrentUserdataCourrpted2" xml:space="preserve">
<value>存在多个选中的养成计划</value>
</data>
<data name="ServiceDailyNoteNotifierActionLaunchGameButton" xml:space="preserve">
<value>开始游戏</value>
</data>
@@ -576,6 +588,9 @@
<data name="ViewModelAvatarPropertyShowcaseNotOpen" xml:space="preserve">
<value>角色展柜尚未开启,请前往游戏操作后重试</value>
</data>
<data name="ViewModelCultivationAddWarning" xml:space="preserve">
<value>养成计划添加失败</value>
</data>
<data name="ViewModelCultivationEntryAddSuccess" xml:space="preserve">
<value>已成功添加至当前养成计划</value>
</data>

View File

@@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.Diagnostics;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Extension;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.InterChange.Achievement;

View File

@@ -5,6 +5,7 @@ using CommunityToolkit.Mvvm.Messaging;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Extension;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
@@ -55,10 +56,21 @@ internal class CultivationService : ICultivationService
using (IServiceScope scope = scopeFactory.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
projects = new(appDbContext.CultivateProjects.AsNoTracking().ToList());
projects = appDbContext.CultivateProjects.AsNoTracking().ToObservableCollection();
}
Current ??= projects.SingleOrDefault(proj => proj.IsSelected);
try
{
Current ??= projects.SingleOrDefault(proj => proj.IsSelected);
}
catch (DbUpdateConcurrencyException ex)
{
ThrowHelper.UserdataCorrupted(SH.ServiceCultivationProjectCurrentUserdataCourrpted, ex);
}
catch (InvalidOperationException ex)
{
ThrowHelper.UserdataCorrupted(SH.ServiceCultivationProjectCurrentUserdataCourrpted2, ex);
}
}
return projects;
@@ -277,7 +289,15 @@ internal class CultivationService : ICultivationService
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
Current ??= appDbContext.CultivateProjects.AsNoTracking().SingleOrDefault(proj => proj.IsSelected);
try
{
Current ??= appDbContext.CultivateProjects.AsNoTracking().SingleOrDefault(proj => proj.IsSelected);
}
catch (InvalidOperationException ex)
{
ThrowHelper.UserdataCorrupted(SH.ServiceCultivationProjectCurrentUserdataCourrpted2, ex);
}
if (Current == null)
{
return false;

View File

@@ -4,7 +4,6 @@
using Snap.Hutao.Model.Binding.Cultivation;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
using System.Collections.ObjectModel;

View File

@@ -6,7 +6,6 @@ using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;

View File

@@ -16,7 +16,6 @@ using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.InterChange.GachaLog;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.GachaLog.Factory;
using Snap.Hutao.Service.GachaLog.QueryProvider;
using Snap.Hutao.Service.Metadata;
@@ -122,7 +121,7 @@ internal class GachaLogService : IGachaLogService
await ThreadHelper.SwitchToMainThreadAsync();
try
{
archiveCollection ??= appDbContext.GachaArchives.AsNoTracking().ToObservableCollection();
archiveCollection ??= appDbContext.GachaArchives.AsNoTracking().ToObservableCollection();
}
catch (SqliteException ex)
{
@@ -329,6 +328,8 @@ internal class GachaLogService : IGachaLogService
GachaArchive created = GachaArchive.Create(uid);
appDbContext.GachaArchives.AddAndSave(created);
// System.InvalidOperationException: Sequence contains no elements
// ? how this happen here?
archive = appDbContext.GachaArchives.Single(a => a.Uid == uid);
GachaArchive temp = archive;
ThreadHelper.InvokeOnMainThread(() => archiveCollection!.Add(temp));

View File

@@ -48,7 +48,7 @@ internal class GachaLogQueryWebCacheProvider : IGachaLogQueryProvider
{
(bool isOk, string path) = await gameService.GetGamePathAsync().ConfigureAwait(false);
if (isOk)
if (isOk && (!string.IsNullOrEmpty(path)))
{
string cacheFile = GetCacheFile(path);

View File

@@ -59,14 +59,13 @@ internal class GameService : IGameService
{
if (memoryCache.TryGetValue(GamePathKey, out object? value))
{
return new(true, Must.NotNull((value as string)!));
return new(true, (value as string)!);
}
else
{
using (IServiceScope scope = scopeFactory.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
SettingEntry entry = await appDbContext.Settings.SingleOrAddAsync(SettingEntry.GamePath, string.Empty).ConfigureAwait(false);
// Cannot find in setting
@@ -89,7 +88,7 @@ internal class GameService : IGameService
{
// Save result.
entry.Value = result.Value;
appDbContext.Settings.UpdateAndSave(entry);
await appDbContext.Settings.UpdateAndSaveAsync(entry).ConfigureAwait(false);
}
else
{

View File

@@ -1,15 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Core.IO;
using Snap.Hutao.Model.Binding.LaunchGame;
using Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using static Snap.Hutao.Service.Game.GameConstants;
namespace Snap.Hutao.Service.Game.Package;
/// <summary>

View File

@@ -4,6 +4,7 @@
using Microsoft.Extensions.Caching.Memory;
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Core.Diagnostics;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.IO;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Extension;
@@ -11,7 +12,6 @@ using Snap.Hutao.Service.Abstraction;
using System.IO;
using System.Net.Http;
using System.Net.Http.Json;
using System.Security.Cryptography;
namespace Snap.Hutao.Service.Metadata;
@@ -180,10 +180,18 @@ internal partial class MetadataService : IMetadataService, IMetadataServiceIniti
return Must.NotNull((T)value!);
}
using (Stream fileStream = File.OpenRead(Path.Combine(metadataFolderPath, $"{fileName}.json")))
string path = Path.Combine(metadataFolderPath, $"{fileName}.json");
if (File.Exists(path))
{
T? result = await JsonSerializer.DeserializeAsync<T>(fileStream, options, token).ConfigureAwait(false);
return memoryCache.Set(cacheKey, Must.NotNull(result!));
using (Stream fileStream = File.OpenRead(path))
{
T? result = await JsonSerializer.DeserializeAsync<T>(fileStream, options, token).ConfigureAwait(false);
return memoryCache.Set(cacheKey, Must.NotNull(result!));
}
}
else
{
throw ThrowHelper.UserdataCorrupted(SH.ServiceMetadataNotInitialized, null!);
}
}

View File

@@ -3,7 +3,6 @@
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.View.Helper;

View File

@@ -63,61 +63,38 @@ internal class SpiralAbyssRecordService : ISpiralAbyssRecordService
/// <inheritdoc/>
public async Task RefreshSpiralAbyssAsync(UserAndUid userAndUid)
{
Response<Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss> lastResponse = await gameRecordClient
.GetSpiralAbyssAsync(userAndUid, SpiralAbyssSchedule.Last)
await RefreshSpiralAbyssCoreAsync(userAndUid, SpiralAbyssSchedule.Last).ConfigureAwait(false);
await RefreshSpiralAbyssCoreAsync(userAndUid, SpiralAbyssSchedule.Current).ConfigureAwait(false);
}
private async Task RefreshSpiralAbyssCoreAsync(UserAndUid userAndUid, SpiralAbyssSchedule schedule)
{
Response<Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss> response = await gameRecordClient
.GetSpiralAbyssAsync(userAndUid, schedule)
.ConfigureAwait(false);
if (lastResponse.IsOk())
if (response.IsOk())
{
Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss last = lastResponse.Data;
Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss webSpiralAbyss = response.Data;
SpiralAbyssEntry? lastEntry = spiralAbysses!.SingleOrDefault(s => s.ScheduleId == last.ScheduleId);
if (lastEntry != null)
SpiralAbyssEntry? existEntry = spiralAbysses!.SingleOrDefault(s => s.ScheduleId == webSpiralAbyss.ScheduleId);
if (existEntry != null)
{
await ThreadHelper.SwitchToMainThreadAsync();
lastEntry.UpdateSpiralAbyss(last);
existEntry.UpdateSpiralAbyss(webSpiralAbyss);
await ThreadHelper.SwitchToBackgroundAsync();
await appDbContext.SpiralAbysses.UpdateAndSaveAsync(lastEntry).ConfigureAwait(false);
await appDbContext.SpiralAbysses.UpdateAndSaveAsync(existEntry).ConfigureAwait(false);
}
else
{
SpiralAbyssEntry entry = SpiralAbyssEntry.Create(userAndUid.Uid.Value, last);
SpiralAbyssEntry newEntry = SpiralAbyssEntry.Create(userAndUid.Uid.Value, webSpiralAbyss);
await ThreadHelper.SwitchToMainThreadAsync();
spiralAbysses!.Insert(0, entry);
spiralAbysses!.Insert(0, newEntry);
await ThreadHelper.SwitchToBackgroundAsync();
await appDbContext.SpiralAbysses.AddAndSaveAsync(entry).ConfigureAwait(false);
}
}
Response<Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss> currentResponse = await gameRecordClient
.GetSpiralAbyssAsync(userAndUid, SpiralAbyssSchedule.Current)
.ConfigureAwait(false);
if (currentResponse.IsOk())
{
Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss current = currentResponse.Data;
SpiralAbyssEntry? currentEntry = spiralAbysses!.SingleOrDefault(s => s.ScheduleId == current.ScheduleId);
if (currentEntry != null)
{
await ThreadHelper.SwitchToMainThreadAsync();
currentEntry.UpdateSpiralAbyss(current);
await ThreadHelper.SwitchToBackgroundAsync();
await appDbContext.SpiralAbysses.UpdateAndSaveAsync(currentEntry).ConfigureAwait(false);
}
else
{
SpiralAbyssEntry entry = SpiralAbyssEntry.Create(userAndUid.Uid.Value, current);
await ThreadHelper.SwitchToMainThreadAsync();
spiralAbysses!.Insert(0, entry);
await ThreadHelper.SwitchToBackgroundAsync();
await appDbContext.SpiralAbysses.AddAndSaveAsync(entry).ConfigureAwait(false);
await appDbContext.SpiralAbysses.AddAndSaveAsync(newEntry).ConfigureAwait(false);
}
}
}

View File

@@ -99,7 +99,7 @@ internal class UserService : IUserService
// Sync cache
await ThreadHelper.SwitchToMainThreadAsync();
userCollection!.Remove(user);
roleCollection?.RemoveWhere(r => r.User.InnerId == user.Entity.InnerId);
roleCollection?.RemoveWhere(r => r.User.Mid == user.Entity.Mid);
// Sync database
await ThreadHelper.SwitchToBackgroundAsync();

View File

@@ -2,11 +2,9 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.Control;
using Snap.Hutao.Control.Theme;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
using Windows.System;
@@ -58,8 +56,8 @@ public sealed partial class AnnouncementContentViewer : Microsoft.UI.Xaml.Contro
/// </summary>
public Announcement Announcement
{
get { return (Announcement)GetValue(AnnouncementProperty); }
set { SetValue(AnnouncementProperty, value); }
get => (Announcement)GetValue(AnnouncementProperty);
set => SetValue(AnnouncementProperty, value);
}
private static string? GenerateHtml(Announcement? announcement, ElementTheme theme)

View File

@@ -27,7 +27,7 @@ public sealed partial class StatisticsCard : UserControl
/// </summary>
public bool ShowUpPull
{
get { return (bool)GetValue(ShowUpPullProperty); }
set { SetValue(ShowUpPullProperty, value); }
get => (bool)GetValue(ShowUpPullProperty);
set => SetValue(ShowUpPullProperty, value);
}
}

View File

@@ -8,7 +8,6 @@ using Snap.Hutao.Extension;
using Snap.Hutao.Model.Binding.Gacha.Abstraction;
using Snap.Hutao.Service.GachaLog;
using Snap.Hutao.View.Control;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
namespace Snap.Hutao.View.Dialog;

View File

@@ -30,7 +30,7 @@ public sealed partial class LaunchGamePackageConvertDialog : ContentDialog
/// </summary>
public PackageReplaceStatus State
{
get { return (PackageReplaceStatus)GetValue(StateProperty); }
set { SetValue(StateProperty, value); }
get => (PackageReplaceStatus)GetValue(StateProperty);
set => SetValue(StateProperty, value);
}
}

View File

@@ -29,16 +29,23 @@ public sealed partial class LoginMihoyoUserPage : Microsoft.UI.Xaml.Controls.Pag
[SuppressMessage("", "VSTHRD100")]
private async void OnRootLoaded(object sender, RoutedEventArgs e)
{
await WebView.EnsureCoreWebView2Async();
CoreWebView2CookieManager manager = WebView.CoreWebView2.CookieManager;
IReadOnlyList<CoreWebView2Cookie> cookies = await manager.GetCookiesAsync("https://user.mihoyo.com");
foreach (CoreWebView2Cookie item in cookies)
try
{
manager.DeleteCookie(item);
}
await WebView.EnsureCoreWebView2Async();
WebView.CoreWebView2.Navigate("https://user.mihoyo.com/#/login/password");
CoreWebView2CookieManager manager = WebView.CoreWebView2.CookieManager;
IReadOnlyList<CoreWebView2Cookie> cookies = await manager.GetCookiesAsync("https://user.mihoyo.com");
foreach (CoreWebView2Cookie item in cookies)
{
manager.DeleteCookie(item);
}
WebView.CoreWebView2.Navigate("https://user.mihoyo.com/#/login/password");
}
catch (Exception ex)
{
Ioc.Default.GetRequiredService<IInfoBarService>().Error(ex);
}
}
private async Task HandleCurrentCookieAsync(CancellationToken token)

View File

@@ -1,9 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Navigation;
using Snap.Hutao.Control;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.ViewModel;
namespace Snap.Hutao.View.Page;

View File

@@ -21,7 +21,7 @@
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/>
</mxi:Interaction.Behaviors>
<Grid>
<Grid Visibility="{Binding IsInitialized, Converter={StaticResource BoolToVisibilityConverter}}">
<SplitView
Grid.Row="1"
DisplayMode="Inline"

View File

@@ -1,9 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Navigation;
using Snap.Hutao.Control;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.ViewModel;
namespace Snap.Hutao.View.Page;

View File

@@ -43,7 +43,7 @@ public abstract class ViewModel : ObservableObject, IViewModel
/// 当页面被释放后抛出异常
/// </summary>
/// <exception cref="OperationCanceledException">操作被用户取消</exception>
protected void ThrowIfViewDisposed()
private void ThrowIfViewDisposed()
{
if (IsViewDisposed)
{

View File

@@ -5,10 +5,8 @@ using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI.UI;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control.Extension;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.IO;
using Snap.Hutao.Core.IO.DataTransfer;
using Snap.Hutao.Core.LifeCycle;
using Snap.Hutao.Extension;
using Snap.Hutao.Factory.Abstraction;

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Imaging;
@@ -18,7 +19,6 @@ using Snap.Hutao.Service.AvatarInfo;
using Snap.Hutao.Service.Cultivation;
using Snap.Hutao.Service.User;
using Snap.Hutao.View.Dialog;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
using Snap.Hutao.Web.Response;
using System.Runtime.InteropServices;
using Windows.Graphics.Imaging;
@@ -39,30 +39,21 @@ namespace Snap.Hutao.ViewModel;
[Injection(InjectAs.Scoped)]
internal class AvatarPropertyViewModel : Abstraction.ViewModel
{
private readonly IServiceProvider serviceProvider;
private readonly IUserService userService;
private readonly IAvatarInfoService avatarInfoService;
private readonly IInfoBarService infoBarService;
private readonly IContentDialogFactory contentDialogFactory;
private Summary? summary;
private Avatar? selectedAvatar;
/// <summary>
/// 构造一个新的角色属性视图模型
/// </summary>
/// <param name="userService">用户服务</param>
/// <param name="avatarInfoService">角色信息服务</param>
/// <param name="contentDialogFactory">对话框工厂</param>
/// <param name="infoBarService">信息条服务</param>
public AvatarPropertyViewModel(
IUserService userService,
IAvatarInfoService avatarInfoService,
IContentDialogFactory contentDialogFactory,
IInfoBarService infoBarService)
/// <param name="serviceProvider">服务提供器</param>
public AvatarPropertyViewModel(IServiceProvider serviceProvider)
{
this.userService = userService;
this.avatarInfoService = avatarInfoService;
this.infoBarService = infoBarService;
this.contentDialogFactory = contentDialogFactory;
userService = serviceProvider.GetRequiredService<IUserService>();
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
this.serviceProvider = serviceProvider;
OpenUICommand = new AsyncRelayCommand(OpenUIAsync);
RefreshFromEnkaApiCommand = new AsyncRelayCommand(RefreshByEnkaApiAsync);
@@ -159,14 +150,18 @@ internal class AvatarPropertyViewModel : Abstraction.ViewModel
ValueResult<RefreshResult, Summary?> summaryResult;
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
ContentDialog dialog = await contentDialogFactory
ContentDialog dialog = await serviceProvider
.GetRequiredService<IContentDialogFactory>()
.CreateForIndeterminateProgressAsync(SH.ViewModelAvatarPropertyFetch)
.ConfigureAwait(false);
await ThreadHelper.SwitchToMainThreadAsync();
await using (await dialog.BlockAsync().ConfigureAwait(false))
{
summaryResult = await avatarInfoService.GetSummaryAsync(userAndUid, option, token).ConfigureAwait(false);
summaryResult = await serviceProvider
.GetRequiredService<IAvatarInfoService>()
.GetSummaryAsync(userAndUid, option, token)
.ConfigureAwait(false);
}
}
@@ -199,9 +194,6 @@ internal class AvatarPropertyViewModel : Abstraction.ViewModel
{
if (avatar != null)
{
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
IUserService userService = Ioc.Default.GetRequiredService<IUserService>();
if (userService.Current != null)
{
if (avatar.Weapon == null)
@@ -218,14 +210,14 @@ internal class AvatarPropertyViewModel : Abstraction.ViewModel
if (isOk)
{
Response<CalcConsumption> consumptionResponse = await Ioc.Default
Response<CalcConsumption> consumptionResponse = await serviceProvider
.GetRequiredService<CalcClient>()
.ComputeAsync(userService.Current.Entity, delta)
.ConfigureAwait(false);
if (consumptionResponse.IsOk())
{
ICultivationService cultivationService = Ioc.Default.GetRequiredService<ICultivationService>();
ICultivationService cultivationService = serviceProvider.GetRequiredService<ICultivationService>();
CalcConsumption consumption = consumptionResponse.Data;
List<CalcItem> items = CalcItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume);
@@ -233,18 +225,25 @@ internal class AvatarPropertyViewModel : Abstraction.ViewModel
.SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items)
.ConfigureAwait(false);
// take a hot path if avatar is not saved.
bool avatarAndWeaponSaved = avatarSaved && await cultivationService
.SaveConsumptionAsync(CultivateType.Weapon, avatar.Weapon.Id, consumption.WeaponConsume.EmptyIfNull())
.ConfigureAwait(false);
try
{
// take a hot path if avatar is not saved.
bool avatarAndWeaponSaved = avatarSaved && await cultivationService
.SaveConsumptionAsync(CultivateType.Weapon, avatar.Weapon.Id, consumption.WeaponConsume.EmptyIfNull())
.ConfigureAwait(false);
if (avatarAndWeaponSaved)
{
infoBarService.Success(SH.ViewModelCultivationEntryAddSuccess);
if (avatarAndWeaponSaved)
{
infoBarService.Success(SH.ViewModelCultivationEntryAddSuccess);
}
else
{
infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning);
}
}
else
catch (Core.ExceptionService.UserdataCorruptedException ex)
{
infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning);
infoBarService.Error(ex, SH.ViewModelCultivationAddWarning);
}
}
}
@@ -267,7 +266,7 @@ internal class AvatarPropertyViewModel : Abstraction.ViewModel
bool clipboardOpened = false;
using (SoftwareBitmap softwareBitmap = SoftwareBitmap.CreateCopyFromBuffer(buffer, BitmapPixelFormat.Bgra8, bitmap.PixelWidth, bitmap.PixelHeight))
{
Color tintColor = (Color)Ioc.Default.GetRequiredService<App>().Resources["CompatBackgroundColor"];
Color tintColor = (Color)serviceProvider.GetRequiredService<App>().Resources["CompatBackgroundColor"];
Bgra8 tint = Bgra8.FromColor(tintColor);
softwareBitmap.NormalBlend(tint);
using (InMemoryRandomAccessStream memory = new())

View File

@@ -51,18 +51,11 @@ internal class DailyNoteViewModel : Abstraction.ViewModel
/// 构造一个新的实时便笺视图模型
/// </summary>
/// <param name="serviceProvider">服务提供器</param>
/// <param name="userService">用户服务</param>
/// <param name="dailyNoteService">实时便笺服务</param>
/// <param name="appDbContext">数据库上下文</param>
public DailyNoteViewModel(
IServiceProvider serviceProvider,
IUserService userService,
IDailyNoteService dailyNoteService,
AppDbContext appDbContext)
public DailyNoteViewModel(IServiceProvider serviceProvider)
{
this.userService = userService;
this.dailyNoteService = dailyNoteService;
this.appDbContext = appDbContext;
userService = serviceProvider.GetRequiredService<IUserService>();
dailyNoteService = serviceProvider.GetRequiredService<IDailyNoteService>();
appDbContext = serviceProvider.GetRequiredService<AppDbContext>();
this.serviceProvider = serviceProvider;
OpenUICommand = new AsyncRelayCommand(OpenUIAsync);
@@ -92,7 +85,7 @@ internal class DailyNoteViewModel : Abstraction.ViewModel
{
if (!ScheduleTaskHelper.RegisterForDailyNoteRefresh(value.Value))
{
Ioc.Default.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelDailyNoteRegisterTaskFail);
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelDailyNoteRegisterTaskFail);
}
else
{
@@ -244,10 +237,13 @@ internal class DailyNoteViewModel : Abstraction.ViewModel
{
if (entry != null)
{
// ContentDialog must be created by main thread.
await ThreadHelper.SwitchToMainThreadAsync();
await new DailyNoteNotificationDialog(entry).ShowAsync();
appDbContext.DailyNotes.UpdateAndSave(entry);
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
// ContentDialog must be created by main thread.
await ThreadHelper.SwitchToMainThreadAsync();
await new DailyNoteNotificationDialog(entry).ShowAsync();
appDbContext.DailyNotes.UpdateAndSave(entry);
}
}
}

View File

@@ -18,11 +18,16 @@ namespace Snap.Hutao.ViewModel;
[Injection(InjectAs.Scoped)]
internal class ExperimentalFeaturesViewModel : ObservableObject
{
private readonly IServiceProvider serviceProvider;
/// <summary>
/// 构造一个新的实验性功能视图模型
/// </summary>
public ExperimentalFeaturesViewModel()
/// <param name="serviceProvider">服务提供器</param>
public ExperimentalFeaturesViewModel(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
OpenCacheFolderCommand = new AsyncRelayCommand(OpenCacheFolderAsync);
OpenDataFolderCommand = new AsyncRelayCommand(OpenDataFolderAsync);
DeleteUsersCommand = new AsyncRelayCommand(DangerousDeleteUsersAsync);
@@ -61,7 +66,7 @@ internal class ExperimentalFeaturesViewModel : ObservableObject
private async Task DangerousDeleteUsersAsync()
{
using (IServiceScope scope = Ioc.Default.CreateScope())
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await appDbContext.Users.ExecuteDeleteAsync().ConfigureAwait(false);
@@ -73,7 +78,7 @@ internal class ExperimentalFeaturesViewModel : ObservableObject
private void DangerousDeleteAllScheduleTasks()
{
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
IInfoBarService infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
if (Core.ScheduleTaskHelper.UnregisterAllTasks())
{
infoBarService.Success(SH.ViewModelExperimentalDeleteTaskSuccess);

View File

@@ -150,11 +150,8 @@ internal class GachaLogViewModel : Abstraction.ViewModel
if (await gachaLogService.InitializeAsync(CancellationToken).ConfigureAwait(true))
{
ObservableCollection<GachaArchive> archives;
ThrowIfViewDisposed();
using (await DisposeLock.EnterAsync().ConfigureAwait(false))
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
ThrowIfViewDisposed();
archives = await gachaLogService.GetArchiveCollectionAsync().ConfigureAwait(false);
}
@@ -204,13 +201,13 @@ internal class GachaLogViewModel : Abstraction.ViewModel
try
{
using (await DisposeLock.EnterAsync().ConfigureAwait(false))
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
try
{
authkeyValid = await gachaLogService.RefreshGachaLogAsync(query, strategy, progress, CancellationToken).ConfigureAwait(false);
}
catch (Core.ExceptionService.UserdataCorruptedException ex)
catch (UserdataCorruptedException ex)
{
authkeyValid = false;
infoBarService.Error(ex);
@@ -304,11 +301,14 @@ internal class GachaLogViewModel : Abstraction.ViewModel
if (result == ContentDialogResult.Primary)
{
await gachaLogService.RemoveArchiveAsync(SelectedArchive).ConfigureAwait(false);
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
await gachaLogService.RemoveArchiveAsync(SelectedArchive).ConfigureAwait(false);
// reselect first archive
await ThreadHelper.SwitchToMainThreadAsync();
SelectedArchive = Archives.FirstOrDefault();
// reselect first archive
await ThreadHelper.SwitchToMainThreadAsync();
SelectedArchive = Archives.FirstOrDefault();
}
}
}
}
@@ -354,7 +354,7 @@ internal class GachaLogViewModel : Abstraction.ViewModel
}
catch (UserdataCorruptedException ex)
{
Ioc.Default.GetRequiredService<IInfoBarService>().Error(ex);
infoBarService.Error(ex);
}
}

View File

@@ -2,15 +2,9 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media.Imaging;
using Snap.Hutao.Model.Binding.Hutao;
using Snap.Hutao.Service.Hutao;
using Snap.Hutao.Web.Hutao.Model;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Graphics.Imaging;
using Windows.Storage.Streams;
namespace Snap.Hutao.ViewModel;

View File

@@ -4,6 +4,7 @@
using CommunityToolkit.Mvvm.Input;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Control.Extension;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.LifeCycle;
@@ -12,7 +13,6 @@ using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.Game;
using Snap.Hutao.Service.Game.Unlocker;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.User;
using Snap.Hutao.View.Dialog;
@@ -36,6 +36,7 @@ internal class LaunchGameViewModel : Abstraction.ViewModel
private static readonly string TrueString = true.ToString();
private static readonly string FalseString = false.ToString();
private readonly IServiceProvider serviceProvider;
private readonly IGameService gameService;
private readonly AppDbContext appDbContext;
private readonly IMemoryCache memoryCache;
@@ -56,17 +57,13 @@ internal class LaunchGameViewModel : Abstraction.ViewModel
/// <summary>
/// 构造一个新的启动游戏视图模型
/// </summary>
/// <param name="gameService">游戏服务</param>
/// <param name="memoryCache">内存缓存</param>
/// <param name="appDbContext">数据库上下文</param>
public LaunchGameViewModel(
IGameService gameService,
IMemoryCache memoryCache,
AppDbContext appDbContext)
/// <param name="serviceProvider">服务提供器</param>
public LaunchGameViewModel(IServiceProvider serviceProvider)
{
this.gameService = gameService;
this.appDbContext = appDbContext;
this.memoryCache = memoryCache;
gameService = serviceProvider.GetRequiredService<IGameService>();
appDbContext = serviceProvider.GetRequiredService<AppDbContext>();
memoryCache = serviceProvider.GetRequiredService<IMemoryCache>();
this.serviceProvider = serviceProvider;
OpenUICommand = new AsyncRelayCommand(OpenUIAsync);
LaunchCommand = new AsyncRelayCommand(LaunchAsync);
@@ -210,11 +207,8 @@ internal class LaunchGameViewModel : Abstraction.ViewModel
{
try
{
ThrowIfViewDisposed();
using (await DisposeLock.EnterAsync(CancellationToken).ConfigureAwait(true))
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
ThrowIfViewDisposed();
MultiChannel multi = gameService.GetMultiChannel();
if (string.IsNullOrEmpty(multi.ConfigFilePath))
{
@@ -222,7 +216,7 @@ internal class LaunchGameViewModel : Abstraction.ViewModel
}
else
{
Ioc.Default.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelLaunchGameMultiChannelReadFail);
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelLaunchGameMultiChannelReadFail);
}
GameAccounts = await gameService.GetGameAccountCollectionAsync().ConfigureAwait(true);
@@ -231,6 +225,7 @@ internal class LaunchGameViewModel : Abstraction.ViewModel
if (memoryCache.TryGetValue(DesiredUid, out object? value) && value is string uid)
{
SelectedGameAccount = GameAccounts.FirstOrDefault(g => g.AttachUid == uid);
memoryCache.Remove(DesiredUid);
}
// Sync from Settings
@@ -243,8 +238,8 @@ internal class LaunchGameViewModel : Abstraction.ViewModel
}
else
{
Ioc.Default.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelLaunchGamePathInvalid);
await Ioc.Default.GetRequiredService<INavigationService>()
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelLaunchGamePathInvalid);
await serviceProvider.GetRequiredService<INavigationService>()
.NavigateAsync<View.Page.SettingPage>(INavigationAwaiter.Default, true)
.ConfigureAwait(false);
}
@@ -288,7 +283,7 @@ internal class LaunchGameViewModel : Abstraction.ViewModel
private async Task LaunchAsync()
{
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
IInfoBarService infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
if (gameService.IsGameRunning())
{
@@ -344,7 +339,7 @@ internal class LaunchGameViewModel : Abstraction.ViewModel
}
catch (Core.ExceptionService.UserdataCorruptedException ex)
{
Ioc.Default.GetRequiredService<IInfoBarService>().Error(ex);
serviceProvider.GetRequiredService<IInfoBarService>().Error(ex);
}
}
@@ -352,8 +347,8 @@ internal class LaunchGameViewModel : Abstraction.ViewModel
{
if (gameAccount != null)
{
IUserService userService = Ioc.Default.GetRequiredService<IUserService>();
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
IUserService userService = serviceProvider.GetRequiredService<IUserService>();
IInfoBarService infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
if (userService.Current?.SelectedUserGameRole is UserGameRole role)
{

View File

@@ -3,6 +3,7 @@
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.IO;
using Snap.Hutao.Core.Setting;
@@ -27,6 +28,7 @@ namespace Snap.Hutao.ViewModel;
[Injection(InjectAs.Scoped)]
internal class SettingViewModel : Abstraction.ViewModel
{
private readonly IServiceProvider serviceProvider;
private readonly AppDbContext appDbContext;
private readonly IGameService gameService;
private readonly ILogger<SettingViewModel> logger;
@@ -46,21 +48,14 @@ internal class SettingViewModel : Abstraction.ViewModel
/// <summary>
/// 构造一个新的设置视图模型
/// </summary>
/// <param name="appDbContext">数据库上下文</param>
/// <param name="gameService">游戏服务</param>
/// <param name="experimental">实验性功能</param>
/// <param name="logger">日志器</param>
public SettingViewModel(
AppDbContext appDbContext,
IGameService gameService,
ExperimentalFeaturesViewModel experimental,
ILogger<SettingViewModel> logger)
/// <param name="serviceProvider">服务提供器</param>
public SettingViewModel(IServiceProvider serviceProvider)
{
this.appDbContext = appDbContext;
this.gameService = gameService;
this.logger = logger;
Experimental = experimental;
appDbContext = serviceProvider.GetRequiredService<AppDbContext>();
gameService = serviceProvider.GetRequiredService<IGameService>();
logger = serviceProvider.GetRequiredService<ILogger<SettingViewModel>>();
Experimental = serviceProvider.GetRequiredService<ExperimentalFeaturesViewModel>();
this.serviceProvider = serviceProvider;
isEmptyHistoryWishVisibleEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.IsEmptyHistoryWishVisible, SettingEntryHelper.TrueString);
IsEmptyHistoryWishVisible = bool.Parse(isEmptyHistoryWishVisibleEntry.Value!);
@@ -152,7 +147,7 @@ internal class SettingViewModel : Abstraction.ViewModel
{
selectedBackdropTypeEntry.Value = value.Value.ToString();
appDbContext.Settings.UpdateAndSave(selectedBackdropTypeEntry);
Ioc.Default.GetRequiredService<IMessenger>().Send(new Message.BackdropTypeChangedMessage(value.Value));
serviceProvider.GetRequiredService<IMessenger>().Send(new Message.BackdropTypeChangedMessage(value.Value));
}
}
}
@@ -189,7 +184,7 @@ internal class SettingViewModel : Abstraction.ViewModel
private async Task SetGamePathAsync()
{
IGameLocator locator = Ioc.Default.GetRequiredService<IEnumerable<IGameLocator>>()
IGameLocator locator = serviceProvider.GetRequiredService<IEnumerable<IGameLocator>>()
.Single(l => l.Name == nameof(ManualGameLocator));
(bool isOk, string path) = await locator.LocateGamePathAsync().ConfigureAwait(false);
@@ -203,7 +198,7 @@ internal class SettingViewModel : Abstraction.ViewModel
private void DeleteGameWebCache()
{
IGameService gameService = Ioc.Default.GetRequiredService<IGameService>();
IGameService gameService = serviceProvider.GetRequiredService<IGameService>();
string gamePath = gameService.GetGamePathSkipLocator();
if (!string.IsNullOrEmpty(gamePath))
@@ -211,7 +206,7 @@ internal class SettingViewModel : Abstraction.ViewModel
string cacheFilePath = GachaLogQueryWebCacheProvider.GetCacheFile(gamePath);
string cacheFolder = Path.GetDirectoryName(cacheFilePath)!;
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
IInfoBarService infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
if (Directory.Exists(cacheFolder))
{
try
@@ -243,7 +238,7 @@ internal class SettingViewModel : Abstraction.ViewModel
private async Task CheckUpdateAsync()
{
#if DEBUG
await Ioc.Default
await serviceProvider
.GetRequiredService<Service.Navigation.INavigationService>()
.NavigateAsync<View.Page.TestPage>(Service.Navigation.INavigationAwaiter.Default)
.ConfigureAwait(false);
@@ -254,11 +249,11 @@ internal class SettingViewModel : Abstraction.ViewModel
private async Task SetDataFolderAsync()
{
IPickerFactory pickerFactory = Ioc.Default.GetRequiredService<IPickerFactory>();
IPickerFactory pickerFactory = serviceProvider.GetRequiredService<IPickerFactory>();
FolderPicker picker = pickerFactory.GetFolderPicker();
(bool isOk, string folder) = await picker.TryPickSingleFolderAsync().ConfigureAwait(false);
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
IInfoBarService infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
if (isOk)
{
LocalSetting.Set(SettingKeys.DataFolderPath, folder);

View File

@@ -3,6 +3,7 @@
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Message;
using Snap.Hutao.Model.Binding.SpiralAbyss;
using Snap.Hutao.Model.Binding.User;
@@ -25,6 +26,7 @@ namespace Snap.Hutao.ViewModel;
[Injection(InjectAs.Scoped)]
internal class SpiralAbyssRecordViewModel : Abstraction.ViewModel, IRecipient<UserChangedMessage>
{
private readonly IServiceProvider serviceProvider;
private readonly ISpiralAbyssRecordService spiralAbyssRecordService;
private readonly IMetadataService metadataService;
private readonly IUserService userService;
@@ -37,25 +39,23 @@ internal class SpiralAbyssRecordViewModel : Abstraction.ViewModel, IRecipient<Us
/// <summary>
/// 构造一个新的深渊记录视图模型
/// </summary>
/// <param name="serviceProvider">服务提供器</param>
/// <param name="spiralAbyssRecordService">深渊记录服务</param>
/// <param name="metadataService">元数据服务</param>
/// <param name="userService">用户服务</param>
/// <param name="messenger">消息器</param>
public SpiralAbyssRecordViewModel(
ISpiralAbyssRecordService spiralAbyssRecordService,
IMetadataService metadataService,
IUserService userService,
IMessenger messenger)
public SpiralAbyssRecordViewModel(IServiceProvider serviceProvider)
{
this.spiralAbyssRecordService = spiralAbyssRecordService;
this.metadataService = metadataService;
this.userService = userService;
spiralAbyssRecordService = serviceProvider.GetRequiredService<ISpiralAbyssRecordService>();
metadataService = serviceProvider.GetRequiredService<IMetadataService>();
userService = serviceProvider.GetRequiredService<IUserService>();
this.serviceProvider = serviceProvider;
OpenUICommand = new AsyncRelayCommand(OpenUIAsync);
RefreshCommand = new AsyncRelayCommand(RefreshAsync);
UploadSpiralAbyssRecordCommand = new AsyncRelayCommand(UploadSpiralAbyssRecordAsync);
messenger.Register(this);
serviceProvider.GetRequiredService<IMessenger>().Register(this);
}
/// <summary>
@@ -123,10 +123,12 @@ internal class SpiralAbyssRecordViewModel : Abstraction.ViewModel, IRecipient<Us
if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
{
await UpdateSpiralAbyssCollectionAsync(userAndUid).ConfigureAwait(false);
await ThreadHelper.SwitchToMainThreadAsync();
IsInitialized = true;
}
else
{
Ioc.Default.GetRequiredService<IInfoBarService>().Warning(SH.MustSelectUserAndUid);
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.MustSelectUserAndUid);
}
}
}
@@ -136,10 +138,8 @@ internal class SpiralAbyssRecordViewModel : Abstraction.ViewModel, IRecipient<Us
ObservableCollection<SpiralAbyssEntry>? temp = null;
try
{
ThrowIfViewDisposed();
using (await DisposeLock.EnterAsync().ConfigureAwait(false))
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
ThrowIfViewDisposed();
temp = await spiralAbyssRecordService
.GetSpiralAbyssCollectionAsync(userAndUid)
.ConfigureAwait(false);
@@ -156,16 +156,14 @@ internal class SpiralAbyssRecordViewModel : Abstraction.ViewModel, IRecipient<Us
private async Task RefreshAsync()
{
if (await metadataService.InitializeAsync().ConfigureAwait(false))
if (SpiralAbyssEntries != null)
{
if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
{
try
{
ThrowIfViewDisposed();
using (await DisposeLock.EnterAsync().ConfigureAwait(false))
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
ThrowIfViewDisposed();
await spiralAbyssRecordService
.RefreshSpiralAbyssAsync(userAndUid)
.ConfigureAwait(false);
@@ -176,15 +174,15 @@ internal class SpiralAbyssRecordViewModel : Abstraction.ViewModel, IRecipient<Us
}
await ThreadHelper.SwitchToMainThreadAsync();
SelectedEntry = SpiralAbyssEntries?.FirstOrDefault();
SelectedEntry = SpiralAbyssEntries.FirstOrDefault();
}
}
}
private async Task UploadSpiralAbyssRecordAsync()
{
HomaClient homaClient = Ioc.Default.GetRequiredService<HomaClient>();
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
HomaClient homaClient = serviceProvider.GetRequiredService<HomaClient>();
IInfoBarService infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
{

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Core.IO;
using Snap.Hutao.Core.IO.Bits;
using Snap.Hutao.Service.User;
@@ -18,11 +19,16 @@ namespace Snap.Hutao.ViewModel;
[Injection(InjectAs.Scoped)]
internal class TestViewModel : Abstraction.ViewModel
{
private readonly IServiceProvider serviceProvider;
/// <summary>
/// 构造一个新的测试视图模型
/// </summary>
public TestViewModel()
/// <param name="serviceProvider">服务提供器</param>
public TestViewModel(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
ShowCommunityGameRecordDialogCommand = new AsyncRelayCommand(ShowCommunityGameRecordDialogAsync);
ShowAdoptCalculatorDialogCommand = new AsyncRelayCommand(ShowAdoptCalculatorDialogAsync);
DangerousLoginMihoyoBbsCommand = new AsyncRelayCommand(DangerousLoginMihoyoBbsAsync);
@@ -71,7 +77,7 @@ internal class TestViewModel : Abstraction.ViewModel
if (isOk)
{
(Response<LoginResult>? resp, Aigis? aigis) = await Ioc.Default
(Response<LoginResult>? resp, Aigis? aigis) = await serviceProvider
.GetRequiredService<PassportClient2>()
.LoginByPasswordAsync(data, CancellationToken.None)
.ConfigureAwait(false);
@@ -82,7 +88,7 @@ internal class TestViewModel : Abstraction.ViewModel
{
Cookie cookie = Cookie.FromLoginResult(resp.Data);
await Ioc.Default
await serviceProvider
.GetRequiredService<IUserService>()
.ProcessInputCookieAsync(cookie)
.ConfigureAwait(false);
@@ -97,9 +103,9 @@ internal class TestViewModel : Abstraction.ViewModel
private async Task DownloadStaticFileAsync()
{
BitsManager bitsManager = Ioc.Default.GetRequiredService<BitsManager>();
BitsManager bitsManager = serviceProvider.GetRequiredService<BitsManager>();
Uri testUri = new(Web.HutaoEndpoints.StaticZip("AvatarIcon"));
ILogger<TestViewModel> logger = Ioc.Default.GetRequiredService<ILogger<TestViewModel>>();
ILogger<TestViewModel> logger = serviceProvider.GetRequiredService<ILogger<TestViewModel>>();
Progress<ProgressUpdateStatus> progress = new(status => logger.LogInformation("{info}", status));
(bool isOk, TempFile file) = await bitsManager.DownloadAsync(testUri, progress).ConfigureAwait(false);

View File

@@ -3,6 +3,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.DependencyInjection;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.IO.DataTransfer;
using Snap.Hutao.Extension;
@@ -24,6 +25,7 @@ namespace Snap.Hutao.ViewModel;
[Injection(InjectAs.Singleton)]
internal class UserViewModel : ObservableObject
{
private readonly IServiceProvider serviceProvider;
private readonly IUserService userService;
private readonly IInfoBarService infoBarService;
@@ -33,12 +35,14 @@ internal class UserViewModel : ObservableObject
/// <summary>
/// 构造一个新的用户视图模型
/// </summary>
/// <param name="serviceProvider">服务提供器</param>
/// <param name="userService">用户服务</param>
/// <param name="infoBarService">信息条服务</param>
public UserViewModel(IUserService userService, IInfoBarService infoBarService)
public UserViewModel(IServiceProvider serviceProvider)
{
this.userService = userService;
this.infoBarService = infoBarService;
userService = serviceProvider.GetRequiredService<IUserService>();
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
this.serviceProvider = serviceProvider;
OpenUICommand = new AsyncRelayCommand(OpenUIAsync);
AddUserCommand = new AsyncRelayCommand(AddUserAsync);
@@ -155,7 +159,7 @@ internal class UserViewModel : ObservableObject
{
if (Core.WebView2Helper.IsSupported)
{
Ioc.Default.GetRequiredService<INavigationService>().Navigate<LoginMihoyoUserPage>(INavigationAwaiter.Default);
serviceProvider.GetRequiredService<INavigationService>().Navigate<LoginMihoyoUserPage>(INavigationAwaiter.Default);
}
else
{

View File

@@ -207,15 +207,22 @@ internal class WelcomeViewModel : ObservableObject
private void ExtractFiles(string file)
{
IImageCacheFilePathOperation imageCache = serviceProvider.GetRequiredService<IImageCache>().ImplictAs<IImageCacheFilePathOperation>()!;
using (ZipArchive archive = ZipFile.OpenRead(file))
try
{
foreach (ZipArchiveEntry entry in archive.Entries)
using (ZipArchive archive = ZipFile.OpenRead(file))
{
string destPath = imageCache.GetFilePathFromCategoryAndFileName(fileName, entry.FullName);
entry.ExtractToFile(destPath, true);
foreach (ZipArchiveEntry entry in archive.Entries)
{
string destPath = imageCache.GetFilePathFromCategoryAndFileName(fileName, entry.FullName);
entry.ExtractToFile(destPath, true);
}
}
}
catch (InvalidDataException)
{
// System.IO.InvalidDataException: End of Central Directory record could not be found.
// Basically the file downloaded is corrupted, skip anyway.
}
}
}
}

View File

@@ -3,6 +3,7 @@
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.WinUI.UI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using Snap.Hutao.Extension;
using Snap.Hutao.Model.Binding.Cultivation;
@@ -34,6 +35,7 @@ namespace Snap.Hutao.ViewModel;
[Injection(InjectAs.Scoped)]
internal class WikiAvatarViewModel : Abstraction.ViewModel
{
private readonly IServiceProvider serviceProvider;
private readonly IMetadataService metadataService;
private readonly IHutaoCache hutaoCache;
@@ -44,12 +46,13 @@ internal class WikiAvatarViewModel : Abstraction.ViewModel
/// <summary>
/// 构造一个新的角色资料视图模型
/// </summary>
/// <param name="metadataService">元数据服务</param>
/// <param name="hutaoCache">胡桃缓存</param>
public WikiAvatarViewModel(IMetadataService metadataService, IHutaoCache hutaoCache)
/// <param name="serviceProvider">服务提供器</param>
public WikiAvatarViewModel(IServiceProvider serviceProvider)
{
this.metadataService = metadataService;
this.hutaoCache = hutaoCache;
metadataService = serviceProvider.GetRequiredService<IMetadataService>();
hutaoCache = serviceProvider.GetRequiredService<IHutaoCache>();
this.serviceProvider = serviceProvider;
OpenUICommand = new AsyncRelayCommand(OpenUIAsync);
CultivateCommand = new AsyncRelayCommand<Avatar>(CultivateAsync);
FilterCommand = new RelayCommand<string>(ApplyFilter);
@@ -123,8 +126,8 @@ internal class WikiAvatarViewModel : Abstraction.ViewModel
{
if (avatar != null)
{
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
IUserService userService = Ioc.Default.GetRequiredService<IUserService>();
IInfoBarService infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
IUserService userService = serviceProvider.GetRequiredService<IUserService>();
if (userService.Current != null)
{
@@ -136,7 +139,7 @@ internal class WikiAvatarViewModel : Abstraction.ViewModel
if (isOk)
{
Response<CalcConsumption> consumptionResponse = await Ioc.Default
Response<CalcConsumption> consumptionResponse = await serviceProvider
.GetRequiredService<CalcClient>()
.ComputeAsync(userService.Current.Entity, delta)
.ConfigureAwait(false);
@@ -146,18 +149,25 @@ internal class WikiAvatarViewModel : Abstraction.ViewModel
CalcConsumption consumption = consumptionResponse.Data;
List<CalcItem> items = CalcItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume);
bool saved = await Ioc.Default
.GetRequiredService<ICultivationService>()
.SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items)
.ConfigureAwait(false);
try
{
bool saved = await serviceProvider
.GetRequiredService<ICultivationService>()
.SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items)
.ConfigureAwait(false);
if (saved)
{
infoBarService.Success(SH.ViewModelCultivationEntryAddSuccess);
if (saved)
{
infoBarService.Success(SH.ViewModelCultivationEntryAddSuccess);
}
else
{
infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning);
}
}
else
catch (Core.ExceptionService.UserdataCorruptedException ex)
{
infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning);
infoBarService.Error(ex, SH.ViewModelCultivationAddWarning);
}
}
}

View File

@@ -3,6 +3,7 @@
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.WinUI.UI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using Snap.Hutao.Extension;
using Snap.Hutao.Model.Binding.Cultivation;
@@ -37,6 +38,7 @@ internal class WikiWeaponViewModel : Abstraction.ViewModel
11419, 11420, 11421, // 「一心传」名刀
};
private readonly IServiceProvider serviceProvider;
private readonly IMetadataService metadataService;
private readonly IHutaoCache hutaoCache;
@@ -47,12 +49,12 @@ internal class WikiWeaponViewModel : Abstraction.ViewModel
/// <summary>
/// 构造一个新的武器资料视图模型
/// </summary>
/// <param name="metadataService">元数据服务</param>
/// <param name="hutaoCache">胡桃缓存</param>
public WikiWeaponViewModel(IMetadataService metadataService, IHutaoCache hutaoCache)
/// <param name="serviceProvider">服务提供器</param>
public WikiWeaponViewModel(IServiceProvider serviceProvider)
{
this.metadataService = metadataService;
this.hutaoCache = hutaoCache;
metadataService = serviceProvider.GetRequiredService<IMetadataService>();
hutaoCache = serviceProvider.GetRequiredService<IHutaoCache>();
this.serviceProvider = serviceProvider;
OpenUICommand = new AsyncRelayCommand(OpenUIAsync);
CultivateCommand = new AsyncRelayCommand<Weapon>(CultivateAsync);
@@ -122,8 +124,8 @@ internal class WikiWeaponViewModel : Abstraction.ViewModel
{
if (weapon != null)
{
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
IUserService userService = Ioc.Default.GetRequiredService<IUserService>();
IInfoBarService infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
IUserService userService = serviceProvider.GetRequiredService<IUserService>();
if (userService.Current != null)
{
@@ -135,7 +137,7 @@ internal class WikiWeaponViewModel : Abstraction.ViewModel
if (isOk)
{
Response<CalcConsumption> consumptionResponse = await Ioc.Default
Response<CalcConsumption> consumptionResponse = await serviceProvider
.GetRequiredService<CalcClient>()
.ComputeAsync(userService.Current.Entity, delta)
.ConfigureAwait(false);
@@ -144,18 +146,25 @@ internal class WikiWeaponViewModel : Abstraction.ViewModel
{
CalcConsumption consumption = consumptionResponse.Data;
bool saved = await Ioc.Default
.GetRequiredService<ICultivationService>()
.SaveConsumptionAsync(CultivateType.Weapon, weapon.Id, consumption.WeaponConsume.EmptyIfNull())
.ConfigureAwait(false);
try
{
bool saved = await serviceProvider
.GetRequiredService<ICultivationService>()
.SaveConsumptionAsync(CultivateType.Weapon, weapon.Id, consumption.WeaponConsume.EmptyIfNull())
.ConfigureAwait(false);
if (saved)
{
infoBarService.Success(SH.ViewModelCultivationEntryAddSuccess);
if (saved)
{
infoBarService.Success(SH.ViewModelCultivationEntryAddSuccess);
}
else
{
infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning);
}
}
else
catch (Core.ExceptionService.UserdataCorruptedException ex)
{
infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning);
infoBarService.Error(ex, SH.ViewModelCultivationAddWarning);
}
}
}

View File

@@ -3,7 +3,6 @@
using Snap.Hutao.Web.Request;
using System.Net.Http;
using System.Text;
namespace Snap.Hutao.Web.Hoyolab.DynamicSecret;

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Extension;
using System.IO;
using System.Net.Http;
using System.Net.Http.Json;