diff options
Diffstat (limited to 'addons/mail/static/src/js/chat_manager.js')
| -rw-r--r-- | addons/mail/static/src/js/chat_manager.js | 242 |
1 files changed, 190 insertions, 52 deletions
diff --git a/addons/mail/static/src/js/chat_manager.js b/addons/mail/static/src/js/chat_manager.js index f05a3e8b506..75b1819ddd2 100644 --- a/addons/mail/static/src/js/chat_manager.js +++ b/addons/mail/static/src/js/chat_manager.js @@ -16,12 +16,15 @@ var preview_msg_max_size = 350; // optimal for native english speakers var MessageModel = new Model('mail.message', session.context); var ChannelModel = new Model('mail.channel', session.context); var UserModel = new Model('res.users', session.context); +var PartnerModel = new Model('res.partner', session.context); // Private model //---------------------------------------------------------------------------------- var messages = []; var channels = []; +var channels_preview_def; var channel_defs = {}; +var chat_unread_counter = 0; var emojis = []; var emoji_substitutions = {}; var needaction_counter = 0; @@ -50,9 +53,9 @@ bus.on("window_focus", null, function() { function notify_incoming_message (msg, options) { var title = _t('New message'); if (msg.author_id[1]) { - title += _t(' from ') + _.escape(msg.author_id[1]); + title = _.escape(msg.author_id[1]); } - var content = $(msg.body).text().substr(0, preview_msg_max_size); + var content = parse_and_transform(msg.body, strip_html).substr(0, preview_msg_max_size); if (bus.is_odoo_focused()) { if (!options.is_displayed) { @@ -65,7 +68,7 @@ function notify_incoming_message (msg, options) { if (Notification && Notification.permission === "granted") { 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 { web_client.do_notify(title, content); @@ -98,7 +101,7 @@ function add_link (node, transform_children) { if (node.nodeType === 3) { // text node return node.data.replace(url_regexp, function (url) { var href = (!/^(f|ht)tps?:\/\//i.test(url)) ? "http://" + url : url; - return '<a href="' + href + '">' + url + '</a>'; + return '<a target="_blank" href="' + href + '">' + url + '</a>'; }); } if (node.tagName === "A") return node.outerHTML; @@ -106,6 +109,20 @@ function add_link (node, transform_children) { return node.outerHTML; } +function strip_html (node, transform_children) { + if (node.nodeType === 3) return node.data; // text node + if (node.tagName === "BR") return "\n"; + return transform_children(); +} + +function inline (node, transform_children) { + if (node.nodeType === 3) return node.data; + if (node.tagName === "BR") return " "; + if (node.tagName.match(/^(A|P|DIV|PRE)$/)) return transform_children(); + node.innerHTML = transform_children(); + return node.outerHTML; +} + // Message and channel manipulation helpers //---------------------------------------------------------------------------------- @@ -116,28 +133,29 @@ function add_message (data, options) { if (!msg) { msg = make_message(data); - // Keep the array ordered by date when inserting the new message + // Keep the array ordered by id when inserting the new message messages.splice(_.sortedIndex(messages, msg, 'id'), 0, msg); _.each(msg.channel_ids, function (channel_id) { var channel = chat_manager.get_channel(channel_id); - var is_chat = channel && !_.contains(["public", "private"], channel.type); if (channel) { add_to_cache(msg, []); if (options.domain && options.domain !== []) { add_to_cache(msg, options.domain); } - if (options.increment_unread) { - update_channel_unread_counter(channel, channel.unread_counter+1); + if (channel.hidden) { + channel.hidden = false; + chat_manager.bus.trigger('new_channel', channel); + } + if (!msg.author_id || msg.author_id[0] !== session.partner_id) { + if (options.increment_unread) { + update_channel_unread_counter(channel, channel.unread_counter+1); + } + if (channel.is_chat && options.show_notification) { + var query = {is_displayed: false}; + chat_manager.bus.trigger('anyone_listening', channel, query); + notify_incoming_message(msg, query); + } } - } - if (channel && channel.hidden) { - channel.hidden = false; - chat_manager.bus.trigger('new_channel', channel); - } - if (is_chat && options.show_notification && (!msg.author_id || msg.author_id[0] !== session.partner_id)) { - var query = {is_displayed: false}; - chat_manager.bus.trigger('anyone_listening', channel, query); - notify_incoming_message(msg, query); } }); if (!options.silent) { @@ -289,7 +307,7 @@ function make_channel (data, options) { display_needactions: options.display_needactions, mass_mailing: data.mass_mailing, needaction_counter: data.message_needaction_counter || 0, - unread_counter: data.message_unread_counter || 0, + unread_counter: 0, last_seen_message_id: data.seen_message_id, cache: {'[]': { all_history_loaded: false, @@ -309,6 +327,12 @@ function make_channel (data, options) { channel.status = data.direct_partner[0].im_status; pinned_dm_partners.push(channel.direct_partner_id); 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)$/); + if (data.message_unread_counter) { + update_channel_unread_counter(channel, data.message_unread_counter); } return channel; } @@ -425,37 +449,56 @@ function fetch_document_messages (ids, options) { } function update_channel_unread_counter (channel, counter) { + if (channel.is_chat) { + chat_unread_counter = chat_unread_counter - channel.unread_counter + counter; + } channel.unread_counter = counter; chat_manager.bus.trigger("update_channel_unread_counter", channel); } 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; - }); + return ChannelModel.call('channel_seen', [[channel.id]], {}, {shadow: true}); }, 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]); - } else if (model === 'bus.presence') { - // update presence of users - on_presence_notification(notification[1]); +function on_notification (notifications) { + // sometimes, the web client receives unsubscribe notification and an extra + // notification on that channel. This is then followed by an attempt to + // rejoin the channel that we just left. The next few lines remove the + // extra notification to prevent that situation to occur. + var unsubscribed_notif = _.find(notifications, function (notif) { + return notif[1].info === "unsubscribe"; + }); + if (unsubscribed_notif) { + notifications = _.reject(notifications, function (notif) { + return notif[0][1] === "mail.channel" && notif[0][2] === unsubscribed_notif[1].id; + }); } + _.each(notifications, function (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]); + } else if (model === 'bus.presence') { + // update presence of users + on_presence_notification(notification[1]); + } + }); } function on_needaction_notification (message) { - message = add_message(message, { channel_id: 'channel_inbox', show_notification: true} ); + message = add_message(message, { + channel_id: 'channel_inbox', + show_notification: true, + increment_unread: true, + }); invalidate_caches(message.channel_ids); needaction_counter++; _.each(message.channel_ids, function (channel_id) { @@ -490,6 +533,8 @@ function on_partner_notification (data) { on_mark_as_read_notification(data); } else if (data.type === 'mark_as_unread') { on_mark_as_unread_notification(data); + } else if (data.info === 'channel_seen') { + on_channel_seen_notification(data); } else { on_chat_session_notification(data); } @@ -526,7 +571,7 @@ function on_mark_as_read_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; + channel.needaction_counter = Math.max(channel.needaction_counter - data.message_ids.length, 0); } }); } else { // if no channel_ids specified, this is a 'mark all read' in the inbox @@ -534,7 +579,7 @@ function on_mark_as_read_notification (data) { channel.needaction_counter = 0; }); } - needaction_counter -= data.message_ids.length; + needaction_counter = Math.max(needaction_counter - data.message_ids.length, 0); chat_manager.bus.trigger('update_needaction', needaction_counter); } @@ -560,6 +605,16 @@ function on_mark_as_unread_notification (data) { chat_manager.bus.trigger('update_needaction', needaction_counter); } +function on_channel_seen_notification (data) { + var channel = chat_manager.get_channel(data.id); + if (channel) { + channel.last_seen_message_id = data.last_message_id; + if (channel.unread_counter) { + update_channel_unread_counter(channel, 0); + } + } +} + function on_chat_session_notification (chat_session) { var channel; if ((chat_session.channel_type === "channel") && (chat_session.state === "open")) { @@ -572,18 +627,21 @@ function on_chat_session_notification (chat_session) { if ((chat_session.state === "open") || (chat_session.state === "folded")) { channel = chat_session.is_minimized && chat_manager.get_channel(chat_session.id); if (channel) { + channel.is_detached = true; + channel.is_folded = (chat_session.state === "folded"); chat_manager.bus.trigger("open_chat", channel); } } else if (chat_session.state === "closed") { channel = chat_manager.get_channel(chat_session.id); if (channel) { + channel.is_detached = false; chat_manager.bus.trigger("close_chat", channel); } } } function on_presence_notification (data) { - var dm = _.findWhere(channels, {direct_partner_id: data.id}); + var dm = chat_manager.get_dm_from_partner_id(data.id); if (dm) { dm.status = data.im_status; chat_manager.bus.trigger('update_dm_presence', dm); @@ -696,7 +754,7 @@ var chat_manager = { return MessageModel.call('mark_as_unread', [message_ids, [channel.id]]); }, 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); channel_seen(channel); } @@ -710,6 +768,10 @@ var chat_manager = { return _.findWhere(channels, {id: id}); }, + get_dm_from_partner_id: function (partner_id) { + return _.findWhere(channels, {direct_partner_id: partner_id}); + }, + all_history_loaded: function (channel, domain) { return get_channel_cache(channel, domain).all_history_loaded; }, @@ -720,7 +782,7 @@ var chat_manager = { } if (!channel.members_deferred) { channel.members_deferred = ChannelModel - .call("channel_fetch_listeners", [channel.uuid]) + .call("channel_fetch_listeners", [channel.uuid], {}, {shadow: true}) .then(function (members) { var suggestions = []; _.each(mention_partner_suggestions, function (partners) { @@ -729,7 +791,7 @@ var chat_manager = { })); }); - return [members].concat(suggestions); + return [members]; }); } return channel.members_deferred; @@ -742,13 +804,31 @@ var chat_manager = { get_needaction_counter: function () { return needaction_counter; }, + get_chat_unread_counter: function () { + return chat_unread_counter; + }, + + get_last_seen_message: function (channel) { + if (channel.last_seen_message_id) { + var messages = channel.cache['[]'].messages; + var msg = _.findWhere(messages, {id: channel.last_seen_message_id}); + if (msg) { + var i = _.sortedIndex(messages, msg, 'id') + 1; + while (i < messages.length && messages[i].author_id && messages[i].author_id[0] === session.partner_id) { + msg = messages[i]; + i++; + } + return msg; + } + } + }, get_discuss_ids: function () { return discuss_ids; }, detach_channel: function (channel) { - return ChannelModel.call("channel_minimize", [channel.uuid, true]); + return ChannelModel.call("channel_minimize", [channel.uuid, true], {}, {shadow: true}); }, remove_chatter_messages: function (model) { messages = _.reject(messages, function (message) { @@ -783,6 +863,9 @@ var chat_manager = { } return channel_defs[channel_id]; }, + open_and_detach_dm: function (partner_id) { + return ChannelModel.call('channel_get_and_minimize', [[partner_id]]).then(add_channel); + }, unsubscribe: function (channel) { var def; @@ -797,13 +880,16 @@ var chat_manager = { }, close_chat_session: function (channel_id) { var channel = this.get_channel(channel_id); - ChannelModel.call("channel_fold", [], {uuid : channel.uuid, state : "closed"}); + ChannelModel.call("channel_fold", [], {uuid : channel.uuid, state : "closed"}, {shadow: true}); }, - fold_channel: function (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; - }); + fold_channel: function (channel_id, folded) { + var args = { + uuid: this.get_channel(channel_id).uuid, + }; + if (_.isBoolean(folded)) { + args.state = folded ? 'folded' : 'open'; + } + return ChannelModel.call("channel_fold", [], args, {shadow: true}); }, /** * Special redirection handling for given model and id @@ -839,6 +925,58 @@ var chat_manager = { }); } }, + + get_channels_preview: function (channels) { + var channels_preview = _.map(channels, function (channel) { + var info = _.pick(channel, 'id', 'is_chat', 'name', 'status', 'unread_counter'); + info.last_message = _.last(channel.cache['[]'].messages); + if (!info.is_chat) { + info.image_src = '/web/image/mail.channel/'+channel.id+'/image_small'; + } else if (channel.direct_partner_id) { + info.image_src = '/web/image/res.partner/'+channel.direct_partner_id+'/image_small'; + } else { + info.image_src = '/mail/static/src/img/smiley/avatar.jpg'; + } + return info; + }); + if (!channels_preview_def) { + var missing_channel_ids = _.pluck(_.where(channels_preview, {last_message: undefined}), 'id'); + if (missing_channel_ids.length) { + channels_preview_def = ChannelModel + .call('channel_fetch_preview', [missing_channel_ids], {}, {shadow: true}) + .then(function (channels) { + _.each(channels, function (channel) { + var msg = add_message(channel.last_message); + _.findWhere(channels_preview, {id: channel.id}).last_message = msg; + }); + }); + } else { + channels_preview_def = $.when(); + } + } + return channels_preview_def.then(function () { + return _.filter(channels_preview, function (channel) { + return channel.last_message; // remove empty channels + }); + }); + }, + get_message_body_preview: function (message_body) { + return parse_and_transform(message_body, inline); + }, + + search_partner: function (search_val, limit) { + return PartnerModel.call('im_search', [search_val, limit || 20], {}, {shadow: true}).then(function(result) { + var values = []; + _.each(result, function(user) { + var escaped_name = _.escape(user.name); + values.push(_.extend(user, { + 'value': escaped_name, + 'label': escaped_name, + })); + }); + return values; + }); + }, }; // Initialization @@ -867,13 +1005,13 @@ function init () { var load_emojis = session.rpc("/mail/chat_init").then(function (result) { emojis = result.emoji; _.each(emojis, function(emoji) { - emoji_substitutions[emoji.source] = emoji.substitution; + emoji_substitutions[_.escape(emoji.source)] = emoji.substitution; }); }); var ir_model = new Model("ir.model.data"); - 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"]); + var load_menu_id = ir_model.call("xmlid_to_res_id", ["mail.mail_channel_menu_root_chat"], {}, {shadow: true}); + var load_action_id = ir_model.call("xmlid_to_res_id", ["mail.mail_channel_action_client_chat"], {}, {shadow: true}); bus.on('notification', null, on_notification); |
