0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

Added Best Comment to top of API output (#21374)

ref PLG-220

- Added the ability to move the Best comment to the first entry when
order=best is passed as params.
This commit is contained in:
Ronald Langeveld 2024-10-23 09:48:16 +09:00 committed by GitHub
parent 1b468f333b
commit 1e8bb253bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 108 additions and 0 deletions

View file

@ -202,6 +202,21 @@ const Comment = ghostBookshelf.Model.extend({
return options; return options;
}, },
async findMostLikedComment(options = {}) {
let query = ghostBookshelf.knex('comments')
.select('comments.*')
.count('comment_likes.id as count__likes') // Counting likes for sorting
.leftJoin('comment_likes', 'comments.id', 'comment_likes.comment_id')
.groupBy('comments.id') // Group by comment ID to aggregate likes count
.orderBy('count__likes', 'desc') // Order by likes in descending order (most likes first)
.limit(1); // Limit to just 1 result
// Execute the query and get the result
const result = await query.first(); // Fetch the single top comment
const id = result && result.id;
// Fetch the comment model by ID
return this.findOne({id}, options);
},
async findPage(options) { async findPage(options) {
const {withRelated} = this.defaultRelations('findPage', options); const {withRelated} = this.defaultRelations('findPage', options);
@ -218,6 +233,16 @@ const Comment = ghostBookshelf.Model.extend({
await model.load(relationsToLoadIndividually, _.omit(options, 'withRelated')); await model.load(relationsToLoadIndividually, _.omit(options, 'withRelated'));
} }
// if options.order === 'best', we findMostLikedComment
// then we remove it from the result set and add it as the first element
if (options.order === 'best') {
const mostLikedComment = await this.findMostLikedComment(options);
if (mostLikedComment) {
result.data = result.data.filter(comment => comment.id !== mostLikedComment.id);
result.data.unshift(mostLikedComment);
}
}
return result; return result;
}, },

View file

@ -3314,6 +3314,55 @@ Object {
} }
`; `;
exports[`Comments API when commenting enabled for all when authenticated can show most liked comment first when order param = best 1: [headers] 1`] = `
Object {
"access-control-allow-origin": "*",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"x-cache-invalidate": "/api/members/comments/post/618ba1ffbe2896088840a6df/",
"x-powered-by": "Express",
}
`;
exports[`Comments API when commenting enabled for all when authenticated can show most liked comment first when order param = best 2: [body] 1`] = `
Object {
"comments": Array [
Object {
"count": Object {
"likes": Any<Number>,
"replies": 0,
},
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"edited_at": null,
"html": "<p>This is a comment</p>",
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"liked": Any<Boolean>,
"member": Object {
"avatar_image": null,
"expertise": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"name": "Egon Spengler",
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
},
"replies": Array [],
"status": "published",
},
],
}
`;
exports[`Comments API when commenting enabled for all when authenticated can show most liked comment first when order param = best 3: [headers] 1`] = `
Object {
"access-control-allow-origin": "*",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "367",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Comments API when commenting enabled for all when not authenticated Can browse all comments of a post (legacy) 1: [body] 1`] = ` exports[`Comments API when commenting enabled for all when not authenticated Can browse all comments of a post (legacy) 1: [body] 1`] = `
Object { Object {
"comments": Array [ "comments": Array [

View file

@ -510,6 +510,40 @@ describe('Comments API', function () {
should.not.exist(response.body.comments[0].unsubscribe_url); should.not.exist(response.body.comments[0].unsubscribe_url);
}); });
it('can show most liked comment first when order param = best', async function () {
await setupBrowseCommentsData();
const data = await membersAgent
.get(`/api/comments/post/${postId}`);
await dbFns.addLike({
comment_id: data.body.comments[1].id,
member_id: loggedInMember.id
});
const data2 = await membersAgent
.get(`/api/comments/post/${postId}/?order=best`)
.expectStatus(200);
should(data2.body.comments[0].id).eql(data.body.comments[1].id);
});
it('does not most liked comment first when order param and keeps normal order', async function () {
await setupBrowseCommentsData();
const data = await membersAgent
.get(`/api/comments/post/${postId}`);
await dbFns.addLike({
comment_id: data.body.comments[1].id,
member_id: loggedInMember.id
});
const data2 = await membersAgent
.get(`/api/comments/post/${postId}/`)
.expectStatus(200);
should(data2.body.comments[0].id).not.eql(data.body.comments[1].id);
});
it('Can reply to your own comment', async function () { it('Can reply to your own comment', async function () {
// Should not update last_seen_at or last_commented_at when both are already set to a value on the same day // Should not update last_seen_at or last_commented_at when both are already set to a value on the same day
const timezone = settingsCache.get('timezone'); const timezone = settingsCache.get('timezone');