From c27de6c738af1f9d4f7a50ed9d4a854c0f4e247f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20K=C3=BChn?= Date: Wed, 22 Jun 2022 09:25:02 +0000 Subject: [PATCH] [IMP] im_livechat, *: Refactor JS Public Livechat (step 5) 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-2894102 closes odoo/odoo#94508 Signed-off-by: Alexandre Kühn (aku) --- .../src/legacy/public_livechat_chatbot.js | 170 ++++++++++-------- .../website_livechat_message_chatbot.js | 4 - .../src/legacy/widgets/livechat_button.js | 5 +- .../im_livechat/static/src/models/chatbot.js | 71 ++++++++ .../static/src/models/chatbot_step.js | 17 ++ .../static/src/models/livechat_button_view.js | 103 ++++++++++- .../website_livechat_chatbot_test_script.js | 16 +- 7 files changed, 286 insertions(+), 100 deletions(-) create mode 100644 addons/im_livechat/static/src/models/chatbot.js create mode 100644 addons/im_livechat/static/src/models/chatbot_step.js 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 b2d7a94a776..914488e002b 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 } from '@mail/model/model_field_command'; +import { clear, insertAndReplace } from '@mail/model/model_field_command'; const _t = core._t; const QWeb = core.qweb; @@ -52,19 +52,14 @@ const QWeb = core.qweb; const superResult = await this._super(...arguments); if (this.messaging.livechatButtonView.rule && !!this.messaging.livechatButtonView.rule.chatbot) { - this.messaging.livechatButtonView.update({ isChatbot: true }); - this.messaging.livechatButtonView.update({ chatbotState: 'init' }); + // noop } else if (this.messaging.livechatButtonView.history !== null && this.messaging.livechatButtonView.history.length === 0) { - this.messaging.livechatButtonView.update({ livechatInit: await session.rpc('/im_livechat/init', {channel_id: this.options.channel_id}) }); - if (this.messaging.livechatButtonView.livechatInit.rule.chatbot) { - this.messaging.livechatButtonView.update({ isChatbot: true }); - this.messaging.livechatButtonView.update({ chatbotState: 'welcome' }); - } + this.messaging.livechatButtonView.update({ livechatInit: await session.rpc('/im_livechat/init', {channel_id: this.messaging.livechatButtonView.channelId}) }); } else if (this.messaging.livechatButtonView.history !== null && this.messaging.livechatButtonView.history.length !== 0) { const sessionCookie = utils.get_cookie('im_livechat_session'); if (sessionCookie) { - const sessionKey = 'im_livechat.chatbot.state.uuid_' + JSON.parse(sessionCookie).uuid; - if (localStorage.getItem(sessionKey)) { + this.messaging.livechatButtonView.update({ sessionCookie }); + if (localStorage.getItem(this.messaging.livechatButtonView.sessionCookieKey)) { this.messaging.livechatButtonView.update({ isChatbot: true }); this.messaging.livechatButtonView.update({ chatbotState: 'restore_session' }); } @@ -74,14 +69,11 @@ const QWeb = core.qweb; if (this.messaging.livechatButtonView.chatbotState === 'init') { // we landed on a website page where a channel rule is configured to run a chatbot.script // -> initialize necessary state - this.messaging.livechatButtonView.update({ - chatbot: this.messaging.livechatButtonView.rule.chatbot, - }); if (this.messaging.livechatButtonView.rule.chatbot_welcome_steps && this.messaging.livechatButtonView.rule.chatbot_welcome_steps.length !== 0) { - this.messaging.livechatButtonView.update({ - chatbotCurrentStep: this.messaging.livechatButtonView.chatbot.chatbot_welcome_steps[ - this.messaging.livechatButtonView.chatbot.chatbot_welcome_steps.length - 1 - ], + this.messaging.livechatButtonView.chatbot.update({ + currentStep: insertAndReplace({ + data: this.messaging.livechatButtonView.chatbot.lastWelcomeStep, + }), }); } } else if (this.messaging.livechatButtonView.chatbotState === 'welcome') { @@ -93,13 +85,11 @@ const QWeb = core.qweb; utils.set_cookie('im_livechat_auto_popup', '', -1); this.messaging.livechatButtonView.update({ history: clear() }); this.messaging.livechatButtonView.update({ rule: this.messaging.livechatButtonView.livechatInit.rule }); - this.messaging.livechatButtonView.update({ chatbot: this.messaging.livechatButtonView.livechatInit.rule.chatbot }); - this.messaging.livechatButtonView.update({ isChatbot: true }); this.messaging.livechatButtonView.update({ isChatbotBatchWelcomeMessages: true }); } else if (this.messaging.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(utils.get_cookie('im_livechat_session')); + this._chatbotRestoreSession(); } return superResult; @@ -148,7 +138,7 @@ const QWeb = core.qweb; } else { this._chatbotSetIsTyping(); this.nextStepTimeout = setTimeout( - this._chatbotTriggerNextStep.bind(this), this.messaging.livechatButtonView.chatbotMessageDelay); + this._chatbotTriggerNextStep.bind(this), this.messaging.livechatButtonView.chatbot.messageDelay); } } }, @@ -176,7 +166,7 @@ const QWeb = core.qweb; const postedWelcomeMessages = await session.rpc('/chatbot/post_welcome_steps', { channel_uuid: this.messaging.livechatButtonView.livechat.getUUID(), - chatbot_script_id: this.messaging.livechatButtonView.chatbot.chatbot_script_id, + chatbot_script_id: this.messaging.livechatButtonView.chatbot.data.chatbot_script_id, }); const welcomeMessagesIds = welcomeMessages.map(welcomeMessage => welcomeMessage._id); @@ -225,7 +215,7 @@ const QWeb = core.qweb; $composerTextField.off('keydown', this.chatbotInputKeyDownHandler); } - if (this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_step_type === 'free_input_multi') { + if (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'free_input_multi') { this.chatbotInputKeyDownHandler = this._onChatbotInputKeyDown.bind(this); $composerTextField.on('keydown', this.chatbotInputKeyDownHandler); } @@ -237,7 +227,11 @@ const QWeb = core.qweb; * @private */ _chatbotEndScript() { - if (this.messaging.livechatButtonView.chatbotCurrentStep && this.messaging.livechatButtonView.chatbotCurrentStep.conversation_closed) { + if ( + this.messaging.livechatButtonView.chatbot.currentStep && + this.messaging.livechatButtonView.chatbot.currentStep.data && + this.messaging.livechatButtonView.chatbot.currentStep.data.conversation_closed + ) { // don't touch anything if the user has closed the conversation, let the chat window // handle the display return; @@ -257,9 +251,9 @@ const QWeb = core.qweb; * @private */ _chatbotDisplayRestartButton() { - return this.messaging.livechatButtonView.isChatbot && (!this.messaging.livechatButtonView.chatbotCurrentStep || - (this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_step_type !== 'forward_operator' || - !this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_operator_found)); + return this.messaging.livechatButtonView.isChatbot && (!this.messaging.livechatButtonView.chatbot.currentStep || + (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type !== 'forward_operator' || + !this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_operator_found)); }, /** * Works as a hook since other modules can add their own step types. @@ -272,7 +266,7 @@ const QWeb = core.qweb; 'question_email', 'free_input_single', 'free_input_multi', - ].includes(this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_step_type); + ].includes(this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type); }, /** * Processes the step, depending on the current state of the script and the author of the last @@ -304,21 +298,21 @@ const QWeb = core.qweb; _chatbotProcessStep() { if (this._chatbotShouldEndScript()) { this._chatbotEndScript(); - } else if (this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_step_type === 'forward_operator' - && this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_operator_found) { + } else if (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'forward_operator' + && this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_operator_found) { this._chatbotEnableInput(); } else if (this._chatbotIsExpectingUserInput()) { if (this._isLastMessageFromCustomer()) { // user has already typed a message in -> trigger next step this._chatbotSetIsTyping(); this.nextStepTimeout = setTimeout( - this._chatbotTriggerNextStep.bind(this), this.messaging.livechatButtonView.chatbotMessageDelay); + this._chatbotTriggerNextStep.bind(this), this.messaging.livechatButtonView.chatbot.messageDelay); } else { this._chatbotEnableInput(); } } else { let triggerNextStep = true; - if (this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_step_type === 'question_selection') { + if (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'question_selection') { if (!this._isLastMessageFromCustomer()) { // if there is no last message or if the last message is from the bot // -> don't trigger the next step, we are waiting for the user to pick an option @@ -327,7 +321,7 @@ const QWeb = core.qweb; } if (triggerNextStep) { - let nextStepDelay = this.messaging.livechatButtonView.chatbotMessageDelay; + let nextStepDelay = this.messaging.livechatButtonView.chatbot.messageDelay; if (this.messaging.livechatButtonView.chatWindow.$('.o_livechat_chatbot_typing').length !== 0) { // special case where we already have a "is typing" message displayed // can happen when the previous step did not trigger any message posted from the bot @@ -359,25 +353,22 @@ const QWeb = core.qweb; * * @private */ - _chatbotRestoreSession(sessionCookie) { - const sessionKey = 'im_livechat.chatbot.state.uuid_' + JSON.parse(sessionCookie).uuid; + _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 !== sessionKey) { + if (key.startsWith('im_livechat.chatbot.state.uuid_') && key !== this.messaging.livechatButtonView.sessionCookieKey) { browserLocalStorage.removeItem(key); } } } - let chatbotState = localStorage.getItem(sessionKey); + let chatbotState = localStorage.getItem(this.messaging.livechatButtonView.sessionCookieKey); if (chatbotState) { - chatbotState = JSON.parse(chatbotState); - this.messaging.livechatButtonView.update({ isChatbot: true }); - this.messaging.livechatButtonView.update({ chatbot: chatbotState._chatbot }); - this.messaging.livechatButtonView.update({ chatbotCurrentStep: chatbotState._chatbotCurrentStep }); + this.messaging.livechatButtonView.update({ localStorageChatbotState: JSON.parse(chatbotState) }); + this.messaging.livechatButtonView.chatbot.update({ currentStep: insertAndReplace({ data: this.messaging.livechatButtonView.localStorageChatbotState._chatbotCurrentStep }) }); } }, /** @@ -392,8 +383,8 @@ const QWeb = core.qweb; _chatbotSaveSession() { const chatUuid = this.messaging.livechatButtonView.livechat.toData().uuid; localStorage.setItem('im_livechat.chatbot.state.uuid_' + chatUuid, JSON.stringify({ - '_chatbot': this.messaging.livechatButtonView.chatbot, - '_chatbotCurrentStep': this.messaging.livechatButtonView.chatbotCurrentStep, + '_chatbot': this.messaging.livechatButtonView.chatbot.data, + '_chatbotCurrentStep': this.messaging.livechatButtonView.chatbot.currentStep.data, })); }, /** @@ -415,13 +406,13 @@ const QWeb = core.qweb; 'chatbotImageSrc': `/im_livechat/operator/${ this.messaging.livechatButtonView.livechat.getOperatorPID()[0] }/avatar`, - 'chatbotName': this.messaging.livechatButtonView.chatbot.chatbot_name, + 'chatbotName': this.messaging.livechatButtonView.chatbot.name, 'isWelcomeMessage': isWelcomeMessage, })) ); this.messaging.livechatButtonView.chatWindow.scrollToBottom(); - }, this.messaging.livechatButtonView.chatbotMessageDelay / 3), + }, this.messaging.livechatButtonView.chatbot.messageDelay / 3), }); }, /** @@ -439,21 +430,21 @@ const QWeb = core.qweb; * @private */ _chatbotShouldEndScript() { - if (this.messaging.livechatButtonView.chatbotCurrentStep.conversation_closed) { + if (this.messaging.livechatButtonView.chatbot.currentStep.data.conversation_closed) { return true; } - if (this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_step_is_last && - (this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_step_type !== 'forward_operator' || - !this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_operator_found) + if (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_is_last && + (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type !== 'forward_operator' || + !this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_operator_found) ) { - if (this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_step_type === 'question_email' - && !this.messaging.livechatButtonView.chatbotCurrentStep.is_email_valid + if (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'question_email' + && !this.messaging.livechatButtonView.chatbot.currentStep.data.is_email_valid ) { // email is not (yet) valid, let the user answer / try again return false; } else if ( (this._chatbotIsExpectingUserInput() || - this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_step_type === 'question_selection') && + this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'question_selection') && this.messaging.livechatButtonView.messages.length !== 0 ) { const lastMessage = this.messaging.livechatButtonView.messages[this.messaging.livechatButtonView.messages.length - 1]; @@ -489,7 +480,7 @@ const QWeb = core.qweb; }); if (emailValidResult.success) { - this.messaging.livechatButtonView.chatbotCurrentStep.is_email_valid = true; + this.messaging.livechatButtonView.chatbot.currentStep.data.is_email_valid = true; this._chatbotSaveSession(); return true; @@ -511,7 +502,11 @@ const QWeb = core.qweb; */ async _chatbotTriggerNextStep() { let triggerNextStep = true; - if (this.messaging.livechatButtonView.chatbotCurrentStep && this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_step_type === 'question_email') { + if ( + this.messaging.livechatButtonView.chatbot.currentStep && + this.messaging.livechatButtonView.chatbot.currentStep.data && + this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'question_email' + ) { triggerNextStep = await this._chatbotValidateEmail(); } @@ -521,7 +516,7 @@ const QWeb = core.qweb; const nextStep = await session.rpc('/chatbot/step/trigger', { channel_uuid:this.messaging.livechatButtonView.livechat.getUUID(), - chatbot_script_id: this.messaging.livechatButtonView.chatbot.chatbot_script_id, + chatbot_script_id: this.messaging.livechatButtonView.chatbot.data.chatbot_script_id, }); if (nextStep) { @@ -529,12 +524,12 @@ const QWeb = core.qweb; this._chatbotAddMessage(nextStep.chatbot_posted_message); } - this.messaging.livechatButtonView.update({ chatbotCurrentStep: nextStep.chatbot_step }); + this.messaging.livechatButtonView.chatbot.update({ currentStep: insertAndReplace({ data: nextStep.chatbot_step }) }); this._chatbotProcessStep(); } else { // did not find next step -> end the script - this.messaging.livechatButtonView.chatbotCurrentStep['chatbot_step_is_last'] = true; + this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_is_last = true; this._renderMessages(); this._chatbotEndScript(); } @@ -578,8 +573,12 @@ const QWeb = core.qweb; _askFeedback() { this._super(...arguments); - if (this.messaging.livechatButtonView.chatbotCurrentStep) { - this.messaging.livechatButtonView.chatbotCurrentStep.conversation_closed = true; + if ( + this.messaging.livechatButtonView.chatbot && + this.messaging.livechatButtonView.chatbot.currentStep && + this.messaging.livechatButtonView.chatbot.currentStep.data + ) { + this.messaging.livechatButtonView.chatbot.currentStep.data.conversation_closed = true; this._chatbotSaveSession(); } this.messaging.livechatButtonView.chatWindow.$('.o_livechat_chatbot_main_restart').addClass('d-none'); @@ -607,7 +606,13 @@ const QWeb = core.qweb; } }); - if (this.messaging.livechatButtonView.chatbotCurrentStep && this.messaging.livechatButtonView.messages && this.messaging.livechatButtonView.messages.length !== 0) { + if ( + this.messaging.livechatButtonView.chatbot && + this.messaging.livechatButtonView.chatbot.currentStep && + this.messaging.livechatButtonView.chatbot.currentStep.data && + this.messaging.livechatButtonView.messages && + this.messaging.livechatButtonView.messages.length !== 0 + ) { this._chatbotProcessStep(); } }); @@ -620,7 +625,7 @@ const QWeb = core.qweb; const parameters = this._super(...arguments); if (this.messaging.livechatButtonView.isChatbot) { - parameters.chatbot_script_id = this.messaging.livechatButtonView.chatbot.chatbot_script_id; + parameters.chatbot_script_id = this.messaging.livechatButtonView.chatbot.data.chatbot_script_id; } return parameters; @@ -670,7 +675,11 @@ const QWeb = core.qweb; const superArguments = arguments; const superMethod = this._super; - if (this.messaging.livechatButtonView.isChatbot && this.messaging.livechatButtonView.chatbotCurrentStep) { + if ( + this.messaging.livechatButtonView.isChatbot && + this.messaging.livechatButtonView.chatbot.currentStep && + this.messaging.livechatButtonView.chatbot.currentStep.data + ) { await this._chatbotPostWelcomeMessages(); } @@ -679,18 +688,22 @@ const QWeb = core.qweb; return; } - if (this.messaging.livechatButtonView.isChatbot && this.messaging.livechatButtonView.chatbotCurrentStep) { + if ( + this.messaging.livechatButtonView.isChatbot && + this.messaging.livechatButtonView.chatbot.currentStep && + this.messaging.livechatButtonView.chatbot.currentStep.data + ) { if ( - this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_step_type === 'forward_operator' && - this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_operator_found + this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'forward_operator' && + this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_operator_found ) { return; // operator has taken over the conversation, let them speak - } else if (this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_step_type === 'free_input_multi') { + } else if (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'free_input_multi') { this._debouncedChatbotAwaitUserInput(); } else if (!this._chatbotShouldEndScript()) { this._chatbotSetIsTyping(); this.nextStepTimeout = setTimeout( - this._chatbotTriggerNextStep.bind(this), this.messaging.livechatButtonView.chatbotMessageDelay); + this._chatbotTriggerNextStep.bind(this), this.messaging.livechatButtonView.chatbot.messageDelay); } else { this._chatbotEndScript(); } @@ -707,7 +720,7 @@ const QWeb = core.qweb; if (this.messaging.livechatButtonView.isChatbot) { this._sendWelcomeChatbotMessage( 0, - this.messaging.livechatButtonView.isChatbotBatchWelcomeMessages ? 0 : this.messaging.livechatButtonView.chatbotMessageDelay, + this.messaging.livechatButtonView.isChatbotBatchWelcomeMessages ? 0 : this.messaging.livechatButtonView.chatbot.messageDelay, ); } else { this._super(...arguments); @@ -735,8 +748,8 @@ const QWeb = core.qweb; * @private */ _sendWelcomeChatbotMessage(stepIndex, welcomeMessageDelay) { - const chatbotStep = this.messaging.livechatButtonView.chatbot.chatbot_welcome_steps[stepIndex]; - this.messaging.livechatButtonView.update({ chatbotCurrentStep: chatbotStep }); + const chatbotStep = this.messaging.livechatButtonView.chatbot.welcomeSteps[stepIndex]; + this.messaging.livechatButtonView.chatbot.update({ currentStep: insertAndReplace({ data: chatbotStep }) }); if (chatbotStep.chatbot_step_message) { this._addMessage({ @@ -754,7 +767,7 @@ const QWeb = core.qweb; }); } - if (stepIndex + 1 < this.messaging.livechatButtonView.chatbot.chatbot_welcome_steps.length) { + if (stepIndex + 1 < this.messaging.livechatButtonView.chatbot.welcomeSteps.length) { if (welcomeMessageDelay !== 0) { this._chatbotSetIsTyping(true); } @@ -764,7 +777,7 @@ const QWeb = core.qweb; this._renderMessages(); }, welcomeMessageDelay); } else { - if (this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_step_type === 'forward_operator') { + if (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'forward_operator') { // special case when the last welcome message is a forward to an operator // we need to save the welcome messages before continuing the script // indeed, if there are no operator available, the script will continue @@ -802,23 +815,24 @@ const QWeb = core.qweb; const postedMessage = await session.rpc('/chatbot/restart', { channel_uuid: this.messaging.livechatButtonView.livechat.getUUID(), - chatbot_script_id: this.messaging.livechatButtonView.chatbot.chatbot_script_id, + chatbot_script_id: this.messaging.livechatButtonView.chatbot.data.chatbot_script_id, }); if (postedMessage) { this._chatbotAddMessage(postedMessage); } - this.messaging.livechatButtonView.update({ chatbotCurrentStep: clear() }); + this.messaging.livechatButtonView.chatbot.update({ currentStep: clear() }); this._chatbotSetIsTyping(); this.nextStepTimeout = setTimeout( - this._chatbotTriggerNextStep.bind(this), this.messaging.livechatButtonView.chatbotMessageDelay); + this._chatbotTriggerNextStep.bind(this), this.messaging.livechatButtonView.chatbot.messageDelay); }, _onChatbotInputKeyDown() { if ( - this.messaging.livechatButtonView.chatbotCurrentStep && - this.messaging.livechatButtonView.chatbotCurrentStep.chatbot_step_type === 'free_input_multi' + this.messaging.livechatButtonView.chatbot.currentStep && + this.messaging.livechatButtonView.chatbot.currentStep.data && + this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'free_input_multi' ) { this._debouncedChatbotAwaitUserInput(); } @@ -863,7 +877,7 @@ const QWeb = core.qweb; } const messageId = stepMessage.getID(); stepMessage.setChatbotStepAnswerId(selectedAnswer); - this.messaging.livechatButtonView.chatbotCurrentStep['chatbot_selected_answer_id'] = selectedAnswer; + this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_selected_answer_id = selectedAnswer; this._renderMessages(); this._chatbotSaveSession(); diff --git a/addons/im_livechat/static/src/legacy/website_livechat_message_chatbot.js b/addons/im_livechat/static/src/legacy/website_livechat_message_chatbot.js index 98b3ac46351..6e0f6fcec54 100644 --- a/addons/im_livechat/static/src/legacy/website_livechat_message_chatbot.js +++ b/addons/im_livechat/static/src/legacy/website_livechat_message_chatbot.js @@ -20,10 +20,6 @@ WebsiteLivechatMessage.include({ this._super(...arguments); if (parent.messaging.livechatButtonView.isChatbot) { - this._chatbotId = parent.messaging.livechatButtonView.chatbot.chatbot_script_id; - this._chatbotName = parent.messaging.livechatButtonView.chatbot.chatbot_name; - this._chatbotOperatorPartnerId = parent.messaging.livechatButtonView.chatbot.chatbot_operator_partner_id; - this._chatbotStepId = data.chatbot_script_step_id; this._chatbotStepAnswers = data.chatbot_step_answers; this._chatbotStepAnswerId = data.chatbot_selected_answer_id; 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 eabcb581033..38d65ab3e8b 100644 --- a/addons/im_livechat/static/src/legacy/widgets/livechat_button.js +++ b/addons/im_livechat/static/src/legacy/widgets/livechat_button.js @@ -36,6 +36,7 @@ const LivechatButton = Widget.extend({ this.options = _.defaults(this.messaging.publicLivechatOptions || {}); }, async willStart() { + this.messaging.livechatButtonView.update({ widget: this }); const cookie = utils.get_cookie('im_livechat_session'); if (cookie) { const channel = JSON.parse(cookie); @@ -46,7 +47,7 @@ const LivechatButton = Widget.extend({ message.body = utils.Markup(message.body); } } else { - const result = await session.rpc('/im_livechat/init', {channel_id: this.options.channel_id}); + const result = await session.rpc('/im_livechat/init', {channel_id: this.messaging.livechatButtonView.channelId}); if (!result.available_for_me) { return Promise.reject(); } @@ -332,7 +333,7 @@ const LivechatButton = Widget.extend({ */ _prepareGetSessionParameters() { return { - channel_id: this.options.channel_id, + channel_id: this.messaging.livechatButtonView.channelId, anonymous_name: this.messaging.livechatButtonView.defaultUsername, previous_operator_id: this._get_previous_operator_id(), }; diff --git a/addons/im_livechat/static/src/models/chatbot.js b/addons/im_livechat/static/src/models/chatbot.js new file mode 100644 index 00000000000..0b6e276a5c9 --- /dev/null +++ b/addons/im_livechat/static/src/models/chatbot.js @@ -0,0 +1,71 @@ +/** @odoo-module **/ + +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: 'Chatbot', + identifyingFields: ['livechatButtonViewOwner'], + recordMethods: { + /** + * @private + * @returns {string} + */ + _computeName() { + return this.data.name; + }, + /** + * @private + * @returns {Array|FieldCommand} + */ + _computeLastWelcomeStep() { + if (!this.welcomeSteps) { + return clear(); + } + return this.welcomeSteps[this.welcomeSteps.length - 1]; + }, + /** + * @private + * @returns {integer|FieldCommand} + */ + _computeMessageDelay() { + if (this.livechatButtonViewOwner.isWebsiteLivechatChatbotFlow) { + return 100; + } + return clear(); + }, + /** + * @private + * @returns {Array|FieldCommand} + */ + _computeWelcomeSteps() { + return this.data.chatbot_welcome_steps; + }, + }, + fields: { + data: attr(), + currentStep: one('ChatbotStep', { + inverse: 'chabotOwner', + isCausal: true, + }), + lastWelcomeStep: attr({ + compute: '_computeLastWelcomeStep', + }), + livechatButtonViewOwner: one('LivechatButtonView', { + inverse: 'chatbot', + readonly: true, + required: true, + }), + name: attr({ + compute: '_computeName', + }), + messageDelay: attr({ + compute: '_computeMessageDelay', + default: 3500, // in milliseconds + }), + welcomeSteps: attr({ + compute: '_computeWelcomeSteps', + }), + }, +}); diff --git a/addons/im_livechat/static/src/models/chatbot_step.js b/addons/im_livechat/static/src/models/chatbot_step.js new file mode 100644 index 00000000000..722f45a437a --- /dev/null +++ b/addons/im_livechat/static/src/models/chatbot_step.js @@ -0,0 +1,17 @@ +/** @odoo-module **/ + +import { registerModel } from '@mail/model/model_core'; +import { attr, one } from '@mail/model/model_field'; + +registerModel({ + name: 'ChatbotStep', + identifyingFields: ['chabotOwner'], + fields: { + chabotOwner: one('Chatbot', { + inverse: 'currentStep', + readonly: true, + required: true, + }), + data: attr(), + }, +}); 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 fec2acf2c03..64104489dd3 100644 --- a/addons/im_livechat/static/src/models/livechat_button_view.js +++ b/addons/im_livechat/static/src/models/livechat_button_view.js @@ -1,8 +1,8 @@ /** @odoo-module **/ import { registerModel } from '@mail/model/model_core'; -import { attr } from '@mail/model/model_field'; -import { clear } from '@mail/model/model_field_command'; +import { attr, one } from '@mail/model/model_field'; +import { clear, insertAndReplace } from '@mail/model/model_field_command'; registerModel({ name: 'LivechatButtonView', @@ -27,9 +27,35 @@ registerModel({ /** * @returns {string} */ - _computeButtonTextColor() { + _computeButtonTextColor() { return this.messaging.publicLivechatOptions.button_text_color; }, + /** + * @private + * @returns {integer} + */ + _computeChannelId() { + return this.messaging.publicLivechatOptions.channel_id; + }, + /** + * @private + * @returns {FieldCommand} + */ + _computeChatbot() { + if (this.isTestChatbot) { + return insertAndReplace({ data: this.testChatbotData.chatbot }); + } + if (this.chatbotState === 'init') { + return insertAndReplace({ data: this.rule.chatbot }); + } + if (this.chatbotState === 'welcome') { + return insertAndReplace({ data: this.livechatInit.rule.chatbot }); + } + if (this.chatbotState === 'restore_session' && this.localStorageChatbotState) { + return insertAndReplace({ data: this.localStorageChatbotState._chatbot }); + } + return clear(); + }, /** * @private * @returns {integer|FieldCommand} @@ -40,6 +66,19 @@ registerModel({ } return clear(); }, + /** + * @private + * @returns {string|FieldCommand} + */ + _computeChatbotState() { + if (this.rule && !!this.rule.chatbot) { + return 'init'; + } + if (this.livechatInit && this.livechatInit.rule.chatbot) { + return 'welcome'; + } + return clear(); + }, /** * @private * @returns {integer} @@ -92,6 +131,28 @@ registerModel({ } return this.env._t("Ask something ..."); }, + /** + * @private + * @returns {boolean} + */ + _computeIsChatbot() { + if (this.isTestChatbot) { + return true; + } + if (this.rule && this.rule.chatbot) { + return true; + } + if (this.livechatInit && this.livechatInit.rule.chatbot) { + return true; + } + if (this.chatbotState === 'welcome') { + return true; + } + if (this.localStorageChatbotState) { + return true; + } + return clear(); + }, /** * @private * @returns {string} @@ -102,6 +163,15 @@ registerModel({ } return this.messaging.publicLivechatServerUrl; }, + /** + * @returns {string|FieldCommand} + */ + _computeSessionCookieKey() { + if (!this.sessionCookie) { + return clear(); + } + return 'im_livechat.chatbot.state.uuid_' + JSON.parse(this.sessionCookie).uuid; + }, /** * @private * @returns {string} @@ -121,13 +191,17 @@ registerModel({ buttonTextColor: attr({ compute: '_computeButtonTextColor', }), - chatbot: attr(), - chatbotCurrentStep: attr(), - chatbotMessageDelay: attr({ - compute: '_computeChatbotMessageDelay', - default: 3500, // in milliseconds + channelId: attr({ + compute: '_computeChannelId', + }), + chatbot: one('Chatbot', { + compute: '_computeChatbot', + inverse: 'livechatButtonViewOwner', + isCausal: true, + }), + chatbotState: attr({ + compute: '_computeChatbotState', }), - chatbotState: attr(), // livechat window chatWindow: attr({ default: null, @@ -152,6 +226,7 @@ registerModel({ default: '', }), isChatbot: attr({ + compute: '_computeIsChatbot', default: false, }), isChatbotBatchWelcomeMessages: attr({ @@ -163,6 +238,9 @@ registerModel({ isOpeningChat: attr({ default: false, }), + isTestChatbot: attr({ + default: false, + }), isTypingTimeout: attr(), isWebsiteLivechatChatbotFlow: attr({ default: false, @@ -172,6 +250,7 @@ registerModel({ default: null, }), livechatInit: attr(), + localStorageChatbotState: attr(), messages: attr({ default: [], }), @@ -179,8 +258,14 @@ registerModel({ serverUrl: attr({ compute: '_computeServerUrl', }), + sessionCookie: attr(), + sessionCookieKey: attr({ + compute: '_computeSessionCookieKey', + }), + testChatbotData: attr(), titleColor: attr({ compute: '_computeTitleColor', }), + widget: attr(), }, }); 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 9c704a972a7..c9c6f0f342b 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 @@ -6,6 +6,8 @@ import utils from 'web.utils'; import LivechatButton from '@im_livechat/legacy/widgets/livechat_button'; import WebsiteLivechat from '@im_livechat/legacy/models/website_livechat'; +import { insertAndReplace } from '@mail/model/model_field_command'; + /** * Override of the LivechatButton to create a testing environment for the chatbot script. * @@ -26,14 +28,14 @@ const LivechatButtonTestChatbot = LivechatButton.extend({ 'auto_popup_timer': 0, }, }); - this.messaging.livechatButtonView.update({ chatbot: chatbotData.chatbot }); - this.messaging.livechatButtonView.update({ - chatbotCurrentStep: this.messaging.livechatButtonView.chatbot.chatbot_welcome_steps[ - this.messaging.livechatButtonView.chatbot.chatbot_welcome_steps.length - 1 - ], + this.messaging.livechatButtonView.update({ isTestChatbot: true }); + this.messaging.livechatButtonView.update({ testChatbotData: chatbotData.chatbot }); + this.messaging.livechatButtonView.chatbot.update({ + currentStep: insertAndReplace({ + data: this.messaging.livechatButtonView.chatbot.lastWelcomeStep, + }), }); - this._channelData = chatbotData.channel; - this.messaging.livechatButtonView.update({ isChatbot: true }); + this._channelData = this.messaging.livechatButtonView.testChatbotData.channel; }, /**