improve db layer & homecard experience

This commit is contained in:
DismissedLight
2023-09-10 17:38:18 +08:00
parent f2c38bc72a
commit 396da901a1
57 changed files with 860 additions and 465 deletions

View File

@@ -1,55 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI.Behaviors;
using Microsoft.UI.Xaml.Controls;
namespace Snap.Hutao.Control.Behavior;
/// <summary>
/// AppTitleBar Workaround
/// https://github.com/microsoft/microsoft-ui-xaml/issues/7756
/// </summary>
internal sealed class ComboBoxExtendsContentIntoTitleBarWorkaroundBehavior : BehaviorBase<ComboBox>
{
private readonly IMessenger messenger;
private readonly EventHandler<object> dropDownOpenedHandler;
private readonly EventHandler<object> dropDownClosedHandler;
/// <summary>
/// AppTitleBar Workaround
/// </summary>
public ComboBoxExtendsContentIntoTitleBarWorkaroundBehavior()
{
messenger = Ioc.Default.GetRequiredService<IMessenger>();
dropDownOpenedHandler = OnDropDownOpened;
dropDownClosedHandler = OnDropDownClosed;
}
/// <inheritdoc/>
protected override bool Initialize()
{
AssociatedObject.DropDownOpened += dropDownOpenedHandler;
AssociatedObject.DropDownClosed += dropDownClosedHandler;
return true;
}
/// <inheritdoc/>
protected override bool Uninitialize()
{
AssociatedObject.DropDownOpened -= dropDownOpenedHandler;
AssociatedObject.DropDownClosed -= dropDownClosedHandler;
return true;
}
private void OnDropDownOpened(object? sender, object e)
{
messenger.Send(Message.FlyoutStateChangedMessage.Open);
}
private void OnDropDownClosed(object? sender, object e)
{
messenger.Send(Message.FlyoutStateChangedMessage.Close);
}
}

View File

@@ -1,22 +1,23 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Notifications;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.UI.Xaml;
using Microsoft.Windows.AppLifecycle;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Service.DailyNote;
using Snap.Hutao.Service.Hutao;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.ViewModel.Guide;
using System.Diagnostics;
namespace Snap.Hutao.Core.LifeCycle;
[Injection(InjectAs.Singleton, typeof(ICurrentWindowReference))]
internal sealed class CurrentWindowReference : ICurrentWindowReference
{
public Window Window { get; set; } = default!;
private readonly WeakReference<Window> reference = new(default!);
[SuppressMessage("", "SH007")]
public Window Window
{
get
{
reference.TryGetTarget(out Window? window);
return window!;
}
set => reference.SetTarget(value);
}
}

View File

@@ -1,55 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
namespace Snap.Hutao.Core.Setting;
/// <summary>
/// 功能
/// </summary>
internal sealed class Feature : ObservableObject
{
private readonly string displayName;
private readonly string description;
private readonly string settingKey;
private readonly bool defaultValue;
/// <summary>
/// 构造一个新的功能
/// </summary>
/// <param name="displayName">显示名称</param>
/// <param name="description">描述</param>
/// <param name="settingKey">键</param>
/// <param name="defaultValue">默认值</param>
public Feature(string displayName, string description, string settingKey, bool defaultValue)
{
this.displayName = displayName;
this.description = description;
this.settingKey = settingKey;
this.defaultValue = defaultValue;
}
/// <summary>
/// 显示名称
/// </summary>
public string DisplayName { get => displayName; }
/// <summary>
/// 描述
/// </summary>
public string Description { get => description; }
/// <summary>
/// 值
/// </summary>
public bool Value
{
get => LocalSetting.Get(settingKey, defaultValue);
set
{
LocalSetting.Set(settingKey, value);
OnPropertyChanged();
}
}
}

View File

@@ -1,41 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Collections;
namespace Snap.Hutao.Core.Setting;
/// <summary>
/// 功能选项
/// </summary>
internal sealed class FeatureOptions : IReadOnlyCollection<Feature>
{
/// <summary>
/// 启用实时便笺无感验证
/// </summary>
public Feature IsDailyNoteSilentVerificationEnabled { get; } = new(
"IsDailyNoteSilentVerificationEnabled", "启用实时便笺无感验证", "IsDailyNoteSilentVerificationEnabled", true);
/// <summary>
/// 元数据检查是否忽略
/// </summary>
public Feature IsMetadataUpdateCheckSuppressed { get; } = new(
"IsMetadataUpdateCheckSuppressed", "禁用元数据更新检查", "IsMetadataUpdateCheckSuppressed", false);
/// <inheritdoc/>
public int Count { get => 2; }
/// <inheritdoc/>
public IEnumerator<Feature> GetEnumerator()
{
// TODO: Use source generator
yield return IsDailyNoteSilentVerificationEnabled;
yield return IsMetadataUpdateCheckSuppressed;
}
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

View File

@@ -76,4 +76,9 @@ internal static class SettingKeys
public const string CultivationWeapon90LevelTarget = "CultivationWeapon90LevelTarget";
public const string CultivationWeapon70LevelCurrent = "CultivationWeapon70LevelCurrent";
public const string CultivationWeapon70LevelTarget = "CultivationWeapon70LevelTarget";
public const string IsHomeCardLaunchGamePresented = "IsHomeCardLaunchGamePresented";
public const string IsHomeCardGachaStatisticsPresented = "IsHomeCardGachaStatisticsPresented";
public const string IsHomeCardAchievementPresented = "IsHomeCardAchievementPresented";
public const string IsHomeCardDailyNotePresented = "IsHomeCardDailyNotePresented";
}

View File

@@ -17,6 +17,10 @@ internal sealed partial class ShellLinkInterop : IShellLinkInterop
public void CreateDesktopShoutcutForElevatedLaunch()
{
string sourceLogoPath = Path.Combine(runtimeOptions.InstalledLocation, "Assets/Logo.ico");
string targetLogoPath = Path.Combine(runtimeOptions.DataFolder, "ShellLinkLogo.ico");
File.Copy(sourceLogoPath, targetLogoPath);
IShellLinkW shellLink = (IShellLinkW)new ShellLink();
shellLink.SetPath("powershell");
shellLink.SetArguments($"""
@@ -24,8 +28,7 @@ internal sealed partial class ShellLinkInterop : IShellLinkInterop
""");
shellLink.SetShowCmd(SHOW_WINDOW_CMD.SW_SHOWMINNOACTIVE);
string iconPath = Path.Combine(runtimeOptions.InstalledLocation, "Snap.Hutao.exe");
shellLink.SetIconLocation(iconPath, 0);
shellLink.SetIconLocation(targetLogoPath, 0);
string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string target = Path.Combine(desktop, $"{SH.AppNameAndVersion.Format(runtimeOptions.Version)}.lnk");

View File

@@ -22,9 +22,6 @@ internal sealed class CalculableWeapon
IMappingFrom<CalculableWeapon, Weapon>,
IMappingFrom<CalculableWeapon, WeaponView>
{
private uint levelCurrent;
private uint levelTarget;
/// <summary>
/// 构造一个新的可计算武器
/// </summary>

View File

@@ -13,32 +13,8 @@ internal sealed class UIIFItem
/// 物品Id
/// </summary>
[JsonPropertyName("itemId")]
public int ItemId { get; set; }
public uint ItemId { get; set; }
/// <summary>
/// 物品Id
/// </summary>
[JsonPropertyName("count")]
public int Count { get; set; }
/// <summary>
/// 等级
/// Reliquary/Weapon
/// </summary>
[JsonPropertyName("level")]
public int? Level { get; set; }
/// <summary>
/// 副属性列表
/// Reliquary
/// </summary>
[JsonPropertyName("appendPropIdList")]
public List<int>? AppendPropIdList { get; set; } = default!;
/// <summary>
/// 精炼等级 0-4
/// Weapon
/// </summary>
[JsonPropertyName("promoteLevel")]
public int? PromoteLevel { get; set; }
[JsonPropertyName("material")]
public UIIFMaterial Material { get; set; } = default!;
}

View File

@@ -0,0 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.InterChange.Inventory;
internal sealed class UIIFMaterial
{
public uint Count { get; set; }
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

View File

@@ -5245,7 +5245,7 @@ namespace Snap.Hutao.Resource.Localization {
}
/// <summary>
/// 查找类似 在桌面上创建默认以管理员方式启动的快捷方式,更新后需要重新创建 的本地化字符串。
/// 查找类似 在桌面上创建默认以管理员方式启动的快捷方式 的本地化字符串。
/// </summary>
internal static string ViewPageSettingCreateDesktopShortcutDescription {
get {
@@ -5469,6 +5469,87 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 管理主页仪表板中的卡片 的本地化字符串。
/// </summary>
internal static string ViewpageSettingHomeCardDescription {
get {
return ResourceManager.GetString("ViewpageSettingHomeCardDescription", resourceCulture);
}
}
/// <summary>
/// 查找类似 主页卡片 的本地化字符串。
/// </summary>
internal static string ViewpageSettingHomeCardHeader {
get {
return ResourceManager.GetString("ViewpageSettingHomeCardHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 成就管理 的本地化字符串。
/// </summary>
internal static string ViewpageSettingHomeCardItemAchievementHeader {
get {
return ResourceManager.GetString("ViewpageSettingHomeCardItemAchievementHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 实时便笺 的本地化字符串。
/// </summary>
internal static string ViewpageSettingHomeCardItemDailyNoteHeader {
get {
return ResourceManager.GetString("ViewpageSettingHomeCardItemDailyNoteHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 祈愿记录 的本地化字符串。
/// </summary>
internal static string ViewpageSettingHomeCardItemgachaStatisticsHeader {
get {
return ResourceManager.GetString("ViewpageSettingHomeCardItemgachaStatisticsHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 启动游戏 的本地化字符串。
/// </summary>
internal static string ViewpageSettingHomeCardItemLaunchGameHeader {
get {
return ResourceManager.GetString("ViewpageSettingHomeCardItemLaunchGameHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 隐藏 的本地化字符串。
/// </summary>
internal static string ViewPageSettingHomeCardOff {
get {
return ResourceManager.GetString("ViewPageSettingHomeCardOff", resourceCulture);
}
}
/// <summary>
/// 查找类似 显示 的本地化字符串。
/// </summary>
internal static string ViewPageSettingHomeCardOn {
get {
return ResourceManager.GetString("ViewPageSettingHomeCardOn", resourceCulture);
}
}
/// <summary>
/// 查找类似 主页 的本地化字符串。
/// </summary>
internal static string ViewpageSettingHomeHeader {
get {
return ResourceManager.GetString("ViewpageSettingHomeHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 胡桃账号 的本地化字符串。
/// </summary>
@@ -5577,6 +5658,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 胡桃使用 PowerShell 更改注册表中的信息以修改游戏内账号 的本地化字符串。
/// </summary>
internal static string ViewPageSettingSetPowerShellDescription {
get {
return ResourceManager.GetString("ViewPageSettingSetPowerShellDescription", resourceCulture);
}
}
/// <summary>
/// 查找类似 PowerShell 路径 的本地化字符串。
/// </summary>
@@ -5586,6 +5676,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 Shell 体验 的本地化字符串。
/// </summary>
internal static string ViewPageSettingShellExperienceHeader {
get {
return ResourceManager.GetString("ViewPageSettingShellExperienceHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 赞助我们 的本地化字符串。
/// </summary>

View File

@@ -1902,7 +1902,7 @@
<value>创建</value>
</data>
<data name="ViewPageSettingCreateDesktopShortcutDescription" xml:space="preserve">
<value>在桌面上创建默认以管理员方式启动的快捷方式,更新后需要重新创建</value>
<value>在桌面上创建默认以管理员方式启动的快捷方式</value>
</data>
<data name="ViewPageSettingCreateDesktopShortcutHeader" xml:space="preserve">
<value>创建快捷方式</value>
@@ -1976,6 +1976,33 @@
<data name="ViewPageSettingGeetestVerificationHeader" xml:space="preserve">
<value>无感验证</value>
</data>
<data name="ViewpageSettingHomeCardDescription" xml:space="preserve">
<value>管理主页仪表板中的卡片</value>
</data>
<data name="ViewpageSettingHomeCardHeader" xml:space="preserve">
<value>主页卡片</value>
</data>
<data name="ViewpageSettingHomeCardItemAchievementHeader" xml:space="preserve">
<value>成就管理</value>
</data>
<data name="ViewpageSettingHomeCardItemDailyNoteHeader" xml:space="preserve">
<value>实时便笺</value>
</data>
<data name="ViewpageSettingHomeCardItemgachaStatisticsHeader" xml:space="preserve">
<value>祈愿记录</value>
</data>
<data name="ViewpageSettingHomeCardItemLaunchGameHeader" xml:space="preserve">
<value>启动游戏</value>
</data>
<data name="ViewPageSettingHomeCardOff" xml:space="preserve">
<value>隐藏</value>
</data>
<data name="ViewPageSettingHomeCardOn" xml:space="preserve">
<value>显示</value>
</data>
<data name="ViewpageSettingHomeHeader" xml:space="preserve">
<value>主页</value>
</data>
<data name="ViewPageSettingHutaoPassportHeader" xml:space="preserve">
<value>胡桃账号</value>
</data>
@@ -2012,9 +2039,15 @@
<data name="ViewPageSettingSetGamePathHint" xml:space="preserve">
<value>设置游戏路径时请选择游戏本体YuanShen.exe 或 GenshinImpact.exe而不是启动器launcher.exe</value>
</data>
<data name="ViewPageSettingSetPowerShellDescription" xml:space="preserve">
<value>胡桃使用 PowerShell 更改注册表中的信息以修改游戏内账号</value>
</data>
<data name="ViewPageSettingSetPowerShellPathHeader" xml:space="preserve">
<value>PowerShell 路径</value>
</data>
<data name="ViewPageSettingShellExperienceHeader" xml:space="preserve">
<value>Shell 体验</value>
</data>
<data name="ViewPageSettingSponsorNavigate" xml:space="preserve">
<value>赞助我们</value>
</data>

View File

@@ -89,6 +89,21 @@ internal sealed partial class AchievementDbService : IAchievementDbService
}
}
public async ValueTask OverwriteAchievementAsync(EntityAchievement achievement)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// Delete exists one.
await appDbContext.Achievements.ExecuteDeleteWhereAsync(e => e.InnerId == achievement.InnerId).ConfigureAwait(false);
if (achievement.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
{
await appDbContext.Achievements.AddAndSaveAsync(achievement).ConfigureAwait(false);
}
}
}
public ObservableCollection<AchievementArchive> GetAchievementArchiveCollection()
{
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -98,7 +113,7 @@ internal sealed partial class AchievementDbService : IAchievementDbService
}
}
public async ValueTask DeleteAchievementArchiveAsync(AchievementArchive archive)
public async ValueTask RemoveAchievementArchiveAsync(AchievementArchive archive)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
@@ -121,6 +136,19 @@ internal sealed partial class AchievementDbService : IAchievementDbService
}
}
public async ValueTask<List<EntityAchievement>> GetAchievementListByArchiveIdAsync(Guid archiveId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return await appDbContext.Achievements
.AsNoTracking()
.Where(i => i.ArchiveId == archiveId)
.ToListAsync()
.ConfigureAwait(false);
}
}
public List<AchievementArchive> GetAchievementArchiveList()
{
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -129,4 +157,13 @@ internal sealed partial class AchievementDbService : IAchievementDbService
return appDbContext.AchievementArchives.AsNoTracking().ToList();
}
}
public async ValueTask<List<AchievementArchive>> GetAchievementArchiveListAsync()
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return await appDbContext.AchievementArchives.AsNoTracking().ToListAsync().ConfigureAwait(false);
}
}
}

View File

@@ -74,6 +74,6 @@ internal sealed partial class AchievementService
// Sync database
await taskContext.SwitchToBackgroundAsync();
await achievementDbService.DeleteAchievementArchiveAsync(archive).ConfigureAwait(false);
await achievementDbService.RemoveAchievementArchiveAsync(archive).ConfigureAwait(false);
}
}

View File

@@ -50,9 +50,10 @@ internal sealed partial class AchievementService
public async ValueTask<UIAF> ExportToUIAFAsync(AchievementArchive archive)
{
await taskContext.SwitchToBackgroundAsync();
List<UIAFItem> list = achievementDbService
.GetAchievementListByArchiveId(archive.InnerId)
.SelectList(UIAFItem.From);
List<EntityAchievement> entities = await achievementDbService
.GetAchievementListByArchiveIdAsync(archive.InnerId)
.ConfigureAwait(false);
List<UIAFItem> list = entities.SelectList(UIAFItem.From);
return new()
{

View File

@@ -22,7 +22,7 @@ internal sealed partial class AchievementStatisticsService : IAchievementStatist
await taskContext.SwitchToBackgroundAsync();
List<AchievementStatistics> results = new();
foreach (AchievementArchive archive in achievementDbService.GetAchievementArchiveList())
foreach (AchievementArchive archive in await achievementDbService.GetAchievementArchiveListAsync().ConfigureAwait(false))
{
int finishedCount = await achievementDbService
.GetFinishedAchievementCountByArchiveIdAsync(archive.InnerId)

View File

@@ -9,14 +9,18 @@ namespace Snap.Hutao.Service.Achievement;
internal interface IAchievementDbService
{
ValueTask DeleteAchievementArchiveAsync(Model.Entity.AchievementArchive archive);
ValueTask RemoveAchievementArchiveAsync(Model.Entity.AchievementArchive archive);
ObservableCollection<Model.Entity.AchievementArchive> GetAchievementArchiveCollection();
List<Model.Entity.AchievementArchive> GetAchievementArchiveList();
ValueTask<List<Model.Entity.AchievementArchive>> GetAchievementArchiveListAsync();
List<EntityAchievement> GetAchievementListByArchiveId(Guid archiveId);
ValueTask<List<EntityAchievement>> GetAchievementListByArchiveIdAsync(Guid archiveId);
Dictionary<AchievementId, EntityAchievement> GetAchievementMapByArchiveId(Guid archiveId);
ValueTask<int> GetFinishedAchievementCountByArchiveIdAsync(Guid archiveId);
@@ -24,4 +28,6 @@ internal interface IAchievementDbService
ValueTask<List<EntityAchievement>> GetLatestFinishedAchievementListByArchiveIdAsync(Guid archiveId, int take);
void OverwriteAchievement(EntityAchievement achievement);
ValueTask OverwriteAchievementAsync(EntityAchievement achievement);
}

View File

@@ -77,7 +77,7 @@ internal sealed partial class AvatarInfoDbBulkOperation
{
token.ThrowIfCancellationRequested();
string uid = userAndUid.Uid.Value;
List<EntityAvatarInfo> dbInfos = avatarInfoDbService.GetAvatarInfoListByUid(uid);
List<EntityAvatarInfo> dbInfos = await avatarInfoDbService.GetAvatarInfoListByUidAsync(uid).ConfigureAwait(false);
EnsureItemsAvatarIdDistinct(ref dbInfos, uid);
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -121,7 +121,7 @@ internal sealed partial class AvatarInfoDbBulkOperation
}
}
return avatarInfoDbService.GetAvatarInfoListByUid(uid);
return await avatarInfoDbService.GetAvatarInfoListByUidAsync(uid).ConfigureAwait(false);
}
/// <summary>
@@ -134,7 +134,7 @@ internal sealed partial class AvatarInfoDbBulkOperation
{
token.ThrowIfCancellationRequested();
string uid = userAndUid.Uid.Value;
List<EntityAvatarInfo> dbInfos = avatarInfoDbService.GetAvatarInfoListByUid(uid);
List<EntityAvatarInfo> dbInfos = await avatarInfoDbService.GetAvatarInfoListByUidAsync(uid).ConfigureAwait(false);
EnsureItemsAvatarIdDistinct(ref dbInfos, uid);
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -173,7 +173,7 @@ internal sealed partial class AvatarInfoDbBulkOperation
}
}
return avatarInfoDbService.GetAvatarInfoListByUid(uid);
return await avatarInfoDbService.GetAvatarInfoListByUidAsync(uid).ConfigureAwait(false);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -243,7 +243,7 @@ internal sealed partial class AvatarInfoDbBulkOperation
// This means that there are duplicate items.
if (distinctCount < dbInfos.Count)
{
avatarInfoDbService.DeleteAvatarInfoRangeByUid(uid);
avatarInfoDbService.RemoveAvatarInfoRangeByUid(uid);
dbInfos = new();
}
}

View File

@@ -24,7 +24,20 @@ internal sealed partial class AvatarInfoDbService : IAvatarInfoDbService
}
}
public void DeleteAvatarInfoRangeByUid(string uid)
public async ValueTask<List<EntityAvatarInfo>> GetAvatarInfoListByUidAsync(string uid)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return await appDbContext.AvatarInfos
.AsNoTracking()
.Where(i => i.Uid == uid)
.ToListAsync()
.ConfigureAwait(false);
}
}
public void RemoveAvatarInfoRangeByUid(string uid)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
@@ -32,4 +45,13 @@ internal sealed partial class AvatarInfoDbService : IAvatarInfoDbService
appDbContext.AvatarInfos.ExecuteDeleteWhere(i => i.Uid == uid);
}
}
public async ValueTask RemoveAvatarInfoRangeByUidAsync(string uid)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await appDbContext.AvatarInfos.ExecuteDeleteWhereAsync(i => i.Uid == uid).ConfigureAwait(false);
}
}
}

View File

@@ -78,7 +78,7 @@ internal sealed partial class AvatarInfoService : IAvatarInfoService
default:
{
List<EntityAvatarInfo> list = avatarInfoDbService.GetAvatarInfoListByUid(userAndUid.Uid.Value);
List<EntityAvatarInfo> list = await avatarInfoDbService.GetAvatarInfoListByUidAsync(userAndUid.Uid.Value).ConfigureAwait(false);
Summary summary = await GetSummaryCoreAsync(list, token).ConfigureAwait(false);
token.ThrowIfCancellationRequested();
return new(RefreshResult.Ok, summary.Avatars.Count == 0 ? null : summary);
@@ -97,7 +97,7 @@ internal sealed partial class AvatarInfoService : IAvatarInfoService
?? await enkaClient.GetDataAsync(uid, token).ConfigureAwait(false);
}
private async ValueTask<Summary> GetSummaryCoreAsync(IEnumerable<Model.Entity.AvatarInfo> avatarInfos, CancellationToken token)
private async ValueTask<Summary> GetSummaryCoreAsync(IEnumerable<EntityAvatarInfo> avatarInfos, CancellationToken token)
{
using (ValueStopwatch.MeasureExecution(logger))
{

View File

@@ -7,7 +7,11 @@ namespace Snap.Hutao.Service.AvatarInfo;
internal interface IAvatarInfoDbService
{
void DeleteAvatarInfoRangeByUid(string uid);
void RemoveAvatarInfoRangeByUid(string uid);
List<EntityAvatarInfo> GetAvatarInfoListByUid(string uid);
ValueTask<List<EntityAvatarInfo>> GetAvatarInfoListByUidAsync(string uid);
ValueTask RemoveAvatarInfoRangeByUidAsync(string uid);
}

View File

@@ -66,7 +66,7 @@ internal sealed partial class CultivationDbService : ICultivationDbService
}
}
public async ValueTask DeleteCultivateEntryByIdAsync(Guid entryId)
public async ValueTask RemoveCultivateEntryByIdAsync(Guid entryId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
@@ -77,15 +77,6 @@ internal sealed partial class CultivationDbService : ICultivationDbService
}
}
public void UpdateInventoryItem(InventoryItem item)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
appDbContext.InventoryItems.UpdateAndSave(item);
}
}
public void UpdateCultivateItem(CultivateItem item)
{
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -95,6 +86,15 @@ internal sealed partial class CultivationDbService : ICultivationDbService
}
}
public async ValueTask UpdateCultivateItemAsync(CultivateItem item)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await appDbContext.CultivateItems.UpdateAndSaveAsync(item).ConfigureAwait(false);
}
}
public async ValueTask<CultivateEntry?> GetCultivateEntryByProjectIdAndItemIdAsync(Guid projectId, uint itemId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -115,7 +115,7 @@ internal sealed partial class CultivationDbService : ICultivationDbService
}
}
public async ValueTask DeleteCultivateItemRangeByEntryIdAsync(Guid entryId)
public async ValueTask RemoveCultivateItemRangeByEntryIdAsync(Guid entryId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
@@ -144,7 +144,7 @@ internal sealed partial class CultivationDbService : ICultivationDbService
}
}
public async ValueTask DeleteCultivateProjectByIdAsync(Guid projectId)
public async ValueTask RemoveCultivateProjectByIdAsync(Guid projectId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{

View File

@@ -75,6 +75,6 @@ internal sealed partial class CultivationService
// Sync database
await taskContext.SwitchToBackgroundAsync();
await cultivationDbService.DeleteCultivateProjectByIdAsync(project.InnerId).ConfigureAwait(false);
await cultivationDbService.RemoveCultivateProjectByIdAsync(project.InnerId).ConfigureAwait(false);
}
}

