Skip to content

quick vote modal ux#2169

Merged
ragnep merged 9 commits intomainfrom
quick-vote-ux
Mar 26, 2026
Merged

quick vote modal ux#2169
ragnep merged 9 commits intomainfrom
quick-vote-ux

Conversation

@ragnep
Copy link
Copy Markdown
Contributor

@ragnep ragnep commented Mar 25, 2026

Summary by CodeRabbit

  • New Features

    • New quick-vote action bar with quick-amount buttons, custom-amount input (Enter to submit), feedback states and Skip control.
    • New zap icon for vote actions and improved swipe/commit flow for mobile voting.
    • Improved remaining/unexplored count tracking with formatted numbers and recovery logic.
  • UI/UX Improvements

    • Quick-vote trigger redesigned as a card-style row showing “Uncast votes” and “unexplored”.
    • Dialog, responsive headers, skeletons and focus/close behaviors refreshed for mobile and desktop.

Signed-off-by: ragnep <ragneinfo@gmail.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 25, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Reworks Memes Quick Vote: adds a Zap SVG icon, extracts/rewires quick-vote UI (action bar, amount buttons, custom row), introduces swipe hook with Swiper fallback, centralizes optimistic queue/storage logic, changes discovery/hide semantics, and updates dialog, skeleton, and responsive behaviors.

Changes

Cohort / File(s) Summary
Icon
components/brain/left-sidebar/waves/MemesWaveZapIcon.tsx
Add new MemesWaveZapIcon SVG React component (accepts SVG props).
Footer & Trigger
components/brain/left-sidebar/waves/MemesWaveFooter.tsx, components/brain/left-sidebar/waves/MemesWaveQuickVoteTrigger.tsx
Replace BoltIcon with MemesWaveZapIcon; rebuild quick-vote CTA layout, aria-label/content, and styling.
Quick-vote UI (new components)
components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteActionBar.tsx, .../MemesQuickVoteAmountButton.tsx, .../MemesQuickVoteCustomAmountRow.tsx, .../MemesQuickVoteDropHeader.tsx, .../MemesQuickVoteQuickAmountsRow.tsx
Add action-bar and multiple reusable quick-vote components (amount buttons, quick amounts row, custom input row, drop header) and wire accessibility/feedback states.
Preview & Swipe
components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVotePreview.tsx, components/brain/left-sidebar/waves/memes-quick-vote/useMemesQuickVotePreviewSwipe.ts
Extract useMemesQuickVotePreviewSwipe hook; integrate Swiper and fallback touch handling; centralize swipe transform/commit and transition-driven commits.
Dialog & Skeletons
components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx, .../MemesQuickVoteDialogSkeleton.tsx
Restructure dialog layout/close behavior, add onClose, implement two-phase bar-feedback flow, and refactor skeletons for mobile/desktop.
Controls Composition
components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteControls.tsx
Replace previous inline controls with composed header + MemesQuickVoteActionBar; update prop signatures and thread vote-feedback props.
Optimistic & Queue logic
hooks/useMemesQuickVoteQueue.ts, hooks/useMemesQuickVoteQueue.optimistic.ts
Move optimistic remaining-power state and vote handlers into new optimistic module; refactor queue to use composed handlers and optimistic state.
Discovery / Storage / Cleanup
hooks/memesQuickVote.helpers.ts, hooks/useMemesQuickVoteDiscovery.ts, hooks/useMemesQuickVoteHiddenSummaryCleanup.ts, hooks/useMemesQuickVoteStorage.ts
Add isMemesQuickVoteDiscoverableDrop and remaining-count util; change defer→hide semantics, split skipped-drop storage, add hidden-summary cleanup, and validate skipped ids via queries.
Queue helpers / Active drop
hooks/memesQuickVote.queue.helpers.ts, hooks/useMemesQuickVoteActiveDrop.ts
Simplify deferred logic (deferredIds removed/ignored), unify candidate selection, and use discoverability helper to gate invalidation.
Footer stats & Wave hook
hooks/useMemesWaveFooterStats.ts
Compute unratedCount subtracting persisted skippedDropIds, add recovery fetch to restore visibility, and clear skipped IDs after summary completes.
Errors & query key shape
hooks/memesQuickVote.errors.ts, hooks/memesQuickVote.query.ts
Add error helpers to extract HTTP-like status and detect missing-drop errors; change drop query key to include contextProfile and proxyId.
Prefetch & other wiring
hooks/usePrefetchMemesQuickVote.ts, hooks/useMemesQuickVoteActiveDrop.ts, hooks/useMemesQuickVoteDiscovery.ts
Propagate proxyId into storage/prefetch, update prefetch query keys, and replace deferDropId with hideDropId in discovery hook.
Other minor UI tweaks
components/brain/my-stream/..., components/common/RecipientSelector.tsx, components/home/..., components/latest-activity/..., components/meme-calendar/..., components/user/collected/...
Responsive back-button breakpoint change, small Tailwind/classname reorderings, import reorderings, and minor text-size adjustments.

Sequence Diagram(s)

sequenceDiagram
  participant User as User
  participant Preview as Preview Card (Swiper / Hook)
  participant Dialog as QuickVote Dialog / ActionBar
  participant Optimistic as Optimistic Queue State
  participant API as Backend API

  User->>Preview: swipe or tap amount
  Preview->>Dialog: onVoteIntent(amount, source)
  Dialog->>Optimistic: queueVote(amount) (optimistic update)
  Optimistic->>API: sendVoteRequest(amount)
  API-->>Optimistic: success / failure
  Optimistic-->>Dialog: update optimistic state / trigger refetch
  Dialog-->>Preview: advance to next card / update counts
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐰
I hopped and coded through the night,
A zap, a swipe — the votes take flight,
Cards whoosh, counts shrink, the action’s bright,
Quick votes bloom in clustered light,
Hooray — the wave now feels just right

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'quick vote modal ux' is vague and uses non-descriptive language that does not convey specific information about the changeset's primary modifications. Specify the main change more clearly, such as 'Refactor quick vote modal UI components and interaction flows' or 'Restructure quick vote modal preview and voting controls layout'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch quick-vote-ux

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

