mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Thread pool optimization
This commit is contained in:
@@ -4,50 +4,57 @@
|
||||
xmlns:contract7NotPresent="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractNotPresent(Windows.Foundation.UniversalApiContract,7)"
|
||||
xmlns:contract7Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,7)">
|
||||
|
||||
<Style x:Key="SettingButtonStyle" TargetType="Button" BasedOn="{StaticResource DefaultButtonStyle}" >
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}" />
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
|
||||
<Setter Property="Padding" Value="16,5,16,6" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Style
|
||||
x:Key="SettingButtonStyle"
|
||||
BasedOn="{StaticResource DefaultButtonStyle}"
|
||||
TargetType="Button">
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}"/>
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||
<Setter Property="Padding" Value="16,5,16,6"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="HyperlinkButtonStyle" TargetType="HyperlinkButton" >
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
|
||||
<Style x:Key="HyperlinkButtonStyle" TargetType="HyperlinkButton">
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="TextButtonStyle" TargetType="ButtonBase">
|
||||
<Setter Property="Background" Value="{ThemeResource HyperlinkButtonBackground}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource HyperlinkButtonForeground}" />
|
||||
<Setter Property="MinWidth" Value="0" />
|
||||
<Setter Property="MinHeight" Value="0" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
||||
<Setter Property="Background" Value="{ThemeResource HyperlinkButtonBackground}"/>
|
||||
<Setter Property="Foreground" Value="{ThemeResource HyperlinkButtonForeground}"/>
|
||||
<Setter Property="MinWidth" Value="0"/>
|
||||
<Setter Property="MinHeight" Value="0"/>
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ButtonBase">
|
||||
<Grid Margin="{TemplateBinding Padding}" CornerRadius="4" Background="{TemplateBinding Background}">
|
||||
<ContentPresenter x:Name="Text"
|
||||
Content="{TemplateBinding Content}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
FontWeight="SemiBold"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
|
||||
<Grid
|
||||
Margin="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}"
|
||||
CornerRadius="4">
|
||||
<ContentPresenter
|
||||
x:Name="Text"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Content="{TemplateBinding Content}"
|
||||
FontWeight="SemiBold"/>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Normal"/>
|
||||
|
||||
<VisualState x:Name="PointerOver">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonForegroundPointerOver}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonForegroundPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBackgroundPointerOver}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBackgroundPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBorderBrushPointerOver}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBorderBrushPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
@@ -56,13 +63,13 @@
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonForegroundPressed}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonForegroundPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBackgroundPressed}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBackgroundPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBorderBrushPressed}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBorderBrushPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
@@ -71,13 +78,13 @@
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonForegroundDisabled}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonForegroundDisabled}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBackgroundDisabled}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBackgroundDisabled}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBorderBrushDisabled}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBorderBrushDisabled}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
@@ -85,7 +92,6 @@
|
||||
</VisualStateGroup>
|
||||
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
</Grid>
|
||||
|
||||
</ControlTemplate>
|
||||
@@ -93,192 +99,434 @@
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- This style overrides the default style so that all ToggleSwitches are right aligned, with the label on the left -->
|
||||
<!-- This style overrides the default style so that all ToggleSwitches are right aligned, with the label on the left -->
|
||||
<Style x:Key="ToggleSwitchSettingStyle" TargetType="ToggleSwitch">
|
||||
<Setter Property="Foreground" Value="{ThemeResource ToggleSwitchContentForeground}" />
|
||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="ManipulationMode" Value="System,TranslateX" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource ToggleSwitchContentForeground}"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right"/>
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
|
||||
<Setter Property="ManipulationMode" Value="System,TranslateX"/>
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}"/>
|
||||
|
||||
<Setter Property="FocusVisualMargin" Value="-7,-3,-7,-3" />
|
||||
<Setter Property="FocusVisualMargin" Value="-7,-3,-7,-3"/>
|
||||
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleSwitch">
|
||||
<Grid
|
||||
contract7Present:CornerRadius="{TemplateBinding CornerRadius}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
contract7Present:CornerRadius="{TemplateBinding CornerRadius}">
|
||||
BorderThickness="{TemplateBinding BorderThickness}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ContentPresenter
|
||||
x:Name="HeaderContentPresenter"
|
||||
Grid.Row="0"
|
||||
Margin="{ThemeResource ToggleSwitchTopHeaderMargin}"
|
||||
VerticalAlignment="Top"
|
||||
x:DeferLoadStrategy="Lazy"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
Foreground="{ThemeResource ToggleSwitchHeaderForeground}"
|
||||
IsHitTestVisible="False"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="Collapsed"/>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="{ThemeResource ToggleSwitchPreContentMargin}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{ThemeResource ToggleSwitchPostContentMargin}"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="12" MaxWidth="12"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
x:Name="SwitchAreaGrid"
|
||||
Grid.RowSpan="3"
|
||||
Grid.ColumnSpan="3"
|
||||
Margin="0,5"
|
||||
contract7NotPresent:CornerRadius="{StaticResource ControlCornerRadius}"
|
||||
contract7Present:CornerRadius="{TemplateBinding CornerRadius}"
|
||||
Background="{ThemeResource ToggleSwitchContainerBackground}"
|
||||
Control.IsTemplateFocusTarget="True"/>
|
||||
<ContentPresenter
|
||||
x:Name="OffContentPresenter"
|
||||
Grid.RowSpan="3"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Content="{TemplateBinding OffContent}"
|
||||
ContentTemplate="{TemplateBinding OffContentTemplate}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0"/>
|
||||
<ContentPresenter
|
||||
x:Name="OnContentPresenter"
|
||||
Grid.RowSpan="3"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Content="{TemplateBinding OnContent}"
|
||||
ContentTemplate="{TemplateBinding OnContentTemplate}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0"/>
|
||||
<Rectangle
|
||||
x:Name="OuterBorder"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Width="40"
|
||||
Height="20"
|
||||
Fill="{ThemeResource ToggleSwitchFillOff}"
|
||||
RadiusX="10"
|
||||
RadiusY="10"
|
||||
Stroke="{ThemeResource ToggleSwitchStrokeOff}"
|
||||
StrokeThickness="{ThemeResource ToggleSwitchOuterBorderStrokeThickness}"/>
|
||||
<Rectangle
|
||||
x:Name="SwitchKnobBounds"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Width="40"
|
||||
Height="20"
|
||||
Fill="{ThemeResource ToggleSwitchFillOn}"
|
||||
Opacity="0"
|
||||
RadiusX="10"
|
||||
RadiusY="10"
|
||||
Stroke="{ThemeResource ToggleSwitchStrokeOn}"
|
||||
StrokeThickness="{ThemeResource ToggleSwitchOnStrokeThickness}"/>
|
||||
<Grid
|
||||
x:Name="SwitchKnob"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Width="20"
|
||||
Height="20"
|
||||
HorizontalAlignment="Left">
|
||||
<Border
|
||||
x:Name="SwitchKnobOn"
|
||||
Grid.Column="2"
|
||||
Width="12"
|
||||
Height="12"
|
||||
Margin="0,0,1,0"
|
||||
HorizontalAlignment="Center"
|
||||
contract7Present:BackgroundSizing="OuterBorderEdge"
|
||||
Background="{ThemeResource ToggleSwitchKnobFillOn}"
|
||||
BorderBrush="{ThemeResource ToggleSwitchKnobStrokeOn}"
|
||||
CornerRadius="7"
|
||||
Opacity="0"
|
||||
RenderTransformOrigin="0.5, 0.5">
|
||||
<Border.RenderTransform>
|
||||
<CompositeTransform/>
|
||||
</Border.RenderTransform>
|
||||
</Border>
|
||||
<Rectangle
|
||||
x:Name="SwitchKnobOff"
|
||||
Grid.Column="2"
|
||||
Width="12"
|
||||
Height="12"
|
||||
Margin="-1,0,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Fill="{ThemeResource ToggleSwitchKnobFillOff}"
|
||||
RadiusX="7"
|
||||
RadiusY="7"
|
||||
RenderTransformOrigin="0.5, 0.5">
|
||||
<Rectangle.RenderTransform>
|
||||
<CompositeTransform/>
|
||||
</Rectangle.RenderTransform>
|
||||
</Rectangle>
|
||||
<Grid.RenderTransform>
|
||||
<TranslateTransform x:Name="KnobTranslateTransform"/>
|
||||
</Grid.RenderTransform>
|
||||
</Grid>
|
||||
<Thumb
|
||||
x:Name="SwitchThumb"
|
||||
Grid.RowSpan="3"
|
||||
Grid.ColumnSpan="3"
|
||||
AutomationProperties.AccessibilityView="Raw">
|
||||
<Thumb.Template>
|
||||
<ControlTemplate TargetType="Thumb">
|
||||
<Rectangle Fill="Transparent"/>
|
||||
</ControlTemplate>
|
||||
</Thumb.Template>
|
||||
</Thumb>
|
||||
|
||||
</Grid>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOff}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOff}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOff}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOff}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOff}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOff}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOn}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOn}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOn}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOn}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOn}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOn}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchContainerBackground}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchContainerBackground}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Width" EnableDependentAnimation="True" >
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="12" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="12"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Height" EnableDependentAnimation="True">
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="12" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="12"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Width" EnableDependentAnimation="True">
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="12" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="12"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Height" EnableDependentAnimation="True">
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="12" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="12"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchStrokeOffPointerOver}" />
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchStrokeOffPointerOver}"/>
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchFillOffPointerOver}" />
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchFillOffPointerOver}"/>
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOffPointerOver}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOffPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOnPointerOver}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOnPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOnPointerOver}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOnPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOnPointerOver}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOnPointerOver}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchContainerBackgroundPointerOver}" />
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchContainerBackgroundPointerOver}"/>
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Width" EnableDependentAnimation="True" >
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="14" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Height" EnableDependentAnimation="True">
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="14" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Width" EnableDependentAnimation="True">
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="14" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Height" EnableDependentAnimation="True">
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="14" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
|
||||
<VisualState.Setters>
|
||||
<Setter Target="SwitchKnobOn.HorizontalAlignment" Value="Right" />
|
||||
<Setter Target="SwitchKnobOn.Margin" Value="0,0,3,0" />
|
||||
<Setter Target="SwitchKnobOff.HorizontalAlignment" Value="Left" />
|
||||
<Setter Target="SwitchKnobOff.Margin" Value="3,0,0,0" />
|
||||
<Setter Target="SwitchKnobOn.HorizontalAlignment" Value="Right"/>
|
||||
<Setter Target="SwitchKnobOn.Margin" Value="0,0,3,0"/>
|
||||
<Setter Target="SwitchKnobOff.HorizontalAlignment" Value="Left"/>
|
||||
<Setter Target="SwitchKnobOff.Margin" Value="3,0,0,0"/>
|
||||
</VisualState.Setters>
|
||||
|
||||
<Storyboard>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchStrokeOffPressed}" />
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchStrokeOffPressed}"/>
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchFillOffPressed}" />
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchFillOffPressed}"/>
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOnPressed}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOnPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOnPressed}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOnPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOffPressed}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOffPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOnPressed}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOnPressed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchContainerBackgroundPressed}" />
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchContainerBackgroundPressed}"/>
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Width" EnableDependentAnimation="True" >
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="17" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="17"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Height" EnableDependentAnimation="True">
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="14" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Width" EnableDependentAnimation="True">
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="17" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="17"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Height" EnableDependentAnimation="True">
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="14" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
||||
Value="14"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchHeaderForegroundDisabled}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchHeaderForegroundDisabled}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OffContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchContentForegroundDisabled}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchContentForegroundDisabled}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OnContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchContentForegroundDisabled}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchContentForegroundDisabled}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchStrokeOffDisabled}" />
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchStrokeOffDisabled}"/>
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchFillOffDisabled}" />
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchFillOffDisabled}"/>
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOnDisabled}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOnDisabled}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Stroke">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOnDisabled}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOnDisabled}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Fill">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOffDisabled}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOffDisabled}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOnDisabled}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOnDisabled}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchContainerBackgroundDisabled}" />
|
||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchContainerBackgroundDisabled}"/>
|
||||
</ColorAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Width" EnableDependentAnimation="True" >
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlNormalAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="12" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
||||
Value="12"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Height" EnableDependentAnimation="True">
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlNormalAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="12" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOn"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
||||
Value="12"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Width" EnableDependentAnimation="True">
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlNormalAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="12" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
||||
Value="12"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Height" EnableDependentAnimation="True">
|
||||
<SplineDoubleKeyFrame KeyTime="{StaticResource ControlNormalAnimationDuration}" KeySpline="{StaticResource ControlFastOutSlowInKeySpline}" Value="12" />
|
||||
<DoubleAnimationUsingKeyFrames
|
||||
EnableDependentAnimation="True"
|
||||
Storyboard.TargetName="SwitchKnobOff"
|
||||
Storyboard.TargetProperty="Height">
|
||||
<SplineDoubleKeyFrame
|
||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
||||
Value="12"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
@@ -287,111 +535,117 @@
|
||||
<VisualStateGroup x:Name="ToggleStates">
|
||||
|
||||
<VisualStateGroup.Transitions>
|
||||
<VisualTransition x:Name="DraggingToOnTransition"
|
||||
<VisualTransition
|
||||
x:Name="DraggingToOnTransition"
|
||||
GeneratedDuration="0"
|
||||
From="Dragging"
|
||||
To="On"
|
||||
GeneratedDuration="0">
|
||||
To="On">
|
||||
|
||||
<Storyboard>
|
||||
<RepositionThemeAnimation TargetName="SwitchKnob" FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobCurrentToOnOffset}" />
|
||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobCurrentToOnOffset}" TargetName="SwitchKnob"/>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition x:Name="OnToDraggingTransition"
|
||||
<VisualTransition
|
||||
x:Name="OnToDraggingTransition"
|
||||
GeneratedDuration="0"
|
||||
From="On"
|
||||
To="Dragging"
|
||||
GeneratedDuration="0">
|
||||
To="Dragging">
|
||||
<Storyboard>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="0" Value="1" />
|
||||
<LinearDoubleKeyFrame KeyTime="0" Value="1"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="0" Value="1" />
|
||||
<LinearDoubleKeyFrame KeyTime="0" Value="1"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="0" Value="0" />
|
||||
<LinearDoubleKeyFrame KeyTime="0" Value="0"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition x:Name="DraggingToOffTransition"
|
||||
<VisualTransition
|
||||
x:Name="DraggingToOffTransition"
|
||||
GeneratedDuration="0"
|
||||
From="Dragging"
|
||||
To="Off"
|
||||
GeneratedDuration="0">
|
||||
To="Off">
|
||||
|
||||
<Storyboard>
|
||||
<RepositionThemeAnimation TargetName="SwitchKnob" FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobCurrentToOffOffset}" />
|
||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobCurrentToOffOffset}" TargetName="SwitchKnob"/>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition x:Name="OnToOffTransition"
|
||||
<VisualTransition
|
||||
x:Name="OnToOffTransition"
|
||||
GeneratedDuration="0"
|
||||
From="On"
|
||||
To="Off"
|
||||
GeneratedDuration="0">
|
||||
To="Off">
|
||||
|
||||
<Storyboard>
|
||||
<RepositionThemeAnimation TargetName="SwitchKnob" FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobOnToOffOffset}" />
|
||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobOnToOffOffset}" TargetName="SwitchKnob"/>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition x:Name="OffToOnTransition"
|
||||
<VisualTransition
|
||||
x:Name="OffToOnTransition"
|
||||
GeneratedDuration="0"
|
||||
From="Off"
|
||||
To="On"
|
||||
GeneratedDuration="0">
|
||||
To="On">
|
||||
|
||||
<Storyboard>
|
||||
<RepositionThemeAnimation TargetName="SwitchKnob" FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobOffToOnOffset}"/>
|
||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobOffToOnOffset}" TargetName="SwitchKnob"/>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
</VisualStateGroup.Transitions>
|
||||
<VisualState x:Name="Dragging" />
|
||||
<VisualState x:Name="Off" />
|
||||
<VisualState x:Name="Dragging"/>
|
||||
<VisualState x:Name="Off"/>
|
||||
<VisualState x:Name="On">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="KnobTranslateTransform"
|
||||
<DoubleAnimation
|
||||
Storyboard.TargetName="KnobTranslateTransform"
|
||||
Storyboard.TargetProperty="X"
|
||||
To="20"
|
||||
Duration="0" />
|
||||
Duration="0"/>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Opacity">
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0" />
|
||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
@@ -401,11 +655,12 @@
|
||||
<VisualState x:Name="OffContent">
|
||||
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="OffContentPresenter"
|
||||
<DoubleAnimation
|
||||
Storyboard.TargetName="OffContentPresenter"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"
|
||||
Duration="0" />
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" Storyboard.TargetName="OffContentPresenter">
|
||||
Duration="0"/>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OffContentPresenter" Storyboard.TargetProperty="IsHitTestVisible">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
@@ -417,11 +672,12 @@
|
||||
<VisualState x:Name="OnContent">
|
||||
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="OnContentPresenter"
|
||||
<DoubleAnimation
|
||||
Storyboard.TargetName="OnContentPresenter"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"
|
||||
Duration="0" />
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" Storyboard.TargetName="OnContentPresenter">
|
||||
Duration="0"/>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OnContentPresenter" Storyboard.TargetProperty="IsHitTestVisible">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
@@ -434,149 +690,11 @@
|
||||
</VisualStateGroup>
|
||||
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ContentPresenter x:Name="HeaderContentPresenter"
|
||||
x:DeferLoadStrategy="Lazy"
|
||||
Grid.Row="0"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
Foreground="{ThemeResource ToggleSwitchHeaderForeground}"
|
||||
IsHitTestVisible="False"
|
||||
Margin="{ThemeResource ToggleSwitchTopHeaderMargin}"
|
||||
TextWrapping="Wrap"
|
||||
VerticalAlignment="Top"
|
||||
Visibility="Collapsed"
|
||||
AutomationProperties.AccessibilityView="Raw" />
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="{ThemeResource ToggleSwitchPreContentMargin}" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="{ThemeResource ToggleSwitchPostContentMargin}" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="12" MaxWidth="12" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid x:Name="SwitchAreaGrid"
|
||||
Grid.RowSpan="3"
|
||||
Grid.ColumnSpan="3"
|
||||
Margin="0,5"
|
||||
contract7Present:CornerRadius="{TemplateBinding CornerRadius}"
|
||||
contract7NotPresent:CornerRadius="{StaticResource ControlCornerRadius}"
|
||||
Control.IsTemplateFocusTarget="True"
|
||||
Background="{ThemeResource ToggleSwitchContainerBackground}" />
|
||||
<ContentPresenter x:Name="OffContentPresenter"
|
||||
Grid.RowSpan="3"
|
||||
Grid.Column="0"
|
||||
Opacity="0"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
IsHitTestVisible="False"
|
||||
Content="{TemplateBinding OffContent}"
|
||||
ContentTemplate="{TemplateBinding OffContentTemplate}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AutomationProperties.AccessibilityView="Raw" />
|
||||
<ContentPresenter x:Name="OnContentPresenter"
|
||||
Grid.RowSpan="3"
|
||||
Grid.Column="0"
|
||||
Opacity="0"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
IsHitTestVisible="False"
|
||||
Content="{TemplateBinding OnContent}"
|
||||
ContentTemplate="{TemplateBinding OnContentTemplate}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AutomationProperties.AccessibilityView="Raw" />
|
||||
<Rectangle x:Name="OuterBorder"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Height="20"
|
||||
Width="40"
|
||||
RadiusX="10"
|
||||
RadiusY="10"
|
||||
Fill="{ThemeResource ToggleSwitchFillOff}"
|
||||
Stroke="{ThemeResource ToggleSwitchStrokeOff}"
|
||||
StrokeThickness="{ThemeResource ToggleSwitchOuterBorderStrokeThickness}" />
|
||||
<Rectangle x:Name="SwitchKnobBounds"
|
||||
Grid.Row="1"
|
||||
Height="20"
|
||||
Width="40"
|
||||
RadiusX="10"
|
||||
RadiusY="10"
|
||||
Grid.Column="2"
|
||||
Fill="{ThemeResource ToggleSwitchFillOn}"
|
||||
Stroke="{ThemeResource ToggleSwitchStrokeOn}"
|
||||
StrokeThickness="{ThemeResource ToggleSwitchOnStrokeThickness}"
|
||||
Opacity="0" />
|
||||
<Grid x:Name="SwitchKnob"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Left"
|
||||
Width="20"
|
||||
Height="20">
|
||||
<Border x:Name="SwitchKnobOn"
|
||||
Background="{ThemeResource ToggleSwitchKnobFillOn}"
|
||||
BorderBrush="{ThemeResource ToggleSwitchKnobStrokeOn}"
|
||||
contract7Present:BackgroundSizing="OuterBorderEdge"
|
||||
Width="12"
|
||||
Height="12"
|
||||
CornerRadius="7"
|
||||
Grid.Column="2"
|
||||
Opacity="0"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0,0,1,0"
|
||||
RenderTransformOrigin="0.5, 0.5">
|
||||
<Border.RenderTransform>
|
||||
<CompositeTransform/>
|
||||
</Border.RenderTransform>
|
||||
</Border>
|
||||
<Rectangle x:Name="SwitchKnobOff"
|
||||
Fill="{ThemeResource ToggleSwitchKnobFillOff}"
|
||||
Width="12"
|
||||
Height="12"
|
||||
RadiusX="7"
|
||||
Grid.Column="2"
|
||||
RadiusY="7"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="-1,0,0,0"
|
||||
RenderTransformOrigin="0.5, 0.5">
|
||||
<Rectangle.RenderTransform>
|
||||
<CompositeTransform/>
|
||||
</Rectangle.RenderTransform>
|
||||
</Rectangle>
|
||||
<Grid.RenderTransform>
|
||||
<TranslateTransform x:Name="KnobTranslateTransform" />
|
||||
</Grid.RenderTransform>
|
||||
</Grid>
|
||||
<Thumb x:Name="SwitchThumb"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Grid.RowSpan="3"
|
||||
Grid.ColumnSpan="3">
|
||||
<Thumb.Template>
|
||||
<ControlTemplate TargetType="Thumb">
|
||||
<Rectangle Fill="Transparent" />
|
||||
</ControlTemplate>
|
||||
</Thumb.Template>
|
||||
</Thumb>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:controls="using:SettingsUI.Controls"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:SettingsUI.Controls">
|
||||
<Style x:Key="ListViewItemSettingStyle" TargetType="ListViewItem">
|
||||
<Setter Property="Margin" Value="0,0,0,2" />
|
||||
<Setter Property="Padding" Value="0,0,0,0" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Margin" Value="0,0,0,2"/>
|
||||
<Setter Property="Padding" Value="0,0,0,0"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
TargetType="controls:CheckBoxWithDescriptionControl"
|
||||
BasedOn="{StaticResource DefaultCheckBoxStyle}" />
|
||||
|
||||
<Style BasedOn="{StaticResource DefaultCheckBoxStyle}" TargetType="controls:CheckBoxWithDescriptionControl"/>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style x:Key="OobeSubtitleStyle" TargetType="TextBlock">
|
||||
<Setter Property="Margin" Value="0,16,0,0" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource DefaultTextForegroundThemeBrush}" />
|
||||
<Setter Property="AutomationProperties.HeadingLevel" Value="Level3" />
|
||||
<Setter Property="FontFamily" Value="XamlAutoFontFamily" />
|
||||
<Setter Property="FontSize" Value="{StaticResource BodyTextBlockFontSize}" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="TextTrimming" Value="CharacterEllipsis" />
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
<Setter Property="LineStackingStrategy" Value="MaxHeight" />
|
||||
<Setter Property="TextLineBounds" Value="Full" />
|
||||
<Setter Property="Margin" Value="0,16,0,0"/>
|
||||
<Setter Property="Foreground" Value="{ThemeResource DefaultTextForegroundThemeBrush}"/>
|
||||
<Setter Property="AutomationProperties.HeadingLevel" Value="Level3"/>
|
||||
<Setter Property="FontFamily" Value="XamlAutoFontFamily"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource BodyTextBlockFontSize}"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
|
||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||
<Setter Property="LineStackingStrategy" Value="MaxHeight"/>
|
||||
<Setter Property="TextLineBounds" Value="Full"/>
|
||||
</Style>
|
||||
|
||||
<x:Double x:Key="SecondaryTextFontSize">12</x:Double>
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<StaticResource x:Key="CardBackgroundBrush" ResourceKey="CardBackgroundFillColorDefaultBrush" />
|
||||
<StaticResource x:Key="CardBorderBrush" ResourceKey="CardStrokeColorDefaultBrush" />
|
||||
<StaticResource x:Key="CardPrimaryForegroundBrush" ResourceKey="TextFillColorPrimaryBrush" />
|
||||
<StaticResource x:Key="CardBackgroundBrush" ResourceKey="CardBackgroundFillColorDefaultBrush"/>
|
||||
<StaticResource x:Key="CardBorderBrush" ResourceKey="CardStrokeColorDefaultBrush"/>
|
||||
<StaticResource x:Key="CardPrimaryForegroundBrush" ResourceKey="TextFillColorPrimaryBrush"/>
|
||||
<SolidColorBrush x:Key="InfoBarInformationalSeverityBackgroundBrush" Color="#FF34424d"/>
|
||||
<Color x:Key="InfoBarInformationalSeverityIconBackground">#FF5fb2f2</Color>
|
||||
<Thickness x:Key="CardBorderThickness">1</Thickness>
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="CardBackgroundBrush" ResourceKey="CardBackgroundFillColorDefaultBrush" />
|
||||
<StaticResource x:Key="CardBorderBrush" ResourceKey="CardStrokeColorDefaultBrush" />
|
||||
<StaticResource x:Key="CardPrimaryForegroundBrush" ResourceKey="TextFillColorPrimaryBrush" />
|
||||
<StaticResource x:Key="CardBackgroundBrush" ResourceKey="CardBackgroundFillColorDefaultBrush"/>
|
||||
<StaticResource x:Key="CardBorderBrush" ResourceKey="CardStrokeColorDefaultBrush"/>
|
||||
<StaticResource x:Key="CardPrimaryForegroundBrush" ResourceKey="TextFillColorPrimaryBrush"/>
|
||||
<SolidColorBrush x:Key="InfoBarInformationalSeverityBackgroundBrush" Color="#FFd3e7f7"/>
|
||||
<Color x:Key="InfoBarInformationalSeverityIconBackground">#FF0063b1</Color>
|
||||
<Thickness x:Key="CardBorderThickness">1</Thickness>
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<StaticResource x:Key="CardBackgroundBrush" ResourceKey="SystemColorButtonFaceColorBrush" />
|
||||
<StaticResource x:Key="CardBorderBrush" ResourceKey="SystemColorButtonTextColorBrush" />
|
||||
<StaticResource x:Key="CardPrimaryForegroundBrush" ResourceKey="SystemColorButtonTextColorBrush" />
|
||||
<StaticResource x:Key="CardBackgroundBrush" ResourceKey="SystemColorButtonFaceColorBrush"/>
|
||||
<StaticResource x:Key="CardBorderBrush" ResourceKey="SystemColorButtonTextColorBrush"/>
|
||||
<StaticResource x:Key="CardPrimaryForegroundBrush" ResourceKey="SystemColorButtonTextColorBrush"/>
|
||||
<SolidColorBrush x:Key="InfoBarInformationalSeverityBackgroundBrush" Color="#FF34424d"/>
|
||||
<Color x:Key="InfoBarInformationalSeverityIconBackground">#FF5fb2f2</Color>
|
||||
<Thickness x:Key="CardBorderThickness">2</Thickness>
|
||||
|
||||
@@ -11,42 +11,38 @@
|
||||
|
||||
<!-- Styles -->
|
||||
<!-- Setting used in a Expander header -->
|
||||
<Style x:Key="ExpanderHeaderSettingStyle"
|
||||
TargetType="controls:Setting">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Padding" Value="0,14,0,14" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Style x:Key="ExpanderHeaderSettingStyle" TargetType="controls:Setting">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="0,14,0,14"/>
|
||||
<Setter Property="Margin" Value="0"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
</Style>
|
||||
|
||||
<Thickness x:Key="ExpanderChevronMargin">0,0,8,0</Thickness>
|
||||
|
||||
<!-- Setting used in a Expander header -->
|
||||
<Style x:Key="ExpanderContentSettingStyle"
|
||||
TargetType="controls:Setting">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0,1,0,0" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}" />
|
||||
<Setter Property="CornerRadius" Value="0" />
|
||||
<Setter Property="Padding" Value="{StaticResource ExpanderSettingMargin}" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Style x:Key="ExpanderContentSettingStyle" TargetType="controls:Setting">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0,1,0,0"/>
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}"/>
|
||||
<Setter Property="CornerRadius" Value="0"/>
|
||||
<Setter Property="Padding" Value="{StaticResource ExpanderSettingMargin}"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
</Style>
|
||||
|
||||
<!-- Setting expander style -->
|
||||
<Style x:Key="SettingExpanderStyle"
|
||||
TargetType="Expander">
|
||||
<Setter Property="Background" Value="{ThemeResource CardBackgroundBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource CardBorderThickness}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Style x:Key="SettingExpanderStyle" TargetType="Expander">
|
||||
<Setter Property="Background" Value="{ThemeResource CardBackgroundBrush}"/>
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource CardBorderThickness}"/>
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ExpanderSeparatorStyle"
|
||||
TargetType="Rectangle">
|
||||
<Setter Property="Height" Value="1" />
|
||||
<Setter Property="Stroke" Value="{ThemeResource CardBorderBrush}" />
|
||||
<Style x:Key="ExpanderSeparatorStyle" TargetType="Rectangle">
|
||||
<Setter Property="Height" Value="1"/>
|
||||
<Setter Property="Stroke" Value="{ThemeResource CardBorderBrush}"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="../Styles/Common.xaml"/>
|
||||
<ResourceDictionary Source="../Styles/TextBlock.xaml"/>
|
||||
@@ -9,6 +7,6 @@
|
||||
<ResourceDictionary Source="../Themes/Colors.xaml"/>
|
||||
<ResourceDictionary Source="../Themes/SettingsExpanderStyles.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
|
||||
<x:Double x:Key="SettingActionControlMinWidth">240</x:Double>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -144,4 +145,34 @@ internal static partial class IocHttpClientConfiguration
|
||||
sourceCodeBuilder.Append(line);
|
||||
}
|
||||
}
|
||||
|
||||
private class HttpClientSyntaxContextReceiver : ISyntaxContextReceiver
|
||||
{
|
||||
/// <summary>
|
||||
/// 注入特性的名称
|
||||
/// </summary>
|
||||
public const string AttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientAttribute";
|
||||
|
||||
/// <summary>
|
||||
/// 所有需要注入的类型符号
|
||||
/// </summary>
|
||||
public List<INamedTypeSymbol> Classes { get; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
|
||||
{
|
||||
// any class with at least one attribute is a candidate for injection generation
|
||||
if (context.Node is ClassDeclarationSyntax classDeclarationSyntax && classDeclarationSyntax.AttributeLists.Count > 0)
|
||||
{
|
||||
// get as named type symbol
|
||||
if (context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax) is INamedTypeSymbol classSymbol)
|
||||
{
|
||||
if (classSymbol.GetAttributes().Any(ad => ad.AttributeClass!.ToDisplayString() == AttributeName))
|
||||
{
|
||||
Classes.Add(classSymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Snap.Hutao.SourceGeneration.DedendencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// Created on demand before each generation pass
|
||||
/// </summary>
|
||||
public class HttpClientSyntaxContextReceiver : ISyntaxContextReceiver
|
||||
{
|
||||
/// <summary>
|
||||
/// 注入特性的名称
|
||||
/// </summary>
|
||||
public const string AttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient.HttpClientAttribute";
|
||||
|
||||
/// <summary>
|
||||
/// 所有需要注入的类型符号
|
||||
/// </summary>
|
||||
public List<INamedTypeSymbol> Classes { get; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
|
||||
{
|
||||
// any class with at least one attribute is a candidate for injection generation
|
||||
if (context.Node is ClassDeclarationSyntax classDeclarationSyntax && classDeclarationSyntax.AttributeLists.Count > 0)
|
||||
{
|
||||
// get as named type symbol
|
||||
if (context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax) is INamedTypeSymbol classSymbol)
|
||||
{
|
||||
if (classSymbol.GetAttributes().Any(ad => ad.AttributeClass!.ToDisplayString() == AttributeName))
|
||||
{
|
||||
Classes.Add(classSymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -122,4 +123,34 @@ internal static partial class ServiceCollectionExtension
|
||||
sourceCodeBuilder.Append(line);
|
||||
}
|
||||
}
|
||||
|
||||
private class InjectionSyntaxContextReceiver : ISyntaxContextReceiver
|
||||
{
|
||||
/// <summary>
|
||||
/// 注入特性的名称
|
||||
/// </summary>
|
||||
public const string AttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectionAttribute";
|
||||
|
||||
/// <summary>
|
||||
/// 所有需要注入的类型符号
|
||||
/// </summary>
|
||||
public List<INamedTypeSymbol> Classes { get; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
|
||||
{
|
||||
// any class with at least one attribute is a candidate for injection generation
|
||||
if (context.Node is ClassDeclarationSyntax classDeclarationSyntax && classDeclarationSyntax.AttributeLists.Count > 0)
|
||||
{
|
||||
// get as named type symbol
|
||||
if (context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax) is INamedTypeSymbol classSymbol)
|
||||
{
|
||||
if (classSymbol.GetAttributes().Any(ad => ad.AttributeClass!.ToDisplayString() == AttributeName))
|
||||
{
|
||||
Classes.Add(classSymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Snap.Hutao.SourceGeneration.DedendencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// Created on demand before each generation pass
|
||||
/// </summary>
|
||||
public class InjectionSyntaxContextReceiver : ISyntaxContextReceiver
|
||||
{
|
||||
/// <summary>
|
||||
/// 注入特性的名称
|
||||
/// </summary>
|
||||
public const string AttributeName = "Snap.Hutao.Core.DependencyInjection.Annotation.InjectionAttribute";
|
||||
|
||||
/// <summary>
|
||||
/// 所有需要注入的类型符号
|
||||
/// </summary>
|
||||
public List<INamedTypeSymbol> Classes { get; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
|
||||
{
|
||||
// any class with at least one attribute is a candidate for injection generation
|
||||
if (context.Node is ClassDeclarationSyntax classDeclarationSyntax && classDeclarationSyntax.AttributeLists.Count > 0)
|
||||
{
|
||||
// get as named type symbol
|
||||
if (context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax) is INamedTypeSymbol classSymbol)
|
||||
{
|
||||
if (classSymbol.GetAttributes().Any(ad => ad.AttributeClass!.ToDisplayString() == AttributeName))
|
||||
{
|
||||
Classes.Add(classSymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,28 +21,28 @@
|
||||
<Color x:Key="CompatBackgroundColor">#FF272727</Color>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<!--Modify Window title bar color-->
|
||||
|
||||
<!-- Modify Window title bar color -->
|
||||
<StaticResource x:Key="WindowCaptionBackground" ResourceKey="ControlFillColorTransparentBrush"/>
|
||||
<StaticResource x:Key="WindowCaptionBackgroundDisabled" ResourceKey="ControlFillColorTransparentBrush"/>
|
||||
<!--Page Transparent Background-->
|
||||
<!-- Page Transparent Background -->
|
||||
<StaticResource x:Key="ApplicationPageBackgroundThemeBrush" ResourceKey="ControlFillColorTransparentBrush"/>
|
||||
<!--IconFont-->
|
||||
<!-- IconFont -->
|
||||
<FontFamily x:Key="SymbolThemeFontFamily">ms-appx:///Resource/Font/Segoe Fluent Icons.ttf#Segoe Fluent Icons</FontFamily>
|
||||
<!--InfoBar Resource-->
|
||||
<!-- InfoBar Resource -->
|
||||
<Thickness x:Key="InfoBarIconMargin">6,16,16,16</Thickness>
|
||||
<Thickness x:Key="InfoBarContentRootPadding">16,0,0,0</Thickness>
|
||||
<!--Pivot Resource-->
|
||||
<!-- Pivot Resource -->
|
||||
<x:Double x:Key="PivotHeaderItemFontSize">16</x:Double>
|
||||
<Thickness x:Key="PivotHeaderItemMargin">16,0,0,0</Thickness>
|
||||
<Thickness x:Key="PivotItemMargin">0</Thickness>
|
||||
<!--CornerRadius-->
|
||||
<!-- CornerRadius -->
|
||||
<CornerRadius x:Key="CompatCornerRadius">6</CornerRadius>
|
||||
<CornerRadius x:Key="CompatCornerRadiusTop">6,6,0,0</CornerRadius>
|
||||
<CornerRadius x:Key="CompatCornerRadiusRight">0,6,6,0</CornerRadius>
|
||||
<CornerRadius x:Key="CompatCornerRadiusBottom">0,0,6,6</CornerRadius>
|
||||
<CornerRadius x:Key="CompatCornerRadiusSmall">2</CornerRadius>
|
||||
<!--Converters-->
|
||||
<!-- Converters -->
|
||||
<cwuc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
|
||||
<shmmc:AchievementIconConverter x:Key="AchievementIconConverter"/>
|
||||
<shmmc:AvatarIconConverter x:Key="AvatarIconConverter"/>
|
||||
@@ -59,15 +59,15 @@
|
||||
<shmmc:QualityColorConverter x:Key="QualityColorConverter"/>
|
||||
<shmmc:WeaponTypeIconConverter x:Key="WeaponTypeIconConverter"/>
|
||||
<shvc:BoolToVisibilityRevertConverter x:Key="BoolToVisibilityRevertConverter"/>
|
||||
|
||||
<!--Styles-->
|
||||
|
||||
<!-- Styles -->
|
||||
<Style
|
||||
x:Key="LargeGridViewItemStyle"
|
||||
TargetType="GridViewItem"
|
||||
BasedOn="{StaticResource DefaultGridViewItemStyle}">
|
||||
BasedOn="{StaticResource DefaultGridViewItemStyle}"
|
||||
TargetType="GridViewItem">
|
||||
<Setter Property="Margin" Value="0,0,12,12"/>
|
||||
</Style>
|
||||
<!--ItemsPanelTemplate-->
|
||||
<!-- ItemsPanelTemplate -->
|
||||
<ItemsPanelTemplate x:Key="ItemsStackPanelTemplate">
|
||||
<ItemsStackPanel/>
|
||||
</ItemsPanelTemplate>
|
||||
|
||||
@@ -64,6 +64,20 @@ public static class DbSetExtension
|
||||
return dbSet.Context().SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步添加并保存
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">实体类型</typeparam>
|
||||
/// <param name="dbSet">数据库集</param>
|
||||
/// <param name="entity">实体</param>
|
||||
/// <returns>影响条数</returns>
|
||||
public static Task<int> AddAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||
where TEntity : class
|
||||
{
|
||||
dbSet.Add(entity);
|
||||
return dbSet.Context().SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加列表并保存
|
||||
/// </summary>
|
||||
@@ -78,6 +92,20 @@ public static class DbSetExtension
|
||||
return dbSet.Context().SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步添加列表并保存
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">实体类型</typeparam>
|
||||
/// <param name="dbSet">数据库集</param>
|
||||
/// <param name="entities">实体</param>
|
||||
/// <returns>影响条数</returns>
|
||||
public static Task<int> AddRangeAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, IEnumerable<TEntity> entities)
|
||||
where TEntity : class
|
||||
{
|
||||
dbSet.AddRange(entities);
|
||||
return dbSet.Context().SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除并保存
|
||||
/// </summary>
|
||||
@@ -93,17 +121,17 @@ public static class DbSetExtension
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除并保存
|
||||
/// 异步移除并保存
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">实体类型</typeparam>
|
||||
/// <param name="dbSet">数据库集</param>
|
||||
/// <param name="entities">实体列表</param>
|
||||
/// <param name="entity">实体</param>
|
||||
/// <returns>影响条数</returns>
|
||||
public static int RemoveRangeAndSave<TEntity>(this DbSet<TEntity> dbSet, IEnumerable<TEntity> entities)
|
||||
public static Task<int> RemoveAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||
where TEntity : class
|
||||
{
|
||||
dbSet.RemoveRange(entities);
|
||||
return dbSet.Context().SaveChanges();
|
||||
dbSet.Remove(entity);
|
||||
return dbSet.Context().SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -119,4 +147,18 @@ public static class DbSetExtension
|
||||
dbSet.Update(entity);
|
||||
return dbSet.Context().SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步更新并保存
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">实体类型</typeparam>
|
||||
/// <param name="dbSet">数据库集</param>
|
||||
/// <param name="entity">实体</param>
|
||||
/// <returns>影响条数</returns>
|
||||
public static Task<int> UpdateAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||
where TEntity : class
|
||||
{
|
||||
dbSet.Update(entity);
|
||||
return dbSet.Context().SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
81
src/Snap.Hutao/Snap.Hutao/Core/Database/ScopedDbCurrent.cs
Normal file
81
src/Snap.Hutao/Snap.Hutao/Core/Database/ScopedDbCurrent.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Snap.Hutao.Core.Database;
|
||||
|
||||
/// <summary>
|
||||
/// 范围化的数据库当前项
|
||||
/// 简化对数据库中选中项的管理
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">实体的类型</typeparam>
|
||||
/// <typeparam name="TMessage">消息的类型</typeparam>
|
||||
internal class ScopedDbCurrent<TEntity, TMessage>
|
||||
where TEntity : class, ISelectable
|
||||
where TMessage : Message.ValueChangedMessage<TEntity>, new()
|
||||
{
|
||||
private readonly IServiceScopeFactory scopeFactory;
|
||||
private readonly Func<IServiceProvider, DbSet<TEntity>> dbSetFunc;
|
||||
private readonly IMessenger messenger;
|
||||
|
||||
private TEntity? current;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的数据库当前项
|
||||
/// </summary>
|
||||
/// <param name="scopeFactory">范围工厂</param>
|
||||
/// <param name="dbSetFunc">数据集</param>
|
||||
/// <param name="messenger">消息器</param>
|
||||
public ScopedDbCurrent(IServiceScopeFactory scopeFactory, Func<IServiceProvider, DbSet<TEntity>> dbSetFunc, IMessenger messenger)
|
||||
{
|
||||
this.scopeFactory = scopeFactory;
|
||||
this.dbSetFunc = dbSetFunc;
|
||||
this.messenger = messenger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前选中的项
|
||||
/// </summary>
|
||||
public TEntity? Current
|
||||
{
|
||||
get => current;
|
||||
set
|
||||
{
|
||||
// prevent useless sets
|
||||
if (current == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
DbSet<TEntity> dbSet = dbSetFunc(scope.ServiceProvider);
|
||||
|
||||
// only update when not processing a deletion
|
||||
if (value != null)
|
||||
{
|
||||
if (current != null)
|
||||
{
|
||||
current.IsSelected = false;
|
||||
dbSet.UpdateAndSave(current);
|
||||
}
|
||||
}
|
||||
|
||||
TMessage message = new() { OldValue = current, NewValue = value };
|
||||
|
||||
current = value;
|
||||
|
||||
if (current != null)
|
||||
{
|
||||
current.IsSelected = true;
|
||||
dbSet.UpdateAndSave(current);
|
||||
}
|
||||
|
||||
messenger.Send(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,16 @@ namespace Snap.Hutao.Core.Database;
|
||||
/// </summary>
|
||||
public static class SettingEntryHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// "True"
|
||||
/// </summary>
|
||||
public static readonly string TrueString = true.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// "False"
|
||||
/// </summary>
|
||||
public static readonly string FalseString = false.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// 获取或添加一个对应的设置
|
||||
/// </summary>
|
||||
@@ -37,17 +47,16 @@ public static class SettingEntryHelper
|
||||
/// </summary>
|
||||
/// <param name="dbSet">设置集</param>
|
||||
/// <param name="key">键</param>
|
||||
/// <param name="valueFactory">值工厂</param>
|
||||
/// <param name="value">值</param>
|
||||
/// <returns>设置</returns>
|
||||
public static SettingEntry SingleOrAdd(this DbSet<SettingEntry> dbSet, string key, Func<string> valueFactory)
|
||||
public static async Task<SettingEntry> SingleOrAddAsync(this DbSet<SettingEntry> dbSet, string key, string value)
|
||||
{
|
||||
SettingEntry? entry = dbSet.SingleOrDefault(entry => key == entry.Key);
|
||||
SettingEntry? entry = await dbSet.SingleOrDefaultAsync(entry => key == entry.Key).ConfigureAwait(false);
|
||||
|
||||
if (entry == null)
|
||||
{
|
||||
entry = new(key, valueFactory());
|
||||
dbSet.Add(entry);
|
||||
dbSet.Context().SaveChanges();
|
||||
entry = new(key, value);
|
||||
await dbSet.AddAndSaveAsync(entry).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return entry;
|
||||
|
||||
@@ -40,7 +40,6 @@ internal readonly struct ValueStopwatch
|
||||
/// 获取经过的时间
|
||||
/// </summary>
|
||||
/// <returns>经过的时间</returns>
|
||||
/// <exception cref="InvalidOperationException">当前的停表未合理的初始化</exception>
|
||||
public long GetElapsedTimestamp()
|
||||
{
|
||||
// Start timestamp can't be zero in an initialized ValueStopwatch.
|
||||
@@ -59,7 +58,6 @@ internal readonly struct ValueStopwatch
|
||||
/// 获取经过的时间
|
||||
/// </summary>
|
||||
/// <returns>经过的时间</returns>
|
||||
/// <exception cref="InvalidOperationException">当前的停表未合理的初始化</exception>
|
||||
public TimeSpan GetElapsedTime()
|
||||
{
|
||||
return new TimeSpan(GetElapsedTimestamp());
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Exception;
|
||||
|
||||
/// <summary>
|
||||
/// Error codes used by COM-based APIs.
|
||||
/// </summary>
|
||||
public enum COMError : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// could not be found.
|
||||
/// </summary>
|
||||
STG_E_FILENOTFOUND = 0x80030002,
|
||||
|
||||
/// <summary>
|
||||
/// The component cannot be found.
|
||||
/// </summary>
|
||||
WINCODEC_ERR_COMPONENTNOTFOUND = 0x88982F50,
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Core.Exception;
|
||||
|
||||
/// <summary>
|
||||
/// COM异常扩展
|
||||
/// </summary>
|
||||
internal static class COMExceptionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 比较COM异常是否与某个错误代码等价
|
||||
/// </summary>
|
||||
/// <param name="exception">异常</param>
|
||||
/// <param name="code">错误代码</param>
|
||||
/// <returns>是否为该错误</returns>
|
||||
public static bool Is(this COMException exception, COMError code)
|
||||
{
|
||||
return exception.HResult == unchecked((int)code);
|
||||
}
|
||||
}
|
||||
@@ -28,12 +28,18 @@ internal sealed class TemporaryFile : IDisposable
|
||||
/// </summary>
|
||||
/// <param name="file">源文件</param>
|
||||
/// <returns>临时文件</returns>
|
||||
public static TemporaryFile CreateFromFileCopy(string file)
|
||||
public static TemporaryFile? CreateFromFileCopy(string file)
|
||||
{
|
||||
TemporaryFile temporaryFile = new();
|
||||
File.Copy(file, temporaryFile.Path, true);
|
||||
|
||||
return temporaryFile;
|
||||
try
|
||||
{
|
||||
File.Copy(file, temporaryFile.Path, true);
|
||||
return temporaryFile;
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Json.Converter;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
|
||||
namespace Snap.Hutao.Core.Json.Annotation;
|
||||
|
||||
/// <summary>
|
||||
@@ -9,6 +12,18 @@ namespace Snap.Hutao.Core.Json.Annotation;
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
|
||||
internal class JsonEnumAttribute : Attribute
|
||||
{
|
||||
private static readonly Type ConfigurableEnumConverterType = typeof(ConfigurableEnumConverter<>);
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的Json枚举声明
|
||||
/// </summary>
|
||||
/// <param name="readAndWriteAs">读取与写入</param>
|
||||
public JsonEnumAttribute(JsonSerializeType readAndWriteAs)
|
||||
{
|
||||
ReadAs = readAndWriteAs;
|
||||
WriteAs = readAndWriteAs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的Json枚举声明
|
||||
/// </summary>
|
||||
@@ -29,4 +44,15 @@ internal class JsonEnumAttribute : Attribute
|
||||
/// 写入形式
|
||||
/// </summary>
|
||||
public JsonSerializeType WriteAs { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的转换器
|
||||
/// </summary>
|
||||
/// <param name="info">属性信息</param>
|
||||
/// <returns>Json转换器</returns>
|
||||
internal JsonConverter CreateConverter(JsonPropertyInfo info)
|
||||
{
|
||||
Type converterType = ConfigurableEnumConverterType.MakeGenericType(info.PropertyType);
|
||||
return (JsonConverter)Activator.CreateInstance(converterType, ReadAs, WriteAs)!;
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,12 @@
|
||||
namespace Snap.Hutao.Core.Json.Annotation;
|
||||
|
||||
/// <summary>
|
||||
/// Json 文本字符串序列化类型
|
||||
/// Json 序列化类型
|
||||
/// </summary>
|
||||
public enum JsonSerializeType
|
||||
{
|
||||
/// <summary>
|
||||
/// 数字
|
||||
/// Int32
|
||||
/// </summary>
|
||||
Int32,
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Json.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// 枚举 - 字符串数字 转换器
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">枚举的类型</typeparam>
|
||||
internal class EnumStringValueConverter<TEnum> : JsonConverter<TEnum>
|
||||
where TEnum : struct, Enum
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.GetString() is string str)
|
||||
{
|
||||
return Enum.Parse<TEnum>(str);
|
||||
}
|
||||
|
||||
throw Must.NeverHappen();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString("D"));
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ namespace Snap.Hutao.Core.Json;
|
||||
internal static class JsonTypeInfoResolvers
|
||||
{
|
||||
private static readonly Type JsonEnumAttributeType = typeof(JsonEnumAttribute);
|
||||
private static readonly Type ConfigurableEnumConverterType = typeof(ConfigurableEnumConverter<>);
|
||||
|
||||
/// <summary>
|
||||
/// 解析枚举类型
|
||||
@@ -32,10 +31,9 @@ internal static class JsonTypeInfoResolvers
|
||||
|
||||
foreach (JsonPropertyInfo enumProperty in enumProperties)
|
||||
{
|
||||
JsonEnumAttribute attr = enumProperty.PropertyType.GetCustomAttribute<JsonEnumAttribute>()!;
|
||||
Type converterType = ConfigurableEnumConverterType.MakeGenericType(enumProperty.PropertyType);
|
||||
JsonEnumAttribute attr = enumProperty.AttributeProvider!.GetCustomAttributes(false).OfType<JsonEnumAttribute>().Single();
|
||||
|
||||
enumProperty.CustomConverter = (JsonConverter)Activator.CreateInstance(converterType, attr.ReadAs, attr.WriteAs)!;
|
||||
enumProperty.CustomConverter = attr.CreateConverter(enumProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ namespace Snap.Hutao.Core.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// 调度器队列切换操作
|
||||
/// 等待此类型对象后上下文会被切换至主线程
|
||||
/// </summary>
|
||||
public readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQueueSwitchOperation>, IAwaiter
|
||||
{
|
||||
@@ -29,9 +30,9 @@ public readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQueue
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnCompleted(Action continuation)
|
||||
public DispatherQueueSwitchOperation GetAwaiter()
|
||||
{
|
||||
dispatherQueue.TryEnqueue(() => { continuation(); });
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -40,8 +41,8 @@ public readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQueue
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DispatherQueueSwitchOperation GetAwaiter()
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
return this;
|
||||
dispatherQueue.TryEnqueue(() => { continuation(); });
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Snap.Hutao.Core.Threading;
|
||||
|
||||
/// <summary>
|
||||
@@ -8,13 +10,25 @@ namespace Snap.Hutao.Core.Threading;
|
||||
/// </summary>
|
||||
internal static class ThreadHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用此静态方法以 异步切换到 后台线程
|
||||
/// </summary>
|
||||
/// <remarks>使用 <see cref="SwitchToMainThreadAsync"/> 异步切换到 主线程</remarks>
|
||||
/// <returns>等待体</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ThreadPoolSwitchOperation SwitchToBackgroundAsync()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用此静态方法以 异步切换到 主线程
|
||||
/// </summary>
|
||||
/// <remarks>使用 <see cref="Task.Yield"/> 异步切换到 后台线程</remarks>
|
||||
/// <remarks>使用 <see cref="SwitchToBackgroundAsync"/> 异步切换到 后台线程</remarks>
|
||||
/// <returns>等待体</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static DispatherQueueSwitchOperation SwitchToMainThreadAsync()
|
||||
{
|
||||
return new(Program.DispatcherQueue!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Threading.Abstraction;
|
||||
|
||||
namespace Snap.Hutao.Core.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// 线程池切换操作
|
||||
/// 等待此类型对象后上下文会被切换至线程池线程
|
||||
/// </summary>
|
||||
public readonly struct ThreadPoolSwitchOperation : IAwaitable<ThreadPoolSwitchOperation>, IAwaiter, ICriticalAwaiter
|
||||
{
|
||||
private static readonly WaitCallback WaitCallbackRunAction = RunAction;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsCompleted { get => false; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ThreadPoolSwitchOperation GetAwaiter()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void GetResult()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
QueueContinuation(continuation, flowContext: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
QueueContinuation(continuation, flowContext: false);
|
||||
}
|
||||
|
||||
private static void QueueContinuation(Action continuation, bool flowContext)
|
||||
{
|
||||
if (flowContext)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(WaitCallbackRunAction, continuation);
|
||||
}
|
||||
else
|
||||
{
|
||||
ThreadPool.UnsafeQueueUserWorkItem(WaitCallbackRunAction, continuation);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunAction(object? state)
|
||||
{
|
||||
((Action)state!)();
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ namespace Snap.Hutao.Message;
|
||||
/// <summary>
|
||||
/// 成就存档切换消息
|
||||
/// </summary>
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
|
||||
internal class AchievementArchiveChangedMessage : ValueChangedMessage<AchievementArchive>
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
|
||||
namespace Snap.Hutao.Message;
|
||||
|
||||
/// <summary>
|
||||
/// 养成计划切换消息
|
||||
/// </summary>
|
||||
internal class CultivateProjectChangedMessage : ValueChangedMessage<CultivateProject>
|
||||
{
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class Achievement : ObservableObject
|
||||
// Only update state when checked
|
||||
if (value)
|
||||
{
|
||||
Entity.Status = Intrinsic.AchievementInfoStatus.ACHIEVEMENT_POINT_TAKEN;
|
||||
Entity.Status = Intrinsic.AchievementInfoStatus.STATUS_REWARD_TAKEN;
|
||||
Entity.Time = DateTimeOffset.Now;
|
||||
OnPropertyChanged(nameof(Time));
|
||||
}
|
||||
|
||||
@@ -14,4 +14,9 @@ public class Skill : NameIconDescription
|
||||
/// 技能属性
|
||||
/// </summary>
|
||||
public LevelParam<string, ParameterInfo> Info { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 技能等级,仅用于养成计算
|
||||
/// </summary>
|
||||
internal int Level { get; set; }
|
||||
}
|
||||
|
||||
@@ -30,11 +30,6 @@ public class AchievementArchive : ISelectable
|
||||
/// </summary>
|
||||
public bool IsSelected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 成就
|
||||
/// </summary>
|
||||
public virtual ICollection<Achievement> Achievements { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的存档
|
||||
/// </summary>
|
||||
|
||||
@@ -23,6 +23,12 @@ public class ObjectCacheEntry
|
||||
/// </summary>
|
||||
public DateTimeOffset ExpireTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取该对象是否过期
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public bool IsExpired { get => ExpireTime > DateTimeOffset.Now; }
|
||||
|
||||
/// <summary>
|
||||
/// 值字符串
|
||||
/// </summary>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Json.Converter;
|
||||
using Snap.Hutao.Core.Json.Annotation;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
|
||||
namespace Snap.Hutao.Model.InterChange.GachaLog;
|
||||
@@ -15,6 +15,6 @@ public class UIGFItem : GachaLogItem
|
||||
/// 额外祈愿映射
|
||||
/// </summary>
|
||||
[JsonPropertyName("uigf_gacha_type")]
|
||||
[JsonConverter(typeof(EnumStringValueConverter<GachaConfigType>))]
|
||||
[JsonEnum(JsonSerializeType.Int32AsString)]
|
||||
public GachaConfigType UIGFGachaType { get; set; } = default!;
|
||||
}
|
||||
@@ -16,20 +16,20 @@ public enum AchievementInfoStatus
|
||||
/// <summary>
|
||||
/// 非法值
|
||||
/// </summary>
|
||||
ACHIEVEMENT_INVALID = 0,
|
||||
STATUS_INVALID = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 未完成
|
||||
/// </summary>
|
||||
ACHIEVEMENT_UNFINISHED = 1,
|
||||
STATUS_UNFINISHED = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 已完成
|
||||
/// </summary>
|
||||
ACHIEVEMENT_FINISHED = 2,
|
||||
STATUS_FINISHED = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 奖励已领取
|
||||
/// </summary>
|
||||
ACHIEVEMENT_POINT_TAKEN = 3,
|
||||
STATUS_REWARD_TAKEN = 3,
|
||||
}
|
||||
65
src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/AssociationType.cs
Normal file
65
src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/AssociationType.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
/// <summary>
|
||||
/// 从属地区
|
||||
/// </summary>
|
||||
public enum AssociationType
|
||||
{
|
||||
/// <summary>
|
||||
/// 无
|
||||
/// </summary>
|
||||
ASSOC_TYPE_NONE,
|
||||
|
||||
/// <summary>
|
||||
/// 蒙德
|
||||
/// </summary>
|
||||
ASSOC_TYPE_MONDSTADT,
|
||||
|
||||
/// <summary>
|
||||
/// 璃月
|
||||
/// </summary>
|
||||
ASSOC_TYPE_LIYUE,
|
||||
|
||||
/// <summary>
|
||||
/// 主角
|
||||
/// </summary>
|
||||
ASSOC_TYPE_MAINACTOR,
|
||||
|
||||
/// <summary>
|
||||
/// 愚人众
|
||||
/// </summary>
|
||||
ASSOC_TYPE_FATUI,
|
||||
|
||||
/// <summary>
|
||||
/// 稻妻
|
||||
/// </summary>
|
||||
ASSOC_TYPE_INAZUMA,
|
||||
|
||||
/// <summary>
|
||||
/// 游侠
|
||||
/// </summary>
|
||||
ASSOC_TYPE_RANGER,
|
||||
|
||||
/// <summary>
|
||||
/// 须弥
|
||||
/// </summary>
|
||||
ASSOC_TYPE_SUMERU,
|
||||
|
||||
/// <summary>
|
||||
/// 枫丹
|
||||
/// </summary>
|
||||
ASSOC_TYPE_FONTAINE,
|
||||
|
||||
/// <summary>
|
||||
/// 纳塔
|
||||
/// </summary>
|
||||
ASSOC_TYPE_NATLAN,
|
||||
|
||||
/// <summary>
|
||||
/// 至冬
|
||||
/// </summary>
|
||||
ASSOC_TYPE_SNEZHNAYA,
|
||||
}
|
||||
40
src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/BodyType.cs
Normal file
40
src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/BodyType.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
/// <summary>
|
||||
/// 体型
|
||||
/// </summary>
|
||||
public enum BodyType
|
||||
{
|
||||
/// <summary>
|
||||
/// 无
|
||||
/// </summary>
|
||||
BODY_NONE,
|
||||
|
||||
/// <summary>
|
||||
/// 男孩
|
||||
/// </summary>
|
||||
BODY_BOY,
|
||||
|
||||
/// <summary>
|
||||
/// 女孩
|
||||
/// </summary>
|
||||
BODY_GIRL,
|
||||
|
||||
/// <summary>
|
||||
/// 成女
|
||||
/// </summary>
|
||||
BODY_LADY,
|
||||
|
||||
/// <summary>
|
||||
/// 成男
|
||||
/// </summary>
|
||||
BODY_MALE,
|
||||
|
||||
/// <summary>
|
||||
/// 萝莉
|
||||
/// </summary>
|
||||
BODY_LOLI,
|
||||
}
|
||||
@@ -5,7 +5,6 @@ namespace Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
/// <summary>
|
||||
/// 武器类型
|
||||
/// https://github.com/Grasscutters/Grasscutter/blob/development/src/main/java/emu/grasscutter/game/props/WeaponType.java
|
||||
/// </summary>
|
||||
[SuppressMessage("", "SA1124")]
|
||||
public enum WeaponType
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Json.Annotation;
|
||||
using Snap.Hutao.Model.Binding.Gacha;
|
||||
using Snap.Hutao.Model.Binding.Gacha.Abstraction;
|
||||
using Snap.Hutao.Model.Binding.Hutao;
|
||||
@@ -29,7 +30,8 @@ public class Avatar : IStatisticsItemSource, ISummaryItemSource, INameQuality
|
||||
/// <summary>
|
||||
/// 体型
|
||||
/// </summary>
|
||||
public string Body { get; set; } = default!;
|
||||
[JsonEnum(JsonSerializeType.String)]
|
||||
public BodyType Body { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 正面图标
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Json.Annotation;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Avatar;
|
||||
|
||||
/// <summary>
|
||||
@@ -21,7 +24,8 @@ public class FetterInfo
|
||||
/// <summary>
|
||||
/// 地区
|
||||
/// </summary>
|
||||
public string Association { get; set; } = default!;
|
||||
[JsonEnum(JsonSerializeType.String)]
|
||||
public AssociationType Association { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 属于组织
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Snap.Hutao.Model.Metadata;
|
||||
[SuppressMessage("", "SA1600")]
|
||||
public static class AvatarIds
|
||||
{
|
||||
public static readonly AvatarId Kate = 10000001;
|
||||
public static readonly AvatarId Ayaka = 10000002;
|
||||
public static readonly AvatarId Qin = 10000003;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"$schema": "https://aka.ms/CsWin32.schema.json",
|
||||
"allowMarshaling": true,
|
||||
"public": true
|
||||
"public": true,
|
||||
"emitSingleFile": true
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
|
||||
Publisher="CN=DGP Studio"
|
||||
Version="1.2.6.0" />
|
||||
Version="1.2.7.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>胡桃</DisplayName>
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace Snap.Hutao.Service.Achievement;
|
||||
|
||||
/// <summary>
|
||||
/// 成就数据库操作
|
||||
/// 双指针操作
|
||||
/// </summary>
|
||||
public class AchievementDbOperation
|
||||
{
|
||||
@@ -39,7 +40,6 @@ public class AchievementDbOperation
|
||||
|
||||
int add = 0;
|
||||
int update = 0;
|
||||
int remove = 0;
|
||||
|
||||
using (IEnumerator<EntityAchievement> entityEnumerator = oldData.GetEnumerator())
|
||||
{
|
||||
@@ -105,7 +105,7 @@ public class AchievementDbOperation
|
||||
}
|
||||
}
|
||||
|
||||
return new(add, update, remove);
|
||||
return new(add, update, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -57,30 +57,28 @@ internal class AchievementService : IAchievementService
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task RemoveArchiveAsync(EntityArchive archive)
|
||||
public async Task RemoveArchiveAsync(EntityArchive archive)
|
||||
{
|
||||
Must.NotNull(archiveCollection!);
|
||||
|
||||
// Sync cache
|
||||
archiveCollection.Remove(archive);
|
||||
// Keep this on main thread.
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
archiveCollection!.Remove(archive);
|
||||
|
||||
// Sync database
|
||||
appDbContext.AchievementArchives.Remove(archive);
|
||||
return appDbContext.SaveChangesAsync();
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
await appDbContext.AchievementArchives.RemoveAndSaveAsync(archive).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<ArchiveAddResult> TryAddArchiveAsync(EntityArchive newArchive)
|
||||
{
|
||||
if (string.IsNullOrEmpty(newArchive.Name))
|
||||
if (string.IsNullOrWhiteSpace(newArchive.Name))
|
||||
{
|
||||
return ArchiveAddResult.InvalidName;
|
||||
}
|
||||
|
||||
Must.NotNull(archiveCollection!);
|
||||
|
||||
// 查找是否有相同的名称
|
||||
if (archiveCollection.SingleOrDefault(a => a.Name == newArchive.Name) is EntityArchive userWithSameUid)
|
||||
if (archiveCollection!.SingleOrDefault(a => a.Name == newArchive.Name) is EntityArchive userWithSameUid)
|
||||
{
|
||||
return ArchiveAddResult.AlreadyExists;
|
||||
}
|
||||
@@ -88,11 +86,11 @@ internal class AchievementService : IAchievementService
|
||||
{
|
||||
// Sync cache
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
archiveCollection.Add(newArchive);
|
||||
archiveCollection!.Add(newArchive);
|
||||
|
||||
// Sync database
|
||||
appDbContext.AchievementArchives.Add(newArchive);
|
||||
await appDbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
await appDbContext.AchievementArchives.AddAndSaveAsync(newArchive).ConfigureAwait(false);
|
||||
|
||||
return ArchiveAddResult.Added;
|
||||
}
|
||||
@@ -104,15 +102,12 @@ internal class AchievementService : IAchievementService
|
||||
Guid archiveId = archive.InnerId;
|
||||
List<EntityAchievement> entities = appDbContext.Achievements
|
||||
.Where(a => a.ArchiveId == archiveId)
|
||||
|
||||
// Important! Prevent multiple sql command for SingleOrDefault below.
|
||||
.ToList();
|
||||
|
||||
List<BindingAchievement> results = new();
|
||||
foreach (MetadataAchievement meta in metadata)
|
||||
{
|
||||
EntityAchievement entity = entities.SingleOrDefault(e => e.Id == meta.Id)
|
||||
?? EntityAchievement.Create(archiveId, meta.Id);
|
||||
EntityAchievement entity = entities.SingleOrDefault(e => e.Id == meta.Id) ?? EntityAchievement.Create(archiveId, meta.Id);
|
||||
|
||||
results.Add(new(meta, entity));
|
||||
}
|
||||
@@ -121,8 +116,9 @@ internal class AchievementService : IAchievementService
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<UIAF> ExportToUIAFAsync(EntityArchive archive)
|
||||
public async Task<UIAF> ExportToUIAFAsync(EntityArchive archive)
|
||||
{
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
List<UIAFItem> list = appDbContext.Achievements
|
||||
.Where(i => i.ArchiveId == archive.InnerId)
|
||||
.AsEnumerable()
|
||||
@@ -135,12 +131,15 @@ internal class AchievementService : IAchievementService
|
||||
List = list,
|
||||
};
|
||||
|
||||
return Task.FromResult(uigf);
|
||||
return uigf;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ImportResult ImportFromUIAF(EntityArchive archive, List<UIAFItem> list, ImportStrategy strategy)
|
||||
public async Task<ImportResult> ImportFromUIAFAsync(EntityArchive archive, List<UIAFItem> list, ImportStrategy strategy)
|
||||
{
|
||||
// return Task.Run(() => ImportFromUIAF(archive, list, strategy));
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
|
||||
Guid archiveId = archive.InnerId;
|
||||
|
||||
switch (strategy)
|
||||
@@ -170,12 +169,6 @@ internal class AchievementService : IAchievementService
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<ImportResult> ImportFromUIAFAsync(EntityArchive archive, List<UIAFItem> list, ImportStrategy strategy)
|
||||
{
|
||||
return Task.Run(() => ImportFromUIAF(archive, list, strategy));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void SaveAchievements(EntityArchive archive, IList<BindingAchievement> achievements)
|
||||
{
|
||||
|
||||
@@ -41,15 +41,6 @@ internal interface IAchievementService
|
||||
/// <returns>成就存档集合</returns>
|
||||
ObservableCollection<EntityArchive> GetArchiveCollection();
|
||||
|
||||
/// <summary>
|
||||
/// 导入UIAF数据
|
||||
/// </summary>
|
||||
/// <param name="archive">用户</param>
|
||||
/// <param name="list">UIAF数据</param>
|
||||
/// <param name="strategy">选项</param>
|
||||
/// <returns>导入结果</returns>
|
||||
ImportResult ImportFromUIAF(EntityArchive archive, List<UIAFItem> list, ImportStrategy strategy);
|
||||
|
||||
/// <summary>
|
||||
/// 异步导入UIAF数据
|
||||
/// </summary>
|
||||
|
||||
@@ -37,6 +37,7 @@ internal partial class AnnouncementService : IAnnouncementService
|
||||
return Must.NotNull((AnnouncementWrapper)cache!);
|
||||
}
|
||||
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
AnnouncementWrapper? wrapper = await announcementClient
|
||||
.GetAnnouncementsAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@@ -228,6 +228,11 @@ internal class AvatarInfoService : IAvatarInfoService
|
||||
|
||||
AvatarDetail? detailAvatar = await calculateClient.GetAvatarDetailAsync(userAndRole.User, userAndRole.Role, avatar).ConfigureAwait(false);
|
||||
|
||||
if (detailAvatar == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ModelAvatarInfo? entity = dbInfos.SingleOrDefault(i => i.Info.AvatarId == avatar.Id);
|
||||
|
||||
if (entity == null)
|
||||
|
||||
@@ -65,7 +65,7 @@ internal static class SummaryHelper
|
||||
return new();
|
||||
}
|
||||
|
||||
Dictionary<string, int> skillLevelMapCopy = new(skillLevelMap);
|
||||
Dictionary<string, int> skillExtraLeveledMap = new(skillLevelMap);
|
||||
|
||||
if (proudSkillExtraLevelMap != null)
|
||||
{
|
||||
@@ -74,7 +74,7 @@ internal static class SummaryHelper
|
||||
int skillGroupIdInt32 = int.Parse(skillGroupId);
|
||||
int skillId = proudSkills.Single(p => p.GroupId == skillGroupIdInt32).Id;
|
||||
|
||||
skillLevelMapCopy.Increase($"{skillId}", extraLevel);
|
||||
skillExtraLeveledMap.Increase(skillId.ToString(), extraLevel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,9 @@ internal static class SummaryHelper
|
||||
Name = proudableSkill.Name,
|
||||
Icon = SkillIconConverter.IconNameToUri(proudableSkill.Icon),
|
||||
Description = proudableSkill.Description,
|
||||
Info = DescParamDescriptor.Convert(proudableSkill.Proud, skillLevelMapCopy[$"{proudableSkill.Id}"]),
|
||||
|
||||
Level = skillLevelMap[proudableSkill.Id.ToString()],
|
||||
Info = DescParamDescriptor.Convert(proudableSkill.Proud, skillExtraLeveledMap[proudableSkill.Id.ToString()]),
|
||||
};
|
||||
|
||||
skills.Add(skill);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using Snap.Hutao.Model.Binding.AvatarProperty;
|
||||
using Snap.Hutao.Model.Binding.User;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
|
||||
namespace Snap.Hutao.Service.AvatarInfo;
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Snap.Hutao.Context.Database;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
|
||||
namespace Snap.Hutao.Service.Cultivation;
|
||||
|
||||
/// <summary>
|
||||
/// 养成计算服务
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Singleton, typeof(ICultivationService))]
|
||||
internal class CultivationService : ICultivationService
|
||||
{
|
||||
private readonly DbCurrent<CultivateProject, Message.CultivateProjectChangedMessage> dbCurrent;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的养成计算服务
|
||||
/// </summary>
|
||||
/// <param name="appDbContext">数据库上下文</param>
|
||||
/// <param name="messenger">消息器</param>
|
||||
public CultivationService(AppDbContext appDbContext, IMessenger messenger)
|
||||
{
|
||||
dbCurrent = new(appDbContext.CultivateProjects, messenger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前养成计划
|
||||
/// </summary>
|
||||
public CultivateProject? Current
|
||||
{
|
||||
get => dbCurrent.Current;
|
||||
set => dbCurrent.Current = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Cultivation;
|
||||
|
||||
/// <summary>
|
||||
/// 养成计算服务
|
||||
/// </summary>
|
||||
internal interface ICultivationService
|
||||
{
|
||||
}
|
||||
164
src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifier.cs
Normal file
164
src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifier.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.WinUI.Notifications;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Snap.Hutao.Context.Database;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Message;
|
||||
using Snap.Hutao.Model.Binding.User;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
|
||||
using System.Collections.ObjectModel;
|
||||
using WebDailyNote = Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote.DailyNote;
|
||||
|
||||
namespace Snap.Hutao.Service.DailyNote;
|
||||
|
||||
/// <summary>
|
||||
/// 实时便笺通知器
|
||||
/// </summary>
|
||||
internal class DailyNoteNotifier
|
||||
{
|
||||
private readonly IServiceScopeFactory scopeFactory;
|
||||
private readonly BindingClient bindingClient;
|
||||
private readonly DailyNoteEntry entry;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的实时便笺通知器
|
||||
/// </summary>
|
||||
/// <param name="scopeFactory">范围工厂</param>
|
||||
/// <param name="bindingClient">绑定客户端</param>
|
||||
/// <param name="entry">实时便笺入口</param>
|
||||
public DailyNoteNotifier(IServiceScopeFactory scopeFactory, BindingClient bindingClient, DailyNoteEntry entry)
|
||||
{
|
||||
this.scopeFactory = scopeFactory;
|
||||
this.bindingClient = bindingClient;
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步通知
|
||||
/// </summary>
|
||||
/// <returns>任务</returns>
|
||||
public async ValueTask NotifyAsync()
|
||||
{
|
||||
if (entry.DailyNote == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<string> hints = new();
|
||||
|
||||
// NotifySuppressed judge
|
||||
{
|
||||
if (entry.DailyNote.CurrentResin >= entry.ResinNotifyThreshold)
|
||||
{
|
||||
if (!entry.ResinNotifySuppressed)
|
||||
{
|
||||
hints.Add($"当前原粹树脂:{entry.DailyNote.CurrentResin}");
|
||||
entry.ResinNotifySuppressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.ResinNotifySuppressed = false;
|
||||
}
|
||||
|
||||
if (entry.DailyNote.CurrentHomeCoin >= entry.HomeCoinNotifyThreshold)
|
||||
{
|
||||
if (!entry.HomeCoinNotifySuppressed)
|
||||
{
|
||||
hints.Add($"当前洞天宝钱:{entry.DailyNote.CurrentHomeCoin}");
|
||||
entry.HomeCoinNotifySuppressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.HomeCoinNotifySuppressed = false;
|
||||
}
|
||||
|
||||
if (entry.DailyTaskNotify && !entry.DailyNote.IsExtraTaskRewardReceived)
|
||||
{
|
||||
if (!entry.DailyTaskNotifySuppressed)
|
||||
{
|
||||
hints.Add(entry.DailyNote.ExtraTaskRewardDescription);
|
||||
entry.DailyTaskNotifySuppressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.DailyTaskNotifySuppressed = false;
|
||||
}
|
||||
|
||||
if (entry.TransformerNotify && entry.DailyNote.Transformer.Obtained && entry.DailyNote.Transformer.RecoveryTime.Reached)
|
||||
{
|
||||
if (!entry.TransformerNotifySuppressed)
|
||||
{
|
||||
hints.Add("参量质变仪已准备完成");
|
||||
entry.TransformerNotifySuppressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.TransformerNotifySuppressed = false;
|
||||
}
|
||||
|
||||
if (entry.ExpeditionNotify && entry.DailyNote.Expeditions.All(e => e.Status == ExpeditionStatus.Finished))
|
||||
{
|
||||
if (!entry.ExpeditionNotifySuppressed)
|
||||
{
|
||||
hints.Add("探索派遣已完成");
|
||||
entry.ExpeditionNotifySuppressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.ExpeditionNotifySuppressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (hints.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<UserGameRole> roles = await bindingClient.GetUserGameRolesByCookieAsync(entry.User).ConfigureAwait(false);
|
||||
string attribution = roles.SingleOrDefault(r => r.GameUid == entry.Uid)?.ToString() ?? "未知角色";
|
||||
|
||||
ToastContentBuilder builder = new ToastContentBuilder()
|
||||
.AddHeader("DAILYNOTE", "实时便笺提醒", "DAILYNOTE")
|
||||
.AddAttributionText(attribution)
|
||||
.AddButton(new ToastButton().SetContent("开始游戏").AddArgument("Action", "LaunchGame").AddArgument("Uid", entry.Uid))
|
||||
.AddButton(new ToastButtonDismiss("我知道了"));
|
||||
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
if (appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteReminderNotify, SettingEntryHelper.FalseString).GetBoolean())
|
||||
{
|
||||
builder.SetToastScenario(ToastScenario.Reminder);
|
||||
}
|
||||
}
|
||||
|
||||
if (hints.Count > 2)
|
||||
{
|
||||
builder.AddText("多个提醒项达到设定值");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string hint in hints)
|
||||
{
|
||||
builder.AddText(hint);
|
||||
}
|
||||
}
|
||||
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
builder.Show();
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ internal class DailyNoteService : IDailyNoteService, IRecipient<UserRemovedMessa
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
List<DailyNoteEntry> entryList = appDbContext.DailyNotes.ToList();
|
||||
List<DailyNoteEntry> entryList = appDbContext.DailyNotes.AsNoTracking().ToList();
|
||||
entryList.ForEach(entry => { entry.UserGameRole = userService.GetUserGameRoleByUid(entry.Uid); });
|
||||
entries = new(appDbContext.DailyNotes);
|
||||
}
|
||||
@@ -113,7 +113,7 @@ internal class DailyNoteService : IDailyNoteService, IRecipient<UserRemovedMessa
|
||||
|
||||
if (notify)
|
||||
{
|
||||
await NotifyDailyNoteAsync(bindingClient, entry).ConfigureAwait(false);
|
||||
await new DailyNoteNotifier(scopeFactory, bindingClient, entry).NotifyAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,124 +128,7 @@ internal class DailyNoteService : IDailyNoteService, IRecipient<UserRemovedMessa
|
||||
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
appDbContext.DailyNotes.RemoveAndSave(entry);
|
||||
scope.ServiceProvider.GetRequiredService<AppDbContext>().DailyNotes.RemoveAndSave(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask NotifyDailyNoteAsync(BindingClient client, DailyNoteEntry entry)
|
||||
{
|
||||
if (entry.DailyNote == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<string> hints = new();
|
||||
|
||||
// NotifySuppressed judge
|
||||
{
|
||||
if (entry.DailyNote.CurrentResin >= entry.ResinNotifyThreshold)
|
||||
{
|
||||
if (!entry.ResinNotifySuppressed)
|
||||
{
|
||||
hints.Add($"当前原粹树脂:{entry.DailyNote.CurrentResin}");
|
||||
entry.ResinNotifySuppressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.ResinNotifySuppressed = false;
|
||||
}
|
||||
|
||||
if (entry.DailyNote.CurrentHomeCoin >= entry.HomeCoinNotifyThreshold)
|
||||
{
|
||||
if (!entry.HomeCoinNotifySuppressed)
|
||||
{
|
||||
hints.Add($"当前洞天宝钱:{entry.DailyNote.CurrentHomeCoin}");
|
||||
entry.HomeCoinNotifySuppressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.HomeCoinNotifySuppressed = false;
|
||||
}
|
||||
|
||||
if (entry.DailyTaskNotify && !entry.DailyNote.IsExtraTaskRewardReceived)
|
||||
{
|
||||
if (!entry.DailyTaskNotifySuppressed)
|
||||
{
|
||||
hints.Add(entry.DailyNote.ExtraTaskRewardDescription);
|
||||
entry.DailyTaskNotifySuppressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.DailyTaskNotifySuppressed = false;
|
||||
}
|
||||
|
||||
if (entry.TransformerNotify && entry.DailyNote.Transformer.Obtained && entry.DailyNote.Transformer.RecoveryTime.Reached)
|
||||
{
|
||||
if (!entry.TransformerNotifySuppressed)
|
||||
{
|
||||
hints.Add("参量质变仪已准备完成");
|
||||
entry.TransformerNotifySuppressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.TransformerNotifySuppressed = false;
|
||||
}
|
||||
|
||||
if (entry.ExpeditionNotify && entry.DailyNote.Expeditions.All(e => e.Status == ExpeditionStatus.Finished))
|
||||
{
|
||||
if (!entry.ExpeditionNotifySuppressed)
|
||||
{
|
||||
hints.Add("探索派遣已完成");
|
||||
entry.ExpeditionNotifySuppressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.ExpeditionNotifySuppressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (hints.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<UserGameRole> roles = await client.GetUserGameRolesByCookieAsync(entry.User).ConfigureAwait(false);
|
||||
string attribution = roles.SingleOrDefault(r => r.GameUid == entry.Uid)?.ToString() ?? "未知角色";
|
||||
|
||||
ToastContentBuilder builder = new ToastContentBuilder()
|
||||
.AddHeader("DAILYNOTE", "实时便笺提醒", "DAILYNOTE")
|
||||
.AddAttributionText(attribution)
|
||||
.AddButton(new ToastButton().SetContent("开始游戏").AddArgument("Action", "LaunchGame").AddArgument("Uid", entry.Uid))
|
||||
.AddButton(new ToastButtonDismiss("我知道了"));
|
||||
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
if (appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteReminderNotify, false.ToString()).GetBoolean())
|
||||
{
|
||||
builder.SetToastScenario(ToastScenario.Reminder);
|
||||
}
|
||||
}
|
||||
|
||||
if (hints.Count > 2)
|
||||
{
|
||||
builder.AddText("多个提醒项达到设定值");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string hint in hints)
|
||||
{
|
||||
builder.AddText(hint);
|
||||
}
|
||||
}
|
||||
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
builder.Show();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Snap.Hutao.Service.GachaLog.Factory;
|
||||
|
||||
@@ -10,22 +11,29 @@ namespace Snap.Hutao.Service.GachaLog.Factory;
|
||||
/// </summary>
|
||||
public class GachaConfigTypeComparar : IComparer<GachaConfigType>
|
||||
{
|
||||
private static readonly GachaConfigTypeComparar InnerShared = new();
|
||||
private static readonly ImmutableDictionary<GachaConfigType, int> OrderMap = new Dictionary<GachaConfigType, int>()
|
||||
{
|
||||
{ GachaConfigType.AvatarEventWish, 0 },
|
||||
{ GachaConfigType.AvatarEventWish2, 1 },
|
||||
{ GachaConfigType.WeaponEventWish, 2 },
|
||||
{ GachaConfigType.PermanentWish, 3 },
|
||||
{ GachaConfigType.NoviceWish, 4 },
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
/// <summary>
|
||||
/// 共享的比较器
|
||||
/// </summary>
|
||||
public static GachaConfigTypeComparar Shared { get => InnerShared; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Compare(GachaConfigType x, GachaConfigType y)
|
||||
{
|
||||
return GetOrder(x) - GetOrder(y);
|
||||
return OrderOf(x) - OrderOf(y);
|
||||
}
|
||||
|
||||
private static int GetOrder(GachaConfigType type)
|
||||
private static int OrderOf(GachaConfigType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
GachaConfigType.AvatarEventWish => 0,
|
||||
GachaConfigType.AvatarEventWish2 => 1,
|
||||
GachaConfigType.WeaponEventWish => 2,
|
||||
GachaConfigType.PermanentWish => 3,
|
||||
GachaConfigType.NoviceWish => 4,
|
||||
_ => 0,
|
||||
};
|
||||
return OrderMap.GetValueOrDefault(type, 0);
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
using Snap.Hutao.Model.Binding.Gacha;
|
||||
using Snap.Hutao.Model.Metadata.Abstraction;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Windows.UI;
|
||||
@@ -62,35 +60,11 @@ public static class GachaStatisticsExtensions
|
||||
/// <summary>
|
||||
/// 将计数器转换为统计物品列表
|
||||
/// </summary>
|
||||
/// <typeparam name="TItem">物品类型</typeparam>
|
||||
/// <param name="dict">计数器</param>
|
||||
/// <returns>统计物品列表</returns>
|
||||
public static List<StatisticsItem> ToStatisticsList(this Dictionary<IStatisticsItemSource, int> dict)
|
||||
{
|
||||
return dict
|
||||
.Select(kvp => kvp.Key.ToStatisticsItem(kvp.Value))
|
||||
.OrderByDescending(item => item.Count)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将计数器转换为统计物品列表
|
||||
/// </summary>
|
||||
/// <param name="dict">计数器</param>
|
||||
/// <returns>统计物品列表</returns>
|
||||
public static List<StatisticsItem> ToStatisticsList(this Dictionary<Avatar, int> dict)
|
||||
{
|
||||
return dict
|
||||
.Select(kvp => kvp.Key.ToStatisticsItem(kvp.Value))
|
||||
.OrderByDescending(item => item.Count)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将计数器转换为统计物品列表
|
||||
/// </summary>
|
||||
/// <param name="dict">计数器</param>
|
||||
/// <returns>统计物品列表</returns>
|
||||
public static List<StatisticsItem> ToStatisticsList(this Dictionary<Weapon, int> dict)
|
||||
public static List<StatisticsItem> ToStatisticsList<TItem>(this Dictionary<TItem, int> dict)
|
||||
where TItem : IStatisticsItemSource
|
||||
{
|
||||
return dict
|
||||
.Select(kvp => kvp.Key.ToStatisticsItem(kvp.Value))
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Snap.Hutao.Context.Database;
|
||||
using Snap.Hutao.Core.Database;
|
||||
using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Model.Binding.Gacha;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
@@ -45,20 +46,13 @@ internal class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
Dictionary<string, Weapon> nameWeaponMap = await metadataService.GetNameToWeaponMapAsync().ConfigureAwait(false);
|
||||
|
||||
List<GachaEvent> gachaevents = await metadataService.GetGachaEventsAsync().ConfigureAwait(false);
|
||||
|
||||
List<HistoryWishBuilder> historyWishBuilders = gachaevents.Select(g => new HistoryWishBuilder(g, nameAvatarMap, nameWeaponMap)).ToList();
|
||||
|
||||
SettingEntry? entry = await appDbContext.Settings
|
||||
.SingleOrDefaultAsync(e => e.Key == SettingEntry.IsEmptyHistoryWishVisible)
|
||||
SettingEntry entry = await appDbContext.Settings
|
||||
.SingleOrAddAsync(SettingEntry.IsEmptyHistoryWishVisible, SettingEntryHelper.TrueString)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (entry == null)
|
||||
{
|
||||
entry = new(SettingEntry.IsEmptyHistoryWishVisible, true.ToString());
|
||||
appDbContext.Settings.Add(entry);
|
||||
await appDbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
bool isEmptyHistoryWishVisible = bool.Parse(entry.Value!);
|
||||
bool isEmptyHistoryWishVisible = entry.GetBoolean();
|
||||
|
||||
IOrderedEnumerable<GachaItem> orderedItems = items.OrderBy(i => i.Id);
|
||||
return await Task.Run(() => CreateCore(orderedItems, historyWishBuilders, idAvatarMap, idWeaponMap, isEmptyHistoryWishVisible)).ConfigureAwait(false);
|
||||
@@ -71,9 +65,9 @@ internal class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
Dictionary<WeaponId, Weapon> weaponMap,
|
||||
bool isEmptyHistoryWishVisible)
|
||||
{
|
||||
TypedWishSummaryBuilder permanentWishBuilder = new("奔行世间", TypedWishSummaryBuilder.PermanentWish, 90, 10);
|
||||
TypedWishSummaryBuilder avatarWishBuilder = new("角色活动", TypedWishSummaryBuilder.AvatarEventWish, 90, 10);
|
||||
TypedWishSummaryBuilder weaponWishBuilder = new("神铸赋形", TypedWishSummaryBuilder.WeaponEventWish, 80, 10);
|
||||
TypedWishSummaryBuilder permanentWishBuilder = new("奔行世间", TypedWishSummaryBuilder.IsPermanentWish, 90, 10);
|
||||
TypedWishSummaryBuilder avatarWishBuilder = new("角色活动", TypedWishSummaryBuilder.IsAvatarEventWish, 90, 10);
|
||||
TypedWishSummaryBuilder weaponWishBuilder = new("神铸赋形", TypedWishSummaryBuilder.IsWeaponEventWish, 80, 10);
|
||||
|
||||
Dictionary<Avatar, int> orangeAvatarCounter = new();
|
||||
Dictionary<Avatar, int> purpleAvatarCounter = new();
|
||||
@@ -86,7 +80,6 @@ internal class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
foreach (GachaItem item in items)
|
||||
{
|
||||
// Find target history wish to operate.
|
||||
// TODO: improve performance.
|
||||
HistoryWishBuilder? targetHistoryWishBuilder = historyWishBuilders
|
||||
.Where(w => w.ConfigType == item.GachaType)
|
||||
.SingleOrDefault(w => w.From <= item.Time && w.To >= item.Time);
|
||||
@@ -101,11 +94,11 @@ internal class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
{
|
||||
case ItemQuality.QUALITY_ORANGE:
|
||||
orangeAvatarCounter.Increase(avatar);
|
||||
isUp = targetHistoryWishBuilder?.IncreaseOrangeAvatar(avatar) ?? false;
|
||||
isUp = targetHistoryWishBuilder?.IncreaseOrange(avatar) ?? false;
|
||||
break;
|
||||
case ItemQuality.QUALITY_PURPLE:
|
||||
purpleAvatarCounter.Increase(avatar);
|
||||
targetHistoryWishBuilder?.IncreasePurpleAvatar(avatar);
|
||||
targetHistoryWishBuilder?.IncreasePurple(avatar);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -123,15 +116,15 @@ internal class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
switch (weapon.RankLevel)
|
||||
{
|
||||
case ItemQuality.QUALITY_ORANGE:
|
||||
isUp = targetHistoryWishBuilder?.IncreaseOrangeWeapon(weapon) ?? false;
|
||||
isUp = targetHistoryWishBuilder?.IncreaseOrange(weapon) ?? false;
|
||||
orangeWeaponCounter.Increase(weapon);
|
||||
break;
|
||||
case ItemQuality.QUALITY_PURPLE:
|
||||
targetHistoryWishBuilder?.IncreasePurpleWeapon(weapon);
|
||||
targetHistoryWishBuilder?.IncreasePurple(weapon);
|
||||
purpleWeaponCounter.Increase(weapon);
|
||||
break;
|
||||
case ItemQuality.QUALITY_BLUE:
|
||||
targetHistoryWishBuilder?.IncreaseBlueWeapon(weapon);
|
||||
targetHistoryWishBuilder?.IncreaseBlue(weapon);
|
||||
blueWeaponCounter.Increase(weapon);
|
||||
break;
|
||||
}
|
||||
@@ -153,8 +146,9 @@ internal class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
HistoryWishes = historyWishBuilders
|
||||
.Where(b => isEmptyHistoryWishVisible || (!b.IsEmpty))
|
||||
.OrderByDescending(builder => builder.From)
|
||||
.ThenBy(builder => builder.ConfigType, new GachaConfigTypeComparar())
|
||||
.Select(builder => builder.ToHistoryWish()).ToList(),
|
||||
.ThenBy(builder => builder.ConfigType, GachaConfigTypeComparar.Shared)
|
||||
.Select(builder => builder.ToHistoryWish())
|
||||
.ToList(),
|
||||
|
||||
// avatars
|
||||
OrangeAvatars = orangeAvatarCounter.ToStatisticsList(),
|
||||
|
||||
@@ -56,79 +56,47 @@ internal class HistoryWishBuilder
|
||||
public GachaConfigType ConfigType { get => configType; }
|
||||
|
||||
/// <inheritdoc cref="GachaEvent.From"/>
|
||||
public DateTimeOffset From
|
||||
{
|
||||
get => gachaEvent.From;
|
||||
}
|
||||
public DateTimeOffset From { get => gachaEvent.From; }
|
||||
|
||||
/// <inheritdoc cref="GachaEvent.To"/>
|
||||
public DateTimeOffset To
|
||||
{
|
||||
get => gachaEvent.To;
|
||||
}
|
||||
public DateTimeOffset To { get => gachaEvent.To; }
|
||||
|
||||
/// <summary>
|
||||
/// 卡池是否为空
|
||||
/// </summary>
|
||||
public bool IsEmpty
|
||||
{
|
||||
get => totalCountTracker <= 0;
|
||||
}
|
||||
public bool IsEmpty { get => totalCountTracker <= 0; }
|
||||
|
||||
/// <summary>
|
||||
/// 计数五星角色
|
||||
/// 计数五星物品
|
||||
/// </summary>
|
||||
/// <param name="avatar">角色</param>
|
||||
/// <returns>是否为Up角色</returns>
|
||||
public bool IncreaseOrangeAvatar(Avatar avatar)
|
||||
/// <param name="item">物品</param>
|
||||
/// <returns>是否为Up物品</returns>
|
||||
public bool IncreaseOrange(IStatisticsItemSource item)
|
||||
{
|
||||
orangeCounter.Increase(avatar);
|
||||
orangeCounter.Increase(item);
|
||||
++totalCountTracker;
|
||||
|
||||
return orangeUpCounter.TryIncrease(avatar);
|
||||
return orangeUpCounter.TryIncrease(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计数四星角色
|
||||
/// 计数四星物品
|
||||
/// </summary>
|
||||
/// <param name="avatar">角色</param>
|
||||
public void IncreasePurpleAvatar(Avatar avatar)
|
||||
/// <param name="item">物品</param>
|
||||
public void IncreasePurple(IStatisticsItemSource item)
|
||||
{
|
||||
purpleUpCounter.TryIncrease(avatar);
|
||||
purpleCounter.Increase(avatar);
|
||||
++totalCountTracker;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计数五星武器
|
||||
/// </summary>
|
||||
/// <param name="weapon">武器</param>
|
||||
/// <returns>是否为Up武器</returns>
|
||||
public bool IncreaseOrangeWeapon(Weapon weapon)
|
||||
{
|
||||
orangeCounter.Increase(weapon);
|
||||
++totalCountTracker;
|
||||
return orangeUpCounter.TryIncrease(weapon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计数四星武器
|
||||
/// </summary>
|
||||
/// <param name="weapon">武器</param>
|
||||
public void IncreasePurpleWeapon(Weapon weapon)
|
||||
{
|
||||
purpleUpCounter.TryIncrease(weapon);
|
||||
purpleCounter.Increase(weapon);
|
||||
purpleUpCounter.TryIncrease(item);
|
||||
purpleCounter.Increase(item);
|
||||
++totalCountTracker;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计数三星武器
|
||||
/// </summary>
|
||||
/// <param name="weapon">武器</param>
|
||||
public void IncreaseBlueWeapon(Weapon weapon)
|
||||
/// <param name="item">武器</param>
|
||||
public void IncreaseBlue(IStatisticsItemSource item)
|
||||
{
|
||||
blueCounter.Increase(weapon);
|
||||
blueCounter.Increase(item);
|
||||
++totalCountTracker;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,17 +18,17 @@ internal class TypedWishSummaryBuilder
|
||||
/// <summary>
|
||||
/// 常驻祈愿
|
||||
/// </summary>
|
||||
public static readonly Func<GachaConfigType, bool> PermanentWish = type => type is GachaConfigType.PermanentWish;
|
||||
public static readonly Func<GachaConfigType, bool> IsPermanentWish = type => type is GachaConfigType.PermanentWish;
|
||||
|
||||
/// <summary>
|
||||
/// 角色活动
|
||||
/// </summary>
|
||||
public static readonly Func<GachaConfigType, bool> AvatarEventWish = type => type is GachaConfigType.AvatarEventWish or GachaConfigType.AvatarEventWish2;
|
||||
public static readonly Func<GachaConfigType, bool> IsAvatarEventWish = type => type is GachaConfigType.AvatarEventWish or GachaConfigType.AvatarEventWish2;
|
||||
|
||||
/// <summary>
|
||||
/// 武器活动
|
||||
/// </summary>
|
||||
public static readonly Func<GachaConfigType, bool> WeaponEventWish = type => type is GachaConfigType.WeaponEventWish;
|
||||
public static readonly Func<GachaConfigType, bool> IsWeaponEventWish = type => type is GachaConfigType.WeaponEventWish;
|
||||
|
||||
private readonly string name;
|
||||
private readonly int guarenteeOrangeThreshold;
|
||||
@@ -37,7 +37,7 @@ internal class TypedWishSummaryBuilder
|
||||
|
||||
private readonly List<int> averageOrangePullTracker = new();
|
||||
private readonly List<int> averageUpOrangePullTracker = new();
|
||||
private readonly List<SummaryItem> summaryItemCache = new();
|
||||
private readonly List<SummaryItem> summaryItems = new();
|
||||
|
||||
private int maxOrangePullTracker;
|
||||
private int minOrangePullTracker;
|
||||
@@ -98,7 +98,7 @@ internal class TypedWishSummaryBuilder
|
||||
lastUpOrangePullTracker = 0;
|
||||
}
|
||||
|
||||
summaryItemCache.Add(source.ToSummaryItem(lastOrangePullTracker, item.Time, isUp));
|
||||
summaryItems.Add(source.ToSummaryItem(lastOrangePullTracker, item.Time, isUp));
|
||||
|
||||
lastOrangePullTracker = 0;
|
||||
++totalOrangePullTracker;
|
||||
@@ -127,8 +127,8 @@ internal class TypedWishSummaryBuilder
|
||||
/// <returns>类型化祈愿统计信息</returns>
|
||||
public TypedWishSummary ToTypedWishSummary()
|
||||
{
|
||||
summaryItemCache.CompleteAdding();
|
||||
double totalCountDouble = totalCountTracker;
|
||||
summaryItems.CompleteAdding();
|
||||
double totalCount = totalCountTracker;
|
||||
|
||||
return new()
|
||||
{
|
||||
@@ -148,12 +148,12 @@ internal class TypedWishSummaryBuilder
|
||||
TotalOrangePull = totalOrangePullTracker,
|
||||
TotalPurplePull = totalPurplePullTracker,
|
||||
TotalBluePull = totalBluePullTracker,
|
||||
TotalOrangePercent = totalOrangePullTracker / totalCountDouble,
|
||||
TotalPurplePercent = totalPurplePullTracker / totalCountDouble,
|
||||
TotalBluePercent = totalBluePullTracker / totalCountDouble,
|
||||
TotalOrangePercent = totalOrangePullTracker / totalCount,
|
||||
TotalPurplePercent = totalPurplePullTracker / totalCount,
|
||||
TotalBluePercent = totalBluePullTracker / totalCount,
|
||||
AverageOrangePull = averageOrangePullTracker.AverageNoThrow(),
|
||||
AverageUpOrangePull = averageUpOrangePullTracker.AverageNoThrow(),
|
||||
OrangeList = summaryItemCache,
|
||||
OrangeList = summaryItems,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -33,13 +33,13 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
/// <summary>
|
||||
/// 祈愿记录查询的类型
|
||||
/// </summary>
|
||||
private static readonly ImmutableList<GachaConfigType> QueryTypes = ImmutableList.Create(new GachaConfigType[]
|
||||
private static readonly ImmutableList<GachaConfigType> QueryTypes = new List<GachaConfigType>
|
||||
{
|
||||
GachaConfigType.NoviceWish,
|
||||
GachaConfigType.PermanentWish,
|
||||
GachaConfigType.AvatarEventWish,
|
||||
GachaConfigType.WeaponEventWish,
|
||||
});
|
||||
}.ToImmutableList();
|
||||
|
||||
private readonly AppDbContext appDbContext;
|
||||
private readonly IEnumerable<IGachaLogUrlProvider> urlProviders;
|
||||
@@ -56,6 +56,7 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
|
||||
private Dictionary<AvatarId, Model.Metadata.Avatar.Avatar>? idAvatarMap;
|
||||
private Dictionary<WeaponId, Model.Metadata.Weapon.Weapon>? idWeaponMap;
|
||||
|
||||
private ObservableCollection<GachaArchive>? archiveCollection;
|
||||
|
||||
/// <summary>
|
||||
@@ -100,8 +101,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
/// <inheritdoc/>
|
||||
public Task<UIGF> ExportToUIGFAsync(GachaArchive archive)
|
||||
{
|
||||
Verify.Operation(IsInitialized, "祈愿记录服务未能正常初始化");
|
||||
|
||||
List<UIGFItem> list = appDbContext.GachaItems
|
||||
.Where(i => i.ArchiveId == archive.InnerId)
|
||||
.AsEnumerable()
|
||||
@@ -179,16 +178,31 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task ImportFromUIGFAsync(List<UIGFItem> list, string uid)
|
||||
public async Task ImportFromUIGFAsync(List<UIGFItem> list, string uid)
|
||||
{
|
||||
return Task.Run(() => ImportFromUIGF(list, uid));
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
|
||||
GachaArchive? archive = null;
|
||||
SkipOrInitArchive(ref archive, uid);
|
||||
Guid archiveId = Must.NotNull(archive!).InnerId;
|
||||
|
||||
long trimId = appDbContext.GachaItems
|
||||
.Where(i => i.ArchiveId == archiveId)
|
||||
.OrderBy(i => i.Id)
|
||||
.FirstOrDefault()?.Id ?? long.MaxValue;
|
||||
|
||||
IEnumerable<GachaItem> toAdd = list
|
||||
.OrderByDescending(i => i.Id)
|
||||
.Where(i => i.Id < trimId)
|
||||
.Select(i => GachaItem.Create(archiveId, i, GetItemId(i)));
|
||||
|
||||
await appDbContext.GachaItems.AddRangeAndSaveAsync(toAdd).ConfigureAwait(false);
|
||||
CurrentArchive = archive;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<bool> RefreshGachaLogAsync(string query, RefreshStrategy strategy, IProgress<FetchState> progress, CancellationToken token)
|
||||
{
|
||||
Verify.Operation(IsInitialized, "祈愿记录服务未能正常初始化");
|
||||
|
||||
bool isLazy = strategy switch
|
||||
{
|
||||
RefreshStrategy.AggressiveMerge => false,
|
||||
@@ -204,17 +218,17 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
/// <inheritdoc/>
|
||||
public async Task RemoveArchiveAsync(GachaArchive archive)
|
||||
{
|
||||
Must.NotNull(archiveCollection!);
|
||||
|
||||
// Sync cache
|
||||
archiveCollection.Remove(archive);
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
archiveCollection!.Remove(archive);
|
||||
|
||||
// Sync database
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
await appDbContext.GachaItems
|
||||
.Where(item => item.ArchiveId == archive.InnerId)
|
||||
.ExecuteDeleteAsync()
|
||||
.ConfigureAwait(false);
|
||||
appDbContext.GachaArchives.RemoveAndSave(archive);
|
||||
await appDbContext.GachaArchives.RemoveAndSaveAsync(archive).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static Task RandomDelayAsync(CancellationToken token)
|
||||
@@ -222,26 +236,6 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
return Task.Delay(TimeSpan.FromSeconds(Random.Shared.NextDouble() + 1), token);
|
||||
}
|
||||
|
||||
private void ImportFromUIGF(List<UIGFItem> list, string uid)
|
||||
{
|
||||
GachaArchive? archive = null;
|
||||
SkipOrInitArchive(ref archive, uid);
|
||||
Guid archiveId = Must.NotNull(archive!).InnerId;
|
||||
|
||||
long trimId = appDbContext.GachaItems
|
||||
.Where(i => i.ArchiveId == archiveId)
|
||||
.OrderBy(i => i.Id)
|
||||
.FirstOrDefault()?.Id ?? long.MaxValue;
|
||||
|
||||
IEnumerable<GachaItem> toAdd = list
|
||||
.OrderByDescending(i => i.Id)
|
||||
.Where(i => i.Id < trimId)
|
||||
.Select(i => GachaItem.Create(archiveId, i, GetItemId(i)));
|
||||
|
||||
appDbContext.GachaItems.AddRangeAndSave(toAdd);
|
||||
CurrentArchive = archive;
|
||||
}
|
||||
|
||||
private async Task<ValueResult<bool, GachaArchive?>> FetchGachaLogsAsync(string query, bool isLazy, IProgress<FetchState> progress, CancellationToken token)
|
||||
{
|
||||
GachaArchive? archive = null;
|
||||
|
||||
@@ -18,7 +18,6 @@ internal class GachaLogUrlManualInputProvider : IGachaLogUrlProvider
|
||||
public async Task<ValueResult<bool, string>> GetQueryAsync()
|
||||
{
|
||||
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
ValueResult<bool, string> result = await new GachaLogUrlDialog(mainWindow).GetInputUrlAsync().ConfigureAwait(false);
|
||||
|
||||
if (result.IsOk)
|
||||
|
||||
@@ -48,18 +48,13 @@ internal class GachaLogUrlWebCacheProvider : IGachaLogUrlProvider
|
||||
{
|
||||
string cacheFile = GetCacheFile(path);
|
||||
|
||||
TemporaryFile tempFile;
|
||||
try
|
||||
using (TemporaryFile? tempFile = TemporaryFile.CreateFromFileCopy(cacheFile))
|
||||
{
|
||||
tempFile = TemporaryFile.CreateFromFileCopy(cacheFile);
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
return new(false, $"找不到原神内置浏览器缓存路径:\n{cacheFile}");
|
||||
}
|
||||
if (tempFile == null)
|
||||
{
|
||||
return new(false, $"找不到原神内置浏览器缓存路径:\n{cacheFile}");
|
||||
}
|
||||
|
||||
using (tempFile)
|
||||
{
|
||||
using (FileStream fileStream = new(tempFile.Path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
using (MemoryStream memoryStream = new())
|
||||
@@ -80,8 +75,8 @@ internal class GachaLogUrlWebCacheProvider : IGachaLogUrlProvider
|
||||
private static string? Match(MemoryStream stream)
|
||||
{
|
||||
ReadOnlySpan<byte> span = stream.ToArray();
|
||||
ReadOnlySpan<byte> match = Encoding.UTF8.GetBytes("https://webstatic.mihoyo.com/hk4e/event/e20190909gacha-v2/index.html");
|
||||
ReadOnlySpan<byte> zero = Encoding.UTF8.GetBytes("\0");
|
||||
ReadOnlySpan<byte> match = "https://webstatic.mihoyo.com/hk4e/event/e20190909gacha-v2/index.html"u8;
|
||||
ReadOnlySpan<byte> zero = "\0"u8;
|
||||
|
||||
int index = span.LastIndexOf(match);
|
||||
if (index >= 0)
|
||||
|
||||
@@ -23,8 +23,7 @@ namespace Snap.Hutao.Service.Game;
|
||||
/// 游戏服务
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Singleton, typeof(IGameService))]
|
||||
[SuppressMessage("", "CA1001")]
|
||||
internal class GameService : IGameService
|
||||
internal class GameService : IGameService, IDisposable
|
||||
{
|
||||
private const string GamePathKey = $"{nameof(GameService)}.Cache.{SettingEntry.GamePath}";
|
||||
private const string ConfigFile = "config.ini";
|
||||
@@ -59,21 +58,21 @@ internal class GameService : IGameService
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
SettingEntry entry = appDbContext.Settings.SingleOrAdd(e => e.Key == SettingEntry.GamePath, () => new(SettingEntry.GamePath, string.Empty), out bool added);
|
||||
SettingEntry entry = await appDbContext.Settings.SingleOrAddAsync(SettingEntry.GamePath, string.Empty).ConfigureAwait(false);
|
||||
|
||||
// Cannot find in setting
|
||||
if (added)
|
||||
if (string.IsNullOrEmpty(entry.Value))
|
||||
{
|
||||
IEnumerable<IGameLocator> gameLocators = scope.ServiceProvider.GetRequiredService<IEnumerable<IGameLocator>>();
|
||||
|
||||
// Try locate by registry
|
||||
IGameLocator locator = gameLocators.Single(l => l.Name == nameof(RegistryLauncherLocator));
|
||||
IGameLocator locator = gameLocators.Single(l => l.Name == nameof(UnityLogGameLocator));
|
||||
ValueResult<bool, string> result = await locator.LocateGamePathAsync().ConfigureAwait(false);
|
||||
|
||||
if (!result.IsOk)
|
||||
{
|
||||
// Try locate manually
|
||||
locator = gameLocators.Single(l => l.Name == nameof(ManualGameLocator));
|
||||
locator = gameLocators.Single(l => l.Name == nameof(RegistryLauncherLocator));
|
||||
result = await locator.LocateGamePathAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -85,7 +84,7 @@ internal class GameService : IGameService
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(false, null!);
|
||||
return new(false, "请启动游戏后再次尝试");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +94,7 @@ internal class GameService : IGameService
|
||||
}
|
||||
|
||||
// Set cache and return.
|
||||
string path = memoryCache.Set(GamePathKey, Must.NotNull(entry.Value!));
|
||||
string path = memoryCache.Set(GamePathKey, entry.Value);
|
||||
return new(true, path);
|
||||
}
|
||||
}
|
||||
@@ -113,13 +112,10 @@ internal class GameService : IGameService
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
SettingEntry entry = appDbContext.Settings.SingleOrAdd(e => e.Key == SettingEntry.GamePath, () => new(SettingEntry.GamePath, null), out bool added);
|
||||
|
||||
entry.Value ??= string.Empty;
|
||||
appDbContext.Settings.UpdateAndSave(entry);
|
||||
SettingEntry entry = appDbContext.Settings.SingleOrAdd(SettingEntry.GamePath, string.Empty);
|
||||
|
||||
// Set cache and return.
|
||||
return memoryCache.Set(GamePathKey, entry.Value);
|
||||
return memoryCache.Set(GamePathKey, entry.Value!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +130,7 @@ internal class GameService : IGameService
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
SettingEntry entry = appDbContext.Settings.SingleOrAdd(e => e.Key == SettingEntry.GamePath, () => new(SettingEntry.GamePath, null), out _);
|
||||
SettingEntry entry = appDbContext.Settings.SingleOrAdd(SettingEntry.GamePath, string.Empty);
|
||||
entry.Value = path;
|
||||
appDbContext.Settings.UpdateAndSave(entry);
|
||||
}
|
||||
@@ -222,7 +218,7 @@ internal class GameService : IGameService
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
gameAccounts = new(appDbContext.GameAccounts.ToList());
|
||||
gameAccounts = new(appDbContext.GameAccounts.AsNoTracking().ToList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,6 +286,7 @@ internal class GameService : IGameService
|
||||
}
|
||||
catch (Win32Exception)
|
||||
{
|
||||
// 通常是用户取消了UAC
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -318,10 +315,14 @@ internal class GameService : IGameService
|
||||
gameAccounts.Add(GameAccount.Create(name, registrySdk));
|
||||
|
||||
// sync database
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
appDbContext.GameAccounts.AddAndSave(account);
|
||||
await scope.ServiceProvider
|
||||
.GetRequiredService<AppDbContext>()
|
||||
.GameAccounts
|
||||
.AddAndSaveAsync(account)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,9 +340,8 @@ internal class GameService : IGameService
|
||||
{
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
gameAccount.UpdateAttachUid(uid);
|
||||
appDbContext.GameAccounts.UpdateAndSave(gameAccount);
|
||||
scope.ServiceProvider.GetRequiredService<AppDbContext>().GameAccounts.UpdateAndSave(gameAccount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,18 +349,18 @@ internal class GameService : IGameService
|
||||
public async ValueTask ModifyGameAccountAsync(GameAccount gameAccount)
|
||||
{
|
||||
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
|
||||
(bool isOk, string name) = await new GameAccountNameDialog(mainWindow).GetInputNameAsync().ConfigureAwait(false);
|
||||
(bool isOk, string name) = await new GameAccountNameDialog(mainWindow).GetInputNameAsync().ConfigureAwait(true);
|
||||
|
||||
if (isOk)
|
||||
{
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
gameAccount.UpdateName(name);
|
||||
|
||||
// sync database
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
appDbContext.GameAccounts.UpdateAndSave(gameAccount);
|
||||
await appDbContext.GameAccounts.UpdateAndSaveAsync(gameAccount).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -368,20 +368,19 @@ internal class GameService : IGameService
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask RemoveGameAccountAsync(GameAccount gameAccount)
|
||||
{
|
||||
Must.NotNull(gameAccounts!).Remove(gameAccount);
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
gameAccounts!.Remove(gameAccount);
|
||||
|
||||
await Task.Yield();
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
try
|
||||
{
|
||||
appDbContext.GameAccounts.RemoveAndSave(gameAccount);
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
// This gameAccount has already been deleted.
|
||||
}
|
||||
await scope.ServiceProvider.GetRequiredService<AppDbContext>().GameAccounts.RemoveAndSaveAsync(gameAccount).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
gameSemaphore?.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,4 @@ internal interface IGameLocator : INamed
|
||||
/// </summary>
|
||||
/// <returns>游戏位置</returns>
|
||||
Task<ValueResult<bool, string>> LocateGamePathAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取游戏启动器位置
|
||||
/// 路径应当包含启动器文件名称
|
||||
/// </summary>
|
||||
/// <returns>游戏启动器位置</returns>
|
||||
[Obsolete("不应定位启动器位置")]
|
||||
Task<ValueResult<bool, string>> LocateLauncherPathAsync();
|
||||
}
|
||||
@@ -33,12 +33,6 @@ internal class ManualGameLocator : IGameLocator
|
||||
return LocateInternalAsync("YuanShen.exe");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<ValueResult<bool, string>> LocateLauncherPathAsync()
|
||||
{
|
||||
return LocateInternalAsync("launcher.exe");
|
||||
}
|
||||
|
||||
private async Task<ValueResult<bool, string>> LocateInternalAsync(string fileName)
|
||||
{
|
||||
FileOpenPicker picker = pickerFactory.GetFileOpenPicker(PickerLocationId.Desktop, "选择游戏本体", ".exe");
|
||||
|
||||
@@ -47,35 +47,31 @@ internal partial class RegistryLauncherLocator : IGameLocator
|
||||
return Task.FromResult<ValueResult<bool, string>>(new(false, null!));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<ValueResult<bool, string>> LocateLauncherPathAsync()
|
||||
{
|
||||
return Task.FromResult(LocateInternal("DisplayIcon"));
|
||||
}
|
||||
|
||||
private static ValueResult<bool, string> LocateInternal(string key)
|
||||
{
|
||||
RegistryKey? uninstallKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\原神");
|
||||
if (uninstallKey != null)
|
||||
using (RegistryKey? uninstallKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\原神"))
|
||||
{
|
||||
if (uninstallKey.GetValue(key) is string path)
|
||||
if (uninstallKey != null)
|
||||
{
|
||||
return new(true, path);
|
||||
if (uninstallKey.GetValue(key) is string path)
|
||||
{
|
||||
return new(true, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(false, null!);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(false, null!);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(false, null!);
|
||||
}
|
||||
}
|
||||
|
||||
private static string Unescape(string str)
|
||||
{
|
||||
string? hex4Result = Utf16Regex().Replace(str, @"\u$1");
|
||||
string? hex4Result = UTF16Regex().Replace(str, @"\u$1");
|
||||
|
||||
// 不包含中文
|
||||
if (!hex4Result.Contains(@"\u"))
|
||||
@@ -88,5 +84,5 @@ internal partial class RegistryLauncherLocator : IGameLocator
|
||||
}
|
||||
|
||||
[GeneratedRegex("\\\\x([0-9a-f]{4})")]
|
||||
private static partial Regex Utf16Regex();
|
||||
private static partial Regex UTF16Regex();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.IO;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Locator;
|
||||
|
||||
/// <summary>
|
||||
/// Unity日志游戏定位器
|
||||
/// </summary>
|
||||
[Injection(InjectAs.Transient, typeof(IGameLocator))]
|
||||
internal partial class UnityLogGameLocator : IGameLocator
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Name { get => nameof(UnityLogGameLocator); }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<ValueResult<bool, string>> LocateGamePathAsync()
|
||||
{
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
string logFilePath = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\原神\output_log.txt");
|
||||
|
||||
using (var tempFile = TemporaryFile.CreateFromFileCopy(logFilePath))
|
||||
{
|
||||
if (tempFile == null)
|
||||
{
|
||||
return new(false, $"找不到 Unity 日志文件:\n{logFilePath}");
|
||||
}
|
||||
|
||||
string content = File.ReadAllText(tempFile.Path);
|
||||
|
||||
var matchResult = WarmupFileLine().Match(content);
|
||||
if (!matchResult.Success)
|
||||
{
|
||||
return new(false, $"在 Unity 日志文件中找不到游戏路径");
|
||||
}
|
||||
|
||||
string entryName = matchResult.Groups[0].Value.Replace("_Data", ".exe");
|
||||
string fullPath = Path.GetFullPath(Path.Combine(matchResult.Value, "..", entryName));
|
||||
return new(true, fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"(?m).:/.+YuanShen_Data")]
|
||||
private static partial Regex WarmupFileLine();
|
||||
}
|
||||
@@ -70,23 +70,18 @@ internal class HutaoCache : IHutaoCache
|
||||
if (await metadataService.InitializeAsync().ConfigureAwait(false))
|
||||
{
|
||||
Dictionary<AvatarId, Avatar> idAvatarMap = await GetIdAvatarMapExtendedAsync().ConfigureAwait(false);
|
||||
List<Task> tasks = new(5)
|
||||
{
|
||||
AvatarAppearanceRankAsync(idAvatarMap),
|
||||
AvatarUsageRanksAsync(idAvatarMap),
|
||||
AvatarConstellationInfosAsync(idAvatarMap),
|
||||
TeamAppearancesAsync(idAvatarMap),
|
||||
OverviewAsync(),
|
||||
};
|
||||
|
||||
Task avatarAppearanceRankTask = AvatarAppearanceRankAsync(idAvatarMap);
|
||||
Task avatarUsageRank = AvatarUsageRanksAsync(idAvatarMap);
|
||||
Task avatarConstellationInfoTask = AvatarConstellationInfosAsync(idAvatarMap);
|
||||
Task teamAppearanceTask = TeamAppearancesAsync(idAvatarMap);
|
||||
Task ovewviewTask = OverviewAsync();
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
await Task.WhenAll(
|
||||
avatarAppearanceRankTask,
|
||||
avatarUsageRank,
|
||||
avatarConstellationInfoTask,
|
||||
teamAppearanceTask,
|
||||
ovewviewTask)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
isDatabaseViewModelInitialized = true;
|
||||
return true;
|
||||
return isDatabaseViewModelInitialized = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -88,14 +88,14 @@ internal class HutaoService : IHutaoService
|
||||
|
||||
if (appDbContext.ObjectCache.SingleOrDefault(e => e.Key == key) is ObjectCacheEntry entry)
|
||||
{
|
||||
if (entry.ExpireTime > DateTimeOffset.Now)
|
||||
if (entry.IsExpired)
|
||||
{
|
||||
T value = JsonSerializer.Deserialize<T>(entry.Value!, options)!;
|
||||
return memoryCache.Set(key, value, TimeSpan.FromMinutes(30));
|
||||
await appDbContext.ObjectCache.RemoveAndSaveAsync(entry).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
appDbContext.ObjectCache.RemoveAndSave(entry);
|
||||
T value = JsonSerializer.Deserialize<T>(entry.Value!, options)!;
|
||||
return memoryCache.Set(key, value, TimeSpan.FromMinutes(30));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,19 +87,17 @@ internal class UserService : IUserService
|
||||
/// <inheritdoc/>
|
||||
public async Task RemoveUserAsync(BindingUser user)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
// Sync cache
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
userCollection!.Remove(user);
|
||||
roleCollection?.RemoveWhere(r => r.User.InnerId == user.Entity.InnerId);
|
||||
|
||||
// Sync database
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
// Note: cascade deleted dailynotes
|
||||
appDbContext.Users.RemoveAndSave(user.Entity);
|
||||
await scope.ServiceProvider.GetRequiredService<AppDbContext>().Users.RemoveAndSaveAsync(user.Entity).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
messenger.Send(new UserRemovedMessage(user.Entity));
|
||||
@@ -108,6 +106,7 @@ internal class UserService : IUserService
|
||||
/// <inheritdoc/>
|
||||
public async Task<ObservableCollection<BindingUser>> GetUserCollectionAsync()
|
||||
{
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
if (userCollection == null)
|
||||
{
|
||||
List<BindingUser> users = new();
|
||||
@@ -118,9 +117,7 @@ internal class UserService : IUserService
|
||||
|
||||
foreach (Model.Entity.User entity in appDbContext.Users)
|
||||
{
|
||||
BindingUser? initialized = await BindingUser
|
||||
.ResumeAsync(entity)
|
||||
.ConfigureAwait(false);
|
||||
BindingUser? initialized = await BindingUser.ResumeAsync(entity).ConfigureAwait(false);
|
||||
|
||||
if (initialized != null)
|
||||
{
|
||||
@@ -129,7 +126,7 @@ internal class UserService : IUserService
|
||||
else
|
||||
{
|
||||
// User is unable to be initialized, remove it.
|
||||
appDbContext.Users.RemoveAndSave(entity);
|
||||
await appDbContext.Users.RemoveAndSaveAsync(entity).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,6 +141,7 @@ internal class UserService : IUserService
|
||||
/// <inheritdoc/>
|
||||
public async Task<ObservableCollection<Model.Binding.User.UserAndRole>> GetRoleCollectionAsync()
|
||||
{
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
if (roleCollection == null)
|
||||
{
|
||||
List<Model.Binding.User.UserAndRole> userAndRoles = new();
|
||||
@@ -178,8 +176,7 @@ internal class UserService : IUserService
|
||||
/// <inheritdoc/>
|
||||
public async Task<ValueResult<UserOptionResult, string>> ProcessInputCookieAsync(Cookie cookie)
|
||||
{
|
||||
Must.NotNull(userCollection!);
|
||||
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
string? mid = cookie.GetValueOrDefault(Cookie.MID);
|
||||
|
||||
if (mid == null)
|
||||
@@ -188,7 +185,7 @@ internal class UserService : IUserService
|
||||
}
|
||||
|
||||
// 检查 mid 对应用户是否存在
|
||||
if (UserHelper.TryGetUser(userCollection, mid, out BindingUser? user))
|
||||
if (UserHelper.TryGetUser(userCollection!, mid, out BindingUser? user))
|
||||
{
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
@@ -200,7 +197,7 @@ internal class UserService : IUserService
|
||||
user.Ltoken = cookie.TryGetAsLtoken(out Cookie? ltoken) ? ltoken : user.Ltoken;
|
||||
user.CookieToken = cookie.TryGetAsCookieToken(out Cookie? cookieToken) ? cookieToken : user.CookieToken;
|
||||
|
||||
appDbContext.Users.UpdateAndSave(user.Entity);
|
||||
await appDbContext.Users.UpdateAndSaveAsync(user.Entity).ConfigureAwait(false);
|
||||
return new(UserOptionResult.Updated, mid);
|
||||
}
|
||||
else
|
||||
@@ -217,6 +214,7 @@ internal class UserService : IUserService
|
||||
|
||||
private async Task<ValueResult<UserOptionResult, string>> TryCreateUserAndAddAsync(Cookie cookie)
|
||||
{
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
@@ -228,19 +226,22 @@ internal class UserService : IUserService
|
||||
if (userCollection != null)
|
||||
{
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
userCollection!.Add(newUser);
|
||||
|
||||
if (roleCollection != null)
|
||||
{
|
||||
foreach (UserGameRole role in newUser.UserGameRoles)
|
||||
userCollection!.Add(newUser);
|
||||
|
||||
if (roleCollection != null)
|
||||
{
|
||||
roleCollection.Add(new(newUser.Entity, role));
|
||||
foreach (UserGameRole role in newUser.UserGameRoles)
|
||||
{
|
||||
roleCollection.Add(new(newUser.Entity, role));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sync database
|
||||
appDbContext.Users.AddAndSave(newUser.Entity);
|
||||
await ThreadHelper.SwitchToBackgroundAsync();
|
||||
await appDbContext.Users.AddAndSaveAsync(newUser.Entity).ConfigureAwait(false);
|
||||
return new(UserOptionResult.Added, newUser.UserInfo!.Uid);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -4,22 +4,10 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Snap.Hutao.Model.Binding.User;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.Web.Bridge;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
namespace Snap.Hutao.View.Dialog;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ public sealed partial class GachaLogUrlDialog : ContentDialog
|
||||
/// <returns>输入的结果</returns>
|
||||
public async Task<ValueResult<bool, string>> GetInputUrlAsync()
|
||||
{
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
ContentDialogResult result = await ShowAsync();
|
||||
string url = InputText.Text;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ public sealed partial class GameAccountNameDialog : ContentDialog
|
||||
/// <returns>输入的结果</returns>
|
||||
public async Task<ValueResult<bool, string>> GetInputNameAsync()
|
||||
{
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
ContentDialogResult result = await ShowAsync();
|
||||
string text = InputText.Text;
|
||||
return new(result == ContentDialogResult.Primary && (!string.IsNullOrEmpty(text)), text);
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Background="{StaticResource CardBackgroundFillColorDefaultBrush}">
|
||||
<Grid
|
||||
Background="{StaticResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{StaticResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,0,0,1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="252"/>
|
||||
<ColumnDefinition/>
|
||||
|
||||
@@ -13,7 +13,6 @@ using Snap.Hutao.Model.Binding.User;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Service.AvatarInfo;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
using Snap.Hutao.WinRT;
|
||||
using Windows.Foundation;
|
||||
|
||||
@@ -9,7 +9,6 @@ using Snap.Hutao.Context.FileSystem.Location;
|
||||
using Snap.Hutao.Factory.Abstraction;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.View.Dialog;
|
||||
using Snap.Hutao.Web.Hutao;
|
||||
using Snap.Hutao.Web.Hutao.Model.Post;
|
||||
using Windows.Storage;
|
||||
|
||||
@@ -25,10 +25,10 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
|
||||
// filters
|
||||
private readonly List<Selectable<string>> filterElementInfos;
|
||||
private readonly List<Selectable<Pair<string, string>>> filterAssociationInfos;
|
||||
private readonly List<Selectable<Pair<string, AssociationType>>> filterAssociationInfos;
|
||||
private readonly List<Selectable<Pair<string, WeaponType>>> filterWeaponTypeInfos;
|
||||
private readonly List<Selectable<Pair<string, ItemQuality>>> filterQualityInfos;
|
||||
private readonly List<Selectable<Pair<string, string>>> filterBodyInfos;
|
||||
private readonly List<Selectable<Pair<string, BodyType>>> filterBodyInfos;
|
||||
|
||||
private AdvancedCollectionView? avatars;
|
||||
private Avatar? selected;
|
||||
@@ -58,12 +58,12 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
|
||||
filterAssociationInfos = new()
|
||||
{
|
||||
new(new("蒙德", "ASSOC_TYPE_MONDSTADT"), OnFilterChanged),
|
||||
new(new("璃月", "ASSOC_TYPE_LIYUE"), OnFilterChanged),
|
||||
new(new("稻妻", "ASSOC_TYPE_INAZUMA"), OnFilterChanged),
|
||||
new(new("须弥", "ASSOC_TYPE_SUMERU"), OnFilterChanged),
|
||||
new(new("愚人众", "ASSOC_TYPE_FATUI"), OnFilterChanged),
|
||||
new(new("游侠", "ASSOC_TYPE_RANGER"), OnFilterChanged),
|
||||
new(new("蒙德", AssociationType.ASSOC_TYPE_MONDSTADT), OnFilterChanged),
|
||||
new(new("璃月", AssociationType.ASSOC_TYPE_LIYUE), OnFilterChanged),
|
||||
new(new("稻妻", AssociationType.ASSOC_TYPE_INAZUMA), OnFilterChanged),
|
||||
new(new("须弥", AssociationType.ASSOC_TYPE_SUMERU), OnFilterChanged),
|
||||
new(new("愚人众", AssociationType.ASSOC_TYPE_FATUI), OnFilterChanged),
|
||||
new(new("游侠", AssociationType.ASSOC_TYPE_RANGER), OnFilterChanged),
|
||||
};
|
||||
|
||||
filterWeaponTypeInfos = new()
|
||||
@@ -84,11 +84,11 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
|
||||
filterBodyInfos = new()
|
||||
{
|
||||
new(new("成女", "BODY_LADY"), OnFilterChanged),
|
||||
new(new("少女", "BODY_GIRL"), OnFilterChanged),
|
||||
new(new("幼女", "BODY_LOLI"), OnFilterChanged),
|
||||
new(new("成男", "BODY_MALE"), OnFilterChanged),
|
||||
new(new("少男", "BODY_BOY"), OnFilterChanged),
|
||||
new(new("成女", BodyType.BODY_LADY), OnFilterChanged),
|
||||
new(new("少女", BodyType.BODY_GIRL), OnFilterChanged),
|
||||
new(new("幼女", BodyType.BODY_LOLI), OnFilterChanged),
|
||||
new(new("成男", BodyType.BODY_MALE), OnFilterChanged),
|
||||
new(new("少男", BodyType.BODY_BOY), OnFilterChanged),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
/// <summary>
|
||||
/// 筛选用所属国家集合
|
||||
/// </summary>
|
||||
public IList<Selectable<Pair<string, string>>> FilterAssociationInfos
|
||||
public IList<Selectable<Pair<string, AssociationType>>> FilterAssociationInfos
|
||||
{
|
||||
get => filterAssociationInfos;
|
||||
}
|
||||
@@ -137,7 +137,7 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
/// <summary>
|
||||
/// 筛选用体型信息集合
|
||||
/// </summary>
|
||||
public IList<Selectable<Pair<string, string>>> FilterBodyInfos
|
||||
public IList<Selectable<Pair<string, BodyType>>> FilterBodyInfos
|
||||
{
|
||||
get => filterBodyInfos;
|
||||
}
|
||||
@@ -189,7 +189,7 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
.Select(e => e.Value)
|
||||
.ToList();
|
||||
|
||||
List<string> targetAssociations = filterAssociationInfos
|
||||
List<AssociationType> targetAssociations = filterAssociationInfos
|
||||
.Where(e => e.IsSelected)
|
||||
.Select(e => e.Value.Value)
|
||||
.ToList();
|
||||
@@ -204,7 +204,7 @@ internal class WikiAvatarViewModel : ObservableObject
|
||||
.Select(e => e.Value.Value)
|
||||
.ToList();
|
||||
|
||||
List<string> targetBodies = filterBodyInfos
|
||||
List<BodyType> targetBodies = filterBodyInfos
|
||||
.Where(e => e.IsSelected)
|
||||
.Select(e => e.Value.Value)
|
||||
.ToList();
|
||||
|
||||
@@ -165,7 +165,6 @@ internal static class ApiEndpoints
|
||||
/// <summary>
|
||||
/// 计算器角色技能列表
|
||||
/// </summary>
|
||||
/// <param name="avatarId">角色Id</param>
|
||||
/// <param name="avatar">元素类型</param>
|
||||
/// <returns>技能列表</returns>
|
||||
public static string CalculateAvatarSkillList(Hoyolab.Takumi.Event.Calculate.Avatar avatar)
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Snap.Hutao.Web.Bridge;
|
||||
/// <summary>
|
||||
/// 调用桥
|
||||
/// </summary>
|
||||
[SuppressMessage("", "SA1600")]
|
||||
public class MiHoYoJSInterface
|
||||
{
|
||||
private const string InitializeJsInterfaceScript2 = """
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Json.Converter;
|
||||
using Snap.Hutao.Core.Json.Annotation;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
@@ -21,13 +21,13 @@ public class GachaLogItem
|
||||
/// 祈愿类型
|
||||
/// </summary>
|
||||
[JsonPropertyName("gacha_type")]
|
||||
[JsonConverter(typeof(EnumStringValueConverter<GachaConfigType>))]
|
||||
[JsonEnum(JsonSerializeType.Int32AsString)]
|
||||
public GachaConfigType GachaType { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 总为 <see cref="string.Empty"/>
|
||||
/// </summary>
|
||||
[Obsolete("API clear this property")]
|
||||
[Obsolete("API set this property empty")]
|
||||
[JsonPropertyName("item_id")]
|
||||
public string ItemId { get; set; } = string.Empty;
|
||||
|
||||
@@ -67,7 +67,7 @@ public class GachaLogItem
|
||||
/// 物品稀有等级
|
||||
/// </summary>
|
||||
[JsonPropertyName("rank_type")]
|
||||
[JsonConverter(typeof(EnumStringValueConverter<ItemQuality>))]
|
||||
[JsonEnum(JsonSerializeType.Int32AsString)]
|
||||
public ItemQuality Rank { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -39,10 +39,10 @@ internal class CalculateClient
|
||||
/// <param name="delta">差异</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>消耗结果</returns>
|
||||
public async Task<Consumption> ComputeAsync(User user, AvatarPromotionDelta delta, CancellationToken token)
|
||||
public async Task<Consumption?> ComputeAsync(User user, AvatarPromotionDelta delta, CancellationToken token)
|
||||
{
|
||||
Response<Consumption>? resp = await httpClient
|
||||
.SetUser(user,CookieType.CookieToken)
|
||||
.SetUser(user, CookieType.CookieToken)
|
||||
.TryCatchPostAsJsonAsync<AvatarPromotionDelta, Response<Consumption>>(ApiEndpoints.CalculateCompute, delta, options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
return resp?.Data;
|
||||
@@ -53,7 +53,6 @@ internal class CalculateClient
|
||||
/// </summary>
|
||||
/// <param name="user">用户</param>
|
||||
/// <param name="uid">Uid</param>
|
||||
/// <param name="sync">同步</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>角色列表</returns>
|
||||
public async Task<List<Avatar>> GetAvatarsAsync(User user, PlayerUid uid, CancellationToken token = default)
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||
using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||
using Snap.Hutao.Extension;
|
||||
using Snap.Hutao.Model.Binding.User;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Web.Hoyolab.Annotation;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Snap.Hutao.WinRT;
|
||||
[ComImport]
|
||||
[Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
unsafe interface IMemoryBufferByteAccess
|
||||
public unsafe interface IMemoryBufferByteAccess
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an IMemoryBuffer as an array of bytes.
|
||||
|
||||
Reference in New Issue
Block a user