View File

@@ -8,6 +8,7 @@ using Snap.Hutao.Model.Entity.Database;
using Snap.Hutao.Model.Entity.Primitive;
using Snap.Hutao.Model.Metadata.Item;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.Inventroy;
using Snap.Hutao.ViewModel.Cultivation;
using System.Collections.ObjectModel;
@@ -23,6 +24,7 @@ internal sealed partial class CultivationService : ICultivationService
{
private readonly ScopedDbCurrent<CultivateProject, Message.CultivateProjectChangedMessage> dbCurrent;
private readonly ICultivationDbService cultivationDbService;
private readonly IInventoryDbService inventoryDbService;
private readonly IServiceProvider serviceProvider;
private readonly ITaskContext taskContext;
@@ -140,13 +142,13 @@ internal sealed partial class CultivationService : ICultivationService
public async ValueTask RemoveCultivateEntryAsync(Guid entryId)
{
await taskContext.SwitchToBackgroundAsync();
await cultivationDbService.DeleteCultivateEntryByIdAsync(entryId).ConfigureAwait(false);
await cultivationDbService.RemoveCultivateEntryByIdAsync(entryId).ConfigureAwait(false);
}
/// <inheritdoc/>
public void SaveInventoryItem(InventoryItemView item)
{
cultivationDbService.UpdateInventoryItem(item.Entity);
inventoryDbService.UpdateInventoryItem(item.Entity);
}
/// <inheritdoc/>
@@ -181,7 +183,7 @@ internal sealed partial class CultivationService : ICultivationService
}
Guid entryId = entry.InnerId;
await cultivationDbService.DeleteCultivateItemRangeByEntryIdAsync(entryId).ConfigureAwait(false);
await cultivationDbService.RemoveCultivateItemRangeByEntryIdAsync(entryId).ConfigureAwait(false);
IEnumerable<CultivateItem> toAdd = items.Select(item => CultivateItem.From(entryId, item));
await cultivationDbService.AddCultivateItemRangeAsync(toAdd).ConfigureAwait(false);

