Skip to content

feat(e15): multi-OS release picker β€” download installers for other platforms#588

Merged
rainxchzed merged 4 commits into
mainfrom
feat/e15-multi-os-release-picker
May 13, 2026
Merged

feat(e15): multi-OS release picker β€” download installers for other platforms#588
rainxchzed merged 4 commits into
mainfrom
feat/e15-multi-os-release-picker

Conversation

@rainxchzed
Copy link
Copy Markdown
Member

@rainxchzed rainxchzed commented May 12, 2026

Sprint 2 task E15. Survey #7 β€” users want to grab installers across OSes ("Android APKs from desktop, Linux .debs from phone").

What landed:

  • core/domain/util/AssetPlatform.kt β€” filename β†’ DiscoveryPlatform mapping (apk/exe/msi/dmg/pkg/deb/rpm/AppImage/snap/etc.).
  • TweaksRepository.getShowAllPlatforms() / setShowAllPlatforms() β€” global persisted toggle, DataStore key show_all_platforms.
  • DetailsState.showAllPlatforms observed from preference.
  • ReleaseAssetsItemsPicker (the sheet): Switch at top wired to OnToggleShowAllPlatforms. When ON, the list groups assets by DiscoveryPlatform with section headers (Android / Windows / macOS / Linux). Non-installable-on-current-platform assets route to OnDownloadForTransfer which opens the asset's downloadUrl via BrowserHelper.openUrl β€” browser handles save-to-Downloads. Hint row "Other platform β€” opens in browser to save for transfer" under each non-current asset.
  • Current-platform install flow unchanged. Selecting an installable asset still hits SelectDownloadAsset β†’ install button picks it up.
  • 13-locale strings + 1.8.2 what's-new bullet.

Compile-verified: :composeApp:compileDebugKotlinAndroid BUILD SUCCESSFUL.

Summary by CodeRabbit

  • New Features

    • Multi-platform release asset picker: toggle to show installers grouped by platform or current-platform only.
    • Download non-current-platform assets for transfer (opens in browser).
    • Persisted "Show all platforms" preference so your choice is remembered.
    • Long-press β€œhide repository” on Home/Search cards.
  • Localization

    • Added translations for the platform-selection UI across multiple languages.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

No actionable comments were generated in the recent review. πŸŽ‰

ℹ️ Recent review info
βš™οΈ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9a275fe1-e771-49a3-9a52-67f6f311e7f7

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 90352ef and e5898a0.

πŸ“’ Files selected for processing (1)
  • feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt

Walkthrough

Adds a persisted "show all platforms" toggle and cross-platform release-assets picker that groups assets by detected platform; current-platform assets remain installable, others download via browser for transfer. Includes repository APIs, platform detection utility, ViewModel wiring, UI components, header integration, release notes, and localized strings.

Changes

Multi-OS Release Asset Picker with Platform Toggling

Layer / File(s) Summary
Repository contract, platform detection, and persistence
core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/TweaksRepository.kt, core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/TweaksRepositoryImpl.kt, core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/AssetPlatform.kt
TweaksRepository gains getShowAllPlatforms() and setShowAllPlatforms(); implementation persists SHOW_ALL_PLATFORMS_KEY via DataStore. New assetPlatformOf() maps asset filenames to DiscoveryPlatform or returns null for unknown formats.
UI state and action types
feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsState.kt, feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsAction.kt
DetailsState adds showAllPlatforms: Boolean (default false). DetailsAction adds OnToggleShowAllPlatforms(enabled: Boolean) and OnDownloadForTransfer(assetUrl: String) to toggle the persisted preference and initiate transfer-downloads.
ViewModel state observation and action handling
feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt
ViewModel observes tweaksRepository.getShowAllPlatforms() at init and syncs state. Handles toggle by persisting the value and handles transfer-download by opening the asset URL via helper and warning on failure.
ReleaseAssetsPicker cross-platform UI implementation
feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt
Picker adds showAllPlatforms and crossPlatformAssets params, a bottom-sheet switch, groups assets by detected platform, orders sections (local platform first), renders PlatformSectionCard/SectionChip, and routes clicks to select or transfer-download depending on installability; falls back to original list when disabled.
Header component wiring to picker
feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Header.kt
Header now passes showAllPlatforms = state.showAllPlatforms and crossPlatformAssets filtered from the selected release's assets via assetPlatformOf(it.name).
Release notes and multi-language string resources
core/presentation/src/commonMain/composeResources/files/whatsnew/17.json, core/presentation/src/commonMain/composeResources/values*/strings*.xml
Whatsnew JSON updated. Multiple locale string files add show_all_platforms_label, per-platform section titles, saved_for_transfer_hint, and section chip labels for the new UI across locales.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I sniff .apk, .deb, and .dmg with care,
I hop and group installers by OS in the air,
A toggle shows all platforms with a twitchy cheer,
Non-installables open browsers for transfer near,
I munch translations and hum as the picker appears.

