diff --git a/addons/im_livechat/static/src/embed/chat_window/chat_window_patch.js b/addons/im_livechat/static/src/embed/chat_window/chat_window_patch.js index f8b370e7a56..7fb261678f9 100644 --- a/addons/im_livechat/static/src/embed/chat_window/chat_window_patch.js +++ b/addons/im_livechat/static/src/embed/chat_window/chat_window_patch.js @@ -30,6 +30,7 @@ patch(ChatWindow.prototype, { this.livechatState.hasFeedbackPanel = true; this.chatWindowService.show(this.props.chatWindow); } else { + this.thread?.delete(); super.close(); } this.livechatService.leaveSession(); diff --git a/addons/im_livechat/static/src/embed/chatbot/chatbot_service.js b/addons/im_livechat/static/src/embed/chatbot/chatbot_service.js index ca081cd6f6e..26a5165e534 100644 --- a/addons/im_livechat/static/src/embed/chatbot/chatbot_service.js +++ b/addons/im_livechat/static/src/embed/chatbot/chatbot_service.js @@ -107,11 +107,16 @@ export class ChatBotService { } /** - * Stop the chatbot script. + * Stop the chatbot script and reset its state. */ stop() { this.clear(); clearTimeout(this.nextStepTimeout); + this.currentStep = null; + this.isTyping = false; + if (this.livechatService.rule?.chatbot) { + this.chatbot = new Chatbot(this.livechatService.rule.chatbot); + } } /** @@ -168,6 +173,9 @@ export class ChatBotService { this.isTyping = true; this.nextStepTimeout = browser.setTimeout(async () => { const { step, stepMessage } = await this._getNextStep(); + if (!this.active) { + return; + } this.isTyping = false; if (!step && this.currentStep) { this.currentStep.isLast = true; @@ -369,11 +377,7 @@ export class ChatBotService { } get canRestart() { - return ( - this.livechatService.state !== SESSION_STATE.CLOSED && - this.completed && - !this.currentStep?.operatorFound - ); + return this.completed && !this.currentStep?.operatorFound; } get inputEnabled() { diff --git a/addons/im_livechat/static/src/embed/core/livechat_service.js b/addons/im_livechat/static/src/embed/core/livechat_service.js index 3f709386089..4e5e934a73f 100644 --- a/addons/im_livechat/static/src/embed/core/livechat_service.js +++ b/addons/im_livechat/static/src/embed/core/livechat_service.js @@ -24,7 +24,6 @@ export const SESSION_STATE = Object.freeze({ NONE: "NONE", CREATED: "CREATED", PERSISTED: "PERSISTED", - CLOSED: "CLOSED", }); export class LivechatService { @@ -134,7 +133,7 @@ export class LivechatService { async leaveSession({ notifyServer = true } = {}) { const session = JSON.parse(this.cookie.current[this.SESSION_COOKIE] ?? "{}"); this.cookie.deleteCookie(this.SESSION_COOKIE); - this.state = SESSION_STATE.CLOSED; + this.state = SESSION_STATE.NONE; if (!session?.uuid || !notifyServer) { return; } diff --git a/addons/im_livechat/static/src/embed/core/thread_model_patch.js b/addons/im_livechat/static/src/embed/core/thread_model_patch.js index ced9c32999e..8a0a39680b3 100644 --- a/addons/im_livechat/static/src/embed/core/thread_model_patch.js +++ b/addons/im_livechat/static/src/embed/core/thread_model_patch.js @@ -33,7 +33,7 @@ patch(Thread, { }); } onChange(thread, ["state", "seen_message_id", "message_unread_counter"], () => { - if (![SESSION_STATE.CLOSED, SESSION_STATE.NONE].includes(livechatService.state)) { + if (livechatService.state !== SESSION_STATE.NONE) { livechatService.updateSession({ state: thread.state, seen_message_id: thread.seen_message_id, diff --git a/addons/im_livechat/static/src/embed/core_ui/livechat_button.js b/addons/im_livechat/static/src/embed/core_ui/livechat_button.js index 0dea2f02c62..bfb7154f50a 100644 --- a/addons/im_livechat/static/src/embed/core_ui/livechat_button.js +++ b/addons/im_livechat/static/src/embed/core_ui/livechat_button.js @@ -1,12 +1,34 @@ /* @odoo-module */ -import { SESSION_STATE } from "@im_livechat/embed/core/livechat_service"; - -import { Component, useState } from "@odoo/owl"; +import { Component, useRef, useState } from "@odoo/owl"; +import { makeDraggableHook } from "@web/core/utils/draggable_hook_builder"; import { useService } from "@web/core/utils/hooks"; import { debounce } from "@web/core/utils/timing"; +const LIVECHAT_BUTTON_SIZE = 56; + +const useMovable = makeDraggableHook({ + name: "useMovable", + onWillStartDrag({ ctx, addCleanup, addStyle, getRect }) { + const { height } = getRect(ctx.current.element); + ctx.current.container = document.createElement("div"); + addStyle(ctx.current.container, { + position: "fixed", + top: 0, + bottom: `${height}px`, + left: 0, + right: 0, + }); + ctx.current.element.after(ctx.current.container); + addCleanup(() => ctx.current.container.remove()); + }, + onDrop({ ctx, getRect }) { + const { top, left } = getRect(ctx.current.element); + return { top, left }; + }, +}); + export class LivechatButton extends Component { static template = "im_livechat.LivechatButton"; static DEBOUNCE_DELAY = 500; @@ -20,6 +42,21 @@ export class LivechatButton extends Component { this.onClick = debounce(this.onClick.bind(this), LivechatButton.DEBOUNCE_DELAY, { leading: true, }); + this.ref = useRef("button"); + this.size = LIVECHAT_BUTTON_SIZE; + this.position = useState({ + left: `calc(97% - ${LIVECHAT_BUTTON_SIZE}px)`, + top: `calc(97% - ${LIVECHAT_BUTTON_SIZE}px)`, + }); + useMovable({ + cursor: "grabbing", + ref: this.ref, + elements: ".o-livechat-LivechatButton", + onDrop: ({ top, left }) => { + this.position.left = `${left}px`; + this.position.top = `${top}px`; + }, + }); } onClick() { @@ -31,12 +68,7 @@ export class LivechatButton extends Component { this.livechatService.initialized && this.livechatService.available && !this.livechatService.shouldRestoreSession && - this.livechatService.state !== SESSION_STATE.CLOSED && this.store.ChatWindow.records.length === 0 ); } - - get text() { - return this.livechatService.options.button_text; - } } diff --git a/addons/im_livechat/static/src/embed/core_ui/livechat_button.xml b/addons/im_livechat/static/src/embed/core_ui/livechat_button.xml index 7efa069109a..ba6e52c7c50 100644 --- a/addons/im_livechat/static/src/embed/core_ui/livechat_button.xml +++ b/addons/im_livechat/static/src/embed/core_ui/livechat_button.xml @@ -5,10 +5,11 @@