diff --git a/addons/bus/__manifest__.py b/addons/bus/__manifest__.py index 31676236bb0..71814b33828 100644 --- a/addons/bus/__manifest__.py +++ b/addons/bus/__manifest__.py @@ -26,7 +26,7 @@ 'bus/static/tests/**/*.js', ], 'web.qunit_mobile_suite_tests': [ - 'bus/static/tests/helpers/*.js', + 'bus/static/tests/helpers/**/*.js', ], 'bus.websocket_worker_assets': [ 'web/static/src/legacy/js/promise_extension.js', diff --git a/addons/bus/controllers/main.py b/addons/bus/controllers/main.py index 209ee685253..230acaa6fbc 100644 --- a/addons/bus/controllers/main.py +++ b/addons/bus/controllers/main.py @@ -6,12 +6,6 @@ from odoo.http import Controller, request, route class BusController(Controller): - @route('/bus/im_status', type="json", auth="user") - def im_status(self, partner_ids): - return { - 'partners': request.env['res.partner'].with_context(active_test=False).search([('id', 'in', partner_ids)]).read(['im_status']) - } - @route('/bus/get_model_definitions', methods=['POST'], type='http', auth='user') def get_model_definitions(self, model_names_to_fetch, **kwargs): return request.make_response(json.dumps( diff --git a/addons/bus/controllers/websocket.py b/addons/bus/controllers/websocket.py index 5384c973915..f0121df7864 100644 --- a/addons/bus/controllers/websocket.py +++ b/addons/bus/controllers/websocket.py @@ -43,8 +43,8 @@ class WebsocketController(Controller): return {'channels': channels, 'notifications': notifications} @route('/websocket/update_bus_presence', type='http', auth='public', cors='*') - def update_bus_presence(self, inactivity_period): - request.env['ir.websocket']._update_bus_presence(int(inactivity_period)) + def update_bus_presence(self, inactivity_period, im_status_ids_by_model): + request.env['ir.websocket']._update_bus_presence(int(inactivity_period), im_status_ids_by_model) @route('/bus/websocket_worker_bundle', type='http', auth='public', cors='*') def get_websocket_worker_bundle(self): diff --git a/addons/bus/models/bus_presence.py b/addons/bus/models/bus_presence.py index f6d5513c6ce..72e1983fcb7 100644 --- a/addons/bus/models/bus_presence.py +++ b/addons/bus/models/bus_presence.py @@ -6,11 +6,11 @@ from psycopg2 import OperationalError from odoo import api, fields, models from odoo import tools -from odoo.addons.bus.models.bus import TIMEOUT from odoo.service.model import PG_CONCURRENCY_ERRORS_TO_RETRY from odoo.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT -DISCONNECTION_TIMER = TIMEOUT + 5 +UPDATE_PRESENCE_DELAY = 60 +DISCONNECTION_TIMER = UPDATE_PRESENCE_DELAY + 5 AWAY_TIMER = 1800 # 30 minutes diff --git a/addons/bus/models/ir_websocket.py b/addons/bus/models/ir_websocket.py index b26df830625..567abe1cd98 100644 --- a/addons/bus/models/ir_websocket.py +++ b/addons/bus/models/ir_websocket.py @@ -8,6 +8,14 @@ class IrWebsocket(models.AbstractModel): _name = 'ir.websocket' _description = 'websocket message handling' + 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'] + ) + return im_status + def _build_bus_channel_list(self, channels): """ Return the list of channels to subscribe to. Override this @@ -25,13 +33,16 @@ class IrWebsocket(models.AbstractModel): channels = set(self._build_bus_channel_list(data['channels'])) dispatch.subscribe(channels, data['last'], self.env.registry.db_name, wsrequest.ws) - def _update_bus_presence(self, inactivity_period): + def _update_bus_presence(self, inactivity_period, im_status_ids_by_model): if self.env.uid: self.env['bus.presence'].update( inactivity_period, identity_field='user_id', identity_value=self.env.uid ) + im_status_notification = self._get_im_status(im_status_ids_by_model) + if im_status_ids_by_model: + self.env['bus.bus']._sendone(self.env.user.partner_id, 'bus/im_status', im_status_notification) @classmethod def _authenticate(cls): diff --git a/addons/bus/static/src/im_status_service.js b/addons/bus/static/src/im_status_service.js index 6eb44d23822..23694433a77 100644 --- a/addons/bus/static/src/im_status_service.js +++ b/addons/bus/static/src/im_status_service.js @@ -1,46 +1,68 @@ /** @odoo-module **/ +import { browser } from '@web/core/browser/browser'; import { registry } from '@web/core/registry'; +export const UPDATE_BUS_PRESENCE_DELAY = 60000; /** * This service updates periodically the user presence in order for the * im_status to be up to date. + * + * In order to receive bus notifications related to im_status, one must + * register model/ids to monitor to this service. */ export const imStatusService = { dependencies: ['bus_service', 'multi_tab', 'presence'], start(env, { bus_service, multi_tab, presence }) { - const UPDATE_BUS_PRESENCE_DELAY = 30000; - let updateBusPresenceInterval; + const imStatusModelToIds = {}; + let updateBusPresenceTimeout; function updateBusPresence() { - const now = new Date().getTime(); - bus_service.send("update_presence", now - presence.getLastPresence()); - } - - function stopUpdatingBusPresence() { - clearInterval(updateBusPresenceInterval); - updateBusPresenceInterval = null; - } - - function startUpdatingBusPresence() { - if (updateBusPresenceInterval || !multi_tab.isOnMainTab()) { + clearTimeout(updateBusPresenceTimeout); + if (!multi_tab.isOnMainTab()) { return; } - updateBusPresence(); - updateBusPresenceInterval = setInterval( - updateBusPresence, - UPDATE_BUS_PRESENCE_DELAY, - ); + const now = new Date().getTime(); + bus_service.send("update_presence", { + inactivity_period: now - presence.getLastPresence(), + im_status_ids_by_model: { ...imStatusModelToIds }, + }); + updateBusPresenceTimeout = browser.setTimeout(updateBusPresence, UPDATE_BUS_PRESENCE_DELAY); } + // wait for im_status model/ids to be registered before starting. + browser.setTimeout(updateBusPresence, 250); + multi_tab.bus.addEventListener('become_main_tab', updateBusPresence); + bus_service.addEventListener('reconnect', updateBusPresence); + multi_tab.bus.addEventListener('no_longer_main_tab', () => clearTimeout(updateBusPresenceTimeout)); + bus_service.addEventListener('disconnect', () => clearTimeout(updateBusPresenceTimeout)); - if (multi_tab.isOnMainTab()) { - startUpdatingBusPresence(); - } - multi_tab.bus.addEventListener('become_main_tab', startUpdatingBusPresence); - bus_service.addEventListener('reconnect', startUpdatingBusPresence); - multi_tab.bus.addEventListener('no_longer_main_tab', stopUpdatingBusPresence); - bus_service.addEventListener('disconnect', stopUpdatingBusPresence); + return { + /** + * Register model/ids whose im_status should be monitored. + * Notification related to the im_status are then sent + * through the bus. Overwrite registration if already + * present. + * + * @param {string} model model related to the given ids. + * @param {Number[]} ids ids whose im_status should be + * monitored. + */ + registerToImStatus(model, ids) { + if (!ids.length) { + return this.unregisterFromImStatus(model); + } + imStatusModelToIds[model] = ids; + }, + /** + * Unregister model from im_status notifications. + * + * @param {string} model model to unregister. + */ + unregisterFromImStatus(model) { + delete imStatusModelToIds[model]; + }, + }; }, }; diff --git a/addons/bus/static/tests/helpers/mock_server.js b/addons/bus/static/tests/helpers/mock_server.js index 43340b552b9..e3983320400 100644 --- a/addons/bus/static/tests/helpers/mock_server.js +++ b/addons/bus/static/tests/helpers/mock_server.js @@ -12,7 +12,13 @@ patch(MockServer.prototype, 'bus', { init() { this._super(...arguments); Object.assign(this, TEST_USER_IDS); - this.websocketWorker = patchWebsocketWorkerWithCleanup(); + const self = this; + this.websocketWorker = patchWebsocketWorkerWithCleanup({ + _sendToServer(message) { + self._performWebsocketRequest(message); + this._super(message); + }, + }); this.pendingLongpollingPromise = null; this.notificationsToBeResolved = []; this.lastBusNotificationId = 0; @@ -22,6 +28,19 @@ patch(MockServer.prototype, 'bus', { // Private //-------------------------------------------------------------------------- + /** + * @param {Object} message Message sent through the websocket to the + * server. + * @param {string} [message.event_name] + * @param {any} [message.data] + */ + _performWebsocketRequest({ event_name, data }) { + if (event_name === 'update_presence') { + const { inactivity_period, im_status_ids_by_model } = data; + this._mockIrWebsocket__updatePresence(inactivity_period, im_status_ids_by_model); + } + }, + /** * @override */ @@ -71,7 +90,6 @@ patch(MockServer.prototype, 'bus', { this.websocketWorker.broadcast('notification', values); }, - /** * Simulate the lost of the connection by simulating a closeEvent on * the worker websocket. 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 new file mode 100644 index 00000000000..147f0c8e3b1 --- /dev/null +++ b/addons/bus/static/tests/helpers/mock_server/models/ir_websocket.js @@ -0,0 +1,28 @@ +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { MockServer } from "@web/../tests/helpers/mock_server"; + +patch(MockServer.prototype, 'bus/models/ir_websocket', { + /** + * Simulates `_update_presence` on `ir.websocket`. + * + * @param inactivityPeriod + * @param imStatusIdsByModel + */ + _mockIrWebsocket__updatePresence(inactivityPeriod, imStatusIdsByModel) { + this._mockBusBus__sendone(this.currentPartnerId, 'bus/im_status', this._mockIrWebsocket__getImStatus(imStatusIdsByModel)); + }, + /** + * Simulates `_get_im_status` on `ir.websocket`. + * + * @param {Object} imStatusIdsByModel + * @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'] }) + }; + }, +}); diff --git a/addons/bus/websocket.py b/addons/bus/websocket.py index f1ab4e79a23..aecd42d50a9 100644 --- a/addons/bus/websocket.py +++ b/addons/bus/websocket.py @@ -766,7 +766,7 @@ class WebsocketRequest: if event_name == 'subscribe': ir_websocket._subscribe(data) if event_name == 'update_presence': - ir_websocket._update_bus_presence(data) + ir_websocket._update_bus_presence(**data) def _get_session(self): session = root.session_store.get(self.ws._session.sid) diff --git a/addons/hr_holidays/static/tests/qunit_suite_tests/components/persona_im_status_icon_tests.js b/addons/hr_holidays/static/tests/qunit_suite_tests/components/persona_im_status_icon_tests.js index c3f2d871e4d..c61006c8158 100644 --- a/addons/hr_holidays/static/tests/qunit_suite_tests/components/persona_im_status_icon_tests.js +++ b/addons/hr_holidays/static/tests/qunit_suite_tests/components/persona_im_status_icon_tests.js @@ -1,5 +1,7 @@ /** @odoo-module **/ +import { UPDATE_BUS_PRESENCE_DELAY } from '@bus/im_status_service'; + import { start, startServer } from '@mail/../tests/helpers/test_utils'; QUnit.module('hr_holidays', {}, function () { @@ -18,7 +20,7 @@ QUnit.test('on leave & online', async function (assert) { model: 'mail.channel', res_id: mailChannelId, }); - const { advanceTime, afterNextRender, messaging, openDiscuss } = await start({ + const { advanceTime, afterNextRender, openDiscuss } = await start({ discuss: { params: { default_active_id: mailChannelId, @@ -27,7 +29,7 @@ QUnit.test('on leave & online', async function (assert) { hasTimeControl: true, }); await openDiscuss(); - await afterNextRender(() => advanceTime(messaging.fetchImStatusTimerDuration)); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); assert.hasClass( document.querySelector('.o_PersonaImStatusIcon_icon'), 'o-online', @@ -52,7 +54,7 @@ QUnit.test('on leave & away', async function (assert) { model: 'mail.channel', res_id: mailChannelId, }); - const { advanceTime, afterNextRender, messaging, openDiscuss } = await start({ + const { advanceTime, afterNextRender, openDiscuss } = await start({ discuss: { params: { default_active_id: mailChannelId, @@ -61,7 +63,7 @@ QUnit.test('on leave & away', async function (assert) { hasTimeControl: true, }); await openDiscuss(); - await afterNextRender(() => advanceTime(messaging.fetchImStatusTimerDuration)); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); assert.hasClass( document.querySelector('.o_PersonaImStatusIcon_icon'), 'o-away', @@ -86,7 +88,7 @@ QUnit.test('on leave & offline', async function (assert) { model: 'mail.channel', res_id: mailChannelId, }); - const { advanceTime, afterNextRender, messaging, openDiscuss } = await start({ + const { advanceTime, afterNextRender, openDiscuss } = await start({ discuss: { params: { default_active_id: mailChannelId, @@ -95,7 +97,7 @@ QUnit.test('on leave & offline', async function (assert) { hasTimeControl: true, }); await openDiscuss(); - await afterNextRender(() => advanceTime(messaging.fetchImStatusTimerDuration)); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); assert.hasClass( document.querySelector('.o_PersonaImStatusIcon_icon'), 'o-offline', diff --git a/addons/hr_presence/models/ir_websocket.py b/addons/hr_presence/models/ir_websocket.py index 6ec159ca00c..37024ffc62f 100644 --- a/addons/hr_presence/models/ir_websocket.py +++ b/addons/hr_presence/models/ir_websocket.py @@ -9,8 +9,8 @@ from odoo.addons.bus.websocket import wsrequest class IrWebsocket(models.AbstractModel): _inherit = 'ir.websocket' - def _update_bus_presence(self, inactivity_period): - super()._update_bus_presence(inactivity_period) + def _update_bus_presence(self, inactivity_period, im_status_ids_by_model): + super()._update_bus_presence(inactivity_period, im_status_ids_by_model) # This method can either be called due to an http or a # websocket request. The request itself is necessary to # retrieve the current guest. Let's retrieve the proper diff --git a/addons/mail/controllers/bus.py b/addons/mail/controllers/bus.py index a12ec4cfb66..a737be6c0df 100644 --- a/addons/mail/controllers/bus.py +++ b/addons/mail/controllers/bus.py @@ -50,10 +50,3 @@ class MailChatController(BusController): return [] else: return channel._channel_fetch_message(last_id, limit) - - @route('/bus/im_status', type="json", auth="user") - def im_status(self, partner_ids, guest_ids=None): - im_status = super().im_status(partner_ids) - if guest_ids: - im_status['guests'] = request.env['mail.guest'].sudo().with_context(active_test=False).search([('id', 'in', guest_ids)]).read(['im_status']) - return im_status diff --git a/addons/mail/models/ir_websocket.py b/addons/mail/models/ir_websocket.py index bde8aa05cf4..1461e3c55ac 100644 --- a/addons/mail/models/ir_websocket.py +++ b/addons/mail/models/ir_websocket.py @@ -6,6 +6,12 @@ from odoo.addons.bus.websocket import wsrequest class IrWebsocket(models.AbstractModel): _inherit = 'ir.websocket' + 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']) + return im_status + def _build_bus_channel_list(self, channels): # This method can either be called due to an http or a # websocket request. The request itself is necessary to @@ -26,8 +32,8 @@ class IrWebsocket(models.AbstractModel): channels.append(mail_channel) return super()._build_bus_channel_list(channels) - def _update_bus_presence(self, inactivity_period): - super()._update_bus_presence(inactivity_period) + def _update_bus_presence(self, inactivity_period, im_status_ids_by_model): + super()._update_bus_presence(inactivity_period, im_status_ids_by_model) if not self.env.uid: # This method can either be called due to an http or a # websocket request. The request itself is necessary to diff --git a/addons/mail/static/src/models/messaging.js b/addons/mail/static/src/models/messaging.js index e861014a7d9..4eb12b6bbe5 100644 --- a/addons/mail/static/src/models/messaging.js +++ b/addons/mail/static/src/models/messaging.js @@ -17,6 +17,8 @@ registerModel({ this.refreshIsNotificationPermissionDefault(); }, _willDelete() { + this.env.services['im_status'].unregisterFromImStatus('res.partner'); + this.env.services['im_status'].unregisterFromImStatus('mail.guest'); this.env.bus.removeEventListener('window_focus', this._handleGlobalWindowFocus); delete odoo.__DEBUG__.messaging; }, @@ -93,9 +95,6 @@ registerModel({ const { message, ...options } = params; return this.env.services.notification.add(message, options); }, - onFetchImStatusTimerTimeout() { - this.startFetchImStatus(); - }, /** * Opens a chat with the provided person and returns it. * @@ -221,36 +220,19 @@ registerModel({ isNotificationPermissionDefault: Boolean(browserNotification) && browserNotification.permission === 'default', }); }, - async startFetchImStatus() { - this.update({ - fetchImStatusTimer: { doReset: this.fetchImStatusTimer ? true : undefined }, - }); + updateImStatusRegistration() { const partnerIds = []; for (const partner of this.models['Partner'].all()) { if (partner.im_status !== 'im_partner' && !partner.is_public) { partnerIds.push(partner.id); } } - if (partnerIds.length === 0) { - return; - } const guestIds = []; for (const guest of this.models['Guest'].all()) { guestIds.push(guest.id); } - if (partnerIds.length !== 0 || guestIds.length !== 0) { - const dataList = await this.messaging.rpc({ - route: '/bus/im_status', - params: { - partner_ids: partnerIds, - guest_ids: guestIds, - }, - }, { shadow: true }); - this.models['Partner'].insert(dataList.partners); - if (dataList.guests) { - this.models['Guest'].insert(dataList.guests); - } - } + this.env.services['im_status'].registerToImStatus('res.partner', partnerIds); + this.env.services['im_status'].registerToImStatus('mail.guest', guestIds); }, /** * @private @@ -329,6 +311,14 @@ registerModel({ this.env.services.bus_service.forceUpdateChannels(); } }, + /** + * @private + */ + _onChangeAllPersonas() { + if (this.isInitialized) { + this.updateImStatusRegisterThrottle.do(); + } + }, /** * @private */ @@ -344,6 +334,9 @@ registerModel({ allMailboxes: many('Mailbox', { inverse: 'messagingAsAnyMailbox', }), + allPersonas: many('Persona', { + inverse: 'messagingAsAnyPersona', + }), /** * Inverse of the messaging field present on all models. This field * therefore contains all existing records. @@ -404,12 +397,6 @@ registerModel({ isCausal: true, readonly: true, }), - fetchImStatusTimer: one('Timer', { - inverse: 'messagingOwnerAsFetchImStatusTimer', - }), - fetchImStatusTimerDuration: attr({ - default: 50 * 1000, - }), hasLinkPreviewFeature: attr(), history: one('Mailbox', { default: {}, @@ -516,6 +503,12 @@ registerModel({ isCausal: true, readonly: true, }), + updateImStatusRegisterThrottle: one('Throttle', { + compute() { + return { func: this.updateImStatusRegistration }; + }, + inverse: 'messagingAsUpdateImStatusRegister', + }), userSetting: one('UserSetting', { default: {}, isCausal: true, @@ -530,5 +523,9 @@ registerModel({ dependencies: ['allCurrentClientThreads'], methodName: '_onChangeAllCurrentClientThreads', }, + { + dependencies: ['allPersonas'], + methodName: '_onChangeAllPersonas', + }, ], }); diff --git a/addons/mail/static/src/models/messaging_initializer.js b/addons/mail/static/src/models/messaging_initializer.js index d9c11d9d7cf..4cc465c39ed 100644 --- a/addons/mail/static/src/models/messaging_initializer.js +++ b/addons/mail/static/src/models/messaging_initializer.js @@ -33,7 +33,7 @@ registerModel({ discuss.openInitThread(); } if (this.messaging.currentUser) { - this.messaging.startFetchImStatus(); + this.messaging.updateImStatusRegistration(); this._loadMessageFailures(); } }, diff --git a/addons/mail/static/src/models/messaging_notification_handler.js b/addons/mail/static/src/models/messaging_notification_handler.js index 9e976be4e3a..b1ee7197e36 100644 --- a/addons/mail/static/src/models/messaging_notification_handler.js +++ b/addons/mail/static/src/models/messaging_notification_handler.js @@ -48,6 +48,8 @@ registerModel({ const proms = notifications.map(message => { if (typeof message === 'object') { switch (message.type) { + case 'bus/im_status': + return this._handleNotificationBusImStatus(message.payload); case 'ir.attachment/delete': return this._handleNotificationAttachmentDelete(message.payload); case 'mail.channel.member/seen': @@ -149,6 +151,20 @@ registerModel({ * @param {Object} message */ _handleNotification(message) {}, + /** + * @private + * @param {Object} payload + * @param {Object[]} [payload.partners] + * @param {Object[]|undefined} [payload.guests] + */ + _handleNotificationBusImStatus({ partners, guests }) { + if (partners) { + this.models['Partner'].insert(partners); + } + if (guests) { + this.models['Guest'].insert(guests); + } + }, /** * @private * @param {Object} payload diff --git a/addons/mail/static/src/models/persona.js b/addons/mail/static/src/models/persona.js index 2715464437b..9bc324c40d6 100644 --- a/addons/mail/static/src/models/persona.js +++ b/addons/mail/static/src/models/persona.js @@ -60,6 +60,11 @@ registerModel({ im_status: attr({ compute: '_computeImStatus', }), + messagingAsAnyPersona: one('Messaging', { + default: {}, + inverse: 'allPersonas', + }), + name: attr({ compute: '_computeName', }), diff --git a/addons/mail/static/src/models/throttle.js b/addons/mail/static/src/models/throttle.js index 53143682023..2f433facd72 100644 --- a/addons/mail/static/src/models/throttle.js +++ b/addons/mail/static/src/models/throttle.js @@ -53,6 +53,9 @@ registerModel({ if (this.threadAsThrottleNotifyCurrentPartnerTypingStatus) { return 2.5 * 1000; } + if (this.messagingAsUpdateImStatusRegister) { + return 10 * 1000; + } return clear(); }, }, @@ -75,6 +78,10 @@ registerModel({ * Inner function to be invoked and throttled. */ func: attr(), + messagingAsUpdateImStatusRegister: one('Messaging', { + identifying: true, + inverse: 'updateImStatusRegisterThrottle', + }), shouldInvoke: attr({ default: false, }), diff --git a/addons/mail/static/src/models/timer.js b/addons/mail/static/src/models/timer.js index 29e2e057e91..8c312cba1e9 100644 --- a/addons/mail/static/src/models/timer.js +++ b/addons/mail/static/src/models/timer.js @@ -33,9 +33,6 @@ registerModel({ if (this.messageViewOwnerAsHighlight) { return 2 * 1000; } - if (this.messagingOwnerAsFetchImStatusTimer) { - return this.messagingOwnerAsFetchImStatusTimer.fetchImStatusTimerDuration; - } if (this.rtcSessionOwnerAsBroadcast) { return 3 * 1000; } @@ -74,10 +71,6 @@ registerModel({ this.messageViewOwnerAsHighlight.onHighlightTimerTimeout(); return; } - if (this.messagingOwnerAsFetchImStatusTimer) { - this.messagingOwnerAsFetchImStatusTimer.onFetchImStatusTimerTimeout(); - return; - } if (this.rtcSessionOwnerAsBroadcast) { this.rtcSessionOwnerAsBroadcast.onBroadcastTimeout(); return; @@ -134,10 +127,6 @@ registerModel({ compute: '_computeDuration', required: true, }), - messagingOwnerAsFetchImStatusTimer: one('Messaging', { - identifying: true, - inverse: 'fetchImStatusTimer', - }), messageViewOwnerAsHighlight: one('MessageView', { identifying: true, inverse: 'highlightTimer', diff --git a/addons/mail/static/tests/helpers/mock_server.js b/addons/mail/static/tests/helpers/mock_server.js index 31f61d9bbb9..67ce38180e2 100644 --- a/addons/mail/static/tests/helpers/mock_server.js +++ b/addons/mail/static/tests/helpers/mock_server.js @@ -62,12 +62,6 @@ patch(MockServer.prototype, 'mail', { */ async _performRPC(route, args) { // routes - if (route === '/bus/im_status') { - const { partner_ids } = args; - return { - 'partners': this.pyEnv['res.partner'].searchRead([['id', 'in', partner_ids]], { context: { 'active_test': false }, fields: ['im_status'] }) - }; - } if (route === '/mail/message/post') { if (args.thread_model === 'mail.channel') { return this._mockMailChannelMessagePost(args.thread_id, args.post_data, args.context); diff --git a/addons/mail/static/tests/helpers/webclient_setup.js b/addons/mail/static/tests/helpers/webclient_setup.js index 231ccc7b6b9..722ad413d9f 100644 --- a/addons/mail/static/tests/helpers/webclient_setup.js +++ b/addons/mail/static/tests/helpers/webclient_setup.js @@ -1,6 +1,7 @@ /** @odoo-module **/ import { busService } from '@bus/services/bus_service'; +import { imStatusService } from '@bus/im_status_service'; import { multiTabService } from '@bus/multi_tab_service'; import { makeMultiTabToLegacyEnv } from '@bus/services/legacy/make_multi_tab_to_legacy_env'; import { makeBusServiceToLegacyEnv } from '@bus/services/legacy/make_bus_service_to_legacy_env'; @@ -84,6 +85,7 @@ function setupMessagingServiceRegistries({ services = { bus_service: busService, + im_status: imStatusService, messaging: messagingService, messagingValues, presence: makeFakePresenceService({ 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 1248ebef2e0..3723f8cc3d7 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 @@ -1,5 +1,7 @@ /** @odoo-module **/ +import { UPDATE_BUS_PRESENCE_DELAY } from '@bus/im_status_service'; + import { start, startServer } from '@mail/../tests/helpers/test_utils'; QUnit.module('mail', {}, function () { @@ -18,7 +20,7 @@ QUnit.test('initially online', async function (assert) { model: 'mail.channel', res_id: mailChannelId, }); - const { advanceTime, afterNextRender, messaging, openDiscuss } = await start({ + const { advanceTime, afterNextRender, openDiscuss } = await start({ discuss: { params: { default_active_id: mailChannelId, @@ -27,7 +29,7 @@ QUnit.test('initially online', async function (assert) { hasTimeControl: true, }); await openDiscuss(); - await afterNextRender(() => advanceTime(messaging.fetchImStatusTimerDuration)); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); assert.strictEqual( document.querySelectorAll(`.o_PersonaImStatusIcon.o-online`).length, 1, @@ -47,7 +49,7 @@ QUnit.test('initially offline', async function (assert) { model: 'mail.channel', res_id: mailChannelId, }); - const { advanceTime, afterNextRender, messaging, openDiscuss } = await start({ + const { advanceTime, afterNextRender, openDiscuss } = await start({ discuss: { params: { default_active_id: mailChannelId, @@ -56,7 +58,7 @@ QUnit.test('initially offline', async function (assert) { hasTimeControl: true, }); await openDiscuss(); - await afterNextRender(() => advanceTime(messaging.fetchImStatusTimerDuration)); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); assert.strictEqual( document.querySelectorAll(`.o_PersonaImStatusIcon.o-offline`).length, 1, @@ -76,7 +78,7 @@ QUnit.test('initially away', async function (assert) { model: 'mail.channel', res_id: mailChannelId, }); - const { advanceTime, afterNextRender, messaging, openDiscuss } = await start({ + const { advanceTime, afterNextRender, openDiscuss } = await start({ discuss: { params: { default_active_id: mailChannelId, @@ -85,7 +87,7 @@ QUnit.test('initially away', async function (assert) { hasTimeControl: true, }); await openDiscuss(); - await afterNextRender(() => advanceTime(messaging.fetchImStatusTimerDuration)); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); assert.strictEqual( document.querySelectorAll(`.o_PersonaImStatusIcon.o-away`).length, 1, @@ -105,7 +107,7 @@ QUnit.test('change icon on change partner im_status', async function (assert) { model: 'mail.channel', res_id: mailChannelId, }); - const { advanceTime, afterNextRender, messaging, openDiscuss } = await start({ + const { advanceTime, afterNextRender, openDiscuss } = await start({ discuss: { params: { default_active_id: mailChannelId, @@ -114,7 +116,7 @@ QUnit.test('change icon on change partner im_status', async function (assert) { hasTimeControl: true, }); await openDiscuss(); - await afterNextRender(() => advanceTime(messaging.fetchImStatusTimerDuration)); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); assert.strictEqual( document.querySelectorAll(`.o_PersonaImStatusIcon.o-online`).length, 1, @@ -122,7 +124,7 @@ QUnit.test('change icon on change partner im_status', async function (assert) { ); pyEnv['res.partner'].write([partnerId], { im_status: 'offline' }); - await afterNextRender(() => advanceTime(messaging.fetchImStatusTimerDuration)); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); assert.strictEqual( document.querySelectorAll(`.o_PersonaImStatusIcon.o-offline`).length, 1, @@ -130,7 +132,7 @@ QUnit.test('change icon on change partner im_status', async function (assert) { ); pyEnv['res.partner'].write([partnerId], { im_status: 'away' }); - await afterNextRender(() => advanceTime(messaging.fetchImStatusTimerDuration)); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); assert.strictEqual( document.querySelectorAll(`.o_PersonaImStatusIcon.o-away`).length, 1, @@ -138,7 +140,7 @@ QUnit.test('change icon on change partner im_status', async function (assert) { ); pyEnv['res.partner'].write([partnerId], { im_status: 'online' }); - await afterNextRender(() => advanceTime(messaging.fetchImStatusTimerDuration)); + await afterNextRender(() => advanceTime(UPDATE_BUS_PRESENCE_DELAY)); assert.strictEqual( document.querySelectorAll(`.o_PersonaImStatusIcon.o-online`).length, 1,