daily note card

This commit is contained in:
Lightczx
2023-04-20 15:54:57 +08:00
parent c90f147564
commit bdb40aca6a
37 changed files with 418 additions and 156 deletions

View File

@@ -50,6 +50,7 @@ internal sealed class UniversalAnalyzer : DiagnosticAnalyzer
context.RegisterSyntaxNodeAction(HandleTypeDeclaration, types);
context.RegisterSyntaxNodeAction(HandleMethodDeclaration, SyntaxKind.MethodDeclaration);
context.RegisterSyntaxNodeAction(HandleConstructorDeclaration, SyntaxKind.ConstructorDeclaration);
}
private void HandleTypeDeclaration(SyntaxNodeAnalysisContext context)
@@ -133,6 +134,35 @@ internal sealed class UniversalAnalyzer : DiagnosticAnalyzer
}
}
private void HandleConstructorDeclaration(SyntaxNodeAnalysisContext context)
{
ConstructorDeclarationSyntax constructorSyntax = (ConstructorDeclarationSyntax)context.Node;
foreach (ParameterSyntax parameter in constructorSyntax.ParameterList.Parameters)
{
if (context.SemanticModel.GetDeclaredSymbol(parameter) is IParameterSymbol symbol)
{
if (IsBuiltInType(symbol.Type))
{
continue;
}
// 跳过 CancellationToken
if (symbol.Type.ToDisplayString() == "System.Threading.CancellationToken")
{
continue;
}
if (symbol.Type.IsReadOnly && symbol.RefKind == RefKind.None)
{
Location location = parameter.GetLocation();
Diagnostic diagnostic = Diagnostic.Create(readOnlyStructRefDescriptor, location, symbol.Type);
context.ReportDiagnostic(diagnostic);
}
}
}
}
private bool IsBuiltInType(ITypeSymbol symbol)
{
return symbol.SpecialType switch

View File

@@ -53,7 +53,9 @@
<SolidColorBrush x:Key="AvatarPropertyAddValueBrush" Color="{ThemeResource AvatarPropertyAddValueColor}"/>
<!-- Settings -->
<x:Double x:Key="SettingsCardSpacing">3</x:Double>
<x:Double x:Key="SettingsCardMinHeight">0</x:Double>
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
<x:Double x:Key="SettingsCardWrapNoIconThreshold">0</x:Double>
<Style
x:Key="SettingsSectionHeaderTextBlockStyle"
BasedOn="{StaticResource BodyStrongTextBlockStyle}"

View File

@@ -1,7 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Runtime.InteropServices;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using Windows.UI;
namespace Snap.Hutao.Control.Media;
@@ -10,70 +11,37 @@ namespace Snap.Hutao.Control.Media;
/// BGRA8 结构
/// </summary>
[HighQuality]
[StructLayout(LayoutKind.Explicit)]
internal struct Bgra8
{
/// <summary>
/// B
/// </summary>
[FieldOffset(0)]
public byte B;
/// <summary>
/// G
/// </summary>
[FieldOffset(1)]
public byte G;
/// <summary>
/// R
/// </summary>
[FieldOffset(2)]
public byte R;
/// <summary>
/// A
/// </summary>
[FieldOffset(3)]
public byte A;
[FieldOffset(0)]
private readonly uint data;
/// <summary>
/// 构造一个新的 BGRA8 结构
/// </summary>
/// <param name="b">B</param>
/// <param name="g">G</param>
/// <param name="r">R</param>
/// <param name="a">A</param>
public Bgra8(byte b, byte g, byte r, byte a)
{
B = b;
G = g;
R = r;
A = a;
}
/// <summary>
/// 从Color值转换
/// 从 Color 转换
/// </summary>
/// <param name="color">颜色</param>
/// <returns>新的 BGRA8 结构</returns>
public static Bgra8 FromColor(Color color)
public static unsafe Bgra8 FromColor(Color color)
{
return new(color.B, color.G, color.R, color.A);
}
/// <summary>
/// 从RGB值转换
/// </summary>
/// <param name="r">R</param>
/// <param name="g">G</param>
/// <param name="b">B</param>
/// <returns>新的 BGRA8 结构</returns>
public static Bgra8 FromRgb(byte r, byte g, byte b)
{
return new(b, g, r, 0xFF);
Unsafe.SkipInit(out Bgra8 bgra8);
*(uint*)&bgra8 = BinaryPrimitives.ReverseEndianness(*(uint*)&color);
return bgra8;
}
}

View File

@@ -47,7 +47,7 @@ internal struct Rgba8
/// 构造一个新的 RGBA8 颜色
/// </summary>
/// <param name="hex">色值字符串</param>
public Rgba8(ReadOnlySpan<char> hex)
public Rgba8(in ReadOnlySpan<char> hex)
{
R = 0;
G = 0;

View File

@@ -85,7 +85,7 @@ file readonly struct MeasureExecutionDisposable : IDisposable
private readonly ILogger logger;
private readonly string callerName;
public MeasureExecutionDisposable(ValueStopwatch stopwatch, ILogger logger, string callerName)
public MeasureExecutionDisposable(in ValueStopwatch stopwatch, ILogger logger, string callerName)
{
this.stopwatch = stopwatch;
this.logger = logger;

View File

@@ -30,7 +30,7 @@ internal sealed class WindowSubclass<TWindow> : IDisposable
/// 构造一个新的窗体子类管理器
/// </summary>
/// <param name="options">选项</param>
public WindowSubclass(WindowOptions<TWindow> options)
public WindowSubclass(in WindowOptions<TWindow> options)
{
this.options = options;
}

View File

@@ -32,6 +32,7 @@ internal sealed class HistoryWishBuilder
/// </summary>
/// <param name="gachaEvent">卡池配置</param>
/// <param name="context">祈愿记录上下文</param>
[SuppressMessage("", "SH002")]
public HistoryWishBuilder(GachaEvent gachaEvent, GachaLogServiceContext context)
{
this.gachaEvent = gachaEvent;

View File

@@ -24,7 +24,7 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
private bool isValid = true;
/// <summary>
/// 构造一个新的 <see cref="Unlocker"/> 对象,
/// 构造一个新的 <see cref="GameFpsUnlocker"/> 对象,
/// 每个解锁器只能解锁一次原神的进程,
/// 再次解锁需要重新创建对象
/// <para/>
@@ -62,43 +62,16 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
private static unsafe bool UnsafeReadModuleMemory(Process process, MODULEENTRY32 entry, out Span<byte> memory)
{
memory = new byte[entry.modBaseSize];
fixed (byte* lpBuffer = memory)
{
bool hProcessAddRef = false;
try
{
process.SafeHandle.DangerousAddRef(ref hProcessAddRef);
HANDLE hProcessLocal = (HANDLE)process.SafeHandle.DangerousGetHandle();
return ReadProcessMemory(hProcessLocal, entry.modBaseAddr, lpBuffer, entry.modBaseSize, null);
}
finally
{
if (hProcessAddRef)
{
process.SafeHandle.DangerousRelease();
}
}
return ReadProcessMemory((HANDLE)process.Handle, entry.modBaseAddr, lpBuffer, entry.modBaseSize, null);
}
}
private static unsafe bool UnsafeWriteModuleMemory(Process process, nuint baseAddress, int write)
{
int* lpBuffer = &write;
bool hProcessAddRef = false;
try
{
process.SafeHandle.DangerousAddRef(ref hProcessAddRef);
HANDLE hProcessLocal = (HANDLE)process.SafeHandle.DangerousGetHandle();
return WriteProcessMemory(hProcessLocal, (void*)baseAddress, lpBuffer, sizeof(int), null);
}
finally
{
if (hProcessAddRef)
{
process.SafeHandle.DangerousRelease();
}
}
return WriteProcessMemory((HANDLE)process.Handle, (void*)baseAddress, &write, sizeof(int), null);
}
private static unsafe MODULEENTRY32 UnsafeFindModule(int processId, in ReadOnlySpan<byte> moduleName)
@@ -109,8 +82,7 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
foreach (MODULEENTRY32 entry in StructMarshal.EnumerateModuleEntry32(snapshot))
{
__CHAR_256* pszModule = &entry.szModule;
ReadOnlySpan<byte> szModuleLocal = MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)pszModule);
ReadOnlySpan<byte> szModuleLocal = MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)&entry.szModule);
if (entry.th32ProcessID == processId && szModuleLocal.SequenceEqual(moduleName))
{
return entry;

View File

@@ -108,6 +108,7 @@
<None Remove="Resource\WelcomeView_Background.png" />
<None Remove="stylecop.json" />
<None Remove="View\Card\AchievementCard.xaml" />
<None Remove="View\Card\DailyNoteCard.xaml" />
<None Remove="View\Card\GachaStatisticsCard.xaml" />
<None Remove="View\Card\LaunchGameCard.xaml" />
<None Remove="View\Control\BaseValueSlider.xaml" />
@@ -299,6 +300,12 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Page Update="View\Card\DailyNoteCard.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="View\Card\AchievementCard.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@@ -22,6 +22,7 @@
</mxi:Interaction.Behaviors>
<Grid>
<FlipView
x:Name="RootFlipView"
Background="{x:Null}"
ItemsSource="{Binding StatisticsList}"
Visibility="{Binding IsInitialized, Converter={StaticResource BoolToVisibilityConverter}}">
@@ -64,8 +65,14 @@
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
<PipsPager
Height="16"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
NumberOfPages="{Binding StatisticsList.Count}"
SelectedPageIndex="{x:Bind Path=RootFlipView.SelectedIndex, Mode=TwoWay}"
Visibility="{Binding IsInitialized, Converter={StaticResource BoolToVisibilityConverter}}"/>
<shvc:LoadingViewSlim IsLoading="{Binding IsInitialized, Converter={StaticResource BoolNegationConverter}}"/>
</Grid>
</Button>

View File

@@ -15,6 +15,7 @@ internal sealed partial class AchievementCard : Button
/// </summary>
public AchievementCard()
{
DataContext = Ioc.Default.GetRequiredService<ViewModel.Achievement.AchievementViewModelSlim>();
InitializeComponent();
}
}

View File

@@ -0,0 +1,164 @@
<Button
x:Class="Snap.Hutao.View.Card.DailyNoteCard"
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:mxi="using:Microsoft.Xaml.Interactivity"
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shvc="using:Snap.Hutao.View.Control"
xmlns:shvd="using:Snap.Hutao.ViewModel.DailyNote"
Padding="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
d:DataContext="{d:DesignInstance shvd:DailyNoteViewModelSlim}"
Command="{Binding NavigateCommand}"
Style="{ThemeResource DefaultButtonStyle}"
mc:Ignorable="d">
<mxi:Interaction.Behaviors>
<shcb:InvokeCommandOnLoadedBehavior Command="{Binding OpenUICommand}"/>
</mxi:Interaction.Behaviors>
<Grid>
<FlipView
x:Name="RootFlipView"
Background="{x:Null}"
ItemsSource="{Binding DailyNoteEntries}"
Visibility="{Binding IsInitialized, Converter={StaticResource BoolToVisibilityConverter}}">
<FlipView.ItemTemplate>
<DataTemplate>
<Grid
Margin="12"
ColumnSpacing="6"
RowSpacing="6">
<Grid.Resources>
<x:Double x:Key="ProgressBarOpacity">0.2</x:Double>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" RowSpacing="6">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock HorizontalAlignment="Center" Text="{Binding Uid}"/>
<Border Grid.Row="1" Style="{StaticResource BorderCardStyle}">
<StackPanel VerticalAlignment="Center">
<Image Width="64" Source="ms-appx:///Resource/Icon/UI_ItemIcon_210_256.png"/>
<TextBlock
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding DailyNote.ResinFormatted}"/>
</StackPanel>
</Border>
<InfoBadge
Grid.Row="1"
Width="8"
Height="8"
Margin="8"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Style="{ThemeResource AttentionDotInfoBadgeStyle}"
Visibility="{Binding ResinNotifySuppressed, Converter={StaticResource BoolToVisibilityConverter}}"/>
</Grid>
<Grid Grid.Column="1" RowSpacing="6">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Style="{StaticResource BorderCardStyle}">
<StackPanel VerticalAlignment="Center">
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_ItemIcon_204.png"/>
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding DailyNote.HomeCoinFormatted}"/>
</StackPanel>
</Border>
<InfoBadge
Width="8"
Height="8"
Margin="8"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Style="{ThemeResource AttentionDotInfoBadgeStyle}"
Visibility="{Binding HomeCoinNotifySuppressed, Converter={StaticResource BoolToVisibilityConverter}}"/>
<Border Grid.Row="1" Style="{StaticResource BorderCardStyle}">
<StackPanel VerticalAlignment="Center">
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_MarkQuest_Events_Proce.png"/>
<TextBlock
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding DailyNote.TaskFormatted}"/>
</StackPanel>
</Border>
<InfoBadge
Grid.Row="1"
Width="8"
Height="8"
Margin="8"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Style="{ThemeResource AttentionDotInfoBadgeStyle}"
Visibility="{Binding DailyTaskNotifySuppressed, Converter={StaticResource BoolToVisibilityConverter}}"/>
</Grid>
<Grid Grid.Column="2" RowSpacing="6">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Style="{StaticResource BorderCardStyle}">
<StackPanel VerticalAlignment="Center">
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_MarkTower.png"/>
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding DailyNote.ResinDiscountFormatted}"/>
</StackPanel>
</Border>
<Border Grid.Row="1" Style="{StaticResource BorderCardStyle}">
<StackPanel VerticalAlignment="Center">
<Image Width="32" Source="ms-appx:///Resource/Icon/UI_ItemIcon_220021.png"/>
<TextBlock
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding DailyNote.Transformer.RecoveryTime.TimeLeftFormatted}"/>
</StackPanel>
</Border>
<InfoBadge
Grid.Row="1"
Width="8"
Height="8"
Margin="8"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Style="{ThemeResource AttentionDotInfoBadgeStyle}"
Visibility="{Binding TransformerNotifySuppressed, Converter={StaticResource BoolToVisibilityConverter}}"/>
</Grid>
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
<PipsPager
Height="16"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
NumberOfPages="{Binding DailyNoteEntries.Count}"
SelectedPageIndex="{x:Bind Path=RootFlipView.SelectedIndex, Mode=TwoWay}"
Visibility="{Binding IsInitialized, Converter={StaticResource BoolToVisibilityConverter}}"/>
<shvc:LoadingViewSlim IsLoading="{Binding IsInitialized, Converter={StaticResource BoolNegationConverter}}"/>
</Grid>
</Button>

