mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b11526761e | ||
|
|
d293149672 | ||
|
|
3784df67a3 | ||
|
|
4aaca4d19f | ||
|
|
e6cf39831d | ||
|
|
24a2a18760 | ||
|
|
d8dce5c062 |
@@ -31,4 +31,5 @@
|
|||||||
<x:String x:Key="UI_EmotionIcon250">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon250.png</x:String>
|
<x:String x:Key="UI_EmotionIcon250">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon250.png</x:String>
|
||||||
<x:String x:Key="UI_EmotionIcon272">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon272.png</x:String>
|
<x:String x:Key="UI_EmotionIcon272">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon272.png</x:String>
|
||||||
<x:String x:Key="UI_EmotionIcon293">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon293.png</x:String>
|
<x:String x:Key="UI_EmotionIcon293">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon293.png</x:String>
|
||||||
|
<x:String x:Key="UI_EmotionIcon445">https://api.snapgenshin.com/static/raw/EmotionIcon/UI_EmotionIcon445.png</x:String>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.Threading;
|
|
||||||
|
|
||||||
internal sealed class Throttler
|
|
||||||
{
|
|
||||||
private readonly ConcurrentDictionary<string, SemaphoreSlim> methodSemaphoreMap = new();
|
|
||||||
|
|
||||||
public ValueTask<SemaphoreSlimToken> ThrottleAsync(CancellationToken token = default, [CallerMemberName] string callerName = default!, [CallerLineNumber] int callerLine = 0)
|
|
||||||
{
|
|
||||||
string key = $"{callerName}L{callerLine}";
|
|
||||||
SemaphoreSlim semaphore = methodSemaphoreMap.GetOrAdd(key, name => new SemaphoreSlim(1));
|
|
||||||
return semaphore.EnterAsync(token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<Identity
|
<Identity
|
||||||
Name="60568DGPStudio.SnapHutao"
|
Name="60568DGPStudio.SnapHutao"
|
||||||
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
||||||
Version="1.9.0.0" />
|
Version="1.9.1.0" />
|
||||||
|
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>Snap Hutao</DisplayName>
|
<DisplayName>Snap Hutao</DisplayName>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<Identity
|
<Identity
|
||||||
Name="60568DGPStudio.SnapHutaoDev"
|
Name="60568DGPStudio.SnapHutaoDev"
|
||||||
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
||||||
Version="1.9.0.0" />
|
Version="1.9.1.0" />
|
||||||
|
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>Snap Hutao Dev</DisplayName>
|
<DisplayName>Snap Hutao Dev</DisplayName>
|
||||||
|
|||||||
@@ -2144,6 +2144,9 @@
|
|||||||
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
<data name="ViewPageLaunchGameResourcePreDownloadHeader" xml:space="preserve">
|
||||||
<value>预下载</value>
|
<value>预下载</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ViewPageLaunchGameSelectGamePath" xml:space="preserve">
|
||||||
|
<value>选择游戏路径</value>
|
||||||
|
</data>
|
||||||
<data name="ViewPageLaunchGameSwitchAccountAttachUidNull" xml:space="preserve">
|
<data name="ViewPageLaunchGameSwitchAccountAttachUidNull" xml:space="preserve">
|
||||||
<value>该账号尚未绑定实时便笺通知 UID</value>
|
<value>该账号尚未绑定实时便笺通知 UID</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient<U
|
|||||||
|
|
||||||
List<DailyNoteEntry> entryList = await dailyNoteDbService.GetDailyNoteEntryIncludeUserListAsync().ConfigureAwait(false);
|
List<DailyNoteEntry> entryList = await dailyNoteDbService.GetDailyNoteEntryIncludeUserListAsync().ConfigureAwait(false);
|
||||||
entryList.ForEach(entry => { entry.UserGameRole = userService.GetUserGameRoleByUid(entry.Uid); });
|
entryList.ForEach(entry => { entry.UserGameRole = userService.GetUserGameRoleByUid(entry.Uid); });
|
||||||
entries = new(entryList);
|
entries = entryList.ToObservableCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries;
|
return entries;
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ internal sealed partial class UpdateService : IUpdateService
|
|||||||
|
|
||||||
progress.Report(new(versionInformation.Version.ToString(), 0, 0));
|
progress.Report(new(versionInformation.Version.ToString(), 0, 0));
|
||||||
|
|
||||||
if (versionInformation.Sha256 is not { } sha256)
|
if (versionInformation.Sha256 is not { Length: > 0 } sha256)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Snap.Hutao.Service.User;
|
|||||||
|
|
||||||
[ConstructorGenerated]
|
[ConstructorGenerated]
|
||||||
[Injection(InjectAs.Singleton, typeof(IUserCollectionService))]
|
[Injection(InjectAs.Singleton, typeof(IUserCollectionService))]
|
||||||
internal sealed partial class UserCollectionService : IUserCollectionService
|
internal sealed partial class UserCollectionService : IUserCollectionService, IDisposable
|
||||||
{
|
{
|
||||||
private readonly ScopedDbCurrent<BindingUser, Model.Entity.User, UserChangedMessage> dbCurrent;
|
private readonly ScopedDbCurrent<BindingUser, Model.Entity.User, UserChangedMessage> dbCurrent;
|
||||||
private readonly IUserInitializationService userInitializationService;
|
private readonly IUserInitializationService userInitializationService;
|
||||||
@@ -22,7 +22,7 @@ internal sealed partial class UserCollectionService : IUserCollectionService
|
|||||||
private readonly ITaskContext taskContext;
|
private readonly ITaskContext taskContext;
|
||||||
private readonly IMessenger messenger;
|
private readonly IMessenger messenger;
|
||||||
|
|
||||||
private readonly Throttler throttler = new();
|
private readonly SemaphoreSlim throttler = new(1);
|
||||||
|
|
||||||
private ObservableCollection<BindingUser>? userCollection;
|
private ObservableCollection<BindingUser>? userCollection;
|
||||||
private Dictionary<string, BindingUser>? midUserMap;
|
private Dictionary<string, BindingUser>? midUserMap;
|
||||||
@@ -38,7 +38,9 @@ internal sealed partial class UserCollectionService : IUserCollectionService
|
|||||||
|
|
||||||
public async ValueTask<ObservableCollection<BindingUser>> GetUserCollectionAsync()
|
public async ValueTask<ObservableCollection<BindingUser>> GetUserCollectionAsync()
|
||||||
{
|
{
|
||||||
using (await throttler.ThrottleAsync().ConfigureAwait(false))
|
// Force run in background thread, otherwise will cause reentrance
|
||||||
|
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
|
||||||
|
using (await throttler.EnterAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
if (userCollection is null)
|
if (userCollection is null)
|
||||||
{
|
{
|
||||||
@@ -131,17 +133,7 @@ internal sealed partial class UserCollectionService : IUserCollectionService
|
|||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
return uidUserGameRoleMap.GetValueOrDefault(uid);
|
||||||
{
|
|
||||||
return uidUserGameRoleMap[uid];
|
|
||||||
}
|
|
||||||
catch (InvalidOperationException)
|
|
||||||
{
|
|
||||||
// Sequence contains more than one matching element
|
|
||||||
// TODO: return a specialize UserGameRole to indicate error
|
|
||||||
}
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetUserByMid(string mid, [NotNullWhen(true)] out BindingUser? user)
|
public bool TryGetUserByMid(string mid, [NotNullWhen(true)] out BindingUser? user)
|
||||||
@@ -186,4 +178,9 @@ internal sealed partial class UserCollectionService : IUserCollectionService
|
|||||||
ArgumentNullException.ThrowIfNull(newUser.UserInfo);
|
ArgumentNullException.ThrowIfNull(newUser.UserInfo);
|
||||||
return new(UserOptionResult.Added, newUser.UserInfo.Uid);
|
return new(UserOptionResult.Added, newUser.UserInfo.Uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
throttler.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
|
||||||
xmlns:shccs="using:Snap.Hutao.Control.Collection.Selector"
|
xmlns:shccs="using:Snap.Hutao.Control.Collection.Selector"
|
||||||
xmlns:shch="using:Snap.Hutao.Control.Helper"
|
xmlns:shch="using:Snap.Hutao.Control.Helper"
|
||||||
|
xmlns:shci="using:Snap.Hutao.Control.Image"
|
||||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||||
xmlns:shvc="using:Snap.Hutao.View.Control"
|
xmlns:shvc="using:Snap.Hutao.View.Control"
|
||||||
xmlns:shvg="using:Snap.Hutao.ViewModel.Game"
|
xmlns:shvg="using:Snap.Hutao.ViewModel.Game"
|
||||||
@@ -346,10 +347,20 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid Visibility="{Binding GamePathSelectedAndValid, Converter={StaticResource BoolToVisibilityRevertConverter}}">
|
<Grid Visibility="{Binding GamePathSelectedAndValid, Converter={StaticResource BoolToVisibilityRevertConverter}}">
|
||||||
<StackPanel
|
<StackPanel
|
||||||
MaxWidth="600"
|
Margin="128,0"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Spacing="3">
|
Spacing="3">
|
||||||
|
<shci:CachedImage
|
||||||
|
Width="120"
|
||||||
|
Height="120"
|
||||||
|
EnableLazyLoading="False"
|
||||||
|
Source="{StaticResource UI_EmotionIcon445}"/>
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,5,0,21"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||||
|
Text="{shcm:ResourceString Name=ViewPageLaunchGameSelectGamePath}"/>
|
||||||
<Border Style="{ThemeResource BorderCardStyle}">
|
<Border Style="{ThemeResource BorderCardStyle}">
|
||||||
<ListView
|
<ListView
|
||||||
ItemTemplate="{StaticResource GamePathEntryListTemplate}"
|
ItemTemplate="{StaticResource GamePathEntryListTemplate}"
|
||||||
|
|||||||
@@ -136,17 +136,17 @@
|
|||||||
Background="{ThemeResource SystemFillColorSuccessBackgroundBrush}"
|
Background="{ThemeResource SystemFillColorSuccessBackgroundBrush}"
|
||||||
Description="{shcm:ResourceString Name=ViewPageSettingHutaoPassportLicensedDeveloperDescription}"
|
Description="{shcm:ResourceString Name=ViewPageSettingHutaoPassportLicensedDeveloperDescription}"
|
||||||
Header="{shcm:ResourceString Name=ViewPageSettingHutaoPassportLicensedDeveloperHeader}"
|
Header="{shcm:ResourceString Name=ViewPageSettingHutaoPassportLicensedDeveloperHeader}"
|
||||||
Visibility="{Binding UserOptions.IsLicensedDeveloper, Converter={StaticResource BoolToVisibilityConverter}}"/>
|
Visibility="{Binding UserOptions.IsLicensedDeveloper, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||||
<cwc:SettingsCard
|
|
||||||
Background="{ThemeResource SystemFillColorSuccessBackgroundBrush}"
|
|
||||||
Description="{shcm:ResourceString Name=ViewPageSettingHutaoPassportMaintainerDescription}"
|
|
||||||
Header="{shcm:ResourceString Name=ViewPageSettingHutaoPassportMaintainerHeader}"
|
|
||||||
Visibility="{Binding UserOptions.IsMaintainer, Converter={StaticResource BoolToVisibilityConverter}}">
|
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding OpenTestPageCommand}"
|
Command="{Binding OpenTestPageCommand}"
|
||||||
Content="TEST"
|
Content="TEST"
|
||||||
Style="{ThemeResource SettingButtonStyle}"/>
|
Style="{ThemeResource SettingButtonStyle}"/>
|
||||||
</cwc:SettingsCard>
|
</cwc:SettingsCard>
|
||||||
|
<cwc:SettingsCard
|
||||||
|
Background="{ThemeResource SystemFillColorSuccessBackgroundBrush}"
|
||||||
|
Description="{shcm:ResourceString Name=ViewPageSettingHutaoPassportMaintainerDescription}"
|
||||||
|
Header="{shcm:ResourceString Name=ViewPageSettingHutaoPassportMaintainerHeader}"
|
||||||
|
Visibility="{Binding UserOptions.IsMaintainer, Converter={StaticResource BoolToVisibilityConverter}}"/>
|
||||||
<cwc:SettingsCard Description="{Binding UserOptions.GachaLogExpireAtSlim}" Header="{shcm:ResourceString Name=ViewPageSettingHutaoPassportGachaLogExpiredAtHeader}"/>
|
<cwc:SettingsCard Description="{Binding UserOptions.GachaLogExpireAtSlim}" Header="{shcm:ResourceString Name=ViewPageSettingHutaoPassportGachaLogExpiredAtHeader}"/>
|
||||||
<cwc:SettingsCard
|
<cwc:SettingsCard
|
||||||
Command="{Binding Passport.OpenRedeemWebsiteCommand}"
|
Command="{Binding Passport.OpenRedeemWebsiteCommand}"
|
||||||
|
|||||||
Reference in New Issue
Block a user