Skip to content

Comments

Redesigned comment moderation list layout#28

Open
tomerqodo wants to merge 23 commits intocoderabbit_combined_20260121_augment_sentry_coderabbit_1_base_redesigned_comment_moderation_list_layout_pr228from
coderabbit_combined_20260121_augment_sentry_coderabbit_1_head_redesigned_comment_moderation_list_layout_pr228
Open

Redesigned comment moderation list layout#28
tomerqodo wants to merge 23 commits intocoderabbit_combined_20260121_augment_sentry_coderabbit_1_base_redesigned_comment_moderation_list_layout_pr228from
coderabbit_combined_20260121_augment_sentry_coderabbit_1_head_redesigned_comment_moderation_list_layout_pr228

Conversation

@tomerqodo
Copy link

@tomerqodo tomerqodo commented Jan 21, 2026

Benchmark PR from qodo-benchmark#228

Summary by CodeRabbit

  • New Features
    • Enhanced comment displays with metadata (reply, like, report counts) and contextual action menus
    • Expandable content for lengthy comments with dynamic clamping
    • Hide/show functionality for individual comments
    • Post feature images now displayed in comment listings
    • Improved responsive comment layout with better visual hierarchy

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 21, 2026

Walkthrough

The changes add support for displaying post feature images in comments UI. The API type definition adds an optional feature_image field to comments, a serializer includes feature_image in the post response payload, and the comments list component is redesigned with enhanced UI using modular components for improved rendering and interactivity.

Changes

Cohort / File(s) Summary
API Type Definitions
apps/admin-x-framework/src/api/comments.ts
Added optional feature_image?: string field to Comment.post interface
Serializer Output Mapper
ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
Extended postFields constant to include feature_image alongside url when serializing post data in comments response
Comments List Component
apps/posts/src/views/comments/components/comments-list.tsx
Significant UI redesign: introduced ExpandButton and CommentContent components for clamping/expansion logic; restructured row rendering with modular author/post/date/status components; added metadata displays (reply/like/report counts), inline actions, dropdown menu, and multi-cell layout with optional post feature image; changed onAddFilter signature from optional to required; expanded header row configuration

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Feature images hop into view,
Comments now show posts bright and true!
With nested rows and expanding cells,
Each comment tells its story well—
Modular magic, UI swell! ✨

🚥 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 accurately describes the primary change: a redesign of the comment moderation list layout with new UI components, metadata displays, and responsive layout improvements.

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

✨ Finishing touches
  • 📝 Generate docstrings

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: 1

🤖 Fix all issues with AI agents
In `@apps/posts/src/views/comments/components/comments-list.tsx`:
- Around line 92-104: The effect that computes clamping in useEffect (the
function using contentRef and setIsClamped) must list the content it depends on
so it updates when comment content changes: add item.html to the dependency
array of that useEffect and ensure the effect recalculates clamping and
re-attaches the resize listener accordingly; also reset isExpanded to false
whenever item.html changes (e.g., inside the same effect or a separate one that
watches item.html) so expansion state doesn’t persist across new content.
🧹 Nitpick comments (2)
apps/posts/src/views/comments/components/comments-list.tsx (2)

200-207: Consider removing or tracking the commented-out avatar image code.

The commented-out AvatarImage code adds clutter. If Gravatar support is intentionally disabled pending a fallback, consider adding a TODO comment with a tracking reference, or remove the code entirely and re-add it when ready.

♻️ Option 1: Add tracking comment
                                         <Avatar className={`size-5 ${item.status === 'hidden' && 'opacity-40'}`}>
-                                            {/* {item.member.avatar_image && (
-                                                <AvatarImage alt={item.member.name} src={item.member.avatar_image} />
-                                            )} */}
+                                            {/* TODO: Re-enable AvatarImage once Gravatar fallback is implemented. See issue `#XXX` */}
                                             <AvatarFallback>
♻️ Option 2: Remove commented code
                                         <Avatar className={`size-5 ${item.status === 'hidden' && 'opacity-40'}`}>
-                                            {/* {item.member.avatar_image && (
-                                                <AvatarImage alt={item.member.name} src={item.member.avatar_image} />
-                                            )} */}
                                             <AvatarFallback>

276-317: Consider wrapping tooltips with a single TooltipProvider.

Multiple TooltipProvider instances are created for each tooltip (replies, likes, reports). A single TooltipProvider can wrap all tooltips in this section for better performance.

♻️ Proposed refactor
-                                            <TooltipProvider>
-                                                <Tooltip>
+                                            <Tooltip>
                                                     <TooltipTrigger asChild>
                                                         <div className={`ml-2 flex items-center gap-1 text-xs ${!item.count?.replies && 'text-muted-foreground/70'}`}>
                                                             <LucideIcon.Reply size={16} strokeWidth={1.5} />
                                                             <span>{formatNumber(item.count?.replies)}</span>
                                                         </div>
                                                     </TooltipTrigger>
                                                     <TooltipContent>
                                                         Replies
                                                     </TooltipContent>
-                                                </Tooltip>
-                                            </TooltipProvider>
+                                            </Tooltip>

Apply the same pattern to the likes and reports tooltips, and wrap the parent <div className='ml-4 flex items-center gap-3'> with a single <TooltipProvider>.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec1d02c and 9bf328f.

⛔ Files ignored due to path filters (2)
  • ghost/core/test/e2e-api/admin/__snapshots__/activity-feed.test.js.snap is excluded by !**/*.snap
  • ghost/core/test/e2e-api/admin/__snapshots__/comments.test.js.snap is excluded by !**/*.snap
📒 Files selected for processing (3)
  • apps/admin-x-framework/src/api/comments.ts
  • apps/posts/src/views/comments/components/comments-list.tsx
  • ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
🧰 Additional context used
🪛 ast-grep (0.40.5)
apps/posts/src/views/comments/components/comments-list.tsx

[warning] 109-109: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
apps/posts/src/views/comments/components/comments-list.tsx

[error] 110-110: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🔇 Additional comments (5)
apps/admin-x-framework/src/api/comments.ts (1)

28-28: LGTM!

The optional feature_image field addition aligns with the backend serializer changes and follows the existing type patterns in this interface.

ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js (1)

34-40: LGTM!

The feature_image field is correctly added to postFields, enabling the frontend to display post feature images in comment rows. The existing _.pick pattern handles optional/missing values gracefully.

apps/posts/src/views/comments/components/comments-list.tsx (3)

351-357: LGTM!

The feature image is conditionally rendered with a proper alt text fallback and responsive visibility. Consider adding an onError handler to gracefully hide broken images in the future, but this is acceptable as-is.


135-135: onAddFilter prop is required and properly provided by all callers.

The prop is indeed marked as required (no optional marker), but the single usage of CommentsList in the codebase properly provides onAddFilter={handleAddFilter}, so there is no actual breaking change.


109-113: No XSS risk—HTML is sanitized server-side before storage.

The Comment model sanitizes all HTML content in its onSaving() hook using the sanitize-html library, allowing only safe tags (p, br, a, blockquote) and attributes. Links are automatically transformed with target="_blank" and rel="ugc noopener noreferrer nofollow". The item.html rendered via dangerouslySetInnerHTML is therefore safe, as it contains pre-sanitized content that has already been validated and restricted on the backend before being stored and served to the frontend.

Likely an incorrect or invalid review comment.

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

Comment on lines +92 to +104
useEffect(() => {
const checkIfClamped = () => {
if (contentRef.current) {
// Check if the content is clamped by comparing scrollHeight with clientHeight
setIsClamped(contentRef.current.scrollHeight > contentRef.current.clientHeight);
}
};

checkIfClamped();
// Recheck on window resize
window.addEventListener('resize', checkIfClamped);
return () => window.removeEventListener('resize', checkIfClamped);
}, []);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing dependency in useEffect may cause stale clamping state.

The useEffect has an empty dependency array but should include item.html since the clamping calculation depends on the content. If item.html changes without remounting the component, isClamped won't update.

Additionally, isExpanded should reset when content changes.

🔧 Proposed fix
     useEffect(() => {
         const checkIfClamped = () => {
             if (contentRef.current) {
                 // Check if the content is clamped by comparing scrollHeight with clientHeight
                 setIsClamped(contentRef.current.scrollHeight > contentRef.current.clientHeight);
             }
         };

         checkIfClamped();
         // Recheck on window resize
         window.addEventListener('resize', checkIfClamped);
         return () => window.removeEventListener('resize', checkIfClamped);
-    }, []);
+    }, [item.html]);

Also consider resetting expansion state when content changes:

+    useEffect(() => {
+        setIsExpanded(false);
+    }, [item.html]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
const checkIfClamped = () => {
if (contentRef.current) {
// Check if the content is clamped by comparing scrollHeight with clientHeight
setIsClamped(contentRef.current.scrollHeight > contentRef.current.clientHeight);
}
};
checkIfClamped();
// Recheck on window resize
window.addEventListener('resize', checkIfClamped);
return () => window.removeEventListener('resize', checkIfClamped);
}, []);
useEffect(() => {
const checkIfClamped = () => {
if (contentRef.current) {
// Check if the content is clamped by comparing scrollHeight with clientHeight
setIsClamped(contentRef.current.scrollHeight > contentRef.current.clientHeight);
}
};
checkIfClamped();
// Recheck on window resize
window.addEventListener('resize', checkIfClamped);
return () => window.removeEventListener('resize', checkIfClamped);
}, [item.html]);
🤖 Prompt for AI Agents
In `@apps/posts/src/views/comments/components/comments-list.tsx` around lines 92 -
104, The effect that computes clamping in useEffect (the function using
contentRef and setIsClamped) must list the content it depends on so it updates
when comment content changes: add item.html to the dependency array of that
useEffect and ensure the effect recalculates clamping and re-attaches the resize
listener accordingly; also reset isExpanded to false whenever item.html changes
(e.g., inside the same effect or a separate one that watches item.html) so
expansion state doesn’t persist across new content.

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.

3 participants