diff --git a/public/openapi/write.yaml b/public/openapi/write.yaml index 60ff8dde38..143b7ee3d3 100644 --- a/public/openapi/write.yaml +++ b/public/openapi/write.yaml @@ -826,6 +826,110 @@ paths: $ref: '#/components/schemas/Status' response: $ref: components/schemas/PostsObject.yaml#/PostsObject + delete: + tags: + - posts + summary: Purge a post + description: This operation purges a post. + responses: + '200': + description: Post successfully purged + content: + application/json: + schema: + type: object + properties: + status: + $ref: '#/components/schemas/Status' + response: + type: object + properties: {} + /posts/{pid}/state: + put: + tags: + - posts + summary: Restore a post + description: This operation restores a post. + responses: + '200': + description: Topic successfully restored + content: + application/json: + schema: + type: object + properties: + status: + $ref: '#/components/schemas/Status' + response: + type: object + properties: {} + delete: + tags: + - posts + summary: Deletes a post + description: This operation soft deletes a post. + responses: + '200': + description: Post successfully deleted + content: + application/json: + schema: + type: object + properties: + status: + $ref: '#/components/schemas/Status' + response: + type: object + properties: {} + /posts/{pid}/vote: + put: + tags: + - posts + summary: Vote on a post + description: This operation casts a vote on a post. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + delta: + type: number + description: Positive integer for upvote, negative integer for downvote (0 to unvote.) + example: + delta: 1 + responses: + '200': + description: Topic successfully upvoted + content: + application/json: + schema: + type: object + properties: + status: + $ref: '#/components/schemas/Status' + response: + type: object + properties: {} + delete: + tags: + - posts + summary: Unvote a post + description: This operation removes a pre-cast vote on a post. + responses: + '200': + description: Post successfully unvoted + content: + application/json: + schema: + type: object + properties: + status: + $ref: '#/components/schemas/Status' + response: + type: object + properties: {} components: schemas: Status: diff --git a/public/src/client/topic/postTools.js b/public/src/client/topic/postTools.js index 16a6b18fc4..9fb56d9735 100644 --- a/public/src/client/topic/postTools.js +++ b/public/src/client/topic/postTools.js @@ -109,11 +109,11 @@ define('forum/topic/postTools', [ }); postContainer.on('click', '[component="post/upvote"]', function () { - return votes.toggleVote($(this), '.upvoted', 'posts.upvote'); + return votes.toggleVote($(this), '.upvoted', 1); }); postContainer.on('click', '[component="post/downvote"]', function () { - return votes.toggleVote($(this), '.downvoted', 'posts.downvote'); + return votes.toggleVote($(this), '.downvoted', -1); }); postContainer.on('click', '[component="post/vote-count"]', function () { diff --git a/public/src/client/topic/votes.js b/public/src/client/topic/votes.js index 4c1a0e34fb..e6ef2b38d8 100644 --- a/public/src/client/topic/votes.js +++ b/public/src/client/topic/votes.js @@ -1,7 +1,7 @@ 'use strict'; -define('forum/topic/votes', ['components', 'translator', 'benchpress'], function (components, translator, Benchpress) { +define('forum/topic/votes', ['components', 'translator', 'benchpress', 'api'], function (components, translator, Benchpress, api) { var Votes = {}; Votes.addVoteHandler = function () { @@ -61,19 +61,17 @@ define('forum/topic/votes', ['components', 'translator', 'benchpress'], function } - Votes.toggleVote = function (button, className, method) { + Votes.toggleVote = function (button, className, delta) { var post = button.closest('[data-pid]'); var currentState = post.find(className).length; - socket.emit(currentState ? 'posts.unvote' : method, { - pid: post.attr('data-pid'), - room_id: 'topic_' + ajaxify.data.tid, - }, function (err) { - if (err) { - app.alertError(err.message); - } + const method = currentState ? 'del' : 'put'; + api[method](`/posts/${post.attr('data-pid')}/vote`, { + delta: delta, + }, undefined, (err) => { + app.alertError(err.status.message); - if (err && err.message === '[[error:not-logged-in]]') { + if (err.status.message === '[[error:not-logged-in]]') { ajaxify.go('login'); } }); diff --git a/src/controllers/write/posts.js b/src/controllers/write/posts.js index abe49a2be0..0acaf5e315 100644 --- a/src/controllers/write/posts.js +++ b/src/controllers/write/posts.js @@ -13,6 +13,7 @@ const utils = require('../../utils'); const helpers = require('../helpers'); const sockets = require('../../socket.io'); +const socketPostHelpers = require('../../socket.io/posts/helpers'); // eehhh... const socketTopics = require('../../socket.io/topics'); // eehhh... const Posts = module.exports; @@ -145,6 +146,31 @@ Posts.delete = async (req, res) => { helpers.formatApiResponse(200, res); }; +Posts.vote = async (req, res) => { + const tid = await posts.getPostField(req.params.pid, 'tid'); + const data = { pid: req.params.pid, room_id: `topic_${tid}` }; + const socketMock = { uid: req.user.uid }; + + if (req.body.delta > 0) { + await socketPostHelpers.postCommand(socketMock, 'upvote', 'voted', 'notifications:upvoted_your_post_in', data); + } else if (req.body.delta < 0) { + await socketPostHelpers.postCommand(socketMock, 'downvote', 'voted', '', data); + } else { + await socketPostHelpers.postCommand(socketMock, 'unvote', 'voted', '', data); + } + + helpers.formatApiResponse(200, res); +}; + +Posts.unvote = async (req, res) => { + const tid = await posts.getPostField(req.params.pid, 'tid'); + const data = { pid: req.params.pid, room_id: `topic_${tid}` }; + const socketMock = { uid: req.user.uid }; + + await socketPostHelpers.postCommand(socketMock, 'unvote', 'voted', '', data); + helpers.formatApiResponse(200, res); +}; + async function isMainAndLastPost(pid) { const [isMain, topicData] = await Promise.all([ posts.isMain(pid), diff --git a/src/routes/write/posts.js b/src/routes/write/posts.js index 9c0655d336..b28153881a 100644 --- a/src/routes/write/posts.js +++ b/src/routes/write/posts.js @@ -16,31 +16,8 @@ module.exports = function () { setupApiRoute(router, '/:pid/state', middleware, [...middlewares, middleware.assertPost], 'put', controllers.write.posts.restore); setupApiRoute(router, '/:pid/state', middleware, [...middlewares, middleware.assertPost], 'delete', controllers.write.posts.delete); - // app.route('/:pid/vote') - // .post(apiMiddleware.requireUser, function(req, res) { - // if (!utils.checkRequired(['delta'], req, res)) { - // return false; - // } - - // if (req.body.delta > 0) { - // posts.upvote(req.params.pid, req.user.uid, function(err, data) { - // errorHandler.handle(err, res, data); - // }) - // } else if (req.body.delta < 0) { - // posts.downvote(req.params.pid, req.user.uid, function(err, data) { - // errorHandler.handle(err, res, data); - // }) - // } else { - // posts.unvote(req.params.pid, req.user.uid, function(err, data) { - // errorHandler.handle(err, res, data); - // }) - // } - // }) - // .delete(apiMiddleware.requireUser, function(req, res) { - // posts.unvote(req.params.pid, req.user.uid, function(err, data) { - // errorHandler.handle(err, res, data); - // }) - // }); + setupApiRoute(router, '/:pid/vote', middleware, [...middlewares, middleware.checkRequired.bind(null, ['delta']), middleware.assertPost], 'put', controllers.write.posts.vote); + setupApiRoute(router, '/:pid/vote', middleware, [...middlewares, middleware.assertPost], 'delete', controllers.write.posts.unvote); // app.route('/:pid/bookmark') // .post(apiMiddleware.requireUser, function(req, res) { diff --git a/src/socket.io/posts/votes.js b/src/socket.io/posts/votes.js index 3e53a2435b..94c911aec0 100644 --- a/src/socket.io/posts/votes.js +++ b/src/socket.io/posts/votes.js @@ -7,6 +7,8 @@ const privileges = require('../../privileges'); const meta = require('../../meta'); const helpers = require('./helpers'); +const sockets = require('..'); + module.exports = function (SocketPosts) { SocketPosts.getVoters = async function (socket, data) { if (!data || !data.pid || !data.cid) { @@ -61,14 +63,17 @@ module.exports = function (SocketPosts) { }; SocketPosts.upvote = async function (socket, data) { + sockets.warnDeprecated(socket, 'PUT /api/v1/posts/:pid/vote'); return await helpers.postCommand(socket, 'upvote', 'voted', 'notifications:upvoted_your_post_in', data); }; SocketPosts.downvote = async function (socket, data) { + sockets.warnDeprecated(socket, 'PUT /api/v1/posts/:pid/vote'); return await helpers.postCommand(socket, 'downvote', 'voted', '', data); }; SocketPosts.unvote = async function (socket, data) { + sockets.warnDeprecated(socket, 'DELETE /api/v1/posts/:pid/vote'); return await helpers.postCommand(socket, 'unvote', 'voted', '', data); }; };