diff options
| author | Sébastien Theys <seb@odoo.com> | 2020-06-15 18:12:21 +0000 |
|---|---|---|
| committer | Sébastien Theys <seb@odoo.com> | 2020-06-15 18:12:21 +0000 |
| commit | 3fea5b2136627c1d7e83b2a5e111952fe3eb10a1 (patch) | |
| tree | 5eefad07972a87ce4689b83260f981bc87aadad4 /addons/mail/static/src/js/thread_windows/thread_window.js | |
| parent | 0a82848606151ef60eb6523e9c4c161171f214e7 (diff) | |
[REF] mail, *: refactor messaging with OWL
This commit is a significant rewriting of client-side discuss, chatter,
chat window, and messaging menu using OWL. The behavior should be broadly
the same, with some slight functional changes here and there.
From a technical standpoint, the code of messaging is mainly organized in 2
main groups of modules:
- models, which are logical entities that depict the client-side state of
messaging as a whole.
- components, which are in charge of displaying information from models.
This refactoring also introduces new JS guidelines regarding folder structure
(/static) and naming rules for JS modules.
Community PR: https://github.com/odoo/odoo/pull/39023
Enterprise PR: https://github.com/odoo/enterprise/pull/6249
Task-1914207
This PR is a collaborative work by Alexandre, Julien, Sébastien and Xavier,
with the precious help of Lucas to speed it up towards the end.
closes odoo/odoo#39023
Related: odoo/enterprise#6249
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
Co-authored-by: Alexandre Kühn <aku@odoo.com>
Co-authored-by: Julien Giannone <jgi@odoo.com>
Co-authored-by: Lucas Perais <lpe@odoo.com>
Co-authored-by: Sébastien Theys <seb@odoo.com>
Co-authored-by: Xavier Dubuc <xdu@odoo.com>
Diffstat (limited to 'addons/mail/static/src/js/thread_windows/thread_window.js')
| -rw-r--r-- | addons/mail/static/src/js/thread_windows/thread_window.js | 403 |
1 files changed, 0 insertions, 403 deletions
diff --git a/addons/mail/static/src/js/thread_windows/thread_window.js b/addons/mail/static/src/js/thread_windows/thread_window.js deleted file mode 100644 index 7280f704fa3..00000000000 --- a/addons/mail/static/src/js/thread_windows/thread_window.js +++ /dev/null @@ -1,403 +0,0 @@ -odoo.define('mail.ThreadWindow', function (require) { -"use strict"; - -var AbstractThreadWindow = require('mail.AbstractThreadWindow'); -var BasicComposer = require('mail.composer.Basic'); -var ExtendedComposer = require('mail.composer.Extended'); - -var config = require('web.config'); -var core = require('web.core'); - -var QWeb = core.qweb; -var _t = core._t; - -/** - * This is the main widget for rendering small windows for mail.model.Thread. - * Almost all instances of this class are linked to a thread. The sole - * exception is the "blank" thread window. This window let us open another - * thread window, using this "blank" thread window. - */ -var ThreadWindow = AbstractThreadWindow.extend({ - template: 'mail.ThreadWindow', - custom_events: AbstractThreadWindow.prototype.custom_events, - events: _.extend({}, AbstractThreadWindow.prototype.events, { - 'click .o_mail_thread': '_onThreadWindowFocus', - 'click .o_thread_composer': '_onThreadWindowFocus', - 'click .o_thread_window_expand': '_onClickExpand', - }), - /** - * Version of thread window that supports {mail.model.Thread} - * - * @override - * @param {mail.Manager} parent - * @param {mail.model.Thread} [thread = null] if not set, this is a "blank" - * thread window. It lets us open a DM chat by providing the name of a - * user. - * @param {Object} [options={}] - * @param {boolean} [options.passively=false] - */ - init: function (parent, thread, options) { - this._super.apply(this, arguments); - - // don't automatically mark unread messages as seen when at the bottom - // of the thread - this._passive = this.options.passively || false; - - if (!this.hasThread()) { - // remembered partner ID of "blank" thread window in order to be - // replaced with newly opened DM chat window - this.directPartnerID = null; - } - }, - - /** - * @override - */ - start: function () { - var self = this; - - var superDef = this._super().then(this._listenThreadWidget.bind(this)); - - this.call('mail_service', 'getMailBus') - .on('update_typing_partners', this, this._onUpdateTypingPartners) - .on('update_channel', this, this._onUpdateChannel); - - var composerDef; - if (!this.hasThread()) { - this._startWithoutThread(); - } else if (this.needsComposer()) { - var composer; - var composerParams = { - mentionPartnersRestricted: this._thread.getType() !== 'document_thread', - thread: this._thread, - }; - if ( - config.device.isMobile && - this._thread.isMassMailing() - ) { - composer = new ExtendedComposer(this, composerParams); - } else { - composer = new BasicComposer(this, _.extend({ isMini: true }, composerParams)); - } - composer.on('post_message', this, this._postMessage); - composer.once('input_focused', this, function () { - var commands = this._thread.getCommands(); - var partners = this._thread.getMentionPartnerSuggestions(); - composer.mentionSetCommands(commands); - composer.mentionSetPrefetchedPartners(partners); - }); - composerDef = composer.replace(this.$('.o_thread_composer')); - composerDef.then(function () { - self.$input = self.$('.o_composer_text_field'); - }); - } - return Promise.all([superDef, composerDef]); - }, - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- - - /** - * @override - */ - close: function () { - if (this.hasThread()) { - this._thread.close(); - } else { - this.trigger_up('close_blank_thread_window'); - } - }, - /** - * Set the focus on the input of this thread window - */ - focusInput: function () { - this._focusInput(); - }, - /** - * Overrides so that if this thread window is not linked to any thread - * (= "blank" thread window), displays "New message" as its title. - * - * @override - * @returns {string} - */ - getTitle: function () { - if (!this.hasThread()) { - return _t("New message"); - } - return this._super.apply(this, arguments); - }, - /** - * Tell whether the thread window is passive or not. A passive thread window - * does not auto-mark the thread as read when scrolling at the bottom. - * - * @returns {boolean} - */ - isPassive: function () { - return this._passive; - }, - /** - * Turn the thread window in active mode, so that when the bottom of the - * thread is visible, it is automatically marked as read. - */ - removePassive: function () { - this._passive = false; - }, - renderOutOfOffice: function () { - var $outOfOffice = this.$('.o_out_of_office'); - const thread = this.getThread(); - if (!thread.hasOutOfOffice()) { - if ($outOfOffice.length) { - $outOfOffice.remove(); - } - return; - } - const $newOutOfOffice = $(QWeb.render('mail.thread.OutOfOffice', { thread })); - if ($outOfOffice.length) { - $outOfOffice.replaceWith($newOutOfOffice); - } else { - $newOutOfOffice.insertAfter(this.$('.o_thread_window_header')); - } - }, - /** - * Update this thread window - * - * @param {Object} options - * @param {boolean} [options.keepBottom=false] if set, this thread window - * should scroll to the bottom if it was at the bottom before update - * @param {boolean} [options.passively=false] if set, this thread window - * becomes passive, so that it is marked as read only when the focus is - * on it. Ignore this option if the focus is already on the thread window. - */ - update: function (options) { - var self = this; - if (options.passively && !this._hasFocus()) { - this._setPassive(); - } - var bottomVisible = !this.isFolded() && - !this.isHidden() && - this.isAtBottom(); - if (bottomVisible && !this.isPassive()) { - this._thread.markAsRead(); - } - this._thread.fetchMessages().then(function () { - self.render(); - if (bottomVisible && options.keepBottom) { - self.scrollToBottom(); - } - }); - }, - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - /** - * @override - * @private - */ - _getHeaderRenderingOptions: function () { - var options = this._super.apply(this, arguments); - if (this._thread.getType() === 'document_thread') { - options.expandTitle = _t("Open document"); - } else { - options.expandTitle = _t("Open in Discuss"); - } - return options; - }, - /** - * Listen on thread widget events - * - * @private - */ - _listenThreadWidget: function () { - this._threadWidget - .on('redirect', this, this._onRedirect) - .on('redirect_to_channel', this, this._onRedirectToChannel) - .on('toggle_star_status', this, this._onToggleStarStatus); - }, - /** - * Open this thread window. - * This private method exists only for the purpose of providing a callback - * function on redirect to a partner. - * - * @private - */ - _open: function (channelID) { - this.call('mail_service', 'openThreadWindow', channelID || this.getID()); - }, - /** - * Set the thread window in passive mode, so that new received message will - * keep the thread window as unread until there is focus on the thread - * window. - * - * @private - */ - _setPassive: function () { - this._passive = true; - }, - /** - * @private - */ - _startWithoutThread: function () { - var self = this; - this.$el.addClass('o_thread_less'); - this.$('.o_thread_search_input input') - .autocomplete({ - autoFocus: true, - source: function (request, response) { - self.call('mail_service', 'searchPartner', request.term, 10) - .then(response); - }, - select: function (event, ui) { - // remember partner ID so that we can replace this window - // with new DM chat window - var partnerID = ui.item.id; - self.directPartnerID = partnerID; - self.call('mail_service', 'openDMChatWindowFromBlankThreadWindow', partnerID); - }, - }) - .focus(); - }, - - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- - - /** - * Expand the thread, which will open the thread in discuss. - * - * If there is no thread linked to this window, it means this is the "blank" - * thread window, therefore it opens discuss with the default thread (which - * should be the mailbox 'inbox') - * - * @private - * @param {MouseEvent} ev - */ - _onClickExpand: _.debounce(function (ev) { - var self = this; - ev.preventDefault(); - if (this.hasThread() && this._thread.getType() === 'document_thread') { - this.do_action({ - type: 'ir.actions.act_window', - res_model: this._thread.getDocumentModel(), - views: [[false, 'form']], - res_id: this._thread.getDocumentID(), - }); - } else { - this.do_action('mail.action_discuss', { - clear_breadcrumbs: false, - active_id: this.hasThread() ? this._getThreadID() : undefined, - on_reverse_breadcrumb: function () { - var mailBus = self.call('mail_service', 'getMailBus'); - if (mailBus) { - mailBus.trigger('discuss_open', false); - } - }, - }); - } - }, 1000, true), - /** - * @override - * @private - * @param {KeyboardEvent} ev - * - * Override _onKeydown to only prevent jquery's blockUI to cancel event, - * but without sending the message on ENTER keydown as this is handled by - * the BasicComposer - */ - _onKeydown: function (ev) { - ev.stopPropagation(); - }, - /** - * callback function receives the threadID linked to resModel and resID - * Note: this callback function is only called for 'res.partner' - * - * @private - * @param {string} resModel - * @param {integer} resID - */ - _onRedirect: function (resModel, resID) { - var callback = this._open.bind(this); - this.call('mail_service', 'redirect', resModel, resID, callback); - }, - /** - * @private - * @param {integer} channelID - */ - _onRedirectToChannel: function (channelID) { - var self = this; - var thread = this.call('mail_service', 'getThread', channelID); - if (!thread) { - this.call('mail_service', 'joinChannel', channelID) - .then(function (channelID) { - var channel = self.call('mail_service', 'getThread', channelID); - channel.detach(); - }); - } else { - this.toggleFold(false); - } - }, - /** - * Override it so that passive thread windows do not mark thread as read - * on scroll - * - * @override - * @private - */ - _onScroll: function () { - if (this.isPassive()) { - return; - } - return this._super.apply(this, arguments); - }, - /** - * Called when focusing the thread window (click on thread or composer) - * - * @private - */ - _onThreadWindowFocus: function () { - if (this.isPassive()) { - this.removePassive(); - this._thread.markAsRead(); - } - }, - /** - * @private - * @param {integer} messageID - */ - _onToggleStarStatus: function (messageID) { - var message = this.call('mail_service', 'getMessage', messageID); - message.toggleStarStatus(); - }, - /** - * @private - * @param {integer|string} threadID - */ - _onUpdateTypingPartners: function (threadID) { - if (!this.hasThread()) { - return; - } - if (this._thread.getID() !== threadID) { - return; - } - this.renderHeader(); - }, - /** - * @private - * @param {integer} channelID - */ - _onUpdateChannel: function (channelID) { - if (!this.hasThread()) { - return; - } - if (this._thread.getID() !== channelID) { - return; - } - this.render(); - }, -}); - -return ThreadWindow; - -}); |
