[REF] mail: chatter: followers

Integration in the new design
DOM simplification
LESS refactoring
JS lint
Subtypes edition: fix edition in the modal and style it
This commit is contained in:
Aaron Bohy
2015-09-01 21:04:32 +02:00
parent 223143bd97
commit ad39752093
3 changed files with 203 additions and 245 deletions
+83 -90
View File
@@ -2,7 +2,6 @@ odoo.define('mail.mail_followers', function (require) {
"use strict";
var ajax = require('web.ajax');
var mail_utils = require('mail.utils');
var core = require('web.core');
var data = require('web.data');
var Dialog = require('web.Dialog');
@@ -54,7 +53,7 @@ var Followers = form_common.AbstractField.extend({
},
on_check_visibility_mode: function () {
this.set({"force_invisible": this.view.get("actual_mode") == "create"});
this.set({"force_invisible": this.view.get("actual_mode") === "create"});
},
set_value: function(_value) {
@@ -72,8 +71,8 @@ var Followers = form_common.AbstractField.extend({
var self = this;
// event: click on '(Un)Follow' button, that toggles the follow for uid
this.$('.o_timeline_follower').on('click', function () {
if($(this).hasClass('o_timeline_notfollow')) {
this.$('.o_followers_follow_button').on('click', function () {
if($(this).hasClass('o_followers_notfollow')) {
self.do_follow();
}
else {
@@ -82,24 +81,24 @@ var Followers = form_common.AbstractField.extend({
});
// event: click on a subtype, that (un)subscribe for this subtype
this.$el.on('click', '.o_timeline_subtype_list input', function(event) {
this.$el.on('click', '.o_subtypes_list input', function(event) {
self.do_update_subscription(event);
var $list = self.$('.o_timeline_subtype_list');
var $list = self.$('.o_subtypes_list');
if(!$list.hasClass('open')) {
$list.addClass('open');
}
if(self.$('.o_timeline_subtype_list ul')[0].children.length < 1) {
if(self.$('.o_subtypes_list ul')[0].children.length < 1) {
$list.removeClass('open');
}
event.stopPropagation();
});
// event: click on 'invite' button, that opens the invite wizard
this.$el.on('click', '.oe_invite', self.on_invite_follower);
this.$el.on('click', '.o_add_follower', self.on_invite_follower);
// event: click on 'edit_subtype(pencil)' button to edit subscription
this.$el.on('click', '.oe_edit_subtype', self.on_edit_subtype);
this.$el.on('click', '.oe_remove_follower', self.on_remove_follower);
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', 'a[data-partner]', self.on_follower_clicked);
},
@@ -107,23 +106,29 @@ var Followers = form_common.AbstractField.extend({
var self = this;
var $currentTarget = $(event.currentTarget);
var user_pid = $currentTarget.data('id');
$('div.oe_edit_actions').remove();
self.$dialog = new Dialog(this, {
self.dialog = new Dialog(this, {
size: 'small',
title: _t('Edit Subscription of ') + $currentTarget.siblings('a').text(),
buttons: [
{ text: _t("Apply"), click: function() {
self.do_update_subscription(event, user_pid);
this.parents('.modal').modal('hide');
}},
{ text: _t("Cancel"), click: function() { this.parents('.modal').modal('hide'); }}
{
text: _t("Apply"),
click: function() {
self.do_update_subscription(event, user_pid);
},
close: true
},
{
text: _t("Cancel"),
close: true,
},
],
}, "<div class='oe_edit_actions'>").open();
}).open();
return self.fetch_subtypes(user_pid);
},
on_invite_follower: function () {
on_invite_follower: function (event) {
var self = this;
event.preventDefault();
var action = {
type: 'ir.actions.act_window',
res_model: 'mail.wizard.invite',
@@ -147,7 +152,7 @@ var Followers = form_common.AbstractField.extend({
on_remove_follower: function (event) {
var res_model = $(event.target).parent().find('a').data('res-model');
var res_id = $(event.target).parent().find('a').data('res-id');
if (res_model == 'res.partner') { return this.do_unfollow(undefined, [res_id], undefined); }
if (res_model === 'res.partner') { return this.do_unfollow(undefined, [res_id], undefined); }
else { return this.do_unfollow(undefined, undefined, [res_id]); }
},
@@ -201,8 +206,8 @@ var Followers = form_common.AbstractField.extend({
return this.ds_users.call('read', [[session.uid], ['partner_id']])
.then(function (results) {
var pid = results[0]['partner_id'][0];
self.message_is_follower = (_.indexOf(self.value, pid) != -1);
var pid = results[0].partner_id[0];
self.message_is_follower = (_.indexOf(self.value, pid) !== -1);
})
.then(self.proxy('display_generic'));
},
@@ -210,7 +215,7 @@ var Followers = form_common.AbstractField.extend({
_format_followers: function(count){
var str = '';
if(count <= 0){
str = _t('No followers');
str = _t('No follower');
}else if(count === 1){
str = _t('One follower');
}else{
@@ -221,8 +226,8 @@ var Followers = form_common.AbstractField.extend({
/* Display generic info about follower, for people not having access to res_partner */
display_generic: function () {
this.$('.o_timeline_follower_list').empty();
this.$('.o_timeline_follower_title').html(this._format_followers(this.value.length));
this.$('.o_followers_list').empty();
this.$('.o_followers_count').html(this._format_followers(this.value.length));
},
/** Display the followers */
@@ -231,26 +236,26 @@ var Followers = form_common.AbstractField.extend({
this.followers = records || this.followers;
// clean and display title
var node_user_list = this.$('.o_timeline_follower_list').empty();
this.$('.o_timeline_follower_title').html(this._format_followers(this.followers.length));
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;
$(qweb.render('mail.Followers.add_more', {'widget': self})).appendTo(node_user_list);
this.follower_id = this.message_is_follower ? user_follower[0].id : undefined;
// truncate number of displayed followers
// render the dropdown content
$(qweb.render('mail.Followers.add_more', {'widget': self})).appendTo($followers_list);
var $follower_li;
_(this.followers).each(function (record) {
$(qweb.render('mail.Followers.partner', {
$follower_li = $(qweb.render('mail.Followers.partner', {
'record': _.extend(record, {'avatar_url': '/web/image/' + record.res_model + '/' + record.res_id + '/image_small'}),
'widget': self})
).appendTo(node_user_list);
);
$follower_li.appendTo($followers_list);
// On mouse-enter it will show the edit_subtype pencil.
if (record.is_editable) {
self.$('.o_timeline_follower_list').on('mouseenter mouseleave', function(e) {
self.$('.o_timeline_edit_subtype')
.toggleClass('oe_hidden', e.type == 'mouseleave');
self.$('.o_timeline_follower_list').find('.oe_partner')
.toggleClass('oe_partner_name', e.type == 'mouseenter');
$follower_li.on('mouseenter mouseleave', function(e) {
$(e.currentTarget).find('.o_edit_subtype').toggleClass('hide', e.type === 'mouseleave');
});
}
});
@@ -258,17 +263,9 @@ var Followers = form_common.AbstractField.extend({
display_buttons: function () {
if (this.message_is_follower) {
this.$('button.o_timeline_follower').removeClass('o_timeline_notfollow').addClass('o_timeline_following');
}
else {
this.$('button.o_timeline_follower').removeClass('o_timeline_following').addClass('o_timeline_notfollow');
}
if (this.view.is_action_enabled('edit')) {
this.$('span.oe_timeline_invite_wrapper').hide();
}
else {
this.$('span.oe_timeline_invite_wrapper').show();
this.$('button.o_followers_follow_button').removeClass('o_followers_notfollow').addClass('o_followers_following');
} else {
this.$('button.o_followers_follow_button').removeClass('o_followers_following').addClass('o_followers_notfollow');
}
},
@@ -281,13 +278,13 @@ var Followers = form_common.AbstractField.extend({
dialog = true;
}
else {
this.$('.o_timeline_subtype_list ul').empty();
this.$('.o_subtypes_list ul').empty();
if (! this.message_is_follower) {
this.$('.o_timeline_subtype_list > .dropdown-toggle').attr('disabled', true);
this.$('.o_subtypes_list > .dropdown-toggle').attr('disabled', true);
return;
}
else {
this.$('.o_timeline_subtype_list > .dropdown-toggle').attr('disabled', false);
this.$('.o_subtypes_list > .dropdown-toggle').attr('disabled', false);
}
}
if (this.follower_id) {
@@ -301,59 +298,55 @@ var Followers = form_common.AbstractField.extend({
/** Display subtypes: {'name': default, followed} */
display_subtypes:function (data, dialog) {
var self = this;
var $list = this.$('.o_timeline_subtype_list ul');
if (_.isEmpty(this.data_subtype)) {
this.data_subtype = data;
if (dialog) {
$list = this.$dialog.$el;
}
var old_parent_model;
this.records_length = $.map(data, function(value, index) { return index; }).length;
if (this.records_length > 1) {
self.display_followers();
}
_.each(data, function (record) {
if (old_parent_model !== record.parent_model && old_parent_model !== undefined) {
var $last_separator = $list.find('.oe_subtype').last();
if ($last_separator) {
$last_separator.addClass('oe_subtype_border');
}
}
old_parent_model = record.parent_model;
record.followed = record.followed || undefined;
$(qweb.render('mail.Followers.subtype', {'record': record,
'dialog': dialog}))
.appendTo($list);
});
var old_parent_model;
var $list;
if (dialog) {
$list = 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) {
self.display_followers();
}
_.each(data, function (record) {
if (old_parent_model !== record.parent_model && old_parent_model !== undefined) {
$list.append($('<li>').addClass('divider'));
}
old_parent_model = record.parent_model;
record.followed = record.followed || undefined;
$(qweb.render('mail.Followers.subtype', {'record': record,
'dialog': dialog}))
.appendTo($list);
});
},
do_follow: function () {
var context = new data.CompoundContext(this.build_context(), {});
this.$('.o_timeline_subtype_list > .dropdown-toggle').attr('disabled', false);
this.$('.o_subtypes_list > .dropdown-toggle').attr('disabled', false);
this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id],
[session.uid],
undefined,
context])
.then(this.proxy('read_value'));
_.each(this.$('.o_timeline_subtype_list input'), function (record) {
_.each(this.$('.o_subtypes_list input'), function (record) {
$(record).attr('checked', 'checked');
});
},
do_unfollow: function (user_ids, partner_ids, channel_ids) {
if (confirm(_t("Warning! \n If you remove a follower, he won't be notified of any email or discussion on this document. Do you really want to remove this follower ?"))) {
_(this.$('.o_timeline_msg_subtype_check')).each(function (record) {
_(this.$('.o_subtype_checkbox')).each(function (record) {
$(record).attr('checked',false);
});
this.$('.o_timeline_subtype_list > .dropdown-toggle').attr('disabled', true);
this.$('.o_subtypes_list > .dropdown-toggle').attr('disabled', true);
var context = new data.CompoundContext(this.build_context(), {});
if (partner_ids || channel_ids) {
@@ -383,16 +376,17 @@ var Followers = form_common.AbstractField.extend({
var action_subscribe = 'message_subscribe_users';
var follower_ids = [session.uid];
var oe_action = this.$('.oe_actions input[type="checkbox"]');
var subtypes = this.$('.o_followers_actions input[type="checkbox"]');
// Subtypes edited from the modal
if (user_pid) {
action_subscribe = 'message_subscribe';
follower_ids = [user_pid];
oe_action = $('.oe_edit_actions input[type="checkbox"]');
subtypes = this.dialog.$('input[type="checkbox"]');
}
var checklist = [];
_(oe_action).each(function (record) {
_(subtypes).each(function (record) {
if ($(record).is(':checked')) {
checklist.push(parseInt($(record).data('id')));
}
@@ -401,9 +395,8 @@ var Followers = form_common.AbstractField.extend({
if (!checklist.length) {
if (!this.do_unfollow(undefined, [user_pid], undefined)) {
$(event.target).attr("checked", "checked");
}
else {
self.$('.o_timeline_subtype_list ul').empty();
} else {
self.$('.o_subtypes_list ul').empty();
}
}
else {
+90 -119
View File
@@ -1,123 +1,94 @@
.o_web_client .o_timeline_followers {
position: relative;
display: inline-block;
width: 270px;
height: 0px;
.o_mail_followers {
height: 0px; // to be on the same line as mail composer buttons
float: right;
margin-right: 20px;
z-index: 1;
@media (min-width: @screen-sm-min) {
&.o_form_field {
margin: 0; // override o_form_field's margin-bottom of 5px
}
}
@media (max-width: @screen-xs-max) {
height: auto; // to be displayed above the mail composer buttons
float: left;
}
.o_followers_actions {
position: relative;
float: right;
.o_followers_follow_button {
width: 110px;
.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: 120px;
}
.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;
}
}
}
}
}
.o_web_client .o_timeline_followers .oe_actions {
float: right;
position: relative;
}
.o_web_client .o_timeline_followers button.o_timeline_follower {
background-color: white;
text-align: center;
width: 110px;
}
.o_web_client .o_timeline_followers button.o_timeline_follower.o_timeline_following {
width: 110px;
color: #337ab7;
}
.o_web_client .o_timeline_followers button.o_timeline_follower.o_timeline_following:hover {
width: 110px;
color: #D43F3A;
text-overflow: ellipsis;
}
.o_web_client .o_timeline_followers button.o_timeline_follower .o_timeline_follow,
.o_web_client .o_timeline_followers button.o_timeline_follower .o_timeline_unfollow,
.o_web_client .o_timeline_followers button.o_timeline_follower .o_timeline_following {
display: none;
}
/* a.1) when following, show 'following' */
.o_web_client .o_timeline_followers button.o_timeline_follower.o_timeline_following .o_timeline_following {
display: inline;
}
/* a.2) when following and hovering, show 'unfollow' */
.o_web_client .o_timeline_followers button.o_timeline_follower.o_timeline_following:hover .o_timeline_following {
display: none;
}
.o_web_client .o_timeline_followers button.o_timeline_follower.o_timeline_following:hover .o_timeline_unfollow {
display: inline;
}
/* a.3) when not following show 'follow' */
.o_web_client .o_timeline_followers button.o_timeline_follower.o_timeline_notfollow .o_timeline_follow {
display: inline;
}
.o_web_client .o_timeline_followers .o_timeline_follower_title {
display: inline;
padding-right: 10px;
}
.o_web_client .o_timeline_followers .o_timeline_follower_title_box {
position: relative;
float: right;
margin-left: 10px;
}
.o_web_client .o_timeline_followers button.o_timeline_partner_list {
width: 110px;
}
.o_web_client .o_timeline_followers .o_timeline_follower_list {
height: auto;
max-height: 350px;
overflow-x: hidden;
}
.o_web_client .o_timeline_followers .oe_partner {
height: 32px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 120px;
}
.o_web_client .o_timeline_followers .oe_partner img {
width: 32px;
margin-right: 4px;
border-radius: 2px;
}
.o_web_client .o_timeline_followers .oe_partner a,
.o_web_client .o_timeline_followers .oe_invite a {
font-size: 0.95em;
color: #337ab7;
}
.o_web_client .o_timeline_followers .oe_remove_follower {
cursor: pointer;
position: absolute;
right: 10px;
height: 25px;
vertical-align: middle;
}
.o_web_client .o_timeline_followers .oe_remove_follower.fa-remove {
margin-top: 6px;
}
.o_web_client .o_timeline_followers .oe_partner .oe_hidden {
display: none;
}
.o_web_client .o_timeline_followers .o_timeline_sbtp_list {
margin-left: -5px;
}
.o_web_client .o_timeline_followers .oe_subtype {
padding-left: 5px;
}
.o_web_client .o_timeline_followers .oe_subtype_border {
border-bottom: 1px solid #dddddd;
margin-bottom: 10px;
// Edit subtypes in dialog
.modal .modal-body > li.o_subtype {
list-style: none;
}
+30 -36
View File
@@ -6,27 +6,25 @@
Template used to display the followers, the actions and the subtypes in a record.
-->
<t t-name="mail.Followers">
<div class="o_timeline_followers">
<div t-attf-class="btn-group-sm o_timeline_follower_title_box">
<button t-attf-class="btn btn-default dropdown-toggle o_timeline_partner_list"
<div class="o_mail_followers">
<div t-attf-class="o_followers_title_box">
<button t-attf-class="btn btn-sm btn-default dropdown-toggle"
data-toggle="dropdown" aria-expanded="false">
<span class="o_timeline_follower_title">Followers</span>
<span class="caret"></span>
<span class="o_followers_count"/>
<span class="caret"/>
</button>
<ul class="dropdown-menu o_timeline_follower_list" role="menu"></ul>
<ul class="dropdown-menu o_followers_list" role="menu"></ul>
</div>
<div class="oe_actions">
<div t-attf-class="btn-group-sm o_timeline_subtype_list">
<button class="btn btn-default o_timeline_follower o_timeline_notfollow">
<span class="o_timeline_follow">Follow</span>
<span class="fa fa-times o_timeline_unfollow" aria-hidden="true"> Unfollow</span>
<span class="fa fa-check o_timeline_following" aria-hidden="true"> Following</span>
<div class="o_followers_actions">
<div t-attf-class="btn-group btn-group-sm o_subtypes_list">
<button class="btn btn-primary o_followers_follow_button o_followers_notfollow">
<span class="o_follow">Follow</span>
<span class="fa fa-times o_followers_unfollow" aria-hidden="true"> Unfollow</span>
<span class="fa fa-check o_followers_following" aria-hidden="true"> Following</span>
</button><button t-attf-class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="caret"/>
</button>
<button t-attf-class="btn btn-default dropdown-toggle o_timeline_sbtp_list"
data-toggle="dropdown" aria-expanded="false">
<span class="caret"></span>
</button>
<ul class="dropdown-menu oe_subtype" role="menu"></ul>
<ul class="dropdown-menu" role="menu"/>
</div>
</div>
</div>
@@ -37,29 +35,26 @@
Template used to display a partner following the record
-->
<t t-name="mail.Followers.partner">
<li class='oe_partner'>
<span> <img class="oe_mail_thumbnail oe_mail_frame" t-att-src="record.avatar_url"/> </span>
<span class="oe_partner_name">
<a t-attf-href="#model=res.partner&amp;id=#{record.id}"
t-att-title="record.name"
t-att-data-res-model="record.res_model"
t-att-data-res-id="record.res_id"
t-att-data-id="record.id">
<t t-esc="record.name"/></a>
</span>
<li class='o_partner'>
<img t-att-src="record.avatar_url"/>
<a t-attf-href="#model=res.partner&amp;id=#{record.id}"
t-att-title="record.name"
t-att-data-res-model="record.res_model"
t-att-data-res-id="record.res_id"
t-att-data-id="record.id"><t t-esc="record.name"/></a>
<i t-if="record.is_editable and (widget.records_length &gt; 1)"
class="oe_edit_subtype fa fa-pencil oe_hidden" title="Edit subscription"
class="fa fa-pencil o_edit_subtype hide" title="Edit subscription"
t-att-data-id="record.id"/>
<i t-if="widget.view_is_editable" class="oe_remove_follower fa fa-remove"
<i t-if="widget.view_is_editable" class="fa fa-remove o_remove_follower"
title="Remove this follower"/>
</li>
</t>
<t t-name="mail.Followers.add_more">
<li t-if="widget.view_is_editable" class="oe_invite">
<li t-if="widget.view_is_editable" class="o_add_follower">
<a href="#"> Add followers </a>
</li>
<li t-if="widget.view_is_editable and (widget.followers.length > 0)" class="divider"></li>
<li t-if="widget.view_is_editable and (widget.followers.length > 0)" class="divider"/>
</t>
<!--
@@ -67,14 +62,13 @@
Template used to display message subtypes of a follower subscription
-->
<t t-name="mail.Followers.subtype">
<li class="oe_subtype">
<li class="o_subtype">
<input type="checkbox" t-att-checked="record.followed"
t-att-id="'input_mail_followers_subtype_'+record.id+(dialog ? '_in_dialog': '')"
t-att-data-id="record.id" t-att-name="record.name" class="o_timeline_msg_subtype_check"/>
<label t-att-for="'input_mail_followers_subtype_'+record.id+(dialog ? '_in_dialog': '')">
t-att-data-id="record.id" t-att-name="record.name" class="o_subtype_checkbox"/>
<span t-att-for="'input_mail_followers_subtype_'+record.id+(dialog ? '_in_dialog': '')">
<t t-raw="record.name"/>
</label>
</span>
</li>
</t>