diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml b/src/Snap.Hutao/Snap.Hutao/App.xaml index dd3332bd..ebf0792b 100644 --- a/src/Snap.Hutao/Snap.Hutao/App.xaml +++ b/src/Snap.Hutao/Snap.Hutao/App.xaml @@ -48,7 +48,7 @@ 288 212 304 - + 320 180 64 100 diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/DateTimeOffsetExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/DateTimeOffsetExtension.cs index 7edab75f..867ef104 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/DateTimeOffsetExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/DateTimeOffsetExtension.cs @@ -9,6 +9,8 @@ namespace Snap.Hutao.Extension; [HighQuality] internal static class DateTimeOffsetExtension { + public static readonly DateTimeOffset DatebaseDefaultTime = new(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)); + /// /// 从Unix时间戳转换 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/DailyNoteEntry.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/DailyNoteEntry.cs index c2d5f859..62e69ed8 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/DailyNoteEntry.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/DailyNoteEntry.cs @@ -58,7 +58,15 @@ internal sealed class DailyNoteEntry : ObservableObject, IMappingFrom SH.ModelEntityDailyNoteRefreshTimeFormat.Format(RefreshTime); } + public string RefreshTimeFormatted + { + get + { + return RefreshTime == DateTimeOffsetExtension.DatebaseDefaultTime + ? SH.ModelEntityDailyNoteNotRefreshed + : SH.ModelEntityDailyNoteRefreshTimeFormat.Format(RefreshTime); + } + } /// /// 树脂提醒阈值 diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs index 70b3f9bb..e4619b1e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs @@ -420,6 +420,15 @@ namespace Snap.Hutao.Resource.Localization { } } + /// + /// 查找类似 尚未刷新 的本地化字符串。 + /// + internal static string ModelEntityDailyNoteNotRefreshed { + get { + return ResourceManager.GetString("ModelEntityDailyNoteNotRefreshed", resourceCulture); + } + } + /// /// 查找类似 刷新于 {0:yyyy/MM/dd HH:mm:ss} 的本地化字符串。 /// @@ -1122,6 +1131,60 @@ namespace Snap.Hutao.Resource.Localization { } } + /// + /// 查找类似 养成计算:尚未刷新 的本地化字符串。 + /// + internal static string ServiceAvatarInfoSummaryCalculatorNotRefreshed { + get { + return ResourceManager.GetString("ServiceAvatarInfoSummaryCalculatorNotRefreshed", resourceCulture); + } + } + + /// + /// 查找类似 养成计算:{0:MM-dd HH:mm} 的本地化字符串。 + /// + internal static string ServiceAvatarInfoSummaryCalculatorRefreshTimeFormat { + get { + return ResourceManager.GetString("ServiceAvatarInfoSummaryCalculatorRefreshTimeFormat", resourceCulture); + } + } + + /// + /// 查找类似 我的角色:尚未刷新 的本地化字符串。 + /// + internal static string ServiceAvatarInfoSummaryGameRecordNotRefreshed { + get { + return ResourceManager.GetString("ServiceAvatarInfoSummaryGameRecordNotRefreshed", resourceCulture); + } + } + + /// + /// 查找类似 我的角色:{0:MM-dd HH:mm} 的本地化字符串。 + /// + internal static string ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat { + get { + return ResourceManager.GetString("ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat", resourceCulture); + } + } + + /// + /// 查找类似 角色橱窗:尚未刷新 的本地化字符串。 + /// + internal static string ServiceAvatarInfoSummaryShowcaseNotRefreshed { + get { + return ResourceManager.GetString("ServiceAvatarInfoSummaryShowcaseNotRefreshed", resourceCulture); + } + } + + /// + /// 查找类似 角色橱窗:{0:MM-dd HH:mm} 的本地化字符串。 + /// + internal static string ServiceAvatarInfoSummaryShowcaseRefreshTimeFormat { + get { + return ResourceManager.GetString("ServiceAvatarInfoSummaryShowcaseRefreshTimeFormat", resourceCulture); + } + } + /// /// 查找类似 保存养成计划状态失败 的本地化字符串。 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index 9cd1ebf6..037f796e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -237,6 +237,9 @@ 网络异常 + + 尚未刷新 + 刷新于 {0:yyyy/MM/dd HH:mm:ss} @@ -527,6 +530,24 @@ 风元素抗性 Need EXACT same string in game + + 养成计算:尚未刷新 + + + 养成计算:{0:MM-dd HH:mm} + + + 我的角色:尚未刷新 + + + 我的角色:{0:MM-dd HH:mm} + + + 角色橱窗:尚未刷新 + + + 角色橱窗:{0:MM-dd HH:mm} + 保存养成计划状态失败 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbBulkOperation.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbBulkOperation.cs index 5dee9171..3a99e0ac 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbBulkOperation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbBulkOperation.cs @@ -40,7 +40,7 @@ internal sealed partial class AvatarInfoDbBulkOperation /// Enka信息 /// 取消令牌 /// 角色列表 - public List UpdateDbAvatarInfosByShowcase(string uid, IEnumerable webInfos, CancellationToken token) + public List UpdateDbAvatarInfosByShowcase(string uid, IEnumerable webInfos, CancellationToken token) { token.ThrowIfCancellationRequested(); List dbInfos = avatarInfoDbService.GetAvatarInfoListByUid(uid); @@ -63,7 +63,7 @@ internal sealed partial class AvatarInfoDbBulkOperation } token.ThrowIfCancellationRequested(); - return avatarInfoDbService.GetAvatarInfoInfoListByUid(uid); + return avatarInfoDbService.GetAvatarInfoListByUid(uid); } } @@ -73,7 +73,7 @@ internal sealed partial class AvatarInfoDbBulkOperation /// 用户与角色 /// 取消令牌 /// 角色列表 - public async ValueTask> UpdateDbAvatarInfosByGameRecordCharacterAsync(UserAndUid userAndUid, CancellationToken token) + public async ValueTask> UpdateDbAvatarInfosByGameRecordCharacterAsync(UserAndUid userAndUid, CancellationToken token) { token.ThrowIfCancellationRequested(); string uid = userAndUid.Uid.Value; @@ -121,7 +121,7 @@ internal sealed partial class AvatarInfoDbBulkOperation } } - return avatarInfoDbService.GetAvatarInfoInfoListByUid(uid); + return avatarInfoDbService.GetAvatarInfoListByUid(uid); } /// @@ -130,7 +130,7 @@ internal sealed partial class AvatarInfoDbBulkOperation /// 用户与角色 /// 取消令牌 /// 角色列表 - public async ValueTask> UpdateDbAvatarInfosByCalculateAvatarDetailAsync(UserAndUid userAndUid, CancellationToken token) + public async ValueTask> UpdateDbAvatarInfosByCalculateAvatarDetailAsync(UserAndUid userAndUid, CancellationToken token) { token.ThrowIfCancellationRequested(); string uid = userAndUid.Uid.Value; @@ -173,7 +173,7 @@ internal sealed partial class AvatarInfoDbBulkOperation } } - return avatarInfoDbService.GetAvatarInfoInfoListByUid(uid); + return avatarInfoDbService.GetAvatarInfoListByUid(uid); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbService.cs index 59455ba6..1ae7fb4c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoDbService.cs @@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore; using Snap.Hutao.Core.Database; using Snap.Hutao.Model.Entity.Database; using EnkaAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo; -using ModelAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo; +using EntityAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo; namespace Snap.Hutao.Service.AvatarInfo; @@ -15,7 +15,7 @@ internal sealed partial class AvatarInfoDbService : IAvatarInfoDbService { private readonly IServiceProvider serviceProvider; - public List GetAvatarInfoListByUid(string uid) + public List GetAvatarInfoListByUid(string uid) { using (IServiceScope scope = serviceProvider.CreateScope()) { @@ -24,19 +24,6 @@ internal sealed partial class AvatarInfoDbService : IAvatarInfoDbService } } - public List GetAvatarInfoInfoListByUid(string uid) - { - using (IServiceScope scope = serviceProvider.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - return appDbContext.AvatarInfos - .AsNoTracking() - .Where(i => i.Uid == uid) - .Select(i => i.Info) - .ToList(); - } - } - public void DeleteAvatarInfoRangeByUid(string uid) { using (IServiceScope scope = serviceProvider.CreateScope()) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs index 0ab000cb..ee1200d0 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/AvatarInfoService.cs @@ -10,6 +10,7 @@ using Snap.Hutao.Web.Enka; using Snap.Hutao.Web.Enka.Model; using Snap.Hutao.Web.Hoyolab; using EnkaAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo; +using EntityAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo; namespace Snap.Hutao.Service.AvatarInfo; @@ -56,28 +57,28 @@ internal sealed partial class AvatarInfoService : IAvatarInfoService return new(RefreshResult.ShowcaseNotOpen, default); } - List list = avatarInfoDbBulkOperation.UpdateDbAvatarInfosByShowcase(userAndUid.Uid.Value, resp.AvatarInfoList, token); + List list = avatarInfoDbBulkOperation.UpdateDbAvatarInfosByShowcase(userAndUid.Uid.Value, resp.AvatarInfoList, token); Summary summary = await GetSummaryCoreAsync(list, token).ConfigureAwait(false); return new(RefreshResult.Ok, summary); } case RefreshOption.RequestFromHoyolabGameRecord: { - List list = await avatarInfoDbBulkOperation.UpdateDbAvatarInfosByGameRecordCharacterAsync(userAndUid, token).ConfigureAwait(false); + List list = await avatarInfoDbBulkOperation.UpdateDbAvatarInfosByGameRecordCharacterAsync(userAndUid, token).ConfigureAwait(false); Summary summary = await GetSummaryCoreAsync(list, token).ConfigureAwait(false); return new(RefreshResult.Ok, summary); } case RefreshOption.RequestFromHoyolabCalculate: { - List list = await avatarInfoDbBulkOperation.UpdateDbAvatarInfosByCalculateAvatarDetailAsync(userAndUid, token).ConfigureAwait(false); + List list = await avatarInfoDbBulkOperation.UpdateDbAvatarInfosByCalculateAvatarDetailAsync(userAndUid, token).ConfigureAwait(false); Summary summary = await GetSummaryCoreAsync(list, token).ConfigureAwait(false); return new(RefreshResult.Ok, summary); } default: { - List list = avatarInfoDbService.GetAvatarInfoInfoListByUid(userAndUid.Uid.Value); + List list = avatarInfoDbService.GetAvatarInfoListByUid(userAndUid.Uid.Value); Summary summary = await GetSummaryCoreAsync(list, token).ConfigureAwait(false); token.ThrowIfCancellationRequested(); return new(RefreshResult.Ok, summary.Avatars.Count == 0 ? null : summary); @@ -96,7 +97,7 @@ internal sealed partial class AvatarInfoService : IAvatarInfoService ?? await enkaClient.GetDataAsync(uid, token).ConfigureAwait(false); } - private async ValueTask GetSummaryCoreAsync(IEnumerable avatarInfos, CancellationToken token) + private async ValueTask GetSummaryCoreAsync(IEnumerable avatarInfos, CancellationToken token) { using (ValueStopwatch.MeasureExecution(logger)) { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/ISummaryFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/ISummaryFactory.cs index 8a3fb260..222ce266 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/ISummaryFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/ISummaryFactory.cs @@ -17,5 +17,5 @@ internal interface ISummaryFactory /// 角色列表 /// 取消令牌 /// 简述对象 - ValueTask CreateAsync(IEnumerable avatarInfos, CancellationToken token); + ValueTask CreateAsync(IEnumerable avatarInfos, CancellationToken token); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarFactory.cs index a4d82b30..ba2b4b49 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryAvatarFactory.cs @@ -6,6 +6,7 @@ using Snap.Hutao.Model.Metadata.Converter; using Snap.Hutao.Model.Primitive; using Snap.Hutao.ViewModel.AvatarProperty; using Snap.Hutao.Web.Enka.Model; +using EntityAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo; using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar; using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon; using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo; @@ -21,7 +22,12 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory; [HighQuality] internal sealed class SummaryAvatarFactory { + private static readonly DateTimeOffset DefaultRefreshTime = new(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)); + private readonly ModelAvatarInfo avatarInfo; + private readonly DateTimeOffset showcaseRefreshTime; + private readonly DateTimeOffset gameRecordRefreshTime; + private readonly DateTimeOffset calculatorRefreshTime; private readonly SummaryMetadataContext metadataContext; /// @@ -29,10 +35,14 @@ internal sealed class SummaryAvatarFactory /// /// 元数据上下文 /// 角色信息 - public SummaryAvatarFactory(SummaryMetadataContext metadataContext, ModelAvatarInfo avatarInfo) + public SummaryAvatarFactory(SummaryMetadataContext metadataContext, EntityAvatarInfo avatarInfo) { this.metadataContext = metadataContext; - this.avatarInfo = avatarInfo; + this.avatarInfo = avatarInfo.Info; + + showcaseRefreshTime = avatarInfo.ShowcaseRefreshTime; + gameRecordRefreshTime = avatarInfo.GameRecordRefreshTime; + calculatorRefreshTime = avatarInfo.CalculatorRefreshTime; } /// @@ -67,6 +77,17 @@ internal sealed class SummaryAvatarFactory Weapon = reliquaryAndWeapon.Weapon, Reliquaries = reliquaryAndWeapon.Reliquaries, Score = $"{reliquaryAndWeapon.Reliquaries.Sum(r => r.Score):F2}", + + // times + ShowcaseRefreshTimeFormat = showcaseRefreshTime == DateTimeOffsetExtension.DatebaseDefaultTime + ? SH.ServiceAvatarInfoSummaryShowcaseNotRefreshed + : SH.ServiceAvatarInfoSummaryShowcaseRefreshTimeFormat.Format(showcaseRefreshTime), + GameRecordRefreshTimeFormat = gameRecordRefreshTime == DateTimeOffsetExtension.DatebaseDefaultTime + ? SH.ServiceAvatarInfoSummaryGameRecordNotRefreshed + : SH.ServiceAvatarInfoSummaryGameRecordRefreshTimeFormat.Format(gameRecordRefreshTime), + CalculatorRefreshTimeFormat = calculatorRefreshTime == DateTimeOffsetExtension.DatebaseDefaultTime + ? SH.ServiceAvatarInfoSummaryCalculatorNotRefreshed + : SH.ServiceAvatarInfoSummaryCalculatorRefreshTimeFormat.Format(calculatorRefreshTime), }; ApplyCostumeIconOrDefault(ref propertyAvatar, avatar); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs index ff899c70..2937b9d0 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/Factory/SummaryFactory.cs @@ -4,7 +4,6 @@ using Snap.Hutao.Model.Metadata; using Snap.Hutao.Service.Metadata; using Snap.Hutao.ViewModel.AvatarProperty; -using ModelAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo; namespace Snap.Hutao.Service.AvatarInfo.Factory; @@ -19,7 +18,7 @@ internal sealed partial class SummaryFactory : ISummaryFactory private readonly IMetadataService metadataService; /// - public async ValueTask CreateAsync(IEnumerable avatarInfos, CancellationToken token) + public async ValueTask CreateAsync(IEnumerable avatarInfos, CancellationToken token) { SummaryMetadataContext metadataContext = new() { @@ -35,7 +34,7 @@ internal sealed partial class SummaryFactory : ISummaryFactory return new() { Avatars = avatarInfos - .Where(a => !AvatarIds.IsPlayer(a.AvatarId)) + .Where(a => !AvatarIds.IsPlayer(a.Info.AvatarId)) .Select(a => new SummaryAvatarFactory(metadataContext, a).Create()) .OrderByDescending(a => a.LevelNumber) .ThenBy(a => a.Name) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/IAvatarInfoDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/IAvatarInfoDbService.cs index 3fa3f867..ef7f9383 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/IAvatarInfoDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/AvatarInfo/IAvatarInfoDbService.cs @@ -1,8 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using EnkaAvatarInfo = Snap.Hutao.Web.Enka.Model.AvatarInfo; -using ModelAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo; +using EntityAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo; namespace Snap.Hutao.Service.AvatarInfo; @@ -10,7 +9,5 @@ internal interface IAvatarInfoDbService { void DeleteAvatarInfoRangeByUid(string uid); - List GetAvatarInfoInfoListByUid(string uid); - - List GetAvatarInfoListByUid(string uid); + List GetAvatarInfoListByUid(string uid); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml index 019cf69d..de7a022f 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AvatarPropertyPage.xaml @@ -227,7 +227,7 @@ Grid.Row="1" DisplayMode="Inline" IsPaneOpen="True" - OpenPaneLength="{StaticResource CompatSplitViewOpenPaneLength}" + OpenPaneLength="{StaticResource CompatSplitViewOpenPaneLength3}" PaneBackground="Transparent"> @@ -236,7 +236,8 @@ - + + - + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarView.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarView.cs index 5ddffe75..6a4a2a55 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarView.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarProperty/AvatarView.cs @@ -93,6 +93,12 @@ internal sealed class AvatarView : INameIconSide, ICalculableSource public uint FetterLevel { get; set; } + public string ShowcaseRefreshTimeFormat { get; set; } + + public string GameRecordRefreshTimeFormat { get; set; } + + public string CalculatorRefreshTimeFormat { get; set; } + /// /// Id ///