Conversation
Signed-off-by: ragnep <ragneinfo@gmail.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds URL-backed wave curations: new curation UI (view/create/edit), tab integration to select/clear curations, API/schema renames from selection/curation-group → curation with Changes
Sequence DiagramsequenceDiagram
participant User as User (UI)
participant Tabs as MyStreamWaveDesktopTabs
participant MyStream as MyStreamWave
participant Router as Router
participant Query as ReactQuery
participant API as API Server
User->>Tabs: Click curation tab "curation:123"
Tabs->>MyStream: onSelectCuration("123")
MyStream->>Router: router.replace(...?curation=123)
Router-->>MyStream: updated searchParams
MyStream->>Query: useWaveDrops(waveId, curationId="123")
Query->>API: GET /waves/{id}/drops?curation_id=123
API-->>Query: returns drops
Query-->>MyStream: drops data
MyStream->>User: render MyStreamWaveCurationContent
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
🚥 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 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: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
openapi.yaml (1)
6018-6022:⚠️ Potential issue | 🟡 MinorKeep curation identifiers typed consistently as UUIDs.
curation_idis described elsewhere in this spec as a UUID, but the path param, request body field, andApiWaveCuration.idare plain strings here. That makes the contract internally inconsistent and weakens validation/codegen.🧩 Align the identifier shape
properties: curation_id: type: string + format: uuid- name: curationId in: path required: true schema: type: string + format: uuidproperties: id: type: string + format: uuidAlso applies to: 8491-8498, 11477-11479
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi.yaml` around lines 6018 - 6022, The path parameter curationId and related fields (curation_id in request bodies and ApiWaveCuration.id model) are currently typed as plain strings; change their schema type/format to a UUID to match the rest of the spec — update the path parameter named curationId to schema: { type: string, format: uuid }, update any request body properties named curation_id to the same UUID schema, and update the ApiWaveCuration.id property to type: string with format: uuid so all references are consistent (also apply the same change at the other occurrences noted: the blocks around lines 8491-8498 and 11477-11479).
🧹 Nitpick comments (8)
hooks/useWaveDropsLeaderboard.ts (1)
138-138: Consider finishing terminology migration in local names.Lines 138 and 181 correctly send
curation_id; optionally renamecuratedByGroupIdinternals tocurationIdto reduce future parameter drift.Also applies to: 181-181
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@hooks/useWaveDropsLeaderboard.ts` at line 138, The code sends curation_id externally but still uses the older internal names curatedByGroupId/normalizedCuratedByGroupId; rename these internals to curationId and normalizedCurationId (and update any function parameters, destructured props, local vars, and all references in hooks/useWaveDropsLeaderboard.ts) so the internal names match the outgoing field (curation_id) — specifically replace occurrences of curatedByGroupId -> curationId and normalizedCuratedByGroupId -> normalizedCurationId (including the spots that supply the curation_id at the places around the previous lines 138 and 181) and run a quick grep to update call sites and tests.components/waves/utils/getOptimisticDrop.ts (1)
90-90: Consider using nullish coalescing for consistency.
buildPreviewDrop.tsusesconnectedProfile?.profile_wave_id ?? nullto ensure a consistentnullvalue when the property is undefined. Here,connectedProfile.profile_wave_idcould produceundefinedif the property is not set, leading to inconsistent drop shapes.♻️ Suggested fix
- profile_wave_id: connectedProfile.profile_wave_id, + profile_wave_id: connectedProfile.profile_wave_id ?? null,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/utils/getOptimisticDrop.ts` at line 90, The drop object in getOptimisticDrop.ts sets profile_wave_id directly from connectedProfile (profile_wave_id: connectedProfile.profile_wave_id) which can be undefined and cause inconsistent shapes; change it to use optional chaining and nullish coalescing (connectedProfile?.profile_wave_id ?? null) so the field is explicitly null when missing—update the assignment inside the function that builds the optimistic drop (look for profile_wave_id in the object returned by the function) to use the optional chain and ?? null.components/brain/my-stream/tabs/MyStreamWaveTabsMeme.tsx (1)
122-143: Initial countdown may briefly show zeros before first calculation.The
timeLeftstate initializes toEMPTY_TIME_LEFT(all zeros), but the interval only starts calculating after mount. This could cause a brief flash of00:00:00:00before the actual time is computed.Consider calculating the initial value immediately:
🔧 Suggested fix
- const [timeLeft, setTimeLeft] = useState<TimeLeft>(EMPTY_TIME_LEFT); + const [timeLeft, setTimeLeft] = useState<TimeLeft>(() => + typeof nextDecisionTime === "number" + ? calculateTimeLeft(nextDecisionTime) + : EMPTY_TIME_LEFT + );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/my-stream/tabs/MyStreamWaveTabsMeme.tsx` around lines 122 - 143, The countdown flashes zeros because timeLeft is initialized to EMPTY_TIME_LEFT and the interval waits one second before the first calculateTimeLeft; to fix, compute and set the initial value synchronously when nextDecisionTime is a number (call calculateTimeLeft(nextDecisionTime) and setTimeLeft(...) before creating the interval) and then start the setInterval that updates timeLeft; ensure you still clearInterval on unmount and keep the displayedTimeLeft fallback to EMPTY_TIME_LEFT when nextDecisionTime is not a number.components/brain/my-stream/curations/MyStreamWaveCurationContent.tsx (1)
34-40: Async handler doesn't needasync/awaithere.The
handleBottomIntersectionfunction is markedasyncbut theawait fetchNextPage()return value isn't used. Since React Query'sfetchNextPagereturns a promise that's safe to ignore, you can simplify this.♻️ Optional simplification
- const handleBottomIntersection = async (isIntersecting: boolean) => { + const handleBottomIntersection = (isIntersecting: boolean) => { if (!isIntersecting || !hasNextPage || isFetchingNextPage) { return; } - await fetchNextPage(); + void fetchNextPage(); };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/my-stream/curations/MyStreamWaveCurationContent.tsx` around lines 34 - 40, The handleBottomIntersection function is unnecessarily marked async and awaits fetchNextPage even though its promise isn't used; change handleBottomIntersection to a synchronous function (remove the async keyword) and call fetchNextPage() without awaiting it, keeping the existing guard logic that checks isIntersecting, hasNextPage, and isFetchingNextPage so behavior and early returns remain the same.components/brain/my-stream/MyStreamWave.tsx (1)
178-184:curationNameprop is not passed toMyStreamWaveCurationContent.The
MyStreamWaveCurationContentcomponent accepts an optionalcurationNameprop (used for the empty state title), but it's not provided here. The component will fall back to the generic "Curation" label. Consider fetching and passing the curation name for a better user experience.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/my-stream/MyStreamWave.tsx` around lines 178 - 184, The MyStreamWaveWave rendering omits the optional curationName prop when rendering MyStreamWaveCurationContent, so the empty state falls back to a generic label; locate where activeCurationId is resolved (the MyStreamWave component) and derive the name for that curation (e.g., find the matching item by id in wave.curations or call the existing helper that returns a curation title) and pass it as curationName={derivedName} into the MyStreamWaveCurationContent alongside wave, curationId={activeCurationId}, and onDropClick so the component can show the actual curation title.components/waves/groups/curation/WaveActiveCurationSection.tsx (2)
104-111: Redundant cache operations: optimistic update followed by immediate invalidation.The
onSuccesshandler sets the cache optimistically withsetQueryData, then immediately callsinvalidateQuerieswhich will refetch and potentially overwrite the optimistic update. Either:
- Remove the
setQueryDataand rely only on invalidation, or- Keep
setQueryDatafor instant UI feedback but moveinvalidateQueriestoonSettledto ensure it runs after both success and error cases.♻️ Suggested approach: keep optimistic update, defer invalidation
onSuccess: () => { if (!activeCuration) { return; } queryClient.setQueryData<ApiWaveCuration[]>( getWaveCurationsQueryKey(wave.id), (current) => current?.filter((item) => item.id !== activeCuration.id) ?? current ); - void queryClient.invalidateQueries({ - queryKey: getWaveCurationsQueryKey(wave.id), - }); setToast({ type: "success", message: "Curation deleted.", }); setIsDeleteOpen(false); removeActiveCurationFromUrl(); }, + onSettled: () => { + void queryClient.invalidateQueries({ + queryKey: getWaveCurationsQueryKey(wave.id), + }); + },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/groups/curation/WaveActiveCurationSection.tsx` around lines 104 - 111, The onSuccess currently performs an optimistic update via queryClient.setQueryData for getWaveCurationsQueryKey(wave.id) then immediately calls queryClient.invalidateQueries, which makes the optimistic update redundant; keep the optimistic update (queryClient.setQueryData(..., current => current?.filter(item => item.id !== activeCuration.id) ?? current)) for instant UI feedback and move the queryClient.invalidateQueries call into the mutation's onSettled handler (or remove it entirely) so refetching happens after success or error; update the mutation handlers (onSuccess -> remove immediate invalidateQueries, and add invalidateQueries to onSettled) referencing getWaveCurationsQueryKey, queryClient.setQueryData, queryClient.invalidateQueries, and activeCuration.
177-181: Fallback displays raw group ID which may be confusing to users.When the group is not yet loaded and
isFetchingActiveGroupis false, the UI shows the rawactiveCuration.group_id(a UUID). Consider showing a more user-friendly placeholder like "Unknown group" or hiding the row until data is available.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/groups/curation/WaveActiveCurationSection.tsx` around lines 177 - 181, Fallback shows raw UUID to users; update the UI to show a friendly placeholder when group details aren't available. In WaveActiveCurationSection, change the branch that currently renders activeCuration.group_id when isFetchingActiveGroup is false so it instead renders a user-friendly string (e.g., "Unknown group" or an empty/hide state) — locate the span that references activeCuration.group_id and replace the raw id with a conditional expression like: if isFetchingActiveGroup show "Loading...", else if activeCuration.group_id and resolved group name available show name, otherwise show "Unknown group" (or hide the row). Ensure you only touch the rendering logic around isFetchingActiveGroup / activeCuration.group_id and preserve existing CSS classes.components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx (1)
143-156: Consider improving accessibility for the Group selector.The "Group" label is a plain
spanwithout an accessible connection toSelectGroupSearchPanel. For better screen reader support, consider adding anidto the span and passing anaria-labelledbyprop to the panel (if supported).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx` around lines 143 - 156, The "Group" label is a plain span and isn't programmatically associated with SelectGroupSearchPanel, hurting accessibility; add an id on the label span (e.g., "group-label") and pass that id into SelectGroupSearchPanel via an aria-labelledby prop (or ariaLabelledby camel/prop name the component expects) so screen readers associate the label with the control; update the span element and the SelectGroupSearchPanel invocation (referencing SelectGroupSearchPanel and handleGroupSelect/selectedGroup props) to include the id/aria-labelledby pairing and adjust the component prop name if it uses a different attribute (ariaLabel or labelledBy).
🤖 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/my-stream/MyStreamWaveDesktopTabs.tsx`:
- Around line 214-223: The current logic may cast a curation option key like
"curation:{id}" to MyStreamWaveTab when computing firstOption.key; update the
block that checks activeCurationId/isMyVotesHidden/isSalesHidden/isFaqHidden and
options so you first select/filter a standard tab option (e.g., filter options
to those whose key matches the MyStreamWaveTab set or does not start with
"curation:") and then call setActiveTab with that key after a safe
type-check/guard instead of directly casting firstOption.key; reference
activeCurationId, options, firstOption.key, setActiveTab and MyStreamWaveTab
when making the change.
In `@components/brain/my-stream/MyStreamWaveLeaderboard.tsx`:
- Line 152: The URL query param key used in MyStreamWaveLeaderboard.tsx is
inconsistent with the parent component: replace any usage of "curation_id"
(e.g., the call reading into rawCuratedByGroupId via
searchParams.get("curation_id") and the other occurrences around lines ~198-200)
with "curation" so it matches the parent MyStreamWave.tsx read/write key; update
the variable/name where needed to reflect the "curation" param and ensure all
get/set operations and comparisons (e.g., rawCuratedByGroupId and any related
parsing/assignment logic) use the unified "curation" key.
In `@components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx`:
- Around line 69-74: The update branch currently calls commonApiPost for both
create and update; change the update call to use commonApiPut when isEditMode is
true so the endpoint `waves/${wave.id}/curations/${curation.id}` is issued with
HTTP PUT; locate the code in MyStreamWaveCurationCreateDialog where
commonApiPost is invoked (the return statement using endpoint conditional) and
replace the update path invocation with commonApiPut while keeping the create
branch using commonApiPost and passing the same body.
In `@components/react-query-wrapper/ReactQueryWrapper.tsx`:
- Line 98: The enum removed the key WAVE_CURATION_GROUPS causing tests to fail
and cache-key divergence with the new WAVE_CURATIONS; restore backward
compatibility by adding a deprecated alias entry WAVE_CURATION_GROUPS that maps
to the same value as WAVE_CURATIONS in the enum (keep the canonical name
WAVE_CURATIONS for production), then update the test reference in the failing
test to use WAVE_CURATIONS and remove the alias once tests are migrated. Ensure
you modify the enum containing WAVE_CURATIONS (add WAVE_CURATION_GROUPS =
WAVE_CURATIONS) and then change the test at the referenced test to use
WAVE_CURATIONS.
In `@hooks/waves/useWaveCurationGroups.ts`:
- Around line 8-9: Update the test to reference the new enum value
QueryKey.WAVE_CURATIONS instead of the old QueryKey.WAVE_CURATION_GROUPS so
query invalidation matches the hook; locate usages in tests that
assert/invalidate by query key and replace the enum. Also rename the hook and
related identifiers from useWaveCurationGroups to useWaveCurations (and update
any imports/usages) to reflect that getWaveCurationsQueryKey and the hook return
ApiWaveCuration[] from the waves/${waveId}/curations endpoint, ensuring test
names and mocks align with the new data shape.
In `@openapi.yaml`:
- Around line 1534-1542: The DELETE operation removeDropCuration should not use
a JSON requestBody; remove the requestBody block and add a required query
parameter named curation_id (type: string or the same type used in
ApiDropCurationRequest.curation_id) under parameters for the operation (keep
operationId: removeDropCuration if present); update the OpenAPI schema reference
usage accordingly (remove or repurpose ApiDropCurationRequest) so generated
clients will send curation_id as ?curation_id=... instead of relying on a DELETE
body.
---
Outside diff comments:
In `@openapi.yaml`:
- Around line 6018-6022: The path parameter curationId and related fields
(curation_id in request bodies and ApiWaveCuration.id model) are currently typed
as plain strings; change their schema type/format to a UUID to match the rest of
the spec — update the path parameter named curationId to schema: { type: string,
format: uuid }, update any request body properties named curation_id to the same
UUID schema, and update the ApiWaveCuration.id property to type: string with
format: uuid so all references are consistent (also apply the same change at the
other occurrences noted: the blocks around lines 8491-8498 and 11477-11479).
---
Nitpick comments:
In `@components/brain/my-stream/curations/MyStreamWaveCurationContent.tsx`:
- Around line 34-40: The handleBottomIntersection function is unnecessarily
marked async and awaits fetchNextPage even though its promise isn't used; change
handleBottomIntersection to a synchronous function (remove the async keyword)
and call fetchNextPage() without awaiting it, keeping the existing guard logic
that checks isIntersecting, hasNextPage, and isFetchingNextPage so behavior and
early returns remain the same.
In `@components/brain/my-stream/MyStreamWave.tsx`:
- Around line 178-184: The MyStreamWaveWave rendering omits the optional
curationName prop when rendering MyStreamWaveCurationContent, so the empty state
falls back to a generic label; locate where activeCurationId is resolved (the
MyStreamWave component) and derive the name for that curation (e.g., find the
matching item by id in wave.curations or call the existing helper that returns a
curation title) and pass it as curationName={derivedName} into the
MyStreamWaveCurationContent alongside wave, curationId={activeCurationId}, and
onDropClick so the component can show the actual curation title.
In `@components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx`:
- Around line 143-156: The "Group" label is a plain span and isn't
programmatically associated with SelectGroupSearchPanel, hurting accessibility;
add an id on the label span (e.g., "group-label") and pass that id into
SelectGroupSearchPanel via an aria-labelledby prop (or ariaLabelledby camel/prop
name the component expects) so screen readers associate the label with the
control; update the span element and the SelectGroupSearchPanel invocation
(referencing SelectGroupSearchPanel and handleGroupSelect/selectedGroup props)
to include the id/aria-labelledby pairing and adjust the component prop name if
it uses a different attribute (ariaLabel or labelledBy).
In `@components/brain/my-stream/tabs/MyStreamWaveTabsMeme.tsx`:
- Around line 122-143: The countdown flashes zeros because timeLeft is
initialized to EMPTY_TIME_LEFT and the interval waits one second before the
first calculateTimeLeft; to fix, compute and set the initial value synchronously
when nextDecisionTime is a number (call calculateTimeLeft(nextDecisionTime) and
setTimeLeft(...) before creating the interval) and then start the setInterval
that updates timeLeft; ensure you still clearInterval on unmount and keep the
displayedTimeLeft fallback to EMPTY_TIME_LEFT when nextDecisionTime is not a
number.
In `@components/waves/groups/curation/WaveActiveCurationSection.tsx`:
- Around line 104-111: The onSuccess currently performs an optimistic update via
queryClient.setQueryData for getWaveCurationsQueryKey(wave.id) then immediately
calls queryClient.invalidateQueries, which makes the optimistic update
redundant; keep the optimistic update (queryClient.setQueryData(..., current =>
current?.filter(item => item.id !== activeCuration.id) ?? current)) for instant
UI feedback and move the queryClient.invalidateQueries call into the mutation's
onSettled handler (or remove it entirely) so refetching happens after success or
error; update the mutation handlers (onSuccess -> remove immediate
invalidateQueries, and add invalidateQueries to onSettled) referencing
getWaveCurationsQueryKey, queryClient.setQueryData,
queryClient.invalidateQueries, and activeCuration.
- Around line 177-181: Fallback shows raw UUID to users; update the UI to show a
friendly placeholder when group details aren't available. In
WaveActiveCurationSection, change the branch that currently renders
activeCuration.group_id when isFetchingActiveGroup is false so it instead
renders a user-friendly string (e.g., "Unknown group" or an empty/hide state) —
locate the span that references activeCuration.group_id and replace the raw id
with a conditional expression like: if isFetchingActiveGroup show "Loading...",
else if activeCuration.group_id and resolved group name available show name,
otherwise show "Unknown group" (or hide the row). Ensure you only touch the
rendering logic around isFetchingActiveGroup / activeCuration.group_id and
preserve existing CSS classes.
In `@components/waves/utils/getOptimisticDrop.ts`:
- Line 90: The drop object in getOptimisticDrop.ts sets profile_wave_id directly
from connectedProfile (profile_wave_id: connectedProfile.profile_wave_id) which
can be undefined and cause inconsistent shapes; change it to use optional
chaining and nullish coalescing (connectedProfile?.profile_wave_id ?? null) so
the field is explicitly null when missing—update the assignment inside the
function that builds the optimistic drop (look for profile_wave_id in the object
returned by the function) to use the optional chain and ?? null.
In `@hooks/useWaveDropsLeaderboard.ts`:
- Line 138: The code sends curation_id externally but still uses the older
internal names curatedByGroupId/normalizedCuratedByGroupId; rename these
internals to curationId and normalizedCurationId (and update any function
parameters, destructured props, local vars, and all references in
hooks/useWaveDropsLeaderboard.ts) so the internal names match the outgoing field
(curation_id) — specifically replace occurrences of curatedByGroupId ->
curationId and normalizedCuratedByGroupId -> normalizedCurationId (including the
spots that supply the curation_id at the places around the previous lines 138
and 181) and run a quick grep to update call sites and tests.
🪄 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: bd310971-c294-44fb-96b6-5c8d845fbe1d
⛔ Files ignored due to path filters (14)
generated/models/ApiDrop.tsis excluded by!**/generated/**generated/models/ApiDropCurationRequest.tsis excluded by!**/generated/**generated/models/ApiDropResolvedIdentityProfile.tsis excluded by!**/generated/**generated/models/ApiDropWithoutWave.tsis excluded by!**/generated/**generated/models/ApiIdentity.tsis excluded by!**/generated/**generated/models/ApiProfileMin.tsis excluded by!**/generated/**generated/models/ApiSeizeSettings.tsis excluded by!**/generated/**generated/models/ApiSetProfileWaveRequest.tsis excluded by!**/generated/**generated/models/ApiWave.tsis excluded by!**/generated/**generated/models/ApiWaveCuration.tsis excluded by!**/generated/**generated/models/ApiWaveCurationRequest.tsis excluded by!**/generated/**generated/models/ApiWaveMin.tsis excluded by!**/generated/**generated/models/ApiWaveSelection.tsis excluded by!**/generated/**generated/models/ObjectSerializer.tsis excluded by!**/generated/**
📒 Files selected for processing (28)
components/brain/my-stream/MyStreamWave.tsxcomponents/brain/my-stream/MyStreamWaveDesktopTabs.tsxcomponents/brain/my-stream/MyStreamWaveLeaderboard.tsxcomponents/brain/my-stream/curations/MyStreamWaveCurationContent.tsxcomponents/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsxcomponents/brain/my-stream/tabs/MyStreamWaveTabs.tsxcomponents/brain/my-stream/tabs/MyStreamWaveTabsDefault.tsxcomponents/brain/my-stream/tabs/MyStreamWaveTabsMeme.tsxcomponents/react-query-wrapper/ReactQueryWrapper.tsxcomponents/utils/select-group/SelectGroupModalItems.tsxcomponents/utils/select-group/SelectGroupSearchPanel.tsxcomponents/waves/drops/DropAuthorBadges.tsxcomponents/waves/drops/reaction-utils.tscomponents/waves/groups/WaveGroups.tsxcomponents/waves/groups/curation/WaveActiveCurationSection.tsxcomponents/waves/groups/curation/WaveCurationGroupsSection.tsxcomponents/waves/leaderboard/header/WaveLeaderboardCurationGroupSelect.tsxcomponents/waves/leaderboard/header/WaveleaderboardHeader.tsxcomponents/waves/memes/submission/utils/buildPreviewDrop.tscomponents/waves/specs/groups/group/edit/buttons/utils/identityGroupWorkflow.tscomponents/waves/utils/getOptimisticDrop.tscontexts/SeizeSettingsContext.tsxhelpers/ProfileHelpers.tshooks/useWaveDrops.tshooks/useWaveDropsLeaderboard.tshooks/useWaveDropsSearch.tshooks/waves/useWaveCurationGroups.tsopenapi.yaml
💤 Files with no reviewable changes (2)
- components/waves/groups/curation/WaveCurationGroupsSection.tsx
- components/waves/specs/groups/group/edit/buttons/utils/identityGroupWorkflow.ts
There was a problem hiding this comment.
♻️ Duplicate comments (1)
components/brain/my-stream/MyStreamWaveDesktopTabs.tsx (1)
216-223:⚠️ Potential issue | 🟠 MajorFallback tab selection still uses an unsafe enum cast.
This still picks from
options[0](which may be acuration:*key) and casts it toMyStreamWaveTab. UsestandardOptions[0](or an explicit enum guard) before callingsetActiveTab.Suggested fix
if ( !activeCurationId && (isMyVotesHidden || isSalesHidden || isFaqHidden) && - options.length > 0 + standardOptions.length > 0 ) { - const firstOption = options[0]; + const firstOption = standardOptions[0]; if (firstOption) { - setActiveTab(firstOption.key as MyStreamWaveTab); + setActiveTab(firstOption.key as MyStreamWaveTab); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/my-stream/MyStreamWaveDesktopTabs.tsx` around lines 216 - 223, The fallback tab selection uses options[0] and casts it unsafely to MyStreamWaveTab; change the logic to pick from standardOptions[0] (or validate the key with an enum guard) before calling setActiveTab so you never pass a curation:* key: inside the block that checks !activeCurationId && (isMyVotesHidden || isSalesHidden || isFaqHidden) && options.length > 0, replace the use of options[0] with standardOptions[0] (or run a predicate like isMyStreamWaveTab(key) that ensures the key is a valid MyStreamWaveTab) and only then call setActiveTab(firstStandardKey as MyStreamWaveTab).
🧹 Nitpick comments (1)
components/waves/groups/curation/WaveCurationGroupsSection.tsx (1)
34-35: Normalize unknowncurationquery values to avoid a “no active option” state.If the URL has a stale/invalid
curationid, neither “Chat” nor any curation button is active. Treat unknown ids asnullfor UI state (and optionally clean the URL) so selection stays consistent.Proposed change
- const activeCurationId = searchParams.get("curation"); + const requestedCurationId = searchParams.get("curation"); + const activeCurationId = curations.some( + (curation) => curation.id === requestedCurationId + ) + ? requestedCurationId + : null;Also applies to: 85-93
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/groups/curation/WaveCurationGroupsSection.tsx` around lines 34 - 35, The UI reads activeCurationId from searchParams.get("curation") and can end up with an unknown/stale id which causes no button to be active; change the logic that computes activeCurationId to validate the value against the current list of curation ids (e.g., the collection used to render buttons) and normalize any value not present to null (and optionally remove/replace the query param via the same searchParams setter). Update both occurrences where activeCurationId is derived (the initial const activeCurationId = searchParams.get("curation") and the similar logic around lines 85-93) so components rendering “Chat” and curation buttons use the normalized value to determine active state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@components/brain/my-stream/MyStreamWaveDesktopTabs.tsx`:
- Around line 216-223: The fallback tab selection uses options[0] and casts it
unsafely to MyStreamWaveTab; change the logic to pick from standardOptions[0]
(or validate the key with an enum guard) before calling setActiveTab so you
never pass a curation:* key: inside the block that checks !activeCurationId &&
(isMyVotesHidden || isSalesHidden || isFaqHidden) && options.length > 0, replace
the use of options[0] with standardOptions[0] (or run a predicate like
isMyStreamWaveTab(key) that ensures the key is a valid MyStreamWaveTab) and only
then call setActiveTab(firstStandardKey as MyStreamWaveTab).
---
Nitpick comments:
In `@components/waves/groups/curation/WaveCurationGroupsSection.tsx`:
- Around line 34-35: The UI reads activeCurationId from
searchParams.get("curation") and can end up with an unknown/stale id which
causes no button to be active; change the logic that computes activeCurationId
to validate the value against the current list of curation ids (e.g., the
collection used to render buttons) and normalize any value not present to null
(and optionally remove/replace the query param via the same searchParams
setter). Update both occurrences where activeCurationId is derived (the initial
const activeCurationId = searchParams.get("curation") and the similar logic
around lines 85-93) so components rendering “Chat” and curation buttons use the
normalized value to determine active state.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5db45b88-7dac-4d25-b668-17dc7d2a3e5c
📒 Files selected for processing (4)
components/brain/my-stream/MyStreamWave.tsxcomponents/brain/my-stream/MyStreamWaveDesktopTabs.tsxcomponents/waves/groups/WaveGroups.tsxcomponents/waves/groups/curation/WaveCurationGroupsSection.tsx
✅ Files skipped from review due to trivial changes (1)
- components/waves/groups/WaveGroups.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- components/brain/my-stream/MyStreamWave.tsx
There was a problem hiding this comment.
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)
contexts/SeizeSettingsContext.tsx (1)
51-60:⚠️ Potential issue | 🔴 CriticalRemove duplicate
announcements_wave_idproperty.The
announcements_wave_idproperty is declared twice in the initial state object (lines 56 and 59). In JavaScript, duplicate object keys cause the second declaration to overwrite the first, making line 56 ineffective.🐛 Proposed fix to remove the duplicate property
const [seizeSettings, setSeizeSettings] = useState<TempApiSeizeSettings>({ rememes_submission_tdh_threshold: 0, all_drops_notifications_subscribers_limit: 0, memes_wave_id: null, curation_wave_id: null, announcements_wave_id: null, distribution_admin_wallets: [], claims_admin_wallets: [], - announcements_wave_id: null, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@contexts/SeizeSettingsContext.tsx` around lines 51 - 60, The initial state object for seizeSettings in useState<TempApiSeizeSettings> contains a duplicate key announcements_wave_id; remove the redundant property so the object only declares announcements_wave_id once (update the object literal used when calling useState in contexts/SeizeSettingsContext.tsx, e.g., the seizeSettings / setSeizeSettings initial value) to prevent accidental overwrites and ensure the intended default is preserved.
♻️ Duplicate comments (2)
components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx (1)
140-145:⚠️ Potential issue | 🟠 MajorUse
commonApiPutfor updates instead of POST.At Line 140, edit mode still sends
POSTtowaves/${wave.id}/curations/${curation.id}(Line 142). KeepPOSTfor create and usePUTfor update.Suggested fix
-import { commonApiPost } from "@/services/api/common-api"; +import { commonApiPost, commonApiPut } from "@/services/api/common-api"; ... - return await commonApiPost<ApiWaveCurationRequest, ApiWaveCuration>({ - endpoint: isEditMode - ? `waves/${wave.id}/curations/${curation.id}` - : `waves/${wave.id}/curations`, - body, - }); + if (isEditMode) { + return await commonApiPut<ApiWaveCurationRequest, ApiWaveCuration>({ + endpoint: `waves/${wave.id}/curations/${curation.id}`, + body, + }); + } + return await commonApiPost<ApiWaveCurationRequest, ApiWaveCuration>({ + endpoint: `waves/${wave.id}/curations`, + body, + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx` around lines 140 - 145, The update branch is incorrectly using commonApiPost when isEditMode is true; change the call so create (isEditMode false) continues to call commonApiPost with endpoint `waves/${wave.id}/curations` and update (isEditMode true) calls commonApiPut with endpoint `waves/${wave.id}/curations/${curation.id}` while passing the same body; locate this in MyStreamWaveCurationCreateDialog where commonApiPost is used and switch to conditional invocation of commonApiPut for edits.components/brain/my-stream/MyStreamWaveDesktopTabs.tsx (1)
217-224:⚠️ Potential issue | 🟡 MinorUse
standardOptionsfor fallback tab selection.At Line 221,
options[0]can be a curation key and is cast at Line 223 toMyStreamWaveTab. Pick fromstandardOptionsinstead.Suggested fix
if ( !activeCurationId && (isMyVotesHidden || isSalesHidden || isFaqHidden) && - options.length > 0 + standardOptions.length > 0 ) { - const firstOption = options[0]; + const firstOption = standardOptions[0]; if (firstOption) { setActiveTab(firstOption.key as MyStreamWaveTab); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/my-stream/MyStreamWaveDesktopTabs.tsx` around lines 217 - 224, The fallback tab selection currently uses options[0] and casts it to MyStreamWaveTab which can be a curation key; change the fallback to use standardOptions instead: when !activeCurationId && (isMyVotesHidden || isSalesHidden || isFaqHidden) && options.length > 0, pick const fallback = standardOptions[0] (ensure it exists) and call setActiveTab(fallback.key as MyStreamWaveTab) rather than using firstOption from options; update any null/undefined guards accordingly so setActiveTab only runs with a valid standardOptions[0].
🧹 Nitpick comments (2)
components/utils/select-group/SelectGroupModalCard.tsx (2)
11-11: Minor: Redundant type annotation.The
| undefinedis redundant when combined with the optional?modifier. Both achieve the same result.♻️ Simplified type
- readonly onClear?: (() => void) | undefined; + readonly onClear?: () => void;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/utils/select-group/SelectGroupModalCard.tsx` at line 11, The onClear prop declaration redundantly uses "| undefined" together with the optional modifier; update the SelectGroupModalCard prop/interface by removing the " | undefined" from the readonly onClear?: declaration so it reads simply as an optional function type (readonly onClear?: () => void) and leave other optional props unchanged; locate the onClear symbol in SelectGroupModalCard.tsx to apply the change.
29-45: Consider using a semantic<button>element instead ofrole="button"on a<div>.SonarCloud flagged this as an accessibility concern. While your implementation includes proper ARIA attributes (
aria-pressed,aria-label) and keyboard handling (Enter/Space), using a native<button>element provides better accessibility guarantees across assistive technologies and devices.If the complex nested content makes
<button>styling difficult, the current implementation is acceptable since all necessary accessibility features are present. However, a<button>withtw-appearance-noneand reset styles would be the preferred approach.♻️ Suggested refactor using semantic button
- <div - role="button" - tabIndex={0} + <button + type="button" aria-pressed={isSelected} aria-label={`Select ${group.name}`} onClick={() => onSelect(group)} - onKeyDown={(event) => { - if (event.key === "Enter" || event.key === " ") { - event.preventDefault(); - onSelect(group); - } - }} - className={`tw-group tw-relative tw-cursor-pointer tw-overflow-hidden tw-rounded-xl tw-border tw-border-solid tw-bg-iron-950 tw-no-underline tw-shadow-sm tw-shadow-black/20 tw-backdrop-blur-sm tw-transition-all tw-duration-300 tw-ease-out desktop-hover:hover:tw-bg-iron-900 desktop-hover:hover:tw-shadow-md desktop-hover:hover:tw-shadow-black/25 ${ + className={`tw-group tw-relative tw-w-full tw-cursor-pointer tw-overflow-hidden tw-rounded-xl tw-border tw-border-solid tw-bg-iron-950 tw-p-0 tw-text-left tw-no-underline tw-shadow-sm tw-shadow-black/20 tw-backdrop-blur-sm tw-transition-all tw-duration-300 tw-ease-out desktop-hover:hover:tw-bg-iron-900 desktop-hover:hover:tw-shadow-md desktop-hover:hover:tw-shadow-black/25 ${ isSelected ? "tw-shadow-primary-900/20 tw-border-primary-300 tw-shadow-md tw-ring-1 tw-ring-primary-400/25" : "tw-border-white/10" } focus-visible:tw-outline focus-visible:tw-outline-2 focus-visible:tw-outline-offset-2 focus-visible:tw-outline-primary-500`} > ... - </div> + </button>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/utils/select-group/SelectGroupModalCard.tsx` around lines 29 - 45, Replace the non-semantic clickable <div> in the SelectGroupModalCard component with a native <button type="button"> to address accessibility concerns: remove role="button" and tabIndex, keep aria-pressed and aria-label, preserve the onClick={onSelect(group)} handler and conditional className logic that uses isSelected, and drop the manual onKeyDown Enter/Space handling since the button natively supports keyboard activation; add reset styles (e.g., tw-appearance-none and any existing focus/outline classes) to the button so styling and behavior remain identical.
🤖 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/my-stream/MyStreamWaveDesktopTabs.tsx`:
- Line 208: The file MyStreamWaveDesktopTabs.tsx contains two eslint-disable
comments for the unregistered rule
react-you-might-not-need-an-effect/no-pass-data-to-parent; remove those
suppression comments in the MyStreamWaveDesktopTabs component (or replace them
with a suppression for a rule that exists in eslint.config.mjs) so the file no
longer references an unconfigured plugin, or alternatively add the plugin and
rule to eslint.config.mjs so the suppression becomes valid; locate occurrences
by searching for "react-you-might-not-need-an-effect/no-pass-data-to-parent" in
MyStreamWaveDesktopTabs and update accordingly.
In `@components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx`:
- Around line 205-208: The standalone <label> in the
MyStreamWaveCurationCreateDialog component is used without an associated form
control; replace it with proper grouped form semantics by wrapping the preset
button group in a <fieldset> and move the "Name" text into a <legend> (or render
plain text styled like the current label) so the heading correctly groups the
related controls; update the surrounding markup where the current label and the
div with className "tw-flex tw-flex-wrap tw-gap-2.5" are defined to use
fieldset/legend and keep existing classes for styling.
---
Outside diff comments:
In `@contexts/SeizeSettingsContext.tsx`:
- Around line 51-60: The initial state object for seizeSettings in
useState<TempApiSeizeSettings> contains a duplicate key announcements_wave_id;
remove the redundant property so the object only declares announcements_wave_id
once (update the object literal used when calling useState in
contexts/SeizeSettingsContext.tsx, e.g., the seizeSettings / setSeizeSettings
initial value) to prevent accidental overwrites and ensure the intended default
is preserved.
---
Duplicate comments:
In `@components/brain/my-stream/MyStreamWaveDesktopTabs.tsx`:
- Around line 217-224: The fallback tab selection currently uses options[0] and
casts it to MyStreamWaveTab which can be a curation key; change the fallback to
use standardOptions instead: when !activeCurationId && (isMyVotesHidden ||
isSalesHidden || isFaqHidden) && options.length > 0, pick const fallback =
standardOptions[0] (ensure it exists) and call setActiveTab(fallback.key as
MyStreamWaveTab) rather than using firstOption from options; update any
null/undefined guards accordingly so setActiveTab only runs with a valid
standardOptions[0].
In `@components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx`:
- Around line 140-145: The update branch is incorrectly using commonApiPost when
isEditMode is true; change the call so create (isEditMode false) continues to
call commonApiPost with endpoint `waves/${wave.id}/curations` and update
(isEditMode true) calls commonApiPut with endpoint
`waves/${wave.id}/curations/${curation.id}` while passing the same body; locate
this in MyStreamWaveCurationCreateDialog where commonApiPost is used and switch
to conditional invocation of commonApiPut for edits.
---
Nitpick comments:
In `@components/utils/select-group/SelectGroupModalCard.tsx`:
- Line 11: The onClear prop declaration redundantly uses "| undefined" together
with the optional modifier; update the SelectGroupModalCard prop/interface by
removing the " | undefined" from the readonly onClear?: declaration so it reads
simply as an optional function type (readonly onClear?: () => void) and leave
other optional props unchanged; locate the onClear symbol in
SelectGroupModalCard.tsx to apply the change.
- Around line 29-45: Replace the non-semantic clickable <div> in the
SelectGroupModalCard component with a native <button type="button"> to address
accessibility concerns: remove role="button" and tabIndex, keep aria-pressed and
aria-label, preserve the onClick={onSelect(group)} handler and conditional
className logic that uses isSelected, and drop the manual onKeyDown Enter/Space
handling since the button natively supports keyboard activation; add reset
styles (e.g., tw-appearance-none and any existing focus/outline classes) to the
button so styling and behavior remain identical.
🪄 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: aa9afb00-31f6-4a56-8fff-290f49c2a82a
📒 Files selected for processing (12)
components/brain/my-stream/MyStreamActionTooltip.tsxcomponents/brain/my-stream/MyStreamWaveDesktopTabs.tsxcomponents/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsxcomponents/brain/my-stream/tabs/MyStreamWaveTabsDefault.tsxcomponents/brain/my-stream/tabs/MyStreamWaveTabsMeme.tsxcomponents/mobile-wrapper-dialog/MobileWrapperDialog.tsxcomponents/utils/select-group/SelectGroupModalCard.tsxcomponents/utils/select-group/SelectGroupModalItems.tsxcomponents/utils/select-group/SelectGroupModalSearchName.tsxcomponents/utils/select-group/SelectGroupSearchPanel.tsxcontexts/SeizeSettingsContext.tsxpnpm-workspace.yaml
✅ Files skipped from review due to trivial changes (1)
- pnpm-workspace.yaml
🚧 Files skipped from review as they are similar to previous changes (2)
- components/utils/select-group/SelectGroupModalItems.tsx
- components/utils/select-group/SelectGroupSearchPanel.tsx
There was a problem hiding this comment.
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)
openapi.yaml (1)
6018-6022:⚠️ Potential issue | 🟡 MinorKeep the curation identifier typed consistently.
The new filters document curation IDs as UUIDs, but
ApiWaveCuration.id,{curationId}, andApiDropCurationRequest.curation_idare still plain strings. That makes the same identifier inconsistent across read/write endpoints and weakens generated-client validation.Suggested schema alignment
/waves/{id}/curations/{curationId}: post: parameters: - name: curationId in: path required: true schema: type: string + format: uuidApiDropCurationRequest: type: object required: - curation_id properties: curation_id: type: string + format: uuidApiWaveCuration: type: object required: - id - name - wave_id - group_id - created_at - updated_at properties: id: type: string + format: uuidAlso applies to: 8503-8510, 11489-11514
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi.yaml` around lines 6018 - 6022, The curation identifier is inconsistently typed; update the OpenAPI definitions so the curation ID is a UUID everywhere: change the path parameter schema for {curationId} to schema: type: string with format: uuid (or an equivalent UUID pattern), and update the component schemas ApiWaveCuration.id and ApiDropCurationRequest.curation_id to the same type+format so read/write endpoints match; apply the same change to the other occurrences mentioned (lines referenced in the comment).
♻️ Duplicate comments (4)
openapi.yaml (1)
1523-1539:⚠️ Potential issue | 🟠 MajorModel
removeDropCurationwithout a DELETE body.
DELETErequest bodies are not portable in OpenAPI 3.0.3, so generated clients and intermediaries may ignoreApiDropCurationRequestentirely and dropcuration_id. Put the identifier in the path or query instead.Suggested contract change
/drops/{dropId}/curations: delete: tags: - Drops summary: Remove a drop from a curation operationId: removeDropCuration parameters: - name: dropId in: path required: true schema: type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ApiDropCurationRequest" + - name: curation_id + in: query + required: true + schema: + type: string + format: uuid responses: "200": description: successful operation🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi.yaml` around lines 1523 - 1539, The DELETE operation with operationId removeDropCuration currently requires a request body referencing ApiDropCurationRequest which is non-portable in OpenAPI 3.0.3; change the contract to remove the requestBody and instead add a required path or query parameter (e.g., curation_id) to the delete operation (alongside existing dropId parameter) and update any references to ApiDropCurationRequest accordingly so servers and generated clients receive curation_id via the path/query; ensure operationId removeDropCuration and the Drops tag remain unchanged.components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx (3)
115-120:⚠️ Potential issue | 🟠 MajorUse
commonApiPutfor edit-mode updates.Line 115-Line 120 still sends edit requests with
POST; update operations should usePUTonwaves/${wave.id}/curations/${curation.id}.Suggested fix
-import { commonApiPost } from "@/services/api/common-api"; +import { commonApiPost, commonApiPut } from "@/services/api/common-api"; ... - return await commonApiPost<ApiWaveCurationRequest, ApiWaveCuration>({ - endpoint: isEditMode - ? `waves/${wave.id}/curations/${curation.id}` - : `waves/${wave.id}/curations`, - body, - }); + if (isEditMode && curation) { + return await commonApiPut<ApiWaveCurationRequest, ApiWaveCuration>({ + endpoint: `waves/${wave.id}/curations/${curation.id}`, + body, + }); + } + + return await commonApiPost<ApiWaveCurationRequest, ApiWaveCuration>({ + endpoint: `waves/${wave.id}/curations`, + body, + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx` around lines 115 - 120, The code always calls commonApiPost in submit logic (see commonApiPost, isEditMode, wave.id and curation.id) so edit-mode requests are sent as POST; change the branch so when isEditMode is true you call commonApiPut with endpoint `waves/${wave.id}/curations/${curation.id}` and the same body, and keep commonApiPost only for the create branch `waves/${wave.id}/curations`.
181-205:⚠️ Potential issue | 🟡 MinorReplace standalone
<label>with grouped form semantics.Line 181 uses a
<label>not explicitly bound to a single control; for this preset group + custom input, use<fieldset>/<legend>(or plain text) to keep valid semantics.Suggested fix
- <div className="tw-space-y-4"> - <label className="tw-block tw-text-sm tw-font-medium tw-text-iron-300"> - Name - </label> + <fieldset className="tw-space-y-4"> + <legend className="tw-block tw-text-sm tw-font-medium tw-text-iron-300"> + Name + </legend> <div className="tw-flex tw-flex-wrap tw-gap-2.5"> {CURATION_NAME_PRESETS.map((preset) => { const isSelected = selectedPresetLabel === preset; ... <div className="tw-relative tw-w-full sm:tw-max-w-[260px]"> <input ... /> </div> - </div> + </fieldset>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx` around lines 181 - 205, The <label> used for the preset buttons in MyStreamWaveCurationCreateDialog.tsx is not bound to a single form control; change it to proper grouped semantics by replacing the standalone <label> with a <fieldset> containing a <legend> (e.g., "Name") and move the button list inside that fieldset so CURATION_NAME_PRESETS rendering, selectedPresetLabel state, and handlePresetSelect remain unchanged; keep buttons' aria-pressed attributes for accessibility and ensure any adjacent custom input is also inside the same fieldset so the group is semantically associated.
62-70:⚠️ Potential issue | 🟠 MajorReset form state when dialog context changes.
Line 67-Line 70 only initializes from props once. If this dialog remains mounted, reopening for another curation/group can keep stale
name/selectedGroupand submit incorrect payload.Suggested fix
-import { useRef, useState, type ChangeEvent } from "react"; +import { useEffect, useRef, useState, type ChangeEvent } from "react"; ... const [name, setName] = useState(() => initialName); const [selectedGroup, setSelectedGroup] = useState<ApiGroupFull | null>( () => initialGroup ?? null ); + useEffect(() => { + if (!isOpen) { + return; + } + setName(curation?.name ?? ""); + setSelectedGroup(initialGroup ?? null); + }, [isOpen, curation?.id, curation?.name, initialGroup?.id]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx` around lines 62 - 70, The component MyStreamWaveCurationCreateDialog initializes name and selectedGroup only once which causes stale values when the dialog is reopened for a different curation/group; add an effect that watches curation (or initialName/initialGroup) and the dialog open state and calls setName(initialName) and setSelectedGroup(initialGroup ?? null) to reset state on context changes—use the existing identifiers initialName, initialGroup, name, setName, selectedGroup, setSelectedGroup and ensure the effect dependencies include curation/initialName, initialGroup, and whatever prop signals the dialog is open (e.g., isEditMode or isOpen).
🧹 Nitpick comments (5)
components/utils/select-group/SelectGroupModalSearchName.tsx (1)
1-2: Consider de-duplicating autofill utility fragments.
DARK_AUTOFILL_CLASSESis correct, but the repeated default/hover/focus blocks are hard to maintain. Consider splitting shared tokens to reduce drift risk in future tweaks.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/utils/select-group/SelectGroupModalSearchName.tsx` around lines 1 - 2, DARK_AUTOFILL_CLASSES repeats nearly identical blocks for default/hover/focus; extract the common tokens into smaller constants (e.g., AUTOFILL_BASE and AUTOFILL_STATE or AUTOFILL_PSEUDO) and compose DARK_AUTOFILL_CLASSES from those to avoid duplication and drift. Update the constant definitions in SelectGroupModalSearchName.tsx so that shared fragments (like -webkit-text-fill-color, caret-color, and tw-shadow inset theme tokens) live in a single reusable symbol and the hover/focus variants reuse that symbol when building the final DARK_AUTOFILL_CLASSES string.components/utils/input/identity/IdentitySearch.tsx (1)
150-153: Consider wrapping inuseCallbackfor stable reference.The
closeDropdownfunction is recreated on every render and passed touseClickAwayanduseKeyPressEvent. While these hooks may handle this internally, wrapping inuseCallbackensures a stable reference and prevents potential re-subscriptions.♻️ Suggested refactor
- const closeDropdown = () => { - setIsOpen(false); - setManualHighlightedIndex(null); - }; + const closeDropdown = useCallback(() => { + setIsOpen(false); + setManualHighlightedIndex(null); + }, []);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/utils/input/identity/IdentitySearch.tsx` around lines 150 - 153, closeDropdown is recreated each render and passed into useClickAway and useKeyPressEvent; wrap it with React's useCallback to provide a stable reference. Replace the inline const closeDropdown = () => { setIsOpen(false); setManualHighlightedIndex(null); } with a useCallback that depends only on setIsOpen and setManualHighlightedIndex so the function identity remains stable when used with useClickAway and useKeyPressEvent.components/utils/select-group/SelectGroupModalItems.tsx (1)
29-39: Scope clear action to the selected card and preserve list semantics.Consider limiting
onClearto the selected item and usingul/lito keep collection semantics for assistive tech.Proposed refactor
- <div className="tw-grid tw-grid-cols-1 tw-gap-2"> + <ul className="tw-grid tw-grid-cols-1 tw-gap-2" role="list"> {groups.map((group) => ( - <SelectGroupModalCard - key={group.id} - group={group} - isSelected={selectedGroupId === group.id} - onSelect={onGroupSelect} - onClear={onGroupClear} - /> + <li key={group.id}> + <SelectGroupModalCard + group={group} + isSelected={selectedGroupId === group.id} + onSelect={onGroupSelect} + onClear={selectedGroupId === group.id ? onGroupClear : undefined} + /> + </li> ))} - </div> + </ul>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/utils/select-group/SelectGroupModalItems.tsx` around lines 29 - 39, The current rendering of groups uses a div grid and passes the same onClear to every SelectGroupModalCard; change the container to a semantic list (use ul with li per item) and only pass the onClear handler to the card when that card is the selected one (compare selectedGroupId === group.id), so non-selected cards do not receive/trigger clear; update SelectGroupModalItems to render each group inside an li and conditionally provide onClear to SelectGroupModalCard (keep onSelect always passed) to scope clear actions to the selected item.components/waves/drops/WaveDropCurationsDialog.tsx (1)
337-349: Optional: Consider adding a section title for read-only curations.The read-only curations are rendered without a section title, unlike "Included" and "Available to curate" sections. For consistency and clarity, consider wrapping them in
CurationsSectionwith a title like "Other curations" or "View only".♻️ Optional improvement
{readOnlyCurations.length > 0 && ( - <div className="tw-flex tw-flex-col tw-gap-2"> + <CurationsSection title="Other curations"> {readOnlyCurations.map((curation) => ( <CurationMembershipRow key={curation.id} curation={curation} disabled={true} loading={false} onUpdateMembership={() => undefined} /> ))} - </div> + </CurationsSection> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/waves/drops/WaveDropCurationsDialog.tsx` around lines 337 - 349, Add a titled section wrapper for the read-only list to match the other sections: wrap the readOnlyCurations rendering in a CurationsSection (or the component used for "Included"/"Available to curate") and pass a title such as "Other curations" or "View only"; keep the inner mapping over readOnlyCurations and CurationMembershipRow (with disabled and onUpdateMembership unchanged) so behavior is identical but now clearly labeled.components/brain/my-stream/curations/MyStreamWaveCurationContent.tsx (1)
107-133: Permission check relies on first drop only.The
canManageActiveCurationflag is derived from the first drop's curations permissions. This assumes all drops in the curation have the same permission model, which is likely correct but worth noting.If the first drop is removed (causing a re-render with a different first drop), the permission check will re-evaluate, which could cause a brief UI flicker if permissions differ.
This is acceptable for now since curations are typically wave-level (not drop-level) permissions, but consider caching the permission result or fetching it from a dedicated endpoint if this becomes an issue.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/my-stream/curations/MyStreamWaveCurationContent.tsx` around lines 107 - 133, The permission check currently probes only the first drop (permissionProbeDropId derived from drops[0]?.id) and derives canManageActiveCuration from permissionProbeCurations, which can flicker if the first drop changes; update the logic to request permissions in a curation-scoped manner (e.g., call a curation-level permission endpoint using curationId) or request permissions for all drops (pass all drop ids to useDropCurations) and then compute canManageActiveCuration by aggregating permissionProbeCurations (e.g., any matching curation.id === curationId with authenticated_user_can_curate true), and optionally persist the resolved permission in local state to avoid transient UI flicker during re-renders.
🤖 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/utils/select-group/SelectGroupModalSearchName.tsx`:
- Line 36: The floating label uses a physical left margin (tw-ml-6) which breaks
in RTL; in SelectGroupModalSearchName.tsx update the label's className to use a
logical start margin instead (replace tw-ml-6 with the logical equivalent, e.g.,
tw-ms-6 or the project's configured logical margin utility) so the label
respects tw-start-1 in both LTR/RTL, and remove or adjust any conflicting
rtl:peer-focus:left/translate classes (like rtl:peer-focus:tw-left-auto
rtl:peer-focus:tw-translate-x-1/4) to rely on logical positioning instead.
In `@components/waves/drops/WaveDropMobileMenu.tsx`:
- Around line 105-114: When navigator.clipboard.writeText is not available the
current branch simply calls closeMenu() and returns with no user feedback;
update the copy handler (the branch checking typeof
navigator.clipboard.writeText) to provide user-visible failure feedback instead
of silently returning — e.g., invoke an existing toast/notification API or set a
failure state (similar to setCopied) with a message like "Copy not supported"
and keep closeMenu behavior consistent; optionally implement a fallback
(select+execCommand or showing the link so the user can manually copy)
referencing navigator.clipboard.writeText, closeMenu, setCopied, and dropLink to
locate where to make the change.
---
Outside diff comments:
In `@openapi.yaml`:
- Around line 6018-6022: The curation identifier is inconsistently typed; update
the OpenAPI definitions so the curation ID is a UUID everywhere: change the path
parameter schema for {curationId} to schema: type: string with format: uuid (or
an equivalent UUID pattern), and update the component schemas ApiWaveCuration.id
and ApiDropCurationRequest.curation_id to the same type+format so read/write
endpoints match; apply the same change to the other occurrences mentioned (lines
referenced in the comment).
---
Duplicate comments:
In `@components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx`:
- Around line 115-120: The code always calls commonApiPost in submit logic (see
commonApiPost, isEditMode, wave.id and curation.id) so edit-mode requests are
sent as POST; change the branch so when isEditMode is true you call commonApiPut
with endpoint `waves/${wave.id}/curations/${curation.id}` and the same body, and
keep commonApiPost only for the create branch `waves/${wave.id}/curations`.
- Around line 181-205: The <label> used for the preset buttons in
MyStreamWaveCurationCreateDialog.tsx is not bound to a single form control;
change it to proper grouped semantics by replacing the standalone <label> with a
<fieldset> containing a <legend> (e.g., "Name") and move the button list inside
that fieldset so CURATION_NAME_PRESETS rendering, selectedPresetLabel state, and
handlePresetSelect remain unchanged; keep buttons' aria-pressed attributes for
accessibility and ensure any adjacent custom input is also inside the same
fieldset so the group is semantically associated.
- Around line 62-70: The component MyStreamWaveCurationCreateDialog initializes
name and selectedGroup only once which causes stale values when the dialog is
reopened for a different curation/group; add an effect that watches curation (or
initialName/initialGroup) and the dialog open state and calls
setName(initialName) and setSelectedGroup(initialGroup ?? null) to reset state
on context changes—use the existing identifiers initialName, initialGroup, name,
setName, selectedGroup, setSelectedGroup and ensure the effect dependencies
include curation/initialName, initialGroup, and whatever prop signals the dialog
is open (e.g., isEditMode or isOpen).
In `@openapi.yaml`:
- Around line 1523-1539: The DELETE operation with operationId
removeDropCuration currently requires a request body referencing
ApiDropCurationRequest which is non-portable in OpenAPI 3.0.3; change the
contract to remove the requestBody and instead add a required path or query
parameter (e.g., curation_id) to the delete operation (alongside existing dropId
parameter) and update any references to ApiDropCurationRequest accordingly so
servers and generated clients receive curation_id via the path/query; ensure
operationId removeDropCuration and the Drops tag remain unchanged.
---
Nitpick comments:
In `@components/brain/my-stream/curations/MyStreamWaveCurationContent.tsx`:
- Around line 107-133: The permission check currently probes only the first drop
(permissionProbeDropId derived from drops[0]?.id) and derives
canManageActiveCuration from permissionProbeCurations, which can flicker if the
first drop changes; update the logic to request permissions in a curation-scoped
manner (e.g., call a curation-level permission endpoint using curationId) or
request permissions for all drops (pass all drop ids to useDropCurations) and
then compute canManageActiveCuration by aggregating permissionProbeCurations
(e.g., any matching curation.id === curationId with
authenticated_user_can_curate true), and optionally persist the resolved
permission in local state to avoid transient UI flicker during re-renders.
In `@components/utils/input/identity/IdentitySearch.tsx`:
- Around line 150-153: closeDropdown is recreated each render and passed into
useClickAway and useKeyPressEvent; wrap it with React's useCallback to provide a
stable reference. Replace the inline const closeDropdown = () => {
setIsOpen(false); setManualHighlightedIndex(null); } with a useCallback that
depends only on setIsOpen and setManualHighlightedIndex so the function identity
remains stable when used with useClickAway and useKeyPressEvent.
In `@components/utils/select-group/SelectGroupModalItems.tsx`:
- Around line 29-39: The current rendering of groups uses a div grid and passes
the same onClear to every SelectGroupModalCard; change the container to a
semantic list (use ul with li per item) and only pass the onClear handler to the
card when that card is the selected one (compare selectedGroupId === group.id),
so non-selected cards do not receive/trigger clear; update SelectGroupModalItems
to render each group inside an li and conditionally provide onClear to
SelectGroupModalCard (keep onSelect always passed) to scope clear actions to the
selected item.
In `@components/utils/select-group/SelectGroupModalSearchName.tsx`:
- Around line 1-2: DARK_AUTOFILL_CLASSES repeats nearly identical blocks for
default/hover/focus; extract the common tokens into smaller constants (e.g.,
AUTOFILL_BASE and AUTOFILL_STATE or AUTOFILL_PSEUDO) and compose
DARK_AUTOFILL_CLASSES from those to avoid duplication and drift. Update the
constant definitions in SelectGroupModalSearchName.tsx so that shared fragments
(like -webkit-text-fill-color, caret-color, and tw-shadow inset theme tokens)
live in a single reusable symbol and the hover/focus variants reuse that symbol
when building the final DARK_AUTOFILL_CLASSES string.
In `@components/waves/drops/WaveDropCurationsDialog.tsx`:
- Around line 337-349: Add a titled section wrapper for the read-only list to
match the other sections: wrap the readOnlyCurations rendering in a
CurationsSection (or the component used for "Included"/"Available to curate")
and pass a title such as "Other curations" or "View only"; keep the inner
mapping over readOnlyCurations and CurationMembershipRow (with disabled and
onUpdateMembership unchanged) so behavior is identical but now clearly labeled.
🪄 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: ee31e579-9656-482c-8205-e40853011f2b
⛔ Files ignored due to path filters (2)
generated/models/ApiDropCuration.tsis excluded by!**/generated/**generated/models/ObjectSerializer.tsis excluded by!**/generated/**
📒 Files selected for processing (18)
components/brain/ContentTabContext.tsxcomponents/brain/my-stream/MyStreamWaveDesktopTabs.tsxcomponents/brain/my-stream/curations/MyStreamActiveCurationContext.tsxcomponents/brain/my-stream/curations/MyStreamWaveCurationContent.tsxcomponents/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsxcomponents/utils/button/PrimaryButton.tsxcomponents/utils/input/identity/IdentitySearch.tsxcomponents/utils/select-group/SelectGroupModal.tsxcomponents/utils/select-group/SelectGroupModalCard.tsxcomponents/utils/select-group/SelectGroupModalItems.tsxcomponents/utils/select-group/SelectGroupModalSearchName.tsxcomponents/waves/drops/WaveDropActionsMore.tsxcomponents/waves/drops/WaveDropCurationsActionIcon.tsxcomponents/waves/drops/WaveDropCurationsDialog.tsxcomponents/waves/drops/WaveDropMobileMenu.tsxhooks/drops/useDropCurationMembershipMutation.tshooks/drops/useDropCurations.tsopenapi.yaml
✅ Files skipped from review due to trivial changes (1)
- components/waves/drops/WaveDropCurationsActionIcon.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- components/utils/select-group/SelectGroupModalCard.tsx
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (3)
components/utils/select-group/SelectGroupModalSearchName.tsx (1)
14-14:⚠️ Potential issue | 🟡 MinorKeep RTL alignment fully logical and drop conflicting RTL focus offsets.
Line 36 still has
rtl:peer-focus:tw-left-auto rtl:peer-focus:tw-translate-x-1/4, while Lines 14 and 32 still use physicalleft/pl/prclasses. This leaves RTL positioning inconsistent after moving the label totw-start-*.💡 Suggested patch
- className="tw-pointer-events-none tw-absolute tw-left-3 tw-top-3.5 tw-h-4 tw-w-4 tw-text-iron-300" + className="tw-pointer-events-none tw-absolute tw-start-3 tw-top-3.5 tw-h-4 tw-w-4 tw-text-iron-300" - className={`tw-peer tw-form-input tw-block tw-w-full tw-appearance-none tw-rounded-lg tw-border-0 tw-border-iron-700 tw-bg-iron-900 tw-pb-3 tw-pl-9 tw-pr-4 tw-pt-3 tw-text-sm tw-font-medium tw-text-iron-100 tw-caret-primary-400 tw-shadow-sm tw-ring-1 tw-ring-inset tw-ring-iron-700 tw-transition tw-duration-300 tw-ease-out placeholder:tw-text-iron-500 hover:tw-ring-iron-650 focus:tw-bg-iron-900 focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-inset focus:tw-ring-primary-400 ${DARK_AUTOFILL_CLASSES}`} + className={`tw-peer tw-form-input tw-block tw-w-full tw-appearance-none tw-rounded-lg tw-border-0 tw-border-iron-700 tw-bg-iron-900 tw-pb-3 tw-pl-9 tw-pr-4 rtl:tw-pl-4 rtl:tw-pr-9 tw-pt-3 tw-text-sm tw-font-medium tw-text-iron-100 tw-caret-primary-400 tw-shadow-sm tw-ring-1 tw-ring-inset tw-ring-iron-700 tw-transition tw-duration-300 tw-ease-out placeholder:tw-text-iron-500 hover:tw-ring-iron-650 focus:tw-bg-iron-900 focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-inset focus:tw-ring-primary-400 ${DARK_AUTOFILL_CLASSES}`} - className="tw-absolute tw-start-7 tw-top-2 tw-origin-[0] -tw-translate-y-4 tw-scale-75 tw-transform tw-cursor-text tw-rounded-lg tw-bg-iron-900 tw-px-2 tw-text-sm tw-font-medium tw-text-iron-500 tw-duration-300 peer-placeholder-shown:tw-top-1/2 peer-placeholder-shown:-tw-translate-y-1/2 peer-placeholder-shown:tw-scale-100 peer-focus:tw-top-2 peer-focus:-tw-translate-y-4 peer-focus:tw-scale-75 peer-focus:tw-bg-iron-900 peer-focus:tw-px-2 peer-focus:tw-text-primary-400 rtl:peer-focus:tw-left-auto rtl:peer-focus:tw-translate-x-1/4" + className="tw-absolute tw-start-7 tw-top-2 tw-origin-[0] -tw-translate-y-4 tw-scale-75 tw-transform tw-cursor-text tw-rounded-lg tw-bg-iron-900 tw-px-2 tw-text-sm tw-font-medium tw-text-iron-500 tw-duration-300 peer-placeholder-shown:tw-top-1/2 peer-placeholder-shown:-tw-translate-y-1/2 peer-placeholder-shown:tw-scale-100 peer-focus:tw-top-2 peer-focus:-tw-translate-y-4 peer-focus:tw-scale-75 peer-focus:tw-bg-iron-900 peer-focus:tw-px-2 peer-focus:tw-text-primary-400"Also applies to: 32-32, 36-36
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/utils/select-group/SelectGroupModalSearchName.tsx` at line 14, The component SelectGroupModalSearchName uses physical LTR classes (tw-left-3, tw-pr-*, etc.) and conflicting RTL-specific focus offsets (rtl:peer-focus:tw-left-auto rtl:peer-focus:tw-translate-x-1/4); change all physical positioning to logical utilities (tw-start-3, tw-start-auto, tw-end-*, tw-pl/pr -> tw-start/tw-end padding) and remove the rtl:peer-focus translation/left overrides so peer-focus uses the same logical offsets (or use peer-focus:tw-start-... / peer-focus:tw-end-... if needed); update the className occurrences in SelectGroupModalSearchName that contain tw-left-3, tw-pr-*, and rtl:peer-focus:tw-left-auto rtl:peer-focus:tw-translate-x-1/4 to their logical equivalents to keep RTL alignment consistent.components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx (2)
180-217:⚠️ Potential issue | 🟡 MinorReplace this standalone label with grouped form semantics.
This label is not bound to a single control, so the section still has invalid form semantics for assistive tech.
fieldset/legendfits this preset-group-plus-input UI better.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx` around lines 180 - 217, Replace the standalone label with a fieldset/legend that groups the preset buttons and the input to provide correct form semantics: wrap the preset button list and the input (the JSX containing CURATION_NAME_PRESETS, selectedPresetLabel, handlePresetSelect, nameInputRef, and handleNameChange) in a <fieldset> and use a <legend> containing the same "Name" text and styling instead of the label element; ensure the legend carries the visible label styling and remove the original <label/>; keep existing IDs/classes (e.g., id="curation-name") so the input remains identifiable and assistive tech will now see the grouped controls.
12-12:⚠️ Potential issue | 🟠 MajorUse
commonApiPutfor edits.The edit branch still sends POST to
waves/${wave.id}/curations/${curation.id}. That makes create and update indistinguishable at the HTTP layer and can break servers or caches that key behavior off the method.Suggested fix
-import { commonApiPost } from "@/services/api/common-api"; +import { commonApiPost, commonApiPut } from "@/services/api/common-api"; … - return await commonApiPost<ApiWaveCurationRequest, ApiWaveCuration>({ - endpoint: isEditMode - ? `waves/${wave.id}/curations/${curation.id}` - : `waves/${wave.id}/curations`, - body, - }); + return isEditMode + ? await commonApiPut<ApiWaveCurationRequest, ApiWaveCuration>({ + endpoint: `waves/${wave.id}/curations/${curation.id}`, + body, + }) + : await commonApiPost<ApiWaveCurationRequest, ApiWaveCuration>({ + endpoint: `waves/${wave.id}/curations`, + body, + });Also applies to: 115-120
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx` at line 12, The edit path in MyStreamWaveCurationCreateDialog currently uses commonApiPost for updates; change it to use commonApiPut instead: update the import to pull commonApiPut (replace commonApiPost) and in the submit/update branch where you call the endpoint "waves/${wave.id}/curations/${curation.id}" (the edit flow inside the component) call commonApiPut with the same payload and URL so edits send HTTP PUT; ensure all other occurrences in this file that target that specific curation update route (including the other call around the create/edit branching) are switched from commonApiPost to commonApiPut.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/waves/drops/WaveDropMobileMenu.tsx`:
- Around line 105-114: The clipboard access can throw if navigator.clipboard is
undefined and the writeText promise is unhandled; update the clipboard branch in
WaveDropMobileMenu so it first checks navigator.clipboard exists (e.g., if
(!navigator.clipboard || typeof navigator.clipboard.writeText !== "function") {
closeMenu(); return; }), then call
navigator.clipboard.writeText(dropLink).then(() => { setCopied(true);
setTimeout(() => setCopied(false), 2000); }).catch(() => { /* optionally show
error or noop */ }).finally(() => closeMenu()); this ensures no TypeError,
handles promise rejection, and always closes the menu via closeMenu, referencing
closeMenu, setCopied, and dropLink in the fix.
In `@hooks/waves/useWaveCurationGroups.ts`:
- Around line 3-4: The shim currently re-exports getWaveCurationsQueryKey and
useWaveCurations as useWaveCurationGroups but no longer exposes the legacy
getWaveCurationGroupsQueryKey symbol; add a re-export for the legacy name so
existing imports don't break. In hooks/waves/useWaveCurationGroups.ts re-export
getWaveCurationGroupsQueryKey from "./useWaveCurations" (either directly or by
aliasing getWaveCurationsQueryKey -> getWaveCurationGroupsQueryKey) alongside
the existing exports (getWaveCurationsQueryKey and useWaveCurations as
useWaveCurationGroups).
---
Duplicate comments:
In `@components/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsx`:
- Around line 180-217: Replace the standalone label with a fieldset/legend that
groups the preset buttons and the input to provide correct form semantics: wrap
the preset button list and the input (the JSX containing CURATION_NAME_PRESETS,
selectedPresetLabel, handlePresetSelect, nameInputRef, and handleNameChange) in
a <fieldset> and use a <legend> containing the same "Name" text and styling
instead of the label element; ensure the legend carries the visible label
styling and remove the original <label/>; keep existing IDs/classes (e.g.,
id="curation-name") so the input remains identifiable and assistive tech will
now see the grouped controls.
- Line 12: The edit path in MyStreamWaveCurationCreateDialog currently uses
commonApiPost for updates; change it to use commonApiPut instead: update the
import to pull commonApiPut (replace commonApiPost) and in the submit/update
branch where you call the endpoint "waves/${wave.id}/curations/${curation.id}"
(the edit flow inside the component) call commonApiPut with the same payload and
URL so edits send HTTP PUT; ensure all other occurrences in this file that
target that specific curation update route (including the other call around the
create/edit branching) are switched from commonApiPost to commonApiPut.
In `@components/utils/select-group/SelectGroupModalSearchName.tsx`:
- Line 14: The component SelectGroupModalSearchName uses physical LTR classes
(tw-left-3, tw-pr-*, etc.) and conflicting RTL-specific focus offsets
(rtl:peer-focus:tw-left-auto rtl:peer-focus:tw-translate-x-1/4); change all
physical positioning to logical utilities (tw-start-3, tw-start-auto, tw-end-*,
tw-pl/pr -> tw-start/tw-end padding) and remove the rtl:peer-focus
translation/left overrides so peer-focus uses the same logical offsets (or use
peer-focus:tw-start-... / peer-focus:tw-end-... if needed); update the className
occurrences in SelectGroupModalSearchName that contain tw-left-3, tw-pr-*, and
rtl:peer-focus:tw-left-auto rtl:peer-focus:tw-translate-x-1/4 to their logical
equivalents to keep RTL alignment 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: e4fe6651-58e9-4ce6-b850-860a8f4eb363
📒 Files selected for processing (10)
components/brain/ContentTabContext.tsxcomponents/brain/my-stream/MyStreamWaveDesktopTabs.tsxcomponents/brain/my-stream/MyStreamWaveLeaderboard.tsxcomponents/brain/my-stream/tabs/MyStreamWaveCurationCreateDialog.tsxcomponents/utils/select-group/SelectGroupModalSearchName.tsxcomponents/waves/drops/WaveDropMobileMenu.tsxcomponents/waves/groups/curation/WaveActiveCurationSection.tsxcomponents/waves/groups/curation/WaveCurationGroupsSection.tsxhooks/waves/useWaveCurationGroups.tshooks/waves/useWaveCurations.ts
✅ Files skipped from review due to trivial changes (1)
- components/waves/groups/curation/WaveCurationGroupsSection.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- components/brain/ContentTabContext.tsx
- components/waves/groups/curation/WaveActiveCurationSection.tsx
- components/brain/my-stream/MyStreamWaveLeaderboard.tsx
|

Summary by CodeRabbit
New Features
Improvements
Chores