diff --git a/addons/bus/models/ir_websocket.py b/addons/bus/models/ir_websocket.py index 8f663d16b90..5c1b6b3ffd0 100644 --- a/addons/bus/models/ir_websocket.py +++ b/addons/bus/models/ir_websocket.py @@ -4,6 +4,7 @@ from odoo.service import security from ..models.bus import dispatch from ..websocket import wsrequest + class IrWebsocket(models.AbstractModel): _name = 'ir.websocket' _description = 'websocket message handling' @@ -11,8 +12,9 @@ class IrWebsocket(models.AbstractModel): def _get_im_status(self, im_status_ids_by_model): im_status = {} if 'res.partner' in im_status_ids_by_model: - im_status['partners'] = self.env['res.partner'].with_context(active_test=False).search( - [('id', 'in', im_status_ids_by_model['res.partner'])]).read(['im_status'] + im_status['partners'] = self.env['res.partner'].with_context(active_test=False).search_read( + [('id', 'in', im_status_ids_by_model['res.partner'])], + ['im_status'] ) return im_status @@ -41,7 +43,7 @@ class IrWebsocket(models.AbstractModel): identity_value=self.env.uid ) im_status_notification = self._get_im_status(im_status_ids_by_model) - if im_status_ids_by_model: + if im_status_notification: self.env['bus.bus']._sendone(self.env.user.partner_id, 'bus/im_status', im_status_notification) @classmethod diff --git a/addons/bus/static/tests/helpers/mock_server.js b/addons/bus/static/tests/helpers/mock_server.js index e3983320400..74f0859bfe3 100644 --- a/addons/bus/static/tests/helpers/mock_server.js +++ b/addons/bus/static/tests/helpers/mock_server.js @@ -4,9 +4,7 @@ import { TEST_USER_IDS } from "@bus/../tests/helpers/test_constants"; import { patchWebsocketWorkerWithCleanup } from '@bus/../tests/helpers/mock_websocket'; import { patch } from "@web/core/utils/patch"; -import { ConnectionLostError } from "@web/core/network/rpc_service"; import { MockServer } from "@web/../tests/helpers/mock_server"; -import { makeDeferred } from "@web/../tests/helpers/utils"; patch(MockServer.prototype, 'bus', { init() { @@ -40,26 +38,6 @@ patch(MockServer.prototype, 'bus', { this._mockIrWebsocket__updatePresence(inactivity_period, im_status_ids_by_model); } }, - - /** - * @override - */ - async _performRPC(route, args) { - if (route === '/longpolling/poll') { - const longpollingPromise = makeDeferred(); - if (this.hasLostConnection) { - longpollingPromise.reject(new ConnectionLostError()); - this.hasLostConnection = false; - } else if (this.notificationsToBeResolved.length) { - longpollingPromise.resolve(this.notificationsToBeResolved); - this.notificationsToBeResolved = []; - } else { - this.pendingLongpollingPromise = longpollingPromise; - } - return longpollingPromise; - } - return this._super(route, args); - }, /** * Simulates `_sendone` on `bus.bus`. * diff --git a/addons/bus/static/tests/helpers/mock_server/models/ir_websocket.js b/addons/bus/static/tests/helpers/mock_server/models/ir_websocket.js index 147f0c8e3b1..6d5158c5604 100644 --- a/addons/bus/static/tests/helpers/mock_server/models/ir_websocket.js +++ b/addons/bus/static/tests/helpers/mock_server/models/ir_websocket.js @@ -11,7 +11,10 @@ patch(MockServer.prototype, 'bus/models/ir_websocket', { * @param imStatusIdsByModel */ _mockIrWebsocket__updatePresence(inactivityPeriod, imStatusIdsByModel) { - this._mockBusBus__sendone(this.currentPartnerId, 'bus/im_status', this._mockIrWebsocket__getImStatus(imStatusIdsByModel)); + const imStatusNotifications = this._mockIrWebsocket__getImStatus(imStatusIdsByModel); + if (Object.keys(imStatusNotifications).length > 0) { + this._mockBusBus__sendone(this.currentPartnerId, 'bus/im_status', imStatusNotifications); + } }, /** * Simulates `_get_im_status` on `ir.websocket`. @@ -20,9 +23,12 @@ patch(MockServer.prototype, 'bus/models/ir_websocket', { * @param {Number[]|undefined} res.partner ids of res.partners whose im_status * should be monitored. */ - _mockIrWebsocket__getImStatus({ 'res.partner': partnerIds }) { - return { - 'partners': this.mockSearchRead('res.partner', [[['id', 'in', partnerIds]]], { context: { 'active_test': false }, fields: ['im_status'] }) - }; + _mockIrWebsocket__getImStatus(imStatusIdsByModel) { + const imStatus = {}; + const { 'res.partner': partnerIds } = imStatusIdsByModel; + if (partnerIds) { + imStatus['partners'] = this.mockSearchRead('res.partner', [[['id', 'in', partnerIds]]], { context: { 'active_test': false }, fields: ['im_status'] }) + } + return imStatus; }, }); diff --git a/addons/mail/models/ir_websocket.py b/addons/mail/models/ir_websocket.py index 733c756b44d..07028759b05 100644 --- a/addons/mail/models/ir_websocket.py +++ b/addons/mail/models/ir_websocket.py @@ -9,7 +9,10 @@ class IrWebsocket(models.AbstractModel): def _get_im_status(self, data): im_status = super()._get_im_status(data) if 'mail.guest' in data: - im_status['guests'] = self.env['mail.guest'].sudo().with_context(active_test=False).search([('id', 'in', data['mail.guest'])]).read(['im_status']) + im_status['guests'] = self.env['mail.guest'].sudo().with_context(active_test=False).search_read( + [('id', 'in', data['mail.guest'])], + ['im_status'] + ) return im_status def _build_bus_channel_list(self, channels): diff --git a/addons/mail/static/tests/helpers/mock_server.js b/addons/mail/static/tests/helpers/mock_server.js index 67ce38180e2..ae0619e85f0 100644 --- a/addons/mail/static/tests/helpers/mock_server.js +++ b/addons/mail/static/tests/helpers/mock_server.js @@ -1559,6 +1559,10 @@ patch(MockServer.prototype, 'mail', { ])[0]; response.subtype_description = subtype.description; } + if (message.author_guest_id) { + const [guest] = this.pyEnv['mail.guest'].searchRead([['id', '=', message.author_guest_id]]); + response['guestAuthor'] = { id: guest.id, name: guest.name }; + } return response; }); }, diff --git a/addons/mail/static/tests/helpers/mock_server/models/ir_websocket.js b/addons/mail/static/tests/helpers/mock_server/models/ir_websocket.js new file mode 100644 index 00000000000..f6e6cc80822 --- /dev/null +++ b/addons/mail/static/tests/helpers/mock_server/models/ir_websocket.js @@ -0,0 +1,25 @@ +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { MockServer } from "@web/../tests/helpers/mock_server"; + +// ensure bus override is applied first. +import "@bus/../tests/helpers/mock_server"; + +patch(MockServer.prototype, 'mail/models/ir_websocket', { + /** + * Simulates `_get_im_status` on `ir.websocket`. + * + * @param {Object} imStatusIdsByModel + * @param {Number[]|undefined} mail.guest ids of mail.guest whose im_status + * should be monitored. + */ + _mockIrWebsocket__getImStatus(imStatusIdsByModel) { + const imStatus = this._super(imStatusIdsByModel); + const { 'mail.guest': guestIds } = imStatusIdsByModel; + if (guestIds) { + imStatus['guests'] = this.pyEnv['mail.guest'].searchRead([['id', 'in', guestIds]], { context: { 'active_test': false }, fields: ['im_status'] }); + } + return imStatus; + }, +}); diff --git a/addons/mail/static/tests/helpers/model_definitions_setup.js b/addons/mail/static/tests/helpers/model_definitions_setup.js index 538da118fce..9efad779e15 100644 --- a/addons/mail/static/tests/helpers/model_definitions_setup.js +++ b/addons/mail/static/tests/helpers/model_definitions_setup.js @@ -14,7 +14,7 @@ import { addModelNamesToFetch([ 'mail.activity', 'mail.activity.type', 'mail.channel', 'mail.channel.member', - 'mail.channel.rtc.session', 'mail.followers', 'mail.link.preview', 'mail.message', + 'mail.channel.rtc.session', 'mail.followers', 'mail.guest', 'mail.link.preview', 'mail.message', 'mail.message.subtype', 'mail.notification', 'mail.shortcode', 'mail.template', 'mail.tracking.value', 'res.users.settings', 'res.users.settings.volumes' ]); diff --git a/addons/mail/static/tests/qunit_suite_tests/components/persona_im_status_icon_tests.js b/addons/mail/static/tests/qunit_suite_tests/components/persona_im_status_icon_tests.js index 3723f8cc3d7..235b1ec287e 100644 --- a/addons/mail/static/tests/qunit_suite_tests/components/persona_im_status_icon_tests.js +++ b/addons/mail/static/tests/qunit_suite_tests/components/persona_im_status_icon_tests.js @@ -148,5 +148,61 @@ QUnit.test('change icon on change partner im_status', async function (assert) { ); }); +QUnit.test('change icon on change guest im_status', async function (assert) { + assert.expect(4); + + const pyEnv = await startServer(); + const guestId = pyEnv['mail.guest'].create({ im_status: 'online' }); + const mailChannelId = pyEnv['mail.channel'].create({ + channel_member_ids: [[0, 0, { partner_id: pyEnv.currentPartnerId }], [0, 0, { guest_id: guestId }]], + channel_type: 'group', + }); + pyEnv['mail.message'].create({ + author_guest_id: guestId, + author_id: false, + body: 'not empty', + model: 'mail.channel', + res_id: mailChannelId, + }); + const { advanceTime, afterNextRender, openDiscuss } = await start({ + discuss: { + params: { + default_active_id: mailChannelId, + }, + }, + hasTimeControl: true, + }); + await openDiscuss(); + assert.strictEqual( + document.querySelectorAll(`.o_PersonaImStatusIcon.o-online`).length, + 1, + "persona IM status icon should have online status rendering" + ); + + pyEnv['mail.guest'].write([guestId], { im_status: 'offline' }); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); + assert.strictEqual( + document.querySelectorAll(`.o_PersonaImStatusIcon.o-offline`).length, + 1, + "persona IM status icon should have offline status rendering" + ); + + pyEnv['mail.guest'].write([guestId], { im_status: 'away' }); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); + assert.strictEqual( + document.querySelectorAll(`.o_PersonaImStatusIcon.o-away`).length, + 1, + "persona IM status icon should have away status rendering" + ); + + pyEnv['mail.guest'].write([guestId], { im_status: 'online' }); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); + assert.strictEqual( + document.querySelectorAll(`.o_PersonaImStatusIcon.o-online`).length, + 1, + "persona IM status icon should have online status rendering in the end" + ); +}); + }); });