Skip to content

fix(web): use unique keys for duplicate search results#1886

Merged
s0up4200 merged 3 commits into
autobrr:developfrom
GtwoK:indexer-search-results-sorting
May 15, 2026
Merged

fix(web): use unique keys for duplicate search results#1886
s0up4200 merged 3 commits into
autobrr:developfrom
GtwoK:indexer-search-results-sorting

Conversation

@GtwoK
Copy link
Copy Markdown
Contributor

@GtwoK GtwoK commented May 15, 2026

Fixes duplicate React keys in search results when multiple indexers return results with the same GUID. Now key results using the same method as the backend to ensure unique GUID for each result.

Fixes #1777

Summary by CodeRabbit

  • Bug Fixes
    • Improved search result selection stability across multiple sources: selection now persists correctly for visible items.
    • Selection is now cleared when a previously selected item is filtered out or when a new search begins.
    • Consistent highlighting and rendering of the selected result across both mobile and desktop views.

Review Change Stack

…ross trackers, etc. Defined result key using the same method as the backend to ensure unique GUID for each result
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 15, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 77579256-f8bb-4e16-8ac6-0b6814090e42

📥 Commits

Reviewing files that changed from the base of the PR and between f54d1fd and 0fef178.

📒 Files selected for processing (1)
  • web/src/pages/Search.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/src/pages/Search.tsx

Walkthrough

This PR adds getSearchResultKey(result) (uses result.guid or falls back to result.downloadUrl), migrates selection state from selectedResultGuid to selectedResultKey, clears/resets selection appropriately, and updates mobile and desktop renderers to use the composite key for React keys and selection checks.

Changes

Selection and rendering update

Layer / File(s) Summary
Selection key helper and state migration
web/src/pages/Search.tsx
Add getSearchResultKey(result) that composes ${result.indexerId}-${resultId} where resultId is guid or downloadUrl; replace selectedResultGuid with selectedResultKey; clear/reset selection when searches start or selected item is filtered out; derive selected item by comparing composite keys.
Renderer key & selection binding
web/src/pages/Search.tsx
Update mobile SearchResultCard list and desktop table rows to use key={getSearchResultKey(result)} and compute isSelected via selectedResultKey === getSearchResultKey(result).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I stitched a key from two bits bright,
indexer first, then id in light.
When GUIDs are hollow, I borrow a trail,
so lists keep their place and selections prevail. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 accurately describes the main change: implementing unique keys for search results to handle duplicates from multiple indexers, which matches the core functionality added in the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/src/pages/Search.tsx`:
- Around line 1311-1314: selectedResultGuid currently holds only result.guid
causing ambiguous selection across indexers; update the selection state and
comparisons to use the same composite key used for React keys (e.g.
`${result.indexerId}-${result.guid}`). Change handleToggleResultSelection to
compute and toggle that composite id, update any place comparing
selectedResultGuid === result.guid to compare against the composite id instead,
and ensure selectedResult (lookup/filter) uses the composite id when resolving
the currently selected item; apply this change consistently for both the Search
result list rendering and the other occurrence around
handleToggleResultSelection/selectedResult usage.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e1a670fc-8aa6-412f-b11d-eabf13ce53b4

📥 Commits

Reviewing files that changed from the base of the PR and between d70d0bb and c843adf.

📒 Files selected for processing (1)
  • web/src/pages/Search.tsx

Comment thread web/src/pages/Search.tsx Outdated
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 (2)
web/src/pages/Search.tsx (2)

1312-1328: ⚡ Quick win

Consider computing the result key once per iteration.

The mobile view calls getSearchResultKey(result) twice (lines 1314 and 1316). While the function is inexpensive, computing it once improves clarity and avoids redundant work.

♻️ Refactor to compute key once
 <div className="sm:hidden space-y-2 max-h-150 overflow-auto">
   {filteredAndSortedResults.map((result) => {
+    const resultKey = getSearchResultKey(result)
     return (
       <SearchResultCard
-        key={getSearchResultKey(result)}
+        key={resultKey}
         result={result}
-        isSelected={selectedResultKey === getSearchResultKey(result)}
+        isSelected={selectedResultKey === resultKey}
         onSelect={() => handleToggleResultSelection(result)}
🤖 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 `@web/src/pages/Search.tsx` around lines 1312 - 1328, Compute the search result
key once per iteration instead of calling getSearchResultKey(result) twice:
inside the map over filteredAndSortedResults, call getSearchResultKey(result)
once into a local variable (e.g., resultKey) and then pass that variable as the
SearchResultCard key and use it to compute isSelected (compare resultKey ===
selectedResultKey) and any other places that used getSearchResultKey(result);
update usages in the JSX where SearchResultCard is rendered (key, isSelected,
and any handlers referencing the key) so the expensive or duplicate call is
eliminated.

1585-1694: ⚡ Quick win

Consider computing the result key once per iteration.

The desktop table row calls getSearchResultKey(result) twice (lines 1586 and 1589). Computing it once at the start of the map callback improves clarity.

♻️ Refactor to compute key once
 {filteredAndSortedResults.map((result) => {
-  const isSelected = selectedResultKey === getSearchResultKey(result)
+  const resultKey = getSearchResultKey(result)
+  const isSelected = selectedResultKey === resultKey
   return (
     <TableRow
-      key={getSearchResultKey(result)}
+      key={resultKey}
       className={cn(
🤖 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 `@web/src/pages/Search.tsx` around lines 1585 - 1694, Compute the search result
key once inside the filteredAndSortedResults.map callback by adding a local
const (e.g., const resultKey = getSearchResultKey(result)) at the top of the map
body and then use resultKey for the TableRow key prop and for the isSelected
comparison (selectedResultKey === resultKey); update any other references in
that callback that call getSearchResultKey(result) to use resultKey instead
(references: getSearchResultKey, selectedResultKey, TableRow key).
🤖 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 `@web/src/pages/Search.tsx`:
- Around line 1312-1328: Compute the search result key once per iteration
instead of calling getSearchResultKey(result) twice: inside the map over
filteredAndSortedResults, call getSearchResultKey(result) once into a local
variable (e.g., resultKey) and then pass that variable as the SearchResultCard
key and use it to compute isSelected (compare resultKey === selectedResultKey)
and any other places that used getSearchResultKey(result); update usages in the
JSX where SearchResultCard is rendered (key, isSelected, and any handlers
referencing the key) so the expensive or duplicate call is eliminated.
- Around line 1585-1694: Compute the search result key once inside the
filteredAndSortedResults.map callback by adding a local const (e.g., const
resultKey = getSearchResultKey(result)) at the top of the map body and then use
resultKey for the TableRow key prop and for the isSelected comparison
(selectedResultKey === resultKey); update any other references in that callback
that call getSearchResultKey(result) to use resultKey instead (references:
getSearchResultKey, selectedResultKey, TableRow key).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f32f0e51-bbfb-4910-a8bb-7395a19cf66a

📥 Commits

Reviewing files that changed from the base of the PR and between c843adf and f54d1fd.

📒 Files selected for processing (1)
  • web/src/pages/Search.tsx

@s0up4200 s0up4200 merged commit b621973 into autobrr:develop May 15, 2026
12 checks passed
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.

2 participants