Skip to content

Outcomes refactor#1671

Merged
simo6529 merged 7 commits intomainfrom
outcomes-refactor
Dec 19, 2025
Merged

Outcomes refactor#1671
simo6529 merged 7 commits intomainfrom
outcomes-refactor

Conversation

@simo6529
Copy link
Copy Markdown
Collaborator

@simo6529 simo6529 commented Dec 19, 2025

Summary by CodeRabbit

  • New Features

    • Infinite scrolling and paginated loading for wave outcomes with clear loading / "loading more..." indicators and "View more" behavior.
  • Refactor

    • Outcome components unified to use a distribution-driven model and accept wave context for consistent pagination; improved loading, empty and error states.
  • UI

    • Avatar images now resolve IPFS URLs for more reliable profile pictures.
  • Tests

    • Updated tests to cover distribution-driven rendering and new pagination props.

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

Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 19, 2025

Walkthrough

Adds paginated/infinite fetching for wave outcomes and per-outcome distributions, migrates outcome types from ApiWaveOutcomeOld to ApiWaveOutcome, wires distribution-driven rendering into outcome components, integrates IPFS avatar resolution, updates related helpers/types, query keys, and tests.

Changes

Cohort / File(s) Summary
New query hooks
\hooks/waves/useWaveOutcomesQuery.ts`, `hooks/waves/useWaveOutcomeDistributionQuery.ts``
Added two infinite-query React hooks to fetch paginated wave outcomes and per-outcome distribution data; expose flattened items, totalCount, pagination controls, errorMessage, and isEnabled flags.
Outcome components refactor
\components/waves/outcome/WaveOutcome.tsx`, `components/waves/outcome/WaveRepOutcome.tsx`, `components/waves/outcome/WaveNICOutcome.tsx`, `components/waves/outcome/WaveManualOutcome.tsx``
Migrated components to use ApiWaveOutcome, added a distribution: WaveOutcomeDistributionState prop or consumption via hook, and changed rendering to be distribution-driven with view-more / pagination wiring.
MyStream infinite scroll
\components/brain/my-stream/MyStreamWaveOutcome.tsx``
Wired useWaveOutcomesQuery + useInfiniteScroll, added scroll container & sentinel refs, explicit initial/loading/error/empty states, renders WaveOutcome with waveId, and exported component as default.
Avatar IPFS resolution
\components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx`, `components/waves/leaderboard/drops/header/WaveleaderboardDropRaters.tsx``
Replaced raw avatar URIs with resolveIpfsUrlSync(...); switched one avatar render to Next.js Image with explicit width/height and adjusted styling.
Types & helpers
\types/waves.types.ts`, `helpers/waves/create-wave.helpers.ts`, `helpers/waves/waves.helpers.ts``
Added WaveOutcomeDistributionState interface; updated create-wave helpers to return ApiCreateWaveOutcome[]; changed convertWaveToUpdateWave return type to ApiUpdateWaveRequest and removed outcomes from update payload.
React Query keys
\components/react-query-wrapper/ReactQueryWrapper.tsx``
Added WAVE_OUTCOMES and WAVE_OUTCOME_DISTRIBUTION to QueryKey enum and included them in invalidateAllWaves.
Tests updated
\tests/components/waves/outcome/WaveOutcome.test.tsx`, `tests/components/waves/outcome/WaveManualOutcome.test.tsx`, `tests/components/waves/outcome/WaveRepOutcome.test.tsx`, `tests/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx``
Updated/added tests to mock new distribution hooks/props, pass waveId/index where required, assert distribution-driven rendering and queryKey/enabled behavior for the new hooks.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant MyStream as MyStreamWaveOutcome
    participant OutcomesHook as useWaveOutcomesQuery
    participant InfiniteScroll as useInfiniteScroll
    participant API
    participant WaveOutcome
    participant DistributionHook as useWaveOutcomeDistributionQuery

    User->>MyStream: open wave page
    MyStream->>OutcomesHook: query outcomes (waveId, page=1)
    OutcomesHook->>API: GET /waves/{waveId}/outcomes?page=1
    API-->>OutcomesHook: outcomes page 1
    OutcomesHook-->>MyStream: return items
    MyStream->>MyStream: render items + sentinel
    MyStream->>InfiniteScroll: observe sentinel

    User->>MyStream: scrolls to sentinel
    InfiniteScroll->>OutcomesHook: trigger fetchNextPage()
    OutcomesHook->>API: GET /waves/{waveId}/outcomes?page=2
    API-->>OutcomesHook: outcomes page 2
    OutcomesHook-->>MyStream: append items

    MyStream->>WaveOutcome: render outcome (pass waveId, outcome.index)
    WaveOutcome->>DistributionHook: query distribution (waveId, outcomeIndex)
    DistributionHook->>API: GET /waves/{waveId}/outcomes/{index}/distribution?page=1
    API-->>DistributionHook: distribution page 1
    DistributionHook-->>WaveOutcome: distribution state (items, totalCount, fetchNextPage)
    WaveOutcome->>WaveOutcome: render subcomponent (e.g., WaveRepOutcome) with distribution
    WaveOutcome->>DistributionHook: (on "View more") fetchNextPage()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Check consistency of the distribution prop shape across all outcome components and tests.
  • Verify query key construction and that new QueryKey enum values match hook usage.
  • Inspect infinite-scroll sentinel placement and isFetchingNextPage/fetchNextPage handling to avoid duplicate requests.
  • Review updated helper typings and removal of outcomes from update payload for correctness.

Possibly related PRs

Suggested reviewers

  • ragnep
  • prxt6529
  • kmk142789

Poem

🐰 I hopped through pages, one then two,
Outcomes unfurled with a soft review.
IPFS crumbs led each avatar home,
Fetch, paginate, display — I bounded and roamed.
A little rabbit clap for every fetch and view.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
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.
Title check ❓ Inconclusive The title "Outcomes refactor" is vague and generic, using a non-descriptive term that doesn't convey the specific changes or main purpose of the pull request. Consider a more descriptive title that highlights the primary change, such as "Add paginated wave outcomes with infinite scroll and distribution state" or "Refactor wave outcomes to support pagination and infinite scrolling".
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch outcomes-refactor

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.

Signed-off-by: Simo <simo@6529.io>
Copy link
Copy Markdown

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

🧹 Nitpick comments (6)
helpers/waves/create-wave.helpers.ts (1)

208-209: LGTM: Approve outcomes correctly typed.

The return type and variable declaration properly use ApiCreateWaveOutcome. The constructed outcomes omit distribution arrays (appropriate for approve-type waves) and include all required fields.

Note: Lines 228 and 237 use empty description strings for automatic credit distribution outcomes, which is valid but may warrant descriptive text for consistency.

components/brain/my-stream/MyStreamWaveOutcome.tsx (1)

3-3: Prefer direct named import for FC.

Per coding guidelines, prefer direct named imports for React types instead of using React.FC.

🔎 Proposed fix
-import React, { useMemo, useRef } from "react";
+import { useMemo, useRef, type FC } from "react";

Then update line 15:

-const MyStreamWaveOutcome: React.FC<MyStreamWaveOutcomeProps> = ({ wave }) => {
+const MyStreamWaveOutcome: FC<MyStreamWaveOutcomeProps> = ({ wave }) => {
components/waves/outcome/WaveRepOutcome.tsx (1)

37-39: remainingCount may display misleading value.

When showAll is false, the "View more" button displays remainingCount which is totalCount - items.length. However, if items.length is 5 and DEFAULT_AMOUNTS_TO_SHOW is 3, clicking "View more" initially reveals 2 more local items, but the button shows the server-side remaining count. This could confuse users about how many items they'll see next.

Consider computing remainingCount based on current visibility state:

🔎 Proposed fix
-  const remainingCount = Math.max(totalCount - items.length, 0);
+  const hiddenLocalCount = Math.max(items.length - DEFAULT_AMOUNTS_TO_SHOW, 0);
+  const remainingFromServer = Math.max(totalCount - items.length, 0);
+  const remainingCount = showAll ? remainingFromServer : hiddenLocalCount + remainingFromServer;
components/waves/outcome/WaveOutcome.tsx (1)

23-29: Consider extracting WaveOutcomeDistributionState to a shared types file.

This interface is defined here and the same shape is used in WaveRepOutcome.tsx (and likely WaveNICOutcome.tsx and WaveManualOutcome.tsx). Extracting it to a shared types file would reduce duplication and ensure type consistency.

🔎 Proposed approach

Create a shared type in a types file (e.g., types/waves.types.ts):

import type { ApiWaveOutcomeDistributionItem } from "@/generated/models/ApiWaveOutcomeDistributionItem";

export interface WaveOutcomeDistributionState {
  readonly items: ApiWaveOutcomeDistributionItem[];
  readonly totalCount: number;
  readonly hasNextPage: boolean;
  readonly isFetchingNextPage: boolean;
  readonly fetchNextPage: () => void;
}

Then import and use it in both WaveOutcome.tsx and the outcome components.

components/waves/outcome/WaveNICOutcome.tsx (1)

110-123: Consider using item.index for more stable keys.

The current key wave-nic-outcome-${amount}-${i} uses the array index i, which changes if items are reordered or inserted. Since ApiWaveOutcomeDistributionItem has an index field, using visibleItems with item.index would provide a more stable key identity.

🔎 Suggested change
-              {amounts.map((amount, i) => (
+              {visibleItems.map((item, i) => (
                 <div
-                  key={`wave-nic-outcome-${amount}-${i}`}
+                  key={`wave-nic-outcome-${item.index}`}
                   className="tw-px-4 tw-py-3 tw-bg-gradient-to-r hover:tw-from-[#A4C2DB]/5 hover:tw-to-transparent tw-transition-colors tw-duration-300">
                   <div className="tw-flex tw-items-center tw-gap-4">
                     <span className="tw-flex tw-items-center tw-justify-center tw-size-8 tw-rounded-lg tw-bg-gradient-to-br tw-from-[#A4C2DB]/10 tw-to-[#A4C2DB]/5 tw-text-[#A4C2DB] tw-text-sm tw-font-semibold">
                       {i + 1}
                     </span>
                     <span className="tw-whitespace-nowrap tw-text-[#A4C2DB] tw-text-base tw-font-medium">
-                      {formatNumberWithCommas(amount)} NIC
+                      {formatNumberWithCommas(item.amount ?? 0)} NIC
                     </span>
                   </div>
                 </div>
               ))}
components/waves/outcome/WaveManualOutcome.tsx (1)

51-53: Add type="button" to prevent unintended form submission.

The toggle button is missing type="button", unlike WaveNICOutcome. This could cause unintended form submission if the component is used within a form context.

🔎 Suggested fix
       <button
+        type="button"
         onClick={() => setIsOpen(!isOpen)}
         className="tw-w-full tw-border-0 tw-px-4 tw-py-3 tw-bg-iron-950">
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 089ce82 and f47acbb.

⛔ Files ignored due to path filters (1)
  • generated/models/ApiWave.ts is excluded by !**/generated/**
📒 Files selected for processing (13)
  • components/brain/my-stream/MyStreamWaveOutcome.tsx (1 hunks)
  • components/react-query-wrapper/ReactQueryWrapper.tsx (1 hunks)
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx (2 hunks)
  • components/waves/leaderboard/drops/header/WaveleaderboardDropRaters.tsx (2 hunks)
  • components/waves/outcome/WaveManualOutcome.tsx (2 hunks)
  • components/waves/outcome/WaveNICOutcome.tsx (2 hunks)
  • components/waves/outcome/WaveOutcome.tsx (3 hunks)
  • components/waves/outcome/WaveRepOutcome.tsx (2 hunks)
  • helpers/waves/create-wave.helpers.ts (4 hunks)
  • helpers/waves/waves.helpers.ts (2 hunks)
  • hooks/waves/useWaveOutcomeDistributionQuery.ts (1 hunks)
  • hooks/waves/useWaveOutcomesQuery.ts (1 hunks)
  • openapi.yaml (0 hunks)
💤 Files with no reviewable changes (1)
  • openapi.yaml
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
  • components/waves/leaderboard/drops/header/WaveleaderboardDropRaters.tsx
  • hooks/waves/useWaveOutcomesQuery.ts
  • components/waves/outcome/WaveRepOutcome.tsx
  • helpers/waves/create-wave.helpers.ts
  • helpers/waves/waves.helpers.ts
  • hooks/waves/useWaveOutcomeDistributionQuery.ts
  • components/waves/outcome/WaveOutcome.tsx
  • components/waves/outcome/WaveNICOutcome.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/brain/my-stream/MyStreamWaveOutcome.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
  • components/waves/leaderboard/drops/header/WaveleaderboardDropRaters.tsx
  • components/waves/outcome/WaveRepOutcome.tsx
  • components/waves/outcome/WaveOutcome.tsx
  • components/waves/outcome/WaveNICOutcome.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/brain/my-stream/MyStreamWaveOutcome.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Files:

  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
  • components/waves/leaderboard/drops/header/WaveleaderboardDropRaters.tsx
  • hooks/waves/useWaveOutcomesQuery.ts
  • components/waves/outcome/WaveRepOutcome.tsx
  • helpers/waves/create-wave.helpers.ts
  • helpers/waves/waves.helpers.ts
  • hooks/waves/useWaveOutcomeDistributionQuery.ts
  • components/waves/outcome/WaveOutcome.tsx
  • components/waves/outcome/WaveNICOutcome.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/brain/my-stream/MyStreamWaveOutcome.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

**/*.{ts,tsx}: Must pass tsc --noEmit for TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
Use useEffectEvent for non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs: <Link> for internal links, next/image for images, adopt Next's ESLint rules
Use 'use cache' directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace <img> elements with <Image /> from next/image
Use <Link href="/path"> for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with 'use server' directive

Files:

  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
  • components/waves/leaderboard/drops/header/WaveleaderboardDropRaters.tsx
  • hooks/waves/useWaveOutcomesQuery.ts
  • components/waves/outcome/WaveRepOutcome.tsx
  • helpers/waves/create-wave.helpers.ts
  • helpers/waves/waves.helpers.ts
  • hooks/waves/useWaveOutcomeDistributionQuery.ts
  • components/waves/outcome/WaveOutcome.tsx
  • components/waves/outcome/WaveNICOutcome.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/brain/my-stream/MyStreamWaveOutcome.tsx
🧠 Learnings (6)
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use react-query for data fetching

Applied to files:

  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • hooks/waves/useWaveOutcomesQuery.ts
  • hooks/waves/useWaveOutcomeDistributionQuery.ts
  • components/brain/my-stream/MyStreamWaveOutcome.tsx
📚 Learning: 2025-11-25T08:37:44.688Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: app/api/AGENTS.md:0-0
Timestamp: 2025-11-25T08:37:44.688Z
Learning: Applies to app/api/**/*.{ts,tsx} : Use TypeScript types for request parameters and responses; avoid `any` unless a 3rd-party payload truly has no shape guarantees.

Applied to files:

  • helpers/waves/waves.helpers.ts
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Prefer Server Components over Client Components; use Server Functions/Server Actions (`'use server'`) for mutations

Applied to files:

  • components/waves/outcome/WaveOutcome.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/brain/my-stream/MyStreamWaveOutcome.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript and React functional components with hooks

Applied to files:

  • components/brain/my-stream/MyStreamWaveOutcome.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: TypeScript + React functional components with hooks; follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Applied to files:

  • components/brain/my-stream/MyStreamWaveOutcome.tsx
🧬 Code graph analysis (10)
components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx (1)
components/ipfs/IPFSContext.tsx (1)
  • resolveIpfsUrlSync (77-89)
components/waves/leaderboard/drops/header/WaveleaderboardDropRaters.tsx (2)
components/ipfs/IPFSContext.tsx (1)
  • resolveIpfsUrlSync (77-89)
helpers/image.helpers.ts (1)
  • getScaledImageUri (17-45)
hooks/waves/useWaveOutcomesQuery.ts (2)
generated/models/ApiWaveOutcomesPage.ts (1)
  • ApiWaveOutcomesPage (16-58)
components/react-query-wrapper/utils/query-utils.ts (1)
  • getDefaultQueryRetry (21-34)
components/waves/outcome/WaveRepOutcome.tsx (2)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
generated/models/ApiWaveOutcomeDistributionItem.ts (1)
  • ApiWaveOutcomeDistributionItem (15-50)
helpers/waves/create-wave.helpers.ts (1)
generated/models/ApiCreateWaveOutcome.ts (1)
  • ApiCreateWaveOutcome (19-82)
helpers/waves/waves.helpers.ts (1)
generated/models/ApiUpdateWaveRequest.ts (1)
  • ApiUpdateWaveRequest (20-89)
hooks/waves/useWaveOutcomeDistributionQuery.ts (2)
generated/models/ApiWaveOutcomeDistributionItemsPage.ts (1)
  • ApiWaveOutcomeDistributionItemsPage (16-58)
components/react-query-wrapper/utils/query-utils.ts (1)
  • getDefaultQueryRetry (21-34)
components/waves/outcome/WaveNICOutcome.tsx (2)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
generated/models/ApiWaveOutcomeDistributionItem.ts (1)
  • ApiWaveOutcomeDistributionItem (15-50)
components/waves/outcome/WaveManualOutcome.tsx (2)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
generated/models/ApiWaveOutcomeDistributionItem.ts (1)
  • ApiWaveOutcomeDistributionItem (15-50)
components/brain/my-stream/MyStreamWaveOutcome.tsx (6)
generated/models/ApiWave.ts (1)
  • ApiWave (26-171)
__mocks__/react-use.js (1)
  • React (2-2)
components/brain/my-stream/layout/LayoutContext.tsx (1)
  • useLayout (522-522)
hooks/waves/useWaveOutcomesQuery.ts (1)
  • useWaveOutcomesQuery (41-105)
components/common/SpinnerLoader.tsx (1)
  • SpinnerLoader (8-57)
components/waves/outcome/WaveOutcome.tsx (1)
  • WaveOutcome (31-75)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (26)
helpers/waves/waves.helpers.ts (1)

3-3: LGTM! Excellent type safety improvement.

Restoring the ApiUpdateWaveRequest import and updating the return type from any to ApiUpdateWaveRequest significantly improves type safety. The function body correctly constructs all required fields (name, picture, voting, visibility, participation, chat, wave) that match the ApiUpdateWaveRequest schema.

Based on learnings, this change properly avoids using any for API request types, ensuring compile-time type checking and preventing potential runtime errors.

Also applies to: 26-26

helpers/waves/create-wave.helpers.ts (3)

18-18: LGTM: Type migration to new outcome model.

The import correctly transitions from ApiWaveOutcomeOld to ApiCreateWaveOutcome, aligning with the refactoring objectives.


151-152: LGTM: Rank outcomes correctly typed.

The return type and variable declaration properly reflect the new ApiCreateWaveOutcome structure. All constructed outcome objects include required fields and appropriate optional fields for MANUAL, REP, and NIC outcome types.


250-250: LGTM: Outcome delegation correctly typed.

The return type correctly reflects the new ApiCreateWaveOutcome[] structure and delegates appropriately to wave-type-specific helpers.

components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx (1)

12-12: LGTM! Consistent IPFS URL resolution.

The IPFS URL resolution is correctly implemented and aligns with the pattern used in other leaderboard components like WaveleaderboardDropRaters.tsx.

Also applies to: 34-34

components/waves/leaderboard/drops/header/WaveleaderboardDropRaters.tsx (1)

14-14: LGTM! Correct IPFS URL resolution with Next.js Image.

The implementation correctly resolves IPFS URLs for voter profile pictures and properly uses the Next.js <Image> component. The order of operations (scaling then IPFS resolution) works correctly for both IPFS protocol URLs and standard HTTP URLs.

Also applies to: 80-83

components/brain/my-stream/MyStreamWaveOutcome.tsx (4)

21-29: LGTM!

Good integration with the new useWaveOutcomesQuery hook. The destructured properties cover all necessary states for proper UI handling.


36-43: LGTM!

Proper implementation of infinite scroll with a reasonable 200px root margin for preloading content before the user reaches the end.


64-78: LGTM!

The key pattern ${outcome.index ?? index}-${outcome.type} correctly uses the nullish coalescing operator, which only falls back to the array index when outcome.index is null or undefined (not when it's 0). The sentinel element and loading spinner placement are appropriate for infinite scroll.


59-63: LGTM!

Good defensive condition that prevents showing the empty state during background refetches by checking !isFetching.

components/waves/outcome/WaveRepOutcome.tsx (2)

41-48: LGTM!

The onViewMore handler correctly handles both revealing locally cached items and fetching additional pages. The guard !isFetchingNextPage prevents duplicate fetch requests.


120-133: LGTM!

The rendering correctly displays 1-based position numbers. The key wave-rep-outcome-${amount}-${i} is unique since the index i differentiates items with identical amounts.

hooks/waves/useWaveOutcomesQuery.ts (4)

1-14: LGTM!

Clean imports and proper use of "use client" directive. The type imports for InfiniteData and UseInfiniteQueryResult are correctly used with the type keyword.


41-65: LGTM!

Excellent input normalization with proper handling of edge cases (trimming whitespace, validating positive integers, defaulting sort direction). The query key correctly includes all parameters that affect the response.


67-89: LGTM!

Well-structured infinite query configuration. The getNextPageParam correctly uses the next boolean and page number from ApiWaveOutcomesPage. Using keepPreviousData provides smooth UX during pagination. Based on learnings, react-query is the correct choice for data fetching.


91-104: LGTM!

Clean return value construction with proper memoization of the flattened outcomes array. The error message extraction safely handles non-Error types by returning undefined.

components/waves/outcome/WaveOutcome.tsx (2)

31-48: LGTM!

Good separation of concerns - the component fetches distribution data and passes it to specialized outcome components. The Boolean(hasNextPage) on line 45 is defensive but harmless since hasNextPage from react-query is already a boolean.


62-74: LGTM!

The Record-based component selection pattern is readable and maintainable. While it eagerly creates all three JSX elements, React's reconciliation efficiently handles this without significant overhead.

hooks/waves/useWaveOutcomeDistributionQuery.ts (3)

1-42: Well-structured hook with proper type exports and defaults.

The type definitions, constants, and exports are well-organized. Using Readonly<> wrapper on the params parameter at line 50 aligns with the coding guidelines for readonly props.


64-102: Query configuration looks solid.

The infinite query setup correctly handles pagination with getNextPageParam, uses keepPreviousData for smooth UX during page transitions, and integrates the default retry logic.


104-119: LGTM!

The result derivation correctly flattens paginated data and extracts the total count from the first page. The error message extraction is properly type-guarded.

components/waves/outcome/WaveNICOutcome.tsx (2)

11-20: Props interface follows coding guidelines with readonly modifiers.

The distribution prop shape clearly defines the contract for paginated data with fetch controls, which aligns well with the refactored architecture.


30-48: Logic for pagination and visibility is correct.

The shouldShowMore condition properly handles both scenarios: when there are more pages to fetch from the server, and when there are more already-loaded items to reveal locally. The onViewMore handler correctly orchestrates both state changes.

components/waves/outcome/WaveManualOutcome.tsx (3)

11-47: Consistent implementation with WaveNICOutcome.

The props interface and pagination logic are consistent with WaveNICOutcome, which aids maintainability. The use of readonly modifiers follows coding guidelines.


118-120: Verify the conditional logic for displaying description.

The condition item.amount ? item.description : "" will hide the description when amount is 0 or null/undefined. If the intent is to always show the description when present, this should be item.description ?? "". If the intent is to show description only for non-zero amounts, consider using item.amount !== 0 && item.amount != null ? item.description : "" to be explicit.


110-123: Good use of item.index in the key.

The key includes item.index which provides stability. The additional item.amount and item.description are redundant if item.index is unique, but this doesn't cause any issues.

Comment thread components/react-query-wrapper/ReactQueryWrapper.tsx
Comment thread components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx Outdated
Copy link
Copy Markdown

@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

Caution

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

⚠️ Outside diff range comments (1)
components/waves/outcome/WaveManualOutcome.tsx (1)

52-52: Add missing type attribute for consistency.

The button should have type="button" to be consistent with the same button in WaveNICOutcome.tsx (line 52) and WaveRepOutcome.tsx (line 52).

Proposed fix
       <button
+        type="button"
         onClick={() => setIsOpen(!isOpen)}
         className="tw-w-full tw-border-0 tw-px-4 tw-py-3 tw-bg-iron-950">
🧹 Nitpick comments (2)
components/waves/outcome/WaveNICOutcome.tsx (1)

24-48: Consider extracting shared logic into a custom hook.

This component shares nearly identical logic with WaveRepOutcome and WaveManualOutcome (distribution destructuring, visibleItems calculation, remainingCount, shouldShowMore, and onViewMore). Consider extracting this into a shared custom hook like useOutcomeDistribution to reduce duplication and improve maintainability.

Example structure for shared hook
function useOutcomeDistribution(
  distribution: {
    items: ApiWaveOutcomeDistributionItem[];
    totalCount: number;
    hasNextPage: boolean;
    isFetchingNextPage: boolean;
    fetchNextPage: () => void;
  },
  defaultVisible: number = 3
) {
  const [showAll, setShowAll] = useState(false);
  const { items, totalCount, hasNextPage, isFetchingNextPage, fetchNextPage } = distribution;
  
  const visibleItems = showAll ? items : items.slice(0, defaultVisible);
  const remainingCount = Math.max(totalCount - visibleItems.length, 0);
  const shouldShowMore = hasNextPage || (!showAll && items.length > defaultVisible);
  
  const onViewMore = () => {
    if (!showAll) setShowAll(true);
    if (hasNextPage && !isFetchingNextPage) fetchNextPage();
  };
  
  return { visibleItems, remainingCount, shouldShowMore, onViewMore, totalCount };
}
components/waves/outcome/WaveManualOutcome.tsx (1)

112-112: Simplify key to use only unique identifier.

The key includes amount and description which are not unique identifiers and can cause rendering issues. Use only item.index which is the unique identifier.

Proposed fix
                 <div
-                  key={`wave-manual-outcome-${item.amount ?? "na"}-${item.description ?? "na"}-${item.index}`}
+                  key={`wave-manual-outcome-${item.index}`}
                   className="tw-px-4 tw-py-3 tw-bg-gradient-to-r hover:tw-from-amber-500/5 hover:tw-to-transparent tw-transition-colors tw-duration-300">
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f47acbb and e9cb510.

⛔ Files ignored due to path filters (31)
  • generated/models/AirdropAddressResponse.ts is excluded by !**/generated/**
  • generated/models/AirdropAddressResponseTdhWallet.ts is excluded by !**/generated/**
  • generated/models/AllowlistNormalizedEntry.ts is excluded by !**/generated/**
  • generated/models/DistributionNormalized.ts is excluded by !**/generated/**
  • generated/models/DistributionNormalizedPage.ts is excluded by !**/generated/**
  • generated/models/DistributionPhasesPage.ts is excluded by !**/generated/**
  • generated/models/DistributionPhoto.ts is excluded by !**/generated/**
  • generated/models/DistributionPhotosPage.ts is excluded by !**/generated/**
  • generated/models/NFTFinalSubscription.ts is excluded by !**/generated/**
  • generated/models/NFTFinalSubscriptionUpload.ts is excluded by !**/generated/**
  • generated/models/NFTFinalSubscriptionUploadPage.ts is excluded by !**/generated/**
  • generated/models/NFTSubscription.ts is excluded by !**/generated/**
  • generated/models/ObjectSerializer.ts is excluded by !**/generated/**
  • generated/models/RedeemedSubscription.ts is excluded by !**/generated/**
  • generated/models/RedeemedSubscriptionCounts.ts is excluded by !**/generated/**
  • generated/models/RedeemedSubscriptionCountsPage.ts is excluded by !**/generated/**
  • generated/models/RedeemedSubscriptionPage.ts is excluded by !**/generated/**
  • generated/models/SubscribeAllEditionsResponse.ts is excluded by !**/generated/**
  • generated/models/SubscriptionCountResponse.ts is excluded by !**/generated/**
  • generated/models/SubscriptionCounts.ts is excluded by !**/generated/**
  • generated/models/SubscriptionDetails.ts is excluded by !**/generated/**
  • generated/models/SubscriptionLog.ts is excluded by !**/generated/**
  • generated/models/SubscriptionLogPage.ts is excluded by !**/generated/**
  • generated/models/SubscriptionModeResponse.ts is excluded by !**/generated/**
  • generated/models/SubscriptionResponse.ts is excluded by !**/generated/**
  • generated/models/SubscriptionTopUp.ts is excluded by !**/generated/**
  • generated/models/SubscriptionTopUpPage.ts is excluded by !**/generated/**
  • generated/models/UpdateSubscribeAllEditionsRequest.ts is excluded by !**/generated/**
  • generated/models/UpdateSubscriptionCountRequest.ts is excluded by !**/generated/**
  • generated/models/UpdateSubscriptionModeRequest.ts is excluded by !**/generated/**
  • generated/models/UpdateSubscriptionRequest.ts is excluded by !**/generated/**
📒 Files selected for processing (3)
  • components/waves/outcome/WaveManualOutcome.tsx (2 hunks)
  • components/waves/outcome/WaveNICOutcome.tsx (2 hunks)
  • components/waves/outcome/WaveRepOutcome.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/waves/outcome/WaveNICOutcome.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/waves/outcome/WaveRepOutcome.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/waves/outcome/WaveNICOutcome.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/waves/outcome/WaveRepOutcome.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Files:

  • components/waves/outcome/WaveNICOutcome.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/waves/outcome/WaveRepOutcome.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

**/*.{ts,tsx}: Must pass tsc --noEmit for TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
Use useEffectEvent for non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs: <Link> for internal links, next/image for images, adopt Next's ESLint rules
Use 'use cache' directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace <img> elements with <Image /> from next/image
Use <Link href="/path"> for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with 'use server' directive

Files:

  • components/waves/outcome/WaveNICOutcome.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/waves/outcome/WaveRepOutcome.tsx
🧬 Code graph analysis (2)
components/waves/outcome/WaveManualOutcome.tsx (2)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
generated/models/ApiWaveOutcomeDistributionItem.ts (1)
  • ApiWaveOutcomeDistributionItem (15-50)
components/waves/outcome/WaveRepOutcome.tsx (2)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
generated/models/ApiWaveOutcomeDistributionItem.ts (1)
  • ApiWaveOutcomeDistributionItem (15-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
components/waves/outcome/WaveNICOutcome.tsx (1)

3-48: Approve refactoring to distribution-based pagination.

The component correctly accepts and uses the distribution prop for pagination. The logic for visibleItems, remainingCount, and shouldShowMore is sound, and onViewMore properly handles both expanding the visible items and fetching the next page.

components/waves/outcome/WaveRepOutcome.tsx (1)

3-48: LGTM - consistent implementation.

The distribution-based pagination logic is correctly implemented and consistent with WaveNICOutcome. The component properly handles showing visible items, calculating remaining count, and fetching more pages.

components/waves/outcome/WaveManualOutcome.tsx (1)

24-47: LGTM - distribution integration is correct.

The component correctly integrates the distribution prop and implements pagination logic consistent with the other outcome components.

Comment thread components/waves/outcome/WaveManualOutcome.tsx Outdated
Signed-off-by: Simo <simo@6529.io>
Copy link
Copy Markdown

@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)
components/waves/outcome/WaveRepOutcome.tsx (1)

121-134: Use item.index for stable keys instead of amount.

The key wave-rep-outcome-${amount}-${i} can produce duplicates if two items have the same amount value, causing React reconciliation issues. Use item.index from the distribution item for guaranteed uniqueness, consistent with WaveNICOutcome (Line 105).

🔎 Proposed fix
-              {amounts.map((amount, i) => (
+              {visibleItems.map((item, i) => (
                 <div
-                  key={`wave-rep-outcome-${amount}-${i}`}
+                  key={`wave-rep-outcome-${item.index}`}
                   className="tw-px-4 tw-py-3 tw-bg-gradient-to-r hover:tw-from-[#C3B5D9]/5 hover:tw-to-transparent tw-transition-colors tw-duration-300">
                   <div className="tw-flex tw-items-center tw-gap-4">
                     <span className="tw-flex tw-items-center tw-justify-center tw-size-8 tw-rounded-lg tw-bg-gradient-to-br tw-from-[#C3B5D9]/10 tw-to-[#C3B5D9]/5 tw-text-[#C3B5D9] tw-text-sm tw-font-semibold">
                       {i + 1}
                     </span>
                     <span className="tw-whitespace-nowrap tw-text-[#C3B5D9] tw-text-base tw-font-medium">
-                      {formatNumberWithCommas(amount)} Rep
+                      {formatNumberWithCommas(item.amount ?? 0)} Rep
                     </span>
                   </div>
                 </div>
               ))}
__tests__/components/waves/outcome/WaveOutcome.test.tsx (1)

17-49: Use renderWithQueryClient() to wrap component renders.

The tests use standard render() without the renderWithQueryClient() test utility that's already available in __tests__/utils/reactQuery.tsx. Since WaveOutcome calls useWaveOutcomeDistributionQuery, which requires React Query's QueryClientProvider context, replace render() with renderWithQueryClient() in all test cases.

🧹 Nitpick comments (5)
components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx (1)

33-40: Implementation correctly addresses the previous ESLint violation.

The <img> element has been successfully replaced with Next.js <Image> component, resolving the @next/next/no-img-element ESLint rule violation flagged in the previous review. The implementation with fill and sizes="44px" works correctly.

Since the image size is fixed at 44×44px, using explicit width and height props would be simpler and eliminate the need for tw-relative on the parent:

🔎 Optional simplification using fixed dimensions
-                <div className="tw-h-full tw-text-center tw-flex tw-items-center tw-justify-center tw-rounded-lg tw-overflow-hidden tw-relative">
+                <div className="tw-h-full tw-text-center tw-flex tw-items-center tw-justify-center tw-rounded-lg tw-overflow-hidden">
                   <Image
                     src={resolveIpfsUrlSync(drop.author.pfp)}
                     alt="Profile picture"
-                    fill
-                    sizes="44px"
-                    className="tw-bg-transparent tw-object-contain"
+                    width={44}
+                    height={44}
+                    className="tw-rounded-lg tw-object-contain"
                   />
                 </div>

As per coding guidelines, Image components from next/image should be used for all images.

components/brain/my-stream/MyStreamWaveOutcome.tsx (1)

3-3: Unused React import.

The React namespace import is unused since the modern JSX transform handles JSX without requiring it. Only FC, useMemo, and useRef are needed.

🔎 Proposed fix
-import React, { FC, useMemo, useRef } from "react";
+import { FC, useMemo, useRef } from "react";
components/waves/outcome/WaveOutcome.tsx (1)

34-40: Consider memoizing distributionState to stabilize object reference.

The distributionState object is recreated on every render, which could cause unnecessary re-renders in child components if they perform reference equality checks or use React.memo. Wrapping it with useMemo would stabilize the reference when values haven't changed.

🔎 Proposed fix
+import { FC, type JSX, useMemo } from "react";
-import { FC, type JSX } from "react";
...
-  const distributionState: WaveOutcomeDistributionState = {
-    items,
-    totalCount,
-    hasNextPage: Boolean(hasNextPage),
-    isFetchingNextPage,
-    fetchNextPage,
-  };
+  const distributionState: WaveOutcomeDistributionState = useMemo(
+    () => ({
+      items,
+      totalCount,
+      hasNextPage: Boolean(hasNextPage),
+      isFetchingNextPage,
+      fetchNextPage,
+    }),
+    [items, totalCount, hasNextPage, isFetchingNextPage, fetchNextPage]
+  );
components/waves/outcome/WaveManualOutcome.tsx (2)

34-41: Consider adding loading feedback when fetching next page.

The isFetchingNextPage state is destructured but not used to provide visual feedback to the user. Consider disabling the button or showing a loading indicator while fetching.

Suggested improvement
               {shouldShowMore && (
                 <button
                   className="tw-border-0 tw-w-full tw-px-4 tw-py-3 tw-text-left tw-bg-iron-900 tw-text-amber-300/80 tw-text-sm hover:tw-text-amber-300 tw-transition-all tw-duration-300"
-                  onClick={onViewMore}>
-                  <span>View more</span>
+                  onClick={onViewMore}
+                  disabled={isFetchingNextPage}>
+                  <span>{isFetchingNextPage ? "Loading..." : "View more"}</span>
                   <span className="tw-ml-1 tw-text-iron-400">•</span>
                   <span className="tw-ml-1 tw-text-iron-400">
                     {remainingCount} more
                   </span>
                 </button>
               )}

Also applies to: 120-130


105-108: Consider simplifying the key.

The key construction is complex. If item.index is unique within the outcome's distribution (as suggested by the API model), it would be cleaner to use it directly.

Suggested simplification
               {visibleItems.map((item, i) => (
                 <div
-                  key={`wave-manual-outcome-${item.amount ?? "na"}-${item.description ?? "na"}-${item.index}`}
+                  key={`wave-manual-outcome-${item.index}`}
                   className="tw-px-4 tw-py-3 tw-bg-gradient-to-r hover:tw-from-amber-500/5 hover:tw-to-transparent tw-transition-colors tw-duration-300">
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9cb510 and b38e16e.

📒 Files selected for processing (9)
  • __tests__/components/waves/outcome/WaveOutcome.test.tsx (1 hunks)
  • components/brain/my-stream/MyStreamWaveOutcome.tsx (1 hunks)
  • components/react-query-wrapper/ReactQueryWrapper.tsx (2 hunks)
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx (2 hunks)
  • components/waves/outcome/WaveManualOutcome.tsx (2 hunks)
  • components/waves/outcome/WaveNICOutcome.tsx (2 hunks)
  • components/waves/outcome/WaveOutcome.tsx (3 hunks)
  • components/waves/outcome/WaveRepOutcome.tsx (2 hunks)
  • types/waves.types.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/react-query-wrapper/ReactQueryWrapper.tsx
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
  • components/waves/outcome/WaveRepOutcome.tsx
  • types/waves.types.ts
  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/outcome/WaveNICOutcome.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
  • components/waves/outcome/WaveOutcome.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
  • components/waves/outcome/WaveRepOutcome.tsx
  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/outcome/WaveNICOutcome.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
  • components/waves/outcome/WaveOutcome.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
  • components/waves/outcome/WaveRepOutcome.tsx
  • types/waves.types.ts
  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/outcome/WaveNICOutcome.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
  • components/waves/outcome/WaveOutcome.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

**/*.{ts,tsx}: Must pass tsc --noEmit for TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
Use useEffectEvent for non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs: <Link> for internal links, next/image for images, adopt Next's ESLint rules
Use 'use cache' directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace <img> elements with <Image /> from next/image
Use <Link href="/path"> for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with 'use server' directive

Files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
  • components/waves/outcome/WaveRepOutcome.tsx
  • types/waves.types.ts
  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/outcome/WaveNICOutcome.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
  • components/waves/outcome/WaveOutcome.tsx
**/@(__tests__|*.test).{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Tests should live in __tests__/ or ComponentName.test.tsx; mock external dependencies and APIs in tests

Files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Place tests in __tests__/ directory or as ComponentName.test.tsx alongside components

Files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Mock external dependencies and APIs in tests

Files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
__tests__/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use Jest + ts-jest for TypeScript testing

Files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Functions must have ≤ 15 cognitive complexity; extract deep ternaries (>3 levels) and break down complex logic

Files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
__tests__/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (tests/AGENTS.md)

__tests__/**/*.{ts,tsx,js}: Prefer for...of loops over forEach as it allows break/continue and works with async/await
Use array.at(-1) and array.at(-2) instead of index-based array access for negative indexing
Use String.prototype.replaceAll() instead of replace() for global string replacements
Use globalThis.fetch() instead of direct fetch() calls
Organize imports with one import per module in order: external → internal → types, with no duplicates
Use element.remove() instead of parent.removeChild(element) for DOM manipulation
Catch errors only when meaningful; no empty catch blocks; log errors with context
Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (?.)

Files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
__tests__/**/{components,contexts,hooks}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use semantic HTML elements (<label>, <output>) over ARIA attributes when possible; every form control must have a label

Files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
🧠 Learnings (17)
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx,js} : Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (`?.`)

Applied to files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/@(__tests__|*.test).{ts,tsx} : Tests should live in `__tests__/` or `ComponentName.test.tsx`; mock external dependencies and APIs in tests

Applied to files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/components/**/*.test.{ts,tsx} : Use testing-library/react + user-event for React component tests

Applied to files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/{components,contexts,hooks}/**/*.{ts,tsx} : Use semantic HTML elements (`<label>`, `<output>`) over ARIA attributes when possible; every form control must have a label

Applied to files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/*.test.{ts,tsx,js} : Test high-risk areas including happy path workflows, invalid input errors, edge cases/boundaries, component & API interactions, and performance/security when relevant

Applied to files:

  • __tests__/components/waves/outcome/WaveOutcome.test.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript and React functional components with hooks

Applied to files:

  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/outcome/WaveOutcome.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: TypeScript + React functional components with hooks; follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Applied to files:

  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use react-query for data fetching

Applied to files:

  • components/brain/my-stream/MyStreamWaveOutcome.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Replace `<img>` elements with `<Image />` from `next/image`

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Replace `<img>` elements with `<Image />` from `next/image` to satisfy `next/next/no-img-element` ESLint rule

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Use framework APIs: `<Link>` for internal links, `next/image` for images, adopt Next's ESLint rules

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use framework APIs: internal links should use `<Link>`, images should use `next/image`, and adopt Next's ESLint rules (Core Web Vitals)

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `<Link href="/path">` from Next.js for internal navigation instead of plain HTML links to satisfy `next/next/no-html-link-for-pages` ESLint rule

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Prefer Server Components over Client Components; use Server Functions/Server Actions (`'use server'`) for mutations

Applied to files:

  • components/waves/outcome/WaveOutcome.tsx
🧬 Code graph analysis (5)
components/waves/outcome/WaveRepOutcome.tsx (2)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
types/waves.types.ts (1)
  • WaveOutcomeDistributionState (120-126)
types/waves.types.ts (1)
generated/models/ApiWaveOutcomeDistributionItem.ts (1)
  • ApiWaveOutcomeDistributionItem (15-50)
components/waves/outcome/WaveNICOutcome.tsx (3)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
types/waves.types.ts (1)
  • WaveOutcomeDistributionState (120-126)
helpers/Helpers.ts (1)
  • formatNumberWithCommas (131-148)
components/waves/outcome/WaveManualOutcome.tsx (2)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
types/waves.types.ts (1)
  • WaveOutcomeDistributionState (120-126)
components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx (1)
components/ipfs/IPFSContext.tsx (1)
  • resolveIpfsUrlSync (77-89)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (11)
components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx (1)

6-6: LGTM: Imports correctly added.

The Image component and resolveIpfsUrlSync imports are properly added to support the Next.js Image optimization and IPFS URL resolution.

Also applies to: 13-13

components/brain/my-stream/MyStreamWaveOutcome.tsx (1)

36-78: Well-structured infinite scroll implementation.

The pagination logic correctly separates initial loading from fetching next pages, and the sentinel placement within the outcomes container enables proper intersection-based loading. The empty state handling with !isFetching prevents flickering during data fetches.

types/waves.types.ts (1)

120-126: Well-designed distribution state interface.

The WaveOutcomeDistributionState interface cleanly encapsulates pagination state with proper readonly modifiers. Using import type for ApiWaveOutcomeDistributionItem follows best practices for type-only imports.

components/waves/outcome/WaveOutcome.tsx (1)

54-66: The component lookup pattern instantiates all variants.

The Record<OutcomeType, JSX.Element> approach creates all three outcome components on every render, even though only one is displayed. This is a minor inefficiency. Consider conditional rendering if performance becomes a concern, but this is acceptable for now.

components/waves/outcome/WaveRepOutcome.tsx (1)

42-49: Pagination logic is well-implemented.

The onViewMore handler correctly manages both local expansion (showAll) and server-side pagination, with proper guard against duplicate fetches via isFetchingNextPage.

components/waves/outcome/WaveNICOutcome.tsx (1)

18-41: Clean distribution-based implementation.

The component properly uses item.index for keys and correctly separates local (showAll) and server-side pagination state. The remainingCount calculation is simpler than WaveRepOutcome but achieves the same result when showAll is true.

components/waves/outcome/WaveManualOutcome.tsx (5)

1-9: LGTM!

Imports are clean with appropriate type imports and direct named imports from React as per coding guidelines.


11-14: LGTM!

Props interface correctly uses readonly modifiers and properly typed with the new ApiWaveOutcome and WaveOutcomeDistributionState types.


22-32: LGTM!

State derivation logic is sound. The shouldShowMore condition correctly handles both local item hiding (!showAll && items.length > DEFAULT_AMOUNTS_TO_SHOW) and server-side pagination (hasNextPage).


113-115: LGTM!

The description display logic has been corrected to use {item.description ?? ""}, ensuring the description is shown regardless of whether an amount exists.


43-94: LGTM!

The accordion header section is well-structured with proper type="button" attribute, appropriate FontAwesome icons, and clean TailwindCSS styling. The motion elements provide smooth animations for the expand/collapse indicator.

Signed-off-by: Simo <simo@6529.io>
Copy link
Copy Markdown

@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

🧹 Nitpick comments (2)
components/waves/outcome/WaveOutcome.tsx (1)

34-43: Consider removing redundant useMemo.

The distributionState object is memoized, but all its dependencies (items, totalCount, hasNextPage, isFetchingNextPage, fetchNextPage) are already stable references returned from react-query. The useMemo provides minimal benefit here and adds complexity.

🔎 Simplified alternative
- const distributionState: WaveOutcomeDistributionState = useMemo(
-   () => ({
-     items,
-     totalCount,
-     hasNextPage: Boolean(hasNextPage),
-     isFetchingNextPage,
-     fetchNextPage,
-   }),
-   [items, totalCount, hasNextPage, isFetchingNextPage, fetchNextPage]
- );
+ const distributionState: WaveOutcomeDistributionState = {
+   items,
+   totalCount,
+   hasNextPage: Boolean(hasNextPage),
+   isFetchingNextPage,
+   fetchNextPage,
+ };
components/waves/outcome/WaveRepOutcome.tsx (1)

136-146: Consider disabling the "View more" button during fetch for consistency.

The button checks isFetchingNextPage in the click handler but doesn't visually disable the button. This is inconsistent with WaveManualOutcome.tsx (line 133) which uses disabled={isFetchingNextPage} and shows "Loading..." text. Users might click multiple times while fetching, even though the handler guards against it.

🔎 Proposed enhancement
              {shouldShowMore && (
                <button
                  className="tw-border-0 tw-w-full tw-px-4 tw-py-3 tw-text-left tw-bg-iron-900 tw-text-[#C3B5D9]/80 tw-text-sm hover:tw-text-[#C3B5D9] tw-transition-all tw-duration-300"
-                 onClick={onViewMore}>
+                 onClick={onViewMore}
+                 disabled={isFetchingNextPage}>
-                 <span>View more</span>
+                 <span>{isFetchingNextPage ? "Loading..." : "View more"}</span>
                  <span className="tw-ml-1 tw-text-iron-400">•</span>
                  <span className="tw-ml-1 tw-text-iron-400">
                    {remainingCount} more
                  </span>
                </button>
              )}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b38e16e and dbbecab.

📒 Files selected for processing (9)
  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx (1 hunks)
  • __tests__/components/waves/outcome/WaveOutcome.test.tsx (1 hunks)
  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx (1 hunks)
  • components/brain/my-stream/MyStreamWaveOutcome.tsx (1 hunks)
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx (2 hunks)
  • components/waves/outcome/WaveManualOutcome.tsx (2 hunks)
  • components/waves/outcome/WaveOutcome.tsx (3 hunks)
  • components/waves/outcome/WaveRepOutcome.tsx (2 hunks)
  • hooks/waves/useWaveOutcomeDistributionQuery.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/components/waves/outcome/WaveOutcome.test.tsx
  • hooks/waves/useWaveOutcomeDistributionQuery.ts
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/waves/outcome/WaveRepOutcome.tsx
  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/outcome/WaveOutcome.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/waves/outcome/WaveRepOutcome.tsx
  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/outcome/WaveOutcome.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Files:

  • components/waves/outcome/WaveRepOutcome.tsx
  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/outcome/WaveOutcome.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

**/*.{ts,tsx}: Must pass tsc --noEmit for TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
Use useEffectEvent for non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs: <Link> for internal links, next/image for images, adopt Next's ESLint rules
Use 'use cache' directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace <img> elements with <Image /> from next/image
Use <Link href="/path"> for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with 'use server' directive

Files:

  • components/waves/outcome/WaveRepOutcome.tsx
  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/outcome/WaveOutcome.tsx
**/@(__tests__|*.test).{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Tests should live in __tests__/ or ComponentName.test.tsx; mock external dependencies and APIs in tests

Files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Place tests in __tests__/ directory or as ComponentName.test.tsx alongside components

Files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Mock external dependencies and APIs in tests

Files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx
__tests__/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use Jest + ts-jest for TypeScript testing

Files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx
__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Functions must have ≤ 15 cognitive complexity; extract deep ternaries (>3 levels) and break down complex logic

Files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx
__tests__/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (tests/AGENTS.md)

__tests__/**/*.{ts,tsx,js}: Prefer for...of loops over forEach as it allows break/continue and works with async/await
Use array.at(-1) and array.at(-2) instead of index-based array access for negative indexing
Use String.prototype.replaceAll() instead of replace() for global string replacements
Use globalThis.fetch() instead of direct fetch() calls
Organize imports with one import per module in order: external → internal → types, with no duplicates
Use element.remove() instead of parent.removeChild(element) for DOM manipulation
Catch errors only when meaningful; no empty catch blocks; log errors with context
Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (?.)

Files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx
__tests__/**/{components,contexts,hooks}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use semantic HTML elements (<label>, <output>) over ARIA attributes when possible; every form control must have a label

Files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx
🧠 Learnings (14)
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx,js} : Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (`?.`)

Applied to files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/*.test.{ts,tsx,js} : Test high-risk areas including happy path workflows, invalid input errors, edge cases/boundaries, component & API interactions, and performance/security when relevant

Applied to files:

  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Replace `<img>` elements with `<Image />` from `next/image`

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Replace `<img>` elements with `<Image />` from `next/image` to satisfy `next/next/no-img-element` ESLint rule

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Use framework APIs: `<Link>` for internal links, `next/image` for images, adopt Next's ESLint rules

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use framework APIs: internal links should use `<Link>`, images should use `next/image`, and adopt Next's ESLint rules (Core Web Vitals)

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/outcome/WaveOutcome.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `<Link href="/path">` from Next.js for internal navigation instead of plain HTML links to satisfy `next/next/no-html-link-for-pages` ESLint rule

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)

Applied to files:

  • components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: TypeScript + React functional components with hooks; follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Applied to files:

  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/outcome/WaveOutcome.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript and React functional components with hooks

Applied to files:

  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/outcome/WaveOutcome.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use react-query for data fetching

Applied to files:

  • components/brain/my-stream/MyStreamWaveOutcome.tsx
  • components/waves/outcome/WaveOutcome.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/*.{ts,tsx} : Prefer direct named imports for React hooks and types (`import { useMemo, useRef, FC, etc. } from "react"`) over `React.` namespace usage (`React.useMemo`, `React.useRef`, etc.)

Applied to files:

  • components/waves/outcome/WaveOutcome.tsx
🧬 Code graph analysis (7)
components/waves/outcome/WaveRepOutcome.tsx (3)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
types/waves.types.ts (1)
  • WaveOutcomeDistributionState (120-126)
helpers/Helpers.ts (1)
  • formatNumberWithCommas (131-148)
__tests__/components/waves/outcome/WaveRepOutcome.test.tsx (1)
components/waves/outcome/WaveRepOutcome.tsx (1)
  • WaveRepOutcome (18-153)
__tests__/components/waves/outcome/WaveManualOutcome.test.tsx (1)
components/waves/outcome/WaveManualOutcome.tsx (1)
  • WaveManualOutcome (18-147)
components/waves/outcome/WaveManualOutcome.tsx (2)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
helpers/Helpers.ts (1)
  • formatNumberWithCommas (131-148)
components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx (1)
components/ipfs/IPFSContext.tsx (1)
  • resolveIpfsUrlSync (77-89)
components/brain/my-stream/MyStreamWaveOutcome.tsx (5)
generated/models/ApiWave.ts (1)
  • ApiWave (27-179)
components/brain/my-stream/layout/LayoutContext.tsx (1)
  • useLayout (522-522)
hooks/waves/useWaveOutcomesQuery.ts (1)
  • useWaveOutcomesQuery (41-105)
components/common/SpinnerLoader.tsx (1)
  • SpinnerLoader (8-57)
components/waves/outcome/WaveOutcome.tsx (1)
  • WaveOutcome (23-70)
components/waves/outcome/WaveOutcome.tsx (3)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
hooks/waves/useWaveOutcomeDistributionQuery.ts (1)
  • useWaveOutcomeDistributionQuery (44-120)
types/waves.types.ts (1)
  • WaveOutcomeDistributionState (120-126)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (7)
components/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsx (2)

6-6: LGTM! Correct imports for Next.js Image optimization and IPFS resolution.

The imports correctly bring in the Next.js Image component and the IPFS URL resolver, aligning with the coding guidelines and the broader PR pattern.

Also applies to: 13-13


34-40: and

components/waves/outcome/WaveManualOutcome.tsx (1)

105-127: LGTM! Distribution-driven rendering implemented correctly.

The itemLabel logic properly handles all cases:

  • Zero amounts display as "-"
  • Non-zero amounts show formatted numbers
  • Missing amounts fall back to descriptions

This addresses the previous review concern about description display and aligns with the new distribution-based architecture.

components/brain/my-stream/MyStreamWaveOutcome.tsx (4)

1-9: LGTM: Proper Client Component setup with correct imports.

The "use client" directive is correctly placed, and the migration from React.FC to direct named imports aligns with the coding guidelines. The new imports for infinite scroll and data fetching are appropriate for the refactored functionality.


18-43: LGTM: Clean infinite scroll implementation.

The integration of useWaveOutcomesQuery and useInfiniteScroll follows best practices. The refs are properly typed, and all necessary parameters are correctly passed to the infinite scroll hook.


45-46: LGTM: Appropriate loading state logic.

The helper variables correctly differentiate between initial loading and subsequent pagination fetches, ensuring proper UI states are displayed.


47-78: Render logic and key generation are correct.

The conditional rendering properly handles all UI states (initial loading, error, empty, data, pagination). The empty state check correctly includes !isFetching to prevent displaying "No outcomes" while paginating.

The key generation uses outcome.index ?? index where outcome.index is a required (non-optional) number property on the ApiWaveOutcome type, ensuring stable, unique keys that won't be affected by reordering or filtering of outcomes.

Comment thread components/waves/outcome/WaveOutcome.tsx
Signed-off-by: Simo <simo@6529.io>
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
7.6% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Copy link
Copy Markdown

@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

🧹 Nitpick comments (3)
components/waves/outcome/WaveManualOutcome.tsx (1)

38-40: Consider aligning remainingCount calculation with WaveRepOutcome.

This uses totalCount - visibleItems.length, while WaveRepOutcome distinguishes between locally hidden items and server-side remaining items. Both work, but the simpler calculation here may show slightly inaccurate counts when showAll is false and there are more items locally than displayed.

🔎 Align with WaveRepOutcome's pattern for consistency
- const remainingCount = Math.max(totalCount - visibleItems.length, 0);
+ const hiddenLocalCount = Math.max(
+   items.length - DEFAULT_AMOUNTS_TO_SHOW,
+   0
+ );
+ const remainingFromServer = Math.max(totalCount - items.length, 0);
+ const remainingCount = showAll
+   ? remainingFromServer
+   : hiddenLocalCount + remainingFromServer;
__tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx (1)

4-11: Remove unused fetchMock import and variable.

commonApiFetch is mocked but fetchMock is never used in the tests. This creates dead code.

🔎 Remove unused import and variable
 import { useWaveOutcomeDistributionQuery } from '@/hooks/waves/useWaveOutcomeDistributionQuery';
 import { useInfiniteQuery } from '@tanstack/react-query';
-import { commonApiFetch } from '@/services/api/common-api';
 import { QueryKey } from '@/components/react-query-wrapper/ReactQueryWrapper';
 
 jest.mock('@tanstack/react-query');
-jest.mock('@/services/api/common-api');
 
 const useInfiniteQueryMock = useInfiniteQuery as jest.Mock;
-const fetchMock = commonApiFetch as jest.Mock;
components/waves/outcome/WaveNICOutcome.tsx (1)

150-160: Consider adding loading state to the "View more" button.

The button doesn't show loading feedback when isFetchingNextPage is true. Based on the test expectations for WaveRepOutcome (lines 56-58 in the test file), the "View more" button should display loading text and be disabled during pagination for consistent UX.

🔎 Suggested implementation
 {shouldShowMore && (
   <button
     onClick={onViewMore}
+    disabled={isFetchingNextPage}
     className="tw-border-0 tw-w-full tw-px-4 tw-py-3 tw-text-left tw-bg-iron-900 tw-text-[#A4C2DB]/80 tw-text-sm hover:tw-text-[#A4C2DB] tw-transition-all tw-duration-300">
-    <span>View more</span>
+    <span>{isFetchingNextPage ? "loading..." : "View more"}</span>
     <span className="tw-ml-1 tw-text-iron-400">•</span>
     <span className="tw-ml-1 tw-text-iron-400">
       {remainingCount} more
     </span>
   </button>
 )}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbbecab and a11f77f.

📒 Files selected for processing (10)
  • __tests__/components/waves/outcome/WaveManualOutcome.test.tsx (1 hunks)
  • __tests__/components/waves/outcome/WaveOutcome.test.tsx (1 hunks)
  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx (2 hunks)
  • __tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx (1 hunks)
  • components/waves/outcome/WaveManualOutcome.tsx (3 hunks)
  • components/waves/outcome/WaveNICOutcome.tsx (3 hunks)
  • components/waves/outcome/WaveOutcome.tsx (3 hunks)
  • components/waves/outcome/WaveRepOutcome.tsx (3 hunks)
  • hooks/waves/useWaveOutcomeDistributionQuery.ts (1 hunks)
  • types/waves.types.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/components/waves/outcome/WaveOutcome.test.tsx
  • tests/components/waves/outcome/WaveManualOutcome.test.tsx
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Do not include any comments in the code; it should be self-explanatory
Write correct, up-to-date, bug-free, fully componentized, secure, and efficient code
Include all required imports and ensure proper naming of key components
Use NextJS features that match the current version

**/*.{ts,tsx,js,jsx}: Replace <img> elements with <Image /> from next/image to satisfy @next/next/no-img-element ESLint rule
Use <Link href="/path"> from Next.js for internal navigation instead of plain HTML links to satisfy @next/next/no-html-link-for-pages ESLint rule

Files:

  • components/waves/outcome/WaveNICOutcome.tsx
  • hooks/waves/useWaveOutcomeDistributionQuery.ts
  • types/waves.types.ts
  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • __tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx
  • components/waves/outcome/WaveRepOutcome.tsx
  • components/waves/outcome/WaveOutcome.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,jsx}: Use FontAwesome for icons in React components
Use TailwindCSS for styling in React components
Use react-query for data fetching
Always add readonly before props in React components

Files:

  • components/waves/outcome/WaveNICOutcome.tsx
  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • __tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx
  • components/waves/outcome/WaveRepOutcome.tsx
  • components/waves/outcome/WaveOutcome.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks)
Use framework APIs: internal links should use <Link>, images should use next/image, and adopt Next's ESLint rules (Core Web Vitals)

**/*.{js,jsx,ts,tsx}: Code must satisfy ESLint (Next's Core Web Vitals + React Hooks rules)
Follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Files:

  • components/waves/outcome/WaveNICOutcome.tsx
  • hooks/waves/useWaveOutcomeDistributionQuery.ts
  • types/waves.types.ts
  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • __tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx
  • components/waves/outcome/WaveRepOutcome.tsx
  • components/waves/outcome/WaveOutcome.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Must pass tsc --noEmit type checking
Prefer direct named imports for React hooks and types (import { useMemo, useRef, FC, etc. } from "react") over React. namespace usage (React.useMemo, React.useRef, etc.)
If the react-hooks/exhaustive-deps lint rule is triggered: if the Effect only derives state, remove the Effect and compute during render; if listening to an external system and needing fresh props/state, wrap non-reactive logic in useEffectEvent

**/*.{ts,tsx}: Must pass tsc --noEmit for TypeScript type checking
Prefer Server Components over Client Components; use Server Functions/Server Actions ('use server') for mutations
Remove unnecessary Effects; if Effect only derives state, compute during render instead
Use useEffectEvent for non-reactive logic inside Effects to avoid unnecessary re-runs
Use framework APIs: <Link> for internal links, next/image for images, adopt Next's ESLint rules
Use 'use cache' directive and Cache Components features for explicit opt-in caching in Next.js 16
Use TypeScript and React functional components with hooks
When parsing Seize URLs or similar, fail fast if base origin is unavailable; do not fall back to placeholder origins
Replace <img> elements with <Image /> from next/image
Use <Link href="/path"> for internal navigation instead of plain HTML links
Move data fetches to Server Components; handle mutations through Server Functions/Server Actions with 'use server' directive

Files:

  • components/waves/outcome/WaveNICOutcome.tsx
  • hooks/waves/useWaveOutcomeDistributionQuery.ts
  • types/waves.types.ts
  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
  • __tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx
  • components/waves/outcome/WaveRepOutcome.tsx
  • components/waves/outcome/WaveOutcome.tsx
**/@(__tests__|*.test).{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Tests should live in __tests__/ or ComponentName.test.tsx; mock external dependencies and APIs in tests

Files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx
**/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Place tests in __tests__/ directory or as ComponentName.test.tsx alongside components

Files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Mock external dependencies and APIs in tests

Files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx
__tests__/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use Jest + ts-jest for TypeScript testing

Files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx
__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Functions must have ≤ 15 cognitive complexity; extract deep ternaries (>3 levels) and break down complex logic

Files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx
__tests__/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (tests/AGENTS.md)

__tests__/**/*.{ts,tsx,js}: Prefer for...of loops over forEach as it allows break/continue and works with async/await
Use array.at(-1) and array.at(-2) instead of index-based array access for negative indexing
Use String.prototype.replaceAll() instead of replace() for global string replacements
Use globalThis.fetch() instead of direct fetch() calls
Organize imports with one import per module in order: external → internal → types, with no duplicates
Use element.remove() instead of parent.removeChild(element) for DOM manipulation
Catch errors only when meaningful; no empty catch blocks; log errors with context
Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (?.)

Files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx
__tests__/**/{components,contexts,hooks}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (tests/AGENTS.md)

Use semantic HTML elements (<label>, <output>) over ARIA attributes when possible; every form control must have a label

Files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • __tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx
🧠 Learnings (7)
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use react-query for data fetching

Applied to files:

  • hooks/waves/useWaveOutcomeDistributionQuery.ts
  • __tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx
  • components/waves/outcome/WaveOutcome.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/*.{ts,tsx,js} : Avoid double negatives in code; prefer explicit logic and remove redundant annotations; use optional chaining (`?.`)

Applied to files:

  • __tests__/components/waves/outcome/WaveRepOutcome.test.tsx
  • components/waves/outcome/WaveManualOutcome.tsx
📚 Learning: 2025-12-05T10:55:43.476Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-12-05T10:55:43.476Z
Learning: Applies to __tests__/**/__tests__/**/*.test.{ts,tsx,js} : Keep tests independent, deterministic, and fast with production-like data

Applied to files:

  • __tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx
📚 Learning: 2025-12-05T10:55:30.871Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-05T10:55:30.871Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript and React functional components with hooks

Applied to files:

  • components/waves/outcome/WaveOutcome.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Applies to **/*.{ts,tsx} : Prefer direct named imports for React hooks and types (`import { useMemo, useRef, FC, etc. } from "react"`) over `React.` namespace usage (`React.useMemo`, `React.useRef`, etc.)

Applied to files:

  • components/waves/outcome/WaveOutcome.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: TypeScript + React functional components with hooks; follow existing code style and naming conventions; maintain clean code standards (measured by SonarQube)

Applied to files:

  • components/waves/outcome/WaveOutcome.tsx
📚 Learning: 2025-12-03T14:52:34.271Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-03T14:52:34.271Z
Learning: Fix with modernization (no `// eslint-disable` unless explicitly instructed); prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/waves/outcome/WaveOutcome.tsx
🧬 Code graph analysis (7)
components/waves/outcome/WaveNICOutcome.tsx (3)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
types/waves.types.ts (1)
  • WaveOutcomeDistributionState (120-129)
helpers/Helpers.ts (1)
  • formatNumberWithCommas (131-148)
hooks/waves/useWaveOutcomeDistributionQuery.ts (2)
generated/models/ApiWaveOutcomeDistributionItemsPage.ts (1)
  • ApiWaveOutcomeDistributionItemsPage (16-58)
components/react-query-wrapper/utils/query-utils.ts (1)
  • getDefaultQueryRetry (21-34)
types/waves.types.ts (1)
generated/models/ApiWaveOutcomeDistributionItem.ts (1)
  • ApiWaveOutcomeDistributionItem (15-50)
components/waves/outcome/WaveManualOutcome.tsx (3)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
types/waves.types.ts (1)
  • WaveOutcomeDistributionState (120-129)
helpers/Helpers.ts (1)
  • formatNumberWithCommas (131-148)
__tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx (1)
hooks/waves/useWaveOutcomeDistributionQuery.ts (1)
  • useWaveOutcomeDistributionQuery (44-120)
components/waves/outcome/WaveRepOutcome.tsx (3)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
types/waves.types.ts (1)
  • WaveOutcomeDistributionState (120-129)
helpers/Helpers.ts (1)
  • formatNumberWithCommas (131-148)
components/waves/outcome/WaveOutcome.tsx (6)
generated/models/ApiWaveOutcome.ts (1)
  • ApiWaveOutcome (18-81)
hooks/waves/useWaveOutcomeDistributionQuery.ts (1)
  • useWaveOutcomeDistributionQuery (44-120)
types/waves.types.ts (1)
  • WaveOutcomeDistributionState (120-129)
components/waves/outcome/WaveRepOutcome.tsx (1)
  • WaveRepOutcome (18-186)
components/waves/outcome/WaveNICOutcome.tsx (1)
  • WaveNICOutcome (18-167)
components/waves/outcome/WaveManualOutcome.tsx (1)
  • WaveManualOutcome (18-175)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (19)
components/waves/outcome/WaveOutcome.tsx (2)

23-58: Distribution state setup is well-structured.

The distribution query integration is correct. The child components (WaveRepOutcome, WaveNICOutcome, WaveManualOutcome) properly handle isLoading, isError, and errorMessage states internally, so handling them at this level would be redundant. The useMemo dependency array correctly includes all referenced values.


72-82: Distribution props correctly passed to all outcome variants.

The component map properly provides distributionState to each child component, enabling consistent distribution-driven rendering across all outcome types.

hooks/waves/useWaveOutcomeDistributionQuery.ts (3)

51-56: Input normalization handles edge cases well.

The normalization logic correctly handles null, undefined, empty strings, and 0 as outcomeIndex. Converting outcomeIndex to string before trimming ensures numeric indices like 0 are treated as valid (non-empty "0").


80-102: Infinite query configuration is well-implemented.

The query correctly uses keepPreviousData for smooth pagination UX, applies retry logic via getDefaultQueryRetry(), and properly determines next page availability via getNextPageParam. The endpoint construction and parameter passing are correct.


104-119: Derived state and return value are correctly structured.

The items flattening uses useMemo appropriately. Reading totalCount from the first page is the standard pattern for paginated APIs where the count is consistent across pages.

components/waves/outcome/WaveRepOutcome.tsx (3)

24-57: Distribution state destructuring and pagination logic are correct.

The logic for visibleItems, remainingCount, and shouldShowMore correctly handles the interplay between local state (showAll) and server-side pagination (hasNextPage). The onViewMore function appropriately toggles local visibility and fetches more data when available.


150-166: Loading, error, and empty states are handled appropriately.

The conditional rendering correctly prioritizes loading and error states before showing the empty message. The error display includes a fallback message when errorMessage is undefined.


168-179: Pagination button correctly handles fetching state.

The button disables during isFetchingNextPage and shows appropriate loading feedback. The remaining count display is informative.

components/waves/outcome/WaveManualOutcome.tsx (2)

119-137: Verify the itemLabel display logic for manual outcomes.

The condition item.amount === 0 ? "-" : item.description ?? "" shows a dash when amount is exactly 0, otherwise shows the description. This differs from the previous review's suggestion to always show the description. If a manual outcome intentionally has amount: 0 to indicate "no reward" while still having a meaningful description, this would hide that description.

Confirm this is the intended UX—should items with amount === 0 display "-" or should they show their description regardless?


139-155: Loading, error, and empty states are consistent with other outcome components.

The implementation matches the pattern used in WaveRepOutcome, maintaining consistency across outcome types.

__tests__/hooks/waves/useWaveOutcomeDistributionQuery.test.tsx (1)

28-92: Test coverage for key edge cases is good.

The tests correctly verify that:

  • outcomeIndex: null results in enabled: false with empty string in queryKey
  • outcomeIndex: 0 (falsy but valid) results in enabled: true with "0" in queryKey
  • outcomeIndex: "1" (string) works correctly

This validates the critical normalization logic that distinguishes between null/undefined and valid falsy values like 0.

__tests__/components/waves/outcome/WaveRepOutcome.test.tsx (2)

21-32: LGTM! Well-structured distribution mock.

The distribution mock properly implements the WaveOutcomeDistributionState interface with all required fields and a mocked fetchNextPage function.


46-60: LGTM! Comprehensive loading state test.

The test properly validates that the "View more" button shows loading state (disabled with "loading..." text) while fetching the next page, and that the remaining count is still displayed.

types/waves.types.ts (1)

120-129: LGTM! Well-designed distribution state interface.

The WaveOutcomeDistributionState interface comprehensively covers pagination state, loading/error states, and data items with proper readonly fields following the coding guidelines.

components/waves/outcome/WaveNICOutcome.tsx (5)

3-14: LGTM! Proper imports and props structure.

The component correctly uses direct named imports for React hooks and properly defines readonly props following the coding guidelines.


18-40: LGTM! Sound pagination logic.

The derived state logic correctly handles visible items, remaining count, and determines when to show the "View more" button based on both local items and server-side pagination state.


42-49: LGTM! Correct pagination trigger.

The onViewMore handler properly coordinates local state expansion with server-side pagination, preventing duplicate fetch requests.


70-77: LGTM! Clean loading state.

The skeleton loader provides good visual feedback during initial load with appropriate pluralization logic.


117-148: LGTM! Comprehensive state handling.

The component properly renders distribution items using stable keys (item.index) and handles all states including loading, error, and empty scenarios with clear user feedback.

@simo6529 simo6529 linked an issue Dec 19, 2025 that may be closed by this pull request
@simo6529 simo6529 merged commit ad6bf46 into main Dec 19, 2025
8 of 9 checks passed
@simo6529 simo6529 deleted the outcomes-refactor branch December 19, 2025 14:00
@coderabbitai coderabbitai Bot mentioned this pull request Dec 22, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Jan 5, 2026
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.

Adapt to wave outcomes being moved to separate endpoint/table

2 participants