View File

@@ -10,11 +10,11 @@ internal interface ICultivationDbService
{
ValueTask AddCultivateProjectAsync(CultivateProject project);
ValueTask DeleteCultivateEntryByIdAsync(Guid entryId);
ValueTask RemoveCultivateEntryByIdAsync(Guid entryId);
ValueTask DeleteCultivateItemRangeByEntryIdAsync(Guid entryId);
ValueTask RemoveCultivateItemRangeByEntryIdAsync(Guid entryId);
ValueTask DeleteCultivateProjectByIdAsync(Guid projectId);
ValueTask RemoveCultivateProjectByIdAsync(Guid projectId);
ValueTask<CultivateEntry?> GetCultivateEntryByProjectIdAndItemIdAsync(Guid projectId, uint itemId);
@@ -34,5 +34,5 @@ internal interface ICultivationDbService
void UpdateCultivateItem(CultivateItem item);
void UpdateInventoryItem(InventoryItem item);
ValueTask UpdateCultivateItemAsync(CultivateItem item);
}

View File

@@ -23,6 +23,15 @@ internal sealed partial class DailyNoteDbService : IDailyNoteDbService
}
}
public async ValueTask<bool> ContainsUidAsync(string uid)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return await appDbContext.DailyNotes.AsNoTracking().AnyAsync(n => n.Uid == uid).ConfigureAwait(false);
}
}
public async ValueTask AddDailyNoteEntryAsync(DailyNoteEntry entry)
{
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -50,15 +59,6 @@ internal sealed partial class DailyNoteDbService : IDailyNoteDbService
}
}
public List<DailyNoteEntry> GetDailyNoteEntryList()
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return appDbContext.DailyNotes.AsNoTracking().ToList();
}
}
public List<DailyNoteEntry> GetDailyNoteEntryIncludeUserList()
{
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -67,4 +67,13 @@ internal sealed partial class DailyNoteDbService : IDailyNoteDbService
return appDbContext.DailyNotes.AsNoTracking().Include(n => n.User).ToList();
}
}
public async ValueTask<List<DailyNoteEntry>> GetDailyNoteEntryIncludeUserListAsync()
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return await appDbContext.DailyNotes.AsNoTracking().Include(n => n.User).ToListAsync().ConfigureAwait(false);
}
}
}

View File

@@ -42,7 +42,7 @@ internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient<U
{
string roleUid = userAndUid.Uid.Value;
if (!dailyNoteDbService.ContainsUid(roleUid))
if (!await dailyNoteDbService.ContainsUidAsync(roleUid).ConfigureAwait(false))
{
DailyNoteEntry newEntry = DailyNoteEntry.From(userAndUid);
@@ -75,7 +75,7 @@ internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient<U
await userService.GetRoleCollectionAsync().ConfigureAwait(false);
await RefreshDailyNotesCoreAsync(forceRefresh).ConfigureAwait(false);
List<DailyNoteEntry> entryList = dailyNoteDbService.GetDailyNoteEntryIncludeUserList();
List<DailyNoteEntry> entryList = await dailyNoteDbService.GetDailyNoteEntryIncludeUserListAsync().ConfigureAwait(false);
entryList.ForEach(entry => { entry.UserGameRole = userService.GetUserGameRoleByUid(entry.Uid); });
entries = new(entryList);
}
@@ -108,7 +108,7 @@ internal sealed partial class DailyNoteService : IDailyNoteService, IRecipient<U
private async ValueTask RefreshDailyNotesCoreAsync(bool forceRefresh)
{
foreach (DailyNoteEntry entry in dailyNoteDbService.GetDailyNoteEntryIncludeUserList())
foreach (DailyNoteEntry entry in await dailyNoteDbService.GetDailyNoteEntryIncludeUserListAsync().ConfigureAwait(false))
{
if (!forceRefresh && entry.DailyNote is not null)
{

View File

@@ -11,11 +11,13 @@ internal interface IDailyNoteDbService
bool ContainsUid(string uid);
ValueTask<bool> ContainsUidAsync(string uid);
ValueTask DeleteDailyNoteEntryByIdAsync(Guid entryId);
List<DailyNoteEntry> GetDailyNoteEntryIncludeUserList();
List<DailyNoteEntry> GetDailyNoteEntryList();
ValueTask<List<DailyNoteEntry>> GetDailyNoteEntryIncludeUserListAsync();
ValueTask UpdateDailyNoteEntryAsync(DailyNoteEntry entry);
}

View File

@@ -49,10 +49,10 @@ internal readonly struct GachaItemSaveContext
// 全量刷新
if (!IsLazy)
{
GachaLogDbService.DeleteNewerGachaItemsByArchiveIdQueryTypeAndEndId(archive.InnerId, QueryType, EndId);
GachaLogDbService.RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(archive.InnerId, QueryType, EndId);
}
GachaLogDbService.AddGachaItems(ItemsToAdd);
GachaLogDbService.AddGachaItemRange(ItemsToAdd);
}
}
}

View File

@@ -48,7 +48,21 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
}
}
public async ValueTask DeleteGachaArchiveByIdAsync(Guid archiveId)
public async ValueTask<List<GachaItem>> GetGachaItemListByArchiveIdAsync(Guid archiveId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return await appDbContext.GachaItems
.AsNoTracking()
.Where(i => i.ArchiveId == archiveId)
.OrderBy(i => i.Id)
.ToListAsync()
.ConfigureAwait(false);
}
}
public async ValueTask RemoveGachaArchiveByIdAsync(Guid archiveId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
@@ -118,6 +132,36 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
return item?.Id ?? 0L;
}
public async ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType)
{
GachaItem? item = null;
try
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// TODO: replace with MaxBy
// https://github.com/dotnet/efcore/issues/25566
// .MaxBy(i => i.Id);
item = await appDbContext.GachaItems
.AsNoTracking()
.Where(i => i.ArchiveId == archiveId)
.Where(i => i.QueryType == queryType)
.OrderByDescending(i => i.Id)
.FirstOrDefaultAsync()
.ConfigureAwait(false);
}
}
catch (SqliteException ex)
{
ThrowHelper.UserdataCorrupted(SH.ServiceGachaLogEndIdUserdataCorruptedMessage, ex);
}
return item?.Id ?? 0L;
}
public long GetOldestGachaItemIdByArchiveId(Guid archiveId)
{
GachaItem? item = null;
@@ -139,6 +183,28 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
return item?.Id ?? long.MaxValue;
}
public async ValueTask<long> GetOldestGachaItemIdByArchiveIdAsync(Guid archiveId)
{
GachaItem? item = null;
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// TODO: replace with MaxBy
// https://github.com/dotnet/efcore/issues/25566
// .MaxBy(i => i.Id);
item = await appDbContext.GachaItems
.AsNoTracking()
.Where(i => i.ArchiveId == archiveId)
.OrderBy(i => i.Id)
.FirstOrDefaultAsync()
.ConfigureAwait(false);
}
return item?.Id ?? long.MaxValue;
}
public long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType)
{
GachaItem? item = null;
@@ -182,15 +248,6 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
return item?.Id ?? long.MaxValue;
}
public async ValueTask AddGachaArchiveAsync(GachaArchive archive)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await appDbContext.GachaArchives.AddAndSaveAsync(archive).ConfigureAwait(false);
}
}
public void AddGachaArchive(GachaArchive archive)
{
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -200,6 +257,15 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
}
}
public async ValueTask AddGachaArchiveAsync(GachaArchive archive)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await appDbContext.GachaArchives.AddAndSaveAsync(archive).ConfigureAwait(false);
}
}
public List<Web.Hutao.GachaLog.GachaItem> GetHutaoGachaItemList(Guid archiveId, GachaConfigType queryType, long endId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -225,6 +291,32 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
}
}
public async ValueTask<List<Web.Hutao.GachaLog.GachaItem>> GetHutaoGachaItemListAsync(Guid archiveId, GachaConfigType queryType, long endId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return await appDbContext.GachaItems
.AsNoTracking()
.Where(i => i.ArchiveId == archiveId)
.Where(i => i.QueryType == queryType)
.OrderByDescending(i => i.Id)
.Where(i => i.Id > endId)
// Keep this to make SQL generates correctly
.Select(i => new Web.Hutao.GachaLog.GachaItem()
{
GachaType = i.GachaType,
QueryType = i.QueryType,
ItemId = i.ItemId,
Time = i.Time,
Id = i.Id,
})
.ToListAsync()
.ConfigureAwait(false);
}
}
public async ValueTask<GachaArchive?> GetGachaArchiveByIdAsync(Guid archiveId, CancellationToken token)
{
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -258,7 +350,7 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
}
}
public void AddGachaItems(List<GachaItem> items)
public void AddGachaItemRange(List<GachaItem> items)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
@@ -267,7 +359,16 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
}
}
public void DeleteNewerGachaItemsByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaConfigType queryType, long endId)
public async ValueTask AddGachaItemRangeAsync(List<GachaItem> items)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await appDbContext.GachaItems.AddRangeAndSaveAsync(items).ConfigureAwait(false);
}
}
public void RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaConfigType queryType, long endId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
@@ -279,4 +380,18 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
.ExecuteDelete();
}
}
public async ValueTask RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndIdAsync(Guid archiveId, GachaConfigType queryType, long endId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await appDbContext.GachaItems
.AsNoTracking()
.Where(i => i.ArchiveId == archiveId && i.QueryType == queryType)
.Where(i => i.Id >= endId)
.ExecuteDeleteAsync()
.ConfigureAwait(false);
}
}
}

View File

@@ -41,7 +41,9 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
List<Web.Hutao.GachaLog.GachaItem> items = new();
foreach ((GachaConfigType type, long endId) in endIds)
{
List<Web.Hutao.GachaLog.GachaItem> part = gachaLogDbService.GetHutaoGachaItemList(gachaArchive.InnerId, type, endId);
List<Web.Hutao.GachaLog.GachaItem> part = await gachaLogDbService
.GetHutaoGachaItemListAsync(gachaArchive.InnerId, type, endId)
.ConfigureAwait(false);
items.AddRange(part);
}

View File

@@ -88,7 +88,7 @@ internal sealed partial class GachaLogService : IGachaLogService
// Return statistics
using (ValueStopwatch.MeasureExecution(logger))
{
List<GachaItem> items = gachaLogDbService.GetGachaItemListByArchiveId(archive.InnerId);
List<GachaItem> items = await gachaLogDbService.GetGachaItemListByArchiveIdAsync(archive.InnerId).ConfigureAwait(false);
return await gachaStatisticsFactory.CreateAsync(items, context).ConfigureAwait(false);
}
}
@@ -102,7 +102,7 @@ internal sealed partial class GachaLogService : IGachaLogService
List<GachaStatisticsSlim> statistics = new();
foreach (GachaArchive archive in ArchiveCollection)
{
List<GachaItem> items = gachaLogDbService.GetGachaItemListByArchiveId(archive.InnerId);
List<GachaItem> items = await gachaLogDbService.GetGachaItemListByArchiveIdAsync(archive.InnerId).ConfigureAwait(false);
GachaStatisticsSlim slim = await gachaStatisticsSlimFactory.CreateAsync(context, items, archive.Uid).ConfigureAwait(false);
statistics.Add(slim);
}
@@ -150,7 +150,7 @@ internal sealed partial class GachaLogService : IGachaLogService
// Sync database
await taskContext.SwitchToBackgroundAsync();
await gachaLogDbService.DeleteGachaArchiveByIdAsync(archive.InnerId).ConfigureAwait(false);
await gachaLogDbService.RemoveGachaArchiveByIdAsync(archive.InnerId).ConfigureAwait(false);
// Sync cache
await taskContext.SwitchToMainThreadAsync();

View File

@@ -13,13 +13,13 @@ internal interface IGachaLogDbService
ValueTask AddGachaArchiveAsync(GachaArchive archive);
void AddGachaItems(List<GachaItem> items);
void AddGachaItemRange(List<GachaItem> items);
ValueTask AddGachaItemsAsync(List<GachaItem> items);
ValueTask DeleteGachaArchiveByIdAsync(Guid archiveId);
ValueTask RemoveGachaArchiveByIdAsync(Guid archiveId);
void DeleteNewerGachaItemsByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaConfigType queryType, long endId);
void RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaConfigType queryType, long endId);
ValueTask<GachaArchive?> GetGachaArchiveByIdAsync(Guid archiveId, CancellationToken token);
@@ -29,6 +29,8 @@ internal interface IGachaLogDbService
List<GachaItem> GetGachaItemListByArchiveId(Guid archiveId);
ValueTask<List<GachaItem>> GetGachaItemListByArchiveIdAsync(Guid archiveId);
List<Web.Hutao.GachaLog.GachaItem> GetHutaoGachaItemList(Guid archiveId, GachaConfigType queryType, long endId);
long GetNewestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType);
@@ -40,4 +42,14 @@ internal interface IGachaLogDbService
long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType);
ValueTask<long> GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token);
ValueTask<long> GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType);
ValueTask<long> GetOldestGachaItemIdByArchiveIdAsync(Guid archiveId);
ValueTask<List<Web.Hutao.GachaLog.GachaItem>> GetHutaoGachaItemListAsync(Guid archiveId, GachaConfigType queryType, long endId);
ValueTask AddGachaItemRangeAsync(List<GachaItem> items);
ValueTask RemoveNewerGachaItemRangeByArchiveIdQueryTypeAndEndIdAsync(Guid archiveId, GachaConfigType queryType, long endId);
}

View File

@@ -24,10 +24,10 @@ internal sealed partial class UIGFExportService : IUIGFExportService
public async ValueTask<UIGF> ExportAsync(GachaLogServiceMetadataContext context, GachaArchive archive)
{
await taskContext.SwitchToBackgroundAsync();
List<UIGFItem> list = gachaLogDbService
.GetGachaItemListByArchiveId(archive.InnerId)
.SelectList(i => UIGFItem.From(i, context.GetNameQualityByItemId(i.ItemId)));
List<GachaItem> entities = await gachaLogDbService
.GetGachaItemListByArchiveIdAsync(archive.InnerId)
.ConfigureAwait(false);
List<UIGFItem> list = entities.SelectList(i => UIGFItem.From(i, context.GetNameQualityByItemId(i.ItemId)));
UIGF uigf = new()
{

View File

@@ -33,15 +33,6 @@ internal sealed partial class GameDbService : IGameDbService
}
}
public async ValueTask UpdateGameAccountAsync(GameAccount gameAccount)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await appDbContext.GameAccounts.UpdateAndSaveAsync(gameAccount).ConfigureAwait(false);
}
}
public void UpdateGameAccount(GameAccount gameAccount)
{
using (IServiceScope scope = serviceProvider.CreateScope())
@@ -51,7 +42,16 @@ internal sealed partial class GameDbService : IGameDbService
}
}
public async ValueTask DeleteGameAccountByIdAsync(Guid id)
public async ValueTask UpdateGameAccountAsync(GameAccount gameAccount)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await appDbContext.GameAccounts.UpdateAndSaveAsync(gameAccount).ConfigureAwait(false);
}
}
public async ValueTask RemoveGameAccountByIdAsync(Guid id)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{

View File

@@ -386,16 +386,6 @@ internal sealed partial class GameService : IGameService
gameAccounts.Remove(gameAccount);
await taskContext.SwitchToBackgroundAsync();
await gameDbService.DeleteGameAccountByIdAsync(gameAccount.InnerId).ConfigureAwait(false);
}
private static bool LaunchSchemeMatchesExecutable(LaunchScheme launchScheme, string gameFileName)
{
return (launchScheme.IsOversea, gameFileName) switch
{
(true, GenshinImpactFileName) => true,
(false, YuanShenFileName) => true,
_ => false,
};
await gameDbService.RemoveGameAccountByIdAsync(gameAccount.InnerId).ConfigureAwait(false);
}
}

View File

@@ -10,7 +10,7 @@ internal interface IGameDbService
{
ValueTask AddGameAccountAsync(GameAccount gameAccount);
ValueTask DeleteGameAccountByIdAsync(Guid id);
ValueTask RemoveGameAccountByIdAsync(Guid id);
ObservableCollection<GameAccount> GetGameAccountCollection();

View File

@@ -0,0 +1,17 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Entity;
namespace Snap.Hutao.Service.Inventroy;
internal interface IInventoryDbService
{
ValueTask AddInventoryItemRangeByProjectId(List<InventoryItem> items);
ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId);
void UpdateInventoryItem(InventoryItem item);
ValueTask UpdateInventoryItemAsync(InventoryItem item);
}

View File

@@ -0,0 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Service.Inventroy;
internal interface IInventoryService
{
}

View File

@@ -0,0 +1,61 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.EntityFrameworkCore;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Database;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Snap.Hutao.Service.Inventroy;
[ConstructorGenerated]
[Injection(InjectAs.Singleton, typeof(IInventoryDbService))]
internal sealed partial class InventoryDbService : IInventoryDbService
{
private readonly IServiceProvider serviceProvider;
public async ValueTask RemoveInventoryItemRangeByProjectId(Guid projectId)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await appDbContext.InventoryItems
.AsNoTracking()
.Where(a => a.ProjectId == projectId && a.ItemId != 202U) // 摩拉
.ExecuteDeleteAsync()
.ConfigureAwait(false);
}
}
public async ValueTask AddInventoryItemRangeByProjectId(List<InventoryItem> items)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await appDbContext.InventoryItems.AddRangeAndSaveAsync(items).ConfigureAwait(false);
}
}
public void UpdateInventoryItem(InventoryItem item)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
appDbContext.InventoryItems.UpdateAndSave(item);
}
}
public async ValueTask UpdateInventoryItemAsync(InventoryItem item)
{
using (IServiceScope scope = serviceProvider.CreateScope())
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await appDbContext.InventoryItems.UpdateAndSaveAsync(item).ConfigureAwait(false);
}
}
}

View File

@@ -0,0 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Service.Inventroy;
[Injection(InjectAs.Transient)]
internal sealed class InventoryService : IInventoryService
{
}

View File

