diff options
Diffstat (limited to 'addons/mail/static/src/js/composers/basic_composer.js')
| -rw-r--r-- | addons/mail/static/src/js/composers/basic_composer.js | 864 |
1 files changed, 0 insertions, 864 deletions
diff --git a/addons/mail/static/src/js/composers/basic_composer.js b/addons/mail/static/src/js/composers/basic_composer.js deleted file mode 100644 index ffde77eac9a..00000000000 --- a/addons/mail/static/src/js/composers/basic_composer.js +++ /dev/null @@ -1,864 +0,0 @@ -odoo.define('mail.composer.Basic', function (require) { -"use strict"; - -var emojis = require('mail.emojis'); -var MentionManager = require('mail.composer.MentionManager'); -var DocumentViewer = require('mail.DocumentViewer'); -var utils = require('web.utils'); - -var config = require('web.config'); -var core = require('web.core'); -var data = require('web.data'); -var dom = require('web.dom'); -var session = require('web.session'); -var Widget = require('web.Widget'); - -var QWeb = core.qweb; -var _t = core._t; - -var BasicComposer = Widget.extend({ - template: "mail.Composer", - events: { - 'change input.o_input_file': '_onAttachmentChange', - 'click .o_attachment_delete': '_onAttachmentDelete', - 'click .o_attachment_download': '_onAttachmentDownload', - 'click .o_attachment_view': '_onAttachmentView', - 'click .o_composer_button_add_attachment': '_onClickAddAttachment', - 'click .o_composer_button_emoji': '_onEmojiButtonClick', - 'focusout .o_composer_button_emoji': '_onEmojiButtonFocusout', - 'mousedown .o_mail_emoji_container .o_mail_emoji': '_onEmojiImageClick', - 'focus .o_mail_emoji_container .o_mail_emoji': '_onEmojiImageFocus', - 'dragover .o_file_drop_zone_container': '_onFileDragover', - 'drop .o_file_drop_zone_container': '_onFileDrop', - 'input .o_input': '_onInput', - 'keydown .o_composer_input textarea': '_onKeydown', - 'keyup .o_composer_input': '_onKeyup', - 'click .o_composer_button_send': '_sendMessage', - }, - // RPCs done to fetch the mention suggestions are throttled with the - // following value - MENTION_THROTTLE: 200, - - init: function (parent, options) { - this._super.apply(this, arguments); - this.options = _.defaults(options || {}, { - commandsEnabled: true, - context: {}, - inputMinHeight: 28, - mentionFetchLimit: 8, - // set to true to only suggest prefetched partners - mentionPartnersRestricted: false, - sendText: _t("Send"), - defaultBody: '', - defaultMentionSelections: {}, - isMobile: config.device.isMobile - }); - this.context = this.options.context; - - // Attachments - this._attachmentDataSet = new data.DataSetSearch(this, 'ir.attachment', this.context); - this.fileuploadID = _.uniqueId('o_chat_fileupload'); - this.set('attachment_ids', options.attachmentIds || []); - - // Mention - this._mentionManager = new MentionManager(this); - this._mentionManager.register({ - delimiter: '@', - fetchCallback: this._mentionFetchPartners.bind(this), - generateLinks: true, - model: 'res.partner', - redirectClassname: 'o_mail_redirect', - selection: this.options.defaultMentionSelections['@'], - suggestionTemplate: 'mail.MentionSuggestions.Partner', - }); - this._mentionManager.register({ - delimiter: '#', - fetchCallback: this._mentionFetchChannels.bind(this), - generateLinks: true, - model: 'mail.channel', - redirectClassname: 'o_channel_redirect', - selection: this.options.defaultMentionSelections['#'], - suggestionTemplate: 'mail.MentionSuggestions.Channel', - }); - this._mentionManager.register({ - delimiter: ':', - fetchCallback: this._mentionGetCannedResponses.bind(this), - selection: this.options.defaultMentionSelections[':'], - suggestionTemplate: 'mail.MentionSuggestions.CannedResponse', - }); - if (this.options.commandsEnabled) { - this._mentionManager.register({ - beginningOnly: true, - delimiter: '/', - fetchCallback: this._mentionGetCommands.bind(this), - selection: this.options.defaultMentionSelections['/'], - suggestionTemplate: 'mail.MentionSuggestions.Command', - }); - } - - this.isMini = options.isMini; - - this.avatarURL = session.uid > 0 ? session.url('/web/image', { - model: 'res.users', - field: 'image_128', - id: session.uid, - }) : '/web/static/src/img/user_menu_avatar.png'; - }, - start: function () { - var self = this; - - this._$attachmentButton = this.$('.o_composer_button_add_attachment'); - this._$attachmentsList = this.$('.o_composer_attachments_list'); - this.$input = this.$('.o_composer_input textarea'); - this.$input.focus(function () { - self.trigger('input_focused'); - }); - this.$input.val(this.options.defaultBody); - dom.autoresize(this.$input, { - parent: this, - min_height: this.options.inputMinHeight - }); - - // Attachments - this._renderAttachments(); - $(window).on(this.fileuploadID, this._onAttachmentLoaded.bind(this)); - this.on('change:attachment_ids', this, this._renderAttachments); - - this.call('mail_service', 'getMailBus') - .on('update_typing_partners', this, this._onUpdateTypingPartners); - - // Mention - var prependPromise = this._mentionManager.prependTo(this.$('.o_composer')); - - // Drag-Drop files - // Allowing body to detect dragenter and dragleave for display - var $body = $('body'); - this._dropZoneNS = _.uniqueId('o_dz_'); // For event namespace used when multiple chat window is open - $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 Promise.all([this._super(), prependPromise]); - }, - destroy: function () { - $("body").off('dragleave.' + this._dropZoneNS); - $("body").off('dragover.' + this._dropZoneNS); - $("body").off('drop.' + this._dropZoneNS); - $(window).off(this.fileuploadID); - return this._super.apply(this, arguments); - }, - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- - - /** - * Empty input, selected partners and attachments - */ - clearComposer: function () { - this.$input.val(''); - this._mentionManager.resetSelections(); - this.set('attachment_ids', []); - }, - /** - * Set the focus on the input - */ - focus: function () { - this.$input.focus(); - }, - /** - * Get cursor position and selection - * - * @returns {Object} a current cursor position as { start: {integer}, end: {integer} } - */ - getSelectionPositions: function () { - var InputElement = this.$input.get(0); - return InputElement ? dom.getSelectionRange(InputElement) : { start: 0, end: 0 }; - }, - /** - * Get the state of the composer. - * - * This is useful in order to (re)store its state when switchng to another - * thread with a composer, and coming back to the thread with this composer. - * - * @returns {Object} - */ - getState: function () { - return { - attachments: this.get('attachment_ids'), - text: this.$input.val(), - }; - }, - /** - * @returns {integer} it returns length of attachment and trim content of - * input box to check for empty. - */ - isEmpty: function () { - return !this.$input.val().trim() && - !this.$('.o_attachments').children().length; - }, - /** - * Set the state of the composer. - * - * This is useful in order to (re)store its state when switching to another - * thread with a composer, and coming back to the thread with this composer. - * - * @param {Object} state - * @param {Array} state.attachments - * @param {string} state.text - */ - setState: function (state) { - this.set('attachment_ids', state.attachments); - this.$input.val(state.text); - }, - /** - * Set the thread that this composer refers to. - * - * @param {mail.model.Thread} thread - */ - setThread: function (thread) { - this.options.thread = thread; - }, - /** - * Set the list of command suggestions on the thread. - * - * When the user partially types a command, matching commands are displayed - * to the user (@see _mentionGetCommands). - * - * @param {Object[]} commands - */ - mentionSetCommands: function (commands) { - this._mentionCommands = commands; - }, - /** - * Set the list of partner mentions suggestions that has been prefetched. - * - * When the user partially types a mention, matching prefetched partners are - * displayed to the user. If none of them match, then it will fetch for more - * partner suggestions (@see _mentionFetchPartners). - * - * @param {Promise<Object[]>} prefetchedPartners list of list of - * prefetched partners. - */ - mentionSetPrefetchedPartners: function (prefetchedPartners) { - this._mentionPrefetchedPartners = prefetchedPartners; - }, - /** - * @returns {Object} containing listener selections, grouped by delimiter - * as key of the object. - */ - getMentionListenerSelections: function () { - return this._mentionManager.getListenerSelections(); - }, - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - /** - * @private - */ - _clearComposerOnSend: function () { - this.clearComposer(); - }, - /** - * @private - */ - _doCheckAttachmentUpload: function () { - if (_.find(this.get('attachment_ids'), function (file) { return file.upload; })) { - this.do_warn(false, _t("Please wait while the file is uploading...")); - return false; - } - return true; - }, - /** - * Hides the emojis container. - * - * @private - */ - _hideEmojis: function () { - this._$emojisContainer.remove(); - }, - /** - * Making sure that dragging content is external files. - * Ignoring other content draging like text. - * - * @private - * @param {DataTransfer} dataTransfer - * @returns {boolean} - */ - _isDragSourceExternalFile: function (dataTransfer) { - var DragDataType = dataTransfer.types; - if (DragDataType.constructor === DOMStringList) { - return DragDataType.contains('Files'); - } - if (DragDataType.constructor === Array) { - return DragDataType.indexOf('Files') !== -1; - } - return false; - }, - /** - * @private - * @param {string} search - * @returns {Promise<Object[]>} - */ - _mentionGetCannedResponses: function (search) { - var self = this; - 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; - }, - /** - * @private - * @param {string} search - * @returns {Array} - */ - _mentionGetCommands: function (search) { - var searchRegexp = new RegExp(_.str.escapeRegExp(utils.unaccent(search)), 'i'); - return _.filter(this._mentionCommands, function (command) { - return searchRegexp.test(command.name); - }).slice(0, this.options.mentionFetchLimit); - }, - /** - * @private - * @param {string} search - */ - _mentionFetchChannels: function (search) { - return this._mentionFetchThrottled('mail.channel', 'get_mention_suggestions', { - limit: this.options.mentionFetchLimit, - search: search, - }).then(function (suggestions) { - return _.partition(suggestions, function (suggestion) { - return _.contains(['public', 'groups'], suggestion.public); - }); - }); - }, - /** - * @private - * @param {string} search - */ - _mentionFetchPartners: function (search) { - var self = this; - return Promise.resolve(this._mentionPrefetchedPartners).then(function (prefetchedPartners) { - // filter prefetched partners with the given search string - var suggestions = []; - var limit = self.options.mentionFetchLimit; - var searchRegexp = new RegExp(_.str.escapeRegExp(utils.unaccent(search)), 'i'); - _.each(prefetchedPartners, function (partners) { - if (limit > 0) { - var filteredPartners = _.filter(partners, function (partner) { - return partner.email && searchRegexp.test(partner.email) || - partner.name && searchRegexp.test(utils.unaccent(partner.name)); - }); - if (filteredPartners.length) { - suggestions.push(filteredPartners.slice(0, limit)); - limit -= filteredPartners.length; - } - } - }); - if (!suggestions.length && !self.options.mentionPartnersRestricted) { - // no result found among prefetched partners, fetch other suggestions - suggestions = self._mentionFetchThrottled( - 'res.partner', - 'get_mention_suggestions', - { limit: limit, search: search } - ); - } - return Promise.resolve(suggestions).then(function (suggestions) { - //add im_status on suggestions - _.each(suggestions, function (suggestionsSet) { - _.each(suggestionsSet, function (suggestion) { - suggestion.im_status = self.call('mail_service', 'getImStatus', { partnerID: suggestion.id }); - }); - }); - return suggestions; - }); - }); - }, - /** - * @private - * @param {string} model - * @param {string} method - * @param {Object} kwargs - * @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 - 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) { - resolve(results); - }); - }, self.MENTION_THROTTLE); - }); - }, - /** - * @private - * @returns {Promise} - */ - _preprocessMessage: function () { - // Return a promise as this function is extended with asynchronous - // behavior for the chatter composer - - //Removing unwanted extra spaces from message - var value = _.escape(this.$input.val()).trim(); - value = value.replace(/(\r|\n){2,}/g, '<br/><br/>'); - value = value.replace(/(\r|\n)/g, '<br/>'); - - // prevent html space collapsing - value = value.replace(/ /g, ' ').replace(/([^>]) ([^<])/g, '$1 $2'); - var commands = this.options.commandsEnabled ? - this._mentionManager.getListenerSelection('/') : - []; - return Promise.resolve({ - content: this._mentionManager.generateLinks(value), - attachment_ids: _.pluck(this.get('attachment_ids'), 'id'), - partner_ids: _.uniq(_.pluck(this._mentionManager.getListenerSelection('@'), 'id')), - canned_response_ids: _.uniq(_.pluck(this._mentionManager.getListenerSelections()[':'], 'id')), - channel_ids: _.uniq(_.pluck(this._mentionManager.getListenerSelection('#'), 'id')), - command: commands.length > 0 ? commands[0].name : undefined, - }); - }, - /** - * Allowing to upload attachment with file selector as well drag drop feature. - * - * @private - * @param {Array<File>} params.files - * @param {boolean} params.submitForm [optional] - */ - _processAttachmentChange: function (params) { - var self = this; - var attachments = this.get('attachment_ids'); - var files = params.files; - var submitForm = params.submitForm; - var $form = this.$('form.o_form_binary_form'); - - /** - * makes a new formData as formData.delete() is not supported by IE or Safari Mobile. - * - * @return {FormData} - */ - function makeFormDataWithoutUfile() { - var newFormData = new window.FormData(); - $form.find('input').each(function (index, input) { - if (input.name !== 'ufile') { - newFormData.append(input.name, input.value); - } - }); - return newFormData; - } - - _.each(files, function (file) { - var attachment = _.findWhere(attachments, { - name: file.name, - size: file.size - }); - // if the files already exits, delete the file before upload - if (attachment) { - self._attachmentDataSet.unlink([attachment.id]); - attachments = _.without(attachments, attachment); - } - }); - if (submitForm) { - $form.submit(); - this._$attachmentButton.prop('disabled', true); - } else { - _.each(files, function (file) { - var formData = makeFormDataWithoutUfile(); - formData.append("ufile", file, file.name); - $.ajax({ - url: $form.attr("action"), - type: "POST", - enctype: 'multipart/form-data', - processData: false, - contentType: false, - data: formData, - success: function (result) { - var $el = $(result); - $.globalEval($el.contents().text()); - } - }); - }); - } - var uploadAttachments = _.map(files, function (file){ - return { - id: 0, - name: file.name, - filename: file.name, - url: '', - upload: true, - mimetype: '', - }; - }); - attachments = attachments.concat(uploadAttachments); - this.set('attachment_ids', attachments); - }, - /** - * @private - */ - _renderAttachments: function () { - this._$attachmentsList.html(QWeb.render('mail.composer.Attachments', { - attachments: this.get('attachment_ids'), - })); - }, - /** - * @private - */ - _sendMessage: function () { - if (this.isEmpty() || !this._doCheckAttachmentUpload()) { - return; - } - - clearTimeout(this._cannedTimeout); - var self = this; - this._preprocessMessage().then(function (message) { - self.trigger('post_message', message); - self._clearComposerOnSend(); - self.$input.focus(); - }); - }, - /** - * Send the message on ENTER, but go to new line on SHIFT+ENTER - * - * @private - */ - _shouldSend: function (ev) { - return !ev.shiftKey; - }, - - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- - - /** - * @private - * @param {jQuery.Event} ev - */ - _onAttachmentChange: function (ev) { - this._processAttachmentChange({ - files: ev.currentTarget.files, - submitForm: true - }); - ev.target.value = ""; - }, - /** - * @private - * @param {MouseEvent} ev - */ - _onAttachmentDelete: function (ev){ - ev.stopPropagation(); - var self = this; - var attachmentID = $(ev.currentTarget).data('id'); - if (attachmentID) { - var attachments = []; - _.each(this.get('attachment_ids'), function (attachment){ - if (attachmentID !== attachment.id) { - attachments.push(attachment); - } else { - self._attachmentDataSet.unlink([attachmentID]); - } - }); - this.set('attachment_ids', attachments); - this.$('input.o_input_file').val(''); - } - }, - /** - * @private - * @param {MouseEvent} ev - */ - _onAttachmentDownload: function (ev) { - ev.stopPropagation(); - }, - /** - * @private - */ - _onAttachmentLoaded: function () { - var attachments = this.get('attachment_ids'); - var files = Array.prototype.slice.call(arguments, 1); - - _.each(files, function (file) { - if (file.error || !file.id){ - this.do_warn(file.error); - attachments = _.filter(attachments, function (attachment) { - return !attachment.upload; - }); - } else { - var attachment = _.findWhere(attachments, { filename: file.filename, upload: true }); - if (attachment) { - attachments = _.without(attachments, attachment); - attachments.push({ - id: file.id, - name: file.name || file.filename, - filename: file.filename, - mimetype: file.mimetype, - size: file.size, - url: session.url('/web/content', { id: file.id, download: true }), - }); - } - } - }.bind(this)); - this.set('attachment_ids', attachments); - this._$attachmentButton.prop('disabled', false); - }, - /** - * @private - * @param {MouseEvent} ev - */ - _onAttachmentView: function (ev) { - var activeAttachmentID = $(ev.currentTarget).data('id'); - var attachments = this.get('attachment_ids'); - if (activeAttachmentID) { - var attachmentViewer = new DocumentViewer(this, attachments, activeAttachmentID); - attachmentViewer.appendTo($('body')); - } - }, - /** - * @private - * @param {MouseEvent} ev - */ - _onBodyFileDragLeave: function (ev) { - // On every dragenter chain created with parent child element - // That's why dragleave is fired every time when a child elemnt is hovered - // so here we hide dropzone based on mouse position - if (ev.originalEvent.clientX <= 0 - || ev.originalEvent.clientY <= 0 - || ev.originalEvent.clientX >= window.innerWidth - || ev.originalEvent.clientY >= window.innerHeight - ) { - this.$(".o_file_drop_zone_container").addClass("d-none"); - } - }, - /** - * When user start dragging on element drop area will be visible to drop selected files. - * - * @private - * @param {MouseEvent} ev - */ - _onBodyFileDragover: function (ev) { - ev.preventDefault(); - if (this._isDragSourceExternalFile(ev.originalEvent.dataTransfer)) { - this.$(".o_file_drop_zone_container").removeClass("d-none"); - } - }, - /** - * @private - * @param {MouseEvent} ev - */ - _onBodyFileDrop: function (ev) { - ev.preventDefault(); - this.$(".o_file_drop_zone_container").addClass("d-none"); - }, - /** - * @private - */ - _onClickAddAttachment: function () { - this.$('input.o_input_file').click(); - this.$input.focus(); - }, - /** - * Called when the emoji button is clicked -> opens/hides the emoji panel. - * Also, this method is in charge of the rendering of this panel the first - * time it is opened. - * - * @private - */ - _onEmojiButtonClick: function () { - if (!this._$emojisContainer) { // lazy rendering - this._$emojisContainer = $(QWeb.render('mail.Composer.emojis', { - emojis: emojis, - })); - } - if (this._$emojisContainer.parent().length) { - this._hideEmojis(); - } else { - this._$emojisContainer.appendTo(this.$('.o_composer')); - } - }, - /** - * Called when the emoji button is blurred -> closes the emoji panel. The - * closing is scheduled to be done at the end of the current execution - * stack to allow stoping this closing if the button was focusout to select - * an emoji (for example). - * - * @private - */ - _onEmojiButtonFocusout: function () { - if (this._$emojisContainer) { - this._hideEmojisTimeout = setTimeout(this._hideEmojis.bind(this), 0); - } - }, - /** - * Called when an emoji is clicked from the emoji panel. - * Emoji is inserted in the composer based on the position of the cursor, - * and it automatically focuses the composer and set the cursor position - * just after the newly inserted emoji. - * - * @private - * @param {Event} ev - */ - _onEmojiImageClick: function (ev) { - ev.preventDefault(); - var cursorPosition = this.getSelectionPositions(); - var inputVal = this.$input.val(); - var leftSubstring = inputVal.substring(0, cursorPosition.start); - var rightSubstring = inputVal.substring(cursorPosition.end); - var newInputVal = [leftSubstring , $(ev.currentTarget).data('emoji'), rightSubstring].join(" "); - var newCursorPosition = newInputVal.length - rightSubstring.length; - this.$input.val(newInputVal); - this.$input.focus(); - this.$input[0].setSelectionRange(newCursorPosition, newCursorPosition); - this._hideEmojis(); - }, - /** - * Called when an emoji is focused -> @see _onEmojiButtonFocusout - * - * @private - */ - _onEmojiImageFocus: function () { - clearTimeout(this._hideEmojisTimeout); - }, - /** - * Setting drop Effect to copy so when mouse pointer on dropzone - * cursor icon changed to copy ('+') - * - * @private - * @param {MouseEvent} ev - */ - _onFileDragover: function (ev) { - ev.originalEvent.dataTransfer.dropEffect = "copy"; - }, - /** - * Called when user drop selected files on drop area - * - * @private - * @param {MouseEvent} ev - */ - _onFileDrop: function (ev) { - ev.preventDefault(); - // FIX: In case multiple chat windows are opened, and file droped in one of them - // at that time, other chat windows are still displaing drop areas so here hide them all with $ selector - $(".o_file_drop_zone_container").addClass("d-none"); - if (this._isDragSourceExternalFile(ev.originalEvent.dataTransfer)) { - var files = ev.originalEvent.dataTransfer.files; - this._processAttachmentChange({ files: files }); - } - }, - /** - * Called when the input in the composer changes - * - * @private - */ - _onInput: function () { - if (this.options.thread && this.options.thread.hasTypingNotification()) { - var isTyping = this.$input.val().length > 0; - this.options.thread.setMyselfTyping({ typing: isTyping }); - } - }, - /** - * _onKeydown event is triggered when is key is pressed - * - on UP and DOWN arrow is pressed then event prevents it's default - * behaviour if mention manager is open else it break it. - * - on ENTER key is pressed and mention manager is open then event - * prevents it's default behaviour else check if ControlKey is - * pressed or not if yes then it send a message - * - * @private - * @param {KeyboardEvent} ev - */ - _onKeydown: function (ev) { - switch (ev.which) { - // UP, DOWN, TAB: prevent moving cursor if navigation in mention propositions - case $.ui.keyCode.UP: - case $.ui.keyCode.DOWN: - case $.ui.keyCode.TAB: - if (this._mentionManager.isOpen()) { - ev.preventDefault(); - } - break; - // ENTER: submit the message only if the dropdown mention proposition is not displayed - case $.ui.keyCode.ENTER: - if (this._mentionManager.isOpen()) { - ev.preventDefault(); - } else { - var sendMessage = ev.ctrlKey || this._shouldSend(ev); - if (sendMessage) { - ev.preventDefault(); - this._sendMessage(); - } - } - break; - } - }, - /** - * _onKeyup event is triggered when key is released. - * on ESCAP key it close the mention suggestion dropdown menu. - * on ENTER key it selects that mention and add to a content editable div. - * on UP or DOWN key it highlights previous or next mention respectively. - * - * @private - * @param {KeyboardEvent} ev - */ - _onKeyup: function (ev) { - switch (ev.which) { - // ESCAPED KEYS: do nothing - case $.ui.keyCode.END: - case $.ui.keyCode.PAGE_UP: - case $.ui.keyCode.PAGE_DOWN: - break; - // ESCAPE: close mention propositions - case $.ui.keyCode.ESCAPE: - if (this._mentionManager.isOpen()) { - ev.stopPropagation(); - this._mentionManager.resetSuggestions(); - } else { - this.trigger_up('escape_pressed'); - } - break; - // ENTER, UP, DOWN, TAB: check if navigation in mention propositions - case $.ui.keyCode.ENTER: - case $.ui.keyCode.UP: - case $.ui.keyCode.DOWN: - case $.ui.keyCode.TAB: - if (this._mentionManager.isOpen()) { - this._mentionManager.propositionNavigation(ev.which); - } - break; - // Otherwise, check if a mention is typed - default: - this._mentionManager.detectDelimiter(); - } - }, - /** - * @private - * @param {integer|string} threadID - */ - _onUpdateTypingPartners: function (threadID) { - if (!this.options.showTyping) { - return; - } - if (!this.options.thread) { - return; - } - if (this.options.thread.getID() !== threadID) { - return; - } - this.$('.o_composer_thread_typing').html(QWeb.render('mail.Composer.ThreadTyping', { - thread: this.options.thread, - })); - }, -}); - -return BasicComposer; - -}); |