View File

@@ -0,0 +1,21 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
namespace Snap.Hutao.View.Card;
/// <summary>
/// 实时便笺卡片
/// </summary>
internal sealed partial class DailyNoteCard : Button
{
/// <summary>
/// 构造一个新的实时便笺卡片
/// </summary>
public DailyNoteCard()
{
DataContext = Ioc.Default.GetRequiredService<ViewModel.DailyNote.DailyNoteViewModelSlim>();
InitializeComponent();
}
}

View File

@@ -23,6 +23,7 @@
</mxi:Interaction.Behaviors>
<Grid>
<FlipView
x:Name="RootFlipView"
Background="{x:Null}"
ItemsSource="{Binding StatisticsList}"
Visibility="{Binding IsInitialized, Converter={StaticResource BoolToVisibilityConverter}}">
@@ -32,6 +33,7 @@
<Grid.Resources>
<SolidColorBrush x:Key="PurpleBrush" Color="#FFA156E0"/>
<SolidColorBrush x:Key="OrangeBrush" Color="#FFBC6932"/>
<x:Double x:Key="PullProgressOpacity">0.2</x:Double>
</Grid.Resources>
<Grid.RowDefinitions>
@@ -81,7 +83,7 @@
CornerRadius="{StaticResource CompatCornerRadius}"
Foreground="{StaticResource OrangeBrush}"
Maximum="{Binding GuaranteeOrangeThreshold}"
Opacity="0.15"
Opacity="{StaticResource PullProgressOpacity}"
Style="{StaticResource DefaultProgressBarStyle}"
Value="{Binding LastOrangePull}"/>
<TextBlock
@@ -114,7 +116,7 @@
CornerRadius="{StaticResource CompatCornerRadius}"
Foreground="{StaticResource PurpleBrush}"
Maximum="{Binding GuaranteePurpleThreshold}"
Opacity="0.15"
Opacity="{StaticResource PullProgressOpacity}"
Style="{StaticResource DefaultProgressBarStyle}"
Value="{Binding LastPurplePull}"/>
<TextBlock
@@ -161,7 +163,7 @@
CornerRadius="{StaticResource CompatCornerRadius}"
Foreground="{StaticResource OrangeBrush}"
Maximum="{Binding GuaranteeOrangeThreshold}"
Opacity="0.15"
Opacity="{StaticResource PullProgressOpacity}"
Style="{StaticResource DefaultProgressBarStyle}"
Value="{Binding LastOrangePull}"/>
<TextBlock
@@ -194,7 +196,7 @@
CornerRadius="{StaticResource CompatCornerRadius}"
Foreground="{StaticResource PurpleBrush}"
Maximum="{Binding GuaranteePurpleThreshold}"
Opacity="0.15"
Opacity="{StaticResource PullProgressOpacity}"
Style="{StaticResource DefaultProgressBarStyle}"
Value="{Binding LastPurplePull}"/>
<TextBlock
@@ -241,7 +243,7 @@
CornerRadius="{StaticResource CompatCornerRadius}"
Foreground="{StaticResource OrangeBrush}"
Maximum="{Binding GuaranteeOrangeThreshold}"
Opacity="0.15"
Opacity="{StaticResource PullProgressOpacity}"
Style="{StaticResource DefaultProgressBarStyle}"
Value="{Binding LastOrangePull}"/>
<TextBlock
@@ -274,7 +276,7 @@
CornerRadius="{StaticResource CompatCornerRadius}"
Foreground="{StaticResource PurpleBrush}"
Maximum="{Binding GuaranteePurpleThreshold}"
Opacity="0.15"
Opacity="{StaticResource PullProgressOpacity}"
Style="{StaticResource DefaultProgressBarStyle}"
Value="{Binding LastPurplePull}"/>
<TextBlock
@@ -299,6 +301,13 @@
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
<PipsPager
Height="16"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
NumberOfPages="{Binding StatisticsList.Count}"
SelectedPageIndex="{x:Bind Path=RootFlipView.SelectedIndex, Mode=TwoWay}"
Visibility="{Binding IsInitialized, Converter={StaticResource BoolToVisibilityConverter}}"/>
<shvc:LoadingViewSlim IsLoading="{Binding IsInitialized, Converter={StaticResource BoolNegationConverter}}"/>
</Grid>
</Button>

