Launch Game QoL

This commit is contained in:
DismissedLight
2023-04-04 18:44:19 +08:00
parent 79118cdb4d
commit 179b78ca83
9 changed files with 143 additions and 116 deletions

View 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;
}
}

View File

@@ -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);
} }
} }

View File

@@ -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)
{ {

View File

@@ -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();
} }

View File

@@ -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>

View File

@@ -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=&#xE7ED;}"> HeaderIcon="{shcm:FontIcon Glyph=&#xE7ED;}">
<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=&#xEA8F;}"> HeaderIcon="{shcm:FontIcon Glyph=&#xEA8F;}">
<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>

View File

@@ -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)

View File

@@ -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)

View File

@@ -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", // 欧服