feat: include starred repos in auto-link suggestions#686
Conversation
WalkthroughThis PR adds starred repository support to the external app import workflow. When scanning GitHub apps, the feature now fetches the user's starred repositories, normalizes and scores them against candidate labels, and surfaces matching starred repos as auto-suggestions alongside matches from manifest, fingerprint, backend, and Forgejo sources. ChangesStarred Repository Match Feature
Sequence DiagramsequenceDiagram
participant ImportVM as ExternalImportViewModel
participant ExternalImportRepository
participant StarredRepository
participant MatchScoringEngine as Match Scoring
ImportVM->>ExternalImportRepository: resolveMatches(...)
ExternalImportRepository->>StarredRepository: getAllStarred()
StarredRepository-->>ExternalImportRepository: starred repo list
ExternalImportRepository->>MatchScoringEngine: score candidates vs starred repos
MatchScoringEngine->>MatchScoringEngine: normalizeMatchToken() per repo label/name
MatchScoringEngine->>MatchScoringEngine: apply exact/contains confidence thresholds
MatchScoringEngine-->>ExternalImportRepository: starred match suggestions
ExternalImportRepository->>ExternalImportRepository: combine with manifest/fingerprint/backend matches
ExternalImportRepository->>ExternalImportRepository: sort by confidence, dedupe by host/owner/repo
ExternalImportRepository-->>ImportVM: resolved suggestions with STARRED source
ImportVM->>ImportVM: toUi() map STARRED to SuggestionSource.STARRED
Estimated Code Review Effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly Related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 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/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt`:
- Around line 270-272: The current use of runCatching around
starredRepository.getAllStarred().first() in ExternalImportRepositoryImpl (the
starred variable) swallows CancellationException and converts cancellations into
emptyList(); change it to explicitly preserve cancellation by replacing
runCatching with a try/catch that rethrows CancellationException and only
catches/logs non-cancellation exceptions, returning emptyList() on those; locate
the starredRepository.getAllStarred().first() call in
ExternalImportRepositoryImpl.kt and implement the try { val starred = ... }
catch (e: CancellationException) { throw e } catch (e: Exception) { Logger.d {
"starred fetch for match failed: ${e.message}" }; emptyList() } pattern.
In `@core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml`:
- Line 598: The Polish label for the starred source is ambiguous; update the
string resource named match_source_starred to use a clearer phrase for source
provenance by replacing its value "Z gwiazdką" with "Oznaczone gwiazdką" so it
matches the rest of the locale and reads unambiguously in the UI.
In `@core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml`:
- Line 598: The Russian label for the starred source is awkward; update the
string resource with name "match_source_starred" to use the consistent
terminology by replacing its value "Со звездой" with "Из избранного" so it
matches other locale strings that use "избранные" for starred repos.
In `@docs/release-notes/1.9.0.md`:
- Around line 5-48: The English release notes are missing the starred-repo
matching feature; insert a new subsection titled "### 🌟 Starred Repository
Matching" into the "New Features" section immediately after the "Desktop
Maturity" subsection and add the three bullets: "Linking installed apps now
matches against **your starred repositories**", "'Scan GitHub Apps'
automatically suggests starred repos as candidates", and "Reduces manual URL
pasting when linking apps you've already starred" so the English markdown
mirrors the localized JSON entries.
- Line 3: The document uses an h3 heading ("### The biggest visual overhaul
since launch. New Geist typography, hero app headers, redesigned Home cards with
platform glyphs, refreshed Library with updates banner + \"Ready to install\",
Apple-style menus everywhere. Tablet two-pane lands. Inner Details pages get
dedicated screens. Desktop finally feels native — window state persists, Windows
11 + macOS dark title bars, fluid content widths. Root installs work again on
modern Magisk via libsu rewrite.") immediately after the h1, skipping h2; change
that h3 to h2 (replace the leading "###" with "##") in
docs/release-notes/1.9.0.md so heading levels increment by one and restore
correct document structure and accessibility.
🪄 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: 2c85cba7-4ee5-4eaa-b33e-4606bc4cdf47
📒 Files selected for processing (33)
core/data/src/commonMain/kotlin/zed/rainxch/core/data/di/SharedModule.ktcore/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.ktcore/domain/src/commonMain/kotlin/zed/rainxch/core/domain/system/RepoMatchSource.ktcore/presentation/src/commonMain/composeResources/files/whatsnew/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/ar/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/bn/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/es/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/fr/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/hi/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/it/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/ja/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/ko/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/pl/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/ru/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/tr/19.jsoncore/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/19.jsoncore/presentation/src/commonMain/composeResources/values-ar/strings-ar.xmlcore/presentation/src/commonMain/composeResources/values-bn/strings-bn.xmlcore/presentation/src/commonMain/composeResources/values-es/strings-es.xmlcore/presentation/src/commonMain/composeResources/values-fr/strings-fr.xmlcore/presentation/src/commonMain/composeResources/values-hi/strings-hi.xmlcore/presentation/src/commonMain/composeResources/values-it/strings-it.xmlcore/presentation/src/commonMain/composeResources/values-ja/strings-ja.xmlcore/presentation/src/commonMain/composeResources/values-ko/strings-ko.xmlcore/presentation/src/commonMain/composeResources/values-pl/strings-pl.xmlcore/presentation/src/commonMain/composeResources/values-ru/strings-ru.xmlcore/presentation/src/commonMain/composeResources/values-tr/strings-tr.xmlcore/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xmlcore/presentation/src/commonMain/composeResources/values/strings.xmldocs/release-notes/1.9.0.mdfeature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/components/LinkAppBottomSheet.ktfeature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/import/ExternalImportViewModel.ktfeature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/import/model/RepoSuggestionUi.kt
| val starred = runCatching { starredRepository.getAllStarred().first() } | ||
| .onFailure { Logger.d { "starred fetch for match failed: ${it.message}" } } | ||
| .getOrDefault(emptyList()) |
There was a problem hiding this comment.
Preserve cancellation when reading starred repos
Line 270 uses runCatching, which catches CancellationException and turns cancellation into emptyList(). This can keep cancelled scans running.
Proposed fix
- val starred = runCatching { starredRepository.getAllStarred().first() }
- .onFailure { Logger.d { "starred fetch for match failed: ${it.message}" } }
- .getOrDefault(emptyList())
+ val starred = try {
+ starredRepository.getAllStarred().first()
+ } catch (e: CancellationException) {
+ throw e
+ } catch (e: Exception) {
+ Logger.d { "starred fetch for match failed: ${e.message}" }
+ emptyList()
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| val starred = runCatching { starredRepository.getAllStarred().first() } | |
| .onFailure { Logger.d { "starred fetch for match failed: ${it.message}" } } | |
| .getOrDefault(emptyList()) | |
| val starred = try { | |
| starredRepository.getAllStarred().first() | |
| } catch (e: CancellationException) { | |
| throw e | |
| } catch (e: Exception) { | |
| Logger.d { "starred fetch for match failed: ${e.message}" } | |
| emptyList() | |
| } |
🤖 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/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt`
around lines 270 - 272, The current use of runCatching around
starredRepository.getAllStarred().first() in ExternalImportRepositoryImpl (the
starred variable) swallows CancellationException and converts cancellations into
emptyList(); change it to explicitly preserve cancellation by replacing
runCatching with a try/catch that rethrows CancellationException and only
catches/logs non-cancellation exceptions, returning emptyList() on those; locate
the starredRepository.getAllStarred().first() call in
ExternalImportRepositoryImpl.kt and implement the try { val starred = ... }
catch (e: CancellationException) { throw e } catch (e: Exception) { Logger.d {
"starred fetch for match failed: ${e.message}" }; emptyList() } pattern.
| <string name="match_source_fingerprint">Podpis</string> | ||
| <string name="match_source_search">Wyszukiwanie</string> | ||
| <string name="match_source_manual">Ręczne</string> | ||
| <string name="match_source_starred">Z gwiazdką</string> |
There was a problem hiding this comment.
Use a clearer Polish label for starred source.
Z gwiazdką is ambiguous in UI (can read like “with an asterisk”). For source provenance, use a clearer phrase like Oznaczone gwiazdką to match the rest of the locale.
Suggested fix
- <string name="match_source_starred">Z gwiazdką</string>
+ <string name="match_source_starred">Oznaczone gwiazdką</string>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <string name="match_source_starred">Z gwiazdką</string> | |
| <string name="match_source_starred">Oznaczone gwiazdką</string> |
🤖 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-pl/strings-pl.xml`
at line 598, The Polish label for the starred source is ambiguous; update the
string resource named match_source_starred to use a clearer phrase for source
provenance by replacing its value "Z gwiazdką" with "Oznaczone gwiazdką" so it
matches the rest of the locale and reads unambiguously in the UI.
| <string name="match_source_fingerprint">Подпись</string> | ||
| <string name="match_source_search">Поиск</string> | ||
| <string name="match_source_manual">Вручную</string> | ||
| <string name="match_source_starred">Со звездой</string> |
There was a problem hiding this comment.
Russian label is awkward and inconsistent with existing terminology.
Со звездой reads unnatural here. This locale already uses “избранные” for starred repos, so the source chip should be aligned (e.g., Из избранного).
Suggested fix
- <string name="match_source_starred">Со звездой</string>
+ <string name="match_source_starred">Из избранного</string>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <string name="match_source_starred">Со звездой</string> | |
| <string name="match_source_starred">Из избранного</string> |
🤖 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-ru/strings-ru.xml`
at line 598, The Russian label for the starred source is awkward; update the
string resource with name "match_source_starred" to use the consistent
terminology by replacing its value "Со звездой" with "Из избранного" so it
matches other locale strings that use "избранные" for starred repos.
| @@ -0,0 +1,52 @@ | |||
| # 🚀 GitHub Store 1.9 — Design Refresh & Desktop Maturity | |||
|
|
|||
| ### The biggest visual overhaul since launch. New Geist typography, hero app headers, redesigned Home cards with platform glyphs, refreshed Library with updates banner + "Ready to install", Apple-style menus everywhere. Tablet two-pane lands. Inner Details pages get dedicated screens. Desktop finally feels native — window state persists, Windows 11 + macOS dark title bars, fluid content widths. Root installs work again on modern Magisk via libsu rewrite. | |||
There was a problem hiding this comment.
Fix heading level skip.
Line 3 uses h3 (###) directly after the h1 title on line 1, skipping h2. Markdown heading levels should increment by one level at a time for proper document structure and accessibility.
📝 Proposed fix
-### The biggest visual overhaul since launch. New Geist typography, hero app headers, redesigned Home cards with platform glyphs, refreshed Library with updates banner + "Ready to install", Apple-style menus everywhere. Tablet two-pane lands. Inner Details pages get dedicated screens. Desktop finally feels native — window state persists, Windows 11 + macOS dark title bars, fluid content widths. Root installs work again on modern Magisk via libsu rewrite.
+## The biggest visual overhaul since launch. New Geist typography, hero app headers, redesigned Home cards with platform glyphs, refreshed Library with updates banner + "Ready to install", Apple-style menus everywhere. Tablet two-pane lands. Inner Details pages get dedicated screens. Desktop finally feels native — window state persists, Windows 11 + macOS dark title bars, fluid content widths. Root installs work again on modern Magisk via libsu rewrite.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ### The biggest visual overhaul since launch. New Geist typography, hero app headers, redesigned Home cards with platform glyphs, refreshed Library with updates banner + "Ready to install", Apple-style menus everywhere. Tablet two-pane lands. Inner Details pages get dedicated screens. Desktop finally feels native — window state persists, Windows 11 + macOS dark title bars, fluid content widths. Root installs work again on modern Magisk via libsu rewrite. | |
| ## The biggest visual overhaul since launch. New Geist typography, hero app headers, redesigned Home cards with platform glyphs, refreshed Library with updates banner + "Ready to install", Apple-style menus everywhere. Tablet two-pane lands. Inner Details pages get dedicated screens. Desktop finally feels native — window state persists, Windows 11 + macOS dark title bars, fluid content widths. Root installs work again on modern Magisk via libsu rewrite. |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 3-3: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3
(MD001, heading-increment)
🤖 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 `@docs/release-notes/1.9.0.md` at line 3, The document uses an h3 heading ("###
The biggest visual overhaul since launch. New Geist typography, hero app
headers, redesigned Home cards with platform glyphs, refreshed Library with
updates banner + \"Ready to install\", Apple-style menus everywhere. Tablet
two-pane lands. Inner Details pages get dedicated screens. Desktop finally feels
native — window state persists, Windows 11 + macOS dark title bars, fluid
content widths. Root installs work again on modern Magisk via libsu rewrite.")
immediately after the h1, skipping h2; change that h3 to h2 (replace the leading
"###" with "##") in docs/release-notes/1.9.0.md so heading levels increment by
one and restore correct document structure and accessibility.
| ## ✨ New Features | ||
|
|
||
| ### 🎨 Design Overhaul | ||
| - New **Geist** typography across the app | ||
| - Hero app header on Details with clickable owner avatar + ✓ verification badge | ||
| - Redesigned Home cards now show **every platform** a repo ships installers for | ||
| - Refreshed Library with **Updates banner** and **Ready to install** section | ||
| - Apple-style dropdown menus (`GhsDropdownMenu`) across all overflow surfaces | ||
| - Real Apple + Tux icons for macOS / Linux platform indicators | ||
| - Native Compose contribution calendar on developer profiles (hidden for orgs) | ||
| - Clickable @mentions and clickable company in bios | ||
|
|
||
| ### 📱 Tablet Two-Pane | ||
| - Home / Search / Library list on the left, repo opens on the right | ||
| - Draggable divider, persists across sessions | ||
| - Inner Details (About, What's New) slide *within* the right pane | ||
|
|
||
| ### 🖥 Desktop Maturity | ||
| - **Window state persists** — size, position, maximized survive across launches (#664) | ||
| - **Windows 11 + macOS** title bars match system dark mode (#663) | ||
| - **Fluid content width** — Compact / Wide / Extra wide scale as 55% / 75% / 95% of window | ||
| - Real GitHub Store logo in side drawer (no more "G" placeholder) | ||
| - MenuBar with About / Feedback / Licenses / Privacy | ||
|
|
||
| ## 🐛 Bug Fixes | ||
| - **Mirror + direct download race** corrupting destination file (#667) | ||
| - **Root installer** on Android 14+ / Magisk 27+ rewritten on **libsu** (#651) | ||
| - Linked apps now show **"Update to X"** immediately instead of stale "Install vX" | ||
| - Multi-flavor APK repos no longer show false "Update" CTA (#638) | ||
| - Long release tags no longer wrap into one-char vertical date columns | ||
| - URL paste verifies repo exists before showing match | ||
| - Dynamic color contrast on Search TextField across all palettes | ||
| - README + release-notes keep scroll position on return | ||
|
|
||
| ## ⚡ Performance | ||
| - Markdown no longer re-renders on every download progress tick (~10×/sec → 0) | ||
| - Chunked progressive markdown — large READMEs paint first screen in <100ms | ||
| - `LazyColumn.animateItem()` across discovery / search / library | ||
| - Direction-aware bottom-nav transitions based on tab index | ||
|
|
||
| ## 🧹 Cleanups | ||
| - Anonymous telemetry removed completely | ||
| - App info moved from Tweaks → Profile (where users look for it) | ||
| - Discovery platforms moved into Tweaks → Sources |
There was a problem hiding this comment.
Add missing starred repository matching feature documentation.
The primary feature of this PR—starred repository matching in the external app import workflow—is documented in all 12 localized JSON release notes (line 15 in each file) but is completely absent from this English markdown release notes file. Users reading docs/release-notes/1.9.0.md will not learn about this significant new capability.
📝 Suggested addition to the "New Features" section
Insert a new subsection after "Desktop Maturity" (around line 27):
### 🌟 Starred Repository Matching
- Linking installed apps now matches against **your starred repositories**
- "Scan GitHub Apps" automatically suggests starred repos as candidates
- Reduces manual URL pasting when linking apps you've already starred🤖 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 `@docs/release-notes/1.9.0.md` around lines 5 - 48, The English release notes
are missing the starred-repo matching feature; insert a new subsection titled
"### 🌟 Starred Repository Matching" into the "New Features" section immediately
after the "Desktop Maturity" subsection and add the three bullets: "Linking
installed apps now matches against **your starred repositories**", "'Scan GitHub
Apps' automatically suggests starred repos as candidates", and "Reduces manual
URL pasting when linking apps you've already starred" so the English markdown
mirrors the localized JSON entries.
Greptile SummaryThis PR folds the user's GitHub starred repos into the existing
Confidence Score: 3/5The core matching logic in ExternalImportRepositoryImpl needs a fix before this is safe to ship — specifically how it builds needles from the package name tail. The core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt — specifically Important Files Changed
Sequence DiagramsequenceDiagram
participant VM as ExternalImportViewModel
participant Repo as ExternalImportRepositoryImpl
participant FP as SigningFingerprintDao
participant API as ExternalMatchApi
participant Forgejo as ForgejoClientRegistry
participant Star as StarredRepository (Room)
VM->>Repo: resolveMatches(candidates)
Repo->>FP: lookup(signingFingerprint) per candidate
FP-->>Repo: fingerprintHits (confidence 0.92)
Repo->>API: match(batch) [backend]
API-->>Repo: backendResults
Repo->>Forgejo: "searchRepositories(query) [skipped if existing >= 0.7]"
Forgejo-->>Repo: forgejoHits
Repo->>Star: getAllStarred().first()
Star-->>Repo: starred list (Room cache)
Note over Repo: starredMatches(): normalize label + pkgTail,<br/>score each starred repo (exact=0.78, contains=0.60)
Repo->>Repo: sortedByDescending(confidence).distinctBy(owner/repo)
Repo-->>VM: List of RepoMatchResult (deduped, highest confidence wins)
Reviews (1): Last reviewed commit: "feat(apps): include starred repos in aut..." | Re-trigger Greptile |
| private fun starredMatches( | ||
| candidate: ExternalAppCandidate, | ||
| starred: List<zed.rainxch.core.domain.model.StarredRepository>, | ||
| ): List<RepoMatchSuggestion> { | ||
| if (starred.isEmpty()) return emptyList() | ||
| val label = normalizeMatchToken(candidate.appLabel) | ||
| val pkgTail = normalizeMatchToken(candidate.packageName.substringAfterLast('.')) | ||
| val needles = listOf(label, pkgTail).filter { it.length >= MIN_MATCH_TOKEN_LEN } | ||
| if (needles.isEmpty()) return emptyList() | ||
|
|
||
| return starred.mapNotNull { repo -> | ||
| val repoName = normalizeMatchToken(repo.repoName) | ||
| if (repoName.length < MIN_MATCH_TOKEN_LEN) return@mapNotNull null | ||
| val confidence = needles.maxOf { needle -> | ||
| when { | ||
| needle == repoName -> STARRED_EXACT_CONFIDENCE | ||
| needle.contains(repoName) || repoName.contains(needle) -> STARRED_CONTAINS_CONFIDENCE | ||
| else -> 0.0 | ||
| } | ||
| } | ||
| if (confidence <= 0.0) return@mapNotNull null | ||
| RepoMatchSuggestion( | ||
| owner = repo.repoOwner, | ||
| repo = repo.repoName, | ||
| confidence = confidence, | ||
| source = RepoMatchSource.STARRED, | ||
| stars = repo.stargazersCount, | ||
| description = repo.repoDescription, | ||
| sourceHost = null, | ||
| ) | ||
| } |
There was a problem hiding this comment.
Generic pkgTail produces false-positive preselected suggestions
pkgTail is derived by taking everything after the last . in the package name. For any app with a .android suffix — an extremely common Android convention (com.twitter.android, com.snapchat.android, com.example.android) — the normalized tail is "android" (7 chars, passes MIN_MATCH_TOKEN_LEN). The contains check on line 314 then produces a STARRED_CONTAINS_CONFIDENCE = 0.60 match for every starred repo whose normalized name contains "android" (e.g. google/android-architecture-samples, JakeWharton/android-times-square, any *-android-* repo). Because 0.60 falls inside PRESELECT_MIN..PRESELECT_MAX = [0.5, 0.85], the wrong repo is surfaced as the preselected suggestion in the review card for every such commercial app that has no manifest/fingerprint/backend match. The same problem arises for other common suffixes: mobile, lite, beta, debug, release all pass the 4-char minimum and will substring-match large numbers of starred repos.
| val starred = runCatching { starredRepository.getAllStarred().first() } | ||
| .onFailure { Logger.d { "starred fetch for match failed: ${it.message}" } } | ||
| .getOrDefault(emptyList()) |
There was a problem hiding this comment.
No skip-threshold guard on starred matching — unlike Forgejo search (which skips candidates with existing confidence ≥ 0.7),
starredMatches() runs unconditionally for every candidate on every resolveMatches() call. For a user with 1,000+ starred repos and 50+ candidates that already have fingerprint/backend hits, this is 50,000+ in-memory iterations that produce only noise (the low-confidence starred entries won't win the dedupe). Adding the same guard used for Forgejo would eliminate this work at zero correctness cost.
| val starred = runCatching { starredRepository.getAllStarred().first() } | |
| .onFailure { Logger.d { "starred fetch for match failed: ${it.message}" } } | |
| .getOrDefault(emptyList()) | |
| val hasHighConfidenceMatch = { packageName: String -> | |
| val existing = listOfNotNull( | |
| candidates.firstOrNull { it.packageName == packageName }?.manifestHint?.confidence, | |
| fingerprintHits[packageName]?.confidence, | |
| backendResults[packageName]?.maxOfOrNull { it.confidence }, | |
| forgejoHits[packageName]?.maxOfOrNull { it.confidence }, | |
| ).maxOrNull() ?: 0.0 | |
| existing >= FORGEJO_SEARCH_SKIP_THRESHOLD | |
| } | |
| val starred = runCatching { starredRepository.getAllStarred().first() } | |
| .onFailure { Logger.d { "starred fetch for match failed: ${it.message}" } } | |
| .getOrDefault(emptyList()) |
| val starred = runCatching { starredRepository.getAllStarred().first() } | ||
| .onFailure { Logger.d { "starred fetch for match failed: ${it.message}" } } | ||
| .getOrDefault(emptyList()) |
There was a problem hiding this comment.
Repeated full DB reads in per-package rescans
getAllStarred().first() is called on every resolveMatches() invocation, including inside rescanSinglePackage() which passes a single-element list. When the PackageMonitor detects a burst of installs (or the user retriggers individual rescans), each call issues a full Room query for the entire starred table. Hoisting the fetch outside resolveMatches or caching the starred list at the instance level (similar to how candidateSnapshot works) would avoid these redundant reads.
Closes #637 (method 3).
@FumiyaSenro asked that when linking an already-installed app, the app should also look through the user's starred repos — most apps worth tracking are ones they've already starred, and the manual-URL fallback is annoying (nobody remembers repo URLs).
Rather than bloating the link sheet with a separate "pick from starred" step, this folds starred matching into the existing auto-match path.
ExternalImportRepositoryImpl.resolveMatches(the single resolver behind both the "Scan GitHub Apps" link sheet AND the Obtainium import scan) now adds a 5th candidate source:StarredRepository.getAllStarred()once per resolve.RepoMatchSuggestion(source = STARRED, stars, description)merged with manifest / fingerprint / backend / Forgejo results.distinctBykept whichever source ran first, which could drop a higher-confidence starred hit).Both flows surface starred matches automatically — no new screen, no extra button. Suggestions carry a "Starred" provenance chip.
Changes:
RepoMatchSource.STARRED+SuggestionSource.STARRED.ExternalImportRepositoryImplgainsstarredRepositorydep (wired inSharedModule).match_source_starredstring + all 12 locales; chip label inLinkAppBottomSheet.Compile-verified
:core:data+:feature:apps:presentation(Android) +:composeApp(jvm). Method 2 (reworking the "Add from Starred" screen to link-in-place) intentionally left out to keep this scoped.Summary by CodeRabbit
New Features
Documentation