mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
caching announcements
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
x:Class="Snap.Hutao.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls">
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:system="using:System">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
@@ -16,7 +17,6 @@
|
||||
<StaticResource x:Key="WindowCaptionForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
|
||||
<StaticResource x:Key="WindowCaptionForegroundDisabled" ResourceKey="SystemControlForegroundBaseHighBrush" />
|
||||
|
||||
<CornerRadius x:Key="WindowCornerRadius">8</CornerRadius>
|
||||
<CornerRadius x:Key="CompatCornerRadius">4</CornerRadius>
|
||||
<CornerRadius x:Key="CompatCornerRadiusTop">4,4,0,0</CornerRadius>
|
||||
<CornerRadius x:Key="CompatCornerRadiusRight">0,4,4,0</CornerRadius>
|
||||
|
||||
@@ -39,6 +39,7 @@ public partial class App : Application
|
||||
{
|
||||
IServiceProvider services = new ServiceCollection()
|
||||
.AddLogging(builder => builder.AddDebug())
|
||||
.AddMemoryCache()
|
||||
.AddDatebase()
|
||||
.AddHttpClients()
|
||||
.AddDefaultJsonSerializerOptions()
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Snap.Hutao.Core.Abstraction;
|
||||
/// <summary>
|
||||
/// 可异步初始化
|
||||
/// </summary>
|
||||
internal interface IAsyncInitializable
|
||||
internal interface ISupportAsyncInitialization
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否已经初始化完成
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Abstraction;
|
||||
|
||||
/// <summary>
|
||||
/// 表示支持验证
|
||||
/// </summary>
|
||||
internal interface ISupportValidation
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证
|
||||
/// </summary>
|
||||
/// <returns>当前数据是否有效</returns>
|
||||
public bool Validate();
|
||||
}
|
||||
@@ -9,21 +9,21 @@ namespace Snap.Hutao.Core.Json.Converter;
|
||||
/// <summary>
|
||||
/// 实现日期的转换
|
||||
/// </summary>
|
||||
internal class DateTimeConverter : JsonConverter<DateTime>
|
||||
internal class DateTimeOffsetConverter : JsonConverter<DateTimeOffset>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.GetString() is string dataTimeString)
|
||||
{
|
||||
return DateTime.Parse(dataTimeString);
|
||||
return DateTimeOffset.Parse(dataTimeString);
|
||||
}
|
||||
|
||||
return default(DateTime);
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
@@ -9,11 +9,15 @@ namespace Snap.Hutao.Core;
|
||||
/// 检测 WebView2运行时 是否存在
|
||||
/// 不再使用注册表检查方式
|
||||
/// </summary>
|
||||
internal static class WebView2Helper
|
||||
internal class WebView2Helper
|
||||
{
|
||||
private static bool hasEverDetected = false;
|
||||
private static bool isSupported = false;
|
||||
|
||||
private WebView2Helper()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测 WebView2 是否存在
|
||||
/// </summary>
|
||||
|
||||
@@ -9,13 +9,14 @@
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="28"/>
|
||||
<RowDefinition Height="48.8"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid
|
||||
x:Name="TitleBarGrid"
|
||||
Background="Transparent"/>
|
||||
<view:TitleView
|
||||
x:Name="TitleBarView">
|
||||
|
||||
</view:TitleView>
|
||||
|
||||
<view:MainView Grid.Row="1"/>
|
||||
</Grid>
|
||||
|
||||
@@ -18,6 +18,6 @@ public sealed partial class MainWindow : Window
|
||||
{
|
||||
InitializeComponent();
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
SetTitleBar(TitleBarGrid);
|
||||
SetTitleBar(TitleBarView.DragableArea);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ namespace Snap.Hutao.Service.Abstraction;
|
||||
public interface IAnnouncementService
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步获取游戏公告与活动
|
||||
/// 异步获取游戏公告与活动,通常会进行缓存
|
||||
/// </summary>
|
||||
/// <param name="openAnnouncementUICommand">打开公告时触发的命令</param>
|
||||
/// <param name="cancellationToken">取消令牌</param>
|
||||
/// <returns>公告包装器</returns>
|
||||
Task<AnnouncementWrapper> GetAnnouncementsAsync(ICommand openAnnouncementUICommand, CancellationToken cancellationToken = default);
|
||||
ValueTask<AnnouncementWrapper> GetAnnouncementsAsync(ICommand openAnnouncementUICommand, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||
using System.Collections.Generic;
|
||||
@@ -14,20 +15,31 @@ namespace Snap.Hutao.Service;
|
||||
[Injection(InjectAs.Transient, typeof(IAnnouncementService))]
|
||||
internal class AnnouncementService : IAnnouncementService
|
||||
{
|
||||
private const string CacheKey = $"{nameof(IAnnouncementService)}.Cache.{nameof(AnnouncementWrapper)}";
|
||||
|
||||
private readonly AnnouncementClient announcementClient;
|
||||
private readonly IMemoryCache memoryCache;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的公告服务
|
||||
/// </summary>
|
||||
/// <param name="announcementClient">公告提供器</param>
|
||||
public AnnouncementService(AnnouncementClient announcementClient)
|
||||
/// <param name="memoryCache">缓存</param>
|
||||
public AnnouncementService(AnnouncementClient announcementClient, IMemoryCache memoryCache)
|
||||
{
|
||||
this.announcementClient = announcementClient;
|
||||
this.memoryCache = memoryCache;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<AnnouncementWrapper> GetAnnouncementsAsync(ICommand openAnnouncementUICommand, CancellationToken cancellationToken = default)
|
||||
public async ValueTask<AnnouncementWrapper> GetAnnouncementsAsync(ICommand openAnnouncementUICommand, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 缓存中存在记录,直接返回
|
||||
if (memoryCache.TryGetValue(CacheKey, out object? cache))
|
||||
{
|
||||
return Must.NotNull((cache as AnnouncementWrapper)!);
|
||||
}
|
||||
|
||||
AnnouncementWrapper? wrapper = await announcementClient
|
||||
.GetAnnouncementsAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
@@ -46,7 +58,7 @@ internal class AnnouncementService : IAnnouncementService
|
||||
// 将公告内容联入公告列表
|
||||
JoinAnnouncements(openAnnouncementUICommand, contentMap, wrapper.List);
|
||||
|
||||
return wrapper;
|
||||
return memoryCache.Set(CacheKey, wrapper, TimeSpan.FromMinutes(30));
|
||||
}
|
||||
|
||||
private static void JoinAnnouncements(ICommand openAnnouncementUICommand, Dictionary<int, string> contentMap, List<AnnouncementListWrapper> announcementListWrappers)
|
||||
|
||||
@@ -103,11 +103,6 @@ internal class NavigationService : INavigationService
|
||||
bool navigated = false;
|
||||
try
|
||||
{
|
||||
if (data != null && data.GetType() != typeof(NavigationExtra))
|
||||
{
|
||||
data = new NavigationExtra(data);
|
||||
}
|
||||
|
||||
navigated = Frame?.Navigate(pageType, data) ?? false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
<None Remove="View\Page\AnnouncementContentPage.xaml" />
|
||||
<None Remove="View\Page\AnnouncementPage.xaml" />
|
||||
<None Remove="View\Page\SettingPage.xaml" />
|
||||
<None Remove="View\TitleView.xaml" />
|
||||
<None Remove="View\UserView.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="stylecop.json" />
|
||||
@@ -55,6 +57,7 @@
|
||||
<PackageReference Include="Microsoft.AppCenter.Analytics" Version="4.5.1" />
|
||||
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.5.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.2.32" />
|
||||
@@ -82,6 +85,16 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\SettingsUI\SettingsUI.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\UserView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\TitleView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Page\AnnouncementContentPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Snap.Hutao.Core;
|
||||
|
||||
namespace Snap.Hutao.View.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// This class converts a boolean value into an other object.
|
||||
/// Can be used to convert true/false to visibility, a couple of colors, couple of images, etc.
|
||||
/// </summary>
|
||||
public class BoolToObjectConverter : DependencyObject, IValueConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="TrueValue"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty TrueValueProperty = Property<BoolToObjectConverter>.Depend<object>(nameof(TrueValue));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="FalseValue"/> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty FalseValueProperty = Property<BoolToObjectConverter>.Depend<object>(nameof(FalseValue));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value to be returned when the boolean is true
|
||||
/// </summary>
|
||||
public object TrueValue
|
||||
{
|
||||
get => GetValue(TrueValueProperty);
|
||||
set => SetValue(TrueValueProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value to be returned when the boolean is false
|
||||
/// </summary>
|
||||
public object FalseValue
|
||||
{
|
||||
get => GetValue(FalseValueProperty);
|
||||
set => SetValue(FalseValueProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a boolean value to an other object.
|
||||
/// </summary>
|
||||
/// <param name="value">The source data being passed to the target.</param>
|
||||
/// <param name="targetType">The type of the target property, as a type reference.</param>
|
||||
/// <param name="parameter">An optional parameter to be used to invert the converter logic.</param>
|
||||
/// <param name="language">The language of the conversion.</param>
|
||||
/// <returns>The value to be passed to the target dependency property.</returns>
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
bool boolValue = value is bool valid && valid;
|
||||
|
||||
// Negate if needed
|
||||
if (ConvertHelper.TryParseBool(parameter))
|
||||
{
|
||||
boolValue = !boolValue;
|
||||
}
|
||||
|
||||
return ConvertHelper.Convert(boolValue ? TrueValue : FalseValue, targetType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert back the value to a boolean
|
||||
/// </summary>
|
||||
/// <remarks>If the <paramref name="value"/> parameter is a reference type, <see cref="TrueValue"/> must match its reference to return true.</remarks>
|
||||
/// <param name="value">The target data being passed to the source.</param>
|
||||
/// <param name="targetType">The type of the target property, as a type reference (System.Type for Microsoft .NET, a TypeName helper struct for Visual C++ component extensions (C++/CX)).</param>
|
||||
/// <param name="parameter">An optional parameter to be used to invert the converter logic.</param>
|
||||
/// <param name="language">The language of the conversion.</param>
|
||||
/// <returns>The value to be passed to the source object.</returns>
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
bool result = Equals(value, ConvertHelper.Convert(TrueValue, value.GetType()));
|
||||
|
||||
if (ConvertHelper.TryParseBool(parameter))
|
||||
{
|
||||
result = !result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Snap.Hutao.View.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// This class converts a boolean value into a Visibility enumeration.
|
||||
/// </summary>
|
||||
public class BoolToVisibilityConverter : BoolToObjectConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BoolToVisibilityConverter"/> class.
|
||||
/// </summary>
|
||||
public BoolToVisibilityConverter()
|
||||
{
|
||||
TrueValue = Visibility.Visible;
|
||||
FalseValue = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.UI.Converters;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Snap.Hutao.View.Converter;
|
||||
@@ -11,7 +12,7 @@ namespace Snap.Hutao.View.Converter;
|
||||
public class BoolToVisibilityRevertConverter : BoolToObjectConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BoolToVisibilityConverter"/> class.
|
||||
/// Initializes a new instance of the <see cref="BoolToVisibilityRevertConverter"/> class.
|
||||
/// </summary>
|
||||
public BoolToVisibilityRevertConverter()
|
||||
{
|
||||
|
||||
@@ -6,17 +6,24 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:helper="using:Snap.Hutao.View.Helper"
|
||||
xmlns:page="using:Snap.Hutao.View.Page"
|
||||
xmlns:view="using:Snap.Hutao.View"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<!-- x:Bind can't get property update here seems like a WinUI 3 bug-->
|
||||
<NavigationView
|
||||
x:Name="NavView"
|
||||
CompactPaneLength="48"
|
||||
OpenPaneLength="172"
|
||||
OpenPaneLength="200"
|
||||
CompactModeThresholdWidth="128"
|
||||
ExpandedModeThresholdWidth="720"
|
||||
IsPaneOpen="True"
|
||||
IsBackEnabled="{Binding ElementName=ContentFrame,Path=CanGoBack}">
|
||||
<!-- x:Bind can't get property update here seems like a WinUI 3 bug-->
|
||||
|
||||
<NavigationView.PaneCustomContent>
|
||||
<view:UserView IsExpanded="{Binding ElementName=NavView,Path=IsPaneOpen}"/>
|
||||
</NavigationView.PaneCustomContent>
|
||||
|
||||
<NavigationView.MenuItems>
|
||||
|
||||
<NavigationViewItem Content="活动" helper:NavHelper.NavigateTo="page:AnnouncementPage">
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Service.Abstraction.Navigation;
|
||||
|
||||
namespace Snap.Hutao.View.Page;
|
||||
|
||||
@@ -33,11 +34,15 @@ openInWebview: function(url){ location.href = url }}";
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
targetContent = e.Parameter as string;
|
||||
LoadAnnouncementAsync().Forget();
|
||||
|
||||
if (e.Parameter is NavigationExtra extra)
|
||||
{
|
||||
targetContent = extra.Data as string;
|
||||
LoadAnnouncementAsync(extra).Forget();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadAnnouncementAsync()
|
||||
private async Task LoadAnnouncementAsync(NavigationExtra extra)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -52,5 +57,6 @@ openInWebview: function(url){ location.href = url }}";
|
||||
}
|
||||
|
||||
WebView.NavigateToString(targetContent);
|
||||
extra.NavigationCompletedTaskCompletionSource.TrySetResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:cwui="using:CommunityToolkit.WinUI.UI"
|
||||
xmlns:cwu="using:CommunityToolkit.WinUI.UI"
|
||||
xmlns:cwua="using:CommunityToolkit.WinUI.UI.Animations"
|
||||
xmlns:cwub="using:CommunityToolkit.WinUI.UI.Behaviors"
|
||||
xmlns:cwuc="using:CommunityToolkit.WinUI.UI.Controls"
|
||||
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:mxim="using:Microsoft.Xaml.Interactions.Media"
|
||||
xmlns:cwucont="using:CommunityToolkit.WinUI.UI.Controls"
|
||||
xmlns:cwuconv="using:CommunityToolkit.WinUI.UI.Converters"
|
||||
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:shca="using:Snap.Hutao.Control.Animation"
|
||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||
xmlns:shcc="using:Snap.Hutao.Control.Cancellable"
|
||||
xmlns:shvc="using:Snap.Hutao.View.Converter" xmlns:shca="using:Snap.Hutao.Control.Animation"
|
||||
xmlns:shvc="using:Snap.Hutao.View.Converter"
|
||||
mc:Ignorable="d"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
@@ -23,7 +24,7 @@
|
||||
</mxic:EventTriggerBehavior>
|
||||
</mxi:Interaction.Behaviors>
|
||||
<shcc:CancellablePage.Resources>
|
||||
<shvc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
|
||||
<cwuconv:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
|
||||
<shvc:BoolToVisibilityRevertConverter x:Key="BoolToVisibilityRevertConverter"/>
|
||||
</shcc:CancellablePage.Resources>
|
||||
<Grid>
|
||||
@@ -35,7 +36,7 @@
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding Announcement.List}"
|
||||
Padding="0"
|
||||
Margin="12,12,0,0">
|
||||
Margin="12,12,0,-12">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel>
|
||||
@@ -43,24 +44,24 @@
|
||||
Text="{Binding TypeLabel}"
|
||||
Margin="0,0,0,12"
|
||||
Style="{StaticResource TitleTextBlockStyle}"/>
|
||||
<cwuc:AdaptiveGridView
|
||||
<cwucont:AdaptiveGridView
|
||||
cwua:ItemsReorderAnimation.Duration="0:0:0.06"
|
||||
SelectionMode="None"
|
||||
DesiredWidth="320"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding List}"
|
||||
Margin="0,0,0,0">
|
||||
<cwuc:AdaptiveGridView.ItemContainerStyle>
|
||||
<cwucont:AdaptiveGridView.ItemContainerStyle>
|
||||
<Style BasedOn="{StaticResource DefaultGridViewItemStyle}" TargetType="GridViewItem">
|
||||
<Setter Property="Margin" Value="0,0,12,12"/>
|
||||
</Style>
|
||||
</cwuc:AdaptiveGridView.ItemContainerStyle>
|
||||
<cwuc:AdaptiveGridView.ItemTemplate>
|
||||
</cwucont:AdaptiveGridView.ItemContainerStyle>
|
||||
<cwucont:AdaptiveGridView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
CornerRadius="{StaticResource CompatCornerRadius}"
|
||||
Background="{ThemeResource SystemControlPageBackgroundAltHighBrush}"
|
||||
cwui:UIElementExtensions.ClipToBounds="True">
|
||||
cwu:UIElementExtensions.ClipToBounds="True">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
@@ -68,10 +69,10 @@
|
||||
</Grid.RowDefinitions>
|
||||
<!--Image Layer-->
|
||||
<Border
|
||||
cwui:UIElementExtensions.ClipToBounds="True">
|
||||
cwu:UIElementExtensions.ClipToBounds="True">
|
||||
<Border
|
||||
VerticalAlignment="Top"
|
||||
cwui:VisualExtensions.NormalizedCenterPoint="0.5">
|
||||
cwu:VisualExtensions.NormalizedCenterPoint="0.5">
|
||||
<mxi:Interaction.Behaviors>
|
||||
<shcb:AutoHeightBehavior TargetWidth="1080" TargetHeight="390"/>
|
||||
</mxi:Interaction.Behaviors>
|
||||
@@ -111,33 +112,14 @@
|
||||
Grid.Row="1"
|
||||
CornerRadius="{StaticResource CompatCornerRadiusBottom}">
|
||||
<StackPanel Margin="4" VerticalAlignment="Bottom">
|
||||
<Grid Margin="4,6,0,0" HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Margin="4,6,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding Subtitle}"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
TextWrapping="NoWrap"
|
||||
TextTrimming="WordEllipsis"/>
|
||||
|
||||
<TextBlock
|
||||
Text="{Binding Subtitle}"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
TextWrapping="NoWrap"
|
||||
TextTrimming="WordEllipsis"/>
|
||||
|
||||
<Button
|
||||
x:Name="OpenAnnouncementButton"
|
||||
Content=""
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Stretch"
|
||||
Grid.Column="1"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
BorderBrush="{x:Null}"
|
||||
Visibility="Collapsed"
|
||||
Command="{Binding OpenAnnouncementUICommand}"
|
||||
CommandParameter="{Binding Content}"/>
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Text="{Binding Title}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
@@ -145,6 +127,7 @@
|
||||
TextTrimming="WordEllipsis"
|
||||
Margin="4,6,0,0"
|
||||
Opacity="0.6"/>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
@@ -167,49 +150,26 @@
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding TimeDescription}" />
|
||||
</Grid>
|
||||
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
<Border.Resources>
|
||||
<Storyboard x:Name="OpenAnnouncementButtonVisibleStoryboard">
|
||||
<ObjectAnimationUsingKeyFrames
|
||||
Storyboard.TargetName="OpenAnnouncementButton"
|
||||
Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
|
||||
<Storyboard x:Name="OpenAnnouncementButtonCollapsedStoryboard">
|
||||
<ObjectAnimationUsingKeyFrames
|
||||
Storyboard.TargetName="OpenAnnouncementButton"
|
||||
Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</Border.Resources>
|
||||
<mxi:Interaction.Behaviors>
|
||||
<mxic:EventTriggerBehavior EventName="Tapped">
|
||||
<mxic:InvokeCommandAction
|
||||
Command="{Binding OpenAnnouncementUICommand}"
|
||||
CommandParameter="{Binding Content}"/>
|
||||
</mxic:EventTriggerBehavior>
|
||||
<mxic:EventTriggerBehavior EventName="PointerEntered">
|
||||
<cwub:StartAnimationAction Animation="{Binding ElementName=ImageZoomInAnimation}" />
|
||||
<mxim:ControlStoryboardAction Storyboard="{StaticResource OpenAnnouncementButtonVisibleStoryboard}"/>
|
||||
</mxic:EventTriggerBehavior>
|
||||
<mxic:EventTriggerBehavior EventName="PointerExited">
|
||||
<cwub:StartAnimationAction Animation="{Binding ElementName=ImageZoomOutAnimation}" />
|
||||
<mxim:ControlStoryboardAction Storyboard="{StaticResource OpenAnnouncementButtonCollapsedStoryboard}"/>
|
||||
</mxic:EventTriggerBehavior>
|
||||
</mxi:Interaction.Behaviors>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</cwuc:AdaptiveGridView.ItemTemplate>
|
||||
</cwuc:AdaptiveGridView>
|
||||
</cwucont:AdaptiveGridView.ItemTemplate>
|
||||
</cwucont:AdaptiveGridView>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
|
||||
18
src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml
Normal file
18
src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml
Normal file
@@ -0,0 +1,18 @@
|
||||
<UserControl
|
||||
x:Class="Snap.Hutao.View.TitleView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Snap.Hutao.View"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Height="48.8">
|
||||
<Grid x:Name="DragableGrid">
|
||||
<TextBlock
|
||||
Text="胡桃"
|
||||
TextWrapping="NoWrap"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
VerticalAlignment="Center"
|
||||
Margin="12,0,0,0"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
29
src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml.cs
Normal file
29
src/Snap.Hutao/Snap.Hutao/View/TitleView.xaml.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Snap.Hutao.View;
|
||||
|
||||
/// <summary>
|
||||
/// 标题视图
|
||||
/// </summary>
|
||||
public sealed partial class TitleView : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的标题视图
|
||||
/// </summary>
|
||||
public TitleView()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取可拖动区域
|
||||
/// </summary>
|
||||
public UIElement DragableArea
|
||||
{
|
||||
get => DragableGrid;
|
||||
}
|
||||
}
|
||||
74
src/Snap.Hutao/Snap.Hutao/View/UserView.xaml
Normal file
74
src/Snap.Hutao/Snap.Hutao/View/UserView.xaml
Normal file
@@ -0,0 +1,74 @@
|
||||
<UserControl
|
||||
x:Class="Snap.Hutao.View.UserView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Snap.Hutao.View"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<Grid Height="48">
|
||||
<Grid Height="48">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<PersonPicture
|
||||
ProfilePicture="https://upload-bbs.mihoyo.com/game_record/genshin/character_icon/UI_AvatarIcon_Hutao.png"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="4,0,4,0"
|
||||
Height="40"
|
||||
Initials="LB"/>
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,0,2"
|
||||
Grid.Column="1"
|
||||
Text="胡桃胡桃胡桃胡桃胡桃胡桃"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
<Button
|
||||
Background="Transparent"
|
||||
BorderBrush="{x:Null}"
|
||||
Height="38.4"
|
||||
Content=""
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
Grid.Column="2"
|
||||
Margin="4">
|
||||
<Button.Flyout>
|
||||
<Flyout
|
||||
Placement="BottomEdgeAlignedRight"
|
||||
LightDismissOverlayMode="On">
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style
|
||||
TargetType="FlyoutPresenter"
|
||||
BasedOn="{StaticResource DefaultFlyoutPresenterStyle}">
|
||||
<Setter Property="Padding" Value="0,2,0,2"/>
|
||||
</Style>
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<StackPanel Width="192">
|
||||
<CommandBar DefaultLabelPosition="Right">
|
||||
<AppBarButton Icon="Add" Label="添加新用户"/>
|
||||
</CommandBar>
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
Margin="4"
|
||||
CanReorderItems="True">
|
||||
<ListViewItem Content="角色1"/>
|
||||
<ListViewItem Content="角色2"/>
|
||||
</ListView>
|
||||
<MenuFlyoutSeparator/>
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
Margin="4"
|
||||
CanReorderItems="True">
|
||||
<ListViewItem Content="用户1"/>
|
||||
<ListViewItem Content="用户2"/>
|
||||
<ListViewItem Content="用户3"/>
|
||||
<ListViewItem Content="用户4"/>
|
||||
</ListView>
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
33
src/Snap.Hutao/Snap.Hutao/View/UserView.xaml.cs
Normal file
33
src/Snap.Hutao/Snap.Hutao/View/UserView.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Snap.Hutao.Core;
|
||||
|
||||
namespace Snap.Hutao.View;
|
||||
|
||||
/// <summary>
|
||||
/// 用户视图
|
||||
/// </summary>
|
||||
public sealed partial class UserView : UserControl
|
||||
{
|
||||
private static readonly DependencyProperty IsExpandedProperty = Property<UserView>.Depend(nameof(IsExpanded), true);
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的用户视图
|
||||
/// </summary>
|
||||
public UserView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前用户控件是否处于展开状态
|
||||
/// </summary>
|
||||
public bool IsExpanded
|
||||
{
|
||||
get => (bool)GetValue(IsExpandedProperty);
|
||||
set => SetValue(IsExpandedProperty, value);
|
||||
}
|
||||
}
|
||||
@@ -89,18 +89,18 @@ internal class AnnouncementViewModel : ObservableObject, ISupportCancellation
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
logger.LogInformation("Open UI cancelled");
|
||||
logger.LogInformation($"{nameof(OpenUIAsync)} cancelled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenAnnouncementUI(string? content)
|
||||
{
|
||||
logger.LogInformation("Open Announcement Command Triggered");
|
||||
logger.LogInformation($"{nameof(OpenAnnouncementUICommand)} Triggered");
|
||||
|
||||
if (WebView2Helper.IsSupported)
|
||||
{
|
||||
navigationService.Navigate<View.Page.AnnouncementContentPage>(data: content);
|
||||
navigationService.Navigate<View.Page.AnnouncementContentPage>(data: new(content));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Json.Converter;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Windows.Input;
|
||||
|
||||
@@ -124,15 +123,15 @@ public class Announcement : AnnouncementContent
|
||||
/// 开始时间
|
||||
/// </summary>
|
||||
[JsonPropertyName("start_time")]
|
||||
[JsonConverter(typeof(DateTimeConverter))]
|
||||
public DateTime StartTime { get; set; }
|
||||
[JsonConverter(typeof(Core.Json.Converter.DateTimeOffsetConverter))]
|
||||
public DateTimeOffset StartTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 结束时间
|
||||
/// </summary>
|
||||
[JsonPropertyName("end_time")]
|
||||
[JsonConverter(typeof(DateTimeConverter))]
|
||||
public DateTime EndTime { get; set; }
|
||||
[JsonConverter(typeof(Core.Json.Converter.DateTimeOffsetConverter))]
|
||||
public DateTimeOffset EndTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 类型
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Snap.Hutao.Web.Hutao;
|
||||
/// 胡桃API客户端
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient)]
|
||||
internal class HutaoClient : IAsyncInitializable
|
||||
internal class HutaoClient : ISupportAsyncInitialization
|
||||
{
|
||||
private const string AuthAPIHost = "https://auth.snapgenshin.com";
|
||||
private const string HutaoAPI = "https://hutao-api.snapgenshin.com";
|
||||
@@ -342,7 +342,7 @@ internal class HutaoClient : IAsyncInitializable
|
||||
await playerRecord.UploadRecordAsync(this, token);
|
||||
}
|
||||
|
||||
await resultAsyncFunc(resp ?? Response.Response.CreateForException($"{role.GameUid}-记录提交失败。"));
|
||||
// await resultAsyncFunc(resp ?? Response.Response.CreateForException($"{role.GameUid}-记录提交失败。"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Abstraction;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Snap.Hutao.Web.Response;
|
||||
@@ -8,8 +10,27 @@ namespace Snap.Hutao.Web.Response;
|
||||
/// <summary>
|
||||
/// 提供 <see cref="Response{T}"/> 的非泛型基类
|
||||
/// </summary>
|
||||
public class Response
|
||||
public class Response : ISupportValidation
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的响应
|
||||
/// </summary>
|
||||
/// <param name="returnCode">返回代码</param>
|
||||
/// <param name="message">消息</param>
|
||||
[JsonConstructor]
|
||||
public Response(int returnCode, string message)
|
||||
{
|
||||
ReturnCode = returnCode;
|
||||
Message = message;
|
||||
|
||||
if (!Validate())
|
||||
{
|
||||
Ioc.Default
|
||||
.GetRequiredService<IInfoBarService>()
|
||||
.Information(ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回代码
|
||||
/// </summary>
|
||||
@@ -32,18 +53,10 @@ public class Response
|
||||
return response is not null && response.ReturnCode == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个失败的响应
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <returns>响应</returns>
|
||||
public static Response CreateForException(string message)
|
||||
/// <inheritdoc/>
|
||||
public bool Validate()
|
||||
{
|
||||
return new Response()
|
||||
{
|
||||
ReturnCode = (int)KnownReturnCode.InternalFailure,
|
||||
Message = message,
|
||||
};
|
||||
return Enum.IsDefined(typeof(KnownReturnCode), ReturnCode);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -11,50 +11,21 @@ namespace Snap.Hutao.Web.Response;
|
||||
/// <typeparam name="TData">数据类型</typeparam>
|
||||
public class Response<TData> : Response
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的 Mihoyo 标准API响应
|
||||
/// </summary>
|
||||
/// <param name="returnCode">返回代码</param>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="data">数据</param>
|
||||
public Response(int returnCode, string message, TData? data)
|
||||
: base(returnCode, message)
|
||||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据
|
||||
/// </summary>
|
||||
[JsonPropertyName("data")]
|
||||
public TData? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个失败的响应
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <returns>响应</returns>
|
||||
public static new Response<TData> CreateForException(string message)
|
||||
{
|
||||
return new Response<TData>()
|
||||
{
|
||||
ReturnCode = (int)KnownReturnCode.InternalFailure,
|
||||
Message = message,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个失败的响应
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <returns>响应</returns>
|
||||
public static Response<TData> CreateForJsonException(string message)
|
||||
{
|
||||
return new Response<TData>()
|
||||
{
|
||||
ReturnCode = (int)KnownReturnCode.InternalFailure,
|
||||
Message = message,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个空Url的响应
|
||||
/// </summary>
|
||||
/// <returns>响应</returns>
|
||||
public static Response<TData> CreateForEmptyUrl()
|
||||
{
|
||||
return new Response<TData>()
|
||||
{
|
||||
ReturnCode = (int)KnownReturnCode.UrlIsEmpty,
|
||||
Message = "请求的 Url 不应为空",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user