fix(mypage): populate user_item_list from viewer.Items
MyPageTask.Parse (Wizard/MyPageTask.cs:155-163) does `_userItemDict.Clear();` the moment `user_item_list` is present in the response body — not when it's non-empty — then re-populates from the wire. Our /mypage/index was emitting [] by default (the field initializer on the DTO), which wiped the inventory that /load/index had just populated. Downstream consequence: the client's PackChildGachaInfo.CostGoodsCount reads from _userItemDict, so a wiped dict makes every ticket-cost pack report CostGoodsCount=0, PackConfig.EnableBuyPack returns false, and is_hide=1 packs (including the tutorial legendary starter 99047) disappear from the rotation pack list — even though the gift bundle just granted the ticket and the DB row exists. The tutorial then auto-selects whatever non-tutorial pack happens to be at index 0 of the filtered list, the user can't afford it, and the flow is stuck. Fix: - MyPageController.Index now sets UserItemList from viewer.Items (already loaded by GetViewerByShortUdid's home-screen graph). - DTO docstring rewritten to call out the presence-sensitive semantics and the load-bearing path through PackConfig.EnableBuyPack, so the next developer doesn't get the "empty is fine" hint the old comment implied. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -100,6 +100,13 @@ public class MyPageController : SVSimController
|
||||
},
|
||||
BasicPuzzle = new Models.Dtos.Common.BadgeFlag { IsDisplayBadge = false }, // TODO(mypage-stub): viewer practice-puzzle progress
|
||||
IsBattlePassPeriod = rotation.IsBattlePassPeriod,
|
||||
// The client's MyPageTask.Parse (line 155-163) does `_userItemDict.Clear();` whenever
|
||||
// user_item_list is present in the response — not when it's non-empty — and then
|
||||
// repopulates from the wire. Emitting [] here wipes the inventory the client populated
|
||||
// from /load/index, which makes PackChildGachaInfo.CostGoodsCount return 0 and filters
|
||||
// out is_hide=1 tutorial packs (the legendary starter 99047) via PackConfig.EnableBuyPack.
|
||||
// Populate from viewer.Items so the client's dict stays in sync with the DB.
|
||||
UserItemList = viewer.Items.Select(i => new UserItem(i)).ToList(),
|
||||
SpecialCrystalInfo = new(), // TODO(mypage-stub): same shape/source as /load/index
|
||||
// CompetitionInfo, ShopNotification, StoryNotification, GuildNotification, GatheringInfo,
|
||||
// IsHiddenBossAppeared, SubBanner/SubBannerList/HomeDialogList/UserOfflineEvent/UserItemList,
|
||||
|
||||
@@ -267,8 +267,14 @@ public class MyPageIndexResponse
|
||||
// ── Per-viewer / event state ───────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Updated item counts. Empty list = "no items to update" (client iterates 0 times, no UI change).
|
||||
/// Per-viewer state — populate from viewer.Items when that wiring lands.
|
||||
/// Full snapshot of the viewer's owned items — NOT a delta. The client's
|
||||
/// <c>MyPageTask.Parse</c> (line 155-163) clears <c>_userItemDict</c> the moment it sees
|
||||
/// this key, then re-populates from the wire list. Emitting <c>[]</c> wipes whatever
|
||||
/// /load/index populated, breaking any client logic that reads from the dict — most
|
||||
/// load-bearingly <c>PackChildGachaInfo.CostGoodsCount</c>, which gates tutorial-pack
|
||||
/// visibility via <c>PackConfig.EnableBuyPack</c>. Controllers MUST populate the full
|
||||
/// owned-items snapshot from <c>viewer.Items</c>; an empty list is correct only when the
|
||||
/// viewer genuinely owns nothing.
|
||||
/// </summary>
|
||||
[JsonPropertyName("user_item_list")]
|
||||
[Key("user_item_list")]
|
||||
|
||||
Reference in New Issue
Block a user