Commit Graph

19 Commits

Author SHA1 Message Date
gamer147
05d143d2e8 fix(mypage): populate unread_present_count from ViewerPresents
The home-screen crate button reads this count via MyPageTask (parsed
into Data.MyPage.data.unread_mail_count) and MyPageItemHome.cs:148
routes it into SetUnreadGiftCount to render the "N" bubble on the
gift button. It was hardcoded 0, so tutorial gifts (5 presents sitting
in the viewer's inbox after signup) showed no badge until the crate
was opened.

Add IViewerRepository.CountUnclaimedPresentsAsync and call it from
MyPageController.Index. Regression test seeds a viewer with tutorial
presents and asserts unread_present_count > 0.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 14:44:52 -04:00
gamer147
d62f1b7ba7 feat(guild): MyPage guild_notification + load_index audit
- Replace GuildNotification TODO stub in MyPageController.Index with
  BuildGuildNotificationAsync: populates guild_id, guild_room_message_id,
  is_join_request, is_invited from real DB state.
- Inject IGuildService, IGuildInviteRepository, IGuildJoinRequestRepository,
  IGuildChatMessageRepository into MyPageController.
- Add GetMaxMessageIdSafelyAsync to IGuildChatMessageRepository (returns null
  when no messages exist, vs GetMaxMessageIdAsync which returns 0).
- LoadDetail.cs audit: grep returned no guild fields — no LoadController changes.
- Add GuildCrossSurfaceTests (4 tests): guild member, pending invite, pending
  join request, no-guild viewer all verified green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 15:24:46 -04:00
gamer147
0d2da66795 feat(mypage): can_give_daily_login_bonus reflects IsDue(viewer)
Injects ILoginBonusService into MyPageController and sets
CanGiveDailyLoginBonus from IsDue(viewer) in the Index response.
Adds two integration tests: fresh viewer → true, post-load-index → false.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:50:17 -04:00
gamer147
ba7040580f fix(arena-colosseum): tips + colosseum_id wiring + round-aware time derivation
Three fixups the Phase 1-3 work missed (would bite the first time an admin
flipped on a real event):

* IsDisplayTips + TipsId added to ColosseumSeasonConfig. Drives
  Wizard/ColosseumEntryInfoTask.cs:129 (NeedsFirstTips = is_display_tips == 1).
  The wire DTO already had the properties; the season config didn't carry
  them, so admins had no way to gate the first-time tutorial tip popup.
* colosseum_id is now emitted from SeasonId. Wire field was always-absent
  before (DTO had the property but builder never set it); client fell back
  to 0 and couldn't disambiguate cups.
* ColosseumLobbyInfoBuilder.Build now takes ColosseumRoundsConfig + nowUtc
  and derives the wire is_round_period / now_round / next_round / start_time
  / end_time from the rounds schedule against the current clock. Previous
  hardcode (is_round_period: true, now_round: 1, event-window dates) would
  have shown "Round 1 ends 2026-07-31" instead of the actual round's end.
  Per the client parser at ColosseumEntryInfoTask.cs:111-126:
    - inside a round → emit that round's window + is_round_period=true
    - between rounds → emit next round's start as start_time + next_round
    - pre-event → next_round=1, start_time=event start
    - post-event → anchor to last round so the client doesn't NRE
    - no rounds configured → fall back to event-level window

Why these were missed: I implemented ColosseumSeasonConfig from the plan's
explicit field list rather than cross-checking against the captured
colosseum.json wire shape; I added ColosseumId to the wire DTO without
wiring the builder side; and no test exercised a mid-event scenario with
realistic dates — Phase 2's E2E hand-edits run.RoundId via
PromoteRunToRoundAsync instead of letting the controller derive it.

* 10 new builder unit tests cover off-season / mid-round / between-rounds /
  pre-event / post-event / empty-rounds-fallback + the new field surfacing.
  Full suite: 1355/1355 (was 1345 + 10).
2026-06-13 13:44:10 -04:00
gamer147
43a3665775 refactor(arena-colosseum): dedup colosseum_info source-of-truth
/mypage/index data.colosseum_info and /arena_colosseum/{top,get_fee_info}
now both project through ColosseumLobbyInfoBuilder.Build(ColosseumSeasonConfig).
One source of truth for the home-tab gating and the lobby reads — admins
flip the season on by writing to GameConfigs (per the runbook), no second
table to keep in sync.

* New ColosseumLobbyInfoBuilder — static projection from ColosseumSeasonConfig
  to the wire ColosseumLobbyInfo. Replaces ArenaColosseumController's private
  BuildColosseumInfo and MyPageController's BuildColosseumInfo(ColosseumConfig).
* MyPageController reads ColosseumSeasonConfig via IGameConfigService; the
  IGlobalsRepository.GetCurrentColosseum() call goes away.
* MyPageIndexResponse.ColosseumInfo retyped to the new ColosseumLobbyInfo.
* ColosseumSalesPeriodInfo relocated into the ArenaColosseum namespace
  alongside the rest of the family DTOs.
* Drops: Colosseums DB table (migration DropColosseumsTable), ColosseumConfig
  entity, the captured-shape Models.Dtos.ColosseumInfo DTO, ColosseumSeed,
  MyPageGlobalsImporter.ImportColosseumAsync + seed file colosseum.json,
  IGlobalsRepository.GetCurrentColosseum + impl, the two related tests.

Net: home-screen Grand Prix tab now hidden by default because the season
ships with IsColosseumPeriod=false. Full suite: 1345/1345 (was 1347 — the
2 dropped tests covered the now-dead surface).
2026-06-13 13:31:03 -04:00
gamer147
715dd4dea8 feat(config): wire /config/update + /config/update_challenge_config (closes family 4/4)
is_skip_gacha_effect, use_challenge_two_pick_premium_card, and
challenge_two_pick_sleeve_id all persist on ViewerInfo and round-trip
through /load/index.

The two challenge fields move from the global ChallengeConfig section to
ViewerInfo — they're viewer preferences, not server-wide policy. Premium
card defaults to 0; sleeve falls back to DefaultLoadoutConfig.SleeveId
(3000011) when unset. MatchContextBuilder's TK2 sleeve read switches to
the same viewer-scoped path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-13 11:25:40 -04:00
gamer147
13ca7d118e feat(config): persist is_foil_preferred + is_prize_preferred (stubs → real) 2026-06-13 09:54:51 -04:00
gamer147
e2f5786b60 feat(mypage): /mypage/get_special_crystal_info (empty — no offers) 2026-06-12 23:12:49 -04:00
gamer147
43e4c06a2c feat(mypage): /mypage/finish_battle (returns check=0 — no in-flight state yet) 2026-06-12 23:08:42 -04:00
gamer147
483cc1c1e0 feat(mypage): /mypage/index reflects persisted bg selection
Wires MyPageController.Index to read Viewer.MyPageBgId/SelectType/BgRotation
instead of emitting an empty MyPageBgSetting placeholder; adds Include for
MyPageBgRotation in ViewerRepository.GetViewerByShortUdid; adds fresh-viewer
zero-defaults test and write→read round-trip test (9/9 controller tests pass).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 16:53:34 -04:00
gamer147
9d6a5cc3b9 feat(home-dialog): populate home_dialog_list on /mypage/index
Walk-down behavior: each call emits the highest-priority unfired
active dialog; subsequent calls walk to the next-priority entry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 18:55:48 -04:00
gamer147
1af56b4ec4 fix(tk2): per-viewer is_join in arena_info + stub /arena/get_challenge_info
Bug 1 ("pay to enter again after restart"):
arena_info[0].is_join shipped from the static ArenaSeasonConfig seed,
so /load/index and /mypage/index always emitted false regardless of
viewer state. The client uses is_join to choose between the "Pay to
enter" and "Resume run" dialogs (Wizard/ChallengeEntry.cs:165 + the
ArenaEntryBase._isJoinFunc pivot). Without a per-viewer override every
cold start after a partial run looked like "no run" and the player got
charged again.

LoadController + MyPageController now compute is_join from
ViewerArenaTwoPickRuns presence. MyPageController grew an
IArenaTwoPickRunRepository dep (LoadController already had _db).

Bug 2: /arena/get_challenge_info 404. Stubbed via a new
ArenaController + DTO pair. Returns the season seed's begin/end_time
+ name where available; placeholder zeros for win history. All 6 keys
required by ChallangeHistoryInfoTask.Parse are present (unconditional
JsonData lookups).

Routing smoke added for /arena/get_challenge_info.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 13:13:11 -04:00
gamer147
b5e33c15f6 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>
2026-05-28 18:03:50 -04:00
gamer147
5e7a65fe5a Story 2026-05-25 14:36:12 -04:00
gamer147
558e8288eb Puzzles 2026-05-25 12:03:47 -04:00
gamer147
c14408ba06 Seeding reorg 2026-05-24 21:13:15 -04:00
gamer147
d9ef9fe1fc Pack logic cleanup 2026-05-24 09:27:10 -04:00
gamer147
d3b2970e11 Deck list work 2026-05-23 19:57:34 -04:00
gamer147
66184b3685 Things were working, suddenly regressed 2026-05-23 18:14:42 -04:00