[FIX] im_livechat: remove non-required dependencies

This commit reduces the size and avoid conflicts caused by
the embed live chat bundle: jquery, legacy libs...

At the same time, this commit fixes the emoji picker that was
wrongly positionned and scrolled up the page when opening.

Finally, this commit ensures the chat window is always at the
bottom of the screen.

opw-3539362

closes odoo/odoo#139261

X-original-commit: 1b29419e50343bfd723b41368ba38ce63b063d92
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
tsm-odoo
2023-10-20 09:35:27 +00:00
parent e77cf7477d
commit c5517ce86e
8 changed files with 132 additions and 107 deletions
+2 -2
View File
@@ -102,7 +102,6 @@ Help your customers with this chat, and analyse their feedback.
'im_livechat/static/src/embed/common/**/*',
],
'im_livechat.assets_embed_external': [
'web/static/lib/jquery/jquery.js',
'web/static/src/libs/fontawesome/css/font-awesome.css',
'im_livechat/static/src/embed/common/scss/bootstrap_overridden.scss',
('include', 'web._assets_helpers'),
@@ -121,7 +120,8 @@ Help your customers with this chat, and analyse their feedback.
'bus/static/src/services/**/*.js',
'bus/static/src/workers/websocket_worker.js',
'bus/static/src/workers/websocket_worker_utils.js',
('remove', 'bus/static/src/services/assets_watchdog_service.js'),
('remove', 'bus/static/src/simple_notification_service.js'),
('include', 'im_livechat.assets_embed_core'),
'im_livechat/static/src/embed/external/**/*',
],
+1 -4
View File
@@ -1,11 +1,9 @@
/* @odoo-module */
import { makeRoot, makeShadow } from "@im_livechat/embed/common/boot_helpers";
import { LivechatButton } from "@im_livechat/embed/common/livechat_button";
import { makeShadow, makeRoot } from "@im_livechat/embed/common/boot_helpers";
import { serverUrl } from "@im_livechat/embed/common/livechat_data";
import { ChatWindowContainer } from "@mail/core/common/chat_window_container";
import { mount, whenReady } from "@odoo/owl";
import { templates } from "@web/core/assets";
@@ -19,7 +17,6 @@ import { session } from "@web/session";
await whenReady();
const mainComponentsRegistry = registry.category("main_components");
mainComponentsRegistry.add("LivechatRoot", { Component: LivechatButton });
mainComponentsRegistry.add("ChatWindowContainer", { Component: ChatWindowContainer });
const env = makeEnv();
await startServices(env);
odoo.isReady = true;
@@ -0,0 +1,3 @@
.o-main-components-container, .o_notification_manager {
position: fixed !important;
}
+1
View File
@@ -134,6 +134,7 @@ For more specific needs, you may also assign custom-defined actions
('remove', 'mail/static/lib/lame/lame.js'),
'mail/static/src/js/**/*',
'mail/static/src/core/common/**/*',
'mail/static/src/core/web_portal/**/*',
'mail/static/src/core/web/**/*',
'mail/static/src/**/common/**/*',
'mail/static/src/**/web/**/*',
@@ -7,7 +7,7 @@
t-att-class="{
'w-100 h-100 o-mobile': ui.isSmall,
'o-folded': props.chatWindow.folded,
'position-absolute bottom-0 overflow-hidden d-flex flex-column': !props.chatWindow.hidden,
'position-fixed bottom-0 overflow-hidden d-flex flex-column': !props.chatWindow.hidden,
'rounded-top-3': !props.chatWindow.hidden and !ui.isSmall,
}"
t-on-keydown="onKeydown"
+7 -96
View File
@@ -18,7 +18,6 @@ import {
Component,
onMounted,
onPatched,
onWillUnmount,
useChildSubEnv,
useEffect,
useRef,
@@ -99,8 +98,6 @@ export class Message extends Component {
isHovered: false,
isClicked: false,
expandOptions: false,
lastReadMoreIndex: 0,
isReadMoreByIndex: new Map(),
originalEmail: false,
emailHeaderOpen: false,
});
@@ -150,15 +147,14 @@ export class Message extends Component {
});
onMounted(() => {
if (this.messageBody.el) {
$(this.messageBody.el).find(".o-mail-read-more-less").remove();
this.insertReadMoreLess($(this.messageBody.el));
this.prepareMessageBody(this.messageBody.el);
}
if (this.shadowBody.el) {
this.shadowRoot = this.shadowBody.el.attachShadow({ mode: "open" });
const body = document.createElement("span");
body.innerHTML =
this.props.messageSearch?.highlight(this.message.body) ?? this.message.body;
this.insertReadMoreLess($(body));
this.prepareMessageBody(body);
const color = cookie.get("color_scheme") === "dark" ? "white" : "black";
this.shadowStyle = document.createElement("style");
this.shadowStyle.innerHTML = `
@@ -180,11 +176,6 @@ export class Message extends Component {
this.shadowRoot.appendChild(body);
}
});
onWillUnmount(() => {
if (this.messageBody.el) {
$(this.messageBody.el).find(".o-mail-read-more-less").remove();
}
});
}
get attClass() {
@@ -436,6 +427,11 @@ export class Message extends Component {
this.enterEditMode();
}
/**
* @param {HTMLElement} element
*/
prepareMessageBody(element) {}
enterEditMode() {
const messageContent = convertBrToLineBreak(this.props.message.body);
this.props.message.composer = {
@@ -473,91 +469,6 @@ export class Message extends Component {
});
}
/**
* Modifies the message to add the 'read more/read less' functionality
* All element nodes with 'data-o-mail-quote' attribute are concerned.
* All text nodes after a ``#stopSpelling`` element are concerned.
* Those text nodes need to be wrapped in a span (toggle functionality).
* All consecutive elements are joined in one 'read more/read less'.
*
* FIXME This method should be rewritten (task-2308951)
*
* @param {jQuery} $element
*/
insertReadMoreLess($element) {
const groups = [];
let readMoreNodes;
// nodeType 1: element_node
// nodeType 3: text_node
const $children = $element
.contents()
.filter(
(index, content) =>
content.nodeType === 1 || (content.nodeType === 3 && content.nodeValue.trim())
);
for (const child of $children) {
let $child = $(child);
// Hide Text nodes if "stopSpelling"
if (child.nodeType === 3 && $child.prevAll('[id*="stopSpelling"]').length > 0) {
// Convert Text nodes to Element nodes
$child = $("<span>", {
text: child.textContent,
"data-o-mail-quote": "1",
});
child.parentNode.replaceChild($child[0], child);
}
// Create array for each 'read more' with nodes to toggle
if (
$child.attr("data-o-mail-quote") ||
($child.get(0).nodeName === "BR" &&
$child.prev('[data-o-mail-quote="1"]').length > 0)
) {
if (!readMoreNodes) {
readMoreNodes = [];
groups.push(readMoreNodes);
}
$child.hide();
readMoreNodes.push($child);
} else {
readMoreNodes = undefined;
this.insertReadMoreLess($child);
}
}
for (const group of groups) {
const index = this.state.lastReadMoreIndex++;
// Insert link just before the first node
const $readMoreLess = $("<a>", {
class: "o-mail-read-more-less d-block",
href: "#",
text: "Read More",
}).insertBefore(group[0]);
// Toggle All next nodes
if (!this.state.isReadMoreByIndex.has(index)) {
this.state.isReadMoreByIndex.set(index, true);
}
const updateFromState = () => {
const isReadMore = this.state.isReadMoreByIndex.get(index);
for (const $child of group) {
$child.hide();
$child.toggle(!isReadMore);
}
$readMoreLess.text(isReadMore ? "Read More" : "Read Less");
};
$readMoreLess.click((e) => {
e.preventDefault();
this.state.isReadMoreByIndex.set(index, !this.state.isReadMoreByIndex.get(index));
updateFromState();
});
updateFromState();
}
}
openReactionMenu() {
this.dialog.add(MessageReactionMenu, {
message: this.props.message,
@@ -0,0 +1,111 @@
/* @odoo-module */
import { patch } from "@web/core/utils/patch";
import { Message } from "@mail/core/common/message";
import { onWillUnmount } from "@odoo/owl";
patch(Message.prototype, {
setup() {
super.setup(...arguments);
this.state.lastReadMoreIndex = 0;
this.state.isReadMoreByIndex = new Map();
onWillUnmount(() => {
this.messageBody.el?.querySelector(".o-mail-read-more-less")?.remove();
});
},
/**
* @override
* @param {HTMLElement} element
*/
prepareMessageBody(element) {
const $el = $(element);
$el.find(".o-mail-read-more-less").remove();
this.insertReadMoreLess($el);
},
/**
* Modifies the message to add the 'read more/read less' functionality
* All element nodes with 'data-o-mail-quote' attribute are concerned.
* All text nodes after a ``#stopSpelling`` element are concerned.
* Those text nodes need to be wrapped in a span (toggle functionality).
* All consecutive elements are joined in one 'read more/read less'.
*
* FIXME This method should be rewritten (task-2308951)
*
* @param {jQuery} $element
*/
insertReadMoreLess($element) {
const groups = [];
let readMoreNodes;
// nodeType 1: element_node
// nodeType 3: text_node
const $children = $element
.contents()
.filter(
(index, content) =>
content.nodeType === 1 || (content.nodeType === 3 && content.nodeValue.trim())
);
for (const child of $children) {
let $child = $(child);
// Hide Text nodes if "stopSpelling"
if (child.nodeType === 3 && $child.prevAll('[id*="stopSpelling"]').length > 0) {
// Convert Text nodes to Element nodes
$child = $("<span>", {
text: child.textContent,
"data-o-mail-quote": "1",
});
child.parentNode.replaceChild($child[0], child);
}
// Create array for each 'read more' with nodes to toggle
if (
$child.attr("data-o-mail-quote") ||
($child.get(0).nodeName === "BR" &&
$child.prev('[data-o-mail-quote="1"]').length > 0)
) {
if (!readMoreNodes) {
readMoreNodes = [];
groups.push(readMoreNodes);
}
$child.hide();
readMoreNodes.push($child);
} else {
readMoreNodes = undefined;
this.insertReadMoreLess($child);
}
}
for (const group of groups) {
const index = this.state.lastReadMoreIndex++;
// Insert link just before the first node
const $readMoreLess = $("<a>", {
class: "o-mail-read-more-less d-block",
href: "#",
text: "Read More",
}).insertBefore(group[0]);
// Toggle All next nodes
if (!this.state.isReadMoreByIndex.has(index)) {
this.state.isReadMoreByIndex.set(index, true);
}
const updateFromState = () => {
const isReadMore = this.state.isReadMoreByIndex.get(index);
for (const $child of group) {
$child.hide();
$child.toggle(!isReadMore);
}
$readMoreLess.text(isReadMore ? "Read More" : "Read Less");
};
$readMoreLess.click((e) => {
e.preventDefault();
this.state.isReadMoreByIndex.set(index, !this.state.isReadMoreByIndex.get(index));
updateFromState();
});
updateFromState();
}
},
});
@@ -64,11 +64,13 @@ export function parseAndTransform(htmlString, transformFunction) {
const string = htmlString.replace(/&lt;/g, openToken);
let children;
try {
children = $("<div>").html(string).contents();
const div = document.createElement("div");
div.innerHTML = string;
children = Array.from(div.childNodes);
} catch {
children = $("<div>")
.html("<pre>" + string + "</pre>")
.contents();
const div = document.createElement("div");
div.innerHTML = `<pre>${string}</pre>`;
children = Array.from(div.childNodes);
}
return _parseAndTransform(children, transformFunction).replace(
new RegExp(openToken, "g"),