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 @@