diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py index 8af27a4f011..ea32babb318 100644 --- a/addons/mail/__openerp__.py +++ b/addons/mail/__openerp__.py @@ -61,9 +61,11 @@ Main Features 'static/src/xml/client_action.xml', 'static/src/xml/composer.xml', 'static/src/xml/chatter.xml', + 'static/src/xml/followers.xml', 'static/src/xml/systray.xml', 'static/src/xml/thread.xml', 'static/src/xml/chat_window.xml', + 'static/src/xml/extended_chat_window.xml', 'static/src/xml/announcement.xml', ], } diff --git a/addons/mail/static/src/js/chat_manager.js b/addons/mail/static/src/js/chat_manager.js index fcc7cffd5d1..856b4def3fd 100644 --- a/addons/mail/static/src/js/chat_manager.js +++ b/addons/mail/static/src/js/chat_manager.js @@ -2,6 +2,7 @@ odoo.define('mail.chat_manager', function (require) { "use strict"; var bus = require('bus.bus').bus; +var utils = require('mail.utils'); var config = require('web.config'); var core = require('web.core'); var data = require('web.data'); @@ -36,36 +37,13 @@ var global_unread_counter = 0; var pinned_dm_partners = []; // partner_ids we have a pinned DM with var client_action_open = false; -// Utils: Window focus/unfocus, beep, tab title, parsing html strings +// Global unread counter and notifications //---------------------------------------------------------------------------------- -var beep = (function () { - if (typeof(Audio) === "undefined") { - return function () {}; - } - var audio = new Audio(); - var ext = audio.canPlayType("audio/ogg; codecs=vorbis") ? ".ogg" : ".mp3"; - audio.src = session.url("/mail/static/src/audio/ting" + ext); - return function () { audio.play(); }; -})(); - bus.on("window_focus", null, function() { global_unread_counter = 0; web_client.set_title_part("_chat"); }); -// to do: move this to mail.utils -function send_native_notification(title, content) { - var notification = new Notification(title, {body: content, icon: "/mail/static/src/img/odoo_o.png"}); - notification.onclick = function (e) { - window.focus(); - if (this.cancel) { - this.cancel(); - } else if (this.close) { - this.close(); - } - }; -} - function notify_incoming_message (msg, options) { if (bus.is_odoo_focused() && options.is_displayed) { // no need to notify @@ -75,7 +53,7 @@ function notify_incoming_message (msg, options) { if (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 = utils.parse_and_transform(msg.body, utils.strip_html).substr(0, preview_msg_max_size); if (!bus.is_odoo_focused()) { global_unread_counter++; @@ -83,60 +61,7 @@ function notify_incoming_message (msg, options) { web_client.set_title_part("_chat", tab_title); } - if (Notification && Notification.permission === "granted") { - if (bus.is_master) { - send_native_notification(title, content); - } - } else { - web_client.do_notify(title, content); - if (bus.is_master) { - beep(); - } - } -} - -function parse_and_transform(html_string, transform_function) { - var open_token = "OPEN" + Date.now(); - var string = html_string.replace(/</g, open_token); - var children = $('
, )
- * TDE note : should be done server-side, in Python -> use mail.compose.message ?
- */
-var get_text2html = function (text) {
- return text
- .replace(/((?:https?|ftp):\/\/[\S]+)/g,'$1 ')
- .replace(/[\n\r]/g,' , )
+// TDE note : should be done server-side, in Python -> use mail.compose.message ?
+function get_text2html (text) {
+ return text
+ .replace(/((?:https?|ftp):\/\/[\S]+)/g,'$1 ')
+ .replace(/[\n\r]/g,'
');
-};
-
-core.form_widget_registry.add('mail_followers', Followers);
core.form_widget_registry.add('mail_thread', Chatter);
return Chatter;
diff --git a/addons/mail/static/src/js/client_action.js b/addons/mail/static/src/js/client_action.js
index b1167eccdaa..73a0c0423a3 100644
--- a/addons/mail/static/src/js/client_action.js
+++ b/addons/mail/static/src/js/client_action.js
@@ -4,6 +4,7 @@ odoo.define('mail.chat_client_action', function (require) {
var chat_manager = require('mail.chat_manager');
var composer = require('mail.composer');
var ChatThread = require('mail.ChatThread');
+var utils = require('mail.utils');
var config = require('web.config');
var ControlPanelMixin = require('web.ControlPanelMixin');
@@ -42,10 +43,8 @@ var PartnerInviteDialog = Dialog.extend({
click: _.bind(this.on_click_add, this),
}],
});
- this.PartnersModel = new Model('res.partner');
},
start: function(){
- var self = this;
this.$input = this.$('.o_mail_chat_partner_invite_input');
this.$input.select2({
width: '100%',
@@ -56,7 +55,7 @@ var PartnerInviteDialog = Dialog.extend({
return $('').text(item.text).prepend(status);
},
query: function (query) {
- self.PartnersModel.call('im_search', [query.term, 20]).then(function(result){
+ chat_manager.search_partner(query.term, 20).then(function(result){
var data = [];
_.each(result, function(partner){
partner.text = partner.name;
@@ -125,7 +124,7 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
var def = window.Notification.requestPermission();
if (def) {
def.then(function () {
- chat_manager.send_native_notification('Permission granted', 'Odoo has now the permission to send you native notifications on this device.');
+ utils.send_native_notification('Permission granted', 'Odoo has now the permission to send you native notifications on this device.');
});
}
},
diff --git a/addons/mail/static/src/js/composer.js b/addons/mail/static/src/js/composer.js
index 4fafe8427eb..35c5e37f054 100644
--- a/addons/mail/static/src/js/composer.js
+++ b/addons/mail/static/src/js/composer.js
@@ -2,6 +2,7 @@ odoo.define('mail.composer', function (require) {
"use strict";
var chat_manager = require('mail.chat_manager');
+var utils = require('mail.utils');
var core = require('web.core');
var data = require('web.data');
@@ -13,20 +14,6 @@ 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': '[ýÿ]',
- ' ': '[()\\[\\]]',
-};
-
var NON_BREAKING_SPACE = '\u00a0';
var MENTION_PARTNER_DELIMITER = '@';
@@ -614,12 +601,12 @@ var BasicComposer = Widget.extend({
// 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');
+ var search_regexp = new RegExp(utils.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;
+ partner.name && utils.unaccent(partner.name).search(search_regexp) !== -1;
});
if (filtered_partners.length) {
suggestions.push(filtered_partners.slice(0, limit));
@@ -652,12 +639,6 @@ var BasicComposer = 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;
- },
});
var ExtendedComposer = BasicComposer.extend({
diff --git a/addons/mail/static/src/js/extended_chat_window.js b/addons/mail/static/src/js/extended_chat_window.js
new file mode 100644
index 00000000000..652a6236591
--- /dev/null
+++ b/addons/mail/static/src/js/extended_chat_window.js
@@ -0,0 +1,30 @@
+odoo.define('mail.ExtendedChatWindow', function (require) {
+"use strict";
+
+var chat_manager = require('mail.chat_manager');
+var ChatWindow = require('mail.ChatWindow');
+
+return ChatWindow.extend({
+ template: "mail.ExtendedChatWindow",
+
+ start: function () {
+ var self = this;
+ return this._super().then(function () {
+ if (self.options.thread_less) {
+ self.$el.addClass('o_thread_less');
+ self.$('.o_chat_search_input input')
+ .autocomplete({
+ source: function(request, response) {
+ chat_manager.search_partner(request.term, 10).done(response);
+ },
+ select: function(event, ui) {
+ self.trigger('open_dm_session', ui.item.id);
+ },
+ })
+ .focus();
+ }
+ });
+ },
+});
+
+});
diff --git a/addons/mail/static/src/js/followers.js b/addons/mail/static/src/js/followers.js
new file mode 100644
index 00000000000..36f45af2893
--- /dev/null
+++ b/addons/mail/static/src/js/followers.js
@@ -0,0 +1,411 @@
+odoo.define('mail.Followers', function (require) {
+"use strict";
+
+var ajax = require('web.ajax');
+var core = require('web.core');
+var data = require('web.data');
+var Dialog = require('web.Dialog');
+var form_common = require('web.form_common');
+var session = require('web.session');
+
+var _t = core._t;
+var qweb = core.qweb;
+
+
+// -----------------------------------------------------------------------------
+// Followers Widget ('mail_followers' widget)
+//
+// Since it is displayed on a form view, it extends 'AbstractField' widget.
+//
+// Note: the followers widget is moved inside the chatter by the chatter itself
+// for layout purposes.
+// -----------------------------------------------------------------------------
+var Followers = form_common.AbstractField.extend({
+ template: 'mail.Followers',
+
+ init: function () {
+ this._super.apply(this, arguments);
+
+ this.image = this.node.attrs.image || 'image_small';
+ this.comment = this.node.attrs.help || false;
+ this.ds_model = new data.DataSetSearch(this, this.view.model);
+
+ this.value = [];
+ this.followers = [];
+ this.followers_fetched = $.Deferred();
+ this.data_subtype = {};
+
+ this.view_is_editable = this.__parentedParent.is_action_enabled('edit');
+ },
+
+ start: function () {
+ // use actual_mode property on view to know if the view is in create mode anymore
+ this.view.on("change:actual_mode", this, this.on_check_visibility_mode);
+ this.on_check_visibility_mode();
+ this.reinit();
+ this.bind_events();
+ return this._super();
+ },
+
+ on_check_visibility_mode: function () {
+ this.set({"force_invisible": this.view.get("actual_mode") === "create"});
+ },
+
+ set_value: function (_value) {
+ this.value = _value;
+ this._super(_value);
+ },
+
+ reinit: function () {
+ this.data_subtype = {};
+ this.message_is_follower = undefined;
+ this.display_buttons();
+ },
+
+ bind_events: function () {
+ var self = this;
+
+ // event: click on '(Un)Follow' button, that toggles the follow for uid
+ this.$el.on('click', '.o_followers_follow_button', function () {
+ if ($(this).hasClass('o_followers_notfollow')) {
+ self.do_follow();
+ } else {
+ self.do_unfollow({user_ids: [session.uid]});
+ }
+ });
+
+ // event: click on a subtype, that (un)subscribe for this subtype
+ this.$el.on('click', '.o_subtypes_list input', function(event) {
+ event.stopPropagation();
+ self.do_update_subscription(event);
+ var $list = self.$('.o_subtypes_list');
+ if (!$list.hasClass('open')) {
+ $list.addClass('open');
+ }
+ if (self.$('.o_subtypes_list ul')[0].children.length < 1) {
+ $list.removeClass('open');
+ }
+ });
+
+ // event: click on 'invite' button, that opens the invite wizard
+ this.$el.on('click', '.o_add_follower', function(event) {
+ event.preventDefault();
+ self.on_invite_follower(false);
+ });
+ this.$el.on('click', '.o_add_follower_channel', function(event) {
+ event.preventDefault();
+ self.on_invite_follower(true);
+ });
+
+ // event: click on 'edit_subtype(pencil)' button to edit subscription
+ this.$el.on('click', '.o_edit_subtype', self.on_edit_subtype);
+ this.$el.on('click', '.o_remove_follower', self.on_remove_follower);
+ this.$el.on('click', '.o_mail_redirect', self.on_click_redirect);
+ },
+
+ on_edit_subtype: function (event) {
+ var self = this;
+ var $currentTarget = $(event.currentTarget);
+ var follower_id = $currentTarget.data('follower-id'); // id of model mail_follower
+ var res_id = $currentTarget.data('oe-id'); // id of model res_partner or mail_channel
+ var is_channel = $currentTarget.data('oe-model') === 'mail.channel';
+ self.dialog = new Dialog(this, {
+ size: 'medium',
+ title: _t('Edit Subscription of ') + $currentTarget.siblings('a').text(),
+ buttons: [
+ {
+ text: _t("Apply"),
+ classes: 'btn-primary',
+ click: function () {
+ self.do_update_subscription(event, res_id, is_channel);
+ },
+ close: true
+ },
+ {
+ text: _t("Cancel"),
+ close: true,
+ },
+ ],
+ }).open();
+ return self.fetch_subtypes(follower_id).then(function (data) {
+ self.display_subtypes(data, true, is_channel);
+ });
+ },
+
+ on_invite_follower: function (channel_only) {
+ var self = this;
+ var action = {
+ type: 'ir.actions.act_window',
+ res_model: 'mail.wizard.invite',
+ view_mode: 'form',
+ view_type: 'form',
+ views: [[false, 'form']],
+ name: _t('Invite Follower'),
+ target: 'new',
+ context: {
+ 'default_res_model': this.view.dataset.model,
+ 'default_res_id': this.view.datarecord.id,
+ 'mail_invite_follower_channel_only': channel_only,
+ },
+ };
+ this.do_action(action, {
+ on_close: function () {
+ self.read_value();
+ },
+ });
+ },
+
+ on_remove_follower: function (event) {
+ var res_model = $(event.target).parent().find('a').data('oe-model');
+ var res_id = $(event.target).parent().find('a').data('oe-id');
+ if (res_model === 'res.partner') {
+ return this.do_unfollow({partner_ids: [res_id]});
+ } else {
+ return this.do_unfollow({channel_ids: [res_id]});
+ }
+ },
+
+ on_click_redirect: function (event) {
+ event.preventDefault();
+ var res_id = $(event.target).data('oe-id');
+ var res_model = $(event.target).data('oe-model');
+ this.trigger('redirect', res_model, res_id);
+ },
+
+ read_value: function () {
+ var self = this;
+ return this.ds_model.read_ids([this.view.datarecord.id], ['message_follower_ids'])
+ .then(function (results) {
+ self.value = results[0].message_follower_ids;
+ self.render_value();
+ });
+ },
+
+ render_value: function () {
+ var self = this;
+ this.reinit();
+ this.value = this.value || [];
+ return this.fetch_followers().then(function (results) {
+ self.display_followers(results.followers);
+ if (results.subtypes) { // current user is follower
+ self.display_subtypes(results.subtypes);
+ }
+ self.display_buttons();
+ }).fail(this.display_generic.bind(this));
+ },
+
+ fetch_followers: function () {
+ return ajax.jsonRpc('/mail/read_followers', 'call', {
+ follower_ids: this.value,
+ res_model: this.view.model,
+ res_id: this.view.datarecord.id,
+ });
+ },
+
+ /** Fetch subtypes of the given follower
+ * @param {int} [follower_id] the id of the follower
+ * @param {string} [follower_model] 'res.partner' or 'mail.channel'
+ */
+ fetch_subtypes: function (follower_id) {
+ return ajax.jsonRpc('/mail/read_subscription_data', 'call', {
+ res_model: this.view.model,
+ follower_id: follower_id,
+ });
+ },
+
+ _format_followers: function (count){
+ var str = '';
+ if(count <= 0){
+ str = _t('No follower');
+ }else if(count === 1){
+ str = _t('One follower');
+ }else{
+ str = ''+count+' '+_t('followers');
+ }
+ return str;
+ },
+
+ /** Read on res.partner failed: only display the number of followers */
+ display_generic: function () {
+ this.$('.o_followers_actions').hide();
+ this.$('.o_followers_list').hide();
+ this.$('.o_followers_title_box > button').prop('disabled', true);
+ this.$('.o_followers_count').html(this._format_followers(this.value.length));
+ },
+
+ /** Display the followers */
+ display_followers: function (records) {
+ var self = this;
+ this.followers = records || this.followers;
+ this.trigger('followers_update', this.followers);
+
+ // clean and display title
+ var $followers_list = this.$('.o_followers_list').empty();
+ this.$('.o_followers_count').html(this._format_followers(this.followers.length));
+ var user_follower = _.filter(this.followers, function (rec) { return rec.is_uid;});
+ this.message_is_follower = user_follower.length >= 1;
+ this.follower_id = this.message_is_follower ? user_follower[0].id : undefined;
+
+ // render the dropdown content
+ $(qweb.render('mail.Followers.add_more', {'widget': self})).appendTo($followers_list);
+ var $follower_li;
+ _(this.followers).each(function (record) {
+ $follower_li = $(qweb.render('mail.Followers.partner', {
+ 'record': _.extend(record, {'avatar_url': '/web/image/' + record.res_model + '/' + record.res_id + '/image_small'}),
+ 'widget': self})
+ );
+ $follower_li.appendTo($followers_list);
+
+ // On mouse-enter it will show the edit_subtype pencil.
+ if (record.is_editable) {
+ $follower_li.on('mouseenter mouseleave', function(e) {
+ $(e.currentTarget).find('.o_edit_subtype').toggleClass('hide', e.type === 'mouseleave');
+ });
+ }
+ });
+ },
+
+ display_buttons: function () {
+ if (this.message_is_follower) {
+ this.$('button.o_followers_follow_button').removeClass('o_followers_notfollow').addClass('o_followers_following');
+ this.$('.o_subtypes_list > .dropdown-toggle').attr('disabled', false);
+ } else {
+ this.$('button.o_followers_follow_button').removeClass('o_followers_following').addClass('o_followers_notfollow');
+ this.$('.o_subtypes_list > .dropdown-toggle').attr('disabled', true);
+ }
+ },
+
+ /** Display subtypes: {'name': default, followed} */
+ display_subtypes:function (data, dialog, display_warning) {
+ var old_parent_model;
+ var $list;
+ if (dialog) {
+ $list = $('').appendTo(this.dialog.$el);
+ } else {
+ $list = this.$('.o_subtypes_list ul');
+ }
+ $list.empty();
+
+ this.data_subtype = data;
+ this.records_length = _.map(data, function(value, index) { return index; }).length;
+
+ if (this.records_length > 1) {
+ this.display_followers();
+ }
+
+ _.each(data, function (record) {
+ if (old_parent_model !== record.parent_model && old_parent_model !== undefined) {
+ $list.append($('
');
+}
+
+var accented_letters_mapping = {
+ 'a': '[àáâãäå]',
+ 'ae': 'æ',
+ 'c': 'ç',
+ 'e': '[èéêë]',
+ 'i': '[ìíîï]',
+ 'n': 'ñ',
+ 'o': '[òóôõö]',
+ 'oe': 'œ',
+ 'u': '[ùúûűü]',
+ 'y': '[ýÿ]',
+ ' ': '[()\\[\\]]',
+};
+function unaccent (str) {
+ _.each(accented_letters_mapping, function (value, key) {
+ str = str.replace(new RegExp(value, 'g'), key);
+ });
+ return str;
+}
+
+return {
+ send_notification: send_notification,
+ parse_and_transform: parse_and_transform,
+ add_link: add_link,
+ strip_html: strip_html,
+ inline: inline,
+ parse_email: parse_email,
+ get_text2html: get_text2html,
+ unaccent: unaccent,
+};
+
+});
diff --git a/addons/mail/static/src/js/window_manager.js b/addons/mail/static/src/js/window_manager.js
index 6b46ea4abee..eccafd08b0c 100644
--- a/addons/mail/static/src/js/window_manager.js
+++ b/addons/mail/static/src/js/window_manager.js
@@ -398,34 +398,3 @@ core.bus.on('web_client_ready', null, function () {
});
});
-
-// FIXME: move this to its own file in master
-odoo.define('mail.ExtendedChatWindow', function (require) {
-"use strict";
-
-var chat_manager = require('mail.chat_manager');
-var ChatWindow = require('mail.ChatWindow');
-
-return ChatWindow.extend({
- template: "mail.ExtendedChatWindow",
- start: function () {
- var self = this;
- return this._super().then(function () {
- if (self.options.thread_less) {
- self.$el.addClass('o_thread_less');
- self.$('.o_chat_search_input input')
- .autocomplete({
- source: function(request, response) {
- chat_manager.search_partner(request.term, 10).done(response);
- },
- select: function(event, ui) {
- self.trigger('open_dm_session', ui.item.id);
- },
- })
- .focus();
- }
- });
- },
-});
-
-});
diff --git a/addons/mail/static/src/less/chat_window.less b/addons/mail/static/src/less/chat_window.less
index 3d61cb0cc19..12d7ae3e682 100644
--- a/addons/mail/static/src/less/chat_window.less
+++ b/addons/mail/static/src/less/chat_window.less
@@ -135,24 +135,3 @@
.o_no_chat_window .o_chat_window {
display: none;
}
-
-// FIXME: move this to its own file in master
-.o_chat_window.o_thread_less {
- .o_chat_search_input {
- .o-flex-display();
- padding: 5px;
- > span {
- .o-flex(0, 0, auto);
- }
- > input {
- margin-left: 5px;
- .o-flex(1, 1, 100%);
- }
- }
- .o_mail_thread {
- background-color: lightgray;
- }
- .o_chat_input {
- display: none;
- }
-}
diff --git a/addons/mail/static/src/less/chatter.less b/addons/mail/static/src/less/chatter.less
index 8ea43b8373e..faa79584067 100644
--- a/addons/mail/static/src/less/chatter.less
+++ b/addons/mail/static/src/less/chatter.less
@@ -40,94 +40,3 @@
margin-top: 10px;
}
}
-
-.o_followers {
- float: right;
- @media (max-width: @screen-xs-max) {
- display: none !important; // Hide the followers in mobile
- }
-
- .o_followers_actions {
- position: relative;
- float: right;
-
- .o_followers_follow_button {
- width: 120px;
-
- .o_follow, .o_followers_unfollow, .o_followers_following {
- display: none;
- }
- // when following, show 'following'
- &.o_followers_following .o_followers_following {
- display: inline;
- }
- // when following and hovering, show 'unfollow'
- &.o_followers_following:hover .o_followers_following {
- display: none;
- }
- &.o_followers_following:hover .o_followers_unfollow {
- display: inline;
- }
- // when not following show 'follow'
- &.o_followers_notfollow .o_follow {
- display: inline;
- }
- }
-
- .o_subtype {
- padding-left: 10px;
- }
-
- }
-
- .o_followers_title_box {
- position: relative;
- float: right;
- margin-left: 5px;
- > button {
- width: 140px;
- }
-
- .o_followers_count {
- padding-right: 5px;
- }
-
- .o_followers_list {
- font-size: small;
-
- .o_partner {
- @partner_avatar_size: 24px;
- position: relative; // required for old design compatibility
- > a {
- max-width: 160px;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- height: @partner_avatar_size;
- padding: 3px @partner_avatar_size + 8px;
- }
- > img {
- .o-position-absolute();
- width: @partner_avatar_size;
- }
- .o_remove_follower {
- .o-position-absolute(6px, 5px);
- cursor: pointer;
- }
- .o_edit_subtype {
- .o-position-absolute(6px, 18px);
- cursor: pointer;
- }
- }
-
- }
- }
-}
-
-// Edit subtypes in dialog
-.modal .modal-body > ul {
- padding-left: 0;
- > li.o_subtype {
- list-style: none;
- }
-}
diff --git a/addons/mail/static/src/less/client_action.less b/addons/mail/static/src/less/client_action.less
index b2749199ca4..eb91fd78fb3 100644
--- a/addons/mail/static/src/less/client_action.less
+++ b/addons/mail/static/src/less/client_action.less
@@ -274,7 +274,7 @@
}
}
-// User status bullet (in Discuss sidebar and Invite dialog)
+// User status bullet
.o_mail_user_status {
font-size: 10px;
position: relative;
@@ -286,123 +286,3 @@
color: orange;
}
}
-
-// Navbar icon and dropdown (FIXME: move to systray.less)
-.o_mail_navbar_item {
- > a {
- opacity: 1;
- > i {
- font-size: larger;
- }
- }
- &.o_no_notification > a {
- opacity: 0.5;
- .o_notification_counter {
- display: none;
- }
- }
- &.o_unread_chat > a > i {
- color: @odoo-brand-optional;
- }
-
- &.open .o_mail_navbar_dropdown {
- .o-flex-display();
- .o-flex-flow(column, nowrap);
- }
- .o_mail_navbar_dropdown {
- width: 350px;
- padding: 0;
-
- .o_spinner {
- .o-flex-display();
- .o-align-items(center);
- .o-justify-content(center);
- height: 50px;
- }
-
- .o_mail_navbar_dropdown_top {
- .o-flex-display();
- .o-flex(0, 0, auto);
- justify-content: space-between;
- border-bottom: 1px solid lightgray;
-
- .o_filter_button, .o_new_message {
- .btn-link;
- padding: 5px;
- }
- .o_filter_button {
- color: @odoo-main-color-muted;
- &:hover, &.o_selected {
- color: @odoo-brand-optional;
- }
- &.o_selected {
- cursor: default;
- font-weight: bold;
- }
- }
- }
-
- .o_mail_navbar_dropdown_channels {
- .o-flex(0, 1, auto);
- max-height: 400px;
- min-height: 50px;
- overflow-y: auto;
-
- @media (max-width: @screen-xs-max) {
- max-height: none;
- }
-
- .o_mail_channel_preview {
- .o-flex-display();
- height: 50px;
- align-items: center;
- padding: 5px;
- cursor: pointer;
- font-size: 12px;
- overflow: hidden;
- &~.o_mail_channel_preview {
- border-top: 1px solid lightgray;
- }
- &.o_channel_unread {
- background-color: lighten(lightgray, 10%);
- }
- &:hover {
- background-color: lighten(lightgray, 5%);
- }
-
- .o_channel_image {
- .o-flex(0, 0, 36px);
- max-height: 36px;
- }
- .o_channel_info {
- .o-flex(1, 1, 100%);
- margin-left: 5px;
- overflow: hidden;
-
- .o_channel_title {
- .o-flex-display();
- .o_channel_name {
- .o-flex(1, 1, auto);
- .o-text-overflow();
- }
- .o_last_message_date {
- .o-flex(0, 0, auto);
- padding-top: 3px;
- font-size: xx-small;
- color: @odoo-main-color-muted;
- }
- }
- .o_last_message_preview {
- width: 100%;
- color: @odoo-main-color-muted;
- .o-text-overflow();
- }
- }
- }
- }
- }
-}
-
-.o_no_chat_window .o_mail_navbar_dropdown .o_new_message {
- display: none; // hide 'new message' button if chat windows are disabled
-}
diff --git a/addons/mail/static/src/less/extended_chat_window.less b/addons/mail/static/src/less/extended_chat_window.less
new file mode 100644
index 00000000000..68e58ffab12
--- /dev/null
+++ b/addons/mail/static/src/less/extended_chat_window.less
@@ -0,0 +1,19 @@
+.o_chat_window.o_thread_less {
+ .o_chat_search_input {
+ .o-flex-display();
+ padding: 5px;
+ > span {
+ .o-flex(0, 0, auto);
+ }
+ > input {
+ margin-left: 5px;
+ .o-flex(1, 1, 100%);
+ }
+ }
+ .o_mail_thread {
+ background-color: lightgray;
+ }
+ .o_chat_input {
+ display: none;
+ }
+}
diff --git a/addons/mail/static/src/less/followers.less b/addons/mail/static/src/less/followers.less
new file mode 100644
index 00000000000..311ccb18add
--- /dev/null
+++ b/addons/mail/static/src/less/followers.less
@@ -0,0 +1,90 @@
+.o_followers {
+ float: right;
+ @media (max-width: @screen-xs-max) {
+ display: none !important; // Hide the followers in mobile
+ }
+
+ .o_followers_actions {
+ position: relative;
+ float: right;
+
+ .o_followers_follow_button {
+ width: 120px;
+
+ .o_follow, .o_followers_unfollow, .o_followers_following {
+ display: none;
+ }
+ // when following, show 'following'
+ &.o_followers_following .o_followers_following {
+ display: inline;
+ }
+ // when following and hovering, show 'unfollow'
+ &.o_followers_following:hover .o_followers_following {
+ display: none;
+ }
+ &.o_followers_following:hover .o_followers_unfollow {
+ display: inline;
+ }
+ // when not following show 'follow'
+ &.o_followers_notfollow .o_follow {
+ display: inline;
+ }
+ }
+
+ .o_subtype {
+ padding-left: 10px;
+ }
+
+ }
+
+ .o_followers_title_box {
+ position: relative;
+ float: right;
+ margin-left: 5px;
+ > button {
+ width: 140px;
+ }
+
+ .o_followers_count {
+ padding-right: 5px;
+ }
+
+ .o_followers_list {
+ font-size: small;
+
+ .o_partner {
+ @partner_avatar_size: 24px;
+ position: relative; // required for old design compatibility
+ > a {
+ max-width: 160px;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ height: @partner_avatar_size;
+ padding: 3px @partner_avatar_size + 8px;
+ }
+ > img {
+ .o-position-absolute();
+ width: @partner_avatar_size;
+ }
+ .o_remove_follower {
+ .o-position-absolute(6px, 5px);
+ cursor: pointer;
+ }
+ .o_edit_subtype {
+ .o-position-absolute(6px, 18px);
+ cursor: pointer;
+ }
+ }
+
+ }
+ }
+}
+
+// Edit subtypes in dialog
+.modal .modal-body > ul {
+ padding-left: 0;
+ > li.o_subtype {
+ list-style: none;
+ }
+}
diff --git a/addons/mail/static/src/less/systray.less b/addons/mail/static/src/less/systray.less
new file mode 100644
index 00000000000..8ffee6e2470
--- /dev/null
+++ b/addons/mail/static/src/less/systray.less
@@ -0,0 +1,119 @@
+// Navbar icon and dropdown
+.o_mail_navbar_item {
+ > a {
+ opacity: 1;
+ > i {
+ font-size: larger;
+ }
+ }
+ &.o_no_notification > a {
+ opacity: 0.5;
+ .o_notification_counter {
+ display: none;
+ }
+ }
+ &.o_unread_chat > a > i {
+ color: @odoo-brand-optional;
+ }
+
+ &.open .o_mail_navbar_dropdown {
+ .o-flex-display();
+ .o-flex-flow(column, nowrap);
+ }
+ .o_mail_navbar_dropdown {
+ width: 350px;
+ padding: 0;
+
+ .o_spinner {
+ .o-flex-display();
+ .o-align-items(center);
+ .o-justify-content(center);
+ height: 50px;
+ }
+
+ .o_mail_navbar_dropdown_top {
+ .o-flex-display();
+ .o-flex(0, 0, auto);
+ justify-content: space-between;
+ border-bottom: 1px solid lightgray;
+
+ .o_filter_button, .o_new_message {
+ .btn-link;
+ padding: 5px;
+ }
+ .o_filter_button {
+ color: @odoo-main-color-muted;
+ &:hover, &.o_selected {
+ color: @odoo-brand-optional;
+ }
+ &.o_selected {
+ cursor: default;
+ font-weight: bold;
+ }
+ }
+ }
+
+ .o_mail_navbar_dropdown_channels {
+ .o-flex(0, 1, auto);
+ max-height: 400px;
+ min-height: 50px;
+ overflow-y: auto;
+
+ @media (max-width: @screen-xs-max) {
+ max-height: none;
+ }
+
+ .o_mail_channel_preview {
+ .o-flex-display();
+ height: 50px;
+ align-items: center;
+ padding: 5px;
+ cursor: pointer;
+ font-size: 12px;
+ overflow: hidden;
+ &~.o_mail_channel_preview {
+ border-top: 1px solid lightgray;
+ }
+ &.o_channel_unread {
+ background-color: lighten(lightgray, 10%);
+ }
+ &:hover {
+ background-color: lighten(lightgray, 5%);
+ }
+
+ .o_channel_image {
+ .o-flex(0, 0, 36px);
+ max-height: 36px;
+ }
+ .o_channel_info {
+ .o-flex(1, 1, 100%);
+ margin-left: 5px;
+ overflow: hidden;
+
+ .o_channel_title {
+ .o-flex-display();
+ .o_channel_name {
+ .o-flex(1, 1, auto);
+ .o-text-overflow();
+ }
+ .o_last_message_date {
+ .o-flex(0, 0, auto);
+ padding-top: 3px;
+ font-size: xx-small;
+ color: @odoo-main-color-muted;
+ }
+ }
+ .o_last_message_preview {
+ width: 100%;
+ color: @odoo-main-color-muted;
+ .o-text-overflow();
+ }
+ }
+ }
+ }
+ }
+}
+
+.o_no_chat_window .o_mail_navbar_dropdown .o_new_message {
+ display: none; // hide 'new message' button if chat windows are disabled
+}
diff --git a/addons/mail/static/src/xml/chat_window.xml b/addons/mail/static/src/xml/chat_window.xml
index fda9222681a..ef2d478b467 100644
--- a/addons/mail/static/src/xml/chat_window.xml
+++ b/addons/mail/static/src/xml/chat_window.xml
@@ -18,16 +18,6 @@
-
+