From da8e5f248990ed71f970576e418dc9a388c04ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20K=C3=BChn?= Date: Mon, 20 Jun 2022 09:33:36 +0000 Subject: [PATCH] [IMP] im_livechat, *: Refactor JS Public Livechat (step 2) 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-2888734 closes odoo/odoo#94090 Signed-off-by: Alexandre Kühn (aku) --- .../src/legacy/public_livechat_chatbot.js | 133 ++++++++++-------- .../website_livechat_message_chatbot.js | 6 +- .../src/legacy/widgets/livechat_button.js | 106 +++++++------- .../static/src/models/livechat_button_view.js | 46 ++++++ addons/website_livechat/__manifest__.py | 1 + .../website_livechat_chatbot_test_script.js | 30 ++-- .../static/src/models/messaging.js | 10 ++ .../tours/website_livechat_chatbot_flow.js | 4 +- .../tests/tours/website_livechat_common.js | 4 +- 9 files changed, 210 insertions(+), 130 deletions(-) create mode 100644 addons/website_livechat/static/src/models/messaging.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 9474d82f776..ce1d595a99f 100644 --- a/addons/im_livechat/static/src/legacy/public_livechat_chatbot.js +++ b/addons/im_livechat/static/src/legacy/public_livechat_chatbot.js @@ -7,6 +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'; const _t = core._t; const QWeb = core.qweb; @@ -54,17 +55,17 @@ const QWeb = core.qweb; this.chatbotState = null; - if (this._rule && !!this._rule.chatbot) { + if (this.messaging.livechatButtonView.rule && this.messaging.livechatButtonView.rule.chatbot) { this.messaging.livechatButtonView.update({ isChatbot: true }); this.chatbotState = 'init'; - } else if (this._history !== null && this._history.length === 0) { - this._livechatInit = await session.rpc('/im_livechat/init', {channel_id: this.options.channel_id}); + } 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._livechatInit.rule.chatbot) { + if (this.messaging.livechatButtonView.livechatInit.rule.chatbot) { this.messaging.livechatButtonView.update({ isChatbot: true }); this.chatbotState = 'welcome'; } - } else if (this._history !== null && this._history.length !== 0) { + } 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; @@ -78,10 +79,12 @@ const QWeb = core.qweb; if (this.chatbotState === 'init') { // we landed on a website page where a channel rule is configured to run a chatbot.script // -> initialize necessary state - this._chatbot = this._rule.chatbot; - if (this._rule.chatbot_welcome_steps && this._rule.chatbot_welcome_steps.length !== 0) { - this._chatbotCurrentStep = this._chatbot.chatbot_welcome_steps[ - this._chatbot.chatbot_welcome_steps.length - 1]; + 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._chatbotCurrentStep = this.messaging.livechatButtonView.chatbot.chatbot_welcome_steps[ + this.messaging.livechatButtonView.chatbot.chatbot_welcome_steps.length - 1]; } } else if (this.chatbotState === 'welcome') { // we landed on a website page and a chatbot script was initialized on a previous one @@ -90,11 +93,11 @@ const QWeb = core.qweb; // -> initialize necessary state // -> batch welcome message (see '_sendWelcomeChatbotMessage') utils.set_cookie('im_livechat_auto_popup', '', -1); - this._history = null; - this._rule = this._livechatInit.rule; - this._chatbot = this._livechatInit.rule.chatbot; + 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._chatbotBatchWelcomeMessages = true; + this.messaging.livechatButtonView.update({ isChatbotBatchWelcomeMessages: true }); } else if (this.chatbotState === 'restore_session') { // we landed on a website page and a chatbot script is currently running // -> restore the user's session (see '_chatbotRestoreSession') @@ -119,7 +122,7 @@ const QWeb = core.qweb; message.body = utils.Markup(message.body); this._addMessage(message, options); if (this.messaging.livechatButtonView.chatWindow.isFolded() || !this.messaging.livechatButtonView.chatWindow.isAtBottom()) { - this._livechat.incrementUnreadCounter(); + this.messaging.livechatButtonView.livechat.incrementUnreadCounter(); } if (!options || !options.skipRenderMessages) { @@ -174,13 +177,15 @@ const QWeb = core.qweb; } const postedWelcomeMessages = await session.rpc('/chatbot/post_welcome_steps', { - channel_uuid: this._livechat.getUUID(), - chatbot_script_id: this._chatbot.chatbot_script_id, + channel_uuid: this.messaging.livechatButtonView.livechat.getUUID(), + chatbot_script_id: this.messaging.livechatButtonView.chatbot.chatbot_script_id, }); const welcomeMessagesIds = welcomeMessages.map(welcomeMessage => welcomeMessage._id); - this._messages = this._messages.filter((message) => { - !welcomeMessagesIds.includes(message._id); + this.messaging.livechatButtonView.update({ + messages: this.messaging.livechatButtonView.messages.filter((message) => { + !welcomeMessagesIds.includes(message._id); + }), }); postedWelcomeMessages.reverse(); @@ -368,7 +373,7 @@ const QWeb = core.qweb; if (chatbotState) { chatbotState = JSON.parse(chatbotState); this.messaging.livechatButtonView.update({ isChatbot: true }); - this._chatbot = chatbotState._chatbot; + this.messaging.livechatButtonView.update({ chatbot: chatbotState._chatbot }); this._chatbotCurrentStep = chatbotState._chatbotCurrentStep; } }, @@ -382,9 +387,9 @@ const QWeb = core.qweb; * @private */ _chatbotSaveSession() { - const chatUuid = this._livechat.toData().uuid; + const chatUuid = this.messaging.livechatButtonView.livechat.toData().uuid; localStorage.setItem('im_livechat.chatbot.state.uuid_' + chatUuid, JSON.stringify({ - '_chatbot': this._chatbot, + '_chatbot': this.messaging.livechatButtonView.chatbot, '_chatbotCurrentStep': this._chatbotCurrentStep, })); }, @@ -394,23 +399,27 @@ const QWeb = core.qweb; * @private */ _chatbotSetIsTyping(isWelcomeMessage=false) { - if (this.isTypingTimeout) { + if (this.messaging.livechatButtonView.isTypingTimeout) { clearTimeout(this.isTypingTimeout); } this._chatbotDisableInput(''); - this.isTypingTimeout = setTimeout(() => { - this.messaging.livechatButtonView.chatWindow.$('.o_mail_thread_content').append( - $(QWeb.render('im_livechat.legacy.chatbot.is_typing_message', { - 'chatbotImageSrc': `/im_livechat/operator/${this._livechat.getOperatorPID()[0]}/avatar`, - 'chatbotName': this._chatbot.chatbot_name, - 'isWelcomeMessage': isWelcomeMessage, - })) - ); + this.messaging.livechatButtonView.update({ + isTypingTimeout: setTimeout(() => { + this.messaging.livechatButtonView.chatWindow.$('.o_mail_thread_content').append( + $(QWeb.render('im_livechat.legacy.chatbot.is_typing_message', { + 'chatbotImageSrc': `/im_livechat/operator/${ + this.messaging.livechatButtonView.livechat.getOperatorPID()[0] + }/avatar`, + 'chatbotName': this.messaging.livechatButtonView.chatbot.chatbot_name, + 'isWelcomeMessage': isWelcomeMessage, + })) + ); - this.messaging.livechatButtonView.chatWindow.scrollToBottom(); - }, this._chatbotMessageDelay / 3); + this.messaging.livechatButtonView.chatWindow.scrollToBottom(); + }, this._chatbotMessageDelay / 3), + }); }, /** * Helper method that checks if the script should be ended or not. @@ -432,11 +441,13 @@ const QWeb = core.qweb; && !this._chatbotCurrentStep.is_email_valid) { // email is not (yet) valid, let the user answer / try again return false; - } else if ((this._chatbotIsExpectingUserInput() || - this._chatbotCurrentStep.chatbot_step_type === 'question_selection') && - this._messages.length !== 0) { - const lastMessage = this._messages[this._messages.length - 1]; - if (lastMessage.getAuthorID() !== this._livechat.getOperatorPID()[0]) { + } else if ( + (this._chatbotIsExpectingUserInput() || + this._chatbotCurrentStep.chatbot_step_type === 'question_selection') && + this.messaging.livechatButtonView.messages.length !== 0 + ) { + const lastMessage = this.messaging.livechatButtonView.messages[this.messaging.livechatButtonView.messages.length - 1]; + if (lastMessage.getAuthorID() !== this.messaging.livechatButtonView.livechat.getOperatorPID()[0]) { // we are on the last step of the script, expect a user input and the user has // already answered // -> end the script @@ -464,7 +475,7 @@ const QWeb = core.qweb; */ async _chatbotValidateEmail() { let emailValidResult = await session.rpc('/chatbot/step/validate_email', { - channel_uuid: this._livechat.getUUID(), + channel_uuid: this.messaging.livechatButtonView.livechat.getUUID(), }); if (emailValidResult.success) { @@ -499,8 +510,8 @@ const QWeb = core.qweb; } const nextStep = await session.rpc('/chatbot/step/trigger', { - channel_uuid: this._livechat.getUUID(), - chatbot_script_id: this._chatbot.chatbot_script_id, + channel_uuid:this.messaging.livechatButtonView.livechat.getUUID(), + chatbot_script_id: this.messaging.livechatButtonView.chatbot.chatbot_script_id, }); if (nextStep) { @@ -523,13 +534,13 @@ const QWeb = core.qweb; return nextStep; }, /** - * Returns the 'this._messages' filtered on our special 'welcome' ones. + * Returns the 'this.messaging.livechatButtonView.messages' filtered on our special 'welcome' ones. * See '_sendWelcomeChatbotMessage'. * * @private */ _getWelcomeMessages() { - return this._messages.filter((message) => { + return this.messaging.livechatButtonView.messages.filter((message) => { return message._id && typeof message._id === 'string' && message._id.startsWith('_welcome_'); }); }, @@ -539,8 +550,8 @@ const QWeb = core.qweb; * @private */ _isLastMessageFromCustomer() { - const lastMessage = this._messages.length !== 0 ? this._messages[this._messages.length - 1] : null; - return lastMessage && lastMessage.getAuthorID() !== this._livechat.getOperatorPID()[0]; + const lastMessage = this.messaging.livechatButtonView.messages.length !== 0 ? this.messaging.livechatButtonView.messages[this.messaging.livechatButtonView.messages.length - 1] : null; + return lastMessage && lastMessage.getAuthorID() !== this.messaging.livechatButtonView.livechat.getOperatorPID()[0]; }, //-------------------------------------------------------------------------- @@ -582,7 +593,7 @@ const QWeb = core.qweb; } }); - if (this._chatbotCurrentStep && this._messages && this._messages.length !== 0) { + if (this._chatbotCurrentStep && this.messaging.livechatButtonView.messages && this.messaging.livechatButtonView.messages.length !== 0) { this._chatbotProcessStep(); } }); @@ -595,7 +606,7 @@ const QWeb = core.qweb; const parameters = this._super(...arguments); if (this.messaging.livechatButtonView.isChatbot) { - parameters.chatbot_script_id = this._chatbot.chatbot_script_id; + parameters.chatbot_script_id = this.messaging.livechatButtonView.chatbot.chatbot_script_id; } return parameters; @@ -615,8 +626,8 @@ const QWeb = core.qweb; this.messaging.livechatButtonView.chatWindow.$('.o_livechat_chatbot_main_restart').on('click', this._onChatbotRestartScript.bind(this)); - if (this._messages.length !== 0) { - const lastMessage = this._messages[this._messages.length - 1]; + if (this.messaging.livechatButtonView.messages.length !== 0) { + const lastMessage = this.messaging.livechatButtonView.messages[this.messaging.livechatButtonView.messages.length - 1]; const stepAnswers = lastMessage.getChatbotStepAnswers(); if (stepAnswers && stepAnswers.length !== 0 && !lastMessage.getChatbotStepAnswerId()) { this._chatbotDisableInput(_t('Select an option above')); @@ -650,7 +661,7 @@ const QWeb = core.qweb; } return superMethod.apply(this, superArguments).then(() => { - if (this._isChatbotRedirecting) { + if (this.messaging.livechatButtonView.isChatbotRedirecting) { return; } @@ -680,7 +691,7 @@ const QWeb = core.qweb; if (this.messaging.livechatButtonView.isChatbot) { this._sendWelcomeChatbotMessage( 0, - this._chatbotBatchWelcomeMessages ? 0 : this._chatbotMessageDelay, + this.messaging.livechatButtonView.isChatbotBatchWelcomeMessages ? 0 : this._chatbotMessageDelay, ); } else { this._super(...arguments); @@ -708,7 +719,7 @@ const QWeb = core.qweb; * @private */ _sendWelcomeChatbotMessage(stepIndex, welcomeMessageDelay) { - const chatbotStep = this._chatbot.chatbot_welcome_steps[stepIndex]; + const chatbotStep = this.messaging.livechatButtonView.chatbot.chatbot_welcome_steps[stepIndex]; this._chatbotCurrentStep = chatbotStep; if (chatbotStep.chatbot_step_message) { @@ -716,18 +727,18 @@ const QWeb = core.qweb; id: '_welcome_' + stepIndex, is_discussion: true, // important for css style -> we only want white background for chatbot attachment_ids: [], - author_id: this._livechat.getOperatorPID(), + author_id: this.messaging.livechatButtonView.livechat.getOperatorPID(), body: utils.Markup(chatbotStep.chatbot_step_message), chatbot_script_step_id: chatbotStep.chatbot_script_step_id, chatbot_step_answers: chatbotStep.chatbot_step_answers, date: time.datetime_to_str(new Date()), model: "mail.channel", message_type: "comment", - res_id: this._livechat.getID(), + res_id: this.messaging.livechatButtonView.livechat.getID(), }); } - if (stepIndex + 1 < this._chatbot.chatbot_welcome_steps.length) { + if (stepIndex + 1 < this.messaging.livechatButtonView.chatbot.chatbot_welcome_steps.length) { if (welcomeMessageDelay !== 0) { this._chatbotSetIsTyping(true); } @@ -774,8 +785,8 @@ const QWeb = core.qweb; } const postedMessage = await session.rpc('/chatbot/restart', { - channel_uuid: this._livechat.getUUID(), - chatbot_script_id: this._chatbot.chatbot_script_id, + channel_uuid: this.messaging.livechatButtonView.livechat.getUUID(), + chatbot_script_id: this.messaging.livechatButtonView.chatbot.chatbot_script_id, }); if (postedMessage) { @@ -797,7 +808,7 @@ const QWeb = core.qweb; /** * Saves the selected chatbot.script.answer onto our chatbot.message. - * Will update the state of the related message (in this._messages) to set the selected option + * Will update the state of the related message (in this.messaging.livechatButtonView.messages) to set the selected option * as well which will in turn adapt the display to not show options anymore. * * This method also handles an optional redirection link placed on the chatbot.script.answer and @@ -814,14 +825,14 @@ const QWeb = core.qweb; const selectedAnswer = $target.data('chatbotStepAnswerId'); const redirectLink = $target.data('chatbotStepRedirectLink'); - this._isChatbotRedirecting = !!redirectLink; + this.messaging.livechatButtonView.update({ isChatbotRedirecting: !!redirectLink }); await this._sendMessage({ content: $target.text().trim(), }); let stepMessage = null; - this._messages.forEach((message) => { + for (const message of this.messaging.livechatButtonView.messages) { // we do NOT want to use a 'find' here because we want the LAST message that respects // this condition. // indeed, if you restart the script, you can have multiple messages with the same step id, @@ -831,7 +842,7 @@ const QWeb = core.qweb; if (message.getChatbotStepId() === stepId) { stepMessage = message; } - }); + } const messageId = stepMessage.getID(); stepMessage.setChatbotStepAnswerId(selectedAnswer); this._chatbotCurrentStep['chatbot_selected_answer_id'] = selectedAnswer; @@ -839,7 +850,7 @@ const QWeb = core.qweb; this._chatbotSaveSession(); const saveAnswerPromise = session.rpc('/chatbot/answer/save', { - channel_uuid: this._livechat.getUUID(), + channel_uuid: this.messaging.livechatButtonView.livechat.getUUID(), message_id: messageId, selected_answer_id: selectedAnswer, }); 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 1ad25dfcc55..98b3ac46351 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,9 +20,9 @@ WebsiteLivechatMessage.include({ this._super(...arguments); if (parent.messaging.livechatButtonView.isChatbot) { - this._chatbotId = parent._chatbot.chatbot_script_id; - this._chatbotName = parent._chatbot.chatbot_name; - this._chatbotOperatorPartnerId = parent._chatbot.chatbot_operator_partner_id; + 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; 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 15d8f9b7814..05e063588ce 100644 --- a/addons/im_livechat/static/src/legacy/widgets/livechat_button.js +++ b/addons/im_livechat/static/src/legacy/widgets/livechat_button.js @@ -13,6 +13,8 @@ import WebsiteLivechat from '@im_livechat/legacy/models/website_livechat'; import WebsiteLivechatMessage from '@im_livechat/legacy/models/website_livechat_message'; import WebsiteLivechatWindow from '@im_livechat/legacy/models/website_livechat_window'; +import { clear } from '@mail/model/model_field_command'; + const _t = core._t; const QWeb = core.qweb; @@ -31,41 +33,39 @@ const LivechatButton = Widget.extend({ init(parent, messaging) { this._super(parent); this.messaging = messaging; - this.options = _.defaults(this.messaging.publicLivechatOptions || {}, { - default_username: _t("Visitor"), - }); - - this._history = null; - // livechat model - this._livechat = null; - this._messages = []; - this._serverURL = this.messaging.publicLivechatServerUrl; + this.options = _.defaults(this.messaging.publicLivechatOptions || {}); }, async willStart() { const cookie = utils.get_cookie('im_livechat_session'); if (cookie) { const channel = JSON.parse(cookie); - this._history = await session.rpc('/mail/chat_history', {uuid: channel.uuid, limit: 100}); - this._history.reverse().forEach(message => { message.body = utils.Markup(message.body); }); + const history = await session.rpc('/mail/chat_history', {uuid: channel.uuid, limit: 100}); + history.reverse(); + this.messaging.livechatButtonView.update({ history }); + for (const message of this.messaging.livechatButtonView.history) { + message.body = utils.Markup(message.body); + } } else { const result = await session.rpc('/im_livechat/init', {channel_id: this.options.channel_id}); if (!result.available_for_me) { return Promise.reject(); } - this._rule = result.rule; + this.messaging.livechatButtonView.update({ rule: result.rule }); } return this._loadQWebTemplate(); }, start() { this.$el.text(this.messaging.livechatButtonView.buttonText); - if (this._history) { - this._history.forEach(m => this._addMessage(m)); + if (this.messaging.livechatButtonView.history) { + for (const m of this.messaging.livechatButtonView.history) { + this._addMessage(m); + } this._openChat(); - } else if (!config.device.isMobile && this._rule.action === 'auto_popup') { + } else if (!config.device.isMobile && this.messaging.livechatButtonView.rule.action === 'auto_popup') { const autoPopupCookie = utils.get_cookie('im_livechat_auto_popup'); if (!autoPopupCookie || JSON.parse(autoPopupCookie)) { this.messaging.livechatButtonView.update({ - autoOpenChatTimeout: setTimeout(this._openChat.bind(this), this._rule.auto_popup_timer * 1000), + autoOpenChatTimeout: setTimeout(this._openChat.bind(this), this.messaging.livechatButtonView.rule.auto_popup_timer * 1000), }); } } @@ -98,25 +98,29 @@ const LivechatButton = Widget.extend({ */ _addMessage(data, options) { options = Object.assign({}, this.options, options, { - serverURL: this._serverURL, + serverURL: this.messaging.livechatButtonView.serverURL, }); const message = new WebsiteLivechatMessage(this, data, options); - const hasAlreadyMessage = _.some(this._messages, function (msg) { + const hasAlreadyMessage = _.some(this.messaging.livechatButtonView.messages, function (msg) { return message.getID() === msg.getID(); }); if (hasAlreadyMessage) { return; } - if (this._livechat) { - this._livechat.addMessage(message); + if (this.messaging.livechatButtonView.livechat) { + this.messaging.livechatButtonView.livechat.addMessage(message); } if (options && options.prepend) { - this._messages.unshift(message); + this.messaging.livechatButtonView.update({ + messages: [message, ...this.messaging.livechatButtonView.messages], + }); } else { - this._messages.push(message); + this.messaging.livechatButtonView.update({ + messages: [...this.messaging.livechatButtonView.messages, message], + }); } }, /** @@ -125,7 +129,7 @@ const LivechatButton = Widget.extend({ _askFeedback() { this.messaging.livechatButtonView.chatWindow.$('.o_thread_composer input').prop('disabled', true); - const feedback = new Feedback(this, this._livechat); + const feedback = new Feedback(this, this.messaging.livechatButtonView.livechat); this.messaging.livechatButtonView.chatWindow.replaceContentWith(feedback); feedback.on('send_message', this, this._sendMessage); @@ -147,20 +151,20 @@ const LivechatButton = Widget.extend({ _handleNotification({ payload, type }) { switch (type) { case 'im_livechat.history_command': { - if (payload.id !== this._livechat._id) { + if (payload.id !== this.messaging.livechatButtonView.livechat._id) { return; } const cookie = utils.get_cookie(LIVECHAT_COOKIE_HISTORY); const history = cookie ? JSON.parse(cookie) : []; session.rpc('/im_livechat/history', { - pid: this._livechat.getOperatorPID()[0], - channel_uuid: this._livechat.getUUID(), + pid: this.messaging.livechatButtonView.livechat.getOperatorPID()[0], + channel_uuid: this.messaging.livechatButtonView.livechat.getUUID(), page_history: history, }); return; } case 'mail.channel.partner/typing_status': { - if (payload.channel_id !== this._livechat._id) { + if (payload.channel_id !== this.messaging.livechatButtonView.livechat._id) { return; } const partnerID = payload.partner_id; @@ -169,31 +173,31 @@ const LivechatButton = Widget.extend({ return; } if (payload.is_typing) { - this._livechat.registerTyping({ partnerID }); + this.messaging.livechatButtonView.livechat.registerTyping({ partnerID }); } else { - this._livechat.unregisterTyping({ partnerID }); + this.messaging.livechatButtonView.livechat.unregisterTyping({ partnerID }); } return; } case 'mail.channel/new_message': { - if (payload.id !== this._livechat._id) { + if (payload.id !== this.messaging.livechatButtonView.livechat._id) { return; } const notificationData = payload.message; // If message from notif is already in chatter messages, stop handling - if (this._messages.some(message => message.getID() === notificationData.id)) { + if (this.messaging.livechatButtonView.messages.some(message => message.getID() === notificationData.id)) { return; } notificationData.body = utils.Markup(notificationData.body); this._addMessage(notificationData); if (this.messaging.livechatButtonView.chatWindow.isFolded() || !this.messaging.livechatButtonView.chatWindow.isAtBottom()) { - this._livechat.incrementUnreadCounter(); + this.messaging.livechatButtonView.livechat.incrementUnreadCounter(); } this._renderMessages(); return; } case 'mail.message/insert': { - const message = this._messages.find(message => message._id === payload.id); + const message = this.messaging.livechatButtonView.messages.find(message => message._id === payload.id); if (!message) { return; } @@ -222,13 +226,13 @@ const LivechatButton = Widget.extend({ } const cookie = utils.get_cookie('im_livechat_session'); let def; - this._openingChat = true; this.messaging.livechatButtonView.update({ isOpeningChat: true }); clearTimeout(this.messaging.livechatButtonView.autoOpenChatTimeout); if (cookie) { def = Promise.resolve(JSON.parse(cookie)); } else { - this._messages = []; // re-initialize messages cache + // re-initialize messages cache + this.messaging.livechatButtonView.update({ messages: clear() }); def = session.rpc( '/im_livechat/get_session', this._prepareGetSessionParameters(), @@ -253,19 +257,21 @@ const LivechatButton = Widget.extend({ console.warn(_t("No available collaborator, please try again later.")); } } else { - this._livechat = new WebsiteLivechat({ - parent: this, - data: livechatData + this.messaging.livechatButtonView.update({ + livechat: new WebsiteLivechat({ + parent: this, + data: livechatData + }), }); return this._openChatWindow().then(() => { - if (!this._history) { + if (!this.messaging.livechatButtonView.history) { this._sendWelcomeMessage(); } this._renderMessages(); - this.call('bus_service', 'addChannel', this._livechat.getUUID()); + this.call('bus_service', 'addChannel', this.messaging.livechatButtonView.livechat.getUUID()); this.call('bus_service', 'startPolling'); - utils.set_cookie('im_livechat_session', utils.unaccent(JSON.stringify(this._livechat.toData()), true), 60 * 60); + utils.set_cookie('im_livechat_session', utils.unaccent(JSON.stringify(this.messaging.livechatButtonView.livechat.toData()), true), 60 * 60); utils.set_cookie('im_livechat_auto_popup', JSON.stringify(false), 60 * 60); if (livechatData.operator_pid[0]) { // livechatData.operator_pid contains a tuple (id, name) @@ -312,7 +318,7 @@ const LivechatButton = Widget.extend({ titleColor: this.options.title_color, }; this.messaging.livechatButtonView.update({ - chatWindow: new WebsiteLivechatWindow(this, this._livechat, options), + chatWindow: new WebsiteLivechatWindow(this, this.messaging.livechatButtonView.livechat, options), }); return this.messaging.livechatButtonView.chatWindow.appendTo($('body')).then(() => { const cssProps = { bottom: 0 }; @@ -327,7 +333,7 @@ const LivechatButton = Widget.extend({ _prepareGetSessionParameters() { return { channel_id: this.options.channel_id, - anonymous_name: this.options.default_username, + anonymous_name: this.messaging.livechatButtonView.defaultUsername, previous_operator_id: this._get_previous_operator_id(), }; }, @@ -336,7 +342,7 @@ const LivechatButton = Widget.extend({ */ _renderMessages() { const shouldScroll = !this.messaging.livechatButtonView.chatWindow.isFolded() && this.messaging.livechatButtonView.chatWindow.isAtBottom(); - this._livechat.setMessages(this._messages); + this.messaging.livechatButtonView.livechat.setMessages(this.messaging.livechatButtonView.messages); this.messaging.livechatButtonView.chatWindow.render(); if (shouldScroll) { this.messaging.livechatButtonView.chatWindow.scrollToBottom(); @@ -348,9 +354,9 @@ const LivechatButton = Widget.extend({ * @return {Promise} */ _sendMessage(message) { - this._livechat._notifyMyselfTyping({ typing: false }); + this.messaging.livechatButtonView.livechat._notifyMyselfTyping({ typing: false }); return session - .rpc('/mail/chat_post', { uuid: this._livechat.getUUID(), message_content: message.content }) + .rpc('/mail/chat_post', { uuid: this.messaging.livechatButtonView.livechat.getUUID(), message_content: message.content }) .then((messageId) => { if (!messageId) { try { @@ -382,11 +388,11 @@ const LivechatButton = Widget.extend({ this._addMessage({ id: '_welcome', attachment_ids: [], - author_id: this._livechat.getOperatorPID(), + author_id: this.messaging.livechatButtonView.livechat.getOperatorPID(), body: this.messaging.livechatButtonView.defaultMessage, date: time.datetime_to_str(new Date()), model: "mail.channel", - res_id: this._livechat.getID(), + res_id: this.messaging.livechatButtonView.livechat.getID(), }, { prepend: true }); } }, @@ -402,7 +408,7 @@ const LivechatButton = Widget.extend({ _onCloseChatWindow(ev) { ev.stopPropagation(); const isComposerDisabled = this.messaging.livechatButtonView.chatWindow.$('.o_thread_composer input').prop('disabled'); - const shouldAskFeedback = !isComposerDisabled && this._messages.find(function (message) { + const shouldAskFeedback = !isComposerDisabled && this.messaging.livechatButtonView.messages.find(function (message) { return message.getID() !== '_welcome'; }); if (shouldAskFeedback) { @@ -441,7 +447,7 @@ const LivechatButton = Widget.extend({ */ _onSaveChatWindow(ev) { ev.stopPropagation(); - utils.set_cookie('im_livechat_session', utils.unaccent(JSON.stringify(this._livechat.toData()), true), 60 * 60); + utils.set_cookie('im_livechat_session', utils.unaccent(JSON.stringify(this.messaging.livechatButtonView.livechat.toData()), true), 60 * 60); }, /** * @private 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 366b51d51a6..baab3001281 100644 --- a/addons/im_livechat/static/src/models/livechat_button_view.js +++ b/addons/im_livechat/static/src/models/livechat_button_view.js @@ -27,6 +27,16 @@ registerModel({ } return this.env._t("How may I help you?"); }, + /** + * @private + * @returns {string} + */ + _computeDefaultUsername() { + if (this.messaging.publicLivechatOptions.default_username) { + return this.messaging.publicLivechatOptions.default_username; + } + return this.env._t("Visitor"); + }, /** * @private * @returns {string|FieldCommand} @@ -42,12 +52,23 @@ registerModel({ } return this.env._t("Ask something ..."); }, + /** + * @private + * @returns {string} + */ + _computeServerUrl() { + if (this.isChatbot) { + return this.messaging.publicLivechatServerUrlChatbot; + } + return this.messaging.publicLivechatServerUrl; + }, }, fields: { autoOpenChatTimeout: attr(), buttonText: attr({ compute: '_computeButtonText', }), + chatbot: attr(), // livechat window chatWindow: attr({ default: null, @@ -55,6 +76,12 @@ registerModel({ defaultMessage: attr({ compute: '_computeDefaultMessage', }), + defaultUsername: attr({ + compute: '_computeDefaultUsername', + }), + history: attr({ + default: null, + }), inputPlaceholder: attr({ compute: '_computeInputPlaceholder', default: '', @@ -62,8 +89,27 @@ registerModel({ isChatbot: attr({ default: false, }), + isChatbotBatchWelcomeMessages: attr({ + default: false, + }), + isChatbotRedirecting: attr({ + default: false, + }), isOpeningChat: attr({ default: false, }), + isTypingTimeout: attr(), + // livechat model + livechat: attr({ + default: null, + }), + livechatInit: attr(), + messages: attr({ + default: [], + }), + rule: attr(), + serverUrl: attr({ + compute: '_computeServerUrl', + }), }, }); diff --git a/addons/website_livechat/__manifest__.py b/addons/website_livechat/__manifest__.py index 15c0f40d257..d44a5235f9c 100644 --- a/addons/website_livechat/__manifest__.py +++ b/addons/website_livechat/__manifest__.py @@ -32,6 +32,7 @@ Allow website visitors to chat with the collaborators. This module also brings a 'website_livechat/static/src/legacy/public_livechat.js', 'website_livechat/static/src/legacy/website_livechat_chatbot_test_script.js', 'website_livechat/static/src/legacy/public_livechat.scss', + 'website_livechat/static/src/models/*.js', ], 'website.assets_editor': [ 'website_livechat/static/src/js/**/*', 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 3774ff0c345..9e9fa515150 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 @@ -20,16 +20,17 @@ const LivechatButtonTestChatbot = LivechatButton.extend({ init: function (parent, messaging, chatbotData) { this._super(...arguments); - this._rule = { - 'action': 'auto_popup', - 'auto_popup_timer': 0, - }; - this._chatbot = chatbotData.chatbot; - this._chatbotCurrentStep = this._chatbot.chatbot_welcome_steps[ - this._chatbot.chatbot_welcome_steps.length - 1]; + this.messaging.livechatButtonView.update({ + rule: { + 'action': 'auto_popup', + 'auto_popup_timer': 0, + }, + }); + this.messaging.livechatButtonView.update({ chatbot: chatbotData.chatbot }); + this._chatbotCurrentStep = this.messaging.livechatButtonView.chatbot.chatbot_welcome_steps[ + this.messaging.livechatButtonView.chatbot.chatbot_welcome_steps.length - 1]; this._channelData = chatbotData.channel; this.messaging.livechatButtonView.update({ isChatbot: true }); - this._serverURL = chatbotData.serverUrl; }, /** @@ -53,17 +54,19 @@ const LivechatButtonTestChatbot = LivechatButton.extend({ * @override */ _openChat: function () { - this._livechat = new WebsiteLivechat({ - parent: this, - data: this._channelData, + this.messaging.livechatButtonView.update({ + livechat: new WebsiteLivechat({ + parent: this, + data: this._channelData, + }), }); return this._openChatWindow().then(() => { this._sendWelcomeMessage(); this._renderMessages(); - this.call('bus_service', 'addChannel', this._livechat.getUUID()); + this.call('bus_service', 'addChannel', this.messaging.livechatButtonView.livechat.getUUID()); this.call('bus_service', 'startPolling'); - utils.set_cookie('im_livechat_session', utils.unaccent(JSON.stringify(this._livechat.toData()), true), 60 * 60); + utils.set_cookie('im_livechat_session', utils.unaccent(JSON.stringify(this.messaging.livechatButtonView.livechat.toData()), true), 60 * 60); this.messaging.livechatButtonView.update({ isOpeningChat: false }); }); }, @@ -88,6 +91,7 @@ publicWidget.registry.livechatChatbotTestScript = publicWidget.Widget.extend({ messaging.update({ isInPublicLivechat: true, isPublicLivechatAvailable: true, + publicLivechatServerUrlChatbot: 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 new file mode 100644 index 00000000000..92f4db61ebf --- /dev/null +++ b/addons/website_livechat/static/src/models/messaging.js @@ -0,0 +1,10 @@ +/** @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(), +}); diff --git a/addons/website_livechat/static/tests/tours/website_livechat_chatbot_flow.js b/addons/website_livechat/static/tests/tours/website_livechat_chatbot_flow.js index ab1ab18c314..b0cb9652c45 100644 --- a/addons/website_livechat/static/tests/tours/website_livechat_chatbot_flow.js +++ b/addons/website_livechat/static/tests/tours/website_livechat_chatbot_flow.js @@ -3,6 +3,8 @@ import tour from "web_tour.tour"; import LivechatButton from '@im_livechat/legacy/widgets/livechat_button'; +import { clear } from '@mail/model/model_field_command'; + LivechatButton.include({ /** * Let us make it a bit faster than the default delay (3500ms). @@ -23,7 +25,7 @@ LivechatButton.include({ * the thread and making it hard to write proper jQuery selectors in the tour. */ _onChatbotRestartScript: function () { - this._messages = []; + this.messaging.livechatButtonView.update({ messages: clear() }); this._renderMessages(); this._super(...arguments); diff --git a/addons/website_livechat/static/tests/tours/website_livechat_common.js b/addons/website_livechat/static/tests/tours/website_livechat_common.js index 795d66b390b..c92f50928dd 100644 --- a/addons/website_livechat/static/tests/tours/website_livechat_common.js +++ b/addons/website_livechat/static/tests/tours/website_livechat_common.js @@ -30,9 +30,9 @@ LivechatButton.include({ self._handleNotification({ type: 'mail.channel/new_message', payload: { - id: self._livechat._id, + id: self.messaging.livechatButtonView.livechat._id, message: { - id: self._messages.length + 1, + id: self.messaging.livechatButtonView.messages.length + 1, author_id: [0, 'Website Visitor Test'], email_from: 'Website Visitor Test', body: utils.Markup('

' + message.content + '

'),