Caution

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

⚠️ Outside diff range comments (1)
components/brain/left-sidebar/waves/MemesWaveFooter.tsx (1)

66-76: ⚠️ Potential issue | 🟠 Major

Avoid rendering a dead quick-vote button.

When unratedCount is 0, this still renders as an enabled button, but the handlers immediately return. That leaves a focusable control that does nothing in expanded mode, while the collapsed variant hides the trigger entirely. Either swap the wrapper to a non-interactive container for the empty state or disable the button.

Also applies to: 93-95

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/brain/left-sidebar/waves/MemesWaveFooter.tsx` around lines 66 -
76, The quick-vote trigger renders a focusable button even when unratedCount ===
0; change the behavior so the control is non-interactive in that empty state:
when unratedCount is 0 either render a non-interactive wrapper (swap the
<button> to a <div> or similar) or keep the button but set
disabled/aria-disabled and remove onClick/onFocus/onMouseEnter handlers to
prevent a focusable dead control; update the code paths that reference
handleOpenQuickVote and handlePrefetchQuickVote and the element rendering the
votingLabel/unratedCount (also apply the same change to the similar control
around the other instance at lines 93-95) and adjust CSS classes (remove
cursor-pointer and interactive styles) so visual appearance remains consistent
while accessibility is correct.
🧹 Nitpick comments (3)
components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteActionBar.tsx (1)

45-48: Minor: Redundant parsing of customValue.

The customAmountLabel parsing logic parses customValue twice when displaying the vote button label. Consider extracting the parsed value.

♻️ Suggested improvement
+  const parsedCustomValue = Number.parseInt(customValue.trim(), 10);
   const customAmountLabel =
-    customValue.trim().length > 0 && Number.parseInt(customValue, 10) > 0
-      ? formatNumberWithCommas(Number.parseInt(customValue, 10))
+    customValue.trim().length > 0 && parsedCustomValue > 0
+      ? formatNumberWithCommas(parsedCustomValue)
       : null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteActionBar.tsx`
around lines 45 - 48, customAmountLabel currently calls
Number.parseInt(customValue, 10) twice; extract the parsed integer into a single
variable (e.g., parsedCustomValue) and reuse it to avoid redundant parsing,
ensuring you still validate trimmed input and that parsedCustomValue > 0 before
calling formatNumberWithCommas for the label. Reference: customAmountLabel,
customValue, formatNumberWithCommas.
components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx (1)

138-250: Consider extracting duplicated props/callbacks.

The MemesQuickVotePreview and MemesQuickVoteControls are rendered with identical props in both mobile and non-mobile branches. While the layout containers differ, the component props are duplicated.

This is acceptable for now given the layout differences, but consider extracting common prop objects if maintenance becomes difficult:

