Skip to content

Profile: Only load one month of games by default#3491

Open
dlin2028 wants to merge 4 commits intoFAForever:developfrom
dlin2028:develop
Open

Profile: Only load one month of games by default#3491
dlin2028 wants to merge 4 commits intoFAForever:developfrom
dlin2028:develop

Conversation

@dlin2028
Copy link

@dlin2028 dlin2028 commented Mar 18, 2026

Currently profile page gets all games by default, this causes a lot of lag for players with a lot of games. This changes the default to only fetch games from the last month, significantly reducing loading time.

  • New Features
    • Rating history now filters based on your selected time period instead of always fetching all data.
    • Default time period changed from "All Time" to "Last Month" for improved performance.

Before:
image

After:
image

Summary by CodeRabbit

  • New Features

    • Rating statistics now filter based on the selected time period, allowing users to view data for specific periods (Last Month, Last Year, etc.) instead of all-time history.
    • Default time period selection changed to "Last Month" for a more relevant initial view.
  • Bug Fixes

    • Time period selection now properly triggers rating display updates.

@coderabbitai
Copy link

coderabbitai bot commented Mar 18, 2026

📝 Walkthrough

Walkthrough

Default time period now defaults to LAST_MONTH; time-period selection changes trigger a unified onRatingTypeChange flow that computes a nullable "since" OffsetDateTime and passes it to StatisticsService.getRatingHistory, which supports optional since-based filtering.

Changes

Cohort / File(s) Summary
Controller & Service
src/main/java/com/faforever/client/player/PlayerInfoWindowController.java, src/main/java/com/faforever/client/stats/StatisticsService.java
Controller now computes an optional since (null for ALL_TIME, UTC date otherwise) and passes it to getRatingHistory(player, leaderboard, @nullable OffsetDateTime). Service query logic updated to filter by gamePlayerStats.scoreTime.after(since.toInstant()) when since is non-null. Default time period changed to LAST_MONTH.
UI Binding
src/main/resources/theme/user_info_window.fxml
timePeriodComboBox onAction handler changed from #plotPlayerRatingGraph to #onRatingTypeChange, unifying event handling with ratingTypeComboBox.
Tests
src/test/java/com/faforever/client/player/PlayerInfoWindowControllerTest.java, src/test/java/com/faforever/client/stats/StatisticsServiceTest.java
Unit tests updated to stub and verify the new three-argument getRatingHistory signature; added tests guaranteeing non-null since for LAST_MONTH and null for ALL_TIME; service test covers the instant-based filter behavior.

Sequence Diagram

sequenceDiagram
    participant UI as User
    participant Controller as PlayerInfoWindowController
    participant Service as StatisticsService
    participant DB as Database/Cache

    UI->>Controller: Change time period selection
    Controller->>Controller: Compute since (ALL_TIME → null, else → OffsetDateTime at UTC)
    Controller->>Service: getRatingHistory(player, leaderboard, since)
    activate Service
    alt since is not null
        Service->>DB: Query with filter<br/>gamePlayerStats.scoreTime > since.toInstant()
    else since is null
        Service->>DB: Query all rating history for player/leaderboard
    end
    DB-->>Service: Return entries
    Service->>Controller: Emit Flux<LeaderboardRatingJournal>
    deactivate Service
    Controller->>Controller: Update ratingData and plot graph
    Controller-->>UI: Render updated graph
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through code to set the date,
LAST_MONTH now guides the rating's fate,
A "since" that’s null or crisp and new,
From combo to service, the flow runs true,
Graphs bloom where filters once were few. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: defaulting to loading only one month of games instead of all games for performance reasons.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/test/java/com/faforever/client/player/PlayerInfoWindowControllerTest.java (1)

100-101: Strengthen assertions for the new since contract.

Using any() for the third argument misses the core behavior added in this PR. Assert that default LAST_MONTH sends a non-null since, and add a case for ALL_TIME expecting null.

🧪 Suggested assertion tightening
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
@@
-    verify(statisticsService, times(2)).getRatingHistory(eq(player), eq(leaderboard), any());
+    verify(statisticsService, times(2)).getRatingHistory(
+        eq(player),
+        eq(leaderboard),
+        argThat(Objects::nonNull)
+    );