View File

@@ -15,6 +15,7 @@ internal sealed partial class GachaStatisticsCard : Button
/// </summary>
public GachaStatisticsCard()
{
DataContext = Ioc.Default.GetRequiredService<ViewModel.GachaLog.GachaLogViewModelSlim>();
InitializeComponent();
}
}

View File

@@ -15,6 +15,7 @@ internal sealed partial class LaunchGameCard : Button
/// </summary>
public LaunchGameCard()
{
DataContext = Ioc.Default.GetRequiredService<ViewModel.Game.LaunchGameViewModelSlim>();
InitializeComponent();
}
}

View File

@@ -14,9 +14,20 @@
Style="{StaticResource DefaultContentDialogStyle}"
mc:Ignorable="d">
<ScrollViewer>
<StackPanel>
<TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{Binding UserGameRole}"/>
<clw:SettingsCard Padding="16,8" Header="{shcm:ResourceString Name=ViewDialogDailyNoteNotificationResinNotifyThreshold}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<StackPanel.Resources>
<x:Double x:Key="SettingsCardMinHeight">0</x:Double>
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
<x:Double x:Key="SettingsCardWrapNoIconThreshold">0</x:Double>
</StackPanel.Resources>
<TextBlock
Margin="0,3,0,3"
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="{Binding UserGameRole}"/>
<clw:SettingsCard
Width="480"
Padding="16,8"
Header="{shcm:ResourceString Name=ViewDialogDailyNoteNotificationResinNotifyThreshold}">
<Slider
MinWidth="160"
Margin="32,0,0,0"

View File

@@ -169,13 +169,11 @@
DesiredWidth="300"
ItemContainerStyle="{StaticResource LargeGridViewItemStyle}"
SelectionMode="None">
<shvca:LaunchGameCard Height="{StaticResource HomeAdaptiveCardHeight}" DataContext="{Binding LaunchGameViewModelSlim}"/>
<shvca:GachaStatisticsCard DataContext="{Binding GachaLogViewModelSlim}"/>
<shvca:AchievementCard DataContext="{Binding AchievementViewModelSlim}"/>
<shvca:LaunchGameCard Height="{StaticResource HomeAdaptiveCardHeight}"/>
<shvca:GachaStatisticsCard/>
<shvca:AchievementCard/>
<shvca:DailyNoteCard/>
<Border Style="{StaticResource BorderCardStyle}">
<TextBlock Text="实时便笺"/>
</Border>
<Border Style="{StaticResource BorderCardStyle}">
<TextBlock Text="养成计划"/>
</Border>

View File

@@ -14,8 +14,8 @@
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shci="using:Snap.Hutao.Control.Image"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
xmlns:shv="using:Snap.Hutao.ViewModel"
d:DataContext="{d:DesignInstance shv:DailyNoteViewModel}"
xmlns:shvd="using:Snap.Hutao.ViewModel.DailyNote"
d:DataContext="{d:DesignInstance shvd:DailyNoteViewModel}"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">

