Add Steam collections#1633
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds Steam collections support across fetch, parse, cache, filtering, library state, and UI. The library now loads user collections after login, persists them, exposes selection state, filters apps by collection membership, and renders selectable collection options with localized strings. ChangesSteam Collections Filter
Sequence Diagram(s)sequenceDiagram
participant SteamService
participant CloudConfigStoreService
participant SteamCollectionParser
participant SteamCollectionRepository
participant LibraryViewModel
SteamService->>CloudConfigStoreService: download(user collections namespace)
CloudConfigStoreService-->>SteamService: Download response
SteamService->>SteamCollectionParser: parse(raw entries)
SteamCollectionParser-->>SteamService: ParseResult(collections, skippedDynamicCount)
SteamService->>SteamCollectionRepository: update(result)
SteamCollectionRepository-->>LibraryViewModel: collections / skippedDynamic emits
LibraryViewModel->>LibraryViewModel: reconcile + onFilterApps()
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
6 issues found across 31 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/ui/screen/library/LibraryScreen.kt">
<violation number="1" location="app/src/main/java/app/gamenative/ui/screen/library/LibraryScreen.kt:1092">
P2: Steam login state is passed to `LibraryOptionsPanel` via a non-observable static property (`SteamService.isLoggedIn`). In Compose, this will not trigger recomposition when the login state changes, so the collection filter UI can become stale.</violation>
</file>
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (1)
app/src/test/java/app/gamenative/steam/SteamCollectionFilterTest.kt (1)
51-60: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd a regression test for selecting an empty static collection.
SteamCollectionParserTestalready treats"added":[]as a valid static collection, but this suite never asserts the filter behavior when that collection is selected. That leaves room forallowedAppIds(...)to collapse an empty union tonulland fail open to “show all” instead of “show none”.Suggested test
class SteamCollectionFilterTest { private val favorites = SteamCollection("fav", "Favorites", setOf(440, 570)) private val shooters = SteamCollection("sht", "Shooters", setOf(730)) - private val all = listOf(favorites, shooters) + private val empty = SteamCollection("emp", "Empty", emptySet()) + private val all = listOf(favorites, shooters, empty) @@ `@Test` fun allowedAppIdsUnionsSelectedCollections() { assertEquals(setOf(440, 570), SteamCollectionFilter.allowedAppIds(setOf("fav"), all)) assertEquals(setOf(440, 570, 730), SteamCollectionFilter.allowedAppIds(setOf("fav", "sht"), all)) } + + `@Test` fun emptySelectedCollectionMatchesNothing() { + assertEquals(emptySet<Int>(), SteamCollectionFilter.allowedAppIds(setOf("emp"), all)) + assertFalse(SteamCollectionFilter.passes(440, setOf("emp"), all)) + } }🤖 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/steam/SteamCollectionFilterTest.kt` around lines 51 - 60, Add a regression test in SteamCollectionFilterTest for the empty static collection case so allowedAppIds() does not fail open. Reuse the existing all fixture and SteamCollectionFilter.allowedAppIds, then assert that selecting the static collection key for the empty collection returns an empty set rather than null. This should specifically cover the behavior around allowedAppIds and the static collection that SteamCollectionParserTest already accepts as valid.
🤖 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/data/SteamCollectionRepository.kt`:
- Around line 22-41: Persist the skipped-dynamic state alongside the cached
collections so offline/cold starts restore the same UI state. Update
SteamCollectionRepository.loadFromCache() and update() to read/write the
skippedDynamic flag together with the existing librarySteamCollectionsCache
snapshot, and make sure _skippedDynamic is restored from cache before the UI
relies on it. Use the existing SteamCollectionRepository, loadFromCache(), and
update(result: SteamCollectionParser.ParseResult) symbols to keep the fix
localized.
In `@app/src/main/java/app/gamenative/service/SteamService.kt`:
- Around line 3747-3749: The collections fetch launched from SteamService’s
fetchSteamCollections flow is not tied to the active Steam session, so it can
still write into SteamCollectionRepository after logout/reconnect. Track the
coroutine/job started by the scope.launch call, cancel it from clearUserData and
any reconnect/session reset path, and make fetchSteamCollections verify the
SteamID/session it started with still matches before applying results. Apply the
same session guard to the other fetch path referenced in SteamService so stale
CloudConfigStore results cannot repopulate or overwrite a newer account’s
collections.
In `@app/src/main/java/app/gamenative/ui/model/LibraryViewModel.kt`:
- Around line 600-609: The active Steam collection filter is only applied to
Steam apps in the LibraryViewModel flow, so non-Steam sources still leak into
the combined library and tab counts. Update the library assembly logic around
allowedSteamAppIds and steamFilteredBeforeCompatibility in LibraryViewModel to
also gate custom/GOG/Epic/Amazon entries whenever allowedSteamAppIds is not
null, or otherwise make the filter explicitly source-scoped so the combined
results and counts stay consistent with the selected collection.
In
`@app/src/main/java/app/gamenative/ui/screen/library/components/LibraryOptionsPanel.kt`:
- Around line 356-358: The empty-state handling in LibraryOptionsPanel should
not return early when steamCollections is empty, because that prevents the
status copy from rendering. Update the logic around the
steamCollections.isEmpty() branch and the related offline/smart-collection cases
so only the selectable list is gated by isNotEmpty(), while the status text is
rendered independently. Use the existing LibraryOptionsPanel composable and the
collection/status rendering block to keep dynamic-collection users seeing the
explanation instead of an empty section.
In
`@app/src/main/java/app/gamenative/ui/screen/library/components/LibraryTabBar.kt`:
- Around line 199-205: The active-filter badge in the Options button is
visual-only, so update the accessibility semantics in LibraryTabBar’s
badge/button block and the related Options button block to announce filter state
when isAnyFilterActive is true. Adjust the CompactIconButton contentDescription
or add semantics/stateDescription so TalkBack users hear that filters are
active, while keeping the inactive state unchanged.
In `@app/src/main/res/values-zh-rCN/strings.xml`:
- Line 1633: Update the localized title string for the Steam collections header
in strings.xml so it uses the same “collections” term as the surrounding
translations; change the Steam collections title entry to match the existing 收藏集
wording used elsewhere for consistency.
---
Nitpick comments:
In `@app/src/test/java/app/gamenative/steam/SteamCollectionFilterTest.kt`:
- Around line 51-60: Add a regression test in SteamCollectionFilterTest for the
empty static collection case so allowedAppIds() does not fail open. Reuse the
existing all fixture and SteamCollectionFilter.allowedAppIds, then assert that
selecting the static collection key for the empty collection returns an empty
set rather than null. This should specifically cover the behavior around
allowedAppIds and the static collection that SteamCollectionParserTest already
accepts as valid.
🪄 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
Run ID: de761d9b-189a-48db-977e-0686ba7476f9
📒 Files selected for processing (31)
app/src/main/java/app/gamenative/PrefManager.ktapp/src/main/java/app/gamenative/data/SteamCollection.ktapp/src/main/java/app/gamenative/data/SteamCollectionRepository.ktapp/src/main/java/app/gamenative/service/SteamService.ktapp/src/main/java/app/gamenative/steam/CloudConfigStoreService.ktapp/src/main/java/app/gamenative/steam/SteamCollectionFilter.ktapp/src/main/java/app/gamenative/steam/SteamCollectionParser.ktapp/src/main/java/app/gamenative/ui/component/OptionListItem.ktapp/src/main/java/app/gamenative/ui/data/LibraryState.ktapp/src/main/java/app/gamenative/ui/model/LibraryViewModel.ktapp/src/main/java/app/gamenative/ui/screen/library/LibraryScreen.ktapp/src/main/java/app/gamenative/ui/screen/library/components/LibraryOptionsPanel.ktapp/src/main/java/app/gamenative/ui/screen/library/components/LibraryTabBar.ktapp/src/main/java/in/dragonbra/javasteam/protobufs/steamclient/SteammessagesCloudconfigstoreSteamclient.javaapp/src/main/res/values-da/strings.xmlapp/src/main/res/values-de/strings.xmlapp/src/main/res/values-es/strings.xmlapp/src/main/res/values-fr/strings.xmlapp/src/main/res/values-it/strings.xmlapp/src/main/res/values-ja/strings.xmlapp/src/main/res/values-ko/strings.xmlapp/src/main/res/values-pl/strings.xmlapp/src/main/res/values-pt-rBR/strings.xmlapp/src/main/res/values-ro/strings.xmlapp/src/main/res/values-ru/strings.xmlapp/src/main/res/values-uk/strings.xmlapp/src/main/res/values-zh-rCN/strings.xmlapp/src/main/res/values-zh-rTW/strings.xmlapp/src/main/res/values/strings.xmlapp/src/test/java/app/gamenative/steam/SteamCollectionFilterTest.ktapp/src/test/java/app/gamenative/steam/SteamCollectionParserTest.kt
There was a problem hiding this comment.
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/PrefManager.kt (1)
865-885: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winClear the new collection prefs on Steam logout.
clearSteamSessionPreferences()still only removes auth/profile keys, so these newlibrarySteamCollections*values survive account logout.SteamCollectionRepository.loadFromCache()restores the cached collections/skipped-dynamic flag from disk, andLibraryStateseedsselectedSteamCollectionIdsfromPrefManager.librarySteamCollections, so the next Steam account can inherit the previous user's collections/filter state until a fresh sync reconciles it.Suggested fix
fun clearSteamSessionPreferences() { scope.launch { dataStore.edit { pref -> pref.remove(USER_NAME) pref.remove(ACCESS_TOKEN_ENC) pref.remove(REFRESH_TOKEN_ENC) pref.remove(CLIENT_ID) pref.remove(PERSONA_STATE) pref.remove(STEAM_USER_ACCOUNT_ID) pref.remove(STEAM_USER_STEAM_ID_64) pref.remove(STEAM_USER_AVATAR_HASH) pref.remove(STEAM_USER_NAME) pref.remove(LAST_PICS_CHANGE_NUMBER) pref.remove(STEAM_GAMES_COUNT) + pref.remove(LIBRARY_STEAM_COLLECTIONS_CACHE) + pref.remove(LIBRARY_STEAM_COLLECTIONS_SKIPPED_DYNAMIC) + pref.remove(LIBRARY_STEAM_COLLECTIONS) } } }🤖 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/PrefManager.kt` around lines 865 - 885, Clear the new Steam collection preference values during logout by updating clearSteamSessionPreferences() so it also removes librarySteamCollectionsCache, librarySteamCollectionsSkippedDynamic, and librarySteamCollections in PrefManager. This prevents SteamCollectionRepository.loadFromCache() and LibraryState from restoring the previous user’s collection cache/filter state after account sign-out; keep the change localized to the logout cleanup path alongside the existing auth/profile key removals.
🤖 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/main/java/app/gamenative/PrefManager.kt`:
- Around line 865-885: Clear the new Steam collection preference values during
logout by updating clearSteamSessionPreferences() so it also removes
librarySteamCollectionsCache, librarySteamCollectionsSkippedDynamic, and
librarySteamCollections in PrefManager. This prevents
SteamCollectionRepository.loadFromCache() and LibraryState from restoring the
previous user’s collection cache/filter state after account sign-out; keep the
change localized to the logout cleanup path alongside the existing auth/profile
key removals.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 94b62e50-26c2-4b37-9201-33b227c4d51b
📒 Files selected for processing (5)
app/src/main/java/app/gamenative/PrefManager.ktapp/src/main/java/app/gamenative/data/SteamCollectionRepository.ktapp/src/main/java/app/gamenative/service/SteamService.ktapp/src/main/java/app/gamenative/steam/SteamCollectionParser.ktapp/src/main/res/values-zh-rCN/strings.xml
✅ Files skipped from review due to trivial changes (1)
- app/src/main/res/values-zh-rCN/strings.xml
🚧 Files skipped from review as they are similar to previous changes (3)
- app/src/main/java/app/gamenative/data/SteamCollectionRepository.kt
- app/src/main/java/app/gamenative/steam/SteamCollectionParser.kt
- app/src/main/java/app/gamenative/service/SteamService.kt
There was a problem hiding this comment.
1 issue found across 5 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/PrefManager.kt">
<violation number="1" location="app/src/main/java/app/gamenative/PrefManager.kt:873">
P2: Persisted `librarySteamCollectionsSkippedDynamic` can become stale because it is not reset when the cache is cleared on corruption. In `SteamCollectionRepository.loadFromCache()`, when the cached JSON is corrupt, only `librarySteamCollectionsCache` is cleared, leaving the skipped-dynamic flag stale in prefs.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
There was a problem hiding this comment.
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 `@app/src/main/java/app/gamenative/data/SteamCollectionRepository.kt`:
- Around line 29-33: The corrupt-cache recovery path in
SteamCollectionRepository.loadFromCache() clears persistence but leaves the
in-memory collections state stale. Update the catch block to also reset
_collections when cached data fails to load, alongside clearing
PrefManager.librarySteamCollectionsCache and
PrefManager.librarySteamCollectionsSkippedDynamic. Make sure the fix keeps the
in-memory state aligned with the cleared cache so subsequent loads do not reuse
old data.
🪄 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
Run ID: eb0d2c90-86a1-41bf-bd71-fafcc686b180
📒 Files selected for processing (1)
app/src/main/java/app/gamenative/data/SteamCollectionRepository.kt
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
743457b to
6233433
Compare
6233433 to
0c1204b
Compare
Description
Adds a Steam Collections filter to the library. It pulls your collections from Steam (via CloudConfigStore) and lets you narrow the library down to one or more of them from the options panel.
A few notes on the behavior:
Most of the diff is the generated CloudConfigStore protobuf stub (
SteammessagesCloudconfigstoreSteamclient.java); the actual feature code is small.Recording
Type of Change
Checklist
#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.CONTRIBUTING.md.Summary by cubic
Adds a Steam Collections filter to narrow your library by one or more Steam cloud collections. The section only shows when Steam is connected; selecting collections hides non‑Steam sources and excludes Steam’s Hidden games by default.
New Features
Download#1, caches a snapshot for offline/boot, reconciles removed collections with a snackbar, and skips smart/dynamic collections.Dependencies
in.dragonbra:javasteamto1.8.0.1-22-SNAPSHOT(includes CloudConfigStore protobufs); adds a minimalCloudConfigStoreServiceunified‑messages stub to receiveDownload#1replies and removes the vendored proto.Written for commit 0c1204b. Summary will update on new commits.
Summary by CodeRabbit