Skip to content

Boosted drops#1706

Merged
simo6529 merged 11 commits intomainfrom
boosted-drops
Jan 8, 2026
Merged

Boosted drops#1706
simo6529 merged 11 commits intomainfrom
boosted-drops

Conversation

@simo6529
Copy link
Copy Markdown
Collaborator

@simo6529 simo6529 commented Jan 7, 2026

Summary by CodeRabbit

  • New Features

    • Boost drops via action or double-tap, with boost counts and animated on-screen feedback.
    • Curated boosted drops shown inline in lists with an accessible boosted drop preview card and click-to-open behavior.
    • New boost iconography and animated visuals for boost state.
  • UX Improvements

    • Optimistic, faster UI updates for boost toggles and clearer tooltips/labels.
    • Boosted lists refresh more frequently for up-to-date counts.

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

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

coderabbitai Bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

Adds a "boost" feature across UI, hooks, animations, list integration, optimistic mutation logic, and OpenAPI schema/endpoints to support boosted drops.

Changes

Cohort / File(s) Summary
Boost UI Components
components/drops/view/BoostedDropCard.tsx, components/common/icons/BoostIcon.tsx, components/waves/drops/DropBoostAnimation.tsx, components/waves/drops/WaveDropActionsBoost.tsx
New BoostedDropCard preview, BoostIcon SVG, portal-based DropBoostAnimation component, and WaveDropActionsBoost action button with animation/visual states.
WaveDrop integration
components/waves/drops/WaveDrop.tsx, components/waves/drops/WaveDropActions.tsx
Integrates double-tap boosting, boost animation rendering, guard logic to prevent boosting during edits/menus, and adds boost action to the drop actions toolbar.
Lists & content surfaces
components/drops/view/DropsList.tsx, components/waves/drops/wave-drops-all/index.tsx, components/waves/drops/wave-drops-all/subcomponents/WaveDropsContent.tsx, components/waves/drops/wave-drops-all/subcomponents/WaveDropsMessageListSection.tsx
Propagates boostedDrops and onBoostedDropClick through components and inserts BoostedDropCard at computed positions within DropsList rendering pipeline.
Boost hooks & data
hooks/drops/useDropBoostMutation.ts, hooks/useDoubleTap.ts, hooks/useWaveBoostedDrops.ts
New optimistic boost mutation hook with rollback, double-tap gesture hook, and react-query hook to fetch boosted drops (cached/refetching).
Reactions & actions refinements
components/waves/drops/WaveDropReactions.tsx, components/waves/drops/WaveDropActionsAddReaction.tsx
Reaction optimistic-update flow updated to include boosted context; emoji rendering changes; refined optimistic sync and permission logic.
Create & utilities
components/waves/CreateDropContent.tsx, components/react-query-wrapper/ReactQueryWrapper.tsx, components/react-query-wrapper/utils/toggleWaveFollowing.ts, components/waves/drops/item/rate/give/DropListItemRateGiveSubmit.tsx
CreateDropContent: payload tweaks (adds boosts: 0), various logic refinements; ReactQueryWrapper: added QueryKey.BOOSTED_DROPS; minor formatting/import tweaks elsewhere.
API / OpenAPI
openapi.yaml
New endpoints for /boosted-drops and /drops/{dropId}/boosts; added ApiDropBoost/ApiDropBoostsPage, boosts field on ApiDrop, and boosted context fields; schema updates reflecting boosts.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant UI as WaveDrop (client)
    participant DT as useDoubleTap hook
    participant Mut as useDropBoostMutation
    participant Cache as Local Cache / MyStream
    participant API as Backend API
    participant Anim as DropBoostAnimation

    User->>UI: double-tap on a drop
    UI->>DT: forward tap events
    DT-->>UI: onDoubleTap detected
    UI->>Mut: toggleBoost(dropId)

    alt optimistic path
        Mut->>Cache: apply optimistic update (update boosts, set context.boosted)
        Mut->>Mut: mark pendingDropId
    end

    Mut->>API: POST / DELETE /drops/{id}/boosts
    par visual feedback
        UI->>Anim: render boost/unboost animation (x,y,type)
        Anim-->>UI: onComplete callback
    and server response
        API-->>Mut: success or error
        alt success
            Mut->>Cache: clear pending/rollback
        else error
            Mut->>Cache: rollback optimistic update
            Mut->>UI: surface error (toast)
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Drops highlight #1552 — Modifies DropsList.tsx rendering pipeline; directly related to BoostedDropCard insertion logic.
  • Text copy #1580 — Alters DropsList drop-rendering pipeline and wrapper behavior; likely overlaps with list integration changes.
  • Migrate nextjs16 part 2 #1576 — Related IPFS URL resolution usage (resolveIpfsUrlSync) used by BoostedDropCard.

Suggested reviewers

  • ragnep

Poem

🐰
I hopped, I tapped, I gave a cheer,
Flames and puffs of boost appear,
Cards that show a tiny spark,
Optimistic hops brighten the dark,
Nibble carrots — boosts are here! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Boosted drops' directly reflects the main feature addition across the changeset - a comprehensive boosted drops system including new components, API integration, and UI interactions.

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

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (2)
components/waves/CreateDropContent.tsx (1)

1004-1023: Remove comments per coding guidelines.

Lines 1006 and 1009 contain comments that violate the coding guideline: "Do not include any comments in the code; it should be self-explanatory."

As per coding guidelines, the code must be self-explanatory without comments.