View File

@@ -2,7 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Control;
using Snap.Hutao.ViewModel;
using Snap.Hutao.ViewModel.DailyNote;
namespace Snap.Hutao.View.Page;
@@ -20,4 +20,4 @@ internal sealed partial class DailyNotePage : ScopedPage
InitializeWith<DailyNoteViewModel>();
InitializeComponent();
}
}
}

View File

@@ -9,8 +9,8 @@
xmlns:mxim="using:Microsoft.Xaml.Interactions.Media"
xmlns:shc="using:Snap.Hutao.Control"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
xmlns:shvm="using:Snap.Hutao.ViewModel"
d:DataContext="{d:DesignInstance shvm:UserViewModel}"
xmlns:shvu="using:Snap.Hutao.ViewModel.User"
d:DataContext="{d:DesignInstance shvu:UserViewModel}"
mc:Ignorable="d">
<mxi:Interaction.Behaviors>
<mxic:EventTriggerBehavior EventName="Loaded">
@@ -121,6 +121,7 @@
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<PersonPicture
Height="32"
@@ -132,10 +133,14 @@
Margin="12,0,0,0"
VerticalAlignment="Center"
Text="{Binding UserInfo.Nickname}"/>
<TextBlock
Grid.Column="2"
VerticalAlignment="Center"
Text="HoYoLAB"
Visibility="{Binding IsOversea}"/>
<StackPanel
x:Name="ButtonPanel"
Grid.Column="2"
Grid.Column="3"
Orientation="Horizontal"
Visibility="Collapsed">
<Button

