diff --git a/package-lock.json b/package-lock.json index cdef413604d4b..4bccc97faab10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4473,7 +4473,6 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "optional": true, "requires": { "prr": "1.0.1" } @@ -9478,8 +9477,7 @@ "natives": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.4.tgz", - "integrity": "sha512-Q29yeg9aFKwhLVdkTAejM/HvYG0Y1Am1+HUkFQGn5k2j8GS+v60TVmZh6nujpEAj/qql+wGUrlryO8bF+b1jEg==", - "optional": true + "integrity": "sha512-Q29yeg9aFKwhLVdkTAejM/HvYG0Y1Am1+HUkFQGn5k2j8GS+v60TVmZh6nujpEAj/qql+wGUrlryO8bF+b1jEg==" }, "natural-compare": { "version": "1.4.0", @@ -11211,8 +11209,7 @@ "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "optional": true + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" }, "pseudomap": { "version": "1.0.2", diff --git a/packages/rocketchat-api/server/v1/chat.js b/packages/rocketchat-api/server/v1/chat.js index 3bfaacea2907d..d6d06997d588f 100644 --- a/packages/rocketchat-api/server/v1/chat.js +++ b/packages/rocketchat-api/server/v1/chat.js @@ -274,7 +274,7 @@ RocketChat.API.v1.addRoute('chat.react', { authRequired: true }, { throw new Meteor.Error('error-emoji-param-not-provided', 'The required "emoji" param is missing.'); } - Meteor.runAsUser(this.userId, () => Meteor.call('setReaction', emoji, msg._id)); + Meteor.runAsUser(this.userId, () => Meteor.call('setReaction', emoji, msg._id, this.bodyParams.shouldReact)); return RocketChat.API.v1.success(); } diff --git a/packages/rocketchat-reactions/setReaction.js b/packages/rocketchat-reactions/setReaction.js index 89df6b8139f6e..2cf5a8c7bdc79 100644 --- a/packages/rocketchat-reactions/setReaction.js +++ b/packages/rocketchat-reactions/setReaction.js @@ -1,8 +1,16 @@ /* globals msgStream */ import _ from 'underscore'; +const removeUserReaction = (message, reaction, username) => { + message.reactions[reaction].usernames.splice(message.reactions[reaction].usernames.indexOf(username), 1); + if (message.reactions[reaction].usernames.length === 0) { + delete message.reactions[reaction]; + } + return message; +}; + Meteor.methods({ - setReaction(reaction, messageId) { + setReaction(reaction, messageId, shouldReact) { if (!Meteor.userId()) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'setReaction' }); } @@ -39,12 +47,12 @@ Meteor.methods({ return false; } - if (message.reactions && message.reactions[reaction] && message.reactions[reaction].usernames.indexOf(user.username) !== -1) { - message.reactions[reaction].usernames.splice(message.reactions[reaction].usernames.indexOf(user.username), 1); - - if (message.reactions[reaction].usernames.length === 0) { - delete message.reactions[reaction]; - } + const userAlreadyReacted = Boolean(message.reactions) && message.reactions[reaction] && message.reactions[reaction].usernames.indexOf(user.username) !== -1; + if (userAlreadyReacted === shouldReact) { + return; + } + if (userAlreadyReacted) { + removeUserReaction(message, reaction, user.username); if (_.isEmpty(message.reactions)) { delete message.reactions; diff --git a/tests/end-to-end/api/05-chat.js b/tests/end-to-end/api/05-chat.js index a4f56aec3fc13..07736412471b0 100644 --- a/tests/end-to-end/api/05-chat.js +++ b/tests/end-to-end/api/05-chat.js @@ -363,8 +363,22 @@ describe('[Chat]', function() { .end(done); }); }); - - describe('/chat.react', () => { + describe('[/chat.react]', () => { + it('should return statusCode: 200 and success when try unreact a message that\'s no reacted yet', (done) => { + request.post(api('chat.react')) + .set(credentials) + .send({ + emoji: ':squid:', + messageId: message._id, + shouldReact: false + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); it('should react a message successfully', (done) => { request.post(api('chat.react')) .set(credentials) @@ -394,7 +408,51 @@ describe('[Chat]', function() { }) .end(done); }); - + it('should return statusCode: 200 and success when try react a message that\'s already reacted', (done) => { + request.post(api('chat.react')) + .set(credentials) + .send({ + emoji: ':squid:', + messageId: message._id, + shouldReact: true + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('should return statusCode: 200 when unreact a message with flag, shouldReact: false', (done) => { + request.post(api('chat.react')) + .set(credentials) + .send({ + emoji: ':squid:', + messageId: message._id, + shouldReact: false + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('should return statusCode: 200 when react a message with flag, shouldReact: true', (done) => { + request.post(api('chat.react')) + .set(credentials) + .send({ + emoji: ':squid:', + messageId: message._id, + shouldReact: true + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); it('should return statusCode: 200 when the emoji is valid and has no colons', (done) => { request.post(api('chat.react')) .set(credentials) @@ -409,7 +467,6 @@ describe('[Chat]', function() { }) .end(done); }); - it('should return statusCode: 200 for reaction property when the emoji is valid', (done) => { request.post(api('chat.react')) .set(credentials)