From 3355d681d95a42498a381e88b12f88c0ed7caa3e Mon Sep 17 00:00:00 2001
From: tsm-odoo <tsm@odoo.com>
Date: Wed, 12 Jul 2023 13:03:55 +0000
Subject: [PATCH] [FIX] im_livechat: fix crash when user changes after starting
 livechat

Before this commit, a crash occurred when the user changed after
starting a livechat conversation.

Steps to reproduce (login => logout)
- Login on admin
- Go to the website
- Start a livechat conversation
- Logout
- Start typing on the chat window composer
- Crash due to `NotFound` raised after `notify_typing`

The same error could occur the other way around (logout => login).
This error is due to the user not being a channel member after it
changed.

In order to fix this issue, livechat session is cleared when the
user changes. Indeed, it the "new" user is not linked to the livechat
conversation so it makes no sense to display it.

fixes https://github.com/odoo/odoo/issues/128206
opw-3421386

closes odoo/odoo#128301

Signed-off-by: Matthieu Stockbauer (tsm) <tsm@odoo.com>
---
 .../src/legacy/models/public_livechat.js      |  1 +
 .../public_livechat_window.js                 |  2 +-
 .../src/public_models/livechat_button_view.js | 14 ---
 .../public_models/public_livechat_global.js   | 34 ++++++-
 .../public_models/public_livechat_global.js   |  1 +
 .../website_livechat_session_user_changes.js  | 98 +++++++++++++++++++
 addons/website_livechat/tests/__init__.py     |  1 +
 .../test_livechat_session_user_changes.py     | 14 +++
 8 files changed, 148 insertions(+), 17 deletions(-)
 create mode 100644 addons/website_livechat/static/tests/tours/website_livechat_session_user_changes.js
 create mode 100644 addons/website_livechat/tests/test_livechat_session_user_changes.py

