mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Launch Game QoL
This commit is contained in:
30
src/Snap.Hutao/Snap.Hutao/Extension/MemoryCacheExtension.cs
Normal file
30
src/Snap.Hutao/Snap.Hutao/Extension/MemoryCacheExtension.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Extension;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 内存缓存拓展
|
||||||
|
/// </summary>
|
||||||
|
internal static class MemoryCacheExtension
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 尝试从 IMemoryCache 中移除并返回具有指定键的值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="memoryCache">缓存</param>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="value">值</param>
|
||||||
|
/// <returns>是否移除成功</returns>
|
||||||
|
public static bool TryRemove(this IMemoryCache memoryCache, string key, [NotNullWhen(true)] out object? value)
|
||||||
|
{
|
||||||
|
if (memoryCache.TryGetValue(key, out value))
|
||||||
|
{
|
||||||
|
memoryCache.Remove(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
// 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 Snap.Hutao.Core;
|
||||||
|
using Snap.Hutao.Model;
|
||||||
|
using Snap.Hutao.Model.Entity;
|
||||||
using Snap.Hutao.Service.Abstraction;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.DailyNote;
|
namespace Snap.Hutao.Service.DailyNote;
|
||||||
@@ -11,12 +14,72 @@ namespace Snap.Hutao.Service.DailyNote;
|
|||||||
[Injection(InjectAs.Singleton)]
|
[Injection(InjectAs.Singleton)]
|
||||||
internal sealed class DailyNoteOptions : DbStoreOptions
|
internal sealed class DailyNoteOptions : DbStoreOptions
|
||||||
{
|
{
|
||||||
|
private readonly IServiceProvider serviceProvider;
|
||||||
|
private readonly List<NameValue<int>> refreshTimes = new()
|
||||||
|
{
|
||||||
|
new(SH.ViewModelDailyNoteRefreshTime4, 240),
|
||||||
|
new(SH.ViewModelDailyNoteRefreshTime8, 480),
|
||||||
|
new(SH.ViewModelDailyNoteRefreshTime30, 1800),
|
||||||
|
new(SH.ViewModelDailyNoteRefreshTime40, 2400),
|
||||||
|
new(SH.ViewModelDailyNoteRefreshTime60, 3600),
|
||||||
|
};
|
||||||
|
|
||||||
|
private NameValue<int>? selectedRefreshTime;
|
||||||
|
private bool? isReminderNotification;
|
||||||
|
private bool? isSilentWhenPlayingGame;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的实时便笺选项
|
/// 构造一个新的实时便笺选项
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="serviceScopeFactory">服务范围工厂</param>
|
/// <param name="serviceProvider">服务提供器</param>
|
||||||
public DailyNoteOptions(IServiceScopeFactory serviceScopeFactory)
|
public DailyNoteOptions(IServiceProvider serviceProvider)
|
||||||
: base(serviceScopeFactory)
|
: base(serviceProvider.GetRequiredService<IServiceScopeFactory>())
|
||||||
{
|
{
|
||||||
|
this.serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 刷新时间
|
||||||
|
/// </summary>
|
||||||
|
public List<NameValue<int>> RefreshTimes { get => refreshTimes; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 选中的刷新时间
|
||||||
|
/// </summary>
|
||||||
|
public NameValue<int>? SelectedRefreshTime
|
||||||
|
{
|
||||||
|
get => GetOption(ref selectedRefreshTime, SettingEntry.DailyNoteReminderNotify, time => RefreshTimes.Single(t => t.Value == int.Parse(time)), RefreshTimes[1]);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
if (ScheduleTaskHelper.RegisterForDailyNoteRefresh(value.Value))
|
||||||
|
{
|
||||||
|
SetOption(ref selectedRefreshTime, SettingEntry.DailyNoteReminderNotify, value, value => value.Value.ToString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelDailyNoteRegisterTaskFail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 提醒式通知
|
||||||
|
/// </summary>
|
||||||
|
public bool IsReminderNotification
|
||||||
|
{
|
||||||
|
get => GetOption(ref isReminderNotification, SettingEntry.DailyNoteReminderNotify);
|
||||||
|
set => SetOption(ref isReminderNotification, SettingEntry.DailyNoteReminderNotify, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否开启免打扰模式
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSilentWhenPlayingGame
|
||||||
|
{
|
||||||
|
get => GetOption(ref isSilentWhenPlayingGame, SettingEntry.DailyNoteSilentWhenPlayingGame);
|
||||||
|
set => SetOption(ref isSilentWhenPlayingGame, SettingEntry.DailyNoteSilentWhenPlayingGame, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -368,6 +368,28 @@ internal sealed class GameService : IGameService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public GameAccount? DetectCurrentGameAccount()
|
||||||
|
{
|
||||||
|
Must.NotNull(gameAccounts!);
|
||||||
|
|
||||||
|
string? registrySdk = RegistryInterop.Get();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(registrySdk))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return gameAccounts.SingleOrDefault(a => a.MihoyoSDK == registrySdk);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
ThrowHelper.UserdataCorrupted(SH.ServiceGameDetectGameAccountMultiMatched, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool SetGameAccount(GameAccount account)
|
public bool SetGameAccount(GameAccount account)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -92,4 +92,10 @@ internal interface IGameService
|
|||||||
/// <param name="scheme">方案</param>
|
/// <param name="scheme">方案</param>
|
||||||
/// <returns>是否更改了ini文件</returns>
|
/// <returns>是否更改了ini文件</returns>
|
||||||
bool SetMultiChannel(LaunchScheme scheme);
|
bool SetMultiChannel(LaunchScheme scheme);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检测账号
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>账号</returns>
|
||||||
|
GameAccount? DetectCurrentGameAccount();
|
||||||
}
|
}
|
||||||
@@ -148,7 +148,7 @@ internal sealed class LaunchOptions : DbStoreOptions
|
|||||||
public NameValue<int> Monitor
|
public NameValue<int> Monitor
|
||||||
{
|
{
|
||||||
get => GetOption(ref monitor, SettingEntry.LaunchMonitor, index => Monitors[int.Parse(index) - 1], Monitors[0]);
|
get => GetOption(ref monitor, SettingEntry.LaunchMonitor, index => Monitors[int.Parse(index) - 1], Monitors[0]);
|
||||||
set => SetOption(ref monitor, SettingEntry.LaunchMonitor, value, selected => selected.Value.ToString());
|
set => SetOption(ref monitor, SettingEntry.LaunchMonitor, value, selected => selected?.Value.ToString() ?? "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -85,7 +85,7 @@
|
|||||||
<AppBarButton.Flyout>
|
<AppBarButton.Flyout>
|
||||||
<Flyout Placement="BottomEdgeAlignedRight">
|
<Flyout Placement="BottomEdgeAlignedRight">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<RadioButtons ItemsSource="{Binding RefreshTimes}" SelectedItem="{Binding SelectedRefreshTime, Mode=TwoWay}">
|
<RadioButtons ItemsSource="{Binding Options.RefreshTimes}" SelectedItem="{Binding Options.SelectedRefreshTime, Mode=TwoWay}">
|
||||||
<RadioButtons.Header>
|
<RadioButtons.Header>
|
||||||
<TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageDailyNoteRefreshTime}"/>
|
<TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageDailyNoteRefreshTime}"/>
|
||||||
</RadioButtons.Header>
|
</RadioButtons.Header>
|
||||||
@@ -106,13 +106,13 @@
|
|||||||
Description="{shcm:ResourceString Name=ViewPageDailyNoteSlientModeDescription}"
|
Description="{shcm:ResourceString Name=ViewPageDailyNoteSlientModeDescription}"
|
||||||
Header="{shcm:ResourceString Name=ViewPageDailyNoteSlientModeHeader}"
|
Header="{shcm:ResourceString Name=ViewPageDailyNoteSlientModeHeader}"
|
||||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
HeaderIcon="{shcm:FontIcon Glyph=}">
|
||||||
<ToggleSwitch Margin="24,0,0,0" IsOn="{Binding IsSilentWhenPlayingGame, Mode=TwoWay}"/>
|
<ToggleSwitch Margin="24,0,0,0" IsOn="{Binding Options.IsSilentWhenPlayingGame, Mode=TwoWay}"/>
|
||||||
</clw:SettingsCard>
|
</clw:SettingsCard>
|
||||||
<clw:SettingsCard
|
<clw:SettingsCard
|
||||||
Description="{shcm:ResourceString Name=ViewPageDailyNoteReminderDescription}"
|
Description="{shcm:ResourceString Name=ViewPageDailyNoteReminderDescription}"
|
||||||
Header="{shcm:ResourceString Name=ViewPageDailyNoteReminderHeader}"
|
Header="{shcm:ResourceString Name=ViewPageDailyNoteReminderHeader}"
|
||||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
HeaderIcon="{shcm:FontIcon Glyph=}">
|
||||||
<ToggleSwitch Margin="24,0,0,0" IsOn="{Binding IsReminderNotification, Mode=TwoWay}"/>
|
<ToggleSwitch Margin="24,0,0,0" IsOn="{Binding Options.IsReminderNotification, Mode=TwoWay}"/>
|
||||||
</clw:SettingsCard>
|
</clw:SettingsCard>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|||||||
@@ -28,24 +28,8 @@ internal sealed class DailyNoteViewModel : Abstraction.ViewModel
|
|||||||
private readonly IDailyNoteService dailyNoteService;
|
private readonly IDailyNoteService dailyNoteService;
|
||||||
private readonly AppDbContext appDbContext;
|
private readonly AppDbContext appDbContext;
|
||||||
|
|
||||||
private readonly List<NameValue<int>> refreshTimes = new()
|
|
||||||
{
|
|
||||||
new(SH.ViewModelDailyNoteRefreshTime4, 240),
|
|
||||||
new(SH.ViewModelDailyNoteRefreshTime8, 480),
|
|
||||||
new(SH.ViewModelDailyNoteRefreshTime30, 1800),
|
|
||||||
new(SH.ViewModelDailyNoteRefreshTime40, 2400),
|
|
||||||
new(SH.ViewModelDailyNoteRefreshTime60, 3600),
|
|
||||||
};
|
|
||||||
|
|
||||||
private bool isReminderNotification;
|
|
||||||
private NameValue<int>? selectedRefreshTime;
|
|
||||||
private ObservableCollection<UserAndUid>? userAndUids;
|
private ObservableCollection<UserAndUid>? userAndUids;
|
||||||
|
|
||||||
private SettingEntry? refreshSecondsEntry;
|
|
||||||
private SettingEntry? reminderNotifyEntry;
|
|
||||||
private SettingEntry? silentModeEntry;
|
|
||||||
private ObservableCollection<DailyNoteEntry>? dailyNoteEntries;
|
private ObservableCollection<DailyNoteEntry>? dailyNoteEntries;
|
||||||
private bool isSilentWhenPlayingGame;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的实时便笺视图模型
|
/// 构造一个新的实时便笺视图模型
|
||||||
@@ -56,6 +40,7 @@ internal sealed class DailyNoteViewModel : Abstraction.ViewModel
|
|||||||
userService = serviceProvider.GetRequiredService<IUserService>();
|
userService = serviceProvider.GetRequiredService<IUserService>();
|
||||||
dailyNoteService = serviceProvider.GetRequiredService<IDailyNoteService>();
|
dailyNoteService = serviceProvider.GetRequiredService<IDailyNoteService>();
|
||||||
appDbContext = serviceProvider.GetRequiredService<AppDbContext>();
|
appDbContext = serviceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
Options = serviceProvider.GetRequiredService<DailyNoteOptions>();
|
||||||
this.serviceProvider = serviceProvider;
|
this.serviceProvider = serviceProvider;
|
||||||
|
|
||||||
TrackRoleCommand = new AsyncRelayCommand<UserAndUid>(TrackRoleAsync);
|
TrackRoleCommand = new AsyncRelayCommand<UserAndUid>(TrackRoleAsync);
|
||||||
@@ -66,67 +51,9 @@ internal sealed class DailyNoteViewModel : Abstraction.ViewModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 刷新时间
|
/// 选项
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<NameValue<int>> RefreshTimes { get => refreshTimes; }
|
public DailyNoteOptions Options { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 选中的刷新时间
|
|
||||||
/// </summary>
|
|
||||||
public NameValue<int>? SelectedRefreshTime
|
|
||||||
{
|
|
||||||
get => selectedRefreshTime;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref selectedRefreshTime, value))
|
|
||||||
{
|
|
||||||
if (value != null)
|
|
||||||
{
|
|
||||||
if (!ScheduleTaskHelper.RegisterForDailyNoteRefresh(value.Value))
|
|
||||||
{
|
|
||||||
serviceProvider.GetRequiredService<IInfoBarService>().Warning(SH.ViewModelDailyNoteRegisterTaskFail);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
refreshSecondsEntry!.SetInt32(value.Value);
|
|
||||||
appDbContext.Settings.UpdateAndSave(refreshSecondsEntry!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 提醒式通知
|
|
||||||
/// </summary>
|
|
||||||
public bool IsReminderNotification
|
|
||||||
{
|
|
||||||
get => isReminderNotification;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref isReminderNotification, value))
|
|
||||||
{
|
|
||||||
reminderNotifyEntry!.SetBoolean(value);
|
|
||||||
appDbContext.Settings.UpdateAndSave(reminderNotifyEntry!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否开启免打扰模式
|
|
||||||
/// </summary>
|
|
||||||
public bool IsSilentWhenPlayingGame
|
|
||||||
{
|
|
||||||
get => isSilentWhenPlayingGame;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref isSilentWhenPlayingGame, value))
|
|
||||||
{
|
|
||||||
silentModeEntry!.SetBoolean(value);
|
|
||||||
appDbContext.Settings.UpdateAndSave(silentModeEntry!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户与角色集合
|
/// 用户与角色集合
|
||||||
@@ -168,43 +95,19 @@ internal sealed class DailyNoteViewModel : Abstraction.ViewModel
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
UserAndUids = await userService.GetRoleCollectionAsync().ConfigureAwait(true);
|
await ThreadHelper.SwitchToBackgroundAsync();
|
||||||
|
ObservableCollection<UserAndUid> roles = await userService.GetRoleCollectionAsync().ConfigureAwait(false);
|
||||||
|
ObservableCollection<DailyNoteEntry> entries = await dailyNoteService.GetDailyNoteEntriesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
await ThreadHelper.SwitchToMainThreadAsync();
|
||||||
|
UserAndUids = roles;
|
||||||
|
DailyNoteEntries = entries;
|
||||||
}
|
}
|
||||||
catch (Core.ExceptionService.UserdataCorruptedException ex)
|
catch (Core.ExceptionService.UserdataCorruptedException ex)
|
||||||
{
|
{
|
||||||
serviceProvider.GetRequiredService<IInfoBarService>().Error(ex);
|
serviceProvider.GetRequiredService<IInfoBarService>().Error(ex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
await ThreadHelper.SwitchToMainThreadAsync();
|
|
||||||
|
|
||||||
refreshSecondsEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteRefreshSeconds, "480");
|
|
||||||
int refreshSeconds = refreshSecondsEntry.GetInt32();
|
|
||||||
selectedRefreshTime = refreshTimes.Single(t => t.Value == refreshSeconds);
|
|
||||||
OnPropertyChanged(nameof(SelectedRefreshTime));
|
|
||||||
ScheduleTaskHelper.RegisterForDailyNoteRefresh(refreshSeconds);
|
|
||||||
|
|
||||||
reminderNotifyEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteReminderNotify, Core.StringLiterals.False);
|
|
||||||
isReminderNotification = reminderNotifyEntry.GetBoolean();
|
|
||||||
OnPropertyChanged(nameof(IsReminderNotification));
|
|
||||||
|
|
||||||
silentModeEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteSilentWhenPlayingGame, Core.StringLiterals.False);
|
|
||||||
isSilentWhenPlayingGame = silentModeEntry.GetBoolean();
|
|
||||||
OnPropertyChanged(nameof(IsSilentWhenPlayingGame));
|
|
||||||
}
|
|
||||||
|
|
||||||
await ThreadHelper.SwitchToBackgroundAsync();
|
|
||||||
ObservableCollection<DailyNoteEntry> entries = await dailyNoteService.GetDailyNoteEntriesAsync().ConfigureAwait(false);
|
|
||||||
await ThreadHelper.SwitchToMainThreadAsync();
|
|
||||||
DailyNoteEntries = entries;
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task TrackRoleAsync(UserAndUid? role)
|
private async Task TrackRoleAsync(UserAndUid? role)
|
||||||
|
|||||||
@@ -171,11 +171,13 @@ internal sealed class LaunchGameViewModel : Abstraction.ViewModel
|
|||||||
GameAccounts = accounts;
|
GameAccounts = accounts;
|
||||||
|
|
||||||
// Sync uid
|
// Sync uid
|
||||||
if (memoryCache.TryGetValue(DesiredUid, out object? value) && value is string uid)
|
if (memoryCache.TryRemove(DesiredUid, out object? value) && value is string uid)
|
||||||
{
|
{
|
||||||
SelectedGameAccount = GameAccounts.FirstOrDefault(g => g.AttachUid == uid);
|
SelectedGameAccount = GameAccounts.FirstOrDefault(g => g.AttachUid == uid);
|
||||||
memoryCache.Remove(DesiredUid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try set to the current account.
|
||||||
|
SelectedGameAccount ??= gameService.DetectCurrentGameAccount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ internal readonly struct PlayerUid
|
|||||||
// CN
|
// CN
|
||||||
>= '1' and <= '4' => "cn_gf01", // 国服
|
>= '1' and <= '4' => "cn_gf01", // 国服
|
||||||
'5' => "cn_qd01", // 渠道
|
'5' => "cn_qd01", // 渠道
|
||||||
|
|
||||||
// OS
|
// OS
|
||||||
'6' => "os_usa", // 美服
|
'6' => "os_usa", // 美服
|
||||||
'7' => "os_euro", // 欧服
|
'7' => "os_euro", // 欧服
|
||||||
|
|||||||
Reference in New Issue
Block a user