diff options
| author | Martin Trigaux <mat@odoo.com> | 2015-12-01 10:54:51 +0100 |
|---|---|---|
| committer | Martin Trigaux <mat@odoo.com> | 2015-12-01 10:54:51 +0100 |
| commit | 87f96c942ab4381b489db5b6bf5de959f0ec3d7f (patch) | |
| tree | e9302d09e1fce1d4544e7fa92d4465105ded0140 /addons/mail/static/src | |
| parent | d025251c7e4ee4c26462e61b5d63e5c5d58689f8 (diff) | |
| parent | 90254beedbb6a9bdf055ae582af866073eab4f2c (diff) | |
[MERGE] forward port of branch saas-7 up to 90254bee
Diffstat (limited to 'addons/mail/static/src')
| -rw-r--r-- | addons/mail/static/src/js/chat_manager.js | 130 | ||||
| -rw-r--r-- | addons/mail/static/src/js/chatter.js | 39 | ||||
| -rw-r--r-- | addons/mail/static/src/js/client_action.js | 14 | ||||
| -rw-r--r-- | addons/mail/static/src/js/composer.js | 124 | ||||
| -rw-r--r-- | addons/mail/static/src/js/thread.js | 38 | ||||
| -rw-r--r-- | addons/mail/static/src/less/client_action.less | 12 | ||||
| -rw-r--r-- | addons/mail/static/src/less/composer.less | 4 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/chatter.xml | 4 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/composer.xml | 37 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/thread.xml | 23 |
10 files changed, 256 insertions, 169 deletions
diff --git a/addons/mail/static/src/js/chat_manager.js b/addons/mail/static/src/js/chat_manager.js index f297a751fa9..e8a2ad3951e 100644 --- a/addons/mail/static/src/js/chat_manager.js +++ b/addons/mail/static/src/js/chat_manager.js @@ -6,6 +6,7 @@ var core = require('web.core'); var data = require('web.data'); var Model = require('web.Model'); var session = require('web.session'); +var time = require('web.time'); var web_client = require('web.web_client'); var _t = core._t; @@ -23,6 +24,7 @@ var channel_defs = {}; var emojis = []; var emoji_substitutions = {}; var needaction_counter = 0; +var mention_partner_suggestions = []; var discuss_ids = {}; // Message and channel manipulation helpers @@ -39,6 +41,13 @@ function add_message (data, options) { messages.splice(_.sortedIndex(messages, msg, 'id'), 0, msg); _.each(msg.channel_ids, function (channel_id) { var channel = chat_manager.get_channel(channel_id); + + if (channel) { + add_to_cache(msg, []); + if (options.domain && options.domain !== []) { + add_to_cache(msg, options.domain); + } + } if (channel && channel.hidden) { channel.hidden = false; chat_manager.bus.trigger('new_channel', channel); @@ -61,6 +70,8 @@ function add_message (data, options) { if (!options.silent) { chat_manager.bus.trigger('new_message', msg); } + } else if (options.domain && options.domain !== []) { + add_to_cache(msg, options.domain); } return msg; } @@ -71,7 +82,7 @@ function make_message (data) { author_id: data.author_id, body_short: data.body_short || "", body: data.body || "", - date: data.date, + date: moment(time.str_to_datetime(data.date)), message_type: data.message_type, subtype_description: data.subtype_description, is_note: data.is_note, @@ -118,13 +129,38 @@ function make_message (data) { if (_.contains(data.starred_partner_ids, session.partner_id)) { msg.is_starred = true; } - var real_channels = _.without(msg.channel_ids, 'channel_inbox', 'channel_starred'); - var origin = real_channels.length ? real_channels[0] : undefined; - var channel = origin && chat_manager.get_channel(origin); - if (channel) { - msg.origin_id = origin; - msg.origin_name = channel.name; + if (msg.model === 'mail.channel') { + var real_channels = _.without(msg.channel_ids, 'channel_inbox', 'channel_starred'); + var origin = real_channels.length === 1 ? real_channels[0] : undefined; + var channel = origin && chat_manager.get_channel(origin); + if (channel) { + msg.origin_id = origin; + msg.origin_name = channel.name; + } + } + + // Compute displayed author name or email + if ((!msg.author_id || !msg.author_id[0]) && msg.email_from) { + msg.mailto = msg.email_from; + } else { + msg.displayed_author = msg.author_id && msg.author_id[1] || + msg.email_from || _t('Anonymous'); } + + // Compute the avatar_url + if (msg.author_id && msg.author_id[0]) { + msg.avatar_src = "/web/image/res.partner/" + msg.author_id[0] + "/image_small"; + } else if (msg.message_type === 'email') { + msg.avatar_src = "/mail/static/src/img/email_icon.png"; + } else { + msg.avatar_src = "/mail/static/src/img/smiley/avatar.jpg"; + } + + // Compute url of attachments + _.each(msg.attachment_ids, function(a) { + a.url = '/web/content/' + a.id + '?download=true'; + }); + return msg; } @@ -207,7 +243,7 @@ function make_channel (data, options) { cache: {'[]': { all_history_loaded: false, loaded: false, - message_ids: [], + messages: [], }}, }; if (channel.type === "channel" && data.public !== "private") { @@ -229,17 +265,39 @@ function get_channel_cache (channel, domain) { channel.cache[stringified_domain] = { all_history_loaded: false, loaded: false, - message_ids: [], + messages: [], }; } return channel.cache[stringified_domain]; } +function invalidate_caches(channel_ids) { + _.each(channel_ids, function (channel_id) { + var channel = chat_manager.get_channel(channel_id); + if (channel) { + channel.cache = { '[]': channel.cache['[]']}; + } + }); +} + +function add_to_cache(message, domain) { + _.each(message.channel_ids, function (channel_id) { + var channel = chat_manager.get_channel(channel_id); + if (channel) { + var channel_cache = get_channel_cache(channel, domain); + var index = _.sortedIndex(channel_cache.messages, message, 'id'); + if (channel_cache.messages[index] !== message) { + channel_cache.messages.splice(index, 0, message); + } + } + }); +} + function remove_message_from_channel (channel_id, message) { message.channel_ids = _.without(message.channel_ids, channel_id); var channel = _.findWhere(channels, { id: channel_id }); _.each(channel.cache, function (cache) { - cache.message_ids = _.without(cache.message_ids, message.id); + cache.messages = _.without(cache.messages, message); }); } @@ -256,28 +314,21 @@ function fetch_from_channel (channel, options) { domain = new data.CompoundDomain(domain, options.domain || []); } if (options.load_more) { - var min_message_id = _.chain(messages) - .filter(function (msg) { return _.contains(cache.message_ids, msg.id); }) - .pluck("id") - .min() - .value(); - + var min_message_id = cache.messages[0].id; domain = new data.CompoundDomain([['id', '<', min_message_id]], domain); } return MessageModel.call('message_fetch', [domain], {limit: LIMIT}).then(function (msgs) { - cache.message_ids = _.uniq(cache.message_ids.concat(_.pluck(msgs, 'id'))); if (!cache.all_history_loaded) { cache.all_history_loaded = msgs.length < LIMIT; } cache.loaded = true; _.each(msgs, function (msg) { - add_message(msg, {channel_id: channel.id, silent: true}); - }); - return _.filter(messages, function (m) { - return _.contains(cache.message_ids, m.id); + add_message(msg, {channel_id: channel.id, silent: true, domain: options.domain}); }); + var channel_cache = get_channel_cache(channel, options.domain || []); + return channel_cache.messages; }); } @@ -335,6 +386,7 @@ function on_notification (notification) { function on_needaction_notification (message) { message = add_message(message, { channel_id: 'channel_inbox', show_notification: true} ); + invalidate_caches(message.channel_ids); needaction_counter++; _.each(message.channel_ids, function (channel_id) { var channel = chat_manager.get_channel(channel_id); @@ -360,6 +412,7 @@ function on_channel_notification (message) { } }); add_message(message, { show_notification: true }); + invalidate_caches(message.channel_ids); }); } @@ -382,6 +435,7 @@ function on_toggle_star_notification (data) { _.each(data.message_ids, function (msg_id) { var message = _.findWhere(messages, { id: msg_id }); if (message) { + invalidate_caches(message.channel_ids); message.is_starred = data.starred; if (!message.is_starred) { remove_message_from_channel("channel_starred", message); @@ -395,6 +449,7 @@ function on_mark_as_read_notification (data) { _.each(data.message_ids, function (msg_id) { var message = _.findWhere(messages, { id: msg_id }); if (message) { + invalidate_caches(message.channel_ids); remove_message_from_channel("channel_inbox", message); chat_manager.bus.trigger('update_message', message); } @@ -419,6 +474,7 @@ function on_mark_as_unread_notification (data) { _.each(data.message_ids, function (message_id) { var message = _.findWhere(messages, { id: message_id }); if (message) { + invalidate_caches(message.channel_ids); add_channel_to_message(message, 'channel_inbox'); } }); @@ -433,10 +489,10 @@ function on_mark_as_unread_notification (data) { } function on_chat_session_notification (chat_session) { - if ((chat_session.channel_type === "channel") && (chat_session.public === "private") && (chat_session.state === "open")) { + if ((chat_session.channel_type === "channel") && (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); + if (!chat_session.is_minimized && chat_session.info !== 'creation') { + web_client.do_notify(_t("Invitation"), _t("You have been invited to: ") + chat_session.name); } } // partner specific change (open a detached window for example) @@ -460,9 +516,7 @@ var chat_manager = { var channel = this.get_channel(options.channel_id); var channel_cache = get_channel_cache(channel, options.domain); if (channel_cache.loaded) { - return $.when(_.filter(messages, function (message) { - return _.contains(message.channel_ids, options.channel_id); - })); + return $.when(channel_cache.messages); } else { return fetch_from_channel(channel, {domain: options.domain}); } @@ -531,6 +585,27 @@ var chat_manager = { return get_channel_cache(channel, domain).all_history_loaded; }, + get_mention_partner_suggestions: function (channel) { + if (!channel) { + return mention_partner_suggestions; + } + if (!channel.members_deferred) { + channel.members_deferred = ChannelModel + .call("channel_fetch_listeners", [channel.uuid]) + .then(function (members) { + var suggestions = []; + _.each(mention_partner_suggestions, function (partners) { + suggestions.push(_.filter(partners, function (partner) { + return !_.findWhere(members, { id: partner.id }); + })); + }); + + return [members].concat(suggestions); + }); + } + return channel.members_deferred; + }, + get_emojis: function() { return emojis; }, @@ -618,6 +693,7 @@ function init () { _.each(channels, add_channel); }); needaction_counter = result.needaction_inbox_counter; + mention_partner_suggestions = result.mention_partner_suggestions; }); var load_emojis = session.rpc("/mail/chat_init").then(function (result) { diff --git a/addons/mail/static/src/js/chatter.js b/addons/mail/static/src/js/chatter.js index f8c9f1e032e..d29312cf6f6 100644 --- a/addons/mail/static/src/js/chatter.js +++ b/addons/mail/static/src/js/chatter.js @@ -37,6 +37,7 @@ var Followers = form_common.AbstractField.extend({ this.value = []; this.followers = []; + this.followers_fetched = $.Deferred(); this.data_subtype = {}; this.view_is_editable = this.__parentedParent.is_action_enabled('edit'); @@ -188,9 +189,14 @@ var Followers = form_common.AbstractField.extend({ }, fetch_followers: function (value_) { + var self = this; this.value = value_ || []; return ajax.jsonRpc('/mail/read_followers', 'call', {'follower_ids': this.value}) - .then(this.proxy('display_followers'), this.proxy('fetch_generic')) + .then(function (followers) { + self.trigger('followers_update', followers); + self.display_followers(followers); + self.fetch_generic(); + }) .then(this.proxy('display_buttons')) .then(this.proxy('fetch_subtypes')); }, @@ -706,6 +712,7 @@ var Chatter = form_common.AbstractField.extend({ if (this.followers) { this.$('.o_chatter_topbar').append(this.followers.$el); this.followers.on('redirect', this, this.on_redirect); + this.followers.on('followers_update', this, this.on_followers_update); } this.thread = new ChatThread(this, { @@ -798,6 +805,30 @@ var Chatter = form_common.AbstractField.extend({ }); }, + on_followers_update: function (followers) { + this.mention_suggestions = []; + var self = this; + var prefetched_partners = chat_manager.get_mention_partner_suggestions(); + var follower_suggestions = []; + _.each(followers, function (follower) { + if (follower.res_model === 'res.partner') { + follower_suggestions.push({ + id: follower.res_id, + name: follower.name, + email: follower.email, + }); + } + }); + if (follower_suggestions.length) { + this.mention_suggestions.push(follower_suggestions); + } + _.each(prefetched_partners, function (partners) { + self.mention_suggestions.push(_.filter(partners, function (partner) { + return !_.findWhere(follower_suggestions, { id: partner.id }); + })); + }); + }, + load_more_messages: function () { this.fetch_and_render_thread(this.msg_ids, {force_fetch: true}); }, @@ -854,9 +885,9 @@ var Chatter = form_common.AbstractField.extend({ internal_subtypes: this.options.internal_subtypes, is_log: options && options.is_log, record_name: this.record_name, - get_channel_info: function () { - return { res_id: self.res_id, res_model: self.model }; - }, + }); + this.composer.on('input_focused', this, function () { + this.composer.mention_set_prefetched_partners(this.mention_suggestions || []); }); this.composer.insertBefore(this.$('.o_mail_thread')).then(function () { // destroy existing composer diff --git a/addons/mail/static/src/js/client_action.js b/addons/mail/static/src/js/client_action.js index 20078af06a2..b96e9af6777 100644 --- a/addons/mail/static/src/js/client_action.js +++ b/addons/mail/static/src/js/client_action.js @@ -171,11 +171,7 @@ var ChatAction = Widget.extend(ControlPanelMixin, { this.searchview = new SearchView(this, dataset, view_id, {}, options); this.searchview.on('search_data', this, this.on_search); - this.composer = new ChatComposer(this, { - get_channel_info: function () { - return { channel_id: self.channel.id }; - }, - }); + this.composer = new ChatComposer(this); this.thread = new ChatThread(this, { display_help: true }); @@ -211,6 +207,7 @@ var ChatAction = Widget.extend(ControlPanelMixin, { chat_manager.toggle_star_status(message_id); }); this.composer.on('post_message', this, this.on_post_message); + this.composer.on('input_focused', this, this.on_composer_input_focused); var def1 = this.thread.prependTo(this.$('.o_mail_chat_content')); var def2 = this.composer.appendTo(this.$('.o_mail_chat_content')); @@ -436,6 +433,9 @@ var ChatAction = Widget.extend(ControlPanelMixin, { display_needactions: this.channel.display_needactions, messages_separator_position: this.messages_separator_position, squash_close_messages: this.channel.type !== 'static', + display_empty_channel: !messages.length && !this.domain.length, + display_no_match: !messages.length && this.domain.length, + display_subject: this.channel.mass_mailing || this.channel.id === "channel_inbox", }; }, @@ -592,6 +592,10 @@ var ChatAction = Widget.extend(ControlPanelMixin, { this.set_channel(channel); } }, + on_composer_input_focused: function () { + var suggestions = chat_manager.get_mention_partner_suggestions(this.channel); + this.composer.mention_set_prefetched_partners(suggestions); + }, on_click_button_invite: function () { var title = _.str.sprintf(_t('Invite people to %s'), this.channel.name); diff --git a/addons/mail/static/src/js/composer.js b/addons/mail/static/src/js/composer.js index bfbf3618a98..24257d1a72a 100644 --- a/addons/mail/static/src/js/composer.js +++ b/addons/mail/static/src/js/composer.js @@ -12,6 +12,19 @@ var Widget = require('web.Widget'); var QWeb = core.qweb; var _t = core._t; +var accented_letters_mapping = { + 'a': '[àáâãäå]', + 'ae': 'æ', + 'c': 'ç', + 'e': '[èéêë]', + 'i': '[ìíîï]', + 'n': 'ñ', + 'o': '[òóôõö]', + 'oe': 'œ', + 'u': '[ùúûűü]', + 'y': '[ýÿ]', +}; + // The MentionManager allows the Composer to register listeners. For each // listener, it detects if the user is currently typing a mention (starting by a // given delimiter). If so, if fetches mention suggestions and renders them. On @@ -63,16 +76,14 @@ var MentionManager = Widget.extend({ }; // add the selected suggestion to the list - var selection = this.active_listener.selection; - if (selection.length && this.active_listener.sort_selection) { + if (this.active_listener.selection.length) { // get mention matches (ordered by index in the text) var matches = this._get_match(text_input, this.active_listener); var index = get_mention_index(matches, this._get_selection_positions().start); - selection.splice(index, 0, selected_suggestion); + this.active_listener.selection.splice(index, 0, selected_suggestion); } else { - selection = selection.concat([selected_suggestion]); + this.active_listener.selection.push(selected_suggestion); } - this._update_listener_selection(this.active_listener, selection); // update input text, and reset dropdown var cursor_position = this._get_selection_positions().start; @@ -90,10 +101,7 @@ var MentionManager = Widget.extend({ * @param {char} [delimiter] the mention delimiter * @param {function} [fetch_callback] the callback to fetch mention suggestions * @param {string} [model] the model used for redirection - * @param {boolean} [notify_selection_changed] true to trigger an event when the selection changes * @param {string} [redirect_classname] the classname of the <a> wrapping the mention - * @param {boolean} [sort_selection] true if the selection must be sorted - * @param {string} [suggestions_template] the QWeb template to render suggestions */ register: function (listener) { this.listeners.push(_.extend(listener, { @@ -137,7 +145,6 @@ var MentionManager = Widget.extend({ */ detect_delimiter: function () { var self = this; - var options = this.options; var text_val = this.composer.$input.val(); var cursor_position = this._get_selection_positions().start; var left_string = text_val.substring(0, cursor_position); @@ -146,7 +153,7 @@ var MentionManager = Widget.extend({ var pattern = "(^"+delimiter+"|(^\\s"+delimiter+"))"; var regex_start = new RegExp(pattern, "g"); search_str = search_str.replace(/^\s\s*|^[\n\r]/g, ''); - if (regex_start.test(search_str) && search_str.length > options.min_length) { + if (regex_start.test(search_str) && search_str.length > self.options.min_length) { search_str = search_str.replace(pattern, ''); return search_str.indexOf(' ') < 0 && !/[\r\n]/.test(search_str) ? search_str.replace(delimiter, '') : false; } @@ -164,19 +171,19 @@ var MentionManager = Widget.extend({ } } - if (this.active_listener) { - // start a timeout to fetch data with the current 'mention word'. The timer avoid to start - // an RPC for each pushed key when the user is still typing. - // The 'typing_speed' option should approach the time for a human to type a letter. - clearTimeout(this.mention_fetch_timer); - this.mention_fetch_timer = setTimeout(function () { - $.when(listener.fetch_callback(self.mention_word, self.active_listener)).then(function (suggestions) { + // start a timeout to fetch data with the current 'mention word'. The timer avoid to start + // an RPC for each pushed key when the user is still typing. + // The 'typing_speed' option should approach the time for a human to type a letter. + clearTimeout(this.mention_fetch_timer); + this.mention_fetch_timer = setTimeout(function () { + if (self.active_listener) { + $.when(listener.fetch_callback(self.mention_word)).then(function (suggestions) { self.set('mention_suggestions', suggestions); }); - }, this.options.typing_speed); - } else { - this.set('mention_suggestions', []); // close the dropdown - } + } else { + self.set('mention_suggestions', []); // close the dropdown + } + }, this.options.typing_speed); }, /** @@ -201,7 +208,7 @@ var MentionManager = Widget.extend({ } } if (to_remove.length) { - self._update_listener_selection(listener, _.difference(mention_selection, to_remove)); + listener.selection = _.difference(mention_selection, to_remove); } }); }, @@ -235,9 +242,8 @@ var MentionManager = Widget.extend({ }, reset_selections: function () { - var self = this; _.each(this.listeners, function (listener) { - self._update_listener_selection(listener, []); + listener.selection = []; }); }, @@ -269,7 +275,7 @@ var MentionManager = Widget.extend({ }, _render_suggestions: function () { if (_.flatten(this.get('mention_suggestions')).length) { - this.$el.html(QWeb.render(this.active_listener.suggestions_template, { + this.$el.html(QWeb.render('mail.ChatComposer.MentionSuggestions', { suggestions: this.get('mention_suggestions'), })); this.$el @@ -282,12 +288,6 @@ var MentionManager = Widget.extend({ this.open = false; } }, - _update_listener_selection: function (listener, selection) { - listener.selection = selection; - if (listener.notify_selection_changed) { - this.trigger('selection_changed', listener); - } - }, // Cursor position and selection utils _get_selection_positions: function () { @@ -327,10 +327,8 @@ var Composer = Widget.extend({ input_max_height: 150, input_min_height: 28, mention_fetch_limit: 8, - get_channel_info: function () {}, }); this.context = this.options.context; - this.get_channel_info = this.options.get_channel_info; // Attachments this.AttachmentDataSet = new data.DataSetSearch(this, 'ir.attachment', this.context); @@ -343,19 +341,14 @@ var Composer = Widget.extend({ delimiter: '@', fetch_callback: this.mention_fetch_partners.bind(this), model: 'res.partner', - notify_selection_changed: true, redirect_classname: 'o_mail_redirect', - sort_selection: true, - suggestions_template: 'mail.ChatComposer.MentionPartnerSuggestions', }); this.mention_manager.register({ delimiter: '#', fetch_callback: this.mention_fetch_channels.bind(this), model: 'mail.channel', redirect_classname: 'o_channel_redirect', - suggestions_template: 'mail.ChatComposer.MentionChannelSuggestions' }); - this.mention_manager.on('selection_changed', this, this.mention_render_selection); this.PartnerModel = new Model('res.partner'); this.ChannelModel = new Model('mail.channel'); }, @@ -365,8 +358,10 @@ var Composer = Widget.extend({ this.$attachment_button = this.$(".o_composer_button_add_attachment"); this.$attachments_list = this.$('.o_composer_attachments_list'); - this.$mention_partner_tags = this.$('.o_composer_mentioned_partners'); this.$input = this.$('.o_composer_input'); + this.$input.focus(function () { + self.trigger('input_focused'); + }); this.resize_input(); // Attachments @@ -403,7 +398,7 @@ var Composer = Widget.extend({ return $.when({ content: this.mention_manager.generate_links(value), attachment_ids: _.pluck(this.get('attachment_ids'), 'id'), - partner_ids: _.pluck(this.mention_manager.get_listener_selection('@'), 'id'), + partner_ids: _.uniq(_.pluck(this.mention_manager.get_listener_selection('@'), 'id')), }); }, @@ -607,19 +602,38 @@ var Composer = Widget.extend({ }); }); }, - mention_fetch_partners: function (search, listener) { - var kwargs = { - channel: this.get_channel_info(), - exclude: listener ? _.pluck(listener.selection, 'id') : [], - limit: this.options.mention_fetch_limit, - search: search, - }; - return this.PartnerModel.call('get_mention_suggestions', kwargs); + mention_fetch_partners: function (search) { + var self = this; + return $.when(this.mention_prefetched_partners).then(function (prefetched_partners) { + // filter prefetched partners with the given search string + var suggestions = []; + var limit = self.options.mention_fetch_limit; + var search_regexp = new RegExp(self.unaccent(search), 'i'); + _.each(prefetched_partners, function (partners) { + if (limit > 0) { + var filtered_partners = _.filter(partners, function (partner) { + return partner.email && partner.email.search(search_regexp) !== -1 || + partner.name && self.unaccent(partner.name).search(search_regexp) !== -1; + }); + if (filtered_partners.length) { + suggestions.push(filtered_partners.slice(0, limit)); + limit -= filtered_partners.length; + } + } + }); + if (!suggestions.length) { + // no result found among prefetched partners, fetch other suggestions + var kwargs = { + limit: limit, + search: search, + }; + suggestions = self.PartnerModel.call('get_mention_suggestions', kwargs); + } + return suggestions; + }); }, - mention_render_selection: function (listener) { - this.$mention_partner_tags.html(QWeb.render('mail.ChatComposer.MentionTags', { - partners: listener.selection, - })); + mention_set_prefetched_partners: function (prefetched_partners) { + this.mention_prefetched_partners = prefetched_partners; }, // Others @@ -629,6 +643,12 @@ var Composer = Widget.extend({ focus: function () { this.$input.focus(); }, + unaccent: function (str) { + _.each(accented_letters_mapping, function (value, key) { + str = str.replace(new RegExp(value, 'g'), key); + }); + return str; + }, }); return Composer; diff --git a/addons/mail/static/src/js/thread.js b/addons/mail/static/src/js/thread.js index 6fd5838aa48..a1f8fc8a076 100644 --- a/addons/mail/static/src/js/thread.js +++ b/addons/mail/static/src/js/thread.js @@ -2,7 +2,6 @@ odoo.define('mail.ChatThread', function (require) { "use strict"; var core = require('web.core'); -var time = require('web.time'); var Widget = require('web.Widget'); var QWeb = core.qweb; @@ -49,7 +48,6 @@ var Thread = Widget.extend({ display_order: ORDER.ASC, display_needactions: true, display_stars: true, - default_username: _t('Anonymous'), display_document_link: true, display_avatar: true, squash_close_messages: true, @@ -67,7 +65,7 @@ var Thread = Widget.extend({ // one are both comments wrote by the same author at the same minute var prev_msg; _.each(msgs, function (msg) { - if (!prev_msg || (Math.abs(moment(msg.date).diff(prev_msg.date)) > 60000) || + if (!prev_msg || (Math.abs(msg.date.diff(prev_msg.date)) > 60000) || prev_msg.message_type !== 'comment' || msg.message_type !== 'comment' || (prev_msg.author_id[0] !== msg.author_id[0])) { msg.display_author = true; @@ -79,7 +77,7 @@ var Thread = Widget.extend({ this.$el.html(QWeb.render('mail.ChatThread', { messages: msgs, - options: _.extend({}, this.options, options), + options: options, ORDER: ORDER, })); }, @@ -113,31 +111,19 @@ var Thread = Widget.extend({ var msg = _.extend({}, message); // Set the date in the browser timezone - msg.date = moment(time.str_to_datetime(msg.date)).format('YYYY-MM-DD HH:mm:ss'); - - // Compute displayed author name or email - if ((!msg.author_id || !msg.author_id[0]) && msg.email_from) { - msg.mailto = msg.email_from; - } else { - msg.displayed_author = msg.author_id && msg.author_id[1] || - msg.email_from || - this.options.default_username; - } - - // Compute the avatar_url - if (msg.author_id && msg.author_id[0]) { - msg.avatar_src = "/web/image/res.partner/" + msg.author_id[0] + "/image_small"; - } else if (msg.message_type === 'email') { - msg.avatar_src = "/mail/static/src/img/email_icon.png"; + var date = msg.date.format('YYYY-MM-DD'); + + if (date === moment().format('YYYY-MM-DD')) { + msg.day = _t("Today"); + msg.hour = msg.date.fromNow(); + } else if (date === moment().subtract(1, 'days').format('YYYY-MM-DD')) { + msg.day = _t("Yesterday"); + msg.hour = msg.date.format('hh:mm'); } else { - msg.avatar_src = "/mail/static/src/img/smiley/avatar.jpg"; + msg.day = msg.date.format('LL'); + msg.hour = msg.date.format('hh:mm'); } - // Compute url of attachments - _.each(msg.attachment_ids, function(a) { - a.url = '/web/content/' + a.id + '?download=true'; - }); - return msg; }, diff --git a/addons/mail/static/src/less/client_action.less b/addons/mail/static/src/less/client_action.less index 6b3df329cce..c87f5a4e6bc 100644 --- a/addons/mail/static/src/less/client_action.less +++ b/addons/mail/static/src/less/client_action.less @@ -131,7 +131,7 @@ .o_mail_chat { .o_mail_chat_sidebar { background-color: darken(@gray, 8%); - color: lighten(@gray, 50%); + color: lighten(@gray, 60%); opacity: 0.9; .o_mail_chat_channel_item { @@ -220,16 +220,6 @@ color: white; } } - - .o_composer_mentioned_partners { - color: white; - - > .badge { - color: white; - border: 1px solid white; - background-color: transparent; - } - } } } } diff --git a/addons/mail/static/src/less/composer.less b/addons/mail/static/src/less/composer.less index 11cfbae04d3..37e3b7d0e97 100644 --- a/addons/mail/static/src/less/composer.less +++ b/addons/mail/static/src/less/composer.less @@ -2,8 +2,8 @@ resize: none; } -.o_composer_mentioned_partners, .o_composer_attachments_list { - margin-top: 5px; +.o_composer_attachments_list { + margin-bottom: 5px; text-align: justify; } diff --git a/addons/mail/static/src/xml/chatter.xml b/addons/mail/static/src/xml/chatter.xml index 6cc8b86b19e..6207cdb3980 100644 --- a/addons/mail/static/src/xml/chatter.xml +++ b/addons/mail/static/src/xml/chatter.xml @@ -6,7 +6,7 @@ --> <t t-name="mail.chatter.ChatComposer" t-extend="mail.ChatComposer"> <!-- Insert information before the composer --> - <t t-jquery=".o_composer_mentioned_partners" t-operation="before"> + <t t-jquery=".o_composer_attachments_list" t-operation="before"> <!-- Internal note composer --> <t t-if="widget.options.is_log"> <span class="o_chatter_composer_info"> @@ -53,7 +53,7 @@ </t> <!-- Add a button to open the full composer dialog --> - <t t-jquery=".o_composer_buttons" t-operation="append"> + <t t-jquery=".o_composer_button_add_attachment" t-operation="after"> <button class="btn btn-sm btn-icon fa fa-pencil-square-o o_composer_button_full_composer" type="button"/> </t> </t> diff --git a/addons/mail/static/src/xml/composer.xml b/addons/mail/static/src/xml/composer.xml index e47363c07bf..d199c0d5fdf 100644 --- a/addons/mail/static/src/xml/composer.xml +++ b/addons/mail/static/src/xml/composer.xml @@ -2,14 +2,13 @@ <templates xml:space="preserve"> <div t-name="mail.ChatComposer" class="o_chat_composer"> - <div class="o_composer_mentioned_partners"/> <div class="o_composer_attachments_list"/> <div class="o_composer"> - <textarea class="o_composer_input" placeholder="Write something"/> + <textarea class="o_composer_input" placeholder="Write something..."/> <div class="btn-group o_composer_buttons"> - <button class="btn btn-sm btn-primary o_composer_button_send" type="button">Send</button> <button class="btn btn-sm btn-icon fa fa-smile-o o_composer_button_emoji" type="button" data-toggle="popover"/> <button class="btn btn-sm btn-icon fa fa-paperclip o_composer_button_add_attachment" type="button"/> + <button class="btn btn-sm btn-primary o_composer_button_send" type="button">Send</button> </div> </div> @@ -33,16 +32,16 @@ </t> </t> - <t t-name="mail.ChatComposer.MentionPartnerSuggestions"> + <t t-name="mail.ChatComposer.MentionSuggestions"> <ul class="dropdown-menu" aria-labelledby="dropdownMailMentionMenu"> <t t-set="show_divider" t-value="false"/> <t t-foreach="suggestions" t-as="group"> - <t t-foreach="group" t-as="partner"> - <li t-if="show_divider && (partner_index === 0)" class="divider"/> + <t t-foreach="group" t-as="suggestion"> + <li t-if="show_divider && (suggestion_index === 0)" class="divider"/> <t t-set="show_divider" t-value="true"/> - <li class="o_mention_proposition" t-att-data-id="partner.id"> + <li class="o_mention_proposition" t-att-data-id="suggestion.id"> <a href="#"> - <t t-esc="partner.name"/> <t t-if="partner.email">(<t t-esc="partner.email"/>)</t> + <t t-esc="suggestion.name"/> <t t-if="suggestion.email">(<t t-esc="suggestion.email"/>)</t> </a> </li> </t> @@ -50,26 +49,4 @@ </ul> </t> - <t t-name="mail.ChatComposer.MentionChannelSuggestions"> - <ul class="dropdown-menu" aria-labelledby="dropdownMailMentionMenu"> - <t t-set="show_divider" t-value="false"/> - <t t-foreach="suggestions" t-as="group"> - <t t-foreach="group" t-as="channel"> - <li t-if="show_divider && (channel_index === 0)" class="divider"/> - <t t-set="show_divider" t-value="true"/> - <li class="o_mention_proposition" t-att-data-id="channel.id"> - <a href="#"><t t-esc="channel.name"/></a> - </li> - </t> - </t> - </ul> - </t> - - <t t-name="mail.ChatComposer.MentionTags"> - <t t-if="partners.length">Mention(s): </t> - <t t-foreach="partners" t-as="partner"> - <span class="badge"><t t-esc="partner.name"/></span> - </t> - </t> - </templates> diff --git a/addons/mail/static/src/xml/thread.xml b/addons/mail/static/src/xml/thread.xml index 394627940cb..ebf6687c1a3 100644 --- a/addons/mail/static/src/xml/thread.xml +++ b/addons/mail/static/src/xml/thread.xml @@ -9,9 +9,13 @@ <t t-if="options.display_load_more && options.display_order == ORDER.DESC" t-call="mail.ChatThread.LoadMore"/> </t> - <t t-if="!messages.length && options.display_help"> + <t t-if="options.display_empty_channel"> <t t-call="mail.EmptyChannel" /> </t> + <t t-if="options.display_no_match"> + <div class="o_thread_title">No matches found</div> + <div>No message matches your search. Try to change your search filters.</div> + </t> </t> <!-- When a channel is empty--> @@ -30,11 +34,11 @@ <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"> + <div t-if="current_day != message.day" class="o_thread_date_separator"> <span class="o_thread_date"> - <t t-esc="moment(message.date, 'YYYY-MM-DD hh:mm:ss').format('LL')"/> + <t t-esc="message.day"/> </span> - <t t-set="current_day" t-value="message.date.split(' ')[0]"/> + <t t-set="current_day" t-value="message.day"/> </div> <t t-call="mail.ChatThread.Message"/> @@ -56,8 +60,8 @@ t-att-src="message.avatar_src" class="o_thread_message_avatar"/> </t> - <span t-if="!message.display_author" t-att-title="moment(message.date, 'YYYY-MM-DD hh:mm:ss').format('LLL')" class="o_thread_message_side_date"> - <t t-esc="moment(message.date, 'YYYY-MM-DD hh:mm:ss').format('hh:mm')"/> + <span t-if="!message.display_author" t-att-title="message.date" class="o_thread_message_side_date"> + <t t-esc="message.date.format('hh:mm')"/> </span> <i t-if="!message.display_author && options.display_stars && message.message_type != 'notification'" t-att-class="'fa o_thread_message_star ' + (message.is_starred ? 'fa-star' : 'fa-star-o')" @@ -83,8 +87,8 @@ <t t-esc="message.displayed_author"/> </strong> - <small t-att-title="moment(message.date, 'YYYY-MM-DD hh:mm:ss').format('LLL')"> - - <t t-esc="moment(message.date, 'YYYY-MM-DD hh:mm:ss').format('hh:mm A')"/> + <small t-att-title="message.date"> + - <t t-esc="message.hour"/> </small> <t t-if="message.model && (message.model != 'mail.channel') && options.display_document_link"> on <a class="o_mail_redirect" href="#" t-att-data-oe-model="message.model" t-att-data-oe-id="message.res_id"><t t-esc="message.record_name"/></a> @@ -107,7 +111,7 @@ </t> <t t-call="mail.ChatThread.MessageTracking"/> </t> - <p t-if="message.subject" class="o_mail_subject">Subject: <t t-esc="message.subject"/></p> + <p t-if="options.display_subject && message.subject && message.message_type != 'notification'" class="o_mail_subject">Subject: <t t-esc="message.subject"/></p> <t t-if="!(message.tracking_value_ids && message.tracking_value_ids.length > 0)"> <t t-if="message.body_short"> <div class="o_mail_body_short"> @@ -115,7 +119,6 @@ </div> <div class="o_mail_body_long"> <t t-raw="message.body"/> - <span class="oe_mail_expand"><a href="#">... read less</a></span> </div> </t> <t t-if="! message.body_short"> |
