rename signin interface

This commit is contained in:
DismissedLight
2023-10-04 22:20:06 +08:00
parent 07f0c37b2c
commit 3d3d004441
29 changed files with 329 additions and 290 deletions

View File

@@ -17,6 +17,7 @@
<ResourceDictionary Source="Control/Theme/PageOverride.xaml"/>
<ResourceDictionary Source="Control/Theme/PivotOverride.xaml"/>
<ResourceDictionary Source="Control/Theme/SettingsStyle.xaml"/>
<ResourceDictionary Source="Control/Theme/TransitionCollection.xaml"/>
<ResourceDictionary Source="Control/Theme/WindowOverride.xaml"/>
<ResourceDictionary Source="Control/Loading.xaml"/>
</ResourceDictionary.MergedDictionaries>
@@ -263,19 +264,7 @@
Columns="5"
RowSpacing="4"/>
</ItemsPanelTemplate>
<!-- Transitions -->
<TransitionCollection x:Key="ReorderThemeTransitions">
<ReorderThemeTransition/>
</TransitionCollection>
<TransitionCollection x:Key="ContentThemeTransitions">
<ContentThemeTransition/>
</TransitionCollection>
<TransitionCollection x:Key="ListViewLikeThemeTransitions">
<AddDeleteThemeTransition/>
<ContentThemeTransition/>
<ReorderThemeTransition/>
<EntranceThemeTransition IsStaggeringEnabled="False"/>
</TransitionCollection>
<cwm:AttachedCardShadow
x:Key="CompatCardShadow"

View File

@@ -0,0 +1,17 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TransitionCollection x:Key="ContentThemeTransitions">
<ContentThemeTransition/>
</TransitionCollection>
<TransitionCollection x:Key="EntranceThemeTransitions">
<EntranceThemeTransition/>
</TransitionCollection>
<TransitionCollection x:Key="ListViewLikeThemeTransitions">
<AddDeleteThemeTransition/>
<ContentThemeTransition/>
<ReorderThemeTransition/>
<EntranceThemeTransition IsStaggeringEnabled="False"/>
</TransitionCollection>
<TransitionCollection x:Key="ReorderThemeTransitions">
<ReorderThemeTransition/>
</TransitionCollection>
</ResourceDictionary>

View File

@@ -3750,6 +3750,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 无感验证复合 Url 配置成功 的本地化字符串。
/// </summary>
internal static string ViewModelSettingGeetestCustomUrlSucceed {
get {
return ResourceManager.GetString("ViewModelSettingGeetestCustomUrlSucceed", resourceCulture);
}
}
/// <summary>
/// 查找类似 设置数据目录成功,重启以应用更改 的本地化字符串。
/// </summary>

View File

@@ -1403,6 +1403,9 @@
<data name="ViewModelSettingCreateDesktopShortcutFailed" xml:space="preserve">
<value>创建桌面快捷方式失败</value>
</data>
<data name="ViewModelSettingGeetestCustomUrlSucceed" xml:space="preserve">
<value>无感验证复合 Url 配置成功</value>
</data>
<data name="ViewModelSettingSetDataFolderSuccess" xml:space="preserve">
<value>设置数据目录成功,重启以应用更改</value>
</data>

View File