const previewProps = {
  drop: activeDrop,
  isBusy: isAdvancing,
  isMobile,
  remainingCount,
  // ...rest
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx`
around lines 138 - 250, The preview and controls props are duplicated across the
isMobile branches; extract the shared prop objects for MemesQuickVotePreview
(e.g., previewProps containing drop, isBusy/isAdvancing, isMobile,
remainingCount, swipeVoteAmount, uncastPower, votingLabel, onAdvanceStart,
onSkip, onVoteWithSwipe/queueVoteAmount) and MemesQuickVoteControls (e.g.,
controlsProps containing customValue, drop, isCustomOpen,
isSubmitting/isAdvancing, latestUsedAmount, remainingCount,
quickAmounts/visibleQuickAmounts, uncastPower, votingLabel, onCustomChange,
onCustomSubmit/handleVoteAmount, onOpenCustom, onSkip/handleSkip,
onVoteAmount/handleVoteAmount) and replace the duplicated prop lists with
{...previewProps} and {...controlsProps} in both branches; keep the layout
container markup separate but reuse these objects to avoid duplication.
components/brain/left-sidebar/waves/MemesWaveZapIcon.tsx (1)

12-23: Make the shared icon decorative by default.

This icon is only used next to button labels in this PR. Defaulting it to aria-hidden and focusable="false" avoids duplicate announcements, while still letting callers opt into an accessible label via rest.

Suggested tweak
     <svg
+      aria-hidden="true"
+      focusable="false"
       xmlns="http://www.w3.org/2000/svg"
       width="24"
       height="24"
       viewBox="0 0 24 24"
       fill="currentColor"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/brain/left-sidebar/waves/MemesWaveZapIcon.tsx` around lines 12 -
23, The SVG in MemesWaveZapIcon (the <svg ... className={className} {...rest}>)
should be decorative by default: add aria-hidden="true" and focusable="false"
attributes to the svg element but ensure callers can override via rest (i.e.,
apply defaults that are replaceable by values in rest). Update the svg element
in the MemesWaveZapIcon component to include these default accessibility
attributes while preserving the existing className and spread of rest props.
🤖 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/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteActionBar.tsx`:
- Line 61: In MemesQuickVoteActionBar, the span element in the render contains
duplicate/conflicting Tailwind classes ("tw-text-primary-300" followed by
"tw-text-zinc-400"); remove the unused/overridden class so only the intended
color class remains (update the className on the span inside the
MemesQuickVoteActionBar component to remove either "tw-text-primary-300" or
"tw-text-zinc-400" so there is no duplicate/overriding text-color class).

In `@components/brain/left-sidebar/waves/MemesWaveFooter.tsx`:
- Around line 68-72: The aria-label for the footer is ambiguous for the second
count—update the aria-label expression (the attribute using
formatNumberWithCommas, uncastPower, votingLabel, and unratedCount) so the
trailing number mirrors the visible UI wording (use "unexplored" instead of the
generic "left"); keep the existing votingLabel fallback and formatting but
replace the `${formatNumberWithCommas(unratedCount)} left` fragment with
`${formatNumberWithCommas(unratedCount)} unexplored` (or equivalent phrasing) to
restore clarity for screen readers.

---

Outside diff comments:
In `@components/brain/left-sidebar/waves/MemesWaveFooter.tsx`:
- Around line 66-76: The quick-vote trigger renders a focusable button even when
unratedCount === 0; change the behavior so the control is non-interactive in
that empty state: when unratedCount is 0 either render a non-interactive wrapper
(swap the <button> to a <div> or similar) or keep the button but set
disabled/aria-disabled and remove onClick/onFocus/onMouseEnter handlers to
prevent a focusable dead control; update the code paths that reference
handleOpenQuickVote and handlePrefetchQuickVote and the element rendering the
votingLabel/unratedCount (also apply the same change to the similar control
around the other instance at lines 93-95) and adjust CSS classes (remove
cursor-pointer and interactive styles) so visual appearance remains consistent
while accessibility is correct.

---

Nitpick comments:
In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteActionBar.tsx`:
- Around line 45-48: customAmountLabel currently calls
Number.parseInt(customValue, 10) twice; extract the parsed integer into a single
variable (e.g., parsedCustomValue) and reuse it to avoid redundant parsing,
ensuring you still validate trimmed input and that parsedCustomValue > 0 before
calling formatNumberWithCommas for the label. Reference: customAmountLabel,
customValue, formatNumberWithCommas.

In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx`:
- Around line 138-250: The preview and controls props are duplicated across the
isMobile branches; extract the shared prop objects for MemesQuickVotePreview
(e.g., previewProps containing drop, isBusy/isAdvancing, isMobile,
remainingCount, swipeVoteAmount, uncastPower, votingLabel, onAdvanceStart,
onSkip, onVoteWithSwipe/queueVoteAmount) and MemesQuickVoteControls (e.g.,
controlsProps containing customValue, drop, isCustomOpen,
isSubmitting/isAdvancing, latestUsedAmount, remainingCount,
quickAmounts/visibleQuickAmounts, uncastPower, votingLabel, onCustomChange,
onCustomSubmit/handleVoteAmount, onOpenCustom, onSkip/handleSkip,
onVoteAmount/handleVoteAmount) and replace the duplicated prop lists with
{...previewProps} and {...controlsProps} in both branches; keep the layout
container markup separate but reuse these objects to avoid duplication.

In `@components/brain/left-sidebar/waves/MemesWaveZapIcon.tsx`:
- Around line 12-23: The SVG in MemesWaveZapIcon (the <svg ...
className={className} {...rest}>) should be decorative by default: add
aria-hidden="true" and focusable="false" attributes to the svg element but
ensure callers can override via rest (i.e., apply defaults that are replaceable
by values in rest). Update the svg element in the MemesWaveZapIcon component to
include these default accessibility attributes while preserving the existing
className and spread of rest props.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8fdbe3bb-9558-4947-9c69-c1ef9848bc6b

📥 Commits

Reviewing files that changed from the base of the PR and between a33789f and 395d230.

📒 Files selected for processing (10)
  • components/brain/left-sidebar/waves/MemesWaveFooter.tsx
  • components/brain/left-sidebar/waves/MemesWaveQuickVoteTrigger.tsx
  • components/brain/left-sidebar/waves/MemesWaveZapIcon.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteActionBar.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteControls.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialogSkeleton.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVotePreview.tsx
  • hooks/memesQuickVote.helpers.ts
  • hooks/useMemesQuickVoteQueue.ts

Comment thread components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteActionBar.tsx Outdated
Comment thread components/brain/left-sidebar/waves/MemesWaveFooter.tsx Outdated
Signed-off-by: ragnep <ragneinfo@gmail.com>
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: 3

Caution

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

⚠️ Outside diff range comments (1)
components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVotePreview.tsx (1)

28-33: ⚠️ Potential issue | 🟠 Major

Failed swipe votes leave the preview blank.

This flow never resets swipeExitDirection after onVoteWithSwipe(). When queueVoteAmount() in components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx, Lines 85-91 returns false, the same drop stays mounted but the card stays translated/transparent. Make the swipe callback report success so the swipe hook can restore the card on failed submits.

Also applies to: 50-61, 189-210, 334-350

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVotePreview.tsx`
around lines 28 - 33, The preview never resets swipeExitDirection after
onVoteWithSwipe(), so failed queueVoteAmount() submissions leave the card
translated/transparent; update the swipe callback contract so onVoteWithSwipe
reports success (e.g., return boolean or accept a completion callback/Promise
that resolves true/false) and make callers in MemesQuickVoteDialog (where
queueVoteAmount() is called) return/resolve false on failure; then, in the swipe
hook/handler that reads swipeExitDirection, detect the failure result and reset
swipeExitDirection (or call the provided restore callback) to restore the card
state. Ensure all usages (the preview's onVoteWithSwipe invocation and the
implementations tied to queueVoteAmount()) follow the new success-reporting
pattern so failed submits trigger the swipe restoration.
🤖 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/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx`:
- Around line 117-137: The mobile close affordance is hidden in some content
branches; ensure a persistent close button/header is rendered for mobile by
moving or duplicating the close button out of the conditional content inside
MemesQuickVoteDialog (use the existing isMobile header block or create a small
persistent header component) so it is always present even when
MemesQuickVoteDialogContent renders the done/error/loading states; keep using
the same onClose handler and remainingCount display (references: isMobile,
onClose, remainingCount, MemesQuickVoteDialogContent, XMarkIcon) and remove the
sm:tw-hidden/sm:tw-inline-flex behavior that hides the root close in
small-screen states so mobile users can always dismiss the sheet.

In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVotePreview.tsx`:
- Around line 100-103: The touch-start handler (handleCardTouchStart) currently
returns early whenever hasTouchEventPageX(event) is true, which prevents arming
isFallbackTouchActiveRef in the native-touch fallback path; change the condition
so the handler only bails out when no pageX is present (i.e., if
(!hasTouchEventPageX(event)) return) or otherwise ensure that when the code is
running the fallback listeners it sets isFallbackTouchActiveRef.current = true
before returning; update corresponding touchmove/touchend logic that relies on
isFallbackTouchActiveRef (also referenced in touchmove handlers around lines
250-258 and 497-513) so the fallback path correctly arms and processes swipes.
- Around line 618-629: Remove the "tw-hidden" class from the div with
data-testid="quick-vote-preview-status" so the screen-reader-only spans are not
hidden from assistive tech; keep the swipeInstructionText and uncastPower spans
as "tw-sr-only" (they already are) and mark the decorative visible badge span
(the span that renders "{formatNumberWithCommas(remainingCount)} unexplored") as
aria-hidden="true" if it should not be announced, or leave it accessible if it
should be announced—this ensures the sr-only instructions (swipeInstructionText,
uncastPower) are reachable by screen readers while preserving the intended
visual/decorative behavior.

---

Outside diff comments:
In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVotePreview.tsx`:
- Around line 28-33: The preview never resets swipeExitDirection after
onVoteWithSwipe(), so failed queueVoteAmount() submissions leave the card
translated/transparent; update the swipe callback contract so onVoteWithSwipe
reports success (e.g., return boolean or accept a completion callback/Promise
that resolves true/false) and make callers in MemesQuickVoteDialog (where
queueVoteAmount() is called) return/resolve false on failure; then, in the swipe
hook/handler that reads swipeExitDirection, detect the failure result and reset
swipeExitDirection (or call the provided restore callback) to restore the card
state. Ensure all usages (the preview's onVoteWithSwipe invocation and the
implementations tied to queueVoteAmount()) follow the new success-reporting
pattern so failed submits trigger the swipe restoration.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0c604290-c656-4500-ab5d-d45ecc412fad

📥 Commits

Reviewing files that changed from the base of the PR and between 395d230 and e810d82.

📒 Files selected for processing (5)
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVotePreview.tsx
  • hooks/memesQuickVote.helpers.ts
  • hooks/useMemesQuickVoteDiscovery.ts
  • hooks/useMemesQuickVoteQueue.ts
✅ Files skipped from review due to trivial changes (1)
  • hooks/useMemesQuickVoteQueue.ts

Comment thread components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVotePreview.tsx Outdated
Comment thread components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVotePreview.tsx Outdated
ragnep added 4 commits March 26, 2026 15:22
Signed-off-by: ragnep <ragneinfo@gmail.com>
Signed-off-by: ragnep <ragneinfo@gmail.com>
Signed-off-by: ragnep <ragneinfo@gmail.com>
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

🧹 Nitpick comments (6)
components/meme-calendar/MemeCalendarOverview.tsx (1)

379-381: Consider if !important override is necessary.

Using !tw-text-3xl and md:!tw-text-4xl with the ! prefix forces !important, which can make styles harder to override and debug. If this is needed to override conflicting styles from a parent component, consider addressing the root cause instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/meme-calendar/MemeCalendarOverview.tsx` around lines 379 - 381,
The div rendering #{mintDetails.mintNumber.toLocaleString()} currently uses
forced-important Tailwind classes (`!tw-text-3xl md:!tw-text-4xl`); remove the
`!` prefixes from the className to avoid `!important` overrides (change to
`tw-text-3xl tw-font-bold md:tw-text-4xl`) and only reintroduce `!` if you can’t
resolve a conflicting parent style—if conflicts persist, locate the parent
component/style that is overriding font-size and fix that instead of using `!`
on the `mintDetails.mintNumber` element.
components/brain/left-sidebar/waves/memes-quick-vote/useMemesQuickVotePreviewSwipe.ts (1)

164-210: Scope the getComputedStyle shim to the fallback path.

Line 408 installs a permanent window.getComputedStyle wrapper on browsers without window.Touch, even when isMobile is false and the fallback listeners in Lines 411-525 never attach. Please only patch when the fallback swipe path is active, or undo the shim on cleanup.

Also applies to: 407-409

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/brain/left-sidebar/waves/memes-quick-vote/useMemesQuickVotePreviewSwipe.ts`
around lines 164 - 210, The getComputedStyle shim installed by
patchComputedStyleForFallbackSwipe is applied globally even when the fallback
swipe path is inactive; modify patchComputedStyleForFallbackSwipe (and callers)
so the shim is only installed when the fallback listeners are actually attached,
or make the shim reversible by storing originalGetComputedStyle and
QUICK_VOTE_COMPUTED_STYLE_PATCH_FLAG on the patchedWindow and restoring
window.getComputedStyle = originalGetComputedStyle and clearing the flag during
cleanup/unmount; reference the existing symbols
patchComputedStyleForFallbackSwipe, QUICK_VOTE_COMPUTED_STYLE_PATCH_FLAG, and
originalGetComputedStyle to locate and implement the conditional installation or
restore logic.
components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteActionBar.tsx (1)

11-11: Duplicated type definition: VoteFeedbackSource.

This type is also defined in MemesQuickVoteDialog.tsx (Line 27). Consider extracting it to a shared types file to ensure consistency and avoid drift.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteActionBar.tsx`
at line 11, Extract the duplicated type VoteFeedbackSource into a shared module
and import it from both places: create a new types file (e.g., memesTypes.ts or
index in the memes folder) exporting type VoteFeedbackSource = "custom-submit" |
"quick-amount", then replace the local definitions in
MemesQuickVoteActionBar.tsx and MemesQuickVoteDialog.tsx with an import of
VoteFeedbackSource to keep a single source of truth and avoid drift.
hooks/useMemesQuickVoteQueue.optimistic.ts (1)

214-219: Consider clarifying the settledRemainingPower initialization logic.

The ternary chain here handles multiple cases but could benefit from a brief inline comment explaining when each branch applies:

  • First vote in queue: use drop's max_rating
  • Subsequent votes: preserve existing or fallback

This is minor given the code works correctly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/useMemesQuickVoteQueue.optimistic.ts` around lines 214 - 219, The
ternary initializing settledRemainingPower is correct but unclear; add a concise
inline comment next to the settledRemainingPower expression (referencing
settledRemainingPower, baseState.pendingVotes, baseState.settledRemainingPower,
and drop.context_profile_context?.max_rating) that explains the branches: when
pendingVotes is empty use the drop's max_rating for the first vote, otherwise
preserve existing settledRemainingPower or fall back to drop's max_rating/null.
Keep the comment short and colocated with this expression for future
readability.
components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx (2)

145-155: Redundant setIsAdvancing(true) call at Line 153.

isAdvancing is already set to true on Line 145 before the timeout is scheduled. The second call inside the timeout callback is unnecessary.

♻️ Suggested fix
       clearBarVoteTimeout();
       barVoteTimeoutRef.current = window.setTimeout(() => {
         barVoteTimeoutRef.current = null;
-        setIsAdvancing(true);
         void queueVoteAmount(normalizedAmount);
       }, QUICK_VOTE_BAR_FEEDBACK_DURATION_MS);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx`
around lines 145 - 155, Remove the redundant setIsAdvancing(true) call inside
the timeout callback in the block that sets vote feedback and schedules
barVoteTimeoutRef; isAdvancing is already set at the start of the handler, so
delete the second setIsAdvancing(true) (the one inside the window.setTimeout
callback) and leave setVoteFeedback, clearBarVoteTimeout, barVoteTimeoutRef
assignment, and the void queueVoteAmount(normalizedAmount) call intact;
references to help locate the change: setIsAdvancing, setVoteFeedback,
clearBarVoteTimeout, barVoteTimeoutRef, queueVoteAmount,
QUICK_VOTE_BAR_FEEDBACK_DURATION_MS.

485-499: Unusual key dependency on isOpen.

The key includes isOpen ? "open" : "closed", which would force a remount when the dialog reopens. However, when isOpen is false, the dialog is closed and the component isn't visible anyway. If the intent is to reset state on each open, this works but could be clearer with a comment. If unintentional, the isOpen portion can be removed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx`
around lines 485 - 499, The key for the MemesQuickVoteDialogContent includes
isOpen which will force a remount on open/close; decide whether you actually
need that behavior and either remove isOpen from the key (change key to use
sessionId and activeDrop.serial_no only) or keep it but add a clear comment
above the JSX explaining that including isOpen intentionally forces a remount to
reset internal state on each open; update the key string used in the
MemesQuickVoteDialogContent prop accordingly and retain other props as-is.
🤖 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/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialogSkeleton.tsx`:
- Around line 47-50: Replace the problematic shorthand opacity usage
`tw-border-white/6` with the arbitrary value format `tw-border-white/[0.06]` in
the MemesQuickVoteDialogSkeleton markup: update the outer container div that
currently uses `tw-border-white/6` and the inner controls wrapper div (the one
containing `SkeletonBlock` and the inner rounded control group) so both use
`tw-border-white/[0.06]`; search for the class string occurrences in
MemesQuickVoteDialogSkeleton.tsx to make the two replacements and ensure
consistency with other nearby tailwind arbitrary opacity usages.
- Line 45: Replace the invalid Tailwind opacity modifier in the SkeletonBlock
className: locate the SkeletonBlock component instance (symbol: SkeletonBlock)
that currently uses "tw-bg-white/8" and change it to use Tailwind's arbitrary
opacity syntax "tw-bg-white/[0.08]" so the background white has 8% opacity
(ensure you update the className string where "tw-bg-white/8" appears).

In
`@components/brain/left-sidebar/waves/memes-quick-vote/useMemesQuickVotePreviewSwipe.ts`:
- Around line 393-405: The render reads window.Touch (canUseSwiperTouchSurface)
synchronously which breaks SSR hydration; instead compute touch-surface support
after mount and pass that result into useQuickVotePreviewSwipeState. Change
canUseSwiperTouchSurface from a render-time const to a stateful value set inside
useEffect (or via an isMounted hook) and derive usesTransitionlessSwipeCommit as
isMobile && !canUseSwiperTouchSurfaceState; then call
useQuickVotePreviewSwipeState with the new post-mount boolean so the initial
server render matches the client before hydration (refer to
canUseSwiperTouchSurface, swipeState, and useQuickVotePreviewSwipeState).
- Around line 426-445: Remove the spurious check against hasTouchEventPageX in
the handleTouchStart handler so the fallback path can initialize on environments
that synthesize pageX; specifically, in useMemesQuickVotePreviewSwipe's
handleTouchStart replace the conditional that returns when
hasTouchEventPageX(event) || isFallbackTouchActive.current with a check only for
isFallbackTouchActive.current, leaving the early returns for isBusy and
swipeState.swipeExitDirection and keeping calls to
swipeState.resetFallbackTouch, getTouchEventClientX, and the assignments to
fallbackTouchStartRef.current / fallbackTouchCurrentRef.current intact.

In `@components/meme-calendar/MemeCalendarOverview.tsx`:
- Around line 500-510: Validate and sanitize the fontColor and size values
before embedding them into inline styles used by printCalendarInvites to prevent
future XSS regressions: ensure fontColor matches a safe hex/css color whitelist
or a strict regex (e.g., `#RGB/`#RRGGBB or known named colors) and ensure size is
a positive integer within expected bounds (e.g., 10–72); if validation fails,
replace with a safe default (e.g., "#000" and 18). Apply this check either
inside printCalendarInvites (where the CSS is generated) or immediately before
calling it in MemeCalendarOverview, and keep
createGoogleCalendarUrl/createIcsDataUrl unchanged since they already escape
parameters.

In `@hooks/memesQuickVote.queue.helpers.ts`:
- Around line 75-77: The returned state is incorrectly hard-coded to
deferredIds: [] which permanently removes deferred items; restore the real
deferred queue and routing: return the actual deferredIds variable (not []),
update the removal handler (e.g., removeMemesQuickVoteDropId) so it moves IDs
into deferredIds instead of clearing them, and ensure deferMemesQuickVoteDropId
appends IDs to deferredIds rather than flowing them through the same removal
path; also make consumers that only read activeIds (consumers in
useMemesQuickVoteDiscovery and useMemesQuickVoteQueue) include deferredIds when
calculating temporary hides so the temporary-hide flow remains temporary.

---

Nitpick comments:
In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteActionBar.tsx`:
- Line 11: Extract the duplicated type VoteFeedbackSource into a shared module
and import it from both places: create a new types file (e.g., memesTypes.ts or
index in the memes folder) exporting type VoteFeedbackSource = "custom-submit" |
"quick-amount", then replace the local definitions in
MemesQuickVoteActionBar.tsx and MemesQuickVoteDialog.tsx with an import of
VoteFeedbackSource to keep a single source of truth and avoid drift.

In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx`:
- Around line 145-155: Remove the redundant setIsAdvancing(true) call inside the
timeout callback in the block that sets vote feedback and schedules
barVoteTimeoutRef; isAdvancing is already set at the start of the handler, so
delete the second setIsAdvancing(true) (the one inside the window.setTimeout
callback) and leave setVoteFeedback, clearBarVoteTimeout, barVoteTimeoutRef
assignment, and the void queueVoteAmount(normalizedAmount) call intact;
references to help locate the change: setIsAdvancing, setVoteFeedback,
clearBarVoteTimeout, barVoteTimeoutRef, queueVoteAmount,
QUICK_VOTE_BAR_FEEDBACK_DURATION_MS.
- Around line 485-499: The key for the MemesQuickVoteDialogContent includes
isOpen which will force a remount on open/close; decide whether you actually
need that behavior and either remove isOpen from the key (change key to use
sessionId and activeDrop.serial_no only) or keep it but add a clear comment
above the JSX explaining that including isOpen intentionally forces a remount to
reset internal state on each open; update the key string used in the
MemesQuickVoteDialogContent prop accordingly and retain other props as-is.

In
`@components/brain/left-sidebar/waves/memes-quick-vote/useMemesQuickVotePreviewSwipe.ts`:
- Around line 164-210: The getComputedStyle shim installed by
patchComputedStyleForFallbackSwipe is applied globally even when the fallback
swipe path is inactive; modify patchComputedStyleForFallbackSwipe (and callers)
so the shim is only installed when the fallback listeners are actually attached,
or make the shim reversible by storing originalGetComputedStyle and
QUICK_VOTE_COMPUTED_STYLE_PATCH_FLAG on the patchedWindow and restoring
window.getComputedStyle = originalGetComputedStyle and clearing the flag during
cleanup/unmount; reference the existing symbols
patchComputedStyleForFallbackSwipe, QUICK_VOTE_COMPUTED_STYLE_PATCH_FLAG, and
originalGetComputedStyle to locate and implement the conditional installation or
restore logic.

In `@components/meme-calendar/MemeCalendarOverview.tsx`:
- Around line 379-381: The div rendering
#{mintDetails.mintNumber.toLocaleString()} currently uses forced-important
Tailwind classes (`!tw-text-3xl md:!tw-text-4xl`); remove the `!` prefixes from
the className to avoid `!important` overrides (change to `tw-text-3xl
tw-font-bold md:tw-text-4xl`) and only reintroduce `!` if you can’t resolve a
conflicting parent style—if conflicts persist, locate the parent component/style
that is overriding font-size and fix that instead of using `!` on the
`mintDetails.mintNumber` element.

In `@hooks/useMemesQuickVoteQueue.optimistic.ts`:
- Around line 214-219: The ternary initializing settledRemainingPower is correct
but unclear; add a concise inline comment next to the settledRemainingPower
expression (referencing settledRemainingPower, baseState.pendingVotes,
baseState.settledRemainingPower, and drop.context_profile_context?.max_rating)
that explains the branches: when pendingVotes is empty use the drop's max_rating
for the first vote, otherwise preserve existing settledRemainingPower or fall
back to drop's max_rating/null. Keep the comment short and colocated with this
expression for future readability.
🪄 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: 62113569-1146-4e7a-bf79-2d0890f06b06

📥 Commits

Reviewing files that changed from the base of the PR and between e810d82 and 7997526.

📒 Files selected for processing (29)
  • components/brain/left-sidebar/waves/MemesWaveFooter.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteActionBar.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteAmountButton.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteControls.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteCustomAmountRow.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialogSkeleton.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDropHeader.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVotePreview.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteQuickAmountsRow.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/useMemesQuickVotePreviewSwipe.ts
  • components/brain/my-stream/tabs/MyStreamWaveTabsDefault.tsx
  • components/brain/my-stream/tabs/MyStreamWaveTabsMeme.tsx
  • components/common/RecipientSelector.tsx
  • components/home/now-minting/NowMintingStatsGrid.tsx
  • components/latest-activity/ActivityHeader.tsx
  • components/meme-calendar/MemeCalendarOverview.tsx
  • components/user/collected/cards/UserPageCollectedCard.tsx
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
  • hooks/memesQuickVote.errors.ts
  • hooks/memesQuickVote.helpers.ts
  • hooks/memesQuickVote.queue.helpers.ts
  • hooks/useMemesQuickVoteActiveDrop.ts
  • hooks/useMemesQuickVoteDiscovery.ts
  • hooks/useMemesQuickVoteHiddenSummaryCleanup.ts
  • hooks/useMemesQuickVoteQueue.optimistic.ts
  • hooks/useMemesQuickVoteQueue.ts
  • hooks/useMemesQuickVoteStorage.ts
  • hooks/useMemesWaveFooterStats.ts
✅ Files skipped from review due to trivial changes (6)
  • components/user/collected/cards/UserPageCollectedNetworkCards.tsx
  • components/brain/my-stream/tabs/MyStreamWaveTabsDefault.tsx
  • components/latest-activity/ActivityHeader.tsx
  • components/user/collected/cards/UserPageCollectedCard.tsx
  • components/home/now-minting/NowMintingStatsGrid.tsx
  • components/common/RecipientSelector.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • hooks/useMemesQuickVoteDiscovery.ts
  • hooks/memesQuickVote.helpers.ts

Comment thread components/meme-calendar/MemeCalendarOverview.tsx
Comment thread hooks/memesQuickVote.queue.helpers.ts
Signed-off-by: ragnep <ragneinfo@gmail.com>
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: 3

♻️ Duplicate comments (1)
components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx (1)

482-489: ⚠️ Potential issue | 🟠 Major

Mobile error/loading states still have no visible dismiss action.

The root close button is still hidden below md, and only the active-content/done branches render an in-content close control. On mobile, the full-screen error and skeleton states can still leave the user with no visible way to close the sheet.

Also applies to: 526-534

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx`
around lines 482 - 489, The mobile dialog states for error/loading lack a
visible dismiss action—update the branches in MemesQuickVoteDialog that render
MemesQuickVoteDialogErrorState and MemesQuickVoteDialogSkeleton (both places
around the activeDrop/isExhausted checks) to include a visible close control on
small screens: either render the same in-content close button used by
MemesQuickVoteDialogDoneState (calling onClose) or ensure the root close button
is not hidden below md for these branches; make the close control call onClose
and ensure the retryDiscovery button remains for the error state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hooks/memesQuickVote.query.ts`:
- Around line 25-40: The discovery cache keys ignore proxyId which lets a proxy
switch reuse another proxy’s discovery pages; update
getMemesQuickVoteDiscoveryStateKey and getMemesQuickVoteDiscoveryQueryKey to
accept the proxyId parameter and include it in the returned key object (use the
same shape used by getMemesQuickVoteDropQueryKey, e.g. include proxyId: proxyId
?? null in the key) so discovery pages are namespaced per proxy.

In `@hooks/useMemesQuickVoteStorage.ts`:
- Around line 73-103: The skipped-drop persistence currently uses
skippedStorageKey keyed only by wave/profile and setAndPersistSkippedDropIds
re-reads raw storage, which causes skips to bleed across proxyId and revive
filtered IDs; update getStorageKey usage in useMemesQuickVoteSkippedDropIds to
include proxyId (namespace by proxy) when constructing skippedStorageKey, and
change setAndPersistSkippedDropIds to base its current set on the
already-filtered storedSkippedDropIds (not readStoredStringArray) and write the
sanitized next array back via writeStoredStringArray; apply the same
proxyId-namespacing and writing-from-cleaned-current logic to the corresponding
block at lines 104-141.

In `@hooks/useMemesWaveFooterStats.ts`:
- Around line 52-89: The recovery query for recoveredFooterCount can return
undefined on error and hide the footer; wrap the queryFn body in a try/catch and
on error return a safe fallback instead of throwing: either the last-known
recovered value from the query cache (use react-query's queryClient.getQueryData
for the "memes-quick-vote-footer-recovery" key) or fall back to the current
stats.unratedCount, so the hook won't return EMPTY_STATS; update the
recoveredFooterCount queryFn (around fetchMemesQuickVoteDiscoveryBatch /
deriveMemesQuickVoteDiscoverySnapshot / getMemesQuickVoteDiscoveredQueue) to
catch errors and return the fallback when shouldRecoverVisibility is true.

---

Duplicate comments:
In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx`:
- Around line 482-489: The mobile dialog states for error/loading lack a visible
dismiss action—update the branches in MemesQuickVoteDialog that render
MemesQuickVoteDialogErrorState and MemesQuickVoteDialogSkeleton (both places
around the activeDrop/isExhausted checks) to include a visible close control on
small screens: either render the same in-content close button used by
MemesQuickVoteDialogDoneState (calling onClose) or ensure the root close button
is not hidden below md for these branches; make the close control call onClose
and ensure the retryDiscovery button remains for the error state.
🪄 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: 4ebe7fdd-6a59-413a-992b-0e9d1958745f

📥 Commits

Reviewing files that changed from the base of the PR and between 7997526 and de9f099.

📒 Files selected for processing (12)
  • components/brain/left-sidebar/waves/MemesWaveFooter.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialogSkeleton.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVotePreview.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/useMemesQuickVotePreviewSwipe.ts
  • hooks/memesQuickVote.query.ts
  • hooks/useMemesQuickVoteActiveDrop.ts
  • hooks/useMemesQuickVoteHiddenSummaryCleanup.ts
  • hooks/useMemesQuickVoteQueue.ts
  • hooks/useMemesQuickVoteStorage.ts
  • hooks/useMemesWaveFooterStats.ts
  • hooks/usePrefetchMemesQuickVote.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • components/brain/left-sidebar/waves/MemesWaveFooter.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialogSkeleton.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/useMemesQuickVotePreviewSwipe.ts
  • hooks/useMemesQuickVoteHiddenSummaryCleanup.ts

Comment thread hooks/memesQuickVote.query.ts
Comment thread hooks/useMemesQuickVoteStorage.ts
Comment thread hooks/useMemesWaveFooterStats.ts
Signed-off-by: ragnep <ragneinfo@gmail.com>
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: 3

🧹 Nitpick comments (1)
hooks/useMemesWaveFooterStats.ts (1)

52-61: Consider hashing skippedSignature for query key stability.

skippedDropIds.join(",") could produce an extremely long string if users skip many drops. This affects:

  • Query key comparison performance
  • Debugging/logging readability
  • Memory for cached key storage

A hash of the array (e.g., simple checksum or length+sample) would provide equivalent cache invalidation semantics with bounded size.

💡 Example using a simple signature
     queryKey: [
       "memes-quick-vote-footer-recovery",
       {
         contextProfile,
         proxyId,
-        skippedSignature: skippedDropIds.join(","),
+        skippedSignature: `${skippedDropIds.length}:${skippedDropIds.slice(0, 3).join(",")}`,
         waveId: memesWaveId,
       },
     ],

Or use a proper hash function if available in the codebase.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/useMemesWaveFooterStats.ts` around lines 52 - 61, The current queryKey
uses skippedSignature: skippedDropIds.join(",") which can become very long;
replace that with a bounded deterministic signature (e.g., a hash or checksum of
the sorted skippedDropIds array or a length+sample string) to keep the query key
size stable—update the hook that builds the useQuery key in
useMemesWaveFooterStats (where recoveredFooterCount is created) to compute a
deterministic signature from skippedDropIds (ensure stable ordering before
hashing), use an existing hash/util if available (or a small checksum fallback),
and pass that signature as skippedSignature alongside contextProfile, proxyId
and waveId (memesWaveId) so cache semantics remain correct but key size is
bounded.
🤖 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/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteActionBar.tsx`:
- Around line 107-125: The quick-amounts row is only visually hidden when
isCustomOpen is true but remains keyboard-focusable; update
MemesQuickVoteActionBar to add tabIndex={-1} to the wrapper (in addition to
aria-hidden) when isCustomOpen and pass a new prop (e.g., isHiddenFromKeyboard
or disableKeyboard) into MemesQuickVoteQuickAmountsRow; then update
MemesQuickVoteQuickAmountsRow to honor that prop by removing its interactive
elements from the tab order (set tabIndex=-1 and/or disabled on buttons) so
hidden controls cannot be focused via keyboard.
- Around line 53-56: customAmountLabel calculation uses Number.parseInt which
accepts partial numeric strings like "12abc"; update the check to only accept
fully-numeric, positive integer input by validating trimmed customValue (e.g.,
with a regex /^\d+$/ or by converting to Number and verifying Number.isInteger
and >0) before calling formatNumberWithCommas; modify the logic around
customAmountLabel and any use of Number.parseInt in this component
(customAmountLabel, customValue) so non-numeric or partially-numeric strings do
not produce a label.

In `@hooks/useMemesQuickVoteStorage.ts`:
- Around line 148-158: The current useMemo that computes skippedDropIds using
getEffectiveMemesQuickVoteSkippedDropIds will not re-run when the react-query
cache contents change because queryClient is a stable reference; update
skippedDropIds calculation to subscribe to relevant query cache/state changes
instead: either include the specific query data or status in the dependency list
(e.g. use queryClient.getQueryData([...drop query key...]) or
queryClient.getQueryState([...drop query key...]) as a dependency) or replace
the useMemo with a useEffect + useState that calls
getEffectiveMemesQuickVoteSkippedDropIds and re-runs when the relevant query
cache entries change by using queryClient.getQueryCache().subscribe and checking
queries matching the drops keys; reference skippedDropIds,
getEffectiveMemesQuickVoteSkippedDropIds, storedSkippedDropIds and queryClient
when locating where to apply this change.

---

Nitpick comments:
In `@hooks/useMemesWaveFooterStats.ts`:
- Around line 52-61: The current queryKey uses skippedSignature:
skippedDropIds.join(",") which can become very long; replace that with a bounded
deterministic signature (e.g., a hash or checksum of the sorted skippedDropIds
array or a length+sample string) to keep the query key size stable—update the
hook that builds the useQuery key in useMemesWaveFooterStats (where
recoveredFooterCount is created) to compute a deterministic signature from
skippedDropIds (ensure stable ordering before hashing), use an existing
hash/util if available (or a small checksum fallback), and pass that signature
as skippedSignature alongside contextProfile, proxyId and waveId (memesWaveId)
so cache semantics remain correct but key size is bounded.
🪄 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: 5ac9f347-0bca-4b07-9e6d-a093b48eeb19

📥 Commits

Reviewing files that changed from the base of the PR and between de9f099 and fd0d28f.

📒 Files selected for processing (5)
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteActionBar.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteAmountButton.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteCustomAmountRow.tsx
  • hooks/useMemesQuickVoteStorage.ts
  • hooks/useMemesWaveFooterStats.ts
✅ Files skipped from review due to trivial changes (2)
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteAmountButton.tsx
  • components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteCustomAmountRow.tsx

Comment thread hooks/useMemesQuickVoteStorage.ts
@ragnep ragnep merged commit e7b9e45 into main Mar 26, 2026
7 checks passed
@ragnep ragnep deleted the quick-vote-ux branch March 26, 2026 15:09
@sonarqubecloud
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot mentioned this pull request Apr 2, 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