Skip to content

Added pinned comment moderation#27653

Open
JohnONolan wants to merge 3 commits intomainfrom
codex/pinned-comments
Open

Added pinned comment moderation#27653
JohnONolan wants to merge 3 commits intomainfrom
codex/pinned-comments

Conversation

@JohnONolan
Copy link
Copy Markdown
Member

@JohnONolan JohnONolan commented May 2, 2026

Summary

Following user feedback, add support for pinning member comments both on front-end and in admin, if the current user has moderation access.

Also refined moderation user menu design to be more consistent with admin comment moderation, adding icons for actions.

2nd/3rd commits fix two additional comments-related issues:

  • Amended default settings for new sites so that comments are enabled by default (rather than off)
  • If comments are off, hide admin sidebar nav item for comments

(I thought this part was going to be a tiny change but it ended up needing all sorts of test fixtures to be updated so it ended up being a larger change)

Front end

CleanShot 2026-05-02 at 16 59 39@2x

Admin

CleanShot 2026-05-02 at 17 17 27@2x

@github-actions github-actions Bot added affects:i18n migration [pull request] Includes migration for review labels May 2, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 2, 2026

It looks like this PR contains a migration 👀
Here's the checklist for reviewing migrations:

General requirements

  • ⚠️ Tested performance on staging database servers, as performance on local machines is not comparable to a production environment
  • Satisfies idempotency requirement (both up() and down())
  • Does not reference models
  • Filename is in the correct format (and correctly ordered)
  • Targets the next minor version
  • All code paths have appropriate log messages
  • Uses the correct utils
  • Contains a minimal changeset
  • Does not mix DDL/DML operations
  • Tested in MySQL and SQLite

Schema changes

  • Both schema change and related migration have been implemented
  • For index changes: has been performance tested for large tables
  • For new tables/columns: fields use the appropriate predefined field lengths
  • For new tables/columns: field names follow the appropriate conventions
  • Does not drop a non-alpha table outside of a major version

Data changes

  • Mass updates/inserts are batched appropriately
  • Does not loop over large tables/datasets
  • Defends against missing or invalid data
  • For settings updates: follows the appropriate guidelines

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 2, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This pull request implements comment pinning functionality. A new nullable pinned_at datetime column is added to the comments table with a composite index. New usePinComment and useUnpinComment mutations are exposed in the API framework. The Comments service validates pinning constraints—replies and deleted comments cannot be pinned—and manages the pinned_at timestamp. Pinned comments are surfaced first in browse endpoints via the pinnedFirst ordering option. UI components render pinned badges with unpin buttons for admins. Output serialization conditionally exposes the pinned boolean field based on request scope and publication status. Internationalization keys for pin/unpin labels are added across all supported locales. Package versions are incremented to reflect the new feature.

✨ 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 codex/pinned-comments

Copy link
Copy Markdown
Contributor

@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

Caution

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

⚠️ Outside diff range comments (1)
ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js (1)

72-79: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Assert the pin/unpin request body too.

These new cases only prove URL + method. Since pinComment/unpinComment hit the same PUT /comments/:id/ route as hide/show, this test would still pass if the handler sent status instead of pinned.

Suggested diff
 const ACTION_CASES = [
     {action: 'browseComments', idField: 'postId', expectedPath: `/comments/post/${VALID_ID}/`},
     {action: 'getReplies', idField: 'commentId', expectedPath: `/comments/${VALID_ID}/replies/`},
     {action: 'readComment', idField: 'commentId', expectedPath: `/comments/${VALID_ID}/`},
     {action: 'hideComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'},
     {action: 'showComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'},
-    {action: 'pinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'},
-    {action: 'unpinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'}
+    {
+        action: 'pinComment',
+        idField: 'id',
+        expectedPath: `/comments/${VALID_ID}/`,
+        expectedMethod: 'PUT',
+        expectedBody: {comments: [{id: VALID_ID, pinned: true}]}
+    },
+    {
+        action: 'unpinComment',
+        idField: 'id',
+        expectedPath: `/comments/${VALID_ID}/`,
+        expectedMethod: 'PUT',
+        expectedBody: {comments: [{id: VALID_ID, pinned: false}]}
+    }
 ];
                 if (testCase.expectedMethod) {
                     assert.equal(calls[0].opts.method, testCase.expectedMethod);
                 }
+                if (testCase.expectedBody) {
+                    assert.deepEqual(JSON.parse(calls[0].opts.body), testCase.expectedBody);
+                }
                 assert.equal(responses.length, 1);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js` around
lines 72 - 79, The test currently only validates URL and method in ACTION_CASES,
so add assertions to ensure pinComment/unpinComment send the correct request
body (e.g., include a pinned boolean) rather than some other field like status;
update ACTION_CASES to include an expectedBody for the pinComment and
unpinComment entries (or add explicit cases) and modify the test loop that uses
ACTION_CASES to assert the outgoing request body equals the expectedBody for
those actions (refer to ACTION_CASES and the 'pinComment'/'unpinComment' action
names in admin-auth-message-handler.test.js).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js`:
- Around line 91-99: The redaction branches in commentMapper currently always
assign response.html which causes returned objects to include html: null even
when the requested fields don't include 'html'; update those redaction branches
to only set response.html when the caller requested it by guarding assignments
with the same condition used above (i.e. use !fields || fields.has('html')) so
that response respects the initial _.pick(fields) selection and remains
deterministic for requests like fields=id,pinned; locate the assignments to
response.html inside commentMapper and wrap them with that guard.

In `@ghost/core/core/server/services/comments/comments-service.js`:
- Around line 238-261: When handling the pinned update in comments-service.js,
avoid resetting pinned_at for repeat PUT {pinned: true} calls: fetch the
existing comment (using this.models.Comment.findOne / existingComment) and if
data.pinned is true and existingComment.get('pinned') is already true, preserve
existingComment.get('pinned_at') instead of assigning new Date(); only set
editData.pinned_at = new Date() when transitioning from unpinned to pinned, and
set it to null when data.pinned is false. Ensure checks around
existingComment.get('parent_id') and deleted status remain unchanged.

---

Outside diff comments:
In `@ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js`:
- Around line 72-79: The test currently only validates URL and method in
ACTION_CASES, so add assertions to ensure pinComment/unpinComment send the
correct request body (e.g., include a pinned boolean) rather than some other
field like status; update ACTION_CASES to include an expectedBody for the
pinComment and unpinComment entries (or add explicit cases) and modify the test
loop that uses ACTION_CASES to assert the outgoing request body equals the
expectedBody for those actions (refer to ACTION_CASES and the
'pinComment'/'unpinComment' action names in admin-auth-message-handler.test.js).
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bf8e43cd-0966-4a7c-86b4-48ab6c854ab4

📥 Commits

Reviewing files that changed from the base of the PR and between 0c4d441 and 08b895a.

⛔ Files ignored due to path filters (11)
  • apps/comments-ui/src/images/icons/external-link.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/eye-off.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/eye.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/flag.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pencil.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pin-off.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pin.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/trash.svg is excluded by !**/*.svg
  • ghost/core/core/frontend/public/admin-auth/admin-auth.min.js is excluded by !**/*.min.js
  • ghost/core/test/e2e-api/admin/__snapshots__/comments.test.js.snap is excluded by !**/*.snap
  • ghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap is excluded by !**/*.snap
