[IMP] im_livechat, web: prettify im_livechat module
closes odoo/odoo#112999 Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
+9
-8
@@ -1,13 +1,12 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { ThreadNeedactionPreviewView } from '@mail/components/thread_needaction_preview/thread_needaction_preview';
|
||||
import { ThreadNeedactionPreviewView } from "@mail/components/thread_needaction_preview/thread_needaction_preview";
|
||||
|
||||
import { patch } from 'web.utils';
|
||||
import { patch } from "web.utils";
|
||||
|
||||
const components = { ThreadNeedactionPreviewView };
|
||||
|
||||
patch(components.ThreadNeedactionPreviewView.prototype, 'thread_needaction_preview', {
|
||||
|
||||
patch(components.ThreadNeedactionPreviewView.prototype, "thread_needaction_preview", {
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -16,10 +15,12 @@ patch(components.ThreadNeedactionPreviewView.prototype, 'thread_needaction_previ
|
||||
* @override
|
||||
*/
|
||||
image(...args) {
|
||||
if (this.threadNeedactionPreviewView.thread.channel && this.threadNeedactionPreviewView.thread.channel.channel_type === 'livechat') {
|
||||
return '/mail/static/src/img/smiley/avatar.jpg';
|
||||
if (
|
||||
this.threadNeedactionPreviewView.thread.channel &&
|
||||
this.threadNeedactionPreviewView.thread.channel.channel_type === "livechat"
|
||||
) {
|
||||
return "/mail/static/src/img/smiley/avatar.jpg";
|
||||
}
|
||||
return this._super(...args);
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
import { assets } from "@web/core/assets";
|
||||
|
||||
/**
|
||||
* This file should be used in the context of an external widget loading (e.g: live chat in a non-Odoo website)
|
||||
* It overrides the 'loadJS' method that is supposed to load additional scripts, based on a relative URL (e.g: '/web/webclient/locale/en_US')
|
||||
* As we're not in an Odoo website context, the calls will not work, and we avoid a 404 request.
|
||||
*/
|
||||
* This file should be used in the context of an external widget loading (e.g: live chat in a non-Odoo website)
|
||||
* It overrides the 'loadJS' method that is supposed to load additional scripts, based on a relative URL (e.g: '/web/webclient/locale/en_US')
|
||||
* As we're not in an Odoo website context, the calls will not work, and we avoid a 404 request.
|
||||
*/
|
||||
assets.loadJS = function (url) {
|
||||
console.log('Tried to load the following script on an external website: ' + url);
|
||||
console.log("Tried to load the following script on an external website: " + url);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from '@web/core/registry';
|
||||
import { registry } from "@web/core/registry";
|
||||
import { standardWidgetProps } from "@web/views/widgets/standard_widget_props";
|
||||
|
||||
const { Component } = owl;
|
||||
@@ -20,4 +20,4 @@ ColorsResetButton.extractProps = ({ attrs }) => {
|
||||
return attrs.options;
|
||||
};
|
||||
|
||||
registry.category('view_widgets').add('colors_reset_button', ColorsResetButton);
|
||||
registry.category("view_widgets").add("colors_reset_button", ColorsResetButton);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import {
|
||||
Many2ManyTagsField,
|
||||
@@ -19,11 +18,11 @@ export class ChatbotScriptTriggeringAnswersMany2Many extends Many2ManyTagsField
|
||||
|
||||
if (this.props.record.model.root.data.id) {
|
||||
this.env.services.user.updateContext({
|
||||
force_domain_chatbot_script_id: this.props.record.model.root.data.id
|
||||
force_domain_chatbot_script_id: this.props.record.model.root.data.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const chatbotScriptTriggeringAnswersMany2Many = {
|
||||
...many2ManyTagsField,
|
||||
|
||||
@@ -2,24 +2,28 @@
|
||||
|
||||
import { ListRenderer } from "@web/views/list/list_renderer";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { patch } from '@web/core/utils/patch';
|
||||
import { useX2ManyCrud, useOpenX2ManyRecord, X2ManyFieldDialog } from "@web/views/fields/relational_utils";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import {
|
||||
useX2ManyCrud,
|
||||
useOpenX2ManyRecord,
|
||||
X2ManyFieldDialog,
|
||||
} from "@web/views/fields/relational_utils";
|
||||
import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field";
|
||||
|
||||
const fieldRegistry = registry.category("fields");
|
||||
|
||||
patch(X2ManyFieldDialog.prototype, 'chatbot_script_step_sequence', {
|
||||
patch(X2ManyFieldDialog.prototype, "chatbot_script_step_sequence", {
|
||||
/**
|
||||
* Dirty patching of the 'X2ManyFieldDialog'.
|
||||
* It is done to force the "save and new" to close the dialog first, and then click again on
|
||||
* the "Add a line" link.
|
||||
*
|
||||
*
|
||||
* This is the only way (or at least the least complicated) to correctly compute the sequence
|
||||
* field, which is crucial when creating chatbot.steps, as they depend on each other.
|
||||
*
|
||||
*
|
||||
*/
|
||||
async save({ saveAndNew }) {
|
||||
if (this.record.resModel !== 'chatbot.script.step') {
|
||||
if (this.record.resModel !== "chatbot.script.step") {
|
||||
return this._super(...arguments);
|
||||
}
|
||||
|
||||
@@ -30,12 +34,12 @@ patch(X2ManyFieldDialog.prototype, 'chatbot_script_step_sequence', {
|
||||
}
|
||||
|
||||
if (saveAndNew) {
|
||||
document.querySelector('.o_field_x2many_list_row_add a').click();
|
||||
document.querySelector(".o_field_x2many_list_row_add a").click();
|
||||
}
|
||||
this.props.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export class ChatbotStepsOne2manyRenderer extends ListRenderer {
|
||||
@@ -65,10 +69,7 @@ export class ChatbotStepsOne2many extends X2ManyField {
|
||||
setup() {
|
||||
super.setup();
|
||||
|
||||
const { saveRecord, updateRecord } = useX2ManyCrud(
|
||||
() => this.list,
|
||||
this.isMany2Many
|
||||
);
|
||||
const { saveRecord, updateRecord } = useX2ManyCrud(() => this.list, this.isMany2Many);
|
||||
|
||||
const openRecord = useOpenX2ManyRecord({
|
||||
resModel: this.list.resModel,
|
||||
@@ -77,7 +78,7 @@ export class ChatbotStepsOne2many extends X2ManyField {
|
||||
getList: () => this.list,
|
||||
saveRecord: async (record) => {
|
||||
await saveRecord(record);
|
||||
await this.props.record.save({stayInEdition: true});
|
||||
await this.props.record.save({ stayInEdition: true });
|
||||
},
|
||||
updateRecord: updateRecord,
|
||||
});
|
||||
@@ -94,7 +95,7 @@ export class ChatbotStepsOne2many extends X2ManyField {
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const chatbotStepsOne2many = {
|
||||
...x2ManyField,
|
||||
@@ -105,5 +106,5 @@ fieldRegistry.add("chatbot_steps_one2many", chatbotStepsOne2many);
|
||||
|
||||
ChatbotStepsOne2many.components = {
|
||||
...X2ManyField.components,
|
||||
ListRenderer: ChatbotStepsOne2manyRenderer
|
||||
ListRenderer: ChatbotStepsOne2manyRenderer,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import CCThrottleFunctionObject from '@im_livechat/legacy/models/cc_throttle_function_object';
|
||||
import CCThrottleFunctionObject from "@im_livechat/legacy/models/cc_throttle_function_object";
|
||||
|
||||
/**
|
||||
* A function that creates a cancellable and clearable (CC) throttle version
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import Class from 'web.Class';
|
||||
import Class from "web.Class";
|
||||
|
||||
/**
|
||||
* This object models the behaviour of the clearable and cancellable (CC)
|
||||
* throttle version of a provided function.
|
||||
*/
|
||||
const CCThrottleFunctionObject = Class.extend({
|
||||
|
||||
/**
|
||||
* @param {Object} params
|
||||
* @param {integer} params.duration duration of the 'cooldown' phase, i.e.
|
||||
@@ -90,10 +89,7 @@ const CCThrottleFunctionObject = Class.extend({
|
||||
*/
|
||||
_cooldown() {
|
||||
this.cancel();
|
||||
this._cooldownTimeout = setTimeout(
|
||||
this._onCooldownTimeout.bind(this),
|
||||
this._duration
|
||||
);
|
||||
this._cooldownTimeout = setTimeout(this._onCooldownTimeout.bind(this), this._duration);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import CCThrottleFunction from '@im_livechat/legacy/models/cc_throttle_function';
|
||||
import Timer from '@im_livechat/legacy/models/timer';
|
||||
import Timers from '@im_livechat/legacy/models/timers';
|
||||
import CCThrottleFunction from "@im_livechat/legacy/models/cc_throttle_function";
|
||||
import Timer from "@im_livechat/legacy/models/timer";
|
||||
import Timers from "@im_livechat/legacy/models/timers";
|
||||
|
||||
import Class from 'web.Class';
|
||||
import { _t } from 'web.core';
|
||||
import session from 'web.session';
|
||||
import Mixins from 'web.mixins';
|
||||
import { sprintf } from 'web.utils';
|
||||
import Class from "web.Class";
|
||||
import { _t } from "web.core";
|
||||
import session from "web.session";
|
||||
import Mixins from "web.mixins";
|
||||
import { sprintf } from "web.utils";
|
||||
|
||||
/**
|
||||
* Thread model that represents a livechat on the website-side. This livechat
|
||||
@@ -98,14 +98,18 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
|
||||
if (params.data.message_unread_counter !== undefined) {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({
|
||||
unreadCounter: params.data.message_unread_counter
|
||||
unreadCounter: params.data.message_unread_counter,
|
||||
});
|
||||
}
|
||||
|
||||
if (_.isBoolean(params.data.folded)) {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({ isFolded: params.data.folded });
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({
|
||||
isFolded: params.data.folded,
|
||||
});
|
||||
} else {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({ isFolded: params.data.state === 'folded' });
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({
|
||||
isFolded: params.data.state === "folded",
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -136,9 +140,11 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
* @override
|
||||
* @returns {@im_livechat/legacy/models/public_livechat_message[]}
|
||||
*/
|
||||
getMessages() {
|
||||
getMessages() {
|
||||
// ignore removed messages
|
||||
return this.messaging.publicLivechatGlobal.messages.filter(message => !message.widget.isEmpty()).map(message => message.widget);
|
||||
return this.messaging.publicLivechatGlobal.messages
|
||||
.filter((message) => !message.widget.isEmpty())
|
||||
.map((message) => message.widget);
|
||||
},
|
||||
/**
|
||||
* Get the text to display when some partners are typing something on the
|
||||
@@ -167,28 +173,34 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
*/
|
||||
getTypingMembersToText() {
|
||||
const typingPartnerIDs = this._typingPartnerIDs;
|
||||
const typingMembers = (
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.operator && this._typingPartnerIDs.includes(this.messaging.publicLivechatGlobal.publicLivechat.operator.id)
|
||||
? [this.messaging.publicLivechatGlobal.publicLivechat.operator]
|
||||
: []
|
||||
);
|
||||
const typingMembers =
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.operator &&
|
||||
this._typingPartnerIDs.includes(
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.operator.id
|
||||
)
|
||||
? [this.messaging.publicLivechatGlobal.publicLivechat.operator]
|
||||
: [];
|
||||
const sortedTypingMembers = _.sortBy(typingMembers, function (member) {
|
||||
return _.indexOf(typingPartnerIDs, member.id);
|
||||
});
|
||||
const displayableTypingMembers = sortedTypingMembers.slice(0, 3);
|
||||
|
||||
if (displayableTypingMembers.length === 0) {
|
||||
return '';
|
||||
return "";
|
||||
} else if (displayableTypingMembers.length === 1) {
|
||||
return sprintf(_t("%s is typing..."), displayableTypingMembers[0].name);
|
||||
} else if (displayableTypingMembers.length === 2) {
|
||||
return sprintf(_t("%s and %s are typing..."),
|
||||
displayableTypingMembers[0].name,
|
||||
displayableTypingMembers[1].name);
|
||||
return sprintf(
|
||||
_t("%s and %s are typing..."),
|
||||
displayableTypingMembers[0].name,
|
||||
displayableTypingMembers[1].name
|
||||
);
|
||||
} else {
|
||||
return sprintf(_t("%s, %s and more are typing..."),
|
||||
displayableTypingMembers[0].name,
|
||||
displayableTypingMembers[1].name);
|
||||
return sprintf(
|
||||
_t("%s, %s and more are typing..."),
|
||||
displayableTypingMembers[0].name,
|
||||
displayableTypingMembers[1].name
|
||||
);
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -204,7 +216,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isSomeoneTyping() {
|
||||
return !(_.isEmpty(this._typingPartnerIDs));
|
||||
return !_.isEmpty(this._typingPartnerIDs);
|
||||
},
|
||||
/**
|
||||
* Mark the thread as read, which resets the unread counter to 0. This is
|
||||
@@ -289,18 +301,18 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
*/
|
||||
toData() {
|
||||
return {
|
||||
chatbot_script_id: this.messaging.publicLivechatGlobal.publicLivechat.data.chatbot_script_id,
|
||||
chatbot_script_id:
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.data.chatbot_script_id,
|
||||
folded: this.messaging.publicLivechatGlobal.publicLivechat.isFolded,
|
||||
id: this.messaging.publicLivechatGlobal.publicLivechat.id,
|
||||
message_unread_counter: this.messaging.publicLivechatGlobal.publicLivechat.unreadCounter,
|
||||
operator_pid: (
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.operator
|
||||
message_unread_counter:
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.unreadCounter,
|
||||
operator_pid: this.messaging.publicLivechatGlobal.publicLivechat.operator
|
||||
? [
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.operator.id,
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.operator.name,
|
||||
]
|
||||
: []
|
||||
),
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.operator.id,
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.operator.name,
|
||||
]
|
||||
: [],
|
||||
name: this.messaging.publicLivechatGlobal.publicLivechat.name,
|
||||
uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
|
||||
};
|
||||
@@ -344,10 +356,14 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
// sent. Until then, do not notify visitor is typing.
|
||||
return;
|
||||
}
|
||||
return session.rpc('/im_livechat/notify_typing', {
|
||||
uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
|
||||
is_typing: params.typing,
|
||||
}, { shadow: true });
|
||||
return session.rpc(
|
||||
"/im_livechat/notify_typing",
|
||||
{
|
||||
uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
|
||||
is_typing: params.typing,
|
||||
},
|
||||
{ shadow: true }
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Warn views that the list of users that are currently typing on this
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import * as mailUtils from '@mail/js/utils';
|
||||
import * as mailUtils from "@mail/js/utils";
|
||||
|
||||
import Class from 'web.Class';
|
||||
import { _t } from 'web.core';
|
||||
import session from 'web.session';
|
||||
import time from 'web.time';
|
||||
import Class from "web.Class";
|
||||
import { _t } from "web.core";
|
||||
import session from "web.session";
|
||||
import time from "web.time";
|
||||
|
||||
/**
|
||||
* This is a message that is handled by im_livechat, without making use of the
|
||||
@@ -15,7 +15,6 @@ import time from 'web.time';
|
||||
* @see @im_livechat/legacy/models/public_livechat_message for more information.
|
||||
*/
|
||||
const PublicLivechatMessage = Class.extend({
|
||||
|
||||
/**
|
||||
* @param {@im_livechat/legacy/widgets/livechat_button} parent
|
||||
* @param {Messaging} messaging
|
||||
@@ -78,7 +77,7 @@ const PublicLivechatMessage = Class.extend({
|
||||
} else if (this.hasAuthor() && session.user_id) {
|
||||
source += `/web/image/res.partner/${this.getAuthorID()}/avatar_128`;
|
||||
} else {
|
||||
source += '/mail/static/src/img/smiley/avatar.jpg';
|
||||
source += "/mail/static/src/img/smiley/avatar.jpg";
|
||||
}
|
||||
return source;
|
||||
},
|
||||
@@ -120,13 +119,13 @@ const PublicLivechatMessage = Class.extend({
|
||||
* @return {string}
|
||||
*/
|
||||
getDateDay() {
|
||||
const date = this.getDate().format('YYYY-MM-DD');
|
||||
if (date === moment().format('YYYY-MM-DD')) {
|
||||
const date = this.getDate().format("YYYY-MM-DD");
|
||||
if (date === moment().format("YYYY-MM-DD")) {
|
||||
return _t("Today");
|
||||
} else if (date === moment().subtract(1, 'days').format('YYYY-MM-DD')) {
|
||||
} else if (date === moment().subtract(1, "days").format("YYYY-MM-DD")) {
|
||||
return _t("Yesterday");
|
||||
}
|
||||
return this.getDate().format('LL');
|
||||
return this.getDate().format("LL");
|
||||
},
|
||||
/**
|
||||
* Get the text to display for the author of the message
|
||||
@@ -260,9 +259,11 @@ const PublicLivechatMessage = Class.extend({
|
||||
* @return {boolean}
|
||||
*/
|
||||
_isMyselfAuthor() {
|
||||
return this.hasAuthor() && (this.getAuthorID() === this.messaging.publicLivechatGlobal.options.current_partner_id);
|
||||
return (
|
||||
this.hasAuthor() &&
|
||||
this.getAuthorID() === this.messaging.publicLivechatGlobal.options.current_partner_id
|
||||
);
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export default PublicLivechatMessage;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import Class from 'web.Class';
|
||||
import Class from "web.Class";
|
||||
|
||||
/**
|
||||
* This class creates a timer which, when times out, calls a function.
|
||||
*/
|
||||
const Timer = Class.extend({
|
||||
|
||||
/**
|
||||
* Instantiate a new timer. Note that the timer is not started on
|
||||
* initialization (@see start method).
|
||||
@@ -60,7 +59,6 @@ const Timer = Class.extend({
|
||||
_onTimeout() {
|
||||
this._timeoutCallback();
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export default Timer;
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import Timer from '@im_livechat/legacy/models/timer';
|
||||
import Timer from "@im_livechat/legacy/models/timer";
|
||||
|
||||
import Class from 'web.Class';
|
||||
import Class from "web.Class";
|
||||
|
||||
/**
|
||||
* This class lists several timers that use a same callback and duration.
|
||||
*/
|
||||
const Timers = Class.extend({
|
||||
|
||||
/**
|
||||
* Instantiate a new list of timers
|
||||
*
|
||||
@@ -39,7 +38,7 @@ const Timers = Class.extend({
|
||||
* @param {Array} [params.timeoutCallbackArguments]
|
||||
* @param {integer} params.timerID
|
||||
*/
|
||||
registerTimer(params) {
|
||||
registerTimer(params) {
|
||||
const timerID = params.timerID;
|
||||
if (this._timers[timerID]) {
|
||||
this._timers[timerID].clear();
|
||||
@@ -48,11 +47,11 @@ const Timers = Class.extend({
|
||||
duration: this._duration,
|
||||
onTimeout: this._timeoutCallback,
|
||||
};
|
||||
if ('timeoutCallbackArguments' in params) {
|
||||
timerParams.onTimeout = this._timeoutCallback.bind.apply(
|
||||
this._timeoutCallback,
|
||||
[null, ...params.timeoutCallbackArguments]
|
||||
);
|
||||
if ("timeoutCallbackArguments" in params) {
|
||||
timerParams.onTimeout = this._timeoutCallback.bind.apply(this._timeoutCallback, [
|
||||
null,
|
||||
...params.timeoutCallbackArguments,
|
||||
]);
|
||||
} else {
|
||||
timerParams.onTimeout = this._timeoutCallback;
|
||||
}
|
||||
@@ -66,14 +65,13 @@ const Timers = Class.extend({
|
||||
* @param {Object} params
|
||||
* @param {integer} params.timerID
|
||||
*/
|
||||
unregisterTimer(params) {
|
||||
unregisterTimer(params) {
|
||||
const timerID = params.timerID;
|
||||
if (this._timers[timerID]) {
|
||||
this._timers[timerID].clear();
|
||||
delete this._timers[timerID];
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export default Timers;
|
||||
|
||||
@@ -21,7 +21,7 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
width: 100%;
|
||||
height: 100%!important;
|
||||
height: 100% !important;
|
||||
box-shadow: none;
|
||||
&.o_folded {
|
||||
display: none;
|
||||
@@ -37,9 +37,9 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
color: white;
|
||||
padding: $o-mail-chatter-gap*0.5 $o-mail-chatter-gap;
|
||||
padding: $o-mail-chatter-gap * 0.5 $o-mail-chatter-gap;
|
||||
border-radius: 3px 3px 0 0;
|
||||
border-bottom: 1px solid map-get($grays, '300');
|
||||
border-bottom: 1px solid map-get($grays, "300");
|
||||
background-color: $o-brand-odoo;
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
@@ -89,7 +89,7 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
padding-left: 2px;
|
||||
|
||||
.o_mail_thread_typing_icon_dot {
|
||||
background: map-get($grays, '300');
|
||||
background: map-get($grays, "300");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,6 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
@include o-hover-opacity(0.7, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.o_mail_thread {
|
||||
@@ -139,7 +138,7 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
width: auto;
|
||||
height: 28px;
|
||||
color: white;
|
||||
background-color: map-get($grays, '900');
|
||||
background-color: map-get($grays, "900");
|
||||
cursor: pointer;
|
||||
box-shadow: none;
|
||||
|
||||
@@ -172,7 +171,7 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
> li.o_thread_window_header {
|
||||
font-size: 12px;
|
||||
padding: 3px 5px;
|
||||
&~li.o_thread_window_header {
|
||||
& ~ li.o_thread_window_header {
|
||||
border-top: 1px solid white;
|
||||
}
|
||||
&:hover {
|
||||
@@ -197,7 +196,6 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
// legacy: mail/thread.scss
|
||||
//-------------------------------------
|
||||
|
||||
|
||||
.o_mail_thread_loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -208,7 +206,8 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.o_mail_thread, .o_mail_activity {
|
||||
.o_mail_thread,
|
||||
.o_mail_activity {
|
||||
.o_thread_show_more {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -230,7 +229,7 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
border-bottom: 1px solid map-get($grays, '400');
|
||||
border-bottom: 1px solid map-get($grays, "400");
|
||||
text-align: center;
|
||||
|
||||
.o_thread_date {
|
||||
@@ -263,8 +262,8 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
margin-bottom: 0px;
|
||||
|
||||
&.o_mail_not_discussion {
|
||||
background-color: rgba(map-get($grays, '300'), 0.5);
|
||||
border-bottom: 1px solid map-get($grays, '400');
|
||||
background-color: rgba(map-get($grays, "300"), 0.5);
|
||||
border-bottom: 1px solid map-get($grays, "400");
|
||||
}
|
||||
|
||||
.o_thread_message_sidebar {
|
||||
@@ -323,7 +322,8 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover, &.o_thread_selected_message {
|
||||
&:hover,
|
||||
&.o_thread_selected_message {
|
||||
.o_thread_message_side_date {
|
||||
display: inline-block;
|
||||
opacity: $o-mail-thread-side-date-opacity;
|
||||
@@ -365,7 +365,8 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
[summary~=o_mail_notification] { // name conflicts with channel notifications, but is odoo notification buttons to hide in chatter if present
|
||||
[summary~="o_mail_notification"] {
|
||||
// name conflicts with channel notifications, but is odoo notification buttons to hide in chatter if present
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -396,7 +397,10 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
}
|
||||
}
|
||||
|
||||
.o_thread_message_star, .o_thread_message_needaction, .o_thread_message_reply, .o_thread_message_notification {
|
||||
.o_thread_message_star,
|
||||
.o_thread_message_needaction,
|
||||
.o_thread_message_reply,
|
||||
.o_thread_message_notification {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
@@ -409,7 +413,8 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
}
|
||||
}
|
||||
|
||||
.o_attachments_list, .o_attachments_previews {
|
||||
.o_attachments_list,
|
||||
.o_attachments_previews {
|
||||
&:last-child {
|
||||
margin-bottom: $grid-gutter-width;
|
||||
}
|
||||
@@ -443,7 +448,7 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
border-radius: 50%;
|
||||
background: map-get($grays, '800');
|
||||
background: map-get($grays, "800");
|
||||
animation: o_mail_thread_typing_icon_dot 1.5s linear infinite;
|
||||
|
||||
&:nth-child(2) {
|
||||
@@ -457,7 +462,9 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
}
|
||||
|
||||
@keyframes o_mail_thread_typing_icon_dot {
|
||||
0%, 40%, 100% {
|
||||
0%,
|
||||
40%,
|
||||
100% {
|
||||
transform: initial;
|
||||
}
|
||||
20% {
|
||||
@@ -478,7 +485,7 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
background-color: rgba(60, 60, 60, 0.6);
|
||||
font-family: 'Lucida Grande', 'Lucida Sans Unicode', Arial, Verdana, sans-serif;
|
||||
font-family: "Lucida Grande", "Lucida Sans Unicode", Arial, Verdana, sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
@@ -498,7 +505,7 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
}
|
||||
|
||||
.btn {
|
||||
color: #FFFFFF;
|
||||
color: #ffffff;
|
||||
background-color: #30908e;
|
||||
border-color: #2d8685;
|
||||
border: 1px solid transparent;
|
||||
@@ -532,9 +539,9 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
background-color: #FFFFFF;
|
||||
background-color: #ffffff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #CED4DA;
|
||||
border: 1px solid #ced4da;
|
||||
}
|
||||
button {
|
||||
display: inline-block;
|
||||
@@ -564,10 +571,11 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
|
||||
> img {
|
||||
width: 65px;
|
||||
opacity: 0.60;
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
margin: 10px;
|
||||
&:hover, &.selected {
|
||||
&:hover,
|
||||
&.selected {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@@ -606,7 +614,7 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
.o_PublicLivechatMessage {
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
|
||||
&.o-isVisitorTheAuthor {
|
||||
flex-direction: row-reverse !important;
|
||||
}
|
||||
@@ -648,7 +656,7 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
justify-content: flex-end;
|
||||
margin-left: map-get($spacers, 4);
|
||||
}
|
||||
|
||||
|
||||
&.o-isOperatorTheAuthor {
|
||||
margin-right: map-get($spacers, 4);
|
||||
}
|
||||
@@ -664,11 +672,10 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
&.o-isVisitorTheAuthor {
|
||||
margin-left: map-get($spacers, 2);
|
||||
}
|
||||
|
||||
|
||||
&.o-isOperatorTheAuthor {
|
||||
margin-right: map-get($spacers, 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.o_PublicLivechatMessage_background {
|
||||
@@ -678,11 +685,11 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: opacity .5s ease-out;
|
||||
transition: opacity 0.5s ease-out;
|
||||
margin-right: map-get($spacers, 5);
|
||||
border-bottom-right-radius: 0.6rem !important;
|
||||
border-bottom-left-radius: 0.6rem !important;
|
||||
border: 1px solid #C9CCD2 !important;
|
||||
border: 1px solid #c9ccd2 !important;
|
||||
|
||||
&.o-isVisitorTheAuthor {
|
||||
border-bottom-left-radius: 0.6rem !important;
|
||||
@@ -700,7 +707,7 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
border-color: #17a2b8 !important;
|
||||
background-color: rgba(23, 162, 184, 0.5) !important;
|
||||
color: #000;
|
||||
opacity: 15%
|
||||
opacity: 15%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import session from 'web.session';
|
||||
import time from 'web.time';
|
||||
import utils from 'web.utils';
|
||||
import session from "web.session";
|
||||
import time from "web.time";
|
||||
import utils from "web.utils";
|
||||
|
||||
import LivechatButton from '@im_livechat/legacy/widgets/livechat_button';
|
||||
import LivechatButton from "@im_livechat/legacy/widgets/livechat_button";
|
||||
|
||||
/**
|
||||
* Override of the LivechatButton to include chatbot capabilities.
|
||||
@@ -14,11 +14,10 @@ import LivechatButton from '@im_livechat/legacy/widgets/livechat_button';
|
||||
* - When the user picks an option or answers to the chatbot, display a "chatbot is typing..."
|
||||
* message for a couple seconds and then trigger the next step of the script
|
||||
*/
|
||||
LivechatButton.include({
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private - LiveChat Overrides
|
||||
//--------------------------------------------------------------------------
|
||||
LivechatButton.include({
|
||||
//--------------------------------------------------------------------------
|
||||
// Private - LiveChat Overrides
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -28,7 +27,11 @@ import LivechatButton from '@im_livechat/legacy/widgets/livechat_button';
|
||||
const parameters = this._super(...arguments);
|
||||
|
||||
const { publicLivechat } = this.messaging.publicLivechatGlobal;
|
||||
if (publicLivechat && publicLivechat.isTemporary && !publicLivechat.data.chatbot_script_id) {
|
||||
if (
|
||||
publicLivechat &&
|
||||
publicLivechat.isTemporary &&
|
||||
!publicLivechat.data.chatbot_script_id
|
||||
) {
|
||||
return parameters;
|
||||
} else if (publicLivechat && publicLivechat.data.chatbot_script_id) {
|
||||
parameters.chatbot_script_id = publicLivechat.data.chatbot_script_id;
|
||||
@@ -46,7 +49,9 @@ import LivechatButton from '@im_livechat/legacy/widgets/livechat_button';
|
||||
if (this.messaging.publicLivechatGlobal.chatbot.isActive) {
|
||||
this._sendWelcomeChatbotMessage(
|
||||
0,
|
||||
this.messaging.publicLivechatGlobal.chatbot.state === 'welcome' ? 0 : this.messaging.publicLivechatGlobal.chatbot.messageDelay,
|
||||
this.messaging.publicLivechatGlobal.chatbot.state === "welcome"
|
||||
? 0
|
||||
: this.messaging.publicLivechatGlobal.chatbot.messageDelay
|
||||
);
|
||||
} else {
|
||||
this._super(...arguments);
|
||||
@@ -79,16 +84,14 @@ import LivechatButton from '@im_livechat/legacy/widgets/livechat_button';
|
||||
|
||||
if (chatbotStep.chatbot_step_message) {
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.addMessage({
|
||||
id: '_welcome_' + stepIndex,
|
||||
id: "_welcome_" + stepIndex,
|
||||
is_discussion: true, // important for css style -> we only want white background for chatbot
|
||||
author: (
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.operator
|
||||
author: this.messaging.publicLivechatGlobal.publicLivechat.operator
|
||||
? {
|
||||
id: this.messaging.publicLivechatGlobal.publicLivechat.operator.id,
|
||||
name: this.messaging.publicLivechatGlobal.publicLivechat.operator.name,
|
||||
}
|
||||
: [['clear']]
|
||||
),
|
||||
id: this.messaging.publicLivechatGlobal.publicLivechat.operator.id,
|
||||
name: this.messaging.publicLivechatGlobal.publicLivechat.operator.name,
|
||||
}
|
||||
: [["clear"]],
|
||||
body: utils.Markup(chatbotStep.chatbot_step_message),
|
||||
chatbot_script_step_id: chatbotStep.chatbot_script_step_id,
|
||||
chatbot_step_answers: chatbotStep.chatbot_step_answers,
|
||||
@@ -106,7 +109,10 @@ import LivechatButton from '@im_livechat/legacy/widgets/livechat_button';
|
||||
|
||||
this.messaging.publicLivechatGlobal.chatbot.update({
|
||||
welcomeMessageTimeout: setTimeout(() => {
|
||||
if (!this.messaging.publicLivechatGlobal.chatWindow || !this.messaging.publicLivechatGlobal.chatWindow.exists()) {
|
||||
if (
|
||||
!this.messaging.publicLivechatGlobal.chatWindow ||
|
||||
!this.messaging.publicLivechatGlobal.chatWindow.exists()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this._sendWelcomeChatbotMessage(stepIndex + 1, welcomeMessageDelay);
|
||||
@@ -114,7 +120,10 @@ import LivechatButton from '@im_livechat/legacy/widgets/livechat_button';
|
||||
}, welcomeMessageDelay),
|
||||
});
|
||||
} else {
|
||||
if (this.messaging.publicLivechatGlobal.chatbot.currentStep.data.chatbot_step_type === 'forward_operator') {
|
||||
if (
|
||||
this.messaging.publicLivechatGlobal.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
|
||||
@@ -147,10 +156,10 @@ import LivechatButton from '@im_livechat/legacy/widgets/livechat_button';
|
||||
ev.stopPropagation();
|
||||
|
||||
const $target = $(ev.currentTarget);
|
||||
const stepId = $target.closest('ul').data('chatbotStepId');
|
||||
const selectedAnswer = $target.data('chatbotStepAnswerId');
|
||||
const stepId = $target.closest("ul").data("chatbotStepId");
|
||||
const selectedAnswer = $target.data("chatbotStepAnswerId");
|
||||
|
||||
const redirectLink = $target.data('chatbotStepRedirectLink');
|
||||
const redirectLink = $target.data("chatbotStepRedirectLink");
|
||||
this.messaging.publicLivechatGlobal.chatbot.update({ isRedirecting: !!redirectLink });
|
||||
|
||||
await this.messaging.publicLivechatGlobal.livechatButtonView.sendMessage({
|
||||
@@ -171,18 +180,19 @@ import LivechatButton from '@im_livechat/legacy/widgets/livechat_button';
|
||||
}
|
||||
const messageId = stepMessage.id;
|
||||
stepMessage.widget.setChatbotStepAnswerId(selectedAnswer);
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep.data.chatbot_selected_answer_id = selectedAnswer;
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep.data.chatbot_selected_answer_id =
|
||||
selectedAnswer;
|
||||
this.messaging.publicLivechatGlobal.chatWindow.renderMessages();
|
||||
this.messaging.publicLivechatGlobal.chatbot.saveSession();
|
||||
|
||||
const saveAnswerPromise = session.rpc('/chatbot/answer/save', {
|
||||
const saveAnswerPromise = session.rpc("/chatbot/answer/save", {
|
||||
channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
|
||||
message_id: messageId,
|
||||
selected_answer_id: selectedAnswer,
|
||||
});
|
||||
|
||||
if (redirectLink) {
|
||||
await saveAnswerPromise; // ensure answer is saved before redirecting
|
||||
await saveAnswerPromise; // ensure answer is saved before redirecting
|
||||
window.location = redirectLink;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import concurrency from 'web.concurrency';
|
||||
import core from 'web.core';
|
||||
import session from 'web.session';
|
||||
import utils from 'web.utils';
|
||||
import Widget from 'web.Widget';
|
||||
import concurrency from "web.concurrency";
|
||||
import core from "web.core";
|
||||
import session from "web.session";
|
||||
import utils from "web.utils";
|
||||
import Widget from "web.Widget";
|
||||
|
||||
const _t = core._t;
|
||||
/*
|
||||
@@ -14,14 +14,14 @@ const _t = core._t;
|
||||
* (only for red smiley), and sends the user feedback to the server.
|
||||
*/
|
||||
const Feedback = Widget.extend({
|
||||
template: 'im_livechat.legacy.im_livechat.FeedBack',
|
||||
template: "im_livechat.legacy.im_livechat.FeedBack",
|
||||
|
||||
events: {
|
||||
'click .o_livechat_rating_choices img': '_onClickSmiley',
|
||||
'click .o_livechat_no_feedback span': '_onClickNoFeedback',
|
||||
'click .o_rating_submit_button': '_onClickSend',
|
||||
'click .o_email_chat_button': '_onEmailChat',
|
||||
'click .o_livechat_email_error .alert-link': '_onTryAgain',
|
||||
"click .o_livechat_rating_choices img": "_onClickSmiley",
|
||||
"click .o_livechat_no_feedback span": "_onClickNoFeedback",
|
||||
"click .o_rating_submit_button": "_onClickSend",
|
||||
"click .o_email_chat_button": "_onEmailChat",
|
||||
"click .o_livechat_email_error .alert-link": "_onTryAgain",
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -45,32 +45,35 @@ const Feedback = Widget.extend({
|
||||
* @private
|
||||
* @param {Object} options
|
||||
*/
|
||||
_sendFeedback(reason) {
|
||||
_sendFeedback(reason) {
|
||||
const args = {
|
||||
uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
|
||||
rate: this.rating,
|
||||
reason,
|
||||
};
|
||||
this.dp.add(session.rpc('/im_livechat/feedback', args)).then((response) => {
|
||||
this.dp.add(session.rpc("/im_livechat/feedback", args)).then((response) => {
|
||||
const emoji = this.messaging.publicLivechatGlobal.RATING_TO_EMOJI[this.rating] || "??";
|
||||
let content;
|
||||
if (!reason) {
|
||||
content = utils.sprintf(_t("Rating: %s"), emoji);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
content = "Rating reason: \n" + reason;
|
||||
}
|
||||
this.trigger('send_message', { content, isFeedback: true });
|
||||
this.trigger("send_message", { content, isFeedback: true });
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
_showThanksMessage() {
|
||||
this.$('.o_livechat_rating_box').empty().append($('<div />', {
|
||||
text: _t('Thank you for your feedback'),
|
||||
class: 'text-muted'
|
||||
}));
|
||||
this.$(".o_livechat_rating_box")
|
||||
.empty()
|
||||
.append(
|
||||
$("<div />", {
|
||||
text: _t("Thank you for your feedback"),
|
||||
class: "text-muted",
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -81,16 +84,16 @@ const Feedback = Widget.extend({
|
||||
* @private
|
||||
*/
|
||||
_onClickNoFeedback() {
|
||||
this.trigger('feedback_sent'); // will close the chat
|
||||
this.trigger("feedback_sent"); // will close the chat
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onClickSend() {
|
||||
this.$('.o_livechat_rating_reason').hide();
|
||||
this.$(".o_livechat_rating_reason").hide();
|
||||
this._showThanksMessage();
|
||||
if (_.isNumber(this.rating)) {
|
||||
this._sendFeedback(this.$('textarea').val());
|
||||
this._sendFeedback(this.$("textarea").val());
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -98,55 +101,59 @@ const Feedback = Widget.extend({
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
_onClickSmiley(ev) {
|
||||
this.rating = parseInt($(ev.currentTarget).data('value'));
|
||||
this.$('.o_livechat_rating_choices img').removeClass('selected');
|
||||
this.$('.o_livechat_rating_choices img[data-value="' + this.rating + '"]').addClass('selected');
|
||||
this.rating = parseInt($(ev.currentTarget).data("value"));
|
||||
this.$(".o_livechat_rating_choices img").removeClass("selected");
|
||||
this.$('.o_livechat_rating_choices img[data-value="' + this.rating + '"]').addClass(
|
||||
"selected"
|
||||
);
|
||||
|
||||
// only display textearea if bad smiley selected
|
||||
if (this.rating !== 5) {
|
||||
this._sendFeedback();
|
||||
this.$('.o_livechat_rating_reason').show();
|
||||
this.$(".o_livechat_rating_reason").show();
|
||||
} else {
|
||||
this.$('.o_livechat_rating_reason').hide();
|
||||
this.$(".o_livechat_rating_reason").hide();
|
||||
this._showThanksMessage();
|
||||
this._sendFeedback();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
_onEmailChat() {
|
||||
const $email = this.$('#o_email');
|
||||
const $email = this.$("#o_email");
|
||||
|
||||
if (utils.is_email($email.val())) {
|
||||
$email.removeAttr('title').removeClass('is-invalid').prop('disabled', true);
|
||||
this.$('.o_email_chat_button').prop('disabled', true);
|
||||
$email.removeAttr("title").removeClass("is-invalid").prop("disabled", true);
|
||||
this.$(".o_email_chat_button").prop("disabled", true);
|
||||
this._rpc({
|
||||
route: '/im_livechat/email_livechat_transcript',
|
||||
route: "/im_livechat/email_livechat_transcript",
|
||||
params: {
|
||||
uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
|
||||
email: $email.val(),
|
||||
}
|
||||
}).then(() => {
|
||||
this.$('o_livechat_email_sentLabel').show();
|
||||
this.$('o_livechat_email_receiveCopyLabel').hide();
|
||||
this.$('o_livechat_email_receiveCopyForm').hide();
|
||||
}).guardedCatch(() => {
|
||||
this.$('.o_livechat_email').hide();
|
||||
this.$('.o_livechat_email_error').show();
|
||||
});
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.$("o_livechat_email_sentLabel").show();
|
||||
this.$("o_livechat_email_receiveCopyLabel").hide();
|
||||
this.$("o_livechat_email_receiveCopyForm").hide();
|
||||
})
|
||||
.guardedCatch(() => {
|
||||
this.$(".o_livechat_email").hide();
|
||||
this.$(".o_livechat_email_error").show();
|
||||
});
|
||||
} else {
|
||||
$email.addClass('is-invalid').prop('title', _t('Invalid email address'));
|
||||
$email.addClass("is-invalid").prop("title", _t("Invalid email address"));
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
_onTryAgain() {
|
||||
this.$('#o_email').prop('disabled', false);
|
||||
this.$('.o_email_chat_button').prop('disabled', false);
|
||||
this.$('.o_livechat_email_error').hide();
|
||||
this.$('.o_livechat_email').show();
|
||||
this.$("#o_email").prop("disabled", false);
|
||||
this.$(".o_email_chat_button").prop("disabled", false);
|
||||
this.$(".o_livechat_email_error").hide();
|
||||
this.$(".o_livechat_email").show();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import time from 'web.time';
|
||||
import {getCookie} from 'web.utils.cookies';
|
||||
import Widget from 'web.Widget';
|
||||
import time from "web.time";
|
||||
import { getCookie } from "web.utils.cookies";
|
||||
import Widget from "web.Widget";
|
||||
|
||||
const LivechatButton = Widget.extend({
|
||||
className: 'openerp o_livechat_button d-print-none',
|
||||
className: "openerp o_livechat_button d-print-none",
|
||||
events: {
|
||||
'click': '_onClick'
|
||||
click: "_onClick",
|
||||
},
|
||||
init(parent, messaging) {
|
||||
this._super(parent);
|
||||
@@ -32,8 +32,8 @@ const LivechatButton = Widget.extend({
|
||||
* @private
|
||||
* @return {integer} operator_id.partner_id.id if the cookie is set
|
||||
*/
|
||||
_get_previous_operator_id() {
|
||||
const cookie = getCookie('im_livechat_previous_operator_pid');
|
||||
_get_previous_operator_id() {
|
||||
const cookie = getCookie("im_livechat_previous_operator_pid");
|
||||
if (cookie) {
|
||||
return cookie;
|
||||
}
|
||||
@@ -55,17 +55,20 @@ const LivechatButton = Widget.extend({
|
||||
*/
|
||||
_sendWelcomeMessage() {
|
||||
if (this.messaging.publicLivechatGlobal.livechatButtonView.defaultMessage) {
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.addMessage({
|
||||
id: '_welcome',
|
||||
author: {
|
||||
id: this.messaging.publicLivechatGlobal.publicLivechat.operator.id,
|
||||
name: this.messaging.publicLivechatGlobal.publicLivechat.operator.name,
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.addMessage(
|
||||
{
|
||||
id: "_welcome",
|
||||
author: {
|
||||
id: this.messaging.publicLivechatGlobal.publicLivechat.operator.id,
|
||||
name: this.messaging.publicLivechatGlobal.publicLivechat.operator.name,
|
||||
},
|
||||
body: this.messaging.publicLivechatGlobal.livechatButtonView.defaultMessage,
|
||||
date: time.datetime_to_str(new Date()),
|
||||
model: "mail.channel",
|
||||
res_id: this.messaging.publicLivechatGlobal.publicLivechat.id,
|
||||
},
|
||||
body: this.messaging.publicLivechatGlobal.livechatButtonView.defaultMessage,
|
||||
date: time.datetime_to_str(new Date()),
|
||||
model: "mail.channel",
|
||||
res_id: this.messaging.publicLivechatGlobal.publicLivechat.id,
|
||||
}, { prepend: true });
|
||||
{ prepend: true }
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
+100
-96
@@ -1,10 +1,10 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import * as mailUtils from '@mail/js/utils';
|
||||
import * as mailUtils from "@mail/js/utils";
|
||||
|
||||
import core from 'web.core';
|
||||
import time from 'web.time';
|
||||
import Widget from 'web.Widget';
|
||||
import core from "web.core";
|
||||
import time from "web.time";
|
||||
import Widget from "web.Widget";
|
||||
|
||||
const QWeb = core.qweb;
|
||||
const _lt = core._lt;
|
||||
@@ -23,20 +23,20 @@ const READ_LESS = _lt("read less");
|
||||
* widget.
|
||||
*/
|
||||
const PublicLivechatView = Widget.extend({
|
||||
className: 'o_mail_thread',
|
||||
className: "o_mail_thread",
|
||||
|
||||
events: {
|
||||
'click a': '_onClickRedirect',
|
||||
'click img': '_onClickRedirect',
|
||||
'click strong': '_onClickRedirect',
|
||||
'click .o_thread_show_more': '_onClickShowMore',
|
||||
'click .o_thread_message_needaction': '_onClickMessageNeedaction',
|
||||
'click .o_thread_message_star': '_onClickMessageStar',
|
||||
'click .o_thread_message_reply': '_onClickMessageReply',
|
||||
'click .oe_mail_expand': '_onClickMailExpand',
|
||||
'click .o_thread_message': '_onClickMessage',
|
||||
'click': '_onClick',
|
||||
'click .o_thread_message_notification_error': '_onClickMessageNotificationError',
|
||||
"click a": "_onClickRedirect",
|
||||
"click img": "_onClickRedirect",
|
||||
"click strong": "_onClickRedirect",
|
||||
"click .o_thread_show_more": "_onClickShowMore",
|
||||
"click .o_thread_message_needaction": "_onClickMessageNeedaction",
|
||||
"click .o_thread_message_star": "_onClickMessageStar",
|
||||
"click .o_thread_message_reply": "_onClickMessageReply",
|
||||
"click .oe_mail_expand": "_onClickMailExpand",
|
||||
"click .o_thread_message": "_onClickMessage",
|
||||
click: "_onClick",
|
||||
"click .o_thread_message_notification_error": "_onClickMessageNotificationError",
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -85,13 +85,18 @@ const PublicLivechatView = Widget.extend({
|
||||
*/
|
||||
render(options) {
|
||||
let shouldScrollToBottomAfterRendering = false;
|
||||
if (this._currentThreadID === this.messaging.publicLivechatGlobal.publicLivechat.id && this.isAtBottom()) {
|
||||
if (
|
||||
this._currentThreadID === this.messaging.publicLivechatGlobal.publicLivechat.id &&
|
||||
this.isAtBottom()
|
||||
) {
|
||||
shouldScrollToBottomAfterRendering = true;
|
||||
}
|
||||
this._currentThreadID = this.messaging.publicLivechatGlobal.publicLivechat.id;
|
||||
|
||||
// copy so that reverse do not alter order in the thread object
|
||||
const messages = _.clone(this.messaging.publicLivechatGlobal.publicLivechat.widget.getMessages());
|
||||
const messages = _.clone(
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.getMessages()
|
||||
);
|
||||
|
||||
const modeOptions = this._enabledOptions;
|
||||
|
||||
@@ -108,14 +113,14 @@ const PublicLivechatView = Widget.extend({
|
||||
// and in the same document (users can now post message in documents
|
||||
// directly from a channel that follows it)
|
||||
let prevMessage;
|
||||
for (let message of messages) {
|
||||
for (const message of messages) {
|
||||
if (
|
||||
// is first message of thread
|
||||
!prevMessage ||
|
||||
// more than 1 min. elasped
|
||||
(Math.abs(message.getDate().diff(prevMessage.getDate())) > 60000) ||
|
||||
prevMessage.getType() !== 'comment' ||
|
||||
message.getType() !== 'comment' ||
|
||||
Math.abs(message.getDate().diff(prevMessage.getDate())) > 60000 ||
|
||||
prevMessage.getType() !== "comment" ||
|
||||
message.getType() !== "comment" ||
|
||||
// from a different author
|
||||
prevMessage.getAuthorID() !== message.getAuthorID()
|
||||
) {
|
||||
@@ -130,17 +135,19 @@ const PublicLivechatView = Widget.extend({
|
||||
messages.reverse();
|
||||
}
|
||||
|
||||
this.$el.html(QWeb.render('im_livechat.legacy.mail.widget.Thread', {
|
||||
displayAuthorMessages,
|
||||
options,
|
||||
ORDER,
|
||||
dateFormat: time.getLangDatetimeFormat(),
|
||||
widget: this,
|
||||
}));
|
||||
this.$el.html(
|
||||
QWeb.render("im_livechat.legacy.mail.widget.Thread", {
|
||||
displayAuthorMessages,
|
||||
options,
|
||||
ORDER,
|
||||
dateFormat: time.getLangDatetimeFormat(),
|
||||
widget: this,
|
||||
})
|
||||
);
|
||||
|
||||
for (let message of messages) {
|
||||
for (const message of messages) {
|
||||
const $message = this.$('.o_thread_message[data-message-id="' + message.getID() + '"]');
|
||||
$message.find('.o_mail_timestamp').data('date', message.getDate());
|
||||
$message.find(".o_mail_timestamp").data("date", message.getDate());
|
||||
|
||||
this._insertReadMore($message);
|
||||
}
|
||||
@@ -161,7 +168,7 @@ const PublicLivechatView = Widget.extend({
|
||||
* @see /mail/init_messaging
|
||||
*/
|
||||
renderLoading() {
|
||||
this.$el.html(QWeb.render('im_livechat.legacy.mail.widget.ThreadLoading'));
|
||||
this.$el.html(QWeb.render("im_livechat.legacy.mail.widget.ThreadLoading"));
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -177,7 +184,7 @@ const PublicLivechatView = Widget.extend({
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
isAtBottom() {
|
||||
isAtBottom() {
|
||||
const fullHeight = this.el.scrollHeight;
|
||||
const topHiddenHeight = this.$el.scrollTop();
|
||||
const visibleHeight = this.$el.outerHeight();
|
||||
@@ -197,14 +204,15 @@ const PublicLivechatView = Widget.extend({
|
||||
* @param {integer} [options.duration]
|
||||
* @param {boolean} [options.onlyIfNecessary]
|
||||
*/
|
||||
scrollToMessage(options) {
|
||||
scrollToMessage(options) {
|
||||
const $target = this.$('.o_thread_message[data-message-id="' + options.messageID + '"]');
|
||||
if (options.onlyIfNecessary) {
|
||||
const delta = $target.parent().height() - $target.height();
|
||||
let offset = delta < 0 ?
|
||||
0 :
|
||||
delta - ($target.offset().top - $target.offsetParent().offset().top);
|
||||
offset = - Math.min(offset, 0);
|
||||
let offset =
|
||||
delta < 0
|
||||
? 0
|
||||
: delta - ($target.offset().top - $target.offsetParent().offset().top);
|
||||
offset = -Math.min(offset, 0);
|
||||
this.$el.scrollTo("+=" + offset + "px", options.duration);
|
||||
} else if ($target.length) {
|
||||
this.$el.scrollTo($target);
|
||||
@@ -229,7 +237,7 @@ const PublicLivechatView = Widget.extend({
|
||||
* Unselect the selected message
|
||||
*/
|
||||
unselectMessage() {
|
||||
this.$('.o_thread_message').removeClass('o_thread_selected_message');
|
||||
this.$(".o_thread_message").removeClass("o_thread_selected_message");
|
||||
this._selectedMessageID = null;
|
||||
},
|
||||
|
||||
@@ -247,43 +255,34 @@ const PublicLivechatView = Widget.extend({
|
||||
* @private
|
||||
* @param {jQuery} $element
|
||||
*/
|
||||
_insertReadMore($element) {
|
||||
|
||||
_insertReadMore($element) {
|
||||
const groups = [];
|
||||
let readMoreNodes;
|
||||
|
||||
// nodeType 1: element_node
|
||||
// nodeType 3: text_node
|
||||
const $children = $element.contents()
|
||||
.filter(function () {
|
||||
return this.nodeType === 1 ||
|
||||
this.nodeType === 3 &&
|
||||
this.nodeValue.trim();
|
||||
});
|
||||
const $children = $element.contents().filter(function () {
|
||||
return this.nodeType === 1 || (this.nodeType === 3 && this.nodeValue.trim());
|
||||
});
|
||||
|
||||
for (let child of $children) {
|
||||
for (const child of $children) {
|
||||
let $child = $(child);
|
||||
|
||||
// Hide Text nodes if "stopSpelling"
|
||||
if (
|
||||
child.nodeType === 3 &&
|
||||
$child.prevAll('[id*="stopSpelling"]').length > 0
|
||||
) {
|
||||
if (child.nodeType === 3 && $child.prevAll('[id*="stopSpelling"]').length > 0) {
|
||||
// Convert Text nodes to Element nodes
|
||||
$child = $('<span>', {
|
||||
$child = $("<span>", {
|
||||
text: child.textContent,
|
||||
'data-o-mail-quote': '1',
|
||||
"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
|
||||
)
|
||||
$child.attr("data-o-mail-quote") ||
|
||||
($child.get(0).nodeName === "BR" &&
|
||||
$child.prev('[data-o-mail-quote="1"]').length > 0)
|
||||
) {
|
||||
if (!readMoreNodes) {
|
||||
readMoreNodes = [];
|
||||
@@ -297,11 +296,11 @@ const PublicLivechatView = Widget.extend({
|
||||
}
|
||||
}
|
||||
|
||||
for (let group of groups) {
|
||||
for (const group of groups) {
|
||||
// Insert link just before the first node
|
||||
const $readMore = $('<a>', {
|
||||
class: 'o_mail_read_more',
|
||||
href: '#',
|
||||
const $readMore = $("<a>", {
|
||||
class: "o_mail_read_more",
|
||||
href: "#",
|
||||
text: READ_MORE,
|
||||
}).insertBefore(group[0]);
|
||||
|
||||
@@ -310,7 +309,7 @@ const PublicLivechatView = Widget.extend({
|
||||
$readMore.click(function (e) {
|
||||
e.preventDefault();
|
||||
isReadMore = !isReadMore;
|
||||
for (let $child of group) {
|
||||
for (const $child of group) {
|
||||
$child.hide();
|
||||
$child.toggle(!isReadMore);
|
||||
}
|
||||
@@ -325,20 +324,24 @@ const PublicLivechatView = Widget.extend({
|
||||
* @param {string} options.model
|
||||
* @param {integer} options.id
|
||||
*/
|
||||
_redirect: _.debounce(function (options) {
|
||||
if ('channelID' in options) {
|
||||
this.trigger('redirect_to_channel', options.channelID);
|
||||
} else {
|
||||
this.trigger('redirect', options.model, options.id);
|
||||
}
|
||||
}, 500, true),
|
||||
_redirect: _.debounce(
|
||||
function (options) {
|
||||
if ("channelID" in options) {
|
||||
this.trigger("redirect_to_channel", options.channelID);
|
||||
} else {
|
||||
this.trigger("redirect", options.model, options.id);
|
||||
}
|
||||
},
|
||||
500,
|
||||
true
|
||||
),
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateTimestamps() {
|
||||
_updateTimestamps() {
|
||||
const isAtBottom = this.isAtBottom();
|
||||
this.$('.o_mail_timestamp').each(function () {
|
||||
const date = $(this).data('date');
|
||||
this.$(".o_mail_timestamp").each(function () {
|
||||
const date = $(this).data("date");
|
||||
$(this).text(mailUtils.timeFromNow(date));
|
||||
});
|
||||
if (isAtBottom && !this.isAtBottom()) {
|
||||
@@ -356,7 +359,7 @@ const PublicLivechatView = Widget.extend({
|
||||
_onClick() {
|
||||
if (this._selectedMessageID) {
|
||||
this.unselectMessage();
|
||||
this.trigger('unselect_message');
|
||||
this.trigger("unselect_message");
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -371,26 +374,26 @@ const PublicLivechatView = Widget.extend({
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
_onClickMessage(ev) {
|
||||
$(ev.currentTarget).toggleClass('o_thread_selected_message');
|
||||
$(ev.currentTarget).toggleClass("o_thread_selected_message");
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
_onClickMessageNeedaction(ev) {
|
||||
const messageID = $(ev.currentTarget).data('message-id');
|
||||
this.trigger('mark_as_read', messageID);
|
||||
_onClickMessageNeedaction(ev) {
|
||||
const messageID = $(ev.currentTarget).data("message-id");
|
||||
this.trigger("mark_as_read", messageID);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
_onClickMessageNotificationError(ev) {
|
||||
const messageID = $(ev.currentTarget).data('message-id');
|
||||
this.do_action('mail.mail_resend_message_action', {
|
||||
const messageID = $(ev.currentTarget).data("message-id");
|
||||
this.do_action("mail.mail_resend_message_action", {
|
||||
additional_context: {
|
||||
mail_message_to_resend: messageID,
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -398,20 +401,21 @@ const PublicLivechatView = Widget.extend({
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
_onClickMessageReply(ev) {
|
||||
this._selectedMessageID = $(ev.currentTarget).data('message-id');
|
||||
this.$('.o_thread_message').removeClass('o_thread_selected_message');
|
||||
this.$('.o_thread_message[data-message-id="' + this._selectedMessageID + '"]')
|
||||
.addClass('o_thread_selected_message');
|
||||
this.trigger('select_message', this._selectedMessageID);
|
||||
this._selectedMessageID = $(ev.currentTarget).data("message-id");
|
||||
this.$(".o_thread_message").removeClass("o_thread_selected_message");
|
||||
this.$('.o_thread_message[data-message-id="' + this._selectedMessageID + '"]').addClass(
|
||||
"o_thread_selected_message"
|
||||
);
|
||||
this.trigger("select_message", this._selectedMessageID);
|
||||
ev.stopPropagation();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
_onClickMessageStar(ev) {
|
||||
const messageID = $(ev.currentTarget).data('message-id');
|
||||
this.trigger('toggle_star_status', messageID);
|
||||
_onClickMessageStar(ev) {
|
||||
const messageID = $(ev.currentTarget).data("message-id");
|
||||
this.trigger("toggle_star_status", messageID);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -419,18 +423,18 @@ const PublicLivechatView = Widget.extend({
|
||||
*/
|
||||
_onClickRedirect(ev) {
|
||||
// ignore inherited branding
|
||||
if ($(ev.target).data('oe-field') !== undefined) {
|
||||
if ($(ev.target).data("oe-field") !== undefined) {
|
||||
return;
|
||||
}
|
||||
const id = $(ev.target).data('oe-id');
|
||||
const id = $(ev.target).data("oe-id");
|
||||
if (id) {
|
||||
ev.preventDefault();
|
||||
const model = $(ev.target).data('oe-model');
|
||||
const model = $(ev.target).data("oe-model");
|
||||
let options;
|
||||
if (model && (model !== 'mail.channel')) {
|
||||
if (model && model !== "mail.channel") {
|
||||
options = {
|
||||
model,
|
||||
id
|
||||
id,
|
||||
};
|
||||
} else {
|
||||
options = { channelID: id };
|
||||
@@ -442,7 +446,7 @@ const PublicLivechatView = Widget.extend({
|
||||
* @private
|
||||
*/
|
||||
_onClickShowMore() {
|
||||
this.trigger('load_more_messages');
|
||||
this.trigger("load_more_messages");
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
+79
-45
@@ -1,11 +1,11 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import config from 'web.config';
|
||||
import { _t, qweb } from 'web.core';
|
||||
import Widget from 'web.Widget';
|
||||
import config from "web.config";
|
||||
import { _t, qweb } from "web.core";
|
||||
import Widget from "web.Widget";
|
||||
|
||||
import {unaccent} from 'web.utils';
|
||||
import {setCookie} from 'web.utils.cookies';
|
||||
import { unaccent } from "web.utils";
|
||||
import { setCookie } from "web.utils.cookies";
|
||||
|
||||
/**
|
||||
* This is the widget that represent windows of livechat in the frontend.
|
||||
@@ -14,17 +14,17 @@ import {setCookie} from 'web.utils.cookies';
|
||||
*/
|
||||
const PublicLivechatWindow = Widget.extend({
|
||||
FOLD_ANIMATION_DURATION: 200, // duration in ms for (un)fold transition
|
||||
HEIGHT_OPEN: '400px', // height in px of thread window when open
|
||||
HEIGHT_FOLDED: '34px', // height, in px, of thread window when folded
|
||||
template: 'im_livechat.legacy.PublicLivechatWindow',
|
||||
HEIGHT_OPEN: "400px", // height in px of thread window when open
|
||||
HEIGHT_FOLDED: "34px", // height, in px, of thread window when folded
|
||||
template: "im_livechat.legacy.PublicLivechatWindow",
|
||||
events: {
|
||||
'click .o_thread_window_close': '_onClickClose',
|
||||
'click .o_thread_window_header': '_onClickFold',
|
||||
'click .o_composer_text_field': '_onComposerClick',
|
||||
'click .o_mail_thread': '_onThreadWindowClicked',
|
||||
'keydown .o_composer_text_field': '_onKeydown',
|
||||
'keypress .o_composer_text_field': '_onKeypress',
|
||||
'input .o_composer_text_field': '_onInput',
|
||||
"click .o_thread_window_close": "_onClickClose",
|
||||
"click .o_thread_window_header": "_onClickFold",
|
||||
"click .o_composer_text_field": "_onComposerClick",
|
||||
"click .o_mail_thread": "_onThreadWindowClicked",
|
||||
"keydown .o_composer_text_field": "_onKeydown",
|
||||
"keypress .o_composer_text_field": "_onKeypress",
|
||||
"input .o_composer_text_field": "_onInput",
|
||||
},
|
||||
/**
|
||||
* @param {Widget} parent
|
||||
@@ -42,29 +42,40 @@ const PublicLivechatWindow = Widget.extend({
|
||||
* @return {Promise}
|
||||
*/
|
||||
async start() {
|
||||
this.$input = this.$('.o_composer_text_field');
|
||||
this.$header = this.$('.o_thread_window_header');
|
||||
this.$input = this.$(".o_composer_text_field");
|
||||
this.$header = this.$(".o_thread_window_header");
|
||||
|
||||
// animate the (un)folding of thread windows
|
||||
this.$el.css({ transition: 'height ' + this.FOLD_ANIMATION_DURATION + 'ms linear' });
|
||||
this.$el.css({ transition: "height " + this.FOLD_ANIMATION_DURATION + "ms linear" });
|
||||
if (this.messaging.publicLivechatGlobal.publicLivechat.isFolded) {
|
||||
this.$el.css('height', this.HEIGHT_FOLDED);
|
||||
this.$el.css("height", this.HEIGHT_FOLDED);
|
||||
} else {
|
||||
this._focusInput();
|
||||
}
|
||||
const def = this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.replace(this.$('.o_thread_window_content')).then(() => {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.$el.on('scroll', this, this._debouncedOnScroll);
|
||||
});
|
||||
const def = this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget
|
||||
.replace(this.$(".o_thread_window_content"))
|
||||
.then(() => {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.$el.on(
|
||||
"scroll",
|
||||
this,
|
||||
this._debouncedOnScroll
|
||||
);
|
||||
});
|
||||
await Promise.all([this._super(), def]);
|
||||
if (this.messaging.publicLivechatGlobal.livechatButtonView.headerBackgroundColor) {
|
||||
this.$('.o_thread_window_header').css('background-color', this.messaging.publicLivechatGlobal.livechatButtonView.headerBackgroundColor);
|
||||
this.$(".o_thread_window_header").css(
|
||||
"background-color",
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.headerBackgroundColor
|
||||
);
|
||||
}
|
||||
if (this.messaging.publicLivechatGlobal.livechatButtonView.titleColor) {
|
||||
this.$('.o_thread_window_header').css('color', this.messaging.publicLivechatGlobal.livechatButtonView.titleColor);
|
||||
this.$(".o_thread_window_header").css(
|
||||
"color",
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.titleColor
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -73,10 +84,14 @@ const PublicLivechatWindow = Widget.extend({
|
||||
* @override
|
||||
*/
|
||||
close() {
|
||||
const isComposerDisabled = this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_thread_composer input').prop('disabled');
|
||||
const shouldAskFeedback = !isComposerDisabled && this.messaging.publicLivechatGlobal.messages.find(function (message) {
|
||||
return message.id !== '_welcome';
|
||||
});
|
||||
const isComposerDisabled = this.messaging.publicLivechatGlobal.chatWindow.widget
|
||||
.$(".o_thread_composer input")
|
||||
.prop("disabled");
|
||||
const shouldAskFeedback =
|
||||
!isComposerDisabled &&
|
||||
this.messaging.publicLivechatGlobal.messages.find(function (message) {
|
||||
return message.id !== "_welcome";
|
||||
});
|
||||
if (shouldAskFeedback) {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.toggleFold(false);
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.askFeedback();
|
||||
@@ -99,7 +114,9 @@ const PublicLivechatWindow = Widget.extend({
|
||||
*/
|
||||
render() {
|
||||
this.renderHeader();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.render({ displayLoadMore: false });
|
||||
this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.render({
|
||||
displayLoadMore: false,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Render the header of this thread window.
|
||||
@@ -109,7 +126,9 @@ const PublicLivechatWindow = Widget.extend({
|
||||
* @private
|
||||
*/
|
||||
renderHeader() {
|
||||
this.$header.html(qweb.render('im_livechat.legacy.PublicLivechatWindow.HeaderContent', { widget: this }));
|
||||
this.$header.html(
|
||||
qweb.render("im_livechat.legacy.PublicLivechatWindow.HeaderContent", { widget: this })
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -126,9 +145,10 @@ const PublicLivechatWindow = Widget.extend({
|
||||
*/
|
||||
adjustPosition() {
|
||||
const cssProps = { bottom: 0 };
|
||||
cssProps[this.messaging.locale.textDirection === 'rtl' ? 'left' : 'right'] = 0;
|
||||
cssProps[this.messaging.locale.textDirection === "rtl" ? "left" : "right"] = 0;
|
||||
if (!config.device.isMobile) {
|
||||
const margin_dir = _t.database.parameters.direction === "rtl" ? "margin-left" : "margin-right";
|
||||
const margin_dir =
|
||||
_t.database.parameters.direction === "rtl" ? "margin-left" : "margin-right";
|
||||
cssProps[margin_dir] = $.position.scrollbarWidth();
|
||||
}
|
||||
this.$el.css(cssProps);
|
||||
@@ -140,7 +160,9 @@ const PublicLivechatWindow = Widget.extend({
|
||||
* @param {$.Element} $element
|
||||
*/
|
||||
replaceContentWith($element) {
|
||||
$element.replace(this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.$el);
|
||||
$element.replace(
|
||||
this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.$el
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Toggle the fold state of this thread window. Also update the fold state
|
||||
@@ -158,7 +180,17 @@ const PublicLivechatWindow = Widget.extend({
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({ isFolded: folded });
|
||||
if (this.messaging.publicLivechatGlobal.publicLivechat.operator) {
|
||||
setCookie('im_livechat_session', unaccent(JSON.stringify(this.messaging.publicLivechatGlobal.publicLivechat.widget.toData()), true), 60 * 60, 'required');
|
||||
setCookie(
|
||||
"im_livechat_session",
|
||||
unaccent(
|
||||
JSON.stringify(
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.toData()
|
||||
),
|
||||
true
|
||||
),
|
||||
60 * 60,
|
||||
"required"
|
||||
);
|
||||
}
|
||||
this.updateVisualFoldState();
|
||||
},
|
||||
@@ -172,7 +204,9 @@ const PublicLivechatWindow = Widget.extend({
|
||||
this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.scrollToBottom();
|
||||
this._focusInput();
|
||||
}
|
||||
const height = this.messaging.publicLivechatGlobal.publicLivechat.isFolded ? this.HEIGHT_FOLDED : this.HEIGHT_OPEN;
|
||||
const height = this.messaging.publicLivechatGlobal.publicLivechat.isFolded
|
||||
? this.HEIGHT_FOLDED
|
||||
: this.HEIGHT_OPEN;
|
||||
this.$el.css({ height });
|
||||
},
|
||||
|
||||
@@ -188,10 +222,7 @@ const PublicLivechatWindow = Widget.extend({
|
||||
* Set the focus on the input of the window
|
||||
*/
|
||||
_focusInput() {
|
||||
if (
|
||||
config.device.touch &&
|
||||
config.device.size_class <= config.device.SIZES.SM
|
||||
) {
|
||||
if (config.device.touch && config.device.size_class <= config.device.SIZES.SM) {
|
||||
return;
|
||||
}
|
||||
this.$input.focus();
|
||||
@@ -204,7 +235,7 @@ const PublicLivechatWindow = Widget.extend({
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_hasFocus() {
|
||||
return this.$input.is(':focus');
|
||||
return this.$input.is(":focus");
|
||||
},
|
||||
/**
|
||||
* Post a message on this thread window, and auto-scroll to the bottom of
|
||||
@@ -222,7 +253,8 @@ const PublicLivechatWindow = Widget.extend({
|
||||
if (!this.messaging.publicLivechatGlobal.publicLivechat.operator) {
|
||||
return;
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.postMessage(messageData)
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget
|
||||
.postMessage(messageData)
|
||||
.then(() => {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.scrollToBottom();
|
||||
});
|
||||
@@ -269,7 +301,7 @@ const PublicLivechatWindow = Widget.extend({
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onComposerClick(ev) {
|
||||
if ($(ev.target).closest('a, button').length) {
|
||||
if ($(ev.target).closest("a, button").length) {
|
||||
return;
|
||||
}
|
||||
this._focusInput();
|
||||
@@ -281,7 +313,9 @@ const PublicLivechatWindow = Widget.extend({
|
||||
*/
|
||||
_onInput() {
|
||||
const isTyping = this.$input.val().length > 0;
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.setMyselfTyping({ typing: isTyping });
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.setMyselfTyping({
|
||||
typing: isTyping,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Called when typing something on the composer of this thread window.
|
||||
@@ -299,7 +333,7 @@ const PublicLivechatWindow = Widget.extend({
|
||||
attachment_ids: [],
|
||||
partner_ids: [],
|
||||
};
|
||||
this.$input.val('');
|
||||
this.$input.val("");
|
||||
if (content) {
|
||||
this._postMessage(messageData);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { attr, one, Patch } from '@mail/model';
|
||||
import { attr, one, Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'Channel',
|
||||
name: "Channel",
|
||||
fields: {
|
||||
anonymous_country: one('Country'),
|
||||
anonymous_country: one("Country"),
|
||||
anonymous_name: attr(),
|
||||
discussSidebarCategory: {
|
||||
compute() {
|
||||
if (this.channel_type === 'livechat') {
|
||||
if (this.channel_type === "livechat") {
|
||||
return this.messaging.discuss.categoryLivechat;
|
||||
}
|
||||
return this._super();
|
||||
@@ -20,12 +20,16 @@ Patch({
|
||||
if (!this.thread) {
|
||||
return;
|
||||
}
|
||||
if (this.channel_type === 'livechat' && this.correspondent) {
|
||||
if (this.channel_type === "livechat" && this.correspondent) {
|
||||
if (!this.correspondent.is_public && this.correspondent.country) {
|
||||
return `${this.thread.getMemberName(this.correspondent.persona)} (${this.correspondent.country.name})`;
|
||||
return `${this.thread.getMemberName(this.correspondent.persona)} (${
|
||||
this.correspondent.country.name
|
||||
})`;
|
||||
}
|
||||
if (this.anonymous_country) {
|
||||
return `${this.thread.getMemberName(this.correspondent.persona)} (${this.anonymous_country.name})`;
|
||||
return `${this.thread.getMemberName(this.correspondent.persona)} (${
|
||||
this.anonymous_country.name
|
||||
})`;
|
||||
}
|
||||
return this.thread.getMemberName(this.correspondent.persona);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { Patch } from '@mail/model';
|
||||
import { Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'ChannelPreviewView',
|
||||
name: "ChannelPreviewView",
|
||||
fields: {
|
||||
imageUrl: {
|
||||
compute() {
|
||||
if (this.channel.channel_type === 'livechat') {
|
||||
return '/mail/static/src/img/smiley/avatar.jpg';
|
||||
if (this.channel.channel_type === "livechat") {
|
||||
return "/mail/static/src/img/smiley/avatar.jpg";
|
||||
}
|
||||
return this._super();
|
||||
},
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { Patch } from '@mail/model';
|
||||
import { Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'ChatWindow',
|
||||
name: "ChatWindow",
|
||||
recordMethods: {
|
||||
/**
|
||||
* @override
|
||||
@@ -12,7 +12,7 @@ Patch({
|
||||
if (
|
||||
this.thread &&
|
||||
this.thread.channel &&
|
||||
this.thread.channel.channel_type === 'livechat' &&
|
||||
this.thread.channel.channel_type === "livechat" &&
|
||||
this.thread.cache.isLoaded &&
|
||||
this.thread.messages.length === 0
|
||||
) {
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { clear, Patch } from '@mail/model';
|
||||
import { clear, Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'ComposerView',
|
||||
name: "ComposerView",
|
||||
fields: {
|
||||
dropZoneView: {
|
||||
compute() {
|
||||
if (this.composer.thread && this.composer.thread.channel && this.composer.thread.channel.channel_type === 'livechat') {
|
||||
if (
|
||||
this.composer.thread &&
|
||||
this.composer.thread.channel &&
|
||||
this.composer.thread.channel.channel_type === "livechat"
|
||||
) {
|
||||
return clear();
|
||||
}
|
||||
return this._super();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { one, Patch } from '@mail/model';
|
||||
import { one, Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'Discuss',
|
||||
name: "Discuss",
|
||||
recordMethods: {
|
||||
/**
|
||||
* @override
|
||||
@@ -19,9 +19,9 @@ Patch({
|
||||
/**
|
||||
* Discuss sidebar category for `livechat` channel threads.
|
||||
*/
|
||||
categoryLivechat: one('DiscussSidebarCategory', {
|
||||
categoryLivechat: one("DiscussSidebarCategory", {
|
||||
default: {},
|
||||
inverse: 'discussAsLivechat',
|
||||
inverse: "discussAsLivechat",
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { clear, one, Patch } from '@mail/model';
|
||||
import { clear, one, Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'DiscussSidebarCategory',
|
||||
name: "DiscussSidebarCategory",
|
||||
fields: {
|
||||
categoryItemsOrderedByLastAction: {
|
||||
compute() {
|
||||
@@ -13,9 +13,9 @@ Patch({
|
||||
return this._super();
|
||||
},
|
||||
},
|
||||
discussAsLivechat: one('Discuss', {
|
||||
discussAsLivechat: one("Discuss", {
|
||||
identifying: true,
|
||||
inverse: 'categoryLivechat',
|
||||
inverse: "categoryLivechat",
|
||||
}),
|
||||
isServerOpen: {
|
||||
compute() {
|
||||
@@ -27,7 +27,8 @@ Patch({
|
||||
return clear();
|
||||
}
|
||||
if (this.discussAsLivechat) {
|
||||
return this.messaging.currentUser.res_users_settings_id.is_discuss_sidebar_category_livechat_open;
|
||||
return this.messaging.currentUser.res_users_settings_id
|
||||
.is_discuss_sidebar_category_livechat_open;
|
||||
}
|
||||
return this._super();
|
||||
},
|
||||
@@ -51,7 +52,7 @@ Patch({
|
||||
serverStateKey: {
|
||||
compute() {
|
||||
if (this.discussAsLivechat) {
|
||||
return 'is_discuss_sidebar_category_livechat_open';
|
||||
return "is_discuss_sidebar_category_livechat_open";
|
||||
}
|
||||
return this._super();
|
||||
},
|
||||
@@ -59,7 +60,7 @@ Patch({
|
||||
supportedChannelTypes: {
|
||||
compute() {
|
||||
if (this.discussAsLivechat) {
|
||||
return ['livechat'];
|
||||
return ["livechat"];
|
||||
}
|
||||
return this._super();
|
||||
},
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { clear, Patch } from '@mail/model';
|
||||
import { clear, Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'DiscussSidebarCategoryItem',
|
||||
name: "DiscussSidebarCategoryItem",
|
||||
fields: {
|
||||
avatarUrl: {
|
||||
compute() {
|
||||
if (this.channel.channel_type === 'livechat') {
|
||||
if (this.channel.channel_type === "livechat") {
|
||||
if (this.channel.correspondent && !this.channel.correspondent.is_public) {
|
||||
return this.channel.correspondent.avatarUrl;
|
||||
}
|
||||
@@ -17,7 +17,7 @@ Patch({
|
||||
},
|
||||
categoryCounterContribution: {
|
||||
compute() {
|
||||
if (this.channel.channel_type === 'livechat') {
|
||||
if (this.channel.channel_type === "livechat") {
|
||||
return this.channel.localMessageUnreadCounter > 0 ? 1 : 0;
|
||||
}
|
||||
return this._super();
|
||||
@@ -25,7 +25,7 @@ Patch({
|
||||
},
|
||||
counter: {
|
||||
compute() {
|
||||
if (this.channel.channel_type === 'livechat') {
|
||||
if (this.channel.channel_type === "livechat") {
|
||||
return this.channel.localMessageUnreadCounter;
|
||||
}
|
||||
return this._super();
|
||||
@@ -33,7 +33,7 @@ Patch({
|
||||
},
|
||||
hasUnpinCommand: {
|
||||
compute() {
|
||||
if (this.channel.channel_type === 'livechat') {
|
||||
if (this.channel.channel_type === "livechat") {
|
||||
return !this.channel.localMessageUnreadCounter;
|
||||
}
|
||||
return this._super();
|
||||
@@ -41,7 +41,7 @@ Patch({
|
||||
},
|
||||
threadIconView: {
|
||||
compute() {
|
||||
if (this.channel.channel_type === 'livechat') {
|
||||
if (this.channel.channel_type === "livechat") {
|
||||
return clear();
|
||||
}
|
||||
return this._super();
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { Patch } from '@mail/model';
|
||||
import { Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'Message',
|
||||
name: "Message",
|
||||
fields: {
|
||||
hasReactionIcon: {
|
||||
compute() {
|
||||
if (this.originThread && this.originThread.channel && this.originThread.channel.channel_type === 'livechat') {
|
||||
if (
|
||||
this.originThread &&
|
||||
this.originThread.channel &&
|
||||
this.originThread.channel.channel_type === "livechat"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return this._super();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { clear, Patch } from '@mail/model';
|
||||
import { clear, Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'MessageActionList',
|
||||
name: "MessageActionList",
|
||||
fields: {
|
||||
actionReplyTo: {
|
||||
compute() {
|
||||
@@ -11,12 +11,12 @@ Patch({
|
||||
this.message &&
|
||||
this.message.originThread &&
|
||||
this.message.originThread.channel &&
|
||||
this.message.originThread.channel.channel_type === 'livechat'
|
||||
this.message.originThread.channel.channel_type === "livechat"
|
||||
) {
|
||||
return clear();
|
||||
}
|
||||
return this._super();
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { Patch } from '@mail/model';
|
||||
import { Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: "MessageView",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { many, Patch } from '@mail/model';
|
||||
import { many, Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'Messaging',
|
||||
name: "Messaging",
|
||||
fields: {
|
||||
/**
|
||||
* All pinned livechats that are known.
|
||||
*/
|
||||
pinnedLivechats: many('Thread', {
|
||||
inverse: 'messagingAsPinnedLivechat',
|
||||
pinnedLivechats: many("Thread", {
|
||||
inverse: "messagingAsPinnedLivechat",
|
||||
readonly: true,
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { insert, Patch } from '@mail/model';
|
||||
import { insert, Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'MessagingInitializer',
|
||||
name: "MessagingInitializer",
|
||||
recordMethods: {
|
||||
/**
|
||||
* @override
|
||||
@@ -13,9 +13,9 @@ Patch({
|
||||
this._super();
|
||||
this.messaging.update({
|
||||
commands: insert({
|
||||
channel_types: ['livechat'],
|
||||
channel_types: ["livechat"],
|
||||
help: this.env._t("See 15 last visited pages"),
|
||||
methodName: 'execute_command_history',
|
||||
methodName: "execute_command_history",
|
||||
name: "history",
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { Patch } from '@mail/model';
|
||||
import { Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'MobileMessagingNavbarView',
|
||||
name: "MobileMessagingNavbarView",
|
||||
fields: {
|
||||
tabs: {
|
||||
compute() {
|
||||
const res = this._super();
|
||||
if (this.messaging.pinnedLivechats.length > 0) {
|
||||
return [...res, {
|
||||
icon: 'fa fa-comments',
|
||||
id: 'livechat',
|
||||
label: this.env._t("Livechat"),
|
||||
}];
|
||||
return [
|
||||
...res,
|
||||
{
|
||||
icon: "fa fa-comments",
|
||||
id: "livechat",
|
||||
label: this.env._t("Livechat"),
|
||||
},
|
||||
];
|
||||
}
|
||||
return res;
|
||||
},
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { Patch } from '@mail/model';
|
||||
import { Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'NotificationListView',
|
||||
name: "NotificationListView",
|
||||
fields: {
|
||||
filteredChannels: {
|
||||
compute() {
|
||||
if (this.filter === 'livechat') {
|
||||
return this.messaging.models['Channel'].all(channel =>
|
||||
channel.channel_type === 'livechat' &&
|
||||
channel.thread.isPinned
|
||||
if (this.filter === "livechat") {
|
||||
return this.messaging.models["Channel"].all(
|
||||
(channel) => channel.channel_type === "livechat" && channel.thread.isPinned
|
||||
);
|
||||
}
|
||||
return this._super();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { attr, Patch } from '@mail/model';
|
||||
import { attr, Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'Partner',
|
||||
name: "Partner",
|
||||
fields: {
|
||||
/**
|
||||
* States the specific name of this partner in the context of livechat.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { attr, Patch } from '@mail/model';
|
||||
import { attr, Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'res.users.settings',
|
||||
name: "res.users.settings",
|
||||
fields: {
|
||||
is_discuss_sidebar_category_livechat_open: attr({
|
||||
default: true,
|
||||
|
||||
@@ -1,18 +1,29 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { clear, one, Patch } from '@mail/model';
|
||||
import { clear, one, Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'Thread',
|
||||
name: "Thread",
|
||||
recordMethods: {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
getMemberName(persona) {
|
||||
if (this.channel && this.channel.channel_type === 'livechat' && persona.partner && persona.partner.user_livechat_username) {
|
||||
if (
|
||||
this.channel &&
|
||||
this.channel.channel_type === "livechat" &&
|
||||
persona.partner &&
|
||||
persona.partner.user_livechat_username
|
||||
) {
|
||||
return persona.partner.user_livechat_username;
|
||||
}
|
||||
if (this.channel && this.channel.channel_type === 'livechat' && persona.partner && persona.partner.is_public && this.channel.anonymous_name) {
|
||||
if (
|
||||
this.channel &&
|
||||
this.channel.channel_type === "livechat" &&
|
||||
persona.partner &&
|
||||
persona.partner.is_public &&
|
||||
this.channel.anonymous_name
|
||||
) {
|
||||
return this.channel.anonymous_name;
|
||||
}
|
||||
return this._super(persona);
|
||||
@@ -21,7 +32,7 @@ Patch({
|
||||
fields: {
|
||||
hasInviteFeature: {
|
||||
compute() {
|
||||
if (this.channel && this.channel.channel_type === 'livechat') {
|
||||
if (this.channel && this.channel.channel_type === "livechat") {
|
||||
return true;
|
||||
}
|
||||
return this._super();
|
||||
@@ -29,7 +40,7 @@ Patch({
|
||||
},
|
||||
hasMemberListFeature: {
|
||||
compute() {
|
||||
if (this.channel && this.channel.channel_type === 'livechat') {
|
||||
if (this.channel && this.channel.channel_type === "livechat") {
|
||||
return true;
|
||||
}
|
||||
return this._super();
|
||||
@@ -37,7 +48,7 @@ Patch({
|
||||
},
|
||||
isChatChannel: {
|
||||
compute() {
|
||||
if (this.channel && this.channel.channel_type === 'livechat') {
|
||||
if (this.channel && this.channel.channel_type === "livechat") {
|
||||
return true;
|
||||
}
|
||||
return this._super();
|
||||
@@ -46,14 +57,19 @@ Patch({
|
||||
/**
|
||||
* If set, current thread is a livechat.
|
||||
*/
|
||||
messagingAsPinnedLivechat: one('Messaging', {
|
||||
messagingAsPinnedLivechat: one("Messaging", {
|
||||
compute() {
|
||||
if (!this.messaging || !this.channel || this.channel.channel_type !== 'livechat' || !this.isPinned) {
|
||||
if (
|
||||
!this.messaging ||
|
||||
!this.channel ||
|
||||
this.channel.channel_type !== "livechat" ||
|
||||
!this.isPinned
|
||||
) {
|
||||
return clear();
|
||||
}
|
||||
return this.messaging;
|
||||
},
|
||||
inverse: 'pinnedLivechats',
|
||||
inverse: "pinnedLivechats",
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { Patch } from '@mail/model';
|
||||
import { Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'ThreadIconView',
|
||||
name: "ThreadIconView",
|
||||
fields: {
|
||||
threadTypingIconView: {
|
||||
compute() {
|
||||
if (
|
||||
this.thread.channel &&
|
||||
this.thread.channel.channel_type === 'livechat' &&
|
||||
this.thread.channel.channel_type === "livechat" &&
|
||||
this.thread.orderedOtherTypingMembers.length > 0
|
||||
) {
|
||||
return {};
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { publicLivechatService } from '@im_livechat/services/public_livechat_service';
|
||||
import { isAvailable, options, serverUrl } from '@im_livechat/livechat_data';
|
||||
import { publicLivechatService } from "@im_livechat/services/public_livechat_service";
|
||||
import { isAvailable, options, serverUrl } from "@im_livechat/livechat_data";
|
||||
|
||||
import { messagingService } from '@mail/services/messaging_service';
|
||||
import { makeMessagingToLegacyEnv } from '@mail/utils/make_messaging_to_legacy_env';
|
||||
import { messagingService } from "@mail/services/messaging_service";
|
||||
import { makeMessagingToLegacyEnv } from "@mail/utils/make_messaging_to_legacy_env";
|
||||
|
||||
import { registry } from '@web/core/registry';
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
const messagingValuesService = {
|
||||
start() {
|
||||
return {
|
||||
publicLivechatGlobal: { isAvailable, options, serverUrl },
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const serviceRegistry = registry.category('services');
|
||||
serviceRegistry.add('messaging', messagingService);
|
||||
serviceRegistry.add('messagingValues', messagingValuesService);
|
||||
serviceRegistry.add('public_livechat_service', publicLivechatService);
|
||||
const serviceRegistry = registry.category("services");
|
||||
serviceRegistry.add("messaging", messagingService);
|
||||
serviceRegistry.add("messagingValues", messagingValuesService);
|
||||
serviceRegistry.add("public_livechat_service", publicLivechatService);
|
||||
|
||||
registry.category('wowlToLegacyServiceMappers').add('make_messaging_to_legacy_env', makeMessagingToLegacyEnv);
|
||||
registry
|
||||
.category("wowlToLegacyServiceMappers")
|
||||
.add("make_messaging_to_legacy_env", makeMessagingToLegacyEnv);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
odoo.define('web.session', function (require) {
|
||||
|
||||
const Session = require('web.Session');
|
||||
const { serverUrl } = require('@im_livechat/livechat_data');
|
||||
odoo.define("web.session", function (require) {
|
||||
const Session = require("web.Session");
|
||||
const { serverUrl } = require("@im_livechat/livechat_data");
|
||||
|
||||
return new Session(undefined, serverUrl, { use_cors: true });
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { attr, one, clear, increment, Model } from '@mail/model';
|
||||
import { attr, one, clear, increment, Model } from "@mail/model";
|
||||
|
||||
import { qweb } from 'web.core';
|
||||
import { Markup } from 'web.utils';
|
||||
import { qweb } from "web.core";
|
||||
import { Markup } from "web.utils";
|
||||
|
||||
Model({
|
||||
name: 'Chatbot',
|
||||
name: "Chatbot",
|
||||
recordMethods: {
|
||||
/**
|
||||
* Add message posted by the bot into the conversation.
|
||||
@@ -18,8 +18,13 @@ Model({
|
||||
addMessage(message, options) {
|
||||
message.body = Markup(message.body);
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.addMessage(message, options);
|
||||
if (this.messaging.publicLivechatGlobal.publicLivechat.isFolded || !this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.isAtBottom()) {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({ unreadCounter: increment() });
|
||||
if (
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.isFolded ||
|
||||
!this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.isAtBottom()
|
||||
) {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({
|
||||
unreadCounter: increment(),
|
||||
});
|
||||
}
|
||||
|
||||
if (!options || !options.skipRenderMessages) {
|
||||
@@ -40,15 +45,24 @@ Model({
|
||||
// handle the display
|
||||
return;
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_composer_text_field').addClass('d-none');
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_livechat_chatbot_end').show();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_livechat_chatbot_restart').one('click', this.messaging.publicLivechatGlobal.livechatButtonView.onChatbotRestartScript);
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget
|
||||
.$(".o_composer_text_field")
|
||||
.addClass("d-none");
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget
|
||||
.$(".o_livechat_chatbot_end")
|
||||
.show();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget
|
||||
.$(".o_livechat_chatbot_restart")
|
||||
.one(
|
||||
"click",
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.onChatbotRestartScript
|
||||
);
|
||||
},
|
||||
onKeydownInput() {
|
||||
if (
|
||||
this.currentStep &&
|
||||
this.currentStep.data &&
|
||||
this.currentStep.data.chatbot_step_type === 'free_input_multi'
|
||||
this.currentStep.data.chatbot_step_type === "free_input_multi"
|
||||
) {
|
||||
this.debouncedAwaitUserInput();
|
||||
}
|
||||
@@ -75,14 +89,14 @@ Model({
|
||||
}
|
||||
|
||||
const postedWelcomeMessages = await this.messaging.rpc({
|
||||
route: '/chatbot/post_welcome_steps',
|
||||
route: "/chatbot/post_welcome_steps",
|
||||
params: {
|
||||
channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
|
||||
chatbot_script_id: this.scriptId,
|
||||
},
|
||||
});
|
||||
|
||||
const welcomeMessagesIds = welcomeMessages.map(welcomeMessage => welcomeMessage.id);
|
||||
const welcomeMessagesIds = welcomeMessages.map((welcomeMessage) => welcomeMessage.id);
|
||||
this.messaging.publicLivechatGlobal.update({
|
||||
messages: this.messaging.publicLivechatGlobal.messages.filter((message) => {
|
||||
!welcomeMessagesIds.includes(message.id);
|
||||
@@ -128,7 +142,7 @@ Model({
|
||||
if (this.shouldEndScript) {
|
||||
this.endScript();
|
||||
} else if (
|
||||
this.currentStep.data.chatbot_step_type === 'forward_operator' &&
|
||||
this.currentStep.data.chatbot_step_type === "forward_operator" &&
|
||||
this.currentStep.data.chatbot_operator_found
|
||||
) {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.enableInput();
|
||||
@@ -137,17 +151,14 @@ Model({
|
||||
// user has already typed a message in -> trigger next step
|
||||
this.setIsTyping();
|
||||
this.update({
|
||||
nextStepTimeout: setTimeout(
|
||||
this.triggerNextStep,
|
||||
this.messageDelay,
|
||||
),
|
||||
nextStepTimeout: setTimeout(this.triggerNextStep, this.messageDelay),
|
||||
});
|
||||
} else {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.enableInput();
|
||||
}
|
||||
} else {
|
||||
let triggerNextStep = true;
|
||||
if (this.currentStep.data.chatbot_step_type === 'question_selection') {
|
||||
if (this.currentStep.data.chatbot_step_type === "question_selection") {
|
||||
if (!this.messaging.publicLivechatGlobal.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
|
||||
@@ -157,7 +168,11 @@ Model({
|
||||
|
||||
if (triggerNextStep) {
|
||||
let nextStepDelay = this.messageDelay;
|
||||
if (this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_livechat_chatbot_typing').length !== 0) {
|
||||
if (
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$(
|
||||
".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
|
||||
@@ -168,16 +183,15 @@ Model({
|
||||
}
|
||||
|
||||
this.update({
|
||||
nextStepTimeout: setTimeout(
|
||||
this.triggerNextStep,
|
||||
nextStepDelay,
|
||||
),
|
||||
nextStepTimeout: setTimeout(this.triggerNextStep, nextStepDelay),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.hasRestartButton) {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_livechat_chatbot_main_restart').addClass('d-none');
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget
|
||||
.$(".o_livechat_chatbot_main_restart")
|
||||
.addClass("d-none");
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -195,7 +209,10 @@ Model({
|
||||
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 !== this.sessionCookieKey) {
|
||||
if (
|
||||
key.startsWith("im_livechat.chatbot.state.uuid_") &&
|
||||
key !== this.sessionCookieKey
|
||||
) {
|
||||
browserLocalStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
@@ -213,10 +230,14 @@ Model({
|
||||
* anyway in that case, since it's stored into a cookie).
|
||||
*/
|
||||
saveSession() {
|
||||
localStorage.setItem('im_livechat.chatbot.state.uuid_' + this.messaging.publicLivechatGlobal.publicLivechat.uuid, JSON.stringify({
|
||||
'_chatbot': this.data,
|
||||
'_chatbotCurrentStep': this.currentStep.data,
|
||||
}));
|
||||
localStorage.setItem(
|
||||
"im_livechat.chatbot.state.uuid_" +
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.uuid,
|
||||
JSON.stringify({
|
||||
_chatbot: this.data,
|
||||
_chatbotCurrentStep: this.currentStep.data,
|
||||
})
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Adds a small "is typing" animation into the chat window.
|
||||
@@ -225,28 +246,32 @@ Model({
|
||||
*/
|
||||
setIsTyping(isWelcomeMessage = false) {
|
||||
if (this.messaging.publicLivechatGlobal.livechatButtonView.isTypingTimeout) {
|
||||
clearTimeout(this.messaging.publicLivechatGlobal.livechatButtonView.isTypingTimeout);
|
||||
clearTimeout(
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.isTypingTimeout
|
||||
);
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.chatWindow.disableInput('');
|
||||
this.messaging.publicLivechatGlobal.chatWindow.disableInput("");
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.update({
|
||||
isTypingTimeout: setTimeout(
|
||||
() => {
|
||||
if (!this.messaging.publicLivechatGlobal.chatWindow || !this.messaging.publicLivechatGlobal.chatWindow.exists()) {
|
||||
return;
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_mail_thread_content').append(
|
||||
$(qweb.render('im_livechat.legacy.chatbot.is_typing_message', {
|
||||
'chatbotImageSrc': `/im_livechat/operator/${
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.operator.id
|
||||
}/avatar`,
|
||||
'chatbotName': this.name,
|
||||
'isWelcomeMessage': isWelcomeMessage,
|
||||
}))
|
||||
isTypingTimeout: setTimeout(() => {
|
||||
if (
|
||||
!this.messaging.publicLivechatGlobal.chatWindow ||
|
||||
!this.messaging.publicLivechatGlobal.chatWindow.exists()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget
|
||||
.$(".o_mail_thread_content")
|
||||
.append(
|
||||
$(
|
||||
qweb.render("im_livechat.legacy.chatbot.is_typing_message", {
|
||||
chatbotImageSrc: `/im_livechat/operator/${this.messaging.publicLivechatGlobal.publicLivechat.operator.id}/avatar`,
|
||||
chatbotName: this.name,
|
||||
isWelcomeMessage: isWelcomeMessage,
|
||||
})
|
||||
)
|
||||
);
|
||||
this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.scrollToBottom();
|
||||
},
|
||||
this.messageDelay / 3,
|
||||
),
|
||||
this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.scrollToBottom();
|
||||
}, this.messageDelay / 3),
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -254,14 +279,17 @@ Model({
|
||||
* This will receive the next step and call step processing.
|
||||
*/
|
||||
async triggerNextStep() {
|
||||
if (!this.messaging.publicLivechatGlobal.chatWindow || !this.messaging.publicLivechatGlobal.chatWindow.exists()) {
|
||||
if (
|
||||
!this.messaging.publicLivechatGlobal.chatWindow ||
|
||||
!this.messaging.publicLivechatGlobal.chatWindow.exists()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let triggerNextStep = true;
|
||||
if (
|
||||
this.currentStep &&
|
||||
this.currentStep.data &&
|
||||
this.currentStep.data.chatbot_step_type === 'question_email'
|
||||
this.currentStep.data.chatbot_step_type === "question_email"
|
||||
) {
|
||||
triggerNextStep = await this.validateEmail();
|
||||
}
|
||||
@@ -271,7 +299,7 @@ Model({
|
||||
}
|
||||
|
||||
const nextStep = await this.messaging.rpc({
|
||||
route: '/chatbot/step/trigger',
|
||||
route: "/chatbot/step/trigger",
|
||||
params: {
|
||||
channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
|
||||
chatbot_script_id: this.scriptId,
|
||||
@@ -307,8 +335,8 @@ Model({
|
||||
* Returns a boolean stating whether the email was valid or not.
|
||||
*/
|
||||
async validateEmail() {
|
||||
let emailValidResult = await this.messaging.rpc({
|
||||
route: '/chatbot/step/validate_email',
|
||||
const emailValidResult = await this.messaging.rpc({
|
||||
route: "/chatbot/step/validate_email",
|
||||
params: { channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid },
|
||||
});
|
||||
|
||||
@@ -347,10 +375,7 @@ Model({
|
||||
} else {
|
||||
this.setIsTyping();
|
||||
this.update({
|
||||
nextStepTimeout: setTimeout(
|
||||
this.triggerNextStep,
|
||||
this.messageDelay,
|
||||
)
|
||||
nextStepTimeout: setTimeout(this.triggerNextStep, this.messageDelay),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -367,31 +392,25 @@ Model({
|
||||
if (this.messaging.publicLivechatGlobal.isTestChatbot) {
|
||||
return this.messaging.publicLivechatGlobal.testChatbotData;
|
||||
}
|
||||
if (this.state === 'init') {
|
||||
if (this.state === "init") {
|
||||
return this.messaging.publicLivechatGlobal.rule.chatbot;
|
||||
}
|
||||
if (this.state === 'welcome') {
|
||||
if (this.state === "welcome") {
|
||||
return this.messaging.publicLivechatGlobal.livechatInit.rule.chatbot;
|
||||
}
|
||||
if (
|
||||
this.state === 'restore_session' &&
|
||||
this.localStorageState
|
||||
) {
|
||||
if (this.state === "restore_session" && this.localStorageState) {
|
||||
return this.localStorageState._chatbot;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
}),
|
||||
currentStep: one('ChatbotStep', {
|
||||
inverse: 'chabotOwner',
|
||||
currentStep: one("ChatbotStep", {
|
||||
inverse: "chabotOwner",
|
||||
}),
|
||||
debouncedAwaitUserInput: attr({
|
||||
compute() {
|
||||
// debounced to let the user type several sentences, see 'Chatbot/awaitUserInput' for details
|
||||
return _.debounce(
|
||||
this.awaitUserInput,
|
||||
this.awaitUserInputDebounceTime,
|
||||
);
|
||||
return _.debounce(this.awaitUserInput, this.awaitUserInputDebounceTime);
|
||||
},
|
||||
}),
|
||||
hasRestartButton: attr({
|
||||
@@ -411,10 +430,8 @@ Model({
|
||||
}
|
||||
return Boolean(
|
||||
!this.currentStep ||
|
||||
(
|
||||
this.currentStep.data.chatbot_step_type !== 'forward_operator' ||
|
||||
this.currentStep.data.chatbot_step_type !== "forward_operator" ||
|
||||
!this.currentStep.data.chatbot_operator_found
|
||||
)
|
||||
);
|
||||
},
|
||||
default: false,
|
||||
@@ -424,13 +441,19 @@ Model({
|
||||
if (this.messaging.publicLivechatGlobal.isTestChatbot) {
|
||||
return true;
|
||||
}
|
||||
if (this.messaging.publicLivechatGlobal.rule && this.messaging.publicLivechatGlobal.rule.chatbot) {
|
||||
if (
|
||||
this.messaging.publicLivechatGlobal.rule &&
|
||||
this.messaging.publicLivechatGlobal.rule.chatbot
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (this.messaging.publicLivechatGlobal.livechatInit && this.messaging.publicLivechatGlobal.livechatInit.rule.chatbot) {
|
||||
if (
|
||||
this.messaging.publicLivechatGlobal.livechatInit &&
|
||||
this.messaging.publicLivechatGlobal.livechatInit.rule.chatbot
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (this.state === 'welcome') {
|
||||
if (this.state === "welcome") {
|
||||
return true;
|
||||
}
|
||||
if (this.localStorageState) {
|
||||
@@ -446,10 +469,10 @@ Model({
|
||||
return clear();
|
||||
}
|
||||
return [
|
||||
'question_phone',
|
||||
'question_email',
|
||||
'free_input_single',
|
||||
'free_input_multi',
|
||||
"question_phone",
|
||||
"question_email",
|
||||
"free_input_single",
|
||||
"free_input_multi",
|
||||
].includes(this.currentStep.data.chatbot_step_type);
|
||||
},
|
||||
default: false,
|
||||
@@ -492,9 +515,9 @@ Model({
|
||||
},
|
||||
default: 3500, // in milliseconds
|
||||
}),
|
||||
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
|
||||
publicLivechatGlobalOwner: one("PublicLivechatGlobal", {
|
||||
identifying: true,
|
||||
inverse: 'chatbot',
|
||||
inverse: "chatbot",
|
||||
}),
|
||||
scriptId: attr({
|
||||
compute() {
|
||||
@@ -510,7 +533,10 @@ Model({
|
||||
if (!this.messaging.publicLivechatGlobal.sessionCookie) {
|
||||
return clear();
|
||||
}
|
||||
return 'im_livechat.chatbot.state.uuid_' + JSON.parse(this.messaging.publicLivechatGlobal.sessionCookie).uuid;
|
||||
return (
|
||||
"im_livechat.chatbot.state.uuid_" +
|
||||
JSON.parse(this.messaging.publicLivechatGlobal.sessionCookie).uuid
|
||||
);
|
||||
},
|
||||
}),
|
||||
shouldEndScript: attr({
|
||||
@@ -532,21 +558,26 @@ Model({
|
||||
if (this.currentStep.data.conversation_closed) {
|
||||
return true;
|
||||
}
|
||||
if (this.currentStep.data.chatbot_step_is_last &&
|
||||
(this.currentStep.data.chatbot_step_type !== 'forward_operator' ||
|
||||
!this.currentStep.data.chatbot_operator_found)
|
||||
if (
|
||||
this.currentStep.data.chatbot_step_is_last &&
|
||||
(this.currentStep.data.chatbot_step_type !== "forward_operator" ||
|
||||
!this.currentStep.data.chatbot_operator_found)
|
||||
) {
|
||||
if (this.currentStep.data.chatbot_step_type === 'question_email'
|
||||
&& !this.currentStep.data.is_email_valid
|
||||
if (
|
||||
this.currentStep.data.chatbot_step_type === "question_email" &&
|
||||
!this.currentStep.data.is_email_valid
|
||||
) {
|
||||
// email is not (yet) valid, let the user answer / try again
|
||||
return false;
|
||||
} else if (
|
||||
(this.isExpectingUserInput ||
|
||||
this.currentStep.data.chatbot_step_type === 'question_selection') &&
|
||||
this.currentStep.data.chatbot_step_type === "question_selection") &&
|
||||
this.messaging.publicLivechatGlobal.messages.length !== 0
|
||||
) {
|
||||
if (this.messaging.publicLivechatGlobal.lastMessage.authorId !== this.messaging.publicLivechatGlobal.publicLivechat.operator.id) {
|
||||
if (
|
||||
this.messaging.publicLivechatGlobal.lastMessage.authorId !==
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.operator.id
|
||||
) {
|
||||
// we are on the last step of the script, expect a user input and the user has
|
||||
// already answered
|
||||
// -> end the script
|
||||
@@ -564,11 +595,17 @@ Model({
|
||||
}),
|
||||
state: attr({
|
||||
compute() {
|
||||
if (this.messaging.publicLivechatGlobal.rule && !!this.messaging.publicLivechatGlobal.rule.chatbot) {
|
||||
return 'init';
|
||||
if (
|
||||
this.messaging.publicLivechatGlobal.rule &&
|
||||
!!this.messaging.publicLivechatGlobal.rule.chatbot
|
||||
) {
|
||||
return "init";
|
||||
}
|
||||
if (this.messaging.publicLivechatGlobal.livechatInit && this.messaging.publicLivechatGlobal.livechatInit.rule.chatbot) {
|
||||
return 'welcome';
|
||||
if (
|
||||
this.messaging.publicLivechatGlobal.livechatInit &&
|
||||
this.messaging.publicLivechatGlobal.livechatInit.rule.chatbot
|
||||
) {
|
||||
return "welcome";
|
||||
}
|
||||
if (
|
||||
!this.messaging.publicLivechatGlobal.rule &&
|
||||
@@ -577,7 +614,7 @@ Model({
|
||||
this.sessionCookieKey &&
|
||||
localStorage.getItem(this.sessionCookieKey)
|
||||
) {
|
||||
return 'restore_session';
|
||||
return "restore_session";
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { attr, one, Model } from '@mail/model';
|
||||
import { attr, one, Model } from "@mail/model";
|
||||
|
||||
Model({
|
||||
name: 'ChatbotStep',
|
||||
name: "ChatbotStep",
|
||||
fields: {
|
||||
chabotOwner: one('Chatbot', {
|
||||
chabotOwner: one("Chatbot", {
|
||||
identifying: true,
|
||||
inverse: 'currentStep',
|
||||
inverse: "currentStep",
|
||||
}),
|
||||
data: attr(),
|
||||
},
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { attr, clear, one, Model } from '@mail/model';
|
||||
import { attr, clear, one, Model } from "@mail/model";
|
||||
|
||||
import {getCookie, deleteCookie} from 'web.utils.cookies';
|
||||
import { getCookie, deleteCookie } from "web.utils.cookies";
|
||||
|
||||
Model({
|
||||
name: 'LivechatButtonView',
|
||||
name: "LivechatButtonView",
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.update({ widget: this.env.services.public_livechat_service.mountLivechatButton() });
|
||||
this.update({
|
||||
widget: this.env.services.public_livechat_service.mountLivechatButton(),
|
||||
});
|
||||
},
|
||||
_willDelete() {
|
||||
this.widget.destroy();
|
||||
@@ -20,19 +22,27 @@ Model({
|
||||
* @param {Object} [options={}]
|
||||
*/
|
||||
addMessage(data, options) {
|
||||
const hasAlreadyMessage = _.some(this.messaging.publicLivechatGlobal.messages, function (msg) {
|
||||
return data.id === msg.id;
|
||||
});
|
||||
const hasAlreadyMessage = _.some(
|
||||
this.messaging.publicLivechatGlobal.messages,
|
||||
function (msg) {
|
||||
return data.id === msg.id;
|
||||
}
|
||||
);
|
||||
if (hasAlreadyMessage) {
|
||||
return;
|
||||
}
|
||||
const message = this.messaging.models['PublicLivechatMessage'].insert({
|
||||
const message = this.messaging.models["PublicLivechatMessage"].insert({
|
||||
data,
|
||||
id: data.id,
|
||||
});
|
||||
|
||||
if (this.messaging.publicLivechatGlobal.publicLivechat && this.messaging.publicLivechatGlobal.publicLivechat.widget) {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.addMessage(message.widget);
|
||||
if (
|
||||
this.messaging.publicLivechatGlobal.publicLivechat &&
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget
|
||||
) {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.addMessage(
|
||||
message.widget
|
||||
);
|
||||
}
|
||||
|
||||
if (options && options.prepend) {
|
||||
@@ -46,7 +56,9 @@ Model({
|
||||
}
|
||||
},
|
||||
askFeedback() {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_thread_composer input').prop('disabled', true);
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget
|
||||
.$(".o_thread_composer input")
|
||||
.prop("disabled", true);
|
||||
this.messaging.publicLivechatGlobal.update({ feedbackView: {} });
|
||||
/**
|
||||
* When we enter the "ask feedback" process of the chat, we hide some elements that become
|
||||
@@ -59,19 +71,28 @@ Model({
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep.data.conversation_closed = true;
|
||||
this.messaging.publicLivechatGlobal.chatbot.saveSession();
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_livechat_chatbot_main_restart').addClass('d-none');
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_livechat_chatbot_end').hide();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_composer_text_field')
|
||||
.removeClass('d-none')
|
||||
.val('');
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget
|
||||
.$(".o_livechat_chatbot_main_restart")
|
||||
.addClass("d-none");
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget
|
||||
.$(".o_livechat_chatbot_end")
|
||||
.hide();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget
|
||||
.$(".o_composer_text_field")
|
||||
.removeClass("d-none")
|
||||
.val("");
|
||||
},
|
||||
/**
|
||||
* Restart the script and then trigger the "next step" (which will be the first of the script
|
||||
* in this case).
|
||||
*/
|
||||
async onChatbotRestartScript(ev) {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_composer_text_field').removeClass('d-none');
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_livechat_chatbot_end').hide();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget
|
||||
.$(".o_composer_text_field")
|
||||
.removeClass("d-none");
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget
|
||||
.$(".o_livechat_chatbot_end")
|
||||
.hide();
|
||||
|
||||
if (this.messaging.publicLivechatGlobal.chatbot.nextStepTimeout) {
|
||||
clearTimeout(this.messaging.publicLivechatGlobal.chatbot.nextStepTimeout);
|
||||
@@ -82,7 +103,7 @@ Model({
|
||||
}
|
||||
|
||||
const postedMessage = await this.messaging.rpc({
|
||||
route: '/chatbot/restart',
|
||||
route: "/chatbot/restart",
|
||||
params: {
|
||||
channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
|
||||
chatbot_script_id: this.messaging.publicLivechatGlobal.chatbot.scriptId,
|
||||
@@ -98,24 +119,27 @@ Model({
|
||||
this.messaging.publicLivechatGlobal.chatbot.update({
|
||||
nextStepTimeout: setTimeout(
|
||||
this.messaging.publicLivechatGlobal.chatbot.triggerNextStep,
|
||||
this.messaging.publicLivechatGlobal.chatbot.messageDelay,
|
||||
this.messaging.publicLivechatGlobal.chatbot.messageDelay
|
||||
),
|
||||
});
|
||||
},
|
||||
closeChat() {
|
||||
this.messaging.publicLivechatGlobal.update({ chatWindow: clear() });
|
||||
deleteCookie('im_livechat_session');
|
||||
deleteCookie("im_livechat_session");
|
||||
},
|
||||
/**
|
||||
* Called when the visitor leaves the livechat chatter the first time (first click on X button)
|
||||
* this will deactivate the mail_channel, notify operator that visitor has left the channel.
|
||||
*/
|
||||
leaveSession() {
|
||||
const cookie = getCookie('im_livechat_session');
|
||||
const cookie = getCookie("im_livechat_session");
|
||||
if (cookie) {
|
||||
const channel = JSON.parse(cookie);
|
||||
this.messaging.rpc({ route: '/im_livechat/visitor_leave_session', params: { uuid: channel.uuid } });
|
||||
deleteCookie('im_livechat_session');
|
||||
this.messaging.rpc({
|
||||
route: "/im_livechat/visitor_leave_session",
|
||||
params: { uuid: channel.uuid },
|
||||
});
|
||||
deleteCookie("im_livechat_session");
|
||||
}
|
||||
},
|
||||
openChat() {
|
||||
@@ -127,7 +151,7 @@ Model({
|
||||
},
|
||||
async openChatWindow() {
|
||||
this.messaging.publicLivechatGlobal.update({ chatWindow: {} });
|
||||
await this.messaging.publicLivechatGlobal.chatWindow.widget.appendTo($('body'));
|
||||
await this.messaging.publicLivechatGlobal.chatWindow.widget.appendTo($("body"));
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.adjustPosition();
|
||||
this.widget.$el.hide();
|
||||
this._openChatWindowChatbot();
|
||||
@@ -156,27 +180,30 @@ Model({
|
||||
this.addMessage(m);
|
||||
}
|
||||
this.openChat();
|
||||
} else if (!this.messaging.device.isSmall && this.messaging.publicLivechatGlobal.rule.action === 'auto_popup') {
|
||||
const autoPopupCookie = getCookie('im_livechat_auto_popup');
|
||||
} else if (
|
||||
!this.messaging.device.isSmall &&
|
||||
this.messaging.publicLivechatGlobal.rule.action === "auto_popup"
|
||||
) {
|
||||
const autoPopupCookie = getCookie("im_livechat_auto_popup");
|
||||
if (!autoPopupCookie || JSON.parse(autoPopupCookie)) {
|
||||
this.update({
|
||||
autoOpenChatTimeout: setTimeout(
|
||||
this.openChat,
|
||||
this.messaging.publicLivechatGlobal.rule.auto_popup_timer * 1000,
|
||||
this.messaging.publicLivechatGlobal.rule.auto_popup_timer * 1000
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
if (this.buttonBackgroundColor) {
|
||||
this.widget.$el.css('background-color', this.buttonBackgroundColor);
|
||||
this.widget.$el.css("background-color", this.buttonBackgroundColor);
|
||||
}
|
||||
if (this.buttonTextColor) {
|
||||
this.widget.$el.css('color', this.buttonTextColor);
|
||||
this.widget.$el.css("color", this.buttonTextColor);
|
||||
}
|
||||
// If website_event_track installed, put the livechat banner above the PWA banner.
|
||||
const pwaBannerHeight = $('.o_pwa_install_banner').outerHeight(true);
|
||||
const pwaBannerHeight = $(".o_pwa_install_banner").outerHeight(true);
|
||||
if (pwaBannerHeight) {
|
||||
this.widget.$el.css('bottom', pwaBannerHeight + 'px');
|
||||
this.widget.$el.css("bottom", pwaBannerHeight + "px");
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -186,7 +213,7 @@ Model({
|
||||
if (this.isOpeningChat) {
|
||||
return;
|
||||
}
|
||||
const cookie = getCookie('im_livechat_session');
|
||||
const cookie = getCookie("im_livechat_session");
|
||||
let def;
|
||||
this.update({ isOpeningChat: true });
|
||||
clearTimeout(this.autoOpenChatTimeout);
|
||||
@@ -195,19 +222,24 @@ Model({
|
||||
} else {
|
||||
// re-initialize messages cache
|
||||
this.messaging.publicLivechatGlobal.update({ messages: clear() });
|
||||
def = this.messaging.rpc({
|
||||
route: '/im_livechat/get_session',
|
||||
params: {
|
||||
...this.widget._prepareGetSessionParameters(),
|
||||
persisted: false,
|
||||
def = this.messaging.rpc(
|
||||
{
|
||||
route: "/im_livechat/get_session",
|
||||
params: {
|
||||
...this.widget._prepareGetSessionParameters(),
|
||||
persisted: false,
|
||||
},
|
||||
},
|
||||
}, { silent: true });
|
||||
{ silent: true }
|
||||
);
|
||||
}
|
||||
def.then((livechatData) => {
|
||||
if (!livechatData || !livechatData.operator_pid) {
|
||||
try {
|
||||
this.widget.displayNotification({
|
||||
message: this.env._t("No available collaborator, please try again later."),
|
||||
message: this.env._t(
|
||||
"No available collaborator, please try again later."
|
||||
),
|
||||
sticky: true,
|
||||
});
|
||||
} catch {
|
||||
@@ -218,7 +250,9 @@ Model({
|
||||
* lib at the moment because they use bootstrap toast and
|
||||
* we don't want to include boostrap in external lib.
|
||||
*/
|
||||
console.warn(this.env._t("No available collaborator, please try again later."));
|
||||
console.warn(
|
||||
this.env._t("No available collaborator, please try again later.")
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.messaging.publicLivechatGlobal.update({
|
||||
@@ -232,11 +266,13 @@ Model({
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.updateSessionCookie();
|
||||
});
|
||||
}
|
||||
}).then(() => {
|
||||
this.update({ isOpeningChat: false });
|
||||
}).guardedCatch(() => {
|
||||
this.update({ isOpeningChat: false });
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
this.update({ isOpeningChat: false });
|
||||
})
|
||||
.guardedCatch(() => {
|
||||
this.update({ isOpeningChat: false });
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Resuming the chatbot script if we are currently running one.
|
||||
@@ -250,7 +286,7 @@ Model({
|
||||
* @override
|
||||
*/
|
||||
_openChatWindowChatbot() {
|
||||
window.addEventListener('resize', () => {
|
||||
window.addEventListener("resize", () => {
|
||||
if (this.messaging.publicLivechatGlobal.chatWindow) {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.scrollToBottom();
|
||||
}
|
||||
@@ -270,10 +306,15 @@ Model({
|
||||
* @param {Object} message
|
||||
*/
|
||||
async _sendMessage(message) {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget._notifyMyselfTyping({ typing: false });
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget._notifyMyselfTyping({
|
||||
typing: false,
|
||||
});
|
||||
const messageId = await this.messaging.rpc({
|
||||
route: '/mail/chat_post',
|
||||
params: { uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid, message_content: message.content },
|
||||
route: "/mail/chat_post",
|
||||
params: {
|
||||
uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
|
||||
message_content: message.content,
|
||||
},
|
||||
});
|
||||
if (!messageId) {
|
||||
try {
|
||||
@@ -309,18 +350,23 @@ Model({
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep.data
|
||||
) {
|
||||
if (
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep.data.chatbot_step_type === 'forward_operator' &&
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep.data.chatbot_operator_found
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep.data
|
||||
.chatbot_step_type === "forward_operator" &&
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep.data
|
||||
.chatbot_operator_found
|
||||
) {
|
||||
return; // operator has taken over the conversation, let them speak
|
||||
} else if (this.messaging.publicLivechatGlobal.chatbot.currentStep.data.chatbot_step_type === 'free_input_multi') {
|
||||
} else if (
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep.data
|
||||
.chatbot_step_type === "free_input_multi"
|
||||
) {
|
||||
this.messaging.publicLivechatGlobal.chatbot.debouncedAwaitUserInput();
|
||||
} else if (!this.messaging.publicLivechatGlobal.chatbot.shouldEndScript) {
|
||||
this.messaging.publicLivechatGlobal.chatbot.setIsTyping();
|
||||
this.messaging.publicLivechatGlobal.chatbot.update({
|
||||
nextStepTimeout: setTimeout(
|
||||
this.messaging.publicLivechatGlobal.chatbot.triggerNextStep,
|
||||
this.messaging.publicLivechatGlobal.chatbot.messageDelay,
|
||||
this.messaging.publicLivechatGlobal.chatbot.messageDelay
|
||||
),
|
||||
});
|
||||
} else {
|
||||
@@ -420,7 +466,7 @@ Model({
|
||||
}
|
||||
return this.env._t("Ask something ...");
|
||||
},
|
||||
default: '',
|
||||
default: "",
|
||||
}),
|
||||
isOpenChatDebounced: attr({
|
||||
compute() {
|
||||
@@ -440,9 +486,9 @@ Model({
|
||||
return _.debounce(this._openChat, 200, true);
|
||||
},
|
||||
}),
|
||||
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
|
||||
publicLivechatGlobalOwner: one("PublicLivechatGlobal", {
|
||||
identifying: true,
|
||||
inverse: 'livechatButtonView',
|
||||
inverse: "livechatButtonView",
|
||||
}),
|
||||
serverUrl: attr({
|
||||
compute() {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { attr, Model } from '@mail/model';
|
||||
import { attr, Model } from "@mail/model";
|
||||
|
||||
Model({
|
||||
name: 'LivechatOperator',
|
||||
name: "LivechatOperator",
|
||||
fields: {
|
||||
id: attr({
|
||||
identifying: true,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { one, Patch } from '@mail/model';
|
||||
import { one, Patch } from "@mail/model";
|
||||
|
||||
Patch({
|
||||
name: 'Messaging',
|
||||
name: "Messaging",
|
||||
fields: {
|
||||
publicLivechatGlobal: one('PublicLivechatGlobal', {
|
||||
publicLivechatGlobal: one("PublicLivechatGlobal", {
|
||||
isCausal: true,
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import PublicLivechat from '@im_livechat/legacy/models/public_livechat';
|
||||
import PublicLivechat from "@im_livechat/legacy/models/public_livechat";
|
||||
|
||||
import { attr, clear, one, Model } from '@mail/model';
|
||||
import { attr, clear, one, Model } from "@mail/model";
|
||||
|
||||
import { unaccent } from 'web.utils';
|
||||
import { deleteCookie, setCookie } from 'web.utils.cookies';
|
||||
import { unaccent } from "web.utils";
|
||||
import { deleteCookie, setCookie } from "web.utils.cookies";
|
||||
|
||||
Model({
|
||||
name: 'PublicLivechat',
|
||||
name: "PublicLivechat",
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.update({
|
||||
@@ -75,9 +75,9 @@ Model({
|
||||
return false;
|
||||
},
|
||||
}),
|
||||
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
|
||||
publicLivechatGlobalOwner: one("PublicLivechatGlobal", {
|
||||
identifying: true,
|
||||
inverse: 'publicLivechat',
|
||||
inverse: "publicLivechat",
|
||||
}),
|
||||
name: attr({
|
||||
compute() {
|
||||
@@ -87,7 +87,7 @@ Model({
|
||||
return this.data.name;
|
||||
},
|
||||
}),
|
||||
operator: one('LivechatOperator', {
|
||||
operator: one("LivechatOperator", {
|
||||
compute() {
|
||||
if (!this.data) {
|
||||
return clear();
|
||||
@@ -109,7 +109,7 @@ Model({
|
||||
if (!this.data) {
|
||||
return clear();
|
||||
}
|
||||
return this.data.status || '';
|
||||
return this.data.status || "";
|
||||
},
|
||||
}),
|
||||
// amount of messages that have not yet been read on this chat
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import Feedback from '@im_livechat/legacy/widgets/feedback/feedback';
|
||||
import Feedback from "@im_livechat/legacy/widgets/feedback/feedback";
|
||||
|
||||
import { attr, one, Model } from '@mail/model';
|
||||
import { attr, one, Model } from "@mail/model";
|
||||
|
||||
Model({
|
||||
name: 'PublicLivechatFeedbackView',
|
||||
name: "PublicLivechatFeedbackView",
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.update({
|
||||
widget: new Feedback(
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.widget,
|
||||
this.messaging,
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget,
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget
|
||||
),
|
||||
});
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.replaceContentWith(this.widget);
|
||||
this.widget.on('feedback_sent', null, this._onFeedbackSent);
|
||||
this.widget.on('send_message', null, this._onSendMessage);
|
||||
this.widget.on("feedback_sent", null, this._onFeedbackSent);
|
||||
this.widget.on("send_message", null, this._onSendMessage);
|
||||
},
|
||||
_willDelete() {
|
||||
this.widget.destroy();
|
||||
@@ -32,9 +32,9 @@ Model({
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
|
||||
publicLivechatGlobalOwner: one("PublicLivechatGlobal", {
|
||||
identifying: true,
|
||||
inverse: 'feedbackView',
|
||||
inverse: "feedbackView",
|
||||
}),
|
||||
widget: attr(),
|
||||
},
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { attr, clear, many, one, Model } from '@mail/model';
|
||||
import { attr, clear, many, one, Model } from "@mail/model";
|
||||
|
||||
import { qweb } from 'web.core';
|
||||
import { Markup } from 'web.utils';
|
||||
import {getCookie, setCookie, deleteCookie} from 'web.utils.cookies';
|
||||
import { qweb } from "web.core";
|
||||
import { Markup } from "web.utils";
|
||||
import { getCookie, setCookie, deleteCookie } from "web.utils.cookies";
|
||||
|
||||
Model({
|
||||
name: 'PublicLivechatGlobal',
|
||||
name: "PublicLivechatGlobal",
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
// History tracking
|
||||
const page = window.location.href.replace(/^.*\/\/[^/]+/, '');
|
||||
const page = window.location.href.replace(/^.*\/\/[^/]+/, "");
|
||||
const pageHistory = getCookie(this.LIVECHAT_COOKIE_HISTORY);
|
||||
let urlHistory = [];
|
||||
if (pageHistory) {
|
||||
@@ -22,7 +22,12 @@ Model({
|
||||
while (urlHistory.length > this.HISTORY_LIMIT) {
|
||||
urlHistory.shift();
|
||||
}
|
||||
setCookie(this.LIVECHAT_COOKIE_HISTORY, JSON.stringify(urlHistory), 60 * 60 * 24, 'optional'); // 1 day cookie
|
||||
setCookie(
|
||||
this.LIVECHAT_COOKIE_HISTORY,
|
||||
JSON.stringify(urlHistory),
|
||||
60 * 60 * 24,
|
||||
"optional"
|
||||
); // 1 day cookie
|
||||
}
|
||||
if (this.isAvailable) {
|
||||
this.willStart();
|
||||
@@ -31,7 +36,7 @@ Model({
|
||||
},
|
||||
recordMethods: {
|
||||
async loadQWebTemplate() {
|
||||
const templates = await this.messaging.rpc({ route: '/im_livechat/load_templates' });
|
||||
const templates = await this.messaging.rpc({ route: "/im_livechat/load_templates" });
|
||||
for (const template of templates) {
|
||||
qweb.add_template(template);
|
||||
}
|
||||
@@ -42,12 +47,12 @@ Model({
|
||||
await this._willStartChatbot();
|
||||
},
|
||||
async _willStart() {
|
||||
const strCookie = getCookie('im_livechat_session');
|
||||
const strCookie = getCookie("im_livechat_session");
|
||||
const isSessionCookieAvailable = Boolean(strCookie);
|
||||
const cookie = JSON.parse(strCookie || '{}');
|
||||
const cookie = JSON.parse(strCookie || "{}");
|
||||
if (cookie.id) {
|
||||
const history = await this.messaging.rpc({
|
||||
route: '/mail/chat_history',
|
||||
route: "/mail/chat_history",
|
||||
params: { uuid: cookie.uuid, limit: 100 },
|
||||
});
|
||||
history.reverse();
|
||||
@@ -60,7 +65,7 @@ Model({
|
||||
this.update({ history: [], isAvailableForMe: true });
|
||||
} else {
|
||||
const result = await this.messaging.rpc({
|
||||
route: '/im_livechat/init',
|
||||
route: "/im_livechat/init",
|
||||
params: { channel_id: this.channelId },
|
||||
});
|
||||
if (result.available_for_me) {
|
||||
@@ -94,37 +99,40 @@ Model({
|
||||
} else if (this.history !== null && this.history.length === 0) {
|
||||
this.update({
|
||||
livechatInit: await this.messaging.rpc({
|
||||
route: '/im_livechat/init',
|
||||
route: "/im_livechat/init",
|
||||
params: { channel_id: this.channelId },
|
||||
}),
|
||||
});
|
||||
} else if (this.history !== null && this.history.length !== 0) {
|
||||
const sessionCookie = getCookie('im_livechat_session');
|
||||
const sessionCookie = getCookie("im_livechat_session");
|
||||
if (sessionCookie) {
|
||||
this.update({ sessionCookie });
|
||||
}
|
||||
}
|
||||
|
||||
if (this.chatbot.state === 'init') {
|
||||
if (this.chatbot.state === "init") {
|
||||
// we landed on a website page where a channel rule is configured to run a chatbot.script
|
||||
// -> initialize necessary state
|
||||
if (this.rule.chatbot_welcome_steps && this.rule.chatbot_welcome_steps.length !== 0) {
|
||||
if (
|
||||
this.rule.chatbot_welcome_steps &&
|
||||
this.rule.chatbot_welcome_steps.length !== 0
|
||||
) {
|
||||
this.chatbot.update({
|
||||
currentStep: {
|
||||
data: this.chatbot.lastWelcomeStep,
|
||||
},
|
||||
});
|
||||
}
|
||||
} else if (this.chatbot.state === 'welcome') {
|
||||
} else if (this.chatbot.state === "welcome") {
|
||||
// we landed on a website page and a chatbot script was initialized on a previous one
|
||||
// however the end-user did not interact with the bot ( :( )
|
||||
// -> remove cookie to force opening the popup again
|
||||
// -> initialize necessary state
|
||||
// -> batch welcome message (see '_sendWelcomeChatbotMessage')
|
||||
deleteCookie('im_livechat_auto_popup');
|
||||
deleteCookie("im_livechat_auto_popup");
|
||||
this.update({ history: clear() });
|
||||
this.update({ rule: this.livechatInit.rule });
|
||||
} else if (this.chatbot.state === 'restore_session') {
|
||||
} else if (this.chatbot.state === "restore_session") {
|
||||
// we landed on a website page and a chatbot script is currently running
|
||||
// -> restore the user's session (see 'Chatbot/restoreSession')
|
||||
this.chatbot.restoreSession();
|
||||
@@ -136,7 +144,7 @@ Model({
|
||||
default: 15,
|
||||
}),
|
||||
LIVECHAT_COOKIE_HISTORY: attr({
|
||||
default: 'im_livechat_history',
|
||||
default: "im_livechat_history",
|
||||
}),
|
||||
RATING_TO_EMOJI: attr({
|
||||
default: {
|
||||
@@ -150,15 +158,15 @@ Model({
|
||||
return this.options.channel_id;
|
||||
},
|
||||
}),
|
||||
chatbot: one('Chatbot', {
|
||||
chatbot: one("Chatbot", {
|
||||
default: {},
|
||||
inverse: 'publicLivechatGlobalOwner',
|
||||
inverse: "publicLivechatGlobalOwner",
|
||||
}),
|
||||
chatWindow: one('PublicLivechatWindow', {
|
||||
inverse: 'publicLivechatGlobalOwner',
|
||||
chatWindow: one("PublicLivechatWindow", {
|
||||
inverse: "publicLivechatGlobalOwner",
|
||||
}),
|
||||
feedbackView: one('PublicLivechatFeedbackView', {
|
||||
inverse: 'publicLivechatGlobalOwner',
|
||||
feedbackView: one("PublicLivechatFeedbackView", {
|
||||
inverse: "publicLivechatGlobalOwner",
|
||||
}),
|
||||
hasLoadedQWebTemplate: attr({
|
||||
default: false,
|
||||
@@ -204,7 +212,7 @@ Model({
|
||||
},
|
||||
default: false,
|
||||
}),
|
||||
lastMessage: one('PublicLivechatMessage', {
|
||||
lastMessage: one("PublicLivechatMessage", {
|
||||
compute() {
|
||||
if (this.messages.length === 0) {
|
||||
return clear();
|
||||
@@ -212,35 +220,40 @@ Model({
|
||||
return this.messages[this.messages.length - 1];
|
||||
},
|
||||
}),
|
||||
livechatButtonView: one('LivechatButtonView', {
|
||||
livechatButtonView: one("LivechatButtonView", {
|
||||
compute() {
|
||||
if (this.isAvailable && (this.isAvailableForMe || this.isTestChatbot) && this.hasLoadedQWebTemplate && this.env.services.public_livechat_service) {
|
||||
if (
|
||||
this.isAvailable &&
|
||||
(this.isAvailableForMe || this.isTestChatbot) &&
|
||||
this.hasLoadedQWebTemplate &&
|
||||
this.env.services.public_livechat_service
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
inverse: 'publicLivechatGlobalOwner',
|
||||
inverse: "publicLivechatGlobalOwner",
|
||||
}),
|
||||
livechatInit: attr(),
|
||||
messages: many('PublicLivechatMessage'),
|
||||
notificationHandler: one('PublicLivechatGlobalNotificationHandler', {
|
||||
inverse: 'publicLivechatGlobalOwner',
|
||||
messages: many("PublicLivechatMessage"),
|
||||
notificationHandler: one("PublicLivechatGlobalNotificationHandler", {
|
||||
inverse: "publicLivechatGlobalOwner",
|
||||
compute() {
|
||||
if (this.publicLivechat && !this.publicLivechat.isTemporary) {
|
||||
return {};
|
||||
}
|
||||
return clear();
|
||||
}
|
||||
},
|
||||
}),
|
||||
options: attr({
|
||||
default: {},
|
||||
}),
|
||||
publicLivechat: one('PublicLivechat', {
|
||||
inverse: 'publicLivechatGlobalOwner',
|
||||
publicLivechat: one("PublicLivechat", {
|
||||
inverse: "publicLivechatGlobalOwner",
|
||||
}),
|
||||
rule: attr(),
|
||||
serverUrl: attr({
|
||||
default: '',
|
||||
default: "",
|
||||
}),
|
||||
sessionCookie: attr(),
|
||||
testChatbotData: attr({
|
||||
@@ -251,10 +264,14 @@ Model({
|
||||
return this.options.testChatbotData;
|
||||
},
|
||||
}),
|
||||
welcomeMessages: many('PublicLivechatMessage', {
|
||||
welcomeMessages: many("PublicLivechatMessage", {
|
||||
compute() {
|
||||
return this.messages.filter((message) => {
|
||||
return message.id && typeof message.id === 'string' && message.id.startsWith('_welcome_');
|
||||
return (
|
||||
message.id &&
|
||||
typeof message.id === "string" &&
|
||||
message.id.startsWith("_welcome_")
|
||||
);
|
||||
});
|
||||
},
|
||||
}),
|
||||
|
||||
+63
-27
@@ -1,17 +1,19 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { increment, one, Model } from '@mail/model';
|
||||
import { increment, one, Model } from "@mail/model";
|
||||
|
||||
import session from 'web.session';
|
||||
import utils from 'web.utils';
|
||||
import {getCookie} from 'web.utils.cookies';
|
||||
import session from "web.session";
|
||||
import utils from "web.utils";
|
||||
import { getCookie } from "web.utils.cookies";
|
||||
|
||||
Model({
|
||||
name: 'PublicLivechatGlobalNotificationHandler',
|
||||
name: "PublicLivechatGlobalNotificationHandler",
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.env.services['bus_service'].addChannel(this.messaging.publicLivechatGlobal.publicLivechat.uuid);
|
||||
this.env.services['bus_service'].addEventListener('notification', this._onNotification);
|
||||
this.env.services["bus_service"].addChannel(
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.uuid
|
||||
);
|
||||
this.env.services["bus_service"].addEventListener("notification", this._onNotification);
|
||||
},
|
||||
},
|
||||
recordMethods: {
|
||||
@@ -23,43 +25,61 @@ Model({
|
||||
*/
|
||||
_handleNotification({ payload: notifPayload, type }) {
|
||||
switch (type) {
|
||||
case 'im_livechat.history_command': {
|
||||
case "im_livechat.history_command": {
|
||||
if (notifPayload.id !== this.messaging.publicLivechatGlobal.publicLivechat.id) {
|
||||
return;
|
||||
}
|
||||
const cookie = getCookie(this.messaging.publicLivechatGlobal.LIVECHAT_COOKIE_HISTORY);
|
||||
const cookie = getCookie(
|
||||
this.messaging.publicLivechatGlobal.LIVECHAT_COOKIE_HISTORY
|
||||
);
|
||||
const history = cookie ? JSON.parse(cookie) : [];
|
||||
session.rpc('/im_livechat/history', {
|
||||
session.rpc("/im_livechat/history", {
|
||||
pid: this.messaging.publicLivechatGlobal.publicLivechat.operator.id,
|
||||
channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
|
||||
page_history: history,
|
||||
});
|
||||
return;
|
||||
}
|
||||
case 'mail.channel.member/typing_status': {
|
||||
if (!this.messaging.publicLivechatGlobal.chatWindow || !this.messaging.publicLivechatGlobal.chatWindow.exists()) {
|
||||
case "mail.channel.member/typing_status": {
|
||||
if (
|
||||
!this.messaging.publicLivechatGlobal.chatWindow ||
|
||||
!this.messaging.publicLivechatGlobal.chatWindow.exists()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const channelMemberData = notifPayload;
|
||||
if (channelMemberData.channel.id !== this.messaging.publicLivechatGlobal.publicLivechat.id) {
|
||||
if (
|
||||
channelMemberData.channel.id !==
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.id
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (!channelMemberData.persona.partner) {
|
||||
return;
|
||||
}
|
||||
if (channelMemberData.persona.partner.id === this.messaging.publicLivechatGlobal.livechatButtonView.currentPartnerId) {
|
||||
if (
|
||||
channelMemberData.persona.partner.id ===
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.currentPartnerId
|
||||
) {
|
||||
// ignore typing display of current partner.
|
||||
return;
|
||||
}
|
||||
if (channelMemberData.isTyping) {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.registerTyping({ partnerID: channelMemberData.persona.partner.id });
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.registerTyping({
|
||||
partnerID: channelMemberData.persona.partner.id,
|
||||
});
|
||||
} else {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.unregisterTyping({ partnerID: channelMemberData.persona.partner.id });
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.unregisterTyping({
|
||||
partnerID: channelMemberData.persona.partner.id,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 'mail.channel/new_message': {
|
||||
if (!this.messaging.publicLivechatGlobal.chatWindow || !this.messaging.publicLivechatGlobal.chatWindow.exists()) {
|
||||
case "mail.channel/new_message": {
|
||||
if (
|
||||
!this.messaging.publicLivechatGlobal.chatWindow ||
|
||||
!this.messaging.publicLivechatGlobal.chatWindow.exists()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (notifPayload.id !== this.messaging.publicLivechatGlobal.publicLivechat.id) {
|
||||
@@ -67,26 +87,42 @@ Model({
|
||||
}
|
||||
const notificationData = notifPayload.message;
|
||||
// If message from notif is already in chatter messages, stop handling
|
||||
if (this.messaging.publicLivechatGlobal.messages.some(message => message.id === notificationData.id)) {
|
||||
if (
|
||||
this.messaging.publicLivechatGlobal.messages.some(
|
||||
(message) => message.id === notificationData.id
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
notificationData.body = utils.Markup(notificationData.body);
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.addMessage(notificationData);
|
||||
if (this.messaging.publicLivechatGlobal.publicLivechat.isFolded || !this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.isAtBottom()) {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({ unreadCounter: increment() });
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.addMessage(
|
||||
notificationData
|
||||
);
|
||||
if (
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.isFolded ||
|
||||
!this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.isAtBottom()
|
||||
) {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({
|
||||
unreadCounter: increment(),
|
||||
});
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.chatWindow.renderMessages();
|
||||
return;
|
||||
}
|
||||
case 'mail.record/insert': {
|
||||
case "mail.record/insert": {
|
||||
const { Message: payload } = notifPayload;
|
||||
if (!payload) {
|
||||
return;
|
||||
}
|
||||
if (!this.messaging.publicLivechatGlobal.chatWindow || !this.messaging.publicLivechatGlobal.chatWindow.exists()) {
|
||||
if (
|
||||
!this.messaging.publicLivechatGlobal.chatWindow ||
|
||||
!this.messaging.publicLivechatGlobal.chatWindow.exists()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const message = this.messaging.publicLivechatGlobal.messages.find(message => message.id === payload.id);
|
||||
const message = this.messaging.publicLivechatGlobal.messages.find(
|
||||
(message) => message.id === payload.id
|
||||
);
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
@@ -108,9 +144,9 @@ Model({
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
|
||||
publicLivechatGlobalOwner: one("PublicLivechatGlobal", {
|
||||
identifying: true,
|
||||
inverse: 'notificationHandler',
|
||||
inverse: "notificationHandler",
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { attr, clear, Model } from '@mail/model';
|
||||
import { attr, clear, Model } from "@mail/model";
|
||||
|
||||
import PublicLivechatMessage from '@im_livechat/legacy/models/public_livechat_message';
|
||||
import PublicLivechatMessage from "@im_livechat/legacy/models/public_livechat_message";
|
||||
|
||||
Model({
|
||||
name: 'PublicLivechatMessage',
|
||||
name: "PublicLivechatMessage",
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.update({ widget: new PublicLivechatMessage(this.messaging.publicLivechatGlobal.livechatButtonView.widget, this.messaging, this.data) });
|
||||
this.update({
|
||||
widget: new PublicLivechatMessage(
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.widget,
|
||||
this.messaging,
|
||||
this.data
|
||||
),
|
||||
});
|
||||
},
|
||||
_willDelete() {
|
||||
this.widget.destroy();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import PublicLivechatView from '@im_livechat/legacy/widgets/public_livechat_view/public_livechat_view';
|
||||
import PublicLivechatView from "@im_livechat/legacy/widgets/public_livechat_view/public_livechat_view";
|
||||
|
||||
import { attr, one, Model } from '@mail/model';
|
||||
import { attr, one, Model } from "@mail/model";
|
||||
|
||||
Model({
|
||||
name: 'PublicLivechatView',
|
||||
name: "PublicLivechatView",
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.update({
|
||||
@@ -17,9 +17,9 @@ Model({
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
publicLivechatWindowOwner: one('PublicLivechatWindow', {
|
||||
publicLivechatWindowOwner: one("PublicLivechatWindow", {
|
||||
identifying: true,
|
||||
inverse: 'publicLivechatView',
|
||||
inverse: "publicLivechatView",
|
||||
}),
|
||||
widget: attr(),
|
||||
},
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import PublicLivechatWindow from '@im_livechat/legacy/widgets/public_livechat_window/public_livechat_window';
|
||||
import PublicLivechatWindow from "@im_livechat/legacy/widgets/public_livechat_window/public_livechat_window";
|
||||
|
||||
import { attr, one, Model } from '@mail/model';
|
||||
import { attr, one, Model } from "@mail/model";
|
||||
|
||||
Model({
|
||||
name: 'PublicLivechatWindow',
|
||||
name: "PublicLivechatWindow",
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.update({
|
||||
widget: new PublicLivechatWindow(
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.widget,
|
||||
this.messaging,
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget,
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget
|
||||
),
|
||||
});
|
||||
},
|
||||
@@ -22,16 +22,25 @@ Model({
|
||||
},
|
||||
recordMethods: {
|
||||
enableInput() {
|
||||
const $composerTextField = this.widget.$('.o_composer_text_field');
|
||||
const $composerTextField = this.widget.$(".o_composer_text_field");
|
||||
$composerTextField
|
||||
.prop('disabled', false)
|
||||
.removeClass('text-center fst-italic bg-200')
|
||||
.val('')
|
||||
.prop("disabled", false)
|
||||
.removeClass("text-center fst-italic bg-200")
|
||||
.val("")
|
||||
.focus();
|
||||
|
||||
$composerTextField.off('keydown', this.messaging.publicLivechatGlobal.chatbot.onKeydownInput);
|
||||
if (this.messaging.publicLivechatGlobal.chatbot.currentStep.data.chatbot_step_type === 'free_input_multi') {
|
||||
$composerTextField.on('keydown', this.messaging.publicLivechatGlobal.chatbot.onKeydownInput);
|
||||
$composerTextField.off(
|
||||
"keydown",
|
||||
this.messaging.publicLivechatGlobal.chatbot.onKeydownInput
|
||||
);
|
||||
if (
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep.data.chatbot_step_type ===
|
||||
"free_input_multi"
|
||||
) {
|
||||
$composerTextField.on(
|
||||
"keydown",
|
||||
this.messaging.publicLivechatGlobal.chatbot.onKeydownInput
|
||||
);
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -41,9 +50,10 @@ Model({
|
||||
* @private
|
||||
*/
|
||||
disableInput(disableText) {
|
||||
this.widget.$('.o_composer_text_field')
|
||||
.prop('disabled', true)
|
||||
.addClass('text-center fst-italic bg-200')
|
||||
this.widget
|
||||
.$(".o_composer_text_field")
|
||||
.prop("disabled", true)
|
||||
.addClass("text-center fst-italic bg-200")
|
||||
.val(disableText);
|
||||
},
|
||||
renderMessages() {
|
||||
@@ -54,11 +64,18 @@ Model({
|
||||
}
|
||||
const self = this;
|
||||
|
||||
this.widget.$('.o_thread_message:last .o_livechat_chatbot_options li').each(function () {
|
||||
$(this).on('click', self.messaging.publicLivechatGlobal.livechatButtonView.widget._onChatbotOptionClicked.bind(self.messaging.publicLivechatGlobal.livechatButtonView.widget));
|
||||
});
|
||||
this.widget
|
||||
.$(".o_thread_message:last .o_livechat_chatbot_options li")
|
||||
.each(function () {
|
||||
$(this).on(
|
||||
"click",
|
||||
self.messaging.publicLivechatGlobal.livechatButtonView.widget._onChatbotOptionClicked.bind(
|
||||
self.messaging.publicLivechatGlobal.livechatButtonView.widget
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
this.widget.$('.o_livechat_chatbot_main_restart').on('click', (ev) => {
|
||||
this.widget.$(".o_livechat_chatbot_main_restart").on("click", (ev) => {
|
||||
ev.stopPropagation(); // prevent fold behaviour
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.onChatbotRestartScript(ev);
|
||||
});
|
||||
@@ -66,7 +83,11 @@ Model({
|
||||
if (this.messaging.publicLivechatGlobal.messages.length !== 0) {
|
||||
const lastMessage = this.messaging.publicLivechatGlobal.lastMessage;
|
||||
const stepAnswers = lastMessage.widget.getChatbotStepAnswers();
|
||||
if (stepAnswers && stepAnswers.length !== 0 && !lastMessage.widget.getChatbotStepAnswerId()) {
|
||||
if (
|
||||
stepAnswers &&
|
||||
stepAnswers.length !== 0 &&
|
||||
!lastMessage.widget.getChatbotStepAnswerId()
|
||||
) {
|
||||
this.disableInput(this.env._t("Select an option above"));
|
||||
}
|
||||
}
|
||||
@@ -81,13 +102,13 @@ Model({
|
||||
return this.env._t("Say something");
|
||||
},
|
||||
}),
|
||||
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
|
||||
publicLivechatGlobalOwner: one("PublicLivechatGlobal", {
|
||||
identifying: true,
|
||||
inverse: 'chatWindow',
|
||||
inverse: "chatWindow",
|
||||
}),
|
||||
publicLivechatView: one('PublicLivechatView', {
|
||||
publicLivechatView: one("PublicLivechatView", {
|
||||
default: {},
|
||||
inverse: 'publicLivechatWindowOwner',
|
||||
inverse: "publicLivechatWindowOwner",
|
||||
}),
|
||||
widget: attr(),
|
||||
},
|
||||
|
||||
@@ -1,35 +1,36 @@
|
||||
.text-muted {
|
||||
color: map-get($grays, '600');
|
||||
color: map-get($grays, "600");
|
||||
}
|
||||
|
||||
.text-start {
|
||||
text-align: left;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.o_thread_window {
|
||||
&,* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.o_thread_window_header {
|
||||
height: 35px;
|
||||
.fa-close {
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
&:before {
|
||||
content: "\00d7";
|
||||
font-size: initial;
|
||||
}
|
||||
&,
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
> span {
|
||||
margin: auto 0;
|
||||
.o_thread_window_header {
|
||||
height: 35px;
|
||||
.fa-close {
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
&:before {
|
||||
content: "\00d7";
|
||||
font-size: initial;
|
||||
}
|
||||
}
|
||||
> span {
|
||||
margin: auto 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_email_chat_button:after {
|
||||
content:' \27A4';
|
||||
}
|
||||
.o_email_chat_button:after {
|
||||
content: " \27A4";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,4 +13,3 @@
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
.o_history_container{
|
||||
.o_history_container {
|
||||
table-layout: fixed;
|
||||
width: 100% !important;
|
||||
> tbody > tr > td {
|
||||
padding: 0px !important;
|
||||
}
|
||||
.o_history_kanban_container {
|
||||
text-align:center;
|
||||
text-align: center;
|
||||
.o_history_kanban_sub_container {
|
||||
.o_kanban_ungrouped {
|
||||
flex-flow: column;
|
||||
max-height:500px;
|
||||
overflow:auto;
|
||||
padding:2px 0px;
|
||||
max-height: 500px;
|
||||
overflow: auto;
|
||||
padding: 2px 0px;
|
||||
.rounded-circle {
|
||||
width:32px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
.o_kanban_record {
|
||||
@@ -25,7 +25,7 @@
|
||||
}
|
||||
}
|
||||
.oe_module_vignette {
|
||||
text-align:left;
|
||||
text-align: left;
|
||||
}
|
||||
.o_kanban_image {
|
||||
padding-top: 8px;
|
||||
@@ -34,6 +34,5 @@
|
||||
padding: 8px 8px 0px 64px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import LivechatButton from '@im_livechat/legacy/widgets/livechat_button';
|
||||
import LivechatButton from "@im_livechat/legacy/widgets/livechat_button";
|
||||
|
||||
import rootWidget from 'root.widget';
|
||||
import rootWidget from "root.widget";
|
||||
|
||||
export const publicLivechatService = {
|
||||
dependencies: ['messaging'],
|
||||
dependencies: ["messaging"],
|
||||
async start(env, { messaging: messagingService }) {
|
||||
const messaging = await messagingService.get();
|
||||
return {
|
||||
mountLivechatButton() {
|
||||
const livechatButton = new LivechatButton(rootWidget, messaging);
|
||||
livechatButton.appendTo(document.body).catch(error => {
|
||||
livechatButton.appendTo(document.body).catch((error) => {
|
||||
console.info("Can't load 'LivechatButton' because:", error);
|
||||
});
|
||||
return livechatButton;
|
||||
|
||||
@@ -3,19 +3,24 @@
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { MockServer } from "@web/../tests/helpers/mock_server";
|
||||
|
||||
patch(MockServer.prototype, 'im_livechat/controllers/main', {
|
||||
patch(MockServer.prototype, "im_livechat/controllers/main", {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
async _performRPC(route, args) {
|
||||
if (route === '/im_livechat/get_session') {
|
||||
if (route === "/im_livechat/get_session") {
|
||||
const channel_id = args.channel_id;
|
||||
const anonymous_name = args.anonymous_name;
|
||||
const previous_operator_id = args.previous_operator_id;
|
||||
const context = args.context;
|
||||
return this._mockRouteImLivechatGetSession(channel_id, anonymous_name, previous_operator_id, context);
|
||||
return this._mockRouteImLivechatGetSession(
|
||||
channel_id,
|
||||
anonymous_name,
|
||||
previous_operator_id,
|
||||
context
|
||||
);
|
||||
}
|
||||
if (route === '/im_livechat/notify_typing') {
|
||||
if (route === "/im_livechat/notify_typing") {
|
||||
const uuid = args.uuid;
|
||||
const is_typing = args.is_typing;
|
||||
const context = args.context;
|
||||
@@ -33,10 +38,10 @@ patch(MockServer.prototype, 'im_livechat/controllers/main', {
|
||||
* @param {Object} [context={}]
|
||||
* @returns {Object}
|
||||
*/
|
||||
_mockRouteImLivechatGetSession(channel_id, anonymous_name, previous_operator_id, context = {}) {
|
||||
_mockRouteImLivechatGetSession(channel_id, anonymous_name, previous_operator_id, context = {}) {
|
||||
let user_id;
|
||||
let country_id;
|
||||
if ('mockedUserId' in context) {
|
||||
if ("mockedUserId" in context) {
|
||||
// can be falsy to simulate not being logged in
|
||||
user_id = context.mockedUserId;
|
||||
} else {
|
||||
@@ -44,18 +49,24 @@ patch(MockServer.prototype, 'im_livechat/controllers/main', {
|
||||
}
|
||||
// don't use the anonymous name if the user is logged in
|
||||
if (user_id) {
|
||||
const user = this.getRecords('res.users', [['id', '=', user_id]])[0];
|
||||
const user = this.getRecords("res.users", [["id", "=", user_id]])[0];
|
||||
country_id = user.country_id;
|
||||
} else {
|
||||
// simulate geoip
|
||||
const countryCode = context.mockedCountryCode;
|
||||
const country = this.getRecords('res.country', [['code', '=', countryCode]])[0];
|
||||
const country = this.getRecords("res.country", [["code", "=", countryCode]])[0];
|
||||
if (country) {
|
||||
country_id = country.id;
|
||||
anonymous_name = anonymous_name + ' (' + country.name + ')';
|
||||
anonymous_name = anonymous_name + " (" + country.name + ")";
|
||||
}
|
||||
}
|
||||
return this._mockImLivechatChannel_openLivechatMailChannel(channel_id, anonymous_name, previous_operator_id, user_id, country_id);
|
||||
return this._mockImLivechatChannel_openLivechatMailChannel(
|
||||
channel_id,
|
||||
anonymous_name,
|
||||
previous_operator_id,
|
||||
user_id,
|
||||
country_id
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Simulates the `/im_livechat/notify_typing` route.
|
||||
@@ -66,9 +77,12 @@ patch(MockServer.prototype, 'im_livechat/controllers/main', {
|
||||
* @param {Object} [context={}]
|
||||
*/
|
||||
_mockRouteImLivechatNotifyTyping(uuid, is_typing, context = {}) {
|
||||
const [mailChannel] = this.getRecords('mail.channel', [['uuid', '=', uuid]]);
|
||||
const [mailChannel] = this.getRecords("mail.channel", [["uuid", "=", uuid]]);
|
||||
const partnerId = context.mockedPartnerId || this.currentPartnerId;
|
||||
const [memberOfCurrentUser] = this.getRecords('mail.channel.member', [['channel_id', '=', mailChannel.id], ['partner_id', '=', partnerId]]);
|
||||
const [memberOfCurrentUser] = this.getRecords("mail.channel.member", [
|
||||
["channel_id", "=", mailChannel.id],
|
||||
["partner_id", "=", partnerId],
|
||||
]);
|
||||
this._mockMailChannelMember_NotifyTyping([memberOfCurrentUser.id], is_typing);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { MockServer } from "@web/../tests/helpers/mock_server";
|
||||
|
||||
patch(MockServer.prototype, 'im_livechat/models/im_livechat_channel', {
|
||||
patch(MockServer.prototype, "im_livechat/models/im_livechat_channel", {
|
||||
/**
|
||||
* Simulates `_get_available_users` on `im_livechat.channel`.
|
||||
*
|
||||
@@ -12,9 +12,9 @@ patch(MockServer.prototype, 'im_livechat/models/im_livechat_channel', {
|
||||
* @returns {Object}
|
||||
*/
|
||||
_mockImLivechatChannel_getAvailableUsers(id) {
|
||||
const livechatChannel = this.getRecords('im_livechat.channel', [['id', '=', id]])[0];
|
||||
const users = this.getRecords('res.users', [['id', 'in', livechatChannel.user_ids]]);
|
||||
return users.filter(user => user.im_status === 'online');
|
||||
const livechatChannel = this.getRecords("im_livechat.channel", [["id", "=", id]])[0];
|
||||
const users = this.getRecords("res.users", [["id", "in", livechatChannel.user_ids]]);
|
||||
return users.filter((user) => user.im_status === "online");
|
||||
},
|
||||
/**
|
||||
* Simulates `_get_livechat_mail_channel_vals` on `im_livechat.channel`.
|
||||
@@ -23,16 +23,28 @@ patch(MockServer.prototype, 'im_livechat/models/im_livechat_channel', {
|
||||
* @param {integer} id
|
||||
* @returns {Object}
|
||||
*/
|
||||
_mockImLivechatChannel_getLivechatMailChannelVals(id, anonymous_name, operator, user_id, country_id) {
|
||||
_mockImLivechatChannel_getLivechatMailChannelVals(
|
||||
id,
|
||||
anonymous_name,
|
||||
operator,
|
||||
user_id,
|
||||
country_id
|
||||
) {
|
||||
// partner to add to the mail.channel
|
||||
const operator_partner_id = operator.partner_id;
|
||||
const membersToAdd = [[0, 0, {
|
||||
is_pinned: false,
|
||||
partner_id: operator_partner_id,
|
||||
}]];
|
||||
const membersToAdd = [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
{
|
||||
is_pinned: false,
|
||||
partner_id: operator_partner_id,
|
||||
},
|
||||
],
|
||||
];
|
||||
let visitor_user;
|
||||
if (user_id) {
|
||||
const visitor_user = this.getRecords('res.users', [['id', '=', user_id]])[0];
|
||||
const visitor_user = this.getRecords("res.users", [["id", "=", user_id]])[0];
|
||||
if (visitor_user && visitor_user.active && visitor_user !== operator) {
|
||||
// valid session user (not public)
|
||||
membersToAdd.push([0, 0, { partner_id: visitor_user.partner_id.id }]);
|
||||
@@ -45,14 +57,14 @@ patch(MockServer.prototype, 'im_livechat/models/im_livechat_channel', {
|
||||
operator.livechat_username ? operator.livechat_username : operator.name,
|
||||
];
|
||||
return {
|
||||
'channel_member_ids': membersToAdd,
|
||||
'livechat_active': true,
|
||||
'livechat_operator_id': operator_partner_id,
|
||||
'livechat_channel_id': id,
|
||||
'anonymous_name': user_id ? false : anonymous_name,
|
||||
'country_id': country_id,
|
||||
'channel_type': 'livechat',
|
||||
'name': membersName.join(' '),
|
||||
channel_member_ids: membersToAdd,
|
||||
livechat_active: true,
|
||||
livechat_operator_id: operator_partner_id,
|
||||
livechat_channel_id: id,
|
||||
anonymous_name: user_id ? false : anonymous_name,
|
||||
country_id: country_id,
|
||||
channel_type: "livechat",
|
||||
name: membersName.join(" "),
|
||||
};
|
||||
},
|
||||
/**
|
||||
@@ -78,11 +90,17 @@ patch(MockServer.prototype, 'im_livechat/models/im_livechat_channel', {
|
||||
* @param {integer} [country_id]
|
||||
* @returns {Object}
|
||||
*/
|
||||
_mockImLivechatChannel_openLivechatMailChannel(id, anonymous_name, previous_operator_id, user_id, country_id) {
|
||||
_mockImLivechatChannel_openLivechatMailChannel(
|
||||
id,
|
||||
anonymous_name,
|
||||
previous_operator_id,
|
||||
user_id,
|
||||
country_id
|
||||
) {
|
||||
let operator;
|
||||
if (previous_operator_id) {
|
||||
const availableUsers = this._mockImLivechatChannel_getAvailableUsers(id);
|
||||
operator = availableUsers.find(user => user.partner_id === previous_operator_id);
|
||||
operator = availableUsers.find((user) => user.partner_id === previous_operator_id);
|
||||
}
|
||||
if (!operator) {
|
||||
operator = this._mockImLivechatChannel_getRandomOperator(id);
|
||||
@@ -92,8 +110,14 @@ patch(MockServer.prototype, 'im_livechat/models/im_livechat_channel', {
|
||||
return false;
|
||||
}
|
||||
// create the session, and add the link with the given channel
|
||||
const mailChannelVals = this._mockImLivechatChannel_getLivechatMailChannelVals(id, anonymous_name, operator, user_id, country_id);
|
||||
const mailChannelId = this.pyEnv['mail.channel'].create(mailChannelVals);
|
||||
const mailChannelVals = this._mockImLivechatChannel_getLivechatMailChannelVals(
|
||||
id,
|
||||
anonymous_name,
|
||||
operator,
|
||||
user_id,
|
||||
country_id
|
||||
);
|
||||
const mailChannelId = this.pyEnv["mail.channel"].create(mailChannelVals);
|
||||
this._mockMailChannel_broadcast([mailChannelId], [operator.partner_id]);
|
||||
return this._mockMailChannelChannelInfo([mailChannelId])[0];
|
||||
},
|
||||
|
||||
@@ -1,26 +1,31 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import '@mail/../tests/helpers/mock_server/models/mail_channel'; // ensure mail overrides are applied first
|
||||
import "@mail/../tests/helpers/mock_server/models/mail_channel"; // ensure mail overrides are applied first
|
||||
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { MockServer } from "@web/../tests/helpers/mock_server";
|
||||
|
||||
patch(MockServer.prototype, 'im_livechat/models/mail_channel', {
|
||||
patch(MockServer.prototype, "im_livechat/models/mail_channel", {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_mockMailChannelChannelInfo(ids) {
|
||||
const channelInfos = this._super(...arguments);
|
||||
for (const channelInfo of channelInfos) {
|
||||
const channel = this.getRecords('mail.channel', [['id', '=', channelInfo.id]])[0];
|
||||
channelInfo['channel']['anonymous_name'] = channel.anonymous_name;
|
||||
const channel = this.getRecords("mail.channel", [["id", "=", channelInfo.id]])[0];
|
||||
channelInfo["channel"]["anonymous_name"] = channel.anonymous_name;
|
||||
// add the last message date
|
||||
if (channel.channel_type === 'livechat') {
|
||||
if (channel.channel_type === "livechat") {
|
||||
// add the operator id
|
||||
if (channel.livechat_operator_id) {
|
||||
const operator = this.getRecords('res.partner', [['id', '=', channel.livechat_operator_id]])[0];
|
||||
const operator = this.getRecords("res.partner", [
|
||||
["id", "=", channel.livechat_operator_id],
|
||||
])[0];
|
||||
// livechat_username ignored for simplicity
|
||||
channelInfo.operator_pid = [operator.id, operator.display_name.replace(',', '')];
|
||||
channelInfo.operator_pid = [
|
||||
operator.id,
|
||||
operator.display_name.replace(",", ""),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,39 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import '@mail/../tests/helpers/mock_server/models/mail_channel_member'; // ensure mail overrides are applied first
|
||||
import "@mail/../tests/helpers/mock_server/models/mail_channel_member"; // ensure mail overrides are applied first
|
||||
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { MockServer } from "@web/../tests/helpers/mock_server";
|
||||
|
||||
patch(MockServer.prototype, 'im_livechat/models/mail_channel_member', {
|
||||
patch(MockServer.prototype, "im_livechat/models/mail_channel_member", {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_mockMailChannelMember_GetPartnerData(ids) {
|
||||
const [member] = this.getRecords('mail.channel.member', [['id', 'in', ids]]);
|
||||
const [channel] = this.getRecords('mail.channel', [['id', '=', member.channel_id]]);
|
||||
const [partner] = this.getRecords('res.partner', [['id', '=', member.partner_id]], { active_test: false });
|
||||
if (channel.channel_type === 'livechat') {
|
||||
const [member] = this.getRecords("mail.channel.member", [["id", "in", ids]]);
|
||||
const [channel] = this.getRecords("mail.channel", [["id", "=", member.channel_id]]);
|
||||
const [partner] = this.getRecords("res.partner", [["id", "=", member.partner_id]], {
|
||||
active_test: false,
|
||||
});
|
||||
if (channel.channel_type === "livechat") {
|
||||
const data = {
|
||||
'id': partner.id,
|
||||
'is_public': partner.is_public,
|
||||
id: partner.id,
|
||||
is_public: partner.is_public,
|
||||
};
|
||||
if (partner.user_livechat_username) {
|
||||
data['user_livechat_username'] = partner.user_livechat_username;
|
||||
data["user_livechat_username"] = partner.user_livechat_username;
|
||||
} else {
|
||||
data['name'] = partner.name;
|
||||
data["name"] = partner.name;
|
||||
}
|
||||
if (!partner.is_public) {
|
||||
const [country] = this.getRecords('res.country', [['id', '=', partner.country_id]]);
|
||||
data['country'] = country
|
||||
const [country] = this.getRecords("res.country", [["id", "=", partner.country_id]]);
|
||||
data["country"] = country
|
||||
? {
|
||||
'code': country.code,
|
||||
'id': country.id,
|
||||
'name': country.name,
|
||||
}
|
||||
: [['clear']];
|
||||
code: country.code,
|
||||
id: country.id,
|
||||
name: country.name,
|
||||
}
|
||||
: [["clear"]];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import '@mail/../tests/helpers/mock_server/models/res_partner'; // ensure mail overrides are applied first
|
||||
import "@mail/../tests/helpers/mock_server/models/res_partner"; // ensure mail overrides are applied first
|
||||
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { MockServer } from "@web/../tests/helpers/mock_server";
|
||||
|
||||
patch(MockServer.prototype, 'im_livechat/models/res_partner', {
|
||||
patch(MockServer.prototype, "im_livechat/models/res_partner", {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_mockResPartner_GetChannelsAsMember(ids) {
|
||||
const partner = this.getRecords('res.partner', [['id', 'in', ids]])[0];
|
||||
const members = this.getRecords('mail.channel.member', [['partner_id', '=', partner.id], ['is_pinned', '=', true]]);
|
||||
const livechats = this.getRecords('mail.channel', [
|
||||
['channel_type', '=', 'livechat'],
|
||||
['channel_member_ids', 'in', members.map(member => member.id)],
|
||||
const partner = this.getRecords("res.partner", [["id", "in", ids]])[0];
|
||||
const members = this.getRecords("mail.channel.member", [
|
||||
["partner_id", "=", partner.id],
|
||||
["is_pinned", "=", true],
|
||||
]);
|
||||
return [
|
||||
...this._super(ids),
|
||||
...livechats,
|
||||
];
|
||||
const livechats = this.getRecords("mail.channel", [
|
||||
["channel_type", "=", "livechat"],
|
||||
["channel_member_ids", "in", members.map((member) => member.id)],
|
||||
]);
|
||||
return [...this._super(ids), ...livechats];
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { addModelNamesToFetch, insertModelFields } from '@bus/../tests/helpers/model_definitions_helpers';
|
||||
import {
|
||||
addModelNamesToFetch,
|
||||
insertModelFields,
|
||||
} from "@bus/../tests/helpers/model_definitions_helpers";
|
||||
|
||||
addModelNamesToFetch(['im_livechat.channel']);
|
||||
insertModelFields('res.users.settings', {
|
||||
addModelNamesToFetch(["im_livechat.channel"]);
|
||||
insertModelFields("res.users.settings", {
|
||||
is_discuss_sidebar_category_livechat_open: { default: true },
|
||||
});
|
||||
|
||||
+54
-45
@@ -1,52 +1,61 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
afterNextRender,
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
import { afterNextRender, start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('chat_window_manager', {}, function () {
|
||||
QUnit.module('chat_window_manager_tests.js');
|
||||
QUnit.module("im_livechat", {}, function () {
|
||||
QUnit.module("components", {}, function () {
|
||||
QUnit.module("chat_window_manager", {}, function () {
|
||||
QUnit.module("chat_window_manager_tests.js");
|
||||
|
||||
QUnit.test('closing a chat window with no message from admin side unpins it', async function (assert) {
|
||||
assert.expect(1);
|
||||
QUnit.test(
|
||||
"closing a chat window with no message from admin side unpins it",
|
||||
async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const resPartnerId1 = pyEnv['res.partner'].create({ name: "Demo" });
|
||||
pyEnv['res.users'].create({ partner_id: resPartnerId1 });
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create(
|
||||
{
|
||||
channel_member_ids: [
|
||||
[0, 0, {
|
||||
is_pinned: true,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}],
|
||||
[0, 0, { partner_id: resPartnerId1 }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
uuid: 'channel-10-uuid',
|
||||
},
|
||||
);
|
||||
const { messaging } = await start();
|
||||
const pyEnv = await startServer();
|
||||
const resPartnerId1 = pyEnv["res.partner"].create({ name: "Demo" });
|
||||
pyEnv["res.users"].create({ partner_id: resPartnerId1 });
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
channel_member_ids: [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
{
|
||||
is_pinned: true,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
],
|
||||
[0, 0, { partner_id: resPartnerId1 }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
uuid: "channel-10-uuid",
|
||||
});
|
||||
const { messaging } = await start();
|
||||
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await afterNextRender(() => document.querySelector(`.o_NotificationListView_preview`).click());
|
||||
await afterNextRender(() => document.querySelector(`.o_ChatWindowHeaderView_commandClose`).click());
|
||||
const channels = await messaging.rpc({
|
||||
model: 'mail.channel',
|
||||
method: 'channel_info',
|
||||
args: [mailChannelId1],
|
||||
}, { shadow: true });
|
||||
assert.strictEqual(
|
||||
channels[0].is_pinned,
|
||||
false,
|
||||
'Livechat channel should not be pinned',
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_MessagingMenu_toggler`).click()
|
||||
);
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_NotificationListView_preview`).click()
|
||||
);
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_ChatWindowHeaderView_commandClose`).click()
|
||||
);
|
||||
const channels = await messaging.rpc(
|
||||
{
|
||||
model: "mail.channel",
|
||||
method: "channel_info",
|
||||
args: [mailChannelId1],
|
||||
},
|
||||
{ shadow: true }
|
||||
);
|
||||
assert.strictEqual(
|
||||
channels[0].is_pinned,
|
||||
false,
|
||||
"Livechat channel should not be pinned"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,50 +1,52 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { start, startServer } from '@mail/../tests/helpers/test_utils';
|
||||
import { start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('composer_tests.js');
|
||||
QUnit.module("im_livechat", {}, function () {
|
||||
QUnit.module("components", {}, function () {
|
||||
QUnit.module("composer_tests.js");
|
||||
|
||||
QUnit.test('livechat: no add attachment button', async function (assert) {
|
||||
// Attachments are not yet supported in livechat, especially from livechat
|
||||
// visitor PoV. This may likely change in the future with task-2029065.
|
||||
assert.expect(2);
|
||||
QUnit.test("livechat: no add attachment button", async function (assert) {
|
||||
// Attachments are not yet supported in livechat, especially from livechat
|
||||
// visitor PoV. This may likely change in the future with task-2029065.
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const livechatId = pyEnv['mail.channel'].create({ channel_type: 'livechat' });
|
||||
const { openDiscuss } = await start({
|
||||
discuss: {
|
||||
context: { active_id: livechatId },
|
||||
},
|
||||
const pyEnv = await startServer();
|
||||
const livechatId = pyEnv["mail.channel"].create({ channel_type: "livechat" });
|
||||
const { openDiscuss } = await start({
|
||||
discuss: {
|
||||
context: { active_id: livechatId },
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsOnce(document.body, ".o_ComposerView", "should have a composer");
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
".o_ComposerView_buttonAttachment",
|
||||
"composer linked to livechat should not have a 'Add attachment' button"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"livechat: disable attachment upload via drag and drop",
|
||||
async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const livechatId = pyEnv["mail.channel"].create({ channel_type: "livechat" });
|
||||
const { openDiscuss } = await start({
|
||||
discuss: {
|
||||
context: { active_id: livechatId },
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsOnce(document.body, ".o_ComposerView", "should have a composer");
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
".o_ComposerView_dropZone",
|
||||
"composer linked to livechat should not have a dropzone"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsOnce(document.body, '.o_ComposerView', "should have a composer");
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_ComposerView_buttonAttachment',
|
||||
"composer linked to livechat should not have a 'Add attachment' button"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat: disable attachment upload via drag and drop', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const livechatId = pyEnv['mail.channel'].create({ channel_type: 'livechat' });
|
||||
const { openDiscuss } = await start({
|
||||
discuss: {
|
||||
context: { active_id: livechatId },
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsOnce(document.body, '.o_ComposerView', "should have a composer");
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_ComposerView_dropZone',
|
||||
"composer linked to livechat should not have a dropzone"
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
+67
-63
@@ -1,77 +1,81 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
import { start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('discuss_sidebar_category_item_tests.js');
|
||||
QUnit.module("im_livechat", {}, function () {
|
||||
QUnit.module("components", {}, function () {
|
||||
QUnit.module("discuss_sidebar_category_item_tests.js");
|
||||
|
||||
QUnit.test('livechat - avatar: should have a smiley face avatar for an anonymous livechat item', async function (assert) {
|
||||
assert.expect(2);
|
||||
QUnit.test(
|
||||
"livechat - avatar: should have a smiley face avatar for an anonymous livechat item",
|
||||
async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
const livechatItem = document.querySelector(`
|
||||
const livechatItem = document.querySelector(`
|
||||
.o_DiscussSidebarCategoryItem[data-channel-id="${mailChannelId1}"]
|
||||
`);
|
||||
assert.containsOnce(
|
||||
livechatItem,
|
||||
`.o_DiscussSidebarCategoryItem_image`,
|
||||
"should have an avatar"
|
||||
);
|
||||
assert.strictEqual(
|
||||
livechatItem.querySelector(`:scope .o_DiscussSidebarCategoryItem_image`).dataset.src,
|
||||
'/mail/static/src/img/smiley/avatar.jpg',
|
||||
'should have the smiley face as the avatar for anonymous users'
|
||||
);
|
||||
});
|
||||
assert.containsOnce(
|
||||
livechatItem,
|
||||
`.o_DiscussSidebarCategoryItem_image`,
|
||||
"should have an avatar"
|
||||
);
|
||||
assert.strictEqual(
|
||||
livechatItem.querySelector(`:scope .o_DiscussSidebarCategoryItem_image`).dataset
|
||||
.src,
|
||||
"/mail/static/src/img/smiley/avatar.jpg",
|
||||
"should have the smiley face as the avatar for anonymous users"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test('livechat - avatar: should have a partner profile picture for a livechat item linked with a partner', async function (assert) {
|
||||
assert.expect(2);
|
||||
QUnit.test(
|
||||
"livechat - avatar: should have a partner profile picture for a livechat item linked with a partner",
|
||||
async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const resPartnerId1 = pyEnv['res.partner'].create({
|
||||
name: "Jean",
|
||||
});
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: resPartnerId1 }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const pyEnv = await startServer();
|
||||
const resPartnerId1 = pyEnv["res.partner"].create({
|
||||
name: "Jean",
|
||||
});
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: resPartnerId1 }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
const livechatItem = document.querySelector(`
|
||||
const livechatItem = document.querySelector(`
|
||||
.o_DiscussSidebarCategoryItem[data-channel-id="${mailChannelId1}"]
|
||||
`);
|
||||
assert.containsOnce(
|
||||
livechatItem,
|
||||
`.o_DiscussSidebarCategoryItem_image`,
|
||||
"should have an avatar"
|
||||
);
|
||||
assert.strictEqual(
|
||||
livechatItem.querySelector(`:scope .o_DiscussSidebarCategoryItem_image`).dataset.src,
|
||||
`/web/image/res.partner/${resPartnerId1}/avatar_128`,
|
||||
'should have the partner profile picture as the avatar for partners'
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
assert.containsOnce(
|
||||
livechatItem,
|
||||
`.o_DiscussSidebarCategoryItem_image`,
|
||||
"should have an avatar"
|
||||
);
|
||||
assert.strictEqual(
|
||||
livechatItem.querySelector(`:scope .o_DiscussSidebarCategoryItem_image`).dataset
|
||||
.src,
|
||||
`/web/image/res.partner/${resPartnerId1}/avatar_128`,
|
||||
"should have the partner profile picture as the avatar for partners"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+450
-396
@@ -1,453 +1,507 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
afterNextRender,
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
import { afterNextRender, start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('discuss_sidebar_category_tests.js');
|
||||
QUnit.module("im_livechat", {}, function () {
|
||||
QUnit.module("components", {}, function () {
|
||||
QUnit.module("discuss_sidebar_category_tests.js");
|
||||
|
||||
QUnit.test('livechat - counter: should not have a counter if the category is unfolded and without unread messages', async function (assert) {
|
||||
assert.expect(1);
|
||||
QUnit.test(
|
||||
"livechat - counter: should not have a counter if the category is unfolded and without unread messages",
|
||||
async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategory_counter`,
|
||||
"should not have a counter if the category is unfolded and without unread messages",
|
||||
);
|
||||
});
|
||||
const pyEnv = await startServer();
|
||||
pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategory_counter`,
|
||||
"should not have a counter if the category is unfolded and without unread messages"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test('livechat - counter: should not have a counter if the category is unfolded and with unread messages', async function (assert) {
|
||||
assert.expect(1);
|
||||
QUnit.test(
|
||||
"livechat - counter: should not have a counter if the category is unfolded and with unread messages",
|
||||
async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, {
|
||||
message_unread_counter: 10,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategory_counter`,
|
||||
"should not have a counter if the category is unfolded and with unread messages",
|
||||
);
|
||||
});
|
||||
const pyEnv = await startServer();
|
||||
pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
{
|
||||
message_unread_counter: 10,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategory_counter`,
|
||||
"should not have a counter if the category is unfolded and with unread messages"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test('livechat - counter: should not have a counter if category is folded and without unread messages', async function (assert) {
|
||||
assert.expect(1);
|
||||
QUnit.test(
|
||||
"livechat - counter: should not have a counter if category is folded and without unread messages",
|
||||
async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
pyEnv['res.users.settings'].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: false,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const pyEnv = await startServer();
|
||||
pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
pyEnv["res.users.settings"].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: false,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategory_counter`,
|
||||
"should not have a counter if the category is folded and without unread messages"
|
||||
);
|
||||
});
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategory_counter`,
|
||||
"should not have a counter if the category is folded and without unread messages"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test('livechat - counter: should have correct value of unread threads if category is folded and with unread messages', async function (assert) {
|
||||
assert.expect(1);
|
||||
QUnit.test(
|
||||
"livechat - counter: should have correct value of unread threads if category is folded and with unread messages",
|
||||
async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, {
|
||||
message_unread_counter: 10,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
pyEnv['res.users.settings'].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: false,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const pyEnv = await startServer();
|
||||
pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
{
|
||||
message_unread_counter: 10,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
pyEnv["res.users.settings"].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: false,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.strictEqual(
|
||||
document.querySelector(`.o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategory_counter`).textContent,
|
||||
"1",
|
||||
"should have correct value of unread threads if category is folded and with unread messages"
|
||||
);
|
||||
});
|
||||
assert.strictEqual(
|
||||
document.querySelector(
|
||||
`.o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategory_counter`
|
||||
).textContent,
|
||||
"1",
|
||||
"should have correct value of unread threads if category is folded and with unread messages"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test('livechat - states: close manually by clicking the title', async function (assert) {
|
||||
assert.expect(2);
|
||||
QUnit.test(
|
||||
"livechat - states: close manually by clicking the title",
|
||||
async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
pyEnv['res.users.settings'].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: true,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
pyEnv["res.users.settings"].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: true,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
|
||||
// fold the livechat category
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_DiscussSidebarCategory[data-category-local-id="${
|
||||
messaging.discuss.categoryLivechat.localId}"]
|
||||
// fold the livechat category
|
||||
await afterNextRender(() =>
|
||||
document
|
||||
.querySelector(
|
||||
`.o_DiscussSidebarCategory[data-category-local-id="${messaging.discuss.categoryLivechat.localId}"]
|
||||
.o_DiscussSidebarCategory_title
|
||||
`).click()
|
||||
);
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"Category livechat should be closed and the content should be invisible"
|
||||
);
|
||||
});
|
||||
`
|
||||
)
|
||||
.click()
|
||||
);
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"Category livechat should be closed and the content should be invisible"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test('livechat - states: open manually by clicking the title', async function (assert) {
|
||||
assert.expect(2);
|
||||
QUnit.test(
|
||||
"livechat - states: open manually by clicking the title",
|
||||
async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
pyEnv['res.users.settings'].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: false,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
pyEnv["res.users.settings"].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: false,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
|
||||
// open the livechat category
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_DiscussSidebarCategory[data-category-local-id="${
|
||||
messaging.discuss.categoryLivechat.localId}"]
|
||||
// open the livechat category
|
||||
await afterNextRender(() =>
|
||||
document
|
||||
.querySelector(
|
||||
`.o_DiscussSidebarCategory[data-category-local-id="${messaging.discuss.categoryLivechat.localId}"]
|
||||
.o_DiscussSidebarCategory_title
|
||||
`).click()
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"Category livechat should be open and the content should be visible"
|
||||
);
|
||||
});
|
||||
`
|
||||
)
|
||||
.click()
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"Category livechat should be open and the content should be visible"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test('livechat - states: close should update the value on the server', async function (assert) {
|
||||
assert.expect(2);
|
||||
QUnit.test(
|
||||
"livechat - states: close should update the value on the server",
|
||||
async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
pyEnv['res.users.settings'].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: true,
|
||||
});
|
||||
const currentUserId = pyEnv.currentUserId;
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const pyEnv = await startServer();
|
||||
pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
pyEnv["res.users.settings"].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: true,
|
||||
});
|
||||
const currentUserId = pyEnv.currentUserId;
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
const initalSettings = await messaging.rpc({
|
||||
model: 'res.users.settings',
|
||||
method: '_find_or_create_for_user',
|
||||
args: [[currentUserId]],
|
||||
});
|
||||
assert.strictEqual(
|
||||
initalSettings.is_discuss_sidebar_category_livechat_open,
|
||||
true,
|
||||
"the value in server side should be true"
|
||||
);
|
||||
const initalSettings = await messaging.rpc({
|
||||
model: "res.users.settings",
|
||||
method: "_find_or_create_for_user",
|
||||
args: [[currentUserId]],
|
||||
});
|
||||
assert.strictEqual(
|
||||
initalSettings.is_discuss_sidebar_category_livechat_open,
|
||||
true,
|
||||
"the value in server side should be true"
|
||||
);
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_DiscussSidebarCategory[data-category-local-id="${
|
||||
messaging.discuss.categoryLivechat.localId}"]
|
||||
await afterNextRender(() =>
|
||||
document
|
||||
.querySelector(
|
||||
`.o_DiscussSidebarCategory[data-category-local-id="${messaging.discuss.categoryLivechat.localId}"]
|
||||
.o_DiscussSidebarCategory_title
|
||||
`).click()
|
||||
);
|
||||
const newSettings = await messaging.rpc({
|
||||
model: 'res.users.settings',
|
||||
method: '_find_or_create_for_user',
|
||||
args: [[currentUserId]],
|
||||
});
|
||||
assert.strictEqual(
|
||||
newSettings.is_discuss_sidebar_category_livechat_open,
|
||||
false,
|
||||
"the value in server side should be false"
|
||||
);
|
||||
});
|
||||
`
|
||||
)
|
||||
.click()
|
||||
);
|
||||
const newSettings = await messaging.rpc({
|
||||
model: "res.users.settings",
|
||||
method: "_find_or_create_for_user",
|
||||
args: [[currentUserId]],
|
||||
});
|
||||
assert.strictEqual(
|
||||
newSettings.is_discuss_sidebar_category_livechat_open,
|
||||
false,
|
||||
"the value in server side should be false"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test('livechat - states: open should update the value on the server', async function (assert) {
|
||||
assert.expect(2);
|
||||
QUnit.test(
|
||||
"livechat - states: open should update the value on the server",
|
||||
async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
pyEnv['res.users.settings'].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: false,
|
||||
});
|
||||
const currentUserId = pyEnv.currentUserId;
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const pyEnv = await startServer();
|
||||
pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
pyEnv["res.users.settings"].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: false,
|
||||
});
|
||||
const currentUserId = pyEnv.currentUserId;
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
const initalSettings = await messaging.rpc({
|
||||
model: 'res.users.settings',
|
||||
method: '_find_or_create_for_user',
|
||||
args: [[currentUserId]],
|
||||
});
|
||||
assert.strictEqual(
|
||||
initalSettings.is_discuss_sidebar_category_livechat_open,
|
||||
false,
|
||||
"the value in server side should be false"
|
||||
);
|
||||
const initalSettings = await messaging.rpc({
|
||||
model: "res.users.settings",
|
||||
method: "_find_or_create_for_user",
|
||||
args: [[currentUserId]],
|
||||
});
|
||||
assert.strictEqual(
|
||||
initalSettings.is_discuss_sidebar_category_livechat_open,
|
||||
false,
|
||||
"the value in server side should be false"
|
||||
);
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_DiscussSidebarCategory[data-category-local-id="${
|
||||
messaging.discuss.categoryLivechat.localId}"]
|
||||
await afterNextRender(() =>
|
||||
document
|
||||
.querySelector(
|
||||
`.o_DiscussSidebarCategory[data-category-local-id="${messaging.discuss.categoryLivechat.localId}"]
|
||||
.o_DiscussSidebarCategory_title
|
||||
`).click()
|
||||
);
|
||||
const newSettings = await messaging.rpc({
|
||||
model: 'res.users.settings',
|
||||
method: '_find_or_create_for_user',
|
||||
args: [[currentUserId]],
|
||||
});
|
||||
assert.strictEqual(
|
||||
newSettings.is_discuss_sidebar_category_livechat_open,
|
||||
true,
|
||||
"the value in server side should be true"
|
||||
);
|
||||
});
|
||||
`
|
||||
)
|
||||
.click()
|
||||
);
|
||||
const newSettings = await messaging.rpc({
|
||||
model: "res.users.settings",
|
||||
method: "_find_or_create_for_user",
|
||||
args: [[currentUserId]],
|
||||
});
|
||||
assert.strictEqual(
|
||||
newSettings.is_discuss_sidebar_category_livechat_open,
|
||||
true,
|
||||
"the value in server side should be true"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test('livechat - states: close from the bus', async function (assert) {
|
||||
assert.expect(2);
|
||||
QUnit.test("livechat - states: close from the bus", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const resUsersSettingsId1 = pyEnv['res.users.settings'].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: true,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const resUsersSettingsId1 = pyEnv["res.users.settings"].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: true,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
|
||||
await afterNextRender(() => {
|
||||
pyEnv['bus.bus']._sendone(pyEnv.currentPartner, 'mail.record/insert', {
|
||||
'res.users.settings': {
|
||||
'id': resUsersSettingsId1,
|
||||
'is_discuss_sidebar_category_livechat_open': false,
|
||||
},
|
||||
await afterNextRender(() => {
|
||||
pyEnv["bus.bus"]._sendone(pyEnv.currentPartner, "mail.record/insert", {
|
||||
"res.users.settings": {
|
||||
id: resUsersSettingsId1,
|
||||
is_discuss_sidebar_category_livechat_open: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"Category livechat should be closed and the content should be invisible"
|
||||
);
|
||||
});
|
||||
});
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"Category livechat should be closed and the content should be invisible"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat - states: open from the bus', async function (assert) {
|
||||
assert.expect(2);
|
||||
QUnit.test("livechat - states: open from the bus", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const resUsersSettingsId1 = pyEnv['res.users.settings'].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: false,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const resUsersSettingsId1 = pyEnv["res.users.settings"].create({
|
||||
user_id: pyEnv.currentUserId,
|
||||
is_discuss_sidebar_category_livechat_open: false,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
|
||||
await afterNextRender(() => {
|
||||
pyEnv['bus.bus']._sendone(pyEnv.currentPartner, 'mail.record/insert', {
|
||||
'res.users.settings': {
|
||||
id: resUsersSettingsId1,
|
||||
'is_discuss_sidebar_category_livechat_open': true,
|
||||
},
|
||||
await afterNextRender(() => {
|
||||
pyEnv["bus.bus"]._sendone(pyEnv.currentPartner, "mail.record/insert", {
|
||||
"res.users.settings": {
|
||||
id: resUsersSettingsId1,
|
||||
is_discuss_sidebar_category_livechat_open: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"Category livechat should be open and the content should be visible"
|
||||
);
|
||||
});
|
||||
});
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"Category livechat should be open and the content should be visible"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"livechat - states: category item should be invisible if the category is closed",
|
||||
async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
QUnit.test('livechat - states: category item should be invisible if the category is closed', async function (assert) {
|
||||
assert.expect(2);
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_DiscussSidebarCategory[data-category-local-id="${
|
||||
messaging.discuss.categoryLivechat.localId}"]
|
||||
await afterNextRender(() =>
|
||||
document
|
||||
.querySelector(
|
||||
`.o_DiscussSidebarCategory[data-category-local-id="${messaging.discuss.categoryLivechat.localId}"]
|
||||
.o_DiscussSidebarCategory_title
|
||||
`).click()
|
||||
);
|
||||
`
|
||||
)
|
||||
.click()
|
||||
);
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"inactive item should be invisible if the category is folded"
|
||||
);
|
||||
});
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"inactive item should be invisible if the category is folded"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test('livechat - states: the active category item should be visble even if the category is closed', async function (assert) {
|
||||
assert.expect(3);
|
||||
QUnit.test(
|
||||
"livechat - states: the active category item should be visble even if the category is closed",
|
||||
async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
|
||||
const livechat = document.querySelector(`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`);
|
||||
await afterNextRender(() => {
|
||||
livechat.click();
|
||||
});
|
||||
assert.ok(livechat.classList.contains('o-active'));
|
||||
const livechat = document.querySelector(
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`
|
||||
);
|
||||
await afterNextRender(() => {
|
||||
livechat.click();
|
||||
});
|
||||
assert.ok(livechat.classList.contains("o-active"));
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_DiscussSidebarCategory[data-category-local-id="${
|
||||
messaging.discuss.categoryLivechat.localId}"]
|
||||
await afterNextRender(() =>
|
||||
document
|
||||
.querySelector(
|
||||
`.o_DiscussSidebarCategory[data-category-local-id="${messaging.discuss.categoryLivechat.localId}"]
|
||||
.o_DiscussSidebarCategory_title
|
||||
`).click()
|
||||
);
|
||||
`
|
||||
)
|
||||
.click()
|
||||
);
|
||||
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
'the active livechat item should remain open even if the category is folded'
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_DiscussSidebarCategory_item[data-channel-id="${mailChannelId1}"]`,
|
||||
"the active livechat item should remain open even if the category is folded"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,431 +5,449 @@ import {
|
||||
nextAnimationFrame,
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
} from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
import { datetime_to_str } from 'web.time';
|
||||
import { datetime_to_str } from "web.time";
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('discuss_tests.js');
|
||||
QUnit.module("im_livechat", {}, function () {
|
||||
QUnit.module("components", {}, function () {
|
||||
QUnit.module("discuss_tests.js");
|
||||
|
||||
QUnit.test('livechat in the sidebar: basic rendering', async function (assert) {
|
||||
assert.expect(5);
|
||||
QUnit.test("livechat in the sidebar: basic rendering", async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsOnce(document.body, '.o_DiscussView_sidebar',
|
||||
"should have a sidebar section"
|
||||
);
|
||||
const groupLivechat = document.querySelector('.o_DiscussSidebarView_categoryLivechat');
|
||||
assert.ok(groupLivechat,
|
||||
"should have a channel group livechat"
|
||||
);
|
||||
const titleText = groupLivechat.querySelector('.o_DiscussSidebarCategory_titleText');
|
||||
assert.strictEqual(
|
||||
titleText.textContent.trim(),
|
||||
"Livechat",
|
||||
"should have a channel group named 'Livechat'"
|
||||
);
|
||||
const livechat = groupLivechat.querySelector(`
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
".o_DiscussView_sidebar",
|
||||
"should have a sidebar section"
|
||||
);
|
||||
const groupLivechat = document.querySelector(".o_DiscussSidebarView_categoryLivechat");
|
||||
assert.ok(groupLivechat, "should have a channel group livechat");
|
||||
const titleText = groupLivechat.querySelector(".o_DiscussSidebarCategory_titleText");
|
||||
assert.strictEqual(
|
||||
titleText.textContent.trim(),
|
||||
"Livechat",
|
||||
"should have a channel group named 'Livechat'"
|
||||
);
|
||||
const livechat = groupLivechat.querySelector(`
|
||||
.o_DiscussSidebarCategoryItem[data-channel-id="${mailChannelId1}"]
|
||||
`);
|
||||
assert.ok(
|
||||
livechat,
|
||||
"should have a livechat in sidebar"
|
||||
);
|
||||
assert.strictEqual(
|
||||
livechat.textContent,
|
||||
"Visitor 11",
|
||||
"should have 'Visitor 11' as livechat name"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechat in the sidebar: existing user with country', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const resCountryId1 = pyEnv['res.country'].create({
|
||||
code: 'be',
|
||||
name: "Belgium",
|
||||
});
|
||||
const resPartnerId1 = pyEnv['res.partner'].create({
|
||||
country_id: resCountryId1,
|
||||
name: "Jean",
|
||||
});
|
||||
pyEnv['mail.channel'].create({
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: resPartnerId1 }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_DiscussSidebarView_categoryLivechat',
|
||||
"should have a channel group livechat in the side bar"
|
||||
);
|
||||
const livechat = document.querySelector('.o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategoryItem');
|
||||
assert.ok(
|
||||
livechat,
|
||||
"should have a livechat in sidebar"
|
||||
);
|
||||
assert.strictEqual(
|
||||
livechat.textContent,
|
||||
"Jean (Belgium)",
|
||||
"should have user name and country as livechat name"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('do not add livechat in the sidebar on visitor opening his chat', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['res.users'].write([pyEnv.currentUserId], { im_status: 'online' });
|
||||
const imLivechatChannelId1 = pyEnv['im_livechat.channel'].create({
|
||||
user_ids: [pyEnv.currentUserId],
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_DiscussSidebarView_categoryLivechat',
|
||||
"should not have any livechat in the sidebar initially"
|
||||
);
|
||||
|
||||
// simulate livechat visitor opening his chat
|
||||
await messaging.rpc({
|
||||
route: '/im_livechat/get_session',
|
||||
params: {
|
||||
context: {
|
||||
mockedUserId: false,
|
||||
},
|
||||
channel_id: imLivechatChannelId1,
|
||||
},
|
||||
});
|
||||
await nextAnimationFrame();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_DiscussSidebarView_categoryLivechat',
|
||||
"should still not have any livechat in the sidebar after visitor opened his chat"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('do not add livechat in the sidebar on visitor typing', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['res.users'].write([pyEnv.currentUserId], { im_status: 'online' });
|
||||
const imLivechatChannelId1 = pyEnv['im_livechat.channel'].create({
|
||||
user_ids: [pyEnv.currentUserId],
|
||||
});
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
channel_member_ids: [
|
||||
[0, 0, {
|
||||
is_pinned: false,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_channel_id: imLivechatChannelId1,
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_DiscussSidebarView_categoryLivechat',
|
||||
"should not have any livechat in the sidebar initially"
|
||||
);
|
||||
|
||||
// simulate livechat visitor typing
|
||||
const channel = pyEnv['mail.channel'].searchRead([['id', '=', mailChannelId1]])[0];
|
||||
await messaging.rpc({
|
||||
route: '/im_livechat/notify_typing',
|
||||
params: {
|
||||
context: {
|
||||
mockedPartnerId: pyEnv.publicPartnerId,
|
||||
},
|
||||
is_typing: true,
|
||||
uuid: channel.uuid,
|
||||
},
|
||||
});
|
||||
await nextAnimationFrame();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_DiscussSidebarView_categoryLivechat',
|
||||
"should still not have any livechat in the sidebar after visitor started typing"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('add livechat in the sidebar on visitor sending first message', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv['res.users'].write([pyEnv.currentUserId], { im_status: 'online' });
|
||||
const resCountryId1 = pyEnv['res.country'].create({
|
||||
code: 'be',
|
||||
name: "Belgium",
|
||||
});
|
||||
const imLivechatChannelId1 = pyEnv['im_livechat.channel'].create({
|
||||
user_ids: [pyEnv.currentUserId],
|
||||
});
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor (Belgium)",
|
||||
channel_member_ids: [
|
||||
[0, 0, {
|
||||
is_pinned: false,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
country_id: resCountryId1,
|
||||
livechat_channel_id: imLivechatChannelId1,
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_DiscussSidebarView_categoryLivechat',
|
||||
"should not have any livechat in the sidebar initially"
|
||||
);
|
||||
|
||||
// simulate livechat visitor sending a message
|
||||
const channel = pyEnv['mail.channel'].searchRead([['id', '=', mailChannelId1]])[0];
|
||||
await afterNextRender(async () => messaging.rpc({
|
||||
route: '/mail/chat_post',
|
||||
params: {
|
||||
context: {
|
||||
mockedUserId: false,
|
||||
},
|
||||
uuid: channel.uuid,
|
||||
message_content: "new message",
|
||||
},
|
||||
}));
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_DiscussSidebarView_categoryLivechat',
|
||||
"should have a channel group livechat in the side bar after receiving first message"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategoryItem',
|
||||
"should have a livechat in the sidebar after receiving first message"
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.querySelector('.o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategoryItem .o_DiscussSidebarCategoryItem_name').textContent,
|
||||
"Visitor (Belgium)",
|
||||
"should have visitor name and country as livechat name"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('livechats are sorted by last activity time in the sidebar: most recent at the top', async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const [mailChannelId1, mailChannelId2] = pyEnv['mail.channel'].create([
|
||||
{
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, {
|
||||
last_interest_dt: datetime_to_str(new Date(2021, 0, 1)),
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
{
|
||||
anonymous_name: "Visitor 12",
|
||||
channel_member_ids: [
|
||||
[0, 0, {
|
||||
last_interest_dt: datetime_to_str(new Date(2021, 0, 2)),
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
}],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
]);
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const initialLivechats = document.querySelectorAll('.o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategory_item');
|
||||
assert.strictEqual(
|
||||
initialLivechats.length,
|
||||
2,
|
||||
"should have 2 livechat items"
|
||||
);
|
||||
assert.strictEqual(
|
||||
Number(initialLivechats[0].dataset.channelId),
|
||||
mailChannelId2,
|
||||
"first livechat should be the one with the more recent last activity time"
|
||||
);
|
||||
assert.strictEqual(
|
||||
Number(initialLivechats[1].dataset.channelId),
|
||||
mailChannelId1,
|
||||
"second livechat should be the one with the less recent last activity time"
|
||||
);
|
||||
|
||||
// post a new message on the last channel
|
||||
await afterNextRender(() => initialLivechats[1].click());
|
||||
await afterNextRender(() => document.execCommand('insertText', false, "Blabla"));
|
||||
await afterNextRender(() => document.querySelector('.o_ComposerView_buttonSend').click());
|
||||
|
||||
const newLivechats = document.querySelectorAll('.o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategory_item');
|
||||
assert.strictEqual(
|
||||
newLivechats.length,
|
||||
2,
|
||||
"should have 2 livechat items"
|
||||
);
|
||||
assert.strictEqual(
|
||||
Number(newLivechats[0].dataset.channelId),
|
||||
mailChannelId1,
|
||||
"first livechat should be the one with the more recent last activity time"
|
||||
);
|
||||
assert.strictEqual(
|
||||
Number(newLivechats[1].dataset.channelId),
|
||||
mailChannelId2,
|
||||
"second livechat should be the one with the less recent last activity time"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('invite button should be present on livechat', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create(
|
||||
{
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
);
|
||||
const { openDiscuss } = await start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: `mail.channel_${mailChannelId1}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_ThreadViewTopbar_inviteButton',
|
||||
"Invite button should be visible in top bar when livechat is active thread"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('call buttons should not be present on livechat', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create(
|
||||
{
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
);
|
||||
const { openDiscuss } = await start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: `mail.channel_${mailChannelId1}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_ThreadViewTopbar_callButton',
|
||||
"Call buttons should not be visible in top bar when livechat is active thread"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('reaction button should not be present on livechat', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
channel_partner_ids: [pyEnv.currentPartnerId, pyEnv.publicPartnerId],
|
||||
});
|
||||
const { click, insertText, openDiscuss } = await start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: `mail.channel_${mailChannelId1}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
await insertText('.o_ComposerTextInputView_textarea', "Test");
|
||||
await click('.o_ComposerView_buttonSend');
|
||||
await click('.o_MessageView');
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_MessageActionView_actionReaction',
|
||||
"should not have action to add a reaction"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('reply button should not be present on livechat', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
channel_partner_ids: [pyEnv.currentPartnerId, pyEnv.publicPartnerId],
|
||||
});
|
||||
const { click, insertText, openDiscuss } = await start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: `mail.channel_${mailChannelId1}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
await insertText('.o_ComposerTextInputView_textarea', "Test");
|
||||
await click('.o_ComposerView_buttonSend');
|
||||
await click('.o_MessageView');
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_MessageActionView_actionReplyTo',
|
||||
"should not have reply action"
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
assert.ok(livechat, "should have a livechat in sidebar");
|
||||
assert.strictEqual(
|
||||
livechat.textContent,
|
||||
"Visitor 11",
|
||||
"should have 'Visitor 11' as livechat name"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("livechat in the sidebar: existing user with country", async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const resCountryId1 = pyEnv["res.country"].create({
|
||||
code: "be",
|
||||
name: "Belgium",
|
||||
});
|
||||
const resPartnerId1 = pyEnv["res.partner"].create({
|
||||
country_id: resCountryId1,
|
||||
name: "Jean",
|
||||
});
|
||||
pyEnv["mail.channel"].create({
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: resPartnerId1 }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
".o_DiscussSidebarView_categoryLivechat",
|
||||
"should have a channel group livechat in the side bar"
|
||||
);
|
||||
const livechat = document.querySelector(
|
||||
".o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategoryItem"
|
||||
);
|
||||
assert.ok(livechat, "should have a livechat in sidebar");
|
||||
assert.strictEqual(
|
||||
livechat.textContent,
|
||||
"Jean (Belgium)",
|
||||
"should have user name and country as livechat name"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"do not add livechat in the sidebar on visitor opening his chat",
|
||||
async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv["res.users"].write([pyEnv.currentUserId], { im_status: "online" });
|
||||
const imLivechatChannelId1 = pyEnv["im_livechat.channel"].create({
|
||||
user_ids: [pyEnv.currentUserId],
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
".o_DiscussSidebarView_categoryLivechat",
|
||||
"should not have any livechat in the sidebar initially"
|
||||
);
|
||||
|
||||
// simulate livechat visitor opening his chat
|
||||
await messaging.rpc({
|
||||
route: "/im_livechat/get_session",
|
||||
params: {
|
||||
context: {
|
||||
mockedUserId: false,
|
||||
},
|
||||
channel_id: imLivechatChannelId1,
|
||||
},
|
||||
});
|
||||
await nextAnimationFrame();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
".o_DiscussSidebarView_categoryLivechat",
|
||||
"should still not have any livechat in the sidebar after visitor opened his chat"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("do not add livechat in the sidebar on visitor typing", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv["res.users"].write([pyEnv.currentUserId], { im_status: "online" });
|
||||
const imLivechatChannelId1 = pyEnv["im_livechat.channel"].create({
|
||||
user_ids: [pyEnv.currentUserId],
|
||||
});
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
channel_member_ids: [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
{
|
||||
is_pinned: false,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_channel_id: imLivechatChannelId1,
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
".o_DiscussSidebarView_categoryLivechat",
|
||||
"should not have any livechat in the sidebar initially"
|
||||
);
|
||||
|
||||
// simulate livechat visitor typing
|
||||
const channel = pyEnv["mail.channel"].searchRead([["id", "=", mailChannelId1]])[0];
|
||||
await messaging.rpc({
|
||||
route: "/im_livechat/notify_typing",
|
||||
params: {
|
||||
context: {
|
||||
mockedPartnerId: pyEnv.publicPartnerId,
|
||||
},
|
||||
is_typing: true,
|
||||
uuid: channel.uuid,
|
||||
},
|
||||
});
|
||||
await nextAnimationFrame();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
".o_DiscussSidebarView_categoryLivechat",
|
||||
"should still not have any livechat in the sidebar after visitor started typing"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"add livechat in the sidebar on visitor sending first message",
|
||||
async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
pyEnv["res.users"].write([pyEnv.currentUserId], { im_status: "online" });
|
||||
const resCountryId1 = pyEnv["res.country"].create({
|
||||
code: "be",
|
||||
name: "Belgium",
|
||||
});
|
||||
const imLivechatChannelId1 = pyEnv["im_livechat.channel"].create({
|
||||
user_ids: [pyEnv.currentUserId],
|
||||
});
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor (Belgium)",
|
||||
channel_member_ids: [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
{
|
||||
is_pinned: false,
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
country_id: resCountryId1,
|
||||
livechat_channel_id: imLivechatChannelId1,
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
".o_DiscussSidebarView_categoryLivechat",
|
||||
"should not have any livechat in the sidebar initially"
|
||||
);
|
||||
|
||||
// simulate livechat visitor sending a message
|
||||
const channel = pyEnv["mail.channel"].searchRead([["id", "=", mailChannelId1]])[0];
|
||||
await afterNextRender(async () =>
|
||||
messaging.rpc({
|
||||
route: "/mail/chat_post",
|
||||
params: {
|
||||
context: {
|
||||
mockedUserId: false,
|
||||
},
|
||||
uuid: channel.uuid,
|
||||
message_content: "new message",
|
||||
},
|
||||
})
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
".o_DiscussSidebarView_categoryLivechat",
|
||||
"should have a channel group livechat in the side bar after receiving first message"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
".o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategoryItem",
|
||||
"should have a livechat in the sidebar after receiving first message"
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.querySelector(
|
||||
".o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategoryItem .o_DiscussSidebarCategoryItem_name"
|
||||
).textContent,
|
||||
"Visitor (Belgium)",
|
||||
"should have visitor name and country as livechat name"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"livechats are sorted by last activity time in the sidebar: most recent at the top",
|
||||
async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const [mailChannelId1, mailChannelId2] = pyEnv["mail.channel"].create([
|
||||
{
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
{
|
||||
last_interest_dt: datetime_to_str(new Date(2021, 0, 1)),
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
{
|
||||
anonymous_name: "Visitor 12",
|
||||
channel_member_ids: [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
{
|
||||
last_interest_dt: datetime_to_str(new Date(2021, 0, 2)),
|
||||
partner_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
},
|
||||
]);
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
const initialLivechats = document.querySelectorAll(
|
||||
".o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategory_item"
|
||||
);
|
||||
assert.strictEqual(initialLivechats.length, 2, "should have 2 livechat items");
|
||||
assert.strictEqual(
|
||||
Number(initialLivechats[0].dataset.channelId),
|
||||
mailChannelId2,
|
||||
"first livechat should be the one with the more recent last activity time"
|
||||
);
|
||||
assert.strictEqual(
|
||||
Number(initialLivechats[1].dataset.channelId),
|
||||
mailChannelId1,
|
||||
"second livechat should be the one with the less recent last activity time"
|
||||
);
|
||||
|
||||
// post a new message on the last channel
|
||||
await afterNextRender(() => initialLivechats[1].click());
|
||||
await afterNextRender(() => document.execCommand("insertText", false, "Blabla"));
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(".o_ComposerView_buttonSend").click()
|
||||
);
|
||||
|
||||
const newLivechats = document.querySelectorAll(
|
||||
".o_DiscussSidebarView_categoryLivechat .o_DiscussSidebarCategory_item"
|
||||
);
|
||||
assert.strictEqual(newLivechats.length, 2, "should have 2 livechat items");
|
||||
assert.strictEqual(
|
||||
Number(newLivechats[0].dataset.channelId),
|
||||
mailChannelId1,
|
||||
"first livechat should be the one with the more recent last activity time"
|
||||
);
|
||||
assert.strictEqual(
|
||||
Number(newLivechats[1].dataset.channelId),
|
||||
mailChannelId2,
|
||||
"second livechat should be the one with the less recent last activity time"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("invite button should be present on livechat", async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: `mail.channel_${mailChannelId1}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
".o_ThreadViewTopbar_inviteButton",
|
||||
"Invite button should be visible in top bar when livechat is active thread"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("call buttons should not be present on livechat", async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { openDiscuss } = await start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: `mail.channel_${mailChannelId1}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
".o_ThreadViewTopbar_callButton",
|
||||
"Call buttons should not be visible in top bar when livechat is active thread"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("reaction button should not be present on livechat", async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
channel_partner_ids: [pyEnv.currentPartnerId, pyEnv.publicPartnerId],
|
||||
});
|
||||
const { click, insertText, openDiscuss } = await start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: `mail.channel_${mailChannelId1}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
await insertText(".o_ComposerTextInputView_textarea", "Test");
|
||||
await click(".o_ComposerView_buttonSend");
|
||||
await click(".o_MessageView");
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
".o_MessageActionView_actionReaction",
|
||||
"should not have action to add a reaction"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("reply button should not be present on livechat", async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
channel_partner_ids: [pyEnv.currentPartnerId, pyEnv.publicPartnerId],
|
||||
});
|
||||
const { click, insertText, openDiscuss } = await start({
|
||||
discuss: {
|
||||
params: {
|
||||
default_active_id: `mail.channel_${mailChannelId1}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
await insertText(".o_ComposerTextInputView_textarea", "Test");
|
||||
await click(".o_ComposerView_buttonSend");
|
||||
await click(".o_MessageView");
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
".o_MessageActionView_actionReplyTo",
|
||||
"should not have reply action"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+55
-64
@@ -1,71 +1,62 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
afterNextRender,
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
import { afterNextRender, start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('messaging_menu_tests.js');
|
||||
QUnit.module("im_livechat", {}, function () {
|
||||
QUnit.module("components", {}, function () {
|
||||
QUnit.module("messaging_menu_tests.js");
|
||||
|
||||
QUnit.test('livechats should be in "chat" filter', async function (assert) {
|
||||
assert.expect(7);
|
||||
QUnit.test('livechats should be in "chat" filter', async function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 11",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
await start();
|
||||
assert.containsOnce(document.body, ".o_MessagingMenu", "should have messaging menu");
|
||||
|
||||
await afterNextRender(() => document.querySelector(".o_MessagingMenu_toggler").click());
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_MessagingMenuTabView[data-tab-id="all"]',
|
||||
"should have a tab/filter 'all' in messaging menu"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_MessagingMenuTabView[data-tab-id="chat"]',
|
||||
"should have a tab/filter 'chat' in messaging menu"
|
||||
);
|
||||
assert.hasClass(
|
||||
document.querySelector('.o_MessagingMenuTabView[data-tab-id="all"]'),
|
||||
"o-active",
|
||||
"tab/filter 'all' of messaging menu should be active initially"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_ChannelPreviewView[data-channel-id="${mailChannelId1}"]`,
|
||||
"livechat should be listed in 'all' tab/filter of messaging menu"
|
||||
);
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector('.o_MessagingMenuTabView[data-tab-id="chat"]').click()
|
||||
);
|
||||
assert.hasClass(
|
||||
document.querySelector('.o_MessagingMenuTabView[data-tab-id="chat"]'),
|
||||
"o-active",
|
||||
"tab/filter 'chat' of messaging menu should become active after click"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_ChannelPreviewView[data-channel-id="${mailChannelId1}"]`,
|
||||
"livechat should be listed in 'chat' tab/filter of messaging menu"
|
||||
);
|
||||
});
|
||||
});
|
||||
await start();
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_MessagingMenu',
|
||||
"should have messaging menu"
|
||||
);
|
||||
|
||||
await afterNextRender(() => document.querySelector('.o_MessagingMenu_toggler').click());
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_MessagingMenuTabView[data-tab-id="all"]',
|
||||
"should have a tab/filter 'all' in messaging menu"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_MessagingMenuTabView[data-tab-id="chat"]',
|
||||
"should have a tab/filter 'chat' in messaging menu"
|
||||
);
|
||||
assert.hasClass(
|
||||
document.querySelector('.o_MessagingMenuTabView[data-tab-id="all"]'),
|
||||
'o-active',
|
||||
"tab/filter 'all' of messaging menu should be active initially"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_ChannelPreviewView[data-channel-id="${mailChannelId1}"]`,
|
||||
"livechat should be listed in 'all' tab/filter of messaging menu"
|
||||
);
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector('.o_MessagingMenuTabView[data-tab-id="chat"]').click()
|
||||
);
|
||||
assert.hasClass(
|
||||
document.querySelector('.o_MessagingMenuTabView[data-tab-id="chat"]'),
|
||||
'o-active',
|
||||
"tab/filter 'chat' of messaging menu should become active after click"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
`.o_ChannelPreviewView[data-channel-id="${mailChannelId1}"]`,
|
||||
"livechat should be listed in 'chat' tab/filter of messaging menu"
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,68 +1,65 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
afterNextRender,
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
import { afterNextRender, start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('thread_icon_tests.js');
|
||||
QUnit.module("im_livechat", {}, function () {
|
||||
QUnit.module("components", {}, function () {
|
||||
QUnit.module("thread_icon_tests.js");
|
||||
|
||||
QUnit.test('livechat: public website visitor is typing', async function (assert) {
|
||||
assert.expect(4);
|
||||
QUnit.test("livechat: public website visitor is typing", async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 20",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 20",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start({
|
||||
discuss: {
|
||||
context: { active_id: mailChannelId1 },
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
".o_ThreadViewTopbar .o_ThreadIconView",
|
||||
"should have thread icon"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
".o_ThreadIconView .fa.fa-comments",
|
||||
"should have default livechat icon"
|
||||
);
|
||||
|
||||
const mailChannel1 = pyEnv["mail.channel"].searchRead([["id", "=", mailChannelId1]])[0];
|
||||
// simulate receive typing notification from livechat visitor "is typing"
|
||||
await afterNextRender(() =>
|
||||
messaging.rpc({
|
||||
route: "/im_livechat/notify_typing",
|
||||
params: {
|
||||
context: {
|
||||
mockedPartnerId: pyEnv.publicPartnerId,
|
||||
},
|
||||
is_typing: true,
|
||||
uuid: mailChannel1.uuid,
|
||||
},
|
||||
})
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
".o_ThreadIconView_typing",
|
||||
"should have thread icon with visitor currently typing"
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.querySelector(".o_ThreadIconView_typing").title,
|
||||
"Visitor 20 is typing...",
|
||||
"title of icon should tell visitor is currently typing"
|
||||
);
|
||||
});
|
||||
});
|
||||
const { messaging, openDiscuss } = await start({
|
||||
discuss: {
|
||||
context: { active_id: mailChannelId1 },
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_ThreadViewTopbar .o_ThreadIconView',
|
||||
"should have thread icon"
|
||||
);
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_ThreadIconView .fa.fa-comments',
|
||||
"should have default livechat icon"
|
||||
);
|
||||
|
||||
const mailChannel1 = pyEnv['mail.channel'].searchRead([['id', '=', mailChannelId1]])[0];
|
||||
// simulate receive typing notification from livechat visitor "is typing"
|
||||
await afterNextRender(() => messaging.rpc({
|
||||
route: '/im_livechat/notify_typing',
|
||||
params: {
|
||||
context: {
|
||||
mockedPartnerId: pyEnv.publicPartnerId,
|
||||
},
|
||||
is_typing: true,
|
||||
uuid: mailChannel1.uuid,
|
||||
},
|
||||
}));
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_ThreadIconView_typing',
|
||||
"should have thread icon with visitor currently typing"
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.querySelector('.o_ThreadIconView_typing').title,
|
||||
"Visitor 20 is typing...",
|
||||
"title of icon should tell visitor is currently typing"
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
+49
-52
@@ -1,59 +1,56 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import {
|
||||
afterNextRender,
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
import { afterNextRender, start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
QUnit.module('im_livechat', {}, function () {
|
||||
QUnit.module('components', {}, function () {
|
||||
QUnit.module('thread_textual_typing_status_tests.js');
|
||||
QUnit.module("im_livechat", {}, function () {
|
||||
QUnit.module("components", {}, function () {
|
||||
QUnit.module("thread_textual_typing_status_tests.js");
|
||||
|
||||
QUnit.test('receive visitor typing status "is typing"', async function (assert) {
|
||||
assert.expect(2);
|
||||
QUnit.test('receive visitor typing status "is typing"', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({
|
||||
anonymous_name: "Visitor 20",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: 'livechat',
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv["mail.channel"].create({
|
||||
anonymous_name: "Visitor 20",
|
||||
channel_member_ids: [
|
||||
[0, 0, { partner_id: pyEnv.currentPartnerId }],
|
||||
[0, 0, { partner_id: pyEnv.publicPartnerId }],
|
||||
],
|
||||
channel_type: "livechat",
|
||||
livechat_operator_id: pyEnv.currentPartnerId,
|
||||
});
|
||||
const { messaging, openDiscuss } = await start({
|
||||
discuss: {
|
||||
context: { active_id: mailChannelId1 },
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
|
||||
assert.strictEqual(
|
||||
document.querySelector(".o_ThreadTextualTypingStatusView").textContent,
|
||||
"",
|
||||
"Should display no one is currently typing"
|
||||
);
|
||||
|
||||
const mailChannel1 = pyEnv["mail.channel"].searchRead([["id", "=", mailChannelId1]])[0];
|
||||
// simulate receive typing notification from livechat visitor "is typing"
|
||||
await afterNextRender(() =>
|
||||
messaging.rpc({
|
||||
route: "/im_livechat/notify_typing",
|
||||
params: {
|
||||
context: {
|
||||
mockedPartnerId: pyEnv.publicPartnerId,
|
||||
},
|
||||
is_typing: true,
|
||||
uuid: mailChannel1.uuid,
|
||||
},
|
||||
})
|
||||
);
|
||||
assert.strictEqual(
|
||||
document.querySelector(".o_ThreadTextualTypingStatusView").textContent,
|
||||
"Visitor 20 is typing...",
|
||||
"Should display that visitor is typing"
|
||||
);
|
||||
});
|
||||
});
|
||||
const { messaging, openDiscuss } = await start({
|
||||
discuss: {
|
||||
context: { active_id: mailChannelId1 },
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
|
||||
assert.strictEqual(
|
||||
document.querySelector('.o_ThreadTextualTypingStatusView').textContent,
|
||||
"",
|
||||
"Should display no one is currently typing"
|
||||
);
|
||||
|
||||
const mailChannel1 = pyEnv['mail.channel'].searchRead([['id', '=', mailChannelId1]])[0];
|
||||
// simulate receive typing notification from livechat visitor "is typing"
|
||||
await afterNextRender(() => messaging.rpc({
|
||||
route: '/im_livechat/notify_typing',
|
||||
params: {
|
||||
context: {
|
||||
mockedPartnerId: pyEnv.publicPartnerId,
|
||||
},
|
||||
is_typing: true,
|
||||
uuid: mailChannel1.uuid,
|
||||
},
|
||||
}));
|
||||
assert.strictEqual(
|
||||
document.querySelector('.o_ThreadTextualTypingStatusView').textContent,
|
||||
"Visitor 20 is typing...",
|
||||
"Should display that visitor is typing"
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,21 +12,26 @@ const requestChatSteps = [
|
||||
},
|
||||
];
|
||||
|
||||
registry.category("web_tour.tours").add("im_livechat_request_chat", { test: true, steps: requestChatSteps});
|
||||
registry
|
||||
.category("web_tour.tours")
|
||||
.add("im_livechat_request_chat", { test: true, steps: requestChatSteps });
|
||||
|
||||
registry.category("web_tour.tours").add("im_livechat_request_chat_and_send_message", { test: true, steps: [
|
||||
...requestChatSteps,
|
||||
{
|
||||
trigger: ".o_composer_text_field",
|
||||
run: "text Hello, I need help please !",
|
||||
},
|
||||
{
|
||||
trigger: '.o_composer_text_field',
|
||||
run() {
|
||||
$(".o_composer_text_field").trigger($.Event("keydown", { which: 13 }));
|
||||
registry.category("web_tour.tours").add("im_livechat_request_chat_and_send_message", {
|
||||
test: true,
|
||||
steps: [
|
||||
...requestChatSteps,
|
||||
{
|
||||
trigger: ".o_composer_text_field",
|
||||
run: "text Hello, I need help please !",
|
||||
},
|
||||
},
|
||||
{
|
||||
trigger: ".o_thread_message:contains('Hello, I need help')",
|
||||
},
|
||||
]});
|
||||
{
|
||||
trigger: ".o_composer_text_field",
|
||||
run() {
|
||||
$(".o_composer_text_field").trigger($.Event("keydown", { which: 13 }));
|
||||
},
|
||||
},
|
||||
{
|
||||
trigger: ".o_thread_message:contains('Hello, I need help')",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
+147
-106
@@ -3,122 +3,163 @@
|
||||
import { registry } from "@web/core/registry";
|
||||
import { stepUtils } from "@web_tour/js/tour_step_utils";
|
||||
|
||||
const commonSteps = [stepUtils.showAppsMenuItem(), {
|
||||
trigger: '.o_app[data-menu-xmlid="im_livechat.menu_livechat_root"]',
|
||||
}, {
|
||||
trigger: 'button[data-menu-xmlid="im_livechat.livechat_config"]',
|
||||
}, {
|
||||
trigger: 'a[data-menu-xmlid="im_livechat.chatbot_config"]',
|
||||
}, {
|
||||
trigger: '.o_list_button_add',
|
||||
}, {
|
||||
trigger: 'input[id="title"]',
|
||||
run: 'text Test Chatbot Sequence'
|
||||
}, {
|
||||
trigger: 'div[name="script_step_ids"] .o_field_x2many_list_row_add a'
|
||||
}, {
|
||||
trigger: 'textarea#message',
|
||||
run: 'text Step 1'
|
||||
}, {
|
||||
trigger: 'button:contains("Save & New")'
|
||||
}, {
|
||||
trigger: 'tr:contains("Step 1")',
|
||||
in_modal: false,
|
||||
run: () => {}
|
||||
}, {
|
||||
trigger: 'textarea#message',
|
||||
run: 'text Step 2'
|
||||
}, {
|
||||
trigger: 'button:contains("Save & New")'
|
||||
}, {
|
||||
trigger: 'tr:contains("Step 2")',
|
||||
in_modal: false,
|
||||
run: () => {}
|
||||
}, {
|
||||
trigger: 'textarea#message',
|
||||
run: 'text Step 3'
|
||||
}];
|
||||
|
||||
const commonSteps = [
|
||||
stepUtils.showAppsMenuItem(),
|
||||
{
|
||||
trigger: '.o_app[data-menu-xmlid="im_livechat.menu_livechat_root"]',
|
||||
},
|
||||
{
|
||||
trigger: 'button[data-menu-xmlid="im_livechat.livechat_config"]',
|
||||
},
|
||||
{
|
||||
trigger: 'a[data-menu-xmlid="im_livechat.chatbot_config"]',
|
||||
},
|
||||
{
|
||||
trigger: ".o_list_button_add",
|
||||
},
|
||||
{
|
||||
trigger: 'input[id="title"]',
|
||||
run: "text Test Chatbot Sequence",
|
||||
},
|
||||
{
|
||||
trigger: 'div[name="script_step_ids"] .o_field_x2many_list_row_add a',
|
||||
},
|
||||
{
|
||||
trigger: "textarea#message",
|
||||
run: "text Step 1",
|
||||
},
|
||||
{
|
||||
trigger: 'button:contains("Save & New")',
|
||||
},
|
||||
{
|
||||
trigger: 'tr:contains("Step 1")',
|
||||
in_modal: false,
|
||||
run: () => {},
|
||||
},
|
||||
{
|
||||
trigger: "textarea#message",
|
||||
run: "text Step 2",
|
||||
},
|
||||
{
|
||||
trigger: 'button:contains("Save & New")',
|
||||
},
|
||||
{
|
||||
trigger: 'tr:contains("Step 2")',
|
||||
in_modal: false,
|
||||
run: () => {},
|
||||
},
|
||||
{
|
||||
trigger: "textarea#message",
|
||||
run: "text Step 3",
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Simply create a few steps in order to check the sequences.
|
||||
*/
|
||||
registry.category("web_tour.tours").add('im_livechat_chatbot_steps_sequence_tour', {
|
||||
registry.category("web_tour.tours").add("im_livechat_chatbot_steps_sequence_tour", {
|
||||
test: true,
|
||||
url: '/web',
|
||||
url: "/web",
|
||||
steps: [
|
||||
...commonSteps, {
|
||||
trigger: 'button:contains("Save & Close")'
|
||||
}, {
|
||||
trigger: 'body.o_web_client:not(.modal-open)',
|
||||
run() {},
|
||||
}, ...stepUtils.discardForm()
|
||||
]});
|
||||
...commonSteps,
|
||||
{
|
||||
trigger: 'button:contains("Save & Close")',
|
||||
},
|
||||
{
|
||||
trigger: "body.o_web_client:not(.modal-open)",
|
||||
run() {},
|
||||
},
|
||||
...stepUtils.discardForm(),
|
||||
],
|
||||
});
|
||||
|
||||
/**
|
||||
* Same as above, with an extra drag&drop at the end.
|
||||
*/
|
||||
registry.category("web_tour.tours").add('im_livechat_chatbot_steps_sequence_with_move_tour', {
|
||||
registry.category("web_tour.tours").add("im_livechat_chatbot_steps_sequence_with_move_tour", {
|
||||
test: true,
|
||||
url: '/web',
|
||||
url: "/web",
|
||||
steps: [
|
||||
...commonSteps, {
|
||||
trigger: 'button:contains("Save & New")'
|
||||
}, {
|
||||
trigger: 'tr:contains("Step 3")',
|
||||
in_modal: false,
|
||||
run: () => {}
|
||||
}, {
|
||||
trigger: 'textarea#message',
|
||||
run: 'text Step 4'
|
||||
}, {
|
||||
trigger: 'button:contains("Save & New")'
|
||||
}, {
|
||||
trigger: 'tr:contains("Step 4")',
|
||||
in_modal: false,
|
||||
run: () => {}
|
||||
}, {
|
||||
trigger: 'textarea#message',
|
||||
run: 'text Step 5'
|
||||
}, {
|
||||
trigger: 'button:contains("Save & Close")'
|
||||
}, {
|
||||
trigger: 'body.o_web_client:not(.modal-open)',
|
||||
run: () => {}
|
||||
}, {
|
||||
trigger: 'tr:contains("Step 5") .o_row_handle',
|
||||
run: () => {
|
||||
// move 'step 5' between 'step 1' and 'step 2'
|
||||
const from = document.querySelector('div[name="script_step_ids"] tr:nth-child(5) .o_row_handle');
|
||||
const fromPosition = from.getBoundingClientRect();
|
||||
fromPosition.x += from.offsetWidth / 2;
|
||||
fromPosition.y += from.offsetHeight / 2;
|
||||
...commonSteps,
|
||||
{
|
||||
trigger: 'button:contains("Save & New")',
|
||||
},
|
||||
{
|
||||
trigger: 'tr:contains("Step 3")',
|
||||
in_modal: false,
|
||||
run: () => {},
|
||||
},
|
||||
{
|
||||
trigger: "textarea#message",
|
||||
run: "text Step 4",
|
||||
},
|
||||
{
|
||||
trigger: 'button:contains("Save & New")',
|
||||
},
|
||||
{
|
||||
trigger: 'tr:contains("Step 4")',
|
||||
in_modal: false,
|
||||
run: () => {},
|
||||
},
|
||||
{
|
||||
trigger: "textarea#message",
|
||||
run: "text Step 5",
|
||||
},
|
||||
{
|
||||
trigger: 'button:contains("Save & Close")',
|
||||
},
|
||||
{
|
||||
trigger: "body.o_web_client:not(.modal-open)",
|
||||
run: () => {},
|
||||
},
|
||||
{
|
||||
trigger: 'tr:contains("Step 5") .o_row_handle',
|
||||
run: () => {
|
||||
// move 'step 5' between 'step 1' and 'step 2'
|
||||
const from = document.querySelector(
|
||||
'div[name="script_step_ids"] tr:nth-child(5) .o_row_handle'
|
||||
);
|
||||
const fromPosition = from.getBoundingClientRect();
|
||||
fromPosition.x += from.offsetWidth / 2;
|
||||
fromPosition.y += from.offsetHeight / 2;
|
||||
|
||||
const to = document.querySelector('div[name="script_step_ids"] tr:nth-child(2) .o_row_handle');
|
||||
from.dispatchEvent(new Event("mouseenter", { bubbles: true }));
|
||||
from.dispatchEvent(new MouseEvent("mousedown", {
|
||||
bubbles: true,
|
||||
which: 1,
|
||||
button: 0,
|
||||
clientX: fromPosition.x,
|
||||
clientY: fromPosition.y}));
|
||||
from.dispatchEvent(new Event("mousemove", { bubbles: true }));
|
||||
to.dispatchEvent(new Event("mouseenter", { bubbles: true }));
|
||||
from.dispatchEvent(new Event("mouseup", { bubbles: true }));
|
||||
}
|
||||
}, {
|
||||
trigger: 'div[name="script_step_ids"] .o_field_x2many_list_row_add a'
|
||||
}, {
|
||||
trigger: 'textarea#message',
|
||||
run: 'text Step 6'
|
||||
}, {
|
||||
trigger: 'button:contains("Save & Close")'
|
||||
}, {
|
||||
trigger: 'body.o_web_client:not(.modal-open)',
|
||||
run: () => {}
|
||||
}, {
|
||||
trigger: 'tr:contains("Step 6")',
|
||||
in_modal: false,
|
||||
run: () => {}
|
||||
}, ...stepUtils.discardForm(),
|
||||
]});
|
||||
const to = document.querySelector(
|
||||
'div[name="script_step_ids"] tr:nth-child(2) .o_row_handle'
|
||||
);
|
||||
from.dispatchEvent(new Event("mouseenter", { bubbles: true }));
|
||||
from.dispatchEvent(
|
||||
new MouseEvent("mousedown", {
|
||||
bubbles: true,
|
||||
which: 1,
|
||||
button: 0,
|
||||
clientX: fromPosition.x,
|
||||
clientY: fromPosition.y,
|
||||
})
|
||||
);
|
||||
from.dispatchEvent(new Event("mousemove", { bubbles: true }));
|
||||
to.dispatchEvent(new Event("mouseenter", { bubbles: true }));
|
||||
from.dispatchEvent(new Event("mouseup", { bubbles: true }));
|
||||
},
|
||||
},
|
||||
{
|
||||
trigger: 'div[name="script_step_ids"] .o_field_x2many_list_row_add a',
|
||||
},
|
||||
{
|
||||
trigger: "textarea#message",
|
||||
run: "text Step 6",
|
||||
},
|
||||
{
|
||||
trigger: 'button:contains("Save & Close")',
|
||||
},
|
||||
{
|
||||
trigger: "body.o_web_client:not(.modal-open)",
|
||||
run: () => {},
|
||||
},
|
||||
{
|
||||
trigger: 'tr:contains("Step 6")',
|
||||
in_modal: false,
|
||||
run: () => {},
|
||||
},
|
||||
...stepUtils.discardForm(),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -152,6 +152,10 @@ addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js
|
||||
!addons/mail/
|
||||
!addons/mail/**/*
|
||||
|
||||
# Whitelist im_livechat
|
||||
!addons/im_livechat/
|
||||
!addons/im_livechat/**/*
|
||||
|
||||
# Whitelist point_of_sale
|
||||
!addons/point_of_sale/
|
||||
!addons/point_of_sale/**/*
|
||||
@@ -195,7 +199,7 @@ addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js
|
||||
!addons/pos_six/**/*
|
||||
!addons/pos_stripe
|
||||
!addons/pos_stripe/**/*
|
||||
|
||||
|
||||
# Whitelist enterprise pos modules
|
||||
!l10n_de_pos_cert
|
||||
!l10n_de_pos_cert/**/*
|
||||
@@ -210,4 +214,4 @@ addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js
|
||||
!pos_restaurant_iot
|
||||
!pos_restaurant_iot/**/*
|
||||
!pos_settle_due
|
||||
!pos_settle_due/**/*
|
||||
!pos_settle_due/**/*
|
||||
|
||||
Reference in New Issue
Block a user