mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Announcement Viewer
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.Xaml.Interactivity;
|
||||
|
||||
namespace Snap.Hutao.Control.Behavior;
|
||||
|
||||
/// <summary>
|
||||
/// 打开附着的浮出控件操作
|
||||
/// </summary>
|
||||
internal class OpenAttachedFlyoutAction : DependencyObject, IAction
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public object Execute(object sender, object parameter)
|
||||
{
|
||||
FlyoutBase.ShowAttachedFlyout(sender as FrameworkElement);
|
||||
return null!;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
<Page
|
||||
x:Class="Snap.Hutao.View.Page.AnnouncementContentPage"
|
||||
<UserControl
|
||||
x:Class="Snap.Hutao.View.Control.AnnouncementContentViewer"
|
||||
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"
|
||||
ActualThemeChanged="PageActualThemeChanged"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
Loaded="OnLoaded"
|
||||
mc:Ignorable="d">
|
||||
<Page.Transitions>
|
||||
<UserControl.Transitions>
|
||||
<TransitionCollection>
|
||||
<NavigationThemeTransition>
|
||||
<DrillInNavigationTransitionInfo/>
|
||||
</NavigationThemeTransition>
|
||||
<EntranceThemeTransition/>
|
||||
</TransitionCollection>
|
||||
</Page.Transitions>
|
||||
</UserControl.Transitions>
|
||||
<WebView2
|
||||
x:Name="WebView"
|
||||
Margin="0,0,0,0"
|
||||
DefaultBackgroundColor="Transparent"
|
||||
IsRightTapEnabled="False"/>
|
||||
</Page>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// 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.Core;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||
using Windows.System;
|
||||
|
||||
namespace Snap.Hutao.View.Control;
|
||||
|
||||
/// <summary>
|
||||
/// 公告内容页面
|
||||
/// </summary>
|
||||
public sealed partial class AnnouncementContentViewer : Microsoft.UI.Xaml.Controls.UserControl
|
||||
{
|
||||
// apply in dark mode, Dark theme
|
||||
private const string LightColor1 = "color:rgba(255,255,255,1)";
|
||||
private const string LightColor2 = "color:rgba(238,238,238,1)";
|
||||
private const string LightColor3 = "color:rgba(204,204,204,1)";
|
||||
private const string LightColor4 = "color:rgba(198,196,191,1)";
|
||||
private const string LightColor5 = "color:rgba(170,170,170,1)";
|
||||
private const string LightAccentColor1 = "background-color: rgb(0,40,70)";
|
||||
private const string LightAccentColor2 = "background-color: rgb(1,40,70)";
|
||||
|
||||
// find in content, Light theme
|
||||
private const string DarkColor1 = "color:rgba(0,0,0,1)";
|
||||
private const string DarkColor2 = "color:rgba(17,17,17,1)";
|
||||
private const string DarkColor3 = "color:rgba(51,51,51,1)";
|
||||
private const string DarkColor4 = "color:rgba(57,59,64,1)";
|
||||
private const string DarkColor5 = "color:rgba(85,85,85,1)";
|
||||
private const string DarkAccentColor1 = "background-color: rgb(255, 215, 185);";
|
||||
private const string DarkAccentColor2 = "background-color: rgb(254, 245, 231);";
|
||||
|
||||
// support click open browser.
|
||||
private const string MihoyoSDKDefinition = """
|
||||
window.miHoYoGameJSSDK = {
|
||||
openInBrowser: function(url){ window.chrome.webview.postMessage(url); },
|
||||
openInWebview: function(url){ location.href = url }
|
||||
}
|
||||
""";
|
||||
|
||||
private static readonly DependencyProperty AnnouncementProperty = Property<AnnouncementContentViewer>.Depend<Announcement>(nameof(Announcement));
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的公告窗体
|
||||
/// </summary>
|
||||
public AnnouncementContentViewer()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 目标公告
|
||||
/// </summary>
|
||||
public Announcement Announcement
|
||||
{
|
||||
get { return (Announcement)GetValue(AnnouncementProperty); }
|
||||
set { SetValue(AnnouncementProperty, value); }
|
||||
}
|
||||
|
||||
private static string? GenerateHtml(Announcement? announcement, ElementTheme theme)
|
||||
{
|
||||
if (announcement == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string content = announcement.Content;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
content = content
|
||||
.Replace(@"style=""vertical-align:middle;""", string.Empty)
|
||||
.Replace(@"style=""border:none;vertical-align:middle;""", string.Empty);
|
||||
|
||||
bool isDarkMode = ThemeHelper.IsDarkMode(theme);
|
||||
|
||||
if (isDarkMode)
|
||||
{
|
||||
content = content
|
||||
.Replace(DarkColor5, LightColor5)
|
||||
.Replace(DarkColor4, LightColor4)
|
||||
.Replace(DarkColor3, LightColor3)
|
||||
.Replace(DarkColor2, LightColor2)
|
||||
.Replace(DarkColor1, LightColor1)
|
||||
.Replace(DarkAccentColor1, LightAccentColor1)
|
||||
.Replace(DarkAccentColor2, LightAccentColor2);
|
||||
}
|
||||
|
||||
string document = $$"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
body::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
img{
|
||||
border: none;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="{{(isDarkMode ? LightColor1 : DarkColor1)}}; background-color: transparent;">
|
||||
<h3>{{announcement.Title}}</h3>
|
||||
<img src="{{announcement.Banner}}" />
|
||||
<br>
|
||||
{{content}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
""";
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
private async Task LoadAnnouncementAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await WebView.EnsureCoreWebView2Async();
|
||||
|
||||
CoreWebView2Settings settings = WebView.CoreWebView2.Settings;
|
||||
settings.AreBrowserAcceleratorKeysEnabled = false;
|
||||
settings.AreDefaultContextMenusEnabled = false;
|
||||
settings.AreDevToolsEnabled = false;
|
||||
WebView.CoreWebView2.WebMessageReceived += OnWebMessageReceived;
|
||||
|
||||
await WebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(MihoyoSDKDefinition);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WebView.NavigateToString(GenerateHtml(Announcement, ActualTheme));
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
LoadAnnouncementAsync().SafeForget();
|
||||
}
|
||||
|
||||
private void OnWebMessageReceived(CoreWebView2 coreWebView2, CoreWebView2WebMessageReceivedEventArgs args)
|
||||
{
|
||||
string url = args.TryGetWebMessageAsString();
|
||||
|
||||
if (Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out Uri? uri))
|
||||
{
|
||||
Launcher.LaunchUriAsync(uri).AsTask().SafeForget();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
using Windows.System;
|
||||
|
||||
namespace Snap.Hutao.View.Page;
|
||||
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class AnnouncementContentPage : Microsoft.UI.Xaml.Controls.Page
|
||||
{
|
||||
// apply in dark mode
|
||||
private const string LightColor1 = "color:rgba(255,255,255,1)";
|
||||
private const string LightColor2 = "color:rgba(238,238,238,1)";
|
||||
private const string LightColor3 = "color:rgba(204,204,204,1)";
|
||||
private const string LightColor4 = "color:rgba(198,196,191,1)";
|
||||
private const string LightColor5 = "color:rgba(170,170,170,1)";
|
||||
private const string LightAccentColor1 = "background-color: rgb(0,40,70)";
|
||||
private const string LightAccentColor2 = "background-color: rgb(1,40,70)";
|
||||
|
||||
// find in content
|
||||
private const string DarkColor1 = "color:rgba(0,0,0,1)";
|
||||
private const string DarkColor2 = "color:rgba(17,17,17,1)";
|
||||
private const string DarkColor3 = "color:rgba(51,51,51,1)";
|
||||
private const string DarkColor4 = "color:rgba(57,59,64,1)";
|
||||
private const string DarkColor5 = "color:rgba(85,85,85,1)";
|
||||
private const string DarkAccentColor1 = "background-color: rgb(255, 215, 185);";
|
||||
private const string DarkAccentColor2 = "background-color: rgb(254, 245, 231);";
|
||||
|
||||
// support click open browser.
|
||||
private const string MihoyoSDKDefinition = """
|
||||
window.miHoYoGameJSSDK = {
|
||||
openInBrowser: function(url){ window.chrome.webview.postMessage(url); },
|
||||
openInWebview: function(url){ location.href = url }
|
||||
}
|
||||
""";
|
||||
|
||||
private string? targetContent;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的公告窗体
|
||||
/// </summary>
|
||||
public AnnouncementContentPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if (e.Parameter is INavigationData extra)
|
||||
{
|
||||
targetContent = extra.Data as string;
|
||||
LoadAnnouncementAsync(extra).SafeForget();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedFrom(e);
|
||||
|
||||
// WebView.CoreWebView2.Environment.Exit();
|
||||
}
|
||||
|
||||
private static string? ReplaceForeground(string? rawContent, ElementTheme theme)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(rawContent))
|
||||
{
|
||||
return rawContent;
|
||||
}
|
||||
|
||||
bool isDarkMode = ThemeHelper.IsDarkMode(theme);
|
||||
|
||||
if (isDarkMode)
|
||||
{
|
||||
rawContent = rawContent
|
||||
.Replace(DarkColor5, LightColor5)
|
||||
.Replace(DarkColor4, LightColor4)
|
||||
.Replace(DarkColor3, LightColor3)
|
||||
.Replace(DarkColor2, LightColor2)
|
||||
.Replace(DarkAccentColor1, LightAccentColor1)
|
||||
.Replace(DarkAccentColor2, LightAccentColor2);
|
||||
}
|
||||
|
||||
// wrap a default color body around
|
||||
return $@"<body style=""{(isDarkMode ? LightColor1 : DarkColor1)}"">{rawContent}</body>";
|
||||
}
|
||||
|
||||
private async Task LoadAnnouncementAsync(INavigationData data)
|
||||
{
|
||||
try
|
||||
{
|
||||
await WebView.EnsureCoreWebView2Async();
|
||||
|
||||
WebView.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
|
||||
WebView.CoreWebView2.WebMessageReceived += OnWebMessageReceived;
|
||||
|
||||
await WebView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(MihoyoSDKDefinition);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
data.NotifyNavigationException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
WebView.NavigateToString(ReplaceForeground(targetContent, ActualTheme));
|
||||
data.NotifyNavigationCompleted();
|
||||
}
|
||||
|
||||
private void PageActualThemeChanged(FrameworkElement sender, object args)
|
||||
{
|
||||
WebView.NavigateToString(ReplaceForeground(targetContent, ActualTheme));
|
||||
}
|
||||
|
||||
[SuppressMessage("", "VSTHRD100")]
|
||||
private async void OnWebMessageReceived(CoreWebView2 coreWebView2, CoreWebView2WebMessageReceivedEventArgs args)
|
||||
{
|
||||
string url = args.TryGetWebMessageAsString();
|
||||
|
||||
if (Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out Uri? uri))
|
||||
{
|
||||
await Launcher.LaunchUriAsync(uri).AsTask().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||
xmlns:shci="using:Snap.Hutao.Control.Image"
|
||||
xmlns:shv="using:Snap.Hutao.ViewModel"
|
||||
xmlns:shvc="using:Snap.Hutao.View.Control"
|
||||
d:DataContext="{d:DesignInstance shv:AnnouncementViewModel}"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
mc:Ignorable="d">
|
||||
@@ -122,9 +123,21 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
<FlyoutBase.AttachedFlyout>
|
||||
<Flyout LightDismissOverlayMode="On" Placement="Full">
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style BasedOn="{StaticResource DefaultFlyoutPresenterStyle}" TargetType="FlyoutPresenter">
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="CornerRadius" Value="0"/>
|
||||
<Setter Property="MaxWidth" Value="560"/>
|
||||
</Style>
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<shvc:AnnouncementContentViewer Announcement="{Binding}"/>
|
||||
</Flyout>
|
||||
</FlyoutBase.AttachedFlyout>
|
||||
<mxi:Interaction.Behaviors>
|
||||
<mxic:EventTriggerBehavior EventName="Tapped">
|
||||
<mxic:InvokeCommandAction Command="{Binding DataContext.OpenAnnouncementUICommand, Source={StaticResource BindingProxy}}" CommandParameter="{Binding Content}"/>
|
||||
<shcb:OpenAttachedFlyoutAction/>
|
||||
</mxic:EventTriggerBehavior>
|
||||
<mxic:EventTriggerBehavior EventName="PointerEntered">
|
||||
<cwub:StartAnimationAction Animation="{Binding ElementName=ImageZoomInAnimation}"/>
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style BasedOn="{StaticResource DefaultFlyoutPresenterStyle}" TargetType="FlyoutPresenter">
|
||||
<Setter Property="Padding" Value="0,2,0,2"/>
|
||||
<Setter Property="Background" Value="{ThemeResource FlyoutPresenterBackground}"/>
|
||||
</Style>
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<StackPanel>
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
using Snap.Hutao.View.Page;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||
|
||||
namespace Snap.Hutao.ViewModel;
|
||||
@@ -29,7 +26,6 @@ internal class AnnouncementViewModel : Abstraction.ViewModel
|
||||
this.announcementService = announcementService;
|
||||
|
||||
OpenUICommand = new AsyncRelayCommand(OpenUIAsync);
|
||||
OpenAnnouncementUICommand = new RelayCommand<string>(OpenAnnouncementUI);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -46,11 +42,6 @@ internal class AnnouncementViewModel : Abstraction.ViewModel
|
||||
/// </summary>
|
||||
public ICommand OpenUICommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 打开公告UI触发的命令
|
||||
/// </summary>
|
||||
public ICommand OpenAnnouncementUICommand { get; }
|
||||
|
||||
private async Task OpenUIAsync()
|
||||
{
|
||||
try
|
||||
@@ -61,18 +52,4 @@ internal class AnnouncementViewModel : Abstraction.ViewModel
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenAnnouncementUI(string? content)
|
||||
{
|
||||
if (WebView2Helper.IsSupported)
|
||||
{
|
||||
INavigationService navigationService = Ioc.Default.GetRequiredService<INavigationService>();
|
||||
navigationService.Navigate<AnnouncementContentPage>(data: new NavigationExtra(content));
|
||||
}
|
||||
else
|
||||
{
|
||||
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
||||
infoBarService.Warning("尚未安装 WebView2 Runtime");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user