📒 Files selected for processing (95)
  • apps/admin-x-framework/src/api/comments.ts
  • apps/comments-ui/src/actions.ts
  • apps/comments-ui/src/app-context.ts
  • apps/comments-ui/src/components/content/comment.tsx
  • apps/comments-ui/src/components/content/context-menus/admin-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/author-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/comment-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsx
  • apps/comments-ui/src/utils/admin-api.ts
  • apps/comments-ui/test/e2e/admin-moderation.test.ts
  • apps/comments-ui/test/unit/actions.test.js
  • apps/comments-ui/test/unit/components/content/comment.test.jsx
  • apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx
  • apps/comments-ui/test/utils/fixtures.ts
  • apps/posts/src/views/comments/components/comment-header.tsx
  • apps/posts/src/views/comments/components/comment-menu.tsx
  • apps/posts/src/views/comments/components/comment-thread-list.tsx
  • apps/posts/src/views/comments/components/comments-list.tsx
  • apps/posts/test/unit/views/comments/components/comment-header.test.tsx
  • ghost/core/core/frontend/src/admin-auth/message-handler.js
  • ghost/core/core/server/api/endpoints/comments.js
  • ghost/core/core/server/api/endpoints/utils/serializers/input/comments.js
  • ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
  • ghost/core/core/server/data/migrations/versions/6.36/2026-05-02-00-06-36-add-pinned-at-to-comments.js
  • ghost/core/core/server/data/schema/schema.js
  • ghost/core/core/server/models/comment.js
  • ghost/core/core/server/services/comments/comments-controller.js
  • ghost/core/core/server/services/comments/comments-service.js
  • ghost/core/test/e2e-api/admin/comments.test.js
  • ghost/core/test/e2e-api/members-comments/comments.test.js
  • ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js
  • ghost/core/test/unit/server/data/schema/integrity.test.js
  • ghost/i18n/locales/af/comments.json
  • ghost/i18n/locales/ar/comments.json
  • ghost/i18n/locales/bg/comments.json
  • ghost/i18n/locales/bn/comments.json
  • ghost/i18n/locales/bs/comments.json
  • ghost/i18n/locales/ca/comments.json
  • ghost/i18n/locales/context.json
  • ghost/i18n/locales/cs/comments.json
  • ghost/i18n/locales/da/comments.json
  • ghost/i18n/locales/de-CH/comments.json
  • ghost/i18n/locales/de/comments.json
  • ghost/i18n/locales/el/comments.json
  • ghost/i18n/locales/en/comments.json
  • ghost/i18n/locales/eo/comments.json
  • ghost/i18n/locales/es/comments.json
  • ghost/i18n/locales/et/comments.json
  • ghost/i18n/locales/eu/comments.json
  • ghost/i18n/locales/fa/comments.json
  • ghost/i18n/locales/fi/comments.json
  • ghost/i18n/locales/fr/comments.json
  • ghost/i18n/locales/gd/comments.json
  • ghost/i18n/locales/he/comments.json
  • ghost/i18n/locales/hi/comments.json
  • ghost/i18n/locales/hr/comments.json
  • ghost/i18n/locales/hu/comments.json
  • ghost/i18n/locales/id/comments.json
  • ghost/i18n/locales/is/comments.json
  • ghost/i18n/locales/it/comments.json
  • ghost/i18n/locales/ja/comments.json
  • ghost/i18n/locales/ko/comments.json
  • ghost/i18n/locales/kz/comments.json
  • ghost/i18n/locales/lt/comments.json
  • ghost/i18n/locales/lv/comments.json
  • ghost/i18n/locales/mk/comments.json
  • ghost/i18n/locales/mn/comments.json
  • ghost/i18n/locales/ms/comments.json
  • ghost/i18n/locales/nb/comments.json
  • ghost/i18n/locales/ne/comments.json
  • ghost/i18n/locales/nl/comments.json
  • ghost/i18n/locales/nn/comments.json
  • ghost/i18n/locales/pa/comments.json
  • ghost/i18n/locales/pl/comments.json
  • ghost/i18n/locales/pt-BR/comments.json
  • ghost/i18n/locales/pt/comments.json
  • ghost/i18n/locales/ro/comments.json
  • ghost/i18n/locales/ru/comments.json
  • ghost/i18n/locales/si/comments.json
  • ghost/i18n/locales/sk/comments.json
  • ghost/i18n/locales/sl/comments.json
  • ghost/i18n/locales/sq/comments.json
  • ghost/i18n/locales/sr-Cyrl/comments.json
  • ghost/i18n/locales/sr/comments.json
  • ghost/i18n/locales/sv/comments.json
  • ghost/i18n/locales/sw/comments.json
  • ghost/i18n/locales/ta/comments.json
  • ghost/i18n/locales/th/comments.json
  • ghost/i18n/locales/tr/comments.json
  • ghost/i18n/locales/uk/comments.json
  • ghost/i18n/locales/ur/comments.json
  • ghost/i18n/locales/uz/comments.json
  • ghost/i18n/locales/vi/comments.json
  • ghost/i18n/locales/zh-Hant/comments.json
  • ghost/i18n/locales/zh/comments.json

Comment thread ghost/core/core/server/services/comments/comments-service.js Outdated
@JohnONolan JohnONolan force-pushed the codex/pinned-comments branch from 08b895a to fd3d83f Compare May 3, 2026 00:17
Copy link
Copy Markdown
Contributor

@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)
ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js (1)

78-79: ⚡ Quick win

Add payload assertions for pinComment/unpinComment cases.

These new cases only assert URL/method, so they won’t catch a regression where pinned is sent incorrectly. Please assert request body for these actions.

Proposed test hardening
 const ACTION_CASES = [
     {action: 'browseComments', idField: 'postId', expectedPath: `/comments/post/${VALID_ID}/`},
     {action: 'getReplies', idField: 'commentId', expectedPath: `/comments/${VALID_ID}/replies/`},
     {action: 'readComment', idField: 'commentId', expectedPath: `/comments/${VALID_ID}/`},
     {action: 'hideComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'},
     {action: 'showComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'},
-    {action: 'pinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'},
-    {action: 'unpinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'}
+    {action: 'pinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT', expectedBody: {comments: [{id: VALID_ID, pinned: true}]}},
+    {action: 'unpinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT', expectedBody: {comments: [{id: VALID_ID, pinned: false}]}}
 ];
@@
                 if (testCase.expectedMethod) {
                     assert.equal(calls[0].opts.method, testCase.expectedMethod);
                 }
+                if (testCase.expectedBody) {
+                    assert.deepEqual(JSON.parse(calls[0].opts.body), testCase.expectedBody);
+                }
                 assert.equal(responses.length, 1);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js` around
lines 78 - 79, Update the two test cases for actions 'pinComment' and
'unpinComment' so they also assert the request payload; add an expectedBody
(e.g. {pinned: true} for 'pinComment' and {pinned: false} for 'unpinComment')
alongside the existing expectedPath/expectedMethod, and enhance the assertion
logic that validates the fetch call (the code that inspects calls for the case
matching action and VALID_ID) to JSON.parse the request body and compare it to
expectedBody (or assert it contains the pinned property with the correct
boolean). This change should reference the existing test case objects (action:
'pinComment' / 'unpinComment', expectedPath: `/comments/${VALID_ID}/`) and the
fetch assertion block so the body is checked as part of the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js`:
- Around line 78-79: Update the two test cases for actions 'pinComment' and
'unpinComment' so they also assert the request payload; add an expectedBody
(e.g. {pinned: true} for 'pinComment' and {pinned: false} for 'unpinComment')
alongside the existing expectedPath/expectedMethod, and enhance the assertion
logic that validates the fetch call (the code that inspects calls for the case
matching action and VALID_ID) to JSON.parse the request body and compare it to
expectedBody (or assert it contains the pinned property with the correct
boolean). This change should reference the existing test case objects (action:
'pinComment' / 'unpinComment', expectedPath: `/comments/${VALID_ID}/`) and the
fetch assertion block so the body is checked as part of the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e755a21d-327c-4b56-bd9a-dbab3a66ea04

📥 Commits

Reviewing files that changed from the base of the PR and between 08b895a and fd3d83f.

⛔ Files ignored due to path filters (11)
  • apps/comments-ui/src/images/icons/external-link.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/eye-off.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/eye.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/flag.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pencil.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pin-off.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pin.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/trash.svg is excluded by !**/*.svg
  • ghost/core/core/frontend/public/admin-auth/admin-auth.min.js is excluded by !**/*.min.js
  • ghost/core/test/e2e-api/admin/__snapshots__/comments.test.js.snap is excluded by !**/*.snap
  • ghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap is excluded by !**/*.snap
📒 Files selected for processing (95)
  • apps/admin-x-framework/src/api/comments.ts
  • apps/comments-ui/src/actions.ts
  • apps/comments-ui/src/app-context.ts
  • apps/comments-ui/src/components/content/comment.tsx
  • apps/comments-ui/src/components/content/context-menus/admin-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/author-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/comment-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsx
  • apps/comments-ui/src/utils/admin-api.ts
  • apps/comments-ui/test/e2e/admin-moderation.test.ts
  • apps/comments-ui/test/unit/actions.test.js
  • apps/comments-ui/test/unit/components/content/comment.test.jsx
  • apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx
  • apps/comments-ui/test/utils/fixtures.ts
  • apps/posts/src/views/comments/components/comment-header.tsx
  • apps/posts/src/views/comments/components/comment-menu.tsx
  • apps/posts/src/views/comments/components/comment-thread-list.tsx
  • apps/posts/src/views/comments/components/comments-list.tsx
  • apps/posts/test/unit/views/comments/components/comment-header.test.tsx
  • ghost/core/core/frontend/src/admin-auth/message-handler.js
  • ghost/core/core/server/api/endpoints/comments.js
  • ghost/core/core/server/api/endpoints/utils/serializers/input/comments.js
  • ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
  • ghost/core/core/server/data/migrations/versions/6.36/2026-05-02-00-06-36-add-pinned-at-to-comments.js
  • ghost/core/core/server/data/schema/schema.js
  • ghost/core/core/server/models/comment.js
  • ghost/core/core/server/services/comments/comments-controller.js
  • ghost/core/core/server/services/comments/comments-service.js
  • ghost/core/test/e2e-api/admin/comments.test.js
  • ghost/core/test/e2e-api/members-comments/comments.test.js
  • ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js
  • ghost/core/test/unit/server/data/schema/integrity.test.js
  • ghost/i18n/locales/af/comments.json
  • ghost/i18n/locales/ar/comments.json
  • ghost/i18n/locales/bg/comments.json
  • ghost/i18n/locales/bn/comments.json
  • ghost/i18n/locales/bs/comments.json
  • ghost/i18n/locales/ca/comments.json
  • ghost/i18n/locales/context.json
  • ghost/i18n/locales/cs/comments.json
  • ghost/i18n/locales/da/comments.json
  • ghost/i18n/locales/de-CH/comments.json
  • ghost/i18n/locales/de/comments.json
  • ghost/i18n/locales/el/comments.json
  • ghost/i18n/locales/en/comments.json
  • ghost/i18n/locales/eo/comments.json
  • ghost/i18n/locales/es/comments.json
  • ghost/i18n/locales/et/comments.json
  • ghost/i18n/locales/eu/comments.json
  • ghost/i18n/locales/fa/comments.json
  • ghost/i18n/locales/fi/comments.json
  • ghost/i18n/locales/fr/comments.json
  • ghost/i18n/locales/gd/comments.json
  • ghost/i18n/locales/he/comments.json
  • ghost/i18n/locales/hi/comments.json
  • ghost/i18n/locales/hr/comments.json
  • ghost/i18n/locales/hu/comments.json
  • ghost/i18n/locales/id/comments.json
  • ghost/i18n/locales/is/comments.json
  • ghost/i18n/locales/it/comments.json
  • ghost/i18n/locales/ja/comments.json
  • ghost/i18n/locales/ko/comments.json
  • ghost/i18n/locales/kz/comments.json
  • ghost/i18n/locales/lt/comments.json
  • ghost/i18n/locales/lv/comments.json
  • ghost/i18n/locales/mk/comments.json
  • ghost/i18n/locales/mn/comments.json
  • ghost/i18n/locales/ms/comments.json
  • ghost/i18n/locales/nb/comments.json
  • ghost/i18n/locales/ne/comments.json
  • ghost/i18n/locales/nl/comments.json
  • ghost/i18n/locales/nn/comments.json
  • ghost/i18n/locales/pa/comments.json
  • ghost/i18n/locales/pl/comments.json
  • ghost/i18n/locales/pt-BR/comments.json
  • ghost/i18n/locales/pt/comments.json
  • ghost/i18n/locales/ro/comments.json
  • ghost/i18n/locales/ru/comments.json
  • ghost/i18n/locales/si/comments.json
  • ghost/i18n/locales/sk/comments.json
  • ghost/i18n/locales/sl/comments.json
  • ghost/i18n/locales/sq/comments.json
  • ghost/i18n/locales/sr-Cyrl/comments.json
  • ghost/i18n/locales/sr/comments.json
  • ghost/i18n/locales/sv/comments.json
  • ghost/i18n/locales/sw/comments.json
  • ghost/i18n/locales/ta/comments.json
  • ghost/i18n/locales/th/comments.json
  • ghost/i18n/locales/tr/comments.json
  • ghost/i18n/locales/uk/comments.json
  • ghost/i18n/locales/ur/comments.json
  • ghost/i18n/locales/uz/comments.json
  • ghost/i18n/locales/vi/comments.json
  • ghost/i18n/locales/zh-Hant/comments.json
  • ghost/i18n/locales/zh/comments.json
✅ Files skipped from review due to trivial changes (51)
  • ghost/i18n/locales/mk/comments.json
  • ghost/i18n/locales/bg/comments.json
  • ghost/i18n/locales/de-CH/comments.json
  • ghost/i18n/locales/ms/comments.json
  • apps/comments-ui/src/components/content/context-menus/author-context-menu.tsx
  • apps/comments-ui/test/unit/actions.test.js
  • ghost/core/core/frontend/src/admin-auth/message-handler.js
  • ghost/core/core/server/data/migrations/versions/6.36/2026-05-02-00-06-36-add-pinned-at-to-comments.js
  • apps/comments-ui/src/app-context.ts
  • ghost/i18n/locales/mn/comments.json
  • ghost/i18n/locales/id/comments.json
  • apps/posts/test/unit/views/comments/components/comment-header.test.tsx
  • ghost/i18n/locales/uz/comments.json
  • apps/comments-ui/test/unit/components/content/comment.test.jsx
  • ghost/i18n/locales/si/comments.json
  • ghost/i18n/locales/ur/comments.json
  • ghost/core/test/unit/server/data/schema/integrity.test.js
  • apps/comments-ui/src/components/content/context-menus/comment-context-menu.tsx
  • ghost/i18n/locales/af/comments.json
  • ghost/i18n/locales/bn/comments.json
  • ghost/i18n/locales/eo/comments.json
  • ghost/i18n/locales/is/comments.json
  • ghost/i18n/locales/ro/comments.json
  • ghost/i18n/locales/ca/comments.json
  • ghost/i18n/locales/bs/comments.json
  • ghost/i18n/locales/ne/comments.json
  • apps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsx
  • ghost/i18n/locales/pt/comments.json
  • ghost/i18n/locales/pt-BR/comments.json
  • ghost/i18n/locales/ta/comments.json
  • ghost/i18n/locales/ar/comments.json
  • ghost/i18n/locales/nb/comments.json
  • ghost/i18n/locales/fi/comments.json
  • ghost/i18n/locales/uk/comments.json
  • ghost/i18n/locales/en/comments.json
  • ghost/i18n/locales/gd/comments.json
  • ghost/i18n/locales/sq/comments.json
  • ghost/i18n/locales/sv/comments.json
  • ghost/i18n/locales/pl/comments.json
  • ghost/i18n/locales/zh/comments.json
  • ghost/i18n/locales/el/comments.json
  • ghost/i18n/locales/vi/comments.json
  • ghost/i18n/locales/fa/comments.json
  • ghost/i18n/locales/sr/comments.json
  • ghost/i18n/locales/kz/comments.json
  • ghost/core/test/e2e-api/members-comments/comments.test.js
  • ghost/i18n/locales/da/comments.json
  • ghost/i18n/locales/lv/comments.json
  • ghost/i18n/locales/nn/comments.json
  • ghost/i18n/locales/ko/comments.json
  • apps/comments-ui/src/components/content/comment.tsx
🚧 Files skipped from review as they are similar to previous changes (32)
  • ghost/i18n/locales/th/comments.json
  • apps/posts/src/views/comments/components/comment-menu.tsx
  • apps/comments-ui/test/e2e/admin-moderation.test.ts
  • apps/posts/src/views/comments/components/comments-list.tsx
  • ghost/i18n/locales/et/comments.json
  • ghost/i18n/locales/ru/comments.json
  • ghost/core/core/server/api/endpoints/utils/serializers/input/comments.js
  • ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
  • ghost/i18n/locales/zh-Hant/comments.json
  • apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx
  • ghost/core/core/server/services/comments/comments-controller.js
  • ghost/i18n/locales/sk/comments.json
  • ghost/i18n/locales/eu/comments.json
  • ghost/core/core/server/models/comment.js
  • ghost/i18n/locales/hr/comments.json
  • ghost/i18n/locales/pa/comments.json
  • apps/admin-x-framework/src/api/comments.ts
  • ghost/i18n/locales/hi/comments.json
  • ghost/i18n/locales/hu/comments.json
  • ghost/i18n/locales/ja/comments.json
  • apps/comments-ui/test/utils/fixtures.ts
  • ghost/i18n/locales/tr/comments.json
  • ghost/i18n/locales/context.json
  • ghost/core/core/server/services/comments/comments-service.js
  • ghost/core/core/server/data/schema/schema.js
  • ghost/i18n/locales/sl/comments.json
  • ghost/i18n/locales/sw/comments.json
  • ghost/i18n/locales/sr-Cyrl/comments.json
  • apps/posts/src/views/comments/components/comment-header.tsx
  • apps/comments-ui/src/components/content/context-menus/admin-context-menu.tsx
  • apps/comments-ui/src/actions.ts
  • ghost/i18n/locales/it/comments.json

@JohnONolan JohnONolan force-pushed the codex/pinned-comments branch from fd3d83f to 387b70a Compare May 3, 2026 00:26
Copy link
Copy Markdown
Contributor

@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 (3)
ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js (1)

77-79: ⚡ Quick win

Assert pin/unpin payload shape, not just URL/method.

These cases currently pass even if the handler sends the wrong request body. Add expected payload assertions for pinned: true/false to lock behavior.

Proposed test hardening
 const ACTION_CASES = [
     {action: 'browseComments', idField: 'postId', expectedPath: `/comments/post/${VALID_ID}/`},
     {action: 'getReplies', idField: 'commentId', expectedPath: `/comments/${VALID_ID}/replies/`},
     {action: 'readComment', idField: 'commentId', expectedPath: `/comments/${VALID_ID}/`},
-    {action: 'hideComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'},
-    {action: 'showComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'},
-    {action: 'pinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'},
-    {action: 'unpinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'}
+    {action: 'hideComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT', expectedBody: {comments: [{id: VALID_ID, status: 'hidden'}]}},
+    {action: 'showComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT', expectedBody: {comments: [{id: VALID_ID, status: 'published'}]}},
+    {action: 'pinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT', expectedBody: {comments: [{id: VALID_ID, pinned: true}]}},
+    {action: 'unpinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT', expectedBody: {comments: [{id: VALID_ID, pinned: false}]}}
 ];
                 if (testCase.expectedMethod) {
                     assert.equal(calls[0].opts.method, testCase.expectedMethod);
                 }
