Skip to content

Hide platform-hidden Steam and GOG games from the library - #1796

Open
whatobiplays wants to merge 11 commits into
utkarshdalal:masterfrom
whatobiplays:obi/hide-hidden-games
Open

Hide platform-hidden Steam and GOG games from the library#1796
whatobiplays wants to merge 11 commits into
utkarshdalal:masterfrom
whatobiplays:obi/hide-hidden-games

Conversation

@whatobiplays

@whatobiplays whatobiplays commented Aug 7, 2026

Copy link
Copy Markdown

Description

Adds support for hiding platform-hidden games from the main library tabs.

  • Steam: games in the Steam "Hidden" collection are excluded from every library tab by default. Selecting the Hidden collection in the Steam collections filter still reveals them.
  • GOG: hidden product IDs are fetched from GOG's account library endpoint (account/getFilteredProducts?hiddenFlag=1, embed host with www fallback) and cached locally. Hidden GOG games are excluded from library tabs by default.
  • New Interface setting "Show hidden games by default" (default off) overrides the default exclusion for both Steam and GOG.
  • Hidden metadata refreshes on library load and during GOG library sync; fetch failures keep the previous cache and fail open rather than failing the sync.
  • No database schema changes or Room migration.

Tests added for the hidden-game filter, GOG hidden-page parser, hidden-ID repository/cache, preference defaults, and persisted count behavior. Legacy and modern unit-test suites compile; targeted feature tests pass.

Known limitation: GOG hidden state comes from the gog.com account ("Hidden" folder). Games hidden only inside the Galaxy desktop client's local library are not exposed by GOG's API and cannot be filtered.

Recording

IMG_3209_480p-2.mov

Type of Change

  • Bug fix
  • Performance / stability improvement
  • Compatibility improvements
  • Other (requires prior approval)

Checklist

  • If I have access to #code-changes, I have discussed this change there and it has been green-lighted. If I do not have access, I have still provided clear context in this PR. If I skip both, I accept that this change may face delays in review, may not be reviewed at all, or may be closed.
  • This change aligns with the current project scope (core functionality, stability, or performance). If not, it has been explicitly approved beforehand.
  • I have attached a recording of the change.
  • I have read and agree to the contribution guidelines in CONTRIBUTING.md.

Summary by cubic

Hides platform‑hidden Steam and GOG games from library tabs by default, with a toggle to show them. Preserves Steam’s Hidden collection behavior and keeps GOG hidden state consistent without flicker or stale writes.

  • New Features

    • Steam: Hidden apps are excluded by default; selecting the Hidden collection shows them. Badges: Hidden keeps its full count; others reflect visible games only.
    • GOG: Hidden IDs fetched from account/getFilteredProducts?hiddenFlag=1 (embed host with www fallback), stored in gog_games.hidden. Hidden state is applied to new inserts and refreshed rows using only the latest committed hidden set.
    • GOG refreshes: Run on library load and during sync; failures keep the previous state; flags clear on logout. Hidden-ID refreshes are generation‑tracked to avoid stale overwrites; single‑game refreshes preserve existing hidden flags; cancellations propagate; upserts keep existing cover art.
    • Setting: Added “Show hidden games by default” (off). Toggling emits HiddenGamesSettingChanged and refreshes the library immediately.
    • Filtering and counts: Centralized in HiddenGameFilter (missing metadata fails open). Persisted library counts store post‑hidden totals via LibraryCounts.persist.
    • Limitation: Only GOG account‑level Hidden is supported; Galaxy client local‑only hidden flags aren’t exposed.
  • Migration

    • Database: Added hidden column to gog_games with auto‑migration 25→26. No manual steps.

Written for commit 909b3b0. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added support for hiding Steam and GOG games.
    • Added a setting to show hidden games by default.
    • Steam’s Hidden collection displays hidden titles when selected.
    • Library counts reflect hidden-game visibility.
  • Bug Fixes
    • Hidden GOG statuses persist during library refreshes and clear on logout.
    • Database updates preserve existing hidden-game selections.
  • Tests
    • Added coverage for filtering, persistence, synchronization, and library count updates.

@coderabbitai

coderabbitai Bot commented Aug 7, 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