@@ -48,4 +48,4 @@ internal sealed partial class SpiralAbyssRecordDbService : ISpiralAbyssRecordDbS
await appDbContext.SpiralAbysses.AddAndSaveAsync(entry).ConfigureAwait(false);
}
}
}
}

View File

@@ -9,7 +9,7 @@ internal interface IUserDbService
ValueTask DeleteUserByIdAsync(Guid id);
ValueTask DeleteUsersAsync();
ValueTask RemoveUsersAsync();
ValueTask<List<Model.Entity.User>> GetUserListAsync();

View File

@@ -49,7 +49,7 @@ internal sealed partial class UserDbService : IUserDbService
}
}
public async ValueTask DeleteUsersAsync()
public async ValueTask RemoveUsersAsync()
{
using (IServiceScope scope = serviceProvider.CreateScope())
{

View File

@@ -61,7 +61,7 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe
public async ValueTask UnsafeRemoveUsersAsync()
{
await taskContext.SwitchToBackgroundAsync();
await userDbService.DeleteUsersAsync().ConfigureAwait(false);
await userDbService.RemoveUsersAsync().ConfigureAwait(false);
}
/// <inheritdoc/>

View File

@@ -79,6 +79,7 @@
<None Remove="LaunchGameWindow.xaml" />
<None Remove="NativeMethods.json" />
<None Remove="NativeMethods.txt" />
<None Remove="Resource\BlurBackground.png" />
<None Remove="Resource\Font\CascadiaMono.ttf" />
<None Remove="Resource\Font\MiSans-Regular.ttf" />
<None Remove="Resource\HutaoIconSourceTransparentBackgroundGradient1.png" />
@@ -206,6 +207,7 @@
<!-- Resources Files -->
<ItemGroup>
<Content Include="Resource\BlurBackground.png" />
<Content Include="Resource\Font\CascadiaMono.ttf" />
<Content Include="Resource\Font\MiSans-Regular.ttf" />
<Content Include="Resource\HutaoIconSourceTransparentBackgroundGradient1.png" />
@@ -239,12 +241,12 @@
<PackageReference Include="CommunityToolkit.Labs.WinUI.TokenView" Version="0.1.230809" />
<PackageReference Include="CommunityToolkit.Labs.WinUI.TransitionHelper" Version="0.1.230809" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2-build.1" />
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.0.230828-rc" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.HeaderedControls" Version="8.0.230828-rc" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.0.230828-rc" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.0.230828-rc" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.0.230828-rc" />
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.0.230828-rc" />
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.0.230907" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.HeaderedControls" Version="8.0.230907" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.0.230907" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.0.230907" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.0.230907" />
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.0.230907" />
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls.Core" Version="7.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />

View File

@@ -0,0 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
namespace Snap.Hutao.View.Card;
internal sealed class CardReference
{
public Button? Card { get; set; }
}

View File

@@ -124,7 +124,7 @@
<StackPanel
Grid.Row="1"
Margin="0,8,0,0"
Visibility="{Binding Weapon, Converter={StaticResource EmptyObjectToVisibilityConverter}}">
Visibility="{x:Bind Weapon, Converter={StaticResource EmptyObjectToVisibilityConverter}}">
<Border Style="{StaticResource BorderCardStyle}">
<Grid Margin="8" DataContext="{x:Bind Weapon}">
<Grid.ColumnDefinitions>

View File

@@ -76,11 +76,7 @@
Margin="2,6,3,6"
DisplayMemberPath="Name"
ItemsSource="{Binding Archives, Mode=OneWay}"
SelectedItem="{Binding SelectedArchive, Mode=TwoWay}">
<mxi:Interaction.Behaviors>
<shcb:ComboBoxExtendsContentIntoTitleBarWorkaroundBehavior/>
</mxi:Interaction.Behaviors>
</ComboBox>
SelectedItem="{Binding SelectedArchive, Mode=TwoWay}"/>
</AppBarElementContainer>
<AppBarButton
Command="{Binding AddArchiveCommand}"

View File

@@ -5,7 +5,6 @@
xmlns:cwa="using:CommunityToolkit.WinUI.Animations"
xmlns:cwb="using:CommunityToolkit.WinUI.Behaviors"
xmlns:cwu="using:CommunityToolkit.WinUI.UI"
xmlns:cwucont="using:CommunityToolkit.WinUI.UI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
@@ -15,7 +14,6 @@
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shci="using:Snap.Hutao.Control.Image"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
xmlns:shvca="using:Snap.Hutao.View.Card"
xmlns:shvco="using:Snap.Hutao.View.Control"
xmlns:shvh="using:Snap.Hutao.ViewModel.Home"
d:DataContext="{d:DesignInstance shvh:AnnouncementViewModel}"
@@ -29,140 +27,147 @@
<shc:BindingProxy x:Key="BindingProxy" DataContext="{Binding}"/>
<DataTemplate x:Key="AnnouncementTemplate">
<cwucont:AdaptiveGridView
Margin="16,16,0,-4"
<ItemsView
Margin="16"
HorizontalAlignment="Stretch"
cwa:ItemsReorderAnimation.Duration="0:0:0.1"
DesiredWidth="{StaticResource AdaptiveGridViewDesiredWidth}"
ItemContainerStyle="{StaticResource LargeGridViewItemStyle}"
IsItemInvokedEnabled="False"
ItemsSource="{Binding List}"
SelectionMode="None">
<cwucont:AdaptiveGridView.ItemTemplate>
<ItemsView.Layout>
<UniformGridLayout
ItemsJustification="Start"
ItemsStretch="Fill"
MinColumnSpacing="12"
MinItemWidth="300"
MinRowSpacing="12"/>
</ItemsView.Layout>
<ItemsView.ItemTemplate>
<DataTemplate>
<Border cwu:UIElementExtensions.ClipToBounds="True" Style="{StaticResource BorderCardStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<!-- Image Layer -->
<Border cwu:UIElementExtensions.ClipToBounds="True">
<Border VerticalAlignment="Top" cwu:VisualExtensions.NormalizedCenterPoint="0.5">
<shci:CachedImage Source="{Binding Banner}" Stretch="UniformToFill"/>
<ItemContainer>
<Border Style="{StaticResource BorderCardStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<!-- Image Layer -->
<Border cwu:UIElementExtensions.ClipToBounds="True">
<Border VerticalAlignment="Top" cwu:VisualExtensions.NormalizedCenterPoint="0.5">
<shci:CachedImage Source="{Binding Banner}" Stretch="UniformToFill"/>
<mxi:Interaction.Behaviors>
<shcb:AutoHeightBehavior TargetHeight="390" TargetWidth="1080"/>
</mxi:Interaction.Behaviors>
<cwa:Explicit.Animations>
<cwa:AnimationSet x:Name="ImageZoomInAnimation">
<shca:ImageZoomInAnimation/>
</cwa:AnimationSet>
<cwa:AnimationSet x:Name="ImageZoomOutAnimation">
<shca:ImageZoomOutAnimation/>
</cwa:AnimationSet>
</cwa:Explicit.Animations>
<mxi:Interaction.Behaviors>
<shcb:AutoHeightBehavior TargetHeight="390" TargetWidth="1080"/>
</mxi:Interaction.Behaviors>
<cwa:Explicit.Animations>
<cwa:AnimationSet x:Name="ImageZoomInAnimation">
<shca:ImageZoomInAnimation/>
</cwa:AnimationSet>
<cwa:AnimationSet x:Name="ImageZoomOutAnimation">
<shca:ImageZoomOutAnimation/>
</cwa:AnimationSet>
</cwa:Explicit.Animations>
</Border>
</Border>
</Border>
<!-- Time Description -->
<Grid Grid.Row="0">
<Border
Height="24"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Visibility="{Binding ShouldShowTimeDescription, Converter={StaticResource BoolToVisibilityConverter}}">
<ProgressBar
MinHeight="2"
<!-- Time Description -->
<Grid Grid.Row="0">
<Border
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Background="Transparent"
CornerRadius="0"
Maximum="1"
Value="{Binding TimePercent, Mode=OneWay}"/>
Visibility="{Binding ShouldShowTimeDescription, Converter={StaticResource BoolToVisibilityConverter}}">
<ProgressBar
MinHeight="2"
VerticalAlignment="Bottom"
Background="Transparent"
CornerRadius="0"
Maximum="1"
Value="{Binding TimePercent, Mode=OneWay}"/>
</Border>
</Grid>
<!-- General Description -->
<Border Grid.Row="1" CornerRadius="{StaticResource CompatCornerRadiusBottom}">
<StackPanel Margin="4" VerticalAlignment="Bottom">
<TextBlock
Margin="4,6,0,0"
HorizontalAlignment="Stretch"
Style="{StaticResource SubtitleTextBlockStyle}"
Text="{Binding Subtitle}"
TextTrimming="WordEllipsis"
TextWrapping="NoWrap"/>
<TextBlock
Margin="4,6,0,0"
Opacity="0.6"
Style="{StaticResource BodyTextBlockStyle}"
Text="{Binding Title}"
TextTrimming="WordEllipsis"
TextWrapping="NoWrap"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock
Margin="4,4,0,4"
FontSize="10"
Opacity="0.4"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding TimeFormatted}"
TextWrapping="NoWrap"/>
<TextBlock
Grid.Column="1"
Margin="4,4,4,4"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
FontSize="10"
Opacity="0.8"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding TimeDescription}"
Visibility="{Binding ShouldShowTimeDescription, Converter={StaticResource BoolToVisibilityConverter}}"/>
</Grid>
</StackPanel>
</Border>
</Grid>
<!-- General Description -->
<Border Grid.Row="1" CornerRadius="{StaticResource CompatCornerRadiusBottom}">
<StackPanel Margin="4" VerticalAlignment="Bottom">
<TextBlock
Margin="4,6,0,0"
HorizontalAlignment="Stretch"
Style="{StaticResource SubtitleTextBlockStyle}"
Text="{Binding Subtitle}"
TextTrimming="WordEllipsis"
TextWrapping="NoWrap"/>
<TextBlock
Margin="4,6,0,0"
Opacity="0.6"
Style="{StaticResource BodyTextBlockStyle}"
Text="{Binding Title}"
TextTrimming="WordEllipsis"
TextWrapping="NoWrap"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock
Margin="4,4,0,4"
FontSize="10"
Opacity="0.4"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding TimeFormatted}"
TextWrapping="NoWrap"/>
<TextBlock
Grid.Column="1"
Margin="4,4,4,4"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
FontSize="10"
Opacity="0.8"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding TimeDescription}"
Visibility="{Binding ShouldShowTimeDescription, Converter={StaticResource BoolToVisibilityConverter}}"/>
</Grid>
</StackPanel>
</Border>
</Grid>
<FlyoutBase.AttachedFlyout>
<Flyout LightDismissOverlayMode="On" Placement="Full">
<Flyout.FlyoutPresenterStyle>
<Style BasedOn="{StaticResource DefaultFlyoutPresenterStyle}" TargetType="FlyoutPresenter">
<Setter Property="Padding" Value="0"/>
<Setter Property="CornerRadius" Value="0"/>
<Setter Property="MaxWidth" Value="640"/>
</Style>
</Flyout.FlyoutPresenterStyle>
<shvco:AnnouncementContentViewer Announcement="{Binding}"/>
</Flyout>
</FlyoutBase.AttachedFlyout>
<mxi:Interaction.Behaviors>
<mxic:EventTriggerBehavior EventName="Tapped">
<shcb:OpenAttachedFlyoutAction/>
</mxic:EventTriggerBehavior>
<mxic:EventTriggerBehavior EventName="PointerEntered">
<cwb:StartAnimationAction Animation="{Binding ElementName=ImageZoomInAnimation}"/>
</mxic:EventTriggerBehavior>
<mxic:EventTriggerBehavior EventName="PointerExited">
<cwb:StartAnimationAction Animation="{Binding ElementName=ImageZoomOutAnimation}"/>
</mxic:EventTriggerBehavior>
</mxi:Interaction.Behaviors>
</Border>
<FlyoutBase.AttachedFlyout>
<Flyout LightDismissOverlayMode="On" Placement="Full">
<Flyout.FlyoutPresenterStyle>
<Style BasedOn="{StaticResource DefaultFlyoutPresenterStyle}" TargetType="FlyoutPresenter">
<Setter Property="Padding" Value="0"/>
<Setter Property="CornerRadius" Value="0"/>
<Setter Property="MaxWidth" Value="640"/>
</Style>
</Flyout.FlyoutPresenterStyle>
<shvco:AnnouncementContentViewer Announcement="{Binding}"/>
</Flyout>
</FlyoutBase.AttachedFlyout>
<mxi:Interaction.Behaviors>
<mxic:EventTriggerBehavior EventName="Tapped">
<shcb:OpenAttachedFlyoutAction/>
</mxic:EventTriggerBehavior>
<mxic:EventTriggerBehavior EventName="PointerEntered">
<cwb:StartAnimationAction Animation="{Binding ElementName=ImageZoomInAnimation}"/>
</mxic:EventTriggerBehavior>
<mxic:EventTriggerBehavior EventName="PointerExited">
<cwb:StartAnimationAction Animation="{Binding ElementName=ImageZoomOutAnimation}"/>
</mxic:EventTriggerBehavior>
</mxi:Interaction.Behaviors>
</Border>
</ItemContainer>
</DataTemplate>
</cwucont:AdaptiveGridView.ItemTemplate>
</cwucont:AdaptiveGridView>
</ItemsView.ItemTemplate>
</ItemsView>
</DataTemplate>
</shc:ScopedPage.Resources>
<Grid>
<ScrollViewer Padding="0,0,4,0">
<ScrollViewer Padding="0,0,0,0">
<StackPanel>
<StackPanel>
<TextBlock
Margin="16,16,12,0"
Margin="16,16,16,0"
Style="{StaticResource TitleTextBlockStyle}"
Text="{Binding GreetingText}"/>
<TextBlock Margin="16,0,16,0" Text="{Binding UserOptions.UserName}"/>
<TextBlock Margin="16,0" Text="{Binding UserOptions.UserName}"/>
<ItemsControl
Margin="16,16,12,0"
@@ -190,26 +195,33 @@
</ItemsControl.ItemTemplate>
</ItemsControl>
<cwucont:AdaptiveGridView
Margin="16,16,0,0"
<ItemsView
Margin="16"
HorizontalAlignment="Stretch"
cwa:ItemsReorderAnimation.Duration="0:0:0.1"
DesiredWidth="{StaticResource AdaptiveGridViewDesiredWidth}"
ItemContainerStyle="{StaticResource LargeGridViewItemStyle}"
IsItemInvokedEnabled="False"
ItemsSource="{Binding Cards, Mode=OneWay}"
SelectionMode="None"
StretchContentForSingleRow="False">
<shvca:LaunchGameCard Height="{StaticResource HomeAdaptiveCardHeight}"/>
<shvca:GachaStatisticsCard/>
<shvca:AchievementCard/>
<shvca:DailyNoteCard/>
<!--<Border Style="{StaticResource BorderCardStyle}">
<TextBlock Text="养成计划"/>
</Border>
<Border Style="{StaticResource BorderCardStyle}">
<TextBlock Text="深渊"/>
</Border>-->
</cwucont:AdaptiveGridView>
Visibility="{Binding Cards.Count, Converter={StaticResource Int32ToVisibilityConverter}}">
<ItemsView.Layout>
<UniformGridLayout
ItemsJustification="Start"
ItemsStretch="Fill"
MinColumnSpacing="12"
MinItemHeight="180"
MinItemWidth="300"
MinRowSpacing="12"/>
</ItemsView.Layout>
<ItemsView.ItemTemplate>
<DataTemplate>
<ItemContainer>
<ItemContainer.Resources>
<SolidColorBrush x:Key="ItemContainerPointerOverBackground" Color="Transparent"/>
</ItemContainer.Resources>
<ContentControl HorizontalContentAlignment="Stretch" Content="{Binding Card}"/>
</ItemContainer>
</DataTemplate>
</ItemsView.ItemTemplate>
</ItemsView>
</StackPanel>
<Pivot Style="{StaticResource DefaultPivotStyle}">

