caching announcements

This commit is contained in:
DismissedLight
2022-06-17 18:21:15 +08:00
parent 23fc81bba7
commit 7342bfd590
27 changed files with 316 additions and 269 deletions

View File

@@ -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>

View File

@@ -39,6 +39,7 @@ public partial class App : Application
{
IServiceProvider services = new ServiceCollection()
.AddLogging(builder => builder.AddDebug())
.AddMemoryCache()
.AddDatebase()
.AddHttpClients()
.AddDefaultJsonSerializerOptions()

View File

@@ -6,7 +6,7 @@ namespace Snap.Hutao.Core.Abstraction;
/// <summary>
/// 可异步初始化
/// </summary>
internal interface IAsyncInitializable
internal interface ISupportAsyncInitialization
{
/// <summary>
/// 是否已经初始化完成

View File

@@ -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();
}

View File

@@ -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"));
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -18,6 +18,6 @@ public sealed partial class MainWindow : Window
{
InitializeComponent();
ExtendsContentIntoTitleBar = true;
SetTitleBar(TitleBarGrid);
SetTitleBar(TitleBarView.DragableArea);
}
}

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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()
{

View File

@@ -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">

View File

@@ -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();
}
}

View File

@@ -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="&#xE8A7;"
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>

View 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>

View 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;
}
}

View 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="&#xE712;"
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>

View 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);
}
}

View File

@@ -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
{

View File

@@ -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>
/// 类型

View File

@@ -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}-记录提交失败。"));
}
}
}

View File

@@ -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/>

View File

@@ -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 不应为空",
};
}
}