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,