From 8840b37989fcb5b3e904d1ccc2f2e56e5ea2ee1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20K=C3=BChn?= Date: Wed, 29 Jun 2022 12:47:41 +0000 Subject: [PATCH] [IMP] im_livechat, *: Refactor JS Public Livechat (step 8) *: website_livechat This commit is a step towards refactoring the JS of public livechat, so that it reuses the same architecture as the code of Discuss. This implies code that uses JS models and OWL components. Task-2925850 Part-of: odoo/odoo#96447 --- addons/im_livechat/__manifest__.py | 3 - .../src/legacy/models/public_livechat.js | 16 +- .../legacy/models/public_livechat_message.js | 27 ++++ .../src/legacy/public_livechat_chatbot.js | 17 +- .../legacy/public_livechat_message_chatbot.js | 65 -------- .../src/legacy/widgets/livechat_button.js | 22 ++- .../public_livechat_window.js | 153 ++++-------------- .../public_livechat_window.xml | 6 +- .../src/legacy/widgets/thread/thread.js | 16 +- .../src/legacy/widgets/thread/thread.xml | 15 -- .../static/src/models/livechat_button_view.js | 44 ++--- .../static/src/models/messaging.js | 19 +-- .../src/models/messaging_initializer.js | 2 +- .../static/src/models/public_livechat.js | 36 +++-- .../src/models/public_livechat_global.js | 21 +++ .../src/models/public_livechat_message.js | 16 ++ .../src/models/public_livechat_window.js | 42 ++--- addons/im_livechat/static/src/public/main.js | 6 +- .../website_livechat_chatbot_test_script.js | 4 +- .../static/src/models/messaging.js | 10 -- 20 files changed, 198 insertions(+), 342 deletions(-) delete mode 100644 addons/im_livechat/static/src/legacy/public_livechat_message_chatbot.js create mode 100644 addons/im_livechat/static/src/models/public_livechat_global.js create mode 100644 addons/im_livechat/static/src/models/public_livechat_message.js delete mode 100644 addons/website_livechat/static/src/models/messaging.js diff --git a/addons/im_livechat/__manifest__.py b/addons/im_livechat/__manifest__.py index 2fa02db7da9..c0f7a37666e 100644 --- a/addons/im_livechat/__manifest__.py +++ b/addons/im_livechat/__manifest__.py @@ -54,7 +54,6 @@ Help your customers with this chat, and analyse their feedback. 'im_livechat/static/src/services/*.js', 'im_livechat/static/src/legacy/public_livechat_history_tracking.js', 'im_livechat/static/src/legacy/public_livechat_chatbot.js', - 'im_livechat/static/src/legacy/public_livechat_message_chatbot.js', 'im_livechat/static/src/legacy/public_livechat.scss', 'im_livechat/static/src/legacy/public_livechat_chatbot.scss', ], @@ -77,7 +76,6 @@ Help your customers with this chat, and analyse their feedback. 'im_livechat/static/tests/qunit_suite_tests/components/**/*.js', 'im_livechat/static/src/legacy/public_livechat_history_tracking.js', 'im_livechat/static/src/legacy/public_livechat_chatbot.js', - 'im_livechat/static/src/legacy/public_livechat_message_chatbot.js', ], 'web.assets_tests': [ 'im_livechat/static/tests/tours/**/*', @@ -189,7 +187,6 @@ Help your customers with this chat, and analyse their feedback. 'mail/static/src/js/utils.js', 'im_livechat/static/src/legacy/public_livechat_history_tracking.js', 'im_livechat/static/src/legacy/public_livechat_chatbot.js', - 'im_livechat/static/src/legacy/public_livechat_message_chatbot.js', ('include', 'web._assets_helpers'), diff --git a/addons/im_livechat/static/src/legacy/models/public_livechat.js b/addons/im_livechat/static/src/legacy/models/public_livechat.js index 4f1b70872ab..097060295ad 100644 --- a/addons/im_livechat/static/src/legacy/models/public_livechat.js +++ b/addons/im_livechat/static/src/legacy/models/public_livechat.js @@ -16,8 +16,8 @@ import { sprintf } from 'web.utils'; */ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, { /** - * @override * @private + * @param {Messaging} messaging * @param {Object} params * @param {Object} params.data * @param {boolean} [params.data.folded] states whether the livechat is @@ -34,15 +34,15 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, { * @param {string} params.data.uuid the UUID of this livechat. * @param {@im_livechat/legacy/widgets/livechat_button} params.parent */ - init(params) { + init(messaging, params) { Mixins.EventDispatcherMixin.init.call(this, arguments); this.setParent(params.parent); + this.messaging = messaging; this._folded = false; // threads are unfolded by default this._id = params.data.id; this._name = params.data.name; this._status = params.data.status || ''; - this._unreadCounter = 0; // amount of messages not yet been read /** * Initialize the internal data for typing feature on threads. @@ -123,7 +123,9 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, { this._uuid = params.data.uuid; if (params.data.message_unread_counter !== undefined) { - this._unreadCounter = params.data.message_unread_counter; + this.messaging.livechatButtonView.publicLivechat.update({ + unreadCounter: params.data.message_unread_counter + }); } if (_.isBoolean(params.data.folded)) { @@ -232,8 +234,8 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, { * @returns {Promise} */ markAsRead() { - if (this._unreadCounter > 0) { - this._unreadCounter = 0; + if (this.messaging.livechatButtonView.publicLivechat.unreadCounter > 0) { + this.messaging.livechatButtonView.publicLivechat.update({ unreadCounter: 0 }); this.trigger_up('updated_unread_counter'); return Promise.resolve(); } @@ -302,7 +304,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, { return { folded: this._folded, id: this._id, - message_unread_counter: this._unreadCounter, + message_unread_counter: this.messaging.livechatButtonView.publicLivechat.unreadCounter, operator_pid: this._operatorPID, name: this._name, uuid: this._uuid, diff --git a/addons/im_livechat/static/src/legacy/models/public_livechat_message.js b/addons/im_livechat/static/src/legacy/models/public_livechat_message.js index f35889065e2..90026ed771d 100644 --- a/addons/im_livechat/static/src/legacy/models/public_livechat_message.js +++ b/addons/im_livechat/static/src/legacy/models/public_livechat_message.js @@ -52,6 +52,12 @@ const PublicLivechatMessage = Class.extend({ }); this._defaultUsername = options.default_username; this._serverURL = options.serverURL; + + if (parent.messaging.livechatButtonView.isChatbot) { + this._chatbotStepId = data.chatbot_script_step_id; + this._chatbotStepAnswers = data.chatbot_step_answers; + this._chatbotStepAnswerId = data.chatbot_selected_answer_id; + } }, //-------------------------------------------------------------------------- @@ -109,6 +115,24 @@ const PublicLivechatMessage = Class.extend({ getBody() { return this._body; }, + /** + * @return {string} + */ + getChatbotStepId() { + return this._chatbotStepId; + }, + /** + * @return {string} + */ + getChatbotStepAnswers() { + return this._chatbotStepAnswers; + }, + /** + * @return {string} + */ + getChatbotStepAnswerId() { + return this._chatbotStepAnswerId; + }, /** * @return {moment} */ @@ -408,6 +432,9 @@ const PublicLivechatMessage = Class.extend({ return _.contains(attachmentIDs, attachment.id); }); }, + setChatbotStepAnswerId(chatbotStepAnswerId) { + this._chatbotStepAnswerId = chatbotStepAnswerId; + }, /** * State whether this message should redirect to the author * when clicking on the author of this message. diff --git a/addons/im_livechat/static/src/legacy/public_livechat_chatbot.js b/addons/im_livechat/static/src/legacy/public_livechat_chatbot.js index 23dfb88fe33..96a5af9ac29 100644 --- a/addons/im_livechat/static/src/legacy/public_livechat_chatbot.js +++ b/addons/im_livechat/static/src/legacy/public_livechat_chatbot.js @@ -7,7 +7,7 @@ import time from 'web.time'; import utils from 'web.utils'; import LivechatButton from '@im_livechat/legacy/widgets/livechat_button'; -import { clear, insertAndReplace } from '@mail/model/model_field_command'; +import { clear, increment, insertAndReplace } from '@mail/model/model_field_command'; const _t = core._t; const QWeb = core.qweb; @@ -59,8 +59,7 @@ const QWeb = core.qweb; const sessionCookie = utils.get_cookie('im_livechat_session'); if (sessionCookie) { this.messaging.livechatButtonView.update({ sessionCookie }); - if (localStorage.getItem(this.messaging.livechatButtonView.sessionCookieKey)) { - this.messaging.livechatButtonView.update({ isChatbot: true }); + if (localStorage.getItem(this.messaging.livechatButtonView.chatbotSessionCookieKey)) { this.messaging.livechatButtonView.update({ chatbotState: 'restore_session' }); } } @@ -108,8 +107,8 @@ const QWeb = core.qweb; _chatbotAddMessage(message, options) { message.body = utils.Markup(message.body); this._addMessage(message, options); - if (this.messaging.livechatButtonView.chatWindow.legacyChatWindow.isFolded() || !this.messaging.livechatButtonView.chatWindow.legacyChatWindow.isAtBottom()) { - this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._unreadCounter++; + if (this.messaging.livechatButtonView.chatWindow.legacyChatWindow._thread._folded || !this.messaging.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.isAtBottom()) { + this.messaging.livechatButtonView.publicLivechat.update({ unreadCounter: increment() }); } if (!options || !options.skipRenderMessages) { @@ -357,13 +356,13 @@ const QWeb = core.qweb; if (browserLocalStorage && browserLocalStorage.length) { for (let i = 0; i < browserLocalStorage.length; i++) { const key = browserLocalStorage.key(i); - if (key.startsWith('im_livechat.chatbot.state.uuid_') && key !== this.messaging.livechatButtonView.sessionCookieKey) { + if (key.startsWith('im_livechat.chatbot.state.uuid_') && key !== this.messaging.livechatButtonView.chatbotSessionCookieKey) { browserLocalStorage.removeItem(key); } } } - let chatbotState = localStorage.getItem(this.messaging.livechatButtonView.sessionCookieKey); + let chatbotState = localStorage.getItem(this.messaging.livechatButtonView.chatbotSessionCookieKey); if (chatbotState) { this.messaging.livechatButtonView.chatbot.update({ currentStep: insertAndReplace({ data: this.messaging.livechatButtonView.localStorageChatbotState._chatbotCurrentStep }) }); @@ -408,7 +407,7 @@ const QWeb = core.qweb; })) ); - this.messaging.livechatButtonView.chatWindow.legacyChatWindow.scrollToBottom(); + this.messaging.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.scrollToBottom(); }, this.messaging.livechatButtonView.chatbot.messageDelay / 3), }); }, @@ -599,7 +598,7 @@ const QWeb = core.qweb; return this._super(...arguments).then(() => { window.addEventListener('resize', () => { if (this.messaging.livechatButtonView.chatWindow) { - this.messaging.livechatButtonView.chatWindow.legacyChatWindow.scrollToBottom(); + this.messaging.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.scrollToBottom(); } }); diff --git a/addons/im_livechat/static/src/legacy/public_livechat_message_chatbot.js b/addons/im_livechat/static/src/legacy/public_livechat_message_chatbot.js deleted file mode 100644 index 7806ed3c9dd..00000000000 --- a/addons/im_livechat/static/src/legacy/public_livechat_message_chatbot.js +++ /dev/null @@ -1,65 +0,0 @@ -/** @odoo-module **/ - -import PublicLivechatMessage from '@im_livechat/legacy/models/public_livechat_message'; - -/** - * Override of the PublicLivechatMessage that includes chatbot capabilities. - * The main changes are: - * - Allow to display options for the end-user to click on ("_chatbotStepAnswers") - * - Show a different name/icon for the chatbot (instead of the 'OdooBot' operator name/icon) - */ -PublicLivechatMessage.include({ - /** - * @param {@im_livechat/legacy/widgets/livechat_button} parent - * @param {Object} data - * @param {Object} options - * @param {string} options.default_username - * @param {string} options.serverURL - */ - init(parent, data, options) { - this._super(...arguments); - - if (parent.messaging.livechatButtonView.isChatbot) { - this._chatbotStepId = data.chatbot_script_step_id; - this._chatbotStepAnswers = data.chatbot_step_answers; - this._chatbotStepAnswerId = data.chatbot_selected_answer_id; - } - }, - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- - - /** - * Get chat bot script step ID - * - * @return {string} - */ - getChatbotStepId() { - return this._chatbotStepId; - }, - /** - * Get chat bot script answers - * - * @return {string} - */ - getChatbotStepAnswers() { - return this._chatbotStepAnswers; - }, - /** - * Get chat bot script answer ID - * - * @return {string} - */ - getChatbotStepAnswerId() { - return this._chatbotStepAnswerId; - }, - /** - * @override - */ - setChatbotStepAnswerId(chatbotStepAnswerId) { - this._chatbotStepAnswerId = chatbotStepAnswerId; - } -}); - -export default PublicLivechatMessage; diff --git a/addons/im_livechat/static/src/legacy/widgets/livechat_button.js b/addons/im_livechat/static/src/legacy/widgets/livechat_button.js index fc95ea0e9ca..3e00f16faa7 100644 --- a/addons/im_livechat/static/src/legacy/widgets/livechat_button.js +++ b/addons/im_livechat/static/src/legacy/widgets/livechat_button.js @@ -11,7 +11,7 @@ import { LIVECHAT_COOKIE_HISTORY } from 'im_livechat.legacy.im_livechat.Constant import Feedback from '@im_livechat/legacy/widgets/feedback/feedback'; import PublicLivechatMessage from '@im_livechat/legacy/models/public_livechat_message'; -import { clear, insertAndReplace } from '@mail/model/model_field_command'; +import { clear, increment, insert, insertAndReplace } from '@mail/model/model_field_command'; const _t = core._t; const QWeb = core.qweb; @@ -31,7 +31,7 @@ const LivechatButton = Widget.extend({ init(parent, messaging) { this._super(parent); this.messaging = messaging; - this.options = _.defaults(this.messaging.publicLivechatOptions || {}); + this.options = _.defaults(this.messaging.publicLivechatGlobal.options || {}); }, async willStart() { this.messaging.livechatButtonView.update({ widget: this }); @@ -107,6 +107,14 @@ const LivechatButton = Widget.extend({ if (hasAlreadyMessage) { return; } + if (this.messaging.livechatButtonView.publicLivechat) { + this.messaging.livechatButtonView.publicLivechat.update({ + messages: insert({ + id: data.id, + legacyPublicLivechatMessage: message, + }), + }); + } if (this.messaging.livechatButtonView.publicLivechat && this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat) { this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat.addMessage(message); @@ -189,8 +197,8 @@ const LivechatButton = Widget.extend({ } notificationData.body = utils.Markup(notificationData.body); this._addMessage(notificationData); - if (this.messaging.livechatButtonView.chatWindow.legacyChatWindow.isFolded() || !this.messaging.livechatButtonView.chatWindow.legacyChatWindow.isAtBottom()) { - this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._unreadCounter++; + if (this.messaging.livechatButtonView.chatWindow.legacyChatWindow._thread._folded || !this.messaging.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.isAtBottom()) { + this.messaging.livechatButtonView.publicLivechat.update({ unreadCounter: increment() }); } this._renderMessages(); return; @@ -329,11 +337,11 @@ const LivechatButton = Widget.extend({ * @private */ _renderMessages() { - const shouldScroll = !this.messaging.livechatButtonView.chatWindow.legacyChatWindow.isFolded() && this.messaging.livechatButtonView.chatWindow.legacyChatWindow.isAtBottom(); + const shouldScroll = !this.messaging.livechatButtonView.chatWindow.legacyChatWindow._thread._folded && this.messaging.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.isAtBottom(); this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._messages = this.messaging.livechatButtonView.messages; this.messaging.livechatButtonView.chatWindow.legacyChatWindow.render(); if (shouldScroll) { - this.messaging.livechatButtonView.chatWindow.legacyChatWindow.scrollToBottom(); + this.messaging.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.scrollToBottom(); } }, /** @@ -365,7 +373,7 @@ const LivechatButton = Widget.extend({ } this._closeChat(); } - this.messaging.livechatButtonView.chatWindow.legacyChatWindow.scrollToBottom(); + this.messaging.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.scrollToBottom(); }); }, /** diff --git a/addons/im_livechat/static/src/legacy/widgets/public_livechat_window/public_livechat_window.js b/addons/im_livechat/static/src/legacy/widgets/public_livechat_window/public_livechat_window.js index ac16e0e2a1b..da9f4e3d815 100644 --- a/addons/im_livechat/static/src/legacy/widgets/public_livechat_window/public_livechat_window.js +++ b/addons/im_livechat/static/src/legacy/widgets/public_livechat_window/public_livechat_window.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import ThreadWidget from '@im_livechat/legacy/widgets/thread/thread'; +import PublicLivechatView from '@im_livechat/legacy/widgets/thread/thread'; import config from 'web.config'; import { _t, qweb } from 'web.core'; @@ -30,23 +30,19 @@ const PublicLivechatWindow = Widget.extend({ }, /** * @param {Widget} parent + * @param {Messaging} messaging * @param {@im_livechat/legacy/models/public_livechat} thread * @param {Object} [options={}] * @param {string} [options.headerBackgroundColor] * @param {string} [options.titleColor] */ - init(parent, thread, options) { + init(parent, messaging, thread, options) { this._super(parent); - + this.messaging = messaging; this.options = _.defaults(options || {}, { - autofocus: true, - displayStars: true, - displayReplyIcons: false, - displayNotificationIcons: false, placeholder: _t("Say something"), }); - this._hidden = false; this._thread = thread; this._debouncedOnScroll = _.debounce(this._onScroll.bind(this), 100); @@ -60,26 +56,25 @@ const PublicLivechatWindow = Widget.extend({ this.$header = this.$('.o_thread_window_header'); const options = { displayMarkAsRead: false, - displayStars: this.options.displayStars, }; if (this._thread && this._thread._type === 'document_thread') { options.displayDocumentLinks = false; } - this._threadWidget = new ThreadWidget(this, options); + this._publicLivechatView = new PublicLivechatView(this, this.messaging, options); // animate the (un)folding of thread windows this.$el.css({ transition: 'height ' + this.FOLD_ANIMATION_DURATION + 'ms linear' }); - if (this.isFolded()) { + if (this._thread._folded) { this.$el.css('height', this.HEIGHT_FOLDED); - } else if (this.options.autofocus) { + } else { this._focusInput(); } if (!config.device.isMobile) { const margin_dir = _t.database.parameters.direction === "rtl" ? "margin-left" : "margin-right"; this.$el.css(margin_dir, $.position.scrollbarWidth()); } - const def = this._threadWidget.replace(this.$('.o_thread_window_content')).then(() => { - this._threadWidget.$el.on('scroll', this, this._debouncedOnScroll); + const def = this._publicLivechatView.replace(this.$('.o_thread_window_content')).then(() => { + this._publicLivechatView.$el.on('scroll', this, this._debouncedOnScroll); }); await Promise.all([this._super(), def]); if (this.options.headerBackgroundColor) { @@ -89,27 +84,6 @@ const PublicLivechatWindow = Widget.extend({ this.$('.o_thread_window_header').css('color', this.options.titleColor); } }, - /** - * @override - */ - do_hide() { - this._hidden = true; - this._super(...arguments); - }, - /** - * @override - */ - do_show() { - this._hidden = false; - this._super(...arguments); - }, - /** - * @override - */ - do_toggle(display) { - this._hidden = _.isBoolean(display) ? !display : !this._hidden; - this._super(...arguments); - }, //-------------------------------------------------------------------------- @@ -122,51 +96,6 @@ const PublicLivechatWindow = Widget.extend({ close() { this.trigger_up('close_chat_window'); }, - /** - * Get the title of the thread window, which usually contains the name of - * the thread. - * - * @returns {string} - */ - getTitle() { - return this._thread._name; - }, - /** - * Get the unread counter of the related thread. If there are no thread - * linked to this window, returns 0. - * - * @returns {integer} - */ - getUnreadCounter() { - return this._thread._unreadCounter; - }, - /** - * Tells whether the bottom of the thread in the thread window is visible - * or not. - * - * @returns {boolean} - */ - isAtBottom() { - 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() { - return this._thread._folded; - }, - /** - * States whether the thread window is hidden or not. - * - * @returns {boolean} - */ - isHidden() { - return this._hidden; - }, /** * States whether the current environment is in mobile or not. This is * useful in order to customize the template rendering for mobile view. @@ -176,21 +105,12 @@ const PublicLivechatWindow = Widget.extend({ isMobile() { return config.device.isMobile; }, - /** - * 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() { - return true; - }, /** * Render the thread window */ render() { this.renderHeader(); - this._threadWidget.render(this._thread, { displayLoadMore: false }); + this._publicLivechatView.render(this._thread, { displayLoadMore: false }); }, /** * Render the header of this thread window. @@ -200,9 +120,15 @@ const PublicLivechatWindow = Widget.extend({ * @private */ renderHeader() { - const options = this._getHeaderRenderingOptions(); this.$header.html( - qweb.render('im_livechat.legacy.PublicLivechatWindow.HeaderContent', options)); + qweb.render('im_livechat.legacy.PublicLivechatWindow.HeaderContent', { + status: this._thread._status, + thread: this._thread, + title: this._thread._name, + unreadCounter: this.messaging.livechatButtonView.publicLivechat.unreadCounter, + widget: this, + }) + ); }, /** * Replace the thread content with provided new content @@ -210,13 +136,7 @@ const PublicLivechatWindow = Widget.extend({ * @param {$.Element} $element */ replaceContentWith($element) { - $element.replace(this._threadWidget.$el); - }, - /** - * Scroll to the bottom of the thread in the thread window - */ - scrollToBottom() { - this._threadWidget.scrollToBottom(); + $element.replace(this._publicLivechatView.$el); }, /** * Toggle the fold state of this thread window. Also update the fold state @@ -230,7 +150,7 @@ const PublicLivechatWindow = Widget.extend({ */ toggleFold(folded) { if (!_.isBoolean(folded)) { - folded = !this.isFolded(); + folded = !this._thread._folded; } this._updateThreadFoldState(folded); this.trigger_up('save_chat_window'); @@ -242,13 +162,11 @@ const PublicLivechatWindow = Widget.extend({ * that has been changed. */ updateVisualFoldState() { - if (!this.isFolded()) { - this._threadWidget.scrollToBottom(); - if (this.options.autofocus) { - this._focusInput(); - } + if (!this._thread._folded) { + this._publicLivechatView.scrollToBottom(); + this._focusInput(); } - const height = this.isFolded() ? this.HEIGHT_FOLDED : this.HEIGHT_OPEN; + const height = this._thread._folded ? this.HEIGHT_FOLDED : this.HEIGHT_OPEN; this.$el.css({ height }); }, @@ -272,21 +190,6 @@ const PublicLivechatWindow = Widget.extend({ } this.$input.focus(); }, - /** - * Returns the options used by the rendering of the window's header - * - * @private - * @returns {Object} - */ - _getHeaderRenderingOptions() { - return { - status: this._thread._status, - thread: this._thread, - title: this.getTitle(), - unreadCounter: this.getUnreadCounter(), - widget: this, - }; - }, /** * Tells whether there is focus on this thread. Note that a thread that has * the focus means the input has focus. @@ -308,7 +211,7 @@ const PublicLivechatWindow = Widget.extend({ this.trigger_up('post_message_chat_window', { messageData }); this._thread.postMessage(messageData) .then(() => { - this._threadWidget.scrollToBottom(); + this._publicLivechatView.scrollToBottom(); }); }, /** @@ -340,8 +243,8 @@ const PublicLivechatWindow = Widget.extend({ ev.stopPropagation(); ev.preventDefault(); if ( - this._thread._unreadCounter > 0 && - !this.isFolded() + this.messaging.livechatButtonView.publicLivechat.unreadCounter > 0 && + !this._thread._folded ) { this._thread.markAsRead(); } @@ -419,7 +322,7 @@ const PublicLivechatWindow = Widget.extend({ * @private */ _onScroll() { - if (this.isAtBottom()) { + if (this._publicLivechatView.isAtBottom()) { this._thread.markAsRead(); } }, diff --git a/addons/im_livechat/static/src/legacy/widgets/public_livechat_window/public_livechat_window.xml b/addons/im_livechat/static/src/legacy/widgets/public_livechat_window/public_livechat_window.xml index d72884a1832..59fa678ace4 100644 --- a/addons/im_livechat/static/src/legacy/widgets/public_livechat_window/public_livechat_window.xml +++ b/addons/im_livechat/static/src/legacy/widgets/public_livechat_window/public_livechat_window.xml @@ -8,14 +8,14 @@
- - + +
-
+
diff --git a/addons/im_livechat/static/src/legacy/widgets/thread/thread.js b/addons/im_livechat/static/src/legacy/widgets/thread/thread.js index 2cd7b1345d2..247919e9a53 100644 --- a/addons/im_livechat/static/src/legacy/widgets/thread/thread.js +++ b/addons/im_livechat/static/src/legacy/widgets/thread/thread.js @@ -23,7 +23,7 @@ const READ_LESS = _lt("read less"); * Any thread that extends mail.model.AbstractThread can be used with this * widget. */ -const ThreadWidget = Widget.extend({ +const PublicLivechatView = Widget.extend({ className: 'o_mail_thread', events: { @@ -48,10 +48,12 @@ const ThreadWidget = Widget.extend({ /** * @override * @param {widget} parent + * @param {Messaging} messaging * @param {Object} options */ - init(parent, options) { + init(parent, messaging, options) { this._super(...arguments); + this.messaging = messaging; this.attachments = []; // options when the thread is enabled (e.g. can send message, // interact on messages, etc.) @@ -59,12 +61,9 @@ const ThreadWidget = Widget.extend({ displayOrder: ORDER.ASC, displayMarkAsRead: true, displayModerationCommands: false, - displayStars: true, displayDocumentLinks: true, displayAvatars: true, squashCloseMessages: true, - displayNotificationIcons: true, - displayReplyIcons: false, loadMoreOnScroll: false, hasMessageAttachmentDeletable: false, }); @@ -73,12 +72,9 @@ const ThreadWidget = Widget.extend({ displayOrder: this._enabledOptions.displayOrder, displayMarkAsRead: false, displayModerationCommands: false, - displayStars: false, displayDocumentLinks: false, displayAvatars: this._enabledOptions.displayAvatars, squashCloseMessages: false, - displayNotificationIcons: false, - displayReplyIcons: false, loadMoreOnScroll: this._enabledOptions.loadMoreOnScroll, hasMessageAttachmentDeletable: false, }; @@ -613,6 +609,6 @@ const ThreadWidget = Widget.extend({ }, }); -ThreadWidget.ORDER = ORDER; +PublicLivechatView.ORDER = ORDER; -export default ThreadWidget; +export default PublicLivechatView; diff --git a/addons/im_livechat/static/src/legacy/widgets/thread/thread.xml b/addons/im_livechat/static/src/legacy/widgets/thread/thread.xml index a7157f20869..08cfa0d1b8b 100644 --- a/addons/im_livechat/static/src/legacy/widgets/thread/thread.xml +++ b/addons/im_livechat/static/src/legacy/widgets/thread/thread.xml @@ -127,12 +127,9 @@ @param {Object} options @param {boolean} [options.displayAvatars] @param {boolean} [options.displayDocumentLinks] - @param {boolean} [options.displayNotificationIcons] @param {boolean} [options.displayMarkAsRead] @param {boolean} [options.displayModerationCommands] when set, display the moderation commands on the message. This includes the moderation checkboxes (needs a control panel such as in Discuss app). - @param {boolean} [options.displayReplyIcons] - @param {boolean} [options.displayStars] @param {boolean} [options.displaySubjectsOnMessages] @param {boolean} options.hasMessageAttachmentDeletable @param {string|integer} [options.messagesSeparatorPosition] 'top' or @@ -165,9 +162,6 @@ -

@@ -196,16 +190,7 @@ (from #) - - - - - - - diff --git a/addons/im_livechat/static/src/models/livechat_button_view.js b/addons/im_livechat/static/src/models/livechat_button_view.js index 57f34071b0f..9e5c78c7a8b 100644 --- a/addons/im_livechat/static/src/models/livechat_button_view.js +++ b/addons/im_livechat/static/src/models/livechat_button_view.js @@ -13,14 +13,14 @@ registerModel({ * @returns {string} */ _computeButtonBackgroundColor() { - return this.messaging.publicLivechatOptions.button_background_color; + return this.messaging.publicLivechatGlobal.options.button_background_color; }, /** * @returns {string} */ _computeButtonText() { - if (this.messaging.publicLivechatOptions.button_text) { - return this.messaging.publicLivechatOptions.button_text; + if (this.messaging.publicLivechatGlobal.options.button_text) { + return this.messaging.publicLivechatGlobal.options.button_text; } return this.env._t("Chat with one of our collaborators"); }, @@ -28,14 +28,14 @@ registerModel({ * @returns {string} */ _computeButtonTextColor() { - return this.messaging.publicLivechatOptions.button_text_color; + return this.messaging.publicLivechatGlobal.options.button_text_color; }, /** * @private * @returns {integer} */ _computeChannelId() { - return this.messaging.publicLivechatOptions.channel_id; + return this.messaging.publicLivechatGlobal.options.channel_id; }, /** * @private @@ -84,18 +84,18 @@ registerModel({ * @returns {integer} */ _computeCurrentPartnerId() { - if (!this.messaging.isPublicLivechatAvailable) { + if (!this.messaging.publicLivechatGlobal.isAvailable) { return clear(); } - return this.messaging.publicLivechatOptions.current_partner_id; + return this.messaging.publicLivechatGlobal.options.current_partner_id; }, /** * @private * @returns {string} */ _computeDefaultMessage() { - if (this.messaging.publicLivechatOptions.default_message) { - return this.messaging.publicLivechatOptions.default_message; + if (this.messaging.publicLivechatGlobal.options.default_message) { + return this.messaging.publicLivechatGlobal.options.default_message; } return this.env._t("How may I help you?"); }, @@ -104,8 +104,8 @@ registerModel({ * @returns {string} */ _computeDefaultUsername() { - if (this.messaging.publicLivechatOptions.default_username) { - return this.messaging.publicLivechatOptions.default_username; + if (this.messaging.publicLivechatGlobal.options.default_username) { + return this.messaging.publicLivechatGlobal.options.default_username; } return this.env._t("Visitor"); }, @@ -114,7 +114,7 @@ registerModel({ * @returns {string} */ _computeHeaderBackgroundColor() { - return this.messaging.publicLivechatOptions.header_background_color; + return this.messaging.publicLivechatGlobal.options.header_background_color; }, /** * @private @@ -126,8 +126,8 @@ registerModel({ // as we use it for specific things (e.g: showing "please select an option above") return clear(); } - if (this.messaging.publicLivechatOptions.input_placeholder) { - return this.messaging.publicLivechatOptions.input_placeholder; + if (this.messaging.publicLivechatGlobal.options.input_placeholder) { + return this.messaging.publicLivechatGlobal.options.input_placeholder; } return this.env._t("Ask something ..."); }, @@ -161,7 +161,7 @@ registerModel({ if (!this.sessionCookie) { return clear(); } - const data = localStorage.getItem(this.sessionCookieKey); + const data = localStorage.getItem(this.chatbotSessionCookieKey); if (!data) { return clear(); } @@ -173,14 +173,14 @@ registerModel({ */ _computeServerUrl() { if (this.isChatbot) { - return this.messaging.publicLivechatServerUrlChatbot; + return this.messaging.publicLivechatGlobal.chatbotServerUrl; } - return this.messaging.publicLivechatServerUrl; + return this.messaging.publicLivechatGlobal.serverUrl; }, /** * @returns {string|FieldCommand} */ - _computeSessionCookieKey() { + _computeChatbotSessionCookieKey() { if (!this.sessionCookie) { return clear(); } @@ -191,7 +191,7 @@ registerModel({ * @returns {string} */ _computeTitleColor() { - return this.messaging.publicLivechatOptions.title_color; + return this.messaging.publicLivechatGlobal.options.title_color; }, }, fields: { @@ -213,6 +213,9 @@ registerModel({ inverse: 'livechatButtonViewOwner', isCausal: true, }), + chatbotSessionCookieKey: attr({ + compute: '_computeChatbotSessionCookieKey', + }), chatbotState: attr({ compute: '_computeChatbotState', }), @@ -272,9 +275,6 @@ registerModel({ compute: '_computeServerUrl', }), sessionCookie: attr(), - sessionCookieKey: attr({ - compute: '_computeSessionCookieKey', - }), testChatbotData: attr(), titleColor: attr({ compute: '_computeTitleColor', diff --git a/addons/im_livechat/static/src/models/messaging.js b/addons/im_livechat/static/src/models/messaging.js index 3895166f897..8d845ab7ae1 100644 --- a/addons/im_livechat/static/src/models/messaging.js +++ b/addons/im_livechat/static/src/models/messaging.js @@ -1,18 +1,12 @@ /** @odoo-module **/ import { addFields, addRecordMethods, patchRecordMethods } from '@mail/model/model_core'; -import { attr, many, one } from '@mail/model/model_field'; +import { many, one } from '@mail/model/model_field'; import { clear, insertAndReplace } from '@mail/model/model_field_command'; // ensure that the model definition is loaded before the patch import '@mail/models/messaging'; addFields('Messaging', { - isInPublicLivechat: attr({ - default: false, - }), - isPublicLivechatAvailable: attr({ - default: false, - }), livechatButtonView: one('LivechatButtonView', { compute: '_computeLivechatButtonView', isCausal: true, @@ -24,11 +18,8 @@ addFields('Messaging', { inverse: 'messagingAsPinnedLivechat', readonly: true, }), - publicLivechatOptions: attr({ - default: {}, - }), - publicLivechatServerUrl: attr({ - default: '', + publicLivechatGlobal: one('PublicLivechatGlobal', { + isCausal: true, }), }); @@ -38,7 +29,7 @@ addRecordMethods('Messaging', { * @returns {FieldCommand} */ _computeLivechatButtonView() { - if (this.isInPublicLivechat && this.isPublicLivechatAvailable) { + if (this.publicLivechatGlobal && this.publicLivechatGlobal.isAvailable) { return insertAndReplace(); } return clear(); @@ -50,7 +41,7 @@ patchRecordMethods('Messaging', { * @override */ _computeNotificationHandler() { - if (this.isInPublicLivechat) { + if (this.publicLivechatGlobal) { return clear(); } return this._super(); diff --git a/addons/im_livechat/static/src/models/messaging_initializer.js b/addons/im_livechat/static/src/models/messaging_initializer.js index 9b1e1b231e2..19a19ccd503 100644 --- a/addons/im_livechat/static/src/models/messaging_initializer.js +++ b/addons/im_livechat/static/src/models/messaging_initializer.js @@ -10,7 +10,7 @@ patchRecordMethods('MessagingInitializer', { * @override */ async performInitRpc() { - if (this.messaging.isInPublicLivechat) { + if (this.messaging.publicLivechatGlobal) { return {}; } else { return this._super(); diff --git a/addons/im_livechat/static/src/models/public_livechat.js b/addons/im_livechat/static/src/models/public_livechat.js index 8af2b0c9683..c9e49744b04 100644 --- a/addons/im_livechat/static/src/models/public_livechat.js +++ b/addons/im_livechat/static/src/models/public_livechat.js @@ -3,36 +3,38 @@ import PublicLivechat from '@im_livechat/legacy/models/public_livechat'; import { registerModel } from '@mail/model/model_core'; -import { attr, one } from '@mail/model/model_field'; -import { clear } from '@mail/model/model_field_command'; +import { attr, many, one } from '@mail/model/model_field'; registerModel({ name: 'PublicLivechat', identifyingFields: ['livechatButtonOwner'], - recordMethods: { - /** - * @private - * @returns {FieldCommand|im_livechat/legacy/models/public_livechat} - */ - _computeLegacyPublicLivechat() { - if (!this.data) { - return clear(); - } - return new PublicLivechat({ - parent: this.livechatButtonOwner.widget, - data: this.data, + lifecycleHooks: { + _created() { + this.update({ + legacyPublicLivechat: new PublicLivechat(this.messaging, { + parent: this.livechatButtonOwner.widget, + data: this.data, + }), }); }, + _willDelete() { + this.legacyPublicLivechat.destroy(); + }, + }, + recordMethods: { }, fields: { data: attr(), - legacyPublicLivechat: attr({ - compute: '_computeLegacyPublicLivechat', - }), + legacyPublicLivechat: attr(), livechatButtonOwner: one('LivechatButtonView', { inverse: 'publicLivechat', readonly: true, required: true, }), + messages: many('PublicLivechatMessage'), + // amount of messages that have not yet been read on this chat + unreadCounter: attr({ + default: 0, + }), }, }); diff --git a/addons/im_livechat/static/src/models/public_livechat_global.js b/addons/im_livechat/static/src/models/public_livechat_global.js new file mode 100644 index 00000000000..ba551eae407 --- /dev/null +++ b/addons/im_livechat/static/src/models/public_livechat_global.js @@ -0,0 +1,21 @@ +/** @odoo-module **/ + +import { registerModel } from '@mail/model/model_core'; +import { attr } from '@mail/model/model_field'; + +registerModel({ + name: 'PublicLivechatGlobal', + identifyingFields: ['messaging'], + fields: { + chatbotServerUrl: attr(), + isAvailable: attr({ + default: false, + }), + options: attr({ + default: {}, + }), + serverUrl: attr({ + default: '', + }), + }, +}); diff --git a/addons/im_livechat/static/src/models/public_livechat_message.js b/addons/im_livechat/static/src/models/public_livechat_message.js new file mode 100644 index 00000000000..e0853e88b87 --- /dev/null +++ b/addons/im_livechat/static/src/models/public_livechat_message.js @@ -0,0 +1,16 @@ +/** @odoo-module **/ + +import { registerModel } from '@mail/model/model_core'; +import { attr } from '@mail/model/model_field'; + +registerModel({ + name: 'PublicLivechatMessage', + identifyingFields: ['id'], + fields: { + id: attr({ + readonly: true, + required: true, + }), + legacyPublicLivechatMessage: attr(), + }, +}); diff --git a/addons/im_livechat/static/src/models/public_livechat_window.js b/addons/im_livechat/static/src/models/public_livechat_window.js index a6ad3972f11..cc69ef41179 100644 --- a/addons/im_livechat/static/src/models/public_livechat_window.js +++ b/addons/im_livechat/static/src/models/public_livechat_window.js @@ -4,43 +4,31 @@ import PublicLivechatWindow from '@im_livechat/legacy/widgets/public_livechat_wi import { registerModel } from '@mail/model/model_core'; import { attr, one } from '@mail/model/model_field'; -import { clear } from '@mail/model/model_field_command'; registerModel({ name: 'PublicLivechatWindow', identifyingFields: ['livechatButtonViewOwner'], lifecycleHooks: { - _willDelete() { - if (this.legacyChatWindow) { - this.legacyChatWindow.destroy(); - } + _created() { + this.update({ + legacyChatWindow: new PublicLivechatWindow( + this.livechatButtonViewOwner.widget, + this.messaging, + this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat, + { + headerBackgroundColor: this.livechatButtonViewOwner.headerBackgroundColor, + placeholder: this.livechatButtonViewOwner.inputPlaceholder, + titleColor: this.livechatButtonViewOwner.titleColor, + }, + ), + }); }, - }, - recordMethods: { - /** - * @private - * @returns {im_livechat/legacy/widgets/public_chat_window|FieldCommand} - */ - _computeLegacyChatWindow() { - if (!this.livechatButtonViewOwner.widget && this.legacyChatWindow) { - this.legacyChatWindow.destroy(); - return clear(); - } - return new PublicLivechatWindow( - this.livechatButtonViewOwner.widget, - this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat, - { - displayStars: false, - headerBackgroundColor: this.livechatButtonViewOwner.headerBackgroundColor, - placeholder: this.livechatButtonViewOwner.inputPlaceholder, - titleColor: this.livechatButtonViewOwner.titleColor, - }, - ); + _willDelete() { + this.legacyChatWindow.destroy(); }, }, fields: { legacyChatWindow: attr({ - compute: '_computeLegacyChatWindow', default: null, }), livechatButtonViewOwner: one('LivechatButtonView', { diff --git a/addons/im_livechat/static/src/public/main.js b/addons/im_livechat/static/src/public/main.js index 159711679f4..2e7f9f371a0 100644 --- a/addons/im_livechat/static/src/public/main.js +++ b/addons/im_livechat/static/src/public/main.js @@ -3,6 +3,7 @@ import { publicLivechatService } from '@im_livechat/services/public_livechat_service'; import { isAvailable, options, serverUrl } from 'im_livechat.loaderData'; +import { insertAndReplace } from '@mail/model/model_field_command'; import { messagingService } from '@mail/services/messaging_service'; import { makeMessagingToLegacyEnv } from '@mail/utils/make_messaging_to_legacy_env'; @@ -11,10 +12,7 @@ import { registry } from '@web/core/registry'; const messagingValuesService = { start() { return { - isInPublicLivechat: true, - isPublicLivechatAvailable: isAvailable, - publicLivechatOptions: options, - publicLivechatServerUrl: serverUrl, + publicLivechatGlobal: insertAndReplace({ isAvailable, options, serverUrl }), }; } }; diff --git a/addons/website_livechat/static/src/legacy/website_livechat_chatbot_test_script.js b/addons/website_livechat/static/src/legacy/website_livechat_chatbot_test_script.js index 1894a48f9c6..2aed88feef3 100644 --- a/addons/website_livechat/static/src/legacy/website_livechat_chatbot_test_script.js +++ b/addons/website_livechat/static/src/legacy/website_livechat_chatbot_test_script.js @@ -90,9 +90,7 @@ publicWidget.registry.livechatChatbotTestScript = publicWidget.Widget.extend({ const messaging = this.env.services.messaging.get(); return this._super(...arguments).then(() => { messaging.update({ - isInPublicLivechat: true, - isPublicLivechatAvailable: true, - publicLivechatServerUrlChatbot: this.$el.data().serverUrl, + publicLivechatGlobal: insertAndReplace({ isAvailable: true, chatbotServerUrl: this.$el.data().serverUrl }), }); this.livechatButton = new LivechatButtonTestChatbot(this, messaging, this.$el.data()); this.livechatButton.appendTo(document.body); diff --git a/addons/website_livechat/static/src/models/messaging.js b/addons/website_livechat/static/src/models/messaging.js deleted file mode 100644 index 92f4db61ebf..00000000000 --- a/addons/website_livechat/static/src/models/messaging.js +++ /dev/null @@ -1,10 +0,0 @@ -/** @odoo-module **/ - -import { addFields } from '@mail/model/model_core'; -import { attr } from '@mail/model/model_field'; -// ensure that the model definition is loaded before the patch -import '@mail/models/messaging'; - -addFields('Messaging', { - publicLivechatServerUrlChatbot: attr(), -});