πŸš₯ Pre-merge checks | βœ… 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.88% 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
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed The PR title accurately summarizes the main feature: adding multi-OS release picker to allow downloading installers for other platforms.
Linked Issues check βœ… Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check βœ… Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
πŸ“ Generate docstrings
  • Create stacked PR
  • Commit on current branch
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/e15-multi-os-release-picker

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.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 12, 2026

Greptile Summary

Implements the multi-OS release asset picker (E15): a persisted "Show all platforms" toggle on the asset-picker sheet that groups release installers by platform (Android / Windows / macOS / Linux) and routes non-current-platform downloads through the browser.

  • AssetPlatform.kt maps asset filenames to DiscoveryPlatform by extension; TweaksRepository gains a DataStore-backed show_all_platforms preference observed into DetailsState.showAllPlatforms.
  • ReleaseAssetsItemsPicker grows a toggle Surface row and a PlatformSectionCard per platform group; groups / installableIds are correctly memoised with remember; the isPickerEnabled guard now falls back to crossPlatformAssets.isNotEmpty() so the toggle is reachable even when no current-platform assets exist.
  • saved_for_transfer_hint is added to all 14 locale files and called out in the PR description as "Hint row … under each non-current asset," but ReleaseAssetItem has no hint/subtitle parameter and the string is never rendered.

Confidence Score: 5/5

Safe to merge β€” all core logic paths (install, toggle persistence, cross-platform routing) are correct and follow established patterns.

The DataStore integration, ViewModel action handling, and Compose memoisation are all implemented correctly. The only gap is a dead string resource (saved_for_transfer_hint) that was described in the PR as a visible hint row but was never wired to ReleaseAssetItem; this is a UX omission rather than a functional defect.

ReleaseAssetsPicker.kt β€” the saved_for_transfer_hint string is defined across all locale files but the per-asset hint row is not rendered.

Important Files Changed

Filename Overview
feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt Main UI change β€” adds the cross-platform grouped sheet with toggle, PlatformSectionCard, SectionChip, and correctly memoised groups/installableIds. saved_for_transfer_hint string is referenced in the PR description and locale files but is never rendered because ReleaseAssetItem has no hint parameter.
core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/AssetPlatform.kt New utility mapping release asset filenames to DiscoveryPlatform via extension; handles apk/exe/msi/dmg/pkg/deb/rpm/appimage/snap/flatpakref/pkg.tar.zst with null fallback for unrecognised formats.
feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt Adds observeShowAllPlatforms() collected in viewModelScope, handles OnToggleShowAllPlatforms with correct CancellationException re-throw, and OnDownloadForTransfer via helper.openUrl. Follows established ViewModel patterns.
feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Header.kt Derives crossPlatformAssets with remember(state.selectedRelease) to avoid repeated filter calls on recompose, then passes it alongside showAllPlatforms to ReleaseAssetsPicker.
core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/TweaksRepositoryImpl.kt Implements the two new preference methods; DataStore key show_all_platforms added alongside existing preference keys. Implementation is consistent with surrounding code.

Sequence Diagram

sequenceDiagram
    participant U as User
    participant H as Header.kt
    participant P as ReleaseAssetsPicker
    participant VM as DetailsViewModel
    participant TR as TweaksRepository

    H->>H: remember(selectedRelease) β†’ crossPlatformAssets
    H->>P: showAllPlatforms, crossPlatformAssets, assetsList

    U->>P: Open picker sheet
    P->>P: "isPickerEnabled = assetsList||crossPlatformAssets non-empty"

    U->>P: Toggle Show all platforms
    P->>VM: OnToggleShowAllPlatforms(enabled)
    VM->>TR: setShowAllPlatforms(enabled)
    TR-->>VM: "Flow<Boolean> emits"
    VM-->>H: state.showAllPlatforms updated

    alt "showAllPlatforms=true and groups non-empty"
        P->>P: Render PlatformSectionCard per DiscoveryPlatform
        U->>P: Tap current-platform asset
        P->>VM: SelectDownloadAsset(asset)
        U->>P: Tap other-platform asset
        P->>VM: OnDownloadForTransfer(assetUrl)
        VM->>VM: helper.openUrl(assetUrl)
    else "showAllPlatforms=false or no classified assets"
        P->>P: Render assetsList (current-platform only)
        U->>P: Tap asset
        P->>VM: SelectDownloadAsset(asset)
    end
Loading

Reviews (4): Last reviewed commit: "fix(e15): fall back to assetsList when t..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

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

🧹 Nitpick comments (1)
core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml (1)

1151-1151: ⚑ Quick win

Consider a more concise Korean phrasing for better readability.

The current translation is grammatically correct and understandable, but it's slightly verbose. A more natural Korean phrasing would improve the user experience.

πŸ“ Suggested alternative phrasing
-    <string name="saved_for_transfer_hint">λ‹€λ₯Έ ν”Œλž«νΌ β€” μ „μ†‘μš© μ €μž₯을 μœ„ν•΄ λΈŒλΌμš°μ €μ—μ„œ μ—΄λ¦½λ‹ˆλ‹€</string>
+    <string name="saved_for_transfer_hint">λ‹€λ₯Έ ν”Œλž«νΌ β€” λΈŒλΌμš°μ €μ—μ„œ λ‹€μš΄λ‘œλ“œν•˜μ—¬ 전솑</string>

This makes the hint more concise while preserving the meaning: "Other platform β€” Download in browser to transfer."

πŸ€– 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 `@core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml`
at line 1151, Replace the verbose Korean value for the string resource
saved_for_transfer_hint with a more concise, natural phrasing; update the text
from "λ‹€λ₯Έ ν”Œλž«νΌ β€” μ „μ†‘μš© μ €μž₯을 μœ„ν•΄ λΈŒλΌμš°μ €μ—μ„œ μ—΄λ¦½λ‹ˆλ‹€" to a shorter form such as "λ‹€λ₯Έ ν”Œλž«νΌ β€” μ „μ†‘ν•˜λ €λ©΄
λΈŒλΌμš°μ €μ—μ„œ λ‹€μš΄λ‘œλ“œν•˜μ„Έμš”" in the strings-ko.xml entry for saved_for_transfer_hint.
πŸ€– 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
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt`:
- Around line 76-79: The current derivedStateOf in the remember block for
isPickerEnabled only enables the picker when showAllPlatforms is true and
crossPlatformAssets exists, or when showAllPlatforms is false and assetsList
exists; change the predicate so the picker is enabled whenever either assetsList
or crossPlatformAssets is non-empty (i.e., use assetsList.isNotEmpty() ||
crossPlatformAssets.isNotEmpty()) inside the remember/derivedStateOf for
isPickerEnabled in ReleaseAssetsPicker.kt to allow the multi-OS flow when local
installers are missing but other-platform installers exist.

In
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt`:
- Around line 497-502: The current handler for
DetailsAction.OnToggleShowAllPlatforms flips based on
_state.value.showAllPlatforms which can race on rapid toggles; change the action
to carry the explicit enabled boolean
(DetailsAction.OnToggleShowAllPlatforms(enabled)) and in the ViewModel use that
enabled value directly when calling
tweaksRepository.setShowAllPlatforms(enabled) inside the existing
viewModelScope.launch (preserving the CancellationException catch), and update
the UI to call onAction(DetailsAction.OnToggleShowAllPlatforms(checked)) from
its onCheckedChange so the persisted value matches the user's intent.

---

Nitpick comments:
In `@core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml`:
- Line 1151: Replace the verbose Korean value for the string resource
saved_for_transfer_hint with a more concise, natural phrasing; update the text
from "λ‹€λ₯Έ ν”Œλž«νΌ β€” μ „μ†‘μš© μ €μž₯을 μœ„ν•΄ λΈŒλΌμš°μ €μ—μ„œ μ—΄λ¦½λ‹ˆλ‹€" to a shorter form such as "λ‹€λ₯Έ ν”Œλž«νΌ β€” μ „μ†‘ν•˜λ €λ©΄
λΈŒλΌμš°μ €μ—μ„œ λ‹€μš΄λ‘œλ“œν•˜μ„Έμš”" in the strings-ko.xml entry for saved_for_transfer_hint.
πŸͺ„ 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 316c49df-1c96-4567-88a0-903b4b127374

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 0d403f4 and 34933a9.

πŸ“’ Files selected for processing (22)
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/TweaksRepositoryImpl.kt
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/TweaksRepository.kt
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/AssetPlatform.kt
  • core/presentation/src/commonMain/composeResources/files/whatsnew/17.json
  • core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml
  • core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml
  • core/presentation/src/commonMain/composeResources/values-es/strings-es.xml
  • core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml
  • core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml
  • core/presentation/src/commonMain/composeResources/values-it/strings-it.xml
  • core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml
  • core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml
  • core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml
  • core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml
  • core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml
  • core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml
  • core/presentation/src/commonMain/composeResources/values/strings.xml
  • feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsAction.kt
  • feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsState.kt
  • feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt
  • feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt
  • feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Header.kt

Copy link
Copy Markdown
Contributor

@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.

♻️ Duplicate comments (1)
feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt (1)

79-83: ⚠️ Potential issue | 🟠 Major | ⚑ Quick win

Picker stays disabled when only cross-platform assets exist.

When showAllPlatforms is false and assetsList is empty, the OutlinedCard is disabled β€” but crossPlatformAssets may have installers for other OSes. Users in that state cannot open the sheet to flip the toggle on, defeating the multi-OS flow exactly when it's needed. Decouple isPickerEnabled from showAllPlatforms.

πŸ”§ Proposed fix
-    val isPickerEnabled by remember(assetsList, crossPlatformAssets, showAllPlatforms) {
+    val isPickerEnabled by remember(assetsList, crossPlatformAssets) {
         derivedStateOf {
-            if (showAllPlatforms) crossPlatformAssets.isNotEmpty() else assetsList.isNotEmpty()
+            assetsList.isNotEmpty() || crossPlatformAssets.isNotEmpty()
         }
     }
πŸ€– 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
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt`
around lines 79 - 83, The computed state isPickerEnabled incorrectly depends on
showAllPlatforms, causing the picker to stay disabled when assetsList is empty
but crossPlatformAssets contains installers; update the derivedStateOf in the
remember block (isPickerEnabled) to return true if either
assetsList.isNotEmpty() or crossPlatformAssets.isNotEmpty(), and adjust the
remember keys to still include assetsList and crossPlatformAssets (no need to
depend on showAllPlatforms) so the OutlinedCard can open whenever any assets
exist and the sheet can let users toggle showAllPlatforms.
🧹 Nitpick comments (2)
feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt (2)

269-320: πŸ’€ Low value

Hoist per-recomposition work into remember and consider importing the fully-qualified symbols.

groups, installableIds, and sectionOrder are rebuilt on every recomposition of the sheet (each animation frame, each scroll-state change of the LazyColumn parent, etc.). They're pure functions of crossPlatformAssets and assetsList, so wrapping them in remember(crossPlatformAssets, assetsList) { … } is a cheap win. While here, importing DiscoveryPlatform and assetPlatformOf at the top of the file improves readability over inline FQNs.

♻️ Proposed refactor sketch
+import zed.rainxch.core.domain.model.DiscoveryPlatform
+import zed.rainxch.core.domain.util.assetPlatformOf
@@
-                if (showAllPlatforms) {
-                    ...
-                    val groups: Map<zed.rainxch.core.domain.model.DiscoveryPlatform, List<GithubAsset>> =
-                        crossPlatformAssets
-                            .groupBy { zed.rainxch.core.domain.util.assetPlatformOf(it.name) }
-                            .filterKeys { it != null }
-                            .mapKeys { it.key!! }
+                if (showAllPlatforms) {
+                    val groups = remember(crossPlatformAssets) {
+                        crossPlatformAssets
+                            .groupBy { assetPlatformOf(it.name) }
+                            .mapNotNull { (k, v) -> k?.let { it to v } }
+                            .toMap()
+                    }
+                    val installableIds = remember(assetsList) { assetsList.mapTo(HashSet()) { it.id } }
πŸ€– 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
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt`
around lines 269 - 320, The grouping and ordering work (variables groups,
installableIds, sectionOrder derived from crossPlatformAssets and assetsList)
are recomputed on every recompositionβ€”wrap those computations in a
remember(crossPlatformAssets, assetsList) { ... } block so they are only
recalculated when inputs change, and move the assetPlatformOf and
DiscoveryPlatform FQNs to top-level imports to simplify the code; specifically,
compute val (groups, installableIds, sectionOrder) =
remember(crossPlatformAssets, assetsList) { /* groupBy(assetPlatformOf),
filter/map keys, build installableIds set, and build sectionOrder list */ } and
keep the rest of the UI logic using these remembered values.

223-255: ⚑ Quick win

Use Modifier.toggleable with Role.Switch for the toggle row.

The Row currently combines .clickable { onToggleShowAllPlatforms() } with a Switch that has its own onCheckedChange. Screen readers see two distinct actionable nodes instead of one switch control, and the row's click semantics don't expose checked state. The idiomatic Material3 pattern is to put toggleable on the row with role = Role.Switch and pass onCheckedChange = null to the Switch so it inherits the row's semantics.

♻️ Proposed refactor
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.ui.semantics.Role
@@
-                Row(
-                    modifier =
-                        Modifier
-                            .clickable(onClick = onToggleShowAllPlatforms)
-                            .padding(horizontal = 16.dp, vertical = 10.dp),
-                    verticalAlignment = Alignment.CenterVertically,
-                ) {
+                Row(
+                    modifier =
+                        Modifier
+                            .toggleable(
+                                value = showAllPlatforms,
+                                role = Role.Switch,
+                                onValueChange = { onToggleShowAllPlatforms() },
+                            )
+                            .padding(horizontal = 16.dp, vertical = 10.dp),
+                    verticalAlignment = Alignment.CenterVertically,
+                ) {
@@
-                    androidx.compose.material3.Switch(
-                        checked = showAllPlatforms,
-                        onCheckedChange = { onToggleShowAllPlatforms() },
-                    )
+                    androidx.compose.material3.Switch(
+                        checked = showAllPlatforms,
+                        onCheckedChange = null,
+                    )
πŸ€– 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
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt`
around lines 223 - 255, The Row uses Modifier.clickable plus a Switch, which
creates duplicate actionable nodes and hides checked semantics; replace
Modifier.clickable with Modifier.toggleable(value = showAllPlatforms,
onValueChange = { onToggleShowAllPlatforms() }, role = Role.Switch) on the Row
(the component containing the Icon/Text/Switch) and update the Switch inside
ReleaseAssetsPicker to pass onCheckedChange = null and keep checked =
showAllPlatforms so the row owns the accessibility semantics and state toggling.
πŸ€– 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.

Duplicate comments:
In
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt`:
- Around line 79-83: The computed state isPickerEnabled incorrectly depends on
showAllPlatforms, causing the picker to stay disabled when assetsList is empty
but crossPlatformAssets contains installers; update the derivedStateOf in the
remember block (isPickerEnabled) to return true if either
assetsList.isNotEmpty() or crossPlatformAssets.isNotEmpty(), and adjust the
remember keys to still include assetsList and crossPlatformAssets (no need to
depend on showAllPlatforms) so the OutlinedCard can open whenever any assets
exist and the sheet can let users toggle showAllPlatforms.

---

Nitpick comments:
In
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt`:
- Around line 269-320: The grouping and ordering work (variables groups,
installableIds, sectionOrder derived from crossPlatformAssets and assetsList)
are recomputed on every recompositionβ€”wrap those computations in a
remember(crossPlatformAssets, assetsList) { ... } block so they are only
recalculated when inputs change, and move the assetPlatformOf and
DiscoveryPlatform FQNs to top-level imports to simplify the code; specifically,
compute val (groups, installableIds, sectionOrder) =
remember(crossPlatformAssets, assetsList) { /* groupBy(assetPlatformOf),
filter/map keys, build installableIds set, and build sectionOrder list */ } and
keep the rest of the UI logic using these remembered values.
- Around line 223-255: The Row uses Modifier.clickable plus a Switch, which
creates duplicate actionable nodes and hides checked semantics; replace
Modifier.clickable with Modifier.toggleable(value = showAllPlatforms,
onValueChange = { onToggleShowAllPlatforms() }, role = Role.Switch) on the Row
(the component containing the Icon/Text/Switch) and update the Switch inside
ReleaseAssetsPicker to pass onCheckedChange = null and keep checked =
showAllPlatforms so the row owns the accessibility semantics and state toggling.

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9986a6a7-edc5-406e-b00f-ec73c52502d5

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 34933a9 and 47ca1ab.

πŸ“’ Files selected for processing (14)
  • core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml
  • core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml
  • core/presentation/src/commonMain/composeResources/values-es/strings-es.xml
  • core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml
  • core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml
  • core/presentation/src/commonMain/composeResources/values-it/strings-it.xml
  • core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml
  • core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml
  • core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml
  • core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml
  • core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml
  • core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml
  • core/presentation/src/commonMain/composeResources/values/strings.xml
  • feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt
βœ… Files skipped from review due to trivial changes (2)
  • core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml
  • core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml
🚧 Files skipped from review as they are similar to previous changes (9)
  • core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml
  • core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml
  • core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml
  • core/presentation/src/commonMain/composeResources/values-it/strings-it.xml
  • core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml
  • core/presentation/src/commonMain/composeResources/values/strings.xml
  • core/presentation/src/commonMain/composeResources/values-es/strings-es.xml
  • core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml
  • core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml

Copy link
Copy Markdown
Contributor

@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)
feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsAction.kt (1)

170-177: ⚑ Quick win

Consider including assetName parameter for better observability.

The existing DownloadAsset action (lines 33-37) includes assetName alongside the URL. Including assetName here would improve log messages, error diagnostics, and analytics when the transfer-download flow encounters issues, especially since the ViewModel logs failures for this action.

πŸ“‹ Suggested enhancement
 data class OnDownloadForTransfer(
     val assetUrl: String,
+    val assetName: String,
 ) : DetailsAction
πŸ€– 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
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsAction.kt`
around lines 170 - 177, The OnDownloadForTransfer data class lacks an assetName
field which reduces observability compared to the existing DownloadAsset action;
update the DetailsAction sealed types by adding a val assetName: String
parameter to the OnDownloadForTransfer data class (mirroring DownloadAsset's
shape), update all call sites and ViewModel logging/analytics that construct or
consume OnDownloadForTransfer to pass and use assetName, and adjust any
serializers/tests that reference DetailsAction/OnDownloadForTransfer accordingly
so logs and error diagnostics include the asset name.
feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt (1)

229-261: ⚑ Quick win

Use Modifier.toggleable(role = Role.Switch) instead of clickable + Switch.onCheckedChange.

The row currently exposes two interactive nodes to accessibility services: the clickable Row (no role) and the Switch itself. TalkBack/Switch Access will see them as separate targets and double-announce the control. The Material3-recommended pattern is to drive the toggle from the row with toggleable(role = Role.Switch) and make the Switch a non-interactive visual indicator.

♻️ Proposed refactor
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.ui.semantics.Role
@@
             Surface(
                 shape = RoundedCornerShape(20.dp),
                 color = MaterialTheme.colorScheme.surfaceContainerHigh,
                 modifier =
                     Modifier
                         .fillMaxWidth()
                         .padding(horizontal = 16.dp, vertical = 8.dp),
             ) {
                 Row(
                     modifier =
                         Modifier
-                            .clickable(onClick = { onToggleShowAllPlatforms(!showAllPlatforms) })
+                            .toggleable(
+                                value = showAllPlatforms,
+                                onValueChange = onToggleShowAllPlatforms,
+                                role = Role.Switch,
+                            )
                             .padding(horizontal = 16.dp, vertical = 10.dp),
                     verticalAlignment = Alignment.CenterVertically,
                 ) {
@@
                     androidx.compose.material3.Switch(
                         checked = showAllPlatforms,
-                        onCheckedChange = onToggleShowAllPlatforms,
+                        onCheckedChange = null,
                     )
                 }
             }
πŸ€– 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
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt`
around lines 229 - 261, The Row that currently uses Modifier.clickable(...) and
a live androidx.compose.material3.Switch should be converted to a single
toggleable control: replace Modifier.clickable in the Row with
Modifier.toggleable(checked = showAllPlatforms, onValueChange =
onToggleShowAllPlatforms, role = Role.Switch) and make the Switch
non-interactive (remove its onCheckedChange and keep checked = showAllPlatforms
only) so the Row drives the toggle; update imports to include
androidx.compose.foundation.selection.toggleable and
androidx.compose.ui.semantics.Role as needed and ensure the unique identifiers
(the Row block, onToggleShowAllPlatforms, showAllPlatforms, and the Switch
component) are adjusted accordingly.
πŸ€– 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
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt`:
- Around line 282-293: When showAllPlatforms is true the UI currently shows the
empty-state when groups.isEmpty() even though installableAssets (derived via
installer.isAssetInstallable(asset.name) from assetsList) may contain items;
change the conditional to show the "no assets" message only when both groups and
the installable assets set are empty (e.g. replace the check if
(groups.isEmpty()) with if (groups.isEmpty() && installableAssets.isEmpty()) or
equivalent), keeping references to installer.isAssetInstallable,
assetPlatformOf, assetsList, installableAssets and crossPlatformAssets to ensure
assets without platform mappings still surface.

---

Nitpick comments:
In
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt`:
- Around line 229-261: The Row that currently uses Modifier.clickable(...) and a
live androidx.compose.material3.Switch should be converted to a single
toggleable control: replace Modifier.clickable in the Row with
Modifier.toggleable(checked = showAllPlatforms, onValueChange =
onToggleShowAllPlatforms, role = Role.Switch) and make the Switch
non-interactive (remove its onCheckedChange and keep checked = showAllPlatforms
only) so the Row drives the toggle; update imports to include
androidx.compose.foundation.selection.toggleable and
androidx.compose.ui.semantics.Role as needed and ensure the unique identifiers
(the Row block, onToggleShowAllPlatforms, showAllPlatforms, and the Switch
component) are adjusted accordingly.

In
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsAction.kt`:
- Around line 170-177: The OnDownloadForTransfer data class lacks an assetName
field which reduces observability compared to the existing DownloadAsset action;
update the DetailsAction sealed types by adding a val assetName: String
parameter to the OnDownloadForTransfer data class (mirroring DownloadAsset's
shape), update all call sites and ViewModel logging/analytics that construct or
consume OnDownloadForTransfer to pass and use assetName, and adjust any
serializers/tests that reference DetailsAction/OnDownloadForTransfer accordingly
so logs and error diagnostics include the asset name.
πŸͺ„ 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dde7f492-d1cf-427b-bf22-17501135edcc

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 47ca1ab and 90352ef.

πŸ“’ Files selected for processing (4)
  • feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsAction.kt
  • feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt
  • feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/ReleaseAssetsPicker.kt
  • feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Header.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/Header.kt
  • feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt

@rainxchzed rainxchzed merged commit 67f4b3d into main May 13, 2026
1 check passed
@rainxchzed rainxchzed deleted the feat/e15-multi-os-release-picker branch May 13, 2026 00:40
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