[IMP] mail: needactions and messages read
Mainly concern the client action: - Display needactions per channels - For all channels except Inbox, clear needactions when opening the channel - Add snackbar to undo needactions clearing - Display a separator between read and unread messages in threads - Fix active channel (for Inbox and Starred) display in sidebar Various minor changes in chat_manager: - Code refactoring of bus notification handler - get_channel() don't fallback anymore on first channel
This commit is contained in:
committed by
Géry Debongnie
parent
dbc1061854
commit
5264ccf0d0
@@ -396,6 +396,7 @@ class Channel(models.Model):
|
||||
if self.channel_message_ids.ids:
|
||||
last_message_id = self.channel_message_ids.ids[0] # zero is the index of the last message
|
||||
self.env['mail.channel.partner'].search([('channel_id', 'in', self.ids), ('partner_id', '=', self.env.user.partner_id.id)]).write({'seen_message_id': last_message_id})
|
||||
return last_message_id
|
||||
|
||||
@api.multi
|
||||
def channel_invite(self, partner_ids):
|
||||
|
||||
@@ -146,52 +146,84 @@ class Message(models.Model):
|
||||
# Notification API
|
||||
#------------------------------------------------------
|
||||
|
||||
@api.multi
|
||||
def set_message_needaction(self, partner_ids=None):
|
||||
if not partner_ids:
|
||||
partner_ids = [self.env.user.partner_id.id]
|
||||
if set(partner_ids) == set([self.env.user.partner_id.id]):
|
||||
# a user should be able to mark a message as needaction for him
|
||||
self = self.sudo()
|
||||
return self.write({'needaction_partner_ids': [(4, pid) for pid in partner_ids]})
|
||||
|
||||
@api.multi
|
||||
def mark_all_as_read(self):
|
||||
user_id = self.env.user.partner_id.id
|
||||
|
||||
@api.model
|
||||
def mark_all_as_read(self, channel_ids=None):
|
||||
""" Remove all needactions of the current partner. If channel_ids is
|
||||
given, restrict to messages written in one of those channels. """
|
||||
partner_id = self.env.user.partner_id.id
|
||||
# possibly horribly inefficient method:
|
||||
# it does one db request for the search, and one for each message in
|
||||
# the result set to remove the current user from the relation.
|
||||
# unread_messages = self.search([('needaction_partner_ids', 'in', user_id)])
|
||||
# unread_messages.write({'needaction_partner_ids': [(3, user_id)]})
|
||||
# domain = [('needaction_partner_ids', 'in', partner_id)]
|
||||
# if channel_ids:
|
||||
# domain += [('channel_ids', 'in', channel_ids)]
|
||||
# unread_messages = self.search(domain)
|
||||
# unread_messages.write({'needaction_partner_ids': [(3, partner_id)]})
|
||||
|
||||
# a much faster way to do this is in pure sql:
|
||||
self.env.cr.execute("DELETE FROM mail_message_res_partner_needaction_rel WHERE res_partner_id IN %s", [(user_id,)])
|
||||
query = "DELETE FROM mail_message_res_partner_needaction_rel WHERE res_partner_id IN %s"
|
||||
args = [(partner_id,)]
|
||||
if channel_ids:
|
||||
query += """
|
||||
AND mail_message_id in
|
||||
(SELECT mail_message_id
|
||||
FROM mail_message_mail_channel_rel
|
||||
WHERE mail_channel_id in %s)"""
|
||||
args += [tuple(channel_ids)]
|
||||
query += " RETURNING mail_message_id as id"
|
||||
self._cr.execute(query, args)
|
||||
self.invalidate_cache()
|
||||
|
||||
ids = [m['id'] for m in self._cr.dictfetchall()]
|
||||
notification = {'type': 'mark_as_read', 'message_ids': ids, 'channel_ids': channel_ids}
|
||||
self.env['bus.bus'].sendone((self._cr.dbname, 'res.partner', self.env.user.partner_id.id), notification)
|
||||
|
||||
return ids
|
||||
|
||||
@api.multi
|
||||
def mark_as_unread(self, channel_ids=None):
|
||||
""" Add needactions to messages for the current partner. """
|
||||
partner_id = self.env.user.partner_id.id
|
||||
for message in self:
|
||||
message.write({'needaction_partner_ids': [(4, partner_id)]})
|
||||
|
||||
ids = [m.id for m in self]
|
||||
notification = {'type': 'mark_as_unread', 'message_ids': ids, 'channel_ids': channel_ids}
|
||||
self.env['bus.bus'].sendone((self._cr.dbname, 'res.partner', self.env.user.partner_id.id), notification)
|
||||
|
||||
@api.multi
|
||||
def set_message_done(self, partner_ids=None):
|
||||
""" Remove the needaction from messages for the current partner, or for
|
||||
partners in partner_ids if partner_ids is given. """
|
||||
if not partner_ids:
|
||||
partner_ids = [self.env.user.partner_id.id]
|
||||
if set(partner_ids) == set([self.env.user.partner_id.id]):
|
||||
# a user should be able to mark a message as done for him
|
||||
self = self.sudo()
|
||||
return self.write({'needaction_partner_ids': [(3, pid) for pid in partner_ids]})
|
||||
self.write({'needaction_partner_ids': [(3, pid) for pid in partner_ids]})
|
||||
|
||||
channel_ids = [c.id for c in self.channel_ids]
|
||||
notification = {'type': 'mark_as_read', 'message_ids': [self.id], 'channel_ids': channel_ids}
|
||||
self.env['bus.bus'].sendone((self._cr.dbname, 'res.partner', self.env.user.partner_id.id), notification)
|
||||
|
||||
@api.model
|
||||
def unstar_all(self):
|
||||
""" Unstar messages for the current partner. """
|
||||
partner_id = self.env.user.partner_id.id
|
||||
|
||||
starred_messages = self.search([('starred_partner_ids', 'in', partner_id)])
|
||||
starred_messages.write({'starred_partner_ids': [(3, partner_id)]})
|
||||
|
||||
ids = [m.id for m in starred_messages]
|
||||
notification = {'type': 'toggle_star', 'message_ids': ids, 'starred': False}
|
||||
self.env['bus.bus'].sendone((self._cr.dbname, 'res.partner', self.env.user.partner_id.id), notification)
|
||||
|
||||
@api.multi
|
||||
def set_message_starred(self, starred):
|
||||
""" Set messages as (un)starred. Technically, the notifications related
|
||||
to uid are set to (un)starred.
|
||||
|
||||
:param bool starred: set notification as (un)starred
|
||||
:param bool create_missing: create notifications for missing entries
|
||||
(i.e. when acting on displayed messages not notified)
|
||||
"""
|
||||
# a user should always be able to star a message he can read
|
||||
self.check_access_rule('read')
|
||||
@@ -199,7 +231,9 @@ class Message(models.Model):
|
||||
self.sudo().write({'starred_partner_ids': [(4, self.env.user.partner_id.id)]})
|
||||
else:
|
||||
self.sudo().write({'starred_partner_ids': [(3, self.env.user.partner_id.id)]})
|
||||
return starred
|
||||
|
||||
notification = {'type': 'toggle_star', 'message_ids': [self.id], 'starred': starred}
|
||||
self.env['bus.bus'].sendone((self._cr.dbname, 'res.partner', self.env.user.partner_id.id), notification)
|
||||
|
||||
#------------------------------------------------------
|
||||
# Message loading for web interface
|
||||
|
||||
@@ -37,7 +37,7 @@ function add_message (data, options) {
|
||||
// Keep the array ordered by date when inserting the new message
|
||||
messages.splice(_.sortedIndex(messages, msg, 'id'), 0, msg);
|
||||
if (options.channel_id) {
|
||||
var channel = _.findWhere(channels, {id: options.channel_id});
|
||||
var channel = chat_manager.get_channel(options.channel_id);
|
||||
if (channel.hidden) {
|
||||
channel.hidden = false;
|
||||
chat_manager.bus.trigger('new_channel', channel);
|
||||
@@ -117,8 +117,8 @@ function make_message (data) {
|
||||
}
|
||||
var real_channels = _.without(msg.channel_ids, 'channel_inbox', 'channel_starred');
|
||||
var origin = real_channels.length ? real_channels[0] : undefined;
|
||||
var channel;
|
||||
if (origin && (channel = _.findWhere(channels, {id: origin}))) {
|
||||
var channel = origin && chat_manager.get_channel(origin);
|
||||
if (channel) {
|
||||
msg.origin_id = origin;
|
||||
msg.origin_name = channel.name;
|
||||
}
|
||||
@@ -165,7 +165,7 @@ function post_document_message (model_name, res_id, data) {
|
||||
|
||||
function add_channel (data, options) {
|
||||
options = typeof options === "object" ? options : {};
|
||||
var channel = _.findWhere(channels, {id: data.id});
|
||||
var channel = chat_manager.get_channel(data.id);
|
||||
if (channel) {
|
||||
if (channel.is_folded !== (data.state === "folded")) {
|
||||
channel.is_folded = (data.state === "folded");
|
||||
@@ -197,6 +197,9 @@ function make_channel (data, options) {
|
||||
autoswitch: 'autoswitch' in options ? options.autoswitch : true,
|
||||
hidden: options.hidden,
|
||||
display_needactions: options.display_needactions,
|
||||
needaction_counter: data.message_needaction_counter || 0,
|
||||
unread_counter: data.message_unread_counter || 0,
|
||||
last_seen_message_id: data.seen_message_id,
|
||||
cache: {'[]': {
|
||||
all_history_loaded: false,
|
||||
loaded: false,
|
||||
@@ -299,6 +302,126 @@ function fetch_document_messages (ids, options) {
|
||||
}
|
||||
}
|
||||
|
||||
var channel_seen = _.throttle(function (channel) {
|
||||
return ChannelModel.call('channel_seen', [[channel.id]]).then(function (last_seen_message_id) {
|
||||
channel.last_seen_message_id = last_seen_message_id;
|
||||
});
|
||||
}, 3000);
|
||||
|
||||
// Notification handlers
|
||||
// ---------------------------------------------------------------------------------
|
||||
function on_notification (notification) {
|
||||
var model = notification[0][1];
|
||||
if (model === 'ir.needaction') {
|
||||
// new message in the inbox
|
||||
on_needaction_notification(notification[1]);
|
||||
} else if (model === 'mail.channel') {
|
||||
// new message in a channel
|
||||
on_channel_notification(notification[1]);
|
||||
} else if (model === 'res.partner') {
|
||||
// channel joined/left, message marked as read/(un)starred, chat open/closed
|
||||
on_partner_notification(notification[1]);
|
||||
}
|
||||
}
|
||||
|
||||
function on_needaction_notification (message) {
|
||||
message = add_message(message, { channel_id: 'channel_inbox', show_notification: true} );
|
||||
needaction_counter++;
|
||||
var channel = chat_manager.get_channel(message.channel_ids[0]);
|
||||
if (channel) {
|
||||
channel.needaction_counter++;
|
||||
}
|
||||
chat_manager.bus.trigger('update_needaction', needaction_counter);
|
||||
}
|
||||
|
||||
function on_channel_notification (message) {
|
||||
var channel = chat_manager.get_channel(message.channel_ids[0]);
|
||||
if (channel) {
|
||||
channel.unread_counter++;
|
||||
add_message(message, { channel_id: channel.id, show_notification: true });
|
||||
}
|
||||
}
|
||||
|
||||
function on_partner_notification (data) {
|
||||
if (data.info === "unsubscribe") {
|
||||
channels = _.without(channels, chat_manager.get_channel(data.id));
|
||||
chat_manager.bus.trigger("unsubscribe_from_channel", data.id);
|
||||
} else if (data.type === 'toggle_star') {
|
||||
on_toggle_star_notification(data);
|
||||
} else if (data.type === 'mark_as_read') {
|
||||
on_mark_as_read_notification(data);
|
||||
} else if (data.type === 'mark_as_unread') {
|
||||
on_mark_as_unread_notification(data);
|
||||
} else {
|
||||
on_chat_session_notification(data);
|
||||
}
|
||||
}
|
||||
|
||||
function on_toggle_star_notification (data) {
|
||||
_.each(data.message_ids, function (msg_id) {
|
||||
var message = _.findWhere(messages, { id: msg_id });
|
||||
if (message) {
|
||||
message.is_starred = data.starred;
|
||||
if (!message.is_starred) {
|
||||
remove_message_from_channel("channel_starred", message);
|
||||
}
|
||||
chat_manager.bus.trigger('update_message', message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function on_mark_as_read_notification (data) {
|
||||
_.each(data.message_ids, function (msg_id) {
|
||||
var message = _.findWhere(messages, { id: msg_id });
|
||||
if (message) {
|
||||
remove_message_from_channel("channel_inbox", message);
|
||||
chat_manager.bus.trigger('update_message', message);
|
||||
}
|
||||
});
|
||||
if (data.channel_ids.length) {
|
||||
_.each(data.channel_ids, function (channel_id) {
|
||||
var channel = chat_manager.get_channel(channel_id);
|
||||
if (channel) {
|
||||
channel.needaction_counter -= data.message_ids.length;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
_.each(channels, function (channel) {
|
||||
channel.needaction_counter = 0;
|
||||
});
|
||||
}
|
||||
needaction_counter -= data.message_ids.length;
|
||||
chat_manager.bus.trigger('update_needaction', needaction_counter);
|
||||
}
|
||||
|
||||
function on_mark_as_unread_notification (data) {
|
||||
_.each(data.channel_ids, function (channel_id) {
|
||||
var channel = chat_manager.get_channel(channel_id);
|
||||
if (channel) {
|
||||
channel.needaction_counter += data.message_ids.length;
|
||||
}
|
||||
});
|
||||
needaction_counter += data.message_ids.length;
|
||||
chat_manager.bus.trigger('update_needaction', needaction_counter);
|
||||
}
|
||||
|
||||
function on_chat_session_notification (chat_session) {
|
||||
if ((chat_session.channel_type === "channel") && (chat_session.public === "private") && (chat_session.state === "open")) {
|
||||
add_channel(chat_session, {autoswitch: false});
|
||||
if (!chat_session.is_minimized) {
|
||||
web_client.do_notify(_t("Private Channel"), _t("You have been invited to: ") + chat_session.name);
|
||||
}
|
||||
}
|
||||
// partner specific change (open a detached window for example)
|
||||
if ((chat_session.state === "open") || (chat_session.state === "folded")) {
|
||||
add_channel(chat_session, {autoswitch: false});
|
||||
if (chat_session.is_minimized) {
|
||||
chat_manager.bus.trigger("open_chat", chat_session);
|
||||
}
|
||||
} else if (chat_session.state === "closed") {
|
||||
chat_manager.bus.trigger("close_chat", chat_session);
|
||||
}
|
||||
}
|
||||
|
||||
// Public interface
|
||||
//----------------------------------------------------------------------------------
|
||||
@@ -335,42 +458,28 @@ var chat_manager = {
|
||||
toggle_star_status: function (message_id) {
|
||||
var msg = _.findWhere(messages, { id: message_id });
|
||||
|
||||
return MessageModel.call('set_message_starred', [[message_id], !msg.is_starred]).then(function () {
|
||||
msg.is_starred = !msg.is_starred;
|
||||
if (!msg.is_starred) {
|
||||
remove_message_from_channel("channel_starred", msg);
|
||||
}
|
||||
chat_manager.bus.trigger('update_message', msg);
|
||||
});
|
||||
return MessageModel.call('set_message_starred', [[message_id], !msg.is_starred]);
|
||||
},
|
||||
unstar_all: function () {
|
||||
return MessageModel.call('unstar_all', [[]], {});
|
||||
},
|
||||
mark_as_read: function (message_id) {
|
||||
return MessageModel.call('set_message_done', [[message_id]]).then(function () {
|
||||
var message = _.findWhere(messages, { id: message_id });
|
||||
remove_message_from_channel("channel_inbox", message);
|
||||
chat_manager.bus.trigger('update_message', message);
|
||||
needaction_counter = needaction_counter - 1;
|
||||
chat_manager.bus.trigger('update_needaction', needaction_counter);
|
||||
});
|
||||
return MessageModel.call('set_message_done', [[message_id]]);
|
||||
},
|
||||
|
||||
mark_all_as_read: function () {
|
||||
MessageModel.call('mark_all_as_read', [[]], {}).then(function (ids) {
|
||||
_.each(messages, function (msg) {
|
||||
remove_message_from_channel("channel_inbox", msg);
|
||||
chat_manager.bus.trigger('update_message', msg);
|
||||
});
|
||||
needaction_counter = 0;
|
||||
chat_manager.bus.trigger('update_needaction', needaction_counter);
|
||||
});
|
||||
mark_all_as_read: function (channel) {
|
||||
if ((!channel && needaction_counter) || (channel && channel.needaction_counter)) {
|
||||
return MessageModel.call('mark_all_as_read', channel ? [[channel.id]] : [[]]);
|
||||
}
|
||||
return $.when();
|
||||
},
|
||||
|
||||
unstar_all: function () {
|
||||
MessageModel.call('unstar_all', [[]], {}).then(function (ids) {
|
||||
_.each(messages, function (msg) {
|
||||
remove_message_from_channel("channel_starred", msg);
|
||||
chat_manager.bus.trigger('update_message', msg);
|
||||
});
|
||||
});
|
||||
undo_mark_as_read: function (message_ids, channel) {
|
||||
return MessageModel.call('mark_as_unread', [message_ids, [channel.id]]);
|
||||
},
|
||||
mark_channel_as_seen: function (channel) {
|
||||
if (channel.unread_counter > 0) {
|
||||
channel.unread_counter = 0;
|
||||
channel_seen(channel);
|
||||
}
|
||||
},
|
||||
|
||||
get_channels: function () {
|
||||
@@ -378,7 +487,7 @@ var chat_manager = {
|
||||
},
|
||||
|
||||
get_channel: function (id) {
|
||||
return _.findWhere(channels, {id: id}) || channels[0];
|
||||
return _.findWhere(channels, {id: id});
|
||||
},
|
||||
|
||||
all_history_loaded: function (channel, domain) {
|
||||
@@ -435,11 +544,11 @@ var chat_manager = {
|
||||
});
|
||||
},
|
||||
close_chat_session: function (channel_id) {
|
||||
var channel = _.findWhere(channels, {id: channel_id});
|
||||
var channel = this.get_channel(channel_id);
|
||||
ChannelModel.call("channel_fold", [], {uuid : channel.uuid, state : "closed"});
|
||||
},
|
||||
fold_channel: function (channel_id) {
|
||||
var channel = _.findWhere(channels, {id: channel_id});
|
||||
var channel = this.get_channel(channel_id);
|
||||
return ChannelModel.call("channel_fold", [], {uuid : channel.uuid}).then(function () {
|
||||
channel.is_folded = !channel.is_folded;
|
||||
});
|
||||
@@ -479,52 +588,7 @@ function init () {
|
||||
var load_menu_id = ir_model.call("xmlid_to_res_id", ["mail.mail_channel_menu_root_chat"]);
|
||||
var load_action_id = ir_model.call("xmlid_to_res_id", ["mail.mail_channel_action_client_chat"]);
|
||||
|
||||
bus.on('notification', null, function (notification) {
|
||||
var model = notification[0][1];
|
||||
if (model === 'ir.needaction') {
|
||||
// new message in the inbox
|
||||
var message = notification[1];
|
||||
message = add_message(message, { channel_id: 'channel_inbox', show_notification: true} );
|
||||
needaction_counter = needaction_counter + 1;
|
||||
chat_manager.bus.trigger('update_needaction', needaction_counter);
|
||||
}
|
||||
if (model === 'mail.channel') {
|
||||
// new message in a channel
|
||||
var message = notification[1];
|
||||
var channel_id = message.channel_ids[0];
|
||||
var channel = _.findWhere(channels, {id: channel_id});
|
||||
if (channel) {
|
||||
add_message(message, { channel_id: channel_id, show_notification: true });
|
||||
}
|
||||
}
|
||||
if (model === 'res.partner') {
|
||||
var chat_session = notification[1];
|
||||
if (chat_session.info === "unsubscribe") {
|
||||
var channel = _.findWhere(channels, {id: chat_session.id});
|
||||
if (channel) {
|
||||
channels = _.without(channels, channel);
|
||||
}
|
||||
chat_manager.bus.trigger("unsubscribe_from_channel", chat_session.id);
|
||||
return;
|
||||
}
|
||||
if ((chat_session.channel_type === "channel") && (chat_session.public === "private") && (chat_session.state === "open")) {
|
||||
add_channel(chat_session, {autoswitch: false});
|
||||
if (!chat_session.is_minimized) {
|
||||
web_client.do_notify(_t("Private Channel"), _t("You have been invited to: ") + chat_session.name);
|
||||
}
|
||||
}
|
||||
// partner specific change (open a detached window for example)
|
||||
if ((chat_session.state === "open") || (chat_session.state === "folded")) {
|
||||
add_channel(chat_session, {autoswitch: false});
|
||||
if (chat_session.is_minimized) {
|
||||
chat_manager.bus.trigger("open_chat", chat_session);
|
||||
}
|
||||
}
|
||||
if (chat_session.state === "closed") {
|
||||
chat_manager.bus.trigger("close_chat", chat_session);
|
||||
}
|
||||
}
|
||||
});
|
||||
bus.on('notification', null, on_notification);
|
||||
|
||||
return $.when(load_menu_id, load_action_id, load_channels, load_emojis).then(function (menu_id, action_id) {
|
||||
discuss_ids = {
|
||||
|
||||
@@ -776,7 +776,7 @@ var Chatter = form_common.AbstractField.extend({
|
||||
var def;
|
||||
var channel = chat_manager.get_channel(channel_id);
|
||||
// If not registered to 'channel' yet, do it
|
||||
if (channel.id !== channel_id) {
|
||||
if (!channel) {
|
||||
def = chat_manager.join_channel(channel_id);
|
||||
}
|
||||
$.when(def).then(function () {
|
||||
|
||||
@@ -109,6 +109,14 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
var channel_id = $(event.target).data("channel-id");
|
||||
this.unsubscribe_from_channel(chat_manager.get_channel(channel_id));
|
||||
},
|
||||
"click .o_snackbar_undo": function (event) {
|
||||
event.preventDefault();
|
||||
var channel = this.channel;
|
||||
this.$snackbar.remove();
|
||||
this.clear_needactions_def.then(function (msgs_ids) {
|
||||
chat_manager.undo_mark_as_read(msgs_ids, channel);
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
on_attach_callback: function () {
|
||||
@@ -146,6 +154,9 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
this.action.context.active_id ||
|
||||
this.action.params.default_active_id ||
|
||||
'channel_inbox';
|
||||
var default_channel = chat_manager.get_channel(default_channel_id) ||
|
||||
chat_manager.get_channel('channel_inbox');
|
||||
|
||||
this.searchview = new SearchView(this, dataset, view_id, {}, options);
|
||||
this.searchview.on('search_data', this, this.on_search);
|
||||
|
||||
@@ -167,14 +178,15 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
this.$buttons.on('click', '.o_mail_toggle_channels', function () {
|
||||
self.$('.o_mail_chat_sidebar').slideToggle(200);
|
||||
});
|
||||
this.$buttons.on('click', '.o_mail_chat_button_mark_read', chat_manager.mark_all_as_read);
|
||||
this.$buttons.on('click', '.o_mail_chat_button_mark_read', function () {
|
||||
chat_manager.mark_all_as_read();
|
||||
});
|
||||
this.$buttons.on('click', '.o_mail_chat_button_unstar_all', chat_manager.unstar_all);
|
||||
|
||||
|
||||
this.thread.on('redirect', this, this.on_redirect);
|
||||
this.thread.on('redirect_to_channel', this, function (channel_id) {
|
||||
var channel = chat_manager.get_channel(channel_id);
|
||||
if (channel.id === channel_id) {
|
||||
if (channel) {
|
||||
this.set_channel(channel);
|
||||
} else {
|
||||
chat_manager.join_channel(channel_id);
|
||||
@@ -196,7 +208,7 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
this.render_sidebar();
|
||||
|
||||
return $.when(def1, def2, def3)
|
||||
.then(this.set_channel.bind(this, chat_manager.get_channel(default_channel_id)))
|
||||
.then(this.set_channel.bind(this, default_channel))
|
||||
.then(function () {
|
||||
chat_manager.bus.on('new_message', self, self.on_new_message);
|
||||
chat_manager.bus.on('update_message', self, self.on_update_message);
|
||||
@@ -204,7 +216,7 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
chat_manager.bus.on('anyone_listening', self, function (channel, query) {
|
||||
query.is_displayed = query.is_displayed || channel.id === self.channel.id;
|
||||
});
|
||||
chat_manager.bus.on('update_needaction', self, self.on_update_needaction);
|
||||
chat_manager.bus.on('update_needaction', self, self.render_sidebar);
|
||||
chat_manager.bus.on('unsubscribe_from_channel', self, self.render_sidebar);
|
||||
});
|
||||
},
|
||||
@@ -212,8 +224,9 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
render_sidebar: function () {
|
||||
var self = this;
|
||||
var $sidebar = QWeb.render("mail.chat.Sidebar", {
|
||||
active_channel_id: this.channel ? this.channel.id: undefined,
|
||||
channels: chat_manager.get_channels(),
|
||||
needaction_counter: chat_manager.get_needaction_counter()
|
||||
needaction_counter: chat_manager.get_needaction_counter(),
|
||||
});
|
||||
this.$(".o_mail_chat_sidebar").replaceWith($sidebar);
|
||||
|
||||
@@ -303,41 +316,59 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
this.channels_scrolltop[this.channel.id] = this.thread.get_scrolltop();
|
||||
}
|
||||
var new_channel_scrolltop = this.channels_scrolltop[channel.id];
|
||||
|
||||
this.channel = channel;
|
||||
|
||||
// Update control panel
|
||||
this.set("title", channel.name);
|
||||
// Hide 'detach' button in static channels
|
||||
this.$buttons
|
||||
.find('.o_mail_chat_button_detach')
|
||||
.toggle(channel.type !== "static");
|
||||
// Hide 'invite', 'unsubscribe' and 'settings' buttons in static channels and DM
|
||||
this.$buttons
|
||||
.find('.o_mail_chat_button_invite, .o_mail_chat_button_unsubscribe, .o_mail_chat_button_settings')
|
||||
.toggle(channel.type !== "dm" && channel.type !== 'static');
|
||||
|
||||
this.$buttons
|
||||
.find('.o_mail_chat_button_mark_read')
|
||||
.toggle(channel.id === "channel_inbox");
|
||||
|
||||
this.$buttons
|
||||
.find('.o_mail_chat_button_unstar_all')
|
||||
.toggle(channel.id === "channel_starred");
|
||||
|
||||
this.update_cp();
|
||||
this.messages_separator_position = undefined; // reset value on channel change
|
||||
this.unread_counter = this.channel.unread_counter;
|
||||
this.last_seen_message_id = this.channel.last_seen_message_id;
|
||||
this.clear_needactions_def = $.Deferred();
|
||||
if (this.$snackbar) {
|
||||
this.$snackbar.remove();
|
||||
}
|
||||
|
||||
this.action.context.active_id = channel.id;
|
||||
this.action.context.active_ids = [channel.id];
|
||||
|
||||
this.$('.o_mail_chat_channel_item')
|
||||
.removeClass('o_active')
|
||||
.filter('[data-channel-id=' + channel.id + ']')
|
||||
.removeClass('o_unread_message')
|
||||
.addClass('o_active');
|
||||
|
||||
this.$('.o_chat_composer').toggle(channel.type !== 'static');
|
||||
|
||||
return this.fetch_and_render_thread().then(function () {
|
||||
// Mark channel's messages as read and clear needactions
|
||||
if (channel.type !== 'static') {
|
||||
// Display snackbar if needactions have been cleared
|
||||
if (channel.needaction_counter > 0) {
|
||||
self.$snackbar = $(QWeb.render('mail.chat.UndoSnackbar', {
|
||||
nb_needactions: channel.needaction_counter,
|
||||
}));
|
||||
self.$('.o_mail_chat_content').append(self.$snackbar);
|
||||
}
|
||||
chat_manager.mark_channel_as_seen(channel);
|
||||
self.clear_needactions_def = chat_manager.mark_all_as_read(channel);
|
||||
}
|
||||
|
||||
// Update control panel
|
||||
self.set("title", channel.name);
|
||||
// Hide 'detach' button in static channels
|
||||
self.$buttons
|
||||
.find('.o_mail_chat_button_detach')
|
||||
.toggle(channel.type !== "static");
|
||||
// Hide 'invite', 'unsubscribe' and 'settings' buttons in static channels and DM
|
||||
self.$buttons
|
||||
.find('.o_mail_chat_button_invite, .o_mail_chat_button_unsubscribe, .o_mail_chat_button_settings')
|
||||
.toggle(channel.type !== "dm" && channel.type !== 'static');
|
||||
self.$buttons
|
||||
.find('.o_mail_chat_button_mark_read')
|
||||
.toggle(channel.id === "channel_inbox");
|
||||
self.$buttons
|
||||
.find('.o_mail_chat_button_unstar_all')
|
||||
.toggle(channel.id === "channel_starred");
|
||||
self.update_cp();
|
||||
|
||||
self.$('.o_chat_composer').toggle(channel.type !== 'static');
|
||||
|
||||
self.$('.o_mail_chat_channel_item')
|
||||
.removeClass('o_active')
|
||||
.filter('[data-channel-id=' + channel.id + ']')
|
||||
.removeClass('o_unread_message')
|
||||
.addClass('o_active');
|
||||
|
||||
self.thread.scroll_to({offset: new_channel_scrolltop});
|
||||
if (!config.device.touch) {
|
||||
self.composer.focus();
|
||||
@@ -367,11 +398,24 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
});
|
||||
},
|
||||
|
||||
get_thread_rendering_options: function () {
|
||||
get_thread_rendering_options: function (messages) {
|
||||
// Compute position of the 'New messages' separator, only once when joining
|
||||
// a channel to keep it in the thread when new messages arrive
|
||||
if (_.isUndefined(this.messages_separator_position)) {
|
||||
var msg_id = this.last_seen_message_id;
|
||||
if (!this.unread_counter) {
|
||||
this.messages_separator_position = false; // no unread message -> don't display separator
|
||||
} else if ((msg_id === false) || !_.findWhere(messages, {id: msg_id})) {
|
||||
this.messages_separator_position = 'top'; // all displayed messages are unread
|
||||
} else {
|
||||
this.messages_separator_position = msg_id; // last read message is msg_id
|
||||
}
|
||||
}
|
||||
return {
|
||||
channel_id: this.channel.id,
|
||||
display_load_more: !chat_manager.all_history_loaded(this.channel, this.domain),
|
||||
display_needactions: this.channel.display_needactions,
|
||||
channel_id: this.channel.id,
|
||||
messages_separator_position: this.messages_separator_position,
|
||||
squash_close_messages: this.channel.type !== 'static',
|
||||
};
|
||||
},
|
||||
@@ -379,7 +423,7 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
fetch_and_render_thread: function () {
|
||||
var self = this;
|
||||
return chat_manager.get_messages({channel_id: this.channel.id, domain: this.domain}).then(function(result) {
|
||||
self.thread.render(result, self.get_thread_rendering_options());
|
||||
self.thread.render(result, self.get_thread_rendering_options(result));
|
||||
self.update_button_status(result.length === 0);
|
||||
});
|
||||
},
|
||||
@@ -405,7 +449,10 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
return chat_manager
|
||||
.fetch_more(this.channel, this.domain)
|
||||
.then(function(result) {
|
||||
self.thread.render(result, self.get_thread_rendering_options());
|
||||
if (self.messages_separator_position === 'top') {
|
||||
self.messages_separator_position = undefined; // reset value to re-compute separator position
|
||||
}
|
||||
self.thread.render(result, self.get_thread_rendering_options(result));
|
||||
offset += framework.getPosition(document.querySelector(oldest_msg_selector)).top;
|
||||
self.thread.scroll_to({offset: offset});
|
||||
});
|
||||
@@ -493,16 +540,15 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
on_new_message: function (message) {
|
||||
var self = this;
|
||||
if (_.contains(message.channel_ids, this.channel.id)) {
|
||||
if (this.channel.type !== 'static') {
|
||||
chat_manager.mark_channel_as_seen(this.channel);
|
||||
}
|
||||
this.fetch_and_render_thread().then(function () {
|
||||
self.thread.scroll_to({id: message.id});
|
||||
});
|
||||
}
|
||||
// Indicate in the sidebar that there is a new message in the corresponding channels
|
||||
_.each(_.without(message.channel_ids, this.channel.id), function (channel_id) {
|
||||
self.$('.o_mail_chat_channel_item')
|
||||
.filter('[data-channel-id=' + channel_id + ']')
|
||||
.addClass('o_unread_message');
|
||||
});
|
||||
// Re-render sidebar to indicate that there is a new message in the corresponding channels
|
||||
this.render_sidebar();
|
||||
// Dump scroll position of channels in which the new message arrived
|
||||
this.channels_scrolltop = _.omit(this.channels_scrolltop, message.channel_ids);
|
||||
},
|
||||
@@ -512,7 +558,7 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
if ((current_channel_id === "channel_starred" && !message.is_starred) ||
|
||||
(current_channel_id === "channel_inbox" && !message.is_needaction)) {
|
||||
chat_manager.get_messages({channel_id: this.channel.id, domain: this.domain}).then(function (messages) {
|
||||
var options = self.get_thread_rendering_options();
|
||||
var options = self.get_thread_rendering_options(messages);
|
||||
self.thread.remove_message_and_render(message.id, messages, options).then(function () {
|
||||
self.update_button_status(messages.length === 0);
|
||||
});
|
||||
@@ -527,12 +573,6 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
this.set_channel(channel);
|
||||
}
|
||||
},
|
||||
on_update_needaction: function () {
|
||||
var $needaction_badge = QWeb.render("mail.chat.SidebarNeedaction", {
|
||||
needaction_counter: chat_manager.get_needaction_counter()
|
||||
});
|
||||
this.$('.o_mail_sidebar_needaction').replaceWith($needaction_badge);
|
||||
},
|
||||
|
||||
on_click_button_invite: function () {
|
||||
var title = _.str.sprintf(_t('Invite people to %s'), this.channel.name);
|
||||
|
||||
@@ -67,9 +67,10 @@
|
||||
|
||||
.o_mail_chat_content {
|
||||
.o-flex(1, 1, 100%);
|
||||
height: 100%;
|
||||
.o-flex-display();
|
||||
.o-flex-flow(column, nowrap);
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
.o_mail_thread {
|
||||
.o-flex(1, 0, 0);
|
||||
@@ -100,6 +101,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.o_mail_snackbar {
|
||||
.o-position-absolute(15px, 15px);
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
background-color: lighten(@odoo-brand-optional, 50%);
|
||||
border-color: lighten(@odoo-brand-optional, 40%);
|
||||
color: @odoo-brand-optional;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
.o_thread_new_messages_separator {
|
||||
border-bottom: solid lighten(@odoo-brand-primary, 15%) 1px;
|
||||
text-align: right;
|
||||
.o_thread_separator_label {
|
||||
position: relative;
|
||||
top: 8px;
|
||||
padding: 0 10px;
|
||||
background: white;
|
||||
color: lighten(@odoo-brand-primary, 15%);
|
||||
font-size: smaller;
|
||||
}
|
||||
}
|
||||
|
||||
.o_thread_message {
|
||||
.o-flex-display();
|
||||
padding: 4px 0;
|
||||
|
||||
@@ -10,11 +10,14 @@
|
||||
</t>
|
||||
<t t-name="mail.chat.Sidebar">
|
||||
<div class="o_mail_chat_sidebar">
|
||||
<div class="o_mail_chat_channel_item" data-channel-id="channel_inbox">
|
||||
<div t-attf-class="o_mail_chat_channel_item #{(active_channel_id == 'channel_inbox') ? 'o_active': ''}"
|
||||
data-channel-id="channel_inbox">
|
||||
@ Inbox
|
||||
<t t-set="counter" t-value="needaction_counter"/>
|
||||
<t t-call="mail.chat.SidebarNeedaction"/>
|
||||
</div>
|
||||
<div class="o_mail_chat_channel_item" data-channel-id="channel_starred">
|
||||
<div t-attf-class="o_mail_chat_channel_item #{(active_channel_id == 'channel_starred') ? 'o_active': ''}"
|
||||
data-channel-id="channel_starred">
|
||||
<span class="fa fa-star-o"/> Starred
|
||||
</div>
|
||||
|
||||
@@ -47,8 +50,8 @@
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="mail.chat.SidebarNeedaction">
|
||||
<span t-attf-class="o_mail_sidebar_needaction badge pull-right #{(!needaction_counter ? 'hide' : '')}">
|
||||
<t t-esc="needaction_counter"/>
|
||||
<span t-attf-class="o_mail_sidebar_needaction badge pull-right #{(!counter ? 'hide' : '')}">
|
||||
<t t-esc="counter"/>
|
||||
</span>
|
||||
</t>
|
||||
<t t-name="mail.chat.SidebarTitle">
|
||||
@@ -71,10 +74,14 @@
|
||||
</t>
|
||||
<t t-name="mail.chat.SidebarItems">
|
||||
<t t-foreach="channels" t-as="channel">
|
||||
<div t-if="channel.type === channel_type" class="o_mail_chat_channel_item" t-att-data-channel-id="channel.id">
|
||||
<t t-set="counter" t-value="channel.needaction_counter"/>
|
||||
<div t-if="channel.type === channel_type" t-att-data-channel-id="channel.id"
|
||||
t-attf-class="o_mail_chat_channel_item #{channel.unread_counter ? ' o_unread_message' : ''} #{(active_channel_id == channel.id) ? 'o_active': ''}">
|
||||
<span><i t-if="display_status" t-att-class="'o_user_status fa ' + (channel.status == 'online' ? 'fa-circle' : 'fa-circle-o')"/></span>
|
||||
<span t-if="display_hash" class="o_mail_hash">#</span> <t t-esc="channel.name"/>
|
||||
<span class="fa fa-times pull-right o_mail_partner_unpin" t-att-title="_t('Close')" t-att-data-channel-id="channel.id"/>
|
||||
<t t-call="mail.chat.SidebarNeedaction"/>
|
||||
<span t-attf-class="fa fa-times pull-right o_mail_partner_unpin #{counter ? 'hide' : ''}"
|
||||
t-att-title="_t('Close')" t-att-data-channel-id="channel.id"/>
|
||||
</div>
|
||||
</t>
|
||||
<div t-if="!disable_add_channel" class="o_mail_add_channel" t-attf-data-type="#{channel_type}">
|
||||
@@ -83,6 +90,16 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="mail.chat.UndoSnackbar">
|
||||
<div class="alert alert-dismissible o_mail_snackbar" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">&times;</span>
|
||||
</button>
|
||||
<t t-esc="nb_needactions"/> message(s) marked as read.
|
||||
<a href="#" class="alert-link o_snackbar_undo">Undo</a>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Buttons of the Control Panel -->
|
||||
<t t-name="mail.chat.ControlButtons">
|
||||
<div>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
</t>
|
||||
|
||||
<t t-name="mail.ChatThread.Content">
|
||||
<t t-if="options.messages_separator_position == 'top'" t-call="mail.MessagesSeparator"/>
|
||||
<t t-set="current_day" t-value="0"/>
|
||||
<t t-foreach="messages" t-as="message">
|
||||
<div t-if="current_day != message.date.split(' ')[0]" class="o_thread_date_separator">
|
||||
@@ -102,6 +103,15 @@
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<t t-if="options.messages_separator_position == message.id">
|
||||
<t t-call="mail.MessagesSeparator"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="mail.MessagesSeparator">
|
||||
<div class="o_thread_new_messages_separator">
|
||||
<span class="o_thread_separator_label">New messages</span>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="mail.ChatThread.MessageTracking">
|
||||
|
||||
Reference in New Issue
Block a user