@@ -79,6 +79,7 @@
<None Remove="Control\Theme\PageOverride.xaml" />
<None Remove="Control\Theme\PivotOverride.xaml" />
<None Remove="Control\Theme\SettingsStyle.xaml" />
<None Remove="Control\Theme\TransitionCollection.xaml" />
<None Remove="Control\Theme\WindowOverride.xaml" />
<None Remove="GuideWindow.xaml" />
<None Remove="IdentityStructs.json" />
@@ -140,7 +141,6 @@
<None Remove="View\Dialog\GeetestCustomUrlDialog.xaml" />
<None Remove="View\Dialog\LaunchGameAccountNameDialog.xaml" />
<None Remove="View\Dialog\LaunchGamePackageConvertDialog.xaml" />
<None Remove="View\Dialog\SignInWebViewDialog.xaml" />
<None Remove="View\Dialog\UserDialog.xaml" />
<None Remove="View\Guide\GuideView.xaml" />
<None Remove="View\InfoBarView.xaml" />
@@ -303,6 +303,12 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Page Update="Control\Theme\TransitionCollection.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Control\Theme\SettingsStyle.xaml">
<Generator>MSBuild:Compile</Generator>
@@ -470,11 +476,6 @@
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="View\Dialog\SignInWebViewDialog.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="View\Dialog\DailyNoteNotificationDialog.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@@ -5,6 +5,7 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.Control.Theme;
using Snap.Hutao.Web.Bridge;
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
using System.Text;
using System.Text.RegularExpressions;
@@ -140,13 +141,7 @@ internal sealed partial class AnnouncementContentViewer : UserControl
try
{
await WebView.EnsureCoreWebView2Async();
CoreWebView2Settings settings = WebView.CoreWebView2.Settings;
#if !DEBUG
settings.AreBrowserAcceleratorKeysEnabled = false;
settings.AreDefaultContextMenusEnabled = false;
settings.AreDevToolsEnabled = false;
#endif
WebView.CoreWebView2.DisableDevToolsOnReleaseBuild();
WebView.CoreWebView2.WebMessageReceived += webMessageReceivedHandler;
await WebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(MihoyoSDKDefinition);

View File

@@ -1,11 +1,15 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Bridge;
namespace Snap.Hutao.View.Control;
internal interface IWebViewerSource
{
MiHoYoJSInterface CreateJsInterface(IServiceProvider serviceProvider, CoreWebView2 coreWebView2, UserAndUid userAndUid);
string GetSource(UserAndUid userAndUid);
}

View File

@@ -2,15 +2,23 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Bridge;
namespace Snap.Hutao.View.Control;
[DependencyProperty("Source", typeof(string))]
[DependencyProperty("ChineseSource", typeof(string))]
[DependencyProperty("OverseaSource", typeof(string))]
internal sealed partial class StaticWebview2ViewerSource : DependencyObject, IWebViewerSource
{
public MiHoYoJSInterface CreateJsInterface(IServiceProvider serviceProvider, CoreWebView2 coreWebView2, UserAndUid userAndUid)
{
return serviceProvider.CreateInstance<MiHoYoJSInterface>(coreWebView2, userAndUid);
}
public string GetSource(UserAndUid userAndUid)
{
return Source;
return userAndUid.User.IsOversea ? OverseaSource : ChineseSource;
}
}

View File

@@ -4,14 +4,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Transitions="{ThemeResource EntranceThemeTransitions}"
mc:Ignorable="d">
<UserControl.Transitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</UserControl.Transitions>
<WebView2
x:Name="WebView"
Margin="0"
DefaultBackgroundColor="Transparent"/>
<WebView2 x:Name="WebView" DefaultBackgroundColor="Transparent"/>
</UserControl>

View File

@@ -39,11 +39,6 @@ internal partial class WebViewer : UserControl, IRecipient<UserChangedMessage>
public void Receive(UserChangedMessage message)
{
if (message.NewValue?.SelectedUserGameRole is null)
{
return;
}
ITaskContext taskContext = serviceProvider.GetRequiredService<ITaskContext>();
taskContext.InvokeOnMainThread(RefreshWebview2Content);
}
@@ -63,13 +58,14 @@ internal partial class WebViewer : UserControl, IRecipient<UserChangedMessage>
private async ValueTask InitializeAsync()
{
await WebView.EnsureCoreWebView2Async();
WebView.CoreWebView2.DisableDevToolsOnReleaseBuild();
RefreshWebview2Content();
}
private async void RefreshWebview2Content()
{
User? user = serviceProvider.GetRequiredService<IUserService>().Current;
if (user is null)
if (user is null || user.SelectedUserGameRole is null)
{
return;
}
@@ -91,13 +87,9 @@ internal partial class WebViewer : UserControl, IRecipient<UserChangedMessage>
string source = SourceProvider.GetSource(userAndUid);
if (!string.IsNullOrEmpty(source))
{
foreach (CoreWebView2Cookie cookie in await coreWebView2.CookieManager.GetCookiesAsync(".mihoyo.com"))
{
coreWebView2.CookieManager.DeleteCookie(cookie);
}
await coreWebView2.DeleteCookiesAsync(".mihoyo.com").ConfigureAwait(true);
coreWebView2.SetCookie(user.CookieToken, user.LToken, user.SToken).SetMobileUserAgent();
jsInterface = serviceProvider.CreateInstance<MiHoYoJSInterface>(coreWebView2, userAndUid);
jsInterface = SourceProvider.CreateJsInterface(serviceProvider, coreWebView2, userAndUid);
CoreWebView2Navigator navigator = new(coreWebView2);
await navigator.NavigateAsync("about:blank").ConfigureAwait(true);

View File

@@ -1,20 +0,0 @@
<ContentDialog
x:Class="Snap.Hutao.View.Dialog.SignInWebViewDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
Closed="OnContentDialogClosed"
DefaultButton="Primary"
PrimaryButtonText="{shcm:ResourceString Name=ContentDialogCompletePrimaryButtonText}"
Style="{StaticResource WebView2ContentDialogStyle}"
mc:Ignorable="d">
<Grid Loaded="OnGridLoaded">
<WebView2
Name="WebView"
Width="380"
Height="456"/>
</Grid>
</ContentDialog>

View File

@@ -1,66 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.Service.User;
using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Bridge;
namespace Snap.Hutao.View.Dialog;
/// <summary>
/// 签到网页视图对话框
/// </summary>
[HighQuality]
internal sealed partial class SignInWebViewDialog : ContentDialog
{
private readonly IServiceScope scope;
[SuppressMessage("", "IDE0052")]
private MiHoYoJSInterface? jsInterface;
/// <summary>
/// 构造一个新的签到网页视图对话框
/// </summary>
/// <param name="serviceProvider">服务提供器</param>
public SignInWebViewDialog(IServiceProvider serviceProvider)
{
InitializeComponent();
XamlRoot = serviceProvider.GetRequiredService<MainWindow>().Content.XamlRoot;
scope = serviceProvider.CreateScope();
}
private void OnGridLoaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
InitializeAsync().SafeForget();
}
private async ValueTask InitializeAsync()
{
await WebView.EnsureCoreWebView2Async();
CoreWebView2 coreWebView2 = WebView.CoreWebView2;
User? user = scope.ServiceProvider.GetRequiredService<IUserService>().Current;
if (UserAndUid.TryFromUser(user, out UserAndUid? userAndUid))
{
if (user.Entity.IsOversea)
{
coreWebView2.SetCookie(user.CookieToken, user.LToken, null, true).SetMobileOverseaUserAgent();
jsInterface = new SignInJSInterfaceOversea(coreWebView2, scope.ServiceProvider, userAndUid);
coreWebView2.Navigate("https://act.hoyolab.com/ys/event/signin-sea-v3/index.html?act_id=e202102251931481");
}
else
{
coreWebView2.SetCookie(user.CookieToken, user.LToken, null, false).SetMobileUserAgent();
jsInterface = new SignInJsInterface(coreWebView2, scope.ServiceProvider, userAndUid);
coreWebView2.Navigate("https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?act_id=e202009291139501");
}
}
}
private void OnContentDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args)
{
jsInterface = null;
scope.Dispose();
}
}

View File

@@ -35,33 +35,29 @@
</Grid.RowDefinitions>
<CommandBar Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" DefaultLabelPosition="Right">
<CommandBar.Content>
<Button Margin="12,8,0,0" Content="{shcm:ResourceString Name=ViewPageDailyNoteVerify}">
<mxi:Interaction.Behaviors>
<mxic:EventTriggerBehavior EventName="Click">
<shcb:OpenAttachedFlyoutAction/>
</mxic:EventTriggerBehavior>
</mxi:Interaction.Behaviors>
<FlyoutBase.AttachedFlyout>
<Flyout
LightDismissOverlayMode="On"
Placement="Full"
ShowMode="Standard">
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="Padding" Value="0"/>
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
</Style>
</Flyout.FlyoutPresenterStyle>
<shvc:WebViewer SourceProvider="{Binding VerifyUrlSource, Mode=OneWay}"/>
</Flyout>
</FlyoutBase.AttachedFlyout>
</Button>
</CommandBar.Content>
<AppBarButton
Command="{Binding RefreshCommand}"
Icon="{shcm:FontIcon Glyph={StaticResource FontIconContentRefresh}}"
Label="{shcm:ResourceString Name=ViewPageDailyNoteRefresh}"/>
<AppBarButton
AllowFocusOnInteraction="True"
Icon="{shcm:FontIcon Glyph=&#xE9D5;}"
Label="{shcm:ResourceString Name=ViewPageDailyNoteVerify}">
<AppBarButton.Flyout>
<Flyout
LightDismissOverlayMode="On"
Placement="Full"
ShowMode="Standard">
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="Padding" Value="0"/>
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
</Style>
</Flyout.FlyoutPresenterStyle>
<shvc:WebViewer SourceProvider="{Binding VerifyUrlSource, Mode=OneWay}"/>
</Flyout>
</AppBarButton.Flyout>
</AppBarButton>
<AppBarButton Icon="{shcm:FontIcon Glyph={StaticResource FontIconContentAdd}}" Label="{shcm:ResourceString Name=ViewPageDailyNoteAddEntry}">
<AppBarButton.Flyout>
<Flyout LightDismissOverlayMode="On" Placement="Bottom">

View File

@@ -7,6 +7,7 @@ using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.User;
using Snap.Hutao.Web;
using Snap.Hutao.Web.Bridge;
using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
using Snap.Hutao.Web.Request;
@@ -18,74 +19,62 @@ namespace Snap.Hutao.View.Page;
/// <summary>
/// 登录米哈游通行证页面
/// </summary>
internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Controls.Page
internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Controls.Page, ISupportLoginByWebView
{
private const string CookieHost = "https://account.hoyoverse.com";
private const string NavigateUrl = "https://account.hoyoverse.com/#/login";
private readonly IServiceProvider serviceProvider;
private readonly IInfoBarService infoBarService;
private readonly JsonSerializerOptions jsonSerializerOptions;
private readonly ILogger<LoginHoyoverseUserPage> logger;
/// <summary>
/// 构造一个新的登录米哈游通行证页面
/// </summary>
public LoginHoyoverseUserPage()
{
serviceProvider = Ioc.Default;
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
jsonSerializerOptions = serviceProvider.GetRequiredService<JsonSerializerOptions>();
logger = serviceProvider.GetRequiredService<ILogger<LoginHoyoverseUserPage>>();
InitializeComponent();
}
private static async ValueTask<string> GetUidFromCookieAsync(IServiceProvider serviceProvider, Cookie cookie, CancellationToken token = default)
private async ValueTask<string> GetUidFromCookieAsync(Cookie cookie, CancellationToken token = default)
{
JsonSerializerOptions options = serviceProvider.GetRequiredService<JsonSerializerOptions>();
ILogger<LoginHoyoverseUserPage> logger = serviceProvider.GetRequiredService<ILogger<LoginHoyoverseUserPage>>();
HttpClient httpClient = serviceProvider.GetRequiredService<HttpClient>();
httpClient.DefaultRequestHeaders.Set("Cookie", cookie.ToString());
WebApiResponse<AccountInfoWrapper>? resp = await httpClient
.TryCatchGetFromJsonAsync<WebApiResponse<AccountInfoWrapper>>(ApiOsEndpoints.WebApiOsAccountLoginByCookie, options, logger, token)
.TryCatchGetFromJsonAsync<WebApiResponse<AccountInfoWrapper>>(ApiOsEndpoints.WebApiOsAccountLoginByCookie, jsonSerializerOptions, logger, token)
.ConfigureAwait(false);
return $"{resp?.Data?.AccountInfo?.AccountId}";
}
private async void OnRootLoaded(object sender, RoutedEventArgs e)
private void OnRootLoaded(object sender, RoutedEventArgs e)
{
try
{
await WebView.EnsureCoreWebView2Async();
CoreWebView2CookieManager manager = WebView.CoreWebView2.CookieManager;
IReadOnlyList<CoreWebView2Cookie> cookies = await manager.GetCookiesAsync("https://account.hoyoverse.com");
foreach (CoreWebView2Cookie item in cookies)
{
manager.DeleteCookie(item);
}
CoreWebView2 coreWebView2 = WebView.CoreWebView2;
coreWebView2.Settings.IsGeneralAutofillEnabled = false;
coreWebView2.Settings.IsPasswordAutosaveEnabled = false;
coreWebView2.Navigate("https://account.hoyoverse.com/#/login");
}
catch (Exception ex)
{
Ioc.Default.GetRequiredService<IInfoBarService>().Error(ex);
}
ISupportLoginByWebView.InitialzeAsync(WebView, infoBarService, CookieHost, NavigateUrl).SafeForget();
}
[Command("HandleCurrentCookieCommand")]
private async Task HandleCurrentCookieAsync()
{
CoreWebView2CookieManager manager = WebView.CoreWebView2.CookieManager;
IReadOnlyList<CoreWebView2Cookie> cookies = await manager.GetCookiesAsync("https://account.hoyoverse.com");
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
IReadOnlyList<CoreWebView2Cookie> cookies = await WebView.CoreWebView2.CookieManager.GetCookiesAsync("https://account.hoyoverse.com");
Cookie loginTicketCookie = Cookie.FromCoreWebView2Cookies(cookies);
if (loginTicketCookie.IsEmpty())
{
return;
}
string uid = await GetUidFromCookieAsync(Ioc.Default, loginTicketCookie).ConfigureAwait(false);
string uid = await GetUidFromCookieAsync(loginTicketCookie).ConfigureAwait(false);
loginTicketCookie[Cookie.LOGIN_UID] = uid;
// 使用 loginTicket 获取 stoken
Response<ListWrapper<NameToken>> multiTokenResponse = await Ioc.Default
Response<ListWrapper<NameToken>> multiTokenResponse = await serviceProvider
.GetRequiredService<AuthClient>()
.GetMultiTokenByLoginTicketAsync(loginTicketCookie, true)
.ConfigureAwait(false);
@@ -96,19 +85,10 @@ internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Control
}
Dictionary<string, string> multiTokenMap = multiTokenResponse.Data.List.ToDictionary(n => n.Name, n => n.Token);
Cookie hoyolabCookie = Cookie.FromSToken(uid, multiTokenMap[Cookie.STOKEN]);
Cookie stokenV1 = Cookie.FromSToken(uid, multiTokenMap[Cookie.STOKEN]);
// 处理 cookie 并添加用户
(UserOptionResult result, string nickname) = await Ioc.Default
.GetRequiredService<IUserService>()
.ProcessInputCookieAsync(hoyolabCookie, true)
.ConfigureAwait(false);
Ioc.Default.GetRequiredService<INavigationService>().GoBack();
await Ioc.Default
.GetRequiredService<ViewModel.User.UserViewModel>()
.HandleUserOptionResultAsync(result, nickname)
await ISupportLoginByWebView
.PostHandleCurrentCookieAsync(serviceProvider, stokenV1, true)
.ConfigureAwait(false);
}

View File

@@ -2,10 +2,12 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.User;
using Snap.Hutao.Web.Bridge;
using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.Passport;
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
@@ -17,53 +19,41 @@ namespace Snap.Hutao.View.Page;
/// 登录米哈游通行证页面
/// </summary>
[HighQuality]
internal sealed partial class LoginMihoyoUserPage : Microsoft.UI.Xaml.Controls.Page
internal sealed partial class LoginMihoyoUserPage : Microsoft.UI.Xaml.Controls.Page, ISupportLoginByWebView
{
private const string CookieHost = "https://user.mihoyo.com";
private const string NavigateUrl = "https://user.mihoyo.com/#/login/password";
private readonly IServiceProvider serviceProvider;
private readonly IInfoBarService infoBarService;
/// <summary>
/// 构造一个新的登录米哈游通行证页面
/// </summary>
public LoginMihoyoUserPage()
{
serviceProvider = Ioc.Default;
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
InitializeComponent();
}
private async void OnRootLoaded(object sender, RoutedEventArgs e)
private void OnRootLoaded(object sender, RoutedEventArgs e)
{
try
{
await WebView.EnsureCoreWebView2Async();
CoreWebView2CookieManager manager = WebView.CoreWebView2.CookieManager;
IReadOnlyList<CoreWebView2Cookie> cookies = await manager.GetCookiesAsync("https://user.mihoyo.com");
foreach (CoreWebView2Cookie item in cookies)
{
manager.DeleteCookie(item);
}
CoreWebView2 coreWebView2 = WebView.CoreWebView2;
coreWebView2.Settings.IsGeneralAutofillEnabled = false;
coreWebView2.Settings.IsPasswordAutosaveEnabled = false;
coreWebView2.Navigate("https://user.mihoyo.com/#/login/password");
}
catch (Exception ex)
{
Ioc.Default.GetRequiredService<IInfoBarService>().Error(ex);
}
ISupportLoginByWebView.InitialzeAsync(WebView, infoBarService, CookieHost, NavigateUrl).SafeForget();
}
[Command("HandleCurrentCookieCommand")]
private async Task HandleCurrentCookieAsync()
{
CoreWebView2CookieManager manager = WebView.CoreWebView2.CookieManager;
IReadOnlyList<CoreWebView2Cookie> cookies = await manager.GetCookiesAsync("https://user.mihoyo.com");
IReadOnlyList<CoreWebView2Cookie> cookies = await WebView.CoreWebView2.CookieManager.GetCookiesAsync("https://user.mihoyo.com");
Cookie webCookie = Cookie.FromCoreWebView2Cookies(cookies);
if (!webCookie.TryGetCookieToken(out Cookie? loginTicketCookie))
{
return;
}
Response<ListWrapper<NameToken>> multiTokenResponse = await Ioc.Default
Response<ListWrapper<NameToken>> multiTokenResponse = await serviceProvider
.GetRequiredService<AuthClient>()
.GetMultiTokenByLoginTicketAsync(loginTicketCookie, false)
.ConfigureAwait(false);
@@ -74,10 +64,9 @@ internal sealed partial class LoginMihoyoUserPage : Microsoft.UI.Xaml.Controls.P
}
Dictionary<string, string> multiTokenMap = multiTokenResponse.Data.List.ToDictionary(n => n.Name, n => n.Token);
Cookie stokenV1 = Cookie.FromSToken(loginTicketCookie[Cookie.LOGIN_UID], multiTokenMap[Cookie.STOKEN]);
Response<LoginResult> loginResultResponse = await Ioc.Default
Response<LoginResult> loginResultResponse = await serviceProvider
.GetRequiredService<PassportClient2>()
.LoginBySTokenAsync(stokenV1)
.ConfigureAwait(false);
@@ -88,16 +77,44 @@ internal sealed partial class LoginMihoyoUserPage : Microsoft.UI.Xaml.Controls.P
}
Cookie stokenV2 = Cookie.FromLoginResult(loginResultResponse.Data);
(UserOptionResult result, string nickname) = await Ioc.Default
await ISupportLoginByWebView
.PostHandleCurrentCookieAsync(serviceProvider, stokenV2, false)
.ConfigureAwait(false);
}
}
internal interface ISupportLoginByWebView
{
static async ValueTask InitialzeAsync(WebView2 webView2, IInfoBarService infoBarService, string cookie, string navigate)
{
try
{
await webView2.EnsureCoreWebView2Async();
await webView2.CoreWebView2.DeleteCookiesAsync("https://user.mihoyo.com").ConfigureAwait(true);
webView2.CoreWebView2.DisableDevToolsOnReleaseBuild();
webView2.CoreWebView2.DisableAutoCompletion();
webView2.CoreWebView2.Navigate("https://user.mihoyo.com/#/login/password");
}
catch (Exception ex)
{
infoBarService.Error(ex);
}
}
static async ValueTask PostHandleCurrentCookieAsync(IServiceProvider serviceProvider, Cookie cookie, bool isOversea)
{
(UserOptionResult result, string nickname) = await serviceProvider
.GetRequiredService<IUserService>()
.ProcessInputCookieAsync(stokenV2, false)
.ProcessInputCookieAsync(cookie, false)
.ConfigureAwait(false);
Ioc.Default.GetRequiredService<INavigationService>().GoBack();
serviceProvider.GetRequiredService<INavigationService>().GoBack();
await Ioc.Default
await serviceProvider
.GetRequiredService<ViewModel.User.UserViewModel>()
.HandleUserOptionResultAsync(result, nickname)
.ConfigureAwait(false);
}
}
}

