[IMP] mail: various UI improvements

- don't add 'New message from' in the title of notifications when an
  author name is defined
- use a simple O icon instead of the full Odoo logo when a notification
  is natively displayed
- don't scroll to bottom if the thread is not already at bottom
- don't mark a channel read if thread is not at the bottom
- add # in front of channel name in chat windows and in invite dialog
- accept anonymous names in channel (for ex, livechat channels)
- always go to the next line with shift+enter
- always send message with ctrl+enter
- pressing enter sends message or go to next line depending of context
- don't allow to detach mass mailing channels
This commit is contained in:
Géry Debongnie
2016-01-14 11:22:26 +01:00
committed by Aaron Bohy
parent 8397abd871
commit 30c26d7654
8 changed files with 62 additions and 33 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

+5 -3
View File
@@ -53,7 +53,7 @@ bus.on("window_focus", null, function() {
function notify_incoming_message (msg, options) { function notify_incoming_message (msg, options) {
var title = _t('New message'); var title = _t('New message');
if (msg.author_id[1]) { if (msg.author_id[1]) {
title += _t(' from ') + _.escape(msg.author_id[1]); title = _.escape(msg.author_id[1]);
} }
var content = parse_and_transform(msg.body, strip_html).substr(0, preview_msg_max_size); var content = parse_and_transform(msg.body, strip_html).substr(0, preview_msg_max_size);
@@ -68,7 +68,7 @@ function notify_incoming_message (msg, options) {
if (Notification && Notification.permission === "granted") { if (Notification && Notification.permission === "granted") {
if (bus.is_master) { if (bus.is_master) {
new Notification(title, {body: content, icon: "/web/static/src/img/odoo.png", silent: false}); new Notification(title, {body: content, icon: "/mail/static/src/img/odoo_o.png", silent: false});
} }
} else { } else {
web_client.do_notify(title, content); web_client.do_notify(title, content);
@@ -325,6 +325,8 @@ function make_channel (data, options) {
channel.status = data.direct_partner[0].im_status; channel.status = data.direct_partner[0].im_status;
pinned_dm_partners.push(channel.direct_partner_id); pinned_dm_partners.push(channel.direct_partner_id);
bus.update_option('bus_presence_partner_ids', pinned_dm_partners); bus.update_option('bus_presence_partner_ids', pinned_dm_partners);
} else if ('anonymous_name' in data) {
channel.name = data.anonymous_name;
} }
channel.is_chat = !channel.type.match(/^(public|private|static)$/); channel.is_chat = !channel.type.match(/^(public|private|static)$/);
if (data.message_unread_counter) { if (data.message_unread_counter) {
@@ -735,7 +737,7 @@ var chat_manager = {
return MessageModel.call('mark_as_unread', [message_ids, [channel.id]]); return MessageModel.call('mark_as_unread', [message_ids, [channel.id]]);
}, },
mark_channel_as_seen: function (channel) { mark_channel_as_seen: function (channel) {
if (channel.unread_counter > 0) { if (channel.unread_counter > 0 && channel.type !== 'static') {
update_channel_unread_counter(channel, 0); update_channel_unread_counter(channel, 0);
channel_seen(channel); channel_seen(channel);
} }
+2 -7
View File
@@ -33,7 +33,6 @@ return Widget.extend({
this.is_hidden = false; this.is_hidden = false;
}, },
start: function () { start: function () {
this.$content = this.$('.o_chat_content');
this.$input = this.$('.o_chat_input input'); this.$input = this.$('.o_chat_input input');
this.thread = new ChatThread(this, { this.thread = new ChatThread(this, {
@@ -48,7 +47,7 @@ return Widget.extend({
if (this.folded) { if (this.folded) {
this.$el.css('height', HEIGHT_FOLDED); this.$el.css('height', HEIGHT_FOLDED);
} }
var def = this.thread.appendTo(this.$content); var def = this.thread.replace(this.$('.o_chat_content'));
return $.when(this._super(), def); return $.when(this._super(), def);
}, },
render: function (messages) { render: function (messages) {
@@ -59,9 +58,6 @@ return Widget.extend({
this.unread_msgs = counter; this.unread_msgs = counter;
this.$('.o_unread_counter').text(counter > 0 ? '(' + counter + ')' : ''); this.$('.o_unread_counter').text(counter > 0 ? '(' + counter + ')' : '');
}, },
scrollBottom: function () {
this.$content.scrollTop(this.$content[0].scrollHeight);
},
fold: function () { fold: function () {
this.$el.animate({ this.$el.animate({
height: this.folded ? HEIGHT_FOLDED : HEIGHT_OPEN height: this.folded ? HEIGHT_FOLDED : HEIGHT_OPEN
@@ -70,8 +66,7 @@ return Widget.extend({
toggle_fold: function (fold) { toggle_fold: function (fold) {
this.folded = _.isBoolean(fold) ? fold : !this.folded; this.folded = _.isBoolean(fold) ? fold : !this.folded;
if (!this.folded) { if (!this.folded) {
this.unread_msgs = 0; this.thread.scroll_to();
this.trigger('messages_read');
} }
this.fold(); this.fold();
}, },
+18 -5
View File
@@ -225,12 +225,17 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
chat_manager.bus.on('update_message', self, self.on_update_message); chat_manager.bus.on('update_message', self, self.on_update_message);
chat_manager.bus.on('new_channel', self, self.on_new_channel); chat_manager.bus.on('new_channel', self, self.on_new_channel);
chat_manager.bus.on('anyone_listening', self, function (channel, query) { chat_manager.bus.on('anyone_listening', self, function (channel, query) {
query.is_displayed = query.is_displayed || channel.id === self.channel.id; query.is_displayed = query.is_displayed || (channel.id === self.channel.id && self.thread.is_at_bottom());
}); });
chat_manager.bus.on('unsubscribe_from_channel', self, self.render_sidebar); chat_manager.bus.on('unsubscribe_from_channel', self, self.render_sidebar);
chat_manager.bus.on('update_needaction', self, self.throttled_render_sidebar); chat_manager.bus.on('update_needaction', self, self.throttled_render_sidebar);
chat_manager.bus.on('update_channel_unread_counter', self, self.throttled_render_sidebar); chat_manager.bus.on('update_channel_unread_counter', self, self.throttled_render_sidebar);
chat_manager.bus.on('update_dm_presence', self, self.throttled_render_sidebar); chat_manager.bus.on('update_dm_presence', self, self.throttled_render_sidebar);
self.thread.$el.on("scroll", null, _.debounce(function () {
if (self.thread.is_at_bottom()) {
chat_manager.mark_channel_as_seen(self.channel);
}
}, 100));
}); });
}, },
@@ -353,7 +358,7 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
// Hide 'detach' button in static channels // Hide 'detach' button in static channels
self.$buttons self.$buttons
.find('.o_mail_chat_button_detach') .find('.o_mail_chat_button_detach')
.toggle(channel.type !== "static"); .toggle(channel.type !== "static" && !channel.mass_mailing);
// Hide 'invite', 'unsubscribe' and 'settings' buttons in static channels and DM // Hide 'invite', 'unsubscribe' and 'settings' buttons in static channels and DM
self.$buttons self.$buttons
.find('.o_mail_chat_button_invite, .o_mail_chat_button_unsubscribe, .o_mail_chat_button_settings') .find('.o_mail_chat_button_invite, .o_mail_chat_button_unsubscribe, .o_mail_chat_button_settings')
@@ -506,9 +511,13 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
}, },
on_post_message: function (message) { on_post_message: function (message) {
var self = this;
var options = {channel_id: this.channel.id}; var options = {channel_id: this.channel.id};
chat_manager chat_manager
.post_message(message, options) .post_message(message, options)
.then(function() {
self.thread.scroll_to();
})
.fail(function () { .fail(function () {
// todo: display notification // todo: display notification
}); });
@@ -516,11 +525,15 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
on_new_message: function (message) { on_new_message: function (message) {
var self = this; var self = this;
if (_.contains(message.channel_ids, this.channel.id)) { if (_.contains(message.channel_ids, this.channel.id)) {
if (this.channel.type !== 'static') { if (this.channel.type !== 'static' && this.thread.is_at_bottom()) {
chat_manager.mark_channel_as_seen(this.channel); chat_manager.mark_channel_as_seen(this.channel);
} }
var should_scroll = this.thread.is_at_bottom();
this.fetch_and_render_thread().then(function () { this.fetch_and_render_thread().then(function () {
self.thread.scroll_to({id: message.id}); if (should_scroll) {
self.thread.scroll_to({id: message.id});
}
}); });
} }
// Re-render sidebar to indicate that there is a new message in the corresponding channels // Re-render sidebar to indicate that there is a new message in the corresponding channels
@@ -556,7 +569,7 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
}, },
on_click_button_invite: function () { on_click_button_invite: function () {
var title = _.str.sprintf(_t('Invite people to %s'), this.channel.name); var title = _.str.sprintf(_t('Invite people to #%s'), this.channel.name);
new PartnerInviteDialog(this, title, this.channel.id).open(); new PartnerInviteDialog(this, title, this.channel.id).open();
}, },
on_click_button_detach: function () { on_click_button_detach: function () {
+10 -7
View File
@@ -443,8 +443,8 @@ var BasicComposer = Widget.extend({
/** /**
* Send the message on ENTER, but go to new line on SHIFT+ENTER * Send the message on ENTER, but go to new line on SHIFT+ENTER
*/ */
prevent_send: function (event) { should_send: function (event) {
return event.shiftKey; return !event.shiftKey;
}, },
on_keydown: function (event) { on_keydown: function (event) {
@@ -465,9 +465,12 @@ var BasicComposer = Widget.extend({
case $.ui.keyCode.ENTER: case $.ui.keyCode.ENTER:
if (this.mention_manager.is_open()) { if (this.mention_manager.is_open()) {
event.preventDefault(); event.preventDefault();
} else if (!this.prevent_send(event)) { } else {
event.preventDefault(); var send_message = event.ctrlKey || this.should_send(event);
this.send_message(); if (send_message) {
event.preventDefault();
this.send_message();
}
} }
break; break;
} }
@@ -667,8 +670,8 @@ var ExtendedComposer = BasicComposer.extend({
}); });
}, },
prevent_send: function () { should_send: function () {
return true; return false;
}, },
}); });
+3
View File
@@ -165,6 +165,9 @@ var Thread = Widget.extend({
get_scrolltop: function () { get_scrolltop: function () {
return this.$el.scrollTop(); return this.$el.scrollTop();
}, },
is_at_bottom: function () {
return this.el.scrollHeight - this.$el.scrollTop() - this.$el.outerHeight() < 5;
},
}); });
Thread.ORDER = ORDER; Thread.ORDER = ORDER;
+21 -8
View File
@@ -38,11 +38,12 @@ function open_chat (session) {
} }
var chat_session = _.findWhere(chat_sessions, {id: session.id}); var chat_session = _.findWhere(chat_sessions, {id: session.id});
if (!chat_session) { if (!chat_session) {
var prefix = !session.is_chat ? "#" : "";
chat_session = { chat_session = {
id: session.id, id: session.id,
uuid: session.uuid, uuid: session.uuid,
name: session.name, name: session.name,
window: new ExtendedChatWindow(web_client, session.id, session.name, session.is_folded, session.unread_counter), window: new ExtendedChatWindow(web_client, session.id, prefix + session.name, session.is_folded, session.unread_counter),
}; };
chat_session.window.on("close_chat_session", null, function () { chat_session.window.on("close_chat_session", null, function () {
close_chat(chat_session); close_chat(chat_session);
@@ -58,7 +59,11 @@ function open_chat (session) {
chat_session.window.on("post_message", null, function (message, channel_id) { chat_session.window.on("post_message", null, function (message, channel_id) {
message.content = _.escape(message.content); message.content = _.escape(message.content);
chat_manager.post_message(message, {channel_id: channel_id}); chat_manager
.post_message(message, {channel_id: channel_id})
.then(function () {
chat_session.window.thread.scroll_to();
});
}); });
chat_session.window.on("messages_read", null, function () { chat_session.window.on("messages_read", null, function () {
chat_manager.mark_channel_as_seen(session); chat_manager.mark_channel_as_seen(session);
@@ -91,7 +96,14 @@ function open_chat (session) {
return chat_manager.get_messages({channel_id: chat_session.id}); return chat_manager.get_messages({channel_id: chat_session.id});
}).then(function (messages) { }).then(function (messages) {
chat_session.window.render(messages); chat_session.window.render(messages);
chat_session.window.scrollBottom(); chat_session.window.thread.scroll_to();
setTimeout(function () {
chat_session.window.thread.$el.on("scroll", null, _.debounce(function () {
if (chat_session.window.thread.is_at_bottom()) {
chat_manager.mark_channel_as_seen(session);
}
}, 100));
}, 0); // setTimeout to prevent to execute handler on first scroll_to, which is asynchronous
if (!session.is_folded) { if (!session.is_folded) {
chat_manager.mark_channel_as_seen(session); chat_manager.mark_channel_as_seen(session);
} }
@@ -230,8 +242,8 @@ var reposition_windows = function (options) {
function make_session_visible (session) { function make_session_visible (session) {
utils.swap(chat_sessions, session, chat_sessions[display_state.nb_slots-1]); utils.swap(chat_sessions, session, chat_sessions[display_state.nb_slots-1]);
session.window.toggle_fold(false);
reposition_windows(); reposition_windows();
session.window.toggle_fold(false);
} }
function render_hidden_sessions_dropdown () { function render_hidden_sessions_dropdown () {
@@ -254,13 +266,14 @@ function reposition_hidden_sessions_dropdown () {
function update_sessions (message, scrollBottom) { function update_sessions (message, scrollBottom) {
_.each(chat_sessions, function (session) { _.each(chat_sessions, function (session) {
if (_.contains(message.channel_ids, session.id)) { if (_.contains(message.channel_ids, session.id)) {
if (!session.window.folded && !session.window.is_hidden) { var message_visible = !session.window.folded && !session.window.is_hidden && session.window.thread.is_at_bottom();
if (message_visible) {
chat_manager.mark_channel_as_seen(chat_manager.get_channel(session.id)); chat_manager.mark_channel_as_seen(chat_manager.get_channel(session.id));
} }
chat_manager.get_messages({channel_id: session.id}).then(function (messages) { chat_manager.get_messages({channel_id: session.id}).then(function (messages) {
session.window.render(messages); session.window.render(messages);
if (scrollBottom) { if (scrollBottom && message_visible) {
session.window.scrollBottom(); session.window.thread.scroll_to();
} }
}); });
} }
@@ -288,7 +301,7 @@ core.bus.on('web_client_ready', null, function () {
chat_manager.bus.on('anyone_listening', null, function (channel, query) { chat_manager.bus.on('anyone_listening', null, function (channel, query) {
_.each(chat_sessions, function (session) { _.each(chat_sessions, function (session) {
if (channel.id === session.id) { if (channel.id === session.id && session.window.thread.is_at_bottom()) {
query.is_displayed = true; query.is_displayed = true;
} }
}); });
+3 -3
View File
@@ -53,12 +53,12 @@
} }
} }
.o_chat_content { .o_mail_thread {
.o-flex(1, 1, auto); .o-flex(1, 1, auto);
overflow: auto; overflow: auto;
padding: 5px; padding: 5px;
.o_mail_thread .o_thread_date_separator { .o_thread_date_separator {
margin-top: 0px; margin-top: 0px;
margin-bottom: 15px; margin-bottom: 15px;
border-color: lightgray; border-color: lightgray;
@@ -138,7 +138,7 @@
.o-flex(1, 1, 100%); .o-flex(1, 1, 100%);
} }
} }
.o_chat_content { .o_mail_thread {
background-color: lightgray; background-color: lightgray;
} }
.o_chat_input { .o_chat_input {