Skip to content

Track my delegated mods better#3397

Merged
tzarebczan merged 4 commits intomasterfrom
track-delegates
Jan 15, 2026
Merged

Track my delegated mods better#3397
tzarebczan merged 4 commits intomasterfrom
track-delegates

Conversation

@keikari
Copy link
Contributor

@keikari keikari commented Jan 15, 2026

-Fetch delegated mods for all my channels on page load
-Updated delegated mods when adding/removing mods
-Handle correct Add/Remove delegate mod in comment menu

Summary by CodeRabbit

  • New Features
    • Remove users as moderators from the comment menu; a dedicated "Remove as moderator" option is shown when applicable.
    • Moderator delegate lists are aggregated across all owned channels and synchronized to the UI.
    • Success messaging shown when a moderator is removed.
    • Added four new user-facing translation strings for remove-moderator text.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 15, 2026

📝 Walkthrough

Walkthrough

Adds moderation delegate management: fetch delegates for user's channels, add/remove delegate actions with completion actions, reducer handling, App integration to fetch delegates, and CommentMenuList UI to show conditional Add/Remove moderator options.

Changes

Cohort / File(s) Summary
Translation Strings
static/app-strings.json
Added 4 new translation entries for removing moderators and removal confirmation with placeholders.
Redux Action Types
ui/constants/action_types.js
Added ADD_MODERATOR_COMPLETED, REMOVE_MODERATOR_COMPLETED, and three COMMENT_MODERATION_DELEGATES_FOR_MY_CHANNELS_* action types.
Redux Actions
ui/redux/actions/comments.js
Updated doCommentModAddDelegate and doCommentModRemoveDelegate to dispatch completion actions and optional toasts; added doCommentModListDelegatesForMyChannels to fetch delegates across user-owned channels, handle per-channel signing, and aggregate results.
Redux Reducer
ui/redux/reducers/comments.js
Added reducer cases for delegates fetch completion, add-completed (merge new delegates), and remove-completed (filter out removed delegate), updating moderationDelegatesById immutably.
App Component Integration
ui/component/app/index.js, ui/component/app/view.jsx
Exposed and invoked fetchDelegatesForMyChannels (bound to doCommentModListDelegatesForMyChannels) when user-owned channels are present.
Comment Menu UI
ui/component/commentMenuList/index.js, ui/component/commentMenuList/view.jsx
Mapped moderationDelegatesById state and commentModRemoveDelegate dispatcher to props; added logic to compute authorIsModerator, conditionally render "Add as moderator" or "Remove as moderator", and call appropriate add/remove delegate actions.

Sequence Diagram(s)

sequenceDiagram
    participant App
    participant Redux as Redux (Actions/Reducers)
    participant API as Moderation API
    participant CommentMenu as CommentMenuList

    App->>Redux: fetchDelegatesForMyChannels()
    activate Redux
    Redux->>Redux: Dispatch STARTED
    Note over Redux: Sign each user-owned channel
    Redux->>API: moderation_list_delegates (per channel with signature)
    API-->>Redux: Return delegates per channel
    Redux->>Redux: Dispatch COMPLETED with moderationDelegatesById
    deactivate Redux

    Redux-->>CommentMenu: moderationDelegatesById (via props)
    CommentMenu->>CommentMenu: Compute authorIsModerator from delegates
    alt authorIsModerator true
        CommentMenu->>Redux: commentModRemoveDelegate(modChannelId,...)
        activate Redux
        Redux->>API: moderation_remove_delegate
        API-->>Redux: Success
        Redux->>Redux: Dispatch REMOVE_MODERATOR_COMPLETED
        deactivate Redux
        Redux-->>CommentMenu: Updated moderationDelegatesById
    else authorIsModerator false
        CommentMenu->>Redux: commentModAddDelegate(...)
    end
    CommentMenu->>CommentMenu: Re-render menu
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped through code with a twitchy nose,
Delegates listed in tidy rows,
Add or remove with a gentle thump,
Redux carrots make the state jump,
Hooray — moderators come and go!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 'Track my delegated mods better' is directly related to the PR's main objective of fetching, updating, and managing delegated moderators across the user's channels.

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

✨ Finishing touches
  • 📝 Generate docstrings


📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c294eb3 and b1504fb.

📒 Files selected for processing (2)
  • ui/component/commentMenuList/view.jsx
  • ui/redux/reducers/comments.js
🧰 Additional context used
🧬 Code graph analysis (1)
ui/redux/reducers/comments.js (2)
ui/page/creatorMemberships/supporterArea/pledgesTab/internal/membershipRow/view.jsx (1)
  • creatorChannelId (26-26)
ui/component/commentMenuList/view.jsx (1)
  • delegates (102-102)
🪛 Biome (2.1.2)
ui/component/commentMenuList/view.jsx

[error] 38-39: Expected a statement but instead found ',
moderationDelegatesById:'.

Expected a statement here.

(parse)


[error] 39-39: Expected a property, an expression, or a method but instead found '?string'.

Expected a property, an expression, or a method here.

(parse)


[error] 39-39: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement

(parse)


[error] 39-43: Expected a statement but instead found ',
authorCanonicalUri: ?string,
authorId: string,
// --- perform ---
doToast: ('.

Expected a statement here.

(parse)

ui/redux/reducers/comments.js

[error] 1051-1051: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1051-1051: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1057-1057: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1127-1127: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1127-1127: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1147-1147: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1147-1147: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)

🔇 Additional comments (5)
ui/redux/reducers/comments.js (2)

1051-1072: LGTM!

The handler correctly iterates over entries, validates arrays before mapping, and provides sensible defaults for non-array values. The delegate normalization to { channelId, channelName } is consistent with the existing COMMENT_FETCH_MODERATION_DELEGATES_COMPLETED handler pattern at lines 1031-1049.

Note: The static analysis hints about TypeScript syntax are false positives — this file uses Flow type annotations (see // @flow`` at line 1), which are valid.


1127-1146: LGTM!

The handler properly normalizes incoming delegates and safely handles the case where moderationDelegatesById[creatorChannelId] might not exist yet.

ui/component/commentMenuList/view.jsx (3)

102-115: LGTM!

The delegates lookup uses optional chaining appropriately, and authorIsModerator correctly guards the array check before calling .some(). The claimIsMine condition ensures this check only applies to the user's own claims — consistent with the PR objective of tracking delegated mods for user-owned channels.


139-144: LGTM!

The removeModerator function follows the same pattern as assignAsModerator, with proper null checks before parsing the URI and invoking the action.


276-291: LGTM!

The additional !authorIsModerator condition correctly prevents showing the "Add as moderator" option when the author is already a delegate, avoiding duplicate moderator assignments.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@ui/component/commentMenuList/view.jsx`:
- Around line 292-304: The "Remove as moderator" menu item uses the wrong icon
constant; in the JSX block rendering the MenuItem for removal (the conditional
that checks activeChannelIsCreator && authorIsModerator and calls
removeModerator) replace the Icon prop value ICONS.ADD with ICONS.REMOVE so the
Icon component shows the correct remove (X) icon for that action; ensure you
only change the Icon import usage inside that MenuItem render and keep the rest
(MenuItem, removeModerator handler, and help text) unchanged.

In `@ui/redux/reducers/comments.js`:
- Around line 1147-1159: The reducer handling ACTIONS.REMOVE_MODERATOR_COMPLETED
can throw if state.moderationDelegatesById[creatorChannelId] is undefined;
update the reducer (identified by ACTIONS.REMOVE_MODERATOR_COMPLETED and
state.moderationDelegatesById) to guard the access by using a safe fallback or
early return — e.g., treat missing entries as an empty array or skip mutation
and return the original state if the creatorChannelId key is absent, then apply
.filter only on a defined array before assigning
moderationDelegatesById[creatorChannelId].
🧹 Nitpick comments (2)
ui/component/app/view.jsx (1)

354-365: Consider triggering delegates fetch on myChannelClaimIds changes, not just hasMyChannels.
If myChannelClaimIds updates while staying non-empty, the effect won’t rerun, so delegates for newly-added channels may be missing until a refresh.

Proposed adjustment
-  useEffect(() => {
+  const myChannelClaimIdsKey = myChannelClaimIds ? myChannelClaimIds.join(',') : '';
+
+  useEffect(() => {
     if (hasNoChannels) {
       setIncognito(true);
     }
 
     if (hasMyChannels) {
       fetchModBlockedList();
       fetchModAmIList();
       fetchDelegatesForMyChannels();
     }
     // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [hasMyChannels, hasNoChannels, setIncognito]);
+  }, [myChannelClaimIdsKey, hasNoChannels, setIncognito]);
ui/constants/action_types.js (1)

556-578: Consider namespacing ADD_MODERATOR_COMPLETED / REMOVE_MODERATOR_COMPLETED to avoid future collisions.
E.g. COMMENT_MODERATION_ADD_MODERATOR_COMPLETED to match the rest of the moderation action naming.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca2217a and c294eb3.

📒 Files selected for processing (8)
  • static/app-strings.json
  • ui/component/app/index.js
  • ui/component/app/view.jsx
  • ui/component/commentMenuList/index.js
  • ui/component/commentMenuList/view.jsx
  • ui/constants/action_types.js
  • ui/redux/actions/comments.js
  • ui/redux/reducers/comments.js
🧰 Additional context used
🧬 Code graph analysis (5)
ui/redux/reducers/comments.js (2)
ui/page/creatorMemberships/supporterArea/pledgesTab/internal/membershipRow/view.jsx (1)
  • creatorChannelId (26-26)
ui/component/commentMenuList/view.jsx (1)
  • delegates (102-102)
ui/component/commentMenuList/view.jsx (4)
ui/page/settingsCreator/view.jsx (1)
  • delegates (201-201)
ui/page/claim/internal/claimPageComponent/internal/channelPage/tabs/creatorSettingsTab/view.jsx (1)
  • delegates (243-243)
ui/redux/reducers/comments.js (2)
  • moderationDelegatesById (1129-1129)
  • moderationDelegatesById (1149-1149)
ui/component/claimMenuList/view.jsx (1)
  • parseURI (138-138)
ui/component/commentMenuList/index.js (1)
ui/redux/actions/comments.js (8)
  • state (57-57)
  • state (304-304)
  • state (720-720)
  • state (961-961)
  • state (1184-1184)
  • state (2030-2030)
  • creatorChannelClaim (73-73)
  • doCommentModRemoveDelegate (1739-1781)
ui/component/app/index.js (1)
ui/redux/actions/comments.js (1)
  • doCommentModListDelegatesForMyChannels (1815-1867)
ui/redux/actions/comments.js (3)
ui/redux/actions/notifications.js (1)
  • doToast (13-25)
web/src/lbryURI.js (1)
  • __ (4-4)
ui/redux/selectors/claims.js (2)
  • selectMyChannelClaims (773-776)
  • selectMyChannelClaims (773-776)
🪛 Biome (2.1.2)
ui/redux/reducers/comments.js

[error] 1051-1051: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1051-1051: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1057-1057: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1127-1127: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1127-1127: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1147-1147: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1147-1147: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)

ui/component/commentMenuList/view.jsx

[error] 38-39: Expected a statement but instead found ',
moderationDelegatesById:'.

Expected a statement here.

(parse)


[error] 39-39: Expected a property, an expression, or a method but instead found '?string'.

Expected a property, an expression, or a method here.

(parse)


[error] 39-39: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement

(parse)


[error] 39-43: Expected a statement but instead found ',
authorCanonicalUri: ?string,
authorId: string,
// --- perform ---
doToast: ('.

Expected a statement here.

(parse)

ui/redux/actions/comments.js

[error] 1742-1743: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1743-1743: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1816-1816: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 1816-1816: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: lint
🔇 Additional comments (11)
static/app-strings.json (1)

2986-2989: Verify placeholder names match the UI interpolation keys exactly.
These strings introduce %channel%, %user%, and %myChannel%; any mismatch with the code-provided keys will render raw placeholders in the UI.

ui/component/app/view.jsx (1)

64-106: Prop wiring for fetchDelegatesForMyChannels looks consistent.
Flow typing + destructuring aligns with how the prop is used later.

Also applies to: 110-150

ui/component/app/index.js (1)

35-39: fetchDelegatesForMyChannels mapping is straightforward and consistent with App props.

Also applies to: 66-82

ui/redux/reducers/comments.js (2)

1051-1072: LGTM!

The handler correctly iterates over the delegates data, normalizes each entry to the { channelId, channelName } format, and handles the case where delegates is not an array by defaulting to an empty array.


1127-1146: LGTM!

The ADD_MODERATOR_COMPLETED handler properly normalizes the new delegates and correctly handles the case where the existing array might not exist by using a ternary to either concatenate or initialize.

ui/component/commentMenuList/index.js (1)

1-47: LGTM!

The connector properly:

  • Imports the new doCommentModRemoveDelegate action and selectModerationDelegatesById selector
  • Maps moderationDelegatesById to props via select
  • Wires up commentModRemoveDelegate in perform with showToast=true, consistent with the existing commentModAddDelegate pattern
ui/component/commentMenuList/view.jsx (2)

102-115: LGTM!

The logic correctly:

  • Retrieves delegates for the content channel using the optional chaining pattern
  • Computes authorIsModerator only when claimIsMine is true (the viewer owns the content), ensuring the "Remove as moderator" option only appears for content owners

139-144: LGTM!

The removeModerator function correctly mirrors the assignAsModerator pattern, parsing the authorUri and invoking the remove delegate action with the appropriate parameters.

ui/redux/actions/comments.js (3)

1739-1780: LGTM!

The doCommentModRemoveDelegate changes are well-implemented:

  • The showToast parameter follows the same pattern as doCommentModAddDelegate
  • Correctly dispatches REMOVE_MODERATOR_COMPLETED with the removed delegate ID
  • Toast message is properly formatted with channel name placeholders

1815-1867: LGTM!

The doCommentModListDelegatesForMyChannels action is well-structured:

  • Properly validates myChannels before proceeding
  • Uses Promise.allSettled to handle per-channel results gracefully
  • Correctly maps fulfilled results to delegatesById
  • Dispatches appropriate STARTED/COMPLETED/FAILED actions
  • Error handling uses devToast for development visibility

1715-1731: Potential duplicate delegates when adding a moderator — requires backend verification.

The action dispatches ADD_MODERATOR_COMPLETED with newDelegates: res?.Delegates from the API response. The reducer at lines 1138-1140 concatenates this to the existing delegates array. If the moderation_add_delegate endpoint returns the full list of delegates (not just the newly added one), this would cause duplicates.

However, the actual API response shape cannot be verified from the frontend code alone. The semantic pattern suggests the endpoint may return only new delegates (since the reducer concatenates rather than replaces), but this requires confirmation from the backend API documentation or server implementation.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@tzarebczan tzarebczan merged commit 9b5a22b into master Jan 15, 2026
3 checks passed
@tzarebczan tzarebczan deleted the track-delegates branch January 15, 2026 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants