Conversation
📝 WalkthroughWalkthroughThis pull request introduces a comprehensive "Memes Quick Vote" feature including new UI components for dialog and footer interactions, React hooks managing discovery/queue/submission state, localStorage persistence, API integration, and extensive test coverage. The feature centralizes quick-vote context logic and decouples view management from BrainMobile. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User/Mobile
participant Dialog as Quick Vote Dialog
participant Queue as useMemesQuickVoteQueue
participant Discovery as Discovery/Leaderboard
participant Storage as localStorage
participant API as Backend API
User->>Dialog: prefetchQuickVote()
Dialog->>Queue: Reserve session ID
Queue->>Discovery: Prefetch summary & discovery pages
Discovery->>API: GET leaderboard (unvoted_by_me=true)
API-->>Discovery: Drop IDs
Discovery->>API: GET drop details
API-->>Discovery: Hydrated drops
Discovery->>Storage: Cache drops by ID
User->>Dialog: openQuickVote()
Dialog->>Queue: Use reserved session ID
Queue->>Queue: Derive active drop from queue
Queue-->>Dialog: activeDrop + stats
Dialog->>User: Render preview + controls
alt Swipe Vote
User->>Dialog: Swipe left/right
Dialog->>Dialog: Animate swipe offset
else Click Vote
User->>Dialog: Click vote amount
end
Dialog->>Queue: submitVote(drop, amount)
Queue->>API: POST drop/{id}/ratings
API-->>Queue: Success + remaining power
Queue->>Storage: Persist recent amounts
Queue->>Queue: Advance to next drop
Queue->>Discovery: Refetch if needed
Queue-->>Dialog: Update activeDrop
Dialog->>User: Render next drop or done state
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes The diff introduces substantial new feature logic with multiple interdependent hooks managing complex state machines (discovery pagination, optimistic power tracking, session-based prefetching, submission queueing), new UI components with animation/swipe handling, storage abstraction with cross-tab sync, and extensive test coverage. The heterogeneous nature of changes (hooks, components, utilities, tests, integration points) across many files with dense logic requires careful reasoning for each subsystem and understanding of interactions between discovery, queue, submission, and dialog lifecycle. Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (11)
__tests__/components/auth/Auth.test.tsx (1)
31-45: Consider deduplicating theisAuthAddressAuthorizedmock implementation.The same matcher logic is defined twice, which can drift over time.
♻️ Suggested cleanup
+const defaultIsAuthAddressAuthorizedMock = ({ + address, + connectedAccounts, +}: { + readonly address: string | null | undefined; + readonly connectedAccounts: readonly { readonly address: string }[]; +}) => + Boolean( + address && + connectedAccounts.some( + (account) => account.address.toLowerCase() === address.toLowerCase() + ) + ); + jest.mock("@/services/auth/auth.utils", () => ({ canStoreAnotherWalletAccount: jest.fn(() => true), getWalletAddress: jest.fn(() => null), - isAuthAddressAuthorized: jest.fn( - ({ address, connectedAccounts }) => - Boolean( - address && - connectedAccounts.some( - (account) => account.address.toLowerCase() === address.toLowerCase() - ) - ) - ), + isAuthAddressAuthorized: jest.fn(defaultIsAuthAddressAuthorizedMock), ... })); ... - mockIsAuthAddressAuthorized.mockImplementation(({ address, connectedAccounts }) => - Boolean( - address && - connectedAccounts.some( - (account) => account.address.toLowerCase() === address.toLowerCase() - ) - ) - ); + mockIsAuthAddressAuthorized.mockImplementation( + defaultIsAuthAddressAuthorizedMock + );Also applies to: 203-219
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@__tests__/components/auth/Auth.test.tsx` around lines 31 - 45, The isAuthAddressAuthorized mock implementation is duplicated (appearing around the current block and again at lines 203-219); extract the matching logic into a single reusable helper function (e.g., isAuthAddressAuthorizedMatcher) and replace both jest.fn(...) implementations with jest.fn(isAuthAddressAuthorizedMatcher) so both mocks call the same function (update references to the parameter shape used in the existing implementations to preserve behavior).__tests__/services/auth.utils.test.ts (1)
147-197: Add an explicit null/undefined address test for the early-return path.Current additions are solid, but this branch is part of the contract and worth pinning in tests.
✅ Suggested test addition
+ it("returns false when address is null or undefined", () => { + expect( + isAuthAddressAuthorized({ + address: null, + connectedAccounts: [{ address: "0xabc" }], + }) + ).toBe(false); + + expect( + isAuthAddressAuthorized({ + address: undefined, + connectedAccounts: [{ address: "0xabc" }], + }) + ).toBe(false); + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@__tests__/services/auth.utils.test.ts` around lines 147 - 197, Add a unit test covering the early-return path when the supplied address is null or undefined by calling isAuthAddressAuthorized with address: null (and a separate case for undefined if desired) and a minimal connectedAccounts array, asserting it returns false; ensure the test references isAuthAddressAuthorized to locate the function and mirrors existing test style (use require("@/config/env") only if environment mutation is needed) so the behavior for null/undefined addresses is pinned.openapi.yaml (1)
5843-5851: Clarify unauthenticated behavior forunvoted_by_me.Line 5846 ties this filter to the authenticated user, but this operation documents only
200. Please specify whether unauthenticated requests withunvoted_by_me=trueshould return401or ignore the filter.📌 Suggested spec update (if unauthenticated should be rejected)
- name: unvoted_by_me in: query description: >- When true, only returns drops the authenticated user has not voted - on or currently has a 0 vote on + on or currently has a 0 vote on. Requires authentication. required: false schema: type: boolean default: false responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/ApiDropsLeaderboardPage" + "401": + description: Unauthorized🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi.yaml` around lines 5843 - 5851, The parameter unvoted_by_me currently implies authentication but the operation only documents a 200 response; update the OpenAPI spec to explicitly state expected behavior for unauthenticated requests: either (A) add a 401 response object to the operation and update the unvoted_by_me description to say the filter requires authentication and will return 401 if unset, or (B) update the unvoted_by_me description to say the filter is ignored for unauthenticated requests and keep only 200; modify the operation responses accordingly (add a 401 response if choosing A) and mention the authentication requirement next to the unvoted_by_me parameter so clients know whether the filter requires auth.components/brain/mobile/FloatingMemesQuickVoteTrigger.tsx (1)
15-19: Redundant guard conditions.The checks for
typeof uncastPower !== "number"andunratedCount <= 0are already enforced byuseMemesWaveFooterStatswhen determiningisReady. WhenisReadyistrue, the hook guaranteesuncastPoweris a number andunratedCount > 0.The redundancy is harmless (defensive coding), but simplifying would reduce noise:
Optional simplification
- if (!isReady || typeof uncastPower !== "number" || unratedCount <= 0) { + if (!isReady) { return null; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/mobile/FloatingMemesQuickVoteTrigger.tsx` around lines 15 - 19, The guard in FloatingMemesQuickVoteTrigger is overly defensive: remove the redundant checks for typeof uncastPower !== "number" and unratedCount <= 0 and only keep the isReady check from useMemesWaveFooterStats; update the early-return to "if (!isReady) return null" so the hook's contract is relied upon and the code is simplified while still returning null when the data isn't ready.hooks/useMemesWaveFooterStats.ts (1)
6-8: Consider exporting theMemesWaveFooterStatstype.The type is not exported, which may be helpful for consumers who want to explicitly type variables holding this hook's return value. While the return type is inferable via
typeof useMemesWaveFooterStats, directly exporting the type would improve API usability.Suggested export
-type MemesWaveFooterStats = MemesQuickVoteStats & { +export type MemesWaveFooterStats = MemesQuickVoteStats & { readonly isReady: boolean; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@hooks/useMemesWaveFooterStats.ts` around lines 6 - 8, Export the MemesWaveFooterStats type so consumers can explicitly annotate the hook's return value; update the declaration of MemesWaveFooterStats to be exported (export type MemesWaveFooterStats = ...) and ensure any related exports or imports that rely on this type (e.g., usages of useMemesWaveFooterStats) are adjusted accordingly to reference the exported type.hooks/useMemesQuickVoteContext.ts (1)
16-23: Consider memoizing derived values for consistency.The
contextProfileis memoized, butmemesWaveIdandisEnabledare recalculated on every render. While the computation is cheap, memoizing them would be more consistent with thecontextProfilepattern and could prevent unnecessary downstream effects if consumers rely on referential equality.♻️ Optional: Memoize derived values
+ const memesWaveId = useMemo( + () => seizeSettings.memes_wave_id ?? null, + [seizeSettings.memes_wave_id] + ); + + const isEnabled = useMemo( + () => + isLoaded && + typeof memesWaveId === "string" && + memesWaveId.length > 0 && + typeof contextProfile === "string" && + contextProfile.length > 0 && + activeProfileProxy === null, + [isLoaded, memesWaveId, contextProfile, activeProfileProxy] + ); - const memesWaveId = seizeSettings.memes_wave_id ?? null; - const isEnabled = - isLoaded && - typeof memesWaveId === "string" && - memesWaveId.length > 0 && - typeof contextProfile === "string" && - contextProfile.length > 0 && - activeProfileProxy === null;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@hooks/useMemesQuickVoteContext.ts` around lines 16 - 23, Derived values memesWaveId and isEnabled are being recalculated every render while contextProfile is memoized; wrap the computation of memesWaveId (from seizeSettings.memes_wave_id) and the boolean isEnabled in useMemo (or the same memo hook you used for contextProfile) so they preserve referential equality across renders and match the existing memoization pattern (refer to seizeSettings, memesWaveId, isEnabled, contextProfile, and activeProfileProxy to build the memo dependencies).__tests__/hooks/usePrefetchMemesQuickVote.test.tsx (1)
65-134: Consider adding test coverage for edge cases.The current test verifies the happy path well. Consider adding tests for:
- When
isEnabledisfalse(should not prefetch)- API error handling during prefetch
- Duplicate prefetch calls for the same session
These would increase confidence in the hook's robustness.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@__tests__/hooks/usePrefetchMemesQuickVote.test.tsx` around lines 65 - 134, Add unit tests for usePrefetchMemesQuickVote covering the suggested edge cases: create separate test cases that (1) mock useMemesQuickVoteContextMock to return isEnabled: false and assert commonApiFetchMock is not called and no queries are prefetched via QueryClient; (2) mock commonApiFetchMock to throw an error for leaderboard or drop endpoints and assert the hook handles it (e.g., does not crash and logs/returns expected state) by spying on console/process logger or checking QueryClient state; and (3) simulate duplicate invocations by calling the hook twice within the same test (or calling the prefetch function twice) and assert commonApiFetchMock is called only once per unique drop id (or that QueryClient avoids duplicate fetches) using jest mocks and useMemesQuickVoteStorageMock to control skippedDropIds. Ensure each test uses the existing setup patterns (QueryClient, createDrop, WAVE_ID, commonApiFetchMock, useMemesQuickVoteContextMock, useMemesQuickVoteStorageMock) for easy integration.components/brain/BrainMobile.tsx (1)
181-191: Keep the memes quick-vote owner scoped to memes waves.These guards only require “some wave” plus
!isDm, so the memes-specific trigger/dialog path is still mounted on non-memes waves too. Even if the child components currently short-circuit, keeping theisMemesWaveguard here avoids carrying extra quick-vote UI/state on unsupported wave types.💡 Suggested fix
const shouldMountFloatingQuickVoteEntry = isApp && + isMemesWave && hasWave && !!wave && activeView === BrainView.DEFAULT && !isDropOpen && !isDm; const shouldMountQuickVoteDialog = - isQuickVoteOpen || - shouldMountFloatingQuickVoteEntry || - activeView === BrainView.WAVES; + isMemesWave && + (isQuickVoteOpen || + shouldMountFloatingQuickVoteEntry || + activeView === BrainView.WAVES);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/BrainMobile.tsx` around lines 181 - 191, The quick-vote mounting logic (shouldMountFloatingQuickVoteEntry and shouldMountQuickVoteDialog) currently only checks for any wave and !isDm, so memes quick-vote UI can mount on non-memes waves; update the guards to include the isMemesWave boolean so the floating entry and dialog only mount for memes waves (i.e., add isMemesWave to the AND list in shouldMountFloatingQuickVoteEntry and ensure shouldMountQuickVoteDialog respects that same memes-scoped condition alongside isQuickVoteOpen and activeView checks involving BrainView.DEFAULT and BrainView.WAVES).components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteControls.tsx (1)
189-205: Input accessibility could be improved witharia-label.The custom amount input lacks an explicit accessible name for screen readers. While the visible label exists, adding an
aria-labelor ensuring the<label>properly wraps or references the input viahtmlFor/idwould improve accessibility.♿ Suggested improvement for input accessibility
+ <input + id="quick-vote-custom-amount" type="text" inputMode="numeric" pattern="[0-9]*" + aria-label="Custom vote amount" value={customValue}And update the label:
- <label className="tw-min-w-0 tw-flex-1"> + <label htmlFor="quick-vote-custom-amount" className="tw-min-w-0 tw-flex-1">🤖 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/MemesQuickVoteControls.tsx` around lines 189 - 205, The custom amount input in MemesQuickVoteControls lacks an explicit accessible name; update the <input> for customValue to include an aria-label (e.g., aria-label="Custom vote amount") or give it an id and connect the existing visible label using htmlFor so screen readers can identify it, and ensure handlers (onCustomChange, onCustomSubmit) and disabled/isSubmitting behavior remain unchanged.__tests__/components/brain/BrainMobile.test.tsx (1)
336-343: CSS class selector in test may be brittle.The test relies on a specific CSS class combination (
.tw-relative.tw-min-w-0.tw-flex-1) to verify layout positioning. Consider adding adata-testidto the wrapper element in the production code for more stable test queries.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@__tests__/components/brain/BrainMobile.test.tsx` around lines 336 - 343, The test in __tests__/components/brain/BrainMobile.test.tsx is using a brittle CSS selector ".tw-relative.tw-min-w-0.tw-flex-1" to locate the active pane; add a stable data attribute (e.g., data-testid="active-pane") to the wrapper element in the production component (the element that currently has those classes), then update the test to query that element via getByTestId("active-pane") and assert it contains screen.getByTestId("quick-vote-trigger") instead of using the long class selector.components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx (1)
82-88: Reset the local advancing lock on rejected vote submissions.
isAdvancingis only cleared on thefalsebranch today. IfsubmitVote()rejects, the controls stay disabled, and the swipe path also turns that into an unhandled promise rejection. A smalltry/catchorfinallyhere would make the dialog recover cleanly from transient failures.♻️ Possible hardening
const queueVoteAmount = async (amount: number | string) => { - const wasQueued = await submitVote(activeDrop, amount); - - if (!wasQueued) { - setIsAdvancing(false); - } + try { + const wasQueued = await submitVote(activeDrop, amount); + if (!wasQueued) { + setIsAdvancing(false); + } + } catch { + setIsAdvancing(false); + throw; + } };Also applies to: 122-133
🤖 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 82 - 88, The submitVote call in queueVoteAmount can reject and never clears the advancing lock; wrap the await submitVote(activeDrop, amount) in a try/catch/finally inside the queueVoteAmount function (and the other similar block around submitVote at the second occurrence) so that setIsAdvancing(false) is called in finally, handle/log the caught error to avoid an unhandled rejection, and preserve the existing wasQueued logic for the success path.
🤖 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/MemesQuickVotePreview.tsx`:
- Around line 216-227: The inline style always applies a transitionDuration so
the card eases toward the finger; update MemesQuickVotePreview so the style for
transitionDuration only applies for snap-back or committed exit (e.g., when
swipeExitDirection is truthy or when NOT currently dragging). Concretely,
introduce or reuse the component's dragging flag (e.g., isDragging /
isPointerDown / isSwiping) and change the style block that sets
transitionDuration: use `${SWIPE_EXIT_DURATION_MS}ms` only when
swipeExitDirection || !isDragging, otherwise leave transitionDuration undefined
(or 0) so the card follows the finger directly while dragging; keep
cardTransform, opacity, and touchAction logic unchanged.
In `@components/brain/left-sidebar/waves/MemesWaveFooter.tsx`:
- Around line 66-74: The button currently rendered in MemesWaveFooter remains
interactive even when unratedCount is 0; change the render so that when
unratedCount === 0 you render a disabled control (or non-interactive static
element) instead of an enabled button: stop attaching onClick
(handleOpenQuickVote) and pointer event handlers (handlePrefetchQuickVote), add
the disabled attribute or role="status"/aria-disabled as appropriate, and remove
or reduce hover/focus styling (the desktop-hover:hover:… and
onFocus/onMouseEnter handlers) so it looks visually inert; ensure the aria-label
still communicates the 0 state (using uncastPower and votingLabel) and that
keyboard/assistive users cannot tab to or activate the control when unratedCount
is 0.
In `@components/brain/mobile/useBrainMobileActiveView.ts`:
- Around line 149-158: The preserved-selection key ignores createParam so
entering create mode doesn't reset selection; update the context key logic used
to compute shellContextKey/currentContextKey to include createParam (and keep
viewParam) so create mode produces a different key than the normal shell view;
specifically modify shellContextKey (and therefore currentContextKey when
!waveId) to incorporate createParam alongside pathname and viewParam so that
getRouteDefaultView(createParam, ...) will take effect and stale shell
selections are not reused.
In `@config/nextConfig.ts`:
- Line 17: Replace the hard-coded allowedDevOrigins array with an
environment-driven value: add ALLOWED_DEV_ORIGINS to the env schema (e.g., in
env.schema.ts declare ALLOWED_DEV_ORIGINS as an optional string) and update the
allowedDevOrigins setting in nextConfig.ts to read from
publicEnv.ALLOWED_DEV_ORIGINS, split the comma-separated string, trim entries
and filter out empties so it produces an array; ensure the config falls back to
an empty array when the env var is unset.
In `@docs/specs/2026-03-20-memes-quick-vote-refactor.md`:
- Line 273: The document uses inconsistent pagination wording
("restart-from-page-`0`" vs "page `1`"); choose whether pages are 0-indexed or
1-indexed, standardize all occurrences to that choice, and update the lines
referencing "restart paginated discovery from page `1`", "page-`1` pass", and
"restart-from-page-`0`" so they all use the same index convention and include a
short note (one sentence) at the top of the pagination section stating the
chosen indexing convention.
In `@hooks/memesQuickVote.query.ts`:
- Around line 25-32: The cache key getMemesQuickVoteDropQueryKey currently only
varies by dropId and must be extended to include viewer identity so
viewer-specific fields (context_profile_context/eligibility/rating) don't leak
between profiles; update getMemesQuickVoteDropQueryKey to accept and include the
viewer identifiers (e.g., contextProfile and proxyId or the equivalent
context_profile_context) alongside QueryKey.DROP and dropId, and update callers
in usePrefetchMemesQuickVote.ts to pass those two values through when building
the key so the key shape matches other summary/discovery keys that include
viewer identity.
In `@hooks/useMemesQuickVoteDialogController.ts`:
- Around line 33-41: The current prefetchQuickVote marks a reserved sessionId in
prefetchedSessionIdsRef before awaiting the async prefetchMemesQuickVote, so
failures are permanently deduped; change the logic so the sessionId is only
added after a successful prefetch or, if you prefer to keep optimistic marking,
attach a .catch handler to the prefetchMemesQuickVote(sessionId) promise that
removes the sessionId from prefetchedSessionIdsRef on error (and optionally log
the error), ensuring future hovers/focuses can retry; locate this behavior in
prefetchQuickVote, reserveSessionId, prefetchedSessionIdsRef, and
prefetchMemesQuickVote to implement the fix.
In `@hooks/useMemesQuickVoteSubmit.ts`:
- Around line 113-121: The remaining power is being computed from the delta
(appliedAmount) instead of the final rating; update the logic so
nextRemainingPower is derived from the final rating: if
response.context_profile_context?.rating (nextRating) is a number compute
nextRemainingPower = Math.max(0, queuedVote.maxRating - nextRating); otherwise
compute finalRating = queuedVote.currentRating + appliedAmount and set
nextRemainingPower = Math.max(0, queuedVote.maxRating - finalRating); keep
appliedAmount logic unchanged and reference nextRating, appliedAmount,
queuedVote.currentRating, and queuedVote.maxRating to locate the code.
In `@hooks/usePrefetchMemesQuickVote.ts`:
- Around line 51-95: Wrap the discovery fetch and the subsequent drop prefetches
in a try/catch so rejections are handled instead of leaking (surround the
queryClient.fetchQuery call that uses
getMemesQuickVoteDiscoveryQueryKey/fetchMemesQuickVoteDiscoveryBatch and the
mapping that calls queryClient.prefetchQuery for
getMemesQuickVoteDropQueryKey/fetchMemesQuickVoteDrop with a try/catch), and add
a staleTime (e.g. 60_000) to the discovery fetchQuery options and to each drop
prefetchQuery options so those results do not become immediately stale (mirror
the summaryPromise staleTime). Ensure you still keep existing retry settings
(getDefaultQueryRetry / retry: false) when adding staleTime.
---
Nitpick comments:
In `@__tests__/components/auth/Auth.test.tsx`:
- Around line 31-45: The isAuthAddressAuthorized mock implementation is
duplicated (appearing around the current block and again at lines 203-219);
extract the matching logic into a single reusable helper function (e.g.,
isAuthAddressAuthorizedMatcher) and replace both jest.fn(...) implementations
with jest.fn(isAuthAddressAuthorizedMatcher) so both mocks call the same
function (update references to the parameter shape used in the existing
implementations to preserve behavior).
In `@__tests__/components/brain/BrainMobile.test.tsx`:
- Around line 336-343: The test in
__tests__/components/brain/BrainMobile.test.tsx is using a brittle CSS selector
".tw-relative.tw-min-w-0.tw-flex-1" to locate the active pane; add a stable data
attribute (e.g., data-testid="active-pane") to the wrapper element in the
production component (the element that currently has those classes), then update
the test to query that element via getByTestId("active-pane") and assert it
contains screen.getByTestId("quick-vote-trigger") instead of using the long
class selector.
In `@__tests__/hooks/usePrefetchMemesQuickVote.test.tsx`:
- Around line 65-134: Add unit tests for usePrefetchMemesQuickVote covering the
suggested edge cases: create separate test cases that (1) mock
useMemesQuickVoteContextMock to return isEnabled: false and assert
commonApiFetchMock is not called and no queries are prefetched via QueryClient;
(2) mock commonApiFetchMock to throw an error for leaderboard or drop endpoints
and assert the hook handles it (e.g., does not crash and logs/returns expected
state) by spying on console/process logger or checking QueryClient state; and
(3) simulate duplicate invocations by calling the hook twice within the same
test (or calling the prefetch function twice) and assert commonApiFetchMock is
called only once per unique drop id (or that QueryClient avoids duplicate
fetches) using jest mocks and useMemesQuickVoteStorageMock to control
skippedDropIds. Ensure each test uses the existing setup patterns (QueryClient,
createDrop, WAVE_ID, commonApiFetchMock, useMemesQuickVoteContextMock,
useMemesQuickVoteStorageMock) for easy integration.
In `@__tests__/services/auth.utils.test.ts`:
- Around line 147-197: Add a unit test covering the early-return path when the
supplied address is null or undefined by calling isAuthAddressAuthorized with
address: null (and a separate case for undefined if desired) and a minimal
connectedAccounts array, asserting it returns false; ensure the test references
isAuthAddressAuthorized to locate the function and mirrors existing test style
(use require("@/config/env") only if environment mutation is needed) so the
behavior for null/undefined addresses is pinned.
In `@components/brain/BrainMobile.tsx`:
- Around line 181-191: The quick-vote mounting logic
(shouldMountFloatingQuickVoteEntry and shouldMountQuickVoteDialog) currently
only checks for any wave and !isDm, so memes quick-vote UI can mount on
non-memes waves; update the guards to include the isMemesWave boolean so the
floating entry and dialog only mount for memes waves (i.e., add isMemesWave to
the AND list in shouldMountFloatingQuickVoteEntry and ensure
shouldMountQuickVoteDialog respects that same memes-scoped condition alongside
isQuickVoteOpen and activeView checks involving BrainView.DEFAULT and
BrainView.WAVES).
In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteControls.tsx`:
- Around line 189-205: The custom amount input in MemesQuickVoteControls lacks
an explicit accessible name; update the <input> for customValue to include an
aria-label (e.g., aria-label="Custom vote amount") or give it an id and connect
the existing visible label using htmlFor so screen readers can identify it, and
ensure handlers (onCustomChange, onCustomSubmit) and disabled/isSubmitting
behavior remain unchanged.
In
`@components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsx`:
- Around line 82-88: The submitVote call in queueVoteAmount can reject and never
clears the advancing lock; wrap the await submitVote(activeDrop, amount) in a
try/catch/finally inside the queueVoteAmount function (and the other similar
block around submitVote at the second occurrence) so that setIsAdvancing(false)
is called in finally, handle/log the caught error to avoid an unhandled
rejection, and preserve the existing wasQueued logic for the success path.
In `@components/brain/mobile/FloatingMemesQuickVoteTrigger.tsx`:
- Around line 15-19: The guard in FloatingMemesQuickVoteTrigger is overly
defensive: remove the redundant checks for typeof uncastPower !== "number" and
unratedCount <= 0 and only keep the isReady check from useMemesWaveFooterStats;
update the early-return to "if (!isReady) return null" so the hook's contract is
relied upon and the code is simplified while still returning null when the data
isn't ready.
In `@hooks/useMemesQuickVoteContext.ts`:
- Around line 16-23: Derived values memesWaveId and isEnabled are being
recalculated every render while contextProfile is memoized; wrap the computation
of memesWaveId (from seizeSettings.memes_wave_id) and the boolean isEnabled in
useMemo (or the same memo hook you used for contextProfile) so they preserve
referential equality across renders and match the existing memoization pattern
(refer to seizeSettings, memesWaveId, isEnabled, contextProfile, and
activeProfileProxy to build the memo dependencies).
In `@hooks/useMemesWaveFooterStats.ts`:
- Around line 6-8: Export the MemesWaveFooterStats type so consumers can
explicitly annotate the hook's return value; update the declaration of
MemesWaveFooterStats to be exported (export type MemesWaveFooterStats = ...) and
ensure any related exports or imports that rely on this type (e.g., usages of
useMemesWaveFooterStats) are adjusted accordingly to reference the exported
type.
In `@openapi.yaml`:
- Around line 5843-5851: The parameter unvoted_by_me currently implies
authentication but the operation only documents a 200 response; update the
OpenAPI spec to explicitly state expected behavior for unauthenticated requests:
either (A) add a 401 response object to the operation and update the
unvoted_by_me description to say the filter requires authentication and will
return 401 if unset, or (B) update the unvoted_by_me description to say the
filter is ignored for unauthenticated requests and keep only 200; modify the
operation responses accordingly (add a 401 response if choosing A) and mention
the authentication requirement next to the unvoted_by_me parameter so clients
know whether the filter requires auth.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0dc39fd8-1634-47db-94db-93a4615d0e66
📒 Files selected for processing (54)
__tests__/components/auth/Auth.test.tsx__tests__/components/brain/BrainMobile.test.tsx__tests__/components/brain/left-sidebar/waves/MemesWaveFooter.test.tsx__tests__/components/brain/left-sidebar/waves/UnifiedWavesList.test.tsx__tests__/components/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.test.tsx__tests__/components/brain/left-sidebar/web/WebLeftSidebar.test.tsx__tests__/components/brain/left-sidebar/web/WebUnifiedWavesList.test.tsx__tests__/components/brain/mobile/BrainMobileViewContent.test.tsx__tests__/components/brain/mobile/BrainMobileWaves.test.tsx__tests__/components/brain/mobile/FloatingMemesQuickVoteTrigger.test.tsx__tests__/components/brain/my-stream/MyStreamWaveTabsLeaderboard.test.tsx__tests__/components/layout/AppLayout.test.tsx__tests__/hooks/memesQuickVote.helpers.test.ts__tests__/hooks/memesQuickVote.queue.helpers.test.ts__tests__/hooks/useMemesQuickVoteDialogController.test.tsx__tests__/hooks/useMemesQuickVoteQueue.test.tsx__tests__/hooks/useMemesWaveFooterStats.test.tsx__tests__/hooks/usePrefetchMemesQuickVote.test.tsx__tests__/services/auth.utils.test.tscomponents/auth/Auth.tsxcomponents/brain/BrainMobile.tsxcomponents/brain/left-sidebar/waves/MemesWaveFooter.tsxcomponents/brain/left-sidebar/waves/MemesWaveQuickVoteTrigger.tsxcomponents/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteControls.tsxcomponents/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialog.tsxcomponents/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVoteDialogSkeleton.tsxcomponents/brain/left-sidebar/waves/memes-quick-vote/MemesQuickVotePreview.tsxcomponents/brain/left-sidebar/web/WebLeftSidebar.tsxcomponents/brain/mobile/BrainMobileTabs.tsxcomponents/brain/mobile/BrainMobileViewContent.tsxcomponents/brain/mobile/BrainMobileWaves.tsxcomponents/brain/mobile/FloatingMemesQuickVoteTrigger.tsxcomponents/brain/mobile/brainMobileViews.tscomponents/brain/mobile/useBrainMobileActiveView.tscomponents/brain/my-stream/MyStreamWaveTabsLeaderboard.tsxcomponents/layout/AppLayout.tsxconfig/nextConfig.tsdocs/specs/2026-03-20-memes-quick-vote-refactor.mdhooks/memesQuickVote.helpers.tshooks/memesQuickVote.query.tshooks/memesQuickVote.queue.helpers.tshooks/memesQuickVote.storageStore.tshooks/useMemesQuickVoteActiveDrop.tshooks/useMemesQuickVoteContext.tshooks/useMemesQuickVoteDialogController.tshooks/useMemesQuickVoteDiscovery.tshooks/useMemesQuickVoteQueue.tshooks/useMemesQuickVoteStorage.tshooks/useMemesQuickVoteSubmit.tshooks/useMemesQuickVoteSummary.tshooks/useMemesWaveFooterStats.tshooks/usePrefetchMemesQuickVote.tsopenapi.yamlservices/auth/auth.utils.ts
|


Summary by CodeRabbit