Walkthrough

Adds persisted hidden-game state for GOG, synchronizes hidden products from GOG, filters Steam and GOG libraries, adds a visibility setting, and persists filtered library counts. Room schema version 26 includes the migration and updated schema metadata.

Changes

Hidden game visibility

Layer / File(s) Summary
Database schema and hidden state
app/schemas/..., app/src/main/java/app/gamenative/data/GOGGame.kt, app/src/main/java/app/gamenative/db/..., app/src/test/java/app/gamenative/db/...
Room schema version 26 adds persisted GOG hidden state. DAO operations replace hidden flags transactionally and preserve them during upserts.
GOG hidden-game synchronization
app/src/main/java/app/gamenative/service/gog/..., app/src/test/java/app/gamenative/service/gog/...
GOG hidden products are fetched with pagination, parsed with validation, synchronized with generation tracking, persisted after successful refresh, and cleared during logout.
Library filtering and settings
app/src/main/java/app/gamenative/PrefManager.kt, app/src/main/java/app/gamenative/ui/..., app/src/main/java/app/gamenative/data/HiddenGameFilter.kt, app/src/main/java/app/gamenative/steam/SteamCollectionFilter.kt, app/src/main/res/values/strings.xml, app/src/test/java/app/gamenative/...
A setting controls default hidden-game visibility. Steam and GOG filtering, Hidden collection counts, event handling, and filtered library count persistence are added. Tests cover preferences, filtering, counts, and asynchronous persistence.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SettingsGroupInterface
  participant PrefManager
  participant LibraryViewModel
  participant GOGManager
  participant GOGApiClient
  participant GOGGameDao
  SettingsGroupInterface->>PrefManager: persist showHiddenGamesByDefault
  SettingsGroupInterface->>LibraryViewModel: emit HiddenGamesSettingChanged
  LibraryViewModel->>GOGManager: refreshHiddenIds()
  GOGManager->>GOGApiClient: getHiddenGameIds(context)
  GOGManager->>GOGGameDao: applyHiddenFlags(hiddenIds)
  LibraryViewModel->>LibraryViewModel: filter Steam and GOG games
  LibraryViewModel->>PrefManager: persist filtered library counts
Loading

Possibly related PRs

Suggested reviewers: utkarshdalal

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.33% 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
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.
Title check ✅ Passed The title clearly summarizes the primary change: hiding platform-hidden Steam and GOG games from the library.
Description check ✅ Passed The description covers the change, behavior, tests, limitation, recording, type, and checklist, but incorrectly states that there are no database schema or Room migration changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

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.

Wouldn't it just be easier to add a new column to our gog db entries rather than hold an entirely new table? Seems excessive

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

That’s fair. I can roll it into the GOG table, wasn’t sure how y’all felt about migrations, but I guess it’s a very lightweight one

@whatobiplays
whatobiplays marked this pull request as ready for review August 9, 2026 00:53

@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: 3

🧹 Nitpick comments (2)
app/src/main/java/app/gamenative/service/gog/GOGApiClient.kt (2)

205-205: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Move the fallback host into GOGConstants.

The primary host uses GOGConstants.GOG_EMBED_URL, but the fallback host is a string literal. Declare the www host next to the other GOG hosts so both endpoints stay configurable in one place.

🤖 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 `@app/src/main/java/app/gamenative/service/gog/GOGApiClient.kt` at line 205,
Move the fallback host into GOGConstants by declaring a named www-host constant
alongside the existing GOG host constants, then update fetchHiddenGameIdsFrom to
reference that constant instead of the hardcoded URL.

241-271: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Bound the pagination loop.

totalPages is re-read from every response, and the loop continues while page <= totalPages. A wrong or hostile response value drives a long sequence of blocking HTTP calls during library sync. Each call also holds a 30-second read timeout, so the worst case is a very long sync. Add a page cap and stop when it is reached.

