Skip to content

Clan Game History#3988

Merged
evanpelle merged 16 commits into
mainfrom
gamehistory
May 22, 2026
Merged

Clan Game History#3988
evanpelle merged 16 commits into
mainfrom
gamehistory

Conversation

@ryanbarlow97

@ryanbarlow97 ryanbarlow97 commented May 22, 2026

Copy link
Copy Markdown
Contributor

Description:

Adds
image

continuation of infra https://github.com/openfrontio/infra/pull/345

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced

Please put your Discord username so you can be contacted if a bug or regression is found:

w.o.n

Copilot AI review requested due to automatic review settings May 22, 2026 15:37
@ryanbarlow97 ryanbarlow97 requested a review from a team as a code owner May 22, 2026 15:37
@ryanbarlow97 ryanbarlow97 self-assigned this May 22, 2026
@ryanbarlow97 ryanbarlow97 added Backend Server-side features and systems - lobbies, matchmaking, accounts, APIs, etc. UI/UX UI/UX changes including assets, menus, QoL, etc. Feature labels May 22, 2026
@ryanbarlow97 ryanbarlow97 added this to the v32 milestone May 22, 2026
@ryanbarlow97 ryanbarlow97 changed the title Gamehistory Clan Game History May 22, 2026
@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds paginated clan game history and schemas, replaces eager clan-stats fetching with lazy loads, integrates a game-history tab in ClanModal, refactors ClanDetailView to lazy-load members, updates duration formatting and localization, and adapts tests/mocks.

Changes

Clan Game History Feature

