diff --git a/public/src/client/topic/votes.js b/public/src/client/topic/votes.js index 6a22118e70..59a34c86e1 100644 --- a/public/src/client/topic/votes.js +++ b/public/src/client/topic/votes.js @@ -8,15 +8,6 @@ define('forum/topic/votes', [ Votes.addVoteHandler = function () { components.get('topic').on('mouseenter', '[data-pid] [component="post/vote-count"]', loadDataAndCreateTooltip); - components.get('topic').on('mouseout', '[data-pid] [component="post/vote-count"]', function () { - var el = $(this).parent(); - el.on('shown.bs.tooltip', function () { - $('.tooltip').tooltip('destroy'); - el.off('shown.bs.tooltip'); - }); - - $('.tooltip').tooltip('destroy'); - }); }; function loadDataAndCreateTooltip(e) { @@ -26,18 +17,14 @@ define('forum/topic/votes', [ var el = $this.parent(); var pid = el.parents('[data-pid]').attr('data-pid'); - $('.tooltip').tooltip('destroy'); - $this.off('mouseenter', loadDataAndCreateTooltip); - socket.emit('posts.getUpvoters', [pid], function (err, data) { if (err) { return app.alertError(err.message); } if (data.length) { - createTooltip(el, data[0]); + createTooltip($this, data[0]); } - $this.off('mouseenter').on('mouseenter', loadDataAndCreateTooltip); }); return false; } @@ -46,7 +33,8 @@ define('forum/topic/votes', [ function doCreateTooltip(title) { el.attr('title', title).tooltip('fixTitle').tooltip('show'); } - var usernames = data.usernames; + var usernames = data.usernames + .filter(name => name !== '[[global:former_user]]'); if (!usernames.length) { return; } @@ -70,10 +58,10 @@ define('forum/topic/votes', [ const method = currentState ? 'del' : 'put'; api[method](`/posts/${post.attr('data-pid')}/vote`, { delta: delta, - }, undefined, (err) => { - app.alertError(err.status.message); + }).catch((err) => { + app.alertError(err.message); - if (err.status.message === '[[error:not-logged-in]]') { + if (err.message === '[[error:not-logged-in]]') { ajaxify.go('login'); } }); diff --git a/public/src/modules/api.js b/public/src/modules/api.js index ef3f4f33b5..7b01f367c1 100644 --- a/public/src/modules/api.js +++ b/public/src/modules/api.js @@ -22,13 +22,7 @@ define('api', () => { ev.responseJSON.status.message : ev.responseJSON.error; - const error = new Error(errMessage || ev.statusText); - - if (!utils.hasLanguageKey(error.message)) { - app.alertError(error.message); - } - - cb(error); + cb(new Error(errMessage || ev.statusText)); }); } diff --git a/src/api/helpers.js b/src/api/helpers.js index 13aab1b07c..da4d76fddc 100644 --- a/src/api/helpers.js +++ b/src/api/helpers.js @@ -3,8 +3,11 @@ const url = require('url'); const user = require('../user'); const topics = require('../topics'); +const posts = require('../posts'); const privileges = require('../privileges'); +const plugins = require('../plugins'); const socketHelpers = require('../socket.io/helpers'); +const websockets = require('../socket.io'); const events = require('../events'); // creates a slimmed down version of the request object @@ -34,7 +37,6 @@ exports.buildReqObject = (req, payload) => { }; }; - exports.doTopicAction = async function (action, event, caller, { tids }) { if (!Array.isArray(tids)) { throw new Error('[[error:invalid-tid]]'); @@ -73,3 +75,59 @@ async function logTopicAction(action, req, tid, title) { title: String(title), }); } + +exports.postCommand = async function (caller, command, eventName, notification, data) { + if (!caller.uid) { + throw new Error('[[error:not-logged-in]]'); + } + + if (!data || !data.pid) { + throw new Error('[[error:invalid-data]]'); + } + + if (!data.room_id) { + throw new Error('[[error:invalid-room-id, ' + data.room_id + ' ]]'); + } + const [exists, deleted] = await Promise.all([ + posts.exists(data.pid), + posts.getPostField(data.pid, 'deleted'), + ]); + + if (!exists) { + throw new Error('[[error:invalid-pid]]'); + } + + if (deleted) { + throw new Error('[[error:post-deleted]]'); + } + + /* + hooks: + filter:post.upvote + filter:post.downvote + filter:post.unvote + filter:post.bookmark + filter:post.unbookmark + */ + const filteredData = await plugins.fireHook('filter:post.' + command, { + data: data, + uid: caller.uid, + }); + return await executeCommand(caller, command, eventName, notification, filteredData.data); +}; + +async function executeCommand(caller, command, eventName, notification, data) { + const result = await posts[command](data.pid, caller.uid); + if (result && eventName) { + websockets.in('uid_' + caller.uid).emit('posts.' + command, result); + websockets.in(data.room_id).emit('event:' + eventName, result); + } + if (result && command === 'upvote') { + socketHelpers.upvote(result, notification); + } else if (result && notification) { + socketHelpers.sendNotificationToPostOwner(data.pid, caller.uid, command, notification); + } else if (result && command === 'unvote') { + socketHelpers.rescindUpvoteNotification(data.pid, caller.uid); + } + return result; +} diff --git a/src/api/posts.js b/src/api/posts.js index 8c7d225a9e..c02e29c2a0 100644 --- a/src/api/posts.js +++ b/src/api/posts.js @@ -165,3 +165,22 @@ async function isMainAndLastPost(pid) { }; } +postsAPI.upvote = async function (caller, data) { + return await apiHelpers.postCommand(caller, 'upvote', 'voted', 'notifications:upvoted_your_post_in', data); +}; + +postsAPI.downvote = async function (caller, data) { + return await apiHelpers.postCommand(caller, 'downvote', 'voted', '', data); +}; + +postsAPI.unvote = async function (caller, data) { + return await apiHelpers.postCommand(caller, 'unvote', 'voted', '', data); +}; + +postsAPI.bookmark = async function (caller, data) { + return await apiHelpers.postCommand(caller, 'bookmark', 'bookmarked', '', data); +}; + +postsAPI.unbookmark = async function (caller, data) { + return await apiHelpers.postCommand(caller, 'unbookmark', 'bookmarked', '', data); +}; diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index 1712e48eac..54b336aa18 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -11,9 +11,7 @@ const categories = require('../categories'); const plugins = require('../plugins'); const meta = require('../meta'); const middleware = require('../middleware'); -const translator = require('../translator'); -const isLanguageKey = /^\[\[[\w.\-_:]+]]$/; const helpers = module.exports; helpers.noScriptErrors = async function (req, res, error, httpStatus) { @@ -349,12 +347,7 @@ helpers.formatApiResponse = async (statusCode, res, payload) => { response: payload || {}, }); } else if (payload instanceof Error) { - let message = ''; - if (isLanguageKey.test(payload.message)) { - message = await translator.translate(payload.message, 'en-GB'); - } else { - message = payload.message; - } + const message = payload.message; // Update status code based on some common error codes switch (payload.message) { diff --git a/src/controllers/write/posts.js b/src/controllers/write/posts.js index 2448c6f3c6..1a73cb7b1b 100644 --- a/src/controllers/write/posts.js +++ b/src/controllers/write/posts.js @@ -5,7 +5,6 @@ const posts = require('../../posts'); const api = require('../../api'); const helpers = require('../helpers'); const apiHelpers = require('../../api/helpers'); -const socketPostHelpers = require('../../socket.io/posts/helpers'); // eehhh... const Posts = module.exports; @@ -37,43 +36,36 @@ Posts.delete = async (req, res) => { async function mock(req) { 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 }; - - return { data, socketMock }; + return { pid: req.params.pid, room_id: `topic_${tid}` }; } Posts.vote = async (req, res) => { - const { data, socketMock } = await mock(req); - + const data = await mock(req); if (req.body.delta > 0) { - await socketPostHelpers.postCommand(socketMock, 'upvote', 'voted', 'notifications:upvoted_your_post_in', data); + await api.posts.upvote(req, data); } else if (req.body.delta < 0) { - await socketPostHelpers.postCommand(socketMock, 'downvote', 'voted', '', data); + await api.posts.downvote(req, data); } else { - await socketPostHelpers.postCommand(socketMock, 'unvote', 'voted', '', data); + await api.posts.unvote(req, data); } helpers.formatApiResponse(200, res); }; Posts.unvote = async (req, res) => { - const { data, socketMock } = await mock(req); - - await socketPostHelpers.postCommand(socketMock, 'unvote', 'voted', '', data); + const data = await mock(req); + await api.posts.unvote(req, data); helpers.formatApiResponse(200, res); }; Posts.bookmark = async (req, res) => { - const { data, socketMock } = await mock(req); - - await socketPostHelpers.postCommand(socketMock, 'bookmark', 'bookmarked', '', data); + const data = await mock(req); + await api.posts.bookmark(req, data); helpers.formatApiResponse(200, res); }; Posts.unbookmark = async (req, res) => { - const { data, socketMock } = await mock(req); - - await socketPostHelpers.postCommand(socketMock, 'unbookmark', 'bookmarked', '', data); + const data = await mock(req); + await api.posts.unbookmark(req, data); helpers.formatApiResponse(200, res); }; diff --git a/src/socket.io/posts/bookmarks.js b/src/socket.io/posts/bookmarks.js index 4330286b5a..4162047971 100644 --- a/src/socket.io/posts/bookmarks.js +++ b/src/socket.io/posts/bookmarks.js @@ -1,17 +1,16 @@ 'use strict'; - -const helpers = require('./helpers'); const sockets = require('..'); +const api = require('../../api'); module.exports = function (SocketPosts) { SocketPosts.bookmark = async function (socket, data) { sockets.warnDeprecated(socket, 'PUT /api/v3/posts/:pid/bookmark'); - return await helpers.postCommand(socket, 'bookmark', 'bookmarked', '', data); + return await api.posts.bookmark(socket, data); }; SocketPosts.unbookmark = async function (socket, data) { sockets.warnDeprecated(socket, 'DELETE /api/v3/posts/:pid/bookmark'); - return await helpers.postCommand(socket, 'unbookmark', 'bookmarked', '', data); + return await api.posts.unbookmark(socket, data); }; }; diff --git a/src/socket.io/posts/helpers.js b/src/socket.io/posts/helpers.js deleted file mode 100644 index bce4ddb4c8..0000000000 --- a/src/socket.io/posts/helpers.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; - - -const posts = require('../../posts'); -const plugins = require('../../plugins'); -const websockets = require('../index'); -const socketHelpers = require('../helpers'); - -const helpers = module.exports; - -helpers.postCommand = async function (socket, command, eventName, notification, data) { - if (!socket.uid) { - throw new Error('[[error:not-logged-in]]'); - } - - if (!data || !data.pid) { - throw new Error('[[error:invalid-data]]'); - } - - if (!data.room_id) { - throw new Error('[[error:invalid-room-id, ' + data.room_id + ' ]]'); - } - const [exists, deleted] = await Promise.all([ - posts.exists(data.pid), - posts.getPostField(data.pid, 'deleted'), - ]); - - if (!exists) { - throw new Error('[[error:invalid-pid]]'); - } - - if (deleted) { - throw new Error('[[error:post-deleted]]'); - } - - /* - hooks: - filter:post.upvote - filter:post.downvote - filter:post.unvote - filter:post.bookmark - filter:post.unbookmark - */ - const filteredData = await plugins.fireHook('filter:post.' + command, { data: data, uid: socket.uid }); - return await executeCommand(socket, command, eventName, notification, filteredData.data); -}; - -async function executeCommand(socket, command, eventName, notification, data) { - const result = await posts[command](data.pid, socket.uid); - if (result && eventName) { - websockets.in('uid_' + socket.uid).emit('posts.' + command, result); - websockets.in(data.room_id).emit('event:' + eventName, result); - } - if (result && command === 'upvote') { - socketHelpers.upvote(result, notification); - } else if (result && notification) { - socketHelpers.sendNotificationToPostOwner(data.pid, socket.uid, command, notification); - } else if (result && command === 'unvote') { - socketHelpers.rescindUpvoteNotification(data.pid, socket.uid); - } - return result; -} diff --git a/src/socket.io/posts/votes.js b/src/socket.io/posts/votes.js index 23b0467c5f..47dfaf7adb 100644 --- a/src/socket.io/posts/votes.js +++ b/src/socket.io/posts/votes.js @@ -5,7 +5,7 @@ const user = require('../../user'); const posts = require('../../posts'); const privileges = require('../../privileges'); const meta = require('../../meta'); -const helpers = require('./helpers'); +const api = require('../../api'); const sockets = require('..'); @@ -64,16 +64,16 @@ module.exports = function (SocketPosts) { SocketPosts.upvote = async function (socket, data) { sockets.warnDeprecated(socket, 'PUT /api/v3/posts/:pid/vote'); - return await helpers.postCommand(socket, 'upvote', 'voted', 'notifications:upvoted_your_post_in', data); + return await api.posts.upvote(socket, data); }; SocketPosts.downvote = async function (socket, data) { sockets.warnDeprecated(socket, 'PUT /api/v3/posts/:pid/vote'); - return await helpers.postCommand(socket, 'downvote', 'voted', '', data); + return await api.posts.downvote(socket, data); }; SocketPosts.unvote = async function (socket, data) { sockets.warnDeprecated(socket, 'DELETE /api/v3/posts/:pid/vote'); - return await helpers.postCommand(socket, 'unvote', 'voted', '', data); + return await api.posts.unvote(socket, data); }; };