From 1e7221535316210771e9541ea19b592778f682a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20K=C3=BChn?= Date: Tue, 14 Jun 2022 09:12:26 +0000 Subject: [PATCH] [IMP] im_livechat, *: Refactor JS Public Livechat (step 1) 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-2887296 closes odoo/odoo#93966 Signed-off-by: Alexandre Kühn (aku) --- .../src/legacy/public_livechat_chatbot.js | 65 +++++++++---------- .../website_livechat_message_chatbot.js | 2 +- .../src/legacy/widgets/livechat_button.js | 59 +++++++++-------- .../static/src/models/livechat_button_view.js | 61 +++++++++++++++++ .../static/src/legacy/public_livechat.js | 4 +- .../website_livechat_chatbot_test_script.js | 6 +- 6 files changed, 124 insertions(+), 73 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 182294d0bba..9474d82f776 100644 --- a/addons/im_livechat/static/src/legacy/public_livechat_chatbot.js +++ b/addons/im_livechat/static/src/legacy/public_livechat_chatbot.js @@ -52,17 +52,16 @@ const QWeb = core.qweb; async willStart() { const superResult = await this._super(...arguments); - this._isChatbot = false; this.chatbotState = null; if (this._rule && !!this._rule.chatbot) { - this._isChatbot = true; + 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}); if (this._livechatInit.rule.chatbot) { - this._isChatbot = true; + this.messaging.livechatButtonView.update({ isChatbot: true }); this.chatbotState = 'welcome'; } } else if (this._history !== null && this._history.length !== 0) { @@ -70,18 +69,12 @@ const QWeb = core.qweb; if (sessionCookie) { const sessionKey = 'im_livechat.chatbot.state.uuid_' + JSON.parse(sessionCookie).uuid; if (localStorage.getItem(sessionKey)) { - this._isChatbot = true; + this.messaging.livechatButtonView.update({ isChatbot: true }); this.chatbotState = 'restore_session'; } } } - if (this._isChatbot) { - // void the default livechat placeholder in the user input - // as we use it for specific things (e.g: showing "please select an option above") - this.options.input_placeholder = ''; - } - if (this.chatbotState === 'init') { // we landed on a website page where a channel rule is configured to run a chatbot.script // -> initialize necessary state @@ -100,7 +93,7 @@ const QWeb = core.qweb; this._history = null; this._rule = this._livechatInit.rule; this._chatbot = this._livechatInit.rule.chatbot; - this._isChatbot = true; + this.messaging.livechatButtonView.update({ isChatbot: true }); this._chatbotBatchWelcomeMessages = true; } else if (this.chatbotState === 'restore_session') { // we landed on a website page and a chatbot script is currently running @@ -125,7 +118,7 @@ const QWeb = core.qweb; _chatbotAddMessage(message, options) { message.body = utils.Markup(message.body); this._addMessage(message, options); - if (this._chatWindow.isFolded() || !this._chatWindow.isAtBottom()) { + if (this.messaging.livechatButtonView.chatWindow.isFolded() || !this.messaging.livechatButtonView.chatWindow.isAtBottom()) { this._livechat.incrementUnreadCounter(); } @@ -208,7 +201,7 @@ const QWeb = core.qweb; * @private */ _chatbotDisableInput(disableText) { - this._chatWindow.$('.o_composer_text_field') + this.messaging.livechatButtonView.chatWindow.$('.o_composer_text_field') .prop('disabled', true) .addClass('text-center font-italic bg-200') .val(disableText); @@ -218,7 +211,7 @@ const QWeb = core.qweb; * @private */ _chatbotEnableInput() { - const $composerTextField = this._chatWindow.$('.o_composer_text_field'); + const $composerTextField = this.messaging.livechatButtonView.chatWindow.$('.o_composer_text_field'); $composerTextField .prop('disabled', false) .removeClass('text-center font-italic bg-200') @@ -241,9 +234,9 @@ const QWeb = core.qweb; * @private */ _chatbotEndScript() { - this._chatWindow.$('.o_composer_text_field').addClass('d-none'); - this._chatWindow.$('.o_livechat_chatbot_end').show(); - this._chatWindow.$('.o_livechat_chatbot_restart').one('click', + this.messaging.livechatButtonView.chatWindow.$('.o_composer_text_field').addClass('d-none'); + this.messaging.livechatButtonView.chatWindow.$('.o_livechat_chatbot_end').show(); + this.messaging.livechatButtonView.chatWindow.$('.o_livechat_chatbot_restart').one('click', this._onChatbotRestartScript.bind(this)); }, @@ -256,7 +249,7 @@ const QWeb = core.qweb; * @private */ _chatbotDisplayRestartButton() { - return this._isChatbot && (!this._chatbotCurrentStep || + return this.messaging.livechatButtonView.isChatbot && (!this._chatbotCurrentStep || (this._chatbotCurrentStep.chatbot_step_type !== 'forward_operator' || !this._chatbotCurrentStep.chatbot_operator_found)); }, @@ -327,7 +320,7 @@ const QWeb = core.qweb; if (triggerNextStep) { let nextStepDelay = this._chatbotMessageDelay; - if (this._chatWindow.$('.o_livechat_chatbot_typing').length !== 0) { + 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 // e.g: previous step was "forward_operator" and no-one is available @@ -343,7 +336,7 @@ const QWeb = core.qweb; } if (!this._chatbotDisplayRestartButton()) { - this._chatWindow.$('.o_livechat_chatbot_main_restart').addClass('d-none'); + this.messaging.livechatButtonView.chatWindow.$('.o_livechat_chatbot_main_restart').addClass('d-none'); } }, /** @@ -374,7 +367,7 @@ const QWeb = core.qweb; if (chatbotState) { chatbotState = JSON.parse(chatbotState); - this._isChatbot = true; + this.messaging.livechatButtonView.update({ isChatbot: true }); this._chatbot = chatbotState._chatbot; this._chatbotCurrentStep = chatbotState._chatbotCurrentStep; } @@ -408,7 +401,7 @@ const QWeb = core.qweb; this._chatbotDisableInput(''); this.isTypingTimeout = setTimeout(() => { - this._chatWindow.$('.o_mail_thread_content').append( + 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, @@ -416,7 +409,7 @@ const QWeb = core.qweb; })) ); - this._chatWindow.scrollToBottom(); + this.messaging.livechatButtonView.chatWindow.scrollToBottom(); }, this._chatbotMessageDelay / 3); }, /** @@ -564,9 +557,9 @@ const QWeb = core.qweb; _askFeedback() { this._super(...arguments); - this._chatWindow.$('.o_livechat_chatbot_main_restart').addClass('d-none'); - this._chatWindow.$('.o_livechat_chatbot_end').hide(); - this._chatWindow.$('.o_composer_text_field') + this.messaging.livechatButtonView.chatWindow.$('.o_livechat_chatbot_main_restart').addClass('d-none'); + this.messaging.livechatButtonView.chatWindow.$('.o_livechat_chatbot_end').hide(); + this.messaging.livechatButtonView.chatWindow.$('.o_composer_text_field') .removeClass('d-none') .val(''); }, @@ -584,8 +577,8 @@ const QWeb = core.qweb; _openChatWindow() { return this._super(...arguments).then(() => { window.addEventListener('resize', () => { - if (this._chatWindow) { - this._chatWindow.scrollToBottom(); + if (this.messaging.livechatButtonView.chatWindow) { + this.messaging.livechatButtonView.chatWindow.scrollToBottom(); } }); @@ -601,7 +594,7 @@ const QWeb = core.qweb; _prepareGetSessionParameters() { const parameters = this._super(...arguments); - if (this._isChatbot) { + if (this.messaging.livechatButtonView.isChatbot) { parameters.chatbot_script_id = this._chatbot.chatbot_script_id; } @@ -615,11 +608,11 @@ const QWeb = core.qweb; const self = this; - this._chatWindow.$('.o_thread_message:last .o_livechat_chatbot_options li').each(function () { + this.messaging.livechatButtonView.chatWindow.$('.o_thread_message:last .o_livechat_chatbot_options li').each(function () { $(this).on('click', self._onChatbotOptionClicked.bind(self)); }); - this._chatWindow.$('.o_livechat_chatbot_main_restart').on('click', + this.messaging.livechatButtonView.chatWindow.$('.o_livechat_chatbot_main_restart').on('click', this._onChatbotRestartScript.bind(this)); if (this._messages.length !== 0) { @@ -652,7 +645,7 @@ const QWeb = core.qweb; const superArguments = arguments; const superMethod = this._super; - if (this._isChatbot && this._chatbotCurrentStep) { + if (this.messaging.livechatButtonView.isChatbot && this._chatbotCurrentStep) { await this._chatbotPostWelcomeMessages(); } @@ -661,7 +654,7 @@ const QWeb = core.qweb; return; } - if (this._isChatbot && this._chatbotCurrentStep) { + if (this.messaging.livechatButtonView.isChatbot && this._chatbotCurrentStep) { if (this._chatbotCurrentStep.chatbot_step_type === 'forward_operator' && this._chatbotCurrentStep.chatbot_operator_found) { return; // operator has taken over the conversation, let them speak @@ -684,7 +677,7 @@ const QWeb = core.qweb; * @private */ _sendWelcomeMessage() { - if (this._isChatbot) { + if (this.messaging.livechatButtonView.isChatbot) { this._sendWelcomeChatbotMessage( 0, this._chatbotBatchWelcomeMessages ? 0 : this._chatbotMessageDelay, @@ -769,8 +762,8 @@ const QWeb = core.qweb; * @private */ async _onChatbotRestartScript(ev) { - this._chatWindow.$('.o_composer_text_field').removeClass('d-none'); - this._chatWindow.$('.o_livechat_chatbot_end').hide(); + this.messaging.livechatButtonView.chatWindow.$('.o_composer_text_field').removeClass('d-none'); + this.messaging.livechatButtonView.chatWindow.$('.o_livechat_chatbot_end').hide(); if (this.nextStepTimeout) { clearTimeout(this.nextStepTimeout); 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 f3ea176f7f9..1ad25dfcc55 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 @@ -19,7 +19,7 @@ WebsiteLivechatMessage.include({ init(parent, data, options) { this._super(...arguments); - if (parent._isChatbot) { + 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; 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 23d8e693872..15d8f9b7814 100644 --- a/addons/im_livechat/static/src/legacy/widgets/livechat_button.js +++ b/addons/im_livechat/static/src/legacy/widgets/livechat_button.js @@ -32,17 +32,12 @@ const LivechatButton = Widget.extend({ this._super(parent); this.messaging = messaging; this.options = _.defaults(this.messaging.publicLivechatOptions || {}, { - input_placeholder: _t("Ask something ..."), default_username: _t("Visitor"), - button_text: _t("Chat with one of our collaborators"), - default_message: _t("How may I help you?"), }); this._history = null; // livechat model this._livechat = null; - // livechat window - this._chatWindow = null; this._messages = []; this._serverURL = this.messaging.publicLivechatServerUrl; }, @@ -62,15 +57,16 @@ const LivechatButton = Widget.extend({ return this._loadQWebTemplate(); }, start() { - this.$el.text(this.options.button_text); + this.$el.text(this.messaging.livechatButtonView.buttonText); if (this._history) { this._history.forEach(m => this._addMessage(m)); this._openChat(); } else if (!config.device.isMobile && this._rule.action === 'auto_popup') { const autoPopupCookie = utils.get_cookie('im_livechat_auto_popup'); if (!autoPopupCookie || JSON.parse(autoPopupCookie)) { - this._autoPopupTimeout = - setTimeout(this._openChat.bind(this), this._rule.auto_popup_timer * 1000); + this.messaging.livechatButtonView.update({ + autoOpenChatTimeout: setTimeout(this._openChat.bind(this), this._rule.auto_popup_timer * 1000), + }); } } this.call('bus_service', 'onNotification', this, this._onNotification); @@ -127,10 +123,10 @@ const LivechatButton = Widget.extend({ * @private */ _askFeedback() { - this._chatWindow.$('.o_thread_composer input').prop('disabled', true); + this.messaging.livechatButtonView.chatWindow.$('.o_thread_composer input').prop('disabled', true); const feedback = new Feedback(this, this._livechat); - this._chatWindow.replaceContentWith(feedback); + this.messaging.livechatButtonView.chatWindow.replaceContentWith(feedback); feedback.on('send_message', this, this._sendMessage); feedback.on('feedback_sent', this, this._closeChat); @@ -139,7 +135,7 @@ const LivechatButton = Widget.extend({ * @private */ _closeChat() { - this._chatWindow.destroy(); + this.messaging.livechatButtonView.chatWindow.destroy(); utils.set_cookie('im_livechat_session', "", -1); // remove cookie }, /** @@ -190,7 +186,7 @@ const LivechatButton = Widget.extend({ } notificationData.body = utils.Markup(notificationData.body); this._addMessage(notificationData); - if (this._chatWindow.isFolded() || !this._chatWindow.isAtBottom()) { + if (this.messaging.livechatButtonView.chatWindow.isFolded() || !this.messaging.livechatButtonView.chatWindow.isAtBottom()) { this._livechat.incrementUnreadCounter(); } this._renderMessages(); @@ -221,13 +217,14 @@ const LivechatButton = Widget.extend({ * @private */ _openChat: _.debounce(function () { - if (this._openingChat) { + if (this.messaging.livechatButtonView.isOpeningChat) { return; } const cookie = utils.get_cookie('im_livechat_session'); let def; this._openingChat = true; - clearTimeout(this._autoPopupTimeout); + this.messaging.livechatButtonView.update({ isOpeningChat: true }); + clearTimeout(this.messaging.livechatButtonView.autoOpenChatTimeout); if (cookie) { def = Promise.resolve(JSON.parse(cookie)); } else { @@ -280,9 +277,9 @@ const LivechatButton = Widget.extend({ }); } }).then(() => { - this._openingChat = false; + this.messaging.livechatButtonView.update({ isOpeningChat: false }); }).guardedCatch(() => { - this._openingChat = false; + this.messaging.livechatButtonView.update({ isOpeningChat: false }); }); }, 200, true), /** @@ -311,14 +308,16 @@ const LivechatButton = Widget.extend({ const options = { displayStars: false, headerBackgroundColor: this.options.header_background_color, - placeholder: this.options.input_placeholder || "", + placeholder: this.messaging.livechatButtonView.inputPlaceholder, titleColor: this.options.title_color, }; - this._chatWindow = new WebsiteLivechatWindow(this, this._livechat, options); - return this._chatWindow.appendTo($('body')).then(() => { + this.messaging.livechatButtonView.update({ + chatWindow: new WebsiteLivechatWindow(this, this._livechat, options), + }); + return this.messaging.livechatButtonView.chatWindow.appendTo($('body')).then(() => { const cssProps = { bottom: 0 }; cssProps[_t.database.parameters.direction === 'rtl' ? 'left' : 'right'] = 0; - this._chatWindow.$el.css(cssProps); + this.messaging.livechatButtonView.chatWindow.$el.css(cssProps); this.$el.hide(); }); }, @@ -336,11 +335,11 @@ const LivechatButton = Widget.extend({ * @private */ _renderMessages() { - const shouldScroll = !this._chatWindow.isFolded() && this._chatWindow.isAtBottom(); + const shouldScroll = !this.messaging.livechatButtonView.chatWindow.isFolded() && this.messaging.livechatButtonView.chatWindow.isAtBottom(); this._livechat.setMessages(this._messages); - this._chatWindow.render(); + this.messaging.livechatButtonView.chatWindow.render(); if (shouldScroll) { - this._chatWindow.scrollToBottom(); + this.messaging.livechatButtonView.chatWindow.scrollToBottom(); } }, /** @@ -372,19 +371,19 @@ const LivechatButton = Widget.extend({ } this._closeChat(); } - this._chatWindow.scrollToBottom(); + this.messaging.livechatButtonView.chatWindow.scrollToBottom(); }); }, /** * @private */ _sendWelcomeMessage() { - if (this.options.default_message) { + if (this.messaging.livechatButtonView.defaultMessage) { this._addMessage({ id: '_welcome', attachment_ids: [], author_id: this._livechat.getOperatorPID(), - body: this.options.default_message, + body: this.messaging.livechatButtonView.defaultMessage, date: time.datetime_to_str(new Date()), model: "mail.channel", res_id: this._livechat.getID(), @@ -402,12 +401,12 @@ const LivechatButton = Widget.extend({ */ _onCloseChatWindow(ev) { ev.stopPropagation(); - const isComposerDisabled = this._chatWindow.$('.o_thread_composer input').prop('disabled'); + const isComposerDisabled = this.messaging.livechatButtonView.chatWindow.$('.o_thread_composer input').prop('disabled'); const shouldAskFeedback = !isComposerDisabled && this._messages.find(function (message) { return message.getID() !== '_welcome'; }); if (shouldAskFeedback) { - this._chatWindow.toggleFold(false); + this.messaging.livechatButtonView.chatWindow.toggleFold(false); this._askFeedback(); } else { this._closeChat(); @@ -450,7 +449,7 @@ const LivechatButton = Widget.extend({ */ _onUpdatedTypingPartners(ev) { ev.stopPropagation(); - this._chatWindow.renderHeader(); + this.messaging.livechatButtonView.chatWindow.renderHeader(); }, /** * @private @@ -458,7 +457,7 @@ const LivechatButton = Widget.extend({ */ _onUpdatedUnreadCounter(ev) { ev.stopPropagation(); - this._chatWindow.renderHeader(); + this.messaging.livechatButtonView.chatWindow.renderHeader(); }, /** * @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 23e8957e8d9..366b51d51a6 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,69 @@ /** @odoo-module **/ import { registerModel } from '@mail/model/model_core'; +import { attr } from '@mail/model/model_field'; +import { clear } from '@mail/model/model_field_command'; registerModel({ name: 'LivechatButtonView', identifyingFields: ['messaging'], + recordMethods: { + /** + * @returns {string} + */ + _computeButtonText() { + if (this.messaging.publicLivechatOptions.button_text) { + return this.messaging.publicLivechatOptions.button_text; + } + return this.env._t("Chat with one of our collaborators"); + }, + /** + * @private + * @returns {string} + */ + _computeDefaultMessage() { + if (this.messaging.publicLivechatOptions.default_message) { + return this.messaging.publicLivechatOptions.default_message; + } + return this.env._t("How may I help you?"); + }, + /** + * @private + * @returns {string|FieldCommand} + */ + _computeInputPlaceholder() { + if (this.isChatbot) { + // void the default livechat placeholder in the user input + // as we use it for specific things (e.g: showing "please select an option above") + return clear(); + } + if (this.messaging.publicLivechatOptions.input_placeholder) { + return this.messaging.publicLivechatOptions.input_placeholder; + } + return this.env._t("Ask something ..."); + }, + }, + fields: { + autoOpenChatTimeout: attr(), + buttonText: attr({ + compute: '_computeButtonText', + }), + // livechat window + chatWindow: attr({ + default: null, + }), + defaultMessage: attr({ + compute: '_computeDefaultMessage', + }), + inputPlaceholder: attr({ + compute: '_computeInputPlaceholder', + default: '', + }), + isChatbot: attr({ + default: false, + }), + isOpeningChat: attr({ + default: false, + }), + }, }); diff --git a/addons/website_livechat/static/src/legacy/public_livechat.js b/addons/website_livechat/static/src/legacy/public_livechat.js index 9b063681f86..09c5736a122 100644 --- a/addons/website_livechat/static/src/legacy/public_livechat.js +++ b/addons/website_livechat/static/src/legacy/public_livechat.js @@ -31,8 +31,8 @@ LivechatButton.include({ $(window).trigger('resize'); await this._super(...arguments); this.el.innerHTML = ""; - if (this.options.button_text && !config.device.touch) { - this.el.dataset.content = this.options.button_text; + if (this.messaging.livechatButtonView.buttonText && !config.device.touch) { + this.el.dataset.content = this.messaging.livechatButtonView.buttonText; this.el.dataset.toggle = "popover"; this.el.dataset.trigger = "hover"; this.$el.popover({ 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 6412c872155..3774ff0c345 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 @@ -28,10 +28,8 @@ const LivechatButtonTestChatbot = LivechatButton.extend({ this._chatbotCurrentStep = this._chatbot.chatbot_welcome_steps[ this._chatbot.chatbot_welcome_steps.length - 1]; this._channelData = chatbotData.channel; - this._isChatbot = true; + this.messaging.livechatButtonView.update({ isChatbot: true }); this._serverURL = chatbotData.serverUrl; - - this.options.input_placeholder = ''; }, /** @@ -66,7 +64,7 @@ const LivechatButtonTestChatbot = LivechatButton.extend({ this.call('bus_service', 'addChannel', this._livechat.getUUID()); this.call('bus_service', 'startPolling'); utils.set_cookie('im_livechat_session', utils.unaccent(JSON.stringify(this._livechat.toData()), true), 60 * 60); - this._openingChat = false; + this.messaging.livechatButtonView.update({ isOpeningChat: false }); }); }, });