View File

@@ -775,7 +775,8 @@
HorizontalContentAlignment="Stretch"
ItemsPanel="{StaticResource ItemsStackPanelTemplate}"
ItemsSource="{Binding AvatarConstellationInfos}"
SelectionMode="None">
SelectionMode="None"
VirtualizingStackPanel.VirtualizationMode="Standard">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="0,0,0,8" Style="{StaticResource BorderCardStyle}">

View File

@@ -34,7 +34,7 @@
<Grid>
<shvc:WebViewer>
<shvc:WebViewer.SourceProvider>
<shvc:StaticWebview2ViewerSource Source="http://webstatic.mihoyo.com/ys/event/e20200923adopt_calculator/index.html?bbs_presentation_style=fullscreen&amp;bbs_auth_required=true&amp;utm_source=bbs&amp;utm_medium=mys&amp;utm_campaign=GameRecord"/>
<shvc:StaticWebview2ViewerSource ChineseSource="http://webstatic.mihoyo.com/ys/event/e20200923adopt_calculator/index.html?bbs_presentation_style=fullscreen&amp;bbs_auth_required=true&amp;utm_source=bbs&amp;utm_medium=mys&amp;utm_campaign=GameRecord"/>
</shvc:WebViewer.SourceProvider>
</shvc:WebViewer>
</Grid>
@@ -62,7 +62,7 @@
<Grid>
<shvc:WebViewer>
<shvc:WebViewer.SourceProvider>
<shvc:StaticWebview2ViewerSource Source="https://webstatic.mihoyo.com/app/community-game-records/index.html"/>
<shvc:StaticWebview2ViewerSource ChineseSource="https://webstatic.mihoyo.com/app/community-game-records/index.html"/>
</shvc:WebViewer.SourceProvider>
</shvc:WebViewer>
</Grid>

