Skip to content

Add in-app community config browser - #1782

Open
Nightwalker743 wants to merge 15 commits into
utkarshdalal:masterfrom
Nightwalker743:feature/community-config-browser
Open

Add in-app community config browser#1782
Nightwalker743 wants to merge 15 commits into
utkarshdalal:masterfrom
Nightwalker743:feature/community-config-browser

Conversation

@Nightwalker743

@Nightwalker743 Nightwalker743 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Description

Adds a Community configs entry to each game's settings menu, allowing users to browse GameNative compatibility reports for the current game. Results can be filtered by same device, same GPU, or compatible GPU family and sorted by highest rated or newest. Selected configs are validated and applied automatically, with missing manifest dependencies downloaded as needed. Launch arguments and environment variables can be reviewed and applied explicitly, while the existing Use known config behavior remains unchanged.

Recording

https://drive.google.com/file/d/1adYFW1s6apMWX18eIdeAwfwEm-oWhbhf/view?usp=drivesdk

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

Adds an in-app Community Configs browser in each game's settings to discover, filter, and apply shared configs from GameNative. Includes a safe apply flow with validation, dependency downloads, optional launch settings, and graceful rate limit handling.

  • New Features

    • New “Browse community configs” option in the game menu opens a dialog for the current title.
    • Filters: same device, same GPU, compatible GPU family; sort by highest rated or newest.
    • Selected configs are validated and sanitized; missing components are fetched and applied automatically.
    • Launch arguments and environment variables are shown for explicit opt-in.
    • Selections persist while browsing and after refresh; clearer errors and retries.
    • Backend service reduces request pressure, respects rate limits, avoids eager pagination, and updates throttle cooldown while waiting.
    • Added localized strings for all supported languages.
    • Tests cover the service and the apply flow.
  • Refactors

    • Introduced BestConfigService.parseConfigResult returning config data plus missing components.
    • Updated ContainerConfigTransfer to use the new result and the missing components dialog.
    • Added helpers isValidCommunityConfig and prepareCommunityConfigForApply.
    • UI: new BrowseCommunityConfigs menu item with a search icon; Custom games omit “Use known config” but include the community browser.

Written for commit a4982cc. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added a community configurations browser to game options, including browsing, sorting (top/newest), full metadata, and “load more”.
    • Added preview and apply for community configs, with optional launch arguments and environment variables.
    • Added improved, localized community-config UI across languages.
  • Bug Fixes
    • Improved deduplication for paginated, concurrently loaded results to prevent repeats or mismatches.
    • Improved missing-component handling and “apply anyway” validation to reduce failed applies.
    • Enhanced reliability of community config loading under throttling/rate limits.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds community configuration discovery, compatibility filtering, browsing, preview, and application. The change introduces structured parsing with value preservation, composite run identity handling, localized UI resources, and tests for retrieval concurrency and configuration application.

Changes

Community configuration feature

