Skip to content

Read only waves#2215

Closed
simo6529 wants to merge 5 commits intomainfrom
read-only-waves
Closed

Read only waves#2215
simo6529 wants to merge 5 commits intomainfrom
read-only-waves

Conversation

@simo6529
Copy link
Copy Markdown
Collaborator

@simo6529 simo6529 commented Apr 6, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added public read-only wave viewing for signed-out users on web, allowing access to wave content without authentication while restricting mutations (voting, reactions, replies, submissions).
    • Introduced wave selections feature enabling organized drop curation.
  • Documentation

    • Updated wave access documentation to reflect signed-out read-only viewing experience.
  • Tests

    • Added comprehensive test coverage for read-only mode and public wave shell states.

simo6529 added 2 commits April 6, 2026 14:07
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 6, 2026

📝 Walkthrough

Walkthrough

Introduces a public read-only viewer mode for waves, allowing signed-out users to access wave content without interactive capabilities. Adds WaveViewerModeContext to propagate isPublicReadOnly state, gates all mutation-related UI (voting, reactions, posting, curation, chat panels, right sidebar) behind this flag, removes the logged-out skeleton preview approach, refactors public wave shell rendering, and extends OpenAPI schemas with wave selections management endpoints.

Changes

Cohort / File(s) Summary
WaveViewerMode Context & Setup
components/waves/public/WaveViewerModeContext.tsx
New React context and provider for propagating public read-only mode; exports WaveViewerModeProvider and useWaveViewerMode hook.
Public Wave Shell Refactoring
components/waves/public/LoggedOutSkeleton.tsx, components/waves/public/PublicWaveShell.tsx, components/waves/public/usePublicWaveShellState.ts
Removes skeleton preview UI; simplifies PublicWaveShellState to remove wave metadata payload; delegates ready-state rendering to MyStreamWave component.
Wrapper & Layout Updates
components/shared/WavesMessagesWrapper.tsx, components/waves/WavesDesktop.tsx, components/waves/layout/WavesLayout.tsx
Replaces allowRightSidebar prop with isPublicReadOnly; wraps content with WaveViewerModeProvider; gates right-sidebar visibility by public shell readiness; updates active-wave-based read-only logic.
Chat & Drop Panel UI
components/waves/drop/SingleWaveDropWrapper.tsx, __tests__/components/waves/drop/SingleWaveDropWrapper.test.tsx
Refactors chat rendering from direct inclusion to React portals; gates controller by isPublicReadOnly; new test suite validates chat mount/unmount and public read-only behavior.
Content Tab & Stream View
components/brain/ContentTabContext.tsx, components/brain/my-stream/MyStreamWave.tsx, components/brain/my-stream/MyStreamWaveDesktopTabs.tsx
Adds optional publicReadOnly flag to tab parameters; filters MY_VOTES tab in public read-only mode; remaps active tab from MY_VOTES to CHAT when read-only.
Stream Chat & Leaderboard
components/brain/my-stream/MyStreamWaveChat.tsx, components/brain/my-stream/MyStreamWaveLeaderboard.tsx, components/brain/my-stream/tabs/MyStreamWaveTabsMeme.tsx
Gates unread-divider, leave-handler, and UI creation controls by isPublicReadOnly; introduces WaveCreateController for centralized create-surface state; hides submission modals in read-only mode.
Drop Action Gating (Memes)
components/memes/drops/MemeParticipationDrop.tsx, components/memes/drops/MemeWinnerDrop.tsx, components/memes/drops/MemesLeaderboardDrop.tsx
Gates vote UI, delete controls, and action modals by combining isPublicReadOnly with existing capability checks.
Drop Action Gating (Wave Drops)
components/waves/drops/WaveDrop.tsx, components/waves/drops/DropMobileMenuHandler.tsx, components/waves/drops/WaveDropReactions.tsx, components/waves/drops/participation/*
Disables long-press, touch handlers, action menus, and vote/reaction/curation UI in read-only mode; gates modal rendering and interaction handlers.
Leaderboard & Single Drop Info
components/waves/drop/MemesSingleWaveDropInfoPanel.tsx, components/waves/drop/SingleWaveDropInfoPanel.tsx, components/waves/leaderboard/drops/DefaultWaveLeaderboardDrop.tsx, components/waves/leaderboard/gallery/WaveLeaderboardGalleryItem.tsx, components/waves/leaderboard/grid/WaveLeaderboardGridItem.tsx
Derives view-scoped vote/delete capabilities; gates voting/curation UI and action handlers; prevents modal opening in read-only mode.
Drop List & Notification Handling
components/waves/drops/wave-drops-all/*, components/waves/drops/wave-drops-all/hooks/useWaveDropsNotificationRead.ts
Adds optional readOnly prop threaded through WaveDropsAllWaveDropsContentWaveDropsMessageListSection; disables notification-read API calls when readOnly is true; omits unread navigation in read-only mode.
Drop Model & Utilities
components/waves/utils/getOptimisticDrop.ts, components/waves/memes/submission/utils/buildPreviewDrop.ts, hooks/useWaveDropsSearch.ts
Adds selections field to drop and wave models; populates from wave.selections in preview and optimistic drop builders and search mapper.
Visibility & Export Changes
components/user/layout/userPageVisibility.ts
Makes normalizeCountry non-exported (local scope).
OpenAPI Schema & Endpoints
openapi.yaml
Adds wave selections management endpoints (GET/POST/DELETE selections, add/remove drops from selections); adds selection_id query filters to drop-retrieval endpoints; extends schemas with selections: ApiWaveSelection[] fields on ApiDrop, ApiWave, ApiWaveMin, ApiWaveChatConfig.
Test Suite Additions
__tests__/components/brain/my-stream/MyStreamWaveLeaderboard.test.tsx, __tests__/components/brain/my-stream/tabs/MyStreamWaveTabsDefault.test.tsx, __tests__/components/brain/my-stream/tabs/MyStreamWaveTabsMeme.test.tsx, __tests__/components/shared/WavesMessagesWrapper.test.tsx
Wraps tests with WaveViewerModeProvider; adds assertions for UI visibility/gating in public read-only mode; validates right-sidebar and creation UI behavior.
Documentation
docs/waves/README.md, docs/waves/feature-public-wave-preview.md, docs/waves/troubleshooting-wave-navigation-and-posting.md
Reframes signed-out direct wave links from locked preview to publicly resolvable read-only threads; documents safe surfaces (chat, leaderboard, read-only drops) and hidden mutation capabilities; updates troubleshooting for read-only experience.

Sequence Diagram

sequenceDiagram
    participant User as Signed-Out User
    participant Browser as Browser / Router
    participant WavesLayout as WavesLayout Component
    participant WVMProvider as WaveViewerModeProvider
    participant Drop as Drop Component
    participant UI as UI Elements

    User->>Browser: Navigate to /waves/{waveId}
    Browser->>WavesLayout: Render with activeWaveId
    WavesLayout->>WavesLayout: Check if activeWaveId !== null
    WavesLayout->>WVMProvider: Render with isPublicReadOnly=true
    WVMProvider->>Drop: Provide isPublicReadOnly context
    Drop->>Drop: useWaveViewerMode() reads isPublicReadOnly
    Drop->>UI: Gate vote button: show only if !isPublicReadOnly
    Drop->>UI: Gate delete action: show only if !isPublicReadOnly
    Drop->>UI: Gate reaction handlers: disable if isPublicReadOnly
    Drop->>UI: Render read-only view (view vote summary, no voting UI)
    User->>UI: Attempt to vote
    UI->>UI: isPublicReadOnly=true, handler blocked
    User->>Browser: See "Connect wallet" prompt
    Browser->>WavesLayout: User connects wallet
    WavesLayout->>WVMProvider: Re-render with isPublicReadOnly=false
    Drop->>UI: Un-gate vote button, reactions, delete
    User->>UI: Now can vote/react/delete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • #1857: Modifies MyStreamWaveChat and WaveChatLeaveHandler logic; overlaps with read-only gating of unread-divider and leave-handler behavior.
  • #1851: Updates WaveDrop.tsx touch/long-press gating; this PR further gates long-press by isPublicReadOnly.
  • #2153: Modifies public wave shell surface (PublicWaveShell, usePublicWaveShellState, WavesMessagesWrapper); this PR refactors the logged-out skeleton and public-read-only readiness behavior introduced there.

Suggested reviewers

  • prxt6529

🐰 A public wave for all to see,
No votes or reactions in read-only spree,
Chat and leaderboard, a view so free,
Context gates the UI with gentle decree,
Connect your wallet to unlock the key! 🔐

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Read only waves' accurately summarizes the main change—introducing a public read-only mode for waves that prevents mutations and gates interactive UI across multiple components throughout the codebase.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch read-only-waves

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

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

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

⚠️ Outside diff range comments (1)
components/waves/leaderboard/grid/WaveLeaderboardGridItem.tsx (1)

57-82: ⚠️ Potential issue | 🟠 Major

Reset transient menu/modal state when the view flips read-only.

Disabling the touch handlers and hiding the portal here leaves the existing local state intact. If the card was long-pressed first, isActive stays true, and Line 195 keeps swallowing taps after the mode switch even though the mobile menu is gone. isVotingModalOpen has the same stale-state problem when the view becomes interactive again.

Suggested fix
 import React, {
   useCallback,
+  useEffect,
   useMemo,
   useState,
   useSyncExternalStore,
 } from "react";
 ...
   const { canShowVote } = useDropInteractionRules(drop);
   const isCuratableInView = !isPublicReadOnly && isCuratable;
   const canShowVoteInView = !isPublicReadOnly && canShowVote;
+
+  useEffect(() => {
+    if (!isPublicReadOnly) {
+      return;
+    }
+    setIsActive(false);
+    setIsVotingModalOpen(false);
+  }, [isPublicReadOnly, setIsActive]);

Also applies to: 220-222, 393-410

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

In `@components/waves/leaderboard/grid/WaveLeaderboardGridItem.tsx` around lines
57 - 82, The card leaves transient UI state (isActive from
useLongPressInteraction and isVotingModalOpen) stale when isPublicReadOnly
flips, causing taps to be swallowed or modals/menus to persist; add an effect
that watches isPublicReadOnly and when it becomes true call setIsActive(false)
and setIsVotingModalOpen(false) (and any other transient setters for
menus/portals), and also ensure touchHandlers are no longer considered active
after the flip (by clearing isActive rather than relying on touchHandlers).
Update the component to reset these transient states (isActive via setIsActive,
isVotingModalOpen via setIsVotingModalOpen, and any local menu-open flags)
inside a useEffect triggered by changes to isPublicReadOnly.
🧹 Nitpick comments (2)
components/waves/public/WaveViewerModeContext.tsx (1)

5-25: Make a missing provider obvious instead of silently defaulting to writable mode.

All current consumers of useWaveViewerMode() are properly wrapped by WaveViewerModeProvider through the WavesMessagesWrapper hierarchy. However, the context's default value of false makes unwrapped components indistinguishable from intentional interactive mode. Since this flag suppresses create/vote/delete entry points, missing the provider would silently fail open rather than surface quickly.

Using an undefined sentinel and a hook assertion prevents future integration mistakes:

Suggested change
-const WaveViewerModeContext = createContext(false);
+const WaveViewerModeContext = createContext<boolean | undefined>(undefined);
@@
 export function useWaveViewerMode() {
-  return {
-    isPublicReadOnly: useContext(WaveViewerModeContext),
-  };
+  const isPublicReadOnly = useContext(WaveViewerModeContext);
+  if (isPublicReadOnly === undefined) {
+    throw new Error(
+      "useWaveViewerMode must be used within WaveViewerModeProvider"
+    );
+  }
+  return { isPublicReadOnly };
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/waves/public/WaveViewerModeContext.tsx` around lines 5 - 25, The
context currently defaults to false which masks missing providers; change
WaveViewerModeContext to use an undefined sentinel (i.e., createContext<boolean
| undefined>(undefined)), update typing where needed
(WaveViewerModeProviderProps can remain the same), and modify useWaveViewerMode
to read the context and assert/throw a clear error if the value is undefined
(referencing WaveViewerModeContext and useWaveViewerMode) so unwrapped
components fail fast instead of silently behaving as writable.
openapi.yaml (1)

11773-11833: Keep ApiWaveMin lightweight.

ApiDrop already exposes drop-level selections, so adding the full wave selection catalog to ApiWaveMin means every nested wave in list responses repeats the same array. I’d keep the catalog on top-level wave responses and leave ApiWaveMin as the lean embed.

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

In `@openapi.yaml` around lines 11773 - 11833, ApiWaveMin is too heavy because it
includes the full selections catalog; remove the selections property from the
ApiWaveMin schema so nested/embedded wave objects stay lightweight. Keep
selections only on the top-level wave schema (e.g., ApiWave / the full wave
response schema that ApiDrop already uses) and ensure any refs that should embed
the lightweight version continue to use ApiWaveMin (update any $ref consumers if
needed). Do not change other properties on ApiWaveMin.
🤖 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/MyStreamWaveLeaderboard.tsx`:
- Around line 92-96: When isPublicReadOnly flips to true we need to actively
close any open creation flows instead of merely hiding them; add a useEffect
that watches isPublicReadOnly and, when it becomes true, calls the state setters
that control the create UI (e.g., setIsCreateDropOpen(false) and any
composer/modal close setters used alongside isCreateDropOpen) so local flags are
reset; ensure this handles all related flows referenced by
showToggleableDropInput (isCreateDropOpen, submissionExperience branches) and
the other create-flow flags used around lines 328-345.

In `@components/shared/WavesMessagesWrapper.tsx`:
- Around line 148-212: The right sidebar is still rendered even in public
read-only mode; update the two places that render BrainRightSidebar (the inline
block conditioned on rightVariant and the overlay block conditioned on
rightVariant === "overlay") to also check the public-read-only state provided by
WaveViewerModeProvider (use whatever local prop/state indicates read-only, e.g.,
isPublicReadOnly) and skip rendering when public read-only is true so
BrainRightSidebar (and its props waveId, activeTab/sidebarTab,
setActiveTab/setSidebarTab) are not mounted in that mode.

In `@components/waves/drop/SingleWaveDropWrapper.tsx`:
- Around line 147-217: The desktop and mobile chat trees currently both mount
SingleWaveDropChat (causing duplicate effects); update the rendering so the
desktop pane (the lg:tw-flex container with SingleWaveDropChat key={drop.id}) is
wrapped or conditionally rendered only when !isSmallScreen, and the mobile
overlay Transition tree that renders SingleWaveDropChat is only rendered when
isSmallScreen && isChatOpen; use the existing isSmallScreen/isChatOpen state
(and keep toggleChat behavior) to ensure only one SingleWaveDropChat is mounted
per breakpoint while still passing wave and drop props and rendering into
trailingContentContainer.

In `@components/waves/drops/WaveDrop.tsx`:
- Around line 189-190: The action button trigger can remain visible in read-only
mode because only long-press and WaveDropMobileMenu are suppressed; update the
render logic that computes/showProps for the header's action trigger (the
variable/prop associated with showActionsButton and the component that renders
the header action) to also consider isPublicReadOnly and avoid rendering the
trigger when isPublicReadOnly is true; alternatively, set allowLongPress or
showActionsButton to false when isPublicReadOnly so the header button is not
rendered along with suppressing WaveDropMobileMenu and the long-press behavior.

In `@hooks/useWaveDropsSearch.ts`:
- Line 41: The mapping in useWaveDropsSearch forwards wave.selections directly
which can be undefined; update the mapper (the object with selections:
wave.selections) to guard and supply a safe default (e.g., use wave.selections
?? [] or Array.isArray check) so ApiWaveMin.selections is never undefined before
downstream wave/drop mapping; locate the mapping inside the useWaveDropsSearch
hook and replace the raw property access with the guarded/defaulted expression.

---

Outside diff comments:
In `@components/waves/leaderboard/grid/WaveLeaderboardGridItem.tsx`:
- Around line 57-82: The card leaves transient UI state (isActive from
useLongPressInteraction and isVotingModalOpen) stale when isPublicReadOnly
flips, causing taps to be swallowed or modals/menus to persist; add an effect
that watches isPublicReadOnly and when it becomes true call setIsActive(false)
and setIsVotingModalOpen(false) (and any other transient setters for
menus/portals), and also ensure touchHandlers are no longer considered active
after the flip (by clearing isActive rather than relying on touchHandlers).
Update the component to reset these transient states (isActive via setIsActive,
isVotingModalOpen via setIsVotingModalOpen, and any local menu-open flags)
inside a useEffect triggered by changes to isPublicReadOnly.

---

Nitpick comments:
In `@components/waves/public/WaveViewerModeContext.tsx`:
- Around line 5-25: The context currently defaults to false which masks missing
providers; change WaveViewerModeContext to use an undefined sentinel (i.e.,
createContext<boolean | undefined>(undefined)), update typing where needed
(WaveViewerModeProviderProps can remain the same), and modify useWaveViewerMode
to read the context and assert/throw a clear error if the value is undefined
(referencing WaveViewerModeContext and useWaveViewerMode) so unwrapped
components fail fast instead of silently behaving as writable.

In `@openapi.yaml`:
- Around line 11773-11833: ApiWaveMin is too heavy because it includes the full
selections catalog; remove the selections property from the ApiWaveMin schema so
nested/embedded wave objects stay lightweight. Keep selections only on the
top-level wave schema (e.g., ApiWave / the full wave response schema that
ApiDrop already uses) and ensure any refs that should embed the lightweight
version continue to use ApiWaveMin (update any $ref consumers if needed). Do not
change other properties on ApiWaveMin.
🪄 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: ff295a2b-7d2f-423d-81aa-51283fcc3fef

📥 Commits

Reviewing files that changed from the base of the PR and between 97d225e and 901c1e6.

⛔ Files ignored due to path filters (9)
  • generated/models/ApiDrop.ts is excluded by !**/generated/**
  • generated/models/ApiDropWithoutWave.ts is excluded by !**/generated/**
  • generated/models/ApiWave.ts is excluded by !**/generated/**
  • generated/models/ApiWaveMin.ts is excluded by !**/generated/**
  • generated/models/ApiWaveSelection.ts is excluded by !**/generated/**
  • generated/models/ApiWaveSelectionDropRequest.ts is excluded by !**/generated/**
  • generated/models/ApiWaveSelectionRequest.ts is excluded by !**/generated/**
  • generated/models/ObjectSerializer.ts is excluded by !**/generated/**
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (43)
  • __tests__/components/waves/drop/SingleWaveDropWrapper.test.tsx
  • components/brain/ContentTabContext.tsx
  • components/brain/my-stream/MyStreamWave.tsx
  • components/brain/my-stream/MyStreamWaveChat.tsx
  • components/brain/my-stream/MyStreamWaveDesktopTabs.tsx
  • components/brain/my-stream/MyStreamWaveLeaderboard.tsx
  • components/brain/my-stream/tabs/MyStreamWaveTabsDefault.tsx
  • components/brain/my-stream/tabs/MyStreamWaveTabsMeme.tsx
  • components/memes/drops/MemeParticipationDrop.tsx
  • components/memes/drops/MemeWinnerDrop.tsx
  • components/memes/drops/MemesLeaderboardDrop.tsx
  • components/shared/WavesMessagesWrapper.tsx
  • components/user/layout/userPageVisibility.ts
  • components/waves/WavesDesktop.tsx
  • components/waves/drop/MemesSingleWaveDropInfoPanel.tsx
  • components/waves/drop/SingleWaveDropInfoPanel.tsx
  • components/waves/drop/SingleWaveDropWrapper.tsx
  • components/waves/drops/DropMobileMenuHandler.tsx
  • components/waves/drops/WaveDrop.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/participation/EndedParticipationDrop.tsx
  • components/waves/drops/participation/OngoingParticipationDrop.tsx
  • components/waves/drops/participation/ParticipationDropFooter.tsx
  • components/waves/drops/wave-drops-all/hooks/useWaveDropsNotificationRead.ts
  • components/waves/drops/wave-drops-all/index.tsx
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsContent.tsx
  • components/waves/drops/wave-drops-all/subcomponents/WaveDropsMessageListSection.tsx
  • components/waves/drops/winner/DefaultWinnerDrop.tsx
  • components/waves/layout/WavesLayout.tsx
  • components/waves/leaderboard/drops/DefaultWaveLeaderboardDrop.tsx
  • components/waves/leaderboard/gallery/WaveLeaderboardGalleryItem.tsx
  • components/waves/leaderboard/grid/WaveLeaderboardGridItem.tsx
  • components/waves/memes/submission/utils/buildPreviewDrop.ts
  • components/waves/public/LoggedOutSkeleton.tsx
  • components/waves/public/PublicWaveShell.tsx
  • components/waves/public/WaveViewerModeContext.tsx
  • components/waves/public/usePublicWaveShellState.ts
  • components/waves/utils/getOptimisticDrop.ts
  • docs/waves/README.md
  • docs/waves/feature-public-wave-preview.md
  • docs/waves/troubleshooting-wave-navigation-and-posting.md
  • hooks/useWaveDropsSearch.ts
  • openapi.yaml
💤 Files with no reviewable changes (1)
  • components/waves/public/LoggedOutSkeleton.tsx

Comment thread components/brain/my-stream/MyStreamWaveLeaderboard.tsx Outdated
Comment thread components/shared/WavesMessagesWrapper.tsx
Comment thread components/waves/drop/SingleWaveDropWrapper.tsx
Comment on lines +189 to 190
const allowLongPress = hasTouch && !isMdUp && !isPublicReadOnly;
const compact = useCompactMode();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hide the touch actions trigger in read-only mode to avoid a dead button.

Line 189 disables long-press and Line 504 suppresses WaveDropMobileMenu, but the header trigger (showActionsButton on Line 430) can still render. In read-only mode that leaves a visible action button that opens nothing.

🔧 Proposed fix
-              showActionsButton={hasTouch && showReplyAndQuote && !isEditing}
+              showActionsButton={
+                !isPublicReadOnly &&
+                hasTouch &&
+                showReplyAndQuote &&
+                !isEditing
+              }

Also applies to: 504-516

🤖 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 189 - 190, The action
button trigger can remain visible in read-only mode because only long-press and
WaveDropMobileMenu are suppressed; update the render logic that
computes/showProps for the header's action trigger (the variable/prop associated
with showActionsButton and the component that renders the header action) to also
consider isPublicReadOnly and avoid rendering the trigger when isPublicReadOnly
is true; alternatively, set allowLongPress or showActionsButton to false when
isPublicReadOnly so the header button is not rendered along with suppressing
WaveDropMobileMenu and the long-press behavior.

Comment thread hooks/useWaveDropsSearch.ts
simo6529 added 3 commits April 6, 2026 16:56
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Apr 7, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
__tests__/components/brain/my-stream/MyStreamWaveLeaderboard.test.tsx (1)

519-548: Consider extending leaderboardTree to support key prop for consistency.

This test manually constructs the provider tree because it needs the key prop to force component remount on wave change. This works correctly but creates a minor inconsistency: WaveViewerModeProvider here omits isPublicReadOnly while leaderboardTree always passes it explicitly.

Consider extending the helper to accept an optional key:

♻️ Optional refactor to use consistent helper
 const leaderboardTree = ({
   waveOverride = wave,
   isPublicReadOnly = false,
+  componentKey,
 }: {
   readonly waveOverride?: ApiWave;
   readonly isPublicReadOnly?: boolean;
+  readonly componentKey?: string;
 } = {}) => (
   <WaveViewerModeProvider isPublicReadOnly={isPublicReadOnly}>
     <AuthContext.Provider value={authContextValue}>
-      <MyStreamWaveLeaderboard wave={waveOverride} onDropClick={jest.fn()} />
+      <MyStreamWaveLeaderboard
+        key={componentKey}
+        wave={waveOverride}
+        onDropClick={jest.fn()}
+      />
     </AuthContext.Provider>
   </WaveViewerModeProvider>
 );

Then the test could use:

const { rerender } = render(leaderboardTree({ waveOverride: waveA, componentKey: waveA.id }));
// ...
rerender(leaderboardTree({ waveOverride: waveB, componentKey: waveB.id }));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__tests__/components/brain/my-stream/MyStreamWaveLeaderboard.test.tsx` around
lines 519 - 548, The test manually mounts WaveViewerModeProvider with a key to
force remount, causing an inconsistency with the existing leaderboardTree helper
which always passes isPublicReadOnly; update leaderboardTree to accept an
optional componentKey (and keep its existing isPublicReadOnly behavior/default)
and use that prop to set the wrapper/component key when provided (e.g., render
the MyStreamWaveLeaderboard with key={componentKey} and accept waveOverride to
swap waves), then update this test to call render(leaderboardTree({
waveOverride: waveA, componentKey: waveA.id })) and rerender(leaderboardTree({
waveOverride: waveB, componentKey: waveB.id })) so the helper is reused and
consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@__tests__/components/brain/my-stream/MyStreamWaveLeaderboard.test.tsx`:
- Around line 519-548: The test manually mounts WaveViewerModeProvider with a
key to force remount, causing an inconsistency with the existing leaderboardTree
helper which always passes isPublicReadOnly; update leaderboardTree to accept an
optional componentKey (and keep its existing isPublicReadOnly behavior/default)
and use that prop to set the wrapper/component key when provided (e.g., render
the MyStreamWaveLeaderboard with key={componentKey} and accept waveOverride to
swap waves), then update this test to call render(leaderboardTree({
waveOverride: waveA, componentKey: waveA.id })) and rerender(leaderboardTree({
waveOverride: waveB, componentKey: waveB.id })) so the helper is reused and
consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 349ecd8d-af59-4abe-8614-aba2a4611915

📥 Commits

Reviewing files that changed from the base of the PR and between 901c1e6 and c322b23.

📒 Files selected for processing (11)
  • __tests__/components/brain/my-stream/MyStreamWaveLeaderboard.test.tsx
  • __tests__/components/brain/my-stream/tabs/MyStreamWaveTabsDefault.test.tsx
  • __tests__/components/brain/my-stream/tabs/MyStreamWaveTabsMeme.test.tsx
  • __tests__/components/shared/WavesMessagesWrapper.test.tsx
  • __tests__/components/waves/drop/SingleWaveDropWrapper.test.tsx
  • components/brain/my-stream/MyStreamWaveLeaderboard.tsx
  • components/brain/my-stream/tabs/MyStreamWaveTabsMeme.tsx
  • components/shared/WavesMessagesWrapper.tsx
  • components/waves/WavesDesktop.tsx
  • components/waves/drop/SingleWaveDropWrapper.tsx
  • components/waves/layout/WavesLayout.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • components/brain/my-stream/tabs/MyStreamWaveTabsMeme.tsx
  • components/waves/WavesDesktop.tsx
  • tests/components/waves/drop/SingleWaveDropWrapper.test.tsx
  • components/brain/my-stream/MyStreamWaveLeaderboard.tsx

@simo6529 simo6529 closed this Apr 7, 2026
@simo6529 simo6529 deleted the read-only-waves branch April 7, 2026 08:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant