Compare commits

...

3 Commits

Author SHA1 Message Date
DismissedLight
331cc14532 Update Persistence.cs 2022-09-27 14:38:06 +08:00
DismissedLight
c0ddb24825 fix window issue 2022-09-27 14:36:13 +08:00
DismissedLight
e925c5909c update readme 2022-09-27 12:35:37 +08:00
17 changed files with 168 additions and 76 deletions

View File

@@ -6,7 +6,9 @@
## 安装 ## 安装
* 前往 [下载页面](https://go.hut.ao/archive) 下载最新版本的 `胡桃` 安装包 * 前往 [下载页面](https://go.hut.ao/archive) 下载最新版本的 `胡桃` 安装包
* 完全解压后,使用 powershell 运行 `install.ps1` 文件 * (曾启用的可以跳过此步骤)在系统设置中打开 **开发者选项** 界面,勾选 `开发人员模式``允许 PowerShell 脚本`
* 完全解压后,右键使用 powershell 运行 `install.ps1` 文件
* 安装完成后可以关闭 `允许 PowerShell 脚本`
## 特别感谢 ## 特别感谢

View File

@@ -15,7 +15,7 @@ namespace Snap.Hutao.Core.Caching;
/// </summary> /// </summary>
[Injection(InjectAs.Singleton, typeof(IImageCache))] [Injection(InjectAs.Singleton, typeof(IImageCache))]
[HttpClient(HttpClientConfigration.Default)] [HttpClient(HttpClientConfigration.Default)]
[PrimaryHttpMessageHandler(MaxConnectionsPerServer = 20)] [PrimaryHttpMessageHandler(MaxConnectionsPerServer = 16)]
public class ImageCache : CacheBase<BitmapImage>, IImageCache public class ImageCache : CacheBase<BitmapImage>, IImageCache
{ {
private const string DateAccessedProperty = "System.DateAccessed"; private const string DateAccessedProperty = "System.DateAccessed";

View File

@@ -4,6 +4,7 @@
using Microsoft.UI; using Microsoft.UI;
using Microsoft.UI.Windowing; using Microsoft.UI.Windowing;
using Snap.Hutao.Core.Setting; using Snap.Hutao.Core.Setting;
using Snap.Hutao.Win32;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Windows.Graphics; using Windows.Graphics;
using Windows.Win32.Foundation; using Windows.Win32.Foundation;
@@ -25,11 +26,15 @@ internal static class Persistence
// Set first launch size. // Set first launch size.
HWND hwnd = (HWND)Win32Interop.GetWindowFromWindowId(appWindow.Id); HWND hwnd = (HWND)Win32Interop.GetWindowFromWindowId(appWindow.Id);
SizeInt32 size = TransformSizeForWindow(new(1200, 741), hwnd); SizeInt32 size = TransformSizeForWindow(new(1200, 741), hwnd);
RectInt32 rect = new(0, 0, size.Width, size.Height); RectInt32 rect = StructMarshal.RectInt32(size);
// Make it centralized
TransformToCenterScreen(ref rect);
RectInt32 target = (CompactRect)LocalSetting.Get(SettingKeys.WindowRect, (ulong)(CompactRect)rect); RectInt32 target = (CompactRect)LocalSetting.Get(SettingKeys.WindowRect, (ulong)(CompactRect)rect);
if (target.Width * target.Height < 848 * 524)
{
target = rect;
}
TransformToCenterScreen(ref target);
appWindow.MoveAndResize(target); appWindow.MoveAndResize(target);
} }

View File

@@ -77,7 +77,7 @@ public class User : Observable
/// </summary> /// </summary>
/// <param name="cookie">cookie的字符串形式</param> /// <param name="cookie">cookie的字符串形式</param>
/// <returns>包含cookie信息的字典</returns> /// <returns>包含cookie信息的字典</returns>
public static IDictionary<string, string> ParseCookie(string cookie) public static IDictionary<string, string> MapCookie(string cookie)
{ {
SortedDictionary<string, string> cookieDictionary = new(); SortedDictionary<string, string> cookieDictionary = new();
@@ -142,9 +142,9 @@ public class User : Observable
/// <param name="authClient">验证客户端</param> /// <param name="authClient">验证客户端</param>
/// <param name="token">取消令牌</param> /// <param name="token">取消令牌</param>
/// <returns>是否升级成功</returns> /// <returns>是否升级成功</returns>
internal async Task<bool> TryUpgradeAsync(IDictionary<string, string> addition, AuthClient authClient, CancellationToken token) internal async Task<bool> TryUpgradeByLoginTicketAsync(IDictionary<string, string> addition, AuthClient authClient, CancellationToken token)
{ {
IDictionary<string, string> cookie = ParseCookie(Cookie!); IDictionary<string, string> cookie = MapCookie(Cookie!);
if (addition.TryGetValue(CookieKeys.LOGIN_TICKET, out string? loginTicket)) if (addition.TryGetValue(CookieKeys.LOGIN_TICKET, out string? loginTicket))
{ {
cookie[CookieKeys.LOGIN_TICKET] = loginTicket; cookie[CookieKeys.LOGIN_TICKET] = loginTicket;
@@ -155,7 +155,7 @@ public class User : Observable
cookie[CookieKeys.LOGIN_UID] = loginUid; cookie[CookieKeys.LOGIN_UID] = loginUid;
} }
bool result = await TryAddStokenToCookieAsync(cookie, authClient, token).ConfigureAwait(false); bool result = await TryRequestStokenAndAddToCookieAsync(cookie, authClient, token).ConfigureAwait(false);
if (result) if (result)
{ {
@@ -165,12 +165,31 @@ public class User : Observable
return result; return result;
} }
/// <summary>
/// 添加 Stoken
/// </summary>
/// <param name="addition">额外的cookie</param>
internal void AddStoken(IDictionary<string, string> addition)
{
IDictionary<string, string> cookie = MapCookie(Cookie!);
if (addition.TryGetValue(CookieKeys.STOKEN, out string? stoken))
{
cookie[CookieKeys.STOKEN] = stoken;
}
if (addition.TryGetValue(CookieKeys.STUID, out string? stuid))
{
cookie[CookieKeys.STUID] = stuid;
}
}
private static string ToCookieString(IDictionary<string, string> cookie) private static string ToCookieString(IDictionary<string, string> cookie)
{ {
return string.Join(';', cookie.Select(kvp => $"{kvp.Key}={kvp.Value}")); return string.Join(';', cookie.Select(kvp => $"{kvp.Key}={kvp.Value}"));
} }
private static async Task<bool> TryAddStokenToCookieAsync(IDictionary<string, string> cookie, AuthClient authClient, CancellationToken token) private static async Task<bool> TryRequestStokenAndAddToCookieAsync(IDictionary<string, string> cookie, AuthClient authClient, CancellationToken token)
{ {
if (cookie.TryGetValue(CookieKeys.LOGIN_TICKET, out string? loginTicket)) if (cookie.TryGetValue(CookieKeys.LOGIN_TICKET, out string? loginTicket))
{ {
@@ -225,7 +244,7 @@ public class User : Observable
return true; return true;
} }
if (await TryAddStokenToCookieAsync(cookie, authClient, token).ConfigureAwait(false)) if (await TryRequestStokenAndAddToCookieAsync(cookie, authClient, token).ConfigureAwait(false))
{ {
Cookie = ToCookieString(cookie); Cookie = ToCookieString(cookie);
} }

View File

@@ -13,12 +13,10 @@ namespace Snap.Hutao.Model.Metadata.Converter;
internal sealed class DescParamDescriptor : ValueConverterBase<DescParam, IList<LevelParam<string, ParameterInfo>>> internal sealed class DescParamDescriptor : ValueConverterBase<DescParam, IList<LevelParam<string, ParameterInfo>>>
{ {
/// <inheritdoc/> /// <inheritdoc/>
public override IList<LevelParam<string, ParameterInfo>> Convert(DescParam rawDescParam) public override IList<LevelParam<string, ParameterInfo>> Convert(DescParam from)
{ {
IList<LevelParam<string, ParameterInfo>> parameters = rawDescParam.Parameters IList<LevelParam<string, ParameterInfo>> parameters = from.Parameters
.Select(param => new LevelParam<string, ParameterInfo>( .Select(param => new LevelParam<string, ParameterInfo>(param.Level.ToString(), GetParameterInfos(from, param.Parameters)))
param.Level.ToString(),
GetParameterInfos(rawDescParam, param.Parameters)))
.ToList(); .ToList();
return parameters; return parameters;

View File

@@ -9,7 +9,7 @@
<Identity <Identity
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d" Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
Publisher="CN=DGP Studio" Publisher="CN=DGP Studio"
Version="1.1.3.0" /> Version="1.1.4.0" />
<Properties> <Properties>
<DisplayName>胡桃</DisplayName> <DisplayName>胡桃</DisplayName>

View File

@@ -91,19 +91,18 @@ internal class InfoBarService : IInfoBarService
PrepareInfoBarAndShow(InfoBarSeverity.Error, ex.GetType().Name, $"{title}\n{ex.Message}", delay); PrepareInfoBarAndShow(InfoBarSeverity.Error, ex.GetType().Name, $"{title}\n{ex.Message}", delay);
} }
[SuppressMessage("", "VSTHRD100")] private void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay)
private async void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay)
{ {
if (infoBarStack is null) if (infoBarStack is null)
{ {
return; return;
} }
await PrepareInfoBarAndShowInternalAsync(severity, title, message, delay).ConfigureAwait(false); PrepareInfoBarAndShowInternalAsync(severity, title, message, delay).SafeForget();
} }
/// <summary> /// <summary>
/// 此方法应在主线程上运行 /// 准备信息条并显示
/// </summary> /// </summary>
/// <param name="severity">严重程度</param> /// <param name="severity">严重程度</param>
/// <param name="title">标题</param> /// <param name="title">标题</param>
@@ -122,11 +121,12 @@ internal class InfoBarService : IInfoBarService
}; };
infoBar.Closed += OnInfoBarClosed; infoBar.Closed += OnInfoBarClosed;
Must.NotNull(infoBarStack!).Children.Add(infoBar); infoBarStack!.Children.Add(infoBar);
if (delay > 0) if (delay > 0)
{ {
await Task.Delay(delay); await Task.Delay(delay).ConfigureAwait(true);
infoBarStack.Children.Remove(infoBar);
infoBar.IsOpen = false; infoBar.IsOpen = false;
} }
} }
@@ -136,7 +136,7 @@ internal class InfoBarService : IInfoBarService
{ {
await ThreadHelper.SwitchToMainThreadAsync(); await ThreadHelper.SwitchToMainThreadAsync();
Must.NotNull(infoBarStack!).Children.Remove(sender); infoBarStack!.Children.Remove(sender);
sender.Closed -= OnInfoBarClosed; sender.Closed -= OnInfoBarClosed;
} }
} }

View File

@@ -38,10 +38,17 @@ public interface IUserService
/// <summary> /// <summary>
/// 尝试使用 login_ticket 升级用户 /// 尝试使用 login_ticket 升级用户
/// </summary> /// </summary>
/// <param name="cookie">额外的Cookie</param> /// <param name="addiition">额外的Cookie</param>
/// <param name="token">取消令牌</param> /// <param name="token">取消令牌</param>
/// <returns>是否升级成功</returns> /// <returns>是否升级成功</returns>
Task<ValueResult<bool, string>> TryUpgradeUserAsync(IDictionary<string, string> addiition, CancellationToken token = default); Task<ValueResult<bool, string>> TryUpgradeUserByLoginTicketAsync(IDictionary<string, string> addiition, CancellationToken token = default);
/// <summary>
/// 尝试使用 Stoken 升级用户
/// </summary>
/// <param name="stoken">stoken</param>
/// <returns>是否升级成功</returns>
Task<ValueResult<bool, string>> TryUpgradeUserByStokenAsync(IDictionary<string, string> stoken);
/// <summary> /// <summary>
/// 异步移除用户 /// 异步移除用户

View File

@@ -118,6 +118,7 @@ internal class UserService : IUserService
Verify.Operation(newUser.IsInitialized, "该用户尚未初始化"); Verify.Operation(newUser.IsInitialized, "该用户尚未初始化");
// Sync cache // Sync cache
await ThreadHelper.SwitchToMainThreadAsync();
userCollection.Add(newUser); userCollection.Add(newUser);
// Sync database // Sync database
@@ -180,7 +181,7 @@ internal class UserService : IUserService
} }
/// <inheritdoc/> /// <inheritdoc/>
public async Task<ValueResult<bool, string>> TryUpgradeUserAsync(IDictionary<string, string> addition, CancellationToken token = default) public async Task<ValueResult<bool, string>> TryUpgradeUserByLoginTicketAsync(IDictionary<string, string> addition, CancellationToken token = default)
{ {
Must.NotNull(userCollection!); Must.NotNull(userCollection!);
if (addition.TryGetValue(CookieKeys.LOGIN_UID, out string? uid)) if (addition.TryGetValue(CookieKeys.LOGIN_UID, out string? uid))
@@ -189,15 +190,36 @@ internal class UserService : IUserService
if (userCollection.SingleOrDefault(u => u.UserInfo!.Uid == uid) is BindingUser userWithSameUid) if (userCollection.SingleOrDefault(u => u.UserInfo!.Uid == uid) is BindingUser userWithSameUid)
{ {
// Update user cookie here. // Update user cookie here.
if (await userWithSameUid.TryUpgradeAsync(addition, authClient, token)) if (await userWithSameUid.TryUpgradeByLoginTicketAsync(addition, authClient, token))
{ {
appDbContext.Users.Update(userWithSameUid.Entity); appDbContext.Users.Update(userWithSameUid.Entity);
await appDbContext.SaveChangesAsync().ConfigureAwait(false); await appDbContext.SaveChangesAsync().ConfigureAwait(false);
return new(true, uid); return new(true, userWithSameUid.UserInfo?.Nickname ?? string.Empty);
} }
} }
} }
return new(false, string.Empty); return new(false, string.Empty);
} }
/// <inheritdoc/>
public async Task<ValueResult<bool, string>> TryUpgradeUserByStokenAsync(IDictionary<string, string> stoken)
{
Must.NotNull(userCollection!);
if (stoken.TryGetValue(CookieKeys.STUID, out string? uid))
{
// 查找是否有相同的uid
if (userCollection.SingleOrDefault(u => u.UserInfo!.Uid == uid) is BindingUser userWithSameUid)
{
// Update user cookie here.
userWithSameUid.AddStoken(stoken);
appDbContext.Users.Update(userWithSameUid.Entity);
await appDbContext.SaveChangesAsync().ConfigureAwait(false);
return new(true, userWithSameUid.UserInfo?.Nickname ?? string.Empty);
}
}
return new(false, string.Empty);
}
} }