View File

@@ -183,7 +183,38 @@
Width="{StaticResource LargeAppBarButtonWidth}"
MaxWidth="{StaticResource LargeAppBarButtonWidth}"
Margin="2,-4"
AllowFocusOnInteraction="True"
Icon="{shcm:FontIcon Glyph=&#xEC26;}"
Label="{shcm:ResourceString Name=ViewUserCookieOperationGameRecordIndexAction}"
Style="{StaticResource DefaultAppBarButtonStyle}">
<AppBarButton.Flyout>
<Flyout
LightDismissOverlayMode="On"
Placement="Full"
ShouldConstrainToRootBounds="False">
<Flyout.FlyoutPresenterStyle>
<Style BasedOn="{StaticResource DefaultFlyoutPresenterStyle}" TargetType="FlyoutPresenter">
<Setter Property="Padding" Value="0"/>
<Setter Property="CornerRadius" Value="0"/>
</Style>
</Flyout.FlyoutPresenterStyle>
<Grid>
<shvc:WebViewer>
<shvc:WebViewer.SourceProvider>
<shvc:StaticWebview2ViewerSource ChineseSource="https://webstatic.mihoyo.com/app/community-game-records/index.html"/>
</shvc:WebViewer.SourceProvider>
</shvc:WebViewer>
</Grid>
</Flyout>
</AppBarButton.Flyout>
</AppBarButton>
<AppBarButton
Width="{StaticResource LargeAppBarButtonWidth}"
MaxWidth="{StaticResource LargeAppBarButtonWidth}"
Margin="2,-4"
AllowFocusOnInteraction="True"
Command="{Binding ClaimSignInRewardCommand}"
CommandParameter="{Binding Source={RelativeSource Mode=Self}}"
Icon="{shcm:FontIcon Glyph=&#xF133;}"
Label="{shcm:ResourceString Name=ViewUserCookieOperationSignInRewardAction}"
Style="{StaticResource DefaultAppBarButtonStyle}"/>
@@ -198,49 +229,6 @@
<Grid Grid.Column="1" Width="280">
<StackPanel Visibility="{Binding Users.Count, Converter={StaticResource Int32ToVisibilityConverter}}">
<Button
Margin="8"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<mxi:Interaction.Behaviors>
<mxic:EventTriggerBehavior EventName="Click">
<shcb:OpenAttachedFlyoutAction/>
</mxic:EventTriggerBehavior>
</mxi:Interaction.Behaviors>
<Button.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<FontIcon
Grid.Column="0"
Margin="0,0,12,0"
Glyph="&#xEC26;"/>
<TextBlock Grid.Column="1" Text="{shcm:ResourceString Name=ViewUserCookieOperationGameRecordIndexAction}"/>
</Grid>
</Button.Content>
<FlyoutBase.AttachedFlyout>
<Flyout
LightDismissOverlayMode="On"
Placement="Full"
ShouldConstrainToRootBounds="False">
<Flyout.FlyoutPresenterStyle>
<Style BasedOn="{StaticResource DefaultFlyoutPresenterStyle}" TargetType="FlyoutPresenter">
<Setter Property="Padding" Value="0"/>
<Setter Property="CornerRadius" Value="0"/>
</Style>
</Flyout.FlyoutPresenterStyle>
<Grid>
<shvc:WebViewer>
<shvc:WebViewer.SourceProvider>
<shvc:StaticWebview2ViewerSource Source="https://webstatic.mihoyo.com/app/community-game-records/index.html"/>
</shvc:WebViewer.SourceProvider>
</shvc:WebViewer>
</Grid>
</Flyout>
</FlyoutBase.AttachedFlyout>
</Button>
<TextBlock
Margin="10,6,0,6"
Style="{StaticResource BaseTextBlockStyle}"

