From 38747298b519bdfb1661be413ed23e74a4419fbf Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sat, 2 Sep 2023 22:12:37 +0800 Subject: [PATCH] fix hutao cloud fetch merge and remove archive crash --- .../Core/Database/ScopedDbCurrent.cs | 9 ++--- .../EnumerableExtension.Collection.cs | 12 +++++++ .../EnumerableExtension.Dictionary.cs | 6 ++-- .../Service/GachaLog/GachaLogDbService.cs | 34 +++++++++++++++++++ .../GachaLog/GachaLogHutaoCloudService.cs | 8 ++--- .../Service/GachaLog/GachaLogService.cs | 29 +++++++++++++--- .../Service/GachaLog/IGachaLogDbService.cs | 4 +++ .../GachaLog/IGachaLogHutaoCloudService.cs | 2 +- .../Service/GachaLog/IGachaLogService.cs | 2 ++ .../Snap.Hutao/View/Page/GachaLogPage.xaml | 13 ++++--- .../ViewModel/GachaLog/GachaLogViewModel.cs | 16 +++++++-- .../ViewModel/GachaLog/HutaoCloudViewModel.cs | 9 ++--- 12 files changed, 111 insertions(+), 33 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Database/ScopedDbCurrent.cs b/src/Snap.Hutao/Snap.Hutao/Core/Database/ScopedDbCurrent.cs index 720fa5e7..27a8c5c5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Database/ScopedDbCurrent.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Database/ScopedDbCurrent.cs @@ -45,13 +45,10 @@ internal sealed partial class ScopedDbCurrent DbSet dbSet = appDbContext.Set(); // only update when not processing a deletion - if (value is not null) + if (value is not null && current is not null) { - if (current is not null) - { - current.IsSelected = false; - dbSet.UpdateAndSave(current); - } + current.IsSelected = false; + dbSet.UpdateAndSave(current); } TMessage message = new() { OldValue = current, NewValue = value }; diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Collection.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Collection.cs index f1487570..50acd5fc 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Collection.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Collection.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System.Collections.ObjectModel; +using System.Runtime.CompilerServices; namespace Snap.Hutao.Extension; @@ -24,6 +25,17 @@ internal static partial class EnumerableExtension } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNullOrEmpty([NotNullWhen(false)][MaybeNullWhen(true)] this Collection? source) + { + if (source is { Count: > 0 }) + { + return false; + } + + return true; + } + /// /// 移除集合中满足条件的项 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs index c0d9b5e3..369d5549 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs @@ -15,13 +15,11 @@ internal static partial class EnumerableExtension public static bool IsNullOrEmpty([NotNullWhen(false)] this Dictionary? source) where TKey : notnull { - if (source is { } dict) + if (source is { Count: >0 }) { - // empty - return dict.Count <= 0; + return false; } - // null return true; } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogDbService.cs index 39225a7e..ed98e29a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogDbService.cs @@ -160,6 +160,28 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService return item?.Id ?? long.MaxValue; } + public async ValueTask GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token) + { + GachaItem? item = null; + + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + + // 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 && i.QueryType == queryType) + .OrderBy(i => i.Id) + .FirstOrDefaultAsync(token) + .ConfigureAwait(false); + } + + return item?.Id ?? long.MaxValue; + } + public async ValueTask AddGachaArchiveAsync(GachaArchive archive) { using (IServiceScope scope = serviceProvider.CreateScope()) @@ -203,6 +225,18 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService } } + public async ValueTask GetGachaArchiveByIdAsync(Guid archiveId, CancellationToken token) + { + using (IServiceScope scope = serviceProvider.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + return await appDbContext.GachaArchives + .AsNoTracking() + .SingleOrDefaultAsync(a => a.InnerId == archiveId, token) + .ConfigureAwait(false); + } + } + public async ValueTask GetGachaArchiveByUidAsync(string uid, CancellationToken token) { using (IServiceScope scope = serviceProvider.CreateScope()) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogHutaoCloudService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogHutaoCloudService.cs index 99525856..92a94ab4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogHutaoCloudService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogHutaoCloudService.cs @@ -52,7 +52,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer } /// - public async ValueTask> RetrieveGachaItemsAsync(string uid, CancellationToken token = default) + public async ValueTask> RetrieveGachaItemsAsync(string uid, CancellationToken token = default) { GachaArchive? archive = await gachaLogDbService .GetGachaArchiveByUidAsync(uid, token) @@ -63,7 +63,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer if (!resp.IsOk()) { - return new(false, null); + return new(false, default); } if (archive is null) @@ -74,7 +74,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer List gachaItems = resp.Data.SelectList(i => Model.Entity.GachaItem.From(archive.InnerId, i)); await gachaLogDbService.AddGachaItemsAsync(gachaItems).ConfigureAwait(false); - return new(true, archive); + return new(true, archive.InnerId); } /// @@ -120,7 +120,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer if (archive is not null) { endIds[type] = await gachaLogDbService - .GetNewestGachaItemIdByArchiveIdAndQueryTypeAsync(archive.InnerId, type, token) + .GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(archive.InnerId, type, token) .ConfigureAwait(false); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs index 4ff85ee4..dd9ae373 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs @@ -82,6 +82,7 @@ internal sealed partial class GachaLogService : IGachaLogService public async ValueTask GetStatisticsAsync(GachaArchive? archive) { archive ??= CurrentArchive; + archive ??= ArchiveCollection?.FirstOrDefault(); ArgumentNullException.ThrowIfNull(archive); // Return statistics @@ -147,13 +148,33 @@ internal sealed partial class GachaLogService : IGachaLogService { ArgumentNullException.ThrowIfNull(archiveCollection); - // Sync cache - await taskContext.SwitchToMainThreadAsync(); - archiveCollection.Remove(archive); - // Sync database await taskContext.SwitchToBackgroundAsync(); await gachaLogDbService.DeleteGachaArchiveByIdAsync(archive.InnerId).ConfigureAwait(false); + + // Sync cache + await taskContext.SwitchToMainThreadAsync(); + archiveCollection.Remove(archive); + } + + public async ValueTask EnsureArchiveInCollectionAsync(Guid archiveId, CancellationToken token = default) + { + ArgumentNullException.ThrowIfNull(ArchiveCollection); + + if (ArchiveCollection.SingleOrDefault(a => a.InnerId == archiveId) is { } archive) + { + return archive; + } + else + { + // sync cache + GachaArchive? newArchive = await gachaLogDbService.GetGachaArchiveByIdAsync(archiveId, token).ConfigureAwait(false); + ArgumentNullException.ThrowIfNull(newArchive); + + await taskContext.SwitchToMainThreadAsync(); + ArchiveCollection.Add(newArchive); + return newArchive; + } } private async ValueTask> FetchGachaLogsAsync(GachaLogQuery query, bool isLazy, IProgress progress, CancellationToken token) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogDbService.cs index ffa59866..1f55fe71 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogDbService.cs @@ -21,6 +21,8 @@ internal interface IGachaLogDbService void DeleteNewerGachaItemsByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaConfigType queryType, long endId); + ValueTask GetGachaArchiveByIdAsync(Guid archiveId, CancellationToken token); + ValueTask GetGachaArchiveByUidAsync(string uid, CancellationToken token); ObservableCollection GetGachaArchiveCollection(); @@ -36,4 +38,6 @@ internal interface IGachaLogDbService long GetOldestGachaItemIdByArchiveId(Guid archiveId); long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType); + + ValueTask GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogHutaoCloudService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogHutaoCloudService.cs index 8c780391..c8372147 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogHutaoCloudService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogHutaoCloudService.cs @@ -36,7 +36,7 @@ internal interface IGachaLogHutaoCloudService /// uid /// 取消令牌 /// 是否获取成功 - ValueTask> RetrieveGachaItemsAsync(string uid, CancellationToken token = default); + ValueTask> RetrieveGachaItemsAsync(string uid, CancellationToken token = default); /// /// 异步上传祈愿记录 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs index 974e8a4c..68ae9a42 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs @@ -25,6 +25,8 @@ internal interface IGachaLogService /// ObservableCollection? ArchiveCollection { get; } + ValueTask EnsureArchiveInCollectionAsync(Guid archiveId, CancellationToken token = default(CancellationToken)); + /// /// 导出为一个新的UIGF对象 /// diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml index cfc62001..b739c7cf 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/GachaLogPage.xaml @@ -108,11 +108,14 @@ - + + + + + result = await HutaoCloudViewModel.RetrieveAsync(uid).ConfigureAwait(false); + ValueResult result = await HutaoCloudViewModel.RetrieveAsync(uid).ConfigureAwait(false); - if (result.TryGetValue(out GachaArchive? archive)) + if (result.TryGetValue(out Guid archiveId)) { + GachaArchive archive = await gachaLogService.EnsureArchiveInCollectionAsync(archiveId).ConfigureAwait(false); + await taskContext.SwitchToMainThreadAsync(); - Archives?.AddIfNotContains(archive); await SetSelectedArchiveAndUpdateStatisticsAsync(archive, true).ConfigureAwait(false); } } @@ -318,6 +319,15 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel { await UpdateStatisticsAsync(archive).ConfigureAwait(false); } + else + { + // 删光了存档或使用 Ctrl 取消了存档选择时触发 + // 因此我们在这里额外判断一次是否删光了存档 + if (Archives.IsNullOrEmpty()) + { + Statistics = null; + } + } } } diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs index e9eecf5d..89a250e2 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/HutaoCloudViewModel.cs @@ -52,7 +52,7 @@ internal sealed partial class HutaoCloudViewModel : Abstraction.ViewModel /// /// uid /// 祈愿记录 - internal async ValueTask> RetrieveAsync(string uid) + internal async ValueTask> RetrieveAsync(string uid) { ContentDialog dialog = await contentDialogFactory .CreateForIndeterminateProgressAsync(SH.ViewModelGachaLogRetrieveFromHutaoCloudProgress) @@ -64,14 +64,11 @@ internal sealed partial class HutaoCloudViewModel : Abstraction.ViewModel } } - /// - protected override async Task OpenUIAsync() + protected override async ValueTask InitializeUIAsync() { await hutaoUserService.InitializeAsync().ConfigureAwait(false); await RefreshUidCollectionAsync().ConfigureAwait(false); - - await taskContext.SwitchToMainThreadAsync(); - IsInitialized = true; + return true; } [Command("NavigateToAfdianSkuCommand")]