diff --git a/src/Snap.Hutao/Snap.Hutao/.filenesting.json b/src/Snap.Hutao/Snap.Hutao/.filenesting.json
index d1473ce7..f88118b1 100644
--- a/src/Snap.Hutao/Snap.Hutao/.filenesting.json
+++ b/src/Snap.Hutao/Snap.Hutao/.filenesting.json
@@ -9,7 +9,7 @@
},
"pathSegment": {
"add": {
- ".*": [ ".cs" ]
+ ".*": [ ".cs", ".resx" ]
}
},
"fileSuffixToExtension": {
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Database/EnumerableExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/Database/EnumerableExtension.cs
new file mode 100644
index 00000000..696de653
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Database/EnumerableExtension.cs
@@ -0,0 +1,22 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Core.Database;
+
+///
+/// 可枚举扩展
+///
+public static class EnumerableExtension
+{
+ ///
+ /// 获取选中的值或默认值
+ ///
+ /// 源类型
+ /// 源
+ /// 选中的值或默认值
+ public static TSource? SelectedOrDefault(this IEnumerable source)
+ where TSource : ISelectable
+ {
+ return source.SingleOrDefault(i => i.IsSelected);
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs
index 357252dd..8f5c2507 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs
@@ -17,6 +17,7 @@ using Snap.Hutao.Model.InterChange.GachaLog;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.GachaLog.Factory;
+using Snap.Hutao.Service.GachaLog.QueryProvider;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using Snap.Hutao.Web.Response;
@@ -43,7 +44,7 @@ internal class GachaLogService : IGachaLogService
}.ToImmutableList();
private readonly AppDbContext appDbContext;
- private readonly IEnumerable urlProviders;
+ private readonly IEnumerable urlProviders;
private readonly GachaInfoClient gachaInfoClient;
private readonly IMetadataService metadataService;
private readonly IGachaStatisticsFactory gachaStatisticsFactory;
@@ -72,7 +73,7 @@ internal class GachaLogService : IGachaLogService
/// 消息器
public GachaLogService(
AppDbContext appDbContext,
- IEnumerable urlProviders,
+ IEnumerable urlProviders,
GachaInfoClient gachaInfoClient,
IMetadataService metadataService,
IGachaStatisticsFactory gachaStatisticsFactory,
@@ -172,7 +173,7 @@ internal class GachaLogService : IGachaLogService
}
///
- public IGachaLogUrlProvider? GetGachaLogUrlProvider(RefreshOption option)
+ public IGachaLogQueryProvider? GetGachaLogQueryProvider(RefreshOption option)
{
return option switch
{
@@ -207,7 +208,7 @@ internal class GachaLogService : IGachaLogService
}
///
- public async Task RefreshGachaLogAsync(string query, RefreshStrategy strategy, IProgress progress, CancellationToken token)
+ public async Task RefreshGachaLogAsync(GachaLogQuery query, RefreshStrategy strategy, IProgress progress, CancellationToken token)
{
bool isLazy = strategy switch
{
@@ -241,7 +242,7 @@ internal class GachaLogService : IGachaLogService
return Task.Delay(TimeSpan.FromSeconds(Random.Shared.NextDouble() + 1), token);
}
- private async Task> FetchGachaLogsAsync(string query, bool isLazy, IProgress progress, CancellationToken token)
+ private async Task> FetchGachaLogsAsync(GachaLogQuery query, bool isLazy, IProgress progress, CancellationToken token)
{
GachaArchive? archive = null;
FetchState state = new();
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs
index f09f5e14..f89bd435 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/IGachaLogService.cs
@@ -4,6 +4,7 @@
using Snap.Hutao.Model.Binding.Gacha;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.InterChange.GachaLog;
+using Snap.Hutao.Service.GachaLog.QueryProvider;
using System.Collections.ObjectModel;
namespace Snap.Hutao.Service.GachaLog;
@@ -36,7 +37,7 @@ internal interface IGachaLogService
///
/// 刷新模式
/// 祈愿日志Url提供器
- IGachaLogUrlProvider? GetGachaLogUrlProvider(RefreshOption option);
+ IGachaLogQueryProvider? GetGachaLogQueryProvider(RefreshOption option);
///
/// 获得对应的祈愿统计
@@ -69,7 +70,7 @@ internal interface IGachaLogService
/// 进度
/// 取消令牌
/// 验证密钥是否可用
- Task RefreshGachaLogAsync(string query, RefreshStrategy strategy, IProgress progress, CancellationToken token);
+ Task RefreshGachaLogAsync(GachaLogQuery query, RefreshStrategy strategy, IProgress progress, CancellationToken token);
///
/// 删除存档
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuery.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuery.cs
new file mode 100644
index 00000000..ad89ddbd
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogQuery.cs
@@ -0,0 +1,52 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Service.GachaLog.QueryProvider;
+
+///
+/// 祈愿记录query
+///
+internal readonly struct GachaLogQuery
+{
+ ///
+ /// query
+ ///
+ public readonly string Query;
+
+ ///
+ /// 是否为国际服
+ ///
+ public readonly bool IsOversea;
+
+ ///
+ /// 消息
+ ///
+ public readonly string Message;
+
+ ///
+ /// 构造一个新的祈愿记录query
+ ///
+ /// query
+ /// 是否为国际服
+ public GachaLogQuery(string query, bool isOversea)
+ {
+ Query = query;
+ IsOversea = isOversea;
+ Message = string.Empty;
+ }
+
+ ///
+ /// 构造一个新的失败的祈愿记录query
+ ///
+ /// 失败原因
+ public GachaLogQuery(string message)
+ {
+ Message = message;
+ Query = string.Empty;
+ }
+
+ public static implicit operator GachaLogQuery(string message)
+ {
+ return new(message);
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlManualInputProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogUrlManualInputProvider.cs
similarity index 52%
rename from src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlManualInputProvider.cs
rename to src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogUrlManualInputProvider.cs
index 850ba224..67f4ee5e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlManualInputProvider.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogUrlManualInputProvider.cs
@@ -3,29 +3,29 @@
using Snap.Hutao.View.Dialog;
-namespace Snap.Hutao.Service.GachaLog;
+namespace Snap.Hutao.Service.GachaLog.QueryProvider;
///
/// 手动输入方法
///
-[Injection(InjectAs.Transient, typeof(IGachaLogUrlProvider))]
-internal class GachaLogUrlManualInputProvider : IGachaLogUrlProvider
+[Injection(InjectAs.Transient, typeof(IGachaLogQueryProvider))]
+internal class GachaLogUrlManualInputProvider : IGachaLogQueryProvider
{
///
public string Name { get => nameof(GachaLogUrlManualInputProvider); }
///
- public async Task> GetQueryAsync()
+ public async Task> GetQueryAsync()
{
// ContentDialog must be created by main thread.
await ThreadHelper.SwitchToMainThreadAsync();
- ValueResult result = await new GachaLogUrlDialog().GetInputUrlAsync().ConfigureAwait(false);
+ (bool isOk, string query) = await new GachaLogUrlDialog().GetInputUrlAsync().ConfigureAwait(false);
- if (result.IsOk)
+ if (isOk)
{
- if (result.Value.Contains("&auth_appid=webview_gacha"))
+ if (query.Contains("&auth_appid=webview_gacha"))
{
- return result;
+ return new(true, new(query, query.Contains("hoyoverse.com")));
}
else
{
@@ -34,7 +34,7 @@ internal class GachaLogUrlManualInputProvider : IGachaLogUrlProvider
}
else
{
- return new(false, null!);
+ return new(false, string.Empty);
}
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogUrlStokenProvider.cs
similarity index 81%
rename from src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs
rename to src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogUrlStokenProvider.cs
index 231674a3..256534e2 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogUrlStokenProvider.cs
@@ -7,13 +7,13 @@ using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
using Snap.Hutao.Web.Response;
-namespace Snap.Hutao.Service.GachaLog;
+namespace Snap.Hutao.Service.GachaLog.QueryProvider;
///
/// 使用Stokn提供祈愿Url
///
-[Injection(InjectAs.Transient, typeof(IGachaLogUrlProvider))]
-internal class GachaLogUrlStokenProvider : IGachaLogUrlProvider
+[Injection(InjectAs.Transient, typeof(IGachaLogQueryProvider))]
+internal class GachaLogUrlStokenProvider : IGachaLogQueryProvider
{
private readonly IUserService userService;
private readonly BindingClient2 bindingClient2;
@@ -33,7 +33,7 @@ internal class GachaLogUrlStokenProvider : IGachaLogUrlProvider
public string Name { get => nameof(GachaLogUrlStokenProvider); }
///
- public async Task> GetQueryAsync()
+ public async Task> GetQueryAsync()
{
if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
{
@@ -42,7 +42,7 @@ internal class GachaLogUrlStokenProvider : IGachaLogUrlProvider
if (authkeyResponse.IsOk())
{
- return new(true, GachaLogQueryOptions.AsQuery(data, authkeyResponse.Data));
+ return new(true, new(GachaLogQueryOptions.AsQuery(data, authkeyResponse.Data), false));
}
else
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlWebCacheProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogUrlWebCacheProvider.cs
similarity index 81%
rename from src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlWebCacheProvider.cs
rename to src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogUrlWebCacheProvider.cs
index d1a8bdee..f337427d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlWebCacheProvider.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/GachaLogUrlWebCacheProvider.cs
@@ -6,13 +6,13 @@ using Snap.Hutao.Service.Game;
using System.IO;
using System.Text;
-namespace Snap.Hutao.Service.GachaLog;
+namespace Snap.Hutao.Service.GachaLog.QueryProvider;
///
/// 浏览器缓存方法
///
-[Injection(InjectAs.Transient, typeof(IGachaLogUrlProvider))]
-internal class GachaLogUrlWebCacheProvider : IGachaLogUrlProvider
+[Injection(InjectAs.Transient, typeof(IGachaLogQueryProvider))]
+internal class GachaLogUrlWebCacheProvider : IGachaLogQueryProvider
{
private readonly IGameService gameService;
@@ -44,7 +44,7 @@ internal class GachaLogUrlWebCacheProvider : IGachaLogUrlProvider
}
///
- public async Task> GetQueryAsync()
+ public async Task> GetQueryAsync()
{
(bool isOk, string path) = await gameService.GetGamePathAsync().ConfigureAwait(false);
@@ -65,7 +65,15 @@ internal class GachaLogUrlWebCacheProvider : IGachaLogUrlProvider
{
await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
string? result = Match(memoryStream, cacheFile.Contains(GameConstants.GenshinImpactData));
- return new(!string.IsNullOrEmpty(result), result ?? SH.ServiceGachaLogUrlProviderCacheUrlNotFound);
+
+ if (!string.IsNullOrEmpty(result))
+ {
+ return new(true, new(result, result.Contains("hoyoverse.com")));
+ }
+ else
+ {
+ return new(false, SH.ServiceGachaLogUrlProviderCacheUrlNotFound);
+ }
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/IGachaLogUrlProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/IGachaLogQueryProvider.cs
similarity index 70%
rename from src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/IGachaLogUrlProvider.cs
rename to src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/IGachaLogQueryProvider.cs
index a88d6dfc..8e2cf63a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/IGachaLogUrlProvider.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/QueryProvider/IGachaLogQueryProvider.cs
@@ -3,17 +3,17 @@
using Snap.Hutao.Core.Abstraction;
-namespace Snap.Hutao.Service.GachaLog;
+namespace Snap.Hutao.Service.GachaLog.QueryProvider;
///
/// 祈愿记录Url提供器
///
-internal interface IGachaLogUrlProvider : INamed
+internal interface IGachaLogQueryProvider : INamed
{
///
/// 异步获取包含验证密钥的查询语句
/// 查询语句可以仅包含?后的内容
///
/// 包含验证密钥的查询语句
- Task> GetQueryAsync();
-}
+ Task> GetQueryAsync();
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml
index 02da1683..f616e076 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml
@@ -166,13 +166,11 @@
-
+
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Abstraction/ViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Abstraction/ViewModel.cs
index bdf7e987..e83e5d3f 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Abstraction/ViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Abstraction/ViewModel.cs
@@ -26,6 +26,19 @@ public abstract class ViewModel : ObservableObject, IViewModel
///
public bool IsViewDisposed { get; set; }
+ ///
+ /// 保证 using scope 内的代码运行完成
+ /// 防止 视图资源被回收
+ ///
+ /// 解除执行限制
+ protected async Task EnterCriticalExecutionAsync()
+ {
+ ThrowIfViewDisposed();
+ IDisposable disposable = await DisposeLock.EnterAsync(CancellationToken).ConfigureAwait(false);
+ ThrowIfViewDisposed();
+ return disposable;
+ }
+
///
/// 当页面被释放后抛出异常
///
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs
index a40fda32..f9d59bdf 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs
@@ -6,6 +6,7 @@ using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI.UI;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control.Extension;
+using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.IO;
using Snap.Hutao.Core.IO.DataTransfer;
using Snap.Hutao.Core.LifeCycle;
@@ -23,6 +24,7 @@ using Windows.Storage.Pickers;
using BindingAchievement = Snap.Hutao.Model.Binding.Achievement.Achievement;
using BindingAchievementGoal = Snap.Hutao.Model.Binding.Achievement.AchievementGoal;
using EntityAchievementArchive = Snap.Hutao.Model.Entity.AchievementArchive;
+using MetadataAchievement = Snap.Hutao.Model.Metadata.Achievement.Achievement;
using MetadataAchievementGoal = Snap.Hutao.Model.Metadata.Achievement.AchievementGoal;
namespace Snap.Hutao.ViewModel;
@@ -246,11 +248,8 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
List sortedGoals;
ObservableCollection archives;
- ThrowIfViewDisposed();
- using (await DisposeLock.EnterAsync(CancellationToken).ConfigureAwait(false))
+ using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
- ThrowIfViewDisposed();
-
List goals = await metadataService.GetAchievementGoalsAsync(CancellationToken).ConfigureAwait(false);
sortedGoals = goals
.OrderBy(goal => goal.Order)
@@ -262,7 +261,7 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
await ThreadHelper.SwitchToMainThreadAsync();
AchievementGoals = sortedGoals;
Archives = archives;
- SelectedArchive = Archives.SingleOrDefault(a => a.IsSelected == true);
+ SelectedArchive = Archives.SelectedOrDefault();
IsInitialized = true;
}
@@ -271,6 +270,7 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
// User canceled the loading operation,
// Indicate initialization not succeed.
openUICompletionSource.TrySetResult(false);
+ return;
}
}
@@ -343,30 +343,6 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
}
#endregion
- private void SearchAchievement(string? search)
- {
- if (Achievements != null)
- {
- SetProperty(ref selectedAchievementGoal, null);
-
- if (!string.IsNullOrEmpty(search))
- {
- if (search.Length == 5 && int.TryParse(search, out int achiId))
- {
- Achievements.Filter = (object o) => ((BindingAchievement)o).Inner.Id == achiId;
- }
- else
- {
- Achievements.Filter = (object o) =>
- {
- BindingAchievement achi = (BindingAchievement)o;
- return achi.Inner.Title.Contains(search) || achi.Inner.Description.Contains(search);
- };
- }
- }
- }
- }
-
#region 导入导出
private async Task ExportAsUIAFToFileAsync()
{
@@ -492,9 +468,33 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
}
#endregion
+ private void SearchAchievement(string? search)
+ {
+ if (Achievements != null)
+ {
+ SetProperty(ref selectedAchievementGoal, null);
+
+ if (!string.IsNullOrEmpty(search))
+ {
+ if (search.Length == 5 && int.TryParse(search, out int achiId))
+ {
+ Achievements.Filter = obj => ((BindingAchievement)obj).Inner.Id == achiId;
+ }
+ else
+ {
+ Achievements.Filter = obj =>
+ {
+ BindingAchievement achi = (BindingAchievement)obj;
+ return achi.Inner.Title.Contains(search) || achi.Inner.Description.Contains(search);
+ };
+ }
+ }
+ }
+ }
+
private async Task UpdateAchievementsAsync(EntityAchievementArchive archive)
{
- List rawAchievements = await metadataService.GetAchievementsAsync(CancellationToken).ConfigureAwait(false);
+ List rawAchievements = await metadataService.GetAchievementsAsync(CancellationToken).ConfigureAwait(false);
List combined;
try
{
@@ -547,22 +547,22 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
int count = 0;
if (Achievements != null && AchievementGoals != null)
{
- Dictionary counter = AchievementGoals.ToDictionary(x => x.Id, x => new GoalAggregation(x));
- foreach (BindingAchievement achievement in Achievements.SourceCollection.OfType())
+ Dictionary counter = AchievementGoals.ToDictionary(x => x.Id, x => new GoalStatistics(x));
+ foreach (BindingAchievement achievement in Achievements.SourceCollection.Cast())
{
- ref GoalAggregation aggregation = ref CollectionsMarshal.GetValueRefOrNullRef(counter, achievement.Inner.Goal);
- aggregation.Count += 1;
+ ref GoalStatistics stat = ref CollectionsMarshal.GetValueRefOrNullRef(counter, achievement.Inner.Goal);
+ stat.Count += 1;
count += 1;
if (achievement.IsChecked)
{
- aggregation.Finished += 1;
+ stat.Finished += 1;
finished += 1;
}
}
- foreach (GoalAggregation aggregation1 in counter.Values)
+ foreach (GoalStatistics statistics in counter.Values)
{
- aggregation1.AchievementGoal.UpdateFinishPercent(aggregation1.Finished, aggregation1.Count);
+ statistics.AchievementGoal.UpdateFinishPercent(statistics.Finished, statistics.Count);
}
FinishDescription = $"{finished}/{count} - {(double)finished / count:P2}";
@@ -578,13 +578,13 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien
}
}
- private struct GoalAggregation
+ private struct GoalStatistics
{
public readonly BindingAchievementGoal AchievementGoal;
public int Finished;
public int Count;
- public GoalAggregation(BindingAchievementGoal goal)
+ public GoalStatistics(BindingAchievementGoal goal)
{
AchievementGoal = goal;
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs
index 98c7df89..637a1434 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AvatarPropertyViewModel.cs
@@ -124,13 +124,9 @@ internal class AvatarPropertyViewModel : Abstraction.ViewModel
private Task RefreshByEnkaApiAsync()
{
- if (userService.Current is User user)
+ if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
{
- if (user.SelectedUserGameRole is UserGameRole role)
- {
- UserAndUid userAndUid = new(user.Entity, role);
- return RefreshCoreAsync(userAndUid, RefreshOption.RequestFromEnkaAPI, CancellationToken);
- }
+ return RefreshCoreAsync(userAndUid, RefreshOption.RequestFromEnkaAPI, CancellationToken);
}
return Task.CompletedTask;
@@ -138,13 +134,9 @@ internal class AvatarPropertyViewModel : Abstraction.ViewModel
private Task RefreshByHoyolabGameRecordAsync()
{
- if (userService.Current is User user)
+ if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
{
- if (user.SelectedUserGameRole is UserGameRole role)
- {
- UserAndUid userAndUid = new(user.Entity, role);
- return RefreshCoreAsync(userAndUid, RefreshOption.RequestFromHoyolabGameRecord, CancellationToken);
- }
+ return RefreshCoreAsync(userAndUid, RefreshOption.RequestFromHoyolabGameRecord, CancellationToken);
}
return Task.CompletedTask;
@@ -152,13 +144,9 @@ internal class AvatarPropertyViewModel : Abstraction.ViewModel
private Task RefreshByHoyolabCalculateAsync()
{
- if (userService.Current is User user)
+ if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
{
- if (user.SelectedUserGameRole is UserGameRole role)
- {
- UserAndUid userAndUid = new(user.Entity, role);
- return RefreshCoreAsync(userAndUid, RefreshOption.RequestFromHoyolabCalculate, CancellationToken);
- }
+ return RefreshCoreAsync(userAndUid, RefreshOption.RequestFromHoyolabCalculate, CancellationToken);
}
return Task.CompletedTask;
@@ -169,10 +157,8 @@ internal class AvatarPropertyViewModel : Abstraction.ViewModel
try
{
ValueResult summaryResult;
- ThrowIfViewDisposed();
- using (await DisposeLock.EnterAsync(token).ConfigureAwait(false))
+ using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
- ThrowIfViewDisposed();
ContentDialog dialog = await contentDialogFactory
.CreateForIndeterminateProgressAsync(SH.ViewModelAvatarPropertyFetch)
.ConfigureAwait(false);
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNoteViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNoteViewModel.cs
index 513de5c4..aaadeb3a 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNoteViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/DailyNoteViewModel.cs
@@ -175,7 +175,7 @@ internal class DailyNoteViewModel : Abstraction.ViewModel
{
try
{
- UserAndUids = await userService.GetRoleCollectionAsync().ConfigureAwait(false);
+ UserAndUids = await userService.GetRoleCollectionAsync().ConfigureAwait(true);
}
catch (Core.ExceptionService.UserdataCorruptedException ex)
{
@@ -185,10 +185,8 @@ internal class DailyNoteViewModel : Abstraction.ViewModel
try
{
- ThrowIfViewDisposed();
- using (await DisposeLock.EnterAsync().ConfigureAwait(false))
+ using (await EnterCriticalExecutionAsync().ConfigureAwait(false))
{
- ThrowIfViewDisposed();
await ThreadHelper.SwitchToMainThreadAsync();
refreshSecondsEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteRefreshSeconds, "480");
@@ -203,10 +201,9 @@ internal class DailyNoteViewModel : Abstraction.ViewModel
silentModeEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.DailyNoteSilentWhenPlayingGame, SettingEntryHelper.FalseString);
isSilentWhenPlayingGame = silentModeEntry.GetBoolean();
OnPropertyChanged(nameof(IsSilentWhenPlayingGame));
-
- await ThreadHelper.SwitchToBackgroundAsync();
}
+ await ThreadHelper.SwitchToBackgroundAsync();
ObservableCollection temp = await dailyNoteService.GetDailyNoteEntriesAsync().ConfigureAwait(false);
await ThreadHelper.SwitchToMainThreadAsync();
DailyNoteEntries = temp;
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs
index 577a2a4b..671ff11b 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLogViewModel.cs
@@ -12,6 +12,7 @@ using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.InterChange.GachaLog;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.GachaLog;
+using Snap.Hutao.Service.GachaLog.QueryProvider;
using Snap.Hutao.View.Dialog;
using System.Collections.ObjectModel;
using Windows.Storage.Pickers;
@@ -183,11 +184,11 @@ internal class GachaLogViewModel : Abstraction.ViewModel
private async Task RefreshInternalAsync(RefreshOption option)
{
- IGachaLogUrlProvider? provider = gachaLogService.GetGachaLogUrlProvider(option);
+ IGachaLogQueryProvider? provider = gachaLogService.GetGachaLogQueryProvider(option);
if (provider != null)
{
- (bool isOk, string query) = await provider.GetQueryAsync().ConfigureAwait(false);
+ (bool isOk, GachaLogQuery query) = await provider.GetQueryAsync().ConfigureAwait(false);
if (isOk)
{
@@ -265,31 +266,29 @@ internal class GachaLogViewModel : Abstraction.ViewModel
private async Task ExportToUIGFJsonAsync()
{
- if (SelectedArchive == null)
+ if (SelectedArchive != null)
{
- return;
- }
+ FileSavePicker picker = pickerFactory.GetFileSavePicker();
+ picker.SuggestedStartLocation = PickerLocationId.Desktop;
+ picker.SuggestedFileName = SelectedArchive.Uid;
+ picker.CommitButtonText = SH.FilePickerExportCommit;
+ picker.FileTypeChoices.Add(SH.ViewModelGachaLogExportFileType, ".json".Enumerate().ToList());
- FileSavePicker picker = pickerFactory.GetFileSavePicker();
- picker.SuggestedStartLocation = PickerLocationId.Desktop;
- picker.SuggestedFileName = SelectedArchive.Uid;
- picker.CommitButtonText = SH.FilePickerExportCommit;
- picker.FileTypeChoices.Add(SH.ViewModelGachaLogExportFileType, ".json".Enumerate().ToList());
+ (bool isPickerOk, FilePath file) = await picker.TryPickSaveFileAsync().ConfigureAwait(false);
- (bool isPickerOk, FilePath file) = await picker.TryPickSaveFileAsync().ConfigureAwait(false);
-
- if (isPickerOk)
- {
- UIGF uigf = await gachaLogService.ExportToUIGFAsync(SelectedArchive).ConfigureAwait(false);
- bool isOk = await file.SerializeToJsonAsync(uigf, options).ConfigureAwait(false);
-
- if (isOk)
+ if (isPickerOk)
{
- infoBarService.Success(SH.ViewModelExportSuccessTitle, SH.ViewModelExportSuccessMessage);
- }
- else
- {
- infoBarService.Warning(SH.ViewModelExportWarningTitle, SH.ViewModelExportWarningMessage);
+ UIGF uigf = await gachaLogService.ExportToUIGFAsync(SelectedArchive).ConfigureAwait(false);
+ bool isOk = await file.SerializeToJsonAsync(uigf, options).ConfigureAwait(false);
+
+ if (isOk)
+ {
+ infoBarService.Success(SH.ViewModelExportSuccessTitle, SH.ViewModelExportSuccessMessage);
+ }
+ else
+ {
+ infoBarService.Warning(SH.ViewModelExportWarningTitle, SH.ViewModelExportWarningMessage);
+ }
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaInfoClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaInfoClient.cs
index e3aa3858..bf9bf77d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaInfoClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaInfoClient.cs
@@ -40,8 +40,7 @@ internal class GachaInfoClient
{
string query = config.AsQuery();
- // TODO: fix oversea behavior
- string url = query.Contains("hoyoverse.com")
+ string url = config.IsOversea
? ApiOsEndpoints.GachaInfoGetGachaLog(query)
: ApiEndpoints.GachaInfoGetGachaLog(query);
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs
index 65cb4a3d..9b1a5363 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Service.GachaLog.QueryProvider;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
using Snap.Hutao.Web.Request.QueryString;
@@ -9,13 +10,18 @@ namespace Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
///
/// 祈愿记录请求配置
///
-public struct GachaLogQueryOptions
+internal struct GachaLogQueryOptions
{
///
/// 尺寸
///
public const int Size = 20;
+ ///
+ /// 是否为国际服
+ ///
+ public readonly bool IsOversea;
+
///
/// Keys required:
/// authkey_ver
@@ -36,9 +42,10 @@ public struct GachaLogQueryOptions
/// 原始查询字符串
/// 祈愿类型
/// 终止Id
- public GachaLogQueryOptions(string query, GachaConfigType type, long endId = 0L)
+ public GachaLogQueryOptions(GachaLogQuery query, GachaConfigType type, long endId = 0L)
{
- innerQuery = QueryString.Parse(query);
+ IsOversea = query.IsOversea;
+ innerQuery = QueryString.Parse(query.Query);
innerQuery.Set("lang", "zh-cn");
innerQuery.Set("gacha_type", (int)type);
@@ -47,11 +54,6 @@ public struct GachaLogQueryOptions
EndId = endId;
}
- ///
- /// 是否为国际服
- ///
- public bool IsOversea { get; set; }
-
///
/// 结束Id
/// 控制API返回的分页
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs
index 8071e744..693decbd 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs
@@ -189,30 +189,26 @@ internal class HomaClient
.GetPlayerInfoAsync(userAndUid, token)
.ConfigureAwait(false);
- if (!playerInfoResponse.IsOk())
+ if (playerInfoResponse.IsOk())
{
- return null;
+ Response charactersResponse = await gameRecordClient
+ .GetCharactersAsync(userAndUid, playerInfoResponse.Data, token)
+ .ConfigureAwait(false);
+
+ if (charactersResponse.IsOk())
+ {
+ Response spiralAbyssResponse = await gameRecordClient
+ .GetSpiralAbyssAsync(userAndUid, SpiralAbyssSchedule.Current, token)
+ .ConfigureAwait(false);
+
+ if (spiralAbyssResponse.IsOk())
+ {
+ return new(userAndUid.Uid.Value, charactersResponse.Data.Avatars, spiralAbyssResponse.Data);
+ }
+ }
}
- Response charactersResponse = await gameRecordClient
- .GetCharactersAsync(userAndUid, playerInfoResponse.Data, token)
- .ConfigureAwait(false);
-
- if (!charactersResponse.IsOk())
- {
- return null;
- }
-
- Response spiralAbyssResponse = await gameRecordClient
- .GetSpiralAbyssAsync(userAndUid, SpiralAbyssSchedule.Current, token)
- .ConfigureAwait(false);
-
- if (!spiralAbyssResponse.IsOk())
- {
- return null;
- }
-
- return new(userAndUid.Uid.Value, charactersResponse.Data.Avatars, spiralAbyssResponse.Data);
+ return null;
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient2.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient2.cs
index 755cfd6b..c840469d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient2.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient2.cs
@@ -32,16 +32,21 @@ internal class HomaClient2
/// 任务
public async Task UploadLogAsync(Exception exception)
{
- HutaoLog log = new()
- {
- Id = Core.CoreEnvironment.HutaoDeviceId,
- Time = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
- Info = exception.ToString(),
- };
+ HutaoLog log = BuildFromException(exception);
Response? a = await httpClient
.TryCatchPostAsJsonAsync>(HutaoEndpoints.HutaoLogUpload, log)
.ConfigureAwait(false);
return a?.Data;
}
+
+ private static HutaoLog BuildFromException(Exception exception)
+ {
+ return new()
+ {
+ Id = Core.CoreEnvironment.HutaoDeviceId,
+ Time = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
+ Info = exception.ToString(),
+ };
+ }
}
\ No newline at end of file