View File

@@ -66,11 +66,7 @@
Margin="6,6,6,6"
DisplayMemberPath="Name"
ItemsSource="{Binding Projects}"
SelectedItem="{Binding SelectedProject, Mode=TwoWay}">
<mxi:Interaction.Behaviors>
<shcb:ComboBoxExtendsContentIntoTitleBarWorkaroundBehavior/>
</mxi:Interaction.Behaviors>
</ComboBox>
SelectedItem="{Binding SelectedProject, Mode=TwoWay}"/>
</AppBarElementContainer>
<AppBarButton
Command="{Binding AddProjectCommand}"

View File

@@ -36,11 +36,7 @@
Margin="16,6,0,6"
DisplayMemberPath="Uid"
ItemsSource="{Binding Archives}"
SelectedItem="{Binding SelectedArchive, Mode=TwoWay}">
<mxi:Interaction.Behaviors>
<shcb:ComboBoxExtendsContentIntoTitleBarWorkaroundBehavior/>
</mxi:Interaction.Behaviors>
</ComboBox>
SelectedItem="{Binding SelectedArchive, Mode=TwoWay}"/>
</Pivot.LeftHeader>
<Pivot.RightHeader>
<CommandBar DefaultLabelPosition="Right">

View File

@@ -23,9 +23,9 @@
<Grid>
<Image
VerticalAlignment="Center"
Source="ms-appx:///Assets/Square44x44Logo.targetsize-256.png"
Stretch="UniformToFill"/>
<Grid Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}">
Source="ms-appx:///Resource/BlurBackground.png"
Stretch="Fill"/>
<Grid Background="{ThemeResource SystemControlBackgroundAltMediumBrush}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
@@ -91,6 +91,8 @@
Description="{Binding HutaoOptions.WebView2Version}"
Header="{shcm:ResourceString Name=ViewPageSettingWebview2Header}"
HeaderIcon="{shcm:FontIcon Glyph=&#xECAA;}"/>
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageSettingShellExperienceHeader}"/>
<cwc:SettingsCard
ActionIconToolTip="{shcm:ResourceString Name=ViewPageSettingCreateDesktopShortcutAction}"
Command="{Binding CreateDesktopShortcutCommand}"
@@ -119,28 +121,68 @@
SelectedItem="{Binding SelectedBackdropType, Mode=TwoWay}"/>
</cwc:SettingsCard>
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewpageSettingHomeHeader}"/>
<cwc:SettingsExpander
Description="{shcm:ResourceString Name=ViewpageSettingHomeCardDescription}"
Header="{shcm:ResourceString Name=ViewpageSettingHomeCardHeader}"
HeaderIcon="{shcm:FontIcon Glyph=&#xEE40;}">
<cwc:SettingsExpander.Items>
<cwc:SettingsCard Header="{shcm:ResourceString Name=ViewpageSettingHomeCardItemLaunchGameHeader}">
<ToggleSwitch
IsOn="{Binding HomeCardOptions.IsHomeCardLaunchGamePresented, Mode=TwoWay}"
OffContent="{shcm:ResourceString Name=ViewPageSettingHomeCardOff}"
OnContent="{shcm:ResourceString Name=ViewPageSettingHomeCardOn}"/>
</cwc:SettingsCard>
<cwc:SettingsCard Header="{shcm:ResourceString Name=ViewpageSettingHomeCardItemgachaStatisticsHeader}">
<ToggleSwitch
IsOn="{Binding HomeCardOptions.IsHomeCardGachaStatisticsPresented, Mode=TwoWay}"
OffContent="{shcm:ResourceString Name=ViewPageSettingHomeCardOff}"
OnContent="{shcm:ResourceString Name=ViewPageSettingHomeCardOn}"/>
</cwc:SettingsCard>
<cwc:SettingsCard Header="{shcm:ResourceString Name=ViewpageSettingHomeCardItemAchievementHeader}">
<ToggleSwitch
IsOn="{Binding HomeCardOptions.IsHomeCardAchievementPresented, Mode=TwoWay}"
OffContent="{shcm:ResourceString Name=ViewPageSettingHomeCardOff}"
OnContent="{shcm:ResourceString Name=ViewPageSettingHomeCardOn}"/>
</cwc:SettingsCard>
<cwc:SettingsCard Header="{shcm:ResourceString Name=ViewpageSettingHomeCardItemDailyNoteHeader}">
<ToggleSwitch
IsOn="{Binding HomeCardOptions.IsHomeCardDailyNotePresented, Mode=TwoWay}"
OffContent="{shcm:ResourceString Name=ViewPageSettingHomeCardOff}"
OnContent="{shcm:ResourceString Name=ViewPageSettingHomeCardOn}"/>
</cwc:SettingsCard>
</cwc:SettingsExpander.Items>
</cwc:SettingsExpander>
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{shcm:ResourceString Name=ViewPageSettingGameHeader}"/>
<InfoBar
IsClosable="False"
IsOpen="True"
Message="{shcm:ResourceString Name=ViewPageSettingSetGamePathHint}"
Severity="Informational"/>
<cwc:SettingsCard
ActionIcon="{shcm:FontIcon Glyph=&#xE76C;}"
ActionIconToolTip="{shcm:ResourceString Name=ViewPageSettingSetGamePathAction}"
Command="{Binding SetGamePathCommand}"
Description="{Binding Options.GamePath}"
Header="{shcm:ResourceString Name=ViewPageSettingSetGamePathHeader}"
HeaderIcon="{shcm:FontIcon Glyph=&#xE7FC;}"
IsClickEnabled="True"/>
IsClickEnabled="True">
<cwc:SettingsCard.Description>
<StackPanel>
<TextBlock Foreground="{ThemeResource SystemErrorTextColor}" Text="{shcm:ResourceString Name=ViewPageSettingSetGamePathHint}"/>
<TextBlock Text="{Binding Options.GamePath}"/>
</StackPanel>
</cwc:SettingsCard.Description>
</cwc:SettingsCard>
<cwc:SettingsCard
ActionIcon="{shcm:FontIcon Glyph=&#xE76C;}"
ActionIconToolTip="{shcm:ResourceString Name=ViewPageSettingSetGamePathAction}"
Command="{Binding SetPowerShellPathCommand}"
Description="{Binding Options.PowerShellPath}"
Header="{shcm:ResourceString Name=ViewPageSettingSetPowerShellPathHeader}"
HeaderIcon="{shcm:FontIcon Glyph=&#xE756;}"
IsClickEnabled="True"/>
IsClickEnabled="True">
<cwc:SettingsCard.Description>
<StackPanel>
<TextBlock Text="{shcm:ResourceString Name=ViewPageSettingSetPowerShellDescription}"/>
<TextBlock Text="{Binding Options.PowerShellPath}"/>
</StackPanel>
</cwc:SettingsCard.Description>
</cwc:SettingsCard>
<cwc:SettingsCard
ActionIcon="{shcm:FontIcon Glyph=&#xE76C;}"
ActionIconToolTip="{shcm:ResourceString Name=ViewPageSettingDeleteCacheAction}"

