[FIX] mail: correctly handle (non-)passive thread windows

Definition of '(non-)passive' thread window:

  Thread windows can be either 'passive' or 'active'
  (default: 'active').

  - When opening a thread window in 'active' mode, the messages
    are marked as read, and the focus is on the thread window.
  - When opening a thread window in 'passive' mode, the messages
    are not marked as read and there is no focus on the thread
    window. Messages are kept unread until the user interacts with
    the thread window (e.g. by clicking on composer).

  When the user intentionally opens a thread window, it should
  be open in active mode. However, when a thread window is open
  without any user iteractions, it should open in passive mode.

  The later case is useful in some cases. For instance, when
  receiving a new message from a DM conversation, it should
  automatically open the corresponding thread window. However,
  it should not automatically mark messages as read.

This commit contains 2 bug fixes:

1. When receiving a new message on an active thread window,
   it no longer increments the unread counter.

   (When the focus is on the composer of the thread window,
   the user is immediately reading newly received messages.)

2. When receiving a new message from an unpinned DM conversation,
   the newly open thread window is always passive.

   (This was not the case before, so when receiving new messages,
   it did not increment the unread counter.)
This commit is contained in:
Alexandre Kühn
2018-08-30 13:30:12 +02:00
parent be44efe852
commit f0f81e8db3
4 changed files with 131 additions and 3 deletions
@@ -641,7 +641,7 @@ MailManager.include({
*/
_onNewChannel: function (channel) {
if (channel.isDetached()) {
this.openThreadWindow(channel.getID(), { keepFoldState: true });
this.openThreadWindow(channel.getID(), { keepFoldState: true, passively: true });
} else {
this._closeThreadWindow(channel.getID());
}
@@ -349,6 +349,16 @@ var AbstractThreadWindow = Widget.extend({
}
return this._thread.getID();
},
/**
* Tells whether there is focus on this thread. Note that a thread that has
* the focus means the input has focus.
*
* @private
* @returns {boolean}
*/
_hasFocus: function () {
return this.$input.is(':focus');
},
/**
* Post a message on this thread window, and auto-scroll to the bottom of
* the thread.
@@ -164,11 +164,11 @@ var ThreadWindow = AbstractThreadWindow.extend({
* should scroll to the bottom if it was at the bottom before update
* @param {boolean} [options.passively=false] if set, this thread window
* becomes passive, so that it is marked as read only when the focus is
* on it.
* on it. Ignore this option if the focus is already on the thread window.
*/
update: function (options) {
var self = this;
if (options.passively) {
if (options.passively && !this._hasFocus()) {
this._setPassive();
}
var bottomVisible = !this.isFolded() &&
@@ -235,6 +235,124 @@ QUnit.test('do not increase unread counter when receiving message with myself as
parent.destroy();
});
QUnit.test('do not increment unread counter with focus on thread window', function (assert) {
// 'hard' focus means that the user has clicked on the thread window in
// order to set the focus on it.
assert.expect(2);
var parent = this.createParent({
data: this.data,
services: this.services,
session: { partner_id: 3 }
});
// get channel instance to link to thread window
var channel = parent.call('mail_service', 'getChannel', 1);
channel.detach();
assert.strictEqual(channel.getUnreadCounter(), 0,
"thread should have unread counter to 0 initially");
// hard focus on thread window composer
$('.o_composer_text_field').click();
// simulate receiving message from someone else
var messageData = {
author_id: [5, "Someone else"],
body: "<p>Test message</p>",
id: 2,
model: 'mail.channel',
res_id: 1,
channel_ids: [1],
};
var notification = [[false, 'mail.channel', 1], messageData];
parent.call('bus_service', 'trigger', 'notification', [notification]);
assert.strictEqual(channel.getUnreadCounter(), 0,
"thread should not have incremented its unread counter after receiving the message");
parent.destroy();
});
QUnit.test('do not mark as read the newly open thread window from received message', function (assert) {
assert.expect(5);
this.data['mail.channel'] = {
fields: {
name: {
string: "Name",
type: "char",
required: true,
},
channel_type: {
string: "Channel Type",
type: "selection",
},
channel_message_ids: {
string: "Messages",
type: "many2many",
relation: 'mail.message'
},
message_unread_counter: {
string: "Amount of Unread Messages",
type: "integer"
},
},
records: [{
id: 2,
name: "DM",
channel_type: "chat",
message_unread_counter: 0,
}],
};
var parent = this.createParent({
data: this.data,
services: this.services,
session: { partner_id: 3 },
mockRPC: function (route, args) {
if (args.method === 'channel_join_and_get_info') {
this.data['mail.channel'].records[0].state = 'open';
this.data['mail.channel'].records[0].is_minimized = true;
return $.when(this.data['mail.channel'].records[0]);
}
return this._super.apply(this, arguments);
},
});
assert.strictEqual($('.o_thread_window').length, 0,
"no thread window should be open initially");
// simulate receiving message from someone else in DM
var messageData = {
author_id: [5, "Someone else"],
body: "<p>Test message</p>",
id: 2,
model: 'mail.channel',
res_id: 1,
channel_ids: [2],
};
var notification = [[false, 'mail.channel', 1], messageData];
// Unread counter is obtained from fetched channel data, because this is
// a new message from a new channel.
this.data['mail.channel'].records[0].message_unread_counter++;
parent.call('bus_service', 'trigger', 'notification', [notification]);
var $threadWindow = $('.o_thread_window');
assert.strictEqual($threadWindow.length, 1,
"a thread window should be open after receiving a new message on a new DM chat");
assert.strictEqual($threadWindow.data('thread-id'), 2,
"open thread window should be related to DM chat");
assert.strictEqual($threadWindow.find('.o_thread_window_title').text().replace(/\s/g, ''), '#DM(1)',
"open DM chat window should have one unread message");
$threadWindow.find('.o_thread_composer').click();
assert.strictEqual($threadWindow.find('.o_thread_window_title').text().replace(/\s/g, ''), '#DM',
"open DM chat window should have message marked as read on composer focus");
parent.destroy();
});
});
});
});