🔧 Remove comments
  const removeFile = (file: File, partIndex?: number) => {
    if (partIndex === undefined) {
-     // Remove file from the current files array
      setFiles((prevFiles) => prevFiles.filter((f) => f !== file));
    } else {
-     // Remove file from a specific part
      setDrop((prevDrop) => {
openapi.yaml (1)

1088-1109: Mark-unread description quoting and numeric field formatting

The new markDropUnread description is clearer, but the text currently reads user"s / drop"s, which looks like an accidental replacement of apostrophes and will show up as-is in generated docs.

Also, ApiMarkDropUnreadResponse uses bare integer for your_unread_drops_count and first_unread_drop_serial_no, whereas related metrics fields elsewhere are typically format: int64. Aligning these would make generated clients more consistent.

Proposed tweaks for description and response types
   /drops/{dropId}/mark-unread:
     post:
       tags:
         - Drops
       summary: Mark a drop and all subsequent drops in the wave as unread
       description: >-
-        Sets the user"s latest_read_timestamp to just before this drop"s
-        created_at, making this drop and all newer drops unread.
+        Sets the user's latest_read_timestamp to just before this drop's
+        created_at, making this drop and all newer drops unread.

@@
     ApiMarkDropUnreadResponse:
       type: object
       required:
         - your_unread_drops_count
       properties:
         your_unread_drops_count:
-          type: integer
+          type: integer
+          format: int64
         first_unread_drop_serial_no:
-          type: integer
+          type: integer
+          format: int64
           nullable: true

Also applies to: 6968-6977

🤖 Fix all issues with AI agents
In @components/drops/view/BoostedDropCard.tsx:
- Around line 29-30: The container div in BoostedDropCard.tsx mistakenly
includes the class "tw-size-2" which collapses the card to 8px; remove that
class and replace it with an appropriate sizing utility such as "tw-w-full" or
"tw-w-auto" (or "tw-min-w-0" if flex children need it) so the card displays at
normal width; update the className string on the div to remove "tw-size-2" and
add the chosen width utility.

In @components/waves/CreateDropContent.tsx:
- Around line 380-381: The removed defensive fallbacks for profile fields are
causing inconsistency; restore the original fallbacks when building the profile
object in CreateDropContent (use the same pattern as ProfileHelpers).
Specifically, when assigning connectedProfile.winner_main_stage_drop_ids, set a
default of [] (winner_main_stage_drop_ids ?? []); for connectedProfile.pfp set a
default of null (pfp ?? null); and for the wallet field use primary_address with
a default empty string (primary_address ?? "")—apply these fallbacks where the
profile object is constructed (references: connectedProfile,
winner_main_stage_drop_ids, pfp, primary_address) to match ProfileHelpers.ts and
reaction-utils patterns.

In @components/waves/drops/DropBoostAnimation.tsx:
- Around line 71-78: The render currently calls onComplete() directly when
shouldReduceMotion && animation, which is a side-effect during render; instead,
remove the synchronous call and add a useEffect inside the DropBoostAnimation
component that watches [shouldReduceMotion, animation, mounted] and, if mounted
&& shouldReduceMotion && animation, invokes onComplete() (and nothing in render
should call onComplete). Ensure the existing early-return logic (if
shouldReduceMotion || !mounted || !animation) still returns null for rendering
while the effect performs the callback.

In @components/waves/drops/wave-drops-all/subcomponents/WaveDropsContent.tsx:
- Around line 78-82: The optional chaining is inconsistent: dropsCount uses
waveMessages?.drops and isInitialLoading checks waveMessages?.isLoading but
accesses waveMessages.isLoadingNextPage without protection; either ensure
waveMessages is non-null before these reads or consistently use optional
chaining. Update the isInitialLoading computation (symbols: dropsCount,
isInitialLoading, waveMessages, isLoading, isLoadingNextPage) so both isLoading
and isLoadingNextPage are accessed the same way (e.g., waveMessages?.isLoading
and waveMessages?.isLoadingNextPage) or add an explicit guard (if
(!waveMessages) return/handle) above to make plain property access safe.

In @components/waves/drops/WaveDropActionsBoost.tsx:
- Around line 26-34: The click handler sets a timeout to reset animation but
never clears it, risking setState on unmounted components; update handleClick to
store the timeout id (e.g., in a useRef like animationTimeoutRef) and clear it
in a useEffect cleanup (or clear any existing timeout before setting a new one)
so that setIsAnimating(false) is not called after unmount; keep references to
toggleBoost and drop in the dependencies as needed.

In @components/waves/drops/WaveDropReactions.tsx:
- Around line 78-108: The code makes multiple setState calls during render
(setPrevTotal, setAnimate, setPrevContextReaction, setSelected, setPrevProfiles,
setTotal, setHandles) when comparing props like total,
drop.context_profile_context?.reaction, and reaction.profiles; move this
synchronization into one or more useEffect hooks that run when their respective
dependencies change (e.g., [total], [drop.context_profile_context?.reaction,
reaction.reaction, selected], [reaction.profiles]) or compute derived values
with useMemo where appropriate; inside each effect perform the comparisons and
call the setter functions only when values differ to avoid extra renders, and
ensure dependency arrays include the source props/state (reaction, drop, total)
so no state updates occur during render.

In @hooks/useDoubleTap.ts:
- Around line 157-167: The onClick handler currently uses e.detail > 0 to filter
synthetic clicks; update the useCallback onClick (React.MouseEvent) to check
event.nativeEvent.pointerType === 'mouse' before calling handleTap(clientX,
clientY, e), remove the explanatory comments about touch vs mouse, and keep the
dependency array [enabled, handleTap] unchanged so the handler still respects
enabled and uses handleTap correctly.

In @hooks/useWaveBoostedDrops.ts:
- Around line 8-13: Remove the local ApiDropsPage interface and import the
generated type instead: replace the inline interface declaration of ApiDropsPage
with an import from "@/generated/models/ApiDropsPage" and update any usages in
hooks/useWaveBoostedDrops.ts to reference the imported ApiDropsPage type; ensure
there are no remaining duplicate declarations and update any related
imports/exports in the file to avoid name collisions.
🧹 Nitpick comments (11)
components/waves/CreateDropContent.tsx (1)

859-859: Consider restoring optional chaining for defensive coding.

While document.activeElement always returns an Element and all elements have a blur() method, removing the optional chaining eliminates a safety guard. If there's any edge case where the element lacks blur(), this could throw.

♻️ Restore optional chaining
-(document.activeElement as HTMLElement).blur();
+(document.activeElement as HTMLElement)?.blur();
components/react-query-wrapper/ReactQueryWrapper.tsx (1)

70-70: Consider invalidating BOOSTED_DROPS alongside other drop queries.

When drops are modified, the boosted drops data may become stale. Consider adding BOOSTED_DROPS to the invalidateDrops() function (around line 913) alongside other drop-related query keys.

♻️ Proposed enhancement

Add this invalidation call to the invalidateDrops() function:

 const invalidateDrops = () => {
   queryClient.invalidateQueries({
     queryKey: [QueryKey.DROPS],
   });
   queryClient.invalidateQueries({
     queryKey: [QueryKey.DROPS_LEADERBOARD],
   });
+  queryClient.invalidateQueries({
+    queryKey: [QueryKey.BOOSTED_DROPS],
+  });
   queryClient.invalidateQueries({
     queryKey: [QueryKey.DROP],
   });
   queryClient.invalidateQueries({
     queryKey: [QueryKey.PROFILE_DROPS],
   });
   queryClient.invalidateQueries({
     queryKey: [QueryKey.FEED_ITEMS],
   });
   queryClient.invalidateQueries({
     queryKey: [QueryKey.DROP_DISCUSSION],
   });
 };
hooks/useWaveBoostedDrops.ts (1)

20-23: Consider increasing refetch interval for better performance.

The 30-second refetch interval may be aggressive for boosted drops, which typically don't change that frequently. Consider increasing to 60 seconds or using refetchOnWindowFocus instead to balance freshness with performance.

♻️ Optional adjustment
 const DEFAULT_LIMIT = 10;
 const STALE_TIME = 60000; // 1 minute
-const REFETCH_INTERVAL = 30000; // 30 seconds
+const REFETCH_INTERVAL = 60000; // 60 seconds
 const TRENDING_WINDOW_MS = 24 * 60 * 60 * 1000; // 24 hours in milliseconds

Or consider removing refetchInterval and relying on refetchOnWindowFocus: true (React Query default) for more conservative refetching.

components/waves/drops/WaveDropActionsBoost.tsx (1)

90-113: Consider using Tailwind classes for tooltip styling.

The tooltip uses inline styles, but per coding guidelines, Tailwind classes are preferred. If react-tooltip supports className props, refactor the inline styles to use Tailwind utilities.

♻️ Potential refactor

If react-tooltip supports className:

       <Tooltip
         id={tooltipId}
         place="top"
         positionStrategy="fixed"
         offset={8}
         opacity={1}
-        style={{
-          padding: "4px 8px",
-          background: "#37373E",
-          color: "white",
-          fontSize: "13px",
-          fontWeight: 500,
-          borderRadius: "6px",
-          boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
-          zIndex: 99999,
-          pointerEvents: "none",
-        }}
+        className="tw-px-2 tw-py-1 tw-bg-iron-900 tw-text-white tw-text-xs tw-font-medium tw-rounded-md tw-shadow-2xl tw-z-[99999] tw-pointer-events-none"
       >
         <span className="tw-text-xs">
           {isPinned ? "Remove Boost" : "Boost"}
         </span>
       </Tooltip>

Note: Verify that react-tooltip version in use supports className prop.

components/drops/view/BoostedDropCard.tsx (1)

46-51: Remove inline comments; use FontAwesome for icons.

Per coding guidelines: comments should be removed (code should be self-explanatory), and FontAwesome should be used for icons in React components. The inline SVGs on lines 86-97 and 104-116 could be replaced with FontAwesome equivalents like faFire and faChevronRight.

components/waves/drops/DropBoostAnimation.tsx (2)

4-4: Prefer direct named imports over React. namespace usage.

Per coding guidelines, import FC directly from React and use it without the React. prefix on lines 19, 36, and 60.

♻️ Proposed fix
-import React, { useEffect, useState } from "react";
+import { useEffect, useState, type FC } from "react";

Then update usages:

-const FlameIcon: React.FC<{ readonly className?: string }> = ({
+const FlameIcon: FC<{ readonly className?: string }> = ({

83-84: Redundant null check for animation.

The animation check on line 84 is unnecessary since the early return on line 72 already handles the !animation case.

♻️ Simplified rendering
   return createPortal(
     <AnimatePresence mode="wait" onExitComplete={onComplete}>
-      {animation && (
         <motion.div
           key={animation.id}
           ...
         </motion.div>
-      )}
     </AnimatePresence>,
     document.body
   );
components/waves/drops/WaveDrop.tsx (1)

3-3: Consider using useAuth hook for consistency.

The codebase appears to use useAuth hook (seen in index.tsx line 3), but here AuthContext is imported directly and used with useContext. For consistency, consider using the useAuth hook pattern.

♻️ Proposed change
-import { AuthContext } from "@/components/auth/Auth";
+import { useAuth } from "@/components/auth/Auth";
...
-  const { connectedProfile } = useContext(AuthContext);
+  const { connectedProfile } = useAuth();
components/drops/view/DropsList.tsx (1)

183-247: Returning JSX from useMemo is unconventional; consider extracting render logic.

The component returns JSX directly from a useMemo call, which works but is unusual. The memoization is effectively caching the rendered output, but React components typically return JSX directly. If the intent is to avoid re-renders, the memo() wrapper on line 53 already handles that for the component.

Consider whether this pattern is intentional for specific performance reasons, or if the render logic could be simplified.

openapi.yaml (2)

481-520: Distribution photos “complete” request shape and clearing semantics

Switching /distribution_photos/{contract}/{nft_id}/complete to DistributionPhotoCompleteRequest with a photos array and minItems: 1 makes sense for the “replace set of photos” behavior, and the new DistributionPhotosPage for listing is consistent with other *Page types.

One thing to double-check: with minItems: 1, there is no way (via this endpoint) to clear all photos for a card by sending an empty list, despite the description saying it “replaces all existing photos for the card”. If clearing all photos should be supported, you may want to allow an empty array here.

Optional schema tweak if clearing all photos should be allowed
    DistributionPhotoCompleteRequest:
      type: object
      required:
        - photos
      properties:
        photos:
          type: array
-          minItems: 1
          items:
            $ref: "#/components/schemas/DistributionPhotoCompleteRequestPhoto"

Also applies to: 9346-9355, 9377-9386


6024-6141: Boost-related fields and schemas are wired consistently across drops

Adding boosts to both ApiDrop and ApiDropWithoutWave, the boosted flag to ApiDropContextProfileContext, and the new ApiDropBoost/ApiDropBoostsPage schemas cleanly exposes all the data the frontend needs (count, per-booster details, and per-user boosted state). Types (number/integer with format: int64 where used) and pagination inheritance from ApiPageBase match the rest of the spec.

If you want stricter numerical consistency, you could consider standardizing on type: integer + format: int64 for counts/timestamps in new fields, but that’s stylistic and not blocking.

Also applies to: 6151-6173, 6175-6197, 6406-6520

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6eca140 and c9694d2.

⛔ Files ignored due to path filters (8)
  • generated/models/ApiDrop.ts is excluded by !**/generated/**
  • generated/models/ApiDropBoost.ts is excluded by !**/generated/**
  • generated/models/ApiDropBoostsPage.ts is excluded by !**/generated/**
  • generated/models/ApiDropContextProfileContext.ts is excluded by !**/generated/**
  • generated/models/ApiDropWithoutWave.ts is excluded by !**/generated/**
  • generated/models/ApiWave.ts is excluded by !**/generated/**
  • generated/models/ApiWaveOutcomeOld.ts is excluded by !**/generated/**
  • generated/models/ObjectSerializer.ts is excluded by !**/generated/**
📒 Files selected for processing (19)
  • components/drops/view/BoostedDropCard.tsx
  • components/drops/view/DropsList.tsx
  • components/drops/view/item/rate/give/DropListItemRateGiveSubmit.tsx
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/react-query-wrapper/utils/toggleWaveFollowing.ts
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/DropBoostAnimation.tsx
  • components/waves/drops/WaveDrop.tsx
  • components/waves/drops/WaveDropActions.tsx
  • components/waves/drops/WaveDropActionsAddReaction.tsx
  • components/waves/drops/WaveDropActionsBoost.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/wave-drops-all/index.tsx
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsContent.tsx
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsMessageListSection.tsx
  • hooks/drops/useDropBoostMutation.ts
  • hooks/useDoubleTap.ts
  • hooks/useWaveBoostedDrops.ts
  • openapi.yaml
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

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

**/*.{ts,tsx,js,jsx}: Remove unnecessary Effects. If the Effect's only job is to derive or sync internal state, calculate during render or use useMemo instead.
Use useEffectEvent for non-reactive logic inside Effects to read the latest props/state without turning them into dependencies or causing unnecessary re-runs.
Use explicit caching with "use cache" directive at the top of Server Components, routes, or functions. Configure cacheComponents: true in next.config.ts as needed.

**/*.{ts,tsx,js,jsx}: Remove unnecessary Effects; if the Effect only derives state, compute during render instead
Use useEffectEvent when listening to external events but needing the latest props/state without re-running the Effect
Move data fetching from client Effects to Server Components; mutations go through Server Actions ('use server')

Files:

  • hooks/useDoubleTap.ts
  • components/drops/view/item/rate/give/DropListItemRateGiveSubmit.tsx
  • components/waves/drops/WaveDropActionsBoost.tsx
  • hooks/drops/useDropBoostMutation.ts
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsContent.tsx
  • components/drops/view/BoostedDropCard.tsx
  • components/react-query-wrapper/utils/toggleWaveFollowing.ts
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsMessageListSection.tsx
  • components/waves/drops/WaveDropActions.tsx
  • components/waves/drops/DropBoostAnimation.tsx
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/drops/WaveDropActionsAddReaction.tsx
  • components/waves/drops/WaveDrop.tsx
  • hooks/useWaveBoostedDrops.ts
  • components/drops/view/DropsList.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/wave-drops-all/index.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Run npm run lint to ensure code satisfies ESLint (Next's Core Web Vitals + React Hooks). Code must pass linting before completing any task.

**/*.{js,ts,jsx,tsx}: Code must satisfy ESLint with Next's Core Web Vitals and React Hooks rules by running npm run lint
Do not add eslint-disable comments unless explicitly instructed; prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Files:

  • hooks/useDoubleTap.ts
  • components/drops/view/item/rate/give/DropListItemRateGiveSubmit.tsx
  • components/waves/drops/WaveDropActionsBoost.tsx
  • hooks/drops/useDropBoostMutation.ts
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsContent.tsx
  • components/drops/view/BoostedDropCard.tsx
  • components/react-query-wrapper/utils/toggleWaveFollowing.ts
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsMessageListSection.tsx
  • components/waves/drops/WaveDropActions.tsx
  • components/waves/drops/DropBoostAnimation.tsx
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/drops/WaveDropActionsAddReaction.tsx
  • components/waves/drops/WaveDrop.tsx
  • hooks/useWaveBoostedDrops.ts
  • components/drops/view/DropsList.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/wave-drops-all/index.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Use TypeScript with React functional components and hooks. Follow existing code style and naming conventions.

Files:

  • hooks/useDoubleTap.ts
  • components/drops/view/item/rate/give/DropListItemRateGiveSubmit.tsx
  • components/waves/drops/WaveDropActionsBoost.tsx
  • hooks/drops/useDropBoostMutation.ts
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsContent.tsx
  • components/drops/view/BoostedDropCard.tsx
  • components/react-query-wrapper/utils/toggleWaveFollowing.ts
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsMessageListSection.tsx
  • components/waves/drops/WaveDropActions.tsx
  • components/waves/drops/DropBoostAnimation.tsx
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/drops/WaveDropActionsAddReaction.tsx
  • components/waves/drops/WaveDrop.tsx
  • hooks/useWaveBoostedDrops.ts
  • components/drops/view/DropsList.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/wave-drops-all/index.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with React functional components and hooks

Files:

  • hooks/useDoubleTap.ts
  • components/drops/view/item/rate/give/DropListItemRateGiveSubmit.tsx
  • components/waves/drops/WaveDropActionsBoost.tsx
  • hooks/drops/useDropBoostMutation.ts
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsContent.tsx
  • components/drops/view/BoostedDropCard.tsx
  • components/react-query-wrapper/utils/toggleWaveFollowing.ts
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsMessageListSection.tsx
  • components/waves/drops/WaveDropActions.tsx
  • components/waves/drops/DropBoostAnimation.tsx
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/drops/WaveDropActionsAddReaction.tsx
  • components/waves/drops/WaveDrop.tsx
  • hooks/useWaveBoostedDrops.ts
  • components/drops/view/DropsList.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/wave-drops-all/index.tsx
**/*.{ts,js}

📄 CodeRabbit inference engine (AGENTS.md)

When parsing Seize URLs or similar, fail fast if base origin is unavailable instead of falling back to placeholder origins

Files:

  • hooks/useDoubleTap.ts
  • hooks/drops/useDropBoostMutation.ts
  • components/react-query-wrapper/utils/toggleWaveFollowing.ts
  • hooks/useWaveBoostedDrops.ts
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer direct named imports from React (useMemo, useRef, FC) over React. namespace usage

Files:

  • hooks/useDoubleTap.ts
  • components/drops/view/item/rate/give/DropListItemRateGiveSubmit.tsx
  • components/waves/drops/WaveDropActionsBoost.tsx
  • hooks/drops/useDropBoostMutation.ts
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsContent.tsx
  • components/drops/view/BoostedDropCard.tsx
  • components/react-query-wrapper/utils/toggleWaveFollowing.ts
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsMessageListSection.tsx
  • components/waves/drops/WaveDropActions.tsx
  • components/waves/drops/DropBoostAnimation.tsx
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/drops/WaveDropActionsAddReaction.tsx
  • components/waves/drops/WaveDrop.tsx
  • hooks/useWaveBoostedDrops.ts
  • components/drops/view/DropsList.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/wave-drops-all/index.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

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

**/*.{tsx,jsx}: Use internal links via <Link> component from Next.js instead of <a> tags
Replace <img> elements with <Image /> from next/image

Files:

  • components/drops/view/item/rate/give/DropListItemRateGiveSubmit.tsx
  • components/waves/drops/WaveDropActionsBoost.tsx
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsContent.tsx
  • components/drops/view/BoostedDropCard.tsx
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsMessageListSection.tsx
  • components/waves/drops/WaveDropActions.tsx
  • components/waves/drops/DropBoostAnimation.tsx
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/drops/WaveDropActionsAddReaction.tsx
  • components/waves/drops/WaveDrop.tsx
  • components/drops/view/DropsList.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/wave-drops-all/index.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

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

Files:

  • components/drops/view/item/rate/give/DropListItemRateGiveSubmit.tsx
  • components/waves/drops/WaveDropActionsBoost.tsx
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsContent.tsx
  • components/drops/view/BoostedDropCard.tsx
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsMessageListSection.tsx
  • components/waves/drops/WaveDropActions.tsx
  • components/waves/drops/DropBoostAnimation.tsx
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/drops/WaveDropActionsAddReaction.tsx
  • components/waves/drops/WaveDrop.tsx
  • components/drops/view/DropsList.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/wave-drops-all/index.tsx
🧠 Learnings (11)
📚 Learning: 2025-12-30T14:32:19.360Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{tsx,ts} : Use TypeScript with React functional components and hooks

Applied to files:

  • hooks/useDoubleTap.ts
  • hooks/drops/useDropBoostMutation.ts
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsContent.tsx
  • components/waves/drops/WaveDropActions.tsx
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/drops/WaveDrop.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/wave-drops-all/index.tsx
📚 Learning: 2025-12-30T14:31:53.006Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:53.006Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript with React functional components and hooks. Follow existing code style and naming conventions.

Applied to files:

  • hooks/useDoubleTap.ts
  • components/waves/drops/WaveDrop.tsx
  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-12-30T14:31:53.006Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:53.006Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `useEffectEvent` for non-reactive logic inside Effects to read the latest props/state without turning them into dependencies or causing unnecessary re-runs.

Applied to files:

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

Applied to files:

  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • hooks/useWaveBoostedDrops.ts
📚 Learning: 2025-12-30T14:31:53.006Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:53.006Z
Learning: Fix issues with modernization aligned to React 19.2, React Compiler, and Next.js 16 conventions. Do not add `// eslint-disable` comments unless explicitly instructed.

Applied to files:

  • components/waves/drops/WaveDropActionsAddReaction.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/CreateDropContent.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use FontAwesome for icons in React components

Applied to files:

  • components/waves/drops/WaveDropActionsAddReaction.tsx
📚 Learning: 2025-12-30T14:32:19.360Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Prefer direct named imports from React (`useMemo`, `useRef`, `FC`) over `React.` namespace usage

Applied to files:

  • components/waves/drops/WaveDrop.tsx
  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-12-30T14:32:19.360Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Move data fetching from client Effects to Server Components; mutations go through Server Actions (`'use server'`)

Applied to files:

  • components/waves/drops/WaveDrop.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use TailwindCSS for styling in React components

Applied to files:

  • components/waves/drops/WaveDrop.tsx
📚 Learning: 2025-12-30T14:32:19.360Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Remove unnecessary Effects; if the Effect only derives state, compute during render instead

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-12-30T14:31:53.006Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:53.006Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Remove unnecessary Effects. If the Effect's only job is to derive or sync internal state, calculate during render or use `useMemo` instead.

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
🧬 Code graph analysis (9)
components/drops/view/item/rate/give/DropListItemRateGiveSubmit.tsx (1)
generated/models/ApiDropContextProfileContext.ts (1)
  • ApiDropContextProfileContext (16-65)
components/waves/drops/WaveDropActionsBoost.tsx (3)
helpers/waves/drop.helpers.ts (1)
  • ExtendedDrop (16-20)
components/auth/Auth.tsx (1)
  • AuthContext (106-116)
hooks/drops/useDropBoostMutation.ts (1)
  • useDropBoostMutation (27-183)
hooks/drops/useDropBoostMutation.ts (4)
helpers/waves/drop.helpers.ts (1)
  • ExtendedDrop (16-20)
components/auth/Auth.tsx (1)
  • AuthContext (106-116)
contexts/wave/MyStreamContext.tsx (1)
  • useMyStreamOptional (283-285)
generated/models/ApiDropContextProfileContext.ts (1)
  • ApiDropContextProfileContext (16-65)
components/waves/drops/wave-drops-all/subcomponents/WaveDropsContent.tsx (1)
generated/models/ApiDrop.ts (1)
  • ApiDrop (29-237)
components/waves/drops/WaveDropActionsAddReaction.tsx (2)
helpers/waves/drop.helpers.ts (1)
  • ExtendedDrop (16-20)
generated/models/ApiDropContextProfileContext.ts (1)
  • ApiDropContextProfileContext (16-65)
components/drops/view/DropsList.tsx (4)
generated/models/ApiDrop.ts (1)
  • ApiDrop (29-237)
components/waves/drops/Drop.tsx (1)
  • Drop (46-123)
helpers/waves/drop.helpers.ts (1)
  • Drop (14-14)
components/waves/drops/VirtualScrollWrapper.tsx (1)
  • VirtualScrollWrapper (57-182)
components/waves/drops/WaveDropReactions.tsx (2)
components/waves/drops/reaction-utils.ts (1)
  • toProfileMin (76-105)
generated/models/ApiDropContextProfileContext.ts (1)
  • ApiDropContextProfileContext (16-65)
components/waves/CreateDropContent.tsx (1)
components/waves/create-wave/services/multiPartUpload.ts (1)
  • multiPartUpload (15-50)
components/waves/drops/wave-drops-all/index.tsx (1)
hooks/useWaveBoostedDrops.ts (1)
  • useWaveBoostedDrops (25-51)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (34)
components/react-query-wrapper/utils/toggleWaveFollowing.ts (1)

7-28: LGTM!

The utility function is well-implemented with proper TypeScript typing, correct parameter immutability via readonly modifiers, and graceful handling of undefined data. The optimistic query cache update pattern is correct and compatible with the expanded boosted drops feature integration.

components/waves/CreateDropContent.tsx (8)

23-24: LGTM: Clean import consolidation.


121-131: LGTM: Type-aligned null check.

The function now correctly checks only for null since the type signature explicitly defines value: string | number | null, excluding undefined.


257-275: LGTM: Simplified return statement.


290-305: LGTM: Safe removal of optional chaining.

Line 299 guarantees message is always a string, making the optional chaining on Line 300 unnecessary.


429-429: LGTM: Boosts property added for new feature.

The boosts: 0 initialization aligns with the PR objective to introduce boosted drops functionality.


518-523: LGTM: Corrected typing notification logic.

The throttling logic now correctly sends typing notifications only when there is actual content, rather than when the editor is empty. This fixes the previous backward behavior.


942-947: LGTM: Proper useCallback memoization.

Correctly wraps focusMobileInput in useCallback since it's used in the dependency array at Line 973.


1066-1076: LGTM: Robust numeric value handling.

Using Number.isNaN() correctly handles numeric validation and explicitly converts string values when needed.

components/drops/view/item/rate/give/DropListItemRateGiveSubmit.tsx (2)

118-126: LGTM! Proper initialization of boosted context.

The explicit initialization of boosted: false in the base context aligns with the new ApiDropContextProfileContext schema and ensures consistent handling of the boosted state.


104-104: No action needed—the change is safe.

The wave field is a required, non-optional property on ApiDrop (defined as 'wave': ApiWaveMin with no ?), so drop.wave is guaranteed to exist and the optional chaining removal does not introduce any runtime risk.

hooks/drops/useDropBoostMutation.ts (1)

27-183: LGTM! Well-structured boost mutation hook with proper safeguards.

The implementation correctly handles:

  • Optimistic updates with rollback on error
  • Prevention of duplicate mutations via pendingDropIdRef
  • Guarding against temporary drops
  • Proper error messages and toast notifications

The hook follows React Query best practices and integrates cleanly with the MyStream context.

hooks/useDoubleTap.ts (1)

36-174: Well-implemented double-tap detection hook.

The hook correctly:

  • Tracks tap timing and position
  • Handles both touch and mouse events
  • Provides single-tap callback with appropriate delay
  • Cleans up timers on unmount
  • Filters out swipes/scrolls via distance checking
components/waves/drops/WaveDropActionsAddReaction.tsx (2)

87-95: LGTM! Consistent boosted context initialization.

The boosted: false field is properly included in the base context, maintaining consistency with the schema and other components in this PR.


31-31: The concern about simplified canReact logic is invalid.

The component is typed to accept only ExtendedDrop, which guarantees type: DropSize.FULL at the type level. Non-FULL drops cannot reach this component due to TypeScript constraints, so removing the DropSize.FULL check from canReact is safe. The defensive check at line 57 provides additional runtime safety if needed.

Likely an incorrect or invalid review comment.

components/waves/drops/WaveDropReactions.tsx (2)

129-177: LGTM! Proper Next.js Image usage for emoji rendering.

The migration from <img> to Next.js <Image> component with fill layout correctly complies with @next/next/no-img-element ESLint rule and provides better performance.


224-232: LGTM! Consistent boosted field initialization.

The boosted: false field is properly included in the context default, maintaining consistency with the schema changes across the PR.

components/react-query-wrapper/ReactQueryWrapper.tsx (1)

20-21: LGTM! Import consolidation.

The import consolidation is clean and follows best practices.

components/waves/drops/WaveDropActions.tsx (4)

12-12: LGTM! New component import.

The boost action component is properly imported and integrated into the action toolbar.


50-50: LGTM! Correct removal of unnecessary optional chaining.

Since drop.wave is a required field in the ApiDrop type, removing the optional chaining is appropriate and improves code clarity.


77-77: LGTM! Modern array access pattern.

Using .at(0) is a clean, modern approach that safely returns undefined for out-of-bounds access.


96-96: LGTM! Boost action integrated correctly.

The boost action button is properly integrated into the action toolbar with the correct props.

components/waves/drops/WaveDropActionsBoost.tsx (1)

1-8: LGTM! Clean imports.

All necessary dependencies are properly imported.

components/waves/drops/wave-drops-all/subcomponents/WaveDropsContent.tsx (3)

50-51: LGTM! Boosted drops props correctly typed.

The new optional props for boosted drops are correctly typed and integrated into the interface.


120-121: LGTM! Props correctly forwarded.

The boosted drops props are properly passed through to the child component.


10-10: Correct the RefObject type unions for scroll refs.

The ref type change from MutableRefObject to RefObject is appropriate since these refs are read-only in this component. However, fix the type mismatch: scrollContainerRef and bottomAnchorRef are created in useScrollBehavior with useRef<HTMLDivElement>(null), which infers to MutableRefObject<HTMLDivElement> (without the | null union). Update their prop types to RefObject<HTMLDivElement> instead of RefObject<HTMLDivElement | null>. The targetDropRef type is correct as-is.

components/waves/drops/wave-drops-all/subcomponents/WaveDropsMessageListSection.tsx (1)

46-48: Props integration looks correct.

The new boostedDrops and onBoostedDropClick props are properly typed with readonly and correctly forwarded to DropsList. The RefObject type updates align with modern React patterns.

components/waves/drops/wave-drops-all/index.tsx (2)

85-86: Hook integration and data flow look correct.

The useWaveBoostedDrops hook is properly integrated, and boostedDrops is correctly passed down to WaveDropsContent. The onBoostedDropClick callback appropriately uses queueSerialTarget to scroll to the clicked boosted drop.


263-293: Good UX improvement for quote navigation.

The updated handleQuoteClick logic correctly keeps users within the current wave when clicking quotes from the same wave (using queueSerialTarget), while still navigating to other waves when needed. This provides a smoother experience.

components/waves/drops/WaveDrop.tsx (2)

442-466: Verify nested interactive elements don't cause accessibility issues.

Wrapping WaveDropContent in a <button> for double-tap detection could cause nested interactive element issues if the content contains other buttons or links. Ensure that click events on nested interactive elements (like quote links) are handled appropriately and don't conflict with the double-tap handler.


317-359: Boost handling logic looks correct.

The double-tap boost handler properly guards against conflicting states (editing, menu open, pending boost, temp drops), correctly extracts coordinates for animation positioning, and triggers the animation before the mutation for better perceived performance.

components/drops/view/DropsList.tsx (2)

130-151: Boost card positioning logic is well-implemented.

The logarithmic positioning (5, 10, 20, 40, 80, 160 items from bottom) provides a good spread of boosted content as users scroll up through older messages. The Map-based lookup for boostCardAtIndex is efficient.


153-170: Boost card rendering callback is well-structured.

The renderBoostCard function properly checks for the presence of boosted drops, handles undefined cases, and constructs unique keys combining the boost index and drop id.

openapi.yaml (1)

734-813: Boosted drops endpoints look coherent; confirm trailing slash on /boosted-drops/

The new boosted drops listing (/boosted-drops/) and per-drop boosts resource (/drops/{dropId}/boosts) are structurally consistent with the existing API: query params mirror other listing endpoints, responses reuse ApiDropsPage/ApiDropBoostsPage, and pagination/sorting follow established patterns.

One small thing: /boosted-drops/ is the only path here with a trailing slash, while all existing resources (e.g. /drops, /waves, /notifications) omit it. If the backend route is actually /boosted-drops (without /), this spec will be out of sync.

Optional path normalization if backend uses no trailing slash
-  /boosted-drops/:
+  /boosted-drops:
     get:
       tags:
         - Drops

Also applies to: 815-897

Comment thread components/drops/view/BoostedDropCard.tsx Outdated
Comment thread components/waves/CreateDropContent.tsx
Comment thread components/waves/drops/DropBoostAnimation.tsx Outdated
Comment thread components/waves/drops/WaveDropActionsBoost.tsx
Comment thread components/waves/drops/WaveDropReactions.tsx Outdated
Comment thread hooks/useDoubleTap.ts
Comment thread hooks/useWaveBoostedDrops.ts Outdated
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

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

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

886-904: Remove unnecessary Effect; compute derived state with useMemo.

The Effect on lines 902-904 exists solely to derive missingRequirements from other state, which violates the coding guidelines that state: "Remove unnecessary Effects. If the Effect's only job is to derive or sync internal state, calculate during render or use useMemo instead."

The current pattern creates an unnecessary render cycle: the useMemo on lines 886-894 returns a function, which is then called in a useEffect to set state, triggering another render.

♻️ Refactor to compute derived state directly
- const getMissingRequirementsResult = useMemo(() => {
-   return () =>
-     getMissingRequirements(
-       isDropMode,
-       metadata,
-       files,
-       wave.participation.required_media
-     );
- }, [metadata, files, wave.participation.required_media, isDropMode]);
-
- const [missingRequirements, setMissingRequirements] =
-   useState<MissingRequirements>({
-     metadata: [],
-     media: [],
-   });
-
- useEffect(() => {
-   setMissingRequirements(getMissingRequirementsResult());
- }, [metadata, files, getMissingRequirementsResult]);
+ const missingRequirements = useMemo(
+   () =>
+     getMissingRequirements(
+       isDropMode,
+       metadata,
+       files,
+       wave.participation.required_media
+     ),
+   [isDropMode, metadata, files, wave.participation.required_media]
+ );

Based on coding guidelines.

🤖 Fix all issues with AI agents
In @components/drops/view/BoostedDropCard.tsx:
- Around line 24-27: The truncation logic appends "..." when contentPreview
length is exactly 100 because it checks >= 100; update truncatedContent to only
append the ellipsis when the original content is actually longer than 100
characters by checking drop.parts[0]?.content?.length > 100 (or equivalent)
instead of using contentPreview.length >= 100 and ensure you safely handle
undefined content from drop.parts[0]?.content.

In @components/waves/drops/WaveDropReactions.tsx:
- Line 71: Remove the inline comments in WaveDropReactions.tsx at the mentioned
locations (lines referencing the refs/change-detection and the other flagged
inline comments) so the code adheres to the no-comments guideline; instead, make
identifiers and small helper functions self-explanatory (e.g., rename variables
or functions related to previous value tracking and change detection, such as
any refs like previousXRef or usePreviousValue, or inline helper functions used
around the flagged spots) so the intent is clear without comments, and delete
the commented lines at the five reported positions.
- Around line 77-123: The three useEffect blocks wrap state updates in
zero-delay setTimeouts (the ones comparing
drop.context_profile_context?.reaction with prevContextReactionRef.current and
calling setSelected, the one comparing reaction.profiles with
prevProfilesRef.current and calling setTotal/setHandles, and the one comparing
total with prevTotalRef.current and calling setAnimate). Remove the setTimeout
wrappers and their clearTimeout cleanup: perform the state updates directly
inside each effect when the condition passes, leaving the ref updates and
equality checks intact (references: prevContextReactionRef, setSelected,
prevProfilesRef, setTotal, setHandles, prevTotalRef, setAnimate).

In @hooks/useDoubleTap.ts:
- Around line 57-118: Remove the inline comment lines in this block so code
follows the "no comments" guideline: delete the comments before the useEffect
cleanup, the "// Double tap detected" and "// First tap or taps too far
apart/too slow" inside handleTap, the "// Set up single tap callback if
provided" before the onSingleTap branch, and the "// Small buffer after
maxDelay" at the setTimeout; leave all logic and references (useEffect cleanup
returning clearSingleTapTimeout and resetting pendingEventRef/lastTapRef, the
getDistance function, the handleTap callback, and usages of
clearSingleTapTimeout, lastTapRef, pendingEventRef, singleTapTimeoutRef)
unchanged.
- Around line 5-18: Remove all JSDoc-style comments from the DoubleTapOptions
interface and the hook's JSDoc block: delete the comment blocks above each
property in DoubleTapOptions (maxDelay, maxDistance, onDoubleTap, onSingleTap,
enabled) and remove the JSDoc header for the useDoubleTap hook so the code
relies on the self-explanatory type and parameter names (DoubleTapOptions and
useDoubleTap).
- Around line 131-155: Remove the inline explanatory comment inside the
onTouchEnd handler: delete the line "// Check if the touch moved too much (would
be a swipe/scroll)" in the useDoubleTap hook so the conditional using
moveDistance <= maxDistance remains but without the redundant comment; leave the
rest of the onTouchEnd implementation (touchStartRef handling, moveDistance
calc, call to handleTap, and touchStartRef reset) unchanged.
🧹 Nitpick comments (2)
components/common/icons/BoostIcon.tsx (1)

3-8: Consider removing the JSDoc comment.

Per coding guidelines, code should be self-explanatory without comments. The variant names ("filled", "outlined", "animated") are already descriptive. If documentation is desired, TypeScript's type system conveys the allowed values.

♻️ Proposed refactor
 interface BoostIconProps {
   readonly className?: string;
-  /**
-   * - "filled": Solid flame (for boosted state)
-   * - "outlined": Stroke-only flame (for non-boosted state)
-   * - "animated": Filled flame with inner highlight (for animations)
-   */
   readonly variant?: "filled" | "outlined" | "animated";
 }
components/drops/view/BoostedDropCard.tsx (1)

18-111: Well-structured accessible component.

Good use of memo, keyboard accessibility (role="button", tabIndex, onKeyDown), and next/image. The past tw-size-2 issue has been resolved.

Per coding guidelines, consider removing the inline comments ({/* Rank badge */}, {/* Author avatar */}, etc.) as the code structure is self-explanatory.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9694d2 and e899bdd.

📒 Files selected for processing (9)
  • components/common/icons/BoostIcon.tsx
  • components/drops/view/BoostedDropCard.tsx
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/DropBoostAnimation.tsx
  • components/waves/drops/WaveDropActionsBoost.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/wave-drops-all/index.tsx
  • hooks/useDoubleTap.ts
  • hooks/useWaveBoostedDrops.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/waves/drops/WaveDropActionsBoost.tsx
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

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

**/*.{ts,tsx,js,jsx}: Remove unnecessary Effects. If the Effect's only job is to derive or sync internal state, calculate during render or use useMemo instead.
Use useEffectEvent for non-reactive logic inside Effects to read the latest props/state without turning them into dependencies or causing unnecessary re-runs.
Use explicit caching with "use cache" directive at the top of Server Components, routes, or functions. Configure cacheComponents: true in next.config.ts as needed.

**/*.{ts,tsx,js,jsx}: Remove unnecessary Effects; if the Effect only derives state, compute during render instead
Use useEffectEvent when listening to external events but needing the latest props/state without re-running the Effect
Move data fetching from client Effects to Server Components; mutations go through Server Actions ('use server')

Files:

  • components/drops/view/BoostedDropCard.tsx
  • hooks/useWaveBoostedDrops.ts
  • components/common/icons/BoostIcon.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/wave-drops-all/index.tsx
  • hooks/useDoubleTap.ts
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/DropBoostAnimation.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

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

**/*.{tsx,jsx}: Use internal links via <Link> component from Next.js instead of <a> tags
Replace <img> elements with <Image /> from next/image

Files:

  • components/drops/view/BoostedDropCard.tsx
  • components/common/icons/BoostIcon.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/wave-drops-all/index.tsx
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/DropBoostAnimation.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Run npm run lint to ensure code satisfies ESLint (Next's Core Web Vitals + React Hooks). Code must pass linting before completing any task.

**/*.{js,ts,jsx,tsx}: Code must satisfy ESLint with Next's Core Web Vitals and React Hooks rules by running npm run lint
Do not add eslint-disable comments unless explicitly instructed; prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Files:

  • components/drops/view/BoostedDropCard.tsx
  • hooks/useWaveBoostedDrops.ts
  • components/common/icons/BoostIcon.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/wave-drops-all/index.tsx
  • hooks/useDoubleTap.ts
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/DropBoostAnimation.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

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

Files:

  • components/drops/view/BoostedDropCard.tsx
  • components/common/icons/BoostIcon.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/wave-drops-all/index.tsx
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/DropBoostAnimation.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Use TypeScript with React functional components and hooks. Follow existing code style and naming conventions.

Files:

  • components/drops/view/BoostedDropCard.tsx
  • hooks/useWaveBoostedDrops.ts
  • components/common/icons/BoostIcon.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/wave-drops-all/index.tsx
  • hooks/useDoubleTap.ts
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/DropBoostAnimation.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with React functional components and hooks

Files:

  • components/drops/view/BoostedDropCard.tsx
  • hooks/useWaveBoostedDrops.ts
  • components/common/icons/BoostIcon.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/wave-drops-all/index.tsx
  • hooks/useDoubleTap.ts
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/DropBoostAnimation.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer direct named imports from React (useMemo, useRef, FC) over React. namespace usage

Files:

  • components/drops/view/BoostedDropCard.tsx
  • hooks/useWaveBoostedDrops.ts
  • components/common/icons/BoostIcon.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/wave-drops-all/index.tsx
  • hooks/useDoubleTap.ts
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/DropBoostAnimation.tsx
**/*.{ts,js}

📄 CodeRabbit inference engine (AGENTS.md)

When parsing Seize URLs or similar, fail fast if base origin is unavailable instead of falling back to placeholder origins

Files:

  • hooks/useWaveBoostedDrops.ts
  • hooks/useDoubleTap.ts
🧠 Learnings (12)
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use react-query for data fetching

Applied to files:

  • hooks/useWaveBoostedDrops.ts
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Use FontAwesome for icons in React components

Applied to files:

  • components/common/icons/BoostIcon.tsx
📚 Learning: 2025-12-30T14:31:53.006Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:53.006Z
Learning: Fix issues with modernization aligned to React 19.2, React Compiler, and Next.js 16 conventions. Do not add `// eslint-disable` comments unless explicitly instructed.

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/CreateDropContent.tsx
  • components/waves/drops/DropBoostAnimation.tsx
📚 Learning: 2025-12-30T14:31:53.006Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:53.006Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript with React functional components and hooks. Follow existing code style and naming conventions.

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
  • hooks/useDoubleTap.ts
📚 Learning: 2025-12-30T14:31:53.006Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:53.006Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `useEffectEvent` for non-reactive logic inside Effects to read the latest props/state without turning them into dependencies or causing unnecessary re-runs.

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
  • hooks/useDoubleTap.ts
  • components/waves/drops/DropBoostAnimation.tsx
📚 Learning: 2025-12-30T14:31:53.006Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:53.006Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Remove unnecessary Effects. If the Effect's only job is to derive or sync internal state, calculate during render or use `useMemo` instead.

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/DropBoostAnimation.tsx
📚 Learning: 2025-12-30T14:32:19.360Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Remove unnecessary Effects; if the Effect only derives state, compute during render instead

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/DropBoostAnimation.tsx
📚 Learning: 2025-12-30T14:32:19.360Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `useEffectEvent` when listening to external events but needing the latest props/state without re-running the Effect

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/DropBoostAnimation.tsx
📚 Learning: 2025-12-30T14:32:19.360Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Prefer direct named imports from React (`useMemo`, `useRef`, `FC`) over `React.` namespace usage

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-12-30T14:32:19.360Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Do not add `eslint-disable` comments unless explicitly instructed; prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{tsx,jsx} : Always add `readonly` before props in React components

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-12-30T14:32:19.360Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{tsx,ts} : Use TypeScript with React functional components and hooks

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/wave-drops-all/index.tsx
  • hooks/useDoubleTap.ts
  • components/waves/CreateDropContent.tsx
🧬 Code graph analysis (5)
components/drops/view/BoostedDropCard.tsx (3)
generated/models/ApiDrop.ts (1)
  • ApiDrop (29-237)
components/ipfs/IPFSContext.tsx (1)
  • resolveIpfsUrlSync (77-89)
components/user/utils/UserCICAndLevel.tsx (1)
  • UserCICAndLevel (8-37)
hooks/useWaveBoostedDrops.ts (1)
generated/models/ApiDrop.ts (1)
  • ApiDrop (29-237)
components/waves/drops/WaveDropReactions.tsx (2)
components/waves/drops/reaction-utils.ts (1)
  • toProfileMin (76-105)
generated/models/ApiDropContextProfileContext.ts (1)
  • ApiDropContextProfileContext (16-65)
components/waves/drops/wave-drops-all/index.tsx (1)
hooks/useWaveBoostedDrops.ts (1)
  • useWaveBoostedDrops (19-45)
components/waves/CreateDropContent.tsx (2)
scripts/eslint-rule-summary.cjs (1)
  • value (124-124)
components/waves/create-wave/services/multiPartUpload.ts (1)
  • multiPartUpload (15-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (19)
hooks/useDoubleTap.ts (4)

1-3: LGTM!

The "use client" directive is appropriate, and the React imports follow the guideline to use direct named imports rather than the React.* namespace.


36-55: LGTM!

The hook signature, default parameters, and ref declarations are well-structured. The clearSingleTapTimeout helper is properly memoized with useCallback.


157-165: LGTM!

The onClick handler correctly uses e.nativeEvent.pointerType === "mouse" for reliable synthetic click detection, addressing the past review feedback. The implementation is clean and self-explanatory without comments.


167-174: LGTM!

The return object correctly exposes all three event handlers, and the default export is appropriate for this hook module.

components/waves/drops/WaveDropReactions.tsx (5)

142-192: LGTM: Next.js Image implementation is correct.

The emoji rendering logic properly uses Next.js Image with the fill prop and relative parent containers. The fallback to native emoji when custom images are unavailable is well-structured.


194-266: LGTM: Optimistic update logic is well-structured.

The optimistic update implementation correctly:

  • Clones data structures before mutation
  • Handles user profile addition/removal
  • Includes boosted: false in the default context (consistent with ApiDropContextProfileContext)
  • Provides a rollback mechanism

268-333: LGTM: Click handler with optimistic updates and error handling is correct.

The implementation properly:

  • Performs optimistic UI updates immediately
  • Calls the API asynchronously
  • Reverts all state changes symmetrically on error
  • Uses Set-based deduplication for handle management
  • Invokes the rollback function on failure

377-377: LGTM: Tailwind size utility simplifies styling.

The change to tw-size-5 is cleaner and more concise than separate tw-h-5 and tw-w-5 classes while maintaining the same dimensions.


39-39: No issue here. The reactions field in ApiDrop is a required, non-nullable Array<ApiDropReaction> (see generated/models/ApiDrop.ts), so drop.reactions.map() is type-safe and correct. Using optional chaining on a guaranteed-to-be-present field would be unnecessary defensive coding.

Likely an incorrect or invalid review comment.

hooks/useWaveBoostedDrops.ts (1)

1-45: LGTM!

Clean implementation using Page<ApiDrop> from helpers, which addresses the previous duplicate type concern. The rolling 24-hour window computed inside queryFn ensures fresh timestamp on each fetch. Caching strategy (1min stale, 30s refetch) and retry logic are reasonable for a trending/boosted feature.

components/waves/drops/DropBoostAnimation.tsx (2)

56-65: Previous concern addressed; implementation looks good.

The onComplete call has been correctly moved to a useEffect (lines 56-60), resolving the render-phase side effect anti-pattern. The reduced motion handling and portal-based rendering are appropriate for this animation overlay.


69-132: Animation logic is correct.

The conditional {animation && ...} inside AnimatePresence is the correct pattern for framer-motion to handle exit animations when animation becomes null. The onExitComplete callback properly triggers cleanup.

components/waves/drops/wave-drops-all/index.tsx (2)

85-86: Boosted drops integration looks good.

Clean hook usage with proper data flow to child components via boostedDrops and onBoostedDropClick props.


263-293: Quote click handling correctly prioritizes in-wave scroll.

The updated logic properly distinguishes between same-wave drops (scroll to target via queueSerialTarget) and cross-wave drops (navigate to the other wave). The dependency array is correct.

components/common/icons/BoostIcon.tsx (1)

11-38: LGTM!

Clean SVG icon implementation with proper variant handling. The conditional fill/stroke logic correctly switches between filled and outlined appearances.

components/waves/CreateDropContent.tsx (4)

519-522: LGTM: Optimized throttle behavior.

The early return when there's no content is a sensible optimization that avoids unnecessary "typing" indicator broadcasts.


942-947: LGTM: Proper use of useCallback for stable function references.

Wrapping focusMobileInput in useCallback (lines 942-947) is correct React practice, ensuring the function reference remains stable across renders. This is especially important since it's used in the useEffect dependency array on line 973.

The addition of a 100ms timeout for desktop focus (lines 969-973) aligns with the mobile approach and provides consistent behavior.

Also applies to: 969-973


1066-1076: LGTM: Defensive value parsing.

The enhanced number and string parsing logic properly handles edge cases:

  • Lines 1066-1069: Uses Number.isNaN() to detect invalid numeric conversions and defaults to null
  • Lines 1072-1076: Explicitly checks type before coercion, handling cases where newValue might be a number for string fields

380-381: Restore default fallbacks for consistency with codebase patterns.

The removed fallbacks on lines 380-381 and 393 are inconsistent with established patterns in ProfileHelpers.ts and reaction-utils.ts. Since these fields are used to construct the optimistic drop's author object, defensive fallbacks should be applied:

  • Line 380: winner_main_stage_drop_ids: connectedProfile.winner_main_stage_drop_ids ?? []
  • Line 381: pfp: connectedProfile.pfp ?? null
  • Line 393: primary_address: connectedProfile.primary_wallet ?? ""

Also applies to: 393-393

⛔ Skipped due to learnings
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:53.006Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Remove unnecessary Effects. If the Effect's only job is to derive or sync internal state, calculate during render or use `useMemo` instead.
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Remove unnecessary Effects; if the Effect only derives state, compute during render instead

Comment thread components/drops/view/BoostedDropCard.tsx
Comment thread components/waves/drops/WaveDropReactions.tsx
Comment thread components/waves/drops/WaveDropReactions.tsx
Comment thread hooks/useDoubleTap.ts
Comment thread hooks/useDoubleTap.ts
Comment thread hooks/useDoubleTap.ts
Signed-off-by: Simo <simo@6529.io>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jan 7, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @components/waves/drops/WaveDropReactions.tsx:
- Around line 71-74: Remove the inline comments flagged and make the code
self-explanatory by renaming the referenced refs and variables instead of
commenting: replace prevTotalRef, prevContextReactionRef, and prevProfilesRef
with more descriptive names (e.g., previousTotalCountRef,
previousContextReactionRef, previousReactionProfilesRef) and, where a comment
described intent, extract that logic into a clearly named helper function or
boolean (e.g., hasReactionChanged, shouldUpdateProfiles) used inline; delete the
remaining comment lines and ensure all remaining identifiers and helper names
clearly convey their purpose so comments are unnecessary.
- Around line 77-131: The useEffect callbacks in WaveDropReactions (the effects
that update selected, total/handles, and animate) unnecessarily wrap state
updates in setTimeout(..., 0); remove those setTimeout wrappers and their
clearTimeout cleanups and instead call setSelected, setTotal, setHandles, and
setAnimate directly inside the effects (keeping the same guard checks that
compare against prevContextReactionRef.current, prevProfilesRef.current, and
prevTotalRef.current); ensure you still update the prev* refs before calling the
state setters and retain the final effect that clears animate after 100ms using
setTimeout (only this timeout should remain).
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e899bdd and 884b553.

📒 Files selected for processing (3)
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • hooks/useDoubleTap.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • components/react-query-wrapper/ReactQueryWrapper.tsx
  • hooks/useDoubleTap.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

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

**/*.{ts,tsx,js,jsx}: Remove unnecessary Effects. If the Effect's only job is to derive or sync internal state, calculate during render or use useMemo instead.
Use useEffectEvent for non-reactive logic inside Effects to read the latest props/state without turning them into dependencies or causing unnecessary re-runs.
Use explicit caching with "use cache" directive at the top of Server Components, routes, or functions. Configure cacheComponents: true in next.config.ts as needed.

**/*.{ts,tsx,js,jsx}: Remove unnecessary Effects; if the Effect only derives state, compute during render instead
Use useEffectEvent when listening to external events but needing the latest props/state without re-running the Effect
Move data fetching from client Effects to Server Components; mutations go through Server Actions ('use server')

Files:

  • components/waves/drops/WaveDropReactions.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursorrules)

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

**/*.{tsx,jsx}: Use internal links via <Link> component from Next.js instead of <a> tags
Replace <img> elements with <Image /> from next/image

Files:

  • components/waves/drops/WaveDropReactions.tsx
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Run npm run lint to ensure code satisfies ESLint (Next's Core Web Vitals + React Hooks). Code must pass linting before completing any task.

**/*.{js,ts,jsx,tsx}: Code must satisfy ESLint with Next's Core Web Vitals and React Hooks rules by running npm run lint
Do not add eslint-disable comments unless explicitly instructed; prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Files:

  • components/waves/drops/WaveDropReactions.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

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

Files:

  • components/waves/drops/WaveDropReactions.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (GEMINI.md)

Use TypeScript with React functional components and hooks. Follow existing code style and naming conventions.

Files:

  • components/waves/drops/WaveDropReactions.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with React functional components and hooks

Files:

  • components/waves/drops/WaveDropReactions.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer direct named imports from React (useMemo, useRef, FC) over React. namespace usage

Files:

  • components/waves/drops/WaveDropReactions.tsx
🧠 Learnings (10)
📚 Learning: 2025-12-30T14:31:53.006Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:53.006Z
Learning: Fix issues with modernization aligned to React 19.2, React Compiler, and Next.js 16 conventions. Do not add `// eslint-disable` comments unless explicitly instructed.

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-12-30T14:31:53.006Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:53.006Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `useEffectEvent` for non-reactive logic inside Effects to read the latest props/state without turning them into dependencies or causing unnecessary re-runs.

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-12-30T14:31:53.006Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:53.006Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Remove unnecessary Effects. If the Effect's only job is to derive or sync internal state, calculate during render or use `useMemo` instead.

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-12-30T14:32:19.360Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Remove unnecessary Effects; if the Effect only derives state, compute during render instead

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-12-30T14:32:19.360Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Do not add `eslint-disable` comments unless explicitly instructed; prefer refactors aligned with React 19.2, React Compiler, and Next.js 16 conventions

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-12-30T14:31:53.006Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-30T14:31:53.006Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript with React functional components and hooks. Follow existing code style and naming conventions.

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-12-30T14:32:19.360Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Prefer direct named imports from React (`useMemo`, `useRef`, `FC`) over `React.` namespace usage

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-11-25T08:35:58.729Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T08:35:58.729Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Do not include any comments in the code; it should be self-explanatory

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-12-30T14:32:19.360Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `useEffectEvent` when listening to external events but needing the latest props/state without re-running the Effect

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
📚 Learning: 2025-12-30T14:32:19.360Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T14:32:19.360Z
Learning: Applies to **/*.{tsx,ts} : Use TypeScript with React functional components and hooks

Applied to files:

  • components/waves/drops/WaveDropReactions.tsx
🧬 Code graph analysis (1)
components/waves/drops/WaveDropReactions.tsx (2)
components/waves/drops/reaction-utils.ts (1)
  • toProfileMin (76-105)
generated/models/ApiDropContextProfileContext.ts (1)
  • ApiDropContextProfileContext (16-65)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
components/waves/drops/WaveDropReactions.tsx (6)

3-30: LGTM!

Imports are well-organized, use named imports from React as per guidelines, and properly include the Next.js Image component for optimized image handling.


146-194: LGTM!

Good use of useMemo for derived emoji nodes, proper Next.js Image component usage with fill layout, and clean handling of both custom and native emoji fallbacks.


196-268: LGTM!

Well-structured optimistic update logic with proper defensive early return for missing waveId, correct default ApiDropContextProfileContext initialization including the new boosted field, and complete dependency array.


270-335: LGTM!

The click handler correctly implements the optimistic update pattern: local state is updated immediately, then the API is called, and on error all changes are properly reverted. The use of Set for deduplication in handle arrays is appropriate.


337-406: LGTM!

Clean implementation of tooltip text with truncation, proper conditional rendering, and well-organized styling logic using clsx.


36-48: No action needed. The drop.reactions field is guaranteed to be defined as a non-optional array in the ApiDrop type, making the direct mapping without optional chaining safe and correct.

Comment thread components/waves/drops/WaveDropReactions.tsx
Comment thread components/waves/drops/WaveDropReactions.tsx
@simo6529 simo6529 merged commit acb01f0 into main Jan 8, 2026
11 checks passed
@simo6529 simo6529 deleted the boosted-drops branch January 8, 2026 07:22
This was referenced Jan 8, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Feb 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants