From 9beb4668ca48a0e68fa5f5e210a10bc4d214c110 Mon Sep 17 00:00:00 2001 From: tsm-odoo Date: Thu, 22 Jun 2023 13:59:53 +0000 Subject: [PATCH] [IMP] im_livechat: improve feedback messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR improve feedback messages: - Visitor has left the conversation: as note - Rating message: as note - Rating message: Use same emojis as for customer - Rating reason in same message as the emoji, without writing "reason" - Feedback done in 1 rpc instead of 3 part of task-3332872 closes odoo/odoo#125925 Signed-off-by: Alexandre KΓΌhn (aku) --- addons/im_livechat/__manifest__.py | 1 + addons/im_livechat/controllers/main.py | 17 +++++++++++++ addons/im_livechat/models/discuss_channel.py | 11 ++++++--- .../static/src/core_ui/message.scss | 3 +++ .../static/src/embed/core/livechat_service.js | 19 +-------------- .../tests/embed/feedback_panel_tests.js | 24 ++++--------------- .../mock_server/models/discuss_channel.js | 2 +- .../mail/static/src/core/common/thread.scss | 6 +++++ addons/mail/static/src/core/common/thread.xml | 4 ++-- .../models/discuss_channel.py | 4 ++-- addons/website_livechat/tests/common.py | 6 ----- .../tests/test_livechat_basic_flow.py | 10 ++++---- .../tests/test_livechat_request.py | 4 ++-- 13 files changed, 52 insertions(+), 59 deletions(-) create mode 100644 addons/im_livechat/static/src/core_ui/message.scss diff --git a/addons/im_livechat/__manifest__.py b/addons/im_livechat/__manifest__.py index 27c0768cfca..331f650699f 100644 --- a/addons/im_livechat/__manifest__.py +++ b/addons/im_livechat/__manifest__.py @@ -75,6 +75,7 @@ Help your customers with this chat, and analyse their feedback. 'im_livechat/static/src/chat_window/**/*', 'im_livechat/static/src/composer/**/*', 'im_livechat/static/src/core/**/*', + 'im_livechat/static/src/core_ui/**/*', 'im_livechat/static/src/discuss_app/**/*', 'im_livechat/static/src/messaging_menu/**/*', 'im_livechat/static/src/web/**/*', diff --git a/addons/im_livechat/controllers/main.py b/addons/im_livechat/controllers/main.py index 5b7455e63e1..53f2282776f 100644 --- a/addons/im_livechat/controllers/main.py +++ b/addons/im_livechat/controllers/main.py @@ -163,6 +163,22 @@ class LivechatController(http.Controller): persisted=persisted ) + def _post_feedback_message(self, channel, rating, reason): + body = Markup(''' +
+ %(rating)s: rating
%(reason)s +
+ ''') % { + 'rating': _('Rating'), + 'rating_url': rating.rating_image_url, + 'reason': reason, + } + channel.message_post( + body=body, + message_type='notification', + subtype_xmlid='mail.mt_comment' + ) + @http.route('/im_livechat/feedback', type='json', auth='public', cors="*") def feedback(self, uuid, rate, reason=None, **kwargs): channel = request.env['discuss.channel'].sudo().search([('uuid', '=', uuid)], limit=1) @@ -189,6 +205,7 @@ class LivechatController(http.Controller): else: rating = channel.rating_ids[0] rating.write(values) + self._post_feedback_message(channel, rating, reason) return rating.id return False diff --git a/addons/im_livechat/models/discuss_channel.py b/addons/im_livechat/models/discuss_channel.py index 2bca787f580..7ee5e168b1c 100644 --- a/addons/im_livechat/models/discuss_channel.py +++ b/addons/im_livechat/models/discuss_channel.py @@ -120,7 +120,7 @@ class DiscussChannel(models.Model): }) def _get_visitor_leave_message(self, operator=False, cancel=False): - return _('Visitor has left the conversation.') + return _('Visitor left the conversation.') def _close_livechat_session(self, **kwargs): """ Set deactivate the livechat channel and notify (the operator) the reason of closing the session.""" @@ -131,8 +131,13 @@ class DiscussChannel(models.Model): if not self.message_ids: return # Notify that the visitor has left the conversation - self.message_post(author_id=self.env.ref('base.partner_root').id, - body=self._get_visitor_leave_message(**kwargs), message_type='comment', subtype_xmlid='mail.mt_comment') + self.message_post( + author_id=self.env.ref('base.partner_root').id, + body=Markup('
%s
') + % self._get_visitor_leave_message(**kwargs), + message_type='notification', + subtype_xmlid='mail.mt_comment' + ) # Rating Mixin diff --git a/addons/im_livechat/static/src/core_ui/message.scss b/addons/im_livechat/static/src/core_ui/message.scss new file mode 100644 index 00000000000..d3deeb86586 --- /dev/null +++ b/addons/im_livechat/static/src/core_ui/message.scss @@ -0,0 +1,3 @@ +.o_livechat_emoji_rating { + width: 1.5rem; +} diff --git a/addons/im_livechat/static/src/embed/core/livechat_service.js b/addons/im_livechat/static/src/embed/core/livechat_service.js index 8315b372bbf..11c9d30747c 100644 --- a/addons/im_livechat/static/src/embed/core/livechat_service.js +++ b/addons/im_livechat/static/src/embed/core/livechat_service.js @@ -5,7 +5,6 @@ import { reactive } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; import { Deferred } from "@web/core/utils/concurrency"; -import { sprintf } from "@web/core/utils/strings"; import { session } from "@web/session"; /** @@ -21,12 +20,6 @@ export const RATING = Object.freeze({ BAD: 1, }); -export const RATING_TO_EMOJI = { - [RATING.GOOD]: "😊", - [RATING.OK]: "😐", - [RATING.BAD]: "😞", -}; - export const SESSION_STATE = Object.freeze({ NONE: "NONE", CREATED: "CREATED", @@ -172,17 +165,7 @@ export class LivechatService { * @param {string} reason */ async sendFeedback(uuid, rate, reason) { - await this.rpc("/im_livechat/feedback", { reason, rate, uuid }); - await this.rpc("/im_livechat/chat_post", { - uuid, - message_content: sprintf(_t("Rating: %s"), RATING_TO_EMOJI[rate]), - }); - if (reason) { - await this.rpc("/im_livechat/chat_post", { - uuid, - message_content: sprintf(_t("Rating reason: %s"), reason), - }); - } + return this.rpc("/im_livechat/feedback", { reason, rate, uuid }); } /** diff --git a/addons/im_livechat/static/tests/embed/feedback_panel_tests.js b/addons/im_livechat/static/tests/embed/feedback_panel_tests.js index 91235c474d2..4d9c897112d 100644 --- a/addons/im_livechat/static/tests/embed/feedback_panel_tests.js +++ b/addons/im_livechat/static/tests/embed/feedback_panel_tests.js @@ -2,7 +2,7 @@ import { startServer } from "@bus/../tests/helpers/mock_python_environment"; -import { RATING, RATING_TO_EMOJI } from "@im_livechat/embed/core/livechat_service"; +import { RATING } from "@im_livechat/embed/core/livechat_service"; import { loadDefaultConfig, start } from "@im_livechat/../tests/embed/helper/test_utils"; import { afterNextRender, click, insertText } from "@mail/../tests/helpers/test_utils"; @@ -47,7 +47,6 @@ QUnit.test("Close without feedback", async (assert) => { QUnit.test("Feedback with rating and comment", async (assert) => { await startServer(); await loadDefaultConfig(); - let messageCount = 0; await start({ mockRPC(route, args) { if (route === "/im_livechat/visitor_leave_session") { @@ -55,18 +54,8 @@ QUnit.test("Feedback with rating and comment", async (assert) => { } if (route === "/im_livechat/feedback") { assert.step(route); - } - if (route === "/im_livechat/chat_post") { - if (messageCount === 1) { - assert.strictEqual( - args.message_content, - `Rating: ${RATING_TO_EMOJI[RATING.GOOD]}` - ); - } else if (messageCount === 2) { - assert.strictEqual(args.message_content, "Rating reason: Good job!"); - } - messageCount++; - assert.step(route); + assert.ok(args.reason.includes("Good job!")); + assert.strictEqual(args.rate, RATING.GOOD); } }, }); @@ -74,18 +63,13 @@ QUnit.test("Feedback with rating and comment", async (assert) => { assert.containsOnce($, ".o-mail-ChatWindow"); await insertText(".o-mail-Composer-input", "Hello World!"); await afterNextRender(() => triggerHotkey("Enter")); - assert.verifySteps(["/im_livechat/chat_post"]); await click("[title='Close Chat Window']"); assert.verifySteps(["/im_livechat/visitor_leave_session"]); await click(`img[data-alt="${RATING.GOOD}"]`); await insertText("textarea[placeholder='Explain your note']", "Good job!"); await click("button:contains(Send)"); assert.containsOnce($, "p:contains(Thank you for your feedback)"); - assert.verifySteps([ - "/im_livechat/feedback", - "/im_livechat/chat_post", - "/im_livechat/chat_post", - ]); + assert.verifySteps(["/im_livechat/feedback"]); }); QUnit.test("Closing folded chat window should open it with feedback", async (assert) => { diff --git a/addons/im_livechat/static/tests/helpers/mock_server/models/discuss_channel.js b/addons/im_livechat/static/tests/helpers/mock_server/models/discuss_channel.js index e921db59c95..da9a785f403 100644 --- a/addons/im_livechat/static/tests/helpers/mock_server/models/discuss_channel.js +++ b/addons/im_livechat/static/tests/helpers/mock_server/models/discuss_channel.js @@ -71,6 +71,6 @@ patch(MockServer.prototype, "im_livechat/models/discuss_channel", { * Simulates `_get_visitor_leave_message` on `discuss.channel`. */ _mockDiscussChannel_getVisitorLeaveMessage() { - return "Visitor has left the conversation."; + return "Visitor left the conversation."; }, }); diff --git a/addons/mail/static/src/core/common/thread.scss b/addons/mail/static/src/core/common/thread.scss index a1de9a0c285..80ab34e1c4a 100644 --- a/addons/mail/static/src/core/common/thread.scss +++ b/addons/mail/static/src/core/common/thread.scss @@ -33,3 +33,9 @@ .o-mail-NotificationMessage p { margin-bottom: 0; } + +.o-mail-NotificationMessage:has(.o_hide_author) { + & .o-mail-NotificationMessage-author { + display: none!important; + } +} diff --git a/addons/mail/static/src/core/common/thread.xml b/addons/mail/static/src/core/common/thread.xml index 93839f456e2..a350737a885 100644 --- a/addons/mail/static/src/core/common/thread.xml +++ b/addons/mail/static/src/core/common/thread.xml @@ -114,11 +114,11 @@ -
- +
diff --git a/addons/website_livechat/models/discuss_channel.py b/addons/website_livechat/models/discuss_channel.py index dbf34c5a96d..eafb26562a7 100644 --- a/addons/website_livechat/models/discuss_channel.py +++ b/addons/website_livechat/models/discuss_channel.py @@ -60,10 +60,10 @@ class DiscussChannel(models.Model): def _get_visitor_leave_message(self, operator=False, cancel=False): name = _('The visitor') if not self.livechat_visitor_id else self.livechat_visitor_id.display_name if cancel: - message = _("""%s has started a conversation with %s. + message = _("""%s started a conversation with %s. The chat request has been canceled.""") % (name, operator or _('an operator')) else: - message = _('%s has left the conversation.', name) + message = _('%s left the conversation.', name) return message diff --git a/addons/website_livechat/tests/common.py b/addons/website_livechat/tests/common.py index fe11c2b1045..de854f03b70 100644 --- a/addons/website_livechat/tests/common.py +++ b/addons/website_livechat/tests/common.py @@ -91,10 +91,4 @@ class TestLivechatCommon(tests.TransactionCase): res_model_id = self.env['ir.model'].sudo().search([('model', '=', channel._name)], limit=1).id rating = self.env['rating.rating'].search([('res_id', '=', channel.id), ('res_model_id', '=', res_model_id)]) self.assertEqual(rating.rating, rating_value, "The rating is not correct.") - - message = "Rating: %s" % rating_to_emoji[rating_value] - if reason: - message += " \n%s" % reason - - self._send_message(channel, visitor.display_name, message, author_id=False) self.assertEqual(len(channel.message_ids), channel_messages_count + 1) diff --git a/addons/website_livechat/tests/test_livechat_basic_flow.py b/addons/website_livechat/tests/test_livechat_basic_flow.py index ca8a94eb42b..14935cb92ce 100644 --- a/addons/website_livechat/tests/test_livechat_basic_flow.py +++ b/addons/website_livechat/tests/test_livechat_basic_flow.py @@ -77,19 +77,19 @@ class TestLivechatBasicFlowHttpCase(tests.HttpCase, TestLivechatCommon): channel._close_livechat_session() self.assertEqual(len(channel.message_ids), 4) - self.assertEqual(channel.message_ids[0].author_id, self.env.ref('base.partner_root'), "Odoobot must be the sender of the 'has left the conversation' message.") - self.assertEqual(channel.message_ids[0].body, "

%s has left the conversation.

" % self.visitor.display_name) + self.assertEqual(channel.message_ids[0].author_id, self.env.ref('base.partner_root'), "Odoobot must be the sender of the 'left the conversation' message.") + self.assertIn("%s left the conversation." % self.visitor.display_name, channel.message_ids[0].body) self.assertEqual(channel.livechat_active, False, "The livechat session must be inactive as the visitor sent his feedback.") def test_basic_flow_without_rating(self): channel = self._common_basic_flow() - # has left the conversation + # left the conversation channel._close_livechat_session() self.assertEqual(len(channel.message_ids), 3) self.assertEqual(channel.message_ids[0].author_id, self.env.ref('base.partner_root'), "Odoobot must be the author the message.") - self.assertEqual(channel.message_ids[0].body, "

%s has left the conversation.

" % self.visitor.display_name) - self.assertEqual(channel.livechat_active, False, "The livechat session must be inactive since visitor has left the conversation.") + self.assertIn("%s left the conversation." % self.visitor.display_name, channel.message_ids[0].body) + self.assertEqual(channel.livechat_active, False, "The livechat session must be inactive since visitor left the conversation.") def test_visitor_info_access_rights(self): channel = self._common_basic_flow() diff --git a/addons/website_livechat/tests/test_livechat_request.py b/addons/website_livechat/tests/test_livechat_request.py index e7e769d1be0..2f827fb57fd 100644 --- a/addons/website_livechat/tests/test_livechat_request.py +++ b/addons/website_livechat/tests/test_livechat_request.py @@ -66,8 +66,8 @@ class TestLivechatRequestHttpCase(tests.HttpCase, TestLivechatCommon): # Visitor Leave the conversation channel._close_livechat_session() self.assertEqual(len(channel.message_ids), 3) - self.assertEqual(channel.message_ids[0].author_id, self.env.ref('base.partner_root'), "Odoobot must be the sender of the 'has left the conversation' message.") - self.assertEqual(channel.message_ids[0].body, "

%s has left the conversation.

" % self.visitor.display_name) + self.assertEqual(channel.message_ids[0].author_id, self.env.ref('base.partner_root'), "Odoobot must be the sender of the 'left the conversation' message.") + self.assertIn("%s left the conversation." % self.visitor.display_name, channel.message_ids[0].body) self.assertEqual(channel.livechat_active, False, "The livechat session must be inactive as the visitor sent his feedback.") return channel