diff --git a/addons/crm/tests/test_crm_lead_convert_mass.py b/addons/crm/tests/test_crm_lead_convert_mass.py index 29203142dcb..457f132be52 100644 --- a/addons/crm/tests/test_crm_lead_convert_mass.py +++ b/addons/crm/tests/test_crm_lead_convert_mass.py @@ -42,7 +42,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon): with self.assertQueryCount(user_sales_manager=0): test_leads = self.env['crm.lead'].browse(test_leads.ids) - with self.assertQueryCount(user_sales_manager=580): # crm 521 / com 516 / ent 516 + with self.assertQueryCount(user_sales_manager=581): # crm 521 / com 516 / ent 516 test_leads._handle_salesmen_assignment(user_ids=user_ids, team_id=team_id) self.assertEqual(test_leads.team_id, self.sales_team_convert) @@ -167,7 +167,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon): user_ids = self.assign_users.ids # randomness: at least 1 query - with self.assertQueryCount(user_sales_manager=1797): # crm 1410 / com 1677 / ent 1685 + with self.assertQueryCount(user_sales_manager=1798): # crm 1410 / com 1677 / ent 1685 mass_convert = self.env['crm.lead2opportunity.partner.mass'].with_context({ 'active_model': 'crm.lead', 'active_ids': test_leads.ids, diff --git a/addons/hr/static/src/components/avatar_card/avatar_card_popover_patch.xml b/addons/hr/static/src/components/avatar_card/avatar_card_popover_patch.xml index 27277006ae7..10605f9b7d8 100644 --- a/addons/hr/static/src/components/avatar_card/avatar_card_popover_patch.xml +++ b/addons/hr/static/src/components/avatar_card/avatar_card_popover_patch.xml @@ -2,7 +2,7 @@ - + diff --git a/addons/hr/static/tests/m2x_avatar_employee_tests.js b/addons/hr/static/tests/m2x_avatar_employee_tests.js index 80bbd9a37e1..87312833a2b 100644 --- a/addons/hr/static/tests/m2x_avatar_employee_tests.js +++ b/addons/hr/static/tests/m2x_avatar_employee_tests.js @@ -59,34 +59,34 @@ QUnit.test("many2one_avatar_employee widget in list view", async function (asser document.querySelectorAll(".o_data_cell div[name='employee_id']")[2].innerText, "Mario" ); - + // TODO: avatar card employee // click on first employee - dom.click(document.querySelector(".o_data_cell .o_m2o_avatar > img")); - await contains(".o-mail-ChatWindow-name"); - assert.verifySteps([`read hr.employee.public ${employeeId_1}`]); - assert.strictEqual(document.querySelector(".o-mail-ChatWindow-name").textContent, "Mario"); + // dom.click(document.querySelector(".o_data_cell .o_m2o_avatar > img")); + // await contains(".o-mail-ChatWindow-name"); + // assert.verifySteps([`read hr.employee.public ${employeeId_1}`]); + // assert.strictEqual(document.querySelector(".o-mail-ChatWindow-name").textContent, "Mario"); - // click on second employee - dom.click(document.querySelectorAll(".o_data_cell .o_m2o_avatar > img")[1]); - await contains(".o-mail-ChatWindow-name", { count: 2 }); - assert.verifySteps([`read hr.employee.public ${employeeId_2}`]); - assert.strictEqual( - document.querySelectorAll(".o-mail-ChatWindow-name")[1].textContent, - "Luigi" - ); + // // click on second employee + // dom.click(document.querySelectorAll(".o_data_cell .o_m2o_avatar > img")[1]); + // await contains(".o-mail-ChatWindow-name", { count: 2 }); + // assert.verifySteps([`read hr.employee.public ${employeeId_2}`]); + // assert.strictEqual( + // document.querySelectorAll(".o-mail-ChatWindow-name")[1].textContent, + // "Luigi" + // ); - // click on third employee (same as first) - dom.click(document.querySelectorAll(".o_data_cell .o_m2o_avatar > img")[2]); - assert.containsN( - document.body, - ".o-mail-ChatWindow-name", - 2, - "should still have only 2 chat windows because third is the same partner as first" - ); - assert.verifySteps( - [], - "employee should not have been read again because we already know its partner" - ); + // // click on third employee (same as first) + // dom.click(document.querySelectorAll(".o_data_cell .o_m2o_avatar > img")[2]); + // assert.containsN( + // document.body, + // ".o-mail-ChatWindow-name", + // 2, + // "should still have only 2 chat windows because third is the same partner as first" + // ); + // assert.verifySteps( + // [], + // "employee should not have been read again because we already know its partner" + // ); }); QUnit.test("many2one_avatar_employee widget in kanban view", async function (assert) { @@ -158,10 +158,8 @@ QUnit.test( }); await contains(".o_field_widget[name=employee_id] input", { value: "Mario" }); await dom.click(document.querySelector(".o_m2o_avatar > img")); - await contains( - ".o_notification.border-info:contains(You can only chat with employees that have a dedicated user.)" - ); assert.verifySteps([`web_read m2x.avatar.employee ${avatarId}`]); + // Nothing should happen } ); @@ -218,12 +216,10 @@ QUnit.test("many2many_avatar_employee widget in form view", async function (asse await dom.click( document.querySelectorAll(".o_field_many2many_avatar_employee .o_tag .o_m2m_avatar")[1] ); + // TODO: avatar card employee assert.verifySteps([ `web_read m2x.avatar.employee ${avatarId_1}`, - `read hr.employee.public ${employeeId_1}`, - `read hr.employee.public ${employeeId_2}`, ]); - await contains(".o-mail-ChatWindow-name", { count: 2 }); }); QUnit.test("many2many_avatar_employee widget in list view", async function (assert) { @@ -265,21 +261,21 @@ QUnit.test("many2many_avatar_employee widget in list view", async function (asse 2, "should have two avatar" ); + // TODO: avatar card employee + // // click on first employee badge + // dom.click(document.querySelector(".o_data_cell .o_m2m_avatar")); + // await contains(".o-mail-ChatWindow-name"); + // assert.verifySteps([`read hr.employee.public ${employeeId_1}`]); + // assert.strictEqual(document.querySelector(".o-mail-ChatWindow-name").textContent, "Mario"); - // click on first employee badge - dom.click(document.querySelector(".o_data_cell .o_m2m_avatar")); - await contains(".o-mail-ChatWindow-name"); - assert.verifySteps([`read hr.employee.public ${employeeId_1}`]); - assert.strictEqual(document.querySelector(".o-mail-ChatWindow-name").textContent, "Mario"); - - // click on second employee - dom.click(document.querySelectorAll(".o_data_cell .o_m2m_avatar")[1]); - await contains(".o-mail-ChatWindow-name", { count: 2 }); - assert.verifySteps([`read hr.employee.public ${employeeId_2}`]); - assert.strictEqual( - document.querySelectorAll(".o-mail-ChatWindow-name")[1].textContent, - "Yoshi" - ); + // // click on second employee + // dom.click(document.querySelectorAll(".o_data_cell .o_m2m_avatar")[1]); + // await contains(".o-mail-ChatWindow-name", { count: 2 }); + // assert.verifySteps([`read hr.employee.public ${employeeId_2}`]); + // assert.strictEqual( + // document.querySelectorAll(".o-mail-ChatWindow-name")[1].textContent, + // "Yoshi" + // ); }); QUnit.test("many2many_avatar_employee widget in kanban view", async function (assert) { @@ -346,12 +342,9 @@ QUnit.test("many2many_avatar_employee widget in kanban view", async function (as `/web/image/hr.employee.public/${employeeId_1}/avatar_128` ); - await dom.click(document.querySelectorAll(".o_kanban_record img.o_m2m_avatar")[1]); - await dom.click(document.querySelectorAll(".o_kanban_record img.o_m2m_avatar")[0]); - assert.verifySteps([ - `read hr.employee.public ${employeeId_1}`, - `read hr.employee.public ${employeeId_2}`, - ]); + await dom.click(document.querySelectorAll('.o_kanban_record img.o_m2m_avatar')[1]); + await dom.click(document.querySelectorAll('.o_kanban_record img.o_m2m_avatar')[0]); + // TODO: avatar card employee }); QUnit.test( @@ -400,14 +393,9 @@ QUnit.test( await dom.click( document.querySelectorAll(".o_field_many2many_avatar_employee .o_tag .o_m2m_avatar")[1] ); - await contains( - ".o_notification.border-info:contains(You can only chat with employees that have a dedicated user.)" - ); assert.verifySteps([ `web_read m2x.avatar.employee ${employeeId_1}`, - `read hr.employee.public ${employeeId_1}`, - `read hr.employee.public ${employeeId_2}`, ]); - await contains(".o-mail-ChatWindow-name"); + // TODO: avtar card employee } ); diff --git a/addons/hr/static/tests/web/m2x_avatar_user_tests.js b/addons/hr/static/tests/web/m2x_avatar_user_tests.js index 0308002f76f..b0930433126 100644 --- a/addons/hr/static/tests/web/m2x_avatar_user_tests.js +++ b/addons/hr/static/tests/web/m2x_avatar_user_tests.js @@ -1,9 +1,8 @@ /* @odoo-module */ -import { startServer } from "@bus/../tests/helpers/mock_python_environment"; - import { start } from "@mail/../tests/helpers/test_utils"; -import { patchWithCleanup, triggerEvent, getFixture, getNodesTextContent } from "@web/../tests/helpers/utils"; +import { startServer } from "@bus/../tests/helpers/mock_python_environment"; +import { click, patchWithCleanup, getFixture, getNodesTextContent } from "@web/../tests/helpers/utils"; import { registry } from "@web/core/registry"; import { browser } from "@web/core/browser/browser"; import { EventBus } from "@odoo/owl"; @@ -71,6 +70,7 @@ QUnit.module("M2XAvatarUser", ({ beforeEach }) => { "email", "phone", "im_status", + "share", "work_phone", "work_email", "job_title", @@ -107,13 +107,12 @@ QUnit.module("M2XAvatarUser", ({ beforeEach }) => { }, }); // Open card - await triggerEvent(target, ".o_m2o_avatar > img", "mouseover"); - assert.verifySteps(["setTimeout of 350ms", "setTimeout of 250ms", "user read"]); + await click(document, ".o_m2o_avatar > img"); + assert.verifySteps(["setTimeout of 250ms", "user read"]); assert.containsOnce(target, ".o_avatar_card"); - assert.deepEqual(getNodesTextContent(target.querySelectorAll(".o_card_user_infos > *")), ['Mario', 'sub manager', 'Managemment', ' Mario@odoo.pro', ' +585555555']); + assert.deepEqual(getNodesTextContent(target.querySelectorAll(".o_card_user_infos > *")), ['Mario', 'sub manager', 'Managemment', 'Mario@odoo.pro', '+585555555']); // Close card - await triggerEvent(target, ".o_control_panel", "mouseover"); - assert.verifySteps(["setTimeout of 400ms"]); + await click(document, ".o_action_manager"); assert.containsNone(target, ".o_avatar_card"); }); }); diff --git a/addons/hr_work_entry_holidays/tests/test_performance.py b/addons/hr_work_entry_holidays/tests/test_performance.py index ce12484cf49..6fbc70473e8 100644 --- a/addons/hr_work_entry_holidays/tests/test_performance.py +++ b/addons/hr_work_entry_holidays/tests/test_performance.py @@ -32,7 +32,7 @@ class TestWorkEntryHolidaysPerformance(TestWorkEntryHolidaysBase): self.richard_emp.generate_work_entries(date(2018, 1, 1), date(2018, 1, 2)) leave = self.create_leave(datetime(2018, 1, 1, 7, 0), datetime(2018, 1, 1, 18, 0)) - with self.assertQueryCount(__system__=99, admin=102): + with self.assertQueryCount(__system__=100, admin=103): leave.action_validate() leave.action_refuse() @@ -48,7 +48,7 @@ class TestWorkEntryHolidaysPerformance(TestWorkEntryHolidaysBase): @users('__system__', 'admin') @warmup def test_performance_leave_create(self): - with self.assertQueryCount(__system__=45, admin=45): + with self.assertQueryCount(__system__=46, admin=46): leave = self.create_leave(datetime(2018, 1, 1, 7, 0), datetime(2018, 1, 1, 18, 0)) leave.action_refuse() @@ -57,7 +57,7 @@ class TestWorkEntryHolidaysPerformance(TestWorkEntryHolidaysBase): def test_performance_leave_confirm(self): leave = self.create_leave(datetime(2018, 1, 1, 7, 0), datetime(2018, 1, 1, 18, 0)) leave.action_draft() - with self.assertQueryCount(__system__=41, admin=40): + with self.assertQueryCount(__system__=42, admin=41): leave.action_confirm() leave.state = 'refuse' diff --git a/addons/im_livechat/__manifest__.py b/addons/im_livechat/__manifest__.py index 6e38022287c..8bd00970710 100644 --- a/addons/im_livechat/__manifest__.py +++ b/addons/im_livechat/__manifest__.py @@ -78,7 +78,6 @@ Help your customers with this chat, and analyse their feedback. 'im_livechat/static/src/core_ui/**/*', 'im_livechat/static/src/discuss_app/**/*', 'im_livechat/static/src/messaging_menu/**/*', - 'im_livechat/static/src/web/**/*', 'im_livechat/static/src/scss/im_livechat_history.scss', 'im_livechat/static/src/scss/im_livechat_form.scss', ], diff --git a/addons/im_livechat/static/src/web/message_patch.js b/addons/im_livechat/static/src/web/message_patch.js deleted file mode 100644 index 22e0ea451ab..00000000000 --- a/addons/im_livechat/static/src/web/message_patch.js +++ /dev/null @@ -1,14 +0,0 @@ -/* @odoo-module */ - -import { Message } from "@mail/core/common/message"; -import "@mail/discuss/core/web/message_patch"; // dependency ordering - -import { patch } from "@web/core/utils/patch"; - -patch(Message.prototype, { - hasOpenChatFeature() { - return this.message.originThread?.channel?.channel_type === "livechat" - ? false - : super.hasOpenChatFeature(); - }, -}); diff --git a/addons/im_livechat/static/tests/message_patch_tests.js b/addons/im_livechat/static/tests/message_patch_tests.js deleted file mode 100644 index bb4a8a0de28..00000000000 --- a/addons/im_livechat/static/tests/message_patch_tests.js +++ /dev/null @@ -1,30 +0,0 @@ -/* @odoo-module */ - -import { startServer } from "@bus/../tests/helpers/mock_python_environment"; - -import { Command } from "@mail/../tests/helpers/command"; -import { start } from "@mail/../tests/helpers/test_utils"; - -QUnit.module("message (patch)"); - -QUnit.test("redirect to author (open profile) in livechat", async (assert) => { - const pyEnv = await startServer(); - const partnerId = pyEnv["res.partner"].create({ name: "Demo" }); - pyEnv["res.users"].create({ partner_id: partnerId }); - const channelId = pyEnv["discuss.channel"].create({ - channel_member_ids: [ - Command.create({ partner_id: pyEnv.currentPartnerId }), - Command.create({ partner_id: partnerId }), - ], - channel_type: "livechat", - }); - pyEnv["mail.message"].create({ - author_id: partnerId, - body: "not empty", - model: "discuss.channel", - res_id: channelId, - }); - const { openDiscuss } = await start(); - await openDiscuss(channelId); - assert.containsOnce($, ".o-mail-Message-avatarContainer[title='Open profile']"); -}); diff --git a/addons/im_livechat/tests/test_message.py b/addons/im_livechat/tests/test_message.py index 8f37f037633..8381cfe80f5 100644 --- a/addons/im_livechat/tests/test_message.py +++ b/addons/im_livechat/tests/test_message.py @@ -58,6 +58,10 @@ class TestImLivechatMessage(HttpCase): 'id': self.users[1].partner_id.id, 'is_company': self.users[1].partner_id.is_company, 'name': "test1", + 'user': { + 'id': self.users[1].id, + 'isInternalUser': self.users[1]._is_internal(), + } }, 'body': message.body, 'date': message.date, diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index 2742fb954f3..add464ff833 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -876,11 +876,7 @@ class Message(models.Model): thread_ids_by_model_name[message.model].add(message.res_id) for vals in vals_list: message_sudo = self.browse(vals['id']).sudo().with_prefetch(self.ids) - author = { - 'id': message_sudo.author_id.id, - 'is_company': message_sudo.author_id.is_company, - 'name': message_sudo.author_id.name, - } if message_sudo.author_id else [('clear',)] + author = message_sudo.author_id.mail_partner_format({'id': True, 'name': True, 'is_company': True, 'user': {"id": True}}).get(message_sudo.author_id) if message_sudo.author_id else [('clear',)] guestAuthor = { 'id': message_sudo.author_guest_id.id, 'name': message_sudo.author_guest_id.name, diff --git a/addons/mail/static/src/core/web/activity.js b/addons/mail/static/src/core/web/activity.js index b04f6ca3a6c..fa6405f2cd2 100644 --- a/addons/mail/static/src/core/web/activity.js +++ b/addons/mail/static/src/core/web/activity.js @@ -4,6 +4,7 @@ import { useAttachmentUploader } from "@mail/core/common/attachment_uploader_hoo import { ActivityMailTemplate } from "@mail/core/web/activity_mail_template"; import { ActivityMarkAsDone } from "@mail/core/web/activity_markasdone_popover"; import { computeDelay, getMsToTomorrow } from "@mail/utils/common/dates"; +import { AvatarCardPopover } from "@mail/discuss/web/avatar_card/avatar_card_popover"; import { Component, onMounted, onWillUnmount, useState } from "@odoo/owl"; @@ -34,6 +35,7 @@ export class Activity extends Component { this.threadService = useService("mail.thread"); this.state = useState({ showDetails: false }); this.popover = usePopover(ActivityMarkAsDone, { position: "right" }); + this.avatarCard = usePopover(AvatarCardPopover); onMounted(() => { this.updateDelayAtNight(); }); @@ -83,6 +85,15 @@ export class Activity extends Component { await this.threadService.fetchNewMessages(this.thread); } + onClickAvatar(ev) { + const target = ev.currentTarget; + if (!this.avatarCard.isOpen) { + this.avatarCard.open(target, { + id: this.props.data.user_id[0], + }); + } + } + async edit() { const { id, res_model, res_id } = this.props.data; await this.env.services["mail.activity"].schedule(res_model, res_id, id); diff --git a/addons/mail/static/src/core/web/activity.xml b/addons/mail/static/src/core/web/activity.xml index 671a988c434..b3e3cdd0ccd 100644 --- a/addons/mail/static/src/core/web/activity.xml +++ b/addons/mail/static/src/core/web/activity.xml @@ -5,7 +5,7 @@
- +
{ 'cursor-pointer': hasAuthorClickable() } - getAuthorText() + getAuthorText() ev => this.onClickAuthor(ev) - getAuthorText() + { 'cursor-pointer': hasAuthorClickable() } + getAuthorText() ev => this.onClickAuthor(ev) diff --git a/addons/mail/static/src/discuss/core/web/message_patch.js b/addons/mail/static/src/discuss/core/web/message_patch.js deleted file mode 100644 index 4b8bbf033b7..00000000000 --- a/addons/mail/static/src/discuss/core/web/message_patch.js +++ /dev/null @@ -1,32 +0,0 @@ -/* @odoo-module */ - -import { Message } from "@mail/core/common/message"; -import "@mail/core/web/message_patch"; // dependency ordering -import { markEventHandled } from "@web/core/utils/misc"; - -import { _t } from "@web/core/l10n/translation"; -import { patch } from "@web/core/utils/patch"; - -patch(Message.prototype, { - getAuthorText() { - return this.hasOpenChatFeature() ? _t("Open chat") : super.getAuthorText(); - }, - hasAuthorClickable() { - return ( - super.hasAuthorClickable() && - this.message.author.type !== "guest" && - this.message.originThread?.channel?.channel_type !== "chat" - ); - }, - hasOpenChatFeature() { - return this.hasAuthorClickable(); - }, - onClickAuthor(ev) { - if (this.hasOpenChatFeature()) { - markEventHandled(ev, "Message.ClickAuthor"); - this.threadService.openChat({ partnerId: this.message.author.id }); - return; - } - return super.onClickAuthor(ev); - }, -}); diff --git a/addons/mail/static/src/discuss/web/avatar_card/avatar_card_popover.js b/addons/mail/static/src/discuss/web/avatar_card/avatar_card_popover.js index c7c6132b6e3..19642baa338 100644 --- a/addons/mail/static/src/discuss/web/avatar_card/avatar_card_popover.js +++ b/addons/mail/static/src/discuss/web/avatar_card/avatar_card_popover.js @@ -21,7 +21,7 @@ export class AvatarCardPopover extends Component { } get fieldNames() { - return ["name", "email", "phone", "im_status"]; + return ["name", "email", "phone", "im_status", "share"]; } get email() { diff --git a/addons/mail/static/src/discuss/web/avatar_card/avatar_card_popover.xml b/addons/mail/static/src/discuss/web/avatar_card/avatar_card_popover.xml index f7cc2dc716d..a0210bca304 100644 --- a/addons/mail/static/src/discuss/web/avatar_card/avatar_card_popover.xml +++ b/addons/mail/static/src/discuss/web/avatar_card/avatar_card_popover.xml @@ -21,17 +21,17 @@ - + - +
- +
diff --git a/addons/mail/static/src/views/web/fields/many2many_avatar_user_field/many2many_avatar_user_field.js b/addons/mail/static/src/views/web/fields/many2many_avatar_user_field/many2many_avatar_user_field.js index eb9d959b0a9..938a978e448 100644 --- a/addons/mail/static/src/views/web/fields/many2many_avatar_user_field/many2many_avatar_user_field.js +++ b/addons/mail/static/src/views/web/fields/many2many_avatar_user_field/many2many_avatar_user_field.js @@ -1,12 +1,10 @@ /* @odoo-module */ -import { useOpenChat } from "@mail/core/web/open_chat_hook"; import { useAssignUserCommand } from "@mail/views/web/fields/assign_user_command_hook"; import { registry } from "@web/core/registry"; import { TagsList } from "@web/core/tags_list/tags_list"; import { usePopover } from "@web/core/popover/popover_hook"; -import { browser } from "@web/core/browser/browser"; import { AvatarCardPopover } from "@mail/discuss/web/avatar_card/avatar_card_popover"; import { Many2ManyTagsAvatarField, @@ -21,30 +19,24 @@ import { export class Many2ManyAvatarUserTagsList extends TagsList {} Many2ManyAvatarUserTagsList.template = "mail.Many2ManyAvatarUserTagsList"; -const WithUserChatter = (T) => class UserChatterMixin extends T { - setup() { - super.setup(...arguments); - this.openChat = useOpenChat(this.relation); - if (this.props.withCommand) { - useAssignUserCommand(); +const WithUserChatter = (T) => + class UserChatterMixin extends T { + setup() { + super.setup(...arguments); + if (this.props.withCommand) { + useAssignUserCommand(); + } + this.avatarCard = usePopover(AvatarCardPopover); } - this.avatarCard = usePopover(AvatarCardPopover, { - closeOnHoverAway: true, - }); - this.openTimeout = false; - this.lastOpenedId = 0; - } - getTagProps(record) { - return { - ...super.getTagProps(...arguments), - onImageClicked: () => this.openChat(record.resId), - openCard: (ev) => { - if (this.env.isSmall || this.relation !== "res.users") { - return; - } - const target = ev.currentTarget; - this.openTimeout = browser.setTimeout(() => { + getTagProps(record) { + return { + ...super.getTagProps(...arguments), + onImageClicked: (ev) => { + if (this.env.isSmall || this.relation !== "res.users") { + return; + } + const target = ev.currentTarget; if ( !this.avatarCard.isOpen || (this.lastOpenedId && record.resId !== this.lastOpenedId) @@ -54,15 +46,10 @@ const WithUserChatter = (T) => class UserChatterMixin extends T { }); this.lastOpenedId = record.resId; } - }, 350); - }, - clearTimeout: () => { - browser.clearTimeout(this.openTimeout); - delete this.openTimeout; - }, - }; - } -} + }, + }; + } + }; export class Many2ManyTagsAvatarUserField extends WithUserChatter(Many2ManyTagsAvatarField) { static components = { @@ -83,7 +70,9 @@ export class KanbanMany2ManyAvatarUserTagsList extends KanbanMany2ManyTagsAvatar static template = "mail.KanbanMany2ManyAvatarUserTagsList"; } -export class KanbanMany2ManyTagsAvatarUserField extends WithUserChatter(KanbanMany2ManyTagsAvatarField) { +export class KanbanMany2ManyTagsAvatarUserField extends WithUserChatter( + KanbanMany2ManyTagsAvatarField +) { static template = "mail.KanbanMany2ManyTagsAvatarUserField"; static components = { ...KanbanMany2ManyTagsAvatarField.components, @@ -100,7 +89,9 @@ export const kanbanMany2ManyTagsAvatarUserField = { }; registry.category("fields").add("kanban.many2many_avatar_user", kanbanMany2ManyTagsAvatarUserField); -export class ListMany2ManyTagsAvatarUserField extends WithUserChatter(ListMany2ManyTagsAvatarField) { +export class ListMany2ManyTagsAvatarUserField extends WithUserChatter( + ListMany2ManyTagsAvatarField +) { static template = "mail.ListMany2ManyTagsAvatarUserField"; static components = { ...ListMany2ManyTagsAvatarField.components, diff --git a/addons/mail/static/src/views/web/fields/many2many_avatar_user_field/many2many_avatar_user_field.xml b/addons/mail/static/src/views/web/fields/many2many_avatar_user_field/many2many_avatar_user_field.xml index 54ce2b3c899..f6f09715ec5 100644 --- a/addons/mail/static/src/views/web/fields/many2many_avatar_user_field/many2many_avatar_user_field.xml +++ b/addons/mail/static/src/views/web/fields/many2many_avatar_user_field/many2many_avatar_user_field.xml @@ -1,24 +1,14 @@ - - - tag.onImageClicked - tag.clearTimeout - tag.openCard - - - tag.onImageClicked - tag.clearTimeout - tag.openCard diff --git a/addons/mail/static/src/views/web/fields/many2one_avatar_user_field/many2one_avatar_user_field.js b/addons/mail/static/src/views/web/fields/many2one_avatar_user_field/many2one_avatar_user_field.js index bf86e690474..311cb9c061b 100644 --- a/addons/mail/static/src/views/web/fields/many2one_avatar_user_field/many2one_avatar_user_field.js +++ b/addons/mail/static/src/views/web/fields/many2one_avatar_user_field/many2one_avatar_user_field.js @@ -1,6 +1,5 @@ /* @odoo-module */ -import { useOpenChat } from "@mail/core/web/open_chat_hook"; import { useAssignUserCommand } from "@mail/views/web/fields/assign_user_command_hook"; import { _t } from "@web/core/l10n/translation"; @@ -12,50 +11,31 @@ import { kanbanMany2OneAvatarField, } from "@web/views/fields/many2one_avatar/many2one_avatar_field"; import { usePopover } from "@web/core/popover/popover_hook"; -import { browser } from "@web/core/browser/browser"; import { AvatarCardPopover } from "@mail/discuss/web/avatar_card/avatar_card_popover"; const WithUserChatter = (T) => class extends T { setup() { super.setup(...arguments); - this.openChat = useOpenChat(this.relation); if (this.props.withCommand) { useAssignUserCommand(); } - this.avatarCard = usePopover(AvatarCardPopover, { - closeOnHoverAway: true, - }); - this.openTimeout = false; + this.avatarCard = usePopover(AvatarCardPopover); } - onClickAvatar() { + onClickAvatar(ev) { const id = this.props.record.data[this.props.name][0] ?? false; if (id !== false) { - this.openChat(id); - } - } - - openCard(ev) { - if (this.env.isSmall || this.relation !== "res.users") { - return; - } - const target = ev.currentTarget; - if (!target.querySelector(":scope > img")) { - return; - } - this.openTimeout = browser.setTimeout(() => { + if (this.env.isSmall || this.relation !== "res.users") { + return; + } + const target = ev.currentTarget; if (!this.avatarCard.isOpen) { this.avatarCard.open(target, { id: this.props.record.data[this.props.name][0], }); } - }, 350); - } - - clearTimeout() { - browser.clearTimeout(this.openTimeout); - delete this.openTimeout; + } } }; diff --git a/addons/mail/static/src/views/web/fields/many2one_avatar_user_field/many2one_avatar_user_field.xml b/addons/mail/static/src/views/web/fields/many2one_avatar_user_field/many2one_avatar_user_field.xml index bb1ec4edcd0..e1d0c16cf84 100644 --- a/addons/mail/static/src/views/web/fields/many2one_avatar_user_field/many2one_avatar_user_field.xml +++ b/addons/mail/static/src/views/web/fields/many2one_avatar_user_field/many2one_avatar_user_field.xml @@ -1,24 +1,14 @@ - - - onClickAvatar - clearTimeout - openCard - - - onClickAvatar - clearTimeout - openCard props.readonly and props.displayAvatarName diff --git a/addons/mail/static/tests/discuss_app/discuss_tests.js b/addons/mail/static/tests/discuss_app/discuss_tests.js index 033f2bf51eb..6104b08b758 100644 --- a/addons/mail/static/tests/discuss_app/discuss_tests.js +++ b/addons/mail/static/tests/discuss_app/discuss_tests.js @@ -156,7 +156,12 @@ QUnit.test( QUnit.test("Click on avatar opens its partner chat window", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "testPartner" }); - pyEnv["res.users"].create({ partner_id: partnerId }); + pyEnv["res.users"].create({ + partner_id: partnerId, + name: "testPartner", + email: "test@partner.com", + phone: "+45687468", + }); pyEnv["mail.message"].create({ author_id: partnerId, body: "Test", @@ -168,7 +173,10 @@ QUnit.test("Click on avatar opens its partner chat window", async () => { await openFormView("res.partner", partnerId); await contains(".o-mail-Message-sidebar .o-mail-Message-avatarContainer img"); await click(".o-mail-Message-sidebar .o-mail-Message-avatarContainer img"); - await contains(".o-mail-ChatWindow-name", { text: "testPartner" }); + await contains(".o_avatar_card"); + await contains(".o_card_user_infos > span:contains(testPartner)"); + await contains(".o_card_user_infos > a:contains(test@partner.com)"); + await contains(".o_card_user_infos > a:contains(+45687468)"); }); QUnit.test("Can use channel command /who", async () => { diff --git a/addons/mail/static/tests/helpers/mock_server/models/mail_message.js b/addons/mail/static/tests/helpers/mock_server/models/mail_message.js index 0d5fb285419..15f43d78db7 100644 --- a/addons/mail/static/tests/helpers/mock_server/models/mail_message.js +++ b/addons/mail/static/tests/helpers/mock_server/models/mail_message.js @@ -200,11 +200,17 @@ patch(MockServer.prototype, { const [author] = this.getRecords("res.partner", [["id", "=", message.author_id]], { active_test: false, }); + const [user] = this.getRecords("res.users", [ + ["partner_id", "=", message.author_id], + ]); formattedAuthor = { id: author.id, is_company: author.is_company, name: author.name, }; + if (user) { + formattedAuthor["user"] = { id: user.id }; + } } else { formattedAuthor = [["clear"]]; } diff --git a/addons/mail/static/tests/message/message_tests.js b/addons/mail/static/tests/message/message_tests.js index 6644fb7be14..faf09eae2c9 100644 --- a/addons/mail/static/tests/message/message_tests.js +++ b/addons/mail/static/tests/message/message_tests.js @@ -665,10 +665,15 @@ QUnit.test("message comment of same author within 1min. should be squashed", asy await contains(".o-mail-Message:contains(body2) .o-mail-Message-sidebar .o-mail-Message-date"); }); -QUnit.test("redirect to author (open chat)", async () => { +QUnit.test("open author avatar card", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "Demo" }); - pyEnv["res.users"].create({ partner_id: partnerId }); + pyEnv["res.users"].create({ + partner_id: partnerId, + name: "Demo", + email: "demo@example.com", + phone: "+5646548", + }); const [channelId_1] = pyEnv["discuss.channel"].create([ { name: "General" }, { @@ -691,32 +696,10 @@ QUnit.test("redirect to author (open chat)", async () => { await contains(".o-mail-Discuss-content .o-mail-Message-avatarContainer img"); await click(".o-mail-Discuss-content .o-mail-Message-avatarContainer img"); - await contains(".o-mail-DiscussSidebarChannel.o-active", { text: "Demo" }); -}); - -QUnit.test("open chat from avatar should not work on self-authored messages", async () => { - const pyEnv = await startServer(); - const [channelId] = pyEnv["discuss.channel"].create([ - { name: "General" }, - { - channel_member_ids: [Command.create({ partner_id: pyEnv.currentPartnerId })], - channel_type: "chat", - }, - ]); - pyEnv["mail.message"].create({ - author_id: pyEnv.currentPartnerId, - body: "not empty", - model: "discuss.channel", - res_id: channelId, - }); - const { openDiscuss } = await start(); - openDiscuss(channelId); - await contains(".o-mail-Message-avatarContainer:not(.cursor-pointer)"); - await contains(".o-mail-Message-author:not(.cursor-pointer)"); - await click(".o-mail-Message-avatar"); - // weak test, no guarantee that we waited long enough to open the chat/view - await contains(".o-mail-DiscussSidebarChannel.o-active", { count: 0, text: "Mitchell Admin" }); - await contains(".breadcrumb", { count: 0, text: "Mitchell Admin" }); + await contains(".o_avatar_card"); + await contains(".o_card_user_infos > span:contains(Demo)"); + await contains(".o_card_user_infos > a:contains(demo@example.com)"); + await contains(".o_card_user_infos > a:contains(+5646548)"); }); QUnit.test("toggle_star message", async (assert) => { @@ -1126,32 +1109,47 @@ QUnit.test("allow attachment image download on message", async () => { await contains(".o-mail-AttachmentImage .fa-download"); }); -QUnit.test("chat with author should be opened after clicking on their avatar", async (assert) => { - const pyEnv = await startServer(); - const [partnerId_1, partnerId_2] = pyEnv["res.partner"].create([ - { name: "Partner_1" }, - { name: "Partner_2" }, - ]); - pyEnv["res.users"].create({ partner_id: partnerId_2 }); - pyEnv["mail.message"].create({ - author_id: partnerId_2, - body: "not empty", - model: "res.partner", - res_id: partnerId_1, - }); - const { openFormView } = await start(); - openFormView("res.partner", partnerId_1); - await contains(".o-mail-Message-avatar"); - assert.hasClass($(".o-mail-Message-avatarContainer"), "cursor-pointer"); - await click(".o-mail-Message-avatar"); - await contains(".o-mail-ChatWindow-content"); - await contains(".o-mail-ChatWindow-name", { text: "Partner_2" }); -}); +QUnit.test( + "avatar card from author should be opened after clicking on their avatar", + async (assert) => { + const pyEnv = await startServer(); + const [partnerId_1, partnerId_2] = pyEnv["res.partner"].create([ + { name: "Partner_1" }, + { name: "Partner_2" }, + ]); + pyEnv["res.users"].create({ + partner_id: partnerId_2, + name: "Partner_2", + email: "partner2@mail.com", + phone: "+15968415", + }); + pyEnv["mail.message"].create({ + author_id: partnerId_2, + body: "not empty", + model: "res.partner", + res_id: partnerId_1, + }); + const { openFormView } = await start(); + await openFormView("res.partner", partnerId_1); + await contains(".o-mail-Message-avatar"); + assert.hasClass($(".o-mail-Message-avatarContainer"), "cursor-pointer"); + await click(".o-mail-Message-avatar"); + await contains(".o_avatar_card"); + await contains(".o_card_user_infos > span:contains(Partner_2)"); + await contains(".o_card_user_infos > a:contains(partner2@mail.com)"); + await contains(".o_card_user_infos > a:contains(+15968415)"); + } +); -QUnit.test("chat with author should be opened after clicking on their name", async () => { +QUnit.test("avatar card from author should be opened after clicking on their name", async () => { const pyEnv = await startServer(); - const partnerId = pyEnv["res.partner"].create({ name: "Demo User" }); - pyEnv["res.users"].create({ partner_id: partnerId }); + const partnerId = pyEnv["res.partner"].create({ name: "Demo" }); + pyEnv["res.users"].create({ + partner_id: partnerId, + name: "Demo", + email: "demo@example.com", + phone: "+5646548", + }); pyEnv["mail.message"].create({ author_id: partnerId, body: "not empty", @@ -1159,12 +1157,14 @@ QUnit.test("chat with author should be opened after clicking on their name", asy res_id: partnerId, }); const { openFormView } = await start(); - openFormView("res.partner", partnerId); - await contains(".o-mail-Message span", { text: "Demo User" }); + await openFormView("res.partner", partnerId); + await contains(".o-mail-Message span", { text: "Demo" }); - await click(".o-mail-Message span", { text: "Demo User" }); - await contains(".o-mail-ChatWindow"); - await contains(".o-mail-ChatWindow-name", { text: "Demo User" }); + await click(".o-mail-Message span:contains(Demo)"); + await contains(".o_avatar_card"); + await contains(".o_card_user_infos > span:contains(Demo)"); + await contains(".o_card_user_infos > a:contains(demo@example.com)"); + await contains(".o_card_user_infos > a:contains(+5646548)"); }); QUnit.test("subtype description should be displayed if it is different than body", async () => { @@ -1238,36 +1238,6 @@ QUnit.test("Chat with partner should be opened after clicking on their mention", await contains(".o-mail-ChatWindow-name", { text: "Test Partner" }); }); -QUnit.test( - "open chat with author on avatar click should be disabled when currently chatting with the author", - async (assert) => { - const pyEnv = await startServer(); - const partnerId = pyEnv["res.partner"].create({ name: "test" }); - pyEnv["res.users"].create({ partner_id: partnerId }); - const channelId = pyEnv["discuss.channel"].create({ - name: "test", - channel_member_ids: [ - Command.create({ partner_id: pyEnv.currentPartnerId }), - Command.create({ partner_id: partnerId }), - ], - channel_type: "chat", - }); - pyEnv["mail.message"].create({ - author_id: partnerId, - body: "not empty", - model: "discuss.channel", - res_id: channelId, - }); - const { openDiscuss } = await start(); - openDiscuss(channelId); - await contains(".o-mail-Message-avatar"); - assert.doesNotHaveClass($(".o-mail-Message-avatarContainer"), "cursor-pointer"); - await click(".o-mail-Message-avatar"); - // weak test, no guarantee that we waited long enough for the potential chat window to show - await contains(".o-mail-ChatWindow", { count: 0 }); - } -); - QUnit.test("Channel should be opened after clicking on its mention", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({}); diff --git a/addons/mail/static/tests/web/fields/m2x_avatar_user_tests.js b/addons/mail/static/tests/web/fields/m2x_avatar_user_tests.js index 7c5f2f3e904..4b009cffec3 100644 --- a/addons/mail/static/tests/web/fields/m2x_avatar_user_tests.js +++ b/addons/mail/static/tests/web/fields/m2x_avatar_user_tests.js @@ -10,8 +10,8 @@ import { browser } from "@web/core/browser/browser"; import { popoverService } from "@web/core/popover/popover_service"; import { registry } from "@web/core/registry"; import { tooltipService } from "@web/core/tooltip/tooltip_service"; -import { patchWithCleanup, triggerHotkey, getNodesTextContent } from "@web/../tests/helpers/utils"; -import { click, contains, triggerEvents } from "@web/../tests/utils"; +import { patchWithCleanup, triggerHotkey } from "@web/../tests/helpers/utils"; +import { click, contains } from "@web/../tests/utils"; const fakeMultiTab = { start() { @@ -409,7 +409,7 @@ QUnit.test("avatar card preview", async (assert) => { }); const mockRPC = (route, args) => { if (route === "/web/dataset/call_kw/res.users/read") { - assert.deepEqual(args.args[1], ["name", "email", "phone", "im_status"]); + assert.deepEqual(args.args[1], ["name", "email", "phone", "im_status", "share"]); assert.step("user read"); } }; @@ -440,17 +440,14 @@ QUnit.test("avatar card preview", async (assert) => { }, }); // Open card - await triggerEvents(".o_m2o_avatar > img", ["mouseover"]); + await click(".o_m2o_avatar > img"); await contains(".o_avatar_card"); - assert.verifySteps(["setTimeout of 350ms", "setTimeout of 250ms", "user read"]); - assert.deepEqual(getNodesTextContent(document.querySelectorAll(".o_card_user_infos > *")), [ - "Mario", - " Mario@odoo.test", - " +78786987", - ]); + await contains(".o_card_user_infos > span:contains(Mario)"); + await contains(".o_card_user_infos > a:contains(Mario@odoo.test)"); + await contains(".o_card_user_infos > a:contains(+78786987)"); + assert.verifySteps(["setTimeout of 250ms", "user read"]); // Close card - await triggerEvents(".o_control_panel", ["mouseover"]); - assert.verifySteps(["setTimeout of 400ms"]); + await click(".o_action_manager"); await contains(".o_avatar_card", { count: 0 }); }); @@ -478,7 +475,12 @@ QUnit.test("avatar_user widget displays the appropriate user image in form view" QUnit.test("many2one_avatar_user widget in list view", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "Partner 1" }); - const userId = pyEnv["res.users"].create({ name: "Mario", partner_id: partnerId }); + const userId = pyEnv["res.users"].create({ + name: "Mario", + partner_id: partnerId, + email: "Mario@partner.com", + phone: "+45687468", + }); pyEnv["m2x.avatar.user"].create({ user_id: userId }); const views = { "m2x.avatar.user,false,list": @@ -490,13 +492,21 @@ QUnit.test("many2one_avatar_user widget in list view", async () => { views: [[false, "list"]], }); await click(".o_data_cell .o_m2o_avatar > img"); - await contains(".o-mail-ChatWindow-name", { text: "Partner 1" }); + await contains(".o_avatar_card"); + await contains(".o_card_user_infos > span:contains(Mario)"); + await contains(".o_card_user_infos > a:contains(Mario@partner.com)"); + await contains(".o_card_user_infos > a:contains(+45687468)"); }); QUnit.test("many2many_avatar_user widget in form view", async () => { const pyEnv = await startServer(); const partnerId = pyEnv["res.partner"].create({ name: "Partner 1" }); - const userId = pyEnv["res.users"].create({ name: "Mario", partner_id: partnerId }); + const userId = pyEnv["res.users"].create({ + name: "Mario", + partner_id: partnerId, + email: "Mario@partner.com", + phone: "+45687468", + }); const avatarUserId = pyEnv["m2x.avatar.user"].create({ user_ids: [userId] }); const views = { "m2x.avatar.user,false,form": ` @@ -511,5 +521,8 @@ QUnit.test("many2many_avatar_user widget in form view", async () => { views: [[false, "form"]], }); await click(".o_field_many2many_avatar_user .o_avatar img"); - await contains(".o-mail-ChatWindow-name", { text: "Partner 1" }); + await contains(".o_avatar_card"); + await contains(".o_card_user_infos > span:contains(Mario)"); + await contains(".o_card_user_infos > a:contains(Mario@partner.com)"); + await contains(".o_card_user_infos > a:contains(+45687468)"); }); diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py index 45e5862f871..7cd848be2a9 100644 --- a/addons/test_mail/tests/test_performance.py +++ b/addons/test_mail/tests/test_performance.py @@ -401,7 +401,7 @@ class TestMailAPIPerformance(BaseMailPerformance): composer_form.attachment_ids.add(attachment) composer = composer_form.save() - with self.assertQueryCount(admin=53, employee=53): # tm+com 46/46 + with self.assertQueryCount(admin=54, employee=54): # tm+com 46/46 composer._action_send_mail() # notifications @@ -463,7 +463,7 @@ class TestMailAPIPerformance(BaseMailPerformance): 'default_template_id': test_template.id, }).create({}) - with self.assertQueryCount(admin=40, employee=40): # com 36/36 + with self.assertQueryCount(admin=41, employee=41): # com 36/36 composer._action_send_mail() # notifications @@ -487,7 +487,7 @@ class TestMailAPIPerformance(BaseMailPerformance): 'default_template_id': test_template.id, }).create({}) - with self.assertQueryCount(admin=49, employee=49): # com 43/43 + with self.assertQueryCount(admin=50, employee=50): # com 43/43 composer._action_send_mail() # notifications @@ -519,7 +519,7 @@ class TestMailAPIPerformance(BaseMailPerformance): ) composer = composer_form.save() - with self.assertQueryCount(admin=48, employee=48): # com 44/44 + with self.assertQueryCount(admin=50, employee=50): # com 44/44 composer._action_send_mail() # notifications @@ -549,7 +549,7 @@ class TestMailAPIPerformance(BaseMailPerformance): ) composer = composer_form.save() - with self.assertQueryCount(admin=67, employee=67): # com 61/61 + with self.assertQueryCount(admin=69, employee=69): # com 61/61 composer._action_send_mail() # notifications @@ -585,7 +585,7 @@ class TestMailAPIPerformance(BaseMailPerformance): @warmup def test_message_assignation_inbox(self): record = self.env['mail.test.track'].create({'name': 'Test'}) - with self.assertQueryCount(admin=22, employee=22): # com 19/9 + with self.assertQueryCount(admin=23, employee=23): # com 19/9 record.write({ 'user_id': self.user_test_inbox.id, }) @@ -671,7 +671,7 @@ class TestMailAPIPerformance(BaseMailPerformance): def test_message_post_one_inbox_notification(self): record = self.env['mail.test.simple'].create({'name': 'Test'}) - with self.assertQueryCount(admin=20, employee=20): # com 18/18 + with self.assertQueryCount(admin=21, employee=21): # com 18/18 record.message_post( body=Markup('

Test Post Performances with an inbox ping

'), partner_ids=self.user_test.partner_id.ids, @@ -1228,7 +1228,7 @@ class TestMailComplexPerformance(BaseMailPerformance): """ messages_all = self.messages_all.with_env(self.env) - with self.assertQueryCount(employee=25): # test_mail: 24 + with self.assertQueryCount(employee=26): # test_mail: 25 res = messages_all.message_format() self.assertEqual(len(res), 2*2) @@ -1241,7 +1241,7 @@ class TestMailComplexPerformance(BaseMailPerformance): def test_message_format_single(self): message = self.messages_all[0].with_env(self.env) - with self.assertQueryCount(employee=22): # test_mail: 21 + with self.assertQueryCount(employee=23): # test_mail: 22 res = message.message_format() self.assertEqual(len(res), 1) @@ -1262,14 +1262,14 @@ class TestMailComplexPerformance(BaseMailPerformance): 'res_id': record.id } for record in records]) - with self.assertQueryCount(employee=6): + with self.assertQueryCount(employee=7): res = messages.message_format() self.assertEqual(len(res), 6) self.env.flush_all() self.env.invalidate_all() - with self.assertQueryCount(employee=14): + with self.assertQueryCount(employee=15): res = messages.message_format() self.assertEqual(len(res), 6) @@ -1346,7 +1346,7 @@ class TestMailHeavyPerformancePost(BaseMailPerformance): attachments = self.env['ir.attachment'].with_user(self.env.user).create(self.test_attachments_vals) # enable_logging = self.cr._enable_logging() if self.warm else nullcontext() # with self.assertQueryCount(employee=63), enable_logging: - with self.assertQueryCount(employee=65): # com 60 + with self.assertQueryCount(employee=66): # com 60 record_container.with_context({}).message_post( body=Markup('

Test body

'), subject='Test Subject', diff --git a/addons/test_mail_full/tests/test_mail_performance.py b/addons/test_mail_full/tests/test_mail_performance.py index 7eeaa92f41f..0f7ff891dff 100644 --- a/addons/test_mail_full/tests/test_mail_performance.py +++ b/addons/test_mail_full/tests/test_mail_performance.py @@ -91,7 +91,7 @@ class TestMailPerformance(FullBaseMailPerformance): record_ticket = self.env['mail.test.ticket.mc'].browse(self.record_ticket.ids) attachments = self.env['ir.attachment'].create(self.test_attachments_vals) - with self.assertQueryCount(employee=95): # test_mail_full: 88 + with self.assertQueryCount(employee=96): # test_mail_full: 88 new_message = record_ticket.message_post( attachment_ids=attachments.ids, body=Markup('

Test Content

'), diff --git a/addons/test_mail_full/tests/test_rating.py b/addons/test_mail_full/tests/test_rating.py index 223a5d3514f..cb6c9ed5196 100644 --- a/addons/test_mail_full/tests/test_rating.py +++ b/addons/test_mail_full/tests/test_rating.py @@ -172,10 +172,10 @@ class TestRatingPerformance(TestRatingCommon): @users('employee') @warmup def test_rating_last_value_perfs(self): - with self.assertQueryCount(employee=1614): # tmf 1313 / com 1313 + with self.assertQueryCount(employee=1615): # tmf 1313 / com 1313 self.create_ratings('mail.test.rating.thread') - with self.assertQueryCount(employee=2001): # tmf 1901 + with self.assertQueryCount(employee=2101): # tmf 1901 self.apply_ratings(1) with self.assertQueryCount(employee=1900): # tmf 1800 @@ -184,10 +184,10 @@ class TestRatingPerformance(TestRatingCommon): @users('employee') @warmup def test_rating_last_value_perfs_with_rating_mixin(self): - with self.assertQueryCount(employee=1721): # tmf 1419 / com 1419 + with self.assertQueryCount(employee=1722): # tmf 1419 / com 1419 self.create_ratings('mail.test.rating') - with self.assertQueryCount(employee=2204): # tmf 2104 + with self.assertQueryCount(employee=2304): # tmf 2104 self.apply_ratings(1) with self.assertQueryCount(employee=2203): # tmf 2103 diff --git a/addons/web/static/src/core/popover/popover_controller.js b/addons/web/static/src/core/popover/popover_controller.js index bf126f6dbb7..2c835e7d098 100644 --- a/addons/web/static/src/core/popover/popover_controller.js +++ b/addons/web/static/src/core/popover/popover_controller.js @@ -4,7 +4,6 @@ import { Component, onWillDestroy, useExternalListener, xml } from "@odoo/owl"; import { useHotkey } from "../hotkeys/hotkey_hook"; import { useChildRef } from "../utils/hooks"; import { Popover } from "./popover"; -import { browser } from "../browser/browser"; export class PopoverController extends Component { static template = xml` @@ -17,7 +16,6 @@ export class PopoverController extends Component { "target", "close", "closeOnClickAway", - "closeOnHoverAway", "component", "componentProps", "popoverProps", @@ -27,10 +25,6 @@ export class PopoverController extends Component { if (this.props.target.isConnected) { this.popoverRef = useChildRef(); useExternalListener(window, "mousedown", this.onClickAway, { capture: true }); - if (this.props.closeOnHoverAway) { - this.closePopoverTimeout = false; - useExternalListener(window, "mouseover", this.onHoverAway, { capture: true }); - } useHotkey("escape", () => this.props.close()); const targetObserver = new MutationObserver(this.onTargetMutate.bind(this)); targetObserver.observe(this.props.target.parentElement, { childList: true }); @@ -51,17 +45,6 @@ export class PopoverController extends Component { } } - onHoverAway(ev) { - const target = ev.composedPath()[0]; - if (!this.props.target.contains(target) && !this.popoverRef.el.contains(target)) { - this.closePopoverTimeout = browser.setTimeout(() => { - this.props.close(); - }, 400); - } else { - browser.clearTimeout(this.closePopoverTimeout); - } - } - onTargetMutate() { if (!this.props.target.isConnected) { this.props.close(); diff --git a/addons/web/static/src/core/popover/popover_service.js b/addons/web/static/src/core/popover/popover_service.js index af74f47a22c..44851283998 100644 --- a/addons/web/static/src/core/popover/popover_service.js +++ b/addons/web/static/src/core/popover/popover_service.js @@ -7,7 +7,6 @@ import { PopoverController } from "./popover_controller"; /** * @typedef {{ * closeOnClickAway?: boolean | (target: HTMLElement) => boolean; - * closeOnHoverAway?: boolean; * onClose?: () => void; * popoverClass?: string; * position?: import("@web/core/position_hook").Options["position"]; @@ -32,14 +31,12 @@ export const popoverService = { typeof options.closeOnClickAway === "function" ? options.closeOnClickAway : () => options.closeOnClickAway ?? true; - const closeOnHoverAway = options.closeOnHoverAway || false; const remove = overlay.add( PopoverController, { target, close: () => remove(), closeOnClickAway, - closeOnHoverAway, component, componentProps: markRaw(props), popoverProps: {