diff options
| author | Aaron Bohy <aab@odoo.com> | 2015-11-10 12:19:38 +0100 |
|---|---|---|
| committer | Géry Debongnie <ged@odoo.com> | 2015-11-12 08:51:56 +0100 |
| commit | 5264ccf0d0ebbfc6600ba5d3c0efc16bbe05c4d4 (patch) | |
| tree | 00a61360f7c5855b37f16d9bc728e64becdd9b2c /addons/mail/static | |
| parent | dbc1061854c42578905f2aa6ecd0e77e3732c01a (diff) | |
[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
Diffstat (limited to 'addons/mail/static')
| -rw-r--r-- | addons/mail/static/src/js/chat_manager.js | 234 | ||||
| -rw-r--r-- | addons/mail/static/src/js/chatter.js | 2 | ||||
| -rw-r--r-- | addons/mail/static/src/js/client_action.js | 142 | ||||
| -rw-r--r-- | addons/mail/static/src/less/client_action.less | 11 | ||||
| -rw-r--r-- | addons/mail/static/src/less/thread.less | 13 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/client_action.xml | 29 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/thread.xml | 10 |
7 files changed, 297 insertions, 144 deletions
diff --git a/addons/mail/static/src/js/chat_manager.js b/addons/mail/static/src/js/chat_manager.js index 6bd96fdc32d..01cc6918264 100644 --- a/addons/mail/static/src/js/chat_manager.js +++ b/addons/mail/static/src/js/chat_manager.js @@ -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 = { diff --git a/addons/mail/static/src/js/chatter.js b/addons/mail/static/src/js/chatter.js index 5b98786ccdc..fae9e294e57 100644 --- a/addons/mail/static/src/js/chatter.js +++ b/addons/mail/static/src/js/chatter.js @@ -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 () { diff --git a/addons/mail/static/src/js/client_action.js b/addons/mail/static/src/js/client_action.js index 6c5b4ecba5b..7974203abe4 100644 --- a/addons/mail/static/src/js/client_action.js +++ b/addons/mail/static/src/js/client_action.js @@ -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.channel = channel; + 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'); + 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); + } - this.$('.o_chat_composer').toggle(channel.type !== 'static'); + // 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'); - return this.fetch_and_render_thread().then(function () { 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); diff --git a/addons/mail/static/src/less/client_action.less b/addons/mail/static/src/less/client_action.less index 9f8d501c3a2..f6194bfc973 100644 --- a/addons/mail/static/src/less/client_action.less +++ b/addons/mail/static/src/less/client_action.less @@ -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; + } } } diff --git a/addons/mail/static/src/less/thread.less b/addons/mail/static/src/less/thread.less index bcb73d4dd7c..df865d14a4f 100644 --- a/addons/mail/static/src/less/thread.less +++ b/addons/mail/static/src/less/thread.less @@ -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; diff --git a/addons/mail/static/src/xml/client_action.xml b/addons/mail/static/src/xml/client_action.xml index aa6a9bad802..b6dcbb7e18a 100644 --- a/addons/mail/static/src/xml/client_action.xml +++ b/addons/mail/static/src/xml/client_action.xml @@ -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> diff --git a/addons/mail/static/src/xml/thread.xml b/addons/mail/static/src/xml/thread.xml index 94719437441..7d1c28e964a 100644 --- a/addons/mail/static/src/xml/thread.xml +++ b/addons/mail/static/src/xml/thread.xml @@ -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"> |
