diff --git a/addons/im_livechat/static/tests/tours/im_livechat_history_back_and_forth.js b/addons/im_livechat/static/tests/tours/im_livechat_history_back_and_forth.js
new file mode 100644
index 00000000000..2ccc7e0ae1f
--- /dev/null
+++ b/addons/im_livechat/static/tests/tours/im_livechat_history_back_and_forth.js
@@ -0,0 +1,58 @@
+/** @odoo-module */
+
+import { registry } from "@web/core/registry";
+
+registry.category("web_tour.tours").add("im_livechat_history_back_and_forth_tour", {
+ test: true,
+ steps: () => [
+ {
+ trigger: "body",
+ // Open Command Palette
+ run() {
+ this.$anchor[0].dispatchEvent(
+ new KeyboardEvent("keydown", { key: "K", ctrlKey: true, bubbles: true })
+ );
+ },
+ },
+ {
+ trigger: ".o_command_palette_search input",
+ run: "text /",
+ },
+ {
+ trigger: ".o_command_palette_search input",
+ run: "text Live Chat",
+ },
+ {
+ trigger: ".o_command:contains(Sessions History)",
+ },
+ {
+ trigger: ".o_data_cell:contains(Visitor operator)",
+ },
+ {
+ trigger: ".o-mail-DiscussSidebar-item:contains(Visitor).o-active",
+ run() {
+ history.back();
+ },
+ },
+ {
+ trigger: ".o_data_cell:contains(Visitor operator)",
+ run() {
+ history.forward();
+ },
+ },
+ {
+ trigger: ".o-mail-DiscussSidebar-item:contains(Visitor).o-active",
+ },
+ {
+ trigger: ".o-mail-DiscussSidebar-item:contains(Visitor).o-active",
+ run() {
+ history.back();
+ },
+ },
+ {
+ trigger: ".o_data_cell:contains(Visitor operator)",
+ run() {},
+ isCheck: true,
+ },
+ ],
+});
diff --git a/addons/im_livechat/tests/__init__.py b/addons/im_livechat/tests/__init__.py
index 4f6047dc3f1..f80fe315f33 100644
--- a/addons/im_livechat/tests/__init__.py
+++ b/addons/im_livechat/tests/__init__.py
@@ -13,3 +13,4 @@ from . import test_im_livechat_support_page
from . import test_js
from . import test_message
from . import test_upload_attachment
+from . import test_session_history
diff --git a/addons/im_livechat/tests/common.py b/addons/im_livechat/tests/common.py
index 03910f89774..fc16e36e7f9 100644
--- a/addons/im_livechat/tests/common.py
+++ b/addons/im_livechat/tests/common.py
@@ -46,6 +46,6 @@ class TestImLivechatCommon(HttpCase):
def _compute_available_operator_ids(channel_self):
for record in channel_self:
- record.available_operator_ids = type(self).operators
+ record.available_operator_ids = record.user_ids
self.patch(type(self.env['im_livechat.channel']), '_compute_available_operator_ids', _compute_available_operator_ids)
diff --git a/addons/im_livechat/tests/test_session_history.py b/addons/im_livechat/tests/test_session_history.py
new file mode 100644
index 00000000000..d1e6df24c5e
--- /dev/null
+++ b/addons/im_livechat/tests/test_session_history.py
@@ -0,0 +1,21 @@
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.tests import new_test_user, tagged
+from odoo.addons.im_livechat.tests.common import TestImLivechatCommon
+
+
+@tagged("-at_install", "post_install")
+class TestImLivechatSessionHistory(TestImLivechatCommon):
+ def test_session_history_navigation_back_and_forth(self):
+ operator = new_test_user(self.env, login="operator", groups="base.group_user,im_livechat.im_livechat_group_manager")
+ self.env["bus.presence"].create({"user_id": operator.id, "status": "online"})
+ self.livechat_channel.user_ids |= operator
+ self.authenticate(None, None)
+ infos = self.make_jsonrpc_request("/im_livechat/get_session", {
+ "channel_id": self.livechat_channel.id,
+ "anonymous_name": "Visitor",
+ "previous_operator_id": operator.partner_id.id
+ })
+ channel = self.env["discuss.channel"].browse(infos["id"])
+ channel.with_user(operator).message_post(body="Hello, how can I help you?")
+ self.start_tour("/web", "im_livechat_history_back_and_forth_tour", login="operator", step_delay=25)
diff --git a/addons/mail/static/src/core/common/discuss.xml b/addons/mail/static/src/core/common/discuss.xml
index adb8c346479..def28936387 100644
--- a/addons/mail/static/src/core/common/discuss.xml
+++ b/addons/mail/static/src/core/common/discuss.xml
@@ -70,7 +70,7 @@
-
+
No conversation selected.
diff --git a/addons/mail/static/src/core/common/discuss_app_model.js b/addons/mail/static/src/core/common/discuss_app_model.js
index 7ca954f9e02..84208d7c492 100644
--- a/addons/mail/static/src/core/common/discuss_app_model.js
+++ b/addons/mail/static/src/core/common/discuss_app_model.js
@@ -46,6 +46,7 @@ export class DiscussApp extends Record {
activeTab = "main";
chatWindows = Record.many("ChatWindow");
isActive = false;
+ hasRestoredThread = false;
thread = Record.one("Thread");
channels = Record.one("DiscussAppCategory");
chats = Record.one("DiscussAppCategory");
diff --git a/addons/mail/static/src/core/common/thread_service.js b/addons/mail/static/src/core/common/thread_service.js
index 1105a2adcbf..e40bebaf7b7 100644
--- a/addons/mail/static/src/core/common/thread_service.js
+++ b/addons/mail/static/src/core/common/thread_service.js
@@ -642,7 +642,10 @@ export class ThreadService {
* @param {import("models").Thread} thread
* @param {boolean} pushState
*/
- setDiscussThread(thread, pushState = true) {
+ setDiscussThread(thread, pushState) {
+ if (pushState === undefined) {
+ pushState = thread.localId !== this.store.discuss.thread?.localId;
+ }
this.store.discuss.thread = thread;
const activeId =
typeof thread.id === "string"
diff --git a/addons/mail/static/src/core/web/discuss_client_action.js b/addons/mail/static/src/core/web/discuss_client_action.js
index 1f3c1c0d24a..43cc5e9c638 100644
--- a/addons/mail/static/src/core/web/discuss_client_action.js
+++ b/addons/mail/static/src/core/web/discuss_client_action.js
@@ -58,8 +58,9 @@ export class DiscussClientAction extends Component {
activeThread = await this.threadService.fetchChannel(parseInt(id));
}
if (activeThread && activeThread.notEq(this.store.discuss.thread)) {
- this.threadService.setDiscussThread(activeThread);
+ this.threadService.setDiscussThread(activeThread, false);
}
+ this.store.discuss.hasRestoredThread = true;
}
}
diff --git a/addons/mail/static/tests/helpers/test_utils.js b/addons/mail/static/tests/helpers/test_utils.js
index a8ad972d00f..b7c309c15cc 100644
--- a/addons/mail/static/tests/helpers/test_utils.js
+++ b/addons/mail/static/tests/helpers/test_utils.js
@@ -6,7 +6,6 @@ import { timings } from "@bus/misc";
import { loadEmoji } from "@web/core/emoji_picker/emoji_picker";
import { loadLamejs } from "@mail/discuss/voice_message/common/voice_message_service";
import { patchBrowserNotification } from "@mail/../tests/helpers/patch_notifications";
-import { DISCUSS_ACTION_ID } from "@mail/../tests/helpers/test_constants";
import { getAdvanceTime } from "@mail/../tests/helpers/time_control";
import { getWebClientReady } from "@mail/../tests/helpers/webclient_setup";
@@ -37,7 +36,7 @@ function getOpenDiscuss(webClient, { context = {}, params = {}, ...props } = {})
return async function openDiscuss(pActiveId) {
const actionOpenDiscuss = {
context: { ...context, active_id: pActiveId },
- id: DISCUSS_ACTION_ID,
+ id: "mail.action_discuss",
params,
tag: "mail.action_discuss",
type: "ir.actions.client",
diff --git a/addons/web/static/src/webclient/actions/action_service.js b/addons/web/static/src/webclient/actions/action_service.js
index 5c9ed50c1a0..89f0aadddef 100644
--- a/addons/web/static/src/webclient/actions/action_service.js
+++ b/addons/web/static/src/webclient/actions/action_service.js
@@ -315,9 +315,19 @@ function makeActionManager(env) {
const options = { clearBreadcrumbs: true };
let actionRequest = null;
if (state.action) {
+ const context = {};
+ if (state.active_id) {
+ context.active_id = state.active_id;
+ }
+ if (state.active_ids) {
+ context.active_ids = parseActiveIds(state.active_ids);
+ } else if (state.active_id) {
+ context.active_ids = [state.active_id];
+ }
// ClientAction
if (actionRegistry.contains(state.action)) {
actionRequest = {
+ context,
params: state,
tag: state.action,
type: "ir.actions.client",
@@ -325,15 +335,7 @@ function makeActionManager(env) {
} else {
// The action to load isn't the current one => executes it
actionRequest = state.action;
- const context = { params: state };
- if (state.active_id) {
- context.active_id = state.active_id;
- }
- if (state.active_ids) {
- context.active_ids = parseActiveIds(state.active_ids);
- } else if (state.active_id) {
- context.active_ids = [state.active_id];
- }
+ context.params = state;
Object.assign(options, {
additionalContext: context,
viewType: state.view_type,