View File

@@ -1,14 +1,21 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.View.Control;
using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Bridge;
using Snap.Hutao.Web.Request.QueryString;
namespace Snap.Hutao.ViewModel.DailyNote;
internal sealed class DailyNoteWebViewerSource : IWebViewerSource
{
public MiHoYoJSInterface CreateJsInterface(IServiceProvider serviceProvider, CoreWebView2 coreWebView2, UserAndUid userAndUid)
{
return serviceProvider.CreateInstance<MiHoYoJSInterface>(coreWebView2, userAndUid);
}
public string GetSource(UserAndUid userAndUid)
{
QueryString query = userAndUid.Uid.ToQueryString();

View File

@@ -263,15 +263,14 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
{
await taskContext.SwitchToMainThreadAsync();
appOptions.GeetestCustomCompositeUrl = template;
infoBarService.Success("无感验证复合 Url 配置成功");
infoBarService.Success(SH.ViewModelSettingGeetestCustomUrlSucceed);
}
}
[Command("CreateDesktopShortcutCommand")]
private async Task CreateDesktopShortcutForElevatedLaunch()
private async Task CreateDesktopShortcutForElevatedLaunchAsync()
{
bool created = await shellLinkInterop.TryCreateDesktopShoutcutForElevatedLaunchAsync();
if (created)
if (await shellLinkInterop.TryCreateDesktopShoutcutForElevatedLaunchAsync().ConfigureAwait(false))
{
infoBarService.Information(SH.ViewModelSettingActionComplete);
}

View File

@@ -2,6 +2,9 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.Core;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.IO.DataTransfer;
@@ -9,8 +12,10 @@ using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.SignIn;
using Snap.Hutao.Service.User;
using Snap.Hutao.View.Control;
using Snap.Hutao.View.Dialog;
using Snap.Hutao.View.Page;
using Snap.Hutao.Web.Bridge;
using Snap.Hutao.Web.Hoyolab;
using System.Collections.ObjectModel;
using System.Text;
@@ -227,7 +232,7 @@ internal sealed partial class UserViewModel : ObservableObject
}
[Command("ClaimSignInRewardCommand")]
private async Task ClaimSignInRewardAsync()
private async Task ClaimSignInRewardAsync(AppBarButton? appBarButton)
{
if (SelectedUser is not null)
{
@@ -241,6 +246,8 @@ internal sealed partial class UserViewModel : ObservableObject
}
else
{
await taskContext.SwitchToMainThreadAsync();
FlyoutBase.ShowAttachedFlyout(appBarButton);
infoBarService.Warning(message);
}
}
@@ -256,4 +263,19 @@ internal sealed partial class UserViewModel : ObservableObject
{
await Launcher.LaunchUriAsync(new(documentationProvider.GetDocumentation()));
}
}
internal sealed class SignInWebViewerSouce : IWebViewerSource
{
public MiHoYoJSInterface CreateJsInterface(IServiceProvider serviceProvider, CoreWebView2 coreWebView2, UserAndUid userAndUid)
{
return userAndUid.User.IsOversea
? serviceProvider.CreateInstance<SignInJSInterfaceOversea>(coreWebView2, userAndUid)
: serviceProvider.CreateInstance<SignInJSInterface2>(coreWebView2, userAndUid);
}
public string GetSource(UserAndUid userAndUid)
{
throw new NotImplementedException();
}
}

View File

@@ -3,6 +3,7 @@
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.Web.Hoyolab;
using System.Diagnostics;
namespace Snap.Hutao.Web.Bridge;
@@ -12,6 +13,32 @@ namespace Snap.Hutao.Web.Bridge;
[HighQuality]
internal static class CoreWebView2Extension
{
[Conditional("RELEASE")]
public static void DisableDevToolsOnReleaseBuild(this CoreWebView2 webView)
{
CoreWebView2Settings settings = webView.Settings;
settings.AreBrowserAcceleratorKeysEnabled = false;
settings.AreDefaultContextMenusEnabled = false;
settings.AreDevToolsEnabled = false;
}
public static void DisableAutoCompletion(this CoreWebView2 webView)
{
CoreWebView2Settings settings = webView.Settings;
settings.IsGeneralAutofillEnabled = false;
settings.IsPasswordAutosaveEnabled = false;
}
public static async ValueTask DeleteCookiesAsync(this CoreWebView2 webView, string url)
{
CoreWebView2CookieManager manager = webView.CookieManager;
IReadOnlyList<CoreWebView2Cookie> cookies = await manager.GetCookiesAsync(url);
foreach (CoreWebView2Cookie item in cookies)
{
manager.DeleteCookie(item);
}
}
/// <summary>
/// 设置 移动端UA
/// </summary>

View File

@@ -12,10 +12,10 @@ namespace Snap.Hutao.Web.Bridge;
/// 签到页面JS桥
/// </summary>
[HighQuality]
internal sealed class SignInJsInterface : MiHoYoJSInterface
internal sealed class SignInJSInterface2 : MiHoYoJSInterface
{
/// <inheritdoc cref="MiHoYoJSInterface(IServiceProvider, CoreWebView2)"/>
public SignInJsInterface(CoreWebView2 webView, IServiceProvider serviceProvider, UserAndUid userAndUid)
public SignInJSInterface2(CoreWebView2 webView, IServiceProvider serviceProvider, UserAndUid userAndUid)
: base(webView, userAndUid)
{
}

View File

@@ -0,0 +1,13 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
internal sealed class AttendanceReward
{
[JsonPropertyName("status")]
public AttendanceRewardStatus Status { get; set; }
[JsonPropertyName("progress")]
public int Progress { get; set; }
}

View File

@@ -0,0 +1,14 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
internal enum AttendanceRewardStatus
{
AttendanceRewardStatusInvalid,
AttendanceRewardStatusTakenAward,
AttendanceRewardStatusWaitTaken,
AttendanceRewardStatusUnfinished,
AttendanceRewardStatusFinishedNonReward,
AttendanceRewardStatusForbid,
}

View File

@@ -146,4 +146,7 @@ internal sealed class DailyNote : DailyNoteCommon
/// </summary>
[JsonPropertyName("transformer")]
public Transformer Transformer { get; set; } = default!;
[JsonPropertyName("daily_task")]
public DailyTask DailyTask { get; set; } = default!;
}

View File

@@ -0,0 +1,25 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
internal sealed class DailyTask
{
[JsonPropertyName("total_num")]
public int TotalNum { get; set; }
[JsonPropertyName("finished_num")]
public int FinishedNum { get; set; }
[JsonPropertyName("is_extra_task_reward_received")]
public bool IsExtraTaskRewardReceived { get; set; }
[JsonPropertyName("task_rewards")]
public List<TaskReward> TaskRewards { get; set; } = default!;
[JsonPropertyName("attendance_rewards")]
public List<AttendanceReward> AttendanceRewards { get; set; } = default!;
[JsonPropertyName("attendance_visible")]
public bool AttendanceVisible { get; set; }
}

View File

@@ -0,0 +1,10 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
internal sealed class TaskReward
{
[JsonPropertyName("status")]
public TaskRewardStatus Status { get; set; }
}

View File

@@ -0,0 +1,12 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
internal enum TaskRewardStatus
{
TaskRewardStatusInvalid,
TaskRewardStatusTakenAward,
TaskRewardStatusFinished,
TaskRewardStatusUnfinished,
}