diff options
Diffstat (limited to 'addons/mail/static/src')
| -rw-r--r-- | addons/mail/static/src/img/smiley/smile_open_mouth.png | bin | 0 -> 6262 bytes | |||
| -rw-r--r-- | addons/mail/static/src/js/activity.js | 147 | ||||
| -rw-r--r-- | addons/mail/static/src/js/composer.js | 2 | ||||
| -rw-r--r-- | addons/mail/static/src/js/discuss_mobile.js | 4 | ||||
| -rw-r--r-- | addons/mail/static/src/js/document_viewer.js | 35 | ||||
| -rw-r--r-- | addons/mail/static/src/less/composer.less | 4 | ||||
| -rw-r--r-- | addons/mail/static/src/less/discuss.less | 8 | ||||
| -rw-r--r-- | addons/mail/static/src/less/thread.less | 7 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/thread.xml | 27 |
9 files changed, 145 insertions, 89 deletions
diff --git a/addons/mail/static/src/img/smiley/smile_open_mouth.png b/addons/mail/static/src/img/smiley/smile_open_mouth.png Binary files differnew file mode 100644 index 00000000000..7dcdd3f55fe --- /dev/null +++ b/addons/mail/static/src/img/smiley/smile_open_mouth.png diff --git a/addons/mail/static/src/js/activity.js b/addons/mail/static/src/js/activity.js index 37903e3861f..4bb81489789 100644 --- a/addons/mail/static/src/js/activity.js +++ b/addons/mail/static/src/js/activity.js @@ -2,7 +2,7 @@ odoo.define('mail.Activity', function (require) { "use strict"; var AbstractField = require('web.AbstractField'); -var concurrency = require('web.concurrency'); +var BasicModel = require('web.BasicModel'); var core = require('web.core'); var field_registry = require('web.field_registry'); var time = require('web.time'); @@ -12,6 +12,63 @@ var QWeb = core.qweb; var _t = core._t; /** + * Fetches activities and postprocesses them. + * + * This standalone function performs an RPC, but to do so, it needs an instance + * of a widget that implements the _rpc() function. + * + * @todo i'm not very proud of the widget instance given in arguments, we should + * probably try to do it a better way in the future. + * + * @param {Widget} self a widget instance that can perform RPCs + * @param {Array} ids the ids of activities to read + * @return {Deferred<Array>} resolved with the activities + */ +function _readActivities(self, ids) { + if (!ids.length) { + return $.when([]); + } + return self._rpc({ + model: 'mail.activity', + method: 'read', + args: [ids], + }).then(function (activities) { + // convert create_date and date_deadline to moments + _.each(activities, function (activity) { + activity.create_date = moment(time.auto_str_to_date(activity.create_date)); + activity.date_deadline = moment(time.auto_str_to_date(activity.date_deadline)); + }); + + // sort activities by due date + activities = _.sortBy(activities, 'date_deadline'); + + return activities; + }); +} + +BasicModel.include({ + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Fetches the activities displayed by the activity field widget in form + * views. + * + * @private + * @param {Object} record - an element from the localData + * @param {string} fieldName + * @return {Deferred<Array>} resolved with the activities + */ + _fetchSpecialActivity: function (record, fieldName) { + var localID = (record._changes && fieldName in record._changes) ? + record._changes[fieldName] : + record.data[fieldName]; + return _readActivities(this, this.localData[localID].res_ids); + }, +}); + +/** * Set the 'label_delay' entry in activity data according to the deadline date * @param {Array} activities list of activity Object * @return {Array} : list of modified activity Object @@ -44,12 +101,6 @@ var setDelayLabel = function(activities){ }; var AbstractActivityField = AbstractField.extend({ - // inherited - init: function () { - this._super.apply(this, arguments); - this.activities = []; - }, - // private _markActivityDone: function (id, feedback) { return this._rpc({ @@ -59,33 +110,6 @@ var AbstractActivityField = AbstractField.extend({ kwargs: {feedback: feedback}, }); }, - _readActivities: function () { - var self = this; - var missing_ids = _.difference(this.value.res_ids, _.pluck(this.activities, 'id')); - var fetch_def; - if (missing_ids.length) { - fetch_def = this._rpc({ - model: 'mail.activity', - method: 'read', - args: [missing_ids], - }); - } - return $.when(fetch_def).then(function (results) { - // convert create_date and date_deadline to moments - _.each(results, function (activity) { - activity.create_date = moment(time.auto_str_to_date(activity.create_date)); - activity.date_deadline = moment(time.auto_str_to_date(activity.date_deadline)); - }); - - // filter out activities that are no longer linked to this record - self.activities = _.filter(self.activities.concat(results || []), function (activity) { - return _.contains(self.value.res_ids, activity.id); - }); - - // sort activities by due date - self.activities = _.sortBy(self.activities, 'date_deadline'); - }); - }, _scheduleActivity: function (id, previous_activity_type_id, callback) { var action = { type: 'ir.actions.act_window', @@ -116,41 +140,37 @@ var Activity = AbstractActivityField.extend({ 'click .o_activity_unlink': '_onUnlinkActivity', 'click .o_activity_done': '_onMarkActivityDone', }, + specialData: '_fetchSpecialActivity', // inherited init: function () { this._super.apply(this, arguments); - this.dp = new concurrency.DropPrevious(); + this.activities = this.record.specialData[this.name]; }, _render: function () { - // note: the rendering of this widget is asynchronous as it needs to - // fetch the details of the linked activities - var self = this; - var fetch_def = this.dp.add(this._readActivities()); - return fetch_def.then(function () { - _.each(self.activities, function (activity) { - if (activity.note) { - activity.note = utils.parse_and_transform(activity.note, utils.add_link); - } - }); - var activities = setDelayLabel(self.activities); - if (activities.length) { - var nbActivities = _.countBy(activities, 'state'); - self.$el.html(QWeb.render('mail.activity_items', { - activities: activities, - nbPlannedActivities: nbActivities.planned, - nbTodayActivities: nbActivities.today, - nbOverdueActivities: nbActivities.overdue, - date_format: time.getLangDateFormat(), - datetime_format: time.getLangDatetimeFormat(), - })); - } else { - self.$el.empty(); + _.each(this.activities, function (activity) { + if (activity.note) { + activity.note = utils.parse_and_transform(activity.note, utils.add_link); } }); + var activities = setDelayLabel(this.activities); + if (activities.length) { + var nbActivities = _.countBy(activities, 'state'); + this.$el.html(QWeb.render('mail.activity_items', { + activities: activities, + nbPlannedActivities: nbActivities.planned, + nbTodayActivities: nbActivities.today, + nbOverdueActivities: nbActivities.overdue, + date_format: time.getLangDateFormat(), + datetime_format: time.getLangDatetimeFormat(), + })); + } else { + this.$el.empty(); + } }, _reset: function (record) { this._super.apply(this, arguments); + this.activities = this.record.specialData[this.name]; // the mail widgets being persistent, one need to update the res_id on reset this.res_id = record.res_id; }, @@ -196,9 +216,6 @@ var Activity = AbstractActivityField.extend({ }); return this.do_action(action, { on_close: function () { - // remove the edited activity from the array of fetched activities to - // force a reload of that activity - self.activities = _.reject(self.activities, {id: activity_id}); self._reload({activity: true, thread: true}); }, }); @@ -312,10 +329,10 @@ var KanbanActivity = AbstractActivityField.extend({ _renderDropdown: function () { var self = this; this.$('.o_activity').html(QWeb.render("mail.KanbanActivityLoading")); - return this._readActivities().then(function () { + return _readActivities(this, this.value.res_ids).then(function (activities) { self.$('.o_activity').html(QWeb.render("mail.KanbanActivityDropdown", { selection: self.selection, - records: _.groupBy(setDelayLabel(self.activities), 'state'), + records: _.groupBy(setDelayLabel(activities), 'state'), uid: self.getSession().uid, })); }); @@ -328,7 +345,9 @@ var KanbanActivity = AbstractActivityField.extend({ // handlers _onButtonClicked: function (event) { event.preventDefault(); - this._renderDropdown(); + if (!this.$el.hasClass('open')) { + this._renderDropdown(); + } }, _onMarkActivityDone: function (event) { event.stopPropagation(); diff --git a/addons/mail/static/src/js/composer.js b/addons/mail/static/src/js/composer.js index 8de8a242e68..3a7b9c320e1 100644 --- a/addons/mail/static/src/js/composer.js +++ b/addons/mail/static/src/js/composer.js @@ -591,7 +591,7 @@ var BasicComposer = Widget.extend({ */ _onKeydown: function (event) { switch(event.which) { - // UP, DOWN: prevent moving cursor if navigation in mention propositions + // UP, DOWN, TAB: prevent moving cursor if navigation in mention propositions case $.ui.keyCode.UP: case $.ui.keyCode.DOWN: case $.ui.keyCode.TAB: diff --git a/addons/mail/static/src/js/discuss_mobile.js b/addons/mail/static/src/js/discuss_mobile.js index 52622b0ce32..b427ab4b98c 100644 --- a/addons/mail/static/src/js/discuss_mobile.js +++ b/addons/mail/static/src/js/discuss_mobile.js @@ -41,7 +41,7 @@ Discuss.include({ */ on_attach_callback: function () { if (this.channel && this._isInInboxTab()) { - this.thread.scroll_to({offset: this.channels_scrolltop[this.channel.id]}); + this.thread.scroll_to({offset: this.channelsScrolltop[this.channel.id]}); } }, /** @@ -49,7 +49,7 @@ Discuss.include({ */ on_detach_callback: function () { if (this._isInInboxTab()) { - this.channels_scrolltop[this.channel.id] = this.thread.get_scrolltop(); + this.channelsScrolltop[this.channel.id] = this.thread.get_scrolltop(); } }, diff --git a/addons/mail/static/src/js/document_viewer.js b/addons/mail/static/src/js/document_viewer.js index 2fa34b613ba..0aac950cf5b 100644 --- a/addons/mail/static/src/js/document_viewer.js +++ b/addons/mail/static/src/js/document_viewer.js @@ -17,6 +17,7 @@ var DocumentViewer = Widget.extend({ 'click .o_viewer_video': '_onVideoClicked', 'click .move_next': '_onNext', 'click .move_previous': '_onPrevious', + 'click .o_rotate': '_onRotate', 'click .o_zoom_in': '_onZoomIn', 'click .o_zoom_out': '_onZoomOut', 'click .o_close_btn, .o_viewer_img_wrapper': '_onClose', @@ -104,6 +105,30 @@ var DocumentViewer = Widget.extend({ this._reset(); }, /** + * Get CSS transform property based on scale and angle + * + * @private + * @param {float} scale + * @param {float} angle + */ + _getTransform: function(scale, angle) { + return 'scale3d(' + scale + ', ' + scale + ', 1) rotate(' + angle + 'deg)' + }, + /** + * Rotate image clockwise by provided angle + * + * @private + * @param {float} angle + */ + _rotate: function (angle) { + this._reset(); + var new_angle = (this.angle || 0) + angle; + this.$('.o_viewer_img').css('transform', this._getTransform(this.scale, new_angle)); + this.$('.o_viewer_img').css('max-width', new_angle % 180 !== 0 ? $(document).height() : '100%'); + this.$('.o_viewer_img').css('max-height', new_angle % 180 !== 0 ? $(document).width() : '100%'); + this.angle = new_angle; + }, + /** * Zoom in/out image by provided scale * * @private @@ -111,7 +136,7 @@ var DocumentViewer = Widget.extend({ */ _zoom: function (scale) { if (scale > 0.5) { - this.$('.o_viewer_img').css('transform', 'scale3d(' + scale + ', ' + scale + ', 1)'); + this.$('.o_viewer_img').css('transform', this._getTransform(scale, this.angle || 0)); this.scale = scale; } }, @@ -284,6 +309,14 @@ var DocumentViewer = Widget.extend({ * @private * @param {MouseEvent} e */ + _onRotate: function (e) { + e.preventDefault(); + this._rotate(90); + }, + /** + * @private + * @param {MouseEvent} e + */ _onZoomIn: function (e) { e.preventDefault(); var scale = this.scale + ZOOM_STEP; diff --git a/addons/mail/static/src/less/composer.less b/addons/mail/static/src/less/composer.less index 9df25c895a3..e2fae8b106e 100644 --- a/addons/mail/static/src/less/composer.less +++ b/addons/mail/static/src/less/composer.less @@ -264,6 +264,7 @@ img { width: @o-mail-attachment-image-size; height: @o-mail-attachment-image-size; + image-orientation: from-image; // Only supported in Firefox } .o_image_overlay{ display: none; @@ -365,6 +366,9 @@ .o-flex-display(); justify-content: center; align-items: center; + img { + image-orientation: from-image; // Only supported in Firefox + } img, video { max-width: 100%; max-height: 100%; diff --git a/addons/mail/static/src/less/discuss.less b/addons/mail/static/src/less/discuss.less index 46dc6d41bfd..4e0ae26dccc 100644 --- a/addons/mail/static/src/less/discuss.less +++ b/addons/mail/static/src/less/discuss.less @@ -321,15 +321,11 @@ .o_mail_chat_content { .o-flex(1, 0, 0); - margin-left: 0; + margin-left: 10px; + margin-right: 10px; background-color: transparent; - .o_mail_no_content { - .o-position-absolute(@top: 30%); - text-align: center; - } } - .o_mail_chat_tab_pane { overflow-x: scroll; .o_mail_add_channel { diff --git a/addons/mail/static/src/less/thread.less b/addons/mail/static/src/less/thread.less index 8d8bc761829..81dee297011 100644 --- a/addons/mail/static/src/less/thread.less +++ b/addons/mail/static/src/less/thread.less @@ -208,11 +208,14 @@ .o_thread_title { margin-top: 20px; margin-bottom: 20px; - font-size: 20px; + font-weight: bold; + font-size: 125%; } .o_mail_no_content { - .o-webclient-padding(); + .o-position-absolute(30%, 0, 0, 0); + text-align: center; + font-size: 115%; } .o_thread_message .o_thread_message_core .o_mail_read_more { diff --git a/addons/mail/static/src/xml/thread.xml b/addons/mail/static/src/xml/thread.xml index 4a7ba086ebf..26c093c3e84 100644 --- a/addons/mail/static/src/xml/thread.xml +++ b/addons/mail/static/src/xml/thread.xml @@ -51,6 +51,11 @@ <div t-if="widget.activeAttachment.type == 'image'" class="o_viewer_toolbar"> <ul class="list-inline"> <li> + <a class="o_rotate" href="#"> + <i class="fa fa-2x fa-repeat"></i> + </a> + </li> + <li> <a class="o_zoom_out" href="#"> <i class="fa fa-2x fa-search-minus"></i> </a> @@ -105,18 +110,14 @@ <!-- When a channel is empty--> <div t-name="mail.EmptyChannel" class="o_mail_no_content"> - <div class="o_view_nocontent"> - <div class="o_nocontent_help"> - <t t-if="options.channel_id==='channel_inbox'"> - <p>Congratulations, your inbox is empty</p> - <p>New messages appear here.</p> - </t> - <t t-if="options.channel_id==='channel_starred'"> - <p>No starred message</p> - <p>You can mark any message as 'starred', and it shows up in this channel.</p> - </t> - </div> - </div> + <t t-if="options.channel_id==='channel_inbox'"> + <div class="o_thread_title">Congratulations, your inbox is empty</div> + <div>New messages appear here.</div> + </t> + <t t-if="options.channel_id==='channel_starred'"> + <div class="o_thread_title">No starred message</div> + <div>You can mark any message as 'starred', and it shows up in this channel.</div> + </t> </div> <t t-name="mail.ChatThread.Content"> @@ -266,7 +267,7 @@ </t> <t t-name="mail.Attachment"> - <t t-set="type" t-value="attachment.mimetype.split('/').shift()"/> + <t t-set="type" t-value="attachment.mimetype and attachment.mimetype.split('/').shift()"/> <div t-attf-class="o_attachment #{attachment.upload ? 'o_attachment_uploading' : ''}" t-att-title="attachment.name"> <t t-if="type == 'image'"> <div class="o_image_box"> |
