mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
331cc14532 | ||
|
|
c0ddb24825 | ||
|
|
e925c5909c |
@@ -6,7 +6,9 @@
|
|||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
* 前往 [下载页面](https://go.hut.ao/archive) 下载最新版本的 `胡桃` 安装包
|
* 前往 [下载页面](https://go.hut.ao/archive) 下载最新版本的 `胡桃` 安装包
|
||||||
* 完全解压后,使用 powershell 运行 `install.ps1` 文件
|
* (曾启用的可以跳过此步骤)在系统设置中打开 **开发者选项** 界面,勾选 `开发人员模式` 和 `允许 PowerShell 脚本`
|
||||||
|
* 完全解压后,右键使用 powershell 运行 `install.ps1` 文件
|
||||||
|
* 安装完成后可以关闭 `允许 PowerShell 脚本`
|
||||||
|
|
||||||
## 特别感谢
|
## 特别感谢
|
||||||
|
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
/// 异步移除用户
|
/// 异步移除用户
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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"/>
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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=}"
|
||||||
Label="升级Stoken"
|
Label="升级Stoken"
|
||||||
Command="{Binding UpgradeToStokenCommand}"/>
|
Command="{Binding UpgradeToStokenCommand}"/>
|
||||||
<AppBarButton
|
<AppBarButton
|
||||||
Icon="Add"
|
Icon="{shcm:FontIcon Glyph=}"
|
||||||
Label="手动添加"
|
Label="手动添加"
|
||||||
Command="{Binding AddUserCommand}"/>
|
Command="{Binding AddUserCommand}"/>
|
||||||
</CommandBar>
|
</CommandBar>
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user