Skip to content

feat(apps): ignore updates per app (#536)#537

Merged
rainxchzed merged 5 commits into
mainfrom
feat/536-ignore-app-updates
May 7, 2026
Merged

feat(apps): ignore updates per app (#536)#537
rainxchzed merged 5 commits into
mainfrom
feat/536-ignore-app-updates

Conversation

@rainxchzed
Copy link
Copy Markdown
Member

@rainxchzed rainxchzed commented May 7, 2026

Closes #536.

Summary

  • Adds a per-app "Ignore updates" toggle in the apps list overflow menu (rich update card + compact row).
  • When ignored, the app is demoted out of the "Updates available" section to "Up to date" and never gets checked again.
  • Restoring the toggle re-runs checkForUpdates immediately so the badge comes back without waiting for the next periodic check.

Implementation

  • InstalledAppDao.updateUpdateCheckEnabled writes the existing updateCheckEnabled column.
  • InstalledAppsRepository.setUpdateCheckEnabled writes the column, clears stale update metadata on disable, and re-checks on enable. checkForUpdates(packageName) is now gated on the column so single-package callers (e.g. PackageEventReceiver post-install refresh) honour the user's choice.
  • AppsRoot section split now also routes !updateCheckEnabled rows to the idle group as a defensive filter.
  • AppsAction.OnToggleUpdateCheck + AppsViewModel.toggleUpdateCheck thread the toggle through.
  • CompactAppRow overflow gets a new "Ignore updates" item; AppItemCard gains a trailing overflow with the same item.
  • New status dot (filled bar, outline tint) + accessibility label "updates ignored" exposed through CompactStatusFlags.

Strings

  • apps_ignore_updates and apps_compact_status_updates_ignored added to all 13 locales (ar/bn/es/fr/hi/it/ja/ko/pl/ru/tr/zh-rCN + default).

What's-new

  • Seeds versionCode 17 (1.9.0) what's-new bullet under NEW across 13 locales. Release prep can rename the version.

Test plan

  • Android: install + toggle "Ignore updates" on a row with an update available β†’ row moves to "Up to date" with the new dot, no Update button.
  • Re-enable: row returns to the updates section after the re-check completes.
  • Restart app: state persists.
  • PackageEventReceiver post-install does NOT resurrect an isUpdateAvailable badge for ignored apps.
  • Background WorkManager checkAllForUpdates skips ignored apps (was already gated; verify no regression).

Summary by CodeRabbit

  • New Features

    • Per-app update-check toggle: silence update badges, hide update notifications, and control checks per app; enabling triggers an immediate check, disabling clears cached update info.
    • UI improvements: overflow menu action to ignore updates and a new compact-status indicator showing when an app’s updates are ignored.
  • Documentation

    • Added localized β€œWhat’s New” entries for version 1.9.0 describing the per-app update control.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Review Change Stack

Walkthrough

This PR adds per-app update check toggling: users can suppress update notifications per app via an overflow menu; the flag is persisted, repository checks respect it (early-return), the UI shows a bar-shaped ignored indicator, and localized whatsnew/string entries were added.

Changes

Per-App Update Check Toggle Feature

Layer / File(s) Summary
Data & Persistence Contracts
core/data/.../InstalledAppDao.kt, core/domain/.../InstalledAppsRepository.kt
InstalledAppDao.updateUpdateCheckEnabled(packageName, enabled) and repository interface setUpdateCheckEnabled(packageName, enabled) added.
Repository Core Logic
core/data/.../InstalledAppsRepositoryImpl.kt
checkForUpdates() now early-returns false when updateCheckEnabled is disabled. New setUpdateCheckEnabled() persists the flag, re-runs checks when enabling, and clears cached metadata when disabling.
UI Action Type
feature/apps/presentation/.../AppsAction.kt
New AppsAction.OnToggleUpdateCheck(packageName, enabled) action.
ViewModel Integration
feature/apps/presentation/.../AppsViewModel.kt
onAction routes OnToggleUpdateCheck to toggleUpdateCheck() helper that calls repository and handles exceptions.
UI List Structure & Callback Wiring
feature/apps/presentation/.../AppsRoot.kt
Updates grouping to require updateCheckEnabled for "updates available"; AppItemCard extended with onToggleUpdateCheck and wired across groups; overflow added to app card.
Compact App Row UI
feature/apps/presentation/.../components/CompactAppRow.kt
CompactAppRow/CompactRowOverflow accept onToggleUpdateCheck and isUpdateCheckEnabled; new "Ignore updates" menu item shows check and toggles state.
Visual Status Indicator
feature/apps/presentation/.../components/StatusDotCluster.kt
CompactStatusFlags adds updatesIgnored computed from !app.updateCheckEnabled; StatusDotCluster renders new DotShape.Bar and accessibility includes "updates ignored".
Localization & Release Notes
core/presentation/.../whatsnew/*/*.json, core/presentation/.../values*/*.xml
Added apps_compact_status_updates_ignored and apps_ignore_updates strings across locales and what's-new JSON entries for versionCode 17 / v1.9.0 in multiple locales.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I nibbled a carrot and found a switch,
Now badges are quiet when you tell them "no itch".
A tiny bar shows the apps on snooze,
DB keeps the secret β€” you choose which to lose. πŸ₯•

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

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% 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 title 'feat(apps): ignore updates per app (#536)' accurately and concisely summarizes the main feature addition - a per-app update ignore functionality for the apps feature.
Linked Issues check βœ… Passed The PR fully implements the objective from issue #536: it adds a per-app 'Ignore updates' toggle in the UI, prevents ignored apps from being checked for updates, and persists the user preference across restarts.
Out of Scope Changes check βœ… Passed All changes are directly related to implementing the per-app ignore updates feature: database schema support, repository logic, UI toggles, string localization, and what's-new entries are all in scope.

✏️ 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/536-ignore-app-updates

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

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/AppsRoot.kt (1)

551-553: ⚠️ Potential issue | 🟠 Major | ⚑ Quick win

Exclude ignored apps from the β€œUpdate all” trigger condition.

Line 551 currently counts updates even when updateCheckEnabled is false, so ignored apps can still surface the global update CTA. Gate this with updateCheckEnabled to keep UI behavior aligned with ignore-updates semantics.

Proposed fix
-                val hasUpdates = state.apps.any { it.installedApp.isUpdateAvailable }
+                val hasUpdates =
+                    state.apps.any {
+                        it.installedApp.isUpdateAvailable && it.installedApp.updateCheckEnabled
+                    }
πŸ€– 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/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsRoot.kt`
around lines 551 - 553, The current hasUpdates calculation uses state.apps.any {
it.installedApp.isUpdateAvailable } and thus includes apps that have
updateCheckEnabled == false; change the predicate to also require the app's
updateCheckEnabled flag (e.g., state.apps.any {
it.installedApp.updateCheckEnabled && it.installedApp.isUpdateAvailable }) so
the β€œUpdate all” CTA only appears for apps that are not ignored; update the
hasUpdates usage so the gate for Button remains consistent.
🧹 Nitpick comments (1)
core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/InstalledAppsRepository.kt (1)

76-79: πŸ’€ Low value

Consider documenting the non-trivial side effects in KDoc.

setUpdateCheckEnabled has meaningful side-effect semantics β€” disabling clears cached update metadata, enabling immediately re-triggers checkForUpdates β€” that are not obvious from the signature alone. Several other methods in this interface (setAssetFilter, setPreferredVariant, setPendingInstallFilePath) carry KDoc explaining exactly these kinds of implicit behaviors. Callers and future implementors benefit from the same clarity here.

πŸ“ Suggested KDoc
+    /**
+     * Enables or disables the periodic update check for [packageName].
+     *
+     * When [enabled] is `false` the implementation should clear any
+     * cached update metadata (badge + latest-release fields) so the app
+     * moves out of the "Updates available" group immediately.
+     *
+     * When [enabled] is `true` the implementation should call
+     * [checkForUpdates] for [packageName] right away so the badge is
+     * restored without waiting for the next periodic worker run.
+     */
     suspend fun setUpdateCheckEnabled(
         packageName: String,
         enabled: Boolean,
     )
πŸ€– 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/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/InstalledAppsRepository.kt`
around lines 76 - 79, The method setUpdateCheckEnabled in
InstalledAppsRepository.kt has non-obvious side effects (disabling should clear
cached update metadata; enabling should immediately re-trigger checkForUpdates)
that need KDoc similar to other interface methods; add a KDoc comment (/** ...
*/) directly above the suspend fun setUpdateCheckEnabled(packageName: String,
enabled: Boolean) declaration describing these behaviors, the expected semantics
for implementors and callers, and any relevant concurrency or timing guarantees
(e.g., that enabling triggers an immediate checkForUpdates call and disabling
clears cached update metadata).
πŸ€– 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 `@core/presentation/src/commonMain/composeResources/files/whatsnew/pl/17.json`:
- Line 10: Replace the non-idiomatic phrase "per aplikacja" in the JSON string
"Ignorowanie aktualizacji per aplikacja β€” wycisz powiadomienie o aktualizacji
dla dowolnej aplikacji, której nie chcesz aktualizować." with an idiomatic
Polish alternative such as "dla kaΕΌdej aplikacji" or "dla poszczegΓ³lnych
aplikacji" (e.g., "Ignorowanie aktualizacji dla kaΕΌdej aplikacji β€” wycisz
powiadomienie o aktualizacji dla dowolnej aplikacji, ktΓ³rej nie chcesz
aktualizować.") to match natural Polish phrasing used elsewhere.

In `@core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml`:
- Line 1010: Update the Hindi string value for the resource key
apps_compact_status_updates_ignored to use correct grammatical agreement:
replace the current text "ΰ€…ΰ€ͺΰ€‘ΰ₯‡ΰ€Ÿ ΰ€…ΰ€¨ΰ€¦ΰ₯‡ΰ€–ΰ€Ύ ΰ€•ΰ€Ώΰ€ ΰ€—ΰ€" with "ΰ€…ΰ€ͺΰ€‘ΰ₯‡ΰ€Ÿ ΰ€…ΰ€¨ΰ€¦ΰ₯‡ΰ€–ΰ₯‡ ΰ€•ΰ€Ώΰ€ ΰ€—ΰ€" so the
phrase reads naturally and agrees in number/gender with the noun.

In `@core/presentation/src/commonMain/composeResources/values-it/strings-it.xml`:
- Line 1014: Update the Italian string value for the resource named
apps_ignore_updates to use the more natural phrasing "Ignora gli aggiornamenti"
instead of "Ignora aggiornamenti"; locate the <string
name="apps_ignore_updates"> entry and replace its text content accordingly so
the UI shows the idiomatic action label.

In
`@feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsViewModel.kt`:
- Around line 593-600: The toggleUpdateCheck coroutine currently catches all
Exception types, which will also swallow CancellationException and break
coroutine cancellation; update the error handling in toggleUpdateCheck (the
viewModelScope.launch block calling
installedAppsRepository.setUpdateCheckEnabled) so that CancellationException is
not suppressed β€” detect and rethrow CancellationException (or handle it
separately) and only log other exceptions via logger.error; keep the existing
try/catch structure but ensure CancellationException is propagated.

---

Outside diff comments:
In
`@feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsRoot.kt`:
- Around line 551-553: The current hasUpdates calculation uses state.apps.any {
it.installedApp.isUpdateAvailable } and thus includes apps that have
updateCheckEnabled == false; change the predicate to also require the app's
updateCheckEnabled flag (e.g., state.apps.any {
it.installedApp.updateCheckEnabled && it.installedApp.isUpdateAvailable }) so
the β€œUpdate all” CTA only appears for apps that are not ignored; update the
hasUpdates usage so the gate for Button remains consistent.

---

Nitpick comments:
In
`@core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/InstalledAppsRepository.kt`:
- Around line 76-79: The method setUpdateCheckEnabled in
InstalledAppsRepository.kt has non-obvious side effects (disabling should clear
cached update metadata; enabling should immediately re-trigger checkForUpdates)
that need KDoc similar to other interface methods; add a KDoc comment (/** ...
*/) directly above the suspend fun setUpdateCheckEnabled(packageName: String,
enabled: Boolean) declaration describing these behaviors, the expected semantics
for implementors and callers, and any relevant concurrency or timing guarantees
(e.g., that enabling triggers an immediate checkForUpdates call and disabling
clears cached update metadata).
πŸͺ„ 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: 92074511-2e19-44e2-b0f1-0d7604f97f93

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between cf7ae3e and 40c0f72.

πŸ“’ Files selected for processing (34)
  • 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/repository/InstalledAppsRepository.kt
  • core/presentation/src/commonMain/composeResources/files/whatsnew/17.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ar/17.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/bn/17.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/es/17.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/fr/17.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/hi/17.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/it/17.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ja/17.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ko/17.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/pl/17.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ru/17.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/tr/17.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/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/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/AppsViewModel.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/components/CompactAppRow.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/components/StatusDotCluster.kt

Comment thread core/presentation/src/commonMain/composeResources/files/whatsnew/pl/17.json Outdated
Comment thread core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml Outdated
Comment thread core/presentation/src/commonMain/composeResources/values-it/strings-it.xml Outdated
…xception, gate Update All on updateCheckEnabled
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)
feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsRoot.kt (1)

1122-1123: ⚑ Quick win

Use leadingIcon instead of a hardcoded βœ“ glyph for the checked-state indicator.

Appending " βœ“" to the label string is not idiomatic Material3, and TTS engines have inconsistent narration for the Unicode βœ“ character (some say "checkmark", some say "tick mark", some skip it). DropdownMenuItem exposes a leadingIcon slot specifically for this purpose; conditionally placing a check icon there provides both a consistent visual and a properly described semantics node.

♻️ Proposed refactor β€” replace βœ“ text with leadingIcon
 DropdownMenuItem(
-    text = {
-        val baseLabel = stringResource(Res.string.apps_ignore_updates)
-        Text(text = if (!app.updateCheckEnabled) "$baseLabel  βœ“" else baseLabel)
-    },
+    text = { Text(text = stringResource(Res.string.apps_ignore_updates)) },
     onClick = {
         showRowOverflow = false
         onToggleUpdateCheck(!app.updateCheckEnabled)
     },
+    leadingIcon = {
+        if (!app.updateCheckEnabled) {
+            Icon(
+                imageVector = Icons.Default.CheckCircle,
+                contentDescription = null,
+            )
+        } else {
+            // Reserve space so the text column stays aligned
+            Spacer(Modifier.size(24.dp))
+        }
+    },
 )
πŸ€– 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/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsRoot.kt`
around lines 1122 - 1123, Replace the hardcoded "  βœ“" glyph appended to the
label in AppsRoot.kt: stop mutating baseLabel/Text and instead supply the check
indicator via the DropdownMenuItem leadingIcon slot; conditionally render an
Icon (e.g., check icon) when !app.updateCheckEnabled and leave Text(text =
baseLabel) unmodified, and provide an appropriate contentDescription or use
semantics for the Icon so screen readers get a consistent description.
πŸ€– 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.

Nitpick comments:
In
`@feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsRoot.kt`:
- Around line 1122-1123: Replace the hardcoded "  βœ“" glyph appended to the label
in AppsRoot.kt: stop mutating baseLabel/Text and instead supply the check
indicator via the DropdownMenuItem leadingIcon slot; conditionally render an
Icon (e.g., check icon) when !app.updateCheckEnabled and leave Text(text =
baseLabel) unmodified, and provide an appropriate contentDescription or use
semantics for the Icon so screen readers get a consistent description.

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

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 348e76d5-6f46-49cb-9e78-65d16806dd76

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 40c0f72 and d17c97b.

πŸ“’ Files selected for processing (5)
  • core/presentation/src/commonMain/composeResources/files/whatsnew/pl/17.json
  • core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml
  • core/presentation/src/commonMain/composeResources/values-it/strings-it.xml
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsRoot.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsViewModel.kt
βœ… Files skipped from review due to trivial changes (1)
  • core/presentation/src/commonMain/composeResources/files/whatsnew/pl/17.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/AppsViewModel.kt
  • core/presentation/src/commonMain/composeResources/values-it/strings-it.xml

@rainxchzed rainxchzed merged commit 0ae1277 into main May 7, 2026
1 check passed
@rainxchzed rainxchzed deleted the feat/536-ignore-app-updates branch May 7, 2026 07:10
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