[FIX] mail: no wait init_messaging before loading webclient
Before this commit, discuss widgets had to wait for the route `mail/init_messaging`. This behaviour slowed down the loading of the web client. This commit solves the issue by rendering discuss widgets even when `mail/init_messaging` hasn't been resolved. For instance, the messaging menu displays a spinner until messaging becomes ready. Task-Id 2072835 closes odoo/odoo#37031 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
@@ -281,6 +281,8 @@ var Discuss = AbstractAction.extend({
|
||||
this.options.channelQuickSearchThreshold = 20;
|
||||
}
|
||||
|
||||
this._isMessagingReady = this.call('mail_service', 'isReady');
|
||||
this._isStarted = false;
|
||||
this._threadsScrolltop = {};
|
||||
this._composerStates = {};
|
||||
this._defaultThreadID = this.options.active_id ||
|
||||
@@ -292,68 +294,20 @@ var Discuss = AbstractAction.extend({
|
||||
this._updateThreads.bind(this), 100, { leading: false });
|
||||
|
||||
this.controlPanelParams.modelName = 'mail.message';
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
willStart: function () {
|
||||
return Promise.all([this._super(), this.call('mail_service', 'isReady')]);
|
||||
this.call('mail_service', 'getMailBus').on('messaging_ready', this, this._onMessagingReady);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
var self = this;
|
||||
|
||||
this._basicComposer = new BasicComposer(this, {
|
||||
mentionPartnersRestricted: true,
|
||||
showTyping: true,
|
||||
this._isStarted = true;
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
if (!self._isMessagingReady) {
|
||||
return;
|
||||
}
|
||||
return self._initRender();
|
||||
});
|
||||
this._extendedComposer = new ExtendedComposer(this, {
|
||||
mentionPartnersRestricted: true,
|
||||
showTyping: true,
|
||||
});
|
||||
this._basicComposer
|
||||
.on('post_message', this, this._onPostMessage)
|
||||
.on('input_focused', this, this._onComposerFocused);
|
||||
this._extendedComposer
|
||||
.on('post_message', this, this._onPostMessage)
|
||||
.on('input_focused', this, this._onComposerFocused);
|
||||
this._renderButtons();
|
||||
|
||||
var defs = [];
|
||||
defs.push(
|
||||
this._renderThread()
|
||||
);
|
||||
defs.push(
|
||||
this._basicComposer.appendTo(this.$('.o_mail_discuss_content')));
|
||||
defs.push(
|
||||
this._extendedComposer.appendTo(this.$('.o_mail_discuss_content')));
|
||||
defs.push(this._super.apply(this, arguments));
|
||||
|
||||
return Promise.all(defs)
|
||||
.then(function () {
|
||||
return self._setThread(self._defaultThreadID);
|
||||
})
|
||||
.then(function () {
|
||||
self._initThreads();
|
||||
self._startListening();
|
||||
self._threadWidget.$el.on('scroll', null, _.debounce(function () {
|
||||
if (
|
||||
self._threadWidget.getScrolltop() < 20 &&
|
||||
!self._threadWidget.$('.o_mail_no_content').length &&
|
||||
!self._thread.isAllHistoryLoaded(self.domain)
|
||||
) {
|
||||
self._loadMoreMessages();
|
||||
}
|
||||
if (
|
||||
self._threadWidget.isAtBottom() &&
|
||||
self._thread.getType() !== 'mailbox'
|
||||
) {
|
||||
self._thread.markAsRead();
|
||||
}
|
||||
}, 100));
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
@@ -382,8 +336,8 @@ var Discuss = AbstractAction.extend({
|
||||
this.call('mail_service', 'getMailBus').trigger('discuss_open', true);
|
||||
if (this._thread) {
|
||||
this._threadWidget.scrollToPosition(this._threadsScrolltop[this._thread.getID()]);
|
||||
this._loadEnoughMessages();
|
||||
}
|
||||
this._loadEnoughMessages();
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
@@ -547,6 +501,60 @@ var Discuss = AbstractAction.extend({
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_initRender: function () {
|
||||
var self = this;
|
||||
this._basicComposer = new BasicComposer(this, {
|
||||
mentionPartnersRestricted: true,
|
||||
showTyping: true,
|
||||
});
|
||||
this._extendedComposer = new ExtendedComposer(this, {
|
||||
mentionPartnersRestricted: true,
|
||||
showTyping: true,
|
||||
});
|
||||
this._basicComposer
|
||||
.on('post_message', this, this._onPostMessage)
|
||||
.on('input_focused', this, this._onComposerFocused);
|
||||
this._extendedComposer
|
||||
.on('post_message', this, this._onPostMessage)
|
||||
.on('input_focused', this, this._onComposerFocused);
|
||||
this._renderButtons();
|
||||
|
||||
var defs = [];
|
||||
defs.push(
|
||||
this._renderThread()
|
||||
);
|
||||
defs.push(
|
||||
this._basicComposer.appendTo(this.$('.o_mail_discuss_content')));
|
||||
defs.push(
|
||||
this._extendedComposer.appendTo(this.$('.o_mail_discuss_content')));
|
||||
|
||||
return Promise.all(defs)
|
||||
.then(function () {
|
||||
return self._setThread(self._defaultThreadID);
|
||||
})
|
||||
.then(function () {
|
||||
self._initThreads();
|
||||
self._startListening();
|
||||
self._threadWidget.$el.on('scroll', null, _.debounce(function () {
|
||||
if (
|
||||
self._threadWidget.getScrolltop() < 20 &&
|
||||
!self._threadWidget.$('.o_mail_no_content').length &&
|
||||
!self._thread.isAllHistoryLoaded(self.domain)
|
||||
) {
|
||||
self._loadMoreMessages();
|
||||
}
|
||||
if (
|
||||
self._threadWidget.isAtBottom() &&
|
||||
self._thread.getType() !== 'mailbox'
|
||||
) {
|
||||
self._thread.markAsRead();
|
||||
}
|
||||
}, 100));
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Renders the mainside bar with current threads
|
||||
*
|
||||
@@ -1401,6 +1409,20 @@ var Discuss = AbstractAction.extend({
|
||||
this._fetchAndRenderThread();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onMessagingReady: function () {
|
||||
if (this._isMessagingReady) {
|
||||
return;
|
||||
}
|
||||
this._isMessagingReady = true;
|
||||
if (!this._isStarted) {
|
||||
return;
|
||||
}
|
||||
this.$('.o_mail_discuss_loading').remove();
|
||||
this._initRender();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} ev
|
||||
|
||||
@@ -34,7 +34,7 @@ MailManager.include({
|
||||
this.call('local_storage', 'getItem', key);
|
||||
}
|
||||
}
|
||||
this.isReady().then(this._updateDocumentThreadWindows.bind(this, this._documentThreadWindowStates));
|
||||
this._mailBus.on('messaging_ready', this, this._updateDocumentThreadWindows.bind(this, this._documentThreadWindowStates));
|
||||
// listen to localStorage changes to synchronize DocumentThread's
|
||||
// windows between tabs
|
||||
this.call('local_storage', 'onStorage', this, this._onStorage);
|
||||
|
||||
@@ -46,6 +46,7 @@ var PREVIEW_MSG_MAX_SIZE = 350; // optimal for native english speakers
|
||||
|
||||
var MailManager = AbstractService.extend({
|
||||
dependencies: ['ajax', 'bus_service', 'local_storage'],
|
||||
_isReady: false,
|
||||
_ODOOBOT_ID: ["ODOOBOT", "ODOOBOT"], // authorID for transient messages
|
||||
|
||||
/**
|
||||
@@ -294,7 +295,7 @@ var MailManager = AbstractService.extend({
|
||||
* This is the case when it has fetched the initial state from the server,
|
||||
* by means of the route 'mail/init_messaging'
|
||||
*
|
||||
* @returns {Promise}
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isReady: function () {
|
||||
return this._isReady;
|
||||
@@ -636,7 +637,7 @@ var MailManager = AbstractService.extend({
|
||||
*/
|
||||
_fetchMailStateFromServer: function () {
|
||||
var self = this;
|
||||
this._isReady = session.is_bound.then(function () {
|
||||
session.is_bound.then(function () {
|
||||
var context = _.extend(
|
||||
{ isMobile: config.device.isMobile },
|
||||
session.user_context
|
||||
@@ -648,6 +649,8 @@ var MailManager = AbstractService.extend({
|
||||
}).then(function (result) {
|
||||
self._updateInternalStateFromServer(result);
|
||||
self.call('bus_service', 'startPolling');
|
||||
self._isReady = true;
|
||||
self._mailBus.trigger('messaging_ready');
|
||||
});
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -19,9 +19,6 @@ var ActivityMenu = Widget.extend({
|
||||
'click .o_mail_preview': '_onActivityFilterClick',
|
||||
'show.bs.dropdown': '_onActivityMenuShow',
|
||||
},
|
||||
willStart: function () {
|
||||
return Promise.resolve(this.call('mail_service', 'isReady'));
|
||||
},
|
||||
start: function () {
|
||||
this._$activitiesPreview = this.$('.o_mail_systray_dropdown_items');
|
||||
this.call('mail_service', 'getMailBus').on('activity_updated', this, this._updateCounter);
|
||||
|
||||
@@ -26,12 +26,6 @@ var MessagingMenu = Widget.extend({
|
||||
'click .o_mail_preview_mark_as_read': '_onClickPreviewMarkAsRead',
|
||||
'show.bs.dropdown': '_onShowDropdown',
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
willStart: function () {
|
||||
return Promise.all([this._super.apply(this, arguments), this.call('mail_service', 'isReady')]);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
@@ -39,8 +33,10 @@ var MessagingMenu = Widget.extend({
|
||||
this._$filterButtons = this.$('.o_filter_button');
|
||||
this._$previews = this.$('.o_mail_systray_dropdown_items');
|
||||
this._filter = false;
|
||||
this._isMessagingReady = this.call('mail_service', 'isReady');
|
||||
this._updateCounter();
|
||||
var mailBus = this.call('mail_service', 'getMailBus');
|
||||
mailBus.on('messaging_ready', this, this._onMessagingReady);
|
||||
mailBus.on('update_needaction', this, this._updateCounter);
|
||||
mailBus.on('new_channel', this, this._updateCounter);
|
||||
mailBus.on('update_thread_unread_counter', this, this._updateCounter);
|
||||
@@ -206,6 +202,9 @@ var MessagingMenu = Widget.extend({
|
||||
_updatePreviews: function () {
|
||||
// Display spinner while waiting for conversations preview
|
||||
this._$previews.html(QWeb.render('Spinner'));
|
||||
if (!this._isMessagingReady) {
|
||||
return;
|
||||
}
|
||||
this._getPreviews()
|
||||
.then(this._renderPreviews.bind(this));
|
||||
},
|
||||
@@ -228,7 +227,11 @@ var MessagingMenu = Widget.extend({
|
||||
* @private
|
||||
*/
|
||||
_updateCounter: function () {
|
||||
if (!this._isMessagingReady) {
|
||||
return;
|
||||
}
|
||||
var counter = this._computeCounter();
|
||||
this.$('.o_mail_messaging_menu_icon').removeClass('fa-spinner fa-spin');
|
||||
this.$('.o_notification_counter').text(counter);
|
||||
this.$el.toggleClass('o_no_notification', !counter);
|
||||
if (this._isShown()) {
|
||||
@@ -324,6 +327,16 @@ var MessagingMenu = Widget.extend({
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onMessagingReady: function () {
|
||||
if (this._isMessagingReady) {
|
||||
return;
|
||||
}
|
||||
this._isMessagingReady = true;
|
||||
this._updateCounter();
|
||||
},
|
||||
});
|
||||
|
||||
// Systray menu items display order matches order in the list
|
||||
|
||||
@@ -20,23 +20,21 @@ var ThreadField = AbstractField.extend({
|
||||
*/
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this._isMessagingReady = this.call('mail_service', 'isReady');
|
||||
this._isStarted = false;
|
||||
this.call('mail_service', 'getMailBus').on('messaging_ready', this, this._onMessagingReady);
|
||||
// Used to automatically mark document thread as read at the moment we
|
||||
// access the document and render the thread.
|
||||
this._markAsReadOnNextRender = false;
|
||||
this._setDocumentThread();
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
willStart: function () {
|
||||
return this.alive(this.call('mail_service', 'isReady'));
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
var self = this;
|
||||
|
||||
this._isStarted = true;
|
||||
this.dp = new concurrency.DropPrevious();
|
||||
|
||||
this._threadWidget = new ThreadWidget(this, {
|
||||
@@ -159,6 +157,9 @@ var ThreadField = AbstractField.extend({
|
||||
* If it is a new document in create mode, unset the document thread.
|
||||
*/
|
||||
_setDocumentThread: function () {
|
||||
if (!this._isMessagingReady) {
|
||||
return;
|
||||
}
|
||||
var params = {
|
||||
messageIDs: this.value.res_ids,
|
||||
name: this.recordData.display_name,
|
||||
@@ -185,6 +186,19 @@ var ThreadField = AbstractField.extend({
|
||||
_onLoadMoreMessages: function () {
|
||||
this._fetchAndRenderThread({ forceFetch: true });
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onMessagingReady: function () {
|
||||
if (this._isMessagingReady) {
|
||||
return;
|
||||
}
|
||||
this._isMessagingReady = true;
|
||||
if (!this._isStarted) {
|
||||
return;
|
||||
}
|
||||
this._setDocumentThread();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {mail.model.Message}
|
||||
|
||||
@@ -7,6 +7,18 @@
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.o_mail_discuss_loading {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100%;
|
||||
|
||||
> i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.o_mail_discuss_sidebar {
|
||||
width: $o-mail-chat-sidebar-width;
|
||||
height: 100%;
|
||||
|
||||
@@ -8,8 +8,15 @@
|
||||
-->
|
||||
<t t-name="mail.discuss">
|
||||
<div class="o_mail_discuss">
|
||||
<div class="o_mail_discuss_sidebar"/>
|
||||
<div class="o_mail_discuss_sidebar">
|
||||
<div class="o_mail_discuss_loading">
|
||||
<i class="fa fa-spinner fa-spin"/> Please wait...
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_mail_discuss_content">
|
||||
<div class="o_mail_discuss_loading">
|
||||
<i class="fa fa-spinner fa-spin"/> Please wait...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<t t-name="mail.systray.MessagingMenu">
|
||||
<li class="o_mail_systray_item">
|
||||
<a class="dropdown-toggle o-no-caret" data-toggle="dropdown" data-display="static" aria-expanded="false" title="Conversations" href="#" role="button">
|
||||
<i class="fa fa-comments" role="img" aria-label="Messages"/> <span class="o_notification_counter badge badge-pill"/>
|
||||
<i class="o_mail_messaging_menu_icon fa fa-comments fa-spinner fa-spin" role="img" aria-label="Messages"/> <span class="o_notification_counter badge badge-pill"/>
|
||||
</a>
|
||||
<div class="o_mail_systray_dropdown dropdown-menu dropdown-menu-right" role="menu">
|
||||
<t t-if="widget.isMobile()">
|
||||
|
||||
@@ -115,6 +115,56 @@ QUnit.test('basic rendering', async function (assert) {
|
||||
discuss.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('messaging not ready', async function (assert) {
|
||||
assert.expect(9);
|
||||
|
||||
const messagingReadyProm = testUtils.makeTestPromise();
|
||||
const discuss = await createDiscuss({
|
||||
id: 1,
|
||||
context: {},
|
||||
params: {},
|
||||
data: this.data,
|
||||
services: this.services,
|
||||
async mockRPC(route, args) {
|
||||
if (route === '/mail/init_messaging') {
|
||||
const _super = this._super.bind(this, ...arguments); // limitation of class.js
|
||||
assert.step('/mail/init_messaging:pending');
|
||||
await messagingReadyProm;
|
||||
assert.step('/mail/init_messaging:resolved');
|
||||
return _super();
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
assert.verifySteps(['/mail/init_messaging:pending']);
|
||||
assert.ok(
|
||||
discuss.el,
|
||||
"discuss should be rendered");
|
||||
assert.strictEqual(
|
||||
$('.o_action')[0],
|
||||
discuss.el,
|
||||
"should display discuss even when messaging is not ready");
|
||||
assert.containsOnce(
|
||||
discuss,
|
||||
'.o_mail_discuss_sidebar .o_mail_discuss_loading',
|
||||
"should display sidebar is loading (messaging not yet ready)");
|
||||
assert.containsOnce(
|
||||
discuss,
|
||||
'.o_mail_discuss_content .o_mail_discuss_loading',
|
||||
"should display content is loading (messaging not yet ready)");
|
||||
|
||||
messagingReadyProm.resolve();
|
||||
await testUtils.nextTick();
|
||||
assert.verifySteps(['/mail/init_messaging:resolved']);
|
||||
assert.containsNone(
|
||||
discuss,
|
||||
'.o_mail_discuss_loading',
|
||||
"should no longer display sidebar or content is loading (messaging is ready)");
|
||||
|
||||
discuss.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('searchview options visibility', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
|
||||
@@ -120,6 +120,53 @@ QUnit.test('messaging menu widget: menu with no records', async function (assert
|
||||
messagingMenu.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('messaging menu widget: messaging not ready', async function (assert) {
|
||||
assert.expect(8);
|
||||
|
||||
const messagingReadyProm = testUtils.makeTestPromise();
|
||||
|
||||
const messagingMenu = new MessagingMenu();
|
||||
testUtils.mock.addMockEnvironment(messagingMenu, {
|
||||
services: this.services,
|
||||
async mockRPC(route, args) {
|
||||
if (route === '/mail/init_messaging') {
|
||||
const _super = this._super.bind(this, ...arguments); // limitation of class.js
|
||||
assert.step('/mail/init_messaging:pending');
|
||||
await messagingReadyProm;
|
||||
assert.step('/mail/init_messaging:resolved');
|
||||
return _super();
|
||||
}
|
||||
if (args.method === 'message_fetch') {
|
||||
return [];
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
await messagingMenu.appendTo($('#qunit-fixture'));
|
||||
assert.verifySteps(['/mail/init_messaging:pending']);
|
||||
assert.ok(
|
||||
messagingMenu.el,
|
||||
"messaging menu should be rendered");
|
||||
assert.strictEqual(
|
||||
document.querySelector('.o_mail_systray_item'),
|
||||
messagingMenu.el,
|
||||
"should display messaging menu even when messaging is not ready");
|
||||
assert.hasClass(
|
||||
messagingMenu.$('.o_mail_messaging_menu_icon'),
|
||||
'fa-spinner',
|
||||
"Messaging menu icon should be a spinner when messaging menu is not ready");
|
||||
|
||||
messagingReadyProm.resolve();
|
||||
await testUtils.nextTick();
|
||||
assert.verifySteps(['/mail/init_messaging:resolved']);
|
||||
assert.doesNotHaveClass(
|
||||
messagingMenu.$('.o_mail_messaging_menu_icon'),
|
||||
'fa-spinner',
|
||||
"Messaging menu icon should no longer be a spinner when messaging menu is ready");
|
||||
|
||||
messagingMenu.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('messaging menu widget: messaging menu with 1 record', async function (assert) {
|
||||
assert.expect(3);
|
||||
var messagingMenu = new MessagingMenu();
|
||||
|
||||
Reference in New Issue
Block a user