View File

@@ -315,6 +315,7 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
if (result.Interrupted)
{
infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning);
infoBarService.Warning(SH.ViewModelCultivationBatchAddIncompletedFormat.Format(result.SucceedCount, result.SkippedCount));
}
else

View File

@@ -1,9 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.Hutao;
using Snap.Hutao.View.Card;
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
using System.Collections.ObjectModel;
@@ -25,6 +27,7 @@ internal sealed partial class AnnouncementViewModel : Abstraction.ViewModel
private AnnouncementWrapper? announcement;
private string greetingText = SH.ViewPageHomeGreetingTextDefault;
private ObservableCollection<Web.Hutao.HutaoAsAService.Announcement>? hutaoAnnouncements;
private List<CardReference>? cards;
/// <summary>
/// 公告
@@ -43,8 +46,11 @@ internal sealed partial class AnnouncementViewModel : Abstraction.ViewModel
/// </summary>
public string GreetingText { get => greetingText; set => SetProperty(ref greetingText, value); }
public List<CardReference>? Cards { get => cards; set => SetProperty(ref cards, value); }
protected override ValueTask<bool> InitializeUIAsync()
{
InitializeDashboard();
InitializeInGameAnnouncementAsync().SafeForget();
InitializeHutaoAnnouncementAsync().SafeForget();
UpdateGreetingText();
@@ -105,4 +111,31 @@ internal sealed partial class AnnouncementViewModel : Abstraction.ViewModel
}
}
}
private void InitializeDashboard()
{
List<CardReference> result = new();
if (LocalSetting.Get(SettingKeys.IsHomeCardLaunchGamePresented, true))
{
result.Add(new() { Card = new LaunchGameCard() });
}
if (LocalSetting.Get(SettingKeys.IsHomeCardGachaStatisticsPresented, true))
{
result.Add(new() { Card = new GachaStatisticsCard() });
}
if (LocalSetting.Get(SettingKeys.IsHomeCardAchievementPresented, true))
{
result.Add(new() { Card = new AchievementCard() });
}
if (LocalSetting.Get(SettingKeys.IsHomeCardDailyNotePresented, true))
{
result.Add(new() { Card = new DailyNoteCard() });
}
Cards = result;
}
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Setting;
namespace Snap.Hutao.ViewModel;
internal sealed class HomeCardOptions
{
public bool IsHomeCardLaunchGamePresented
{
get => LocalSetting.Get(SettingKeys.IsHomeCardLaunchGamePresented, true);
set => LocalSetting.Set(SettingKeys.IsHomeCardLaunchGamePresented, value);
}
public bool IsHomeCardGachaStatisticsPresented
{
get => LocalSetting.Get(SettingKeys.IsHomeCardGachaStatisticsPresented, true);
set => LocalSetting.Set(SettingKeys.IsHomeCardGachaStatisticsPresented, value);
}
public bool IsHomeCardAchievementPresented
{
get => LocalSetting.Get(SettingKeys.IsHomeCardAchievementPresented, true);
set => LocalSetting.Set(SettingKeys.IsHomeCardAchievementPresented, value);
}
public bool IsHomeCardDailyNotePresented
{
get => LocalSetting.Get(SettingKeys.IsHomeCardDailyNotePresented, true);
set => LocalSetting.Set(SettingKeys.IsHomeCardDailyNotePresented, value);
}
}

View File

@@ -37,6 +37,8 @@ namespace Snap.Hutao.ViewModel;
[Injection(InjectAs.Scoped)]
internal sealed partial class SettingViewModel : Abstraction.ViewModel
{
private readonly HomeCardOptions homeCardOptions = new();
private readonly IContentDialogFactory contentDialogFactory;
private readonly IGameLocatorFactory gameLocatorFactory;
private readonly INavigationService navigationService;
@@ -68,6 +70,8 @@ internal sealed partial class SettingViewModel : Abstraction.ViewModel
/// </summary>
public HutaoUserOptions UserOptions { get => hutaoUserOptions; }
public HomeCardOptions HomeCardOptions { get => homeCardOptions; }
/// <summary>
/// 选中的背景类型
/// </summary>