Layer / File(s) Summary
Game data contracts and API
src/core/ClanApiSchemas.ts, src/client/ClanApi.ts
Zod schemas for clan games, players, results, filters, and paginated response. ClanStatsSchema.stats becomes optional. fetchClanStats removed; fetchClanGames added with error union and Zod validation.
ClanGameHistoryView component
src/client/components/clan/ClanGameHistoryView.ts
New clan-game-history-view Lit element: filter tabs, day-grouped rows, infinite-scroll pagination, partial-win/result badges, "watch replay" actions, per-clan/filter caching, and history-updated events.
Modal tab routing and view integration
src/client/ClanModal.ts
Splits list vs detail tabs; exposes overview, members, game-history for detail; renders clan-game-history-view when active; manages gameHistoryCache and guards detail cache updates by selected tag.
Detail view member lazy-loading
src/client/components/clan/ClanDetailView.ts, src/client/components/clan/ClanShared.ts
ClanDetailView fetches overview-only on open and defers member pages until Members tab; cached detail no longer includes stats; renderClanWL removed.
Duration utility update
src/client/Utils.ts
renderDuration formats hours/minutes/seconds as largest-first components, omitting trailing zero units and using localized short-unit labels.
UI localization and strings
resources/lang/en.json
Adds month names and short duration labels; removes top-level clan_modal.statistics; expands game-history UI strings and tab labels.
Tests and mocks
tests/client/clan/*
Removes fetchClanStats from imports and mocks; adds fetchClanGames test coverage and new schema/component tests; updates test helpers.

Sequence Diagram(s)

sequenceDiagram
  participant Modal as ClanModal
  participant View as ClanGameHistoryView
  participant API as fetchClanGames
  participant Observer as IntersectionObserver
  Modal->>View: render(clanTag, cachedState)
  View->>API: fetchClanGames(tag, filter?, cursor?)
  API-->>View: ClanGamesResponse | { error: "forbidden" | "failed" }
  View->>View: append results, group by day
  Observer->>View: sentinel visible -> fetch next cursor
  View->>Modal: dispatch history-updated(tag, filter, games, nextCursor)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • evanpelle

Poem

🎮 Game history pages unfurl,
Tabs split detail, members wait,
Pages fetch as you scroll the world,
Replays call and errors state,
Localized times now mark the date.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Clan Game History' directly and specifically describes the main feature added: a new game history UI component for clans.
Description check ✅ Passed The description is related to the changeset, referencing the clan game history feature, infra continuation work, and confirming completion of required checklist items (tests, translations, screenshots).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new “Game History” experience to the clan modal, including client-side API/schema support and UI for browsing and replaying recent clan games, while removing the legacy clan stats fetch/render path.

Changes:

  • Introduces fetchClanGames() + new Zod schemas/types for clan game history payloads.
  • Adds a new ClanGameHistoryView tab to ClanModal, with cursor-based infinite scroll and filtering.
  • Adjusts supporting UI/utilities (member list lazy-loading behavior, duration formatting, new i18n keys).

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/client/clan/ClanModalTestUtils.ts Removes fetchClanStats from the Clan API mock factory.
tests/client/clan/ClanModal.rendering.test.ts Updates rendering test setup to stop mocking fetchClanStats.
tests/client/clan/ClanModal.handlers.test.ts Updates handler tests to stop mocking fetchClanStats.
tests/client/clan/ClanApiQueries.test.ts Removes the fetchClanStats query tests.
src/core/ClanApiSchemas.ts Makes ClanStats.stats optional and adds schemas/types for clan game history responses.
src/client/Utils.ts Enhances renderDuration() formatting to support hours and omit trailing zero units.
src/client/components/clan/ClanShared.ts Removes renderClanWL() (legacy clan stats breakdown container).
src/client/components/clan/ClanGameHistoryView.ts New Lit component implementing the game history UI, filtering, and infinite scroll.
src/client/components/clan/ClanDetailView.ts Defers member list fetch until the Members tab is opened.
src/client/ClanModal.ts Adds detail-view tabs (Overview/Members/Game History) and history cache plumbing.
src/client/ClanApi.ts Removes fetchClanStats() and adds fetchClanGames() with Zod validation + error mapping.
resources/lang/en.json Adds month + game-history related translation keys; removes clan_modal.statistics.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/client/components/clan/ClanGameHistoryView.ts Outdated
Comment thread src/client/ClanApi.ts
Comment thread src/client/ClanModal.ts Outdated
Comment thread src/client/components/clan/ClanDetailView.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/core/ClanApiSchemas.ts (1)

140-201: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add tests for these core schema changes.

This PR changes ClanStatsSchema optionality and adds several new ClanGame* wire cases, but I do not see matching tests in the provided changes. A small parse-focused test matrix for missing/null stats, playerTeams, totalPlayers, and nextCursor would make this contract much safer.

As per coding guidelines, "All changes to src/core/ must include tests".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/ClanApiSchemas.ts` around lines 140 - 201, The PR added
optional/nullish fields to core schemas but lacks tests: add unit tests that
exercise parsing/validation for ClanStatsSchema (presence and absence/null of
stats), ClanGameSchema (playerTeams null vs absent, totalPlayers null vs
present), ClanGameResultSchema variants, and ClanGamesResponseSchema (nextCursor
null and non-null); create a small matrix of zod.parse assertions (both valid
and invalid cases) referencing ClanStatsSchema, ClanGameSchema,
ClanGamePlayerSchema, ClanGameResultSchema, and ClanGamesResponseSchema to
ensure the wire cases (null, undefined, and real values) round-trip as expected.
src/client/components/clan/ClanGameHistoryView.ts (1)

1-665: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Run Prettier on this file before merge.

CI is already red on src/client/components/clan/ClanGameHistoryView.ts for formatting, so this will not merge cleanly as-is.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/components/clan/ClanGameHistoryView.ts` around lines 1 - 665, The
file ClanGameHistoryView.ts is failing CI due to formatting; run Prettier on
this file (or your repo's formatter script) and commit the resulting changes so
the export/class/function spacing, template literal indentation, and trailing
commas match project style; specifically reformat the clan-game-history-view
LitElement class and helper functions (ClanGameHistoryView, formatAbsoluteTime,
groupGamesByDay, dayKey, formatDayHeader) and ensure no lint/format errors
remain before pushing.
src/client/ClanModal.ts (1)

190-198: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve the previous list tab when leaving detail.

openDetail() always overwrites activeTab with "overview", and every exit path hardcodes "my-clans". Opening a clan from Browse now sends users back to My Clans, so they lose their browse context and cached filters.

♻️ Minimal fix sketch
+  `@state`() private lastListTab: ListTab = "my-clans";
+
   protected onTabEnter(tab: string): void {
     if (isListTab(tab)) {
+      this.lastListTab = tab;
       this.view = "list";
       this.selectedClan = null;
       this.selectedClanTag = "";
       if (tab === "my-clans") {
         this.loadMyClans();
@@
       onBack: () => {
         this.view = "list";
         this.selectedClan = null;
         this.selectedClanTag = "";
         this.myRole = null;
         this.detailCache = null;
         this.gameHistoryCache = null;
-        this.activeTab = "my-clans";
+        this.activeTab = this.lastListTab;
       },
@@
   private openDetail(tag: string) {
+    if (isListTab(this.activeTab)) {
+      this.lastListTab = this.activeTab;
+    }
     if (this.selectedClanTag !== tag) {
       this.gameHistoryCache = null;
     }
     this.selectedClanTag = tag;
     this.view = "detail";
     this.activeTab = "overview";
   }

Also applies to: 304-305, 366-374, 422-432, 464-472

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/ClanModal.ts` around lines 190 - 198, openDetail() and multiple
exit paths overwrite activeTab to "overview" or hardcode "my-clans", which loses
the user's previous list/browse tab; fix by preserving and restoring the prior
tab: before changing this.view to "detail" in openDetail(), save the current
this.activeTab into a new property like this.previousActiveTab (or only set
this.activeTab to "overview" if it is empty), and on all exit branches (the
onBack handler and other detail-exit code paths that currently set this.view =
"list") restore this.activeTab = this.previousActiveTab (or leave it unchanged)
instead of assigning "my-clans"; update references in openDetail(), onBack, and
the other exit handlers (the methods that clear
selectedClan/selectedClanTag/myRole/detailCache/gameHistoryCache) to use
this.previousActiveTab for restoration.
🧹 Nitpick comments (1)
src/client/components/clan/ClanDetailView.ts (1)

154-175: ⚡ Quick win

Remove the unreachable role fallback.

loadInitialMembers() exits unless myClanRoles.has(this.clanTag) is true, so knownRole on Lines 171-175 cannot be missing anymore. Either delete that branch or relax the earlier guard if this fetch is supposed to discover the current user's role.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/components/clan/ClanDetailView.ts` around lines 154 - 175, The
post-fetch fallback that sets this.myRole based on fetched members is
unreachable because loadInitialMembers() returns early when
this.myClanRoles.has(this.clanTag) is false; either remove the unreachable
branch (the knownRole check and the block that finds me by this.myPublicId and
sets this.myRole) or instead relax the early guard (remove or change the if
(!this.myClanRoles.has(this.clanTag)) return) so the fetch can run to discover
the current user's role; update the loadInitialMembers method accordingly and
keep references to myClanRoles, clanTag, knownRole, myPublicId, and myRole in
your change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/client/ClanModal.ts`:
- Around line 39-43: The helper isDetailTab is unused and causing an ESLint
error; either remove the function or use it where tab keys are validated—update
the tab-routing or validation logic (the same place isListTab and LIST_TABS are
used) to call isDetailTab(key) when you expect a DetailTab, or delete the
isDetailTab function and its DETAIL_TABS reference if detail-tab validation is
handled elsewhere; ensure consistent use of isListTab, isDetailTab, LIST_TABS
and DETAIL_TABS in the tab switch/validation logic so no unused helper remains.

In `@src/client/components/clan/ClanGameHistoryView.ts`:
- Around line 531-550: The current renderPlayersField incorrectly treats Free
For All games as a single-clan slot so it shows only total player count; update
the isSingleClanSlot logic in renderPlayersField (used with renderField and
related to game.mode, game.rankedType, and clanPlayers) to NOT treat game.mode
=== "Free For All" as a single clan slot (i.e., only consider
rankedType/unranked for single-clan behavior) so that FFA rows render
clanPlayers.length (and thus keep renderResultBadge behavior intact) when
appropriate.
- Around line 44-81: The current hydration logic lives only in
connectedCallback, so move that "hydrate from cache vs reload" behavior into
updated(changed) and trigger it when changed.has('clanTag') or
changed.has('cachedState'); before hydrating or calling reload(), increment
asyncGeneration to cancel in-flight requests and reset loading = false,
loadingMore = false, appendFailed = false, and loadState = "ok"; if cachedState
matches clanTag restore games, nextCursor, filter from cachedState, else call
reload(); keep
connectedCallback/disconnectedCallback/ensureObserver/teardownObserver as-is but
remove the hydration logic from connectedCallback so updates while mounted
behave correctly (refer to properties clanTag, cachedState and fields games,
nextCursor, filter, asyncGeneration, loading, loadingMore, appendFailed,
loadState and method reload()).

In `@src/client/Utils.ts`:
- Around line 233-245: The renderDuration function currently emits hardcoded
"h", "min", and "s" suffixes; change it to call translateText() for each unit
(e.g. keys like "duration.hour", "duration.minute", "duration.second") and use
those returned strings when building parts in renderDuration so localization
flows through translateText; also add matching entries to resources/lang/en.json
for those keys (providing "h", "min", "s" or full words as desired) and ensure
translateText is imported/available in Utils.ts before using it.

In `@src/core/ClanApiSchemas.ts`:
- Around line 193-199: Update the comment on nextCursor in the
ClanGamesResponseSchema to reflect that it is an opaque continuation token (to
be round-tripped verbatim as the cursor query parameter) rather than an ISO
datetime or a "before" timestamp; modify the comment text near the nextCursor
field in ClanGamesResponseSchema to mention "opaque continuation token" and
"cursor" so it matches the client/API contract, without changing the
z.string().nullable() validator or schema shape.

---

Outside diff comments:
In `@src/client/ClanModal.ts`:
- Around line 190-198: openDetail() and multiple exit paths overwrite activeTab
to "overview" or hardcode "my-clans", which loses the user's previous
list/browse tab; fix by preserving and restoring the prior tab: before changing
this.view to "detail" in openDetail(), save the current this.activeTab into a
new property like this.previousActiveTab (or only set this.activeTab to
"overview" if it is empty), and on all exit branches (the onBack handler and
other detail-exit code paths that currently set this.view = "list") restore
this.activeTab = this.previousActiveTab (or leave it unchanged) instead of
assigning "my-clans"; update references in openDetail(), onBack, and the other
exit handlers (the methods that clear
selectedClan/selectedClanTag/myRole/detailCache/gameHistoryCache) to use
this.previousActiveTab for restoration.

In `@src/client/components/clan/ClanGameHistoryView.ts`:
- Around line 1-665: The file ClanGameHistoryView.ts is failing CI due to
formatting; run Prettier on this file (or your repo's formatter script) and
commit the resulting changes so the export/class/function spacing, template
literal indentation, and trailing commas match project style; specifically
reformat the clan-game-history-view LitElement class and helper functions
(ClanGameHistoryView, formatAbsoluteTime, groupGamesByDay, dayKey,
formatDayHeader) and ensure no lint/format errors remain before pushing.

In `@src/core/ClanApiSchemas.ts`:
- Around line 140-201: The PR added optional/nullish fields to core schemas but
lacks tests: add unit tests that exercise parsing/validation for ClanStatsSchema
(presence and absence/null of stats), ClanGameSchema (playerTeams null vs
absent, totalPlayers null vs present), ClanGameResultSchema variants, and
ClanGamesResponseSchema (nextCursor null and non-null); create a small matrix of
zod.parse assertions (both valid and invalid cases) referencing ClanStatsSchema,
ClanGameSchema, ClanGamePlayerSchema, ClanGameResultSchema, and
ClanGamesResponseSchema to ensure the wire cases (null, undefined, and real
values) round-trip as expected.

---

Nitpick comments:
In `@src/client/components/clan/ClanDetailView.ts`:
- Around line 154-175: The post-fetch fallback that sets this.myRole based on
fetched members is unreachable because loadInitialMembers() returns early when
this.myClanRoles.has(this.clanTag) is false; either remove the unreachable
branch (the knownRole check and the block that finds me by this.myPublicId and
sets this.myRole) or instead relax the early guard (remove or change the if
(!this.myClanRoles.has(this.clanTag)) return) so the fetch can run to discover
the current user's role; update the loadInitialMembers method accordingly and
keep references to myClanRoles, clanTag, knownRole, myPublicId, and myRole in
your change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7e757e09-5c3d-4646-91b4-5d9225dc6d4d

📥 Commits

Reviewing files that changed from the base of the PR and between 458d04e and d565414.

📒 Files selected for processing (12)
  • resources/lang/en.json
  • src/client/ClanApi.ts
  • src/client/ClanModal.ts
  • src/client/Utils.ts
  • src/client/components/clan/ClanDetailView.ts
  • src/client/components/clan/ClanGameHistoryView.ts
  • src/client/components/clan/ClanShared.ts
  • src/core/ClanApiSchemas.ts
  • tests/client/clan/ClanApiQueries.test.ts
  • tests/client/clan/ClanModal.handlers.test.ts
  • tests/client/clan/ClanModal.rendering.test.ts
  • tests/client/clan/ClanModalTestUtils.ts
💤 Files with no reviewable changes (3)
  • tests/client/clan/ClanModalTestUtils.ts
  • src/client/components/clan/ClanShared.ts
  • tests/client/clan/ClanApiQueries.test.ts

Comment thread src/client/ClanModal.ts Outdated
Comment thread src/client/components/clan/ClanGameHistoryView.ts
Comment thread src/client/components/clan/ClanGameHistoryView.ts
Comment thread src/client/Utils.ts
Comment thread src/core/ClanApiSchemas.ts
@github-project-automation github-project-automation Bot moved this from Triage to Development in OpenFront Release Management May 22, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/client/clan/ClanApiSchemas.test.ts (1)

384-388: ⚡ Quick win

Use an opaque token fixture for nextCursor tests.

At Line 387, using an ISO timestamp can blur the cursor contract. A non-date token makes it clear this value is opaque and only round-tripped.

Proposed test tweak
   it("accepts a non-empty page with a cursor", () => {
     const result = ClanGamesResponseSchema.safeParse({
       results: [validGame],
-      nextCursor: "2024-05-31T00:00:00.000Z",
+      nextCursor: "cursor_v1:eyJpZCI6MTIzfQ",
     });
     expect(result.success).toBe(true);
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/client/clan/ClanApiSchemas.test.ts` around lines 384 - 388, The test
"accepts a non-empty page with a cursor" uses an ISO timestamp for nextCursor
which obscures the cursor-as-opaque-token contract; update the test that calls
ClanGamesResponseSchema.safeParse (the one using validGame) to use a non-date
opaque token (e.g. "opaque-cursor-<unique>") or a dedicated cursor fixture
instead of "2024-05-31T00:00:00.000Z" so the test clearly asserts round-tripping
of an opaque cursor value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/client/clan/ClanApiSchemas.test.ts`:
- Around line 384-388: The test "accepts a non-empty page with a cursor" uses an
ISO timestamp for nextCursor which obscures the cursor-as-opaque-token contract;
update the test that calls ClanGamesResponseSchema.safeParse (the one using
validGame) to use a non-date opaque token (e.g. "opaque-cursor-<unique>") or a
dedicated cursor fixture instead of "2024-05-31T00:00:00.000Z" so the test
clearly asserts round-tripping of an opaque cursor value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9ed59b8b-2a0c-4d0a-a045-9ff9bd2c326a

📥 Commits

Reviewing files that changed from the base of the PR and between d565414 and 6d3bef9.

📒 Files selected for processing (8)
  • resources/lang/en.json
  • src/client/ClanModal.ts
  • src/client/Utils.ts
  • src/client/components/clan/ClanDetailView.ts
  • src/client/components/clan/ClanGameHistoryView.ts
  • src/core/ClanApiSchemas.ts
  • tests/client/clan/ClanApiQueries.test.ts
  • tests/client/clan/ClanApiSchemas.test.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 22, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 22, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/client/ClanModal.ts (1)

127-135: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clear detailCache when routing back to a list tab.

This branch drops selectedClan but keeps detailCache. If the user reaches "browse"/"my-clans" through tab routing and then reopens the same clan, clan-detail-view restores cached members with cachedClan = null, skips loadDetail(), and the detail page renders blank.

💡 Minimal fix
   protected onTabEnter(tab: string): void {
     if (isListTab(tab)) {
       this.view = "list";
       this.selectedClan = null;
       this.selectedClanTag = "";
+      this.myRole = null;
+      this.detailCache = null;
+      this.gameHistoryCache = null;
       if (tab === "my-clans") {
         this.loadMyClans();
       }
       return;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/ClanModal.ts` around lines 127 - 135, When switching to a list tab
in onTabEnter, also clear the cached detail state so reopening the same clan
doesn't reuse stale detailCache; specifically, reset this.detailCache (or
whatever property holds cached clan details) alongside this.selectedClan = null
and this.selectedClanTag = "" in the isListTab branch (before calling
loadMyClans if tab === "my-clans") so clan-detail-view will call loadDetail()
instead of restoring from a stale cache.
🧹 Nitpick comments (1)
tests/client/clan/ClanGameHistoryView.test.ts (1)

1-623: ⚡ Quick win

Consider using the flushAsync helper from ClanModalTestUtils for more robust async handling.

The test file repeatedly uses the pattern:

await new Promise((r) => setTimeout(r, 0));
await el.updateComplete;

However, ClanModalTestUtils exports a flushAsync helper that handles this more robustly with double-tick draining for chained microtasks. Using it consistently would reduce duplication and improve reliability, especially for tests involving state updates that trigger re-renders.

♻️ Refactor example

Import flushAsync at the top:

 import type { ClanGame, ClanGamesResponse } from "../../../src/client/ClanApi";
 import { fetchClanGames } from "../../../src/client/ClanApi";
 import {
   ClanGameHistoryView,
   type ClanGameHistoryCache,
 } from "../../../src/client/components/clan/ClanGameHistoryView";
+import { flushAsync } from "./ClanModalTestUtils";

Then replace patterns like:

-await new Promise((r) => setTimeout(r, 0));
-await el.updateComplete;
+await flushAsync(el);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/client/clan/ClanGameHistoryView.test.ts` around lines 1 - 623, Import
the flushAsync helper from ClanModalTestUtils and replace every occurrence of
the two-line pattern "await new Promise((r) => setTimeout(r, 0)); await
el.updateComplete;" with a single "await flushAsync();" (keeping any following
awaits of el.updateComplete only if still needed) so async microtask draining is
handled robustly when waiting for ClanGameHistoryView render/update cycles that
rely on el.updateComplete.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/client/ClanModal.ts`:
- Around line 127-135: When switching to a list tab in onTabEnter, also clear
the cached detail state so reopening the same clan doesn't reuse stale
detailCache; specifically, reset this.detailCache (or whatever property holds
cached clan details) alongside this.selectedClan = null and this.selectedClanTag
= "" in the isListTab branch (before calling loadMyClans if tab === "my-clans")
so clan-detail-view will call loadDetail() instead of restoring from a stale
cache.

---

Nitpick comments:
In `@tests/client/clan/ClanGameHistoryView.test.ts`:
- Around line 1-623: Import the flushAsync helper from ClanModalTestUtils and
replace every occurrence of the two-line pattern "await new Promise((r) =>
setTimeout(r, 0)); await el.updateComplete;" with a single "await flushAsync();"
(keeping any following awaits of el.updateComplete only if still needed) so
async microtask draining is handled robustly when waiting for
ClanGameHistoryView render/update cycles that rely on el.updateComplete.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 65b58c99-5e4b-40a0-9529-0bc31216b939

📥 Commits

Reviewing files that changed from the base of the PR and between c9de38e and d750502.

📒 Files selected for processing (9)
  • src/client/ClanApi.ts
  • src/client/ClanModal.ts
  • src/client/Utils.ts
  • src/client/components/clan/ClanDetailView.ts
  • src/client/components/clan/ClanGameHistoryView.ts
  • src/core/ClanApiSchemas.ts
  • tests/client/clan/ClanApiSchemas.test.ts
  • tests/client/clan/ClanGameHistoryView.test.ts
  • tests/client/clan/ClanModalTestUtils.ts
💤 Files with no reviewable changes (2)
  • src/client/ClanApi.ts
  • tests/client/clan/ClanApiSchemas.test.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 22, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/client/clan/ClanGameHistoryView.test.ts`:
- Around line 176-177: The test calls mountView({ cachedState: cache }) but
doesn't capture its return and then calls flushAsync() with no argument, which
is inconsistent with other tests; update the test to capture the mounted element
(const el = await mountView({ cachedState: cache });) and then call
flushAsync(el) so it matches the pattern used elsewhere and ensures flushAsync
receives the correct element, or alternatively confirm and document that
flushAsync is intentionally argument-optional if you prefer that behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0896aacd-6bb3-4045-9de0-d0e23bbaa0da

📥 Commits

Reviewing files that changed from the base of the PR and between d750502 and 9e49765.

📒 Files selected for processing (2)
  • src/client/ClanModal.ts
  • tests/client/clan/ClanGameHistoryView.test.ts

Comment thread tests/client/clan/ClanGameHistoryView.test.ts Outdated
@evanpelle evanpelle merged commit a14cf0e into main May 22, 2026
14 checks passed
@evanpelle evanpelle deleted the gamehistory branch May 22, 2026 21:30
@github-project-automation github-project-automation Bot moved this from Development to Complete in OpenFront Release Management May 22, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Jun 12, 2026
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend Server-side features and systems - lobbies, matchmaking, accounts, APIs, etc. Feature UI/UX UI/UX changes including assets, menus, QoL, etc.

Projects

Status: Complete

Development

Successfully merging this pull request may close these issues.

3 participants