summaryrefslogtreecommitdiff
path: root/addons/mail/static/src/js/thread_windows
diff options
context:
space:
mode:
authorSébastien Theys <seb@odoo.com>2020-06-15 18:12:21 +0000
committerSébastien Theys <seb@odoo.com>2020-06-15 18:12:21 +0000
commit3fea5b2136627c1d7e83b2a5e111952fe3eb10a1 (patch)
tree5eefad07972a87ce4689b83260f981bc87aadad4 /addons/mail/static/src/js/thread_windows
parent0a82848606151ef60eb6523e9c4c161171f214e7 (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')
-rw-r--r--addons/mail/static/src/js/thread_windows/abstract_thread_window.js519
-rw-r--r--addons/mail/static/src/js/thread_windows/thread_window.js403
2 files changed, 0 insertions, 922 deletions
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
deleted file mode 100644
index 7e39f9c7e2e..00000000000
--- a/addons/mail/static/src/js/thread_windows/abstract_thread_window.js
+++ /dev/null
@@ -1,519 +0,0 @@
-odoo.define('mail.AbstractThreadWindow', function (require) {
-"use strict";
-
-var ThreadWidget = require('mail.widget.Thread');
-
-var config = require('web.config');
-var core = require('web.core');
-var Widget = require('web.Widget');
-
-var QWeb = core.qweb;
-var _t = core._t;
-
-/**
- * This is an abstract widget for rendering thread windows.
- *
- * It contains logic that are shared between mail.ThreadWindow and
- * im_livechat.WebsiteLivechatWindow.
- *
- * The reason for having two different implementation of thread windows is
- * that mail.ThreadWindow makes use of mail.Manager, which is used in the
- * backend, while im_livechat.WebsiteLivechatWindow must work without this
- * mail service.
- */
-var AbstractThreadWindow = Widget.extend({
- template: 'mail.AbstractThreadWindow',
- custom_events: {
- escape_pressed: '_onEscapePressed',
- document_viewer_closed: '_onDocumentViewerClose',
- },
- events: {
- 'click .o_thread_window_close': '_onClickClose',
- 'click .o_thread_window_title': '_onClickFold',
- 'click .o_composer_text_field': '_onComposerClick',
- 'click .o_mail_thread': '_onThreadWindowClicked',
- 'keydown .o_composer_text_field': '_onKeydown',
- 'keypress .o_composer_text_field': '_onKeypress',
- },
- FOLD_ANIMATION_DURATION: 200, // duration in ms for (un)fold transition
- HEIGHT_OPEN: '400px', // height in px of thread window when open
- HEIGHT_FOLDED: '34px', // height, in px, of thread window when folded
- /**
- * Children of this class must make use of `thread`, which is an object that
- * represent the thread that is linked to this thread window.
- *
- * If no thread is provided, this will represent the "blank" thread window.
- *
- * @abstract
- * @param {Widget} parent
- * @param {mail.model.AbstractThread} [thread=null] the thread that this
- * thread window is linked to. If not set, it is the "blank" thread
- * window.
- * @param {Object} [options={}]
- * @param {mail.model.AbstractThread} [options.thread]
- */
- init: function (parent, thread, options) {
- this._super(parent);
-
- this.options = _.defaults(options || {}, {
- autofocus: true,
- displayStars: true,
- displayReplyIcons: false,
- displayNotificationIcons: false,
- placeholder: _t("Say something"),
- });
-
- this._hidden = false;
- this._thread = thread || null;
-
- this._debouncedOnScroll = _.debounce(this._onScroll.bind(this), 100);
-
- if (!this.hasThread()) {
- // internal fold state of thread window without any thread
- this._folded = false;
- }
- },
- start: function () {
- var self = this;
- this.$input = this.$('.o_composer_text_field');
- this.$header = this.$('.o_thread_window_header');
- var options = {
- displayMarkAsRead: false,
- displayStars: this.options.displayStars,
- };
- if (this._thread && this._thread._type === 'document_thread') {
- options.displayDocumentLinks = false;
- }
- this._threadWidget = new ThreadWidget(this, options);
-
- // animate the (un)folding of thread windows
- this.$el.css({transition: 'height ' + this.FOLD_ANIMATION_DURATION + 'ms linear'});
- if (this.isFolded()) {
- this.$el.css('height', this.HEIGHT_FOLDED);
- } else if (this.options.autofocus) {
- this._focusInput();
- }
- if (!config.device.isMobile) {
- var margin_dir = _t.database.parameters.direction === "rtl" ? "margin-left" : "margin-right";
- this.$el.css(margin_dir, $.position.scrollbarWidth());
- }
- var def = this._threadWidget.replace(this.$('.o_thread_window_content')).then(function () {
- self._threadWidget.$el.on('scroll', self, self._debouncedOnScroll);
- });
- return Promise.all([this._super(), def]);
- },
- /**
- * @override
- */
- do_hide: function () {
- this._hidden = true;
- this._super.apply(this, arguments);
- },
- /**
- * @override
- */
- do_show: function () {
- this._hidden = false;
- this._super.apply(this, arguments);
- },
- /**
- * @override
- */
- do_toggle: function (display) {
- this._hidden = _.isBoolean(display) ? !display : !this._hidden;
- this._super.apply(this, arguments);
- },
-
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * Close this window
- *
- * @abstract
- */
- close: function () {},
- /**
- * Get the ID of the thread window, which is equivalent to the ID of the
- * thread related to this window
- *
- * @returns {integer|string}
- */
- getID: function () {
- return this._getThreadID();
- },
- /**
- * @returns {mail.model.Thread|undefined}
- */
- getThread: function () {
- if (!this.hasThread()) {
- return undefined;
- }
- return this._thread;
- },
- /**
- * Get the status of the thread, such as the im status of a DM chat
- * ('online', 'offline', etc.). If this window has no thread, returns
- * `undefined`.
- *
- * @returns {string|undefined}
- */
- getThreadStatus: function () {
- if (!this.hasThread()) {
- return undefined;
- }
- return this._thread.getStatus();
- },
- /**
- * Get the title of the thread window, which usually contains the name of
- * the thread.
- *
- * @returns {string}
- */
- getTitle: function () {
- if (!this.hasThread()) {
- return _t("Undefined");
- }
- return this._thread.getTitle();
- },
- /**
- * Get the unread counter of the related thread. If there are no thread
- * linked to this window, returns 0.
- *
- * @returns {integer}
- */
- getUnreadCounter: function () {
- if (!this.hasThread()) {
- return 0;
- }
- return this._thread.getUnreadCounter();
- },
- /**
- * States whether this thread window is related to a thread or not.
- *
- * This is useful in order to provide specific behaviour for thread windows
- * without any thread, e.g. let them open a thread from this "blank" thread
- * window.
- *
- * @returns {boolean}
- */
- hasThread: function () {
- return !! this._thread;
- },
- /**
- * Tells whether the bottom of the thread in the thread window is visible
- * or not.
- *
- * @returns {boolean}
- */
- isAtBottom: function () {
- return this._threadWidget.isAtBottom();
- },
- /**
- * State whether the related thread is folded or not. If there are no
- * thread related to this window, it means this is the "blank" thread
- * window, therefore we use the internal folded state.
- *
- * @returns {boolean}
- */
- isFolded: function () {
- if (!this.hasThread()) {
- return this._folded;
- }
- return this._thread.isFolded();
- },
- /**
- * States whether the current environment is in mobile or not. This is
- * useful in order to customize the template rendering for mobile view.
- *
- * @returns {boolean}
- */
- isMobile: function () {
- return config.device.isMobile;
- },
- /**
- * States whether the thread window is hidden or not.
- *
- * @returns {boolean}
- */
- isHidden: function () {
- return this._hidden;
- },
- /**
- * States whether the input of the thread window should be displayed or not.
- * By default, any thread window with a thread needs a composer.
- *
- * @returns {boolean}
- */
- needsComposer: function () {
- return this.hasThread();
- },
- /**
- * Render the thread window
- */
- render: function () {
- this.renderHeader();
- if (this.hasThread()) {
- this._threadWidget.render(this._thread, { displayLoadMore: false });
- }
- },
- /**
- * Render the header of this thread window.
- * This is useful when some information on the header have be updated such
- * as the status or the title of the thread that have changed.
- *
- * @private
- */
- renderHeader: function () {
- var options = this._getHeaderRenderingOptions();
- this.$header.html(
- QWeb.render('mail.AbstractThreadWindow.HeaderContent', options));
- },
- /**
- * Scroll to the bottom of the thread in the thread window
- */
- scrollToBottom: function () {
- this._threadWidget.scrollToBottom();
- },
- /**
- * Toggle the fold state of this thread window. Also update the fold state
- * of the thread model. If the boolean parameter `folded` is provided, it
- * folds/unfolds the window when it is set/unset.
- *
- * @param {boolean} [folded] if not a boolean, toggle the fold state.
- * Otherwise, fold/unfold the window if set/unset.
- */
- toggleFold: function (folded) {
- if (!_.isBoolean(folded)) {
- folded = !this.isFolded();
- }
- this._updateThreadFoldState(folded);
- },
- /**
- * Update the visual state of the window so that it matched the internal
- * fold state. This is useful in case the related thread has its fold state
- * that has been changed.
- */
- updateVisualFoldState: function () {
- if (!this.isFolded()) {
- this._threadWidget.scrollToBottom();
- if (this.options.autofocus) {
- this._focusInput();
- }
- }
- var height = this.isFolded() ? this.HEIGHT_FOLDED : this.HEIGHT_OPEN;
- this.$el.css({height: height});
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * Set the focus on the composer of the thread window. This operation is
- * ignored in mobile context.
- *
- * @private
- * Set the focus on the input of the window
- */
- _focusInput: function () {
- if (
- config.device.touch &&
- config.device.size_class <= config.device.SIZES.SM
- ) {
- return;
- }
- this.$input.focus();
- },
- /**
- * Returns the options used by the rendering of the window's header
- *
- * @private
- * @returns {Object}
- */
- _getHeaderRenderingOptions: function () {
- return {
- status: this.getThreadStatus(),
- thread: this.getThread(),
- title: this.getTitle(),
- unreadCounter: this.getUnreadCounter(),
- widget: this,
- };
- },
- /**
- * Get the ID of the related thread.
- * If this window is not related to a thread, it means this is the "blank"
- * thread window, therefore it returns "_blank" as its ID.
- *
- * @private
- * @returns {integer|string} the threadID, or '_blank' for the window that
- * is not related to any thread.
- */
- _getThreadID: function () {
- if (!this.hasThread()) {
- return '_blank';
- }
- return this._thread.getID();
- },
- /**
- * Tells whether there is focus on this thread. Note that a thread that has
- * the focus means the input has focus.
- *
- * @private
- * @returns {boolean}
- */
- _hasFocus: function () {
- return this.$input.is(':focus');
- },
- /**
- * Post a message on this thread window, and auto-scroll to the bottom of
- * the thread.
- *
- * @private
- * @param {Object} messageData
- */
- _postMessage: function (messageData) {
- var self = this;
- if (!this.hasThread()) {
- return;
- }
- this._thread.postMessage(messageData)
- .then(function () {
- self._threadWidget.scrollToBottom();
- });
- },
- /**
- * Update the fold state of the thread.
- *
- * This function is called when toggling the fold state of this window.
- * If there is no thread linked to this window, it means this is the
- * "blank" thread window, therefore we use the internal state 'folded'
- *
- * @private
- * @param {boolean} folded
- */
- _updateThreadFoldState: function (folded) {
- if (this.hasThread()) {
- this._thread.fold(folded);
- } else {
- this._folded = folded;
- this.updateVisualFoldState();
- }
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * Close the thread window.
- * Mark the thread as read if the thread window was open.
- *
- * @private
- * @param {MouseEvent} ev
- */
- _onClickClose: function (ev) {
- ev.stopPropagation();
- ev.preventDefault();
- if (
- this.hasThread() &&
- this._thread.getUnreadCounter() > 0 &&
- !this.isFolded()
- ) {
- this._thread.markAsRead();
- }
- this.close();
- },
- /**
- * Fold/unfold the thread window.
- * Also mark the thread as read.
- *
- * @private
- */
- _onClickFold: function () {
- if (!config.device.isMobile) {
- this.toggleFold();
- }
- },
- /**
- * Called when the composer is clicked -> forces focus on input even if
- * jquery's blockUI is enabled.
- *
- * @private
- * @param {Event} ev
- */
- _onComposerClick: function (ev) {
- if ($(ev.target).closest('a, button').length) {
- return;
- }
- this._focusInput();
- },
- /**
- * @private
- */
- _onDocumentViewerClose: function () {
- this._focusInput();
- },
- /**
- * @private
- */
- _onEscapePressed: function () {
- if (!this.isFolded()) {
- this.close();
- }
- },
- /**
- * Called when typing something on the composer of this thread window.
- *
- * @private
- * @param {KeyboardEvent} ev
- */
- _onKeydown: function (ev) {
- ev.stopPropagation(); // to prevent jquery's blockUI to cancel event
- // ENTER key (avoid requiring jquery ui for external livechat)
- if (ev.which === 13) {
- var content = _.str.trim(this.$input.val());
- var messageData = {
- content: content,
- attachment_ids: [],
- partner_ids: [],
- };
- this.$input.val('');
- if (content) {
- this._postMessage(messageData);
- }
- }
- },
- /**
- * @private
- * @param {KeyboardEvent} ev
- */
- _onKeypress: function (ev) {
- ev.stopPropagation(); // to prevent jquery's blockUI to cancel event
- },
- /**
- * @private
- */
- _onScroll: function () {
- if (this.hasThread() && this.isAtBottom()) {
- this._thread.markAsRead();
- }
- },
- /**
- * When a thread window is clicked on, we want to give the focus to the main
- * input. An exception is made when the user is selecting something.
- *
- * @private
- */
- _onThreadWindowClicked: function () {
- var selectObj = window.getSelection();
- if (selectObj.anchorOffset === selectObj.focusOffset) {
- this.$input.focus();
- }
- },
-});
-
-return AbstractThreadWindow;
-
-});
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;
-
-});