mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb2665b75e | ||
|
|
d22ac39c1d | ||
|
|
a312603d61 | ||
|
|
0732ea0e06 | ||
|
|
e4d2b3055c | ||
|
|
5668931230 |
10
.github/workflows/PublishDistribution.yml
vendored
10
.github/workflows/PublishDistribution.yml
vendored
@@ -39,3 +39,13 @@ jobs:
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
rclone copy ./release-download/* dgpODCN:/releases/
|
rclone copy ./release-download/* dgpODCN:/releases/
|
||||||
|
|
||||||
|
# Purge Patch System Cache
|
||||||
|
- name: Purge Patch
|
||||||
|
env:
|
||||||
|
PATCH_HOSTS: ${{ secrets.PATCH_HOSTS }}
|
||||||
|
PURGE_TOKEN: ${{ secrets.PURGE_TOKEN }}
|
||||||
|
PURGE_URL: ${{ secrets.PURGE_URL }}
|
||||||
|
run: |
|
||||||
|
sudo echo "$PATCH_HOSTS" | sudo tee -a /etc/hosts
|
||||||
|
curl --header "Authorization: token $PURGE_TOKEN" $PURGE_URL
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
# [Snap.Hutao](https://hut.ao)
|
# [Snap.Hutao](https://hut.ao)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
> 唷,找本堂主有何贵干呀?
|
> 唷,找本堂主有何贵干呀?
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
# 特别感谢
|
# 特别感谢
|
||||||
|
|
||||||
### 原神组织与个人
|
|
||||||
|
|
||||||
* [HolographicHat](https://github.com/HolographicHat)
|
* [HolographicHat](https://github.com/HolographicHat)
|
||||||
* [UIGF organization](https://uigf.org)
|
* [UIGF organization](https://uigf.org)
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,8 @@ steps:
|
|||||||
"Package/Identity/@Publisher": "CN=DGP Studio CI",
|
"Package/Identity/@Publisher": "CN=DGP Studio CI",
|
||||||
"Package/Identity/@Version": "$(build_date).$(rev_number)",
|
"Package/Identity/@Version": "$(build_date).$(rev_number)",
|
||||||
"Package/Properties/DisplayName": "胡桃 Alpha",
|
"Package/Properties/DisplayName": "胡桃 Alpha",
|
||||||
"Package/Properties/PublisherDisplayName":"DGP Studio CI"
|
"Package/Properties/PublisherDisplayName":"DGP Studio CI",
|
||||||
|
"Package/Applications/Application/uap:VisualElements/@DisplayName": "胡桃 Alpha"
|
||||||
}
|
}
|
||||||
|
|
||||||
- task: CmdLine@2
|
- task: CmdLine@2
|
||||||
@@ -167,12 +168,13 @@ steps:
|
|||||||
changeLogType: 'commitBased'
|
changeLogType: 'commitBased'
|
||||||
|
|
||||||
- task: DownloadSecureFile@1
|
- task: DownloadSecureFile@1
|
||||||
name: cerFile
|
name: RcloneConfigFile
|
||||||
displayName: Download Rclone Config
|
displayName: Download Rclone Config
|
||||||
inputs:
|
inputs:
|
||||||
secureFile: 'rclone.conf'
|
secureFile: 'rclone.conf'
|
||||||
|
|
||||||
- task: rclone@1
|
- task: rclone@1
|
||||||
|
displayName: Upload CI via Rclone
|
||||||
inputs:
|
inputs:
|
||||||
arguments: 'copy $(Build.ArtifactStagingDirectory)/* downloadDGPCN:/releases/Alpha/'
|
arguments: 'copy $(Build.ArtifactStagingDirectory)/* downloadDGPCN:/releases/Alpha/'
|
||||||
configPath: '$(cerFile.secureFilePath)/rclone.conf'
|
configPath: '$(RcloneConfigFile.secureFilePath)/rclone.conf'
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<muxc:XamlControlsResources/>
|
<muxc:XamlControlsResources/>
|
||||||
<ResourceDictionary Source="ms-appx:///SettingsUI/Themes/Generic.xaml"/>
|
<ResourceDictionary Source="ms-appx:///SettingsUI/Themes/Generic.xaml"/>
|
||||||
|
<ResourceDictionary Source="Control/Theme/FontStyle.xaml"/>
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
<ResourceDictionary.ThemeDictionaries>
|
<ResourceDictionary.ThemeDictionaries>
|
||||||
@@ -27,8 +28,6 @@
|
|||||||
<StaticResource x:Key="WindowCaptionBackgroundDisabled" ResourceKey="ControlFillColorTransparentBrush"/>
|
<StaticResource x:Key="WindowCaptionBackgroundDisabled" ResourceKey="ControlFillColorTransparentBrush"/>
|
||||||
<!-- Page Transparent Background -->
|
<!-- Page Transparent Background -->
|
||||||
<StaticResource x:Key="ApplicationPageBackgroundThemeBrush" ResourceKey="ControlFillColorTransparentBrush"/>
|
<StaticResource x:Key="ApplicationPageBackgroundThemeBrush" ResourceKey="ControlFillColorTransparentBrush"/>
|
||||||
<!-- 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="InfoBarIconMargin">6,16,16,16</Thickness>
|
||||||
<Thickness x:Key="InfoBarContentRootPadding">16,0,0,0</Thickness>
|
<Thickness x:Key="InfoBarContentRootPadding">16,0,0,0</Thickness>
|
||||||
@@ -90,6 +89,7 @@
|
|||||||
<shvc:Int32ToVisibilityConverter x:Key="Int32ToVisibilityConverter"/>
|
<shvc:Int32ToVisibilityConverter x:Key="Int32ToVisibilityConverter"/>
|
||||||
<shvc:Int32ToVisibilityRevertConverter x:Key="Int32ToVisibilityRevertConverter"/>
|
<shvc:Int32ToVisibilityRevertConverter x:Key="Int32ToVisibilityRevertConverter"/>
|
||||||
<!-- Styles -->
|
<!-- Styles -->
|
||||||
|
|
||||||
<Style
|
<Style
|
||||||
x:Key="LargeGridViewItemStyle"
|
x:Key="LargeGridViewItemStyle"
|
||||||
BasedOn="{StaticResource DefaultGridViewItemStyle}"
|
BasedOn="{StaticResource DefaultGridViewItemStyle}"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using CommunityToolkit.WinUI.Notifications;
|
using CommunityToolkit.WinUI.Notifications;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
using Microsoft.Windows.AppLifecycle;
|
using Microsoft.Windows.AppLifecycle;
|
||||||
using Snap.Hutao.Core;
|
using Snap.Hutao.Core;
|
||||||
using Snap.Hutao.Core.ExceptionService;
|
using Snap.Hutao.Core.ExceptionService;
|
||||||
|
|||||||
210
src/Snap.Hutao/Snap.Hutao/Control/Theme/FontStyle.xaml
Normal file
210
src/Snap.Hutao/Snap.Hutao/Control/Theme/FontStyle.xaml
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
|
||||||
|
<!-- Licensed under the MIT License. -->
|
||||||
|
|
||||||
|
<ResourceDictionary
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:wsc="using:WinUICommunity.SettingsUI.Controls">
|
||||||
|
<FontFamily x:Key="MiSans">ms-appx:///Resource/Font/MiSans-Regular.ttf#MiSans</FontFamily>
|
||||||
|
<FontFamily x:Key="CascadiaMonoAndMiSans">ms-appx:///Resource/Font/CascadiaMono.ttf#Cascadia Mono, ms-appx:///Resource/Font/MiSans-Regular.ttf#MiSans</FontFamily>
|
||||||
|
|
||||||
|
<StaticResource x:Key="PivotHeaderItemFontFamily" ResourceKey="MiSans"/>
|
||||||
|
<StaticResource x:Key="ContentControlThemeFontFamily" ResourceKey="MiSans"/>
|
||||||
|
<Style BasedOn="{StaticResource BodyTextBlockStyle}" TargetType="TextBlock"/>
|
||||||
|
<Style x:Key="BaseTextBlockStyle" TargetType="TextBlock">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource MiSans}"/>
|
||||||
|
<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>
|
||||||
|
<Style
|
||||||
|
x:Key="HeaderTextBlockStyle"
|
||||||
|
BasedOn="{StaticResource BaseTextBlockStyle}"
|
||||||
|
TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="46"/>
|
||||||
|
<Setter Property="FontWeight" Value="Light"/>
|
||||||
|
<Setter Property="OpticalMarginAlignment" Value="TrimSideBearings"/>
|
||||||
|
</Style>
|
||||||
|
<Style
|
||||||
|
x:Key="SubheaderTextBlockStyle"
|
||||||
|
BasedOn="{StaticResource BaseTextBlockStyle}"
|
||||||
|
TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="34"/>
|
||||||
|
<Setter Property="FontWeight" Value="Light"/>
|
||||||
|
<Setter Property="OpticalMarginAlignment" Value="TrimSideBearings"/>
|
||||||
|
</Style>
|
||||||
|
<Style
|
||||||
|
x:Key="TitleTextBlockStyle"
|
||||||
|
BasedOn="{StaticResource BaseTextBlockStyle}"
|
||||||
|
TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="{StaticResource TitleTextBlockFontSize}"/>
|
||||||
|
<Setter Property="OpticalMarginAlignment" Value="TrimSideBearings"/>
|
||||||
|
</Style>
|
||||||
|
<Style
|
||||||
|
x:Key="SubtitleTextBlockStyle"
|
||||||
|
BasedOn="{StaticResource BaseTextBlockStyle}"
|
||||||
|
TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="{StaticResource SubtitleTextBlockFontSize}"/>
|
||||||
|
<Setter Property="OpticalMarginAlignment" Value="TrimSideBearings"/>
|
||||||
|
</Style>
|
||||||
|
<Style
|
||||||
|
x:Key="BodyTextBlockStyle"
|
||||||
|
BasedOn="{StaticResource BaseTextBlockStyle}"
|
||||||
|
TargetType="TextBlock">
|
||||||
|
<Setter Property="FontWeight" Value="Normal"/>
|
||||||
|
</Style>
|
||||||
|
<Style
|
||||||
|
x:Key="CaptionTextBlockStyle"
|
||||||
|
BasedOn="{StaticResource BaseTextBlockStyle}"
|
||||||
|
TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="{StaticResource CaptionTextBlockFontSize}"/>
|
||||||
|
<Setter Property="FontWeight" Value="Normal"/>
|
||||||
|
</Style>
|
||||||
|
<Style
|
||||||
|
x:Key="BodyStrongTextBlockStyle"
|
||||||
|
BasedOn="{StaticResource BaseTextBlockStyle}"
|
||||||
|
TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="{StaticResource BodyStrongTextBlockFontSize}"/>
|
||||||
|
</Style>
|
||||||
|
<Style
|
||||||
|
x:Key="TitleLargeTextBlockStyle"
|
||||||
|
BasedOn="{StaticResource BaseTextBlockStyle}"
|
||||||
|
TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="{StaticResource TitleLargeTextBlockFontSize}"/>
|
||||||
|
<Setter Property="OpticalMarginAlignment" Value="TrimSideBearings"/>
|
||||||
|
</Style>
|
||||||
|
<Style
|
||||||
|
x:Key="DisplayTextBlockStyle"
|
||||||
|
BasedOn="{StaticResource BaseTextBlockStyle}"
|
||||||
|
TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="{StaticResource DisplayTextBlockFontSize}"/>
|
||||||
|
<Setter Property="OpticalMarginAlignment" Value="TrimSideBearings"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style BasedOn="{StaticResource DefaultMenuFlyoutItemStyle}" TargetType="MenuFlyoutItem">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource MiSans}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style BasedOn="{StaticResource DefaultMenuFlyoutSubItemStyle}" TargetType="MenuFlyoutSubItem">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource MiSans}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style BasedOn="{StaticResource DefaultScrollViewerStyle}" TargetType="ScrollViewer">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource MiSans}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="InfoBar">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource MiSans}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style BasedOn="{StaticResource DefaultSettingStyle}" TargetType="wsc:Setting"/>
|
||||||
|
|
||||||
|
<Style x:Key="DefaultSettingStyle" TargetType="wsc:Setting">
|
||||||
|
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||||
|
<Setter Property="Background" Value="{ThemeResource CardBackgroundBrush}"/>
|
||||||
|
<Setter Property="BorderThickness" Value="{ThemeResource CardBorderThickness}"/>
|
||||||
|
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
|
||||||
|
<Setter Property="IsTabStop" Value="False"/>
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||||
|
<Setter Property="Padding" Value="16"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,0,0"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="wsc:Setting">
|
||||||
|
<Grid
|
||||||
|
x:Name="RootGrid"
|
||||||
|
MinHeight="48"
|
||||||
|
Padding="{TemplateBinding Padding}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<!-- Icon -->
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<!-- Header and subtitle -->
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<!-- Action control -->
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<ContentPresenter
|
||||||
|
x:Name="IconPresenter"
|
||||||
|
MaxWidth="20"
|
||||||
|
Margin="2,0,18,0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
AutomationProperties.AccessibilityView="Raw"
|
||||||
|
Content="{TemplateBinding Icon}"
|
||||||
|
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||||
|
FontSize="20"
|
||||||
|
Foreground="{ThemeResource CardPrimaryForegroundBrush}"
|
||||||
|
IsTextScaleFactorEnabled="False"/>
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,0,16,0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
x:Name="HeaderPresenter"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontFamily="{StaticResource MiSans}"
|
||||||
|
Foreground="{ThemeResource CardPrimaryForegroundBrush}"
|
||||||
|
Text="{TemplateBinding Header}"/>
|
||||||
|
|
||||||
|
<ContentPresenter
|
||||||
|
x:Name="DescriptionPresenter"
|
||||||
|
Content="{TemplateBinding Description}"
|
||||||
|
FontFamily="{StaticResource MiSans}"
|
||||||
|
FontSize="{StaticResource SecondaryTextFontSize}"
|
||||||
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
|
TextWrapping="WrapWholeWords">
|
||||||
|
<ContentPresenter.Resources>
|
||||||
|
<Style BasedOn="{StaticResource CaptionTextBlockStyle}" TargetType="TextBlock">
|
||||||
|
<Style.Setters>
|
||||||
|
<Setter Property="TextWrapping" Value="WrapWholeWords"/>
|
||||||
|
</Style.Setters>
|
||||||
|
</Style>
|
||||||
|
<Style BasedOn="{StaticResource TextButtonStyle}" TargetType="HyperlinkButton">
|
||||||
|
<Style.Setters>
|
||||||
|
<Setter Property="FontSize" Value="12"/>
|
||||||
|
<Setter Property="Padding" Value="0,0,0,0"/>
|
||||||
|
</Style.Setters>
|
||||||
|
</Style>
|
||||||
|
</ContentPresenter.Resources>
|
||||||
|
</ContentPresenter>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<ContentPresenter
|
||||||
|
x:Name="ContentPresenter"
|
||||||
|
Grid.Column="2"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Content="{TemplateBinding ActionContent}"/>
|
||||||
|
<VisualStateManager.VisualStateGroups>
|
||||||
|
<VisualStateGroup x:Name="CommonStates">
|
||||||
|
<VisualState x:Name="Normal"/>
|
||||||
|
<VisualState x:Name="Disabled">
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="HeaderPresenter.Foreground" Value="{ThemeResource TextFillColorDisabledBrush}"/>
|
||||||
|
<Setter Target="DescriptionPresenter.Foreground" Value="{ThemeResource TextFillColorDisabledBrush}"/>
|
||||||
|
<Setter Target="IconPresenter.Foreground" Value="{ThemeResource TextFillColorDisabledBrush}"/>
|
||||||
|
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource TextFillColorDisabledBrush}"/>
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
</VisualStateGroup>
|
||||||
|
</VisualStateManager.VisualStateGroups>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||||
using Snap.Hutao.Core.Logging;
|
using Snap.Hutao.Core.Logging;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@@ -24,7 +25,7 @@ public class ImageCache : IImageCache, IImageCacheFilePathOperation
|
|||||||
{
|
{
|
||||||
private const string CacheFolderName = nameof(ImageCache);
|
private const string CacheFolderName = nameof(ImageCache);
|
||||||
|
|
||||||
private static readonly ImmutableDictionary<int, TimeSpan> RetryCountToDelay = new Dictionary<int, TimeSpan>()
|
private static readonly Dictionary<int, TimeSpan> RetryCountToDelay = new Dictionary<int, TimeSpan>()
|
||||||
{
|
{
|
||||||
[0] = TimeSpan.FromSeconds(4),
|
[0] = TimeSpan.FromSeconds(4),
|
||||||
[1] = TimeSpan.FromSeconds(16),
|
[1] = TimeSpan.FromSeconds(16),
|
||||||
@@ -32,13 +33,13 @@ public class ImageCache : IImageCache, IImageCacheFilePathOperation
|
|||||||
[3] = TimeSpan.FromSeconds(4),
|
[3] = TimeSpan.FromSeconds(4),
|
||||||
[4] = TimeSpan.FromSeconds(16),
|
[4] = TimeSpan.FromSeconds(16),
|
||||||
[5] = TimeSpan.FromSeconds(64),
|
[5] = TimeSpan.FromSeconds(64),
|
||||||
}.ToImmutableDictionary();
|
};
|
||||||
|
|
||||||
private readonly ILogger logger;
|
private readonly ILogger logger;
|
||||||
|
|
||||||
// violate di rule
|
|
||||||
private readonly HttpClient httpClient;
|
private readonly HttpClient httpClient;
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string, Task> concurrentTasks = new();
|
||||||
|
|
||||||
private string? baseFolder;
|
private string? baseFolder;
|
||||||
private string? cacheFolder;
|
private string? cacheFolder;
|
||||||
|
|
||||||
@@ -100,11 +101,30 @@ public class ImageCache : IImageCache, IImageCacheFilePathOperation
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<string> GetFileFromCacheAsync(Uri uri)
|
public async Task<string> GetFileFromCacheAsync(Uri uri)
|
||||||
{
|
{
|
||||||
string filePath = Path.Combine(GetCacheFolder(), GetCacheFileName(uri));
|
string fileName = GetCacheFileName(uri);
|
||||||
|
string filePath = Path.Combine(GetCacheFolder(), fileName);
|
||||||
|
|
||||||
if (!File.Exists(filePath) || new FileInfo(filePath).Length == 0)
|
if (!File.Exists(filePath) || new FileInfo(filePath).Length == 0)
|
||||||
{
|
{
|
||||||
await DownloadFileAsync(uri, filePath).ConfigureAwait(false);
|
TaskCompletionSource taskCompletionSource = new();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (concurrentTasks.TryAdd(fileName, taskCompletionSource.Task))
|
||||||
|
{
|
||||||
|
await DownloadFileAsync(uri, filePath).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (concurrentTasks.TryGetValue(fileName, out Task? task))
|
||||||
|
{
|
||||||
|
await task.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
taskCompletionSource.TrySetResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return filePath;
|
return filePath;
|
||||||
@@ -191,7 +211,7 @@ public class ImageCache : IImageCache, IImageCacheFilePathOperation
|
|||||||
|
|
||||||
if (retryCount == 3)
|
if (retryCount == 3)
|
||||||
{
|
{
|
||||||
uri = new UriBuilder(uri) { Host = Web.HutaoEndpoints.StaticHutao, }.Uri;
|
uri = new UriBuilder(uri) { Host = Web.HutaoEndpoints.StaticHutao }.Uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ namespace Snap.Hutao.Core.DependencyInjection;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ServiceScopeExtension
|
public static class ServiceScopeExtension
|
||||||
{
|
{
|
||||||
private static IServiceScope? scopeReference;
|
// Allow GC to Collect the IServiceScope
|
||||||
|
private static readonly WeakReference<IServiceScope> ScopeReference = new(null!);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 追踪服务范围
|
/// 追踪服务范围
|
||||||
@@ -19,7 +20,7 @@ public static class ServiceScopeExtension
|
|||||||
public static void Track(this IServiceScope scope)
|
public static void Track(this IServiceScope scope)
|
||||||
{
|
{
|
||||||
DisposeLast();
|
DisposeLast();
|
||||||
scopeReference = scope;
|
ScopeReference.SetTarget(scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -27,6 +28,9 @@ public static class ServiceScopeExtension
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static void DisposeLast()
|
public static void DisposeLast()
|
||||||
{
|
{
|
||||||
scopeReference?.Dispose();
|
if (ScopeReference.TryGetTarget(out IServiceScope? scope))
|
||||||
|
{
|
||||||
|
scope.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,4 +45,4 @@ internal class ExceptionRecorder
|
|||||||
{
|
{
|
||||||
logger.LogCritical(EventIds.XamlBindingError, "XAML绑定失败: {message}", e.Message);
|
logger.LogCritical(EventIds.XamlBindingError, "XAML绑定失败: {message}", e.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.ExceptionService;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户数据损坏异常
|
||||||
|
/// </summary>
|
||||||
|
internal class UserdataCorruptedException : Exception
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的用户数据损坏异常
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">消息</param>
|
||||||
|
/// <param name="innerException">内部错误</param>
|
||||||
|
public UserdataCorruptedException(string message, Exception innerException)
|
||||||
|
: base($"用户数据已损坏: {message}", innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.Win32.TaskScheduler;
|
using Microsoft.Win32.TaskScheduler;
|
||||||
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using SchedulerTask = Microsoft.Win32.TaskScheduler.Task;
|
using SchedulerTask = Microsoft.Win32.TaskScheduler.Task;
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ internal static class ScheduleTaskHelper
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// TODO: 似乎可以不删除任务,直接注册已经包含了更新功能
|
||||||
SchedulerTask? targetTask = TaskService.Instance.GetTask(DailyNoteRefreshTaskName);
|
SchedulerTask? targetTask = TaskService.Instance.GetTask(DailyNoteRefreshTaskName);
|
||||||
if (targetTask != null)
|
if (targetTask != null)
|
||||||
{
|
{
|
||||||
@@ -36,12 +38,9 @@ internal static class ScheduleTaskHelper
|
|||||||
TaskService.Instance.RootFolder.RegisterTaskDefinition(DailyNoteRefreshTaskName, task);
|
TaskService.Instance.RootFolder.RegisterTaskDefinition(DailyNoteRefreshTaskName, task);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (UnauthorizedAccessException)
|
catch (Exception ex)
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (COMException)
|
|
||||||
{
|
{
|
||||||
|
_ = ex;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,4 +32,9 @@ internal static class SettingKeys
|
|||||||
/// 静态资源合约V2 成就图标与物品图标
|
/// 静态资源合约V2 成就图标与物品图标
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string StaticResourceV2Contract = "StaticResourceV2Contract";
|
public const string StaticResourceV2Contract = "StaticResourceV2Contract";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 静态资源合约V3 刷新 Skill Talent
|
||||||
|
/// </summary>
|
||||||
|
public const string StaticResourceV3Contract = "StaticResourceV3Contract";
|
||||||
}
|
}
|
||||||
@@ -39,6 +39,13 @@ internal static class ThreadHelper
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void InvokeOnMainThread(Action action)
|
public static void InvokeOnMainThread(Action action)
|
||||||
{
|
{
|
||||||
Program.DispatcherQueue!.Invoke(action);
|
if (Program.DispatcherQueue!.HasThreadAccess)
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Program.DispatcherQueue.Invoke(action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ namespace Snap.Hutao.Model.Intrinsic;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 不可变的原生枚举
|
/// 不可变的原生枚举
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ImmutableIntrinsics
|
public static class IntrinsicImmutables
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 所属地区
|
/// 所属地区
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<Identity
|
<Identity
|
||||||
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
|
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
|
||||||
Publisher="CN=DGP Studio"
|
Publisher="CN=DGP Studio"
|
||||||
Version="1.3.9.0" />
|
Version="1.3.10.0" />
|
||||||
|
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>胡桃</DisplayName>
|
<DisplayName>胡桃</DisplayName>
|
||||||
|
|||||||
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Font/CascadiaMono.ttf
Normal file
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Font/CascadiaMono.ttf
Normal file
Binary file not shown.
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Font/MiSans-Regular.ttf
Normal file
BIN
src/Snap.Hutao/Snap.Hutao/Resource/Font/MiSans-Regular.ttf
Normal file
Binary file not shown.
Binary file not shown.
@@ -5,6 +5,7 @@ using CommunityToolkit.Mvvm.Messaging;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Snap.Hutao.Core.Database;
|
using Snap.Hutao.Core.Database;
|
||||||
using Snap.Hutao.Core.Diagnostics;
|
using Snap.Hutao.Core.Diagnostics;
|
||||||
|
using Snap.Hutao.Core.ExceptionService;
|
||||||
using Snap.Hutao.Core.Logging;
|
using Snap.Hutao.Core.Logging;
|
||||||
using Snap.Hutao.Extension;
|
using Snap.Hutao.Extension;
|
||||||
using Snap.Hutao.Model.Entity.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
@@ -114,7 +115,17 @@ internal class AchievementService : IAchievementService
|
|||||||
List<BindingAchievement> results = new();
|
List<BindingAchievement> results = new();
|
||||||
foreach (MetadataAchievement meta in metadata)
|
foreach (MetadataAchievement meta in metadata)
|
||||||
{
|
{
|
||||||
EntityAchievement entity = entities.SingleOrDefault(e => e.Id == meta.Id) ?? EntityAchievement.Create(archiveId, meta.Id);
|
EntityAchievement? entity;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
entity = entities.SingleOrDefault(e => e.Id == meta.Id);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
throw new UserdataCorruptedException("单个成就存档内发现多个相同的成就 Id", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
entity ??= EntityAchievement.Create(archiveId, meta.Id);
|
||||||
|
|
||||||
results.Add(new(meta, entity));
|
results.Add(new(meta, entity));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ namespace Snap.Hutao.Service.AvatarInfo;
|
|||||||
[Injection(InjectAs.Scoped, typeof(IAvatarInfoService))]
|
[Injection(InjectAs.Scoped, typeof(IAvatarInfoService))]
|
||||||
internal class AvatarInfoService : IAvatarInfoService
|
internal class AvatarInfoService : IAvatarInfoService
|
||||||
{
|
{
|
||||||
private readonly AppDbContext appDbContext;
|
|
||||||
private readonly ISummaryFactory summaryFactory;
|
private readonly ISummaryFactory summaryFactory;
|
||||||
private readonly IMetadataService metadataService;
|
private readonly IMetadataService metadataService;
|
||||||
private readonly ILogger<AvatarInfoService> logger;
|
private readonly ILogger<AvatarInfoService> logger;
|
||||||
@@ -42,7 +41,6 @@ internal class AvatarInfoService : IAvatarInfoService
|
|||||||
ISummaryFactory summaryFactory,
|
ISummaryFactory summaryFactory,
|
||||||
ILogger<AvatarInfoService> logger)
|
ILogger<AvatarInfoService> logger)
|
||||||
{
|
{
|
||||||
this.appDbContext = appDbContext;
|
|
||||||
this.metadataService = metadataService;
|
this.metadataService = metadataService;
|
||||||
this.summaryFactory = summaryFactory;
|
this.summaryFactory = summaryFactory;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ internal class SummaryAvatarFactory
|
|||||||
ReliquaryAndWeapon reliquaryAndWeapon = ProcessEquip(avatarInfo.EquipList.EmptyIfNull());
|
ReliquaryAndWeapon reliquaryAndWeapon = ProcessEquip(avatarInfo.EquipList.EmptyIfNull());
|
||||||
MetadataAvatar avatar = metadataContext.IdAvatarMap[avatarInfo.AvatarId];
|
MetadataAvatar avatar = metadataContext.IdAvatarMap[avatarInfo.AvatarId];
|
||||||
|
|
||||||
return new()
|
PropertyAvatar propertyAvatar = new()
|
||||||
{
|
{
|
||||||
Id = avatar.Id,
|
Id = avatar.Id,
|
||||||
Name = avatar.Name,
|
Name = avatar.Name,
|
||||||
@@ -52,8 +52,6 @@ internal class SummaryAvatarFactory
|
|||||||
NameCard = AvatarNameCardPicConverter.AvatarToUri(avatar),
|
NameCard = AvatarNameCardPicConverter.AvatarToUri(avatar),
|
||||||
Quality = avatar.Quality,
|
Quality = avatar.Quality,
|
||||||
Element = ElementNameIconConverter.ElementNameToElementType(avatar.FetterInfo.VisionBefore),
|
Element = ElementNameIconConverter.ElementNameToElementType(avatar.FetterInfo.VisionBefore),
|
||||||
Level = $"Lv.{avatarInfo.PropMap[PlayerProperty.PROP_LEVEL].Value}",
|
|
||||||
LevelNumber = int.Parse(avatarInfo.PropMap[PlayerProperty.PROP_LEVEL].Value ?? string.Empty),
|
|
||||||
FetterLevel = avatarInfo.FetterInfo.ExpLevel,
|
FetterLevel = avatarInfo.FetterInfo.ExpLevel,
|
||||||
Weapon = reliquaryAndWeapon.Weapon,
|
Weapon = reliquaryAndWeapon.Weapon,
|
||||||
Reliquaries = reliquaryAndWeapon.Reliquaries,
|
Reliquaries = reliquaryAndWeapon.Reliquaries,
|
||||||
@@ -62,7 +60,11 @@ internal class SummaryAvatarFactory
|
|||||||
Properties = SummaryHelper.CreateAvatarProperties(avatarInfo.FightPropMap),
|
Properties = SummaryHelper.CreateAvatarProperties(avatarInfo.FightPropMap),
|
||||||
Score = reliquaryAndWeapon.Reliquaries.Sum(r => r.Score).ToString("F2"),
|
Score = reliquaryAndWeapon.Reliquaries.Sum(r => r.Score).ToString("F2"),
|
||||||
CritScore = $"{SummaryHelper.ScoreCrit(avatarInfo.FightPropMap):F2}",
|
CritScore = $"{SummaryHelper.ScoreCrit(avatarInfo.FightPropMap):F2}",
|
||||||
|
LevelNumber = int.Parse(avatarInfo.PropMap?[PlayerProperty.PROP_LEVEL].Value ?? string.Empty),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
propertyAvatar.Level = $"Lv.{propertyAvatar.LevelNumber}";
|
||||||
|
return propertyAvatar;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReliquaryAndWeapon ProcessEquip(List<Equip> equipments)
|
private ReliquaryAndWeapon ProcessEquip(List<Equip> equipments)
|
||||||
@@ -85,7 +87,7 @@ internal class SummaryAvatarFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new(reliquaryList, weapon!);
|
return new(reliquaryList, weapon);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PropertyWeapon CreateWeapon(Equip equip)
|
private PropertyWeapon CreateWeapon(Equip equip)
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ internal class DailyNoteService : IDailyNoteService, IRecipient<UserRemovedMessa
|
|||||||
{
|
{
|
||||||
ThreadHelper.InvokeOnMainThread(() =>
|
ThreadHelper.InvokeOnMainThread(() =>
|
||||||
{
|
{
|
||||||
|
// Database items have been deleted by cascade deleting.
|
||||||
entries?.RemoveWhere(n => n.UserId == message.RemovedUserId);
|
entries?.RemoveWhere(n => n.UserId == message.RemovedUserId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -151,6 +152,7 @@ internal class DailyNoteService : IDailyNoteService, IRecipient<UserRemovedMessa
|
|||||||
|
|
||||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
|
// DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s)
|
||||||
scope.ServiceProvider.GetRequiredService<AppDbContext>().DailyNotes.RemoveAndSave(entry);
|
scope.ServiceProvider.GetRequiredService<AppDbContext>().DailyNotes.RemoveAndSave(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Snap.Hutao.Core.Database;
|
using Snap.Hutao.Core.Database;
|
||||||
using Snap.Hutao.Core.Diagnostics;
|
using Snap.Hutao.Core.Diagnostics;
|
||||||
@@ -296,6 +297,7 @@ internal class GachaLogService : IGachaLogService
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token.ThrowIfCancellationRequested();
|
||||||
SaveGachaItems(itemsToAdd, isLazy, archive, configration.EndId);
|
SaveGachaItems(itemsToAdd, isLazy, archive, configration.EndId);
|
||||||
await RandomDelayAsync(token).ConfigureAwait(false);
|
await RandomDelayAsync(token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -327,14 +329,21 @@ internal class GachaLogService : IGachaLogService
|
|||||||
|
|
||||||
if (archive != null)
|
if (archive != null)
|
||||||
{
|
{
|
||||||
// TODO: replace with MaxBy
|
try
|
||||||
// https://github.com/dotnet/efcore/issues/25566
|
{
|
||||||
// .MaxBy(i => i.Id);
|
// TODO: replace with MaxBy
|
||||||
item = appDbContext.GachaItems
|
// https://github.com/dotnet/efcore/issues/25566
|
||||||
.Where(i => i.ArchiveId == archive.InnerId)
|
// .MaxBy(i => i.Id);
|
||||||
.Where(i => i.QueryType == configType)
|
item = appDbContext.GachaItems
|
||||||
.OrderByDescending(i => i.Id)
|
.Where(i => i.ArchiveId == archive.InnerId)
|
||||||
.FirstOrDefault();
|
.Where(i => i.QueryType == configType)
|
||||||
|
.OrderByDescending(i => i.Id)
|
||||||
|
.FirstOrDefault();
|
||||||
|
}
|
||||||
|
catch (SqliteException ex)
|
||||||
|
{
|
||||||
|
throw new Core.ExceptionService.UserdataCorruptedException("无法获取祈愿记录 End Id", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return item?.Id ?? 0L;
|
return item?.Id ?? 0L;
|
||||||
@@ -369,11 +378,12 @@ internal class GachaLogService : IGachaLogService
|
|||||||
|
|
||||||
private INameQuality GetNameQualityByItemId(int id)
|
private INameQuality GetNameQualityByItemId(int id)
|
||||||
{
|
{
|
||||||
return id.Place() switch
|
int place = id.Place();
|
||||||
|
return place switch
|
||||||
{
|
{
|
||||||
8 => idAvatarMap![id],
|
8 => idAvatarMap![id],
|
||||||
5 => idWeaponMap![id],
|
5 => idWeaponMap![id],
|
||||||
_ => throw Must.NeverHappen(),
|
_ => throw Must.NeverHappen($"Id places: {place}"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -143,6 +143,11 @@ internal class GameService : IGameService, IDisposable
|
|||||||
string gamePath = GetGamePathSkipLocator();
|
string gamePath = GetGamePathSkipLocator();
|
||||||
string configPath = Path.Combine(Path.GetDirectoryName(gamePath) ?? string.Empty, ConfigFile);
|
string configPath = Path.Combine(Path.GetDirectoryName(gamePath) ?? string.Empty, ConfigFile);
|
||||||
|
|
||||||
|
if (!File.Exists(configPath))
|
||||||
|
{
|
||||||
|
return new(null, null, configPath);
|
||||||
|
}
|
||||||
|
|
||||||
using (FileStream stream = File.OpenRead(configPath))
|
using (FileStream stream = File.OpenRead(configPath))
|
||||||
{
|
{
|
||||||
List<IniElement> elements = IniSerializer.Deserialize(stream).ToList();
|
List<IniElement> elements = IniSerializer.Deserialize(stream).ToList();
|
||||||
|
|||||||
@@ -18,14 +18,21 @@ public struct MultiChannel
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string SubChannel;
|
public string SubChannel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置文件路径 当不为 null 时则存在文件读写问题
|
||||||
|
/// </summary>
|
||||||
|
public string? ConfigFilePath;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的多通道
|
/// 构造一个新的多通道
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channel">通道</param>
|
/// <param name="channel">通道</param>
|
||||||
/// <param name="subChannel">子通道</param>
|
/// <param name="subChannel">子通道</param>
|
||||||
public MultiChannel(string? channel, string? subChannel)
|
/// <param name="configFilePath">配置文件路径</param>
|
||||||
|
public MultiChannel(string? channel, string? subChannel, string? configFilePath = null)
|
||||||
{
|
{
|
||||||
Channel = channel ?? string.Empty;
|
Channel = channel ?? string.Empty;
|
||||||
SubChannel = subChannel ?? string.Empty;
|
SubChannel = subChannel ?? string.Empty;
|
||||||
|
ConfigFilePath = configFilePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ using Snap.Hutao.Core.Diagnostics;
|
|||||||
using Snap.Hutao.Win32;
|
using Snap.Hutao.Win32;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using Windows.Win32.Foundation;
|
||||||
using Windows.Win32.System.Diagnostics.ToolHelp;
|
using Windows.Win32.System.Diagnostics.ToolHelp;
|
||||||
using static Windows.Win32.PInvoke;
|
using static Windows.Win32.PInvoke;
|
||||||
|
|
||||||
@@ -75,21 +76,24 @@ internal class GameFpsUnlocker : IGameFpsUnlocker
|
|||||||
|
|
||||||
private static unsafe MODULEENTRY32 FindModule(int processId, string moduleName)
|
private static unsafe MODULEENTRY32 FindModule(int processId, string moduleName)
|
||||||
{
|
{
|
||||||
using (SafeFileHandle snapshot = CreateToolhelp32Snapshot_SafeHandle(CREATE_TOOLHELP_SNAPSHOT_FLAGS.TH32CS_SNAPMODULE, (uint)processId))
|
HANDLE snapshot = CreateToolhelp32Snapshot(CREATE_TOOLHELP_SNAPSHOT_FLAGS.TH32CS_SNAPMODULE, (uint)processId);
|
||||||
|
using (snapshot.AutoClose())
|
||||||
{
|
{
|
||||||
Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
|
Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
|
||||||
|
|
||||||
MODULEENTRY32 entry = StructMarshal.MODULEENTRY32();
|
MODULEENTRY32 entry = StructMarshal.MODULEENTRY32();
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
// First module must be exe. Ignoring it.
|
bool loop = Module32First(snapshot, &entry);
|
||||||
for (Module32First(snapshot, ref entry); Module32Next(snapshot, ref entry);)
|
while (loop)
|
||||||
{
|
{
|
||||||
if (entry.th32ProcessID == processId && entry.szModule.AsString() == moduleName)
|
if (entry.th32ProcessID == processId && entry.szModule.AsString() == moduleName)
|
||||||
{
|
{
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loop = Module32Next(snapshot, &entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return found ? entry : default;
|
return found ? entry : default;
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ internal class NavigationService : INavigationService
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void GoBack()
|
public void GoBack()
|
||||||
{
|
{
|
||||||
Program.DispatcherQueue!.TryEnqueue(() =>
|
ThreadHelper.InvokeOnMainThread(() =>
|
||||||
{
|
{
|
||||||
bool canGoBack = Frame?.CanGoBack ?? false;
|
bool canGoBack = Frame?.CanGoBack ?? false;
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,14 @@ internal class UserService : IUserService
|
|||||||
}
|
}
|
||||||
|
|
||||||
userCollection = users.ToObservableCollection();
|
userCollection = users.ToObservableCollection();
|
||||||
Current = users.SingleOrDefault(user => user.IsSelected);
|
try
|
||||||
|
{
|
||||||
|
Current = users.SingleOrDefault(user => user.IsSelected);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
throw new Core.ExceptionService.UserdataCorruptedException("无法设置当前用户", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return userCollection;
|
return userCollection;
|
||||||
@@ -157,8 +164,16 @@ internal class UserService : IUserService
|
|||||||
{
|
{
|
||||||
if (userCollection != null)
|
if (userCollection != null)
|
||||||
{
|
{
|
||||||
// TODO: optimize match speed.
|
try
|
||||||
return userCollection.SelectMany(u => u.UserGameRoles).SingleOrDefault(r => r.GameUid == uid);
|
{
|
||||||
|
// TODO: optimize match speed.
|
||||||
|
return userCollection.SelectMany(u => u.UserGameRoles).SingleOrDefault(r => r.GameUid == uid);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
// Sequence contains more than one matching element
|
||||||
|
// TODO: return a specialize UserGameRole to indicate error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -37,9 +37,12 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Control\Panel\PanelSelector.xaml" />
|
<None Remove="Control\Panel\PanelSelector.xaml" />
|
||||||
|
<None Remove="Control\Theme\FontStyle.xaml" />
|
||||||
<None Remove="LaunchGameWindow.xaml" />
|
<None Remove="LaunchGameWindow.xaml" />
|
||||||
<None Remove="NativeMethods.json" />
|
<None Remove="NativeMethods.json" />
|
||||||
<None Remove="NativeMethods.txt" />
|
<None Remove="NativeMethods.txt" />
|
||||||
|
<None Remove="Resource\Font\CascadiaMono.ttf" />
|
||||||
|
<None Remove="Resource\Font\MiSans-Regular.ttf" />
|
||||||
<None Remove="Resource\Icon\UI_AchievementIcon_3_3.png" />
|
<None Remove="Resource\Icon\UI_AchievementIcon_3_3.png" />
|
||||||
<None Remove="Resource\Icon\UI_BagTabIcon_Avatar.png" />
|
<None Remove="Resource\Icon\UI_BagTabIcon_Avatar.png" />
|
||||||
<None Remove="Resource\Icon\UI_BagTabIcon_Weapon.png" />
|
<None Remove="Resource\Icon\UI_BagTabIcon_Weapon.png" />
|
||||||
@@ -122,7 +125,8 @@
|
|||||||
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||||
<Content Include="Assets\StoreLogo.png" />
|
<Content Include="Assets\StoreLogo.png" />
|
||||||
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
|
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
|
||||||
<Content Include="Resource\Font\Segoe Fluent Icons.ttf" />
|
<Content Include="Resource\Font\CascadiaMono.ttf" />
|
||||||
|
<Content Include="Resource\Font\MiSans-Regular.ttf" />
|
||||||
<Content Include="Resource\Icon\UI_AchievementIcon_3_3.png" />
|
<Content Include="Resource\Icon\UI_AchievementIcon_3_3.png" />
|
||||||
<Content Include="Resource\Icon\UI_BagTabIcon_Avatar.png" />
|
<Content Include="Resource\Icon\UI_BagTabIcon_Avatar.png" />
|
||||||
<Content Include="Resource\Icon\UI_BagTabIcon_Weapon.png" />
|
<Content Include="Resource\Icon\UI_BagTabIcon_Weapon.png" />
|
||||||
@@ -154,8 +158,8 @@
|
|||||||
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Behaviors" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.UI.Behaviors" Version="7.1.2" />
|
||||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
|
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.1">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.2">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
@@ -171,14 +175,14 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.25267-preview" />
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.25276-preview" />
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221209.1" />
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221209.1" />
|
||||||
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.435">
|
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.435">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="TaskScheduler" Version="2.10.1" />
|
<PackageReference Include="TaskScheduler" Version="2.10.1" />
|
||||||
<PackageReference Include="WinUICommunity.SettingsUI" Version="3.0.0" />
|
<PackageReference Include="WinUICommunity.SettingsUI" Version="3.0.1" />
|
||||||
<Manifest Include="$(ApplicationManifest)" />
|
<Manifest Include="$(ApplicationManifest)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@@ -415,4 +419,9 @@
|
|||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Control\Style\FontStyle.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -64,25 +64,18 @@
|
|||||||
|
|
||||||
<DataTemplate x:Key="OrangeGridTemplate" x:DataType="shmbg:SummaryItem">
|
<DataTemplate x:Key="OrangeGridTemplate" x:DataType="shmbg:SummaryItem">
|
||||||
<Grid Width="40" Margin="0,4,4,0">
|
<Grid Width="40" Margin="0,4,4,0">
|
||||||
<Border
|
<Border Style="{StaticResource BorderCardStyle}" ToolTipService.ToolTip="{Binding TimeFormatted}">
|
||||||
Background="{StaticResource CardBackgroundFillColorDefault}"
|
|
||||||
BorderBrush="{StaticResource CardStrokeColorDefault}"
|
|
||||||
BorderThickness="1"
|
|
||||||
CornerRadius="{StaticResource CompatCornerRadius}"
|
|
||||||
ToolTipService.ToolTip="{Binding TimeFormatted}">
|
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<shvc:ItemIcon
|
<shvc:ItemIcon
|
||||||
Width="40"
|
Width="40"
|
||||||
Height="40"
|
Height="40"
|
||||||
Icon="{Binding Icon}"
|
Icon="{Binding Icon}"
|
||||||
Quality="QUALITY_ORANGE"/>
|
Quality="QUALITY_ORANGE"/>
|
||||||
<!--<shci:CachedImage
|
|
||||||
Source="{Binding Icon}"
|
|
||||||
Height="40" Width="40"/>-->
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Center"
|
HorizontalTextAlignment="Center"
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
Text="{Binding LastPull}"
|
Text="{Binding LastPull}"
|
||||||
|
TextTrimming="None"
|
||||||
TextWrapping="NoWrap">
|
TextWrapping="NoWrap">
|
||||||
<TextBlock.Foreground>
|
<TextBlock.Foreground>
|
||||||
<SolidColorBrush Color="{Binding Color}"/>
|
<SolidColorBrush Color="{Binding Color}"/>
|
||||||
@@ -94,7 +87,7 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
|
|
||||||
<Border Background="{StaticResource CardBackgroundFillColorDefaultBrush}" CornerRadius="{StaticResource CompatCornerRadius}">
|
<Border Style="{StaticResource BorderCardStyle}">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="auto"/>
|
<RowDefinition Height="auto"/>
|
||||||
@@ -108,19 +101,23 @@
|
|||||||
HorizontalContentAlignment="Stretch"
|
HorizontalContentAlignment="Stretch"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
BorderBrush="{x:Null}"
|
BorderBrush="{x:Null}"
|
||||||
|
BorderThickness="0"
|
||||||
|
CornerRadius="4,4,0,0"
|
||||||
IsExpanded="True">
|
IsExpanded="True">
|
||||||
|
<Expander.Resources>
|
||||||
|
<Thickness x:Key="ExpanderHeaderBorderThickness">0,0,0,1</Thickness>
|
||||||
|
</Expander.Resources>
|
||||||
<Expander.Header>
|
<Expander.Header>
|
||||||
<Grid Grid.Row="0">
|
<Grid>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||||
Text="{Binding Name}"/>
|
Text="{Binding Name}"/>
|
||||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0,4,12,2"
|
Margin="0,0,6,0"
|
||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Center"
|
||||||
FontFamily="Consolas"
|
FontSize="20"
|
||||||
FontSize="24"
|
|
||||||
Text="{Binding TotalCount}"
|
Text="{Binding TotalCount}"
|
||||||
Visibility="{Binding ElementName=DetailExpander, Path=IsExpanded, Converter={StaticResource BoolToVisibilityRevertConverter}}"/>
|
Visibility="{Binding ElementName=DetailExpander, Path=IsExpanded, Converter={StaticResource BoolToVisibilityRevertConverter}}"/>
|
||||||
<shcp:PanelSelector
|
<shcp:PanelSelector
|
||||||
@@ -135,7 +132,7 @@
|
|||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0,4,0,4"
|
Margin="0,4,0,4"
|
||||||
FontFamily="Consolas"
|
FontFamily="{StaticResource CascadiaMonoAndMiSans}"
|
||||||
FontSize="48"
|
FontSize="48"
|
||||||
Text="{Binding TotalCount}"/>
|
Text="{Binding TotalCount}"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -149,12 +146,7 @@
|
|||||||
<ColumnDefinition/>
|
<ColumnDefinition/>
|
||||||
<ColumnDefinition/>
|
<ColumnDefinition/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Border
|
<Border Grid.Column="0" Style="{StaticResource BorderCardStyle}">
|
||||||
Grid.Column="0"
|
|
||||||
Background="{StaticResource CardBackgroundFillColorDefault}"
|
|
||||||
BorderBrush="{StaticResource CardStrokeColorDefault}"
|
|
||||||
BorderThickness="1"
|
|
||||||
CornerRadius="{StaticResource CompatCornerRadius}">
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="auto"/>
|
<ColumnDefinition Width="auto"/>
|
||||||
@@ -165,14 +157,13 @@
|
|||||||
Width="40"
|
Width="40"
|
||||||
Height="40"
|
Height="40"
|
||||||
Margin="4"
|
Margin="4"
|
||||||
Background="{StaticResource CardBackgroundFillColorDefaultBrush}"
|
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||||
Foreground="{StaticResource OrangeBrush}"
|
Foreground="{StaticResource OrangeBrush}"
|
||||||
IsIndeterminate="False"
|
IsIndeterminate="False"
|
||||||
Maximum="{Binding GuarenteeOrangeThreshold}"
|
Maximum="{Binding GuarenteeOrangeThreshold}"
|
||||||
Value="{Binding LastOrangePull}"/>
|
Value="{Binding LastOrangePull}"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="0,0,0,2"
|
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Foreground="{StaticResource OrangeBrush}"
|
Foreground="{StaticResource OrangeBrush}"
|
||||||
@@ -180,7 +171,6 @@
|
|||||||
Text="{Binding LastOrangePull}"/>
|
Text="{Binding LastOrangePull}"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="0,0,0,2"
|
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Foreground="{StaticResource OrangeBrush}"
|
Foreground="{StaticResource OrangeBrush}"
|
||||||
@@ -188,12 +178,7 @@
|
|||||||
Text="五星"/>
|
Text="五星"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
<Border
|
<Border Grid.Column="1" Style="{StaticResource BorderCardStyle}">
|
||||||
Grid.Column="1"
|
|
||||||
Background="{StaticResource CardBackgroundFillColorDefault}"
|
|
||||||
BorderBrush="{StaticResource CardStrokeColorDefault}"
|
|
||||||
BorderThickness="1"
|
|
||||||
CornerRadius="{StaticResource CompatCornerRadius}">
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="auto"/>
|
<ColumnDefinition Width="auto"/>
|
||||||
@@ -204,14 +189,13 @@
|
|||||||
Width="40"
|
Width="40"
|
||||||
Height="40"
|
Height="40"
|
||||||
Margin="4"
|
Margin="4"
|
||||||
Background="{StaticResource CardBackgroundFillColorDefaultBrush}"
|
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||||
Foreground="{StaticResource PurpleBrush}"
|
Foreground="{StaticResource PurpleBrush}"
|
||||||
IsIndeterminate="False"
|
IsIndeterminate="False"
|
||||||
Maximum="{Binding GuarenteePurpleThreshold}"
|
Maximum="{Binding GuarenteePurpleThreshold}"
|
||||||
Value="{Binding LastPurplePull}"/>
|
Value="{Binding LastPurplePull}"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="0,0,0,2"
|
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Foreground="{StaticResource PurpleBrush}"
|
Foreground="{StaticResource PurpleBrush}"
|
||||||
@@ -219,7 +203,6 @@
|
|||||||
Text="{Binding LastPurplePull}"/>
|
Text="{Binding LastPurplePull}"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="0,0,0,2"
|
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Foreground="{StaticResource PurpleBrush}"
|
Foreground="{StaticResource PurpleBrush}"
|
||||||
@@ -232,20 +215,17 @@
|
|||||||
<StackPanel Margin="0,8,0,0" Orientation="Horizontal">
|
<StackPanel Margin="0,8,0,0" Orientation="Horizontal">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
FontFamily="Consolas"
|
|
||||||
Opacity="0.6"
|
Opacity="0.6"
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
Text="{Binding FromFormatted}"/>
|
Text="{Binding FromFormatted}"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
FontFamily="Consolas"
|
|
||||||
Opacity="0.6"
|
Opacity="0.6"
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
Text="-"/>
|
Text="-"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
FontFamily="Consolas"
|
|
||||||
Opacity="0.6"
|
Opacity="0.6"
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
Text="{Binding ToFormatted}"/>
|
Text="{Binding ToFormatted}"/>
|
||||||
@@ -260,7 +240,7 @@
|
|||||||
Text="五星"/>
|
Text="五星"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
FontFamily="Consolas"
|
FontFamily="{StaticResource CascadiaMonoAndMiSans}"
|
||||||
Foreground="{StaticResource OrangeBrush}"
|
Foreground="{StaticResource OrangeBrush}"
|
||||||
Style="{StaticResource BodyTextBlockStyle}"
|
Style="{StaticResource BodyTextBlockStyle}"
|
||||||
Text="{Binding TotalOrangeFormatted}"/>
|
Text="{Binding TotalOrangeFormatted}"/>
|
||||||
@@ -272,7 +252,7 @@
|
|||||||
Text="四星"/>
|
Text="四星"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
FontFamily="Consolas"
|
FontFamily="{StaticResource CascadiaMonoAndMiSans}"
|
||||||
Foreground="{StaticResource PurpleBrush}"
|
Foreground="{StaticResource PurpleBrush}"
|
||||||
Style="{StaticResource BodyTextBlockStyle}"
|
Style="{StaticResource BodyTextBlockStyle}"
|
||||||
Text="{Binding TotalPurpleFormatted}"/>
|
Text="{Binding TotalPurpleFormatted}"/>
|
||||||
@@ -284,7 +264,7 @@
|
|||||||
Text="三星"/>
|
Text="三星"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
FontFamily="Consolas"
|
FontFamily="{StaticResource CascadiaMonoAndMiSans}"
|
||||||
Foreground="{StaticResource BlueBrush}"
|
Foreground="{StaticResource BlueBrush}"
|
||||||
Style="{StaticResource BodyTextBlockStyle}"
|
Style="{StaticResource BodyTextBlockStyle}"
|
||||||
Text="{Binding TotalBlueFormatted}"/>
|
Text="{Binding TotalBlueFormatted}"/>
|
||||||
@@ -294,7 +274,6 @@
|
|||||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="五星平均抽数"/>
|
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="五星平均抽数"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
FontFamily="Consolas,MicroSoft YaHei UI"
|
|
||||||
Style="{StaticResource BodyTextBlockStyle}"
|
Style="{StaticResource BodyTextBlockStyle}"
|
||||||
Text="{Binding AverageOrangePullFormatted}"/>
|
Text="{Binding AverageOrangePullFormatted}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -302,19 +281,16 @@
|
|||||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="UP 平均抽数"/>
|
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="UP 平均抽数"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
FontFamily="Consolas,MicroSoft YaHei UI"
|
|
||||||
Style="{StaticResource BodyTextBlockStyle}"
|
Style="{StaticResource BodyTextBlockStyle}"
|
||||||
Text="{Binding AverageUpOrangePullFormatted}"/>
|
Text="{Binding AverageUpOrangePullFormatted}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid Margin="0,2,0,0">
|
<Grid Margin="0,2,0,0">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
FontFamily="Consolas,MicroSoft YaHei UI"
|
|
||||||
Style="{StaticResource BodyTextBlockStyle}"
|
Style="{StaticResource BodyTextBlockStyle}"
|
||||||
Text="{Binding MaxOrangePullFormatted}"/>
|
Text="{Binding MaxOrangePullFormatted}"/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
FontFamily="Consolas,MicroSoft YaHei UI"
|
|
||||||
Style="{StaticResource BodyTextBlockStyle}"
|
Style="{StaticResource BodyTextBlockStyle}"
|
||||||
Text="{Binding MinOrangePullFormatted}"/>
|
Text="{Binding MinOrangePullFormatted}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -4,17 +4,18 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
Title="米游社养成计算"
|
|
||||||
Closed="OnContentDialogClosed"
|
Closed="OnContentDialogClosed"
|
||||||
DefaultButton="Primary"
|
|
||||||
PrimaryButtonText="完成"
|
|
||||||
Style="{StaticResource DefaultContentDialogStyle}"
|
Style="{StaticResource DefaultContentDialogStyle}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
<ContentControl.Resources>
|
||||||
|
<Thickness x:Key="ContentDialogPadding">0</Thickness>
|
||||||
|
<CornerRadius x:Key="OverlayCornerRadius">0</CornerRadius>
|
||||||
|
</ContentControl.Resources>
|
||||||
<Grid Loaded="OnGridLoaded">
|
<Grid Loaded="OnGridLoaded">
|
||||||
<WebView2
|
<WebView2
|
||||||
Name="WebView"
|
Name="WebView"
|
||||||
Width="380"
|
Width="380"
|
||||||
Height="500"/>
|
Height="500"
|
||||||
|
AllowFocusOnInteraction="False"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</ContentDialog>
|
</ContentDialog>
|
||||||
|
|||||||
@@ -8,15 +8,17 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
Closed="OnContentDialogClosed"
|
Closed="OnContentDialogClosed"
|
||||||
DefaultButton="Primary"
|
|
||||||
PrimaryButtonText="完成"
|
|
||||||
Style="{StaticResource DefaultContentDialogStyle}"
|
Style="{StaticResource DefaultContentDialogStyle}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
<ContentControl.Resources>
|
||||||
|
<Thickness x:Key="ContentDialogPadding">0</Thickness>
|
||||||
|
<CornerRadius x:Key="OverlayCornerRadius">0</CornerRadius>
|
||||||
|
</ContentControl.Resources>
|
||||||
<Grid Loaded="OnGridLoaded">
|
<Grid Loaded="OnGridLoaded">
|
||||||
<WebView2
|
<WebView2
|
||||||
Name="WebView"
|
Name="WebView"
|
||||||
Width="360"
|
Width="360"
|
||||||
Height="580"/>
|
Height="580"
|
||||||
|
AllowFocusOnInteraction="True"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</ContentDialog>
|
</ContentDialog>
|
||||||
|
|||||||
@@ -4,17 +4,19 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
Title="米游社实时便笺"
|
|
||||||
Closed="OnContentDialogClosed"
|
Closed="OnContentDialogClosed"
|
||||||
DefaultButton="Primary"
|
|
||||||
PrimaryButtonText="完成"
|
|
||||||
Style="{StaticResource DefaultContentDialogStyle}"
|
Style="{StaticResource DefaultContentDialogStyle}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
<ContentControl.Resources>
|
||||||
|
<Thickness x:Key="ContentDialogPadding">0</Thickness>
|
||||||
|
<CornerRadius x:Key="OverlayCornerRadius">0</CornerRadius>
|
||||||
|
</ContentControl.Resources>
|
||||||
<Grid Loaded="OnGridLoaded">
|
<Grid Loaded="OnGridLoaded">
|
||||||
<WebView2
|
<WebView2
|
||||||
Name="WebView"
|
Name="WebView"
|
||||||
Width="380"
|
Width="380"
|
||||||
Height="448"/>
|
Height="448"
|
||||||
|
AllowFocusOnInteraction="False"
|
||||||
|
DefaultBackgroundColor="Transparent"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</ContentDialog>
|
</ContentDialog>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -11,20 +11,19 @@ using Snap.Hutao.Web.Bridge;
|
|||||||
namespace Snap.Hutao.View.Dialog;
|
namespace Snap.Hutao.View.Dialog;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ʵʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>Ի<EFBFBD><EFBFBD><EFBFBD>
|
/// 实时便笺验证对话框
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class DailyNoteVerificationDialog : ContentDialog
|
public sealed partial class DailyNoteVerificationDialog : ContentDialog
|
||||||
{
|
{
|
||||||
private readonly IServiceScope scope;
|
private readonly IServiceScope scope;
|
||||||
private readonly UserAndUid userAndUid;
|
private readonly UserAndUid userAndUid;
|
||||||
|
|
||||||
[SuppressMessage("", "IDE0052")]
|
|
||||||
private DailyNoteJsInterface? dailyNoteJsInterface;
|
private DailyNoteJsInterface? dailyNoteJsInterface;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD><EFBFBD>µ<EFBFBD>ʵʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>Ի<EFBFBD><EFBFBD><EFBFBD>
|
/// 构造一个新的实时便笺验证对话框
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="userAndUid"><EFBFBD>û<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ</param>
|
/// <param name="userAndUid">用户与角色</param>
|
||||||
public DailyNoteVerificationDialog(UserAndUid userAndUid)
|
public DailyNoteVerificationDialog(UserAndUid userAndUid)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -46,14 +45,21 @@ public sealed partial class DailyNoteVerificationDialog : ContentDialog
|
|||||||
Model.Entity.User user = userAndUid.User;
|
Model.Entity.User user = userAndUid.User;
|
||||||
coreWebView2.SetCookie(user.CookieToken, user.Ltoken, null).SetMobileUserAgent();
|
coreWebView2.SetCookie(user.CookieToken, user.Ltoken, null).SetMobileUserAgent();
|
||||||
dailyNoteJsInterface = new(coreWebView2, scope.ServiceProvider);
|
dailyNoteJsInterface = new(coreWebView2, scope.ServiceProvider);
|
||||||
|
dailyNoteJsInterface.ClosePageRequested += OnClosePageRequested;
|
||||||
|
|
||||||
string query = $"?role_id={userAndUid.Uid.Value}&server={userAndUid.Uid.Region}";
|
string query = $"?role_id={userAndUid.Uid.Value}&server={userAndUid.Uid.Region}";
|
||||||
coreWebView2.Navigate($"https://webstatic.mihoyo.com/app/community-game-records/index.html?bbs_presentation_style=fullscreen#/ys/daily/{query}");
|
coreWebView2.Navigate($"https://webstatic.mihoyo.com/app/community-game-records/index.html?bbs_presentation_style=fullscreen#/ys/daily/{query}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnClosePageRequested()
|
||||||
|
{
|
||||||
|
ThreadHelper.InvokeOnMainThread(Hide);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnContentDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args)
|
private void OnContentDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args)
|
||||||
{
|
{
|
||||||
|
dailyNoteJsInterface.ClosePageRequested -= OnClosePageRequested;
|
||||||
dailyNoteJsInterface = null;
|
dailyNoteJsInterface = null;
|
||||||
scope.Dispose();
|
scope.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.Web.WebView2.Core;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.View.Extension;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="CoreWebView2Environment"/> 扩展
|
||||||
|
/// </summary>
|
||||||
|
internal static class CoreWebView2EnvironmentExtension
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 退出
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="environment">环境</param>
|
||||||
|
public static void Exit(this CoreWebView2Environment environment)
|
||||||
|
{
|
||||||
|
IReadOnlyList<CoreWebView2ProcessInfo> processInfos = environment.GetProcessInfos();
|
||||||
|
|
||||||
|
foreach (CoreWebView2ProcessInfo processInfo in processInfos)
|
||||||
|
{
|
||||||
|
Process p = Process.GetProcessById(processInfo.ProcessId);
|
||||||
|
if (p.ProcessName == "msedgewebview2.exe")
|
||||||
|
{
|
||||||
|
p.Kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -160,6 +160,9 @@
|
|||||||
Margin="16,0,0,16"
|
Margin="16,0,0,16"
|
||||||
ItemsPanel="{StaticResource ItemsStackPanelTemplate}"
|
ItemsPanel="{StaticResource ItemsStackPanelTemplate}"
|
||||||
ItemsSource="{Binding Achievements}">
|
ItemsSource="{Binding Achievements}">
|
||||||
|
<ItemsControl.ItemContainerTransitions>
|
||||||
|
<ContentThemeTransition/>
|
||||||
|
</ItemsControl.ItemContainerTransitions>
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid
|
<Grid
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ using Microsoft.UI.Xaml.Navigation;
|
|||||||
using Microsoft.Web.WebView2.Core;
|
using Microsoft.Web.WebView2.Core;
|
||||||
using Snap.Hutao.Core;
|
using Snap.Hutao.Core;
|
||||||
using Snap.Hutao.Service.Navigation;
|
using Snap.Hutao.Service.Navigation;
|
||||||
|
using Snap.Hutao.View.Extension;
|
||||||
|
using System.Diagnostics;
|
||||||
using Windows.System;
|
using Windows.System;
|
||||||
|
|
||||||
namespace Snap.Hutao.View.Page;
|
namespace Snap.Hutao.View.Page;
|
||||||
@@ -34,10 +36,12 @@ public sealed partial class AnnouncementContentPage : Microsoft.UI.Xaml.Controls
|
|||||||
private const string DarkAccentColor2 = "background-color: rgb(254, 245, 231);";
|
private const string DarkAccentColor2 = "background-color: rgb(254, 245, 231);";
|
||||||
|
|
||||||
// support click open browser.
|
// support click open browser.
|
||||||
private const string MihoyoSDKDefinition =
|
private const string MihoyoSDKDefinition = """
|
||||||
@"window.miHoYoGameJSSDK = {
|
window.miHoYoGameJSSDK = {
|
||||||
openInBrowser: function(url){ window.chrome.webview.postMessage(url); },
|
openInBrowser: function(url){ window.chrome.webview.postMessage(url); },
|
||||||
openInWebview: function(url){ location.href = url }}";
|
openInWebview: function(url){ location.href = url }
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
private string? targetContent;
|
private string? targetContent;
|
||||||
|
|
||||||
@@ -61,6 +65,13 @@ openInWebview: function(url){ location.href = url }}";
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnNavigatedFrom(e);
|
||||||
|
//WebView.CoreWebView2.Environment.Exit();
|
||||||
|
}
|
||||||
|
|
||||||
private static string? ReplaceForeground(string? rawContent, ElementTheme theme)
|
private static string? ReplaceForeground(string? rawContent, ElementTheme theme)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(rawContent))
|
if (string.IsNullOrWhiteSpace(rawContent))
|
||||||
|
|||||||
@@ -149,7 +149,7 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
<Pivot>
|
<Pivot Style="{StaticResource DefaultPivotStyle}">
|
||||||
<PivotItem
|
<PivotItem
|
||||||
Content="{Binding Announcement.List[0]}"
|
Content="{Binding Announcement.List[0]}"
|
||||||
ContentTemplate="{StaticResource AnnouncementTemplate}"
|
ContentTemplate="{StaticResource AnnouncementTemplate}"
|
||||||
|
|||||||
@@ -203,8 +203,12 @@
|
|||||||
Margin="4,0,0,2"
|
Margin="4,0,0,2"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Foreground="#FFFFFFFF"
|
Foreground="#FFFFFFFF"
|
||||||
|
HorizontalTextAlignment="Center"
|
||||||
Style="{StaticResource CaptionTextBlockStyle}"
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
Text="{Binding SelectedAvatar.FetterLevel}"/>
|
Text="{Binding SelectedAvatar.FetterLevel}"
|
||||||
|
TextAlignment="Center"
|
||||||
|
TextTrimming="None"
|
||||||
|
TextWrapping="NoWrap"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -481,7 +481,8 @@
|
|||||||
<wsc:Setting
|
<wsc:Setting
|
||||||
Description="使用当前账号的Cookie信息刷新祈愿记录"
|
Description="使用当前账号的Cookie信息刷新祈愿记录"
|
||||||
Header="Stoken 刷新"
|
Header="Stoken 刷新"
|
||||||
Icon="{shcm:FontIcon Glyph=}">
|
Icon="{shcm:FontIcon Glyph=}"
|
||||||
|
Style="{StaticResource DefaultSettingStyle}">
|
||||||
<Button Command="{Binding RefreshByStokenCommand}" Content="获取"/>
|
<Button Command="{Binding RefreshByStokenCommand}" Content="获取"/>
|
||||||
</wsc:Setting>
|
</wsc:Setting>
|
||||||
<wsc:Setting
|
<wsc:Setting
|
||||||
|
|||||||
@@ -468,7 +468,16 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
|
|||||||
private async Task UpdateAchievementsAsync(Model.Entity.AchievementArchive archive)
|
private async Task UpdateAchievementsAsync(Model.Entity.AchievementArchive archive)
|
||||||
{
|
{
|
||||||
List<Model.Metadata.Achievement.Achievement> rawAchievements = await metadataService.GetAchievementsAsync(CancellationToken).ConfigureAwait(false);
|
List<Model.Metadata.Achievement.Achievement> rawAchievements = await metadataService.GetAchievementsAsync(CancellationToken).ConfigureAwait(false);
|
||||||
List<Model.Binding.Achievement.Achievement> combined = achievementService.GetAchievements(archive, rawAchievements);
|
List<Model.Binding.Achievement.Achievement> combined;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
combined = achievementService.GetAchievements(archive, rawAchievements);
|
||||||
|
}
|
||||||
|
catch (Core.ExceptionService.UserdataCorruptedException ex)
|
||||||
|
{
|
||||||
|
infoBarService.Error(ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Assemble achievements on the UI thread.
|
// Assemble achievements on the UI thread.
|
||||||
await ThreadHelper.SwitchToMainThreadAsync();
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
|
|||||||
@@ -88,9 +88,15 @@ internal class DailyNoteViewModel : Abstraction.ViewModel
|
|||||||
{
|
{
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{
|
{
|
||||||
refreshSecondsEntry!.SetInt32(value.Value);
|
if (!ScheduleTaskHelper.RegisterForDailyNoteRefresh(value.Value))
|
||||||
appDbContext.Settings.UpdateAndSave(refreshSecondsEntry!);
|
{
|
||||||
ScheduleTaskHelper.RegisterForDailyNoteRefresh(value.Value);
|
Ioc.Default.GetRequiredService<IInfoBarService>().Warning("注册计划任务失败,请使用管理员模式重试");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
refreshSecondsEntry!.SetInt32(value.Value);
|
||||||
|
appDbContext.Settings.UpdateAndSave(refreshSecondsEntry!);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,24 +176,38 @@ internal class DailyNoteViewModel : Abstraction.ViewModel
|
|||||||
|
|
||||||
private async Task OpenUIAsync()
|
private async Task OpenUIAsync()
|
||||||
{
|
{
|
||||||
UserAndUids = await userService.GetRoleCollectionAsync().ConfigureAwait(true);
|
UserAndUids = await userService.GetRoleCollectionAsync().ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ThrowIfViewDisposed();
|
||||||
|
using (await DisposeLock.EnterAsync().ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
ThrowIfViewDisposed();
|
||||||
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
|
|
||||||
refreshSecondsEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteRefreshSeconds, "480");
|
refreshSecondsEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteRefreshSeconds, "480");
|
||||||
selectedRefreshTime = refreshTimes.Single(t => t.Value == refreshSecondsEntry.GetInt32());
|
selectedRefreshTime = refreshTimes.Single(t => t.Value == refreshSecondsEntry.GetInt32());
|
||||||
ScheduleTaskHelper.RegisterForDailyNoteRefresh(480);
|
OnPropertyChanged(nameof(SelectedRefreshTime));
|
||||||
OnPropertyChanged(nameof(SelectedRefreshTime));
|
ScheduleTaskHelper.RegisterForDailyNoteRefresh(480);
|
||||||
|
|
||||||
reminderNotifyEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteReminderNotify, SettingEntryHelper.FalseString);
|
reminderNotifyEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteReminderNotify, SettingEntryHelper.FalseString);
|
||||||
isReminderNotification = reminderNotifyEntry.GetBoolean();
|
isReminderNotification = reminderNotifyEntry.GetBoolean();
|
||||||
OnPropertyChanged(nameof(IsReminderNotification));
|
OnPropertyChanged(nameof(IsReminderNotification));
|
||||||
|
|
||||||
silentModeEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteSilentWhenPlayingGame, SettingEntryHelper.FalseString);
|
silentModeEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteSilentWhenPlayingGame, SettingEntryHelper.FalseString);
|
||||||
isSilentWhenPlayingGame = silentModeEntry.GetBoolean();
|
isSilentWhenPlayingGame = silentModeEntry.GetBoolean();
|
||||||
OnPropertyChanged(nameof(IsSilentWhenPlayingGame));
|
OnPropertyChanged(nameof(IsSilentWhenPlayingGame));
|
||||||
|
|
||||||
ObservableCollection<DailyNoteEntry> temp = await dailyNoteService.GetDailyNoteEntriesAsync().ConfigureAwait(false);
|
await ThreadHelper.SwitchToBackgroundAsync();
|
||||||
await ThreadHelper.SwitchToMainThreadAsync();
|
}
|
||||||
DailyNoteEntries = temp;
|
|
||||||
|
ObservableCollection<DailyNoteEntry> temp = await dailyNoteService.GetDailyNoteEntriesAsync().ConfigureAwait(false);
|
||||||
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
|
DailyNoteEntries = temp;
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task TrackRoleAsync(UserAndUid? role)
|
private async Task TrackRoleAsync(UserAndUid? role)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Snap.Hutao.Control.Extension;
|
using Snap.Hutao.Control.Extension;
|
||||||
using Snap.Hutao.Core.IO;
|
using Snap.Hutao.Core.IO;
|
||||||
@@ -199,7 +200,26 @@ internal class GachaLogViewModel : Abstraction.ViewModel
|
|||||||
GachaLogRefreshProgressDialog dialog = new();
|
GachaLogRefreshProgressDialog dialog = new();
|
||||||
IAsyncDisposable dialogHider = await dialog.BlockAsync().ConfigureAwait(false);
|
IAsyncDisposable dialogHider = await dialog.BlockAsync().ConfigureAwait(false);
|
||||||
Progress<FetchState> progress = new(dialog.OnReport);
|
Progress<FetchState> progress = new(dialog.OnReport);
|
||||||
bool authkeyValid = await gachaLogService.RefreshGachaLogAsync(query, strategy, progress, default).ConfigureAwait(false);
|
bool authkeyValid;
|
||||||
|
|
||||||
|
using (await DisposeLock.EnterAsync().ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
authkeyValid = await gachaLogService.RefreshGachaLogAsync(query, strategy, progress, CancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// We set true here in order to hide the dialog.
|
||||||
|
authkeyValid = true;
|
||||||
|
infoBarService.Warning("祈愿记录刷新操作被异常取消");
|
||||||
|
}
|
||||||
|
catch (Core.ExceptionService.UserdataCorruptedException ex)
|
||||||
|
{
|
||||||
|
authkeyValid = false;
|
||||||
|
infoBarService.Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await ThreadHelper.SwitchToMainThreadAsync();
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
if (authkeyValid)
|
if (authkeyValid)
|
||||||
|
|||||||
@@ -221,13 +221,21 @@ internal class LaunchGameViewModel : Abstraction.ViewModel
|
|||||||
ThrowIfViewDisposed();
|
ThrowIfViewDisposed();
|
||||||
|
|
||||||
MultiChannel multi = gameService.GetMultiChannel();
|
MultiChannel multi = gameService.GetMultiChannel();
|
||||||
SelectedScheme = KnownSchemes.FirstOrDefault(s => s.Channel == multi.Channel && s.SubChannel == multi.SubChannel);
|
if (string.IsNullOrEmpty(multi.ConfigFilePath))
|
||||||
|
{
|
||||||
|
SelectedScheme = KnownSchemes.FirstOrDefault(s => s.Channel == multi.Channel && s.SubChannel == multi.SubChannel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Ioc.Default.GetRequiredService<IInfoBarService>().Warning("无法读取游戏配置文件");
|
||||||
|
}
|
||||||
|
|
||||||
GameAccounts = await gameService.GetGameAccountCollectionAsync().ConfigureAwait(true);
|
GameAccounts = await gameService.GetGameAccountCollectionAsync().ConfigureAwait(true);
|
||||||
|
|
||||||
// Sync uid
|
// Sync uid
|
||||||
if (memoryCache.TryGetValue(DesiredUid, out object? value) && value is string uid)
|
if (memoryCache.TryGetValue(DesiredUid, out object? value) && value is string uid)
|
||||||
{
|
{
|
||||||
SelectedGameAccount = GameAccounts.SingleOrDefault(g => g.AttachUid == uid);
|
SelectedGameAccount = GameAccounts.FirstOrDefault(g => g.AttachUid == uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync from Settings
|
// Sync from Settings
|
||||||
|
|||||||
@@ -207,7 +207,16 @@ internal class SettingViewModel : Abstraction.ViewModel
|
|||||||
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
||||||
if (Directory.Exists(cacheFolder))
|
if (Directory.Exists(cacheFolder))
|
||||||
{
|
{
|
||||||
Directory.Delete(cacheFolder, true);
|
try
|
||||||
|
{
|
||||||
|
Directory.Delete(cacheFolder, true);
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
infoBarService.Warning($"清除失败,文件目录权限不足,请使用管理员模式重试");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
infoBarService.Success("清除完成");
|
infoBarService.Success("清除完成");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -52,40 +52,25 @@ internal class WelcomeViewModel : ObservableObject
|
|||||||
|
|
||||||
private async Task OpenUIAsync()
|
private async Task OpenUIAsync()
|
||||||
{
|
{
|
||||||
List<DownloadSummary> downloadSummaries = new();
|
IEnumerable<DownloadSummary> downloadSummaries = GenerateStaticResourceDownloadTasks();
|
||||||
|
|
||||||
if (!LocalSetting.Get(SettingKeys.StaticResourceV1Contract, false))
|
DownloadSummaries = downloadSummaries.ToObservableCollection();
|
||||||
{
|
|
||||||
downloadSummaries.Add(new(serviceProvider, "基础图标", "Bg"));
|
|
||||||
downloadSummaries.Add(new(serviceProvider, "角色图标", "AvatarIcon"));
|
|
||||||
downloadSummaries.Add(new(serviceProvider, "角色立绘图标", "GachaAvatarIcon"));
|
|
||||||
downloadSummaries.Add(new(serviceProvider, "角色立绘图像", "GachaAvatarImg"));
|
|
||||||
downloadSummaries.Add(new(serviceProvider, "武器图标", "EquipIcon"));
|
|
||||||
downloadSummaries.Add(new(serviceProvider, "武器立绘图标", "GachaEquipIcon"));
|
|
||||||
downloadSummaries.Add(new(serviceProvider, "名片图像", "NameCardPic"));
|
|
||||||
downloadSummaries.Add(new(serviceProvider, "天赋图标", "Skill"));
|
|
||||||
downloadSummaries.Add(new(serviceProvider, "命之座图标", "Talent"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!LocalSetting.Get(SettingKeys.StaticResourceV2Contract, false))
|
|
||||||
{
|
|
||||||
downloadSummaries.Add(new(serviceProvider, "成就图标", "AchievementIcon"));
|
|
||||||
downloadSummaries.Add(new(serviceProvider, "物品图标", "ItemIcon"));
|
|
||||||
downloadSummaries.Add(new(serviceProvider, "元素图标", "IconElement"));
|
|
||||||
downloadSummaries.Add(new(serviceProvider, "圣遗物图标", "RelicIcon"));
|
|
||||||
}
|
|
||||||
|
|
||||||
DownloadSummaries = new(downloadSummaries);
|
|
||||||
|
|
||||||
// Cancel all previous created jobs
|
// Cancel all previous created jobs
|
||||||
serviceProvider.GetRequiredService<BitsManager>().CancelAllJobs();
|
serviceProvider.GetRequiredService<BitsManager>().CancelAllJobs();
|
||||||
await Task.WhenAll(downloadSummaries.Select(d => d.DownloadAndExtractAsync())).ConfigureAwait(true);
|
await Task.WhenAll(downloadSummaries.Select(async d =>
|
||||||
|
{
|
||||||
|
await d.DownloadAndExtractAsync().ConfigureAwait(false);
|
||||||
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
|
DownloadSummaries.Remove(d);
|
||||||
|
})).ConfigureAwait(true);
|
||||||
|
|
||||||
serviceProvider.GetRequiredService<IMessenger>().Send(new Message.WelcomeStateCompleteMessage());
|
serviceProvider.GetRequiredService<IMessenger>().Send(new Message.WelcomeStateCompleteMessage());
|
||||||
|
|
||||||
// Complete StaticResourceContracts
|
// Complete StaticResourceContracts
|
||||||
LocalSetting.Set(SettingKeys.StaticResourceV1Contract, true);
|
LocalSetting.Set(SettingKeys.StaticResourceV1Contract, true);
|
||||||
LocalSetting.Set(SettingKeys.StaticResourceV2Contract, true);
|
LocalSetting.Set(SettingKeys.StaticResourceV2Contract, true);
|
||||||
|
LocalSetting.Set(SettingKeys.StaticResourceV3Contract, true);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -100,10 +85,45 @@ internal class WelcomeViewModel : ObservableObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<DownloadSummary> GenerateStaticResourceDownloadTasks()
|
||||||
|
{
|
||||||
|
Dictionary<string, DownloadSummary> downloadSummaries = new();
|
||||||
|
|
||||||
|
if (!LocalSetting.Get(SettingKeys.StaticResourceV1Contract, false))
|
||||||
|
{
|
||||||
|
downloadSummaries.TryAdd("Bg", new(serviceProvider, "基础图标", "Bg"));
|
||||||
|
downloadSummaries.TryAdd("AvatarIcon", new(serviceProvider, "角色图标", "AvatarIcon"));
|
||||||
|
downloadSummaries.TryAdd("GachaAvatarIcon", new(serviceProvider, "角色立绘图标", "GachaAvatarIcon"));
|
||||||
|
downloadSummaries.TryAdd("GachaAvatarImg", new(serviceProvider, "角色立绘图像", "GachaAvatarImg"));
|
||||||
|
downloadSummaries.TryAdd("EquipIcon", new(serviceProvider, "武器图标", "EquipIcon"));
|
||||||
|
downloadSummaries.TryAdd("GachaEquipIcon", new(serviceProvider, "武器立绘图标", "GachaEquipIcon"));
|
||||||
|
downloadSummaries.TryAdd("NameCardPic", new(serviceProvider, "名片图像", "NameCardPic"));
|
||||||
|
downloadSummaries.TryAdd("Skill", new(serviceProvider, "天赋图标", "Skill"));
|
||||||
|
downloadSummaries.TryAdd("Talent", new(serviceProvider, "命之座图标", "Talent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!LocalSetting.Get(SettingKeys.StaticResourceV2Contract, false))
|
||||||
|
{
|
||||||
|
downloadSummaries.TryAdd("AchievementIcon", new(serviceProvider, "成就图标", "AchievementIcon"));
|
||||||
|
downloadSummaries.TryAdd("ItemIcon", new(serviceProvider, "物品图标", "ItemIcon"));
|
||||||
|
downloadSummaries.TryAdd("IconElement", new(serviceProvider, "元素图标", "IconElement"));
|
||||||
|
downloadSummaries.TryAdd("RelicIcon", new(serviceProvider, "圣遗物图标", "RelicIcon"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!LocalSetting.Get(SettingKeys.StaticResourceV3Contract, false))
|
||||||
|
{
|
||||||
|
downloadSummaries.TryAdd("Skill", new(serviceProvider, "天赋图标更新", "Skill"));
|
||||||
|
downloadSummaries.TryAdd("Talent", new(serviceProvider, "命之座图标更新", "Talent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return downloadSummaries.Select(x => x.Value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 下载信息
|
/// 下载信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DownloadSummary : ObservableObject
|
[SuppressMessage("", "CA1067")]
|
||||||
|
public class DownloadSummary : ObservableObject, IEquatable<DownloadSummary>
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
private readonly BitsManager bitsManager;
|
private readonly BitsManager bitsManager;
|
||||||
@@ -145,6 +165,12 @@ internal class WelcomeViewModel : ObservableObject
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public double ProgressValue { get => progressValue; set => SetProperty(ref progressValue, value); }
|
public double ProgressValue { get => progressValue; set => SetProperty(ref progressValue, value); }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool Equals(DownloadSummary? other)
|
||||||
|
{
|
||||||
|
return fileName == other?.fileName;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 异步下载并解压
|
/// 异步下载并解压
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -211,25 +211,25 @@ internal class WikiAvatarViewModel : Abstraction.ViewModel
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImmutableIntrinsics.AssociationTypes.Contains(value))
|
if (IntrinsicImmutables.AssociationTypes.Contains(value))
|
||||||
{
|
{
|
||||||
keep = keep || avatar.FetterInfo.Association.GetDescriptionOrNull() == value;
|
keep = keep || avatar.FetterInfo.Association.GetDescriptionOrNull() == value;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImmutableIntrinsics.WeaponTypes.Contains(value))
|
if (IntrinsicImmutables.WeaponTypes.Contains(value))
|
||||||
{
|
{
|
||||||
keep = keep || avatar.Weapon.GetDescriptionOrNull() == value;
|
keep = keep || avatar.Weapon.GetDescriptionOrNull() == value;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImmutableIntrinsics.ItemQualities.Contains(value))
|
if (IntrinsicImmutables.ItemQualities.Contains(value))
|
||||||
{
|
{
|
||||||
keep = keep || avatar.Quality.GetDescriptionOrNull() == value;
|
keep = keep || avatar.Quality.GetDescriptionOrNull() == value;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImmutableIntrinsics.BodyTypes.Contains(value))
|
if (IntrinsicImmutables.BodyTypes.Contains(value))
|
||||||
{
|
{
|
||||||
keep = keep || avatar.Body.GetDescriptionOrNull() == value;
|
keep = keep || avatar.Body.GetDescriptionOrNull() == value;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -203,19 +203,19 @@ internal class WikiWeaponViewModel : Abstraction.ViewModel
|
|||||||
{
|
{
|
||||||
string value = segment.ToString();
|
string value = segment.ToString();
|
||||||
|
|
||||||
if (ImmutableIntrinsics.WeaponTypes.Contains(value))
|
if (IntrinsicImmutables.WeaponTypes.Contains(value))
|
||||||
{
|
{
|
||||||
keep = keep || weapon.WeaponType.GetDescriptionOrNull() == value;
|
keep = keep || weapon.WeaponType.GetDescriptionOrNull() == value;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImmutableIntrinsics.ItemQualities.Contains(value))
|
if (IntrinsicImmutables.ItemQualities.Contains(value))
|
||||||
{
|
{
|
||||||
keep = keep || weapon.Quality.GetDescriptionOrNull() == value;
|
keep = keep || weapon.Quality.GetDescriptionOrNull() == value;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImmutableIntrinsics.FightProperties.Contains(value))
|
if (IntrinsicImmutables.FightProperties.Contains(value))
|
||||||
{
|
{
|
||||||
keep = keep || weapon.Property.Properties.ElementAtOrDefault(1).GetDescriptionOrNull() == value;
|
keep = keep || weapon.Property.Properties.ElementAtOrDefault(1).GetDescriptionOrNull() == value;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ public class MiHoYoJSInterface
|
|||||||
private readonly CoreWebView2 webView;
|
private readonly CoreWebView2 webView;
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
private readonly ILogger<MiHoYoJSInterface> logger;
|
private readonly ILogger<MiHoYoJSInterface> logger;
|
||||||
|
private readonly SemaphoreSlim webMessageSemaphore = new(1);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的调用桥
|
/// 构造一个新的调用桥
|
||||||
@@ -59,6 +60,8 @@ public class MiHoYoJSInterface
|
|||||||
webView.NavigationStarting += OnNavigationStarting;
|
webView.NavigationStarting += OnNavigationStarting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public event Action? ClosePageRequested;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取ActionTicket
|
/// 获取ActionTicket
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -160,7 +163,7 @@ public class MiHoYoJSInterface
|
|||||||
string q = param.Payload.GetQueryParam();
|
string q = param.Payload.GetQueryParam();
|
||||||
string check = Md5Convert.ToHexString($"salt={salt}&t={t}&r={r}&b={b}&q={q}").ToLowerInvariant();
|
string check = Md5Convert.ToHexString($"salt={salt}&t={t}&r={r}&b={b}&q={q}").ToLowerInvariant();
|
||||||
|
|
||||||
return new() { Data = new() { ["DS"] = $"{t},{r},{check}", }, };
|
return new() { Data = new() { ["DS"] = $"{t},{r},{check}" } };
|
||||||
|
|
||||||
static int GetRandom()
|
static int GetRandom()
|
||||||
{
|
{
|
||||||
@@ -214,9 +217,12 @@ public class MiHoYoJSInterface
|
|||||||
cookieToken = cookieTokenResponse.Data.CookieToken;
|
cookieToken = cookieTokenResponse.Data.CookieToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check null and create a new one to avoid System.NullReferenceException
|
||||||
|
user.CookieToken ??= new();
|
||||||
|
|
||||||
// sync ui and database
|
// sync ui and database
|
||||||
user.CookieToken![Cookie.COOKIE_TOKEN] = cookieToken!;
|
user.CookieToken[Cookie.COOKIE_TOKEN] = cookieToken!;
|
||||||
Ioc.Default.GetRequiredService<AppDbContext>().Users.UpdateAndSave(user.Entity);
|
serviceProvider.GetRequiredService<AppDbContext>().Users.UpdateAndSave(user.Entity);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -234,6 +240,7 @@ public class MiHoYoJSInterface
|
|||||||
[JsMethod("closePage")]
|
[JsMethod("closePage")]
|
||||||
public virtual IJsResult? ClosePage(JsParam param)
|
public virtual IJsResult? ClosePage(JsParam param)
|
||||||
{
|
{
|
||||||
|
ClosePageRequested?.Invoke();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +263,7 @@ public class MiHoYoJSInterface
|
|||||||
[JsMethod("getStatusBarHeight")]
|
[JsMethod("getStatusBarHeight")]
|
||||||
public virtual JsResult<Dictionary<string, object>> GetStatusBarHeight(JsParam param)
|
public virtual JsResult<Dictionary<string, object>> GetStatusBarHeight(JsParam param)
|
||||||
{
|
{
|
||||||
return new() { Data = new() { { "statusBarHeight", 0 } } };
|
return new() { Data = new() { ["statusBarHeight"] = 0 } };
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsMethod("pushPage")]
|
[JsMethod("pushPage")]
|
||||||
@@ -352,11 +359,14 @@ public class MiHoYoJSInterface
|
|||||||
JsParam param = JsonSerializer.Deserialize<JsParam>(message)!;
|
JsParam param = JsonSerializer.Deserialize<JsParam>(message)!;
|
||||||
|
|
||||||
logger.LogInformation("[OnMessage]\nMethod : {method}\nPayload : {payload}\nCallback: {callback}", param.Method, param.Payload, param.Callback);
|
logger.LogInformation("[OnMessage]\nMethod : {method}\nPayload : {payload}\nCallback: {callback}", param.Method, param.Payload, param.Callback);
|
||||||
IJsResult? result = await TryGetJsResultFromJsParamAsync(param).ConfigureAwait(false);
|
using (await webMessageSemaphore.EnterAsync().ConfigureAwait(false))
|
||||||
|
|
||||||
if (result != null && param.Callback != null)
|
|
||||||
{
|
{
|
||||||
await ExecuteCallbackScriptAsync(param.Callback, result.ToString()).ConfigureAwait(false);
|
IJsResult? result = await TryGetJsResultFromJsParamAsync(param).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (result != null && param.Callback != null)
|
||||||
|
{
|
||||||
|
await ExecuteCallbackScriptAsync(param.Callback, result.ToString()).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public partial class Cookie
|
|||||||
{
|
{
|
||||||
SortedDictionary<string, string> cookieMap = new();
|
SortedDictionary<string, string> cookieMap = new();
|
||||||
cookieString = cookieString.Replace(" ", string.Empty);
|
cookieString = cookieString.Replace(" ", string.Empty);
|
||||||
string[] values = cookieString.TrimEnd(';').Split(';');
|
string[] values = cookieString.Split(';', StringSplitOptions.RemoveEmptyEntries);
|
||||||
foreach (string[] parts in values.Select(c => c.Split('=', 2)))
|
foreach (string[] parts in values.Select(c => c.Split('=', 2)))
|
||||||
{
|
{
|
||||||
string name = parts[0].Trim();
|
string name = parts[0].Trim();
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ public class Game
|
|||||||
public Package Latest { get; set; } = default!;
|
public Package Latest { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 差异文件
|
/// 相对于当前版本的之前版本的差异文件(非预下载)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("diffs")]
|
[JsonPropertyName("diffs")]
|
||||||
public IList<DiffPackage> Diffs { get; set; } = default!;
|
public List<DiffPackage> Diffs { get; set; } = default!;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ public class GameResource
|
|||||||
public Plugin Plugin { get; set; } = default!;
|
public Plugin Plugin { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 官网地址
|
/// 官网地址 https://ys.mihoyo.com/launcher
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("web_url")]
|
[JsonPropertyName("web_url")]
|
||||||
public Uri WebUrl { get; set; } = default!;
|
public Uri WebUrl { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 强制更新文件
|
/// 强制更新文件 null
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("force_update")]
|
[JsonPropertyName("force_update")]
|
||||||
public object? ForceUpdate { get; set; }
|
public object? ForceUpdate { get; set; }
|
||||||
@@ -36,17 +36,23 @@ public class GameResource
|
|||||||
/// 预下载
|
/// 预下载
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("pre_download_game")]
|
[JsonPropertyName("pre_download_game")]
|
||||||
public object? PreDownloadGame { get; set; }
|
public Game? PreDownloadGame { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 过期更新包
|
/// 过期更新包
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("deprecated_packages")]
|
[JsonPropertyName("deprecated_packages")]
|
||||||
public List<LocalFile> DeprecatedPackages { get; set; } = default!;
|
public List<NameMd5> DeprecatedPackages { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 渠道服sdk
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("sdk")]
|
||||||
|
public object? Sdk { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 过期的单个文件
|
/// 过期的单个文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("deprecated_files")]
|
[JsonPropertyName("deprecated_files")]
|
||||||
public List<LocalFile>? DeprecatedFiles { get; set; }
|
public List<NameMd5>? DeprecatedFiles { get; set; }
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ namespace Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 资源文件
|
/// 资源文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class LocalFile
|
public class NameMd5
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 文件名称
|
/// 文件名称
|
||||||
@@ -6,7 +6,7 @@ namespace Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 最新客户端
|
/// 最新客户端
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Package : DownloadFile
|
public class Package : PathMd5
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 名称 空
|
/// 名称 空
|
||||||
@@ -27,14 +27,22 @@ public class Package : DownloadFile
|
|||||||
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
|
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
|
||||||
public long Size { get; set; } = default!;
|
public long Size { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 主程序相对路径 YuanShen.exe
|
||||||
|
/// </summary>
|
||||||
|
public string Entry { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 语音包
|
/// 语音包
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("voice_packs")]
|
[JsonPropertyName("voice_packs")]
|
||||||
public List<VoicePackage> VoicePacks { get; set; } = default!;
|
public List<VoicePackage> VoicePacks { get; set; } = default!;
|
||||||
|
|
||||||
|
// We don't want to support
|
||||||
|
// decompressed_path & segments
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 包大小
|
/// 包大小 bytes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("package_size")]
|
[JsonPropertyName("package_size")]
|
||||||
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
|
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 下载的文件
|
/// 下载的文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DownloadFile
|
public class PathMd5
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 下载地址
|
/// 下载地址
|
||||||
@@ -6,7 +6,7 @@ namespace Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 插件项
|
/// 插件项
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PluginItem : LocalFile
|
public class PluginItem : NameMd5
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 版本 一般为空
|
/// 版本 一般为空
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Snap.Hutao.Web.Hoyolab.SdkStatic.Hk4e.Launcher;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 语音包
|
/// 语音包
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class VoicePackage : DownloadFile
|
public class VoicePackage : PathMd5
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 语音
|
/// 语音
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ internal class HomaClient
|
|||||||
public async Task<Response<bool>> CheckRecordUploadedAsync(PlayerUid uid, CancellationToken token = default)
|
public async Task<Response<bool>> CheckRecordUploadedAsync(PlayerUid uid, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
Response<bool>? resp = await httpClient
|
Response<bool>? resp = await httpClient
|
||||||
.GetFromJsonAsync<Response<bool>>(HutaoEndpoints.RecordCheck(uid.Value), token)
|
.TryCatchGetFromJsonAsync<Response<bool>>(HutaoEndpoints.RecordCheck(uid.Value), options, logger, token)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
return Response.Response.DefaultIfNull(resp);
|
return Response.Response.DefaultIfNull(resp);
|
||||||
@@ -67,7 +67,7 @@ internal class HomaClient
|
|||||||
public async Task<Response<RankInfo>> GetRankAsync(PlayerUid uid, CancellationToken token = default)
|
public async Task<Response<RankInfo>> GetRankAsync(PlayerUid uid, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
Response<RankInfo>? resp = await httpClient
|
Response<RankInfo>? resp = await httpClient
|
||||||
.GetFromJsonAsync<Response<RankInfo>>(HutaoEndpoints.RecordRank(uid.Value), token)
|
.TryCatchGetFromJsonAsync<Response<RankInfo>>(HutaoEndpoints.RecordRank(uid.Value), options, logger, token)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
return Response.Response.DefaultIfNull(resp);
|
return Response.Response.DefaultIfNull(resp);
|
||||||
@@ -82,7 +82,7 @@ internal class HomaClient
|
|||||||
public async Task<Response<Overview>> GetOverviewAsync(CancellationToken token = default)
|
public async Task<Response<Overview>> GetOverviewAsync(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
Response<Overview>? resp = await httpClient
|
Response<Overview>? resp = await httpClient
|
||||||
.GetFromJsonAsync<Response<Overview>>(HutaoEndpoints.StatisticsOverview, token)
|
.TryCatchGetFromJsonAsync<Response<Overview>>(HutaoEndpoints.StatisticsOverview, options, logger, token)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
return Response.Response.DefaultIfNull(resp);
|
return Response.Response.DefaultIfNull(resp);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Windows.Graphics;
|
using Windows.Graphics;
|
||||||
|
using Windows.Win32.Foundation;
|
||||||
|
using static Windows.Win32.PInvoke;
|
||||||
|
|
||||||
namespace Snap.Hutao.Win32;
|
namespace Snap.Hutao.Win32;
|
||||||
|
|
||||||
@@ -40,4 +42,29 @@ internal static class StructExtension
|
|||||||
{
|
{
|
||||||
return sizeInt32.Width * sizeInt32.Height;
|
return sizeInt32.Width * sizeInt32.Height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 使用完成后自动关闭句柄
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handle">句柄</param>
|
||||||
|
/// <returns>用于关闭句柄的对象</returns>
|
||||||
|
public static IDisposable AutoClose(this HANDLE handle)
|
||||||
|
{
|
||||||
|
return new HandleCloser(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly struct HandleCloser : IDisposable
|
||||||
|
{
|
||||||
|
private readonly HANDLE handle;
|
||||||
|
|
||||||
|
public HandleCloser(HANDLE handle)
|
||||||
|
{
|
||||||
|
this.handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
CloseHandle(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user