View File

@@ -10,7 +10,7 @@ namespace Snap.Hutao.ViewModel.Achievement;
/// <summary>
/// 简化的成就视图模型
/// </summary>
[Injection(InjectAs.Scoped)]
[Injection(InjectAs.Transient)]
internal sealed class AchievementViewModelSlim : Abstraction.ViewModelSlim<View.Page.AchievementPage>
{
private List<AchievementStatistics>? statisticsList;

View File

@@ -24,10 +24,6 @@ internal sealed class AnnouncementViewModel : Abstraction.ViewModel
public AnnouncementViewModel(IServiceProvider serviceProvider)
{
announcementService = serviceProvider.GetRequiredService<IAnnouncementService>();
LaunchGameViewModelSlim = serviceProvider.GetRequiredService<Game.LaunchGameViewModelSlim>();
GachaLogViewModelSlim = serviceProvider.GetRequiredService<GachaLog.GachaLogViewModelSlim>();
AchievementViewModelSlim = serviceProvider.GetRequiredService<Achievement.AchievementViewModelSlim>();
}
/// <summary>
@@ -35,21 +31,6 @@ internal sealed class AnnouncementViewModel : Abstraction.ViewModel
/// </summary>
public AnnouncementWrapper? Announcement { get => announcement; set => SetProperty(ref announcement, value); }
/// <summary>
/// 启动游戏视图模型
/// </summary>
public Game.LaunchGameViewModelSlim LaunchGameViewModelSlim { get; }
/// <summary>
/// 祈愿记录视图模型
/// </summary>
public GachaLog.GachaLogViewModelSlim GachaLogViewModelSlim { get; }
/// <summary>
/// 成就统计视图模型
/// </summary>
public Achievement.AchievementViewModelSlim AchievementViewModelSlim { get; }
/// <inheritdoc/>
protected override async Task OpenUIAsync()
{

View File

@@ -14,7 +14,7 @@ using Snap.Hutao.View.Dialog;
using Snap.Hutao.ViewModel.User;
using System.Collections.ObjectModel;
namespace Snap.Hutao.ViewModel;
namespace Snap.Hutao.ViewModel.DailyNote;
/// <summary>
/// 实时便笺视图模型

View File

@@ -0,0 +1,64 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.DailyNote;
using Snap.Hutao.Service.User;
using Snap.Hutao.ViewModel.User;
using System.Collections.ObjectModel;
namespace Snap.Hutao.ViewModel.DailyNote;
/// <summary>
/// 简化的实时便笺视图模型
/// </summary>
[Injection(InjectAs.Transient)]
internal sealed class DailyNoteViewModelSlim : Abstraction.ViewModelSlim<View.Page.DailyNotePage>
{
private List<DailyNoteEntry>? dailyNoteEntries;
/// <summary>
/// 构造一个新的简化的实时便笺视图模型
/// </summary>
/// <param name="serviceProvider">服务提供器</param>
public DailyNoteViewModelSlim(IServiceProvider serviceProvider)
: base(serviceProvider)
{
}
/// <summary>
/// 实时便笺集合
/// </summary>
public List<DailyNoteEntry>? DailyNoteEntries { get => dailyNoteEntries; set => SetProperty(ref dailyNoteEntries, value); }
/// <inheritdoc/>
protected override async Task OpenUIAsync()
{
try
{
await ThreadHelper.SwitchToBackgroundAsync();
_ = await ServiceProvider
.GetRequiredService<IUserService>()
.GetRoleCollectionAsync()
.ConfigureAwait(false);
ObservableCollection<DailyNoteEntry> entries = await ServiceProvider
.GetRequiredService<IDailyNoteService>()
.GetDailyNoteEntriesAsync()
.ConfigureAwait(false);
// We have to create a copy here,
// to prevent page add/remove failure.
List<DailyNoteEntry> entryList = entries.ToList();
await ThreadHelper.SwitchToMainThreadAsync();
DailyNoteEntries = entryList;
IsInitialized = true;
}
catch (Core.ExceptionService.UserdataCorruptedException ex)
{
ServiceProvider.GetRequiredService<IInfoBarService>().Error(ex);
return;
}
}
}

View File

@@ -8,7 +8,7 @@ namespace Snap.Hutao.ViewModel.GachaLog;
/// <summary>
/// 简化的祈愿记录视图模型
/// </summary>
[Injection(InjectAs.Scoped)]
[Injection(InjectAs.Transient)]
internal sealed class GachaLogViewModelSlim : Abstraction.ViewModelSlim<View.Page.GachaLogPage>
{
private List<GachaStatisticsSlim>? statisticsList;
@@ -30,14 +30,17 @@ internal sealed class GachaLogViewModelSlim : Abstraction.ViewModelSlim<View.Pag
/// <inheritdoc/>
protected override async Task OpenUIAsync()
{
IGachaLogService gachaLogService = ServiceProvider.GetRequiredService<IGachaLogService>();
if (await gachaLogService.InitializeAsync(default).ConfigureAwait(false))
using (IServiceScope scope = ServiceProvider.CreateScope())
{
List<GachaStatisticsSlim> list = await gachaLogService.GetStatisticsSlimsAsync().ConfigureAwait(false);
await ThreadHelper.SwitchToMainThreadAsync();
StatisticsList = list;
IsInitialized = true;
IGachaLogService gachaLogService = scope.ServiceProvider.GetRequiredService<IGachaLogService>();
if (await gachaLogService.InitializeAsync(default).ConfigureAwait(false))
{
List<GachaStatisticsSlim> list = await gachaLogService.GetStatisticsSlimsAsync().ConfigureAwait(false);
await ThreadHelper.SwitchToMainThreadAsync();
StatisticsList = list;
IsInitialized = true;
}
}
}
}

View File

@@ -12,7 +12,7 @@ namespace Snap.Hutao.ViewModel.Game;
/// <summary>
/// 简化的启动游戏视图模型
/// </summary>
[Injection(InjectAs.Scoped)]
[Injection(InjectAs.Transient)]
internal sealed class LaunchGameViewModelSlim : Abstraction.ViewModelSlim<View.Page.LaunchGamePage>
{
private readonly IGameService gameService;

View File

@@ -30,7 +30,7 @@ internal class AvatarView : INameIconSide
/// </summary>
/// <param name="avatarId">角色Id</param>
/// <param name="idAvatarMap">Id角色映射</param>
public AvatarView(AvatarId avatarId, Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap)
public AvatarView(in AvatarId avatarId, Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap)
: this(idAvatarMap[avatarId])
{
}

View File

@@ -17,7 +17,7 @@ internal sealed class RankAvatar : AvatarView
/// <param name="value">值</param>
/// <param name="avatarId">角色Id</param>
/// <param name="idAvatarMap">Id角色映射</param>
public RankAvatar(int value, AvatarId avatarId, Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap)
public RankAvatar(int value, in AvatarId avatarId, Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap)
: base(avatarId, idAvatarMap)
{
Value = value;

View File

@@ -87,6 +87,11 @@ internal sealed class User : ObservableObject
set => inner.SToken = value;
}
/// <summary>
/// 是否为国际服
/// </summary>
public bool IsOversea { get => Entity.IsOversea; }
/// <summary>
/// 内部的用户实体
/// </summary>

View File

@@ -19,7 +19,7 @@ internal sealed class UserAndUid
/// </summary>
/// <param name="user">实体用户</param>
/// <param name="role">角色</param>
public UserAndUid(EntityUser user, PlayerUid role)
public UserAndUid(EntityUser user, in PlayerUid role)
{
User = user;
Uid = role;

View File

@@ -1,4 +1,7 @@
using Microsoft.Web.WebView2.Core;
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.Web.Bridge.Model;
namespace Snap.Hutao.Web.Bridge;

View File

@@ -51,7 +51,7 @@ internal struct GachaLogQueryOptions
/// <param name="query">原始查询字符串</param>
/// <param name="type">祈愿类型</param>
/// <param name="endId">终止Id</param>
public GachaLogQueryOptions(GachaLogQuery query, GachaConfigType type, long endId = 0L)
public GachaLogQueryOptions(in GachaLogQuery query, GachaConfigType type, long endId = 0L)
{
IsOversea = query.IsOversea;
innerQuery = QueryString.Parse(query.Query);

View File

@@ -17,7 +17,7 @@ internal sealed class GenAuthKeyData
/// <param name="authAppId">AppId</param>
/// <param name="gameBiz">游戏代号</param>
/// <param name="uid">uid</param>
public GenAuthKeyData(string authAppId, string gameBiz, PlayerUid uid)
public GenAuthKeyData(string authAppId, string gameBiz, in PlayerUid uid)
{
AuthAppId = authAppId;
GameBiz = gameBiz;

View File

@@ -15,7 +15,7 @@ internal sealed class CharacterData
/// </summary>
/// <param name="uid">uid</param>
/// <param name="characterIds">角色id</param>
public CharacterData(PlayerUid uid, IEnumerable<AvatarId> characterIds)
public CharacterData(in PlayerUid uid, IEnumerable<AvatarId> characterIds)
{
CharacterIds = characterIds;
Uid = uid.Value;

View File

@@ -77,6 +77,30 @@ internal sealed class RecoveryTime
}
}
/// <summary>
/// 获取格式化的剩余时间
/// </summary>
[JsonIgnore]
public string TimeLeftFormatted
{
get
{
if (Reached)
{
return SH.WebDailyNoteTransformerReady;
}
else
{
return new StringBuilder()
.AppendIf(Day > 0, string.Format(SH.WebDailyNoteTransformerDaysFormat, Day))
.AppendIf(Hour > 0, string.Format(SH.WebDailyNoteTransformerHoursFormat, Hour))
.AppendIf(Minute > 0, string.Format(SH.WebDailyNoteTransformerMinutesFormat, Minute))
.AppendIf(Second > 0, string.Format(SH.WebDailyNoteTransformerSecondsFormat, Second))
.ToString();
}
}
}
/// <summary>
/// 获取格式化的状态
/// </summary>

View File

@@ -44,7 +44,7 @@ internal static class StructMarshal
public static unsafe Windows.UI.Color Color(uint value)
{
Unsafe.SkipInit(out Windows.UI.Color color);
*(uint*)&color = BinaryPrimitives.ReverseEndianness(value); // Unsafe.WriteUnaligned(&color, reversed);
*(uint*)&color = BinaryPrimitives.ReverseEndianness(value);
return color;
}
@@ -90,7 +90,7 @@ internal static class StructMarshal
{
MODULEENTRY32 entry = MODULEENTRY32();
if (!UnsafeModule32First(snapshot, ref entry))
if (!Module32First(snapshot, ref entry))
{
yield break;
}
@@ -99,7 +99,7 @@ internal static class StructMarshal
{
yield return entry;
}
while (UnsafeModule32Next(snapshot, ref entry));
while (Module32Next(snapshot, ref entry));
}
/// <summary>
@@ -111,20 +111,4 @@ internal static class StructMarshal
{
return moduleEntry32.dwSize == 0;
}
private static unsafe BOOL UnsafeModule32First(in HANDLE snapshot, ref MODULEENTRY32 lpme)
{
fixed (MODULEENTRY32* lpmeLocal = &lpme)
{
return Module32First(snapshot, lpmeLocal);
}
}
private static unsafe BOOL UnsafeModule32Next(in HANDLE snapshot, ref MODULEENTRY32 lpme)
{
fixed (MODULEENTRY32* lpmeLocal = &lpme)
{
return Module32Next(snapshot, lpmeLocal);
}
}
}