mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
fix hutao cloud fetch merge and remove archive crash
This commit is contained in:
@@ -45,13 +45,10 @@ internal sealed partial class ScopedDbCurrent<TEntity, TMessage>
|
||||
DbSet<TEntity> dbSet = appDbContext.Set<TEntity>();
|
||||
|
||||
// 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 };
|
||||
|
||||
@@ -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<TSource>([NotNullWhen(false)][MaybeNullWhen(true)] this Collection<TSource>? source)
|
||||
{
|
||||
if (source is { Count: > 0 })
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除集合中满足条件的项
|
||||
/// </summary>
|
||||
|
||||
@@ -15,13 +15,11 @@ internal static partial class EnumerableExtension
|
||||
public static bool IsNullOrEmpty<TKey, TValue>([NotNullWhen(false)] this Dictionary<TKey, TValue>? source)
|
||||
where TKey : notnull
|
||||
{
|
||||
if (source is { } dict)
|
||||
if (source is { Count: >0 })
|
||||
{
|
||||
// empty
|
||||
return dict.Count <= 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// null
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -160,6 +160,28 @@ internal sealed partial class GachaLogDbService : IGachaLogDbService
|
||||
return item?.Id ?? long.MaxValue;
|
||||
}
|
||||
|
||||
public async ValueTask<long> GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token)
|
||||
{
|
||||
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 && 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<GachaArchive?> GetGachaArchiveByIdAsync(Guid archiveId, CancellationToken token)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
return await appDbContext.GachaArchives
|
||||
.AsNoTracking()
|
||||
.SingleOrDefaultAsync(a => a.InnerId == archiveId, token)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<GachaArchive?> GetGachaArchiveByUidAsync(string uid, CancellationToken token)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
|
||||
@@ -52,7 +52,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<ValueResult<bool, GachaArchive?>> RetrieveGachaItemsAsync(string uid, CancellationToken token = default)
|
||||
public async ValueTask<ValueResult<bool, Guid>> 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<Model.Entity.GachaItem> 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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ internal sealed partial class GachaLogService : IGachaLogService
|
||||
public async ValueTask<GachaStatistics> 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<GachaArchive> 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<ValueResult<bool, GachaArchive?>> FetchGachaLogsAsync(GachaLogQuery query, bool isLazy, IProgress<GachaLogFetchStatus> progress, CancellationToken token)
|
||||
|
||||
@@ -21,6 +21,8 @@ internal interface IGachaLogDbService
|
||||
|
||||
void DeleteNewerGachaItemsByArchiveIdQueryTypeAndEndId(Guid archiveId, GachaConfigType queryType, long endId);
|
||||
|
||||
ValueTask<GachaArchive?> GetGachaArchiveByIdAsync(Guid archiveId, CancellationToken token);
|
||||
|
||||
ValueTask<GachaArchive?> GetGachaArchiveByUidAsync(string uid, CancellationToken token);
|
||||
|
||||
ObservableCollection<GachaArchive> GetGachaArchiveCollection();
|
||||
@@ -36,4 +38,6 @@ internal interface IGachaLogDbService
|
||||
long GetOldestGachaItemIdByArchiveId(Guid archiveId);
|
||||
|
||||
long GetOldestGachaItemIdByArchiveIdAndQueryType(Guid archiveId, GachaConfigType queryType);
|
||||
|
||||
ValueTask<long> GetOldestGachaItemIdByArchiveIdAndQueryTypeAsync(Guid archiveId, GachaConfigType queryType, CancellationToken token);
|
||||
}
|
||||
@@ -36,7 +36,7 @@ internal interface IGachaLogHutaoCloudService
|
||||
/// <param name="uid">uid</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>是否获取成功</returns>
|
||||
ValueTask<ValueResult<bool, GachaArchive?>> RetrieveGachaItemsAsync(string uid, CancellationToken token = default);
|
||||
ValueTask<ValueResult<bool, Guid>> RetrieveGachaItemsAsync(string uid, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步上传祈愿记录
|
||||
|
||||
@@ -25,6 +25,8 @@ internal interface IGachaLogService
|
||||
/// </summary>
|
||||
ObservableCollection<GachaArchive>? ArchiveCollection { get; }
|
||||
|
||||
ValueTask<GachaArchive> EnsureArchiveInCollectionAsync(Guid archiveId, CancellationToken token = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// 导出为一个新的UIGF对象
|
||||
/// </summary>
|
||||
|
||||
@@ -108,11 +108,14 @@
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BaseTextBlockStyle}"
|
||||
Text="{Binding Uid}"/>
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||
<TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{Binding Uid}"/>
|
||||
<TextBlock
|
||||
Opacity="0.7"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding ItemCount}"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Margin="6,0,0,0"
|
||||
|
||||
@@ -287,12 +287,13 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel
|
||||
{
|
||||
if (uid is not null)
|
||||
{
|
||||
ValueResult<bool, GachaArchive?> result = await HutaoCloudViewModel.RetrieveAsync(uid).ConfigureAwait(false);
|
||||
ValueResult<bool, Guid> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ internal sealed partial class HutaoCloudViewModel : Abstraction.ViewModel
|
||||
/// </summary>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <returns>祈愿记录</returns>
|
||||
internal async ValueTask<ValueResult<bool, GachaArchive?>> RetrieveAsync(string uid)
|
||||
internal async ValueTask<ValueResult<bool, Guid>> RetrieveAsync(string uid)
|
||||
{
|
||||
ContentDialog dialog = await contentDialogFactory
|
||||
.CreateForIndeterminateProgressAsync(SH.ViewModelGachaLogRetrieveFromHutaoCloudProgress)
|
||||
@@ -64,14 +64,11 @@ internal sealed partial class HutaoCloudViewModel : Abstraction.ViewModel
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OpenUIAsync()
|
||||
protected override async ValueTask<bool> InitializeUIAsync()
|
||||
{
|
||||
await hutaoUserService.InitializeAsync().ConfigureAwait(false);
|
||||
await RefreshUidCollectionAsync().ConfigureAwait(false);
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
IsInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
[Command("NavigateToAfdianSkuCommand")]
|
||||
|
||||
Reference in New Issue
Block a user