diff --git a/src/Starward.Core/GameRecord/GameRecordClient.cs b/src/Starward.Core/GameRecord/GameRecordClient.cs index c53236538..d27eddec9 100644 --- a/src/Starward.Core/GameRecord/GameRecordClient.cs +++ b/src/Starward.Core/GameRecord/GameRecordClient.cs @@ -1,6 +1,7 @@ using Starward.Core.GameRecord.Genshin.SpiralAbyss; using Starward.Core.GameRecord.Genshin.TravelersDiary; using Starward.Core.GameRecord.StarRail.ForgottenHall; +using Starward.Core.GameRecord.StarRail.PureFiction; using Starward.Core.GameRecord.StarRail.SimulatedUniverse; using Starward.Core.GameRecord.StarRail.TrailblazeCalendar; using System.Net; @@ -285,6 +286,16 @@ protected virtual async Task CommonSendAsync(HttpRequestMessage request, Cancell public abstract Task GetForgottenHallInfoAsync(GameRecordRole role, int schedule, CancellationToken cancellationToken = default); + /// + /// 虚构叙事 + /// + /// + /// 1当期,2上期 + /// + /// + public abstract Task GetPureFictionInfoAsync(GameRecordRole role, int schedule, CancellationToken cancellationToken = default); + + /// /// 模拟宇宙 /// diff --git a/src/Starward.Core/GameRecord/GameRecordJsonContext.cs b/src/Starward.Core/GameRecord/GameRecordJsonContext.cs index 8590ad11f..6eb964dc1 100644 --- a/src/Starward.Core/GameRecord/GameRecordJsonContext.cs +++ b/src/Starward.Core/GameRecord/GameRecordJsonContext.cs @@ -1,6 +1,7 @@ using Starward.Core.GameRecord.Genshin.SpiralAbyss; using Starward.Core.GameRecord.Genshin.TravelersDiary; using Starward.Core.GameRecord.StarRail.ForgottenHall; +using Starward.Core.GameRecord.StarRail.PureFiction; using Starward.Core.GameRecord.StarRail.SimulatedUniverse; using Starward.Core.GameRecord.StarRail.TrailblazeCalendar; using System.Text.Json.Serialization; @@ -17,6 +18,7 @@ namespace Starward.Core.GameRecord; [JsonSerializable(typeof(miHoYoApiWrapper))] [JsonSerializable(typeof(miHoYoApiWrapper))] [JsonSerializable(typeof(miHoYoApiWrapper))] +[JsonSerializable(typeof(miHoYoApiWrapper))] [JsonSerializable(typeof(miHoYoApiWrapper))] [JsonSerializable(typeof(miHoYoApiWrapper))] internal partial class GameRecordJsonContext : JsonSerializerContext diff --git a/src/Starward.Core/GameRecord/HoyolabClient.cs b/src/Starward.Core/GameRecord/HoyolabClient.cs index 2a992e13d..10495c786 100644 --- a/src/Starward.Core/GameRecord/HoyolabClient.cs +++ b/src/Starward.Core/GameRecord/HoyolabClient.cs @@ -1,6 +1,7 @@ using Starward.Core.GameRecord.Genshin.SpiralAbyss; using Starward.Core.GameRecord.Genshin.TravelersDiary; using Starward.Core.GameRecord.StarRail.ForgottenHall; +using Starward.Core.GameRecord.StarRail.PureFiction; using Starward.Core.GameRecord.StarRail.SimulatedUniverse; using Starward.Core.GameRecord.StarRail.TrailblazeCalendar; @@ -265,6 +266,29 @@ public override async Task GetForgottenHallInfoAsync(GameReco } + /// + /// 虚构叙事 + /// + /// + /// 1当期,2上期 + /// + /// + public override async Task GetPureFictionInfoAsync(GameRecordRole role, int schedule, CancellationToken cancellationToken = default) + { + var url = $"https://bbs-api-os.hoyolab.com/game_record/app/hkrpg/api/challenge_story?schedule_type={schedule}&server={role.Region}&role_id={role.Uid}&need_all=true"; + var request = new HttpRequestMessage(HttpMethod.Get, url); + request.Headers.Add(Cookie, role.Cookie); + request.Headers.Add(DS, CreateSecret2(url)); + request.Headers.Add(Referer, "https://act.hoyolab.com"); + request.Headers.Add(x_rpc_app_version, AppVersion); + request.Headers.Add(x_rpc_client_type, "5"); + request.Headers.Add(X_Request_With, com_mihoyo_hoyolab); + var data = await CommonSendAsync(request, cancellationToken); + data.Uid = role.Uid; + return data; + } + + /// /// 模拟宇宙 /// diff --git a/src/Starward.Core/GameRecord/HyperionClient.cs b/src/Starward.Core/GameRecord/HyperionClient.cs index 23ad726a5..fba7f1e33 100644 --- a/src/Starward.Core/GameRecord/HyperionClient.cs +++ b/src/Starward.Core/GameRecord/HyperionClient.cs @@ -1,6 +1,7 @@ using Starward.Core.GameRecord.Genshin.SpiralAbyss; using Starward.Core.GameRecord.Genshin.TravelersDiary; using Starward.Core.GameRecord.StarRail.ForgottenHall; +using Starward.Core.GameRecord.StarRail.PureFiction; using Starward.Core.GameRecord.StarRail.SimulatedUniverse; using Starward.Core.GameRecord.StarRail.TrailblazeCalendar; using System.Text.RegularExpressions; @@ -273,6 +274,31 @@ public override async Task GetForgottenHallInfoAsync(GameReco } + /// + /// 虚构叙事 + /// + /// + /// 1当期,2上期 + /// + /// + public override async Task GetPureFictionInfoAsync(GameRecordRole role, int schedule, CancellationToken cancellationToken = default) + { + var url = $"https://api-takumi-record.mihoyo.com/game_record/app/hkrpg/api/challenge_story?schedule_type={schedule}&server={role.Region}&role_id={role.Uid}&need_all=true"; + var request = new HttpRequestMessage(HttpMethod.Get, url); + request.Headers.Add(Cookie, role.Cookie); + request.Headers.Add(DS, CreateSecret2(url)); + request.Headers.Add(Referer, "https://webstatic.mihoyo.com/"); + request.Headers.Add(x_rpc_app_version, AppVersion); + request.Headers.Add(x_rpc_device_id, Regex.Match(role.Cookie ?? "", @"_MHYUUID=([^;]+)").Groups[1].Value); + request.Headers.Add(x_rpc_device_fp, Regex.Match(role.Cookie ?? "", @"DEVICEFP=([^;]+)").Groups[1].Value); + request.Headers.Add(x_rpc_client_type, "5"); + request.Headers.Add(X_Request_With, com_mihoyo_hyperion); + var data = await CommonSendAsync(request, cancellationToken); + data.Uid = role.Uid; + return data; + } + + /// /// 模拟宇宙 /// diff --git a/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallFloorDetail.cs b/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallFloorDetail.cs index 98feac298..7c4965142 100644 --- a/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallFloorDetail.cs +++ b/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallFloorDetail.cs @@ -28,8 +28,9 @@ public class ForgottenHallFloorDetail /// /// 快速通关 /// + [JsonInclude] [JsonPropertyName("is_fast")] - private bool _isFast { get; set; } + internal bool _isFast { get; set; } /// /// 快速通关 @@ -43,11 +44,7 @@ public bool IsFast { return true; } - else if (Node1?.ChallengeTime == Node2?.ChallengeTime) - { - return true; - } - else if (Node1?.Avatars?.Count == 0 || Node2?.Avatars?.Count == 0) + else if (Node1?.Avatars?.Count == 0 && Node2?.Avatars?.Count == 0) { return true; } diff --git a/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallInfo.cs b/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallInfo.cs index c080a0089..8b9bfddda 100644 --- a/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallInfo.cs +++ b/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallInfo.cs @@ -33,12 +33,28 @@ public class ForgottenHallInfo [JsonPropertyName("has_data")] public bool HasData { get; set; } - [JsonPropertyName("max_floor_detail")] - public object MaxFloorDetail { get; set; } - [JsonPropertyName("all_floor_detail")] public List AllFloorDetail { get; set; } + [JsonPropertyName("groups")] + public List? Metas { get; set; } + + [JsonIgnore] + public string? Name + { + get + { + if (Metas?.FirstOrDefault(x => x.ScheduleId == this.ScheduleId) is ForgottenHallMeta meta) + { + return meta.Name; + } + else + { + return null; + } + } + } + [JsonExtensionData] public Dictionary? ExtensionData { get; set; } diff --git a/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallMeta.cs b/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallMeta.cs new file mode 100644 index 000000000..ba7b5bed1 --- /dev/null +++ b/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallMeta.cs @@ -0,0 +1,31 @@ +using System.Text.Json.Serialization; + +namespace Starward.Core.GameRecord.StarRail.ForgottenHall; + + +public class ForgottenHallMeta +{ + + [JsonPropertyName("schedule_id")] + public int ScheduleId { get; set; } + + [JsonPropertyName("begin_time")] + [JsonConverter(typeof(ForgottenHallTimeJsonConverter))] + public DateTime BeginTime { get; set; } + + [JsonPropertyName("end_time")] + [JsonConverter(typeof(ForgottenHallTimeJsonConverter))] + public DateTime EndTime { get; set; } + + [JsonPropertyName("status")] + public string Status { get; set; } + + [JsonPropertyName("name_mi18n")] + public string Name { get; set; } + + + [JsonExtensionData] + public Dictionary? ExtensionData { get; set; } + +} + diff --git a/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallTime.cs b/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallTime.cs index fc468b8d2..2ff6559f0 100644 --- a/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallTime.cs +++ b/src/Starward.Core/GameRecord/StarRail/ForgottenHall/ForgottenHallTime.cs @@ -33,21 +33,24 @@ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, Jso } else { - return new DateTime(); + return DateTime.MinValue; } } public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) { - var time = new ForgottenHallTime + if (value != DateTime.MinValue) { - Year = value.Year, - Month = value.Month, - Day = value.Day, - Hour = value.Hour, - Minute = value.Minute, - }; - writer.WriteRawValue(JsonSerializer.Serialize(time, typeof(ForgottenHallTime), GameRecordJsonContext.Default)); + var time = new ForgottenHallTime + { + Year = value.Year, + Month = value.Month, + Day = value.Day, + Hour = value.Hour, + Minute = value.Minute, + }; + writer.WriteRawValue(JsonSerializer.Serialize(time, typeof(ForgottenHallTime), GameRecordJsonContext.Default)); + } } } diff --git a/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionAvatar.cs b/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionAvatar.cs new file mode 100644 index 000000000..8651308a4 --- /dev/null +++ b/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionAvatar.cs @@ -0,0 +1,33 @@ +using System.Text.Json.Serialization; + +namespace Starward.Core.GameRecord.StarRail.PureFiction; + +public class PureFictionAvatar +{ + + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("level")] + public int Level { get; set; } + + [JsonPropertyName("icon")] + public string Icon { get; set; } + + [JsonPropertyName("rarity")] + public int Rarity { get; set; } + + [JsonPropertyName("element")] + public string Element { get; set; } + + [JsonPropertyName("rank")] + public int Rank { get; set; } + + + [JsonExtensionData] + public Dictionary? ExtensionData { get; set; } + +} + + + diff --git a/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionBuff.cs b/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionBuff.cs new file mode 100644 index 000000000..1f7005f5c --- /dev/null +++ b/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionBuff.cs @@ -0,0 +1,27 @@ +using System.Text.Json.Serialization; + +namespace Starward.Core.GameRecord.StarRail.PureFiction; + +public class PureFictionBuff +{ + + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("name_mi18n")] + public string Name { get; set; } + + [JsonPropertyName("desc_mi18n")] + public string Desc { get; set; } + + [JsonPropertyName("icon")] + public string Icon { get; set; } + + + [JsonExtensionData] + public Dictionary? ExtensionData { get; set; } + +} + + + diff --git a/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionFloorDetail.cs b/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionFloorDetail.cs new file mode 100644 index 000000000..27f210836 --- /dev/null +++ b/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionFloorDetail.cs @@ -0,0 +1,39 @@ +using System.Text.Json.Serialization; + +namespace Starward.Core.GameRecord.StarRail.PureFiction; + +public class PureFictionFloorDetail +{ + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("round_num")] + public int RoundNum { get; set; } + + [JsonPropertyName("star_num")] + public int StarNum { get; set; } + + [JsonPropertyName("node_1")] + public PureFictionNode Node1 { get; set; } + + [JsonPropertyName("node_2")] + public PureFictionNode Node2 { get; set; } + + [JsonPropertyName("maze_id")] + public int MazeId { get; set; } + + [JsonPropertyName("is_fast")] + public bool IsFast { get; set; } + + [JsonIgnore] + public int TotalScore => Node1.Score + Node2.Score; + + + [JsonExtensionData] + public Dictionary? ExtensionData { get; set; } + +} + + + diff --git a/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionInfo.cs b/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionInfo.cs new file mode 100644 index 000000000..a5a918535 --- /dev/null +++ b/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionInfo.cs @@ -0,0 +1,79 @@ +using Starward.Core.GameRecord.StarRail.ForgottenHall; +using System.Text.Json.Serialization; + +namespace Starward.Core.GameRecord.StarRail.PureFiction; + +public class PureFictionInfo : IJsonOnDeserialized +{ + + [JsonPropertyName("uid")] + [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)] + public long Uid { get; set; } + + [JsonPropertyName("schedule_id")] + public int ScheduleId { get; set; } + + [JsonPropertyName("begin_time")] + [JsonConverter(typeof(ForgottenHallTimeJsonConverter))] + public DateTime BeginTime { get; set; } + + [JsonPropertyName("end_time")] + [JsonConverter(typeof(ForgottenHallTimeJsonConverter))] + public DateTime EndTime { get; set; } + + [JsonPropertyName("star_num")] + public int StarNum { get; set; } + + [JsonPropertyName("max_floor")] + public string MaxFloor { get; set; } + + [JsonPropertyName("battle_num")] + public int BattleNum { get; set; } + + [JsonPropertyName("has_data")] + public bool HasData { get; set; } + + [JsonPropertyName("all_floor_detail")] + public List AllFloorDetail { get; set; } + + [JsonPropertyName("groups")] + public List? Metas { get; set; } + + [JsonIgnore] + public string? Name + { + get + { + if (Metas?.FirstOrDefault(x => x.ScheduleId == this.ScheduleId) is PureFictionMeta meta) + { + return meta.Name; + } + else + { + return null; + } + } + } + + + [JsonExtensionData] + public Dictionary? ExtensionData { get; set; } + + + public void OnDeserialized() + { + if (ScheduleId == 0 && Metas?.Count == 1) + { + ScheduleId = Metas[0].ScheduleId; + } + if (BeginTime == DateTime.MinValue && Metas?.Count == 1) + { + BeginTime = Metas[0].BeginTime; + } + if (EndTime == DateTime.MinValue && Metas?.Count == 1) + { + EndTime = Metas[0].EndTime; + } + } + +} diff --git a/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionMeta.cs b/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionMeta.cs new file mode 100644 index 000000000..1f192c74f --- /dev/null +++ b/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionMeta.cs @@ -0,0 +1,33 @@ +using Starward.Core.GameRecord.StarRail.ForgottenHall; +using System.Text.Json.Serialization; + +namespace Starward.Core.GameRecord.StarRail.PureFiction; + +public class PureFictionMeta +{ + + [JsonPropertyName("schedule_id")] + public int ScheduleId { get; set; } + + [JsonPropertyName("begin_time")] + [JsonConverter(typeof(ForgottenHallTimeJsonConverter))] + public DateTime BeginTime { get; set; } + + [JsonPropertyName("end_time")] + [JsonConverter(typeof(ForgottenHallTimeJsonConverter))] + public DateTime EndTime { get; set; } + + [JsonPropertyName("status")] + public string Status { get; set; } + + [JsonPropertyName("name_mi18n")] + public string Name { get; set; } + + + [JsonExtensionData] + public Dictionary? ExtensionData { get; set; } + +} + + + diff --git a/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionNode.cs b/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionNode.cs new file mode 100644 index 000000000..493ede313 --- /dev/null +++ b/src/Starward.Core/GameRecord/StarRail/PureFiction/PureFictionNode.cs @@ -0,0 +1,30 @@ +using Starward.Core.GameRecord.StarRail.ForgottenHall; +using System.Text.Json.Serialization; + +namespace Starward.Core.GameRecord.StarRail.PureFiction; + +public class PureFictionNode +{ + + [JsonPropertyName("challenge_time")] + [JsonConverter(typeof(ForgottenHallTimeJsonConverter))] + public DateTime ChallengeTime { get; set; } + + [JsonPropertyName("avatars")] + public List Avatars { get; set; } + + [JsonPropertyName("buff")] + public PureFictionBuff Buff { get; set; } + + [JsonPropertyName("score")] + [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)] + public int Score { get; set; } + + + [JsonExtensionData] + public Dictionary? ExtensionData { get; set; } + +} + + + diff --git a/src/Starward.Language/Lang.Designer.cs b/src/Starward.Language/Lang.Designer.cs index d724f7cdd..a2dc3b81b 100644 --- a/src/Starward.Language/Lang.Designer.cs +++ b/src/Starward.Language/Lang.Designer.cs @@ -853,6 +853,15 @@ public static string ForgottenHallPage_Period { } } + /// + /// 查找类似 Quick Cleared 的本地化字符串。 + /// + public static string ForgottenHallPage_QuickCleared { + get { + return ResourceManager.GetString("ForgottenHallPage_QuickCleared", resourceCulture); + } + } + /// /// 查找类似 Team Setup 的本地化字符串。 /// @@ -1521,6 +1530,15 @@ public static string HoyolabToolboxPage_LogIn { } } + /// + /// 查找类似 Pure Fiction 的本地化字符串。 + /// + public static string HoyolabToolboxPage_PureFiction { + get { + return ResourceManager.GetString("HoyolabToolboxPage_PureFiction", resourceCulture); + } + } + /// /// 查找类似 Refresh Info 的本地化字符串。 /// diff --git a/src/Starward.Language/Lang.resx b/src/Starward.Language/Lang.resx index fbb268880..63f3898cb 100644 --- a/src/Starward.Language/Lang.resx +++ b/src/Starward.Language/Lang.resx @@ -1262,4 +1262,10 @@ Do you accept the risk and continue to use it? After Starting Game + + Quick Cleared + + + Pure Fiction + \ No newline at end of file diff --git a/src/Starward/Messages/GameRecordRoleChangedMessage.cs b/src/Starward/Messages/GameRecordRoleChangedMessage.cs new file mode 100644 index 000000000..8caa30d13 --- /dev/null +++ b/src/Starward/Messages/GameRecordRoleChangedMessage.cs @@ -0,0 +1,5 @@ +using Starward.Core.GameRecord; + +namespace Starward.Messages; + +public record GameRecordRoleChangedMessage(GameRecordRole? GameRole); diff --git a/src/Starward/Messages/VerifyAccountMessage.cs b/src/Starward/Messages/VerifyAccountMessage.cs new file mode 100644 index 000000000..5aa111ffb --- /dev/null +++ b/src/Starward/Messages/VerifyAccountMessage.cs @@ -0,0 +1,5 @@ +using Starward.Core.GameRecord; + +namespace Starward.Messages; + +public record VerifyAccountMessage(GameRecordRole GameRole, string TargetUrl); diff --git a/src/Starward/Pages/HoyolabToolbox/ForgottenHallPage.xaml b/src/Starward/Pages/HoyolabToolbox/ForgottenHallPage.xaml index 760f08db8..a16ee69ee 100644 --- a/src/Starward/Pages/HoyolabToolbox/ForgottenHallPage.xaml +++ b/src/Starward/Pages/HoyolabToolbox/ForgottenHallPage.xaml @@ -126,6 +126,7 @@ + @@ -253,7 +254,8 @@ + FontSize="12" + Visibility="{x:Bind IsFast, Converter={StaticResource BoolToVisibilityReversedConverter}}"> @@ -263,7 +265,8 @@ + ItemsSource="{x:Bind Node1.Avatars}" + Visibility="{x:Bind IsFast, Converter={StaticResource BoolToVisibilityReversedConverter}}"> @@ -294,7 +297,8 @@ + FontSize="12" + Visibility="{x:Bind IsFast, Converter={StaticResource BoolToVisibilityReversedConverter}}"> @@ -305,7 +309,8 @@ Grid.ColumnSpan="2" Margin="0,0,0,4" HorizontalAlignment="Center" - ItemsSource="{x:Bind Node2.Avatars}"> + ItemsSource="{x:Bind Node2.Avatars}" + Visibility="{x:Bind IsFast, Converter={StaticResource BoolToVisibilityReversedConverter}}"> @@ -333,6 +338,20 @@ + + + + + + diff --git a/src/Starward/Pages/HoyolabToolbox/ForgottenHallPage.xaml.cs b/src/Starward/Pages/HoyolabToolbox/ForgottenHallPage.xaml.cs index 193dd5ef0..ac80a2b9f 100644 --- a/src/Starward/Pages/HoyolabToolbox/ForgottenHallPage.xaml.cs +++ b/src/Starward/Pages/HoyolabToolbox/ForgottenHallPage.xaml.cs @@ -1,5 +1,6 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -8,6 +9,7 @@ using Starward.Core.GameRecord; using Starward.Core.GameRecord.StarRail.ForgottenHall; using Starward.Helpers; +using Starward.Messages; using Starward.Services; using System; using System.Collections.Generic; @@ -79,7 +81,7 @@ private void InitializeForgottenHallData() { CurrentForgottenHall = null; var list = _gameRecordService.GetForgottenHallInfoList(gameRole); - if (list.Any()) + if (list.Count != 0) { ForgottenHallList = list; ListView_ForgottenHall.SelectedIndex = 0; @@ -118,11 +120,7 @@ private async Task RefreshDataAsync() { NotificationBehavior.Instance.ShowWithButton(InfoBarSeverity.Warning, Lang.Common_AccountError, ex.Message, Lang.HoyolabToolboxPage_VerifyAccount, () => { - _gameRecordService.InvokeNavigateChanged(typeof(HyperionWebBridgePage), new HyperionWebBridgePage.PageParameter - { - GameRole = gameRole!, - TargetUrl = "https://webstatic.mihoyo.com/app/community-game-records/rpg/index.html?bbs_presentation_style=fullscreen#/rpg/oblivious?role_id={role_id}&server={server}", - }); + WeakReferenceMessenger.Default.Send(new VerifyAccountMessage(gameRole!, "https://webstatic.mihoyo.com/app/community-game-records/rpg/index.html?bbs_presentation_style=fullscreen#/rpg/oblivious?role_id={role_id}&server={server}&isPrev=&type=challenge")); }); } else diff --git a/src/Starward/Pages/HoyolabToolbox/HoyolabToolboxPage.xaml b/src/Starward/Pages/HoyolabToolbox/HoyolabToolboxPage.xaml index e36d66ce4..a0b701227 100644 --- a/src/Starward/Pages/HoyolabToolbox/HoyolabToolboxPage.xaml +++ b/src/Starward/Pages/HoyolabToolbox/HoyolabToolboxPage.xaml @@ -331,6 +331,23 @@ + + + + + + + + + + + + + (this, (r, m) => + { + LoadGameRoles(m.GameRole); + }); + WeakReferenceMessenger.Default.Register(this, (r, m) => + { + NavigateTo(typeof(HyperionWebBridgePage), new HyperionWebBridgePage.PageParameter(m.GameRole, m.TargetUrl)); + }); await Task.Delay(16); NavigateTo(typeof(BlankPage)); await CheckAgreementAsync(); @@ -88,8 +96,7 @@ private async void Page_Loaded(object sender, RoutedEventArgs e) private void Page_Unloaded(object sender, RoutedEventArgs e) { - _gameRecordService.GameRecordRoleChanged -= _gameRecordService_GameRecordRoleChanged; - _gameRecordService.NavigateChanged -= _gameRecordService_NavigateChanged; + WeakReferenceMessenger.Default.UnregisterAll(this); } @@ -191,6 +198,7 @@ private void InitializeNavigationViewItemVisibility() { NavigationViewItem_SimulatedUniverse.Visibility = Visibility.Visible; NavigationViewItem_ForgottenHall.Visibility = Visibility.Visible; + NavigationViewItem_PureFiction.Visibility = Visibility.Visible; NavigationViewItem_TrailblazeMonthlyCalendar.Visibility = Visibility.Visible; } } @@ -246,12 +254,6 @@ private void LoadGameRoles(GameRecordRole? role = null) - private void _gameRecordService_GameRecordRoleChanged(object? sender, GameRecordRole? e) - { - LoadGameRoles(e); - } - - [RelayCommand] private void WebLogin() @@ -431,6 +433,7 @@ private void NavigationView_Toolbox_ItemInvoked(NavigationView sender, Navigatio nameof(TravelersDiaryPage) => typeof(TravelersDiaryPage), nameof(SimulatedUniversePage) => typeof(SimulatedUniversePage), nameof(ForgottenHallPage) => typeof(ForgottenHallPage), + nameof(PureFictionPage) => typeof(PureFictionPage), nameof(TrailblazeCalendarPage) => typeof(TrailblazeCalendarPage), _ => null, }; @@ -441,12 +444,6 @@ private void NavigationView_Toolbox_ItemInvoked(NavigationView sender, Navigatio } - private void _gameRecordService_NavigateChanged(object? sender, (Type Page, object? Parameter) e) - { - NavigateTo(e.Page, e.Parameter); - } - - #endregion diff --git a/src/Starward/Pages/HoyolabToolbox/HyperionWebBridgePage.xaml.cs b/src/Starward/Pages/HoyolabToolbox/HyperionWebBridgePage.xaml.cs index 4bc108906..bc6cbc7af 100644 --- a/src/Starward/Pages/HoyolabToolbox/HyperionWebBridgePage.xaml.cs +++ b/src/Starward/Pages/HoyolabToolbox/HyperionWebBridgePage.xaml.cs @@ -40,11 +40,11 @@ protected override void OnNavigatedTo(NavigationEventArgs e) - public class PageParameter + public class PageParameter(GameRecordRole gameRole, string targetUrl) { - public GameRecordRole GameRole { get; set; } + public GameRecordRole GameRole { get; set; } = gameRole; - public string TargetUrl { get; set; } + public string TargetUrl { get; set; } = targetUrl; } diff --git a/src/Starward/Pages/HoyolabToolbox/LoginPage.xaml.cs b/src/Starward/Pages/HoyolabToolbox/LoginPage.xaml.cs index f98c1ada0..042618243 100644 --- a/src/Starward/Pages/HoyolabToolbox/LoginPage.xaml.cs +++ b/src/Starward/Pages/HoyolabToolbox/LoginPage.xaml.cs @@ -1,10 +1,12 @@ -using Microsoft.Extensions.Logging; +using CommunityToolkit.Mvvm.Messaging; +using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls.Primitives; using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Navigation; using Microsoft.Web.WebView2.Core; using Starward.Core; +using Starward.Messages; using Starward.Services; using System; using System.Linq; @@ -255,7 +257,7 @@ private async void Button_Finish_Click(object sender, RoutedEventArgs e) var cookieString = string.Join(";", cookies.Select(x => $"{x.Name}={x.Value}")); var user = await _gameRecordService.AddRecordUserAsync(cookieString); var roles = await _gameRecordService.AddGameRolesAsync(cookieString); - _gameRecordService.InvokeGameRecordRoleChanged(roles.FirstOrDefault(x => x.GameBiz == gameBiz.ToString())); + WeakReferenceMessenger.Default.Send(new GameRecordRoleChangedMessage(roles.FirstOrDefault(x => x.GameBiz == gameBiz.ToString()))); TextBlock_Tip.Text = string.Format(Lang.LoginPage_AlreadyAddedGameRoles, roles.Count, string.Join("\r\n", roles.Select(x => $"{x.Nickname} {x.Uid}"))); } catch (miHoYoApiException ex) diff --git a/src/Starward/Pages/HoyolabToolbox/PureFictionPage.xaml b/src/Starward/Pages/HoyolabToolbox/PureFictionPage.xaml new file mode 100644 index 000000000..93c91ac5c --- /dev/null +++ b/src/Starward/Pages/HoyolabToolbox/PureFictionPage.xaml @@ -0,0 +1,391 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Starward/Pages/HoyolabToolbox/PureFictionPage.xaml.cs b/src/Starward/Pages/HoyolabToolbox/PureFictionPage.xaml.cs new file mode 100644 index 000000000..28a0a1f3e --- /dev/null +++ b/src/Starward/Pages/HoyolabToolbox/PureFictionPage.xaml.cs @@ -0,0 +1,169 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using Microsoft.Extensions.Logging; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Navigation; +using Starward.Core; +using Starward.Core.GameRecord; +using Starward.Core.GameRecord.StarRail.PureFiction; +using Starward.Helpers; +using Starward.Messages; +using Starward.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace Starward.Pages.HoyolabToolbox; + +/// +/// An empty page that can be used on its own or navigated to within a Frame. +/// +[INotifyPropertyChanged] +public sealed partial class PureFictionPage : PageBase +{ + + + private readonly ILogger _logger = AppConfig.GetLogger(); + + private readonly GameRecordService _gameRecordService = AppConfig.GetService(); + + + + public PureFictionPage() + { + this.InitializeComponent(); + } + + + + private GameRecordRole gameRole; + + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + base.OnNavigatedTo(e); + if (e.Parameter is GameRecordRole role) + { + gameRole = role; + } + } + + + + private async void Page_Loaded(object sender, RoutedEventArgs e) + { + await Task.Delay(16); + InitializePureFictionInfoData(); + } + + + + + [ObservableProperty] + private List pureFictionList; + + + [ObservableProperty] + private PureFictionInfo? currentPureFiction; + + + + private void InitializePureFictionInfoData() + { + try + { + CurrentPureFiction = null; + var list = _gameRecordService.GetPureFictionInfoList(gameRole); + if (list.Any()) + { + PureFictionList = list; + ListView_ForgottenHall.SelectedIndex = 0; + } + else + { + Image_Emoji.Visibility = Visibility.Visible; + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Init pure fiction data ({gameBiz}, {uid}).", gameRole?.GameBiz, gameRole?.Uid); + } + } + + + + + [RelayCommand] + private async Task RefreshDataAsync() + { + try + { + if (gameRole is null) + { + return; + } + await _gameRecordService.RefreshPureFictionInfoAsync(gameRole, 1); + await _gameRecordService.RefreshPureFictionInfoAsync(gameRole, 2); + InitializePureFictionInfoData(); + } + catch (miHoYoApiException ex) + { + _logger.LogError(ex, "Refresh pure fiction data ({gameBiz}, {uid}).", gameRole?.GameBiz, gameRole?.Uid); + if (ex.ReturnCode is 1034 or 5003) + { + NotificationBehavior.Instance.ShowWithButton(InfoBarSeverity.Warning, Lang.Common_AccountError, ex.Message, Lang.HoyolabToolboxPage_VerifyAccount, () => + { + WeakReferenceMessenger.Default.Send(new VerifyAccountMessage(gameRole!, "https://webstatic.mihoyo.com/app/community-game-records/rpg/index.html?bbs_presentation_style=fullscreen#/rpg/oblivious?role_id={role_id}&server={server}&isPrev=&type=story")); + }); + } + else + { + NotificationBehavior.Instance.Warning(Lang.Common_AccountError, ex.Message); + } + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Refresh pure fiction data ({gameBiz}, {uid}).", gameRole?.GameBiz, gameRole?.Uid); + NotificationBehavior.Instance.Warning(Lang.Common_NetworkError, ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "Refresh pure fiction data ({gameBiz}, {uid}).", gameRole?.GameBiz, gameRole?.Uid); + NotificationBehavior.Instance.Error(ex); + } + } + + + + private void ListView_ForgottenHall_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + try + { + if (e.AddedItems.FirstOrDefault() is PureFictionInfo info) + { + CurrentPureFiction = _gameRecordService.GetPureFictionInfo(gameRole, info.ScheduleId); + Image_Emoji.Visibility = (CurrentPureFiction?.HasData ?? false) ? Visibility.Collapsed : Visibility.Visible; + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Selection changed ({gameBiz}, {uid}).", gameRole?.GameBiz, gameRole?.Uid); + } + } + + private void TextBlock_Deepest_IsTextTrimmedChanged(TextBlock sender, IsTextTrimmedChangedEventArgs args) + { + TextBlock_Deepest.SetValue(Grid.ColumnSpanProperty, 2); + TextBlock_Battles.SetValue(Grid.RowProperty, 1); + TextBlock_Battles.SetValue(Grid.ColumnProperty, 1); + TextBlock_Battles.SetValue(Grid.ColumnSpanProperty, 2); + } + +} diff --git a/src/Starward/Pages/HoyolabToolbox/SimulatedUniversePage.xaml.cs b/src/Starward/Pages/HoyolabToolbox/SimulatedUniversePage.xaml.cs index a1c740772..400c4e923 100644 --- a/src/Starward/Pages/HoyolabToolbox/SimulatedUniversePage.xaml.cs +++ b/src/Starward/Pages/HoyolabToolbox/SimulatedUniversePage.xaml.cs @@ -1,5 +1,6 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -8,6 +9,7 @@ using Starward.Core.GameRecord; using Starward.Core.GameRecord.StarRail.SimulatedUniverse; using Starward.Helpers; +using Starward.Messages; using Starward.Services; using System; using System.Collections.Generic; @@ -142,11 +144,7 @@ private async Task GetSimulatedUniverseInfoBasicAsync(bool detail = false) { NotificationBehavior.Instance.ShowWithButton(InfoBarSeverity.Warning, Lang.Common_AccountError, ex.Message, Lang.HoyolabToolboxPage_VerifyAccount, () => { - _gameRecordService.InvokeNavigateChanged(typeof(HyperionWebBridgePage), new HyperionWebBridgePage.PageParameter - { - GameRole = gameRole!, - TargetUrl = "https://webstatic.mihoyo.com/app/community-game-records/rpg/index.html?bbs_presentation_style=fullscreen#/rpg/rogue?role_id={role_id}&server={server}", - }); + WeakReferenceMessenger.Default.Send(new VerifyAccountMessage(gameRole!, "https://webstatic.mihoyo.com/app/community-game-records/rpg/index.html?bbs_presentation_style=fullscreen#/rpg/rogue?role_id={role_id}&server={server}")); }); } else diff --git a/src/Starward/Pages/HoyolabToolbox/SpiralAbyssPage.xaml.cs b/src/Starward/Pages/HoyolabToolbox/SpiralAbyssPage.xaml.cs index f1ad0002f..83b9e8b88 100644 --- a/src/Starward/Pages/HoyolabToolbox/SpiralAbyssPage.xaml.cs +++ b/src/Starward/Pages/HoyolabToolbox/SpiralAbyssPage.xaml.cs @@ -1,5 +1,6 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -8,6 +9,7 @@ using Starward.Core.GameRecord; using Starward.Core.GameRecord.Genshin.SpiralAbyss; using Starward.Helpers; +using Starward.Messages; using Starward.Services; using System; using System.Collections.Generic; @@ -127,11 +129,7 @@ private async Task RefreshDataAsync() { NotificationBehavior.Instance.ShowWithButton(InfoBarSeverity.Warning, Lang.Common_AccountError, ex.Message, Lang.HoyolabToolboxPage_VerifyAccount, () => { - _gameRecordService.InvokeNavigateChanged(typeof(HyperionWebBridgePage), new HyperionWebBridgePage.PageParameter - { - GameRole = gameRole!, - TargetUrl = "https://webstatic.mihoyo.com/app/community-game-records/index.html?bbs_presentation_style=fullscreen#/ys/deep?role_id={role_id}&server={server}", - }); + WeakReferenceMessenger.Default.Send(new VerifyAccountMessage(gameRole!, "https://webstatic.mihoyo.com/app/community-game-records/index.html?bbs_presentation_style=fullscreen#/ys/deep?role_id={role_id}&server={server}")); }); } else diff --git a/src/Starward/Services/DatabaseService.cs b/src/Starward/Services/DatabaseService.cs index b2533352e..c3c89c298 100644 --- a/src/Starward/Services/DatabaseService.cs +++ b/src/Starward/Services/DatabaseService.cs @@ -209,7 +209,7 @@ public void SetValue(string key, T value, DateTime? time = null) #region Database Structure - private static readonly List DatabaseSqls = new() { Sql_v1, Sql_v2, Sql_v3, Sql_v4, Sql_v5, Sql_v6, Sql_v7 }; + private static readonly List DatabaseSqls = [Sql_v1, Sql_v2, Sql_v3, Sql_v4, Sql_v5, Sql_v6, Sql_v7, Sql_v8]; private const string Sql_v1 = """ @@ -565,6 +565,28 @@ EquipmentRarity INTEGER NOT NULL COMMIT TRANSACTION; """; + private const string Sql_v8 = """ + BEGIN TRANSACTION; + + CREATE TABLE IF NOT EXISTS PureFictionInfo + ( + Uid INTEGER NOT NULL, + ScheduleId INTEGER NOT NULL, + BeginTime TEXT NOT NULL, + EndTime TEXT NOT NULL, + StarNum INTEGER NOT NULL, + MaxFloor TEXT, + BattleNum INTEGER NOT NULL, + HasData INTEGER NOT NULL, + Value TEXT NOT NULL, + PRIMARY KEY (Uid, ScheduleId) + ); + CREATE INDEX IF NOT EXISTS IX_PureFictionInfo_ScheduleId ON PureFictionInfo (ScheduleId); + + PRAGMA USER_VERSION = 8; + COMMIT TRANSACTION; + """; + #endregion diff --git a/src/Starward/Services/GameRecordService.cs b/src/Starward/Services/GameRecordService.cs index e4f6fcded..3d898b82f 100644 --- a/src/Starward/Services/GameRecordService.cs +++ b/src/Starward/Services/GameRecordService.cs @@ -5,6 +5,7 @@ using Starward.Core.GameRecord.Genshin.SpiralAbyss; using Starward.Core.GameRecord.Genshin.TravelersDiary; using Starward.Core.GameRecord.StarRail.ForgottenHall; +using Starward.Core.GameRecord.StarRail.PureFiction; using Starward.Core.GameRecord.StarRail.SimulatedUniverse; using Starward.Core.GameRecord.StarRail.TrailblazeCalendar; using System; @@ -31,11 +32,6 @@ internal class GameRecordService private GameRecordClient _gameRecordClient; - public event EventHandler GameRecordRoleChanged; - - public event EventHandler<(Type Page, object? Parameter)> NavigateChanged; - - public string Language { get => _hoyolabClient.Language; set => _hoyolabClient.Language = value; } @@ -71,17 +67,6 @@ public GameRecordService(ILogger logger, DatabaseService data - public void InvokeGameRecordRoleChanged(GameRecordRole? role) - { - GameRecordRoleChanged?.Invoke(this, role); - } - - public void InvokeNavigateChanged(Type Page, object? Parameter = null) - { - NavigateChanged?.Invoke(this, (Page, Parameter)); - } - - #region Game Role @@ -502,6 +487,74 @@ public List GetForgottenHallInfoList(GameRecordRole role) + #region Pure Fiction + + + + public async Task RefreshPureFictionInfoAsync(GameRecordRole role, int schedule, CancellationToken cancellationToken = default) + { + var info = await _gameRecordClient.GetPureFictionInfoAsync(role, schedule); + if (info.ScheduleId == 0) + { + return info; + } + var obj = new + { + info.Uid, + info.ScheduleId, + info.BeginTime, + info.EndTime, + info.StarNum, + info.MaxFloor, + info.BattleNum, + info.HasData, + Value = JsonSerializer.Serialize(info, AppConfig.JsonSerializerOptions), + }; + using var dapper = _databaseService.CreateConnection(); + dapper.Execute(""" + INSERT OR REPLACE INTO PureFictionInfo (Uid, ScheduleId, BeginTime, EndTime, StarNum, MaxFloor, BattleNum, HasData, Value) + VALUES (@Uid, @ScheduleId, @BeginTime, @EndTime, @StarNum, @MaxFloor, @BattleNum, @HasData, @Value); + """, obj); + return info; + } + + + + public List GetPureFictionInfoList(GameRecordRole role) + { + if (role is null) + { + return new List(); + } + using var dapper = _databaseService.CreateConnection(); + var list = dapper.Query(""" + SELECT Uid, ScheduleId, BeginTime, EndTime, StarNum, MaxFloor, BattleNum, HasData FROM PureFictionInfo WHERE Uid = @Uid ORDER BY ScheduleId DESC; + """, new { role.Uid }); + return list.ToList(); + } + + + + public PureFictionInfo? GetPureFictionInfo(GameRecordRole role, int scheduleId) + { + using var dapper = _databaseService.CreateConnection(); + var value = dapper.QueryFirstOrDefault(""" + SELECT Value FROM PureFictionInfo WHERE Uid = @Uid And ScheduleId = @scheduleId LIMIT 1; + """, new { role.Uid, scheduleId }); + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + return JsonSerializer.Deserialize(value); + } + + + + #endregion + + + + #region Trailblaze Calendar