diff --git a/addons/im_livechat/static/src/js/im_livechat.js b/addons/im_livechat/static/src/js/im_livechat.js index acc1ed8e559..964edddbadc 100644 --- a/addons/im_livechat/static/src/js/im_livechat.js +++ b/addons/im_livechat/static/src/js/im_livechat.js @@ -260,7 +260,7 @@ var LivechatButton = Widget.extend({ */ _renderMessages: function () { var shouldScroll = !this._chatWindow.isFolded() && this._chatWindow.threadWidget.isAtBottom(); - this._chatWindow.render(this._messages); + this._chatWindow.render(this._messages); // AKU: TODO store messages in website livechat thread if (shouldScroll) { this._chatWindow.threadWidget.scrollToBottom(); } diff --git a/addons/im_livechat/static/src/js/website_livechat_window.js b/addons/im_livechat/static/src/js/website_livechat_window.js index 77eaf40b99b..660db0ad71e 100644 --- a/addons/im_livechat/static/src/js/website_livechat_window.js +++ b/addons/im_livechat/static/src/js/website_livechat_window.js @@ -95,15 +95,13 @@ var LivechatWindow = AbstractThreadWindow.extend({ //-------------------------------------------------------------------------- /** - * If there are no chat linked to this livechat window, returns null. + * AKU: TODO drop this method when there's the website livechat thread + * model linked to this website livechat window. * * @override - * @returns {integer|null} + * @returns {integer} */ _getThreadID: function () { - if (!this.hasThread()) { - return null; - } return this._data.id; }, /** diff --git a/addons/im_support/static/src/js/mail_manager.js b/addons/im_support/static/src/js/mail_manager.js index b25b47cc1ba..56ea4b736b6 100644 --- a/addons/im_support/static/src/js/mail_manager.js +++ b/addons/im_support/static/src/js/mail_manager.js @@ -169,7 +169,12 @@ MailManager.include({ */ _makeChannel: function (data, options) { if (data.id === this.supportChannelUUID) { - return new SupportChannel(this, data, options, this._commands); + return new SupportChannel({ + parent: this, + data: data, + options: options, + commands: this._commands + }); } return this._super.apply(this, arguments); }, diff --git a/addons/im_support/static/src/js/support_channel.js b/addons/im_support/static/src/js/support_channel.js index adcf2a0c51e..dee74a6650d 100644 --- a/addons/im_support/static/src/js/support_channel.js +++ b/addons/im_support/static/src/js/support_channel.js @@ -20,7 +20,20 @@ var _t = core._t; * mail.model.ThreadWithCache */ var SupportChannel = ThreadWithCache.extend({ - init: function (parent, data, options) { + /** + * @param {Object} params + * @param {Object} params.data + * @param {boolean} params.data.available + * @param {string|integer} params.data.id + * @param {boolean} params.data.is_minimized + * @param {Object} params.data.operator + * @param {string} params.data.state ['open', 'closed', 'folded'] + * @param {string} params.data.uuid + * @param {string} params.data.welcome_message + * @param {Object} params.options + */ + init: function (params) { + var data = params.data; data.type = 'support_channel'; data.name = _t("Support"); diff --git a/addons/mail/static/src/js/discuss.js b/addons/mail/static/src/js/discuss.js index 4b6bd2efacc..531f5f6559a 100644 --- a/addons/mail/static/src/js/discuss.js +++ b/addons/mail/static/src/js/discuss.js @@ -383,22 +383,21 @@ var Discuss = AbstractAction.extend(ControlPanelMixin, { */ _fetchAndRenderThread: function () { var self = this; - return this._thread.getMessages(this.domain) - .then(function (messages) { + return this._thread.fetchMessages(this.domain) + .then(function () { self._threadWidget.render( - messages, - self._getThreadRenderingOptions(messages) + self._thread, + self._getThreadRenderingOptions() ); - self._updateButtonStatus(messages.length === 0); + self._updateButtonStatus(!self._thread.hasMessages()); return self._loadEnoughMessages(); }); }, /** * @private - * @param {mail.model.Message} messages * @returns {Object} */ - _getThreadRenderingOptions: function (messages) { + _getThreadRenderingOptions: function () { // Compute position of the 'New messages' separator, only once when // joining a channel to keep it in the thread when new messages arrive if (_.isUndefined(this.messagesSeparatorPosition)) { @@ -416,8 +415,8 @@ var Discuss = AbstractAction.extend(ControlPanelMixin, { messagesSeparatorPosition: this.messagesSeparatorPosition, squashCloseMessages: this._thread.getType() !== 'mailbox' && !this._thread.isMassMailing(), - displayEmptyThread: !messages.length && !this.domain.length, - displayNoMatchFound: !messages.length && this.domain.length, + displayEmptyThread: !this._thread.hasMessages() && !this.domain.length, + displayNoMatchFound: !this._thread.hasMessages() && this.domain.length, displaySubjectOnMessages: ( this._thread.getType() !== 'mailbox' && @@ -483,15 +482,15 @@ var Discuss = AbstractAction.extend(ControlPanelMixin, { var oldestMessageID = this.$('.o_thread_message').first().data('messageId'); var oldestMessageSelector = '.o_thread_message[data-message-id="' + oldestMessageID + '"]'; var offset = -dom.getPosition(document.querySelector(oldestMessageSelector)).top; - return this._thread.getMessages(this.domain, true) - .then(function (messages) { + return this._thread.fetchMessages(this.domain, true) + .then(function () { if (self.messagesSeparatorPosition === 'top') { // reset value to re-compute separator position self.messagesSeparatorPosition = undefined; } self._threadWidget.render( - messages, - self._getThreadRenderingOptions(messages) + self._thread, + self._getThreadRenderingOptions() ); offset += dom.getPosition(document.querySelector(oldestMessageSelector) ).top; @@ -1169,12 +1168,12 @@ var Discuss = AbstractAction.extend(ControlPanelMixin, { (currentThreadID === 'mailbox_inbox' && !message.isNeedaction()) || (currentThreadID === 'mailbox_moderation' && !message.needsModeration()) ) { - this._thread.getMessages(this.domain) - .then(function (messages) { - var options = self._getThreadRenderingOptions(messages); - self._threadWidget.removeMessageAndRender(message.getID(), messages, options) + this._thread.fetchMessages(this.domain) + .then(function () { + var options = self._getThreadRenderingOptions(); + self._threadWidget.removeMessageAndRender(message.getID(), self._thread, options) .then(function () { - self._updateButtonStatus(messages.length === 0, type); + self._updateButtonStatus(!self._thread.hasMessages(), type); }); }); } else if (_.contains(message.getThreadIDs(), currentThreadID)) { diff --git a/addons/mail/static/src/js/models/messages/abstract_message.js b/addons/mail/static/src/js/models/messages/abstract_message.js index 6c5f247386f..420ac2da41b 100644 --- a/addons/mail/static/src/js/models/messages/abstract_message.js +++ b/addons/mail/static/src/js/models/messages/abstract_message.js @@ -44,8 +44,6 @@ var AbstractMessage = Class.extend({ this._id = data.id; this._isDiscussion = data.is_discussion; this._isNotification = data.is_notification; - // if set, the message should display the author of the message - this._displayAuthor = false; this._serverAuthorID = data.author_id; this._type = data.message_type || undefined; @@ -268,12 +266,12 @@ var AbstractMessage = Class.extend({ return this._isDiscussion; }, /** - * State whether this message is linked to a document - * By default, messages are not linked to a document. + * State whether this message is linked to a document thread + * By default, messages are not linked to a document thread. * * @return {boolean} */ - isLinkedToDocument: function () { + isLinkedToDocumentThread: function () { return false; }, /** @@ -337,14 +335,6 @@ var AbstractMessage = Class.extend({ needsModeration: function () { return false; }, - /** - * State whether this message should display the author - * - * @return {boolean} - */ - shouldDisplayAuthor: function () { - return this._displayAuthor; - }, /** * State whether this message should redirect to the author * when clicking on the author of this message. 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 3ea263fa996..788a04307a7 100644 --- a/addons/mail/static/src/js/models/threads/abstract_thread.js +++ b/addons/mail/static/src/js/models/threads/abstract_thread.js @@ -6,15 +6,42 @@ var Class = require('web.Class'); /** * Abstract thread is the super class of all threads, either backend threads * (which are compatible with mail service) or website livechats. + * + * Abstract threads contain abstract messages */ var AbstractThread = Class.extend({ - init: function (parent, data) {}, + /** + * @param {Object} params + * @param {Object} params.data + * @param {Object} params.data.id the ID of this thread + */ + init: function (params) { + this._id = params.data.id; + }, //-------------------------------------------------------------------------- // Public //-------------------------------------------------------------------------- + /** + * Get the ID of this thread + * + * @returns {integer|string} + */ + getID: function () { + return this._id; + }, + /** + * @abstract + * @returns {mail.model.AbstractMessage[]} + */ getMessages: function () {}, + /** + * @returns {boolean} + */ + hasMessages: function () { + return !_.isEmpty(this.getMessages()); + }, }); return AbstractThread; diff --git a/addons/mail/static/src/js/models/threads/channel.js b/addons/mail/static/src/js/models/threads/channel.js index e63cab2f907..86482a71a6d 100644 --- a/addons/mail/static/src/js/models/threads/channel.js +++ b/addons/mail/static/src/js/models/threads/channel.js @@ -17,30 +17,35 @@ var time = require('web.time'); var Channel = ThreadWithCache.extend({ /** * @override - * @param {string} [data.anonymous_name] - * @param {string} data.channel_type - * @param {boolean} data.group_based_subscription - * @param {boolean} [data.is_minimized=false] - * @param {boolean} data.is_moderator whether the current user is + * @param {Object} params + * @param {Object} params.data + * @param {string} [params.data.anonymous_name] + * @param {string} params.data.channel_type + * @param {boolean} params.data.group_based_subscription + * @param {boolean} [params.data.is_minimized=false] + * @param {boolean} params.data.is_moderator whether the current user is * moderator of this channel. - * @param {string} [data.last_message_date] date in server-format - * @param {boolean} [data.mass_mailing] - * @param {integer} [data.message_unread_counter] - * @param {string} [data.public] either 'public' or 'private' - * @param {string} data.state - * @param {string} [data.uuid] - * @param {Object} options - * @param {boolean} [options.autoswitch=true] - * @param {Object[]} commands + * @param {string} [params.data.last_message_date] date in server-format + * @param {boolean} [params.data.mass_mailing] + * @param {integer} [params.data.message_unread_counter] + * @param {string} [params.data.public] either 'public' or 'private' + * @param {string} params.data.state + * @param {string} [params.data.uuid] + * @param {Object} params.options + * @param {boolean} [params.options.autoswitch=true] + * @param {Object[]} params.commands */ - init: function (parent, data, options, commands) { + init: function (params) { var self = this; this._super.apply(this, arguments); + var data = params.data; + var options = params.options; + var commands = params.commands; + // If set, autoswitch channel on joining this channel in discuss // the default behaviour is to autoswitch on join. // exception: receiving channel or chat session notifications - // this._autoswitch = 'autoswitch' in options ? options.autoswitch : true; this._chat = undefined; // FIXME: could be dropped when livechat and DM are moved out of this class this._commands = undefined; diff --git a/addons/mail/static/src/js/models/threads/create_mode_document_thread.js b/addons/mail/static/src/js/models/threads/create_mode_document_thread.js new file mode 100644 index 00000000000..ab396984b54 --- /dev/null +++ b/addons/mail/static/src/js/models/threads/create_mode_document_thread.js @@ -0,0 +1,75 @@ +odoo.define('mail.model.CreateModeDocumentThread', function (require) { +"use strict"; + +var AbstractMessage = require('mail.model.AbstractMessage'); +var AbstractThread = require('mail.model.AbstractThread'); + +var core = require('web.core'); +var session = require('web.session'); + +var _t = core._t; + +/** + * Special document thread that is linked to a new document that is in create + * mode. This thread only display a single (fake) message which displays the + * user avatar and the message 'creating a new record...'. + * + * Note: it uses AbstractThread and AbstractMessage, because this is a fake + * thread which contains a fake message. We do not want to register both of them + * in the mail service. + */ +var CreateModeDocumentThread = AbstractThread.extend({ + + /** + * @override + */ + init: function () { + var params = { + data: { + id: '_createModeDocumentThread' + } + }; + + this._super(params); + + this._messages = this._forgeCreateMessages(); + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @returns {mail.model.AbstractMessage[]} + */ + getMessages: function () { + return this._messages; + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Make a fake list of messages to render in create mode. + * Instead of rendering no messages at all, it displays a single message + * with body content "Creating a new record". + * + * @private + * @returns {mail.model.AbstractMessage[]} an array containing a single + * message 'Creating a new record...' + */ + _forgeCreateMessages: function () { + var createMessage = new AbstractMessage(this, { + id: 0, + body: _t("
Creating a new record...
"), + author_id: [session.partner_id, session.partner_display_name], + }); + return [createMessage]; + }, + +}); + +return CreateModeDocumentThread; + +}); diff --git a/addons/mail/static/src/js/models/threads/dm.js b/addons/mail/static/src/js/models/threads/dm.js index cd08dd7526d..dc24681990b 100644 --- a/addons/mail/static/src/js/models/threads/dm.js +++ b/addons/mail/static/src/js/models/threads/dm.js @@ -11,15 +11,18 @@ var Channel = require('mail.model.Channel'); var DM = Channel.extend({ /** * @override - * @param {Object} data - * @param {Object} data.direct_partner - * @param {integer} data.direct_partner[0].id - * @param {string} data.direct_partner[0].im_status - * @param {string} data.direct_partner[0].name + * @param {Object} params + * @param {Object} params.data + * @param {Object} params.data.direct_partner + * @param {integer} params.data.direct_partner[0].id + * @param {string} params.data.direct_partner[0].im_status + * @param {string} params.data.direct_partner[0].name */ - init: function (parent, data) { + init: function (params) { this._super.apply(this, arguments); + var data = params.data; + this._type = 'dm'; this._name = data.direct_partner[0].name; this._directPartnerID = data.direct_partner[0].id; 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 92cc26e845b..cf3e27c8c81 100644 --- a/addons/mail/static/src/js/models/threads/document_thread.js +++ b/addons/mail/static/src/js/models/threads/document_thread.js @@ -24,13 +24,16 @@ var DocumentThread = Thread.extend({ /** * @override - * @param {string} [data.messagesIDs] the list of message ids linked + * @param {Object} params + * @param {Object} params.data + * @param {string} [params.data.messagesIDs] the list of message ids linked * to the document (if not given, they will be fetched before fetching the * messages) - * @param {string} data.model - * @param {integer} data.resID + * @param {string} params.data.model + * @param {integer} params.data.resID */ - init: function (parent, data) { + init: function (params) { + var data = params.data; data.id = data.resModel + '_' + data.resID; data.type = 'document_thread'; @@ -98,6 +101,23 @@ var DocumentThread = Thread.extend({ windowState: windowState, }); }, + /** + * + * Fetch messages of the document thread + * + * @override + * @param {Object} options + * @param {boolean} [options.forceFetch] if true, fetch anyway, as user + * clicked on 'load more'. + * @returns {$.PromiseCreating a new record...
"), - author_id: [session.partner_id, session.partner_display_name], - }); - return [createMessage]; + } }, /** * @override @@ -162,7 +144,7 @@ var ThreadField = AbstractField.extend({ * Sets this._documentThread, the DocumentThread associated with the current * model and resID. * - * @returns {mail.model.DocumentThread} + * If it is a new document in create mode, unset the document thread. */ _setDocumentThread: function () { var params = { @@ -171,7 +153,11 @@ var ThreadField = AbstractField.extend({ resID: this.res_id, resModel: this.model, }; - this._documentThread = this.call('mail_service', 'getOrAddDocumentThread', params); + if (!params.resID) { + this._documentThread = null; + } else { + this._documentThread = this.call('mail_service', 'getOrAddDocumentThread', params); + } }, //-------------------------------------------------------------------------- diff --git a/addons/mail/static/src/js/thread_widget.js b/addons/mail/static/src/js/thread_widget.js index 78efdcde707..65fb90fb35a 100644 --- a/addons/mail/static/src/js/thread_widget.js +++ b/addons/mail/static/src/js/thread_widget.js @@ -124,7 +124,7 @@ var ThreadWidget = Widget.extend({ // dict where key is message ID, and value is whether it should hide // the author of message or not visually - var hideAuthorMessages = {}; + var displayAuthorMessages = {}; // Hide avatar and info of a message if that message and the previous // one are both comments wrote by the same author at the same minute @@ -154,16 +154,15 @@ var ThreadWidget = Widget.extend({ ) ) ) { - hideAuthorMessages[message.getID()] = false; + displayAuthorMessages[message.getID()] = true; } else { - hideAuthorMessages[message.getID()] = options.squashCloseMessages; + displayAuthorMessages[message.getID()] = !options.squashCloseMessages; } prevMessage = message; }); this.$el.html(QWeb.render('mail.widget.Thread', { thread: thread, - hideAuthorMessages: hideAuthorMessages, - // messages: messages, + displayAuthorMessages: displayAuthorMessages, options: options, ORDER: ORDER, dateFormat: time.getLangDatetimeFormat(), @@ -207,17 +206,18 @@ var ThreadWidget = Widget.extend({ * Removes a message and re-renders the thread * * @param {integer} [messageID] the id of the removed message - * @param {mail.model.AbstractMessage[]} [messages] the list of messages to - * display, without the removed one + * @param {mail.model.AbstractThread} thread the thread which contains + * updated list of messages (so it does not contain any message with ID + * `messageID`). * @param {Object} [options] options for the thread rendering */ - removeMessageAndRender: function (messageID, messages, options) { + removeMessageAndRender: function (messageID, thread, options) { var self = this; var done = $.Deferred(); this.$('.o_thread_message[data-message-id="' + messageID + '"]') .fadeOut({ done: function () { - self.render(messages, options); + self.render(thread, options); done.resolve(); }, duration: 200, 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 ae756b6f624..3325cd8be43 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 @@ -46,12 +46,14 @@ var AbstractThreadWindow = Widget.extend({ * * @abstract * @param {Widget} parent - * @param {Object} thread + * @param {Object} thread the thread that this thread window is linked to. * @param {Object} [options={}] */ init: function (parent, thread, options) { this._super(parent); + this._thread = thread; + this.options = _.defaults(options || {}, { autofocus: true, displayStars: true, @@ -174,15 +176,11 @@ var AbstractThreadWindow = Widget.extend({ return this._hidden; }, /** - * Render the thread window, using the provided list of messages - * - * TODO: use messages in thread, instead of providing list of messages - * - * @param {mail.model.AbstractMessage[]} messages + * Render the thread window */ - render: function (messages) { + render: function () { this._renderHeader(); - this.threadWidget.render(messages, { displayLoadMore: false }); + this.threadWidget.render(this._thread, { displayLoadMore: false }); }, /** * Toggle the fold state of this thread window. Also update the fold state @@ -256,12 +254,19 @@ var AbstractThreadWindow = Widget.extend({ }, /** * 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. * - * @abstract * @private - * @return {integer} + * @returns {integer|string} the threadID, or '_blank' for the window that + * is not related to any thread. */ - _getThreadID: function () {}, + _getThreadID: function () { + if (!this.hasThread()) { + return '_blank'; + } + return this._thread.getID(); + }, /** * Post a message on this thread window * 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 c0d7a22ea33..42cbfcec2e3 100644 --- a/addons/mail/static/src/js/thread_windows/thread_window.js +++ b/addons/mail/static/src/js/thread_windows/thread_window.js @@ -198,24 +198,6 @@ var ThreadWindow = AbstractThreadWindow.extend({ // Private //-------------------------------------------------------------------------- - /** - * Get the ID of the thread window, which is equivalent to 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. - * - * @override - * @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(); - }, /** * @override * @private diff --git a/addons/mail/static/src/xml/chatter.xml b/addons/mail/static/src/xml/chatter.xml index 7a5959789cf..9b6d0586bdc 100644 --- a/addons/mail/static/src/xml/chatter.xml +++ b/addons/mail/static/src/xml/chatter.xml @@ -51,6 +51,7 @@