Layer / File(s) Summary
Community configuration retrieval and identity
app/src/main/java/app/gamenative/api/CommunityConfigService.kt, app/src/main/java/app/gamenative/ui/component/dialog/CommunityConfigsDialog.kt, app/src/test/java/app/gamenative/api/CommunityConfigServiceTest.kt
Merges and paginates runs using composite identity keys and permits concurrent device requests under throttle control.
Structured parsing and value-preserving application
app/src/main/java/app/gamenative/utils/BestConfigService.kt, app/src/main/java/app/gamenative/ui/util/ContainerConfigTransfer.kt, app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt, app/src/test/java/app/gamenative/utils/*Test.kt
Adds ParsedConfigResult, propagates missing components, preserves community values when requested, and updates known, imported, and community configuration application paths.
Browsing, preview, and apply flow
app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt, app/src/main/java/app/gamenative/ui/component/dialog/CommunityConfigsDialog.kt, app/src/main/java/app/gamenative/ui/enums/AppOptionMenuType.kt, app/src/main/java/app/gamenative/ui/screen/library/components/GameOptionsPanel.kt, app/src/main/java/app/gamenative/ui/screen/library/appscreen/CustomGameAppScreen.kt
Adds the menu action, Compose request lifecycle, community container state, configuration application flow, and Custom Game integration.
Localized interface and validation
app/src/main/res/values*/strings.xml, app/src/test/java/app/gamenative/utils/*Test.kt
Adds localized community-configuration text and validates parsing, fallback filtering, sanitization, optional settings, and container application.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LibraryScreen
  participant CommunityConfigsDialog
  participant CommunityConfigService
  participant BestConfigService
  participant Container
  LibraryScreen->>CommunityConfigsDialog: open community configurations
  CommunityConfigsDialog->>CommunityConfigService: fetch compatible runs
  CommunityConfigService-->>CommunityConfigsDialog: merged configuration results
  CommunityConfigsDialog->>LibraryScreen: select configuration
  LibraryScreen->>BestConfigService: parse with preserved values
  BestConfigService-->>LibraryScreen: parsed config and missing components
  LibraryScreen->>Container: apply configuration
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 6.00% 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 main change: adding an in-app community config browser.
Description check ✅ Passed The description includes all required sections and provides clear context, recording, type of change, and checklist items.
✨ 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.

@Nightwalker743
Nightwalker743 marked this pull request as ready for review July 30, 2026 01:01

@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 (7)
app/src/main/java/app/gamenative/api/CommunityConfigService.kt (3)

939-955: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Timestamp is re-parsed on every comparison.

createdAtMillis() runs OffsetDateTime.parse inside the comparator, so each run's createdAt is parsed O(log n) times (up to ~200 runs per compatible page). Precomputing the key once is cheaper and keeps ordering identical.

♻️ Suggested refactor
-    val comparator = when (sort) {
-        CommunityConfigSort.HIGHEST_RATED -> compareByDescending<CommunityConfigRun> { it.rating }
-            .thenByDescending { it.createdAtMillis() }
-            .thenByDescending { it.id }
-        CommunityConfigSort.NEWEST -> compareByDescending<CommunityConfigRun> { it.createdAtMillis() }
-            .thenByDescending { it.id }
-    }
-    return runs.sortedWith(comparator)
+    val createdAt = runs.associateBy({ it.id }, { it.createdAtMillis() })
+    fun key(run: CommunityConfigRun) = createdAt[run.id] ?: Long.MIN_VALUE
+    val comparator = when (sort) {
+        CommunityConfigSort.HIGHEST_RATED -> compareByDescending<CommunityConfigRun> { it.rating }
+            .thenByDescending { key(it) }
+            .thenByDescending { it.id }
+        CommunityConfigSort.NEWEST -> compareByDescending<CommunityConfigRun> { key(it) }
+            .thenByDescending { it.id }
+    }
+    return runs.sortedWith(comparator)
🤖 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/api/CommunityConfigService.kt` around lines
939 - 955, Update sortCommunityRuns to precompute each run’s parsed
createdAtMillis value once before sorting, retaining Long.MIN_VALUE for invalid
timestamps. Sort the precomputed entries using the existing HIGHEST_RATED and
NEWEST ordering keys, then return the original CommunityConfigRun objects in the
resulting order.

155-192: 🩺 Stability & Availability | 🔵 Trivial | 🏗️ Heavy lift

awaitPermit sleeps while holding the monitor and isn't cancellation-aware.

awaitPermit() is @Synchronized and calls sleepNanos (default TimeUnit.NANOSECONDS.sleep) inside the lock, so it blocks a Dispatchers.IO thread for up to the full window and cannot be interrupted by coroutine cancellation. When the user dismisses the community dialog, the in-flight throttled request keeps the thread parked, and postpone from any other thread would block behind it too.

Consider making the throttle suspending (Mutex + delay) or at least releasing the lock around the wait, keeping the injectable clock/sleep hooks for the existing 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 `@app/src/main/java/app/gamenative/api/CommunityConfigService.kt` around lines
155 - 192, Refactor awaitPermit so throttling waits do not hold the synchronized
monitor or block an IO thread, while remaining cancellation-aware. Use a
suspending coordination mechanism such as Mutex with delay, or otherwise release
the lock before waiting and reacquire it to recheck quota and blockedUntilNanos;
preserve the injectable clock and sleep hooks required by existing tests, and
ensure postpone can proceed concurrently.

296-340: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Concurrency here is nullified by the global request gate.

Semaphore(minOf(deviceIds.size, MAX_CONCURRENT_DEVICE_REQUESTS)) plus async/awaitAll suggests up to 4 parallel device fetches, but execute() wraps every call in synchronized(requestGate), so all requests run strictly one at a time anyway. Either drop the semaphore/async fan-out (simpler, same behavior) or narrow the gate to the throttle bookkeeping only so the intended parallelism actually happens.

🤖 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/api/CommunityConfigService.kt` around lines
296 - 340, Remove the ineffective Semaphore and async/awaitAll fan-out around
fetchDeviceConfigSlice, and fetch each valid device slice sequentially while
preserving the existing slice merging, sorting, pagination, and totals behavior.
Keep the change scoped to the multi-device branch of the surrounding
configuration fetch flow.
app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt (1)

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

Use dedicated state instead of reusing containerData.

containerData also backs ContainerConfigDialog's edit buffer, and it starts as ContainerData(), so the community dialog renders one frame with empty currentLaunchArguments/currentEnvironmentVariables before the IO load lands (and the reload mutates the config dialog's state if both are active). A separate communityContainerData state avoids both.

🤖 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/ui/screen/library/appscreen/BaseAppScreen.kt`
around lines 1556 - 1584, Introduce dedicated community dialog state, such as
communityContainerData, initialized independently from containerData, and update
the communityConfigsRequested LaunchedEffect to load into it. Pass this state to
CommunityConfigsDialog for currentLaunchArguments and
currentEnvironmentVariables, leaving containerData exclusively for
ContainerConfigDialog.
app/src/main/java/app/gamenative/utils/BestConfigService.kt (1)

886-896: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Force-apply returns a populated config and a non-empty missingComponents.

Callers branch on missingComponents.isNotEmpty() before looking at config (see BaseAppScreen.applyKnownConfigForLibraryItem and ContainerConfigTransfer.importConfig), so any future caller passing forceApply = true to parseConfigResult would re-open the missing-components dialog despite a successful parse. Today it's safe only because those paths call parseConfigToContainerData. Consider clearing (or renaming to e.g. replacedComponents) when defaults were substituted.

Also applies to: 988-992

🤖 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/utils/BestConfigService.kt` around lines 886
- 896, Update parseConfigResult’s forceApply path after replaceWithDefaults so
the returned ParsedConfigResult does not report substituted components as
missing; clear the collection or use a distinct replaced-components status
before constructing the populated result. Preserve the non-force path’s
non-empty missingComponents return so callers such as
BaseAppScreen.applyKnownConfigForLibraryItem and
ContainerConfigTransfer.importConfig still reject unavailable components.
app/src/test/java/app/gamenative/api/CommunityConfigServiceTest.kt (1)

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

Helper produces invalid createdAt for ids > 9.

"2026-01-0${runId}" yields 2026-01-011T00:00:00Z for the runId = deviceId = 11/12 cases (line 662), which fails OffsetDateTime.parse and silently falls back to Long.MIN_VALUE in sortCommunityRuns. It doesn't break the current assertions, but it will mask ordering bugs if those fixtures are reused. Consider padding, e.g. "2026-01-%02d".format(runId.coerceIn(1, 28)).

🤖 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/api/CommunityConfigServiceTest.kt` around
lines 826 - 853, The configPage helper generates invalid ISO dates when runId
exceeds 9; update createdAt formatting in configPage to produce a two-digit,
valid day value while constraining it to the supported day range. Preserve valid
timestamp parsing for all fixture IDs, including the existing 11/12 cases.
app/src/main/java/app/gamenative/ui/component/dialog/CommunityConfigsDialog.kt (1)

787-804: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid magic string for match type comparison.

matchType == "fallback_match" hardcodes a literal that must stay in sync with whatever communityConfigMatchType() (imported from CommunityConfigService) actually returns. A rename/typo on either side silently disables this GPU-mismatch warning without any compile-time signal.

Consider exposing a shared constant (e.g. CommunityConfigService.MATCH_TYPE_FALLBACK) alongside communityConfigMatchType() and referencing it here instead of the literal.

🤖 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/ui/component/dialog/CommunityConfigsDialog.kt`
around lines 787 - 804, Replace the hardcoded "fallback_match" comparison in the
fallback warning block with a shared constant exposed by CommunityConfigService,
such as MATCH_TYPE_FALLBACK, and ensure communityConfigMatchType() uses the same
constant so the warning remains synchronized.
🤖 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/ui/component/dialog/CommunityConfigsDialog.kt`:
- Around line 262-286: Update the pagination merge in the coroutine around
sortCommunityRuns to deduplicate CommunityConfigRun entries using the same
composite identity as the LazyColumn: id, device.id, and createdAt. Replace
distinctBy { it.id } with a matching composite key while preserving the existing
sorting, totals, and pagination behavior.

In `@app/src/main/java/app/gamenative/utils/BestConfigService.kt`:
- Around line 921-928: Update the startupSelection mapping in the best-config
parser to always emit a Byte, including when preserveConfigValues is true. Align
this branch with resolveMissingManifestInstallRequests and the Int? expectations
in applyBestConfigMapToContainerData, while preserving the existing fallback and
filtering behavior.

In `@app/src/main/res/values-ru/strings.xml`:
- Line 130: Update the community_config_highest_rated Russian string to
explicitly mean “С самым высоким рейтингом,” replacing the ambiguous “По
рейтингу” translation while preserving the existing resource key.

---

Nitpick comments:
In `@app/src/main/java/app/gamenative/api/CommunityConfigService.kt`:
- Around line 939-955: Update sortCommunityRuns to precompute each run’s parsed
createdAtMillis value once before sorting, retaining Long.MIN_VALUE for invalid
timestamps. Sort the precomputed entries using the existing HIGHEST_RATED and
NEWEST ordering keys, then return the original CommunityConfigRun objects in the
resulting order.
- Around line 155-192: Refactor awaitPermit so throttling waits do not hold the
synchronized monitor or block an IO thread, while remaining cancellation-aware.
Use a suspending coordination mechanism such as Mutex with delay, or otherwise
release the lock before waiting and reacquire it to recheck quota and
blockedUntilNanos; preserve the injectable clock and sleep hooks required by
existing tests, and ensure postpone can proceed concurrently.
- Around line 296-340: Remove the ineffective Semaphore and async/awaitAll
fan-out around fetchDeviceConfigSlice, and fetch each valid device slice
sequentially while preserving the existing slice merging, sorting, pagination,
and totals behavior. Keep the change scoped to the multi-device branch of the
surrounding configuration fetch flow.

In
`@app/src/main/java/app/gamenative/ui/component/dialog/CommunityConfigsDialog.kt`:
- Around line 787-804: Replace the hardcoded "fallback_match" comparison in the
fallback warning block with a shared constant exposed by CommunityConfigService,
such as MATCH_TYPE_FALLBACK, and ensure communityConfigMatchType() uses the same
constant so the warning remains synchronized.

In
`@app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt`:
- Around line 1556-1584: Introduce dedicated community dialog state, such as
communityContainerData, initialized independently from containerData, and update
the communityConfigsRequested LaunchedEffect to load into it. Pass this state to
CommunityConfigsDialog for currentLaunchArguments and
currentEnvironmentVariables, leaving containerData exclusively for
ContainerConfigDialog.

In `@app/src/main/java/app/gamenative/utils/BestConfigService.kt`:
- Around line 886-896: Update parseConfigResult’s forceApply path after
replaceWithDefaults so the returned ParsedConfigResult does not report
substituted components as missing; clear the collection or use a distinct
replaced-components status before constructing the populated result. Preserve
the non-force path’s non-empty missingComponents return so callers such as
BaseAppScreen.applyKnownConfigForLibraryItem and
ContainerConfigTransfer.importConfig still reject unavailable components.

In `@app/src/test/java/app/gamenative/api/CommunityConfigServiceTest.kt`:
- Around line 826-853: The configPage helper generates invalid ISO dates when
runId exceeds 9; update createdAt formatting in configPage to produce a
two-digit, valid day value while constraining it to the supported day range.
Preserve valid timestamp parsing for all fixture IDs, including the existing
11/12 cases.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f114fad1-5b49-468e-927c-af89bd51d108

📥 Commits

Reviewing files that changed from the base of the PR and between d61318b and 76d0862.

📒 Files selected for processing (25)
  • app/src/main/java/app/gamenative/api/CommunityConfigService.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/CommunityConfigsDialog.kt
  • app/src/main/java/app/gamenative/ui/enums/AppOptionMenuType.kt
  • app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt
  • app/src/main/java/app/gamenative/ui/screen/library/components/GameOptionsPanel.kt
  • app/src/main/java/app/gamenative/ui/util/ContainerConfigTransfer.kt
  • app/src/main/java/app/gamenative/utils/BestConfigService.kt
  • app/src/main/res/values-da/strings.xml
  • app/src/main/res/values-de/strings.xml
  • app/src/main/res/values-es/strings.xml
  • app/src/main/res/values-fr/strings.xml
  • app/src/main/res/values-it/strings.xml
  • app/src/main/res/values-ja/strings.xml
  • app/src/main/res/values-ko/strings.xml
  • app/src/main/res/values-pl/strings.xml
  • app/src/main/res/values-pt-rBR/strings.xml
  • app/src/main/res/values-ro/strings.xml
  • app/src/main/res/values-ru/strings.xml
  • app/src/main/res/values-uk/strings.xml
  • app/src/main/res/values-zh-rCN/strings.xml
  • app/src/main/res/values-zh-rTW/strings.xml
  • app/src/main/res/values/strings.xml
  • app/src/test/java/app/gamenative/api/CommunityConfigServiceTest.kt
  • app/src/test/java/app/gamenative/utils/BestConfigServiceTest.kt
  • app/src/test/java/app/gamenative/utils/CommunityConfigApplicationTest.kt

Comment thread app/src/main/java/app/gamenative/utils/BestConfigService.kt
Comment thread app/src/main/res/values-ru/strings.xml 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 25 files

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

Comment thread app/src/main/java/app/gamenative/api/CommunityConfigService.kt Outdated
Comment thread app/src/main/java/app/gamenative/api/CommunityConfigService.kt
Comment thread app/src/main/java/app/gamenative/api/CommunityConfigService.kt
Comment thread app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt Outdated
Comment thread app/src/main/res/values-ru/strings.xml 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 6 files (changes from recent commits).

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

Re-trigger cubic

Comment thread app/src/main/java/app/gamenative/api/CommunityConfigService.kt
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.

1 participant