From 6c7f178bdc874b5a13a2010ee6238e6455086486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20K=C3=BChn?= Date: Thu, 28 Jul 2022 16:39:12 +0000 Subject: [PATCH] [IMP] im_livechat, *: Refactor JS Public Livechat (step 11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *: 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-2939083 closes odoo/odoo#97219 Signed-off-by: Alexandre Kühn (aku) --- .../src/legacy/public_livechat_chatbot.js | 95 +++++++++---------- .../src/legacy/widgets/livechat_button.js | 60 +----------- .../public_livechat_window.js | 9 +- .../static/src/models/livechat_button_view.js | 64 ++++++++++++- .../static/src/models/public_livechat.js | 3 +- .../src/models/public_livechat_global.js | 7 ++ ...ic_livechat_global_notification_handler.js | 2 +- .../src/models/public_livechat_message.js | 1 + .../src/models/public_livechat_window.js | 1 - .../website_livechat_chatbot_test_script.js | 2 +- 10 files changed, 125 insertions(+), 119 deletions(-) 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 fe1f0ed83a6..483c188f5dc 100644 --- a/addons/im_livechat/static/src/legacy/public_livechat_chatbot.js +++ b/addons/im_livechat/static/src/legacy/public_livechat_chatbot.js @@ -86,8 +86,8 @@ const QWeb = core.qweb; this.messaging.publicLivechatGlobal.livechatButtonView.update({ rule: this.messaging.publicLivechatGlobal.livechatButtonView.livechatInit.rule }); } else if (this.messaging.publicLivechatGlobal.livechatButtonView.chatbotState === 'restore_session') { // we landed on a website page and a chatbot script is currently running - // -> restore the user's session (see '_chatbotRestoreSession') - this._chatbotRestoreSession(); + // -> restore the user's session (see 'chatbotRestoreSession') + this.messaging.publicLivechatGlobal.livechatButtonView.chatbotRestoreSession(); } return superResult; @@ -106,7 +106,7 @@ const QWeb = core.qweb; */ _chatbotAddMessage(message, options) { message.body = utils.Markup(message.body); - this._addMessage(message, options); + this.messaging.publicLivechatGlobal.livechatButtonView.addMessage(message, options); if (this.messaging.publicLivechatGlobal.publicLivechat.isFolded || !this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.isAtBottom()) { this.messaging.publicLivechatGlobal.publicLivechat.update({ unreadCounter: increment() }); } @@ -135,8 +135,12 @@ const QWeb = core.qweb; this._chatbotEndScript(); } else { this._chatbotSetIsTyping(); - this.nextStepTimeout = setTimeout( - this._chatbotTriggerNextStep.bind(this), this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay); + this.messaging.publicLivechatGlobal.livechatButtonView.update({ + chatbotNextStepTimeout: setTimeout( + this._chatbotTriggerNextStep.bind(this), + this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay, + ) + }); } } }, @@ -305,8 +309,12 @@ const QWeb = core.qweb; if (this._isLastMessageFromCustomer()) { // user has already typed a message in -> trigger next step this._chatbotSetIsTyping(); - this.nextStepTimeout = setTimeout( - this._chatbotTriggerNextStep.bind(this), this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay); + this.messaging.publicLivechatGlobal.livechatButtonView.update({ + chatbotNextStepTimeout: setTimeout( + this._chatbotTriggerNextStep.bind(this), + this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay, + ), + }); } else { this._chatbotEnableInput(); } @@ -332,8 +340,12 @@ const QWeb = core.qweb; this._chatbotSetIsTyping(); } - this.nextStepTimeout = setTimeout( - this._chatbotTriggerNextStep.bind(this), nextStepDelay); + this.messaging.publicLivechatGlobal.livechatButtonView.update({ + chatbotNextStepTimeout: setTimeout( + this._chatbotTriggerNextStep.bind(this), + nextStepDelay, + ), + }); } } @@ -341,35 +353,6 @@ const QWeb = core.qweb; this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_main_restart').addClass('d-none'); } }, - /** - * See '_chatbotSaveSession'. - * - * We retrieve the livechat uuid from the session cookie since the livechat Widget is not yet - * initialized when we restore the chatbot state. - * - * We also clear any older keys that store a previously saved chatbot session. - * (In that case we clear the actual browser's local storage, we don't use the localStorage - * object as it does not allow browsing existing keys, see 'local_storage.js'.) - * - * @private - */ - _chatbotRestoreSession() { - const browserLocalStorage = window.localStorage; - 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.publicLivechatGlobal.livechatButtonView.chatbotSessionCookieKey) { - browserLocalStorage.removeItem(key); - } - } - } - - let chatbotState = localStorage.getItem(this.messaging.publicLivechatGlobal.livechatButtonView.chatbotSessionCookieKey); - - if (chatbotState) { - this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.update({ currentStep: insertAndReplace({ data: this.messaging.publicLivechatGlobal.livechatButtonView.localStorageChatbotState._chatbotCurrentStep }) }); - } - }, /** * Register current chatbot step state into localStorage to be able to resume if the visitor * goes to another website page or if he refreshes his page. @@ -700,8 +683,12 @@ const QWeb = core.qweb; this._debouncedChatbotAwaitUserInput(); } else if (!this._chatbotShouldEndScript()) { this._chatbotSetIsTyping(); - this.nextStepTimeout = setTimeout( - this._chatbotTriggerNextStep.bind(this), this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay); + this.messaging.publicLivechatGlobal.livechatButtonView.update({ + chatbotNextStepTimeout: setTimeout( + this._chatbotTriggerNextStep.bind(this), + this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay, + ), + }); } else { this._chatbotEndScript(); } @@ -750,7 +737,7 @@ const QWeb = core.qweb; this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.update({ currentStep: insertAndReplace({ data: chatbotStep }) }); if (chatbotStep.chatbot_step_message) { - this._addMessage({ + this.messaging.publicLivechatGlobal.livechatButtonView.addMessage({ id: '_welcome_' + stepIndex, is_discussion: true, // important for css style -> we only want white background for chatbot attachment_ids: [], @@ -777,10 +764,12 @@ const QWeb = core.qweb; this._chatbotSetIsTyping(true); } - this.welcomeMessageTimeout = setTimeout(() => { - this._sendWelcomeChatbotMessage(stepIndex + 1, welcomeMessageDelay); - this._renderMessages(); - }, welcomeMessageDelay); + this.messaging.publicLivechatGlobal.livechatButtonView.update({ + chatbotWelcomeMessageTimeout: setTimeout(() => { + this._sendWelcomeChatbotMessage(stepIndex + 1, welcomeMessageDelay); + this._renderMessages(); + }, welcomeMessageDelay), + }); } else { if (this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'forward_operator') { // special case when the last welcome message is a forward to an operator @@ -810,12 +799,12 @@ const QWeb = core.qweb; this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_composer_text_field').removeClass('d-none'); this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_end').hide(); - if (this.nextStepTimeout) { - clearTimeout(this.nextStepTimeout); + if (this.messaging.publicLivechatGlobal.livechatButtonView.chatbotNextStepTimeout) { + clearTimeout(this.messaging.publicLivechatGlobal.livechatButtonView.chatbotNextStepTimeout); } - if (this.welcomeMessageTimeout) { - clearTimeout(this.welcomeMessageTimeout); + if (this.messaging.publicLivechatGlobal.livechatButtonView.chatbotWelcomeMessageTimeout) { + clearTimeout(this.messaging.publicLivechatGlobal.livechatButtonView.chatbotWelcomeMessageTimeout); } const postedMessage = await session.rpc('/chatbot/restart', { @@ -829,8 +818,12 @@ const QWeb = core.qweb; this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.update({ currentStep: clear() }); this._chatbotSetIsTyping(); - this.nextStepTimeout = setTimeout( - this._chatbotTriggerNextStep.bind(this), this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay); + this.messaging.publicLivechatGlobal.livechatButtonView.update({ + chatbotNextStepTimeout: setTimeout( + this._chatbotTriggerNextStep.bind(this), + this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay, + ), + }); }, _onChatbotInputKeyDown() { 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 c96b4277138..7a2da22e5f6 100644 --- a/addons/im_livechat/static/src/legacy/widgets/livechat_button.js +++ b/addons/im_livechat/static/src/legacy/widgets/livechat_button.js @@ -7,12 +7,9 @@ import time from 'web.time'; import utils from 'web.utils'; import Widget from 'web.Widget'; -import PublicLivechatMessage from '@im_livechat/legacy/models/public_livechat_message'; - -import { clear, insertAndReplace, link, replace } from '@mail/model/model_field_command'; +import { clear, insertAndReplace } from '@mail/model/model_field_command'; const _t = core._t; -const QWeb = core.qweb; const LivechatButton = Widget.extend({ className: 'openerp o_livechat_button d-print-none', @@ -48,13 +45,13 @@ const LivechatButton = Widget.extend({ } this.messaging.publicLivechatGlobal.livechatButtonView.update({ rule: result.rule }); } - return this._loadQWebTemplate(); + return this.messaging.publicLivechatGlobal.loadQWebTemplate(); }, start() { this.$el.text(this.messaging.publicLivechatGlobal.livechatButtonView.buttonText); if (this.messaging.publicLivechatGlobal.livechatButtonView.history) { for (const m of this.messaging.publicLivechatGlobal.livechatButtonView.history) { - this._addMessage(m); + this.messaging.publicLivechatGlobal.livechatButtonView.addMessage(m); } this._openChat(); } else if (!config.device.isMobile && this.messaging.publicLivechatGlobal.livechatButtonView.rule.action === 'auto_popup') { @@ -85,45 +82,6 @@ const LivechatButton = Widget.extend({ // Private //-------------------------------------------------------------------------- - - /** - * @private - * @param {Object} data - * @param {Object} [options={}] - */ - _addMessage(data, options) { - const legacyMessage = new PublicLivechatMessage(this, this.messaging, data); - - const hasAlreadyMessage = _.some(this.messaging.publicLivechatGlobal.livechatButtonView.messages, function (msg) { - return legacyMessage.getID() === msg.id; - }); - if (hasAlreadyMessage) { - return; - } - const message = this.messaging.models['PublicLivechatMessage'].insert({ - id: data.id, - legacyPublicLivechatMessage: legacyMessage, - }); - if (this.messaging.publicLivechatGlobal.publicLivechat) { - this.messaging.publicLivechatGlobal.publicLivechat.update({ - messages: link(message), - }); - } - - if (this.messaging.publicLivechatGlobal.publicLivechat && this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat) { - this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat.addMessage(legacyMessage); - } - - if (options && options.prepend) { - this.messaging.publicLivechatGlobal.livechatButtonView.update({ - messages: replace([message, ...this.messaging.publicLivechatGlobal.livechatButtonView.messages]), - }); - } else { - this.messaging.publicLivechatGlobal.livechatButtonView.update({ - messages: replace([...this.messaging.publicLivechatGlobal.livechatButtonView.messages, message]), - }); - } - }, /** * @private */ @@ -138,16 +96,6 @@ const LivechatButton = Widget.extend({ this.messaging.publicLivechatGlobal.livechatButtonView.update({ chatWindow: clear() }); utils.set_cookie('im_livechat_session', "", -1); // remove cookie }, - /** - * @private - */ - _loadQWebTemplate() { - return session.rpc('/im_livechat/load_templates').then(function (templates) { - for (let template of templates) { - QWeb.add_template(template); - } - }); - }, /** * @private */ @@ -302,7 +250,7 @@ const LivechatButton = Widget.extend({ */ _sendWelcomeMessage() { if (this.messaging.publicLivechatGlobal.livechatButtonView.defaultMessage) { - this._addMessage({ + this.messaging.publicLivechatGlobal.livechatButtonView.addMessage({ id: '_welcome', attachment_ids: [], author_id: [ 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 77ccad96424..0dd1efe2c1d 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 @@ -32,13 +32,10 @@ const PublicLivechatWindow = Widget.extend({ * @param {Widget} parent * @param {Messaging} messaging * @param {@im_livechat/legacy/models/public_livechat} thread - * @param {Object} options - * @param {string} options.titleColor */ - init(parent, messaging, thread, options) { + init(parent, messaging, thread) { this._super(parent); this.messaging = messaging; - this.options = options; this._debouncedOnScroll = _.debounce(this._onScroll.bind(this), 100); }, @@ -75,8 +72,8 @@ const PublicLivechatWindow = Widget.extend({ if (this.messaging.publicLivechatGlobal.livechatButtonView.headerBackgroundColor) { this.$('.o_thread_window_header').css('background-color', this.messaging.publicLivechatGlobal.livechatButtonView.headerBackgroundColor); } - if (this.options.titleColor) { - this.$('.o_thread_window_header').css('color', this.options.titleColor); + if (this.messaging.publicLivechatGlobal.livechatButtonView.titleColor) { + this.$('.o_thread_window_header').css('color', this.messaging.publicLivechatGlobal.livechatButtonView.titleColor); } }, 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 3fcaf91eb8a..eed3d6bc1dd 100644 --- a/addons/im_livechat/static/src/models/livechat_button_view.js +++ b/addons/im_livechat/static/src/models/livechat_button_view.js @@ -1,13 +1,73 @@ /** @odoo-module **/ +import PublicLivechatMessage from '@im_livechat/legacy/models/public_livechat_message'; + import { registerModel } from '@mail/model/model_core'; import { attr, many, one } from '@mail/model/model_field'; -import { clear, insertAndReplace } from '@mail/model/model_field_command'; +import { clear, insertAndReplace, replace } from '@mail/model/model_field_command'; registerModel({ name: 'LivechatButtonView', identifyingFields: ['publicLivechatGlobalOwner'], recordMethods: { + /** + * @param {Object} data + * @param {Object} [options={}] + */ + addMessage(data, options) { + const legacyMessage = new PublicLivechatMessage(this, this.messaging, data); + + const hasAlreadyMessage = _.some(this.messages, function (msg) { + return legacyMessage.getID() === msg.id; + }); + if (hasAlreadyMessage) { + return; + } + const message = this.messaging.models['PublicLivechatMessage'].insert({ + data, + id: data.id, + legacyPublicLivechatMessage: legacyMessage, + }); + + if (this.messaging.publicLivechatGlobal.publicLivechat && this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat) { + this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat.addMessage(legacyMessage); + } + + if (options && options.prepend) { + this.update({ + messages: replace([message, ...this.messages]), + }); + } else { + this.update({ + messages: replace([...this.messages, message]), + }); + } + }, + /** + * See '_chatbotSaveSession'. + * + * We retrieve the livechat uuid from the session cookie since the livechat Widget is not yet + * initialized when we restore the chatbot state. + * + * We also clear any older keys that store a previously saved chatbot session. + * (In that case we clear the actual browser's local storage, we don't use the localStorage + * object as it does not allow browsing existing keys, see 'local_storage.js'.) + */ + chatbotRestoreSession() { + const browserLocalStorage = window.localStorage; + 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.chatbotSessionCookieKey) { + browserLocalStorage.removeItem(key); + } + } + } + const chatbotState = localStorage.getItem(this.chatbotSessionCookieKey); + if (chatbotState) { + this.chatbot.update({ currentStep: insertAndReplace({ data: this.localStorageChatbotState._chatbotCurrentStep }) }); + } + }, /** * @private * @returns {string} @@ -213,12 +273,14 @@ registerModel({ inverse: 'livechatButtonViewOwner', isCausal: true, }), + chatbotNextStepTimeout: attr(), chatbotSessionCookieKey: attr({ compute: '_computeChatbotSessionCookieKey', }), chatbotState: attr({ compute: '_computeChatbotState', }), + chatbotWelcomeMessageTimeout: attr(), chatWindow: one('PublicLivechatWindow', { inverse: 'livechatButtonViewOwner', isCausal: true, diff --git a/addons/im_livechat/static/src/models/public_livechat.js b/addons/im_livechat/static/src/models/public_livechat.js index 844166361e7..70efa97a0d7 100644 --- a/addons/im_livechat/static/src/models/public_livechat.js +++ b/addons/im_livechat/static/src/models/public_livechat.js @@ -3,7 +3,7 @@ import PublicLivechat from '@im_livechat/legacy/models/public_livechat'; import { registerModel } from '@mail/model/model_core'; -import { attr, many, one } from '@mail/model/model_field'; +import { attr, one } from '@mail/model/model_field'; import { clear, insertAndReplace } from '@mail/model/model_field_command'; registerModel({ @@ -97,7 +97,6 @@ registerModel({ readonly: true, required: true, }), - messages: many('PublicLivechatMessage'), name: attr({ compute: '_computeName', }), diff --git a/addons/im_livechat/static/src/models/public_livechat_global.js b/addons/im_livechat/static/src/models/public_livechat_global.js index 7300b053cbe..5828175186f 100644 --- a/addons/im_livechat/static/src/models/public_livechat_global.js +++ b/addons/im_livechat/static/src/models/public_livechat_global.js @@ -4,6 +4,7 @@ import { registerModel } from '@mail/model/model_core'; import { attr, one } from '@mail/model/model_field'; import { clear, insertAndReplace } from '@mail/model/model_field_command'; +import { qweb } from 'web.core'; import utils from 'web.utils'; registerModel({ @@ -28,6 +29,12 @@ registerModel({ }, }, recordMethods: { + async loadQWebTemplate() { + const templates = await this.messaging.rpc({ route: '/im_livechat/load_templates' }); + for (const template of templates) { + qweb.add_template(template); + } + }, /** * @private * @returns {FieldCommand} diff --git a/addons/im_livechat/static/src/models/public_livechat_global_notification_handler.js b/addons/im_livechat/static/src/models/public_livechat_global_notification_handler.js index ca25f5c54c5..e3bdedb03e7 100644 --- a/addons/im_livechat/static/src/models/public_livechat_global_notification_handler.js +++ b/addons/im_livechat/static/src/models/public_livechat_global_notification_handler.js @@ -65,7 +65,7 @@ registerModel({ return; } notificationData.body = utils.Markup(notificationData.body); - this.messaging.publicLivechatGlobal.livechatButtonView.widget._addMessage(notificationData); + this.messaging.publicLivechatGlobal.livechatButtonView.addMessage(notificationData); if (this.messaging.publicLivechatGlobal.publicLivechat.isFolded || !this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.isAtBottom()) { this.messaging.publicLivechatGlobal.publicLivechat.update({ unreadCounter: increment() }); } diff --git a/addons/im_livechat/static/src/models/public_livechat_message.js b/addons/im_livechat/static/src/models/public_livechat_message.js index e0853e88b87..8a92f022229 100644 --- a/addons/im_livechat/static/src/models/public_livechat_message.js +++ b/addons/im_livechat/static/src/models/public_livechat_message.js @@ -7,6 +7,7 @@ registerModel({ name: 'PublicLivechatMessage', identifyingFields: ['id'], fields: { + data: attr(), id: attr({ readonly: true, required: true, 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 95eb56ad1da..4f61034a4b1 100644 --- a/addons/im_livechat/static/src/models/public_livechat_window.js +++ b/addons/im_livechat/static/src/models/public_livechat_window.js @@ -15,7 +15,6 @@ registerModel({ this.livechatButtonViewOwner.widget, this.messaging, this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat, - { titleColor: this.livechatButtonViewOwner.titleColor }, ), }); }, 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 47ff9e09c1c..0e9a369719f 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 @@ -44,7 +44,7 @@ const LivechatButtonTestChatbot = LivechatButton.extend({ * @override */ willStart: function () { - return this._loadQWebTemplate(); + return this.messaging.publicLivechatGlobal.loadQWebTemplate(); }, /**