And add a dedicated test:

  • set instance.timePeriodComboBox.setValue(TimePeriod.ALL_TIME)
  • trigger instance.onRatingTypeChange()
  • verify third arg is null.

Also applies to: 186-186

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/com/faforever/client/player/PlayerInfoWindowControllerTest.java`
around lines 100 - 101, The test currently stubs
statisticsService.getRatingHistory(player, ..., any()) which hides the new
"since" contract; change the stub for the default case to assert the third
argument is non-null (e.g., use an argument matcher like
argThat(Objects::nonNull) or eq with a constructed Instant) so the test verifies
LAST_MONTH supplies a non-null since, and add a new test that sets
instance.timePeriodComboBox.setValue(TimePeriod.ALL_TIME), calls
instance.onRatingTypeChange(), and verifies
statisticsService.getRatingHistory(player, ..., null) was invoked (i.e., the
third arg is null); target the getRatingHistory(...) call and
onRatingTypeChange() in PlayerInfoWindowControllerTest for these changes.
src/test/java/com/faforever/client/stats/StatisticsServiceTest.java (1)

53-61: Add coverage for the new since != null filter path.

This test validates only the null path. Please add a second test that passes a concrete OffsetDateTime and verifies the gamePlayerStats.scoreTime predicate is included.

✅ Suggested test extension
 `@Test`
 public void testGetStatisticsForPlayer() throws Exception {
@@
 }
+
+@Test
+public void testGetStatisticsForPlayerSinceDate() {
+  PlayerInfo player = PlayerInfoBuilder.create().defaultValues().username("junit").get();
+  OffsetDateTime since = OffsetDateTime.parse("2026-02-18T00:00:00Z");
+  Flux<ElideEntity> resultFlux = Flux.just(leaderboardMapper.map(Instancio.create(LeaderboardRatingJournal.class)));
+  when(fafApiAccessor.getAll(any())).thenReturn(resultFlux);
+
+  StepVerifier.create(instance.getRatingHistory(player, leaderboard, since))
+              .expectNextCount(1)
+              .verifyComplete();
+
+  verify(fafApiAccessor).getAll(argThat(
+      ElideMatchers.hasFilter(
+          qBuilder().instant("gamePlayerStats.scoreTime").after(since.toInstant(), false).and()
+                    .intNum("gamePlayerStats.player.id").eq(player.getId()).and()
+                    .intNum("leaderboard.id").eq(leaderboard.id())
+      )
+  ));
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/com/faforever/client/stats/StatisticsServiceTest.java` around
lines 53 - 61, Add a new test in StatisticsServiceTest that calls
instance.getRatingHistory(player, leaderboard, since) with a concrete
OffsetDateTime (e.g. OffsetDateTime.now().minusDays(1)) and assert the reactive
result (StepVerifier) as in the existing test; then
verify(fafApiAccessor).getAll(argThat(...)) also contains the additional
predicate for gamePlayerStats.scoreTime (use ElideMatchers.hasFilter with a
qBuilder() expression that includes a date/time comparison on
"gamePlayerStats.scoreTime" such as greaterThanOrEqual(since)), and keep the
existing verification for page size (ElideMatchers.hasPageSize(10000)).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/com/faforever/client/player/PlayerInfoWindowController.java`:
- Around line 475-478: Guard against a null value returned by
timePeriodComboBox.getValue(): before calling timePeriod.getDate() check if the
value is null and treat it as a safe fallback (for example TimePeriod.ALL_TIME
or a sensible default), then compute OffsetDateTime since only when the resolved
TimePeriod is not ALL_TIME; update the logic around the timePeriod and since
variables (referencing timePeriodComboBox, TimePeriod, timePeriod, and
getDate()) to perform a null-check or use Optional to avoid dereferencing a null
TimePeriod.

---

Nitpick comments:
In
`@src/test/java/com/faforever/client/player/PlayerInfoWindowControllerTest.java`:
- Around line 100-101: The test currently stubs
statisticsService.getRatingHistory(player, ..., any()) which hides the new
"since" contract; change the stub for the default case to assert the third
argument is non-null (e.g., use an argument matcher like
argThat(Objects::nonNull) or eq with a constructed Instant) so the test verifies
LAST_MONTH supplies a non-null since, and add a new test that sets
instance.timePeriodComboBox.setValue(TimePeriod.ALL_TIME), calls
instance.onRatingTypeChange(), and verifies
statisticsService.getRatingHistory(player, ..., null) was invoked (i.e., the
third arg is null); target the getRatingHistory(...) call and
onRatingTypeChange() in PlayerInfoWindowControllerTest for these changes.

In `@src/test/java/com/faforever/client/stats/StatisticsServiceTest.java`:
- Around line 53-61: Add a new test in StatisticsServiceTest that calls
instance.getRatingHistory(player, leaderboard, since) with a concrete
OffsetDateTime (e.g. OffsetDateTime.now().minusDays(1)) and assert the reactive
result (StepVerifier) as in the existing test; then
verify(fafApiAccessor).getAll(argThat(...)) also contains the additional
predicate for gamePlayerStats.scoreTime (use ElideMatchers.hasFilter with a
qBuilder() expression that includes a date/time comparison on
"gamePlayerStats.scoreTime" such as greaterThanOrEqual(since)), and keep the
existing verification for page size (ElideMatchers.hasPageSize(10000)).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2d511211-e9f0-440f-aaaf-069664604417

📥 Commits

Reviewing files that changed from the base of the PR and between a402032 and 57cd3d3.

📒 Files selected for processing (5)
  • src/main/java/com/faforever/client/player/PlayerInfoWindowController.java
  • src/main/java/com/faforever/client/stats/StatisticsService.java
  • src/main/resources/theme/user_info_window.fxml
  • src/test/java/com/faforever/client/player/PlayerInfoWindowControllerTest.java
  • src/test/java/com/faforever/client/stats/StatisticsServiceTest.java

@dlin2028 dlin2028 changed the title Update player profile page so it defaults to 1 month rating history Profile: Only load one month of games by default Mar 18, 2026
Copilot AI and others added 3 commits March 18, 2026 02:14
Co-authored-by: dlin2028 <28829406+dlin2028@users.noreply.github.com>
…-check

Guard against null TimePeriod from timePeriodComboBox and add contract tests
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/test/java/com/faforever/client/player/PlayerInfoWindowControllerTest.java (1)

186-192: Consider adding waitForFxEvents() for consistency with new tests.

The new tests at lines 200 and 210 call waitForFxEvents() before verification, but this existing test does not. While the synchronous stub likely prevents flakiness, adding the call would improve consistency across the test methods.

♻️ Suggested consistency improvement
   `@Test`
   public void testOnRatingTypeChange() {
     testSetPlayerInfoBean();
     instance.ratingTypeComboBox.setValue(leaderboard);
     instance.onRatingTypeChange();
+    waitForFxEvents();
     verify(statisticsService, times(2)).getRatingHistory(eq(player), eq(leaderboard), any());
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/com/faforever/client/player/PlayerInfoWindowControllerTest.java`
around lines 186 - 192, Add a call to waitForFxEvents() before the verification
in testOnRatingTypeChange to match the other tests' pattern: after calling
instance.ratingTypeComboBox.setValue(leaderboard) and
instance.onRatingTypeChange(), invoke waitForFxEvents() and then run the
verify(statisticsService, times(2)).getRatingHistory(eq(player),
eq(leaderboard), any()) assertion to ensure JavaFX events are flushed
consistently across tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@src/test/java/com/faforever/client/player/PlayerInfoWindowControllerTest.java`:
- Around line 186-192: Add a call to waitForFxEvents() before the verification
in testOnRatingTypeChange to match the other tests' pattern: after calling
instance.ratingTypeComboBox.setValue(leaderboard) and
instance.onRatingTypeChange(), invoke waitForFxEvents() and then run the
verify(statisticsService, times(2)).getRatingHistory(eq(player),
eq(leaderboard), any()) assertion to ensure JavaFX events are flushed
consistently across tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cd536040-f5d4-4035-87fa-76f8c77fdb1d

📥 Commits

Reviewing files that changed from the base of the PR and between 57cd3d3 and 147370b.

📒 Files selected for processing (3)
  • src/main/java/com/faforever/client/player/PlayerInfoWindowController.java
  • src/test/java/com/faforever/client/player/PlayerInfoWindowControllerTest.java
  • src/test/java/com/faforever/client/stats/StatisticsServiceTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/test/java/com/faforever/client/stats/StatisticsServiceTest.java
  • src/main/java/com/faforever/client/player/PlayerInfoWindowController.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants