Skip to content

database: update schema to version 10 to support monorepo asset tracking#407

Merged
rainxchzed merged 8 commits intomainfrom
monorepo-support
Apr 11, 2026
Merged

database: update schema to version 10 to support monorepo asset tracking#407
rainxchzed merged 8 commits intomainfrom
monorepo-support

Conversation

@rainxchzed
Copy link
Copy Markdown
Member

@rainxchzed rainxchzed commented Apr 10, 2026

  • Update AppDatabase schema to version 10.
  • Add assetFilterRegex (TEXT) and fallbackToOlderReleases (INTEGER) columns to the installed_apps table.
  • Implement MIGRATION_9_10 to handle the database upgrade.
  • Update InstalledAppEntity with new fields to allow filtering assets by regex and falling back to older releases when checking for updates.

Summary by CodeRabbit

  • New Features
    • Per-app asset filtering (regex) and optional fallback to older releases
    • Advanced per-app settings sheet with live preview of matching assets
    • Asset filter integrated into add-by-link flow and asset picker
    • Export/import schema updated to include filter settings
  • Chores
    • Database schema migrated to support new per-app settings
  • Localization
    • New UI strings for the feature in 13+ languages

- Update `AppDatabase` schema to version 10.
- Add `assetFilterRegex` (TEXT) and `fallbackToOlderReleases` (INTEGER) columns to the `installed_apps` table.
- Implement `MIGRATION_9_10` to handle the database upgrade.
- Update `InstalledAppEntity` with new fields to allow filtering assets by regex and falling back to older releases when checking for updates.
- Create `AssetFilter` class to handle compiled, validated regex patterns for filtering assets.
- Implement `parse` method to handle user-supplied patterns with validation and case-insensitivity.
- Add `matches` using `containsMatchIn` to support casual substring patterns.
- Implement `suggestFromAssetName` to automatically derive filter prefixes by stripping version suffixes from filenames.
- Implement `assetFilterRegex` and `fallbackToOlderReleases` in `InstalledApp` to support tracking specific apps within monorepos.
- Update `InstalledAppsRepository` to fetch a window of 50 releases (up from 1) to allow falling back to older releases when the latest one does not match the asset filter.
- Add `resolveTrackedRelease` logic to match assets against regex filters and architecture requirements across the release window.
- Introduce `previewMatchingAssets` to provide a dry-run helper for testing asset filters in the UI.
- Update `InstalledAppDao` and database schema (version 10) to persist new filtering fields.
- Update `AppsRepository` to support linking apps with initial asset filter configurations.
- Add Room migration (MIGRATION_9_10) for the new database columns.
- Introduce `AdvancedAppSettingsBottomSheet` to configure per-app asset filters (regex) and "fallback to older releases" logic.
- Add live preview functionality in the advanced settings sheet to visualize asset matching before saving.
- Enhance `LinkAppBottomSheet` with an asset filter and fallback toggle during the repository linking process.
- Implement automatic filter suggestions during linking based on package names and app titles.
- Update `AppsState`, `AppsAction`, and `AppsViewModel` to manage advanced filter state, validation, and debounced preview refreshes.
- Add UI indicators to the main app list to highlight apps with active custom filters.
- Include new localized strings for filter labels, help text, and validation errors.
Introduce a new set of localized strings to support advanced repository filtering, specifically targeting monorepos with multiple applications. These changes include:

- Added labels and descriptions for asset filtering using regular expressions.
- Added support for "fallback to older releases," allowing the app to search through previous releases when the most recent one doesn't match the filter.
- Added strings for advanced settings, including preview functionality, validation states, and save actions.
- Provided consistent translations for these new features across Turkish, Italian, Arabic, Simplified Chinese, Bengali, French, Russian, Japanese, Hindi, Korean, Spanish, and Polish.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

Walkthrough

Added per-app asset-name filtering (regex) and optional fallback-to-older-releases across DB, domain, data, repository, and UI layers; introduced AssetFilter utilities, migration to DB version 10, repository preview/save APIs, and advanced-settings UI with live preview and link-time filtering.

Changes

Cohort / File(s) Summary
Database & Migration
core/data/schemas/.../10.json, core/data/src/.../migrations/MIGRATION_9_10.kt, core/data/src/.../AppDatabase.kt, core/data/src/.../initDatabase.kt
Bumped AppDatabase to version 10; added assetFilterRegex: TEXT and fallbackToOlderReleases: INTEGER NOT NULL DEFAULT 0 to installed_apps; included migration and schema JSON; migration registered in DB initializer.
Domain Models & Utilities
core/domain/src/.../InstalledApp.kt, core/domain/src/.../ExportedApp.kt, core/domain/src/.../InstalledAppsRepository.kt, core/domain/src/.../AssetFilter.kt
Added assetFilterRegex and fallbackToOlderReleases to domain models and export schema (export version → 2). Added AssetFilter class with parse() and suggestFromAssetName(). Repository interface extended with setAssetFilter() and previewMatchingAssets() plus MatchingPreview.
Data Entities, DAO & Mappers
core/data/src/.../entities/InstalledAppEntity.kt, core/data/src/.../dao/InstalledAppDao.kt, core/data/src/.../InstalledAppsMappers.kt
Persisted new fields on entity; DAO gains updateAssetFilter() and clearUpdateMetadata() methods; mappers updated to include new fields.
InstalledApps Repository Logic
core/data/src/.../InstalledAppsRepositoryImpl.kt
Refactored update resolution to fetch up to 50 releases, apply optional AssetFilter, honor fallbackToOlderReleases when resolving installable asset, log/handle invalid regex, and added setAssetFilter() and previewMatchingAssets() implementations.
Apps Feature — API, Data & Export/Import
feature/apps/domain/.../AppsRepository.kt, feature/apps/data/.../AppsRepositoryImpl.kt, core/domain/.../ExportedApp.kt
linkAppToRepo() signature extended to accept optional filter + fallback (defaults provided). Export schema bumped; import/export now includes these fields.
Presentation Models & Mapping
feature/apps/presentation/.../InstalledAppUi.kt, feature/apps/presentation/.../InstalledAppMapper.kt
UI model and mappers updated to carry assetFilterRegex and fallbackToOlderReleases.
ViewModel, State & Actions
feature/apps/presentation/.../AppsViewModel.kt, feature/apps/presentation/.../AppsState.kt, feature/apps/presentation/.../AppsAction.kt
Added link-sheet filter state/validation and advanced-settings state (draft, preview, saving). Actions for link filter and advanced sheet added. ViewModel seeds suggested filter, validates via AssetFilter.parse(), debounces preview (350ms), calls preview/save repo APIs, and triggers update checks after save.
UI Components
feature/apps/presentation/.../AppsRoot.kt, feature/apps/presentation/.../LinkAppBottomSheet.kt, feature/apps/presentation/.../AdvancedAppSettingsBottomSheet.kt
Added AdvancedAppSettingsBottomSheet composable; App item shows filter icon; Link flow now supports inline regex filter input, fallback toggle, and live-filtered asset list with contextual empty states; advanced sheet shows live preview and save flow.
Localization
core/presentation/src/commonMain/composeResources/values*/strings-*.xml (multiple locales)
Added strings and plurals supporting asset-filter UI, invalid/no-match messages, fallback wording, advanced settings controls/preview states across many locales.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as Advanced Settings Sheet
    participant VM as ViewModel
    participant Repo as InstalledAppsRepository
    participant API as GitHub API

    User->>UI: Open advanced settings for app
    User->>UI: Edit asset filter / toggle fallback
    UI->>VM: OnAdvancedFilterChanged / OnAdvancedFallbackToggled
    VM->>VM: Validate via AssetFilter.parse()
    alt valid
        VM->>VM: Clear error, schedule debounce (350ms)
    else invalid
        VM->>VM: Set filter error
    end

    Note over VM: After debounce
    VM->>VM: set advancedPreviewLoading = true
    VM->>Repo: previewMatchingAssets(owner, repo, regex, includePre, fallback)
    Repo->>API: Fetch releases (up to 50)
    API-->>Repo: Releases + assets
    Repo->>Repo: Filter assets by platform + regex, apply fallback logic
    Repo-->>VM: MatchingPreview(release?, matchedAssets, regexError?)
    VM->>UI: Update preview state (matched assets / message)
    UI->>User: Render preview (assets or error)

    User->>UI: Click Save
    UI->>VM: OnAdvancedSaveFilter
    VM->>Repo: setAssetFilter(packageName, regex, fallback)
    Repo->>Repo: Update DB (DAO.updateAssetFilter)
    Repo-->>VM: Complete
    VM->>Repo: checkForUpdates(packageName)
    Repo->>API: Fetch releases (with new filter)
    Repo-->>VM: Update results
    VM->>UI: Dismiss sheet
Loading
sequenceDiagram
    participant User
    participant UI as Add App by Link
    participant VM as ViewModel
    participant Repo as AppsRepository
    participant API as GitHub API

    User->>UI: Enter GitHub URL -> Fetch repo
    UI->>Repo: request repo info
    Repo->>API: Fetch latest release + assets
    API-->>Repo: Release & assets
    Repo-->>VM: linkInstallableAssets

    VM->>VM: Suggest filter (heuristic + AssetFilter.suggestFromAssetName)
    VM->>UI: Seed linkAssetFilter
    UI->>User: Show asset picker with pre-filled filter

    User->>UI: Modify filter / toggle fallback
    UI->>VM: OnLinkAssetFilterChanged / OnLinkFallbackToggled
    VM->>VM: Validate regex, set filteredLinkAssets (live)
    UI->>User: Show filtered assets

    User->>UI: Confirm link
    UI->>VM: linkAppToRepo(..., assetFilterRegex, fallbackToOlderReleases)
    VM->>Repo: Create InstalledApp with filter config
    Repo->>Repo: Persist entity
    Repo-->>VM: Complete
    VM->>UI: Dismiss link sheet
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 Filters, fallbacks, a regex delight—

I hop through releases searching by night.
If latest won't match, older ones I'll scan,
A rabbit-approved, per-app update plan.
Hooray for previews that help find the plan!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: bumping the database schema version to 10 to support monorepo asset tracking via new columns for asset filtering and fallback release behavior.

✏️ 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 monorepo-support

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
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: 8

Caution

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

⚠️ Outside diff range comments (1)
feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsViewModel.kt (1)

1329-1391: ⚠️ Potential issue | 🟠 Major

Block invalid link filters before calling linkAppToRepo().

OnLinkAssetFilterChanged only marks linkAssetFilterError; both link paths here still persist the raw filter. Because the picker intentionally stays usable on invalid regex, a user can save a broken pattern and later update checks will silently ignore it. Re-parse and trim the draft here, and stop linking when validation fails.

Proposed fix
+                val normalizedFilter = _state.value.linkAssetFilter.trim()
+                val parsedFilter = AssetFilter.parse(normalizedFilter)
+                if (parsedFilter != null && parsedFilter.isFailure) {
+                    _state.update {
+                        it.copy(
+                            linkDownloadProgress = null,
+                            linkValidationStatus = null,
+                            linkAssetFilterError = "invalid",
+                        )
+                    }
+                    return@launch
+                }
+
                 if (apkInfo == null) {
                     logger.debug("Could not extract APK info for validation, linking anyway")
                     appsRepository.linkAppToRepo(
-                    deviceApp = selectedApp.toDomain(),
-                    repoInfo = repoInfo.toDomain(),
-                    assetFilterRegex = _state.value.linkAssetFilter.takeIf { it.isNotBlank() },
-                    fallbackToOlderReleases = _state.value.linkFallbackToOlder,
-                )
+                        deviceApp = selectedApp.toDomain(),
+                        repoInfo = repoInfo.toDomain(),
+                        assetFilterRegex = normalizedFilter.takeIf { it.isNotEmpty() },
+                        fallbackToOlderReleases = _state.value.linkFallbackToOlder,
+                    )
                     _state.update {
                         it.copy(
                             linkDownloadProgress = null,
@@
                 appsRepository.linkAppToRepo(
                     deviceApp = selectedApp.toDomain(),
                     repoInfo = repoInfo.toDomain(),
-                    assetFilterRegex = _state.value.linkAssetFilter.takeIf { it.isNotBlank() },
+                    assetFilterRegex = normalizedFilter.takeIf { it.isNotEmpty() },
                     fallbackToOlderReleases = _state.value.linkFallbackToOlder,
                 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsViewModel.kt`
around lines 1329 - 1391, When attempting to call linkAppToRepo from
AppsViewModel (both code paths that currently call
appsRepository.linkAppToRepo), validate and normalize the user's draft filter
before proceeding: trim _state.value.linkAssetFilter, attempt to compile it as a
regex (or otherwise run the same validation used by OnLinkAssetFilterChanged),
and if validation fails set linkAssetFilterError in _state and abort (do not
call linkAppToRepo); only pass assetFilterRegex = validatedTrimmedFilter.takeIf
{ it.isNotBlank() } into linkAppToRepo when validation succeeds. Ensure you
reference the existing symbols: OnLinkAssetFilterChanged,
_state.value.linkAssetFilter, linkAssetFilterError, and linkAppToRepo to locate
and fix both link code paths.
🧹 Nitpick comments (2)
feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsRoot.kt (1)

754-774: Expose the active-filter state with more than tint.

hasFilter only changes the icon color, so screen readers and users who miss the tint get the same control either way. Since this PR adds the list indicator, it should also expose that state via semantics or a small textual/badge cue.

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

In
`@feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsRoot.kt`
around lines 754 - 774, The IconButton currently only reflects active filter
state via tint (hasFilter), which is not accessible; update the control to
expose the same state through semantics and/or a visible badge/textual cue: add
a semantic state or custom property to the IconButton modifier (e.g.,
Modifier.semantics { contentDescription = advancedFilterDescription;
stateDescription = if (hasFilter) "filter active" else "no filter" }) and
optionally render a small Badge/Text near the Icon when hasFilter is true; use
the existing symbols hasFilter, IconButton, advancedFilterDescription,
onAdvancedSettingsClick, app.assetFilterRegex and app.fallbackToOlderReleases to
locate where to add the semantics/stateDescription and the visual badge.
feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsState.kt (1)

50-59: Replace advancedPreviewMessage string tokens with a typed state.

This field is later matched against raw literals ("no_match", "preview_failed", "save_failed") in feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/components/AdvancedAppSettingsBottomSheet.kt, Lines 279-290. A typo or renamed token will silently fall through to the pending UI. An enum or sealed type would make this exhaustive and safer.

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

In
`@feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsState.kt`
around lines 50 - 59, The advancedPreviewMessage: String? in AppsState should be
replaced with a typed sealed class or enum (e.g., AdvancedPreviewMessageState
with cases NoMatch, PreviewFailed, SaveFailed, None) to avoid brittle string
token matching; update AppsState to use this new type (and a sensible default
like AdvancedPreviewMessageState.None), then refactor the match/when in
AdvancedAppSettingsBottomSheet to switch over AdvancedPreviewMessageState cases
instead of comparing raw literals ("no_match", "preview_failed", "save_failed"),
and update any other references (serializers/parcelables/initializers) to
construct and consume the new typed state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/InstalledAppsRepositoryImpl.kt`:
- Around line 189-219: InstalledAppsRepositoryImpl currently only calls
installedAppsDao.updateLastChecked(...) when no releases or when
resolveTrackedRelease returns null, leaving prior isUpdateAvailable/cached
latest-asset/version stale; add a DAO method (e.g.,
installedAppsDao.clearUpdateMetadata(packageName) or clearUpdateInfo) that
atomically clears isUpdateAvailable and any cached latestAsset/latestVersion
fields, implement that DAO method, and call it in both the releases.isEmpty()
branch and the resolved == null branch (before returning) to ensure saved
stricter filters or no matches remove the old badge/version info.
- Around line 347-358: setAssetFilter currently only persists the regex but must
also immediately re-check the app so badge/update metadata is fresh; after
calling installedAppsDao.updateAssetFilter(packageName, regex = normalized,
fallback = fallbackToOlderReleases) invoke the repository’s existing routine
that recomputes an app’s update state for the given package (the same code path
AppsViewModel currently uses to refresh state) so the badge/metadata is updated
immediately — perform this recheck in the same suspend context, handle
errors/transactional consistency as other repo update flows, and keep
setAssetFilter as the single-entry API for persisting the filter + refreshing
update state.
- Around line 103-123: The catch in
InstalledAppsRepositoryImpl.fetchReleaseWindow currently swallows
CancellationException and treats cancellations as empty results; change the
error handling so cancellation is re-thrown: either add an early rethrow for
kotlinx.coroutines.CancellationException (if e is CancellationException ->
throw) before logging/returning, or split catches (catch CancellationException {
throw } catch (e: Exception) { Logger.e { ... }; return emptyList() }), keeping
the existing Logger.e message and behavior for other exceptions while ensuring
coroutine cancellation propagates.

In
`@core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/InstalledAppsRepository.kt`:
- Around line 54-66: The KDoc for InstalledAppsRepository.setAssetFilter
promises an immediate re-check but InstalledAppsRepositoryImpl.setAssetFilter
currently only persists DAO fields and leaves updateAvailable stale; update the
implementation of InstalledAppsRepositoryImpl.setAssetFilter to, after saving
the regex and fallbackToOlderReleases, call and await
checkForUpdates(packageName) (or the repository's internal method that computes
updateAvailable) so the UI-state is refreshed immediately, and handle/log any
exceptions from that call so persistence still succeeds even if the check fails.

In
`@core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/AssetFilter.kt`:
- Around line 65-67: The code currently returns the raw prefix string (variable
prefix) as a regex; instead, escape regex metacharacters and anchor the
suggestion to the start of the string before returning. Replace the return of
prefix.takeIf { it.length >= 2 } with logic that first checks prefix.length >=
2, then builds an anchored, escaped regex like "^" + Regex.escape(prefix) (or
similar) and returns that; keep the same length check and use the
escaped/anchored value so AssetFilter.kt produces literal-prefix-safe
suggestions.

In `@core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml`:
- Around line 604-616: Replace the two fixed strings with Android plurals to
handle Polish inflection: change asset_filter_visible_count to a <plurals
name="asset_filter_visible_count"> and provide appropriate Polish forms (one,
few, many, other) using placeholders %1$d and %2$d, and change
advanced_preview_release to a <plurals name="advanced_preview_release"> with
forms that include the release name placeholder (%1$s) and the resource count
placeholder (%2$d); update any call sites to use getQuantityString for these
resource IDs. Ensure the plurals keys match the original identifiers
(asset_filter_visible_count, advanced_preview_release) so callers can be updated
without renaming.

In
`@feature/apps/data/src/commonMain/kotlin/zed/rainxch/apps/data/repository/AppsRepositoryImpl.kt`:
- Around line 193-194: exportApps() and importApps() in AppsRepositoryImpl
currently only persist package/repo identity and therefore drop InstalledApp
fields assetFilterRegex and fallbackToOlderReleases; update both functions to
include these two fields when serializing and deserializing InstalledApp so
monorepo settings survive backup/restore. Locate the exportApps() serializer
logic and add assetFilterRegex and fallbackToOlderReleases to the exported
representation for each InstalledApp, and in importApps() read those keys back
into the InstalledApp constructor/mapper so restored apps populate the
InstalledApp.assetFilterRegex and InstalledApp.fallbackToOlderReleases
properties.

In
`@feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/components/LinkAppBottomSheet.kt`:
- Around line 569-583: The code always shows the "asset_filter_no_match" message
when visibleAssets.isEmpty(); change this to distinguish three cases: (1) the
repository has no installable assets (use a new/existing string like
"asset_none_available" or "asset_no_assets" when the full asset list is empty),
(2) the user-entered filter/regex is invalid (catch regex compilation errors and
show a "filter_invalid" message), and (3) the filter matched nothing (keep
"asset_filter_no_match" when there are assets but visibleAssets is empty);
update the branch around visibleAssets.isEmpty() in LinkAppBottomSheet.kt to
check the full asset list (e.g., allAssets or repoAssets), validate the
filter/regex before applying it, and choose the appropriate stringResource
(asset_none_available, filter_invalid, or asset_filter_no_match).

---

Outside diff comments:
In
`@feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsViewModel.kt`:
- Around line 1329-1391: When attempting to call linkAppToRepo from
AppsViewModel (both code paths that currently call
appsRepository.linkAppToRepo), validate and normalize the user's draft filter
before proceeding: trim _state.value.linkAssetFilter, attempt to compile it as a
regex (or otherwise run the same validation used by OnLinkAssetFilterChanged),
and if validation fails set linkAssetFilterError in _state and abort (do not
call linkAppToRepo); only pass assetFilterRegex = validatedTrimmedFilter.takeIf
{ it.isNotBlank() } into linkAppToRepo when validation succeeds. Ensure you
reference the existing symbols: OnLinkAssetFilterChanged,
_state.value.linkAssetFilter, linkAssetFilterError, and linkAppToRepo to locate
and fix both link code paths.

---

Nitpick comments:
In
`@feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsRoot.kt`:
- Around line 754-774: The IconButton currently only reflects active filter
state via tint (hasFilter), which is not accessible; update the control to
expose the same state through semantics and/or a visible badge/textual cue: add
a semantic state or custom property to the IconButton modifier (e.g.,
Modifier.semantics { contentDescription = advancedFilterDescription;
stateDescription = if (hasFilter) "filter active" else "no filter" }) and
optionally render a small Badge/Text near the Icon when hasFilter is true; use
the existing symbols hasFilter, IconButton, advancedFilterDescription,
onAdvancedSettingsClick, app.assetFilterRegex and app.fallbackToOlderReleases to
locate where to add the semantics/stateDescription and the visual badge.

In
`@feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsState.kt`:
- Around line 50-59: The advancedPreviewMessage: String? in AppsState should be
replaced with a typed sealed class or enum (e.g., AdvancedPreviewMessageState
with cases NoMatch, PreviewFailed, SaveFailed, None) to avoid brittle string
token matching; update AppsState to use this new type (and a sensible default
like AdvancedPreviewMessageState.None), then refactor the match/when in
AdvancedAppSettingsBottomSheet to switch over AdvancedPreviewMessageState cases
instead of comparing raw literals ("no_match", "preview_failed", "save_failed"),
and update any other references (serializers/parcelables/initializers) to
construct and consume the new typed state.
🪄 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: a1bd1ded-bd5f-4136-8d7b-ae893b8b5ea0

📥 Commits

Reviewing files that changed from the base of the PR and between e1f0b8c and ed2fa6c.

📒 Files selected for processing (34)
  • core/data/schemas/zed.rainxch.core.data.local.db.AppDatabase/10.json
  • core/data/src/androidMain/kotlin/zed/rainxch/core/data/local/db/initDatabase.kt
  • core/data/src/androidMain/kotlin/zed/rainxch/core/data/local/db/migrations/MIGRATION_9_10.kt
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/AppDatabase.kt
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/dao/InstalledAppDao.kt
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/entities/InstalledAppEntity.kt
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/mappers/InstalledAppsMappers.kt
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/InstalledAppsRepositoryImpl.kt
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/model/InstalledApp.kt
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/InstalledAppsRepository.kt
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/AssetFilter.kt
  • 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/apps/data/src/commonMain/kotlin/zed/rainxch/apps/data/repository/AppsRepositoryImpl.kt
  • feature/apps/domain/src/commonMain/kotlin/zed/rainxch/apps/domain/repository/AppsRepository.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsAction.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsRoot.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsState.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsViewModel.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/components/AdvancedAppSettingsBottomSheet.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/components/LinkAppBottomSheet.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/mappers/InstalledAppMapper.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/model/InstalledAppUi.kt

Comment on lines +54 to +66
/**
* Persists per-app monorepo settings: an optional regex applied to asset
* names and whether the update checker should fall back to older
* releases when the latest one has no matching asset.
*
* Implementations should re-check the app for updates immediately so
* the UI reflects the new state without a manual refresh.
*/
suspend fun setAssetFilter(
packageName: String,
regex: String?,
fallbackToOlderReleases: Boolean,
)
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.

⚠️ Potential issue | 🟠 Major

setAssetFilter() promises an immediate refresh that the implementation does not perform.

The KDoc says saving the filter should re-check updates right away, but InstalledAppsRepositoryImpl.setAssetFilter() only persists the DAO fields. That leaves updateAvailable stale until the next explicit/background check. Either trigger checkForUpdates(packageName) as part of this operation or relax this contract and make the caller own the refresh.

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

In
`@core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/InstalledAppsRepository.kt`
around lines 54 - 66, The KDoc for InstalledAppsRepository.setAssetFilter
promises an immediate re-check but InstalledAppsRepositoryImpl.setAssetFilter
currently only persists DAO fields and leaves updateAvailable stale; update the
implementation of InstalledAppsRepositoryImpl.setAssetFilter to, after saving
the regex and fallbackToOlderReleases, call and await
checkForUpdates(packageName) (or the repository's internal method that computes
updateAvailable) so the UI-state is refreshed immediately, and handle/log any
exceptions from that call so persistence still succeeds even if the check fails.

- **Localization & Resources**:
  - Convert `asset_filter_visible_count` and `advanced_preview_release` to plurals to support proper inflection across all supported languages (English, Arabic, Bengali, Spanish, French, Hindi, Italian, Japanese, Korean, Polish, Russian, Turkish, and Simplified Chinese).
  - Add `asset_none_available` string for releases with no installable assets.
  - Update UI components to use `pluralStringResource`.

- **Core Data & Logic**:
  - Add `clearUpdateMetadata` to `InstalledAppDao` to atomically reset update badges and stale metadata when filters no longer match any assets.
  - Improve `AssetFilter.suggestFromAssetName` to return an escaped, anchored literal-prefix regex (e.g., `^\Qprefix\E`) to prevent metacharacters from breaking searches.
  - Update `AppsRepository` to include `assetFilterRegex` and `fallbackToOlderReleases` in the export schema (bumped to version 2).
  - Enhance `AppsViewModel` regex suggestion logic to automatically escape package segments and display name tokens.

- **Bug Fixes & Improvements**:
  - Ensure `checkForUpdates` clears stale metadata when no matching releases are found in the current window.
  - Fix structured concurrency by ensuring `CancellationException` is propagated during release fetches and filter updates.
  - Improve "Link App" UI to distinguish between an invalid regex, no matches, and no available assets.
  - Optimize `setAssetFilter` to perform an internal update check immediately after persisting changes.
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.

🧹 Nitpick comments (1)
core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/InstalledAppsRepositoryImpl.kt (1)

385-429: Consider adding CancellationException handling in previewMatchingAssets.

The previewMatchingAssets method calls fetchReleaseWindow, which can throw CancellationException. While fetchReleaseWindow re-throws it, the exception will propagate up to the ViewModel. This is likely fine since the ViewModel's refreshAdvancedPreview handles it, but for consistency with setAssetFilter, you might consider explicit handling here too.

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

In
`@core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/InstalledAppsRepositoryImpl.kt`
around lines 385 - 429, In previewMatchingAssets, wrap the call to
fetchReleaseWindow(owner, repo, includePreReleases) in a try/catch that
explicitly catches CancellationException and rethrows it (matching the pattern
used in setAssetFilter) so coroutine cancellation isn't accidentally swallowed;
keep existing behavior for other exceptions (i.e., let them propagate or handle
them as before). Reference: previewMatchingAssets and fetchReleaseWindow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/InstalledAppsRepositoryImpl.kt`:
- Around line 385-429: In previewMatchingAssets, wrap the call to
fetchReleaseWindow(owner, repo, includePreReleases) in a try/catch that
explicitly catches CancellationException and rethrows it (matching the pattern
used in setAssetFilter) so coroutine cancellation isn't accidentally swallowed;
keep existing behavior for other exceptions (i.e., let them propagate or handle
them as before). Reference: previewMatchingAssets and fetchReleaseWindow.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 35af2806-9467-488d-9d0e-4d266be72bba

📥 Commits

Reviewing files that changed from the base of the PR and between ed2fa6c and f82610c.

📒 Files selected for processing (21)
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/dao/InstalledAppDao.kt
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/InstalledAppsRepositoryImpl.kt
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/model/ExportedApp.kt
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/AssetFilter.kt
  • 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/apps/data/src/commonMain/kotlin/zed/rainxch/apps/data/repository/AppsRepositoryImpl.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsViewModel.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/components/AdvancedAppSettingsBottomSheet.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/components/LinkAppBottomSheet.kt
✅ Files skipped from review due to trivial changes (6)
  • core/presentation/src/commonMain/composeResources/values/strings.xml
  • core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml
  • core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml
  • core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml
  • core/presentation/src/commonMain/composeResources/values-it/strings-it.xml
  • core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml
🚧 Files skipped from review as they are similar to previous changes (8)
  • core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml
  • core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml
  • core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml
  • core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml
  • core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml
  • core/presentation/src/commonMain/composeResources/values-es/strings-es.xml
  • core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/components/AdvancedAppSettingsBottomSheet.kt

@rainxchzed rainxchzed merged commit 7a5f8e2 into main Apr 11, 2026
1 check passed
@rainxchzed rainxchzed deleted the monorepo-support branch April 11, 2026 01: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