diff options
Diffstat (limited to 'addons/mail/static/src')
36 files changed, 487 insertions, 437 deletions
diff --git a/addons/mail/static/src/js/activity.js b/addons/mail/static/src/js/activity.js index 68ff4b0b86f..607f1b8c553 100644 --- a/addons/mail/static/src/js/activity.js +++ b/addons/mail/static/src/js/activity.js @@ -25,11 +25,11 @@ var _t = core._t; * * @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 + * @return {Promise<Array>} resolved with the activities */ function _readActivities(self, ids) { if (!ids.length) { - return $.when([]); + return Promise.resolve([]); } var context = self.getSession().user_context; if (self.record && !_.isEmpty(self.record.getContext())) { @@ -64,7 +64,7 @@ BasicModel.include({ * @private * @param {Object} record - an element from the localData * @param {string} fieldName - * @return {Deferred<Array>} resolved with the activities + * @return {Promise<Array>} resolved with the activities */ _fetchSpecialActivity: function (record, fieldName) { var localID = (record._changes && fieldName in record._changes) ? @@ -144,7 +144,7 @@ var BasicActivity = AbstractField.extend({ /** * @param {integer} previousActivityTypeID - * @return {$.Promise} + * @return {Promise} */ scheduleActivity: function () { var callback = this._reload.bind(this, { activity: true, thread: true }); @@ -213,7 +213,7 @@ var BasicActivity = AbstractField.extend({ * @param {integer} id * @param {integer} previousActivityTypeID * @param {function} callback - * @return {$.Deferred} + * @return {Promise} */ _openActivityForm: function (id, callback) { var action = { @@ -237,7 +237,7 @@ var BasicActivity = AbstractField.extend({ * @param {integer} activityID * @param {string} feedback * @param {integer[]} attachmentIds - * @return {$.Promise} + * @return {Promise} */ _sendActivityFeedback: function (activityID, feedback, attachmentIds) { return this._rpc({ @@ -303,7 +303,7 @@ var BasicActivity = AbstractField.extend({ /** * @private * @param {MouseEvent} ev - * @returns {$.Promise} + * @returns {Promise} */ _onEditActivity: function (ev) { ev.preventDefault(); @@ -449,7 +449,7 @@ var BasicActivity = AbstractField.extend({ /** * @private * @param {MouseEvent} ev - * @returns {$.Deferred} + * @returns {Promise} */ _onPreviewMailTemplate: function (ev) { ev.stopPropagation(); @@ -477,7 +477,7 @@ var BasicActivity = AbstractField.extend({ /** * @private * @param {MouseEvent} ev - * @returns {$.Promise} + * @returns {Promise} */ _onSendMailTemplate: function (ev) { ev.stopPropagation(); @@ -493,7 +493,7 @@ var BasicActivity = AbstractField.extend({ /** * @private * @param {MouseEvent} ev - * @returns {$.Deferred} + * @returns {Promise} */ _onScheduleActivity: function (ev) { ev.preventDefault(); @@ -504,7 +504,7 @@ var BasicActivity = AbstractField.extend({ * @private * @param {MouseEvent} ev * @param {Object} options - * @returns {$.Promise} + * @returns {Promise} */ _onUnlinkActivity: function (ev, options) { ev.preventDefault(); diff --git a/addons/mail/static/src/js/chatter.js b/addons/mail/static/src/js/chatter.js index 72971fbeee9..397ae2c8a16 100644 --- a/addons/mail/static/src/js/chatter.js +++ b/addons/mail/static/src/js/chatter.js @@ -103,7 +103,7 @@ var Chatter = Widget.extend({ })); // start and append the widgets var fieldDefs = _.invoke(this.fields, 'appendTo', $('<div>')); - var def = this._dp.add($.when.apply($, fieldDefs)); + var def = this._dp.add(Promise.all(fieldDefs)); this._render(def).then(this._updateMentionSuggestions.bind(this)); return this._super.apply(this, arguments); @@ -153,7 +153,7 @@ var Chatter = Widget.extend({ fieldsToReset = this.fields; } var fieldDefs = _.invoke(fieldsToReset, 'reset', record); - var def = this._dp.add($.when.apply($, fieldDefs)); + var def = this._dp.add(Promise.all(fieldDefs)); this._render(def).then(function () { self.$el.height('auto'); self._updateMentionSuggestions(); @@ -203,17 +203,18 @@ var Chatter = Widget.extend({ * Discard changes on the record. * * @private - * @returns {$.Deferred} resolved if successfully discarding changes on + * @returns {Promise} resolved if successfully discarding changes on * the record, rejected otherwise */ _discardChanges: function () { - var def = $.Deferred(); - this.trigger_up('discard_changes', { - recordID: this.record.id, - onSuccess: def.resolve.bind(def), - onFailure: def.reject.bind(def), + var self = this; + return new Promise(function (resolve, reject) { + self.trigger_up('discard_changes', { + recordID: self.record.id, + onSuccess: resolve, + onFailure: reject, + }); }); - return def; }, /** * Discard changes on the record if the message will reload the record @@ -221,14 +222,14 @@ var Chatter = Widget.extend({ * * @private * @param {Object} messageData - * @return {$.Deferred} resolved if no reload or proceed to discard the + * @return {Promise} resolved if no reload or proceed to discard the * changes on the record, rejected otherwise */ _discardOnReload: function (messageData) { if (this._reloadAfterPost(messageData)) { return this._discardChanges(); } - return $.when(); + return Promise.resolve(); }, /** * @private @@ -323,7 +324,7 @@ var Chatter = Widget.extend({ self._reloadAttachmentBox(); self.trigger_up('reload', {fieldNames: ['message_attachment_count'], keepChanges: true}); } - }).fail(function () { + }).guardedCatch(function () { self._enableComposer(); }); }); @@ -375,9 +376,9 @@ var Chatter = Widget.extend({ _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). - // this function takes a deferred as argument, which is resolved once all fields have + // this function takes a promise as argument, which is resolved once all fields have // fetched their data - // this function appends the fields where they should be once the given deferred is resolved + // this function appends the fields where they should be once the given promise is resolved // and if it takes more than 500ms, displays a spinner to indicate that it is loading var self = this; @@ -385,6 +386,11 @@ var Chatter = Widget.extend({ concurrency.rejectAfter(concurrency.delay(500), def).then(function () { $spinner.appendTo(self.$el); }); + var always = function () { + // disable widgets in create mode, otherwise enable + self._isCreateMode ? self._disableChatter() : self._enableChatter(); + $spinner.remove(); + }; return def.then(function () { if (self.fields.activity) { @@ -400,11 +406,7 @@ var Chatter = Widget.extend({ if (self.fields.thread) { self.fields.thread.$el.appendTo(self.$el); } - }).always(function () { - // disable widgets in create mode, otherwise enable - self._isCreateMode ? self._disableChatter() : self._enableChatter(); - $spinner.remove(); - }); + }).then(always).guardedCatch(always); }, /** * @private @@ -517,7 +519,7 @@ var Chatter = Widget.extend({ if (!this._areAttachmentsLoaded) { def = this._fetchAttachments(); } - $.when(def).then(this._openAttachmentBox.bind(this)); + Promise.resolve(def).then(this._openAttachmentBox.bind(this)); } }, /** @@ -534,10 +536,10 @@ var Chatter = Widget.extend({ _onOpenComposerMessage: function () { var self = this; if (!this.suggested_partners_def) { - this.suggested_partners_def = $.Deferred(); - var method = 'message_get_suggested_recipients'; - var args = [[this.context.default_res_id], this.context]; - this._rpc({model: this.record.model, method: method, args: args}) + this.suggested_partners_def = new Promise(function (resolve, reject) { + var method = 'message_get_suggested_recipients'; + var args = [[self.context.default_res_id], self.context]; + self._rpc({model: self.record.model, method: method, args: args}) .then(function (result) { if (!self.suggested_partners_def) { return; // widget has been reset (e.g. we just switched to another record) @@ -555,8 +557,9 @@ var Chatter = Widget.extend({ reason: recipient[2], }); }); - self.suggested_partners_def.resolve(suggested_partners); + resolve(suggested_partners); }); + }); } this.suggested_partners_def.then(function (suggested_partners) { self._openComposer({ isLog: false, suggested_partners: suggested_partners }); diff --git a/addons/mail/static/src/js/composers/basic_composer.js b/addons/mail/static/src/js/composers/basic_composer.js index 9def912612c..72936152ae5 100644 --- a/addons/mail/static/src/js/composers/basic_composer.js +++ b/addons/mail/static/src/js/composers/basic_composer.js @@ -128,7 +128,7 @@ var BasicComposer = Widget.extend({ .on('update_typing_partners', this, this._onUpdateTypingPartners); // Mention - this._mentionManager.prependTo(this.$('.o_composer')); + var prependPromise = this._mentionManager.prependTo(this.$('.o_composer')); // Drag-Drop files // Allowing body to detect dragenter and dragleave for display @@ -137,7 +137,7 @@ var BasicComposer = Widget.extend({ $body.on('dragleave.' + this._dropZoneNS, this._onBodyFileDragLeave.bind(this)); $body.on("dragover." + this._dropZoneNS, this._onBodyFileDragover.bind(this)); $body.on("drop." + this._dropZoneNS, this._onBodyFileDrop.bind(this)); - return this._super(); + return Promise.all([this._super(), prependPromise]); }, destroy: function () { $("body").off('dragleave.' + this._dropZoneNS); @@ -236,7 +236,7 @@ var BasicComposer = Widget.extend({ * displayed to the user. If none of them match, then it will fetch for more * partner suggestions (@see _mentionFetchPartners). * - * @param {$.Deferred<Object[]>} prefetchedPartners list of list of + * @param {Promise<Object[]>} prefetchedPartners list of list of * prefetched partners. */ mentionSetPrefetchedPartners: function (prefetchedPartners) { @@ -299,20 +299,21 @@ var BasicComposer = Widget.extend({ /** * @private * @param {string} search - * @returns {$.Deferred<Object[]>} + * @returns {Promise<Object[]>} */ _mentionGetCannedResponses: function (search) { var self = this; - var def = $.Deferred(); - clearTimeout(this._cannedTimeout); - this._cannedTimeout = setTimeout(function () { - var cannedResponses = self.call('mail_service', 'getCannedResponses'); - var matches = fuzzy.filter(utils.unaccent(search), _.pluck(cannedResponses, 'source')); - var indexes = _.pluck(matches.slice(0, self.options.mentionFetchLimit), 'index'); - def.resolve(_.map(indexes, function (index) { - return cannedResponses[index]; - })); - }, 500); + var def = new Promise(function (resolve, reject) { + clearTimeout(self._cannedTimeout); + self._cannedTimeout = setTimeout(function () { + var cannedResponses = self.call('mail_service', 'getCannedResponses'); + var matches = fuzzy.filter(utils.unaccent(search), _.pluck(cannedResponses, 'source')); + var indexes = _.pluck(matches.slice(0, self.options.mentionFetchLimit), 'index'); + resolve(_.map(indexes, function (index) { + return cannedResponses[index]; + })); + }, 500); + }); return def; }, /** @@ -346,7 +347,7 @@ var BasicComposer = Widget.extend({ */ _mentionFetchPartners: function (search) { var self = this; - return $.when(this._mentionPrefetchedPartners).then(function (prefetchedPartners) { + return Promise.resolve(this._mentionPrefetchedPartners).then(function (prefetchedPartners) { // filter prefetched partners with the given search string var suggestions = []; var limit = self.options.mentionFetchLimit; @@ -371,7 +372,7 @@ var BasicComposer = Widget.extend({ { limit: limit, search: search } ); } - return $.when(suggestions).then(function (suggestions) { + return Promise.resolve(suggestions).then(function (suggestions) { //add im_status on suggestions _.each(suggestions, function (suggestionsSet) { _.each(suggestionsSet, function (suggestion) { @@ -387,27 +388,27 @@ var BasicComposer = Widget.extend({ * @param {string} model * @param {string} method * @param {Object} kwargs - * @return {$.Deferred} + * @return {Promise} */ _mentionFetchThrottled: function (model, method, kwargs) { var self = this; // Delays the execution of the RPC to prevent unnecessary RPCs when the user is still typing - var def = $.Deferred(); - clearTimeout(this.mentionFetchTimer); - this.mentionFetchTimer = setTimeout(function () { - return self._rpc({model: model, method: method, kwargs: kwargs}) + return new Promise(function (resolve, reject) { + clearTimeout(self.mentionFetchTimer); + self.mentionFetchTimer = setTimeout(function () { + return self._rpc({model: model, method: method, kwargs: kwargs}) .then(function (results) { - def.resolve(results); + resolve(results); }); - }, this.MENTION_THROTTLE); - return def; + }, self.MENTION_THROTTLE); + }); }, /** * @private - * @returns {$.Deferred} + * @returns {Promise} */ _preprocessMessage: function () { - // Return a deferred as this function is extended with asynchronous + // Return a promise as this function is extended with asynchronous // behavior for the chatter composer //Removing unwanted extra spaces from message @@ -420,7 +421,7 @@ var BasicComposer = Widget.extend({ var commands = this.options.commandsEnabled ? this._mentionManager.getListenerSelection('/') : []; - return $.when({ + return Promise.resolve({ content: this._mentionManager.generateLinks(value), attachment_ids: _.pluck(this.get('attachment_ids'), 'id'), partner_ids: _.uniq(_.pluck(this._mentionManager.getListenerSelection('@'), 'id')), diff --git a/addons/mail/static/src/js/composers/chatter_composer.js b/addons/mail/static/src/js/composers/chatter_composer.js index 777467e6eb8..8aaaf08dd6d 100644 --- a/addons/mail/static/src/js/composers/chatter_composer.js +++ b/addons/mail/static/src/js/composers/chatter_composer.js @@ -59,109 +59,112 @@ var ChatterComposer = BasicComposer.extend({ * @private * @param {Array} checkedSuggestedPartners list of 'recipient' partners to * complete informations or validate - * @returns {Deferred} resolved with the list of checked suggested partners + * @returns {Promise} resolved with the list of checked suggested partners * (real partner) **/ _checkSuggestedPartners: function (checkedSuggestedPartners) { var self = this; - var checkDone = $.Deferred(); + var checkDone = new Promise(function (resolve, reject) { + var recipients = _.filter(checkedSuggestedPartners, function (recipient) { + return recipient.checked; + }); + var recipientsToFind = _.filter(recipients, function (recipient) { + return (! recipient.partner_id); + }); + var namesToFind = _.pluck(recipientsToFind, 'full_name'); + var recipientsToCheck = _.filter(recipients, function (recipient) { + return (recipient.partner_id && ! recipient.email_address); + }); + var recipientIDs = _.pluck(_.filter(recipients, function (recipient) { + return recipient.partner_id && recipient.email_address; + }), 'partner_id'); - var recipients = _.filter(checkedSuggestedPartners, function (recipient) { - return recipient.checked; - }); - var recipientsToFind = _.filter(recipients, function (recipient) { - return (! recipient.partner_id); - }); - var namesToFind = _.pluck(recipientsToFind, 'full_name'); - var recipientsToCheck = _.filter(recipients, function (recipient) { - return (recipient.partner_id && ! recipient.email_address); - }); - var recipientIDs = _.pluck(_.filter(recipients, function (recipient) { - return recipient.partner_id && recipient.email_address; - }), 'partner_id'); + var namesToRemove = []; + var recipientIDsToRemove = []; - var namesToRemove = []; - var recipientIDsToRemove = []; + // have unknown names + // -> call message_get_partner_info_from_emails to try to find + // partner_id + var def; + if (namesToFind.length > 0) { + def = self._rpc({ + model: self._model, + method: 'message_partner_info_from_emails', + args: [[self.context.default_res_id], namesToFind], + }); + } - // have unknown names - // -> call message_get_partner_info_from_emails to try to find - // partner_id - var def; - if (namesToFind.length > 0) { - def = this._rpc({ - model: this._model, - method: 'message_partner_info_from_emails', - args: [[this.context.default_res_id], namesToFind], - }); - } + // for unknown names + incomplete partners + // -> open popup - cancel = remove from recipients + Promise.resolve(def).then(function (result) { + result = result || []; + var emailDefs = []; + var recipientPopups = result.concat(recipientsToCheck); - // for unknown names + incomplete partners - // -> open popup - cancel = remove from recipients - $.when(def).pipe(function (result) { - result = result || []; - var emailDefs = []; - var recipientPopups = result.concat(recipientsToCheck); + _.each(recipientPopups, function (partnerInfo) { + var prom = new Promise(function(innerResolve, innerReject) { + var partnerName = partnerInfo.full_name; + var partnerID = partnerInfo.partner_id; + var parsedEmail = mailUtils.parseEmail(partnerName); - _.each(recipientPopups, function (partnerInfo) { - var deferred = $.Deferred(); - emailDefs.push(deferred); + var dialog = new viewDialogs.FormViewDialog(self, { + res_model: 'res.partner', + res_id: partnerID, + context: { + active_model: self._model, + active_id: self.context.default_res_id, + force_email: true, + ref: 'compound_context', + default_name: parsedEmail[0], + default_email: parsedEmail[1], + }, + title: _t("Please complete customer's informations"), + disable_multiple_selection: true, + }).open(); + dialog.on('closed', self, function () { + innerResolve(); + }); + dialog.opened().then(function () { + dialog.form_view.on('on_button_cancel', self, function () { + namesToRemove.push(partnerName); + if (partnerID) { + recipientIDsToRemove.push(partnerID); + } + }); + }); + }); - var partnerName = partnerInfo.full_name; - var partnerID = partnerInfo.partner_id; - var parsedEmail = mailUtils.parseEmail(partnerName); + emailDefs.push(prom); - var dialog = new viewDialogs.FormViewDialog(self, { - res_model: 'res.partner', - res_id: partnerID, - context: { - active_model: self._model, - active_id: self.context.default_res_id, - force_email: true, - ref: 'compound_context', - default_name: parsedEmail[0], - default_email: parsedEmail[1], - }, - title: _t("Please complete customer's informations"), - disable_multiple_selection: true, - }).open(); - dialog.on('closed', self, function () { - deferred.resolve(); - }); - dialog.opened().then(function () { - dialog.form_view.on('on_button_cancel', self, function () { - namesToRemove.push(partnerName); - if (partnerID) { - recipientIDsToRemove.push(partnerID); - } - }); }); - }); - $.when.apply($, emailDefs).then(function () { - var newNamesToFind = _.difference(namesToFind, namesToRemove); - var def; - if (newNamesToFind.length > 0) { - def = self._rpc({ - model: self._model, - method: 'message_partner_info_from_emails', - args: [[self.context.default_res_id], newNamesToFind, true], + Promise.all(emailDefs).then(function () { + var newNamesToFind = _.difference(namesToFind, namesToRemove); + var def; + if (newNamesToFind.length > 0) { + def = self._rpc({ + model: self._model, + method: 'message_partner_info_from_emails', + args: [[self.context.default_res_id], newNamesToFind, true], + }); + } + Promise.resolve(def).then(function (result) { + result = result || []; + var recipientPopups = result.concat(recipientsToCheck); + _.each(recipientPopups, function (partnerInfo) { + if ( + partnerInfo.partner_id && + _.indexOf(partnerInfo.partner_id, recipientIDsToRemove) === -1 + ) { + recipientIDs.push(partnerInfo.partner_id); + } }); - } - $.when(def).pipe(function (result) { - result = result || []; - var recipientPopups = result.concat(recipientsToCheck); - _.each(recipientPopups, function (partnerInfo) { - if ( - partnerInfo.partner_id && - _.indexOf(partnerInfo.partner_id, recipientIDsToRemove) === -1 - ) { - recipientIDs.push(partnerInfo.partner_id); - } + }).then(function () { + resolve(recipientIDs); }); - }).pipe(function () { - checkDone.resolve(recipientIDs); }); }); }); + return checkDone; }, /** @@ -187,41 +190,40 @@ var ChatterComposer = BasicComposer.extend({ /** * @override * @private - * @returns {$.Deferred} + * @returns {Promise} */ _preprocessMessage: function () { var self = this; - var def = $.Deferred(); - this._super().then(function (message) { - message = _.extend(message, { - subtype: 'mail.mt_comment', - message_type: 'comment', - context: _.defaults({}, self.context, session.user_context), - }); + return new Promise(function (resolve, reject) { + self._super().then(function (message) { + message = _.extend(message, { + subtype: 'mail.mt_comment', + message_type: 'comment', + context: _.defaults({}, self.context, session.user_context), + }); - // Subtype - if (self.options.isLog) { - message.subtype = 'mail.mt_note'; - } + // Subtype + if (self.options.isLog) { + message.subtype = 'mail.mt_note'; + } - // Partner_ids - if (!self.options.isLog) { - var checkedSuggestedPartners = self._getCheckedSuggestedPartners(); - self._checkSuggestedPartners(checkedSuggestedPartners).done(function (partnerIDs) { - message.partner_ids = (message.partner_ids || []).concat(partnerIDs); - // update context - message.context = _.defaults({}, message.context, { - mail_post_autofollow: true, + // Partner_ids + if (!self.options.isLog) { + var checkedSuggestedPartners = self._getCheckedSuggestedPartners(); + self._checkSuggestedPartners(checkedSuggestedPartners).then(function (partnerIDs) { + message.partner_ids = (message.partner_ids || []).concat(partnerIDs); + // update context + message.context = _.defaults({}, message.context, { + mail_post_autofollow: true, + }); + resolve(message); }); - def.resolve(message); - }); - } else { - def.resolve(message); - } + } else { + resolve(message); + } + }); }); - - return def; }, /** * @override @@ -244,20 +246,20 @@ var ChatterComposer = BasicComposer.extend({ } var self = this; - var recipientDoneDef = $.Deferred(); - - // any operation on the full-composer will reload the record, so - // warn the user that any unsaved changes on the record will be lost. - this.trigger_up('discard_record_changes', { - proceed: function () { - if (self.options.isLog) { - recipientDoneDef.resolve([]); - } else { - var checkedSuggestedPartners = self._getCheckedSuggestedPartners(); - self._checkSuggestedPartners(checkedSuggestedPartners) - .then(recipientDoneDef.resolve.bind(recipientDoneDef)); - } - }, + var recipientDoneDef = new Promise(function (resolve, reject) { + // any operation on the full-composer will reload the record, so + // warn the user that any unsaved changes on the record will be lost. + self.trigger_up('discard_record_changes', { + proceed: function () { + if (self.options.isLog) { + resolve([]); + } else { + var checkedSuggestedPartners = self._getCheckedSuggestedPartners(); + self._checkSuggestedPartners(checkedSuggestedPartners) + .then(resolve); + } + }, + }); }); recipientDoneDef.then(function (partnerIDs) { diff --git a/addons/mail/static/src/js/composers/extended_composer.js b/addons/mail/static/src/js/composers/extended_composer.js index 917b71fa880..1acfba1058c 100644 --- a/addons/mail/static/src/js/composers/extended_composer.js +++ b/addons/mail/static/src/js/composers/extended_composer.js @@ -102,7 +102,7 @@ var ExtendedComposer = BasicComposer.extend({ /** * @override * @private - * @returns {$.Deferred} + * @returns {Promise} */ _preprocessMessage: function () { var self = this; diff --git a/addons/mail/static/src/js/composers/mention_manager.js b/addons/mail/static/src/js/composers/mention_manager.js index 6cbaab5002a..7ad8a36b39a 100644 --- a/addons/mail/static/src/js/composers/mention_manager.js +++ b/addons/mail/static/src/js/composers/mention_manager.js @@ -114,7 +114,7 @@ var MentionManager = Widget.extend({ if (this._activeListener) { var mentionWord = this._mentionWord; - $.when(this._activeListener.fetchCallback(mentionWord)) + Promise.resolve(this._activeListener.fetchCallback(mentionWord)) .then(function (suggestions) { if (mentionWord === self._mentionWord) { // update suggestions only if mentionWord didn't change diff --git a/addons/mail/static/src/js/discuss.js b/addons/mail/static/src/js/discuss.js index 0e3b66dd720..4e88434f86d 100644 --- a/addons/mail/static/src/js/discuss.js +++ b/addons/mail/static/src/js/discuss.js @@ -82,7 +82,7 @@ var PartnerInviteDialog = Dialog.extend({ /** * @private - * @returns {$.Promise} + * @returns {Promise} */ _addChannel: function () { var self = this; @@ -297,7 +297,7 @@ var Discuss = AbstractAction.extend({ * @override */ willStart: function () { - return $.when(this._super(), this.call('mail_service', 'isReady')); + return Promise.all([this._super(), this.call('mail_service', 'isReady')]); }, /** * @override @@ -331,7 +331,7 @@ var Discuss = AbstractAction.extend({ this._extendedComposer.appendTo(this.$('.o_mail_discuss_content'))); defs.push(this._super.apply(this, arguments)); - return $.when.apply($, defs) + return Promise.all(defs) .then(function () { return self._setThread(self._defaultThreadID); }) @@ -474,7 +474,7 @@ var Discuss = AbstractAction.extend({ }, /** * @private - * @returns {$.Promise} + * @returns {Promise} */ _fetchAndRenderThread: function () { var self = this; @@ -567,7 +567,7 @@ var Discuss = AbstractAction.extend({ * loaded when scrolling to the top, so they can't be loaded if there is no * scrollbar) * - * @returns {Deferred} resolved when there are enough messages to fill the + * @returns {Promise} resolved when there are enough messages to fill the * screen, or when there is no more message to fetch */ _loadEnoughMessages: function () { @@ -583,7 +583,7 @@ var Discuss = AbstractAction.extend({ * Load more messages for the current thread * * @private - * @returns {$.Promise} + * @returns {Promise} */ _loadMoreMessages: function () { var self = this; @@ -647,7 +647,7 @@ var Discuss = AbstractAction.extend({ autoFocus: true, source: function (request, response) { self._lastSearchVal = _.escape(request.term); - self._searchChannel(self._lastSearchVal).done(function (result){ + self._searchChannel(self._lastSearchVal).then(function (result){ result.push({ label: _.str.sprintf( '<strong>' + _t("Create %s (Public)") + '</strong>', @@ -689,7 +689,7 @@ var Discuss = AbstractAction.extend({ autoFocus: true, source: function (request, response) { self._lastSearchVal = _.escape(request.term); - self.call('mail_service', 'searchPartner', self._lastSearchVal, 10).done(response); + self.call('mail_service', 'searchPartner', self._lastSearchVal, 10).then(response); }, select: function (ev, ui) { var partnerID = ui.item.id; @@ -819,7 +819,7 @@ var Discuss = AbstractAction.extend({ * Renders, binds events and appends a thread widget. * * @private - * @returns {Deferred} + * @returns {Promise} */ _renderThread: function () { this._threadWidget = new ThreadWidget(this, { @@ -877,7 +877,7 @@ var Discuss = AbstractAction.extend({ /** * @private * @param {string} searchVal - * @returns {$.Promise<Array>} + * @returns {Promise<Array>} */ _searchChannel: function (searchVal){ return this._rpc({ @@ -915,7 +915,7 @@ var Discuss = AbstractAction.extend({ this._extendedComposer.do_show(); this._threadWidget.scrollToMessage({ - msgID: messageID, + messageID: messageID, duration: 200, onlyIfNecessary: true }); @@ -927,7 +927,7 @@ var Discuss = AbstractAction.extend({ * * @private * @param {integer} threadID a thread with such ID - * @returns {$.Promise} + * @returns {Promise} */ _setThread: function (threadID) { var self = this; @@ -1483,9 +1483,6 @@ var Discuss = AbstractAction.extend({ } else { self._threadWidget.scrollToBottom(); } - }) - .fail(function () { - // todo: display notifications }); }, /** diff --git a/addons/mail/static/src/js/discuss_mobile.js b/addons/mail/static/src/js/discuss_mobile.js index 549e05b78fd..617700c20c1 100644 --- a/addons/mail/static/src/js/discuss_mobile.js +++ b/addons/mail/static/src/js/discuss_mobile.js @@ -116,7 +116,7 @@ Discuss.include({ this._thread = thread; if (thread.getType() !== 'mailbox') { this.call('mail_service', 'openThreadWindow', threadID); - return $.when(); + return Promise.resolve(); } else { return this._super.apply(this, arguments); } @@ -179,7 +179,7 @@ Discuss.include({ }); def = this.call('mail_service', 'getChannelPreviews', channels); } - return $.when(def).then(function (previews) { + return Promise.resolve(def).then(function (previews) { // update content if (inMailbox) { if (!previouslyInInbox) { diff --git a/addons/mail/static/src/js/document_viewer.js b/addons/mail/static/src/js/document_viewer.js index 9affc3f41cf..77f33d152c0 100644 --- a/addons/mail/static/src/js/document_viewer.js +++ b/addons/mail/static/src/js/document_viewer.js @@ -76,7 +76,7 @@ var DocumentViewer = Widget.extend({ start: function () { this.$el.modal('show'); this.$el.on('hidden.bs.modal', _.bind(this._onDestroy, this)); - this.$('.o_viewer_img').load(_.bind(this._onImageLoaded, this)); + this.$('.o_viewer_img').on("load", _.bind(this._onImageLoaded, this)); this.$('[data-toggle="tooltip"]').tooltip({delay: 0}); return this._super.apply(this, arguments); }, @@ -132,7 +132,7 @@ var DocumentViewer = Widget.extend({ this.$('.o_viewer_content').html(QWeb.render('DocumentViewer.Content', { widget: this })); - this.$('.o_viewer_img').load(_.bind(this._onImageLoaded, this)); + this.$('.o_viewer_img').on("load", _.bind(this._onImageLoaded, this)); this.$('[data-toggle="tooltip"]').tooltip({delay: 0}); this._reset(); }, diff --git a/addons/mail/static/src/js/followers.js b/addons/mail/static/src/js/followers.js index 0a760516607..a8475f43219 100644 --- a/addons/mail/static/src/js/followers.js +++ b/addons/mail/static/src/js/followers.js @@ -55,14 +55,14 @@ var Followers = AbstractField.extend({ // note: the rendering of this widget is asynchronous as it needs to // fetch the details of the followers, but it performs a first rendering // synchronously (_displayGeneric), and updates its rendering once it - // has fetched the required data, so this function doesn't return a deferred + // has fetched the required data, so this function doesn't return a promise // as we don't want to wait to the data to be loaded to display the widget var self = this; var fetch_def = this.dp.add(this._readFollowers()); concurrency.rejectAfter(concurrency.delay(0), fetch_def).then(this._displayGeneric.bind(this)); - fetch_def.then(function () { + return fetch_def.then(function () { self._displayButtons(); self._displayFollowers(self.followers); if (self.subtypes) { // current user is follower @@ -203,7 +203,7 @@ var Followers = AbstractField.extend({ params: { follower_ids: missing_ids, res_model: this.model } }); } - return $.when(def).then(function (results) { + return Promise.resolve(def).then(function (results) { if (results) { self.followers = _.uniq(results.followers.concat(self.followers), 'id'); if (results.subtypes) { //read_followers will return False if current user is not in the list @@ -241,27 +241,27 @@ var Followers = AbstractField.extend({ */ _unfollow: function (ids) { var self = this; - var def = $.Deferred(); - var text = _t("Warning! \n If you remove a follower, he won't be notified of any email or discussion on this document.\n Do you really want to remove this follower ?"); - Dialog.confirm(this, text, { - confirm_callback: function () { - var args = [ - [self.res_id], - ids.partner_ids, - ids.channel_ids, - {}, // FIXME - ]; - self._rpc({ + return new Promise(function (resolve, reject) { + var text = _t("Warning! \n If you remove a follower, he won't be notified of any email or discussion on this document.\n Do you really want to remove this follower ?"); + Dialog.confirm(this, text, { + confirm_callback: function () { + var args = [ + [self.res_id], + ids.partner_ids, + ids.channel_ids, + {}, // FIXME + ]; + self._rpc({ model: self.model, method: 'message_unsubscribe', args: args }) .then(self._reload.bind(self)); - def.resolve(); - }, - cancel_callback: def.reject.bind(def), + resolve(); + }, + cancel_callback: reject, + }); }); - return def; }, _updateSubscription: function (event, followerID, isChannel) { var ids = {}; @@ -290,7 +290,7 @@ var Followers = AbstractField.extend({ // If no more subtype followed, unsubscribe the follower if (!checklist.length) { - this._unfollow(ids).fail(function () { + this._unfollow(ids).guardedCatch(function () { $(event.currentTarget).find('input').addBack('input').prop('checked', true); }); } else { diff --git a/addons/mail/static/src/js/form_renderer.js b/addons/mail/static/src/js/form_renderer.js index 91d2c2033c4..e7b767c0b2f 100644 --- a/addons/mail/static/src/js/form_renderer.js +++ b/addons/mail/static/src/js/form_renderer.js @@ -50,18 +50,24 @@ FormRenderer.include({ * @private */ _renderNode: function (node) { + var self = this; if (node.tag === 'div' && node.attrs.class === 'oe_chatter') { if (!this.chatter) { this.chatter = new Chatter(this, this.state, this.mailFields, { isEditable: this.activeActions.edit, viewType: 'form', }); - this.chatter.appendTo($('<div>')); - this._handleAttributes(this.chatter.$el, node); + + var $temporaryParentDiv = $('<div>'); + this.defs.push(this.chatter.appendTo($temporaryParentDiv).then(function () { + self.chatter.$el.unwrap(); + self._handleAttributes(self.chatter.$el, node); + })); + return $temporaryParentDiv; } else { this.chatter.update(this.state); + return this.chatter.$el; } - return this.chatter.$el; } else { return this._super.apply(this, arguments); } diff --git a/addons/mail/static/src/js/many2many_tags_email.js b/addons/mail/static/src/js/many2many_tags_email.js index e66412a7a2c..6648ef50423 100644 --- a/addons/mail/static/src/js/many2many_tags_email.js +++ b/addons/mail/static/src/js/many2many_tags_email.js @@ -20,7 +20,7 @@ BasicModel.include({ * @private * @param {Object} record - an element from the localData * @param {string} fieldName - * @return {Deferred<Object>} the deferred is resolved with the + * @return {Promise<Object>} the promise is resolved with the * invalidPartnerIds */ _setInvalidMany2ManyTagsEmail: function (record, fieldName) { @@ -42,10 +42,10 @@ BasicModel.include({ var changes = {operation: 'DELETE', ids: _.pluck(invalidPartnerIds, 'id')}; def = this._applyX2ManyChange(record, fieldName, changes); } - return $.when(def).then(function () { - return $.when({ + return Promise.resolve(def).then(function () { + return { invalidPartnerIds: _.pluck(invalidPartnerIds, 'res_id'), - }); + }; }); }, }); @@ -65,7 +65,7 @@ var FieldMany2ManyTagsEmail = M2MTags.extend({ * Open a popup for each invalid partners (without email) to fill the email. * * @private - * @returns {Deferred} + * @returns {Promise} */ _checkEmailPopup: function () { var self = this; @@ -75,25 +75,25 @@ var FieldMany2ManyTagsEmail = M2MTags.extend({ // propose the user to correct invalid partners _.each(this.record.specialData[this.name].invalidPartnerIds, function (resID) { - var def = $.Deferred(); - popupDefs.push(def); - - var pop = new form_common.FormViewDialog(self, { - res_model: self.field.relation, - res_id: resID, - context: self.record.context, - title: "", - on_saved: function (record) { - if (record.data.email) { - validPartners.push(record.res_id); - } - }, - }).open(); - pop.on('closed', self, function () { - def.resolve(); + var def = new Promise(function (resolve, reject) { + var pop = new form_common.FormViewDialog(self, { + res_model: self.field.relation, + res_id: resID, + context: self.record.context, + title: "", + on_saved: function (record) { + if (record.data.email) { + validPartners.push(record.res_id); + } + }, + }).open(); + pop.on('closed', self, function () { + resolve(); + }); }); + popupDefs.push(def); }); - return $.when.apply($, popupDefs).then(function() { + return Promise.all(popupDefs).then(function() { // All popups have been processed for the given ids // It is now time to set the final value with valid partners ids. validPartners = _.uniq(validPartners); @@ -117,14 +117,14 @@ var FieldMany2ManyTagsEmail = M2MTags.extend({ */ _render: function () { var self = this; - var def = $.Deferred(); var _super = this._super.bind(this); - if (this.record.specialData[this.name].invalidPartnerIds.length) { - def = this._checkEmailPopup(); - } else { - def.resolve(); - } - return def.then(function () { + return new Promise(function (resolve, reject) { + if (self.record.specialData[self.name].invalidPartnerIds.length) { + resolve(self._checkEmailPopup()); + } else { + resolve(); + } + }).then(function () { return _super.apply(self, arguments); }); }, diff --git a/addons/mail/static/src/js/models/messages/message.js b/addons/mail/static/src/js/models/messages/message.js index a414a11874b..77e4120da29 100644 --- a/addons/mail/static/src/js/models/messages/message.js +++ b/addons/mail/static/src/js/models/messages/message.js @@ -511,7 +511,7 @@ var Message = AbstractMessage.extend(Mixins.EventDispatcherMixin, ServicesMixin * @see {mail.Manager.Notification} for the receipt of 'toggle_star' * notification after this rpc. * - * @return {$.Promise} + * @return {Promise} */ toggleStarStatus: function () { return this._rpc({ diff --git a/addons/mail/static/src/js/models/threads/abstract_thread.js b/addons/mail/static/src/js/models/threads/abstract_thread.js index a515c4d2f8a..192d0e58c93 100644 --- a/addons/mail/static/src/js/models/threads/abstract_thread.js +++ b/addons/mail/static/src/js/models/threads/abstract_thread.js @@ -150,18 +150,18 @@ var AbstractThread = Class.extend(Mixins.EventDispatcherMixin, { * Mark the thread as read, which resets the unread counter to 0. This is * only performed if the unread counter is not 0. * - * @returns {$.Promise} + * @returns {Promise} */ markAsRead: function () { if (this._unreadCounter > 0) { return this._markAsRead(); } - return $.when(); + return Promise.resolve(); }, /** * Post a message on this thread * - * @returns {$.Promise} resolved with the message object to be sent to the + * @returns {Promise} resolved with the message object to be sent to the * server */ postMessage: function () { @@ -200,22 +200,22 @@ var AbstractThread = Class.extend(Mixins.EventDispatcherMixin, { * Mark the thread as read * * @private - * @returns {$.Promise} + * @returns {Promise} */ _markAsRead: function () { this.resetUnreadCounter(); - return $.when(); + return Promise.resolve(); }, /** * Post a message on this thread * * @abstract * @private - * @returns {$.Promise} resolved with the message object to be sent to the + * @returns {Promise} resolved with the message object to be sent to the * server */ _postMessage: function () { - return $.when(); + return Promise.resolve(); }, /** * Warn views (e.g. discuss app, thread window, etc.) to update visually diff --git a/addons/mail/static/src/js/models/threads/channel.js b/addons/mail/static/src/js/models/threads/channel.js index 9f0f37264db..9e9f3ac6a39 100644 --- a/addons/mail/static/src/js/models/threads/channel.js +++ b/addons/mail/static/src/js/models/threads/channel.js @@ -70,7 +70,7 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, { this._isMyselfModerator = data.is_moderator; this._lastMessageDate = undefined; this._members = data.members || []; - // Deferred that is resolved on fetched members of this channel. + // Promise that is resolved on fetched members of this channel. this._membersDef = undefined; // number of messages that are 'needaction', which is equivalent to the // number of messages in this channel that are in inbox. @@ -129,19 +129,21 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, { * @pverride */ detach: function () { - this._super.apply(this, arguments); - this._rpc({ - model: 'mail.channel', - method: 'channel_minimize', - args: [this.getUUID(), true], - }, { - shadow: true, + var self = this; + return this._super.apply(this, arguments).then(function () { + self._rpc({ + model: 'mail.channel', + method: 'channel_minimize', + args: [self.getUUID(), true], + }, { + shadow: true, + }); }); }, /** * Force fetch members of the channel * - * @returns {$.Promise<Object[]>} resolved with list of channel listeners + * @returns {Promise<Object[]>} resolved with list of channel listeners */ forceFetchMembers: function () { this._membersDef = undefined; @@ -186,7 +188,7 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, { /** * Get listeners of a channel * - * @returns {$.Promise<Array<Object[]>>} resolved with list of list of + * @returns {Promise<Object[]>} resolved with list of list of * channel listeners. */ getMentionPartnerSuggestions: function () { @@ -302,7 +304,7 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, { * Unsubscribes from channel * * @abstract - * @returns {$.Promise} resolve when unsubscribed + * @returns {Promise} resolve when unsubscribed */ unsubscribe: function () {}, /** @@ -381,19 +383,19 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, { * * @override * @private - * @returns {$.Promise} resolved when message has been marked as read + * @returns {Promise} resolved when message has been marked as read */ _markAsRead: function () { var superDef = this._super.apply(this, arguments); var seenDef = this._notifySeen(); - return $.when(superDef, seenDef); + return Promise.all([superDef, seenDef]); }, /** * @override {mail.model.ThreadTypingMixin} * @private * @param {Object} params * @param {boolean} params.typing - * @returns {$.Promise} + * @returns {Promise} */ _notifyMyselfTyping: function (params) { return this._rpc({ @@ -424,7 +426,7 @@ var Channel = SearchableThread.extend(ThreadTypingMixin, { * @override * @private * @param {Object} data data related to the new message - * @returns {$.Promise<Object>} resolved when the message has been sent to + * @returns {Promise<Object>} resolved when the message has been sent to * the server, with the object message that has been sent to the server. */ _postMessage: function (data) { diff --git a/addons/mail/static/src/js/models/threads/document_thread.js b/addons/mail/static/src/js/models/threads/document_thread.js index 6c15b39d25e..08e441610ee 100644 --- a/addons/mail/static/src/js/models/threads/document_thread.js +++ b/addons/mail/static/src/js/models/threads/document_thread.js @@ -92,7 +92,7 @@ var DocumentThread = Thread.extend({ * @param {Object} options * @param {boolean} [options.forceFetch] if true, fetch anyway, as user * clicked on 'load more'. - * @returns {$.Promise<mail.model.Message[]>} + * @returns {Promise<mail.model.Message[]>} */ fetchMessages: function (options) { var self = this; @@ -217,14 +217,14 @@ var DocumentThread = Thread.extend({ * Get most up to date messageIDs * * @private - * @returns {$.Promise} resolved when message IDs have been fetched and set + * @returns {Promise} resolved when message IDs have been fetched and set * in the model */ _fetchMessageIDs: function () { var self = this; var resID = this.getDocumentID(); if (!resID || !this._mustFetchMessageIDs) { - return $.when(); + return Promise.resolve(); } return this._rpc({ model: this.getDocumentModel(), @@ -239,7 +239,7 @@ var DocumentThread = Thread.extend({ * @private * @param {Object} options * @param {boolean} [options.forceFetch] - * @returns {$.Promise} resolved when messages have been fetched + document + * @returns {Promise} resolved when messages have been fetched + document * thread has updated messages */ _fetchMessages: function (options) { @@ -273,7 +273,7 @@ var DocumentThread = Thread.extend({ }); }); } else { - return $.when(); + return Promise.resolve(); } }); @@ -284,7 +284,7 @@ var DocumentThread = Thread.extend({ * * @override * @private - * @returns {$.Promise} resolved when messages have been marked as read on + * @returns {Promise} resolved when messages have been marked as read on * the server. */ _markAsRead: function () { @@ -299,7 +299,7 @@ var DocumentThread = Thread.extend({ * @override * @private * @param {Object} data data related to the new message - * @return {$.Promise<Object>} resolved when the message has been sent to + * @return {Promise<Object>} resolved when the message has been sent to * the server, with the object message that has been sent to the server. */ _postMessage: function (data) { diff --git a/addons/mail/static/src/js/models/threads/livechat.js b/addons/mail/static/src/js/models/threads/livechat.js index 45cab5aef10..c3b4a5e2b4d 100644 --- a/addons/mail/static/src/js/models/threads/livechat.js +++ b/addons/mail/static/src/js/models/threads/livechat.js @@ -34,7 +34,7 @@ var Livechat = TwoUserChannel.extend({ * display of a user that is typing. * * @override - * @returns {$.Promise<Array<Object[]>>} resolved with list of list of + * @returns {Promise<Object[]>} resolved with list of list of * livechat members. */ getMentionPartnerSuggestions: function () { diff --git a/addons/mail/static/src/js/models/threads/mailbox.js b/addons/mail/static/src/js/models/threads/mailbox.js index 263072f575e..bedd6af27e1 100644 --- a/addons/mail/static/src/js/models/threads/mailbox.js +++ b/addons/mail/static/src/js/models/threads/mailbox.js @@ -59,7 +59,7 @@ var Mailbox = SearchableThread.extend({ * channel, only keep the lastest message of this document/channel in the * previews. * - * @returns {$.Promise<Object[]>} + * @returns {Promise<Object[]>} */ getMessagePreviews: function () { var self = this; @@ -119,7 +119,7 @@ var Mailbox = SearchableThread.extend({ * makes only sense for 'Inbox'. * * @param {Array} domain - * @return {$.Promise} resolved when all messages have been marked as read + * @return {Promise} resolved when all messages have been marked as read * on the server */ markAllMessagesAsRead: function (domain) { @@ -133,7 +133,7 @@ var Mailbox = SearchableThread.extend({ }, }); } - return $.when(); + return Promise.resolve(); }, //-------------------------------------------------------------------------- diff --git a/addons/mail/static/src/js/models/threads/mixins/thread_typing_mixin.js b/addons/mail/static/src/js/models/threads/mixins/thread_typing_mixin.js index f995b4e09b3..7d97538dbb9 100644 --- a/addons/mail/static/src/js/models/threads/mixins/thread_typing_mixin.js +++ b/addons/mail/static/src/js/models/threads/mixins/thread_typing_mixin.js @@ -239,11 +239,11 @@ var ThreadTypingMixin = { * @private * @param {Object} params * @param {boolean} params.typing whether we are typing something or not - * @returns {$.Promise} resolved if the server is notified, rejected + * @returns {Promise} resolved if the server is notified, rejected * otherwise */ _notifyMyselfTyping: function (params) { - return $.when(); + return Promise.resolve(); }, /** * Warn views that the list of users that are currently typing on this diff --git a/addons/mail/static/src/js/models/threads/multi_user_channel.js b/addons/mail/static/src/js/models/threads/multi_user_channel.js index bac417c361d..3dab9337502 100644 --- a/addons/mail/static/src/js/models/threads/multi_user_channel.js +++ b/addons/mail/static/src/js/models/threads/multi_user_channel.js @@ -56,7 +56,7 @@ var MultiUserChannel = Channel.extend({ * Unsubscribes from channel * * @override - * @returns {$.Promise} resolve when unsubscribed + * @returns {Promise} resolve when unsubscribed */ unsubscribe: function () { return this._rpc({ diff --git a/addons/mail/static/src/js/models/threads/searchable_thread.js b/addons/mail/static/src/js/models/threads/searchable_thread.js index 8ddff5457af..511bc6348c8 100644 --- a/addons/mail/static/src/js/models/threads/searchable_thread.js +++ b/addons/mail/static/src/js/models/threads/searchable_thread.js @@ -49,7 +49,7 @@ var SearchableThread = Thread.extend({ } var cache = this._getCache(domain); if (cache.loaded) { - return $.when(cache.messages); + return Promise.all(cache.messages); } else { return this._fetchMessages(domain); } @@ -159,7 +159,7 @@ var SearchableThread = Thread.extend({ * @private * @param {Array} [pDomain] filter on the messages of the channel * @param {boolean} [loadMore] Whether it should load more message - * @return {$.Promise<mail.model.Message[]>} resolved with list of messages + * @return {Promise<mail.model.Message[]>} resolved with list of messages */ _fetchMessages: function (pDomain, loadMore) { var self = this; diff --git a/addons/mail/static/src/js/models/threads/thread.js b/addons/mail/static/src/js/models/threads/thread.js index 8c622779bb3..8f4e8956d27 100644 --- a/addons/mail/static/src/js/models/threads/thread.js +++ b/addons/mail/static/src/js/models/threads/thread.js @@ -58,12 +58,13 @@ var Thread = AbstractThread.extend(ServicesMixin, { * of this thread. Otherwise unfold it while detaching it. * @param {boolean} [options.passively=false] if set, if the thread window * will be created passively. + * @returns {Promise} */ detach: function (options) { options = options || {}; this._detached = true; this._folded = options.keepFoldState ? this._folded : false; - this._warnUpdatedWindowState({ + return this._warnUpdatedWindowState({ passively: options.passively, }); }, @@ -79,10 +80,10 @@ var Thread = AbstractThread.extend(ServicesMixin, { * `this.LIMIT` number of messages at a time. * * @abstract - * @returns {$.Promise<mail.model.Message[]>} + * @returns {Promise<mail.model.Message[]>} */ fetchMessages: function () { - return $.when([]); + return Promise.resolve([]); }, /** * Updates the folded state of the thread. Must be overriden to reflect @@ -110,10 +111,10 @@ var Thread = AbstractThread.extend(ServicesMixin, { * By default, a thread has not listener. * * @abstract - * @returns {$.Promise<Array<Object[]>>} + * @returns {Promise<Array<Object[]>>} */ getMentionPartnerSuggestions: function () { - return $.when([]); + return Promise.resolve([]); }, /** * Returns the information required to render the preview of this channel. @@ -287,10 +288,10 @@ var Thread = AbstractThread.extend(ServicesMixin, { /** * @abstract * @private - * @return {$.Promise} + * @return {Promise} */ _fetchMessages: function () { - return $.when(); + return Promise.resolve(); }, /** * Replace character representations of emojis by their unicode @@ -330,7 +331,7 @@ var Thread = AbstractThread.extend(ServicesMixin, { * @abstract * @private * @param {Object} data - * @returns {$.Promise<Object>} resolved with the message object to be sent + * @returns {Promise<Object>} resolved with the message object to be sent * to the server */ _postMessage: function (data) { @@ -355,7 +356,7 @@ var Thread = AbstractThread.extend(ServicesMixin, { messageData.subject = data.subject; } return this._super.apply(this, arguments).then(function () { - return $.when(messageData); + return messageData; }); }, /** @@ -374,10 +375,11 @@ var Thread = AbstractThread.extend(ServicesMixin, { * @private * @param {Object} [options={}] * @param {boolean} [options.passively=false] + * @returns {Promise} */ _warnUpdatedWindowState: function (options) { options = options || {}; - this.call('mail_service', 'updateThreadWindow', this.getID(), options); + return this.call('mail_service', 'updateThreadWindow', this.getID(), options); }, }); diff --git a/addons/mail/static/src/js/models/threads/two_user_channel.js b/addons/mail/static/src/js/models/threads/two_user_channel.js index 204fd95eb82..54a4145bd13 100644 --- a/addons/mail/static/src/js/models/threads/two_user_channel.js +++ b/addons/mail/static/src/js/models/threads/two_user_channel.js @@ -15,7 +15,7 @@ var TwoUserChannel = Channel.extend({ * Unpin from two-user thread * * @override - * @returns {$.Promise} resolve when unsubscribed + * @returns {Promise} resolve when unsubscribed */ unsubscribe: function () { return this._rpc({ diff --git a/addons/mail/static/src/js/services/mail_document_thread_manager.js b/addons/mail/static/src/js/services/mail_document_thread_manager.js index a98ae317c9d..f1d9eefb44a 100644 --- a/addons/mail/static/src/js/services/mail_document_thread_manager.js +++ b/addons/mail/static/src/js/services/mail_document_thread_manager.js @@ -182,7 +182,7 @@ MailManager.include({ resModel: resModel, }); } - this._super.apply(this, arguments); + return this._super.apply(this, arguments); }, /** * Updates the thread windows related to document threads. diff --git a/addons/mail/static/src/js/services/mail_manager.js b/addons/mail/static/src/js/services/mail_manager.js index 5eb588d24e7..27a7183ec88 100644 --- a/addons/mail/static/src/js/services/mail_manager.js +++ b/addons/mail/static/src/js/services/mail_manager.js @@ -80,13 +80,14 @@ var MailManager = AbstractService.extend({ * the unread_counter of channel. * @param {boolean} [options.silent] whether it should inform in the mailBus * of the newly created message. - * @returns {mail.model.Message} message object + * @returns {Promise<mail.model.Message>} */ addMessage: function (data, options) { options = options || {}; var message = this.getMessage(data.id); + var prom = Promise.resolve(); if (!message) { - message = this._addNewMessage(data, options); + prom = this._addNewMessage(data, options); } else { if (data.moderation_status === 'accepted') { message.setModerationStatus('accepted', { @@ -97,7 +98,7 @@ var MailManager = AbstractService.extend({ this._addMessageToThreads(message, options); } } - return message; + return prom; }, /** * Creates a channel, can be either a true channel or a DM chat based on @@ -105,7 +106,7 @@ var MailManager = AbstractService.extend({ * * @param {integer|string} name id of partner (in case of dm) or name * @param {string} type ['dm_chat', 'public', 'private'] - * @returns {$.Promise<integer>} resolved with ID of the newly created + * @returns {Promise<integer>} resolved with ID of the newly created * channel */ createChannel: function (name, type) { @@ -150,7 +151,7 @@ var MailManager = AbstractService.extend({ * Returns the content that will be shown in the mail navbar dropdown * * @param {mail.model.Channel[]} channels - * @returns {$.Promise<Object[]>} resolved with list of channel previews + * @returns {Promise<Object[]>} resolved with list of channel previews */ getChannelPreviews: function (channels) { var self = this; @@ -227,23 +228,22 @@ var MailManager = AbstractService.extend({ * * @param {string} [filter=undefined] specify 'chat' or 'channels' to only * get previews for that type of threads - * @returns {$.Promise<Object[]>} resolved with list of objects that have a + * @returns {Promise<Object[]>} resolved with list of objects that have a * valid format for rendering a messaging menu preview items. */ getSystrayPreviews: function (filter) { var self = this; - var defs = []; var channelDef = this._getSystrayChannelPreviews(filter); var inboxDef = this._getSystrayInboxPreviews(filter); var failureDef = this._getSystrayMailFailurePreviews(filter); - defs = defs.concat([channelDef, inboxDef, failureDef]); - return $.when.apply($, defs) - .then(function (previewsChannel, previewsInbox, previewsFailure) { + + return Promise.all([channelDef, inboxDef, failureDef]) + .then(function (result) { //previewsChannel, previewsInbox, previewsFailure // order: failures > inbox > channel, each group must be sorted - previewsChannel = self._sortPreviews(previewsChannel); - previewsInbox = self._sortPreviews(previewsInbox); - previewsFailure = self._sortPreviews(previewsFailure); + var previewsChannel = self._sortPreviews(result[0]); + var previewsInbox = self._sortPreviews(result[1]); + var previewsFailure = self._sortPreviews(result[2]); return _.union(previewsFailure, previewsInbox, previewsChannel); }); }, @@ -295,7 +295,7 @@ var MailManager = AbstractService.extend({ * This is the case when it has fetched the initial state from the server, * by means of the route 'mail/init_messaging' * - * @returns {$.Promise} + * @returns {Promise} */ isReady: function () { return this._isReady; @@ -306,24 +306,24 @@ var MailManager = AbstractService.extend({ * * @param {integer} channelID * @param {Object} [options] options to be passed on channel add - * @returns {$.Promise<integer>} resolved with channelID joined + * @returns {Promise<integer>} resolved with channelID joined */ joinChannel: function (channelID, options) { - var def; + var prom; var channel = this.getChannel(channelID); if (channel) { // channel already joined - def = $.when(channelID); + prom = Promise.resolve(channelID); } else { - def = this._joinAndAddChannel(channelID, options); + prom = this._joinAndAddChannel(channelID, options); } - return def; + return prom; }, /** * Mark messages as read * * @param {Array} messageIDs list of messages IDs - * @returns {$.Promise} resolved when messages have been marked as read on + * @returns {Promise} resolved when messages have been marked as read on * the server. */ markMessagesAsRead: function (messageIDs) { @@ -341,7 +341,7 @@ var MailManager = AbstractService.extend({ args: [ids], }); } else { - return $.when(); + return Promise.resolve(); } }, /** @@ -396,19 +396,19 @@ var MailManager = AbstractService.extend({ * * @param {string} searchVal * @param {integer} limit max number of found partners in the response - * @returns {$.Promise<Object[]>} list of found partners (matching + * @returns {Promise<Object[]>} list of found partners (matching * 'searchVal') */ searchPartner: function (searchVal, limit) { - var def = $.Deferred(); + var self = this; var partners = this._searchPartnerPrefetch(searchVal, limit); - - if (!partners.length) { - def = this._searchPartnerFetch(searchVal, limit); - } else { - def = $.when(partners); - } - return def.then(function (partners) { + return new Promise(function (resolve, reject) { + if (!partners.length) { + resolve(self._searchPartnerFetch(searchVal, limit)); + } else { + resolve(partners); + } + }).then(function (partners) { var suggestions = _.map(partners, function (partner) { return { id: partner.id, @@ -422,7 +422,7 @@ var MailManager = AbstractService.extend({ /** * Unstars all messages from all channels * - * @returns {$.Promise} + * @returns {Promise} */ unstarAll: function () { return this._rpc({ @@ -448,11 +448,13 @@ var MailManager = AbstractService.extend({ * @param {string} [data.state] e.g. 'open', 'folded' * @param {Object|integer} [options=undefined] * @param {boolean} [options.silent=false] - * @returns {integer} the ID of the newly or already existing channel + * @returns {Promise<integer>} resolves with the ID of the newly or already + * existing channel */ _addChannel: function (data, options) { options = typeof options === 'object' ? options : {}; var channel = this.getChannel(data.id); + var proms = []; if (!channel) { channel = this._makeChannel(data, options); if (channel.getType() === 'dm_chat') { @@ -462,14 +464,16 @@ var MailManager = AbstractService.extend({ if (data.last_message) { // channel_info in mobile, necessary for showing channel // preview in mobile - this.addMessage(data.last_message); + proms.push(this.addMessage(data.last_message)); } this._sortThreads(); if (!options.silent) { - this._mailBus.trigger('new_channel', channel); + this._mailBus.trigger('new_channel', channel, proms); } } - return channel.getID(); + return Promise.all(proms).then(function () { + return channel.getID(); + }); }, /** * Add a new mailbox @@ -516,21 +520,23 @@ var MailManager = AbstractService.extend({ * @private * @param {Object} data * @param {Object} options - * @returns {mail.model.Message} + * @return {Promise<mail.model.Message>} */ _addNewMessage: function (data, options) { + var self = this; var message = this._makeMessage(data); // Keep the array ordered by ID when inserting the new message var index = _.sortedIndex(this._messages, message, function (msg) { return msg.getID(); }); this._messages.splice(index, 0, message); - this._addNewMessagePostprocessThread(message, options); - this._addMessageToThreads(message, options); - if (!options.silent) { - this._mailBus.trigger('new_message', message); - } - return message; + return this._addNewMessagePostprocessThread(message, options).then(function () { + self._addMessageToThreads(message, options); + if (!options.silent) { + self._mailBus.trigger('new_message', message); + } + return message; + }); }, /** * For newly added message, postprocess threads linked to this message @@ -541,9 +547,11 @@ var MailManager = AbstractService.extend({ * @param {Array} [options.domain] * @param {boolean} [options.incrementUnread] * @param {boolean} [options.showNotification] + * @returns {Promise} */ _addNewMessagePostprocessThread: function (message, options) { var self = this; + var proms = []; _.each(message.getThreadIDs(), function (threadID) { var thread = self.getThread(threadID); if (thread) { @@ -553,6 +561,7 @@ var MailManager = AbstractService.extend({ !message.isSystemNotification() ) { if (thread.isTwoUserThread() && options.showNotification) { + var prom = Promise.resolve(); if ( !self._isDiscussOpen() && !config.device.isMobile && @@ -560,17 +569,21 @@ var MailManager = AbstractService.extend({ ) { // automatically open thread window // while keeping it unread - thread.detach({ passively: true }); - } - var query = { isVisible: false }; - self._mailBus.trigger('is_thread_bottom_visible', thread, query); - if (!self.call('bus_service', 'isOdooFocused') || !query.isVisible) { - self._notifyIncomingMessage(message); + prom = thread.detach({ passively: true }); } + proms.push(prom); + prom.then(function () { + var query = { isVisible: false }; + self._mailBus.trigger('is_thread_bottom_visible', thread, query); + if (!self.call('bus_service', 'isOdooFocused') || !query.isVisible) { + self._notifyIncomingMessage(message); + } + }); } } } }); + return Promise.all(proms); }, /** * Create a Channel (other than a DM) @@ -578,7 +591,7 @@ var MailManager = AbstractService.extend({ * @private * @param {string} name * @param {string} type - * @returns {$.Promise<integer>} ID of the created channel + * @returns {Promise<integer>} ID of the created channel */ _createChannel: function (name, type) { var context = _.extend({ isMobile: config.device.isMobile }, session.user_context); @@ -595,7 +608,7 @@ var MailManager = AbstractService.extend({ * * @private * @param {string} name - * @returns {$.Promise<integer>} ID of the created channel + * @returns {Promise<integer>} ID of the created channel */ _createDMChat: function (name) { var context = _.extend({ isMobile: config.device.isMobile }, session.user_context); @@ -610,7 +623,7 @@ var MailManager = AbstractService.extend({ /** * @private * @param {integer[]} channelIDs - * @returns {$.Promise<Object[]>} resolved with list of channel preview + * @returns {Promise<Object[]>} resolved with list of channel preview */ _fetchChannelPreviews: function (channelIDs) { return this._rpc({ @@ -652,7 +665,7 @@ var MailManager = AbstractService.extend({ * * @private * @param {mail.model.Channel[]} channels - * @returns {$.Deferred<Object[]>} resolved with list of channel previews + * @returns {Promise<Object[]>} resolved with list of channel previews */ _getChannelPreviews: function (channels) { var self = this; @@ -667,19 +680,22 @@ var MailManager = AbstractService.extend({ }); fetchDef = this._fetchChannelPreviews(ids); } else { - fetchDef = $.when(); + fetchDef = Promise.resolve(); } return fetchDef.then(function (fetchedPreviews) { // add fetched messages + var proms = []; _.each(fetchedPreviews, function (fetchedPreview) { if (fetchedPreview.last_message) { - self.addMessage(fetchedPreview.last_message); + proms.push(self.addMessage(fetchedPreview.last_message)); } // mark the channel as previewed, so that we do not need to // fetch preview again var channel = self.getChannel(fetchedPreview.id); channel.markAsPreviewed(); }); + return Promise.all(proms); + }).then(function () { return _.map(channels, function (channel) { return channel.getPreview(); }); @@ -694,7 +710,7 @@ var MailManager = AbstractService.extend({ * the documentID is omitted for a model preview, whereas it is set for a * preview of a failure related to a document. * - * @returns {$.Promise<Object[]>} resolved with previews of mail failures + * @returns {Promise<Object[]>} resolved with previews of mail failures */ _getMailFailurePreviews: function () { // items = list of objects: @@ -754,7 +770,7 @@ var MailManager = AbstractService.extend({ * @private * @param {string|undefined} [filter] the filter to apply on channel * previews in systray messaging menu - * @returns {$.Promise<Object[]>} resolved with object valid for template + * @returns {Promise<Object[]>} resolved with object valid for template * mail.Preview on channel previews, based on selected filter */ _getSystrayChannelPreviews: function (filter) { @@ -796,7 +812,7 @@ var MailManager = AbstractService.extend({ * * @private * @param {string|undefined} [filter] - * @returns {$.Deferred<Object[]>} resolved with valid objects for template + * @returns {Promise<Object[]>} resolved with valid objects for template * mail.Preview on inbox message previews */ _getSystrayInboxPreviews: function (filter) { @@ -806,7 +822,7 @@ var MailManager = AbstractService.extend({ var inbox = this.getMailbox('inbox'); return inbox.getMessagePreviews(); } else { - return $.when([]); + return Promise.resolve([]); } }, /** @@ -820,7 +836,7 @@ var MailManager = AbstractService.extend({ if (filter === 'mailbox_inbox' || !filter) { return this._getMailFailurePreviews(); } else { - return $.when([]); + return Promise.resolve([]); } }, /** @@ -863,7 +879,7 @@ var MailManager = AbstractService.extend({ * @private * @param {integer|string} channelID * @param {Object} options options passed on channel add - * @returns {$.Promise<integer>} channelID of joined and added channel + * @returns {Promise<integer>} channelID of joined and added channel */ _joinAndAddChannel: function (channelID, options) { var self = this; @@ -941,7 +957,7 @@ var MailManager = AbstractService.extend({ this._toNotifyChannelFetchedIDs = []; if (_.isEmpty(channelIDs)) { // no channel to notify fetched - return $.when(); + return Promise.resolve(); } return this._rpc({ model: 'mail.channel', @@ -1098,7 +1114,7 @@ var MailManager = AbstractService.extend({ * @private * @param {string} searchVal * @param {integer} limit - * @returns {$.Promise<Object[]>} fetched partners matching 'searchVal' + * @returns {Promise<Object[]>} fetched partners matching 'searchVal' */ _searchPartnerFetch: function (searchVal, limit) { return this._rpc({ @@ -1194,12 +1210,17 @@ var MailManager = AbstractService.extend({ * (e.g. list of public channel data are stored in 'channel_channel') * @param {Object[]} [data.channel_slots[i] list of data of channel of type * `i` + * @returns {Promise} */ _updateChannelsFromServer: function (data) { var self = this; + var proms = []; _.each(data.channel_slots, function (channels) { - _.each(channels, self._addChannel.bind(self)); + _.each(channels, function (channel) { + proms.push(self._addChannel(channel)); + }); }); + return Promise.all(proms); }, /** * Update commands from mail data fetched from the server @@ -1221,11 +1242,12 @@ var MailManager = AbstractService.extend({ * @param {Array<Object[]>} result.mention_partner_suggestions list of * suggestions. * @param {integer} result.menu_id the menu ID of discuss app + * @returns {Promise} */ _updateInternalStateFromServer: function (result) { // commands are needed for channel instantiation this._updateCommandsFromServer(result); - this._updateChannelsFromServer(result); + var prom = this._updateChannelsFromServer(result); this._updateModerationSettingsFromServer(result); this._updateMailboxesFromServer(result); this._updateMailFailuresFromServer(result); @@ -1233,6 +1255,8 @@ var MailManager = AbstractService.extend({ this._mentionPartnerSuggestions = result.mention_partner_suggestions; this._discussMenuID = result.menu_id; + + return prom; }, /** * Update the mailboxes with mail data fetched from server, namely 'Inbox', diff --git a/addons/mail/static/src/js/services/mail_notification_manager.js b/addons/mail/static/src/js/services/mail_notification_manager.js index fb3527b2b90..75754f375d8 100644 --- a/addons/mail/static/src/js/services/mail_notification_manager.js +++ b/addons/mail/static/src/js/services/mail_notification_manager.js @@ -101,12 +101,12 @@ MailManager.include({ channelAlreadyInCache = !!this.getChannel(messageData.channel_ids[0]); def = this.joinChannel(messageData.channel_ids[0], { autoswitch: false }); } else { - def = $.when(); + def = Promise.resolve(); } def.then(function () { // don't increment unread if channel wasn't in cache yet as // its unread counter has just been fetched - self.addMessage(messageData, { + return self.addMessage(messageData, { showNotification: true, incrementUnread: channelAlreadyInCache }); @@ -179,18 +179,19 @@ MailManager.include({ _handleNeedactionNotification: function (messageData) { var self = this; var inbox = this.getMailbox('inbox'); - var message = this.addMessage(messageData, { + this.addMessage(messageData, { incrementUnread: true, showNotification: true, + }).then(function (message) { + inbox.incrementMailboxCounter(); + _.each(message.getThreadIDs(), function (threadID) { + var channel = self.getChannel(threadID); + if (channel) { + channel.incrementNeedactionCounter(); + } + }); + self._mailBus.trigger('update_needaction', inbox.getMailboxCounter()); }); - inbox.incrementMailboxCounter(); - _.each(message.getThreadIDs(), function (threadID) { - var channel = self.getChannel(threadID); - if (channel) { - channel.incrementNeedactionCounter(); - } - }); - this._mailBus.trigger('update_needaction', inbox.getMailboxCounter()); }, /** * Called when an activity record has been updated on the server @@ -381,8 +382,10 @@ MailManager.include({ * @param {Object} data.message data of message */ _handlePartnerMessageModeratorNotification: function (data) { - this.addMessage(data.message); - this._mailBus.trigger('update_moderation_counter'); + var self = this; + this.addMessage(data.message).then(function () { + self._mailBus.trigger('update_moderation_counter'); + }); }, /** * On receiving a notification that is specific to a user diff --git a/addons/mail/static/src/js/services/mail_status_manager.js b/addons/mail/static/src/js/services/mail_status_manager.js index 5ca151d47bf..4a2df2af94f 100644 --- a/addons/mail/static/src/js/services/mail_status_manager.js +++ b/addons/mail/static/src/js/services/mail_status_manager.js @@ -81,7 +81,7 @@ MailManager.include({ var self = this; var partnerIDs = data.partnerIDs; if (_.isEmpty(partnerIDs)) { - return $.when(); + return Promise.resolve(); } return this._rpc({ model: 'res.partner', diff --git a/addons/mail/static/src/js/services/mail_window_manager.js b/addons/mail/static/src/js/services/mail_window_manager.js index 4e481d6892a..083b46c9ff2 100644 --- a/addons/mail/static/src/js/services/mail_window_manager.js +++ b/addons/mail/static/src/js/services/mail_window_manager.js @@ -94,6 +94,7 @@ MailManager.include({ * is not open yet, and do nothing otherwise. * @param {boolean} [options.keepFoldState=false] if set to true, keep the * fold state of the thread + * @returns {Promise} */ openThreadWindow: function (threadID, options) { var self = this; @@ -108,10 +109,10 @@ MailManager.include({ return; } var threadWindow = this._getThreadWindow(threadID); - var def = $.when(); + var prom = Promise.resolve(); if (!threadWindow) { thread.isCreatingWindow = true; - def = thread.fetchMessages().then(function () { + prom = thread.fetchMessages().then(function () { threadWindow = self._makeNewThreadWindow(thread, options); self._placeNewThreadWindow(threadWindow, options.passively); return threadWindow.appendTo($(self.THREAD_WINDOW_APPENDTO)); @@ -126,12 +127,12 @@ MailManager.include({ ) { thread.markAsRead(); } - }).fail(function () { + thread.isCreatingWindow = false; + }).guardedCatch(function () { // thread window could not be open, which may happen due to // access error while fetching messages to the document. // abort opening the thread window in this case. thread.close(); - }).always(function () { thread.isCreatingWindow = false; }); } else if (!options.passively) { @@ -139,7 +140,7 @@ MailManager.include({ this._makeThreadWindowVisible(threadWindow); } } - def.then(function () { + return prom.then(function () { threadWindow.updateVisualFoldState(); }); }, @@ -155,14 +156,16 @@ MailManager.include({ */ updateThreadWindow: function (threadID, options) { var thread = this.getThread(threadID); + var prom = Promise.resolve(); if (thread) { if (thread.isDetached()) { _.extend(options, { keepFoldState: true }); - this.openThreadWindow(threadID, options); + prom = this.openThreadWindow(threadID, options); } else { this._closeThreadWindow(threadID); } } + return prom; }, //-------------------------------------------------------------------------- @@ -429,7 +432,7 @@ MailManager.include({ * * @private * @param {integer} partnerID - * @returns {$.Promise<integer>} resolved with ID of the DM chat + * @returns {Promise<integer>} resolved with ID of the DM chat */ _openAndDetachDMChat: function (partnerID) { return this._rpc({ @@ -647,10 +650,16 @@ MailManager.include({ * * @private * @param {mail.model.Channel} channel + * @param {Promise[]} proms used to synchronize async operations on the + * channel (like the rendering of its window) */ - _onNewChannel: function (channel) { + _onNewChannel: function (channel, proms) { if (channel.isDetached()) { - this.openThreadWindow(channel.getID(), { keepFoldState: true, passively: true }); + var prom = this.openThreadWindow(channel.getID(), { + keepFoldState: true, + passively: true, + }); + proms.push(prom); } else { this._closeThreadWindow(channel.getID()); } diff --git a/addons/mail/static/src/js/systray/systray_activity_menu.js b/addons/mail/static/src/js/systray/systray_activity_menu.js index 593cac91506..fae95a78c2f 100644 --- a/addons/mail/static/src/js/systray/systray_activity_menu.js +++ b/addons/mail/static/src/js/systray/systray_activity_menu.js @@ -20,7 +20,7 @@ var ActivityMenu = Widget.extend({ 'show.bs.dropdown': '_onActivityMenuShow', }, willStart: function () { - return $.when(this.call('mail_service', 'isReady')); + return Promise.resolve(this.call('mail_service', 'isReady')); }, start: function () { this._$activitiesPreview = this.$('.o_mail_systray_dropdown_items'); diff --git a/addons/mail/static/src/js/systray/systray_messaging_menu.js b/addons/mail/static/src/js/systray/systray_messaging_menu.js index df35de47174..4dacae435d4 100644 --- a/addons/mail/static/src/js/systray/systray_messaging_menu.js +++ b/addons/mail/static/src/js/systray/systray_messaging_menu.js @@ -30,7 +30,7 @@ var MessagingMenu = Widget.extend({ * @override */ willStart: function () { - return $.when(this._super.apply(this, arguments), this.call('mail_service', 'isReady')); + return Promise.all([this._super.apply(this, arguments), this.call('mail_service', 'isReady')]); }, /** * @override @@ -112,7 +112,7 @@ var MessagingMenu = Widget.extend({ }, /** * @private - * @returns {$.Promise<Object[]>} resolved with list of previews that are + * @returns {Promise<Object[]>} resolved with list of previews that are * compatible with the 'mail.Preview' template. */ _getPreviews: function () { diff --git a/addons/mail/static/src/js/thread_field.js b/addons/mail/static/src/js/thread_field.js index a3807802ad7..ab8023b06ad 100644 --- a/addons/mail/static/src/js/thread_field.js +++ b/addons/mail/static/src/js/thread_field.js @@ -57,7 +57,7 @@ var ThreadField = AbstractField.extend({ var def1 = this._threadWidget.appendTo(this.$el); var def2 = this._super.apply(this, arguments); - return this.alive($.when(def1, def2)).then(function () { + return this.alive(Promise.all([def1, def2])).then(function () { // unwrap the thread to remove an unnecessary level on div self.setElement(self._threadWidget.$el); var mailBus = self.call('mail_service', 'getMailBus'); @@ -86,7 +86,7 @@ var ThreadField = AbstractField.extend({ /** * @param {Object} message * @param {integer[]} message.partner_ids - * @return {$.Promise} + * @return {Promise} */ postMessage: function (message) { var self = this; @@ -95,9 +95,9 @@ var ThreadField = AbstractField.extend({ if (message.partner_ids.length) { self.trigger_up('reload_mail_fields', { followers: true }); } - }) - .fail(function () { + }).guardedCatch(function () { self.do_notify(_t("Sending Error"), _t("Your message has not been sent.")); + return Promise.reject(); }); }, @@ -109,7 +109,7 @@ var ThreadField = AbstractField.extend({ * @private * @param {Object} [options] * @param {boolean} [options.forceFetch] - * @return {$.Deferred} + * @return {Promise} */ _fetchAndRenderThread: function (options) { var self = this; @@ -117,7 +117,7 @@ var ThreadField = AbstractField.extend({ var thread = new CreateModeDocumentThread(); options = { isCreateMode: true }; self._threadWidget.render(thread, options); - return $.when(); + return Promise.resolve(); } else { var fetchDef = this.dp.add(this._documentThread.fetchMessages(options)); return fetchDef.then(function () { @@ -134,7 +134,7 @@ var ThreadField = AbstractField.extend({ /** * @override * @private - * @returns {$.Deferred} + * @returns {Promise} */ _render: function () { return this._fetchAndRenderThread(); diff --git a/addons/mail/static/src/js/thread_widget.js b/addons/mail/static/src/js/thread_widget.js index cbac2c3b417..56945533946 100644 --- a/addons/mail/static/src/js/thread_widget.js +++ b/addons/mail/static/src/js/thread_widget.js @@ -248,16 +248,16 @@ var ThreadWidget = Widget.extend({ */ removeMessageAndRender: function (messageID, thread, options) { var self = this; - var done = $.Deferred(); - this.$('.o_thread_message[data-message-id="' + messageID + '"]') + return new Promise(function (resolve, reject) { + self.$('.o_thread_message[data-message-id="' + messageID + '"]') .fadeOut({ done: function () { self.render(thread, options); - done.resolve(); + resolve(); }, duration: 200, }); - return done; + }); }, /** * Scroll to the bottom of the thread @@ -273,7 +273,7 @@ var ThreadWidget = Widget.extend({ * @param {boolean} [options.onlyIfNecessary] */ scrollToMessage: function (options) { - var $target = this.$('.o_thread_message[data-message-id="' + options.msgID + '"]'); + var $target = this.$('.o_thread_message[data-message-id="' + options.messageID + '"]'); if (options.onlyIfNecessary) { var delta = $target.parent().height() - $target.height(); var offset = delta < 0 ? diff --git a/addons/mail/static/src/js/thread_windows/abstract_thread_window.js b/addons/mail/static/src/js/thread_windows/abstract_thread_window.js index 300bfb11a3e..ff5acfea0d2 100644 --- a/addons/mail/static/src/js/thread_windows/abstract_thread_window.js +++ b/addons/mail/static/src/js/thread_windows/abstract_thread_window.js @@ -95,7 +95,7 @@ var AbstractThreadWindow = Widget.extend({ var def = this._threadWidget.replace(this.$('.o_thread_window_content')).then(function () { self._threadWidget.$el.on('scroll', self, self._debouncedOnScroll); }); - return $.when(this._super(), def); + return Promise.all([this._super(), def]); }, /** * @override diff --git a/addons/mail/static/src/js/thread_windows/thread_window.js b/addons/mail/static/src/js/thread_windows/thread_window.js index e5496e0bc3c..9999014a08d 100644 --- a/addons/mail/static/src/js/thread_windows/thread_window.js +++ b/addons/mail/static/src/js/thread_windows/thread_window.js @@ -83,7 +83,7 @@ var ThreadWindow = AbstractThreadWindow.extend({ } this._updateOutOfOfficeReadMoreLessButton(); - return $.when(superDef, composerDef); + return Promise.all([superDef, composerDef]); }, //-------------------------------------------------------------------------- @@ -241,7 +241,7 @@ var ThreadWindow = AbstractThreadWindow.extend({ autoFocus: true, source: function (request, response) { self.call('mail_service', 'searchPartner', request.term, 10) - .done(response); + .then(response); }, select: function (event, ui) { // remember partner ID so that we can replace this window diff --git a/addons/mail/static/src/js/views/activity/activity_model.js b/addons/mail/static/src/js/views/activity/activity_model.js index 738cdb55ca1..f5a2feee988 100644 --- a/addons/mail/static/src/js/views/activity/activity_model.js +++ b/addons/mail/static/src/js/views/activity/activity_model.js @@ -19,7 +19,7 @@ var ActivityModel = AbstractModel.extend({ * @override * @param {Object} params * @param {Array[]} params.domain - * @returns {Deferred} + * @returns {Promise} */ load: function (params) { this.domain = params.domain; @@ -31,7 +31,7 @@ var ActivityModel = AbstractModel.extend({ * @param {any} handle * @param {Object} params * @param {Array[]} params.domain - * @returns {Deferred} + * @returns {Promise} */ reload: function (handle, params) { if (params && 'domain' in params) { @@ -48,7 +48,7 @@ var ActivityModel = AbstractModel.extend({ * Fetch activity data. * * @private - * @returns {Deferred} + * @returns {Promise} */ _fetchData: function () { var self = this; diff --git a/addons/mail/static/src/js/views/activity/activity_renderer.js b/addons/mail/static/src/js/views/activity/activity_renderer.js index d139a4e84cd..75eb25367e4 100644 --- a/addons/mail/static/src/js/views/activity/activity_renderer.js +++ b/addons/mail/static/src/js/views/activity/activity_renderer.js @@ -5,8 +5,8 @@ var AbstractRenderer = require('web.AbstractRenderer'); var core = require('web.core'); var field_registry = require('web.field_registry'); -var QWeb = core.qweb; var _t = core._t; +var QWeb = core.qweb; var ActivityRenderer = AbstractRenderer.extend({ className: 'o_activity_view', @@ -125,21 +125,22 @@ var ActivityRenderer = AbstractRenderer.extend({ }; var KanbanActivity = field_registry.get('kanban_activity'); var widget = new KanbanActivity(self, "activity_ids", record, {}); - widget.appendTo($td); - // replace clock by closest deadline - var $date = $('<div>'); - var formated_date = moment(activity_group.o_closest_deadline).format('ll'); - var current_year = (new Date()).getFullYear(); - if (formated_date.endsWith(current_year)) { // Dummy logic to remove year (only if current year), we will maybe need to improve it - formated_date = formated_date.slice(0, -4); - formated_date = formated_date.replace(/( |,)*$/g, ""); - } - $date - .text(formated_date) - .addClass('o_closest_deadline'); - $td.find('a') - .empty() - .append($date); + widget.appendTo($td).then(function() { + // replace clock by closest deadline + var $date = $('<div>'); + var formated_date = moment(activity_group.o_closest_deadline).format('ll'); + var current_year = (new Date()).getFullYear(); + if (formated_date.endsWith(current_year)) { // Dummy logic to remove year (only if current year), we will maybe need to improve it + formated_date = formated_date.slice(0, -4); + formated_date = formated_date.replace(/( |,)*$/g, ""); + } + $date + .text(formated_date) + .addClass('o_closest_deadline'); + $td.find('a') + .empty() + .append($date); + }); return $td; }); var $tr = $('<tr/>', {class: 'o_data_row'}) |