View File

@@ -19,12 +19,14 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="auto"/> <RowDefinition Height="auto"/>
<RowDefinition/> <RowDefinition/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock <TextBlock
Margin="0,0,0,8" Text="在下方登录"
Text="请在 成功登录米哈游通行证 后点击 [继续] 按钮"/> Grid.Row="0"/>
<WebView2 <WebView2
Grid.Row="1" Grid.Row="2"
Margin="0,12,0,0"
Width="640" Width="640"
Height="400" Height="400"
x:Name="WebView"/> x:Name="WebView"/>

View File

@@ -13,7 +13,7 @@ namespace Snap.Hutao.View.Dialog;
/// </summary> /// </summary>
public sealed partial class UserAutoCookieDialog : ContentDialog public sealed partial class UserAutoCookieDialog : ContentDialog
{ {
private IDictionary<string, string>? cookieString; private IDictionary<string, string>? cookie;
/// <summary> /// <summary>
/// 构造一个新的用户自动Cookie对话框 /// 构造一个新的用户自动Cookie对话框
@@ -32,7 +32,7 @@ public sealed partial class UserAutoCookieDialog : ContentDialog
public async Task<ValueResult<bool, IDictionary<string, string>>> GetInputCookieAsync() public async Task<ValueResult<bool, IDictionary<string, string>>> GetInputCookieAsync()
{ {
ContentDialogResult result = await ShowAsync(); ContentDialogResult result = await ShowAsync();
return new(result == ContentDialogResult.Primary && cookieString != null, cookieString!); return new(result == ContentDialogResult.Primary && cookie != null, cookie!);
} }
[SuppressMessage("", "VSTHRD100")] [SuppressMessage("", "VSTHRD100")]
@@ -62,8 +62,7 @@ public sealed partial class UserAutoCookieDialog : ContentDialog
{ {
CoreWebView2CookieManager manager = WebView.CoreWebView2.CookieManager; CoreWebView2CookieManager manager = WebView.CoreWebView2.CookieManager;
IReadOnlyList<CoreWebView2Cookie> cookies = await manager.GetCookiesAsync("https://user.mihoyo.com"); IReadOnlyList<CoreWebView2Cookie> cookies = await manager.GetCookiesAsync("https://user.mihoyo.com");
cookieString = cookies.ToDictionary(c => c.Name, c => c.Value); cookie = cookies.ToDictionary(c => c.Name, c => c.Value);
WebView.CoreWebView2.SourceChanged -= OnCoreWebView2SourceChanged; WebView.CoreWebView2.SourceChanged -= OnCoreWebView2SourceChanged;
} }
catch (Exception) catch (Exception)

View File

@@ -9,6 +9,7 @@
xmlns:mxi="using:Microsoft.Xaml.Interactivity" xmlns:mxi="using:Microsoft.Xaml.Interactivity"
xmlns:shc="using:Snap.Hutao.Control" xmlns:shc="using:Snap.Hutao.Control"
xmlns:shvm="using:Snap.Hutao.ViewModel" xmlns:shvm="using:Snap.Hutao.ViewModel"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
mc:Ignorable="d" mc:Ignorable="d"
d:DataContext="{d:DesignInstance shvm:UserViewModel}"> d:DataContext="{d:DesignInstance shvm:UserViewModel}">
<mxi:Interaction.Behaviors> <mxi:Interaction.Behaviors>
@@ -199,11 +200,11 @@
Text="Cookie"/> Text="Cookie"/>
<CommandBar DefaultLabelPosition="Right"> <CommandBar DefaultLabelPosition="Right">
<AppBarButton <AppBarButton
Icon="Add" Icon="{shcm:FontIcon Glyph=&#xF4A5;}"
Label="升级Stoken" Label="升级Stoken"
Command="{Binding UpgradeToStokenCommand}"/> Command="{Binding UpgradeToStokenCommand}"/>
<AppBarButton <AppBarButton
Icon="Add" Icon="{shcm:FontIcon Glyph=&#xE710;}"
Label="手动添加" Label="手动添加"
Command="{Binding AddUserCommand}"/> Command="{Binding AddUserCommand}"/>
</CommandBar> </CommandBar>

View File

@@ -10,7 +10,6 @@ using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.View.Dialog; using Snap.Hutao.View.Dialog;
using Snap.Hutao.Web.Hoyolab; using Snap.Hutao.Web.Hoyolab;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Net;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
namespace Snap.Hutao.ViewModel; namespace Snap.Hutao.ViewModel;
@@ -21,8 +20,6 @@ namespace Snap.Hutao.ViewModel;
[Injection(InjectAs.Transient)] [Injection(InjectAs.Transient)]
internal class UserViewModel : ObservableObject internal class UserViewModel : ObservableObject
{ {
private const string AccountIdKey = "account_id";
private readonly IUserService userService; private readonly IUserService userService;
private readonly IInfoBarService infoBarService; private readonly IInfoBarService infoBarService;
@@ -42,7 +39,7 @@ internal class UserViewModel : ObservableObject
OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync); OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync);
AddUserCommand = asyncRelayCommandFactory.Create(AddUserAsync); AddUserCommand = asyncRelayCommandFactory.Create(AddUserAsync);
UpgradeToStokenCommand = asyncRelayCommandFactory.Create(UpgradeToStokenAsync); UpgradeToStokenCommand = asyncRelayCommandFactory.Create(UpgradeByLoginTicketAsync);
RemoveUserCommand = asyncRelayCommandFactory.Create<User>(RemoveUserAsync); RemoveUserCommand = asyncRelayCommandFactory.Create<User>(RemoveUserAsync);
CopyCookieCommand = new RelayCommand<User>(CopyCookie); CopyCookieCommand = new RelayCommand<User>(CopyCookie);
} }
@@ -92,35 +89,45 @@ internal class UserViewModel : ObservableObject
/// </summary> /// </summary>
public ICommand CopyCookieCommand { get; } public ICommand CopyCookieCommand { get; }
private static bool TryValidateCookie(IDictionary<string, string> map, [NotNullWhen(true)] out IDictionary<string, string>? filteredCookie) private static (bool Valid, bool Upgrade) TryValidateCookie(IDictionary<string, string> map, out IDictionary<string, string> cookie)
{ {
int validFlag = 4; int validFlag = 4;
int stokenFlag = 2;
SortedDictionary<string, string> filter = new(); cookie = new SortedDictionary<string, string>();
foreach ((string key, string value) in map) foreach ((string key, string value) in map)
{ {
if (key == CookieKeys.COOKIE_TOKEN || key == CookieKeys.ACCOUNT_ID || key == CookieKeys.LTOKEN || key == CookieKeys.LTUID) switch (key)
{
case CookieKeys.COOKIE_TOKEN:
case CookieKeys.ACCOUNT_ID:
case CookieKeys.LTOKEN:
case CookieKeys.LTUID:
{ {
validFlag--; validFlag--;
filter.Add(key, value); cookie.Add(key, value);
break;
} }
else if (key == CookieKeys.STOKEN || key == CookieKeys.STUID || key == CookieKeys.LOGIN_TICKET || key == CookieKeys.LOGIN_UID)
case CookieKeys.STOKEN:
case CookieKeys.STUID:
{ {
filter.Add(key, value); stokenFlag--;
cookie.Add(key, value);
break;
}
case CookieKeys.LOGIN_TICKET:
case CookieKeys.LOGIN_UID:
{
cookie.Add(key, value);
break;
}
} }
} }
if (validFlag == 0) return (validFlag == 0, stokenFlag == 0);
{
filteredCookie = filter;
return true;
}
else
{
filteredCookie = null;
return false;
}
} }
private async Task OpenUIAsync() private async Task OpenUIAsync()
@@ -138,11 +145,12 @@ internal class UserViewModel : ObservableObject
// User confirms the input // User confirms the input
if (result.IsOk) if (result.IsOk)
{ {
if (TryValidateCookie(User.ParseCookie(result.Value), out IDictionary<string, string>? filteredCookie)) (bool valid, bool upgradable) = TryValidateCookie(User.MapCookie(result.Value), out IDictionary<string, string> cookie);
if (valid)
{ {
if (await userService.CreateUserAsync(filteredCookie).ConfigureAwait(false) is User user) if (await userService.CreateUserAsync(cookie).ConfigureAwait(false) is User user)
{ {
switch (await userService.TryAddUserAsync(user, filteredCookie[AccountIdKey]).ConfigureAwait(false)) switch (await userService.TryAddUserAsync(user, cookie[CookieKeys.ACCOUNT_ID]).ConfigureAwait(false))
{ {
case UserAddResult.Added: case UserAddResult.Added:
infoBarService.Success($"用户 [{user.UserInfo!.Nickname}] 添加成功"); infoBarService.Success($"用户 [{user.UserInfo!.Nickname}] 添加成功");
@@ -163,13 +171,29 @@ internal class UserViewModel : ObservableObject
} }
} }
else else
{
if (upgradable)
{
(bool success, string nickname) = await userService.TryUpgradeUserByStokenAsync(cookie).ConfigureAwait(false);
if (success)
{
infoBarService.Information($"用户 [{nickname}] 的 Stoken 更新成功");
}
else
{
infoBarService.Warning($"未找到匹配的可升级用户");
}
}
else
{ {
infoBarService.Warning("提供的文本不是正确的 Cookie ,请重新输入"); infoBarService.Warning("提供的文本不是正确的 Cookie ,请重新输入");
} }
} }
} }
}
private async Task UpgradeToStokenAsync() private async Task UpgradeByLoginTicketAsync()
{ {
// Get cookie from user input // Get cookie from user input
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>(); MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
@@ -178,10 +202,10 @@ internal class UserViewModel : ObservableObject
// User confirms the input // User confirms the input
if (isOk) if (isOk)
{ {
(bool isUpgradeSucceed, string uid) = await userService.TryUpgradeUserAsync(addition).ConfigureAwait(false); (bool isUpgraded, string nickname) = await userService.TryUpgradeUserByLoginTicketAsync(addition).ConfigureAwait(false);
if (isUpgradeSucceed) if (isUpgraded)
{ {
infoBarService.Information($"用户 [{uid}] 的 Cookie 已成功添加 Stoken"); infoBarService.Information($"用户 [{nickname}] 的 Cookie 已成功添加 Stoken");
} }
else else
{ {

View File

@@ -3,6 +3,7 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.WinUI.UI; using CommunityToolkit.WinUI.UI;
using Snap.Hutao.Core.Threading;
using Snap.Hutao.Factory.Abstraction; using Snap.Hutao.Factory.Abstraction;
using Snap.Hutao.Model; using Snap.Hutao.Model;
using Snap.Hutao.Model.Intrinsic; using Snap.Hutao.Model.Intrinsic;
@@ -143,13 +144,14 @@ internal class WikiAvatarViewModel : ObservableObject
private async Task OpenUIAsync() private async Task OpenUIAsync()
{ {
if (await metadataService.InitializeAsync()) if (await metadataService.InitializeAsync().ConfigureAwait(false))
{ {
IList<Avatar> avatars = await metadataService.GetAvatarsAsync(); IList<Avatar> avatars = await metadataService.GetAvatarsAsync().ConfigureAwait(false);
IOrderedEnumerable<Avatar> sorted = avatars IOrderedEnumerable<Avatar> sorted = avatars
.OrderBy(avatar => avatar.BeginTime) .OrderBy(avatar => avatar.BeginTime)
.ThenBy(avatar => avatar.Sort); .ThenBy(avatar => avatar.Sort);
await ThreadHelper.SwitchToMainThreadAsync();
Avatars = new AdvancedCollectionView(sorted.ToList(), true); Avatars = new AdvancedCollectionView(sorted.ToList(), true);
Avatars.MoveCurrentToFirst(); Avatars.MoveCurrentToFirst();
} }
@@ -174,7 +176,7 @@ internal class WikiAvatarViewModel : ObservableObject
.Select(e => e.Value.Value) .Select(e => e.Value.Value)
.ToList(); .ToList();
List<ItemQuality> targeQualities = FilterQualityInfos List<ItemQuality> targetQualities = FilterQualityInfos
.Where(e => e.IsSelected) .Where(e => e.IsSelected)
.Select(e => e.Value.Value) .Select(e => e.Value.Value)
.ToList(); .ToList();
@@ -188,7 +190,7 @@ internal class WikiAvatarViewModel : ObservableObject
&& targetElements.Contains(avatar.FetterInfo.VisionBefore) && targetElements.Contains(avatar.FetterInfo.VisionBefore)
&& targetAssociations.Contains(avatar.FetterInfo.Association) && targetAssociations.Contains(avatar.FetterInfo.Association)
&& targetWeaponTypes.Contains(avatar.Weapon) && targetWeaponTypes.Contains(avatar.Weapon)
&& targeQualities.Contains(avatar.Quality) && targetQualities.Contains(avatar.Quality)
&& targetBodies.Contains(avatar.Body); && targetBodies.Contains(avatar.Body);
if (!Avatars.Contains(Selected)) if (!Avatars.Contains(Selected))

View File

@@ -12,7 +12,7 @@ public class DetailedCertification : Certification
/// Id /// Id
/// </summary> /// </summary>
[JsonPropertyName("id")] [JsonPropertyName("id")]
public int Id { get; set; } public string Id { get; set; } = default!;
/// <summary> /// <summary>
/// 认证Id /// 认证Id

View File

@@ -7,7 +7,7 @@ using Windows.Win32.System.Diagnostics.ToolHelp;
namespace Snap.Hutao.Win32; namespace Snap.Hutao.Win32;
/// <summary> /// <summary>
/// 内存拓展 /// 内存拓展 for <see cref="Memory{T}"/> <see cref="Span{T}"/>
/// </summary> /// </summary>
internal static class MemoryExtensions internal static class MemoryExtensions
{ {

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved. // Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license. // Licensed under the MIT license.
using Windows.Graphics;
using Windows.Win32.System.Diagnostics.ToolHelp; using Windows.Win32.System.Diagnostics.ToolHelp;
namespace Snap.Hutao.Win32; namespace Snap.Hutao.Win32;
@@ -19,6 +20,16 @@ internal static class StructMarshal
return new() { dwSize = (uint)sizeof(MODULEENTRY32) }; return new() { dwSize = (uint)sizeof(MODULEENTRY32) };
} }
/// <summary>
/// 构造一个新的<see cref="Windows.Graphics.RectInt32"/>
/// </summary>
/// <param name="size">尺寸</param>
/// <returns>新的实例</returns>
public static RectInt32 RectInt32(SizeInt32 size)
{
return new(0, 0, size.Width, size.Height);
}
/// <summary> /// <summary>
/// 判断结构实例是否为默认结构 /// 判断结构实例是否为默认结构
/// </summary> /// </summary>