Skip to content

Commit

Permalink
[IMP] mail: chatter creation message in formview
Browse files Browse the repository at this point in the history
Before this commit, when we create a new object (e.g. task, product, ...),
the part of the form for the chatter was blank.

After this commit, it displays the chatter with a single temporary message.
This message is only visible by the user that is creating the task, and goes away
when saving the newly created task. Any interaction with the chatter (buttons and
widgets at the top of the chatter, including the message itself) are disabled, so
that no interaction from the user is possible.
  • Loading branch information
alexkuhn committed Jan 4, 2018
1 parent 27c3c34 commit 96fe715
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 82 deletions.
109 changes: 104 additions & 5 deletions addons/mail/static/src/js/chatter.js
Expand Up @@ -34,7 +34,15 @@ var Chatter = Widget.extend(chat_mixin, {
},
supportedFieldTypes: ['one2many'],

// inherited
/**
* @override
* @param {widget} parent
* @param {Object} record
* @param {Object} mailFields
* @param {boolean} [mailFields.mail_activity=false]
* @param {boolean} [mailFields.mail_followers=false]
* @param {boolean} [mailFields.mail_thread=false]
*/
init: function (parent, record, mailFields, options) {
this._super.apply(this, arguments);
this._setState(record);
Expand All @@ -61,6 +69,9 @@ var Chatter = Widget.extend(chat_mixin, {
this.postRefresh = nodeOptions.post_refresh || 'never';
}
},
/**
* @override
*/
start: function () {
this.$topbar = this.$('.o_chatter_topbar');

Expand All @@ -80,7 +91,15 @@ var Chatter = Widget.extend(chat_mixin, {
return this._super.apply(this, arguments);
},

// public
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------

/**
* @param {Object} record
* @param {integer} [record.res_id=undefined]
* @param {Object[]} [fieldNames=undefined]
*/
update: function (record, fieldNames) {
var self = this;

Expand Down Expand Up @@ -120,7 +139,14 @@ var Chatter = Widget.extend(chat_mixin, {
});
},

// private
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------

/**
* @private
* @param {boolean} force
*/
_closeComposer: function (force) {
if (this.composer && (this.composer.is_empty() || force)) {
this.$el.removeClass('o_chatter_composer_active');
Expand All @@ -129,6 +155,24 @@ var Chatter = Widget.extend(chat_mixin, {
this.composer.clear_composer();
}
},
/**
* @private
*/
_disableChatter: function () {
this.$('.btn').prop('disabled', true); // disable buttons
},
/**
* @private
*/
_enableChatter: function () {
this.$('.btn').prop('disabled', false); // enable buttons
},
/**
* @private
* @param {Object} options
* @param {Object[]} [options.suggested_partners=[]]
* @param {boolean} [options.is_log=false]
*/
_openComposer: function (options) {
var self = this;
var old_composer = this.composer;
Expand Down Expand Up @@ -172,6 +216,11 @@ var Chatter = Widget.extend(chat_mixin, {
self.$('.o_chatter_button_log_note').toggleClass('o_active', self.composer.options.is_log);
});
},
/**
* @private
* @param {Deferred} def
* @returns {Deferred}
*/
_render: function (def) {
// the rendering of the chatter is aynchronous: relational data of its fields needs to be
// fetched (in some case, it might be synchronous as they hold an internal cache).
Expand All @@ -196,9 +245,24 @@ var Chatter = Widget.extend(chat_mixin, {
if (self.fields.thread) {
self.fields.thread.$el.appendTo(self.$el);
}
}).always($spinner.remove.bind($spinner));
}).always(function () {
// disable widgets in creation mode, otherwise enable
self._switchEnableChatter(!self.creationMode);
$spinner.remove;
});
},
/**
* @private
* @param {Object} record
* @param {integer} [record.res_id=false]
* @param {string} [record.model=false]
* @param {string} record.data.display_name
*/
_setState: function (record) {

this.wasCreationMode = !!this.creationMode;
this.creationMode = !record.res_id;

if (!this.record || this.record.res_id !== record.res_id) {
this.context = {
default_res_id: record.res_id || false,
Expand All @@ -211,6 +275,20 @@ var Chatter = Widget.extend(chat_mixin, {
this.record = record;
this.record_name = record.data.display_name;
},
/**
* @private
* @param {boolean} [enable] use true to enable the buttons or false to disable them
*/
_switchEnableChatter: function (enable) {
if (_.isBoolean(enable)) {
enable ? this._enableChatter() : this._disableChatter();
} else {
this.$('.btn').prop('disabled', !this.$('.btn').first().prop('disabled')); // toggle based on 1st btn
}
},
/**
* @private
*/
_updateMentionSuggestions: function () {
if (!this.fields.followers) {
return;
Expand Down Expand Up @@ -243,7 +321,13 @@ var Chatter = Widget.extend(chat_mixin, {
});
},

// handlers
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------

/**
* @private
*/
_onOpenComposerMessage: function () {
var self = this;
if (!this.suggested_partners_def) {
Expand Down Expand Up @@ -275,9 +359,21 @@ var Chatter = Widget.extend(chat_mixin, {
self._openComposer({ is_log: false, suggested_partners: suggested_partners });
});
},
/**
* @private
*/
_onOpenComposerNote: function () {
this._openComposer({is_log: true});
},
/**
* @private
* @param {OdooEvent} event
* @param {string} event.name='reload_mail_fields'
* @param {Object} event.data
* @param {boolean} [event.data.activity=false]
* @param {boolean} [event.data.followers=false]
* @param {boolean} [event.data.thread=false]
*/
_onReloadMailFields: function (event) {
var fieldNames = [];
if (this.fields.activity && event.data.activity) {
Expand All @@ -294,6 +390,9 @@ var Chatter = Widget.extend(chat_mixin, {
keepChanges: true,
});
},
/**
* @private
*/
_onScheduleActivity: function () {
this.fields.activity.scheduleActivity(false);
},
Expand Down
33 changes: 8 additions & 25 deletions addons/mail/static/src/js/form_renderer.js
Expand Up @@ -44,39 +44,22 @@ FormRenderer.include({
/**
* Overrides the function that renders the nodes to return the chatter's $el
* for the 'oe_chatter' div node.
* Returns an empty div instead of the chatter's $el in create mode.
*
* @override
* @private
*/
_renderNode: function (node) {
if (node.tag === 'div' && node.attrs.class === 'oe_chatter') {
if (this.chatter) {
// Detach the chatter before updating the $el.
// This is important because if the view is now in create mode
// (edit mode with no res_id), the chatter will be removed from
// the DOM, and its handlers will be unbound. By detaching it
// beforehand, we ensure to keep its handlers alive so that if
// it is re-appended later, everything will still work properly
this.chatter.$el.detach();
}
if (this.mode === 'edit' && !this.state.data.id) {
// there is no chatter in create mode
var $div = $('<div>');
this._handleAttributes($div, node);
return $div;
if (!this.chatter) {
this.chatter = new Chatter(this, this.state, this.mailFields, {
isEditable: this.activeActions.edit,
});
this.chatter.appendTo($('<div>'));
this._handleAttributes(this.chatter.$el, node);
} else {
if (!this.chatter) {
this.chatter = new Chatter(this, this.state, this.mailFields, {
isEditable: this.activeActions.edit,
});
this.chatter.appendTo($('<div>'));
this._handleAttributes(this.chatter.$el, node);
} else {
this.chatter.update(this.state);
}
return this.chatter.$el;
this.chatter.update(this.state);
}
return this.chatter.$el;
} else {
return this._super.apply(this, arguments);
}
Expand Down
113 changes: 79 additions & 34 deletions addons/mail/static/src/js/thread.js
Expand Up @@ -67,9 +67,14 @@ var Thread = Widget.extend({
},
},

/**
* @override
* @param {widget} parent
* @param {Object} options
*/
init: function (parent, options) {
this._super.apply(this, arguments);
this.options = _.defaults(options || {}, {
this.enabledOptions = _.defaults(options || {}, {
display_order: ORDER.ASC,
display_needactions: true,
display_stars: true,
Expand All @@ -79,13 +84,35 @@ var Thread = Widget.extend({
display_email_icon: true,
display_reply_icon: false,
});
this.disabledOptions = {
display_order: this.enabledOptions.display_order,
display_needactions: false,
display_stars: false,
display_document_link: false,
display_avatar: this.enabledOptions,
squash_close_messages: false,
display_email_icon: false,
display_reply_icon: false,
};
this.options = this.enabledOptions;
this.expanded_msg_ids = [];
this.selected_id = null;
},

/**
* @param {Object[]} messages
* @param {Object} [options]
* @param {integer} [options.display_order=ORDER.ASC]
* @param {boolean} [options.display_load_more=false]
* @param {boolean} [options.isCreationMode=false]
* @param {boolean} [options.squash_close_messages=false]
*/
render: function (messages, options) {
var self = this;
var msgs = _.map(messages, this._preprocess_message.bind(this));

this.options = options.isCreationMode ? this.disabledOptions : this.enabledOptions;

var msgs = _.map(messages, this._preprocessMessage.bind(this));
if (this.options.display_order === ORDER.DESC) {
msgs.reverse();
}
Expand Down Expand Up @@ -219,42 +246,10 @@ var Thread = Widget.extend({
}
},

_redirect: _.debounce(function (options) {
if ('channel_id' in options) {
this.trigger('redirect_to_channel', options.channel_id);
} else {
this.trigger('redirect', options.model, options.id);
}
}, 200, true),

on_click_show_more: function () {
this.trigger('load_more_messages');
},

_preprocess_message: function (message) {
var msg = _.extend({}, message);

msg.date = moment.min(msg.date, moment());
msg.hour = time_from_now(msg.date);

var date = msg.date.format('YYYY-MM-DD');
if (date === moment().format('YYYY-MM-DD')) {
msg.day = _t("Today");
} else if (date === moment().subtract(1, 'days').format('YYYY-MM-DD')) {
msg.day = _t("Yesterday");
} else {
msg.day = msg.date.format('LL');
}

if (_.contains(this.expanded_msg_ids, message.id)) {
msg.expanded = true;
}

msg.display_subject = message.subject && message.message_type !== 'notification' && !(message.model && (message.model !== 'mail.channel'));
msg.is_selected = msg.id === this.selected_id;
return msg;
},

/**
* Removes a message and re-renders the thread
* @param {int} [message_id] the id of the removed message
Expand Down Expand Up @@ -308,6 +303,56 @@ var Thread = Widget.extend({
clearInterval(this.update_timestamps_interval);
},

//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------

/**
* @private
* @param {Object} message
* @param {integer} [message.id]
* @param {string} [message.model]
* @param {string} [message.subject]
* @param {string} [message.message_type]
*/
_preprocessMessage: function (message) {
var msg = _.extend({}, message);

msg.date = moment.min(msg.date, moment());
msg.hour = time_from_now(msg.date);

var date = msg.date.format('YYYY-MM-DD');
if (date === moment().format('YYYY-MM-DD')) {
msg.day = _t("Today");
} else if (date === moment().subtract(1, 'days').format('YYYY-MM-DD')) {
msg.day = _t("Yesterday");
} else {
msg.day = msg.date.format('LL');
}

if (_.contains(this.expanded_msg_ids, message.id)) {
msg.expanded = true;
}

msg.display_subject = message.subject && message.message_type !== 'notification' && !(message.model && (message.model !== 'mail.channel'));
msg.is_selected = msg.id === this.selected_id;
return msg;
},
/**
* @private
* @param {Object} options
* @param {integer} [options.channel_id]
* @param {string} options.model
* @param {integer} options.id
*/
_redirect: _.debounce(function (options) {
if ('channel_id' in options) {
this.trigger('redirect_to_channel', options.channel_id);
} else {
this.trigger('redirect', options.model, options.id);
}
}, 200, true),

//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
Expand Down

0 comments on commit 96fe715

Please sign in to comment.