+                if (testCase.expectedBody) {
+                    assert.deepEqual(JSON.parse(calls[0].opts.body), testCase.expectedBody);
+                }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js` around
lines 77 - 79, The pin/unpin tests only assert URL/method; add payload
assertions so the handler must send {pinned: true} for 'pinComment' and {pinned:
false} for 'unpinComment'. Update the test cases (the objects with action:
'pinComment' and action: 'unpinComment') to include an expectedPayload property
(expectedPayload: {pinned: true} and expectedPayload: {pinned: false}), and
update the test assertion that inspects the simulated request body to compare it
against expectedPayload (where the test currently checks
expectedPath/expectedMethod) so the request body is validated as well.
ghost/core/test/e2e-api/admin/comments.test.js (1)

565-567: ⚡ Quick win

Tighten reply pinned assertions to false

At Line 566, Line 683, Line 724, and Line 766, pinned: anyBoolean is too permissive for replies and can hide regressions where replies become pinnable.

Suggested diff
-                pinned: anyBoolean
+                pinned: false
-                pinned: anyBoolean
+                pinned: false
-                pinned: anyBoolean
+                pinned: false
-                pinned: anyBoolean
+                pinned: false

Also applies to: 682-684, 723-725, 765-767

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

In `@ghost/core/test/e2e-api/admin/comments.test.js` around lines 565 - 567,
Replace the permissive pinned: anyBoolean used in reply assertions with pinned:
false so replies are asserted non-pinnable; locate the reply expectation objects
in the admin comments E2E tests (the assertion blocks that define reply objects
with a pinned property) and change pinned: anyBoolean to pinned: false for the
reply cases (leave top-level comment assertions as-is if they should remain
flexible), update all occurrences mentioned in the review (the reply assertions
around the three reply-check blocks) and re-run the test suite to confirm no
regressions.
apps/comments-ui/test/utils/fixtures.ts (1)

71-82: ⚡ Quick win

Keep override support in buildCommentsReply for fixture flexibility.

Line 71 currently drops the optional override path, which makes targeted fixture customization harder in tests and can break existing usage patterns.

♻️ Suggested patch
-export function buildCommentsReply() {
+export function buildCommentsReply(override: any = {}) {
     return {
         comments: [],
         meta: {
             pagination: {
                 pages: 1,
                 total: 0,
                 page: 1,
                 limit: 5
             }
-        }
+        },
+        ...override
     };
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/comments-ui/test/utils/fixtures.ts` around lines 71 - 82,
buildCommentsReply currently returns a fixed object and removed support for
overrides; restore an optional parameter (e.g., overrides?: Partial<typeof
defaultReturn>) and merge it into the default fixture (using object spread or a
deep-merge helper) so callers can pass partial overrides for comments, meta,
pagination, etc.; update the function signature buildCommentsReply to accept the
overrides arg and return { ...defaultValues, ...overrides } (ensuring nested
objects like meta.pagination are merged appropriately).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/comments-ui/test/utils/fixtures.ts`:
- Around line 71-82: buildCommentsReply currently returns a fixed object and
removed support for overrides; restore an optional parameter (e.g., overrides?:
Partial<typeof defaultReturn>) and merge it into the default fixture (using
object spread or a deep-merge helper) so callers can pass partial overrides for
comments, meta, pagination, etc.; update the function signature
buildCommentsReply to accept the overrides arg and return { ...defaultValues,
...overrides } (ensuring nested objects like meta.pagination are merged
appropriately).

In `@ghost/core/test/e2e-api/admin/comments.test.js`:
- Around line 565-567: Replace the permissive pinned: anyBoolean used in reply
assertions with pinned: false so replies are asserted non-pinnable; locate the
reply expectation objects in the admin comments E2E tests (the assertion blocks
that define reply objects with a pinned property) and change pinned: anyBoolean
to pinned: false for the reply cases (leave top-level comment assertions as-is
if they should remain flexible), update all occurrences mentioned in the review
(the reply assertions around the three reply-check blocks) and re-run the test
suite to confirm no regressions.

In `@ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js`:
- Around line 77-79: The pin/unpin tests only assert URL/method; add payload
assertions so the handler must send {pinned: true} for 'pinComment' and {pinned:
false} for 'unpinComment'. Update the test cases (the objects with action:
'pinComment' and action: 'unpinComment') to include an expectedPayload property
(expectedPayload: {pinned: true} and expectedPayload: {pinned: false}), and
update the test assertion that inspects the simulated request body to compare it
against expectedPayload (where the test currently checks
expectedPath/expectedMethod) so the request body is validated as well.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 01192958-5df8-457e-9f76-92de8e21e15e

📥 Commits

Reviewing files that changed from the base of the PR and between fd3d83f and 387b70a.

⛔ Files ignored due to path filters (12)
  • apps/comments-ui/src/images/icons/external-link.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/eye-off.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/eye.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/flag.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pencil.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pin-off.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pin.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/trash.svg is excluded by !**/*.svg
  • ghost/core/core/frontend/public/admin-auth/admin-auth.min.js is excluded by !**/*.min.js
  • 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
  • ghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap is excluded by !**/*.snap
📒 Files selected for processing (96)
  • apps/admin-x-framework/src/api/comments.ts
  • apps/comments-ui/package.json
  • apps/comments-ui/src/actions.ts
  • apps/comments-ui/src/app-context.ts
  • apps/comments-ui/src/components/content/comment.tsx
  • apps/comments-ui/src/components/content/context-menus/admin-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/author-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/comment-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsx
  • apps/comments-ui/src/utils/admin-api.ts
  • apps/comments-ui/test/e2e/admin-moderation.test.ts
  • apps/comments-ui/test/unit/actions.test.js
  • apps/comments-ui/test/unit/components/content/comment.test.jsx
  • apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx
  • apps/comments-ui/test/utils/fixtures.ts
  • apps/posts/src/views/comments/components/comment-header.tsx
  • apps/posts/src/views/comments/components/comment-menu.tsx
  • apps/posts/src/views/comments/components/comment-thread-list.tsx
  • apps/posts/src/views/comments/components/comments-list.tsx
  • apps/posts/test/unit/views/comments/components/comment-header.test.tsx
  • ghost/core/core/frontend/src/admin-auth/message-handler.js
  • ghost/core/core/server/api/endpoints/comments.js
  • ghost/core/core/server/api/endpoints/utils/serializers/input/comments.js
  • ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
  • ghost/core/core/server/data/migrations/versions/6.36/2026-05-02-00-06-36-add-pinned-at-to-comments.js
  • ghost/core/core/server/data/schema/schema.js
  • ghost/core/core/server/models/comment.js
  • ghost/core/core/server/services/comments/comments-controller.js
  • ghost/core/core/server/services/comments/comments-service.js
  • ghost/core/test/e2e-api/admin/comments.test.js
  • ghost/core/test/e2e-api/members-comments/comments.test.js
  • ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js
  • ghost/core/test/unit/server/data/schema/integrity.test.js
  • ghost/i18n/locales/af/comments.json
  • ghost/i18n/locales/ar/comments.json
  • ghost/i18n/locales/bg/comments.json
  • ghost/i18n/locales/bn/comments.json
  • ghost/i18n/locales/bs/comments.json
  • ghost/i18n/locales/ca/comments.json
  • ghost/i18n/locales/context.json
  • ghost/i18n/locales/cs/comments.json
  • ghost/i18n/locales/da/comments.json
  • ghost/i18n/locales/de-CH/comments.json
  • ghost/i18n/locales/de/comments.json
  • ghost/i18n/locales/el/comments.json
  • ghost/i18n/locales/en/comments.json
  • ghost/i18n/locales/eo/comments.json
  • ghost/i18n/locales/es/comments.json
  • ghost/i18n/locales/et/comments.json
  • ghost/i18n/locales/eu/comments.json
  • ghost/i18n/locales/fa/comments.json
  • ghost/i18n/locales/fi/comments.json
  • ghost/i18n/locales/fr/comments.json
  • ghost/i18n/locales/gd/comments.json
  • ghost/i18n/locales/he/comments.json
  • ghost/i18n/locales/hi/comments.json
  • ghost/i18n/locales/hr/comments.json
  • ghost/i18n/locales/hu/comments.json
  • ghost/i18n/locales/id/comments.json
  • ghost/i18n/locales/is/comments.json
  • ghost/i18n/locales/it/comments.json
  • ghost/i18n/locales/ja/comments.json
  • ghost/i18n/locales/ko/comments.json
  • ghost/i18n/locales/kz/comments.json
  • ghost/i18n/locales/lt/comments.json
  • ghost/i18n/locales/lv/comments.json
  • ghost/i18n/locales/mk/comments.json
  • ghost/i18n/locales/mn/comments.json
  • ghost/i18n/locales/ms/comments.json
  • ghost/i18n/locales/nb/comments.json
  • ghost/i18n/locales/ne/comments.json
  • ghost/i18n/locales/nl/comments.json
  • ghost/i18n/locales/nn/comments.json
  • ghost/i18n/locales/pa/comments.json
  • ghost/i18n/locales/pl/comments.json
  • ghost/i18n/locales/pt-BR/comments.json
  • ghost/i18n/locales/pt/comments.json
  • ghost/i18n/locales/ro/comments.json
  • ghost/i18n/locales/ru/comments.json
  • ghost/i18n/locales/si/comments.json
  • ghost/i18n/locales/sk/comments.json
  • ghost/i18n/locales/sl/comments.json
  • ghost/i18n/locales/sq/comments.json
  • ghost/i18n/locales/sr-Cyrl/comments.json
  • ghost/i18n/locales/sr/comments.json
  • ghost/i18n/locales/sv/comments.json
  • ghost/i18n/locales/sw/comments.json
  • ghost/i18n/locales/ta/comments.json
  • ghost/i18n/locales/th/comments.json
  • ghost/i18n/locales/tr/comments.json
  • ghost/i18n/locales/uk/comments.json
  • ghost/i18n/locales/ur/comments.json
  • ghost/i18n/locales/uz/comments.json
  • ghost/i18n/locales/vi/comments.json
  • ghost/i18n/locales/zh-Hant/comments.json
  • ghost/i18n/locales/zh/comments.json
✅ Files skipped from review due to trivial changes (56)
  • apps/comments-ui/src/app-context.ts
  • ghost/i18n/locales/nb/comments.json
  • apps/comments-ui/package.json
  • ghost/core/test/unit/server/data/schema/integrity.test.js
  • ghost/i18n/locales/it/comments.json
  • ghost/i18n/locales/mn/comments.json
  • ghost/i18n/locales/ne/comments.json
  • ghost/i18n/locales/sq/comments.json
  • ghost/i18n/locales/kz/comments.json
  • ghost/i18n/locales/el/comments.json
  • apps/comments-ui/src/components/content/context-menus/author-context-menu.tsx
  • ghost/i18n/locales/bn/comments.json
  • ghost/core/core/server/data/migrations/versions/6.36/2026-05-02-00-06-36-add-pinned-at-to-comments.js
  • ghost/i18n/locales/ur/comments.json
  • apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx
  • apps/posts/test/unit/views/comments/components/comment-header.test.tsx
  • apps/posts/src/views/comments/components/comments-list.tsx
  • ghost/i18n/locales/context.json
  • ghost/core/core/server/api/endpoints/comments.js
  • ghost/i18n/locales/eo/comments.json
  • ghost/i18n/locales/ta/comments.json
  • ghost/i18n/locales/af/comments.json
  • ghost/i18n/locales/pt/comments.json
  • ghost/i18n/locales/ja/comments.json
  • ghost/i18n/locales/lt/comments.json
  • ghost/i18n/locales/uz/comments.json
  • ghost/i18n/locales/pl/comments.json
  • ghost/core/core/server/api/endpoints/utils/serializers/input/comments.js
  • ghost/i18n/locales/uk/comments.json
  • ghost/i18n/locales/nn/comments.json
  • apps/comments-ui/src/actions.ts
  • ghost/i18n/locales/zh/comments.json
  • ghost/i18n/locales/es/comments.json
  • ghost/i18n/locales/zh-Hant/comments.json
  • ghost/i18n/locales/de-CH/comments.json
  • ghost/i18n/locales/mk/comments.json
  • apps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsx
  • apps/posts/src/views/comments/components/comment-header.tsx
  • ghost/i18n/locales/ca/comments.json
  • ghost/i18n/locales/hr/comments.json
  • ghost/i18n/locales/id/comments.json
  • ghost/i18n/locales/ar/comments.json
  • ghost/i18n/locales/lv/comments.json
  • ghost/i18n/locales/pa/comments.json
  • ghost/i18n/locales/sr-Cyrl/comments.json
  • ghost/i18n/locales/ko/comments.json
  • ghost/i18n/locales/ms/comments.json
  • ghost/i18n/locales/da/comments.json
  • apps/comments-ui/test/unit/actions.test.js
  • ghost/i18n/locales/bg/comments.json
  • ghost/i18n/locales/he/comments.json
  • ghost/i18n/locales/sl/comments.json
  • ghost/i18n/locales/sv/comments.json
  • ghost/i18n/locales/et/comments.json
  • ghost/i18n/locales/pt-BR/comments.json
  • ghost/i18n/locales/gd/comments.json
🚧 Files skipped from review as they are similar to previous changes (28)
  • apps/posts/src/views/comments/components/comment-menu.tsx
  • ghost/i18n/locales/bs/comments.json
  • ghost/i18n/locales/fa/comments.json
  • ghost/i18n/locales/hu/comments.json
  • apps/comments-ui/test/unit/components/content/comment.test.jsx
  • apps/admin-x-framework/src/api/comments.ts
  • ghost/i18n/locales/fi/comments.json
  • ghost/i18n/locales/vi/comments.json
  • ghost/i18n/locales/si/comments.json
  • ghost/i18n/locales/is/comments.json
  • apps/comments-ui/src/components/content/context-menus/comment-context-menu.tsx
  • ghost/core/core/server/services/comments/comments-service.js
  • ghost/i18n/locales/cs/comments.json
  • ghost/core/core/server/models/comment.js
  • ghost/i18n/locales/fr/comments.json
  • ghost/i18n/locales/de/comments.json
  • ghost/core/core/server/data/schema/schema.js
  • ghost/i18n/locales/sr/comments.json
  • ghost/i18n/locales/sw/comments.json
  • ghost/i18n/locales/hi/comments.json
  • apps/comments-ui/src/components/content/context-menus/admin-context-menu.tsx
  • ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
  • apps/comments-ui/test/e2e/admin-moderation.test.ts
  • ghost/i18n/locales/sk/comments.json
  • ghost/i18n/locales/ru/comments.json
  • ghost/i18n/locales/ro/comments.json
  • ghost/core/test/e2e-api/members-comments/comments.test.js
  • ghost/i18n/locales/en/comments.json

@JohnONolan JohnONolan force-pushed the codex/pinned-comments branch from 387b70a to 35239a8 Compare May 3, 2026 00:36
Copy link
Copy Markdown
Contributor

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ghost/core/core/server/services/comments/comments-service.js`:
- Around line 21-33: The getCommentByID path currently bypasses the pinned-field
shim; update the single-comment read to apply withPinnedSelect so the "pinned"
synthetic column is returned when requested: locate getCommentByID (or the
method that calls this.models.Comment.findOne) and change the call to pass
withPinnedSelect(options) instead of options (e.g.,
this.models.Comment.findOne({id}, withPinnedSelect(options))); ensure the same
options shape (columns/selectRaw) is preserved and that no duplicate selectRaw
merging occurs.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d37ddba8-0faa-4668-a692-3c0ba4795a0b

📥 Commits

Reviewing files that changed from the base of the PR and between 387b70a and 35239a8.

⛔ Files ignored due to path filters (12)
  • apps/comments-ui/src/images/icons/external-link.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/eye-off.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/eye.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/flag.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pencil.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pin-off.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pin.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/trash.svg is excluded by !**/*.svg
  • ghost/core/core/frontend/public/admin-auth/admin-auth.min.js is excluded by !**/*.min.js
  • 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
  • ghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap is excluded by !**/*.snap
📒 Files selected for processing (96)
  • apps/admin-x-framework/src/api/comments.ts
  • apps/comments-ui/package.json
  • apps/comments-ui/src/actions.ts
  • apps/comments-ui/src/app-context.ts
  • apps/comments-ui/src/components/content/comment.tsx
  • apps/comments-ui/src/components/content/context-menus/admin-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/author-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/comment-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsx
  • apps/comments-ui/src/utils/admin-api.ts
  • apps/comments-ui/test/e2e/admin-moderation.test.ts
  • apps/comments-ui/test/unit/actions.test.js
  • apps/comments-ui/test/unit/components/content/comment.test.jsx
  • apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx
  • apps/comments-ui/test/utils/fixtures.ts
  • apps/posts/src/views/comments/components/comment-header.tsx
  • apps/posts/src/views/comments/components/comment-menu.tsx
  • apps/posts/src/views/comments/components/comment-thread-list.tsx
  • apps/posts/src/views/comments/components/comments-list.tsx
  • apps/posts/test/unit/views/comments/components/comment-header.test.tsx
  • ghost/core/core/frontend/src/admin-auth/message-handler.js
  • ghost/core/core/server/api/endpoints/comments.js
  • ghost/core/core/server/api/endpoints/utils/serializers/input/comments.js
  • ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
  • ghost/core/core/server/data/migrations/versions/6.36/2026-05-02-00-06-36-add-pinned-at-to-comments.js
  • ghost/core/core/server/data/schema/schema.js
  • ghost/core/core/server/models/comment.js
  • ghost/core/core/server/services/comments/comments-controller.js
  • ghost/core/core/server/services/comments/comments-service.js
  • ghost/core/test/e2e-api/admin/comments.test.js
  • ghost/core/test/e2e-api/members-comments/comments.test.js
  • ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js
  • ghost/core/test/unit/server/data/schema/integrity.test.js
  • ghost/i18n/locales/af/comments.json
  • ghost/i18n/locales/ar/comments.json
  • ghost/i18n/locales/bg/comments.json
  • ghost/i18n/locales/bn/comments.json
  • ghost/i18n/locales/bs/comments.json
  • ghost/i18n/locales/ca/comments.json
  • ghost/i18n/locales/context.json
  • ghost/i18n/locales/cs/comments.json
  • ghost/i18n/locales/da/comments.json
  • ghost/i18n/locales/de-CH/comments.json
  • ghost/i18n/locales/de/comments.json
  • ghost/i18n/locales/el/comments.json
  • ghost/i18n/locales/en/comments.json
  • ghost/i18n/locales/eo/comments.json
  • ghost/i18n/locales/es/comments.json
  • ghost/i18n/locales/et/comments.json
  • ghost/i18n/locales/eu/comments.json
  • ghost/i18n/locales/fa/comments.json
  • ghost/i18n/locales/fi/comments.json
  • ghost/i18n/locales/fr/comments.json
  • ghost/i18n/locales/gd/comments.json
  • ghost/i18n/locales/he/comments.json
  • ghost/i18n/locales/hi/comments.json
  • ghost/i18n/locales/hr/comments.json
  • ghost/i18n/locales/hu/comments.json
  • ghost/i18n/locales/id/comments.json
  • ghost/i18n/locales/is/comments.json
  • ghost/i18n/locales/it/comments.json
  • ghost/i18n/locales/ja/comments.json
  • ghost/i18n/locales/ko/comments.json
  • ghost/i18n/locales/kz/comments.json
  • ghost/i18n/locales/lt/comments.json
  • ghost/i18n/locales/lv/comments.json
  • ghost/i18n/locales/mk/comments.json
  • ghost/i18n/locales/mn/comments.json
  • ghost/i18n/locales/ms/comments.json
  • ghost/i18n/locales/nb/comments.json
  • ghost/i18n/locales/ne/comments.json
  • ghost/i18n/locales/nl/comments.json
  • ghost/i18n/locales/nn/comments.json
  • ghost/i18n/locales/pa/comments.json
  • ghost/i18n/locales/pl/comments.json
  • ghost/i18n/locales/pt-BR/comments.json
  • ghost/i18n/locales/pt/comments.json
  • ghost/i18n/locales/ro/comments.json
  • ghost/i18n/locales/ru/comments.json
  • ghost/i18n/locales/si/comments.json
  • ghost/i18n/locales/sk/comments.json
  • ghost/i18n/locales/sl/comments.json
  • ghost/i18n/locales/sq/comments.json
  • ghost/i18n/locales/sr-Cyrl/comments.json
  • ghost/i18n/locales/sr/comments.json
  • ghost/i18n/locales/sv/comments.json
  • ghost/i18n/locales/sw/comments.json
  • ghost/i18n/locales/ta/comments.json
  • ghost/i18n/locales/th/comments.json
  • ghost/i18n/locales/tr/comments.json
  • ghost/i18n/locales/uk/comments.json
  • ghost/i18n/locales/ur/comments.json
  • ghost/i18n/locales/uz/comments.json
  • ghost/i18n/locales/vi/comments.json
  • ghost/i18n/locales/zh-Hant/comments.json
  • ghost/i18n/locales/zh/comments.json
✅ Files skipped from review due to trivial changes (50)
  • ghost/i18n/locales/ru/comments.json
  • ghost/core/test/unit/server/data/schema/integrity.test.js
  • apps/comments-ui/package.json
  • ghost/i18n/locales/nb/comments.json
  • apps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsx
  • apps/posts/test/unit/views/comments/components/comment-header.test.tsx
  • ghost/i18n/locales/ar/comments.json
  • apps/comments-ui/src/app-context.ts
  • apps/comments-ui/src/components/content/context-menus/author-context-menu.tsx
  • apps/comments-ui/test/unit/actions.test.js
  • apps/comments-ui/src/utils/admin-api.ts
  • ghost/i18n/locales/ne/comments.json
  • apps/posts/src/views/comments/components/comments-list.tsx
  • ghost/i18n/locales/mk/comments.json
  • ghost/i18n/locales/et/comments.json
  • ghost/i18n/locales/sl/comments.json
  • apps/admin-x-framework/src/api/comments.ts
  • ghost/i18n/locales/vi/comments.json
  • ghost/i18n/locales/pl/comments.json
  • ghost/i18n/locales/is/comments.json
  • ghost/i18n/locales/hu/comments.json
  • ghost/i18n/locales/si/comments.json
  • ghost/i18n/locales/el/comments.json
  • ghost/i18n/locales/sk/comments.json
  • ghost/i18n/locales/bs/comments.json
  • apps/comments-ui/test/e2e/admin-moderation.test.ts
  • ghost/i18n/locales/cs/comments.json
  • ghost/i18n/locales/ta/comments.json
  • ghost/i18n/locales/es/comments.json
  • ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js
  • ghost/i18n/locales/tr/comments.json
  • ghost/i18n/locales/sv/comments.json
  • apps/comments-ui/src/components/content/context-menus/comment-context-menu.tsx
  • ghost/i18n/locales/id/comments.json
  • ghost/i18n/locales/hi/comments.json
  • apps/comments-ui/test/unit/components/content/comment.test.jsx
  • ghost/i18n/locales/pa/comments.json
  • ghost/i18n/locales/uz/comments.json
  • ghost/i18n/locales/da/comments.json
  • ghost/i18n/locales/it/comments.json
  • ghost/i18n/locales/pt/comments.json
  • ghost/i18n/locales/pt-BR/comments.json
  • ghost/i18n/locales/lv/comments.json
  • ghost/core/test/e2e-api/admin/comments.test.js
  • ghost/i18n/locales/context.json
  • ghost/i18n/locales/eo/comments.json
  • ghost/i18n/locales/nn/comments.json
  • ghost/i18n/locales/mn/comments.json
  • ghost/i18n/locales/lt/comments.json
  • ghost/i18n/locales/sr-Cyrl/comments.json
🚧 Files skipped from review as they are similar to previous changes (31)
  • apps/posts/src/views/comments/components/comment-thread-list.tsx
  • ghost/i18n/locales/af/comments.json
  • ghost/i18n/locales/nl/comments.json
  • ghost/i18n/locales/bg/comments.json
  • ghost/i18n/locales/fa/comments.json
  • ghost/i18n/locales/ro/comments.json
  • ghost/i18n/locales/uk/comments.json
  • ghost/i18n/locales/bn/comments.json
  • ghost/i18n/locales/sr/comments.json
  • apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx
  • ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
  • ghost/i18n/locales/de/comments.json
  • ghost/i18n/locales/hr/comments.json
  • ghost/i18n/locales/sq/comments.json
  • apps/comments-ui/src/actions.ts
  • ghost/core/core/server/data/migrations/versions/6.36/2026-05-02-00-06-36-add-pinned-at-to-comments.js
  • ghost/i18n/locales/ko/comments.json
  • ghost/i18n/locales/ur/comments.json
  • ghost/i18n/locales/de-CH/comments.json
  • ghost/i18n/locales/th/comments.json
  • ghost/i18n/locales/sw/comments.json
  • ghost/core/core/server/api/endpoints/comments.js
  • apps/comments-ui/src/components/content/comment.tsx
  • ghost/i18n/locales/ms/comments.json
  • apps/comments-ui/test/utils/fixtures.ts
  • ghost/i18n/locales/ca/comments.json
  • ghost/i18n/locales/he/comments.json
  • ghost/i18n/locales/fi/comments.json
  • ghost/i18n/locales/zh/comments.json
  • apps/comments-ui/src/components/content/context-menus/admin-context-menu.tsx
  • ghost/i18n/locales/zh-Hant/comments.json

Comment thread ghost/core/core/server/services/comments/comments-service.js Outdated
@JohnONolan JohnONolan force-pushed the codex/pinned-comments branch from 35239a8 to 82c82e0 Compare May 3, 2026 00:50
Copy link
Copy Markdown
Contributor

@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 (2)
apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx (1)

83-90: ⚡ Quick win

Add a deleted-comment pin-gating test to complete coverage.

You already cover top-level vs reply, but canPin also excludes deleted comments. Adding that case will lock the constraint and prevent regressions.

✅ Suggested test addition
+    it('does not show pin action for deleted comments', () => {
+        const comment = buildComment({status: 'deleted'});
+
+        contextualRender(<CommentContextMenu close={() => {}} comment={comment} />, {appContext: {isAdmin: true}});
+
+        expect(screen.queryByTestId('pin-button')).not.toBeInTheDocument();
+        expect(screen.queryByTestId('unpin-button')).not.toBeInTheDocument();
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx`
around lines 83 - 90, Add a unit test mirroring the "does not show pin action
for replies" case but for deleted comments: render <CommentContextMenu close={()
=> {}} comment={buildComment({deleted: true})} /> with appContext {isAdmin:
true} using contextualRender, then assert screen.queryByTestId('pin-button') and
screen.queryByTestId('unpin-button') are not in the document; place this
alongside the existing tests to cover the canPin gate for deleted comments.
apps/posts/src/views/comments/components/comment-menu.tsx (1)

74-86: ⚡ Quick win

Wrap menu labels in t() for internationalization.

The PR is labeled affects:i18n, but these menu item labels are hardcoded English strings. Per coding guidelines, use translation functions for UI text.

-                            <DropdownMenuItem onClick={() => unpinComment({id: commentId})}>
-                                <LucideIcon.PinOff className="size-4" />
-                                Unpin comment
+                            <DropdownMenuItem onClick={() => unpinComment({id: commentId})}>
+                                <LucideIcon.PinOff className="size-4" />
+                                {t('Unpin comment')}
                             </DropdownMenuItem>
                         ) : (
-                            <DropdownMenuItem onClick={() => pinComment({id: commentId})}>
-                                <LucideIcon.Pin className="size-4" />
-                                Pin comment
+                            <DropdownMenuItem onClick={() => pinComment({id: commentId})}>
+                                <LucideIcon.Pin className="size-4" />
+                                {t('Pin comment')}
                             </DropdownMenuItem>

As per coding guidelines: **/*.{ts,tsx,js,jsx}: Never split sentences across multiple t() calls in translation strings.

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

In `@apps/posts/src/views/comments/components/comment-menu.tsx` around lines 74 -
86, The menu labels "Pin comment" and "Unpin comment" are hardcoded; update the
JSX in the DropdownMenuItem instances (the branch that checks comment.pinned and
calls pinComment/unpinComment with commentId) to wrap each full label in the
translation function t() (e.g., t("Pin comment") and t("Unpin comment")),
ensuring each label is a single t() call and not split across multiple t()
calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx`:
- Around line 83-90: Add a unit test mirroring the "does not show pin action for
replies" case but for deleted comments: render <CommentContextMenu close={() =>
{}} comment={buildComment({deleted: true})} /> with appContext {isAdmin: true}
using contextualRender, then assert screen.queryByTestId('pin-button') and
screen.queryByTestId('unpin-button') are not in the document; place this
alongside the existing tests to cover the canPin gate for deleted comments.

In `@apps/posts/src/views/comments/components/comment-menu.tsx`:
- Around line 74-86: The menu labels "Pin comment" and "Unpin comment" are
hardcoded; update the JSX in the DropdownMenuItem instances (the branch that
checks comment.pinned and calls pinComment/unpinComment with commentId) to wrap
each full label in the translation function t() (e.g., t("Pin comment") and
t("Unpin comment")), ensuring each label is a single t() call and not split
across multiple t() calls.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4b1f4567-2c4a-4e6e-aef1-558f4230dc35

📥 Commits

Reviewing files that changed from the base of the PR and between 35239a8 and 82c82e0.

⛔ Files ignored due to path filters (12)
  • apps/comments-ui/src/images/icons/external-link.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/eye-off.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/eye.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/flag.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pencil.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pin-off.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/pin.svg is excluded by !**/*.svg
  • apps/comments-ui/src/images/icons/trash.svg is excluded by !**/*.svg
  • ghost/core/core/frontend/public/admin-auth/admin-auth.min.js is excluded by !**/*.min.js
  • 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
  • ghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap is excluded by !**/*.snap
📒 Files selected for processing (99)
  • apps/admin-x-framework/src/api/comments.ts
  • apps/comments-ui/package.json
  • apps/comments-ui/src/actions.ts
  • apps/comments-ui/src/app-context.ts
  • apps/comments-ui/src/components/content/comment.tsx
  • apps/comments-ui/src/components/content/context-menus/admin-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/author-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/comment-context-menu.tsx
  • apps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsx
  • apps/comments-ui/src/utils/admin-api.ts
  • apps/comments-ui/test/e2e/admin-moderation.test.ts
  • apps/comments-ui/test/unit/actions.test.js
  • apps/comments-ui/test/unit/components/content/comment.test.jsx
  • apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx
  • apps/comments-ui/test/utils/fixtures.ts
  • apps/posts/src/views/comments/components/comment-header.tsx
  • apps/posts/src/views/comments/components/comment-menu.tsx
  • apps/posts/src/views/comments/components/comment-thread-list.tsx
  • apps/posts/src/views/comments/components/comments-list.tsx
  • apps/posts/test/unit/views/comments/components/comment-header.test.tsx
  • ghost/admin/package.json
  • ghost/core/core/frontend/src/admin-auth/message-handler.js
  • ghost/core/core/server/api/endpoints/comments.js
  • ghost/core/core/server/api/endpoints/utils/serializers/input/comments.js
  • ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
  • ghost/core/core/server/data/migrations/versions/6.37/2026-05-03-00-46-46-add-pinned-at-to-comments.js
  • ghost/core/core/server/data/schema/schema.js
  • ghost/core/core/server/models/comment.js
  • ghost/core/core/server/services/comments/comments-controller.js
  • ghost/core/core/server/services/comments/comments-service.js
  • ghost/core/package.json
  • ghost/core/test/e2e-api/admin/comments.test.js
  • ghost/core/test/e2e-api/members-comments/comments.test.js
  • ghost/core/test/unit/api/canary/utils/serializers/output/mapper.test.js
  • ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js
  • ghost/core/test/unit/server/data/schema/integrity.test.js
  • ghost/i18n/locales/af/comments.json
  • ghost/i18n/locales/ar/comments.json
  • ghost/i18n/locales/bg/comments.json
  • ghost/i18n/locales/bn/comments.json
  • ghost/i18n/locales/bs/comments.json
  • ghost/i18n/locales/ca/comments.json
  • ghost/i18n/locales/context.json
  • ghost/i18n/locales/cs/comments.json
  • ghost/i18n/locales/da/comments.json
  • ghost/i18n/locales/de-CH/comments.json
  • ghost/i18n/locales/de/comments.json
  • ghost/i18n/locales/el/comments.json
  • ghost/i18n/locales/en/comments.json
  • ghost/i18n/locales/eo/comments.json
  • ghost/i18n/locales/es/comments.json
  • ghost/i18n/locales/et/comments.json
  • ghost/i18n/locales/eu/comments.json
  • ghost/i18n/locales/fa/comments.json
  • ghost/i18n/locales/fi/comments.json
  • ghost/i18n/locales/fr/comments.json
  • ghost/i18n/locales/gd/comments.json
  • ghost/i18n/locales/he/comments.json
  • ghost/i18n/locales/hi/comments.json
  • ghost/i18n/locales/hr/comments.json
  • ghost/i18n/locales/hu/comments.json
  • ghost/i18n/locales/id/comments.json
  • ghost/i18n/locales/is/comments.json
  • ghost/i18n/locales/it/comments.json
  • ghost/i18n/locales/ja/comments.json
  • ghost/i18n/locales/ko/comments.json
  • ghost/i18n/locales/kz/comments.json
  • ghost/i18n/locales/lt/comments.json
  • ghost/i18n/locales/lv/comments.json
  • ghost/i18n/locales/mk/comments.json
  • ghost/i18n/locales/mn/comments.json
  • ghost/i18n/locales/ms/comments.json
  • ghost/i18n/locales/nb/comments.json
  • ghost/i18n/locales/ne/comments.json
  • ghost/i18n/locales/nl/comments.json
  • ghost/i18n/locales/nn/comments.json
  • ghost/i18n/locales/pa/comments.json
  • ghost/i18n/locales/pl/comments.json
  • ghost/i18n/locales/pt-BR/comments.json
  • ghost/i18n/locales/pt/comments.json
  • ghost/i18n/locales/ro/comments.json
  • ghost/i18n/locales/ru/comments.json
  • ghost/i18n/locales/si/comments.json
  • ghost/i18n/locales/sk/comments.json
  • ghost/i18n/locales/sl/comments.json
  • ghost/i18n/locales/sq/comments.json
  • ghost/i18n/locales/sr-Cyrl/comments.json
  • ghost/i18n/locales/sr/comments.json
  • ghost/i18n/locales/sv/comments.json
  • ghost/i18n/locales/sw/comments.json
  • ghost/i18n/locales/ta/comments.json
  • ghost/i18n/locales/th/comments.json
  • ghost/i18n/locales/tr/comments.json
  • ghost/i18n/locales/uk/comments.json
  • ghost/i18n/locales/ur/comments.json
  • ghost/i18n/locales/uz/comments.json
  • ghost/i18n/locales/vi/comments.json
  • ghost/i18n/locales/zh-Hant/comments.json
  • ghost/i18n/locales/zh/comments.json
✅ Files skipped from review due to trivial changes (58)
  • ghost/i18n/locales/ur/comments.json
  • ghost/core/test/unit/server/data/schema/integrity.test.js
  • ghost/admin/package.json
  • apps/comments-ui/package.json
  • ghost/i18n/locales/af/comments.json
  • ghost/core/package.json
  • apps/posts/test/unit/views/comments/components/comment-header.test.tsx
  • ghost/i18n/locales/de/comments.json
  • apps/comments-ui/test/e2e/admin-moderation.test.ts
  • ghost/i18n/locales/bs/comments.json
  • ghost/i18n/locales/pa/comments.json
  • apps/comments-ui/src/app-context.ts
  • ghost/i18n/locales/mk/comments.json
  • apps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsx
  • ghost/i18n/locales/fi/comments.json
  • ghost/i18n/locales/sv/comments.json
  • ghost/i18n/locales/is/comments.json
  • apps/comments-ui/src/actions.ts
  • ghost/i18n/locales/ja/comments.json
  • ghost/i18n/locales/lt/comments.json
  • ghost/i18n/locales/pt/comments.json
  • ghost/i18n/locales/bg/comments.json
  • apps/comments-ui/test/unit/components/content/comment.test.jsx
  • ghost/i18n/locales/ta/comments.json
  • ghost/i18n/locales/et/comments.json
  • ghost/i18n/locales/nn/comments.json
  • apps/comments-ui/test/utils/fixtures.ts
  • ghost/i18n/locales/en/comments.json
  • ghost/i18n/locales/sr-Cyrl/comments.json
  • ghost/i18n/locales/it/comments.json
  • ghost/i18n/locales/sk/comments.json
  • ghost/i18n/locales/id/comments.json
  • ghost/i18n/locales/sl/comments.json
  • ghost/i18n/locales/fa/comments.json
  • ghost/i18n/locales/nb/comments.json
  • ghost/i18n/locales/lv/comments.json
  • ghost/i18n/locales/si/comments.json
  • ghost/core/core/frontend/src/admin-auth/message-handler.js
  • ghost/i18n/locales/el/comments.json
  • apps/comments-ui/test/unit/actions.test.js
  • ghost/i18n/locales/es/comments.json
  • ghost/i18n/locales/mn/comments.json
  • ghost/i18n/locales/da/comments.json
  • ghost/i18n/locales/ca/comments.json
  • ghost/i18n/locales/sq/comments.json
  • ghost/core/test/e2e-api/members-comments/comments.test.js
  • ghost/i18n/locales/uz/comments.json
  • ghost/i18n/locales/eu/comments.json
  • ghost/i18n/locales/hi/comments.json
  • ghost/i18n/locales/ro/comments.json
  • ghost/i18n/locales/uk/comments.json
  • ghost/i18n/locales/ko/comments.json
  • ghost/i18n/locales/pt-BR/comments.json
  • ghost/i18n/locales/eo/comments.json
  • ghost/i18n/locales/kz/comments.json
  • ghost/i18n/locales/ne/comments.json
  • ghost/i18n/locales/pl/comments.json
  • ghost/core/test/e2e-api/admin/comments.test.js
🚧 Files skipped from review as they are similar to previous changes (23)
  • ghost/i18n/locales/ru/comments.json
  • apps/posts/src/views/comments/components/comment-thread-list.tsx
  • ghost/i18n/locales/bn/comments.json
  • ghost/i18n/locales/nl/comments.json
  • ghost/i18n/locales/ms/comments.json
  • ghost/i18n/locales/hu/comments.json
  • ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
  • ghost/i18n/locales/sr/comments.json
  • ghost/i18n/locales/he/comments.json
  • ghost/i18n/locales/vi/comments.json
  • ghost/core/core/server/api/endpoints/comments.js
  • ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js
  • ghost/i18n/locales/th/comments.json
  • apps/admin-x-framework/src/api/comments.ts
  • apps/comments-ui/src/components/content/comment.tsx
  • ghost/i18n/locales/cs/comments.json
  • ghost/i18n/locales/de-CH/comments.json
  • ghost/i18n/locales/sw/comments.json
  • apps/comments-ui/src/utils/admin-api.ts
  • ghost/i18n/locales/zh-Hant/comments.json
  • apps/comments-ui/src/components/content/context-menus/admin-context-menu.tsx
  • ghost/i18n/locales/zh/comments.json
  • ghost/i18n/locales/context.json

@JohnONolan JohnONolan force-pushed the codex/pinned-comments branch 2 times, most recently from cceb344 to e14e945 Compare May 3, 2026 01:25
Staff need a way to elevate important discussion without changing admin moderation order. This adds top-level comment pinning with public pinned-first ordering, staff pin/unpin controls, visual pinned badges, API support, schema changes, and focused coverage for the backend and UI paths.
@JohnONolan JohnONolan force-pushed the codex/pinned-comments branch from e14e945 to c386fa2 Compare May 3, 2026 01:36
@codecov
Copy link
Copy Markdown

codecov Bot commented May 3, 2026

Codecov Report

❌ Patch coverage is 99.28571% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 73.19%. Comparing base (0c4d441) to head (e6b2a2a).

Files with missing lines Patch % Lines
...re/server/services/comments/comments-controller.js 96.55% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #27653      +/-   ##
==========================================
+ Coverage   73.17%   73.19%   +0.01%     
==========================================
  Files        1561     1561              
  Lines      127051   127168     +117     
  Branches    15383    15420      +37     
==========================================
+ Hits        92970    93080     +110     
- Misses      33105    33130      +25     
+ Partials      976      958      -18     
Flag Coverage Δ
admin-tests 49.86% <ø> (-0.02%) ⬇️
e2e-tests 73.19% <99.28%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

ref #27653

New sites should allow members to comment without requiring staff to find and enable the setting first. When comments are explicitly set to off, the Admin sidebar now hides the Comments section so the navigation reflects the disabled state.
JohnONolan added a commit that referenced this pull request May 3, 2026
ref #27653

Comments are now enabled by default for new sites, so default email previews include the comment CTA. Updating these snapshots keeps the acceptance suite aligned with the new default.
JohnONolan added a commit that referenced this pull request May 3, 2026
ref #27653

Comments are now enabled by default for new sites, so default email previews and settings responses changed. Updating these snapshots keeps the acceptance suite aligned with the new default.
@JohnONolan JohnONolan force-pushed the codex/pinned-comments branch from ef70401 to 01ae1ea Compare May 3, 2026 02:29
JohnONolan added a commit that referenced this pull request May 3, 2026
ref #27653

Comments are now enabled by default for new sites, so default email previews, settings responses, and Content API responses changed. Updating these snapshots keeps the acceptance suite aligned with the new default.
@JohnONolan JohnONolan force-pushed the codex/pinned-comments branch from 01ae1ea to 53dcc55 Compare May 3, 2026 02:39
JohnONolan added a commit that referenced this pull request May 3, 2026
ref #27653

Comments are now enabled by default for new sites, so default email previews, email card rendering, settings responses, and Content API responses changed. Updating these snapshots keeps CI aligned with the new default.
@JohnONolan JohnONolan force-pushed the codex/pinned-comments branch from 53dcc55 to 07e7dfa Compare May 3, 2026 02:51
JohnONolan added a commit to JohnONolan/Ghost that referenced this pull request May 3, 2026
ref TryGhost#27653

Comments are now enabled by default for new sites, so default email previews, email card rendering, settings responses, and Content API responses changed. Updating these snapshots keeps CI aligned with the new default.
@JohnONolan JohnONolan force-pushed the codex/pinned-comments branch from 07e7dfa to 2ae90c0 Compare May 3, 2026 03:10
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 3, 2026

E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 25268138056 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 3, 2026

E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 25268472078 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

JohnONolan added a commit that referenced this pull request May 3, 2026
ref #27653

Comments are now enabled by default for new sites, so default email previews, email card rendering, settings responses, and Content API responses changed. Updating these snapshots keeps CI aligned with the new default.
@JohnONolan JohnONolan force-pushed the codex/pinned-comments branch from 2ae90c0 to e222a97 Compare May 3, 2026 03:37
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 3, 2026

E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 25268954611 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

JohnONolan added a commit that referenced this pull request May 3, 2026
ref #27653

Comments are now enabled by default for new sites, so default email previews, email card rendering, settings responses, and Content API responses changed. Updating these snapshots keeps CI aligned with the new default.
@JohnONolan JohnONolan force-pushed the codex/pinned-comments branch from e222a97 to 73a02bb Compare May 3, 2026 04:02
@JohnONolan JohnONolan requested a review from 9larsons as a code owner May 3, 2026 04:02
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 3, 2026

E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 25269390196 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

ref #27653

Comments are now enabled by default for new sites, so default email previews, email card rendering, settings responses, and Content API responses changed. Updating these snapshots keeps CI aligned with the new default.
@JohnONolan JohnONolan force-pushed the codex/pinned-comments branch from 73a02bb to e6b2a2a Compare May 3, 2026 04:44
@cathysarisky
Copy link
Copy Markdown
Member

I'm curious - what's the rationale behind having comments on by default? Probably half the folks who do not want comments because they're a moderation headache and potential spam source. Some of the other half are very interested in comments as an engagement mechanism, so improving comments is awesome -- I'm just not sure that making comments opt-out is desirable. It's a lot easier to tell someone how to turn on commenting than to tell someone how to clear out a bunch of spammy comments that they didn't know were enabled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

affects:i18n migration [pull request] Includes migration for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants