[IMP] mail: remove im status route
*: bus, hr_holidays. The `/bus/im_status` route polls the server every minute in order for the user im_status to be up to date. This commit removes this poll by sending the im_status on the bus when updating the current user presence. Moreover, before [1], the user bus presence was updated on each poll. When the user didn't poll for 50 seconds, we assumed the user was disconnected. Since [1], the bus presence is updated each 30 seconds by the `im_status` service. This is too frequent: there is no need to update the user presence so often. In order not to overhelm the server with unnecessary requests, the update presence interval as well as the delay to be considered disconnected have been updated: the former from 30 to 60 seconds, the later from 55 to 65 seconds (assuming that a user that has missed an update presence tick is disconnected). [1]: odoo/odoo@a5623d2 closes odoo/odoo#100249 Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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];
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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'] })
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -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)
|
||||
|
||||
+8
-6
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ registerModel({
|
||||
discuss.openInitThread();
|
||||
}
|
||||
if (this.messaging.currentUser) {
|
||||
this.messaging.startFetchImStatus();
|
||||
this.messaging.updateImStatusRegistration();
|
||||
this._loadMessageFailures();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -60,6 +60,11 @@ registerModel({
|
||||
im_status: attr({
|
||||
compute: '_computeImStatus',
|
||||
}),
|
||||
messagingAsAnyPersona: one('Messaging', {
|
||||
default: {},
|
||||
inverse: 'allPersonas',
|
||||
}),
|
||||
|
||||
name: attr({
|
||||
compute: '_computeName',
|
||||
}),
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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({
|
||||
|
||||
+13
-11
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user