diff --git a/addons/im_livechat/static/src/legacy/models/public_livechat.js b/addons/im_livechat/static/src/legacy/models/public_livechat.js
index aaeb6abc95b8..952ad6b84c06 100644
--- a/addons/im_livechat/static/src/legacy/models/public_livechat.js
+++ b/addons/im_livechat/static/src/legacy/models/public_livechat.js
@@ -289,6 +289,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
      */
     toData() {
         return {
+            visitor_uid: this.messaging.publicLivechatGlobal.getVisitorUserId(),
             chatbot_script_id: this.messaging.publicLivechatGlobal.publicLivechat.data.chatbot_script_id,
             folded: this.messaging.publicLivechatGlobal.publicLivechat.isFolded,
             id: this.messaging.publicLivechatGlobal.publicLivechat.id,
diff --git a/addons/im_livechat/static/src/legacy/widgets/public_livechat_window/public_livechat_window.js b/addons/im_livechat/static/src/legacy/widgets/public_livechat_window/public_livechat_window.js
index 295638c5626b..9aea23e9d71b 100644
--- a/addons/im_livechat/static/src/legacy/widgets/public_livechat_window/public_livechat_window.js
+++ b/addons/im_livechat/static/src/legacy/widgets/public_livechat_window/public_livechat_window.js
@@ -83,7 +83,7 @@ const PublicLivechatWindow = Widget.extend({
         } else {
             this.messaging.publicLivechatGlobal.livechatButtonView.closeChat();
         }
-        this.messaging.publicLivechatGlobal.livechatButtonView.leaveSession();
+        this.messaging.publicLivechatGlobal.leaveSession();
     },
     /**
      * States whether the current environment is in mobile or not. This is
diff --git a/addons/im_livechat/static/src/public_models/livechat_button_view.js b/addons/im_livechat/static/src/public_models/livechat_button_view.js
index 123ddd299a5e..8b5c0c3b93d1 100644
--- a/addons/im_livechat/static/src/public_models/livechat_button_view.js
+++ b/addons/im_livechat/static/src/public_models/livechat_button_view.js
@@ -108,20 +108,6 @@ registerModel({
             this.messaging.publicLivechatGlobal.update({ chatWindow: clear() });
             deleteCookie('im_livechat_session');
         },
-        /**
-         * Called when the visitor leaves the livechat chatter the first time (first click on X button)
-         * this will deactivate the mail_channel, notify operator that visitor has left the channel.
-         */
-        leaveSession() {
-            const cookie = getCookie('im_livechat_session');
-            if (cookie) {
-                const channel = JSON.parse(cookie);
-                if (channel.uuid) {
-                    this.messaging.rpc({ route: '/im_livechat/visitor_leave_session', params: { uuid: channel.uuid } });
-                }
-                deleteCookie('im_livechat_session');
-            }
-        },
         openChat() {
             if (this.isOpenChatDebounced) {
                 this.openChatDebounced();
diff --git a/addons/im_livechat/static/src/public_models/public_livechat_global.js b/addons/im_livechat/static/src/public_models/public_livechat_global.js
index 030493662a17..121ad071fc21 100644
--- a/addons/im_livechat/static/src/public_models/public_livechat_global.js
+++ b/addons/im_livechat/static/src/public_models/public_livechat_global.js
@@ -4,6 +4,8 @@ import { registerModel } from '@mail/model/model_core';
 import { attr, many, one } from '@mail/model/model_field';
 import { clear } from '@mail/model/model_field_command';
 
+import { session } from "@web/session";
+
 import { qweb } from 'web.core';
 import { Markup } from 'web.utils';
 import {getCookie, setCookie, deleteCookie} from 'web.utils.cookies';
@@ -45,8 +47,13 @@ registerModel({
         },
         async _willStart() {
             const strCookie = getCookie('im_livechat_session');
-            const isSessionCookieAvailable = Boolean(strCookie);
-            const cookie = JSON.parse(strCookie || '{}');
+            let isSessionCookieAvailable = Boolean(strCookie);
+            let cookie = JSON.parse(strCookie || '{}');
+            if (isSessionCookieAvailable && cookie.visitor_uid !== session.user_id) {
+                this.leaveSession();
+                isSessionCookieAvailable = false;
+                cookie = {};
+            }
             if (cookie.id) {
                 const history = await this.messaging.rpc({
                     route: '/mail/chat_history',
@@ -132,6 +139,29 @@ registerModel({
                 this.chatbot.restoreSession();
             }
         },
+
+        getVisitorUserId() {
+            const cookie = JSON.parse(getCookie("im_livechat_session") || "{}");
+            if ("visitor_uid" in cookie) {
+                return cookie.visitor_uid;
+            }
+            return session.user_id;
+        },
+
+        /**
+         * Called when the visitor leaves the livechat chatter the first time (first click on X button)
+         * this will deactivate the mail_channel, notify operator that visitor has left the channel.
+         */
+        leaveSession() {
+            const cookie = getCookie('im_livechat_session');
+            if (cookie) {
+                const channel = JSON.parse(cookie);
+                if (channel.uuid) {
+                    this.messaging.rpc({ route: '/im_livechat/visitor_leave_session', params: { uuid: channel.uuid } });
+                }
+                deleteCookie('im_livechat_session');
+            }
+        },
     },
     fields: {
         HISTORY_LIMIT: attr({
diff --git a/addons/website_livechat/static/src/public_models/public_livechat_global.js b/addons/website_livechat/static/src/public_models/public_livechat_global.js
index 9a749b593507..a023a3738cab 100644
--- a/addons/website_livechat/static/src/public_models/public_livechat_global.js
+++ b/addons/website_livechat/static/src/public_models/public_livechat_global.js
@@ -48,6 +48,7 @@ registerPatch({
                 return this.loadQWebTemplate();
             }
             if (this.options.chat_request_session) {
+                this.options.chat_request_session.visitor_uid = this.getVisitorUserId();
                 setCookie('im_livechat_session', JSON.stringify(this.options.chat_request_session), 60 * 60, 'required');
             }
             return this._super();
diff --git a/addons/website_livechat/static/tests/tours/website_livechat_session_user_changes.js b/addons/website_livechat/static/tests/tours/website_livechat_session_user_changes.js
new file mode 100644
index 000000000000..78f3d5baab89
--- /dev/null
+++ b/addons/website_livechat/static/tests/tours/website_livechat_session_user_changes.js
@@ -0,0 +1,98 @@
+/* @odoo-module */
+
+import tour from "web_tour.tour";
+
+tour.register(
+    "website_livechat_login_after_chat_start",
+    {
+        test: true,
+    },
+    [
+        {
+            trigger: ".o_livechat_button",
+            run: "click",
+        },
+        {
+            trigger: ".o_composer_text_field",
+            run: "text Hello",
+        },
+        {
+            trigger: "input.o_composer_text_field",
+            run: function () {
+                $("input.o_composer_text_field").trigger(
+                    $.Event("keydown", { which: $.ui.keyCode.ENTER })
+                );
+            },
+        },
+        {
+            trigger: "div.o_thread_message_content > p:contains('Hello')",
+        },
+        {
+            trigger: "a:contains(Sign in)",
+            run: "click",
+        },
+        {
+            trigger: "input[name='login']",
+            run: "text admin",
+        },
+        {
+            trigger: "input[name='password']",
+            run: "text admin",
+        },
+        {
+            trigger: "button:contains(Log in)",
+            run: "click",
+        },
+        {
+            trigger: ".o_main_navbar",
+            run() {
+                window.location = "/";
+            },
+        },
+        {
+            content:
+                "Livechat button is present since the old livechat session was linked to the public user, not the current user.",
+            trigger: ".o_livechat_button",
+        },
+    ]
+);
+
+tour.register(
+    "website_livechat_logout_after_chat_start",
+    {
+        test: true,
+    },
+    [
+        {
+            trigger: ".o_livechat_button",
+            run: "click",
+        },
+        {
+            trigger: ".o_composer_text_field",
+            run: "text Hello",
+        },
+        {
+            trigger: "input.o_composer_text_field",
+            run: function () {
+                $("input.o_composer_text_field").trigger(
+                    $.Event("keydown", { which: $.ui.keyCode.ENTER })
+                );
+            },
+        },
+        {
+            trigger: "div.o_thread_message_content > p:contains('Hello')",
+        },
+        {
+            trigger: "#top_menu a:contains(Mitchell Admin)",
+            run: "click",
+        },
+        {
+            trigger: "a:contains(Logout)",
+        },
+        {
+            content:
+                "Livechat button is present since the old livechat session was linked to the logged user, not the public one.",
+            trigger: ".o_livechat_button",
+        },
+    ]
+);
diff --git a/addons/website_livechat/tests/__init__.py b/addons/website_livechat/tests/__init__.py
index 66ed8f00d9fd..6e8d51d8cdc2 100644
--- a/addons/website_livechat/tests/__init__.py
+++ b/addons/website_livechat/tests/__init__.py
@@ -6,3 +6,4 @@ from . import test_chatbot_ui
 from . import test_livechat_basic_flow
 from . import test_livechat_request
 from . import test_website_visitor
+from . import test_livechat_session_user_changes
diff --git a/addons/website_livechat/tests/test_livechat_session_user_changes.py b/addons/website_livechat/tests/test_livechat_session_user_changes.py
new file mode 100644
index 000000000000..99b181dff216
--- /dev/null
+++ b/addons/website_livechat/tests/test_livechat_session_user_changes.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import tests
+from odoo.addons.website_livechat.tests.common import TestLivechatCommon
+
+
+@tests.tagged("-at_install", "post_install")
+class TestLivechatSessionUserChanges(tests.HttpCase, TestLivechatCommon):
+    def test_livechat_login_after_chat_start(self):
+        self.start_tour("/", "website_livechat_login_after_chat_start")
+
+    def test_livechat_logout_after_chat_start(self):
+        self.start_tour("/", "website_livechat_logout_after_chat_start", login="admin")
-- 
GitLab