Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds identity-nomination submission support across waves: new submission strategy types, identity picker UI, identity-aware metadata handling/validation, identity-aware drop/winner rendering, submission-experience resolution, rank styling consolidation, and many tests and OpenAPI schema updates. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CreateDropContent
participant IdentityPickerModal
participant IdentitySearch
participant API
User->>CreateDropContent: open Drop mode
CreateDropContent->>IdentityPickerModal: auto-open picker (if identity experience)
IdentityPickerModal->>IdentitySearch: query profiles / select
IdentitySearch-->>IdentityPickerModal: selection (SelectableIdentityOption)
IdentityPickerModal-->>CreateDropContent: onSelect(selection)
CreateDropContent->>CreateDropContent: buildDropSubmissionMetadata(metadata, selection)
CreateDropContent->>API: submit drop with participation.submission_strategy / submission metadata
API-->>CreateDropContent: success / validation errors
alt validation errors (identity/metadata)
CreateDropContent->>IdentityPickerModal: show errors / reopen picker
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
components/waves/create-wave/drops/submission-mode/CreateWaveDropsSubmissionMode.tsx (1)
312-318: Error message misleads users about the actual validation constraint.The error "Complete the identity nomination settings before continuing" displays when
DROPS_SUBMISSION_STRATEGY_INVALIDis triggered. However, based on the validation logic increate-wave.validation.ts(lines 131-151), this error occurs when a chat wave has a non-null submission strategy—not when identity settings are incomplete.The phrasing "Complete the identity nomination settings" suggests users should fill in additional configuration, when the actual constraint is that chat waves cannot have any submission strategy at all. Consider rephrasing to something like "Chat waves cannot have submission strategy settings" or "Remove the submission strategy setting for chat waves."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/create-wave/drops/submission-mode/CreateWaveDropsSubmissionMode.tsx` around lines 312 - 318, The error text shown when showSubmissionStrategyError is true is misleading: when DROPS_SUBMISSION_STRATEGY_INVALID is set (per create-wave.validation.ts) it means a chat wave has a non-null submission strategy, not that identity nomination fields are incomplete. Update the UI text inside the CommonAnimationHeight block (where showSubmissionStrategyError is used) to reflect the actual constraint (e.g., "Chat waves cannot have submission strategy settings" or "Remove the submission strategy setting for chat waves") so the message matches the DROPS_SUBMISSION_STRATEGY_INVALID validation rule.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@__tests__/helpers/waves/create-wave.helpers.extra.test.ts`:
- Around line 108-109: The tests "calculates rolling end date correctly" (and
the subsequent tests) were placed after the closing of the
describe("create-wave.helpers extra", ...) block; move those it(...) blocks so
they are inside the describe block (i.e., relocate the tests from module scope
back between the describe's opening and its final closing brace) and ensure the
describe's final closing brace is placed after all tests (after the last it(...)
in the file) so they are grouped under create-wave.helpers extra.
---
Nitpick comments:
In
`@components/waves/create-wave/drops/submission-mode/CreateWaveDropsSubmissionMode.tsx`:
- Around line 312-318: The error text shown when showSubmissionStrategyError is
true is misleading: when DROPS_SUBMISSION_STRATEGY_INVALID is set (per
create-wave.validation.ts) it means a chat wave has a non-null submission
strategy, not that identity nomination fields are incomplete. Update the UI text
inside the CommonAnimationHeight block (where showSubmissionStrategyError is
used) to reflect the actual constraint (e.g., "Chat waves cannot have submission
strategy settings" or "Remove the submission strategy setting for chat waves")
so the message matches the DROPS_SUBMISSION_STRATEGY_INVALID validation rule.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 25923ace-83e3-473d-9043-790cdd7bdc93
📒 Files selected for processing (13)
__tests__/components/waves/create-wave/CreateWave.test.tsx__tests__/components/waves/create-wave/drops/CreateWaveDrops.test.tsx__tests__/components/waves/create-wave/drops/submission-mode/CreateWaveDropsSubmissionMode.test.tsx__tests__/components/waves/create-wave/hooks/useWaveConfig.test.ts__tests__/helpers/waves/create-wave.helpers.extra.test.ts__tests__/helpers/waves/create-wave.helpers.test.ts__tests__/helpers/waves/create-wave.validation.test.tscomponents/waves/create-wave/drops/CreateWaveDrops.tsxcomponents/waves/create-wave/drops/submission-mode/CreateWaveDropsSubmissionMode.tsxcomponents/waves/create-wave/hooks/useWaveConfig.tshelpers/waves/create-wave.helpers.tshelpers/waves/create-wave.validation.tstypes/waves.types.ts
There was a problem hiding this comment.
Actionable comments posted: 17
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
components/waves/winners/WaveWinnersSmall.tsx (1)
126-133:⚠️ Potential issue | 🟡 MinorFix count mismatch in decision selector: use filtered winners count
The
WaveWinnersSmallDecisionSelectorat line 129 receiveswinnersCount: point.winners.length, displaying the unfiltered count, while the actual rendered winners at lines 138-148 useactiveDecisionPointWinners(filtered throughgetRenderableWaveDecisionWinners).Since
getRenderableWaveDecisionWinnersfilters out winners without valid drop data (lines 13-27 in wave-decision.helpers.ts), any missing drop data will cause a count mismatch. The warning log at lines 41-48 confirms such invalid winners can exist.Update line 129 to use the filtered count:
♻️ Suggested fix
<WaveWinnersSmallDecisionSelector decisionPoints={decisionPoints.map((point) => ({ id: point.id, date: point.date, - winnersCount: point.winners.length, + winnersCount: getRenderableWaveDecisionWinners(point.winners).length, }))} activeDecisionPoint={activeDecisionPoint} onChange={setSelectedDecisionPoint} />Note: This adds a filtering call per decision point. If performance becomes an issue with many decision points, consider caching or memoizing filtered counts.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/winners/WaveWinnersSmall.tsx` around lines 126 - 133, The decision selector is passing the raw winners length (point.winners.length) to WaveWinnersSmallDecisionSelector which can mismatch the rendered list because the UI uses getRenderableWaveDecisionWinners to filter out winners without valid drop data; update the decisionPoints mapping to compute winnersCount by calling getRenderableWaveDecisionWinners(point.winners).length for each point (referencing WaveWinnersSmallDecisionSelector, decisionPoints, and getRenderableWaveDecisionWinners) so the selector shows the filtered count; if performance is a concern, consider memoizing or caching the filtered counts.components/utils/input/identity/IdentitySearch.tsx (1)
325-331:⚠️ Potential issue | 🟡 MinorThe error-state label classes never win.
The unconditional
peer-placeholder-shown:tw-top-1/2,peer-placeholder-shown:-tw-translate-y-1/2, andpeer-focus:tw-text-primary-400that follow this branch override the error-specific utilities, so the label still uses the normal positioning/color whenerroris true. Split the shared classes from the conflicting ones.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/utils/input/identity/IdentitySearch.tsx` around lines 325 - 331, The label in IdentitySearch builds one long className where unconditional duplicate utilities (peer-placeholder-shown:tw-top-1/2, peer-placeholder-shown:-tw-translate-y-1/2, peer-focus:tw-text-primary-400) override the error-specific classes; refactor the className so shared, non-conflicting classes (including LABEL_CLASSES[size]) are in one string and the state-specific utilities are applied conditionally — e.g. compute an errorClasses string containing the error-specific peer-* rules and apply it when error is true, removing the unconditional conflicting peer-* utilities so the error classes actually take effect for the label in IdentitySearch (label htmlFor={inputId}).
🧹 Nitpick comments (15)
__tests__/components/waves/create-wave/drops/metadata/CreateWaveDropsMetadataRow.test.tsx (1)
17-46: Consider adding a test case forerrorMessage={null}to verify no error is displayed.The current test only covers the error state. Adding a test with
errorMessage={null}would verify the non-error rendering path.📝 Suggested additional test
it("does not display error when errorMessage is null", () => { render( <CreateWaveDropsMetadataRow item={{ key: "valid", type: ApiWaveMetadataType.String }} index={0} errorMessage={null} onItemChange={jest.fn()} onItemRemove={jest.fn()} /> ); expect( screen.queryByText(IDENTITY_SUBMISSION_RESERVED_METADATA_ERROR) ).not.toBeInTheDocument(); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@__tests__/components/waves/create-wave/drops/metadata/CreateWaveDropsMetadataRow.test.tsx` around lines 17 - 46, Add a new unit test for CreateWaveDropsMetadataRow that renders the component with errorMessage={null} (use item={{ key: "valid", type: ApiWaveMetadataType.String }}, index=0 and jest.fn() for onItemChange/onItemRemove) and assert that the reserved error text (IDENTITY_SUBMISSION_RESERVED_METADATA_ERROR) is not present using screen.queryByText(...).not.toBeInTheDocument(); this verifies the non-error rendering path.components/waves/create-wave/drops/metadata/CreateWaveDropsMetadata.tsx (2)
78-86: Consider memoizing computed indices for larger lists.The
reservedIdentityMetadataIdxsand other computations run on every render. For typical metadata counts this is fine, but if lists grow, consideruseMemo.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/create-wave/drops/metadata/CreateWaveDropsMetadata.tsx` around lines 78 - 86, The computation of reservedIdentityMetadataIdxs iterates requiredMetadata on every render which can be expensive for large lists; wrap this calculation in React.useMemo so it only recomputes when requiredMetadata or the predicate changes (useMemo(() => { ... }, [requiredMetadata])) and do the same for any other similar derived arrays; reference the reservedIdentityMetadataIdxs variable, the requiredMetadata input, and the isReservedIdentitySubmissionMetadataKey predicate when adding the dependency array.
88-101: Error priority: reserved identity errors take precedence over non-unique errors.If a row has both a reserved key and is a duplicate, only the reserved identity error is shown. Verify this priority is intentional—if both errors matter, consider combining them or showing both.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/create-wave/drops/metadata/CreateWaveDropsMetadata.tsx` around lines 88 - 101, The current getRowErrorMessage function returns only the reserved identity error when an index is both reserved and non-unique, which hides duplicate errors; update getRowErrorMessage (and related logic that consumes it) to either combine both messages or return both error codes when reservedIdentityMetadataIdxs and nonUniqueMetadataIdxs both include the index and haveReservedIdentityMetadata and haveNonUniqueMetadata are true; reference the constants IDENTITY_SUBMISSION_RESERVED_METADATA_ERROR and NON_UNIQUE_METADATA_ERROR and ensure the consumer can accept a combined string/array (or adjust UI rendering) so both errors are surfaced instead of only returning the reserved error.components/waves/CreateDropMetadataRow.tsx (1)
47-47: Use the enum consistently with the rest of the codebase.Line 47 uses the string literal
"NUMBER"while lines 95 and 129 useApiWaveMetadataType.Number. Both are functionally equivalent (ApiWaveMetadataType.Number='NUMBER'), but for consistency and maintainability, align line 47 with the enum usage throughout the component.♻️ Proposed fix
const handleValueChange = (e: React.ChangeEvent<HTMLInputElement>) => { const newValue = e.target.value; - if (metadata.type === "NUMBER") { + if (metadata.type === ApiWaveMetadataType.Number) { if (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/CreateDropMetadataRow.tsx` at line 47, Replace the string literal check for metadata.type === "NUMBER" with the enum usage to match the rest of the component; update the condition to use ApiWaveMetadataType.Number (the same enum used on lines that reference ApiWaveMetadataType.Number) so all checks against metadata.type consistently use the ApiWaveMetadataType enum (locate the check in CreateDropMetadataRow where metadata.type is compared and swap the literal "NUMBER" for ApiWaveMetadataType.Number).__tests__/components/brain/right-sidebar/BrainRightSidebarContent.test.tsx (1)
25-31: AssertuseRing={false}is passed toWaveIdentitySubmissionSpecs.This test validates order, but not the prop contract that controls the identity-spec styling mode. Capturing/asserting
props.useRing === falsewould prevent silent regressions.Proposed test tweak
jest.mock("@/components/waves/specs/WaveIdentitySubmissionSpecs", () => ({ __esModule: true, default: (props: any) => { - captured.push(`identity:${props.wave.id}`); + captured.push(`identity:${props.wave.id}:useRing=${String(props.useRing)}`); return <div data-testid="identity-submission-specs" />; }, })); @@ expect(captured).toEqual([ "boosted:wave-1", "specs:wave-1", - "identity:wave-1", + "identity:wave-1:useRing=false", "groups:wave-1", ]);Also applies to: 46-59
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@__tests__/components/brain/right-sidebar/BrainRightSidebarContent.test.tsx` around lines 25 - 31, The mock for WaveIdentitySubmissionSpecs should verify the useRing prop is false to prevent regressions; update the mock (the default export for WaveIdentitySubmissionSpecs) to inspect props.useRing and push or assert that it's false (e.g., captured.push(`identity:${props.wave.id}:useRing:${props.useRing}`) or throw if props.useRing !== false) so tests fail if useRing becomes true; apply the same change to the other mocked instance referenced similarly so both occurrences validate props.useRing === false.__tests__/components/waves/winners/podium/WavePodiumItem.test.tsx (1)
58-61: Use a less brittle click target in the test.The multi-level
parentElementchain is fragile and can fail on non-functional DOM reshapes.♻️ Proposed test refactor
- fireEvent.click( - screen.getByRole("link", { name: /alice/i }).parentElement!.parentElement! - .parentElement!.parentElement! - ); + const card = screen.getByRole("link", { name: /alice/i }).closest(".tw-group"); + expect(card).not.toBeNull(); + fireEvent.click(card!);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@__tests__/components/waves/winners/podium/WavePodiumItem.test.tsx` around lines 58 - 61, The test in WavePodiumItem.test.tsx uses a brittle chain of parentElement calls to find the click target; instead target a stable accessible element (e.g., click the link itself returned by screen.getByRole("link", { name: /alice/i }) or find the nearest ancestor with a role/test-id using element.closest or within). Replace the multi-level parentElement chain used with fireEvent.click(...) by directly clicking the link element or by using element.closest('[role="..."]') / getByTestId on the WavePodiumItem test to locate a stable container before calling fireEvent.click.components/waves/utils/buildDropSubmissionMetadata.ts (1)
16-18: Normalize identity before metadata injection.Line [16] only checks truthiness, so whitespace-only identity values can still be submitted. Trim first, then branch.
Suggested refactor
export const buildDropSubmissionMetadata = ({ metadata, identity, }: { readonly metadata: CreateDropMetadataType[]; readonly identity?: string | null | undefined; }): DropMetadata[] => { - if (!identity) { + const normalizedIdentity = identity?.trim(); + if (!normalizedIdentity) { return convertMetadataToDropMetadata(metadata); } @@ { data_key: IDENTITY_SUBMISSION_METADATA_KEY, - data_value: identity, + data_value: normalizedIdentity, }, ]; };Also applies to: 29-29
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/utils/buildDropSubmissionMetadata.ts` around lines 16 - 18, Trim and normalize the identity string before any truthiness checks and before injecting it into metadata: replace direct checks on identity with a normalizedIdentity = identity?.trim() (or equivalent) and use normalizedIdentity for the early return (calling convertMetadataToDropMetadata(metadata) when empty) and wherever identity is injected later (the branch around line 29). Ensure all references in buildDropSubmissionMetadata use normalizedIdentity so whitespace-only values are treated as empty.__tests__/components/waves/leaderboard/identity/WaveLeaderboardIdentity.test.tsx (1)
20-127: Consider adding a test for non-identity waves.The test suite covers resolved and unresolved identity scenarios well, but it would be valuable to add a test verifying that the component returns
nullfor non-identity waves (similar to the test inWaveWinnerIdentity.test.tsxat lines 119-132).✨ Suggested test case
+ it("renders nothing for non-identity waves", () => { + const { container } = render( + <WaveLeaderboardIdentity + drop={ + { + id: "d1", + wave: { submission_type: null }, + metadata: [{ data_key: "identity", data_value: "0xdef" }], + } as any + } + variant="condensed" + /> + ); + + expect(container).toBeEmptyDOMElement(); + }); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@__tests__/components/waves/leaderboard/identity/WaveLeaderboardIdentity.test.tsx` around lines 20 - 127, Add a test to WaveLeaderboardIdentity.test.tsx that verifies WaveLeaderboardIdentity returns null when the drop.wave.submission_type is not ApiWaveParticipationSubmissionStrategyType.Identity: create a render with a drop where wave.submission_type is e.g. ApiWaveParticipationSubmissionStrategyType.Card (or any non-Identity value), call render(<WaveLeaderboardIdentity drop={...} variant="condensed" />) and assert that the component root/output is null by checking that known identity elements (e.g., getByText("Identity") or getByTestId("identity-badges") / getByTestId("identity-full-card")) are not present (use queryBy*). This mirrors the non-identity test pattern used in WaveWinnerIdentity.test.tsx and ensures WaveLeaderboardIdentity returns nothing for non-identity waves.__tests__/components/waves/winners/drops/DefaultWaveWinnerDrop.test.tsx (1)
60-80: Consider adding a negative test case for non-identity waves.The test verifies that identity renders, but it would strengthen coverage to also test that the identity component renders appropriately when
submission_typeis not"IDENTITY"(depending on expected behavior in that case).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@__tests__/components/waves/winners/drops/DefaultWaveWinnerDrop.test.tsx` around lines 60 - 80, Add a negative test that renders DefaultWaveWinnersDrop with a winner whose drop.wave.submission_type is not "IDENTITY" and assert the identity element is absent; specifically, in DefaultWaveWinnerDrop.test.tsx create a new it block that renders <DefaultWaveWinnersDrop> with winner.drop.wave.submission_type set to e.g. "LINK" (or undefined), use onDropClick={jest.fn()}, then use screen.queryByTestId("identity") and expect it toBeNull() or not.toBeInTheDocument(), ensuring the test references DefaultWaveWinnersDrop and the submission_type field so behavior for non-identity waves is covered.__tests__/components/waves/winners/drops/WaveWinnersDrops.test.tsx (1)
44-57: Test depends onNODE_ENV !== "production"– consider mockingpublicEnv.This test asserts that the dev warning text is displayed, but the component only shows this warning when
publicEnv.NODE_ENV !== "production". If the test environment runs withNODE_ENV=production, this assertion will fail.♻️ Proposed fix to mock the environment config
+jest.mock("@/config/env", () => ({ + publicEnv: { NODE_ENV: "development" }, +})); + jest.mock("@/components/waves/winners/drops/WaveWinnersDrop", () => ({🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@__tests__/components/waves/winners/drops/WaveWinnersDrops.test.tsx` around lines 44 - 57, The test relies on publicEnv.NODE_ENV being non-"production" so it should mock the exported publicEnv used by the WaveWinnersDrops component before rendering; update the test to jest.mock the module that exports publicEnv (or override the publicEnv export) so publicEnv.NODE_ENV = "development" (or any non-"production" value) for the duration of the test, then render WaveWinnersDrops and assert the dev warning; ensure you restore/clear the mock between tests so other specs using WaveWinnersDrops are not affected.components/utils/input/profile-search/CommonProfileSearchItems.tsx (1)
24-28: Remove duplicate| undefinedin type definition.Line 27 has
| undefinedrepeated twice.♻️ Proposed fix
readonly onHighlightedOptionIdChange?: | ((optionId: string | undefined) => void) - | undefined | undefined;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/utils/input/profile-search/CommonProfileSearchItems.tsx` around lines 24 - 28, The property signature for onHighlightedOptionIdChange in CommonProfileSearchItems.tsx contains a duplicate "| undefined" token; edit the type declaration for readonly onHighlightedOptionIdChange? so it only includes a single "| undefined" (i.e., remove the duplicated undefined) to clean up the union type while keeping the optional modifier and preserving the existing function type ((optionId: string | undefined) => void).components/waves/winners/drops/WaveWinnersDrops.tsx (1)
42-58: Consider extracting the warning element to avoid duplication.The warning
<p>element with identical styling and content is duplicated at lines 43-45 and 55-57.♻️ Proposed refactor to extract warning element
+ const warningElement = shouldShowInvalidWinnerWarning ? ( + <p className="tw-rounded-md tw-border tw-border-amber-500/40 tw-bg-amber-500/10 tw-px-3 tw-py-2 tw-text-xs tw-text-amber-200"> + {invalidWinnersMessage} + </p> + ) : null; // Empty state handling if (!renderableWinners.length) { - if (shouldShowInvalidWinnerWarning) { - return ( - <p className="tw-rounded-md tw-border tw-border-amber-500/40 tw-bg-amber-500/10 tw-px-3 tw-py-2 tw-text-xs tw-text-amber-200"> - {invalidWinnersMessage} - </p> - ); - } - - return <></>; + return warningElement ?? <></>; } return ( <div className="tw-space-y-3"> - {shouldShowInvalidWinnerWarning ? ( - <p className="tw-rounded-md tw-border tw-border-amber-500/40 tw-bg-amber-500/10 tw-px-3 tw-py-2 tw-text-xs tw-text-amber-200"> - {invalidWinnersMessage} - </p> - ) : null} + {warningElement} {renderableWinners.map((winner) => (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/winners/drops/WaveWinnersDrops.tsx` around lines 42 - 58, Duplicate warning <p> block (using invalidWinnersMessage styling) appears twice in WaveWinnersDrops.tsx; extract it into a single reusable JSX constant or small component (e.g., InvalidWinnersWarning or renderInvalidWinnersWarning) and replace both inline <p> occurrences (including where you check shouldShowInvalidWinnerWarning and the early return) with that reusable reference so styling and content are maintained in one place.__tests__/components/waves/drops/participation/EndedParticipationDrop.test.tsx (1)
14-41: Mock implementation is duplicated – consider using the mock function directly.
ParticipationIdentityProfileCardMockis defined at lines 14-18, but thejest.mockat lines 31-41 creates a new inline implementation instead of reusing the mock function's return value. The current setup works because the mock function is still called for tracking, but it's cleaner to be consistent.♻️ Proposed simplification
-const ParticipationIdentityProfileCardMock = jest.fn(({ profile }: any) => ( - <div data-testid="identity-card"> - {profile.handle ?? profile.primary_address} - </div> -)); +const ParticipationIdentityProfileCardMock = jest.fn(); // ... jest.mock( "@/components/waves/drops/participation/ParticipationIdentityProfileCard", () => (props: any) => { ParticipationIdentityProfileCardMock(props); return ( <div data-testid="identity-card"> {props.profile.handle ?? props.profile.primary_address} </div> ); } );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@__tests__/components/waves/drops/participation/EndedParticipationDrop.test.tsx` around lines 14 - 41, The mock for "@/components/waves/drops/participation/ParticipationIdentityProfileCard" duplicates the inline implementation instead of reusing the existing ParticipationIdentityProfileCardMock; replace the inline factory with a call to ParticipationIdentityProfileCardMock so the jest.mock returns the existing mock function's render (ensuring the test-id and output ({profile.handle ?? profile.primary_address}) remain identical) and remove the duplicated JSX implementation to keep tracking and behavior consistent.components/waves/CreateDropIdentityField.tsx (2)
35-39: Harden avatar initial fallback for empty labels.At Line 38,
slice(0, 1)can render an empty avatar ifidentity.labelis blank/whitespace. Consider a safe fallback character.Proposed patch
- {identity.label.slice(0, 1)} + {(identity.label.trim().slice(0, 1) || "?").toUpperCase()}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/CreateDropIdentityField.tsx` around lines 35 - 39, The avatar initial fallback can be empty when identity.label is blank/whitespace; in CreateDropIdentityField (check the block that returns when !identity.avatarUrl) replace the direct identity.label.slice(0,1) usage with a safe extraction: trim identity.label, take the first character if present, otherwise use a deterministic fallback character (e.g., '?' or '•') and render that instead so the avatar never appears empty.
17-28: Add a defensive fallback ingetHelperText.If an unexpected mode arrives, this currently returns
undefinedand the helper copy disappears. Add a default branch so UI text remains stable.Proposed patch
const getHelperText = ( mode: ApiWaveParticipationIdentitySubmissionWhoCanBeSubmitted ) => { switch (mode) { case ApiWaveParticipationIdentitySubmissionWhoCanBeSubmitted.OnlyMyself: return "Your identity will be used automatically for this submission."; case ApiWaveParticipationIdentitySubmissionWhoCanBeSubmitted.OnlyOthers: return "Select someone else to nominate."; case ApiWaveParticipationIdentitySubmissionWhoCanBeSubmitted.Everyone: return "Select the identity to nominate."; + default: + return "Select the identity to nominate."; } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/CreateDropIdentityField.tsx` around lines 17 - 28, getHelperText currently has no default branch and can return undefined for unexpected values of ApiWaveParticipationIdentitySubmissionWhoCanBeSubmitted; update the getHelperText function to include a default case that returns a safe fallback string (e.g., a generic helper like "Select the identity to nominate." or "Please choose an identity.") so the UI never loses helper text, or alternatively throw/assert in the default if you want explicit failure; reference the getHelperText function and the ApiWaveParticipationIdentitySubmissionWhoCanBeSubmitted enum when making this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@__tests__/components/memes/drops/MemeWinnerDrop.test.tsx`:
- Around line 47-54: Remove the unused onQuote prop from the test render of the
MemeWinnerDrop component: locate the render call that mounts <MemeWinnerDrop ...
/> in MemeWinnerDrop.test.tsx (the render block passing drop, showReplyAndQuote,
onReply, and onQuote) and delete the onQuote={jest.fn()} entry so the test only
supplies props defined by MemeWinnerDropProps (e.g., drop, showReplyAndQuote,
onReply); run the test suite to confirm no other tests pass onQuote and remove
any other occurrences if present.
- Around line 63-70: The test passes an unused prop `onQuote` to the
MemeWinnerDrop component; remove the `onQuote={jest.fn()}` prop from the render
call in MemeWinnerDrop.test.tsx so the component is invoked only with the props
it actually uses (keep drop, showReplyAndQuote, onReply as-is); search for the
render invocation of MemeWinnerDrop to update the JSX accordingly.
In `@__tests__/components/waves/CreateDropContent.identity.test.tsx`:
- Around line 103-107: The mock for CreateDropIdentityField always falls back to
props.selfIdentity?.label which breaks expectations for the literal "none" in
later assertions; update the jest.mock for CreateDropIdentityField so it is
mode-aware (inspect props.mode or props.isDropMode) and only falls back to
selfIdentity when appropriate, rendering "none" when both selectedIdentity and
selfIdentity are null in drop mode, or alternatively have the mock expose the
passed props (e.g., as data attributes or JSON text) and change the assertions
to assert on the props passed into the mock rather than its rendered fallback
text; modify the mock in CreateDropContent.identity.test.tsx (the jest.mock for
CreateDropIdentityField) accordingly and update the related expectations that
previously asserted "none".
In `@__tests__/components/waves/specs/WaveIdentitySubmissionSpecs.test.tsx`:
- Around line 42-47: The test title in WaveIdentitySubmissionSpecs.test.tsx is
misleading: it says "renders nothing when the wave is not identity-based" but
the scenario uses a wave with submission strategy set to null (no submission
strategy configured). Update the it(...) description to something like "renders
nothing when no submission strategy is configured" to match the input; ensure
the test around WaveIdentitySubmissionSpecs and the baseWave fixture remains
unchanged so intent is clear.
In `@__tests__/components/waves/winners/DefaultWaveWinnerDropSmall.test.tsx`:
- Line 32: Remove the unused local variable "wave" and stop passing a
non-existent "wave" prop to DefaultWaveWinnerDropSmall in the test; locate the
declaration "const wave = {} as any" and delete it, and remove the "wave={wave}"
prop from the DefaultWaveWinnerDropSmall JSX so the component only receives its
defined props (drop, onDropClick, rank).
- Around line 1-4: The test file imports screen twice; remove the duplicate
import by consolidating imports from "@testing-library/react" so that render and
screen come from a single import statement (keep import of render and screen
together, and leave userEvent and DefaultWaveWinnerDropSmall imports unchanged),
e.g., ensure the import list for "@testing-library/react" includes both render
and screen and delete the redundant separate import.
In `@__tests__/components/waves/winners/identity/winnerIdentity.helpers.test.ts`:
- Around line 1-75: The tests fail to import getWinnerIdentityProfile and
getWinnerIdentityFallbackValue because those functions are not exported from
winnerIdentity.helpers.ts; open that file and add named exports for
getWinnerIdentityProfile and getWinnerIdentityFallbackValue (and ensure
getWinnerVisibleMetadata remains exported) so the test file can import them;
update any existing export list or add export keywords to the function
declarations to expose these symbols.
In `@components/mobile-wrapper-dialog/MobileWrapperDialog.tsx`:
- Around line 174-177: The current conditional class toggles
"tw-overflow-visible" which disables vertical scrolling; modify the class logic
in MobileWrapperDialog where the classes are built (the allowOverflow ternary
around "tw-overflow-visible" / "tw-overflow-y-auto") so that vertical scrolling
remains enabled—e.g., when allowOverflow is true include "tw-overflow-x-visible"
while still including "tw-overflow-y-auto" (or otherwise ensure
"tw-overflow-y-auto" is always present) instead of using "tw-overflow-visible".
In `@components/utils/input/profile-search/CommonProfileSearchItem.tsx`:
- Around line 31-34: The nested <button> inside the list item violates the
listbox pattern; remove the button element and move its onClick handler to the
<li id={id} role="option" aria-selected={isSelected}> so the <li> is the
interactive target, and add an onMouseDown on that same <li> to preserve the
input focus behavior (follow the pattern used in
CreateWaveGroupSearchField.tsx). Update any handler names (e.g., the existing
button's click handler) to be used on the <li> (and keep any
stopPropagation/preventDefault logic intact if present), and remove the
now-unused button element and its props. Ensure the aria and id values remain
unchanged so aria-activedescendant continues to work.
In `@components/waves/CreateDropContent.tsx`:
- Around line 536-537: The hasMetadata computed in useMemo currently only
reflects user-entered metadata via hasMetadataContent(metadata), but reserved
identity metadata injected later by buildDropSubmissionMetadata() should count
as submission content; update the logic where hasMetadata is computed (and where
ensurePartsWithFallback is called) to treat identity-reserved metadata as
present — e.g., derive hasMetadata by checking metadata OR whether
buildDropSubmissionMetadata would add an "identity" entry (use
buildDropSubmissionMetadata/its signature or a helper like
hasReservedIdentityMetadata) so canSubmit and the ensurePartsWithFallback(...)
calls (referenced by ensurePartsWithFallback and hasMetadataContent) include the
identity-only nomination as valid.
In `@components/waves/drops/identityDisplay.helpers.ts`:
- Around line 54-60: The code calls
getIdentityMetadata(metadata)?.data_value.trim() which can throw if data_value
is undefined because .trim() isn't optional-chained; change the expression to
use optional chaining on data_value before calling trim, e.g. use
getIdentityMetadata(metadata)?.data_value?.trim(), and keep the subsequent
undefined/length checks (references: getIdentityMetadata, metadata, value).
In `@components/waves/drops/participation/ParticipationDropMetadata.tsx`:
- Around line 89-100: The metadata toggle buttons in ParticipationDropMetadata
(the two JSX <button> elements that call handleShowLess and handleShowMore) lack
an explicit type and can inadvertently submit a parent form; update both button
elements to include type="button" so their clicks do not trigger form
submission, keeping the existing onClick handlers and classes unchanged.
In `@components/waves/specs/WaveIdentitySubmissionSpecs.tsx`:
- Around line 33-37: The component reads identity-only fields from
wave.participation.submission_strategy (e.g., accessing .summary) without
ensuring the strategy is an identity-type; update WaveIdentitySubmissionSpecs to
first guard that submissionStrategy represents an identity strategy (e.g., check
a discriminant like submissionStrategy.type === 'identity' or that
submissionStrategy.identity_config exists) before reading identity-specific
keys, and apply the same guard around the other block that accesses identity
config (the code currently around the second access of
submissionStrategy/.summary); use explicit checks or optional chaining combined
with a type/narrowing check so non-identity or unknown enum values won’t cause a
crash.
In `@components/waves/winners/drops/DefaultWaveWinnerDrop.tsx`:
- Line 102: In DefaultWaveWinnerDrop.tsx update the div with className
"tw-flex-whitespace-nowrap tw-flex tw-items-center tw-gap-x-4" to remove the
invalid utility and apply whitespace-nowrap correctly: replace
"tw-flex-whitespace-nowrap" with "tw-whitespace-nowrap" (keeping "tw-flex",
"tw-items-center", "tw-gap-x-4") so the nowrap behavior takes effect for the
stats row.
In `@components/waves/winners/identity/winnerIdentity.helpers.ts`:
- Around line 1-16: The module is missing exports for getWinnerIdentityProfile
and getWinnerIdentityFallbackValue which tests import; add and export these
functions from this file (either by implementing them here or re-exporting from
their existing implementation) so the module exports include
getWinnerIdentityProfile and getWinnerIdentityFallbackValue alongside
getWinnerVisibleMetadata; locate the file where those functions are defined or,
if they should be implemented here, create functions with those exact names and
export them (ensure signatures match what's expected by
__tests__/components/waves/winners/identity/winnerIdentity.helpers.test.ts).
In `@helpers/waves/wave-submission-experience.helpers.ts`:
- Around line 30-32: The current truthy check on submissionStrategy returns
WaveSubmissionExperience.IDENTITY for any non-null value; change the condition
to explicitly check the strategy type (submissionStrategy?.type ===
ApiWaveParticipationSubmissionStrategyType.Identity) so only Identity strategies
map to WaveSubmissionExperience.IDENTITY, updating the conditional that returns
WaveSubmissionExperience.IDENTITY (refer to submissionStrategy,
WaveSubmissionExperience.IDENTITY, and
ApiWaveParticipationSubmissionStrategyType.Identity).
In `@openapi.yaml`:
- Line 11542: The OpenAPI uses invalid OpenAPI 3.0.3 syntax `type: "null"`
(e.g., the submission_type field and ApiCreateDropPart schema) and must be
converted to the 3.0.3 nullable pattern: find every schema that currently uses
`anyOf: [{ type: "null" }, { $ref: ... }]` or `type: "null"` (there are ~36
instances including submission_type and ApiCreateDropPart) and replace each with
the equivalent `allOf: [{ $ref: ... }]` plus `nullable: true` on the schema that
referenced null; update each affected schema (e.g., submission_type,
ApiCreateDropPart and any other fields referencing `type: "null"`) so they use
the `allOf` + `nullable: true` pattern consistently to satisfy OpenAPI 3.0.3
validation.
---
Outside diff comments:
In `@components/utils/input/identity/IdentitySearch.tsx`:
- Around line 325-331: The label in IdentitySearch builds one long className
where unconditional duplicate utilities (peer-placeholder-shown:tw-top-1/2,
peer-placeholder-shown:-tw-translate-y-1/2, peer-focus:tw-text-primary-400)
override the error-specific classes; refactor the className so shared,
non-conflicting classes (including LABEL_CLASSES[size]) are in one string and
the state-specific utilities are applied conditionally — e.g. compute an
errorClasses string containing the error-specific peer-* rules and apply it when
error is true, removing the unconditional conflicting peer-* utilities so the
error classes actually take effect for the label in IdentitySearch (label
htmlFor={inputId}).
In `@components/waves/winners/WaveWinnersSmall.tsx`:
- Around line 126-133: The decision selector is passing the raw winners length
(point.winners.length) to WaveWinnersSmallDecisionSelector which can mismatch
the rendered list because the UI uses getRenderableWaveDecisionWinners to filter
out winners without valid drop data; update the decisionPoints mapping to
compute winnersCount by calling
getRenderableWaveDecisionWinners(point.winners).length for each point
(referencing WaveWinnersSmallDecisionSelector, decisionPoints, and
getRenderableWaveDecisionWinners) so the selector shows the filtered count; if
performance is a concern, consider memoizing or caching the filtered counts.
---
Nitpick comments:
In `@__tests__/components/brain/right-sidebar/BrainRightSidebarContent.test.tsx`:
- Around line 25-31: The mock for WaveIdentitySubmissionSpecs should verify the
useRing prop is false to prevent regressions; update the mock (the default
export for WaveIdentitySubmissionSpecs) to inspect props.useRing and push or
assert that it's false (e.g.,
captured.push(`identity:${props.wave.id}:useRing:${props.useRing}`) or throw if
props.useRing !== false) so tests fail if useRing becomes true; apply the same
change to the other mocked instance referenced similarly so both occurrences
validate props.useRing === false.
In
`@__tests__/components/waves/create-wave/drops/metadata/CreateWaveDropsMetadataRow.test.tsx`:
- Around line 17-46: Add a new unit test for CreateWaveDropsMetadataRow that
renders the component with errorMessage={null} (use item={{ key: "valid", type:
ApiWaveMetadataType.String }}, index=0 and jest.fn() for
onItemChange/onItemRemove) and assert that the reserved error text
(IDENTITY_SUBMISSION_RESERVED_METADATA_ERROR) is not present using
screen.queryByText(...).not.toBeInTheDocument(); this verifies the non-error
rendering path.
In
`@__tests__/components/waves/drops/participation/EndedParticipationDrop.test.tsx`:
- Around line 14-41: The mock for
"@/components/waves/drops/participation/ParticipationIdentityProfileCard"
duplicates the inline implementation instead of reusing the existing
ParticipationIdentityProfileCardMock; replace the inline factory with a call to
ParticipationIdentityProfileCardMock so the jest.mock returns the existing mock
function's render (ensuring the test-id and output ({profile.handle ??
profile.primary_address}) remain identical) and remove the duplicated JSX
implementation to keep tracking and behavior consistent.
In
`@__tests__/components/waves/leaderboard/identity/WaveLeaderboardIdentity.test.tsx`:
- Around line 20-127: Add a test to WaveLeaderboardIdentity.test.tsx that
verifies WaveLeaderboardIdentity returns null when the drop.wave.submission_type
is not ApiWaveParticipationSubmissionStrategyType.Identity: create a render with
a drop where wave.submission_type is e.g.
ApiWaveParticipationSubmissionStrategyType.Card (or any non-Identity value),
call render(<WaveLeaderboardIdentity drop={...} variant="condensed" />) and
assert that the component root/output is null by checking that known identity
elements (e.g., getByText("Identity") or getByTestId("identity-badges") /
getByTestId("identity-full-card")) are not present (use queryBy*). This mirrors
the non-identity test pattern used in WaveWinnerIdentity.test.tsx and ensures
WaveLeaderboardIdentity returns nothing for non-identity waves.
In `@__tests__/components/waves/winners/drops/DefaultWaveWinnerDrop.test.tsx`:
- Around line 60-80: Add a negative test that renders DefaultWaveWinnersDrop
with a winner whose drop.wave.submission_type is not "IDENTITY" and assert the
identity element is absent; specifically, in DefaultWaveWinnerDrop.test.tsx
create a new it block that renders <DefaultWaveWinnersDrop> with
winner.drop.wave.submission_type set to e.g. "LINK" (or undefined), use
onDropClick={jest.fn()}, then use screen.queryByTestId("identity") and expect it
toBeNull() or not.toBeInTheDocument(), ensuring the test references
DefaultWaveWinnersDrop and the submission_type field so behavior for
non-identity waves is covered.
In `@__tests__/components/waves/winners/drops/WaveWinnersDrops.test.tsx`:
- Around line 44-57: The test relies on publicEnv.NODE_ENV being
non-"production" so it should mock the exported publicEnv used by the
WaveWinnersDrops component before rendering; update the test to jest.mock the
module that exports publicEnv (or override the publicEnv export) so
publicEnv.NODE_ENV = "development" (or any non-"production" value) for the
duration of the test, then render WaveWinnersDrops and assert the dev warning;
ensure you restore/clear the mock between tests so other specs using
WaveWinnersDrops are not affected.
In `@__tests__/components/waves/winners/podium/WavePodiumItem.test.tsx`:
- Around line 58-61: The test in WavePodiumItem.test.tsx uses a brittle chain of
parentElement calls to find the click target; instead target a stable accessible
element (e.g., click the link itself returned by screen.getByRole("link", {
name: /alice/i }) or find the nearest ancestor with a role/test-id using
element.closest or within). Replace the multi-level parentElement chain used
with fireEvent.click(...) by directly clicking the link element or by using
element.closest('[role="..."]') / getByTestId on the WavePodiumItem test to
locate a stable container before calling fireEvent.click.
In `@components/utils/input/profile-search/CommonProfileSearchItems.tsx`:
- Around line 24-28: The property signature for onHighlightedOptionIdChange in
CommonProfileSearchItems.tsx contains a duplicate "| undefined" token; edit the
type declaration for readonly onHighlightedOptionIdChange? so it only includes a
single "| undefined" (i.e., remove the duplicated undefined) to clean up the
union type while keeping the optional modifier and preserving the existing
function type ((optionId: string | undefined) => void).
In `@components/waves/create-wave/drops/metadata/CreateWaveDropsMetadata.tsx`:
- Around line 78-86: The computation of reservedIdentityMetadataIdxs iterates
requiredMetadata on every render which can be expensive for large lists; wrap
this calculation in React.useMemo so it only recomputes when requiredMetadata or
the predicate changes (useMemo(() => { ... }, [requiredMetadata])) and do the
same for any other similar derived arrays; reference the
reservedIdentityMetadataIdxs variable, the requiredMetadata input, and the
isReservedIdentitySubmissionMetadataKey predicate when adding the dependency
array.
- Around line 88-101: The current getRowErrorMessage function returns only the
reserved identity error when an index is both reserved and non-unique, which
hides duplicate errors; update getRowErrorMessage (and related logic that
consumes it) to either combine both messages or return both error codes when
reservedIdentityMetadataIdxs and nonUniqueMetadataIdxs both include the index
and haveReservedIdentityMetadata and haveNonUniqueMetadata are true; reference
the constants IDENTITY_SUBMISSION_RESERVED_METADATA_ERROR and
NON_UNIQUE_METADATA_ERROR and ensure the consumer can accept a combined
string/array (or adjust UI rendering) so both errors are surfaced instead of
only returning the reserved error.
In `@components/waves/CreateDropIdentityField.tsx`:
- Around line 35-39: The avatar initial fallback can be empty when
identity.label is blank/whitespace; in CreateDropIdentityField (check the block
that returns when !identity.avatarUrl) replace the direct
identity.label.slice(0,1) usage with a safe extraction: trim identity.label,
take the first character if present, otherwise use a deterministic fallback
character (e.g., '?' or '•') and render that instead so the avatar never appears
empty.
- Around line 17-28: getHelperText currently has no default branch and can
return undefined for unexpected values of
ApiWaveParticipationIdentitySubmissionWhoCanBeSubmitted; update the
getHelperText function to include a default case that returns a safe fallback
string (e.g., a generic helper like "Select the identity to nominate." or
"Please choose an identity.") so the UI never loses helper text, or
alternatively throw/assert in the default if you want explicit failure;
reference the getHelperText function and the
ApiWaveParticipationIdentitySubmissionWhoCanBeSubmitted enum when making this
change.
In `@components/waves/CreateDropMetadataRow.tsx`:
- Line 47: Replace the string literal check for metadata.type === "NUMBER" with
the enum usage to match the rest of the component; update the condition to use
ApiWaveMetadataType.Number (the same enum used on lines that reference
ApiWaveMetadataType.Number) so all checks against metadata.type consistently use
the ApiWaveMetadataType enum (locate the check in CreateDropMetadataRow where
metadata.type is compared and swap the literal "NUMBER" for
ApiWaveMetadataType.Number).
In `@components/waves/utils/buildDropSubmissionMetadata.ts`:
- Around line 16-18: Trim and normalize the identity string before any
truthiness checks and before injecting it into metadata: replace direct checks
on identity with a normalizedIdentity = identity?.trim() (or equivalent) and use
normalizedIdentity for the early return (calling
convertMetadataToDropMetadata(metadata) when empty) and wherever identity is
injected later (the branch around line 29). Ensure all references in
buildDropSubmissionMetadata use normalizedIdentity so whitespace-only values are
treated as empty.
In `@components/waves/winners/drops/WaveWinnersDrops.tsx`:
- Around line 42-58: Duplicate warning <p> block (using invalidWinnersMessage
styling) appears twice in WaveWinnersDrops.tsx; extract it into a single
reusable JSX constant or small component (e.g., InvalidWinnersWarning or
renderInvalidWinnersWarning) and replace both inline <p> occurrences (including
where you check shouldShowInvalidWinnerWarning and the early return) with that
reusable reference so styling and content are maintained in one place.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8dbfbff1-9d09-4a66-9132-0a62443ca45f
⛔ Files ignored due to path filters (2)
generated/models/ApiWaveMin.tsis excluded by!**/generated/**generated/models/ObjectSerializer.tsis excluded by!**/generated/**
📒 Files selected for processing (98)
__tests__/components/DefaultWinnerDrop.test.tsx__tests__/components/brain/my-stream/MyStreamWaveChat.test.tsx__tests__/components/brain/my-stream/MyStreamWaveLeaderboard.test.tsx__tests__/components/brain/right-sidebar/BrainRightSidebarContent.test.tsx__tests__/components/memes/drops/MemeWinnerDrop.test.tsx__tests__/components/mobile-wrapper-dialog/MobileWrapperDialog.test.tsx__tests__/components/utils/input/identity/IdentitySearch.test.tsx__tests__/components/utils/input/profile-search/CommonProfileSearchItem.test.tsx__tests__/components/utils/input/profile-search/CommonProfileSearchItems.test.tsx__tests__/components/waves/CreateDrop.test.tsx__tests__/components/waves/CreateDropContent.identity.test.tsx__tests__/components/waves/CreateDropContent.utils.test.ts__tests__/components/waves/CreateDropIdentityField.test.tsx__tests__/components/waves/CreateDropMetadata.test.tsx__tests__/components/waves/CreateDropMetadataRow.test.tsx__tests__/components/waves/create-wave/drops/metadata/CreateWaveDropsMetadata.test.tsx__tests__/components/waves/create-wave/drops/metadata/CreateWaveDropsMetadataRow.test.tsx__tests__/components/waves/drop/SingleWaveDropContent.test.tsx__tests__/components/waves/drop/SingleWaveDropContentMetadata.test.tsx__tests__/components/waves/drops/OngoingParticipationDrop.test.tsx__tests__/components/waves/drops/identityDisplay.helpers.test.ts__tests__/components/waves/drops/participation/EndedParticipationDrop.test.tsx__tests__/components/waves/drops/participation/ParticipationIdentityProfileCard.test.tsx__tests__/components/waves/drops/participation/participationIdentityProfile.helpers.test.ts__tests__/components/waves/leaderboard/content/WaveLeaderboardDropContent.test.tsx__tests__/components/waves/leaderboard/gallery/WaveLeaderboardGalleryItem.test.tsx__tests__/components/waves/leaderboard/grid/WaveLeaderboardGridItem.test.tsx__tests__/components/waves/leaderboard/identity/WaveLeaderboardIdentity.test.tsx__tests__/components/waves/specs/WaveIdentitySubmissionSpecs.test.tsx__tests__/components/waves/winners/DefaultWaveWinnerDropSmall.test.tsx__tests__/components/waves/winners/MemesWaveWinnerDropSmall.test.tsx__tests__/components/waves/winners/drops/DefaultWaveWinnerDrop.test.tsx__tests__/components/waves/winners/drops/MemesWaveWinnerDrop.test.tsx__tests__/components/waves/winners/drops/WaveWinnersDrops.test.tsx__tests__/components/waves/winners/identity/WaveWinnerIdentity.test.tsx__tests__/components/waves/winners/identity/winnerIdentity.helpers.test.ts__tests__/components/waves/winners/podium/WavePodiumItem.test.tsx__tests__/helpers/waves/create-wave.validation.test.ts__tests__/helpers/waves/wave-submission-experience.helpers.test.ts__tests__/hooks/waves/useWaveDecisions.test.tscomponents/brain/my-stream/MyStreamWaveChat.tsxcomponents/brain/my-stream/MyStreamWaveLeaderboard.tsxcomponents/brain/right-sidebar/BrainRightSidebarContent.tsxcomponents/memes/drops/MemeWinnerDrop.tsxcomponents/mobile-wrapper-dialog/MobileWrapperDialog.tsxcomponents/utils/input/identity/IdentitySearch.tsxcomponents/utils/input/profile-search/CommonProfileSearchItem.tsxcomponents/utils/input/profile-search/CommonProfileSearchItems.tsxcomponents/utils/input/profile-search/getSelectableIdentity.tscomponents/waves/CreateDrop.tsxcomponents/waves/CreateDropContent.tsxcomponents/waves/CreateDropIdentityField.tsxcomponents/waves/CreateDropIdentityPickerModal.tsxcomponents/waves/CreateDropMetadata.tsxcomponents/waves/CreateDropMetadataRow.tsxcomponents/waves/create-wave/drops/metadata/CreateWaveDropsMetadata.tsxcomponents/waves/create-wave/drops/metadata/CreateWaveDropsMetadataRow.tsxcomponents/waves/create-wave/drops/submission-mode/CreateWaveDropsSubmissionMode.tsxcomponents/waves/drop/SingleWaveDropContent.tsxcomponents/waves/drop/SingleWaveDropContentMetadata.tsxcomponents/waves/drops/identity/IdentitySubmissionCompactCard.tsxcomponents/waves/drops/identity/WaveDropIdentity.tsxcomponents/waves/drops/identity/identitySubmissionCard.helpers.tscomponents/waves/drops/identityDisplay.helpers.tscomponents/waves/drops/participation/EndedParticipationDrop.tsxcomponents/waves/drops/participation/OngoingParticipationDrop.tsxcomponents/waves/drops/participation/ParticipationDropMetadata.tsxcomponents/waves/drops/participation/ParticipationIdentityProfileCard.tsxcomponents/waves/drops/participation/participationIdentityProfile.helpers.tscomponents/waves/drops/winner/DefaultWinnerDrop.tsxcomponents/waves/leaderboard/content/WaveLeaderboardDropContent.tsxcomponents/waves/leaderboard/gallery/WaveLeaderboardGalleryItem.tsxcomponents/waves/leaderboard/grid/WaveLeaderboardGridItem.tsxcomponents/waves/leaderboard/identity/WaveLeaderboardIdentity.tsxcomponents/waves/memes/submission/utils/buildPreviewDrop.tscomponents/waves/specs/WaveIdentitySubmissionSpecs.tsxcomponents/waves/utils/buildDropSubmissionMetadata.tscomponents/waves/utils/getOptimisticDrop.tscomponents/waves/utils/identitySubmissionMetadataValidation.tscomponents/waves/utils/identitySubmissionState.tscomponents/waves/winners/DefaultWaveWinnerDropSmall.tsxcomponents/waves/winners/MemesWaveWinnerDropSmall.tsxcomponents/waves/winners/WaveWinnersSmall.tsxcomponents/waves/winners/drops/DefaultWaveWinnerDrop.tsxcomponents/waves/winners/drops/MemesWaveWinnerDrop.tsxcomponents/waves/winners/drops/WaveWinnersDrops.tsxcomponents/waves/winners/identity/WaveWinnerIdentity.tsxcomponents/waves/winners/identity/winnerIdentity.helpers.tscomponents/waves/winners/podium/WavePodiumItem.tsxhelpers/waves/create-wave.validation.tshelpers/waves/identity-submission-metadata.tshelpers/waves/wave-decision.helpers.tshelpers/waves/wave-submission-experience.helpers.tshelpers/waves/wave-submission-strategy.helpers.tshooks/useNextMintDrop.tshooks/useWaveDropsSearch.tshooks/waves/useWaveDecisions.tsopenapi.yaml
✅ Files skipped from review due to trivial changes (10)
- components/brain/right-sidebar/BrainRightSidebarContent.tsx
- components/waves/utils/getOptimisticDrop.ts
- tests/helpers/waves/create-wave.validation.test.ts
- components/waves/leaderboard/grid/WaveLeaderboardGridItem.tsx
- tests/components/waves/drops/participation/participationIdentityProfile.helpers.test.ts
- components/waves/winners/identity/WaveWinnerIdentity.tsx
- helpers/waves/identity-submission-metadata.ts
- helpers/waves/wave-submission-strategy.helpers.ts
- components/waves/drops/participation/participationIdentityProfile.helpers.ts
- tests/helpers/waves/wave-submission-experience.helpers.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- helpers/waves/create-wave.validation.ts
- components/waves/create-wave/drops/submission-mode/CreateWaveDropsSubmissionMode.tsx
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
components/waves/winners/drops/header/WaveWinnersDropOutcome.tsx (1)
61-106:⚠️ Potential issue | 🟡 MinorReact keys may collide if duplicate values exist.
The keys for rendered items are derived solely from outcome values:
NIC-${nicOutcome.value}(Line 63)REP-${repOutcome.category}-${repOutcome.value}(Line 77)MANUAL-${outcome.description}(Line 99)If two awards share the same value/description, React will see duplicate keys, potentially causing incorrect reconciliation or rendering bugs.
🛡️ Proposed fix using index to ensure unique keys
- {nicOutcomes.map((nicOutcome) => ( + {nicOutcomes.map((nicOutcome, index) => ( <span - key={`NIC-${nicOutcome.value}`} + key={`NIC-${index}-${nicOutcome.value}`} className="tw-flex tw-items-center tw-gap-1.5" >- {repOutcomes.map((repOutcome) => ( + {repOutcomes.map((repOutcome, index) => ( <span - key={`REP-${repOutcome.category}-${repOutcome.value}`} + key={`REP-${index}-${repOutcome.category}-${repOutcome.value}`} className="tw-flex tw-gap-1.5" >- {manualOutcomes.map((outcome) => ( + {manualOutcomes.map((outcome, index) => ( <span - key={`MANUAL-${outcome.description}`} + key={`MANUAL-${index}-${outcome.description}`} className="tw-flex tw-gap-1.5" >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/winners/drops/header/WaveWinnersDropOutcome.tsx` around lines 61 - 106, The map keys for NIC, REP, and MANUAL lists in WaveWinnersDropOutcome (nicOutcomes, repOutcomes, manualOutcomes) can collide because they rely only on value/description; update each .map to produce stable unique keys by combining the existing identifier with the map index or another unique property (e.g., use `NIC-${nicOutcome.value}-${index}`, `REP-${repOutcome.category}-${repOutcome.value}-${index}`, `MANUAL-${outcome.description}-${index}`) so keys are deterministic and unique during reconciliation; ensure you reference the map callback index parameter for nicOutcomes, repOutcomes, and manualOutcomes when constructing the key.components/waves/CreateDropDropModeToggle.tsx (1)
63-83:⚠️ Potential issue | 🟠 MajorSwapped label text in restriction messages.
The
isChatparameter appears to be inverted in the messages. WhenisChatistrue, it should show "chat" actions, but the ternary returns "drop" instead:
- Line 66:
isChat ? "drop" : "chat"— should beisChat ? "chat" : "drop"- Line 69: Same issue
- Line 72: Same issue
- Line 82:
isChat ? "Drop" : "Chat"— same inversionThis will display incorrect error messages to users (e.g., "Please log in to drop" when they're trying to chat).
Proposed fix
case ChatRestriction.NOT_LOGGED_IN: case SubmissionRestriction.NOT_LOGGED_IN: - return `Please log in to ${isChat ? "drop" : "chat"}`; + return `Please log in to ${isChat ? "chat" : "drop"}`; case ChatRestriction.PROXY_USER: case SubmissionRestriction.PROXY_USER: - return `Proxy users cannot ${isChat ? "drop" : "chat"}`; + return `Proxy users cannot ${isChat ? "chat" : "drop"}`; case ChatRestriction.NO_PERMISSION: case SubmissionRestriction.NO_PERMISSION: - return `You don't have permission to ${isChat ? "drop" : "chat"}`; + return `You don't have permission to ${isChat ? "chat" : "drop"}`; case ChatRestriction.DISABLED: return "Chat is currently disabled"; case SubmissionRestriction.NOT_STARTED: return "Drop submissions haven't started yet"; case SubmissionRestriction.ENDED: return "Drop submissions have ended"; case SubmissionRestriction.MAX_DROPS_REACHED: return "You have reached the maximum number of drops allowed"; default: - return `${isChat ? "Drop" : "Chat"} mode is unavailable`; + return `${isChat ? "Chat" : "Drop"} mode is unavailable`;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/CreateDropDropModeToggle.tsx` around lines 63 - 83, The restriction messages in CreateDropDropModeToggle are using the isChat flag inverted; update all ternary usages inside the switch handling ChatRestriction/SubmissionRestriction (cases for NOT_LOGGED_IN, PROXY_USER, NO_PERMISSION, and the default case) so they read isChat ? "chat" : "drop" (and isChat ? "Chat" : "Drop" for the capitalized default) instead of the current inverted versions; ensure you update the message strings in the switch within the component/function where isChat is referenced so users see "chat" when isChat is true and "drop" when false.
♻️ Duplicate comments (2)
components/waves/CreateDropContent.tsx (1)
700-706:⚠️ Potential issue | 🟠 MajorIdentity-only nominations still can't submit without extra content.
buildDropSubmissionMetadata()now injects the reserved identity entry here, but Line 544 still deriveshasMetadatafrom user-entered metadata only. That leavescanSubmitfalse on Line 590 and bothensurePartsWithFallback(...)paths empty on Line 727 and Line 805, so an identity-only nomination still no-ops unless the user adds unrelated body text or metadata.Suggested fix
- const hasMetadata = useMemo(() => hasMetadataContent(metadata), [metadata]); + const hasMetadata = useMemo( + () => + hasMetadataContent(metadata) || + (isIdentitySubmissionExperience && + isDropMode && + selectedIdentity !== null), + [isDropMode, isIdentitySubmissionExperience, metadata, selectedIdentity] + );Also applies to: 731-731, 766-766, 816-816
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/CreateDropContent.tsx` around lines 700 - 706, The submission flow treats identity-only nominations as having no metadata because hasMetadata is derived from the raw user-entered metadata instead of the built submission metadata; update the code paths that rely on metadata to use the getSubmissionMetadata() result (call getSubmissionMetadata() once and reuse it) so hasMetadata and canSubmit are computed from buildDropSubmissionMetadata output (which includes the reserved identity), and ensure both branches that call ensurePartsWithFallback(...) (the calls near ensurePartsWithFallback on the CreateDropContent component) pass the built submission metadata/parts so identity-only nominations produce non-empty parts and canSubmit becomes true when appropriate.openapi.yaml (1)
8000-8003:⚠️ Potential issue | 🟠 MajorReplace
type: "null"unions with OpenAPI 3.0.3 nullable syntax.Line 8002, Line 8488, Line 11080, Line 11521, and Line 11780 use
type: "null", which is not valid in OpenAPI 3.0.3. This repeats the same validator-breaking pattern previously flagged.🔧 Suggested pattern (apply to each affected field)
- submission_strategy: - anyOf: - - type: "null" - - $ref: "#/components/schemas/ApiWaveParticipationSubmissionStrategy" + submission_strategy: + allOf: + - $ref: "#/components/schemas/ApiWaveParticipationSubmissionStrategy" + nullable: true#!/bin/bash set -euo pipefail python -m pip install --quiet openapi-spec-validator==0.7.1 pyyaml python - <<'PY' import yaml from openapi_spec_validator import validate_spec with open("openapi.yaml", "r", encoding="utf-8") as f: spec = yaml.safe_load(f) try: validate_spec(spec) print("SPEC_VALID") except Exception as e: print("SPEC_INVALID") print(type(e).__name__) print(str(e).splitlines()[0]) print("\nOccurrences of type: \"null\":") with open("openapi.yaml", "r", encoding="utf-8") as f: for i, line in enumerate(f, 1): if 'type: "null"' in line: print(i) PYAlso applies to: 8487-8489, 11079-11081, 11520-11522, 11779-11781
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi.yaml` around lines 8000 - 8003, The schema uses an anyOf union with type: "null" (e.g., submission_strategy referencing ApiWaveParticipationSubmissionStrategy) which is invalid in OpenAPI 3.0.3; replace each anyOf list that contains - type: "null" and - $ref: "#/components/schemas/SomeSchema" by removing the null member and adding nullable: true to the referenced schema usage (i.e., change the field to reference the schema and set nullable: true on that field or in the referenced schema definition), applying this fix for submission_strategy and the other occurrences that reference ApiWaveParticipationSubmissionStrategy (and similar schemas) so the spec conforms to OpenAPI 3.0.3 nullable semantics.
🧹 Nitpick comments (12)
components/waves/drops/winner/WinnerDropBadge.tsx (1)
50-52: Consider using Tailwind color palette for consistency.The hardcoded hex value
#ffc107works, but it deviates from the pattern used for other ranks (2, 3, default) which use Tailwind's named colors liketw-text-iron-300andtw-text-amber-500. If this specific gold shade is intentional for branding, consider defining it as a custom color in your Tailwind config (e.g.,tw-text-gold-500) for maintainability.Otherwise, the color change looks fine for visual styling purposes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/winner/WinnerDropBadge.tsx` around lines 50 - 52, The color values in WinnerDropBadge (variables colorClasses and textColorClass) use a hardcoded hex (`#ffc107`); replace this with the Tailwind palette by using an existing class (e.g., tw-text-amber-500 and matching tw-bg-amber-500/10 or tw-border-amber-500/20) or, if the hex is a deliberate brand gold, add a custom color alias in the Tailwind config (e.g., "gold-500") and update colorClasses and textColorClass to use that new tw-text-gold-500/tw-bg-gold-500/10/tw-border-gold-500/20 instead of the literal hex so styling stays consistent and maintainable.components/waves/drops/WaveDropActionsOptions.tsx (1)
56-70: Consider centralizing shared action-tooltip config.This tooltip config is now very close to the one in
WaveDropActionsOpen.tsx; extracting shared defaults would reduce drift.♻️ Refactor sketch
+// e.g. components/waves/drops/waveActionTooltip.ts +export const WAVE_ACTION_TOOLTIP_PROPS = { + place: "top-end" as const, + offset: 8, + opacity: 1, + positionStrategy: "fixed" as const, + style: { + padding: "4px 8px", + borderRadius: "6px", + boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)", + backgroundColor: "#1F2937", + color: "white", + zIndex: 10000, + pointerEvents: "none" as const, + }, +};- <Tooltip - id={`delete-${drop.id}`} - place="top-end" - offset={8} - opacity={1} - positionStrategy="fixed" - style={{ ... }} - > + <Tooltip id={`delete-${drop.id}`} {...WAVE_ACTION_TOOLTIP_PROPS}>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/WaveDropActionsOptions.tsx` around lines 56 - 70, The Tooltip styling/props in WaveDropActionsOptions.tsx are duplicated with WaveDropActionsOpen.tsx; extract the shared configuration (props like place, offset, opacity, positionStrategy, style including borderRadius, boxShadow, backgroundColor, color, padding, zIndex, pointerEvents) into a single exported constant or helper (e.g., ACTION_TOOLTIP_PROPS or getActionTooltipProps) and import it into both files, then spread those defaults into each Tooltip usage while keeping unique props like id; update references to the Tooltip calls in WaveDropActionsOptions.tsx and WaveDropActionsOpen.tsx to use the shared constant.components/waves/drops/participation/EndedParticipationDrop.tsx (1)
90-92: Consider inlining or removinggetDropLocationBackground.The function now always returns the same value regardless of any conditions. It could be replaced with a constant or inlined directly where used.
♻️ Optional simplification
- const getDropLocationBackground = () => { - return "tw-bg-iron-950 tw-ring-1 tw-ring-inset tw-ring-iron-800"; - }; + const dropLocationBackground = + "tw-bg-iron-950 tw-ring-1 tw-ring-inset tw-ring-iron-800"; const dropBackgroundClass = isActiveDrop ? "tw-bg-[`#3CCB7F`]/10" - : getDropLocationBackground(); + : dropLocationBackground;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/participation/EndedParticipationDrop.tsx` around lines 90 - 92, The helper getDropLocationBackground always returns a fixed string; remove the function and replace its usages in EndedParticipationDrop with a single constant or inline string to simplify the code—e.g., define a const DROP_LOCATION_BG = "tw-bg-iron-950 tw-ring-1 tw-ring-inset tw-ring-iron-800" near the component (or directly inline the string where getDropLocationBackground() is called) and update all references to use DROP_LOCATION_BG (or the literal) instead of calling getDropLocationBackground().components/memes/drops/MemeWinnerDrop.tsx (1)
28-30: Same redundant wrapper as inMemesWaveWinnerDrop.tsx.Consider calling
getRankHoverBorderClassdirectly instead of through this pass-through function.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/memes/drops/MemeWinnerDrop.tsx` around lines 28 - 30, The getRankHoverClass wrapper simply forwards to getRankHoverBorderClass and is redundant; remove getRankHoverClass and update any callers within this component (and other components importing it) to call getRankHoverBorderClass directly, or re-export getRankHoverBorderClass if a stable symbol is needed; ensure imports/exports are adjusted so no references to getRankHoverClass remain and run the build/tests to confirm nothing breaks.components/memes/drops/MemesLeaderboardDropCard.tsx (1)
11-31: Consider simplifying by delegating rank logic entirely to the helper.The function checks for ranks 1, 2, 3 individually, but
getRankHoverBorderClassalready handles this via a switch. You could passrankdirectly:♻️ Suggested simplification
const getBorderClasses = (drop: ExtendedDrop) => { const rank = typeof drop.rank === "number" && drop.rank <= 3 ? drop.rank : null; const baseClasses = "tw-rounded-xl tw-border tw-border-solid tw-border-iron-800 tw-transition-all tw-duration-200 tw-ease-out tw-overflow-hidden"; - if (rank === 1) { - return `${baseClasses} ${getRankHoverBorderClass(1)}`; - } - - if (rank === 2) { - return `${baseClasses} ${getRankHoverBorderClass(2)}`; - } - - if (rank === 3) { - return `${baseClasses} ${getRankHoverBorderClass(3)}`; - } - - return `${baseClasses} desktop-hover:hover:tw-border-iron-700`; + return `${baseClasses} ${getRankHoverBorderClass(rank)}`; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/memes/drops/MemesLeaderboardDropCard.tsx` around lines 11 - 31, getBorderClasses currently duplicates rank branching (checking rank 1/2/3) before calling getRankHoverBorderClass; simplify by computing rank (null if not 1-3) and passing it directly to getRankHoverBorderClass so the helper handles the switch logic. Update getBorderClasses to derive rank as shown (typeof drop.rank === "number" && drop.rank <= 3 ? drop.rank : null) and, instead of separate ifs for 1/2/3, return `${baseClasses} ${getRankHoverBorderClass(rank)}` (falling back to the existing default class when getRankHoverBorderClass returns a falsy/undefined value).components/memes/drops/MemeParticipationDrop.tsx (1)
33-57: Same simplification opportunity asMemesLeaderboardDropCard.The individual rank checks can be consolidated by passing
rankdirectly togetRankHoverBorderClass. Note: the active drop styling must remain separate.♻️ Suggested simplification
const getBorderClasses = (drop: ExtendedDrop, isActiveDrop: boolean) => { const rank = typeof drop.rank === "number" && drop.rank <= 3 ? drop.rank : null; const baseClasses = "tw-rounded-xl tw-border tw-border-solid tw-border-iron-800 tw-transition-all tw-duration-200 tw-ease-out tw-overflow-hidden"; if (isActiveDrop) { return `${baseClasses} desktop-hover:hover:tw-border-[`#3CCB7F`]/40 tw-bg-[`#3CCB7F`]/5`; } - if (rank === 1) { - return `${baseClasses} ${getRankHoverBorderClass(1)}`; - } - - if (rank === 2) { - return `${baseClasses} ${getRankHoverBorderClass(2)}`; - } - - if (rank === 3) { - return `${baseClasses} ${getRankHoverBorderClass(3)}`; - } - - return `${baseClasses} tw-border-iron-800`; + return `${baseClasses} ${getRankHoverBorderClass(rank)}`; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/memes/drops/MemeParticipationDrop.tsx` around lines 33 - 57, The function getBorderClasses duplicates rank-specific branches; keep the isActiveDrop branch as-is, then compute rank as you already do and, if rank is non-null, return `${baseClasses} ${getRankHoverBorderClass(rank)}` instead of separate ifs for 1/2/3; otherwise return the default `${baseClasses} tw-border-iron-800`. This touches getBorderClasses, the rank variable from ExtendedDrop, and getRankHoverBorderClass.components/waves/drops/dropRankStyles.ts (2)
24-52: Redundantnullanddefaultcases.In both
getRankHoverBorderClassandgetRankHoverRingClass, thecase null:anddefault:branches return identical values. You can remove the explicitnullcase and let it fall through todefault.♻️ Suggested simplification
export const getRankHoverBorderClass = (rank: number | null): string => { switch (rank) { case 1: return GOLD_BORDER_HOVER; case 2: return SILVER_BORDER_HOVER; case 3: return BRONZE_BORDER_HOVER; - case null: - return "desktop-hover:hover:tw-border-iron-700"; default: return "desktop-hover:hover:tw-border-iron-700"; } };Same for
getRankHoverRingClass.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/dropRankStyles.ts` around lines 24 - 52, Both getRankHoverBorderClass and getRankHoverRingClass contain redundant case null branches that return the same value as default; remove the explicit "case null:" branch from each function and let the switch fall through to the existing default branch so null and other non-1/2/3 values use the default hover classes, preserving the function signatures and returned strings.
13-22:getRankStaticBorderClassswitch is a no-op.All cases (1, 2, 3, null, default) return the same value. The switch adds complexity without functionality.
♻️ Suggested simplification
-export const getRankStaticBorderClass = (rank: number | null): string => { - switch (rank) { - case 1: - case 2: - case 3: - case null: - default: - return "tw-border-iron-800"; - } -}; +export const getRankStaticBorderClass = (_rank: number | null): string => { + return "tw-border-iron-800"; +};If different ranks should have distinct static borders in the future, the switch can be reintroduced then.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/dropRankStyles.ts` around lines 13 - 22, The switch in getRankStaticBorderClass is redundant because every branch returns the same string; replace the entire switch with a single return "tw-border-iron-800" in the getRankStaticBorderClass function (or, if you expect per-rank behavior later, collapse to a simple return and add a // TODO note to reintroduce a switch when different values are needed) to remove the no-op complexity.components/waves/winners/drops/MemesWaveWinnerDrop.tsx (1)
43-45: Redundant wrapper function.
getRankHoverClasssimply delegates togetRankHoverBorderClasswithout any transformation. Consider callinggetRankHoverBorderClassdirectly at line 94.♻️ Suggested removal
-const getRankHoverClass = (place: number | null): string => { - return getRankHoverBorderClass(place); -}; -Then at line 94:
- className={`tw-overflow-hidden tw-rounded-xl tw-border tw-border-solid tw-border-iron-800 tw-bg-iron-950 tw-transition-all tw-duration-200 tw-ease-out ${getRankHoverClass(winner.place)}`} + className={`tw-overflow-hidden tw-rounded-xl tw-border tw-border-solid tw-border-iron-800 tw-bg-iron-950 tw-transition-all tw-duration-200 tw-ease-out ${getRankHoverBorderClass(winner.place)}`}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/winners/drops/MemesWaveWinnerDrop.tsx` around lines 43 - 45, The function getRankHoverClass is a redundant wrapper around getRankHoverBorderClass; delete getRankHoverClass and replace all calls to getRankHoverClass(...) with direct calls to getRankHoverBorderClass(...), and remove any related unused imports/exports or type declarations tied only to getRankHoverClass to avoid linter errors.components/waves/drops/participation/ParticipationIdentityProfileCard.tsx (1)
156-183: Consider whether NIC and Rep links should go to more specific routes.Currently, NIC and Rep both link to
rootHref(the profile root), while TDH links to/collectedand xTDH links to/xtdh. If there are dedicated pages for NIC or Rep stats, consider linking to those for better UX consistency.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/participation/ParticipationIdentityProfileCard.tsx` around lines 156 - 183, The NIC and Rep IdentityStatLink components currently use rootHref; update their hrefs to point to more specific routes (e.g., `${rootHref}/nic` and `${rootHref}/rep`) or to the actual named routes/pages for NIC and Rep stats if those exist; modify the IdentityStatLink usages for profile.cic and profile.rep to use those specific hrefs (symbols: IdentityStatLink, rootHref, profile.cic, profile.rep) and verify navigation works and tests/links referencing these routes are updated accordingly.components/waves/CreateDrop.tsx (2)
223-224: Simplified retry configuration works but loses exponential backoff benefits.The simplified
retryDelay: (failureCount) => failureCount * 1000provides linear backoff (1s, 2s, 3s). Consider if exponential backoff would be more appropriate for API calls to avoid overwhelming a recovering server. That said, for only 3 retries the difference is minimal.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/CreateDrop.tsx` around lines 223 - 224, The retry/backoff here (retry: (failureCount) => failureCount < 3 and retryDelay: (failureCount) => failureCount * 1000) uses linear delays; replace retryDelay with an exponential backoff (optionally capped and with small jitter) to better space retries for API calls. Update the retryDelay implementation in CreateDrop.tsx to something like: retryDelay: (failureCount) => Math.min(30000, Math.pow(2, failureCount) * 1000 + Math.random() * 1000) while keeping the existing retry predicate (failureCount < 3).
267-267: Address SonarCloud finding: consider removing void operator.Static analysis flagged the use of
voidoperator. Whilevoid processNextDrop()is a valid pattern to explicitly discard the Promise, it can be replaced with a cleaner approach that satisfies lint rules and maintains clarity about intentionally not awaiting.♻️ Proposed fix to address static analysis finding
// Process immediately - avoids state update timing issues - void processNextDrop(); + processNextDrop().catch(() => { + // Errors are already handled within processNextDrop + });Alternatively, if the project's ESLint config allows it:
// Process immediately - avoids state update timing issues + // eslint-disable-next-line `@typescript-eslint/no-floating-promises` processNextDrop();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/CreateDrop.tsx` at line 267, SonarCloud flagged the use of the void operator on processNextDrop(); remove the void and either await the call inside an async function or explicitly handle the returned Promise to avoid a floating promise: locate the call to processNextDrop in CreateDrop.tsx and replace the void-prefixed call with a proper awaited invocation in its async caller, or call processNextDrop().catch(...) to surface errors (or add a specific ESLint exemption comment if your project policy prefers), ensuring processNextDrop remains referenced by name and any errors are handled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/waves/CreateDropContent.tsx`:
- Around line 417-423: The code mutates refs (prevIsDropModeRef and
dropModeSessionEpochRef) during render based on the isDropMode prop, which can
run in aborted/double-render paths; move that mutation into a side-effect so the
epoch only advances on committed updates: replace the inline render-time if
block with a useEffect that reads prevIsDropModeRef.current and isDropMode,
increments dropModeSessionEpochRef.current when transitioning from true ->
false, and then updates prevIsDropModeRef.current = isDropMode inside the same
effect; ensure the same ref-update pattern is applied wherever scope keys are
computed (the scope key usages referenced around the scope-key generation
locations) so they stay in sync with committed UI state.
In `@components/waves/drops/participation/ParticipationDropFooter.tsx`:
- Line 12: The voteAction prop (used in ParticipationDropFooter) can be a
boolean because ReactNode includes booleans, so the current render check treats
false as a present action and mounts the empty primary-action container; update
the render logic around the voteAction usage (the prop named voteAction and the
JSX that renders the primary-action container / bordered row) to explicitly
filter out booleans (e.g., only treat voteAction as present when it is not
null/undefined and typeof voteAction !== 'boolean') so the container is not
mounted for false values.
In
`@components/waves/drops/participation/ratings/ParticipationDropRatingsTotalSection.tsx`:
- Around line 38-53: Replace the non-focusable <span> trigger for the
VoteBreakdownTooltip with a focusable, semantic control: swap the span that has
className "tw-cursor-help tw-whitespace-nowrap tw-font-normal tw-text-iron-400"
and data-tooltip-id={`total-rating-${drop.id}`} to a styled <button
type="button"> preserving the same className and data-tooltip-id so the Tooltip
(id={`total-rating-${drop.id}`} / VoteBreakdownTooltip) continues to work;
ensure the button keeps the same visible label ({votingLabel}
{WAVE_VOTE_STATS_LABELS.TOTAL}), add an accessible attribute such as
aria-describedby={`total-rating-${drop.id}`} (or aria-label if preferred) so
keyboard users can invoke the tooltip, and retain existing tooltip props
(TOOLTIP_STYLES, position, etc.).
In
`@components/waves/drops/participation/ratings/ParticipationDropRatingsVoterSection.tsx`:
- Around line 27-48: The wrapper div (with tooltip id
`participation-rater-${rater.profile.id}`) is rendered even when
`rater.profile.pfp` is falsy, producing empty space and a phantom tooltip;
update the render logic in ParticipationDropRatingsVoterSection so the entire
rater item (the outer div with className "tw-relative..." and the tooltip
binding) is only rendered when there is visible content — either (a) render a
fallback avatar using the rater's display name/handle (derive initials from
`rater.profile.displayName` or `rater.profile.username`) and keep the same
classes/tooltip id, or (b) skip rendering the whole rater element when
`rater.profile.pfp` is missing; ensure any calls to `getScaledImageUri` /
`ImageScale.W_AUTO_H_50` remain only in the image branch and that `Link` and
tooltip id are attached to the visible element so layout and hover behavior are
correct.
In `@components/waves/drops/WaveDrop.tsx`:
- Around line 61-67: RANK_STYLES.default is never used when you index with a
numeric or null `rank`, so `rankClass` can be undefined for null/out-of-range
ranks; update the lookup where `rankClass` is computed (and the similar logic at
the other occurrence around lines 97-98) to explicitly fallback to
RANK_STYLES.default — e.g., use a defensive expression like: if rank is 1/2/3
use RANK_STYLES[rank] else use RANK_STYLES.default (or use RANK_STYLES[rank] ??
RANK_STYLES.default), keeping references to RANK_STYLES, rank, rankClass and
getRankHoverRingClass.
In `@components/waves/drops/WaveDropRatings.tsx`:
- Around line 57-65: The tooltip and accessible label are currently attached to
the non-focusable wrapper <div> instead of the actual profile <Link>, and the
no-PFP branch renders an unnamed link; move the tooltip attributes and aria
labeling onto the interactive element: attach data-tooltip-id={tooltipId} and a
descriptive aria-label/aria-describedby to the <Link> that uses raterHref (and
ensure the Link has a visible focus style), remove those attributes from the
wrapper <div>, and for the no-PFP branch provide a meaningful accessible name
(e.g., aria-label={`View profile of ${rater.name}`} or use tooltipId) so
keyboard and screen-reader users can identify and trigger the tooltip; this
change applies to the blocks rendering each rater in WaveDropRatings (the
wrapper <div> and its child <Link>, and the no-PFP branch).
In `@openapi.yaml`:
- Around line 11076-11081: The OpenAPI docs currently conflict: the
submission_strategy property (anyOf referencing
ApiWaveParticipationSubmissionStrategy) is described as clearable by setting
null but elsewhere (lines around the immutable note) states it is immutable
after creation; pick the immutable behavior and make the docs consistent by
removing "null" as an allowed value from the anyOf for submission_strategy and
updating the description to state it cannot be changed after creation (must
match existing value), or alternatively (if you want clearable) remove the
immutable note—ensure the change is applied to the submission_strategy field and
the ApiWaveParticipationSubmissionStrategy references so both places convey the
same rule.
---
Outside diff comments:
In `@components/waves/CreateDropDropModeToggle.tsx`:
- Around line 63-83: The restriction messages in CreateDropDropModeToggle are
using the isChat flag inverted; update all ternary usages inside the switch
handling ChatRestriction/SubmissionRestriction (cases for NOT_LOGGED_IN,
PROXY_USER, NO_PERMISSION, and the default case) so they read isChat ? "chat" :
"drop" (and isChat ? "Chat" : "Drop" for the capitalized default) instead of the
current inverted versions; ensure you update the message strings in the switch
within the component/function where isChat is referenced so users see "chat"
when isChat is true and "drop" when false.
In `@components/waves/winners/drops/header/WaveWinnersDropOutcome.tsx`:
- Around line 61-106: The map keys for NIC, REP, and MANUAL lists in
WaveWinnersDropOutcome (nicOutcomes, repOutcomes, manualOutcomes) can collide
because they rely only on value/description; update each .map to produce stable
unique keys by combining the existing identifier with the map index or another
unique property (e.g., use `NIC-${nicOutcome.value}-${index}`,
`REP-${repOutcome.category}-${repOutcome.value}-${index}`,
`MANUAL-${outcome.description}-${index}`) so keys are deterministic and unique
during reconciliation; ensure you reference the map callback index parameter for
nicOutcomes, repOutcomes, and manualOutcomes when constructing the key.
---
Duplicate comments:
In `@components/waves/CreateDropContent.tsx`:
- Around line 700-706: The submission flow treats identity-only nominations as
having no metadata because hasMetadata is derived from the raw user-entered
metadata instead of the built submission metadata; update the code paths that
rely on metadata to use the getSubmissionMetadata() result (call
getSubmissionMetadata() once and reuse it) so hasMetadata and canSubmit are
computed from buildDropSubmissionMetadata output (which includes the reserved
identity), and ensure both branches that call ensurePartsWithFallback(...) (the
calls near ensurePartsWithFallback on the CreateDropContent component) pass the
built submission metadata/parts so identity-only nominations produce non-empty
parts and canSubmit becomes true when appropriate.
In `@openapi.yaml`:
- Around line 8000-8003: The schema uses an anyOf union with type: "null" (e.g.,
submission_strategy referencing ApiWaveParticipationSubmissionStrategy) which is
invalid in OpenAPI 3.0.3; replace each anyOf list that contains - type: "null"
and - $ref: "#/components/schemas/SomeSchema" by removing the null member and
adding nullable: true to the referenced schema usage (i.e., change the field to
reference the schema and set nullable: true on that field or in the referenced
schema definition), applying this fix for submission_strategy and the other
occurrences that reference ApiWaveParticipationSubmissionStrategy (and similar
schemas) so the spec conforms to OpenAPI 3.0.3 nullable semantics.
---
Nitpick comments:
In `@components/memes/drops/MemeParticipationDrop.tsx`:
- Around line 33-57: The function getBorderClasses duplicates rank-specific
branches; keep the isActiveDrop branch as-is, then compute rank as you already
do and, if rank is non-null, return `${baseClasses}
${getRankHoverBorderClass(rank)}` instead of separate ifs for 1/2/3; otherwise
return the default `${baseClasses} tw-border-iron-800`. This touches
getBorderClasses, the rank variable from ExtendedDrop, and
getRankHoverBorderClass.
In `@components/memes/drops/MemesLeaderboardDropCard.tsx`:
- Around line 11-31: getBorderClasses currently duplicates rank branching
(checking rank 1/2/3) before calling getRankHoverBorderClass; simplify by
computing rank (null if not 1-3) and passing it directly to
getRankHoverBorderClass so the helper handles the switch logic. Update
getBorderClasses to derive rank as shown (typeof drop.rank === "number" &&
drop.rank <= 3 ? drop.rank : null) and, instead of separate ifs for 1/2/3,
return `${baseClasses} ${getRankHoverBorderClass(rank)}` (falling back to the
existing default class when getRankHoverBorderClass returns a falsy/undefined
value).
In `@components/memes/drops/MemeWinnerDrop.tsx`:
- Around line 28-30: The getRankHoverClass wrapper simply forwards to
getRankHoverBorderClass and is redundant; remove getRankHoverClass and update
any callers within this component (and other components importing it) to call
getRankHoverBorderClass directly, or re-export getRankHoverBorderClass if a
stable symbol is needed; ensure imports/exports are adjusted so no references to
getRankHoverClass remain and run the build/tests to confirm nothing breaks.
In `@components/waves/CreateDrop.tsx`:
- Around line 223-224: The retry/backoff here (retry: (failureCount) =>
failureCount < 3 and retryDelay: (failureCount) => failureCount * 1000) uses
linear delays; replace retryDelay with an exponential backoff (optionally capped
and with small jitter) to better space retries for API calls. Update the
retryDelay implementation in CreateDrop.tsx to something like: retryDelay:
(failureCount) => Math.min(30000, Math.pow(2, failureCount) * 1000 +
Math.random() * 1000) while keeping the existing retry predicate (failureCount <
3).
- Line 267: SonarCloud flagged the use of the void operator on
processNextDrop(); remove the void and either await the call inside an async
function or explicitly handle the returned Promise to avoid a floating promise:
locate the call to processNextDrop in CreateDrop.tsx and replace the
void-prefixed call with a proper awaited invocation in its async caller, or call
processNextDrop().catch(...) to surface errors (or add a specific ESLint
exemption comment if your project policy prefers), ensuring processNextDrop
remains referenced by name and any errors are handled.
In `@components/waves/drops/dropRankStyles.ts`:
- Around line 24-52: Both getRankHoverBorderClass and getRankHoverRingClass
contain redundant case null branches that return the same value as default;
remove the explicit "case null:" branch from each function and let the switch
fall through to the existing default branch so null and other non-1/2/3 values
use the default hover classes, preserving the function signatures and returned
strings.
- Around line 13-22: The switch in getRankStaticBorderClass is redundant because
every branch returns the same string; replace the entire switch with a single
return "tw-border-iron-800" in the getRankStaticBorderClass function (or, if you
expect per-rank behavior later, collapse to a simple return and add a // TODO
note to reintroduce a switch when different values are needed) to remove the
no-op complexity.
In `@components/waves/drops/participation/EndedParticipationDrop.tsx`:
- Around line 90-92: The helper getDropLocationBackground always returns a fixed
string; remove the function and replace its usages in EndedParticipationDrop
with a single constant or inline string to simplify the code—e.g., define a
const DROP_LOCATION_BG = "tw-bg-iron-950 tw-ring-1 tw-ring-inset
tw-ring-iron-800" near the component (or directly inline the string where
getDropLocationBackground() is called) and update all references to use
DROP_LOCATION_BG (or the literal) instead of calling
getDropLocationBackground().
In `@components/waves/drops/participation/ParticipationIdentityProfileCard.tsx`:
- Around line 156-183: The NIC and Rep IdentityStatLink components currently use
rootHref; update their hrefs to point to more specific routes (e.g.,
`${rootHref}/nic` and `${rootHref}/rep`) or to the actual named routes/pages for
NIC and Rep stats if those exist; modify the IdentityStatLink usages for
profile.cic and profile.rep to use those specific hrefs (symbols:
IdentityStatLink, rootHref, profile.cic, profile.rep) and verify navigation
works and tests/links referencing these routes are updated accordingly.
In `@components/waves/drops/WaveDropActionsOptions.tsx`:
- Around line 56-70: The Tooltip styling/props in WaveDropActionsOptions.tsx are
duplicated with WaveDropActionsOpen.tsx; extract the shared configuration (props
like place, offset, opacity, positionStrategy, style including borderRadius,
boxShadow, backgroundColor, color, padding, zIndex, pointerEvents) into a single
exported constant or helper (e.g., ACTION_TOOLTIP_PROPS or
getActionTooltipProps) and import it into both files, then spread those defaults
into each Tooltip usage while keeping unique props like id; update references to
the Tooltip calls in WaveDropActionsOptions.tsx and WaveDropActionsOpen.tsx to
use the shared constant.
In `@components/waves/drops/winner/WinnerDropBadge.tsx`:
- Around line 50-52: The color values in WinnerDropBadge (variables colorClasses
and textColorClass) use a hardcoded hex (`#ffc107`); replace this with the
Tailwind palette by using an existing class (e.g., tw-text-amber-500 and
matching tw-bg-amber-500/10 or tw-border-amber-500/20) or, if the hex is a
deliberate brand gold, add a custom color alias in the Tailwind config (e.g.,
"gold-500") and update colorClasses and textColorClass to use that new
tw-text-gold-500/tw-bg-gold-500/10/tw-border-gold-500/20 instead of the literal
hex so styling stays consistent and maintainable.
In `@components/waves/winners/drops/MemesWaveWinnerDrop.tsx`:
- Around line 43-45: The function getRankHoverClass is a redundant wrapper
around getRankHoverBorderClass; delete getRankHoverClass and replace all calls
to getRankHoverClass(...) with direct calls to getRankHoverBorderClass(...), and
remove any related unused imports/exports or type declarations tied only to
getRankHoverClass to avoid linter errors.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 23efb527-863e-4e7d-a464-77bf1ed29937
⛔ Files ignored due to path filters (3)
.playwright-mcp/page-2026-03-31T06-28-02-990Z.pngis excluded by!**/*.pnggenerated/models/ObjectSerializer.tsis excluded by!**/generated/**package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (69)
.playwright-mcp/page-2026-03-31T06-27-58-535Z.yml.playwright-mcp/page-2026-03-31T13-04-45-965Z.yml.playwright-mcp/page-2026-03-31T13-15-19-347Z.ymlcomponents/common/profile/ProfileAvatar.tsxcomponents/common/profile/ProfileBadge.tsxcomponents/common/profile/ProfileHandle.tsxcomponents/drops/create/utils/DropPfp.tsxcomponents/drops/view/utils/DropVoteProgressing.tsxcomponents/memes/drops/MemeParticipationDrop.tsxcomponents/memes/drops/MemeWinnerDrop.tsxcomponents/memes/drops/MemesLeaderboardDropCard.tsxcomponents/mobile-wrapper-dialog/MobileWrapperDialog.tsxcomponents/user/utils/UserCICAndLevel.tsxcomponents/voting/VotingModalButton.tsxcomponents/waves/CreateCurationDropContent.tsxcomponents/waves/CreateDrop.tsxcomponents/waves/CreateDropContent.tsxcomponents/waves/CreateDropDropModeToggle.tsxcomponents/waves/CreateDropIdentityField.tsxcomponents/waves/CreateDropIdentityPickerModal.tsxcomponents/waves/PrivilegedDropCreator.tsxcomponents/waves/drop/SingleWaveDropChat.tsxcomponents/waves/dropComposer.types.tscomponents/waves/drops/ArtistActivityBadge.tsxcomponents/waves/drops/Drop.tsxcomponents/waves/drops/DropAuthorBadges.tsxcomponents/waves/drops/DropContext.tsxcomponents/waves/drops/WaveCreatorBadge.tsxcomponents/waves/drops/WaveDrop.tsxcomponents/waves/drops/WaveDropActionsOpen.tsxcomponents/waves/drops/WaveDropActionsOptions.tsxcomponents/waves/drops/WaveDropRatings.tsxcomponents/waves/drops/drop.types.tscomponents/waves/drops/dropRankStyles.tscomponents/waves/drops/identity/WaveDropIdentity.tsxcomponents/waves/drops/participation/DefaultParticipationDrop.tsxcomponents/waves/drops/participation/EndedParticipationDrop.tsxcomponents/waves/drops/participation/OngoingParticipationDrop.tsxcomponents/waves/drops/participation/ParticipationDrop.tsxcomponents/waves/drops/participation/ParticipationDropContainer.tsxcomponents/waves/drops/participation/ParticipationDropContent.tsxcomponents/waves/drops/participation/ParticipationDropFooter.tsxcomponents/waves/drops/participation/ParticipationDropMetadata.tsxcomponents/waves/drops/participation/ParticipationIdentityProfileCard.tsxcomponents/waves/drops/participation/ratings/ParticipationDropRatingsContainer.tsxcomponents/waves/drops/participation/ratings/ParticipationDropRatingsTotalSection.tsxcomponents/waves/drops/participation/ratings/ParticipationDropRatingsUserSection.tsxcomponents/waves/drops/participation/ratings/ParticipationDropRatingsVoterSection.tsxcomponents/waves/drops/winner/DefaultWinnerDrop.tsxcomponents/waves/drops/winner/WinnerDrop.tsxcomponents/waves/drops/winner/WinnerDropBadge.tsxcomponents/waves/leaderboard/content/WaveLeaderboardDropContent.tsxcomponents/waves/leaderboard/create/WaveDropCreate.tsxcomponents/waves/leaderboard/drops/DefaultWaveLeaderboardDrop.tsxcomponents/waves/leaderboard/drops/header/WaveLeaderboardDropAuthor.tsxcomponents/waves/leaderboard/drops/header/WaveLeaderboardDropHeader.tsxcomponents/waves/leaderboard/drops/header/WaveleaderboardDropRaters.tsxcomponents/waves/leaderboard/grid/WaveLeaderboardGridItem.tsxcomponents/waves/leaderboard/identity/WaveLeaderboardIdentity.tsxcomponents/waves/winners/drops/DefaultWaveWinnerDrop.tsxcomponents/waves/winners/drops/MemesWaveWinnerDrop.tsxcomponents/waves/winners/drops/header/WaveWinnersDropHeader.tsxcomponents/waves/winners/drops/header/WaveWinnersDropHeaderAuthorPfp.tsxcomponents/waves/winners/drops/header/WaveWinnersDropHeaderTotalVotes.tsxcomponents/waves/winners/drops/header/WaveWinnersDropHeaderVoter.tsxcomponents/waves/winners/drops/header/WaveWinnersDropHeaderVoters.tsxcomponents/waves/winners/drops/header/WaveWinnersDropOutcome.tsxcomponents/waves/winners/identity/WaveWinnerIdentity.tsxopenapi.yaml
✅ Files skipped from review due to trivial changes (11)
- components/common/profile/ProfileBadge.tsx
- components/waves/drops/DropContext.tsx
- .playwright-mcp/page-2026-03-31T06-27-58-535Z.yml
- components/waves/CreateCurationDropContent.tsx
- components/waves/drops/participation/DefaultParticipationDrop.tsx
- components/waves/winners/drops/header/WaveWinnersDropHeader.tsx
- .playwright-mcp/page-2026-03-31T13-15-19-347Z.yml
- components/waves/dropComposer.types.ts
- components/waves/drops/drop.types.ts
- .playwright-mcp/page-2026-03-31T13-04-45-965Z.yml
- components/waves/CreateDropIdentityPickerModal.tsx
🚧 Files skipped from review as they are similar to previous changes (9)
- components/waves/leaderboard/grid/WaveLeaderboardGridItem.tsx
- components/waves/drops/winner/DefaultWinnerDrop.tsx
- components/waves/drops/participation/OngoingParticipationDrop.tsx
- components/waves/winners/identity/WaveWinnerIdentity.tsx
- components/waves/drops/identity/WaveDropIdentity.tsx
- components/mobile-wrapper-dialog/MobileWrapperDialog.tsx
- components/waves/leaderboard/content/WaveLeaderboardDropContent.tsx
- components/waves/CreateDropIdentityField.tsx
- components/waves/drops/participation/ParticipationDropMetadata.tsx
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
components/waves/drops/participation/ratings/ParticipationDropRatingsUserSection.tsx (1)
9-17: Interface declares unused props and potentially redundantdropproperty.The interface extends
RatingsSectionProps(which likely includesdrop,rank, andtheme) but:
- Re-declares
drop: ApiDropon line 11—ifRatingsSectionPropsalready hasdrop, this is redundant- Only
dropandratingsDataare destructured—themeandrankfrom the extended interface are never usedConsider either narrowing the interface to only what's needed, or removing the extension if these props aren't required.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/participation/ratings/ParticipationDropRatingsUserSection.tsx` around lines 9 - 17, The ParticipationDropRatingsUserSectionProps interface redundantly re-declares drop and inherits unused fields (theme, rank) from RatingsSectionProps; update the interface to only declare the props actually used by ParticipationDropRatingsUserSection (ratingsData and drop) by removing the extends RatingsSectionProps, or alternatively remove the explicit drop declaration and ensure theme/rank are used or omitted; adjust the component signature/prop destructuring (ParticipationDropRatingsUserSection, ratingsData, drop) to match the cleaned interface so no unused props remain.components/waves/drops/participation/ratings/ParticipationDropRatingsContainer.tsx (1)
28-46: Consider removing unusedthemeandrankprops from child components.Per the relevant code snippets,
ParticipationDropRatingsVoterSectionandParticipationDropRatingsTotalSectionextendRatingsSectionProps(which includesthemeandrank) but only destructuredropandratingsData. These are dead props that add noise without being consumed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/participation/ratings/ParticipationDropRatingsContainer.tsx` around lines 28 - 46, The child components ParticipationDropRatingsVoterSection and ParticipationDropRatingsTotalSection receive unused props theme and rank (they extend RatingsSectionProps but only destructure drop and ratingsData); remove theme and rank from the props interface or the child component prop lists and update RatingsSectionProps or component signatures accordingly so only drop and ratingsData are passed/typed, and ensure callers (e.g., ParticipationDropRatingsContainer rendering these components) stop supplying theme and rank to these two components while keeping ParticipationDropRatingsUserSection unchanged if it still needs them.components/waves/CreateDrop.tsx (1)
233-233: Unused ref assignment.
inFlightProcessNextDropRef.currentis assigned at line 270 but never read. If this is intended for future use (e.g., cancellation or debugging), consider adding a comment explaining the intent. Otherwise, this can be removed.Also applies to: 270-270
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/CreateDrop.tsx` at line 233, The ref inFlightProcessNextDropRef is only written to (assigned at the async "process next drop" call) and never read; either remove the unused useRef declaration and its assignment to eliminate dead code, or preserve it but add a concise comment above inFlightProcessNextDropRef explaining its intended future use (e.g., cancellation token / debug hook) and add at least one read/usage (such as checking .current to cancel or log) so the assignment is meaningful; reference the inFlightProcessNextDropRef symbol and the site where you assign to inFlightProcessNextDropRef.current when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/waves/drops/WaveDropRatings.tsx`:
- Around line 65-66: The inline style setting zIndex (style={{ zIndex:
drop.top_raters.length - index }}) conflicts with the hover class; replace the
inline numeric zIndex with a CSS custom property and use Tailwind's arbitrary
value so both base and hover z come from classes: set the style prop to define
--z to drop.top_raters.length - index and update the className on the element
(the one currently containing "hover:tw-z-10") to include "z-[var(--z)]
hover:z-[var(--z-hover)]" or simply "z-[var(--z)] hover:z-10" depending on
desired hover behavior, ensuring the element uses z-[var(--z)] (refer to the
className and the existing style usage) so hover stacking is controlled by the
same mechanism as the base stacking.
- Line 54: The fallback avatar computation in WaveDropRatings.tsx uses
raterLabel.slice(0, 2) which yields "0X" for address-only raters; update the
logic that sets fallbackAvatarLabel to first strip a leading "0x"/"0X" from
raterLabel (e.g., remove the prefix if present) and then take the first two
characters and uppercase them so address-based fallbacks produce meaningful,
distinguishable initials; adjust any related uses of fallbackAvatarLabel
accordingly.
In `@helpers/waves/wave-decision.helpers.ts`:
- Line 50: The current sort call on the winners array (const sortedWinners =
winners.sort((a, b) => a.place - b.place)) can throw when entries are
null/non-objects; make the comparator defensive by normalizing each entry’s
place before subtracting: for each of a and b (in the comparator used by
sortedWinners), treat missing or non-numeric place as a safe fallback (e.g.,
Number.POSITIVE_INFINITY or a defined sentinel) so comparisons never access
properties on null, and then return the numeric difference; alternatively
pre-filter out non-objects from winners before sorting using the same
normalization logic in functions that reference sortedWinners.
---
Nitpick comments:
In `@components/waves/CreateDrop.tsx`:
- Line 233: The ref inFlightProcessNextDropRef is only written to (assigned at
the async "process next drop" call) and never read; either remove the unused
useRef declaration and its assignment to eliminate dead code, or preserve it but
add a concise comment above inFlightProcessNextDropRef explaining its intended
future use (e.g., cancellation token / debug hook) and add at least one
read/usage (such as checking .current to cancel or log) so the assignment is
meaningful; reference the inFlightProcessNextDropRef symbol and the site where
you assign to inFlightProcessNextDropRef.current when making the change.
In
`@components/waves/drops/participation/ratings/ParticipationDropRatingsContainer.tsx`:
- Around line 28-46: The child components ParticipationDropRatingsVoterSection
and ParticipationDropRatingsTotalSection receive unused props theme and rank
(they extend RatingsSectionProps but only destructure drop and ratingsData);
remove theme and rank from the props interface or the child component prop lists
and update RatingsSectionProps or component signatures accordingly so only drop
and ratingsData are passed/typed, and ensure callers (e.g.,
ParticipationDropRatingsContainer rendering these components) stop supplying
theme and rank to these two components while keeping
ParticipationDropRatingsUserSection unchanged if it still needs them.
In
`@components/waves/drops/participation/ratings/ParticipationDropRatingsUserSection.tsx`:
- Around line 9-17: The ParticipationDropRatingsUserSectionProps interface
redundantly re-declares drop and inherits unused fields (theme, rank) from
RatingsSectionProps; update the interface to only declare the props actually
used by ParticipationDropRatingsUserSection (ratingsData and drop) by removing
the extends RatingsSectionProps, or alternatively remove the explicit drop
declaration and ensure theme/rank are used or omitted; adjust the component
signature/prop destructuring (ParticipationDropRatingsUserSection, ratingsData,
drop) to match the cleaned interface so no unused props remain.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6991861d-0fce-4ef7-a43a-8a5084a83171
📒 Files selected for processing (11)
components/utils/input/profile-search/CommonProfileSearchItem.tsxcomponents/waves/CreateDrop.tsxcomponents/waves/CreateDropContent.tsxcomponents/waves/create-wave/drops/metadata/CreateWaveDropsMetadata.tsxcomponents/waves/drops/WaveDropRatings.tsxcomponents/waves/drops/participation/ParticipationDropFooter.tsxcomponents/waves/drops/participation/ParticipationIdentityProfileCard.tsxcomponents/waves/drops/participation/ratings/ParticipationDropRatingsContainer.tsxcomponents/waves/drops/participation/ratings/ParticipationDropRatingsUserSection.tsxcomponents/waves/drops/participation/ratings/ParticipationDropRatingsVoterSection.tsxhelpers/waves/wave-decision.helpers.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- components/waves/create-wave/drops/metadata/CreateWaveDropsMetadata.tsx
- components/waves/drops/participation/ParticipationIdentityProfileCard.tsx
- components/waves/drops/participation/ratings/ParticipationDropRatingsVoterSection.tsx
- components/waves/CreateDropContent.tsx
|

Summary by CodeRabbit
New Features
Improvements
Chores