Conversation
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 16 minutes and 26 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR introduces comprehensive "profile waves" functionality, allowing users to designate a wave/curation as their official profile wave. It adds new route pages, API endpoints, hooks, and UI components for managing and displaying profile waves, enhances drop rendering with identity mode and interaction control flags, implements a masonry/list view toggle for curations, and updates routing and header logic accordingly. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant ProfilePage as Profile Page
participant WaveComponent as UserPageProfileWave
participant API as Profile Wave API
participant State as React Query State
User->>ProfilePage: Visit profile with waves enabled
ProfilePage->>WaveComponent: Load profile wave component
WaveComponent->>State: Check if profileWaveId exists
alt No Profile Wave
WaveComponent->>User: Return null (no wave set)
else Has Profile Wave
WaveComponent->>API: Fetch wave details
API-->>State: Return wave data
WaveComponent->>API: Fetch wave curations
API-->>State: Return curations list
WaveComponent->>WaveComponent: Select earliest curation
WaveComponent->>User: Render official wave panel
User->>WaveComponent: Click "Clear official wave"
WaveComponent->>API: Call clearProfileWave
API-->>State: Update profile (removed wave_id)
WaveComponent->>User: Show success toast & refresh
end
sequenceDiagram
actor User
participant WaveHeader as Wave Header Options
participant WaveAction as WaveProfileWaveAction
participant Mutation as useProfileWaveMutation
participant API as Profile Wave API
User->>WaveHeader: Click wave options menu
WaveHeader->>WaveAction: Render action if user is author
alt User can manage profile waves
User->>WaveAction: Click "Set as profile wave"
WaveAction->>Mutation: Call updateProfileWave(waveId)
Mutation->>API: POST /profiles/{identity}/wave
API-->>Mutation: Return updated profile
Mutation->>User: Show success toast
else User cannot manage
WaveAction->>WaveAction: Return null (hidden)
end
sequenceDiagram
participant Masonry as UserPageProfileWaveMasonry
participant Drops as Drop Components
participant DropRenderer as DropRenderer Logic
Masonry->>Drops: Load wave drops with pagination
Masonry->>Drops: Pass identityMode="minimal" showInteractions=false
Drops->>DropRenderer: Render drops with identity constraints
DropRenderer->>DropRenderer: Check identityMode flag
alt identityMode === "minimal"
DropRenderer->>DropRenderer: Render DropMinimalIdentityRow instead of full header
else identityMode === "hidden"
DropRenderer->>DropRenderer: Hide all identity UI
else identityMode === "default"
DropRenderer->>DropRenderer: Render full identity (default behavior)
end
DropRenderer->>DropRenderer: Gate interactions based on showInteractions flag
alt showInteractions === false
DropRenderer->>DropRenderer: Hide actions, reactions, long-press
end
Drops->>Masonry: Display rendered drops in grid
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 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: 3
🧹 Nitpick comments (1)
services/api/profile-wave-api.ts (1)
6-17: Unify the POST and DELETE paths for this endpoint.
setProfileWavegoes through the shared API helper, whileclearProfileWavereimplements URL building, auth headers, and error parsing locally. That split is easy to drift the next time request semantics change. Please hide both operations behind the same internal request helper (or add a shared DELETE variant) so this endpoint stays consistent.Also applies to: 19-49, 54-83
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@services/api/profile-wave-api.ts` around lines 6 - 17, setProfileWave and clearProfileWave are inconsistent: setProfileWave uses the shared API helper while clearProfileWave rebuilds the URL, headers, and error parsing locally; refactor so both use the same internal request helper (or add a shared DELETE variant) to avoid drift. Update clearProfileWave to call buildProfileWaveUrl(identity) and buildProfileWaveHeaders() and then invoke the existing shared request helper (or add a delete method on that helper) instead of reimplementing fetch/authorization/error handling; ensure you use the same error parsing and response handling logic as setProfileWave so URL/header symbols (buildProfileWaveUrl, buildProfileWaveHeaders) and function names (setProfileWave, clearProfileWave) remain consistent.
🤖 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/user/layout/UserPageLayout.tsx`:
- Line 18: The current assignment of mainAddress from
initialProfile.primary_wallet can become undefined and wipe out the route-level
fallback; change the logic so mainAddress uses initialProfile.primary_wallet
when present and otherwise falls back to the route-level fallbackMainAddress
(i.e., mainAddress = initialProfile.primary_wallet if defined, else
fallbackMainAddress) so UserPageHeader continues to receive the fallback when
handleOrWallet identifies the page.
In `@components/user/waves/UserPageProfileWave.tsx`:
- Around line 114-118: The current useWaveCurations call defaults data to [] so
failures are indistinguishable from an empty wave; update the hook usage
(useWaveCurations) to stop forcing data = [] and destructure isError and error
(e.g., const { data: curations, isLoading: areCurationsLoading, isError:
areCurationsError, error: curationsError } = useWaveCurations(...)), then change
the render logic to explicitly handle areCurationsError (render an error
state/message using curationsError) before falling back to "No curations yet" or
empty list; apply the same change to the other occurrence around lines 216-244.
In `@components/waves/header/options/profile-wave/WaveProfileWaveAction.tsx`:
- Around line 22-26: The ownership check in canManageProfileWave currently fails
when connectedProfile.handle is absent; change it to use the same
identity-fallback logic as useProfileWaveMutation: replace the handle-only
comparison (connectedProfile?.handle === wave.author.handle) with a robust
equality that checks handle, then primary_wallet, then id (e.g., compare
connectedProfile.handle || connectedProfile.primary_wallet ||
connectedProfile.id to wave.author.handle || wave.author.primary_wallet ||
wave.author.id), or call the existing identity-equality helper if one exists;
keep the other guards (activeProfileProxy and isPublicNonDirectMessageWave)
unchanged.
---
Nitpick comments:
In `@services/api/profile-wave-api.ts`:
- Around line 6-17: setProfileWave and clearProfileWave are inconsistent:
setProfileWave uses the shared API helper while clearProfileWave rebuilds the
URL, headers, and error parsing locally; refactor so both use the same internal
request helper (or add a shared DELETE variant) to avoid drift. Update
clearProfileWave to call buildProfileWaveUrl(identity) and
buildProfileWaveHeaders() and then invoke the existing shared request helper (or
add a delete method on that helper) instead of reimplementing
fetch/authorization/error handling; ensure you use the same error parsing and
response handling logic as setProfileWave so URL/header symbols
(buildProfileWaveUrl, buildProfileWaveHeaders) and function names
(setProfileWave, clearProfileWave) remain consistent.
🪄 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: 293ff679-389e-466a-9e78-6b9207b2b90c
📒 Files selected for processing (12)
app/[user]/waves/page.tsxcomponents/brain/my-stream/curations/MyStreamWaveCurationContent.tsxcomponents/brain/my-stream/tabs/MyStreamWaveCreateCurationAction.tsxcomponents/user/layout/UserPageLayout.tsxcomponents/user/layout/UserPageTabs.tsxcomponents/user/layout/userTabs.config.tscomponents/user/waves/UserPageProfileWave.tsxcomponents/waves/header/options/WaveHeaderOptions.tsxcomponents/waves/header/options/profile-wave/WaveProfileWaveAction.tsxhelpers/waves/wave.helpers.tshooks/useProfileWaveMutation.tsservices/api/profile-wave-api.ts
There was a problem hiding this comment.
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/waves/drops/winner/WinnerDrop.tsx (1)
28-38:⚠️ Potential issue | 🟠 Major
MemeWinnerDropdoes not accept theidentityModeprop.While
DefaultWinnerDropaccepts this prop,MemeWinnerDrop's interface only includesdrop,showReplyAndQuote, andonReply. Spreading{...props}passes an unhandled prop to meme wave drops, silently ignoring theidentityModesetting and creating inconsistent behavior between meme and default waves.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/winner/WinnerDrop.tsx` around lines 28 - 38, The WinnerDrop component spreads WinnerDropProps (including identityMode) into MemeWinnerDrop but MemeWinnerDrop's props type/definition doesn't include identityMode, causing it to be dropped; update MemeWinnerDrop's prop interface or component signature to include identityMode (or extend WinnerDropProps / the shared prop type), accept the prop in the MemeWinnerDrop component, and forward/use it where appropriate (e.g., pass it to any child components or logic that DefaultWinnerDrop currently uses) so meme-wave drops honor the identityMode consistently with DefaultWinnerDrop.
♻️ Duplicate comments (1)
components/user/waves/UserPageProfileWave.tsx (1)
213-217:⚠️ Potential issue | 🟠 MajorHandle curation fetch failures separately from “no curations”.
data: curations = []still collapses a faileduseWaveCurations()request into the empty-state path, so backend/access errors will render as “No curations yet”. Please surfaceisError(and ideallyerror) here before falling back to the empty state.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/waves/UserPageProfileWave.tsx` around lines 213 - 217, The current destructure defaults `data: curations = []` from useWaveCurations hides request failures by turning errors into the “no curations” path; change the destructure to also pull out isError and error (e.g., const { data: curations, isLoading: areCurationsLoading, isError: areCurationsError, error: curationsError } = useWaveCurations({ waveId: wave?.id ?? "", enabled: !!wave?.id })), keep curations as undefined when the request fails, and update the rendering logic that uses curations to first check areCurationsError (and optionally display curationsError) before treating an empty array as “no curations yet”.
🧹 Nitpick comments (4)
hooks/useCurationManagementPermission.ts (1)
5-20: Consider makingprobeDropIdtype explicitly allow falsy values.The type declares
probeDropId: stringbut theenabled: Boolean(probeDropId)check suggests empty strings are valid to disable the query. Consider typing it asprobeDropId: string | undefinedor adding a JSDoc comment to clarify the contract.♻️ Suggested type improvement
export function useCurationManagementPermission({ curationId, probeDropId, }: { readonly curationId: string; - readonly probeDropId: string; + readonly probeDropId: string | undefined; }) { const { data: probeCurations = [] } = useDropCurations({ - dropId: probeDropId, + dropId: probeDropId ?? "", enabled: Boolean(probeDropId), });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@hooks/useCurationManagementPermission.ts` around lines 5 - 20, The parameter type for probeDropId in useCurationManagementPermission is declared as string but treated as possibly falsy; change the prop signature in useCurationManagementPermission to accept probeDropId: string | undefined (or null) so Boolean(probeDropId) correctly reflects the type, update any callers that pass optional values to satisfy the new union type, and adjust any related types/props that flow into this hook to maintain type-safety for useDropCurations({ dropId: probeDropId, enabled: Boolean(probeDropId) }).components/waves/drops/DropMinimalIdentityRow.tsx (1)
19-26: Consider simplifying the click handler.The
handleNavigationcallback prevents default Link behavior and then manually callsrouter.push()with the same path. SincestopPropagation()alone would prevent bubbling to parent handlers while still allowing the<Link>to navigate naturally, this approach duplicates navigation logic.♻️ Simplified approach
- const handleNavigation = useCallback( - (event: React.MouseEvent, path: string) => { - event.preventDefault(); - event.stopPropagation(); - router.push(path); - }, - [router] - ); + const handleClick = useCallback((event: React.MouseEvent) => { + event.stopPropagation(); + }, []);Then use
onClick={handleClick}on the Link. This lets Next.js handle navigation optimally (prefetching, client-side transitions) while still preventing event bubbling.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/DropMinimalIdentityRow.tsx` around lines 19 - 26, The click handler handleNavigation currently calls event.preventDefault() and router.push(path), duplicating Link's navigation; change it to a simpler handler (e.g., handleClick) that only calls event.stopPropagation() so the Link's native Next.js navigation/prefetching is preserved, remove the router.push call and router dependency from the useCallback dependencies, and update places using handleNavigation to use the new handler on the Link's onClick.components/header/AppHeader.tsx (1)
101-141:getHeaderTitlemay returnundefined.When
waveIdis truthy and the wave has loaded butwave.nameisnullorundefined, line 128 returnsundefined. While this may render as nothing (React handles undefined gracefully), consider whether this is the intended behavior or if a fallback should be provided.♻️ Optional fallback
if (waveId) { if (isWaveResolving) return <Spinner />; - return wave?.name; + return wave?.name ?? "Wave"; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/header/AppHeader.tsx` around lines 101 - 141, getHeaderTitle can return undefined when waveId is set but wave.name is null/undefined; update the waveId branch in getHeaderTitle to return a safe fallback instead of possibly undefined: when waveId is truthy, if isWaveResolving return <Spinner /> as before, otherwise return wave?.name ?? a fallback (for example sliceString(capitalizeEveryWord(pageTitle), 20) or another sensible string); ensure you reference getHeaderTitle, waveId, wave, isWaveResolving, Spinner, and sliceString/capitalizeEveryWord so the change is applied in the correct branch.components/user/waves/UserPageProfileWaveMasonry.tsx (1)
166-188: Move the masonry reset bookkeeping out of render.
useProfileMasonryRenderState()is callingsetState()while the hook is rendering. That forces an extra render whenever the curation changes or the drop count shrinks, and it’s a brittle pattern under React 19. Please move this synchronization intouseEffect/useReducerand deriveshouldResetMasonryfrom committed state instead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/waves/UserPageProfileWaveMasonry.tsx` around lines 166 - 188, The hook useProfileMasonryRenderState() is calling setState() during render to update resetVersion/lastDropCount when curationId or dropCount change; move this synchronization out of render into a dedicated useEffect or switch to a useReducer so state updates are committed instead of invoked during render, compute shouldResetMasonry from the committed state (resetVersion/lastDropCount) rather than setting it inline, and update the logic that currently checks curationId, dropCount, and uses setState(...) to run inside the effect/reducer (preserve the same updates to resetVersion, lastDropCount, and curationId).
🤖 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/drops/view/item/content/media/DropListItemContentMediaImage.tsx`:
- Around line 92-108: The branch in DropListItemContentMediaImage renders a raw
<img> with src=getScaledImageUri(src,imageScale) which skips the existing
FallbackImage behavior and causes broken images when the scaled derivative is
missing; change this to preserve primary→fallback logic by using the same
FallbackImage approach (or replicate its behavior) here: attempt the scaled URL
from getScaledImageUri(src,imageScale) first, and on its onError swap to the
original src without incrementing errorCount/retryTick so the retry counter only
advances when the original also fails; adjust handleError/ onError wiring
accordingly so DropListItemContentMediaImage still falls back to src before
calling the failure/retry logic.
In `@components/waves/drops/WaveDrop.tsx`:
- Around line 458-469: The reactions/metadata row still hardcodes the avatar
gutter offset (tw-ml-11 / tw-ml-[3.25rem]) causing the footer to shift when
showAuthorInfo is false; update that row to compute its left margin using the
same logic used for the content block by deriving the offset from showAuthorInfo
and the grouping state (shouldGroupWithPreviousDrop/shouldGroupWithNextDrop) —
reuse the output of shouldShowAuthorInfo(...) and getGroupingClass(...) (or
replicate that branching) so the reactions row uses the same conditional margin
classes as the content area when identityMode is "hidden" or when drops are
grouped.
---
Outside diff comments:
In `@components/waves/drops/winner/WinnerDrop.tsx`:
- Around line 28-38: The WinnerDrop component spreads WinnerDropProps (including
identityMode) into MemeWinnerDrop but MemeWinnerDrop's props type/definition
doesn't include identityMode, causing it to be dropped; update MemeWinnerDrop's
prop interface or component signature to include identityMode (or extend
WinnerDropProps / the shared prop type), accept the prop in the MemeWinnerDrop
component, and forward/use it where appropriate (e.g., pass it to any child
components or logic that DefaultWinnerDrop currently uses) so meme-wave drops
honor the identityMode consistently with DefaultWinnerDrop.
---
Duplicate comments:
In `@components/user/waves/UserPageProfileWave.tsx`:
- Around line 213-217: The current destructure defaults `data: curations = []`
from useWaveCurations hides request failures by turning errors into the “no
curations” path; change the destructure to also pull out isError and error
(e.g., const { data: curations, isLoading: areCurationsLoading, isError:
areCurationsError, error: curationsError } = useWaveCurations({ waveId: wave?.id
?? "", enabled: !!wave?.id })), keep curations as undefined when the request
fails, and update the rendering logic that uses curations to first check
areCurationsError (and optionally display curationsError) before treating an
empty array as “no curations yet”.
---
Nitpick comments:
In `@components/header/AppHeader.tsx`:
- Around line 101-141: getHeaderTitle can return undefined when waveId is set
but wave.name is null/undefined; update the waveId branch in getHeaderTitle to
return a safe fallback instead of possibly undefined: when waveId is truthy, if
isWaveResolving return <Spinner /> as before, otherwise return wave?.name ?? a
fallback (for example sliceString(capitalizeEveryWord(pageTitle), 20) or another
sensible string); ensure you reference getHeaderTitle, waveId, wave,
isWaveResolving, Spinner, and sliceString/capitalizeEveryWord so the change is
applied in the correct branch.
In `@components/user/waves/UserPageProfileWaveMasonry.tsx`:
- Around line 166-188: The hook useProfileMasonryRenderState() is calling
setState() during render to update resetVersion/lastDropCount when curationId or
dropCount change; move this synchronization out of render into a dedicated
useEffect or switch to a useReducer so state updates are committed instead of
invoked during render, compute shouldResetMasonry from the committed state
(resetVersion/lastDropCount) rather than setting it inline, and update the logic
that currently checks curationId, dropCount, and uses setState(...) to run
inside the effect/reducer (preserve the same updates to resetVersion,
lastDropCount, and curationId).
In `@components/waves/drops/DropMinimalIdentityRow.tsx`:
- Around line 19-26: The click handler handleNavigation currently calls
event.preventDefault() and router.push(path), duplicating Link's navigation;
change it to a simpler handler (e.g., handleClick) that only calls
event.stopPropagation() so the Link's native Next.js navigation/prefetching is
preserved, remove the router.push call and router dependency from the
useCallback dependencies, and update places using handleNavigation to use the
new handler on the Link's onClick.
In `@hooks/useCurationManagementPermission.ts`:
- Around line 5-20: The parameter type for probeDropId in
useCurationManagementPermission is declared as string but treated as possibly
falsy; change the prop signature in useCurationManagementPermission to accept
probeDropId: string | undefined (or null) so Boolean(probeDropId) correctly
reflects the type, update any callers that pass optional values to satisfy the
new union type, and adjust any related types/props that flow into this hook to
maintain type-safety for useDropCurations({ dropId: probeDropId, enabled:
Boolean(probeDropId) }).
🪄 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: bafd1d0f-8dd9-4d59-a2bd-6a6fa293a78d
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (26)
components/brain/my-stream/curations/CurationEmptyState.tsxcomponents/brain/my-stream/curations/MyStreamWaveCurationContent.tsxcomponents/drops/view/item/content/media/DropListItemContentMedia.tsxcomponents/drops/view/item/content/media/DropListItemContentMediaImage.tsxcomponents/header/AppHeader.tsxcomponents/user/waves/UserPageProfileWave.tsxcomponents/user/waves/UserPageProfileWaveMasonry.tsxcomponents/utils/button/SecondaryButton.tsxcomponents/waves/drops/Drop.tsxcomponents/waves/drops/DropMinimalIdentityRow.tsxcomponents/waves/drops/WaveDrop.tsxcomponents/waves/drops/WaveDropContent.tsxcomponents/waves/drops/WaveDropPart.tsxcomponents/waves/drops/WaveDropPartContent.tsxcomponents/waves/drops/WaveDropPartContentMedias.tsxcomponents/waves/drops/WaveDropPartDrop.tsxcomponents/waves/drops/drop.types.tscomponents/waves/drops/participation/DefaultParticipationDrop.tsxcomponents/waves/drops/participation/EndedParticipationDrop.tsxcomponents/waves/drops/participation/OngoingParticipationDrop.tsxcomponents/waves/drops/participation/ParticipationDrop.tsxcomponents/waves/drops/winner/DefaultWinnerDrop.tsxcomponents/waves/drops/winner/WinnerDrop.tsxhooks/useCurationManagementPermission.tshooks/useProfileCurationViewMode.tspackage.json
✅ Files skipped from review due to trivial changes (2)
- package.json
- components/waves/drops/drop.types.ts
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (3)
components/waves/drops/WaveDrop.tsx (1)
710-716:⚠️ Potential issue | 🟡 MinorReactions row still reserves the avatar gutter when identity is hidden.
This still hardcodes
tw-ml-11/tw-ml-[3.25rem], soidentityMode="hidden"leaves the metadata/reactions row shifted to the right of the content block.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/WaveDrop.tsx` around lines 710 - 716, The reactionsRow JSX currently always applies the avatar gutter classes ("tw-ml-11" / "tw-ml-[3.25rem]") even when the drop identity is hidden; update the className expression in the reactionsRow declaration to conditionally use zero-left-margin when identityMode === "hidden" (e.g., use "tw-ml-0" or omit the ml classes) and keep the existing compact branch otherwise, referencing the reactionsRow variable and the compact and identityMode props to decide between "tw-ml-11" / "tw-ml-[3.25rem]" and the hidden-case margin class.components/drops/view/item/content/media/DropListItemContentMediaImage.tsx (1)
96-109:⚠️ Potential issue | 🟠 MajorKeep the original-source fallback on the
naturalHeightpath.This branch still loads only the scaled URL in a raw
<img>, so a missing derivative now fails hard even when the originalsrcis still valid. Please preserve the same scaled → original fallback behavior before advancing the retry counter.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/drops/view/item/content/media/DropListItemContentMediaImage.tsx` around lines 96 - 109, The naturalHeight branch currently renders only the scaled URL (getScaledImageUri) so a missing derivative causes a hard failure; restore the original-source fallback by preserving the scaled→original behavior: render the image so the browser can fall back to the original src (either by using a <picture> with a <source> for getScaledImageUri(...) and an <img src={src}> or keep getScaledImageUri(...) as src and update handleError to detect a scaled failure and swap the img.src to the original src before you increment errorCount/retryTick). Update handleError/img onError logic (handleError, imgRef, retryTick, errorCount, maxRetries) to try the original src first and only advance the retry counter if the original also fails.components/user/waves/UserPageProfileWave.tsx (1)
296-300:⚠️ Potential issue | 🟠 MajorDifferentiate curation load failures from an empty official wave.
useWaveCurationsis still defaultingdatato[]and ignoring the error state, so failed requests render the same "No curations yet" UI as a genuinely empty wave. That makes backend/access failures invisible to users and much harder to debug.💡 Suggested fix
- const { data: curations = [], isLoading: areCurationsLoading } = + const { + data: curations, + isLoading: areCurationsLoading, + isError: areCurationsError, + } = useWaveCurations({ waveId: wave?.id ?? "", enabled: !!wave?.id, }); @@ function ProfileCurationBody({ areCurationsLoading, + areCurationsError, profileCuration, profileIdentity, viewMode, wave, }: { readonly areCurationsLoading: boolean; + readonly areCurationsError: boolean; readonly profileCuration: ApiWaveCuration | null; @@ if (areCurationsLoading) { return ( <div className="tw-flex tw-min-h-32 tw-items-center tw-justify-center tw-text-sm tw-text-iron-400"> <Spinner dimension={18} /> <span className="tw-ml-3">Loading curation...</span> </div> ); } + + if (areCurationsError) { + return ( + <div className="tw-p-6 tw-text-sm tw-text-iron-400"> + Unable to load curations right now. + </div> + ); + } @@ const profileCuration = useMemo( - () => resolveProfileCuration(curations), + () => resolveProfileCuration(curations ?? []), [curations] ); @@ <ProfileCurationBody areCurationsLoading={areCurationsLoading} + areCurationsError={areCurationsError} profileCuration={profileCuration} profileIdentity={profileIdentityForMasonry} viewMode={viewMode} wave={wave} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/waves/UserPageProfileWave.tsx` around lines 296 - 300, The hook usage currently masks failed requests by defaulting data to []—update the destructure from useWaveCurations to include isError (and error) instead of defaulting data: e.g. const { data: curations, isLoading: areCurationsLoading, isError: areCurationsError, error } = useWaveCurations({ waveId: wave?.id ?? "", enabled: !!wave?.id }); then change the render logic that checks curations.length to first check areCurationsLoading, then areCurationsError (render an error state/message using error), and only treat curations as "no curations yet" when not loading and not error and curations is empty; do not coerce data to [] in the destructure so you can distinguish empty vs failed.
🧹 Nitpick comments (2)
components/utils/button/SecondaryButton.tsx (2)
22-23: Follow-up cleanup opportunity in call sites usingsize="sm"+!tw-text-xs.Since Line 22 now applies
tw-text-xsforsm, callers that still passclassName="!tw-text-xs"are likely redundant and can be simplified to reduce utility-class overrides.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/utils/button/SecondaryButton.tsx` around lines 22 - 23, Call sites that pass size="sm" and also include className="!tw-text-xs" are redundant because SecondaryButton now sets tw-text-xs for size "sm"; remove the explicit override from those callers by deleting the "!tw-text-xs" utility in their className props (or simplify the entire className if it only contained that override). Search for usages of the SecondaryButton component that pass the size prop "sm" and inspect className values containing "!tw-text-xs", then update each call site to omit the override so the built-in sizing in SecondaryButton controls the text size.
29-35: Optional cleanup: compute a singleisInactiveflag to avoid condition drift.
disabled || loadingis repeated across class branches and behavior checks. Extracting it once makes future style/behavior edits safer.Refactor suggestion
export default function SecondaryButton({ onClicked, children, size = "default", disabled = false, className = "", loading = false, }: SecondaryButtonProps) { + const isInactive = disabled || loading; const sizeClasses = size === "sm" ? "tw-px-3 tw-py-2 tw-text-xs" : "tw-px-3.5 tw-py-2.5 tw-text-sm"; return ( <button type="button" - disabled={disabled || loading} + disabled={isInactive} className={`tw-border tw-border-solid ${ - disabled || loading + isInactive ? "tw-cursor-not-allowed tw-border-iron-900" : "tw-border-iron-800 hover:tw-border-iron-700 hover:tw-bg-iron-700 hover:tw-ring-iron-650" } tw-rounded-lg tw-bg-iron-800 tw-ring-1 tw-ring-iron-700 ${sizeClasses} tw-font-semibold ${ - disabled || loading ? "tw-text-iron-600" : "tw-text-iron-300" + isInactive ? "tw-text-iron-600" : "tw-text-iron-300" } tw-flex tw-items-center tw-justify-center tw-gap-x-2 tw-shadow-sm tw-transition tw-duration-300 tw-ease-out focus-visible:tw-outline focus-visible:tw-outline-2 focus-visible:tw-outline-offset-2 focus-visible:tw-outline-iron-700 ${className}`} - onClick={disabled || loading ? undefined : onClicked} + onClick={isInactive ? undefined : onClicked} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/utils/button/SecondaryButton.tsx` around lines 29 - 35, In SecondaryButton, compute a single boolean (e.g., isInactive = disabled || loading) at the top of the component and use that variable everywhere instead of repeating `disabled || loading`; update the className template (and any other conditional checks) to reference isInactive so the branches that now use `disabled || loading` (the border, cursor, text color and hover/ring classes) are driven by a single flag; ensure you still pass through existing variables like sizeClasses and className unchanged.
🤖 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/user/waves/UserPageProfileWave.tsx`:
- Around line 368-370: In UserPageProfileWave
(components/user/waves/UserPageProfileWave.tsx) don't short-circuit to return
null when profileWaveId is falsy; instead render an explicit empty/unavailable
state (e.g., a new UnavailableWaveState component or reuse an existing
EmptyState component) or perform a redirect to a safe profile tab (e.g., push to
the default profile route) when !profileWaveId && !shouldForceUnavailableState;
update the conditional branch that currently reads "if (!profileWaveId &&
!shouldForceUnavailableState) { return null; }" to either return the
unavailable-state JSX (with messaging about missing official wave and a link to
/curations) or call the router redirect, ensuring accessibility and that the tab
for /curations remains visible and the page body shows a helpful message instead
of disappearing.
In `@components/user/waves/UserPageProfileWaveMasonry.tsx`:
- Around line 167-210: The hook useProfileMasonryRenderState is mutating local
state during render by calling setState when state.curationId or dropCount
differs, which can cause render warnings/loops; move that synchronization into a
useEffect that watches [curationId, dropCount] (and optionally containerWidth if
needed) and performs the setState updates (updating curationId, lastDropCount
and incrementing resetVersion when dropCount decreases) so that setState is only
called after render; keep the returned masonryKey and shouldResetMasonry
computed from stable state/resetVersion (use a ref or derived values) so the
hook no longer calls setState during render and behavior of
useProfileMasonryRenderState remains the same.
In `@components/waves/drops/participation/OngoingParticipationDrop.tsx`:
- Around line 162-166: The ParticipationDropFooter is not receiving the identity
layout state, so when identityMode="hidden" the footer still applies the avatar
gutter classes; update the OngoingParticipationDrop component to pass the
identity/layout state (e.g., identityMode or a boolean like showIdentityAvatar)
into ParticipationDropFooter, and then update ParticipationDropFooter
(references: ParticipationDropFooter component and its classnames at the
locations applying sm:tw-ml-[3.25rem] / tw-ml-[3.25rem]) to conditionally apply
or omit the avatar gutter classes based on that prop.
In `@components/waves/drops/WaveDrop.tsx`:
- Around line 354-359: The className template in the WaveDrop JSX div
concatenates conditionals without a separating space so when both showAuthorInfo
and shouldGroupWithPreviousDrop are true it produces "tw-mt-2tw-pl-[3.25rem]";
update the template for the div (the JSX element in WaveDrop.tsx that references
showAuthorInfo, shouldGroupWithPreviousDrop and isProfileView) to ensure a space
is inserted before the pl class when the second conditional is true (for
example, include a leading space in the second conditional's string or append "
" before it) so class names are always separated.
---
Duplicate comments:
In `@components/drops/view/item/content/media/DropListItemContentMediaImage.tsx`:
- Around line 96-109: The naturalHeight branch currently renders only the scaled
URL (getScaledImageUri) so a missing derivative causes a hard failure; restore
the original-source fallback by preserving the scaled→original behavior: render
the image so the browser can fall back to the original src (either by using a
<picture> with a <source> for getScaledImageUri(...) and an <img src={src}> or
keep getScaledImageUri(...) as src and update handleError to detect a scaled
failure and swap the img.src to the original src before you increment
errorCount/retryTick). Update handleError/img onError logic (handleError,
imgRef, retryTick, errorCount, maxRetries) to try the original src first and
only advance the retry counter if the original also fails.
In `@components/user/waves/UserPageProfileWave.tsx`:
- Around line 296-300: The hook usage currently masks failed requests by
defaulting data to []—update the destructure from useWaveCurations to include
isError (and error) instead of defaulting data: e.g. const { data: curations,
isLoading: areCurationsLoading, isError: areCurationsError, error } =
useWaveCurations({ waveId: wave?.id ?? "", enabled: !!wave?.id }); then change
the render logic that checks curations.length to first check
areCurationsLoading, then areCurationsError (render an error state/message using
error), and only treat curations as "no curations yet" when not loading and not
error and curations is empty; do not coerce data to [] in the destructure so you
can distinguish empty vs failed.
In `@components/waves/drops/WaveDrop.tsx`:
- Around line 710-716: The reactionsRow JSX currently always applies the avatar
gutter classes ("tw-ml-11" / "tw-ml-[3.25rem]") even when the drop identity is
hidden; update the className expression in the reactionsRow declaration to
conditionally use zero-left-margin when identityMode === "hidden" (e.g., use
"tw-ml-0" or omit the ml classes) and keep the existing compact branch
otherwise, referencing the reactionsRow variable and the compact and
identityMode props to decide between "tw-ml-11" / "tw-ml-[3.25rem]" and the
hidden-case margin class.
---
Nitpick comments:
In `@components/utils/button/SecondaryButton.tsx`:
- Around line 22-23: Call sites that pass size="sm" and also include
className="!tw-text-xs" are redundant because SecondaryButton now sets
tw-text-xs for size "sm"; remove the explicit override from those callers by
deleting the "!tw-text-xs" utility in their className props (or simplify the
entire className if it only contained that override). Search for usages of the
SecondaryButton component that pass the size prop "sm" and inspect className
values containing "!tw-text-xs", then update each call site to omit the override
so the built-in sizing in SecondaryButton controls the text size.
- Around line 29-35: In SecondaryButton, compute a single boolean (e.g.,
isInactive = disabled || loading) at the top of the component and use that
variable everywhere instead of repeating `disabled || loading`; update the
className template (and any other conditional checks) to reference isInactive so
the branches that now use `disabled || loading` (the border, cursor, text color
and hover/ring classes) are driven by a single flag; ensure you still pass
through existing variables like sizeClasses and className unchanged.
🪄 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: a5c84682-6a29-463e-96f0-a1a8410de1a7
📒 Files selected for processing (22)
app/[user]/curations/page.tsxapp/[user]/waves/page.tsxcomponents/brain/my-stream/curations/MyStreamWaveCurationContent.tsxcomponents/drops/view/item/content/media/DropListItemContentMediaImage.tsxcomponents/memes/drops/MemeParticipationDrop.tsxcomponents/memes/drops/MemeWinnerDrop.tsxcomponents/user/layout/userTabs.config.tscomponents/user/waves/UserPageProfileWave.tsxcomponents/user/waves/UserPageProfileWaveMasonry.tsxcomponents/utils/button/SecondaryButton.tsxcomponents/waves/drops/Drop.tsxcomponents/waves/drops/WaveDrop.tsxcomponents/waves/drops/WaveDropPartContentMedias.tsxcomponents/waves/drops/participation/DefaultParticipationDrop.tsxcomponents/waves/drops/participation/EndedParticipationDrop.tsxcomponents/waves/drops/participation/OngoingParticipationDrop.tsxcomponents/waves/drops/participation/ParticipationDrop.tsxcomponents/waves/drops/participation/ParticipationDropFooter.tsxcomponents/waves/drops/winner/DefaultWinnerDrop.tsxcomponents/waves/drops/winner/WinnerDrop.tsxcomponents/waves/header/options/profile-wave/WaveProfileWaveAction.tsxhooks/useProfileCurationViewMode.ts
🚧 Files skipped from review as they are similar to previous changes (7)
- components/waves/drops/participation/ParticipationDrop.tsx
- components/waves/drops/Drop.tsx
- components/waves/drops/winner/DefaultWinnerDrop.tsx
- components/waves/drops/participation/DefaultParticipationDrop.tsx
- components/waves/header/options/profile-wave/WaveProfileWaveAction.tsx
- components/waves/drops/winner/WinnerDrop.tsx
- hooks/useProfileCurationViewMode.ts
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (2)
components/user/waves/UserPageProfileWave.tsx (1)
474-476:⚠️ Potential issue | 🟠 MajorRedirect or render a state here instead of returning
null.After
clearSelectedProfileWave()succeeds,profileWaveIdbecomes falsy and this branch blanks the/curationspage until the user reloads. The server redirect inapp/[user]/curations/page.tsxdoes not run on that client-side state change.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/waves/UserPageProfileWave.tsx` around lines 474 - 476, When profileWaveId becomes falsy after clearSelectedProfileWave() (and !shouldForceUnavailableState) don’t return null; instead perform a client-side redirect to the curations listing or render the unavailable state UI. Locate the branch in UserPageProfileWave.tsx that checks profileWaveId and shouldForceUnavailableState and replace the early return with either a router.replace('/curations') call (using the app-router hook) or render the existing unavailable/fallback component (e.g., ProfileWaveUnavailable or similar) so the page doesn’t blank on client-side state changes. Ensure the chosen approach runs only when profileWaveId is cleared (and not during initial loading).components/user/waves/UserPageProfileWaveMasonry.tsx (1)
349-359:⚠️ Potential issue | 🟠 MajorHandle failed drop loads separately from an empty curation.
This component only distinguishes “still fetching” from
drops.length === 0, so an initialuseWaveDrops()failure can renderCurationEmptyStateinstead of a retryable error. That masks access/backend problems as “no drops yet.”Also applies to: 394-404
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/user/waves/UserPageProfileWaveMasonry.tsx` around lines 349 - 359, The component treats an empty drops array during isFetching as the only "loading" case, which lets a failed useWaveDrops call render CurationEmptyState; update the rendering logic to check the hook's error state (e.g. isError or error returned from useWaveDrops) and surface a retry/error UI instead of CurationEmptyState when loading failed. Locate the useWaveDrops call and the isInitialLoading calculation (variables: drops, isFetching, isFetchingNextPage, isInitialLoading, permissionProbeDropId, canManageActiveCuration) and add a separate isError (or error) branch: if the hook reports an error show a retryable error component, otherwise use the existing empty-state logic when drops is empty and not errored.
🧹 Nitpick comments (3)
components/waves/drops/WaveDropPartContentMedias.tsx (1)
48-49: MIME type check is case-sensitive.The check
media.mime_type.includes("image")is case-sensitive. While MIME types are typically lowercase, some servers may return mixed-case values like"Image/PNG". Consider using a case-insensitive check for robustness.♻️ Proposed fix for case-insensitive MIME type check
const useNaturalHeightImage = - fullWidthMedia && media.mime_type.includes("image"); + fullWidthMedia && media.mime_type.toLowerCase().includes("image");🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/WaveDropPartContentMedias.tsx` around lines 48 - 49, The MIME type check in the computation of useNaturalHeightImage (currently using media.mime_type.includes("image")) is case-sensitive; update the check to be case-insensitive by normalizing media.mime_type (e.g., call toLowerCase()) before calling includes or use a case-insensitive regex, and guard against null/undefined mime_type to avoid runtime errors; locate the useNaturalHeightImage variable in WaveDropPartContentMedias.tsx and replace the includes-based check accordingly.components/waves/drops/WaveDropPartContentFullWidthImage.tsx (2)
166-166: Tooltip IDs using raw URL could cause invalid selectors.The
data-tooltip-idattributes use the rawsrcURL (e.g.,`reset-zoom-${src}`). Ifsrccontains special characters common in URLs (like?,&,=), these become part of the element ID, which could cause issues with CSS selectors or tooltip matching.Consider using a sanitized or hashed ID instead.
♻️ Proposed fix using stable ID generation
+ // Generate a stable ID from the src URL + const tooltipId = React.useMemo( + () => src.replace(/[^a-zA-Z0-9]/g, "-").slice(0, 50), + [src] + ); // Then use it in data-tooltip-id attributes: - data-tooltip-id={`reset-zoom-${src}`} + data-tooltip-id={`reset-zoom-${tooltipId}`}Also applies to: 181-181, 192-192, 203-203
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/WaveDropPartContentFullWidthImage.tsx` at line 166, The tooltip ID currently concatenates the raw image src (e.g., `reset-zoom-${src}`) which can create invalid/unstable IDs when src contains URL characters; replace these with a sanitized or hashed stable ID generation used consistently across the component (WaveDropPartContentFullWidthImage) — for example add a helper (sanitize or hash) that takes `src` and returns a safe string, then use that helper for every data-tooltip-id occurrence (`reset-zoom-${...}`, and the other two instances) and ensure the tooltip library is given the same generated ID so matching still works.
121-123: Escape key handler fires even when modal is closed.The
useKeyPressEventhook is always active, callinghandleCloseModalon every Escape press regardless of modal state. While this is functionally safe (the handler is idempotent), it could interfere with other Escape handlers on the page.♻️ Proposed optimization to only handle Escape when modal is open
- useKeyPressEvent("Escape", () => { - handleCloseModal(); - }); + useKeyPressEvent("Escape", isModalOpen ? handleCloseModal : null);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/WaveDropPartContentFullWidthImage.tsx` around lines 121 - 123, The Escape handler is being registered always via useKeyPressEvent, so change it to only register or run when the modal is open: either call useKeyPressEvent conditionally (guard the hook call with the modal open state) or keep the hook but wrap the callback to early-return unless the modal-open state (e.g., isOpen / isModalOpen) is true; update the useKeyPressEvent("Escape", ...) usage and reference handleCloseModal so the callback only invokes handleCloseModal when the modal is actually open.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/`[user]/waves/page.tsx:
- Around line 65-69: The code uses canonicalUser = profile.handle ??
profile.primary_wallet which becomes empty when a profile lacks both values;
change the assignment to fall back to the already-initialized user slug (e.g.,
use profile.handle ?? profile.primary_wallet ?? user) so canonicalUser is never
empty, then build basePath from that canonicalUser (keep the existing basePath
logic using encodeURIComponent(canonicalUser)); update the canonicalUser
definition in page.tsx (the block that currently sets canonicalUser and
basePath) to use this three-way fallback.
In `@components/user/waves/UserPageProfileWave.tsx`:
- Around line 374-375: The mock-unavailable toggle (shouldForceUnavailableState)
is currently honored in all environments; change it to only apply in
non-production (e.g., wrap the searchParams check with an env/dev-only guard
such as process.env.NODE_ENV !== 'production' or an isDev/isLocal flag) so
?mockProfileWaveUnavailable=1 cannot force the unavailable UI in production;
update the same pattern where it’s used later (the block around lines
referencing the same flag/behavior) so both the declaration of
shouldForceUnavailableState and any conditional rendering that uses it only
trigger in development environments.
In `@components/user/waves/UserPageProfileWaveMasonry.tsx`:
- Around line 170-188: The remove action currently calls
updateMembership(curationId, "remove") but does not refresh the drops list from
useWaveDrops, so removed cards remain visible; fix by triggering a refresh after
the mutation completes—use the React Query client (useQueryClient) to invalidate
or refetch the useWaveDrops query (or call the refetch function returned by
useWaveDrops) in the updateMembership success/then handler so the masonry
reflects the updated curation membership; update the onClick flow around
updateMembership in UserPageProfileWaveMasonry (and the analogous block at
349-353) to perform this invalidation/refetch.
In `@components/waves/drops/WaveDropPartContentFullWidthImage.tsx`:
- Around line 40-51: The component's local state (currentSrc, usedFallback)
isn't reset when the primarySrc prop changes; add a useEffect that watches
primarySrc (and retryTick if present) and calls setCurrentSrc(primarySrc) and
setUsedFallback(false) so the component reloads the new image and can attempt
the fallback anew; update the dependency array to include primarySrc (and
retryTick if the parent uses it) so state stays in sync with prop changes while
leaving handleError, fallbackSrc, and onFinalError logic untouched.
---
Duplicate comments:
In `@components/user/waves/UserPageProfileWave.tsx`:
- Around line 474-476: When profileWaveId becomes falsy after
clearSelectedProfileWave() (and !shouldForceUnavailableState) don’t return null;
instead perform a client-side redirect to the curations listing or render the
unavailable state UI. Locate the branch in UserPageProfileWave.tsx that checks
profileWaveId and shouldForceUnavailableState and replace the early return with
either a router.replace('/curations') call (using the app-router hook) or render
the existing unavailable/fallback component (e.g., ProfileWaveUnavailable or
similar) so the page doesn’t blank on client-side state changes. Ensure the
chosen approach runs only when profileWaveId is cleared (and not during initial
loading).
In `@components/user/waves/UserPageProfileWaveMasonry.tsx`:
- Around line 349-359: The component treats an empty drops array during
isFetching as the only "loading" case, which lets a failed useWaveDrops call
render CurationEmptyState; update the rendering logic to check the hook's error
state (e.g. isError or error returned from useWaveDrops) and surface a
retry/error UI instead of CurationEmptyState when loading failed. Locate the
useWaveDrops call and the isInitialLoading calculation (variables: drops,
isFetching, isFetchingNextPage, isInitialLoading, permissionProbeDropId,
canManageActiveCuration) and add a separate isError (or error) branch: if the
hook reports an error show a retryable error component, otherwise use the
existing empty-state logic when drops is empty and not errored.
---
Nitpick comments:
In `@components/waves/drops/WaveDropPartContentFullWidthImage.tsx`:
- Line 166: The tooltip ID currently concatenates the raw image src (e.g.,
`reset-zoom-${src}`) which can create invalid/unstable IDs when src contains URL
characters; replace these with a sanitized or hashed stable ID generation used
consistently across the component (WaveDropPartContentFullWidthImage) — for
example add a helper (sanitize or hash) that takes `src` and returns a safe
string, then use that helper for every data-tooltip-id occurrence
(`reset-zoom-${...}`, and the other two instances) and ensure the tooltip
library is given the same generated ID so matching still works.
- Around line 121-123: The Escape handler is being registered always via
useKeyPressEvent, so change it to only register or run when the modal is open:
either call useKeyPressEvent conditionally (guard the hook call with the modal
open state) or keep the hook but wrap the callback to early-return unless the
modal-open state (e.g., isOpen / isModalOpen) is true; update the
useKeyPressEvent("Escape", ...) usage and reference handleCloseModal so the
callback only invokes handleCloseModal when the modal is actually open.
In `@components/waves/drops/WaveDropPartContentMedias.tsx`:
- Around line 48-49: The MIME type check in the computation of
useNaturalHeightImage (currently using media.mime_type.includes("image")) is
case-sensitive; update the check to be case-insensitive by normalizing
media.mime_type (e.g., call toLowerCase()) before calling includes or use a
case-insensitive regex, and guard against null/undefined mime_type to avoid
runtime errors; locate the useNaturalHeightImage variable in
WaveDropPartContentMedias.tsx and replace the includes-based check accordingly.
🪄 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: 4b120423-0c43-4b34-bb38-9e1fd15b720b
📒 Files selected for processing (8)
app/[user]/curations/page.tsxapp/[user]/waves/page.tsxcomponents/user/layout/UserPageLayout.tsxcomponents/user/waves/UserPageProfileWave.tsxcomponents/user/waves/UserPageProfileWaveMasonry.tsxcomponents/waves/drops/WaveDropPartContentFullWidthImage.tsxcomponents/waves/drops/WaveDropPartContentMedias.tsxcomponents/waves/header/options/profile-wave/WaveProfileWaveAction.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- components/user/layout/UserPageLayout.tsx
|



Summary by CodeRabbit
Release Notes
New Features
Improvements