♻️ Proposed guard
+        val maxPages = 100
         return try {
             var page = 1
             var totalPages = 1
             val hiddenIds = mutableSetOf<String>()
-            while (page <= totalPages) {
+            while (page <= totalPages && page <= maxPages) {

Log a warning when the cap truncates the result, so a silent partial hidden set is visible in diagnostics.

🤖 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 `@app/src/main/java/app/gamenative/service/gog/GOGApiClient.kt` around lines
241 - 271, Bound the pagination in the hidden-game retrieval loop around
totalPages/page by enforcing a fixed maximum page count and stopping once that
cap is reached, regardless of response-provided totalPages values. When the cap
truncates pagination, emit a Timber warning indicating that hidden-game results
may be incomplete, while preserving normal failure handling and page progression
below the cap.
🤖 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 `@app/src/main/java/app/gamenative/db/dao/GOGGameDao.kt`:
- Around line 75-87: Update applyHiddenFlags in GOGGameDao to call markHidden
only when hiddenIds.isNotEmpty(), while always clearing existing hidden flags.
Add a GOGGameDaoTest case covering applyHiddenFlags(emptyList()) and verifying
it completes without executing invalid empty-IN SQL.

In `@app/src/main/java/app/gamenative/service/gog/GOGManager.kt`:
- Around line 155-178: Update refreshHiddenIds so failures from
gogGameDao.applyHiddenFlags are caught and logged without propagating,
preserving the method’s null-on-failure behavior and leaving existing flags
unchanged when persistence fails. Keep API fetch handling and successful ID
return behavior intact.

In `@app/src/main/java/app/gamenative/ui/model/LibraryViewModel.kt`:
- Around line 655-660: Update the collection-count calculation in the
LibraryViewModel flow around steamCollectionCounts to use the default-visible
Steam IDs for normal collections, while retaining the pre-hidden count only for
SteamCollection.ID_HIDDEN. Ensure collection badges match the games remaining
after hidden filtering and preserve the Hidden collection’s full count.

---

Nitpick comments:
In `@app/src/main/java/app/gamenative/service/gog/GOGApiClient.kt`:
- Line 205: Move the fallback host into GOGConstants by declaring a named
www-host constant alongside the existing GOG host constants, then update
fetchHiddenGameIdsFrom to reference that constant instead of the hardcoded URL.
- Around line 241-271: Bound the pagination in the hidden-game retrieval loop
around totalPages/page by enforcing a fixed maximum page count and stopping once
that cap is reached, regardless of response-provided totalPages values. When the
cap truncates pagination, emit a Timber warning indicating that hidden-game
results may be incomplete, while preserving normal failure handling and page
progression below the cap.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d2bb3d72-2800-4327-bce9-e3a429460330

📥 Commits

Reviewing files that changed from the base of the PR and between 78e9343 and 8009316.

📒 Files selected for processing (23)
  • app/schemas/app.gamenative.db.PluviaDatabase/26.json
  • app/src/main/java/app/gamenative/PrefManager.kt
  • app/src/main/java/app/gamenative/data/GOGGame.kt
  • app/src/main/java/app/gamenative/data/HiddenGameFilter.kt
  • app/src/main/java/app/gamenative/db/PluviaDatabase.kt
  • app/src/main/java/app/gamenative/db/dao/GOGGameDao.kt
  • app/src/main/java/app/gamenative/events/AndroidEvent.kt
  • app/src/main/java/app/gamenative/service/gog/GOGApiClient.kt
  • app/src/main/java/app/gamenative/service/gog/GOGManager.kt
  • app/src/main/java/app/gamenative/service/gog/GOGService.kt
  • app/src/main/java/app/gamenative/service/gog/GogFilteredProductsParser.kt
  • app/src/main/java/app/gamenative/steam/SteamCollectionFilter.kt
  • app/src/main/java/app/gamenative/ui/data/LibraryCounts.kt
  • app/src/main/java/app/gamenative/ui/model/LibraryViewModel.kt
  • app/src/main/java/app/gamenative/ui/screen/settings/SettingsGroupInterface.kt
  • app/src/main/res/values/strings.xml
  • app/src/test/java/app/gamenative/PrefManagerHiddenGamesDefaultsTest.kt
  • app/src/test/java/app/gamenative/data/HiddenGameFilterTest.kt
  • app/src/test/java/app/gamenative/db/dao/GOGGameDaoTest.kt
  • app/src/test/java/app/gamenative/service/gog/GogFilteredProductsParserTest.kt
  • app/src/test/java/app/gamenative/steam/SteamCollectionFilterTest.kt
  • app/src/test/java/app/gamenative/ui/data/LibraryCountsTest.kt
  • app/src/test/java/app/gamenative/utils/TestPrefManager.kt

Comment thread app/src/main/java/app/gamenative/db/dao/GOGGameDao.kt
Comment thread app/src/main/java/app/gamenative/service/gog/GOGManager.kt
Comment thread app/src/main/java/app/gamenative/ui/model/LibraryViewModel.kt Outdated

@cubic-dev-ai cubic-dev-ai 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.

2 issues found across 23 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/src/main/java/app/gamenative/service/gog/GOGService.kt">

<violation number="1" location="app/src/main/java/app/gamenative/service/gog/GOGService.kt:155">
P2: Logging out during an in-flight hidden-ID refresh can leave the prior account’s hidden flags on installed rows, because this clear races with `applyHiddenFlags`. Serialize/cancel-and-join hidden refreshes before clearing flags (or guard their DB write against logout) so logged-out metadata cannot be restored.</violation>
</file>

<file name="app/src/test/java/app/gamenative/utils/TestPrefManager.kt">

<violation number="1" location="app/src/test/java/app/gamenative/utils/TestPrefManager.kt:59">
P2: installFakePrefManager replaces PrefManager's private backing store via reflection and never restores it. Since PrefManager is a singleton `object`, the fake persists across the whole shared JVM, so any other test class that later calls PrefManager (same-module local unit tests) will silently read/write this test's fake store instead of a real one, producing order-dependent results. Restore the previous field value afterward (or in an @After), or scope the fake per-test.</violation>
</file>

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.

Re-trigger cubic

Comment thread app/src/main/java/app/gamenative/db/dao/GOGGameDao.kt Outdated
Timber.i("[GOGService] All non-installed GOG games removed from database")

// Hidden-game metadata belongs to the logged-out account.
instance.gogManager.clearHiddenFlags()

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.

P2: Logging out during an in-flight hidden-ID refresh can leave the prior account’s hidden flags on installed rows, because this clear races with applyHiddenFlags. Serialize/cancel-and-join hidden refreshes before clearing flags (or guard their DB write against logout) so logged-out metadata cannot be restored.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/java/app/gamenative/service/gog/GOGService.kt, line 155:

<comment>Logging out during an in-flight hidden-ID refresh can leave the prior account’s hidden flags on installed rows, because this clear races with `applyHiddenFlags`. Serialize/cancel-and-join hidden refreshes before clearing flags (or guard their DB write against logout) so logged-out metadata cannot be restored.</comment>

<file context>
@@ -151,6 +151,9 @@ class GOGService : Service() {
                     Timber.i("[GOGService] All non-installed GOG games removed from database")
 
+                    // Hidden-game metadata belongs to the logged-out account.
+                    instance.gogManager.clearHiddenFlags()
+
                     // Stop the service
</file context>

Comment thread app/src/test/java/app/gamenative/utils/TestPrefManager.kt Outdated

val dataStoreField = PrefManager::class.java.getDeclaredField("dataStore")
dataStoreField.isAccessible = true
dataStoreField.set(PrefManager, fake)

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.

P2: installFakePrefManager replaces PrefManager's private backing store via reflection and never restores it. Since PrefManager is a singleton object, the fake persists across the whole shared JVM, so any other test class that later calls PrefManager (same-module local unit tests) will silently read/write this test's fake store instead of a real one, producing order-dependent results. Restore the previous field value afterward (or in an @after), or scope the fake per-test.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/test/java/app/gamenative/utils/TestPrefManager.kt, line 59:

<comment>installFakePrefManager replaces PrefManager's private backing store via reflection and never restores it. Since PrefManager is a singleton `object`, the fake persists across the whole shared JVM, so any other test class that later calls PrefManager (same-module local unit tests) will silently read/write this test's fake store instead of a real one, producing order-dependent results. Restore the previous field value afterward (or in an @After), or scope the fake per-test.</comment>

<file context>
@@ -0,0 +1,60 @@
+
+    val dataStoreField = PrefManager::class.java.getDeclaredField("dataStore")
+    dataStoreField.isAccessible = true
+    dataStoreField.set(PrefManager, fake)
+}
</file context>

Comment thread app/src/main/java/app/gamenative/service/gog/GOGApiClient.kt
Comment thread app/src/main/java/app/gamenative/service/gog/GOGManager.kt Outdated
Comment thread app/src/test/java/app/gamenative/ui/data/LibraryCountsTest.kt
Comment thread app/src/main/java/app/gamenative/ui/model/LibraryViewModel.kt Outdated

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 10 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/src/main/java/app/gamenative/steam/SteamCollectionFilter.kt">

<violation number="1" location="app/src/main/java/app/gamenative/steam/SteamCollectionFilter.kt:41">
P3: This change removes the last production caller of `SteamCollectionFilter.collectionCounts(...)`, leaving it used only by its own unit tests. Since the new `visibleCollectionCounts` supersedes it (and `libraryCounts` in LibraryViewModel now uses that instead), the now-production-unused `collectionCounts` is redundant maintenance surface. Consider removing it along with its two tests, or consolidating it into `visibleCollectionCounts`, to avoid keeping two overlapping count helpers.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread app/src/main/java/app/gamenative/service/gog/GOGManager.kt
* it keeps its full count (and stays discoverable), while every other collection counts only
* [visibleAppIds] so badges match the games actually rendered.
*/
fun visibleCollectionCounts(

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.

P3: This change removes the last production caller of SteamCollectionFilter.collectionCounts(...), leaving it used only by its own unit tests. Since the new visibleCollectionCounts supersedes it (and libraryCounts in LibraryViewModel now uses that instead), the now-production-unused collectionCounts is redundant maintenance surface. Consider removing it along with its two tests, or consolidating it into visibleCollectionCounts, to avoid keeping two overlapping count helpers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/java/app/gamenative/steam/SteamCollectionFilter.kt, line 41:

<comment>This change removes the last production caller of `SteamCollectionFilter.collectionCounts(...)`, leaving it used only by its own unit tests. Since the new `visibleCollectionCounts` supersedes it (and `libraryCounts` in LibraryViewModel now uses that instead), the now-production-unused `collectionCounts` is redundant maintenance surface. Consider removing it along with its two tests, or consolidating it into `visibleCollectionCounts`, to avoid keeping two overlapping count helpers.</comment>

<file context>
@@ -33,6 +33,21 @@ object SteamCollectionFilter {
+     * it keeps its full count (and stays discoverable), while every other collection counts only
+     * [visibleAppIds] so badges match the games actually rendered.
+     */
+    fun visibleCollectionCounts(
+        collections: List<SteamCollection>?,
+        visibleAppIds: Collection<Int>,
</file context>

@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 (3)
app/src/test/java/app/gamenative/ui/data/LibraryCountsTest.kt (3)

41-49: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use non-zero fixtures for every persisted count.

PrefManager defaults these properties to 0. The test writes and asserts 0 for custom, Epic, Epic-installed, and Amazon counts. It will still pass if LibraryCounts.persist stops writing those properties. Use distinct non-zero values to verify the complete persistence mapping.

Also applies to: 59-65

🤖 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 `@app/src/test/java/app/gamenative/ui/data/LibraryCountsTest.kt` around lines
41 - 49, Update the LibraryCounts.persist test fixtures and corresponding
assertions to use distinct non-zero values for every persisted count, including
customGames, epicGames, epicInstalledGames, and amazonInstalledGames. Preserve
the existing visible Steam, visible GOG, and installed GOG coverage while
ensuring each property’s persisted value is independently verified.

26-39: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use independent expected counts.

The test calculates visibleGogCount and visibleSteamCount with HiddenGameFilter, the production code that defines visibility. If either filter regresses, the test derives the incorrect expected value and can still pass. Use explicit expected counts, such as 2 for GOG and 1 for Steam, and test the filter predicates separately.

🤖 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 `@app/src/test/java/app/gamenative/ui/data/LibraryCountsTest.kt` around lines
26 - 39, Replace the derived visibleGogCount and visibleSteamCount values in
LibraryCountsTest with explicit expected counts (2 for GOG and 1 for Steam). Add
or retain separate assertions that directly test the HiddenGameFilter.passesGog
and HiddenGameFilter.passesSteam predicates, without using those production
predicates to calculate the expected library counts.

51-65: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Wait for every persisted value before asserting.

The test treats preference writes as asynchronous but waits only for Steam, GOG, and GOG-installed counts. The remaining assertions can run before their writes complete. Include all seven persisted properties in the awaitUntil condition.

🤖 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 `@app/src/test/java/app/gamenative/ui/data/LibraryCountsTest.kt` around lines
51 - 65, Update the awaitUntil condition in LibraryCountsTest to wait for all
seven persisted count properties asserted below: customGamesCount,
steamGamesCount, gogGamesCount, gogInstalledGamesCount, epicGamesCount,
epicInstalledGamesCount, and amazonInstalledGamesCount. Keep each expected value
aligned with its corresponding assertion before executing the assertions.
🤖 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 `@app/src/test/java/app/gamenative/ui/data/LibraryCountsTest.kt`:
- Around line 41-49: Update the LibraryCounts.persist test fixtures and
corresponding assertions to use distinct non-zero values for every persisted
count, including customGames, epicGames, epicInstalledGames, and
amazonInstalledGames. Preserve the existing visible Steam, visible GOG, and
installed GOG coverage while ensuring each property’s persisted value is
independently verified.
- Around line 26-39: Replace the derived visibleGogCount and visibleSteamCount
values in LibraryCountsTest with explicit expected counts (2 for GOG and 1 for
Steam). Add or retain separate assertions that directly test the
HiddenGameFilter.passesGog and HiddenGameFilter.passesSteam predicates, without
using those production predicates to calculate the expected library counts.
- Around line 51-65: Update the awaitUntil condition in LibraryCountsTest to
wait for all seven persisted count properties asserted below: customGamesCount,
steamGamesCount, gogGamesCount, gogInstalledGamesCount, epicGamesCount,
epicInstalledGamesCount, and amazonInstalledGamesCount. Keep each expected value
aligned with its corresponding assertion before executing the assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1e316843-f807-4bfc-90d5-c15f4fa5d743

📥 Commits

Reviewing files that changed from the base of the PR and between cece3ed and 252fb92.

📒 Files selected for processing (6)
  • app/src/main/java/app/gamenative/service/gog/GOGManager.kt
  • app/src/main/java/app/gamenative/steam/SteamCollectionFilter.kt
  • app/src/test/java/app/gamenative/PrefManagerHiddenGamesDefaultsTest.kt
  • app/src/test/java/app/gamenative/steam/SteamCollectionFilterTest.kt
  • app/src/test/java/app/gamenative/ui/data/LibraryCountsTest.kt
  • app/src/test/java/app/gamenative/utils/TestPrefManager.kt
💤 Files with no reviewable changes (2)
  • app/src/main/java/app/gamenative/steam/SteamCollectionFilter.kt
  • app/src/test/java/app/gamenative/steam/SteamCollectionFilterTest.kt
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/src/test/java/app/gamenative/PrefManagerHiddenGamesDefaultsTest.kt
  • app/src/test/java/app/gamenative/utils/TestPrefManager.kt
  • app/src/main/java/app/gamenative/service/gog/GOGManager.kt

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 6 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread app/src/main/java/app/gamenative/service/gog/GOGManager.kt

@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

Caution

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

⚠️ Outside diff range comments (2)
app/src/main/java/app/gamenative/service/gog/GOGManager.kt (2)

308-317: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve the hidden flag in single-game refreshes.

refreshSingleGame() inserts GOGGame with the default hidden = false, while refreshLibrary() applies hiddenIds before insertion. If the user hides a game after the last library refresh, refreshSingleGame() can overwrite it and expose the previously hidden game. Fetch the existing hidden state before inserting, and preserve it in GOGGameDao.insert() / insertAll() on primary-key conflict, or use upserts that keep existing hidden.

🤖 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 `@app/src/main/java/app/gamenative/service/gog/GOGManager.kt` around lines 308
- 317, The single-game refresh path must preserve an existing game's hidden
state instead of resetting it to false. Update refreshSingleGame and the
GOGGameDao insert/insertAll conflict handling so the current hidden value is
retained when the primary key already exists, while keeping refreshLibrary's
hiddenIds behavior unchanged.

218-224: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Propagate coroutine cancellation.

CancellationException is a RuntimeException; the broad catch (e: Exception) converts a canceled refreshHiddenIds() into a normal null. Re-throw CancellationException before handling database failures.

🤖 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 `@app/src/main/java/app/gamenative/service/gog/GOGManager.kt` around lines 218
- 224, Update the exception handling around gogGameDao.applyHiddenFlags in
refreshHiddenIds to rethrow CancellationException before the broad
database-failure handling; only non-cancellation exceptions should be logged and
converted to null, preserving coroutine cancellation propagation.
🤖 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 `@app/src/main/java/app/gamenative/service/gog/GOGManager.kt`:
- Around line 45-60: Make HiddenRefreshCoordinator.begin() atomically assign and
publish each refresh generation so counter and latestGeneration cannot be
observed out of order; serialize the increment-and-publication sequence or
atomically retain the maximum generation. Preserve isLatest() behavior while
ensuring refreshHiddenIds() rejects stale generations.

---

Outside diff comments:
In `@app/src/main/java/app/gamenative/service/gog/GOGManager.kt`:
- Around line 308-317: The single-game refresh path must preserve an existing
game's hidden state instead of resetting it to false. Update refreshSingleGame
and the GOGGameDao insert/insertAll conflict handling so the current hidden
value is retained when the primary key already exists, while keeping
refreshLibrary's hiddenIds behavior unchanged.
- Around line 218-224: Update the exception handling around
gogGameDao.applyHiddenFlags in refreshHiddenIds to rethrow CancellationException
before the broad database-failure handling; only non-cancellation exceptions
should be logged and converted to null, preserving coroutine cancellation
propagation.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8224c401-f2fe-4fc6-8c1b-88868cce4e2a

📥 Commits

Reviewing files that changed from the base of the PR and between 252fb92 and e6f37b0.

📒 Files selected for processing (5)
  • app/src/main/java/app/gamenative/service/gog/GOGManager.kt
  • app/src/test/java/app/gamenative/PrefManagerHiddenGamesDefaultsTest.kt
  • app/src/test/java/app/gamenative/service/gog/HiddenRefreshCoordinatorTest.kt
  • app/src/test/java/app/gamenative/ui/data/LibraryCountsTest.kt
  • app/src/test/java/app/gamenative/utils/TestPrefManager.kt
💤 Files with no reviewable changes (3)
  • app/src/test/java/app/gamenative/PrefManagerHiddenGamesDefaultsTest.kt
  • app/src/test/java/app/gamenative/ui/data/LibraryCountsTest.kt
  • app/src/test/java/app/gamenative/utils/TestPrefManager.kt

Comment thread app/src/main/java/app/gamenative/service/gog/GOGManager.kt Outdated

@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: 2

Caution

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

⚠️ Outside diff range comments (1)
app/src/main/java/app/gamenative/service/gog/GOGManager.kt (1)

266-269: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Do not insert new games after a superseded hidden-flag refresh.

refreshHiddenIds() returns null when its generation is superseded, and refreshLibrary() uses that to set hidden = false for new games. If a newer refresh persists before the older batch inserts its fresh games, later inserts can leave visible hidden games. Return a distinct isSuperseded result or apply the latest hidden set after the library insert.

🤖 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 `@app/src/main/java/app/gamenative/service/gog/GOGManager.kt` around lines 266
- 269, Update refreshLibrary() and refreshHiddenIds() so a superseded
hidden-flag refresh cannot cause newly inserted games to be assigned hidden =
false; return a distinct isSuperseded result or reapply the latest hidden set
after insertion, while preserving existing flags on refresh failure.
🤖 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 `@app/src/main/java/app/gamenative/service/gog/GOGManager.kt`:
- Around line 126-128: Update refreshSingleGame() to apply the current hidden-ID
set to the parsed game before persistence, including newly inserted and existing
hidden products. Persist it through upsertPreservingHidden(), then return the
record read from the DAO so the result reflects the stored hidden state rather
than the unadjusted parsed object.
- Around line 221-225: Update refreshLibrary() and startBackgroundSync() to
catch and rethrow CancellationException before their generic Exception handlers,
preserving cancellation propagation through both sync wrappers while retaining
existing failure handling for other exceptions.

---

Outside diff comments:
In `@app/src/main/java/app/gamenative/service/gog/GOGManager.kt`:
- Around line 266-269: Update refreshLibrary() and refreshHiddenIds() so a
superseded hidden-flag refresh cannot cause newly inserted games to be assigned
hidden = false; return a distinct isSuperseded result or reapply the latest
hidden set after insertion, while preserving existing flags on refresh failure.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e3fa53f1-e03a-4444-b155-1e2f2929a042

📥 Commits

Reviewing files that changed from the base of the PR and between e6f37b0 and 96cb36a.

📒 Files selected for processing (4)
  • app/src/main/java/app/gamenative/db/dao/GOGGameDao.kt
  • app/src/main/java/app/gamenative/service/gog/GOGApiClient.kt
  • app/src/main/java/app/gamenative/service/gog/GOGManager.kt
  • app/src/test/java/app/gamenative/db/dao/GOGGameDaoTest.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/main/java/app/gamenative/service/gog/GOGApiClient.kt
  • app/src/main/java/app/gamenative/db/dao/GOGGameDao.kt

Comment thread app/src/main/java/app/gamenative/service/gog/GOGManager.kt Outdated
Comment thread app/src/main/java/app/gamenative/service/gog/GOGManager.kt

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 8 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/src/main/java/app/gamenative/db/dao/GOGGameDao.kt">

<violation number="1" location="app/src/main/java/app/gamenative/db/dao/GOGGameDao.kt:36">
P2: This new method duplicates the hidden-preservation logic that `upsertPreservingInstallStatus` already covers, but only preserves the `hidden` flag and not install state. A single-game refresh via GOGManager.insertGame (refreshSingleGame path) inserts a game parsed with isInstalled=false/installPath="" (see parseGameObject), so an already-installed game's install status and path are still wiped on refresh. Reusing `upsertPreservingInstallStatus(listOf(game))` would preserve hidden and install state in one code path and avoid the duplication.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread app/src/main/java/app/gamenative/service/gog/GOGManager.kt
* single-game refresh cannot reset hidden state.
*/
@Transaction
suspend fun upsertPreservingHidden(game: GOGGame) {

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.

P2: This new method duplicates the hidden-preservation logic that upsertPreservingInstallStatus already covers, but only preserves the hidden flag and not install state. A single-game refresh via GOGManager.insertGame (refreshSingleGame path) inserts a game parsed with isInstalled=false/installPath="" (see parseGameObject), so an already-installed game's install status and path are still wiped on refresh. Reusing upsertPreservingInstallStatus(listOf(game)) would preserve hidden and install state in one code path and avoid the duplication.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/java/app/gamenative/db/dao/GOGGameDao.kt, line 36:

<comment>This new method duplicates the hidden-preservation logic that `upsertPreservingInstallStatus` already covers, but only preserves the `hidden` flag and not install state. A single-game refresh via GOGManager.insertGame (refreshSingleGame path) inserts a game parsed with isInstalled=false/installPath="" (see parseGameObject), so an already-installed game's install status and path are still wiped on refresh. Reusing `upsertPreservingInstallStatus(listOf(game))` would preserve hidden and install state in one code path and avoid the duplication.</comment>

<file context>
@@ -28,6 +28,16 @@ interface GOGGameDao {
+     * single-game refresh cannot reset hidden state.
+     */
+    @Transaction
+    suspend fun upsertPreservingHidden(game: GOGGame) {
+        val existing = getById(game.id)
+        insert(if (existing != null) game.copy(hidden = existing.hidden) else game)
</file context>

Comment thread app/src/main/java/app/gamenative/service/gog/GOGManager.kt Outdated
Comment thread app/src/main/java/app/gamenative/service/gog/GOGManager.kt

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 5 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread app/src/main/java/app/gamenative/service/gog/GOGManager.kt Outdated

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 1 file (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread app/src/main/java/app/gamenative/service/gog/GOGManager.kt Outdated
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