From 4be75dd002869b87a0a02667b8a4319222207fe6 Mon Sep 17 00:00:00 2001 From: tsm-odoo Date: Tue, 12 Sep 2023 11:32:00 +0200 Subject: [PATCH] [IMP] im_livechat: allow visitors to receive calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit task-2900771 closes odoo/odoo#135109 Signed-off-by: Sébastien Theys (seb) --- addons/im_livechat/__manifest__.py | 1 + addons/im_livechat/controllers/__init__.py | 1 + addons/im_livechat/controllers/main.py | 8 +++-- addons/im_livechat/controllers/rtc.py | 32 +++++++++++++++++++ .../static/src/core/thread_model_patch.js | 4 +++ .../src/embed/core/disabled_features.js | 2 +- .../static/src/embed/core/livechat_service.js | 9 +++--- .../src/embed/core/messaging_service_patch.js | 3 +- .../static/src/embed/core/thread_actions.js | 32 +++++++++++++++++-- .../src/embed/core/thread_model_patch.js | 4 +++ .../static/tests/chat_window_patch_tests.js | 23 +------------ .../static/tests/discuss_patch_tests.js | 19 ----------- .../static/src/core/common/thread_model.js | 6 +++- 13 files changed, 89 insertions(+), 55 deletions(-) create mode 100644 addons/im_livechat/controllers/rtc.py diff --git a/addons/im_livechat/__manifest__.py b/addons/im_livechat/__manifest__.py index ac4db3b9825..39ac557b485 100644 --- a/addons/im_livechat/__manifest__.py +++ b/addons/im_livechat/__manifest__.py @@ -98,6 +98,7 @@ Help your customers with this chat, and analyse their feedback. 'web/static/lib/odoo_ui_icons/style.css', 'mail/static/src/core/common/**/*', 'mail/static/src/discuss/core/common/*', + 'mail/static/src/discuss/call/common/**', 'mail/static/src/discuss/typing/**/*', 'mail/static/src/utils/common/**/*', ('remove', 'mail/static/src/**/*.dark.scss'), diff --git a/addons/im_livechat/controllers/__init__.py b/addons/im_livechat/controllers/__init__.py index 9a273c80578..a07d2115090 100644 --- a/addons/im_livechat/controllers/__init__.py +++ b/addons/im_livechat/controllers/__init__.py @@ -6,5 +6,6 @@ from . import chatbot from . import link_preview from . import main from . import message_reaction +from . import rtc from . import thread from . import webclient diff --git a/addons/im_livechat/controllers/main.py b/addons/im_livechat/controllers/main.py index 73fce04ea67..b1d7da70bd4 100644 --- a/addons/im_livechat/controllers/main.py +++ b/addons/im_livechat/controllers/main.py @@ -275,14 +275,18 @@ class LivechatController(http.Controller): channel._email_livechat_transcript(email) @http.route('/im_livechat/visitor_leave_session', type='json', auth="public", cors="*") + @add_guest_to_context def visitor_leave_session(self, uuid): """ Called when the livechat visitor leaves the conversation. This will clean the chat request and warn the operator that the conversation is over. This allows also to re-send a new chat request to the visitor, as while the visitor is in conversation with an operator, it's not possible to send the visitor a chat request.""" discuss_channel = request.env['discuss.channel'].sudo().search([('uuid', '=', uuid)]) - if discuss_channel: - discuss_channel._close_livechat_session() + if not discuss_channel: + return + channel_member_sudo = request.env["discuss.channel.member"]._get_as_sudo_from_context_or_raise(channel_id=discuss_channel.id) + channel_member_sudo._rtc_leave_call() + discuss_channel._close_livechat_session() @http.route(['/im_livechat/chat_history'], type="json", auth="public", cors="*") def im_livechat_chat_history(self, uuid, last_id=False, limit=20): diff --git a/addons/im_livechat/controllers/rtc.py b/addons/im_livechat/controllers/rtc.py new file mode 100644 index 00000000000..433be032800 --- /dev/null +++ b/addons/im_livechat/controllers/rtc.py @@ -0,0 +1,32 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.http import route +from odoo.addons.mail.models.discuss.mail_guest import add_guest_to_context +from odoo.addons.mail.controllers.discuss.rtc import RtcController + + +class LivechatRtcController(RtcController): + @route("/mail/rtc/session/notify_call_members", cors="*") + @add_guest_to_context + def session_call_notify(self, peer_notifications): + return super().session_call_notify(peer_notifications) + + @route("/mail/rtc/session/update_and_broadcast", cors="*") + @add_guest_to_context + def session_update_and_broadcast(self, session_id, values): + return super().session_update_and_broadcast(session_id, values) + + @route("/mail/rtc/channel/join_call", cors="*") + @add_guest_to_context + def channel_call_join(self, channel_id, check_rtc_session_ids=None): + return super().channel_call_join(channel_id, check_rtc_session_ids=check_rtc_session_ids) + + @route("/mail/rtc/channel/leave_call", cors="*") + @add_guest_to_context + def channel_call_leave(self, channel_id): + return super().channel_call_leave(channel_id) + + @route("/discuss/channel/ping", cors="*") + @add_guest_to_context + def channel_ping(self, channel_id, rtc_session_id=None, check_rtc_session_ids=None): + return super().channel_ping(channel_id, rtc_session_id=rtc_session_id, check_rtc_session_ids=check_rtc_session_ids) diff --git a/addons/im_livechat/static/src/core/thread_model_patch.js b/addons/im_livechat/static/src/core/thread_model_patch.js index 40c110f8986..88268020722 100644 --- a/addons/im_livechat/static/src/core/thread_model_patch.js +++ b/addons/im_livechat/static/src/core/thread_model_patch.js @@ -31,6 +31,10 @@ patch(Thread, { }); patch(Thread.prototype, { + get typesAllowingCalls() { + return super.typesAllowingCalls.concat(["livechat"]); + }, + get isChannel() { return this.type === "livechat" || super.isChannel; }, diff --git a/addons/im_livechat/static/src/embed/core/disabled_features.js b/addons/im_livechat/static/src/embed/core/disabled_features.js index 56fd683ca2f..c8967f140df 100644 --- a/addons/im_livechat/static/src/embed/core/disabled_features.js +++ b/addons/im_livechat/static/src/embed/core/disabled_features.js @@ -39,7 +39,7 @@ patch(Store.prototype, { }, }); -const allowedThreadActions = new Set(["fold-chat-window", "close", "restart"]); +const allowedThreadActions = new Set(["fold-chat-window", "close", "restart", "settings"]); for (const [actionName] of threadActionsRegistry.getEntries()) { if (!allowedThreadActions.has(actionName)) { threadActionsRegistry.remove(actionName); 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 aabce822d11..f3bdb3f04df 100644 --- a/addons/im_livechat/static/src/embed/core/livechat_service.js +++ b/addons/im_livechat/static/src/embed/core/livechat_service.js @@ -127,14 +127,13 @@ export class LivechatService { */ async leaveSession({ notifyServer = true } = {}) { const session = JSON.parse(this.cookie.current[this.SESSION_COOKIE] ?? "{}"); + if (this.state === SESSION_STATE.PERSISTED && notifyServer) { + this.busService.deleteChannel(session.uuid); + await this.rpc("/im_livechat/visitor_leave_session", { uuid: session.uuid }); + } this.cookie.deleteCookie(this.SESSION_COOKIE); this.state = SESSION_STATE.NONE; this.sessionInitialized = false; - if (!session?.uuid || !notifyServer) { - return; - } - this.busService.deleteChannel(session.uuid); - await this.rpc("/im_livechat/visitor_leave_session", { uuid: session.uuid }); } /** diff --git a/addons/im_livechat/static/src/embed/core/messaging_service_patch.js b/addons/im_livechat/static/src/embed/core/messaging_service_patch.js index 7f002bf1797..8a315177ce2 100644 --- a/addons/im_livechat/static/src/embed/core/messaging_service_patch.js +++ b/addons/im_livechat/static/src/embed/core/messaging_service_patch.js @@ -4,11 +4,10 @@ import { Messaging } from "@mail/core/common/messaging_service"; import { patch } from "@web/core/utils/patch"; import { session } from "@web/session"; -import { SESSION_STATE } from "@im_livechat/embed/core/livechat_service"; patch(Messaging.prototype, { initialize() { - if (this.env.services["im_livechat.livechat"].state === SESSION_STATE.PERSISTED) { + if (this.env.services["im_livechat.livechat"].guestToken) { return super.initialize(); } if (session.livechatData?.options.current_partner_id) { diff --git a/addons/im_livechat/static/src/embed/core/thread_actions.js b/addons/im_livechat/static/src/embed/core/thread_actions.js index 5ce5d017594..b0343dec071 100644 --- a/addons/im_livechat/static/src/embed/core/thread_actions.js +++ b/addons/im_livechat/static/src/embed/core/thread_actions.js @@ -1,9 +1,16 @@ /* @odoo-module */ -import { _t } from "@web/core/l10n/translation"; -import { registry } from "@web/core/registry"; +import { SESSION_STATE } from "@im_livechat/embed/core/livechat_service"; -registry.category("mail.thread/actions").add("restart", { +import { threadActionsRegistry } from "@mail/core/common/thread_actions"; +import "@mail/discuss/call/common/thread_actions"; +import { useComponent } from "@odoo/owl"; + +import { _t } from "@web/core/l10n/translation"; +import { useService } from "@web/core/utils/hooks"; +import { patch } from "@web/core/utils/patch"; + +threadActionsRegistry.add("restart", { condition(component) { return component.chatbotService.canRestart; }, @@ -15,3 +22,22 @@ registry.category("mail.thread/actions").add("restart", { }, sequence: 99, }); + +const callSettingsAction = threadActionsRegistry.get("settings"); +patch(callSettingsAction, { + condition(component) { + if (component.thread?.type !== "livechat") { + return super.condition(...arguments); + } + return ( + component.livechatService.state === SESSION_STATE.PERSISTED && + component.rtcService.state.channel?.eq(component.thread) + ); + }, + setup() { + super.setup(...arguments); + const component = useComponent(); + component.livechatService = useService("im_livechat.livechat"); + component.rtcService = useService("discuss.rtc"); + }, +}); diff --git a/addons/im_livechat/static/src/embed/core/thread_model_patch.js b/addons/im_livechat/static/src/embed/core/thread_model_patch.js index 4f8feed0452..290859e2378 100644 --- a/addons/im_livechat/static/src/embed/core/thread_model_patch.js +++ b/addons/im_livechat/static/src/embed/core/thread_model_patch.js @@ -56,6 +56,10 @@ patch(Thread.prototype, { return this.type === "livechat" || super.isChatChannel; }, + get typesAllowingCalls() { + return super.typesAllowingCalls.concat(["livechat"]); + }, + get isLastMessageFromCustomer() { if (this.type !== "livechat") { return super.isLastMessageFromCustomer; diff --git a/addons/im_livechat/static/tests/chat_window_patch_tests.js b/addons/im_livechat/static/tests/chat_window_patch_tests.js index c88bde6cf8f..11da846b17a 100644 --- a/addons/im_livechat/static/tests/chat_window_patch_tests.js +++ b/addons/im_livechat/static/tests/chat_window_patch_tests.js @@ -1,30 +1,9 @@ /* @odoo-module */ -import { Command } from "@mail/../tests/helpers/command"; -import { click, contains, start, startServer } from "@mail/../tests/helpers/test_utils"; +import { click, start, startServer } from "@mail/../tests/helpers/test_utils"; QUnit.module("chat window (patch)"); -QUnit.test("No call buttons", async () => { - const pyEnv = await startServer(); - const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" }); - pyEnv["discuss.channel"].create({ - anonymous_name: "Visitor 11", - channel_member_ids: [ - [0, 0, { partner_id: pyEnv.currentPartnerId }], - Command.create({ guest_id: guestId }), - ], - channel_type: "livechat", - livechat_operator_id: pyEnv.currentPartnerId, - }); - await start(); - await click(".o_menu_systray i[aria-label='Messages']"); - await click(".o-mail-NotificationItem"); - await contains(".o-mail-ChatWindow"); - await contains(".o-mail-ChatWindow-command i.fa-phone", { count: 0 }); - await contains(".o-mail-ChatWindow-command i.fa-gear", { count: 0 }); -}); - QUnit.test("closing a chat window with no message from admin side unpins it", async (assert) => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "Demo" }); diff --git a/addons/im_livechat/static/tests/discuss_patch_tests.js b/addons/im_livechat/static/tests/discuss_patch_tests.js index b5a4ae25d0f..851211398a0 100644 --- a/addons/im_livechat/static/tests/discuss_patch_tests.js +++ b/addons/im_livechat/static/tests/discuss_patch_tests.js @@ -7,25 +7,6 @@ import { start, startServer } from "@mail/../tests/helpers/test_utils"; QUnit.module("discuss (patch)"); -QUnit.test("No call buttons", async () => { - const pyEnv = await startServer(); - const guestId = pyEnv["mail.guest"].create({ name: "Visitor 11" }); - pyEnv["discuss.channel"].create({ - anonymous_name: "Visitor 11", - channel_member_ids: [ - [0, 0, { partner_id: pyEnv.currentPartnerId }], - Command.create({ guest_id: guestId }), - ], - channel_type: "livechat", - livechat_operator_id: pyEnv.currentPartnerId, - }); - const { openDiscuss } = await start(); - await openDiscuss(); - await contains(".o-mail-Discuss-header"); - await contains(".o-mail-Discuss-header button[title='Start a Call']", { count: 0 }); - await contains(".o-mail-Discuss-header button[title='Show Call Settings']", { count: 0 }); -}); - QUnit.test("add livechat in the sidebar on visitor sending first message", async () => { const pyEnv = await startServer(); pyEnv["res.users"].write([pyEnv.currentUserId], { im_status: "online" }); diff --git a/addons/mail/static/src/core/common/thread_model.js b/addons/mail/static/src/core/common/thread_model.js index a8402e7da91..ebd20c994db 100644 --- a/addons/mail/static/src/core/common/thread_model.js +++ b/addons/mail/static/src/core/common/thread_model.js @@ -248,9 +248,13 @@ export class Thread extends Record { return ["chat", "channel", "group"].includes(this.type); } + get typesAllowingCalls() { + return ["chat", "channel", "group"]; + } + get allowCalls() { return ( - ["chat", "channel", "group"].includes(this.type) && + this.typesAllowingCalls.includes(this.type) && !this.correspondent?.eq(this._store.odoobot) ); }