Skip to content

Conversation

@NFToby
Copy link
Contributor

@NFToby NFToby commented Jan 5, 2026

What

  • Add type filters to search results
  • Replace the hard 20 result cap with infinite scroll

Why

  • Current search stops at 20 results with no way to see more besides a more specific query
  • Filters make it easier to find what you're looking for

Screenshots

Before:
before-screenshot

After:
after-screenshot

Summary by CodeRabbit

  • New Features
    • Added search result filtering with options for Chains, Protocols, Stablecoins, Metrics, Categories, Tools, and Tags
    • Implemented infinite scroll pagination for seamless browsing of search results
    • Auto-loading of additional results when scrolling to the end of the list

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 5, 2026

📝 Walkthrough

Walkthrough

The search functionality has been refactored to support paginated, filterable results across multiple data sources. A new SearchFiltersBar component enables filtering by predefined categories. New hooks—useInfiniteQuery, useFilteredDefaultResults, and useSearchUIState—orchestrate synchronized live search, default results, and recent results with intersection-observer-driven infinite scrolling for both mobile and desktop variants.

Changes

Cohort / File(s) Summary
Search Component Refactoring
src/components/Search/index.tsx
Introduced paginated search via useInfiniteQuery with offset-based paging. Added new hooks: useFilteredDefaultResults for filtered defaults, useSearchUIState for orchestrating multi-source data (live, defaults, recents) and pagination controls. Added SearchFiltersBar component with eight fixed filter options (All, Chains, Protocols, Stablecoins, Metrics, Categories, Tools, Tags). Updated useSearch signature to include filterType parameter. Implemented intersection-observer auto-load-more for mobile and desktop. Expanded API fetch logic to support structured filter-based queries. Refactored loading state display and result synchronization across sources.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as MobileSearch/<br/>DesktopSearch
    participant StateHook as useSearchUIState
    participant InfiniteQ as useInfiniteQuery
    participant FilteredQ as useFilteredDefaultResults
    participant Observer as Intersection<br/>Observer
    participant API

    User->>UI: Select filter type
    UI->>StateHook: Update filter selection
    StateHook->>InfiniteQ: Trigger with new filterType
    StateHook->>FilteredQ: Trigger with new filterType
    
    par Live & Default Results
        InfiniteQ->>API: Fetch live results (filter applied)
        API-->>InfiniteQ: Return paginated results
        FilteredQ->>API: Fetch default results (filter applied)
        API-->>FilteredQ: Return paginated results
    end
    
    InfiniteQ-->>StateHook: Data + pagination state
    FilteredQ-->>StateHook: Data + pagination state
    StateHook-->>UI: Render combined results + UI state
    UI->>User: Display filtered results + load-more sentinel
    
    Observer->>UI: Detect scroll near sentinel
    UI->>StateHook: User scrolled to end
    StateHook->>InfiniteQ: Invoke fetchNextPage()
    InfiniteQ->>API: Fetch next page (offset)
    API-->>InfiniteQ: Return next batch
    InfiniteQ-->>StateHook: Append new data
    StateHook-->>UI: Render additional results
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Whiskers twitching with glee,
We've paginated the search spree!
Filters bloom like clover so bright,
Infinite scrolls through the night,
Data flows smooth, a hoppy delight! 🌟

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ 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 'feat: improve search with filters and infinite scroll' directly and concisely summarizes the main changes: adding filter functionality and infinite scrolling to search.
✨ Finishing touches
  • 📝 Generate docstrings

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

Copy link

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

Caution

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

⚠️ Outside diff range comments (2)
src/components/Search/index.tsx (2)

44-56: Security: API token exposed in client-side code.

The Bearer token at line 48 is hardcoded and will be visible in browser dev tools and bundled source. Even for read-only public APIs, consider proxying requests through a Next.js API route to keep the token server-side.


406-406: Bug: onClick handler receives event, not previous state.

The onClick callback receives a MouseEvent, not the previous state value. Currently !prev evaluates to false (since MouseEvent is truthy), so clicking always closes the search instead of toggling.

🔎 Proposed fix
-<button onClick={(prev) => setOpen(!prev)} className="absolute top-0 bottom-0 left-2 my-auto opacity-50">
+<button onClick={() => setOpen((prev) => !prev)} className="absolute top-0 bottom-0 left-2 my-auto opacity-50">
🧹 Nitpick comments (3)
src/components/Search/index.tsx (3)

163-173: Consider adding type="button" for defensive coding.

While not currently an issue, adding type="button" prevents accidental form submission if this component is ever rendered within a form context.

🔎 Suggested fix
 <button
 	key={filter.value}
+	type="button"
 	onClick={() => setSearchFilter(filter.value)}

287-313: Minor inconsistency: sentinel element styling differs between branches.

Line 287 has className="h-1" but line 313 has no className. Consider making them consistent for predictable scroll behavior.

🔎 Suggested fix
-{hasNextPage && <div ref={loadMoreRef} />}
+{hasNextPage && <div ref={loadMoreRef} className="h-1" />}

458-484: Same inconsistency: sentinel elements lack height.

Both sentinel div elements (lines 458 and 484) have no explicit height, unlike MobileSearch line 287. The IntersectionObserver may not trigger reliably for zero-height elements depending on browser behavior.

🔎 Suggested fix
-{hasNextPage && <div ref={loadMoreRef} />}
+{hasNextPage && <div ref={loadMoreRef} className="h-1" />}

Apply to both lines 458 and 484.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f4a960f and e437bc0.

📒 Files selected for processing (1)
  • src/components/Search/index.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/Search/index.tsx (2)
src/components/Search/types.ts (1)
  • ISearchItem (3-16)
src/components/Loaders.tsx (1)
  • LoadingDots (1-9)
🔇 Additional comments (6)
src/components/Search/index.tsx (6)

1-14: LGTM!

Imports and constants are appropriate. The SEARCH_PAGE_SIZE constant is well-placed for reuse across pagination logic.


72-83: LGTM!

The SearchFilter type and FILTER_OPTIONS array are well-structured with clear value-to-label mappings.


87-151: LGTM!

The useSearchUIState hook effectively orchestrates multiple data sources (live search, default results, recents) with appropriate pagination controls. The conditional logic at lines 131-134 correctly switches between live search pagination and filtered default pagination.


203-223: LGTM!

The IntersectionObserver setup is correct with proper cleanup. The dependency array appropriately triggers re-creation when pagination state changes.


513-600: LGTM!

The SearchItem component, setRecentSearch helper, and useDefaultSearchList hook are well-implemented. The use of useSyncExternalStore for localStorage synchronization is the correct React 18+ pattern.


602-634: LGTM!

The useSearch and useFilteredDefaultResults hooks correctly implement infinite query patterns with proper page param calculation. The enabled conditions appropriately gate network requests.

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