[IMP] im_livechat, *: Refactor JS Public Livechat (step 9)

*: website_livechat

This commit is a step towards refactoring the JS of public livechat,
so that it reuses the same architecture as the code of Discuss.
This implies code that uses JS models and OWL components.

Task-2928837

closes odoo/odoo#96690

Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
Alexandre Kühn
2022-07-28 02:40:06 +02:00
parent 467415f631
commit d27546064a
23 changed files with 599 additions and 460 deletions
-4
View File
@@ -52,7 +52,6 @@ Help your customers with this chat, and analyse their feedback.
'web.assets_frontend': [
'im_livechat/static/src/public/main.js',
'im_livechat/static/src/services/*.js',
'im_livechat/static/src/legacy/public_livechat_history_tracking.js',
'im_livechat/static/src/legacy/public_livechat_chatbot.js',
'im_livechat/static/src/legacy/public_livechat.scss',
'im_livechat/static/src/legacy/public_livechat_chatbot.scss',
@@ -74,7 +73,6 @@ Help your customers with this chat, and analyse their feedback.
],
'web.qunit_suite_tests': [
'im_livechat/static/tests/qunit_suite_tests/components/**/*.js',
'im_livechat/static/src/legacy/public_livechat_history_tracking.js',
'im_livechat/static/src/legacy/public_livechat_chatbot.js',
],
'web.assets_tests': [
@@ -85,7 +83,6 @@ Help your customers with this chat, and analyse their feedback.
],
'mail.assets_messaging': [
'im_livechat/static/src/legacy/models/*',
'im_livechat/static/src/legacy/public_livechat_constants.js',
'im_livechat/static/src/legacy/widgets/*',
'im_livechat/static/src/legacy/widgets/*/*',
'im_livechat/static/src/models/*.js',
@@ -185,7 +182,6 @@ Help your customers with this chat, and analyse their feedback.
'bus/static/src/services/bus_service.js',
'bus/static/src/multi_tab_service.js',
'mail/static/src/js/utils.js',
'im_livechat/static/src/legacy/public_livechat_history_tracking.js',
'im_livechat/static/src/legacy/public_livechat_chatbot.js',
('include', 'web._assets_helpers'),
@@ -39,10 +39,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
this.setParent(params.parent);
this.messaging = messaging;
this._folded = false; // threads are unfolded by default
this._id = params.data.id;
this._name = params.data.name;
this._status = params.data.status || '';
/**
* Initialize the internal data for typing feature on threads.
@@ -112,26 +109,27 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
this.on('message_added', this, this._onTypingMessageAdded);
this.on('message_posted', this, this._onTypingMessagePosted);
this._operatorPID = params.data.operator_pid;
// Necessary for thread typing mixin to display is typing notification
// bar text (at least, for the operator in the members).
this._members = [{
id: this._operatorPID[0],
name: this._operatorPID[1]
}];
this._uuid = params.data.uuid;
this._members = (
this.messaging.publicLivechatGlobal.publicLivechat.operator
? [{
id: this.messaging.publicLivechatGlobal.publicLivechat.operator.id,
name: this.messaging.publicLivechatGlobal.publicLivechat.operator.name,
}]
: []
);
if (params.data.message_unread_counter !== undefined) {
this.messaging.livechatButtonView.publicLivechat.update({
this.messaging.publicLivechatGlobal.publicLivechat.update({
unreadCounter: params.data.message_unread_counter
});
}
if (_.isBoolean(params.data.folded)) {
this._folded = params.data.folded;
this.messaging.publicLivechatGlobal.publicLivechat.update({ isFolded: params.data.folded });
} else {
this._folded = params.data.state === 'folded';
this.messaging.publicLivechatGlobal.publicLivechat.update({ isFolded: params.data.state === 'folded' });
}
},
@@ -147,14 +145,6 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
addMessage(message) {
this.trigger('message_added', message);
},
/**
* Updates the folded state of the thread
*
* @param {boolean} folded
*/
fold(folded) {
this._folded = folded;
},
/**
* @override
* @returns {@im_livechat/legacy/models/public_livechat_message[]}
@@ -234,8 +224,8 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
* @returns {Promise}
*/
markAsRead() {
if (this.messaging.livechatButtonView.publicLivechat.unreadCounter > 0) {
this.messaging.livechatButtonView.publicLivechat.update({ unreadCounter: 0 });
if (this.messaging.publicLivechatGlobal.publicLivechat.unreadCounter > 0) {
this.messaging.publicLivechatGlobal.publicLivechat.update({ unreadCounter: 0 });
this.trigger_up('updated_unread_counter');
return Promise.resolve();
}
@@ -302,12 +292,19 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
*/
toData() {
return {
folded: this._folded,
folded: this.messaging.publicLivechatGlobal.publicLivechat.isFolded,
id: this._id,
message_unread_counter: this.messaging.livechatButtonView.publicLivechat.unreadCounter,
operator_pid: this._operatorPID,
name: this._name,
uuid: this._uuid,
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,
]
: []
),
name: this.messaging.publicLivechatGlobal.publicLivechat.name,
uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
};
},
/**
@@ -345,7 +342,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
*/
_notifyMyselfTyping(params) {
return session.rpc('/im_livechat/notify_typing', {
uuid: this._uuid,
uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
is_typing: params.typing,
}, { shadow: true });
},
@@ -433,7 +430,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
* @param {mail.model.AbstractMessage} message
*/
_onTypingMessageAdded(message) {
const operatorID = this._operatorPID[0];
const operatorID = this.messaging.publicLivechatGlobal.publicLivechat.operator.id;
if (message.hasAuthor() && message.getAuthorID() === operatorID) {
this.unregisterTyping({ partnerID: operatorID });
}
@@ -18,6 +18,7 @@ const PublicLivechatMessage = Class.extend({
/**
* @param {@im_livechat/legacy/widgets/livechat_button} parent
* @param {Messaging} messaging
* @param {Object} data
* @param {Array} [data.attachment_ids=[]]
* @param {Array} [data.author_id]
@@ -28,11 +29,9 @@ const PublicLivechatMessage = Class.extend({
* @param {boolean} [data.is_discussion = false]
* @param {boolean} [data.is_notification = false]
* @param {string} [data.message_type = undefined]
* @param {Object} options
* @param {string} options.default_username
* @param {string} options.serverURL
*/
init(parent, data, options) {
init(parent, messaging, data) {
this.messaging = messaging;
// Attachments are not supported in (public) livechat.
// We ignore data from server, otherwise field commands
// will be wrongly considered as unamed attachments.
@@ -50,10 +49,10 @@ const PublicLivechatMessage = Class.extend({
this._attachmentIDs.forEach(function (attachment) {
attachment.filename = attachment.filename || attachment.name || _t("unnamed");
});
this._defaultUsername = options.default_username;
this._serverURL = options.serverURL;
this._defaultUsername = this.messaging.publicLivechatGlobal.options.default_username;
this._serverURL = this.messaging.publicLivechatGlobal.livechatButtonView.serverUrl;
if (parent.messaging.livechatButtonView.isChatbot) {
if (parent.messaging.publicLivechatGlobal.livechatButtonView.isChatbot) {
this._chatbotStepId = data.chatbot_script_step_id;
this._chatbotStepAnswers = data.chatbot_step_answers;
this._chatbotStepAnswerId = data.chatbot_selected_answer_id;
@@ -51,40 +51,40 @@ const QWeb = core.qweb;
async willStart() {
const superResult = await this._super(...arguments);
if (this.messaging.livechatButtonView.rule && !!this.messaging.livechatButtonView.rule.chatbot) {
if (this.messaging.publicLivechatGlobal.livechatButtonView.rule && !!this.messaging.publicLivechatGlobal.livechatButtonView.rule.chatbot) {
// noop
} else if (this.messaging.livechatButtonView.history !== null && this.messaging.livechatButtonView.history.length === 0) {
this.messaging.livechatButtonView.update({ livechatInit: await session.rpc('/im_livechat/init', {channel_id: this.messaging.livechatButtonView.channelId}) });
} else if (this.messaging.livechatButtonView.history !== null && this.messaging.livechatButtonView.history.length !== 0) {
} else if (this.messaging.publicLivechatGlobal.livechatButtonView.history !== null && this.messaging.publicLivechatGlobal.livechatButtonView.history.length === 0) {
this.messaging.publicLivechatGlobal.livechatButtonView.update({ livechatInit: await session.rpc('/im_livechat/init', {channel_id: this.messaging.publicLivechatGlobal.livechatButtonView.channelId}) });
} else if (this.messaging.publicLivechatGlobal.livechatButtonView.history !== null && this.messaging.publicLivechatGlobal.livechatButtonView.history.length !== 0) {
const sessionCookie = utils.get_cookie('im_livechat_session');
if (sessionCookie) {
this.messaging.livechatButtonView.update({ sessionCookie });
if (localStorage.getItem(this.messaging.livechatButtonView.chatbotSessionCookieKey)) {
this.messaging.livechatButtonView.update({ chatbotState: 'restore_session' });
this.messaging.publicLivechatGlobal.livechatButtonView.update({ sessionCookie });
if (localStorage.getItem(this.messaging.publicLivechatGlobal.livechatButtonView.chatbotSessionCookieKey)) {
this.messaging.publicLivechatGlobal.livechatButtonView.update({ chatbotState: 'restore_session' });
}
}
}
if (this.messaging.livechatButtonView.chatbotState === 'init') {
if (this.messaging.publicLivechatGlobal.livechatButtonView.chatbotState === 'init') {
// we landed on a website page where a channel rule is configured to run a chatbot.script
// -> initialize necessary state
if (this.messaging.livechatButtonView.rule.chatbot_welcome_steps && this.messaging.livechatButtonView.rule.chatbot_welcome_steps.length !== 0) {
this.messaging.livechatButtonView.chatbot.update({
if (this.messaging.publicLivechatGlobal.livechatButtonView.rule.chatbot_welcome_steps && this.messaging.publicLivechatGlobal.livechatButtonView.rule.chatbot_welcome_steps.length !== 0) {
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.update({
currentStep: insertAndReplace({
data: this.messaging.livechatButtonView.chatbot.lastWelcomeStep,
data: this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.lastWelcomeStep,
}),
});
}
} else if (this.messaging.livechatButtonView.chatbotState === 'welcome') {
} else if (this.messaging.publicLivechatGlobal.livechatButtonView.chatbotState === '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')
utils.set_cookie('im_livechat_auto_popup', '', -1);
this.messaging.livechatButtonView.update({ history: clear() });
this.messaging.livechatButtonView.update({ rule: this.messaging.livechatButtonView.livechatInit.rule });
} else if (this.messaging.livechatButtonView.chatbotState === 'restore_session') {
this.messaging.publicLivechatGlobal.livechatButtonView.update({ history: clear() });
this.messaging.publicLivechatGlobal.livechatButtonView.update({ rule: this.messaging.publicLivechatGlobal.livechatButtonView.livechatInit.rule });
} else if (this.messaging.publicLivechatGlobal.livechatButtonView.chatbotState === 'restore_session') {
// we landed on a website page and a chatbot script is currently running
// -> restore the user's session (see '_chatbotRestoreSession')
this._chatbotRestoreSession();
@@ -107,8 +107,8 @@ const QWeb = core.qweb;
_chatbotAddMessage(message, options) {
message.body = utils.Markup(message.body);
this._addMessage(message, options);
if (this.messaging.livechatButtonView.chatWindow.legacyChatWindow._thread._folded || !this.messaging.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.isAtBottom()) {
this.messaging.livechatButtonView.publicLivechat.update({ unreadCounter: increment() });
if (this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow._thread._folded || !this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.isAtBottom()) {
this.messaging.publicLivechatGlobal.publicLivechat.update({ unreadCounter: increment() });
}
if (!options || !options.skipRenderMessages) {
@@ -136,7 +136,7 @@ const QWeb = core.qweb;
} else {
this._chatbotSetIsTyping();
this.nextStepTimeout = setTimeout(
this._chatbotTriggerNextStep.bind(this), this.messaging.livechatButtonView.chatbot.messageDelay);
this._chatbotTriggerNextStep.bind(this), this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay);
}
}
},
@@ -163,13 +163,13 @@ const QWeb = core.qweb;
}
const postedWelcomeMessages = await session.rpc('/chatbot/post_welcome_steps', {
channel_uuid: this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._uuid,
chatbot_script_id: this.messaging.livechatButtonView.chatbot.scriptId,
channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
chatbot_script_id: this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.scriptId,
});
const welcomeMessagesIds = welcomeMessages.map(welcomeMessage => welcomeMessage._id);
this.messaging.livechatButtonView.update({
messages: this.messaging.livechatButtonView.messages.filter((message) => {
this.messaging.publicLivechatGlobal.livechatButtonView.update({
messages: this.messaging.publicLivechatGlobal.livechatButtonView.messages.filter((message) => {
!welcomeMessagesIds.includes(message._id);
}),
});
@@ -192,7 +192,7 @@ const QWeb = core.qweb;
* @private
*/
_chatbotDisableInput(disableText) {
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_composer_text_field')
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_composer_text_field')
.prop('disabled', true)
.addClass('text-center fst-italic bg-200')
.val(disableText);
@@ -202,7 +202,7 @@ const QWeb = core.qweb;
* @private
*/
_chatbotEnableInput() {
const $composerTextField = this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_composer_text_field');
const $composerTextField = this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_composer_text_field');
$composerTextField
.prop('disabled', false)
.removeClass('text-center fst-italic bg-200')
@@ -213,7 +213,7 @@ const QWeb = core.qweb;
$composerTextField.off('keydown', this.chatbotInputKeyDownHandler);
}
if (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'free_input_multi') {
if (this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'free_input_multi') {
this.chatbotInputKeyDownHandler = this._onChatbotInputKeyDown.bind(this);
$composerTextField.on('keydown', this.chatbotInputKeyDownHandler);
}
@@ -226,17 +226,17 @@ const QWeb = core.qweb;
*/
_chatbotEndScript() {
if (
this.messaging.livechatButtonView.chatbot.currentStep &&
this.messaging.livechatButtonView.chatbot.currentStep.data &&
this.messaging.livechatButtonView.chatbot.currentStep.data.conversation_closed
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.conversation_closed
) {
// don't touch anything if the user has closed the conversation, let the chat window
// handle the display
return;
}
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_composer_text_field').addClass('d-none');
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_end').show();
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_restart').one('click',
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_composer_text_field').addClass('d-none');
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_end').show();
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_restart').one('click',
this._onChatbotRestartScript.bind(this));
},
@@ -249,9 +249,9 @@ const QWeb = core.qweb;
* @private
*/
_chatbotDisplayRestartButton() {
return this.messaging.livechatButtonView.isChatbot && (!this.messaging.livechatButtonView.chatbot.currentStep ||
(this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type !== 'forward_operator' ||
!this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_operator_found));
return this.messaging.publicLivechatGlobal.livechatButtonView.isChatbot && (!this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep ||
(this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_type !== 'forward_operator' ||
!this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_operator_found));
},
/**
* Works as a hook since other modules can add their own step types.
@@ -264,7 +264,7 @@ const QWeb = core.qweb;
'question_email',
'free_input_single',
'free_input_multi',
].includes(this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type);
].includes(this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_type);
},
/**
* Processes the step, depending on the current state of the script and the author of the last
@@ -296,21 +296,21 @@ const QWeb = core.qweb;
_chatbotProcessStep() {
if (this._chatbotShouldEndScript()) {
this._chatbotEndScript();
} else if (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'forward_operator'
&& this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_operator_found) {
} else if (this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'forward_operator'
&& this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_operator_found) {
this._chatbotEnableInput();
} else if (this._chatbotIsExpectingUserInput()) {
if (this._isLastMessageFromCustomer()) {
// user has already typed a message in -> trigger next step
this._chatbotSetIsTyping();
this.nextStepTimeout = setTimeout(
this._chatbotTriggerNextStep.bind(this), this.messaging.livechatButtonView.chatbot.messageDelay);
this._chatbotTriggerNextStep.bind(this), this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay);
} else {
this._chatbotEnableInput();
}
} else {
let triggerNextStep = true;
if (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'question_selection') {
if (this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'question_selection') {
if (!this._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
@@ -319,8 +319,8 @@ const QWeb = core.qweb;
}
if (triggerNextStep) {
let nextStepDelay = this.messaging.livechatButtonView.chatbot.messageDelay;
if (this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_typing').length !== 0) {
let nextStepDelay = this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay;
if (this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.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
@@ -336,7 +336,7 @@ const QWeb = core.qweb;
}
if (!this._chatbotDisplayRestartButton()) {
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_main_restart').addClass('d-none');
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_main_restart').addClass('d-none');
}
},
/**
@@ -356,16 +356,16 @@ const QWeb = core.qweb;
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.messaging.livechatButtonView.chatbotSessionCookieKey) {
if (key.startsWith('im_livechat.chatbot.state.uuid_') && key !== this.messaging.publicLivechatGlobal.livechatButtonView.chatbotSessionCookieKey) {
browserLocalStorage.removeItem(key);
}
}
}
let chatbotState = localStorage.getItem(this.messaging.livechatButtonView.chatbotSessionCookieKey);
let chatbotState = localStorage.getItem(this.messaging.publicLivechatGlobal.livechatButtonView.chatbotSessionCookieKey);
if (chatbotState) {
this.messaging.livechatButtonView.chatbot.update({ currentStep: insertAndReplace({ data: this.messaging.livechatButtonView.localStorageChatbotState._chatbotCurrentStep }) });
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.update({ currentStep: insertAndReplace({ data: this.messaging.publicLivechatGlobal.livechatButtonView.localStorageChatbotState._chatbotCurrentStep }) });
}
},
/**
@@ -378,9 +378,9 @@ const QWeb = core.qweb;
* @private
*/
_chatbotSaveSession() {
localStorage.setItem('im_livechat.chatbot.state.uuid_' + this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._uuid, JSON.stringify({
'_chatbot': this.messaging.livechatButtonView.chatbot.data,
'_chatbotCurrentStep': this.messaging.livechatButtonView.chatbot.currentStep.data,
localStorage.setItem('im_livechat.chatbot.state.uuid_' + this.messaging.publicLivechatGlobal.publicLivechat.uuid, JSON.stringify({
'_chatbot': this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.data,
'_chatbotCurrentStep': this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data,
}));
},
/**
@@ -389,26 +389,26 @@ const QWeb = core.qweb;
* @private
*/
_chatbotSetIsTyping(isWelcomeMessage=false) {
if (this.messaging.livechatButtonView.isTypingTimeout) {
if (this.messaging.publicLivechatGlobal.livechatButtonView.isTypingTimeout) {
clearTimeout(this.isTypingTimeout);
}
this._chatbotDisableInput('');
this.messaging.livechatButtonView.update({
this.messaging.publicLivechatGlobal.livechatButtonView.update({
isTypingTimeout: setTimeout(() => {
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_mail_thread_content').append(
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_mail_thread_content').append(
$(QWeb.render('im_livechat.legacy.chatbot.is_typing_message', {
'chatbotImageSrc': `/im_livechat/operator/${
this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._operatorPID[0]
this.messaging.publicLivechatGlobal.publicLivechat.operator.id
}/avatar`,
'chatbotName': this.messaging.livechatButtonView.chatbot.name,
'chatbotName': this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.name,
'isWelcomeMessage': isWelcomeMessage,
}))
);
this.messaging.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.scrollToBottom();
}, this.messaging.livechatButtonView.chatbot.messageDelay / 3),
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.scrollToBottom();
}, this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay / 3),
});
},
/**
@@ -426,25 +426,25 @@ const QWeb = core.qweb;
* @private
*/
_chatbotShouldEndScript() {
if (this.messaging.livechatButtonView.chatbot.currentStep.data.conversation_closed) {
if (this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.conversation_closed) {
return true;
}
if (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_is_last &&
(this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type !== 'forward_operator' ||
!this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_operator_found)
if (this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_is_last &&
(this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_type !== 'forward_operator' ||
!this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_operator_found)
) {
if (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'question_email'
&& !this.messaging.livechatButtonView.chatbot.currentStep.data.is_email_valid
if (this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'question_email'
&& !this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.is_email_valid
) {
// email is not (yet) valid, let the user answer / try again
return false;
} else if (
(this._chatbotIsExpectingUserInput() ||
this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'question_selection') &&
this.messaging.livechatButtonView.messages.length !== 0
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'question_selection') &&
this.messaging.publicLivechatGlobal.livechatButtonView.messages.length !== 0
) {
const lastMessage = this.messaging.livechatButtonView.messages[this.messaging.livechatButtonView.messages.length - 1];
if (lastMessage.getAuthorID() !== this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._operatorPID[0]) {
const lastMessage = this.messaging.publicLivechatGlobal.livechatButtonView.messages[this.messaging.publicLivechatGlobal.livechatButtonView.messages.length - 1];
if (lastMessage.getAuthorID() !== 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
@@ -472,11 +472,11 @@ const QWeb = core.qweb;
*/
async _chatbotValidateEmail() {
let emailValidResult = await session.rpc('/chatbot/step/validate_email', {
channel_uuid: this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._uuid,
channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
});
if (emailValidResult.success) {
this.messaging.livechatButtonView.chatbot.currentStep.data.is_email_valid = true;
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.is_email_valid = true;
this._chatbotSaveSession();
return true;
@@ -499,9 +499,9 @@ const QWeb = core.qweb;
async _chatbotTriggerNextStep() {
let triggerNextStep = true;
if (
this.messaging.livechatButtonView.chatbot.currentStep &&
this.messaging.livechatButtonView.chatbot.currentStep.data &&
this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'question_email'
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'question_email'
) {
triggerNextStep = await this._chatbotValidateEmail();
}
@@ -511,8 +511,8 @@ const QWeb = core.qweb;
}
const nextStep = await session.rpc('/chatbot/step/trigger', {
channel_uuid: this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._uuid,
chatbot_script_id: this.messaging.livechatButtonView.chatbot.scriptId,
channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
chatbot_script_id: this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.scriptId,
});
if (nextStep) {
@@ -520,12 +520,12 @@ const QWeb = core.qweb;
this._chatbotAddMessage(nextStep.chatbot_posted_message);
}
this.messaging.livechatButtonView.chatbot.update({ currentStep: insertAndReplace({ data: nextStep.chatbot_step }) });
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.update({ currentStep: insertAndReplace({ data: nextStep.chatbot_step }) });
this._chatbotProcessStep();
} else {
// did not find next step -> end the script
this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_is_last = true;
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_is_last = true;
this._renderMessages();
this._chatbotEndScript();
}
@@ -535,13 +535,13 @@ const QWeb = core.qweb;
return nextStep;
},
/**
* Returns the 'this.messaging.livechatButtonView.messages' filtered on our special 'welcome' ones.
* Returns the 'this.messaging.publicLivechatGlobal.livechatButtonView.messages' filtered on our special 'welcome' ones.
* See '_sendWelcomeChatbotMessage'.
*
* @private
*/
_getWelcomeMessages() {
return this.messaging.livechatButtonView.messages.filter((message) => {
return this.messaging.publicLivechatGlobal.livechatButtonView.messages.filter((message) => {
return message._id && typeof message._id === 'string' && message._id.startsWith('_welcome_');
});
},
@@ -551,8 +551,8 @@ const QWeb = core.qweb;
* @private
*/
_isLastMessageFromCustomer() {
const lastMessage = this.messaging.livechatButtonView.messages.length !== 0 ? this.messaging.livechatButtonView.messages[this.messaging.livechatButtonView.messages.length - 1] : null;
return lastMessage && lastMessage.getAuthorID() !== this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._operatorPID[0];
const lastMessage = this.messaging.publicLivechatGlobal.livechatButtonView.messages.length !== 0 ? this.messaging.publicLivechatGlobal.livechatButtonView.messages[this.messaging.publicLivechatGlobal.livechatButtonView.messages.length - 1] : null;
return lastMessage && lastMessage.getAuthorID() !== this.messaging.publicLivechatGlobal.publicLivechat.operator.id;
},
//--------------------------------------------------------------------------
@@ -570,16 +570,16 @@ const QWeb = core.qweb;
this._super(...arguments);
if (
this.messaging.livechatButtonView.chatbot &&
this.messaging.livechatButtonView.chatbot.currentStep &&
this.messaging.livechatButtonView.chatbot.currentStep.data
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data
) {
this.messaging.livechatButtonView.chatbot.currentStep.data.conversation_closed = true;
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.conversation_closed = true;
this._chatbotSaveSession();
}
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_main_restart').addClass('d-none');
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_end').hide();
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_composer_text_field')
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_main_restart').addClass('d-none');
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_end').hide();
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_composer_text_field')
.removeClass('d-none')
.val('');
},
@@ -597,17 +597,17 @@ const QWeb = core.qweb;
_openChatWindow() {
return this._super(...arguments).then(() => {
window.addEventListener('resize', () => {
if (this.messaging.livechatButtonView.chatWindow) {
this.messaging.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.scrollToBottom();
if (this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow) {
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.scrollToBottom();
}
});
if (
this.messaging.livechatButtonView.chatbot &&
this.messaging.livechatButtonView.chatbot.currentStep &&
this.messaging.livechatButtonView.chatbot.currentStep.data &&
this.messaging.livechatButtonView.messages &&
this.messaging.livechatButtonView.messages.length !== 0
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data &&
this.messaging.publicLivechatGlobal.livechatButtonView.messages &&
this.messaging.publicLivechatGlobal.livechatButtonView.messages.length !== 0
) {
this._chatbotProcessStep();
}
@@ -620,8 +620,8 @@ const QWeb = core.qweb;
_prepareGetSessionParameters() {
const parameters = this._super(...arguments);
if (this.messaging.livechatButtonView.isChatbot) {
parameters.chatbot_script_id = this.messaging.livechatButtonView.chatbot.scriptId;
if (this.messaging.publicLivechatGlobal.livechatButtonView.isChatbot) {
parameters.chatbot_script_id = this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.scriptId;
}
return parameters;
@@ -634,15 +634,15 @@ const QWeb = core.qweb;
const self = this;
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_thread_message:last .o_livechat_chatbot_options li').each(function () {
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_thread_message:last .o_livechat_chatbot_options li').each(function () {
$(this).on('click', self._onChatbotOptionClicked.bind(self));
});
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_main_restart').on('click',
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_main_restart').on('click',
this._onChatbotRestartScript.bind(this));
if (this.messaging.livechatButtonView.messages.length !== 0) {
const lastMessage = this.messaging.livechatButtonView.messages[this.messaging.livechatButtonView.messages.length - 1];
if (this.messaging.publicLivechatGlobal.livechatButtonView.messages.length !== 0) {
const lastMessage = this.messaging.publicLivechatGlobal.livechatButtonView.messages[this.messaging.publicLivechatGlobal.livechatButtonView.messages.length - 1];
const stepAnswers = lastMessage.getChatbotStepAnswers();
if (stepAnswers && stepAnswers.length !== 0 && !lastMessage.getChatbotStepAnswerId()) {
this._chatbotDisableInput(_t('Select an option above'));
@@ -672,34 +672,34 @@ const QWeb = core.qweb;
const superMethod = this._super;
if (
this.messaging.livechatButtonView.isChatbot &&
this.messaging.livechatButtonView.chatbot.currentStep &&
this.messaging.livechatButtonView.chatbot.currentStep.data
this.messaging.publicLivechatGlobal.livechatButtonView.isChatbot &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data
) {
await this._chatbotPostWelcomeMessages();
}
return superMethod.apply(this, superArguments).then(() => {
if (this.messaging.livechatButtonView.isChatbotRedirecting) {
if (this.messaging.publicLivechatGlobal.livechatButtonView.isChatbotRedirecting) {
return;
}
if (
this.messaging.livechatButtonView.isChatbot &&
this.messaging.livechatButtonView.chatbot.currentStep &&
this.messaging.livechatButtonView.chatbot.currentStep.data
this.messaging.publicLivechatGlobal.livechatButtonView.isChatbot &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data
) {
if (
this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'forward_operator' &&
this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_operator_found
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'forward_operator' &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_operator_found
) {
return; // operator has taken over the conversation, let them speak
} else if (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'free_input_multi') {
} else if (this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'free_input_multi') {
this._debouncedChatbotAwaitUserInput();
} else if (!this._chatbotShouldEndScript()) {
this._chatbotSetIsTyping();
this.nextStepTimeout = setTimeout(
this._chatbotTriggerNextStep.bind(this), this.messaging.livechatButtonView.chatbot.messageDelay);
this._chatbotTriggerNextStep.bind(this), this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay);
} else {
this._chatbotEndScript();
}
@@ -713,10 +713,10 @@ const QWeb = core.qweb;
* @private
*/
_sendWelcomeMessage() {
if (this.messaging.livechatButtonView.isChatbot) {
if (this.messaging.publicLivechatGlobal.livechatButtonView.isChatbot) {
this._sendWelcomeChatbotMessage(
0,
this.messaging.livechatButtonView.chatbotState === 'welcome' ? 0 : this.messaging.livechatButtonView.chatbot.messageDelay,
this.messaging.publicLivechatGlobal.livechatButtonView.chatbotState === 'welcome' ? 0 : this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay,
);
} else {
this._super(...arguments);
@@ -744,26 +744,33 @@ const QWeb = core.qweb;
* @private
*/
_sendWelcomeChatbotMessage(stepIndex, welcomeMessageDelay) {
const chatbotStep = this.messaging.livechatButtonView.chatbot.welcomeSteps[stepIndex];
this.messaging.livechatButtonView.chatbot.update({ currentStep: insertAndReplace({ data: chatbotStep }) });
const chatbotStep = this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.welcomeSteps[stepIndex];
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.update({ currentStep: insertAndReplace({ data: chatbotStep }) });
if (chatbotStep.chatbot_step_message) {
this._addMessage({
id: '_welcome_' + stepIndex,
is_discussion: true, // important for css style -> we only want white background for chatbot
attachment_ids: [],
author_id: this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._operatorPID,
author_id: (
this.messaging.publicLivechatGlobal.publicLivechat.operator
? [
this.messaging.publicLivechatGlobal.publicLivechat.operator.id,
this.messaging.publicLivechatGlobal.publicLivechat.operator.name,
]
: []
),
body: utils.Markup(chatbotStep.chatbot_step_message),
chatbot_script_step_id: chatbotStep.chatbot_script_step_id,
chatbot_step_answers: chatbotStep.chatbot_step_answers,
date: time.datetime_to_str(new Date()),
model: "mail.channel",
message_type: "comment",
res_id: this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._id,
res_id: this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat._id,
});
}
if (stepIndex + 1 < this.messaging.livechatButtonView.chatbot.welcomeSteps.length) {
if (stepIndex + 1 < this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.welcomeSteps.length) {
if (welcomeMessageDelay !== 0) {
this._chatbotSetIsTyping(true);
}
@@ -773,7 +780,7 @@ const QWeb = core.qweb;
this._renderMessages();
}, welcomeMessageDelay);
} else {
if (this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'forward_operator') {
if (this.messaging.publicLivechatGlobal.livechatButtonView.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
@@ -798,8 +805,8 @@ const QWeb = core.qweb;
* @private
*/
async _onChatbotRestartScript(ev) {
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_composer_text_field').removeClass('d-none');
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_end').hide();
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_composer_text_field').removeClass('d-none');
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_livechat_chatbot_end').hide();
if (this.nextStepTimeout) {
clearTimeout(this.nextStepTimeout);
@@ -810,25 +817,25 @@ const QWeb = core.qweb;
}
const postedMessage = await session.rpc('/chatbot/restart', {
channel_uuid: this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._uuid,
chatbot_script_id: this.messaging.livechatButtonView.chatbot.scriptId,
channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
chatbot_script_id: this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.scriptId,
});
if (postedMessage) {
this._chatbotAddMessage(postedMessage);
}
this.messaging.livechatButtonView.chatbot.update({ currentStep: clear() });
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.update({ currentStep: clear() });
this._chatbotSetIsTyping();
this.nextStepTimeout = setTimeout(
this._chatbotTriggerNextStep.bind(this), this.messaging.livechatButtonView.chatbot.messageDelay);
this._chatbotTriggerNextStep.bind(this), this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.messageDelay);
},
_onChatbotInputKeyDown() {
if (
this.messaging.livechatButtonView.chatbot.currentStep &&
this.messaging.livechatButtonView.chatbot.currentStep.data &&
this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'free_input_multi'
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data &&
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_step_type === 'free_input_multi'
) {
this._debouncedChatbotAwaitUserInput();
}
@@ -836,7 +843,7 @@ const QWeb = core.qweb;
/**
* Saves the selected chatbot.script.answer onto our chatbot.message.
* Will update the state of the related message (in this.messaging.livechatButtonView.messages) to set the selected option
* Will update the state of the related message (in this.messaging.publicLivechatGlobal.livechatButtonView.messages) to set the selected option
* as well which will in turn adapt the display to not show options anymore.
*
* This method also handles an optional redirection link placed on the chatbot.script.answer and
@@ -853,14 +860,14 @@ const QWeb = core.qweb;
const selectedAnswer = $target.data('chatbotStepAnswerId');
const redirectLink = $target.data('chatbotStepRedirectLink');
this.messaging.livechatButtonView.update({ isChatbotRedirecting: !!redirectLink });
this.messaging.publicLivechatGlobal.livechatButtonView.update({ isChatbotRedirecting: !!redirectLink });
await this._sendMessage({
content: $target.text().trim(),
});
let stepMessage = null;
for (const message of this.messaging.livechatButtonView.messages) {
for (const message of this.messaging.publicLivechatGlobal.livechatButtonView.messages) {
// we do NOT want to use a 'find' here because we want the LAST message that respects
// this condition.
// indeed, if you restart the script, you can have multiple messages with the same step id,
@@ -873,12 +880,12 @@ const QWeb = core.qweb;
}
const messageId = stepMessage.getID();
stepMessage.setChatbotStepAnswerId(selectedAnswer);
this.messaging.livechatButtonView.chatbot.currentStep.data.chatbot_selected_answer_id = selectedAnswer;
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.currentStep.data.chatbot_selected_answer_id = selectedAnswer;
this._renderMessages();
this._chatbotSaveSession();
const saveAnswerPromise = session.rpc('/chatbot/answer/save', {
channel_uuid: this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._uuid,
channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
message_id: messageId,
selected_answer_id: selectedAnswer,
});
@@ -1,20 +0,0 @@
odoo.define('im_livechat.legacy.im_livechat.Constants', function (require) {
"use strict";
// Constants
const LIVECHAT_COOKIE_HISTORY = 'im_livechat_history';
const HISTORY_LIMIT = 15;
const RATING_TO_EMOJI = {
"5": "😊",
"3": "😐",
"1": "😞"
};
return {
LIVECHAT_COOKIE_HISTORY,
HISTORY_LIMIT,
RATING_TO_EMOJI,
};
});
@@ -1,23 +0,0 @@
odoo.define('im_livechat.legacy.im_livechat.HistoryTracking', function (require) {
"use strict";
const utils = require('web.utils');
const { HISTORY_LIMIT, LIVECHAT_COOKIE_HISTORY } = require ("im_livechat.legacy.im_livechat.Constants");
// History tracking
const page = window.location.href.replace(/^.*\/\/[^/]+/, '');
const pageHistory = utils.get_cookie(LIVECHAT_COOKIE_HISTORY);
let urlHistory = [];
if (pageHistory) {
urlHistory = JSON.parse(pageHistory) || [];
}
if (!_.contains(urlHistory, page)) {
urlHistory.push(page);
while (urlHistory.length > HISTORY_LIMIT) {
urlHistory.shift();
}
utils.set_cookie(LIVECHAT_COOKIE_HISTORY, JSON.stringify(urlHistory), 60 * 60 * 24); // 1 day cookie
}
});
@@ -6,8 +6,6 @@ import session from 'web.session';
import utils from 'web.utils';
import Widget from 'web.Widget';
import { RATING_TO_EMOJI } from 'im_livechat.legacy.im_livechat.Constants';
const _t = core._t;
/*
* Rating for Livechat
@@ -28,11 +26,12 @@ const Feedback = Widget.extend({
/**
* @param {?} parent
* @param {Messaging} messaging
* @param {@im_livechat/legacy/models/public_livechat} livechat
*/
init(parent, livechat) {
init(parent, messaging, livechat) {
this._super(parent);
this._livechat = livechat;
this.messaging = messaging;
this.server_origin = session.origin;
this.rating = undefined;
this.dp = new concurrency.DropPrevious();
@@ -48,12 +47,12 @@ const Feedback = Widget.extend({
*/
_sendFeedback(reason) {
const args = {
uuid: this._livechat._uuid,
uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
rate: this.rating,
reason,
};
this.dp.add(session.rpc('/im_livechat/feedback', args)).then((response) => {
const emoji = RATING_TO_EMOJI[this.rating] || "??";
const emoji = this.messaging.publicLivechatGlobal.RATING_TO_EMOJI[this.rating] || "??";
let content;
if (!reason) {
content = utils.sprintf(_t("Rating: %s"), emoji);
@@ -125,7 +124,7 @@ const Feedback = Widget.extend({
this._rpc({
route: '/im_livechat/email_livechat_transcript',
params: {
uuid: this._livechat._uuid,
uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
email: $email.val(),
}
}).then(() => {
@@ -7,11 +7,9 @@ import time from 'web.time';
import utils from 'web.utils';
import Widget from 'web.Widget';
import { LIVECHAT_COOKIE_HISTORY } from 'im_livechat.legacy.im_livechat.Constants';
import Feedback from '@im_livechat/legacy/widgets/feedback/feedback';
import PublicLivechatMessage from '@im_livechat/legacy/models/public_livechat_message';
import { clear, increment, insert, insertAndReplace } from '@mail/model/model_field_command';
import { clear, insert, insertAndReplace } from '@mail/model/model_field_command';
const _t = core._t;
const QWeb = core.qweb;
@@ -31,49 +29,47 @@ const LivechatButton = Widget.extend({
init(parent, messaging) {
this._super(parent);
this.messaging = messaging;
this.options = _.defaults(this.messaging.publicLivechatGlobal.options || {});
},
async willStart() {
this.messaging.livechatButtonView.update({ widget: this });
this.messaging.publicLivechatGlobal.livechatButtonView.update({ widget: this });
const cookie = utils.get_cookie('im_livechat_session');
if (cookie) {
const channel = JSON.parse(cookie);
const history = await session.rpc('/mail/chat_history', {uuid: channel.uuid, limit: 100});
history.reverse();
this.messaging.livechatButtonView.update({ history });
for (const message of this.messaging.livechatButtonView.history) {
this.messaging.publicLivechatGlobal.livechatButtonView.update({ history });
for (const message of this.messaging.publicLivechatGlobal.livechatButtonView.history) {
message.body = utils.Markup(message.body);
}
} else {
const result = await session.rpc('/im_livechat/init', {channel_id: this.messaging.livechatButtonView.channelId});
const result = await session.rpc('/im_livechat/init', {channel_id: this.messaging.publicLivechatGlobal.livechatButtonView.channelId});
if (!result.available_for_me) {
return Promise.reject();
}
this.messaging.livechatButtonView.update({ rule: result.rule });
this.messaging.publicLivechatGlobal.livechatButtonView.update({ rule: result.rule });
}
return this._loadQWebTemplate();
},
start() {
this.$el.text(this.messaging.livechatButtonView.buttonText);
if (this.messaging.livechatButtonView.history) {
for (const m of this.messaging.livechatButtonView.history) {
this.$el.text(this.messaging.publicLivechatGlobal.livechatButtonView.buttonText);
if (this.messaging.publicLivechatGlobal.livechatButtonView.history) {
for (const m of this.messaging.publicLivechatGlobal.livechatButtonView.history) {
this._addMessage(m);
}
this._openChat();
} else if (!config.device.isMobile && this.messaging.livechatButtonView.rule.action === 'auto_popup') {
} else if (!config.device.isMobile && this.messaging.publicLivechatGlobal.livechatButtonView.rule.action === 'auto_popup') {
const autoPopupCookie = utils.get_cookie('im_livechat_auto_popup');
if (!autoPopupCookie || JSON.parse(autoPopupCookie)) {
this.messaging.livechatButtonView.update({
autoOpenChatTimeout: setTimeout(this._openChat.bind(this), this.messaging.livechatButtonView.rule.auto_popup_timer * 1000),
this.messaging.publicLivechatGlobal.livechatButtonView.update({
autoOpenChatTimeout: setTimeout(this._openChat.bind(this), this.messaging.publicLivechatGlobal.livechatButtonView.rule.auto_popup_timer * 1000),
});
}
}
this.call('bus_service', 'addEventListener', 'notification', this._onNotification.bind(this));
if (this.messaging.livechatButtonView.buttonBackgroundColor) {
this.$el.css('background-color', this.messaging.livechatButtonView.buttonBackgroundColor);
if (this.messaging.publicLivechatGlobal.livechatButtonView.buttonBackgroundColor) {
this.$el.css('background-color', this.messaging.publicLivechatGlobal.livechatButtonView.buttonBackgroundColor);
}
if (this.messaging.livechatButtonView.buttonTextColor) {
this.$el.css('color', this.messaging.livechatButtonView.buttonTextColor);
if (this.messaging.publicLivechatGlobal.livechatButtonView.buttonTextColor) {
this.$el.css('color', this.messaging.publicLivechatGlobal.livechatButtonView.buttonTextColor);
}
// If website_event_track installed, put the livechat banner above the PWA banner.
@@ -96,19 +92,16 @@ const LivechatButton = Widget.extend({
* @param {Object} [options={}]
*/
_addMessage(data, options) {
options = Object.assign({}, this.options, options, {
serverURL: this.messaging.livechatButtonView.serverUrl,
});
const message = new PublicLivechatMessage(this, data, options);
const message = new PublicLivechatMessage(this, this.messaging, data);
const hasAlreadyMessage = _.some(this.messaging.livechatButtonView.messages, function (msg) {
const hasAlreadyMessage = _.some(this.messaging.publicLivechatGlobal.livechatButtonView.messages, function (msg) {
return message.getID() === msg.getID();
});
if (hasAlreadyMessage) {
return;
}
if (this.messaging.livechatButtonView.publicLivechat) {
this.messaging.livechatButtonView.publicLivechat.update({
if (this.messaging.publicLivechatGlobal.publicLivechat) {
this.messaging.publicLivechatGlobal.publicLivechat.update({
messages: insert({
id: data.id,
legacyPublicLivechatMessage: message,
@@ -116,17 +109,17 @@ const LivechatButton = Widget.extend({
});
}
if (this.messaging.livechatButtonView.publicLivechat && this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat) {
this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat.addMessage(message);
if (this.messaging.publicLivechatGlobal.publicLivechat && this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat) {
this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat.addMessage(message);
}
if (options && options.prepend) {
this.messaging.livechatButtonView.update({
messages: [message, ...this.messaging.livechatButtonView.messages],
this.messaging.publicLivechatGlobal.livechatButtonView.update({
messages: [message, ...this.messaging.publicLivechatGlobal.livechatButtonView.messages],
});
} else {
this.messaging.livechatButtonView.update({
messages: [...this.messaging.livechatButtonView.messages, message],
this.messaging.publicLivechatGlobal.livechatButtonView.update({
messages: [...this.messaging.publicLivechatGlobal.livechatButtonView.messages, message],
});
}
},
@@ -134,86 +127,16 @@ const LivechatButton = Widget.extend({
* @private
*/
_askFeedback() {
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_thread_composer input').prop('disabled', true);
const feedback = new Feedback(this, this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat);
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.replaceContentWith(feedback);
feedback.on('send_message', this, this._sendMessage);
feedback.on('feedback_sent', this, this._closeChat);
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_thread_composer input').prop('disabled', true);
this.messaging.publicLivechatGlobal.update({ feedbackView: insertAndReplace() });
},
/**
* @private
*/
_closeChat() {
this.messaging.livechatButtonView.update({ chatWindow: clear() });
this.messaging.publicLivechatGlobal.livechatButtonView.update({ chatWindow: clear() });
utils.set_cookie('im_livechat_session', "", -1); // remove cookie
},
/**
* @private
* @param {Object} notification
* @param {Object} notification.payload
* @param {string} notification.type
*/
_handleNotification({ payload, type }) {
switch (type) {
case 'im_livechat.history_command': {
if (payload.id !== this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._id) {
return;
}
const cookie = utils.get_cookie(LIVECHAT_COOKIE_HISTORY);
const history = cookie ? JSON.parse(cookie) : [];
session.rpc('/im_livechat/history', {
pid: this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._operatorPID[0],
channel_uuid: this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._uuid,
page_history: history,
});
return;
}
case 'mail.channel.member/typing_status': {
if (payload.channel_id !== this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._id) {
return;
}
const partnerID = payload.partner_id;
if (partnerID === this.messaging.livechatButtonView.currentPartnerId) {
// ignore typing display of current partner.
return;
}
if (payload.is_typing) {
this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat.registerTyping({ partnerID });
} else {
this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat.unregisterTyping({ partnerID });
}
return;
}
case 'mail.channel/new_message': {
if (payload.id !== this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._id) {
return;
}
const notificationData = payload.message;
// If message from notif is already in chatter messages, stop handling
if (this.messaging.livechatButtonView.messages.some(message => message.getID() === notificationData.id)) {
return;
}
notificationData.body = utils.Markup(notificationData.body);
this._addMessage(notificationData);
if (this.messaging.livechatButtonView.chatWindow.legacyChatWindow._thread._folded || !this.messaging.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.isAtBottom()) {
this.messaging.livechatButtonView.publicLivechat.update({ unreadCounter: increment() });
}
this._renderMessages();
return;
}
case 'mail.message/insert': {
const message = this.messaging.livechatButtonView.messages.find(message => message._id === payload.id);
if (!message) {
return;
}
message._body = utils.Markup(payload.body);
this._renderMessages();
return;
}
}
},
/**
* @private
*/
@@ -228,18 +151,18 @@ const LivechatButton = Widget.extend({
* @private
*/
_openChat: _.debounce(function () {
if (this.messaging.livechatButtonView.isOpeningChat) {
if (this.messaging.publicLivechatGlobal.livechatButtonView.isOpeningChat) {
return;
}
const cookie = utils.get_cookie('im_livechat_session');
let def;
this.messaging.livechatButtonView.update({ isOpeningChat: true });
clearTimeout(this.messaging.livechatButtonView.autoOpenChatTimeout);
this.messaging.publicLivechatGlobal.livechatButtonView.update({ isOpeningChat: true });
clearTimeout(this.messaging.publicLivechatGlobal.livechatButtonView.autoOpenChatTimeout);
if (cookie) {
def = Promise.resolve(JSON.parse(cookie));
} else {
// re-initialize messages cache
this.messaging.livechatButtonView.update({ messages: clear() });
this.messaging.publicLivechatGlobal.livechatButtonView.update({ messages: clear() });
def = session.rpc(
'/im_livechat/get_session',
this._prepareGetSessionParameters(),
@@ -264,32 +187,29 @@ const LivechatButton = Widget.extend({
console.warn(_t("No available collaborator, please try again later."));
}
} else {
this.messaging.livechatButtonView.update({
this.messaging.publicLivechatGlobal.update({
publicLivechat: insertAndReplace({ data: livechatData }),
});
return this._openChatWindow().then(() => {
if (!this.messaging.livechatButtonView.history) {
if (!this.messaging.publicLivechatGlobal.livechatButtonView.history) {
this._sendWelcomeMessage();
}
this._renderMessages();
this.call('bus_service', 'addChannel', this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._uuid);
this.call('bus_service', 'startPolling');
this.messaging.publicLivechatGlobal.update({ notificationHandler: insertAndReplace() });
utils.set_cookie('im_livechat_session', utils.unaccent(JSON.stringify(this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat.toData()), true), 60 * 60);
utils.set_cookie('im_livechat_session', utils.unaccent(JSON.stringify(this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat.toData()), true), 60 * 60);
utils.set_cookie('im_livechat_auto_popup', JSON.stringify(false), 60 * 60);
if (livechatData.operator_pid[0]) {
// livechatData.operator_pid contains a tuple (id, name)
// we are only interested in the id
const operatorPidId = livechatData.operator_pid[0];
if (this.messaging.publicLivechatGlobal.publicLivechat.operator) {
const operatorPidId = this.messaging.publicLivechatGlobal.publicLivechat.operator.id;
const oneWeek = 7 * 24 * 60 * 60;
utils.set_cookie('im_livechat_previous_operator_pid', operatorPidId, oneWeek);
}
});
}
}).then(() => {
this.messaging.livechatButtonView.update({ isOpeningChat: false });
this.messaging.publicLivechatGlobal.livechatButtonView.update({ isOpeningChat: false });
}).guardedCatch(() => {
this.messaging.livechatButtonView.update({ isOpeningChat: false });
this.messaging.publicLivechatGlobal.livechatButtonView.update({ isOpeningChat: false });
});
}, 200, true),
/**
@@ -315,11 +235,11 @@ const LivechatButton = Widget.extend({
* @return {Promise}
*/
_openChatWindow() {
this.messaging.livechatButtonView.update({ chatWindow: insertAndReplace() });
return this.messaging.livechatButtonView.chatWindow.legacyChatWindow.appendTo($('body')).then(() => {
this.messaging.publicLivechatGlobal.livechatButtonView.update({ chatWindow: insertAndReplace() });
return this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.appendTo($('body')).then(() => {
const cssProps = { bottom: 0 };
cssProps[_t.database.parameters.direction === 'rtl' ? 'left' : 'right'] = 0;
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$el.css(cssProps);
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$el.css(cssProps);
this.$el.hide();
});
},
@@ -328,8 +248,8 @@ const LivechatButton = Widget.extend({
*/
_prepareGetSessionParameters() {
return {
channel_id: this.messaging.livechatButtonView.channelId,
anonymous_name: this.messaging.livechatButtonView.defaultUsername,
channel_id: this.messaging.publicLivechatGlobal.livechatButtonView.channelId,
anonymous_name: this.messaging.publicLivechatGlobal.livechatButtonView.defaultUsername,
previous_operator_id: this._get_previous_operator_id(),
};
},
@@ -337,11 +257,11 @@ const LivechatButton = Widget.extend({
* @private
*/
_renderMessages() {
const shouldScroll = !this.messaging.livechatButtonView.chatWindow.legacyChatWindow._thread._folded && this.messaging.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.isAtBottom();
this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._messages = this.messaging.livechatButtonView.messages;
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.render();
const shouldScroll = !this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow._thread._folded && this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.isAtBottom();
this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat._messages = this.messaging.publicLivechatGlobal.livechatButtonView.messages;
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.render();
if (shouldScroll) {
this.messaging.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.scrollToBottom();
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.scrollToBottom();
}
},
/**
@@ -350,9 +270,9 @@ const LivechatButton = Widget.extend({
* @return {Promise}
*/
_sendMessage(message) {
this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._notifyMyselfTyping({ typing: false });
this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat._notifyMyselfTyping({ typing: false });
return session
.rpc('/mail/chat_post', { uuid: this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._uuid, message_content: message.content })
.rpc('/mail/chat_post', { uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid, message_content: message.content })
.then((messageId) => {
if (!messageId) {
try {
@@ -373,22 +293,25 @@ const LivechatButton = Widget.extend({
}
this._closeChat();
}
this.messaging.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.scrollToBottom();
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.scrollToBottom();
});
},
/**
* @private
*/
_sendWelcomeMessage() {
if (this.messaging.livechatButtonView.defaultMessage) {
if (this.messaging.publicLivechatGlobal.livechatButtonView.defaultMessage) {
this._addMessage({
id: '_welcome',
attachment_ids: [],
author_id: this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._operatorPID,
body: this.messaging.livechatButtonView.defaultMessage,
author_id: [
this.messaging.publicLivechatGlobal.publicLivechat.operator.id,
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.livechatButtonView.publicLivechat.legacyPublicLivechat._id,
res_id: this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat._id,
}, { prepend: true });
}
},
@@ -403,28 +326,18 @@ const LivechatButton = Widget.extend({
*/
_onCloseChatWindow(ev) {
ev.stopPropagation();
const isComposerDisabled = this.messaging.livechatButtonView.chatWindow.legacyChatWindow.$('.o_thread_composer input').prop('disabled');
const shouldAskFeedback = !isComposerDisabled && this.messaging.livechatButtonView.messages.find(function (message) {
const isComposerDisabled = this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.$('.o_thread_composer input').prop('disabled');
const shouldAskFeedback = !isComposerDisabled && this.messaging.publicLivechatGlobal.livechatButtonView.messages.find(function (message) {
return message.getID() !== '_welcome';
});
if (shouldAskFeedback) {
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.toggleFold(false);
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.toggleFold(false);
this._askFeedback();
} else {
this._closeChat();
}
this._visitorLeaveSession();
},
/**
* @private
* @param {CustomEvent} ev
* @param {Array[]} [ev.detail] Notifications coming from the bus.
*/
_onNotification({ detail: notifications }) {
for (let notification of notifications) {
this._handleNotification(notification);
}
},
/**
* @private
* @param {OdooEvent} ev
@@ -444,7 +357,7 @@ const LivechatButton = Widget.extend({
*/
_onSaveChatWindow(ev) {
ev.stopPropagation();
utils.set_cookie('im_livechat_session', utils.unaccent(JSON.stringify(this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat.toData()), true), 60 * 60);
utils.set_cookie('im_livechat_session', utils.unaccent(JSON.stringify(this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat.toData()), true), 60 * 60);
},
/**
* @private
@@ -452,7 +365,7 @@ const LivechatButton = Widget.extend({
*/
_onUpdatedTypingPartners(ev) {
ev.stopPropagation();
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.renderHeader();
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.renderHeader();
},
/**
* @private
@@ -460,7 +373,7 @@ const LivechatButton = Widget.extend({
*/
_onUpdatedUnreadCounter(ev) {
ev.stopPropagation();
this.messaging.livechatButtonView.chatWindow.legacyChatWindow.renderHeader();
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.renderHeader();
},
/**
* @private
@@ -32,16 +32,13 @@ const PublicLivechatWindow = Widget.extend({
* @param {Widget} parent
* @param {Messaging} messaging
* @param {@im_livechat/legacy/models/public_livechat} thread
* @param {Object} [options={}]
* @param {string} [options.headerBackgroundColor]
* @param {string} [options.titleColor]
* @param {Object} options
* @param {string} options.titleColor
*/
init(parent, messaging, thread, options) {
this._super(parent);
this.messaging = messaging;
this.options = _.defaults(options || {}, {
placeholder: _t("Say something"),
});
this.options = options;
this._thread = thread;
@@ -77,8 +74,8 @@ const PublicLivechatWindow = Widget.extend({
this._publicLivechatView.$el.on('scroll', this, this._debouncedOnScroll);
});
await Promise.all([this._super(), def]);
if (this.options.headerBackgroundColor) {
this.$('.o_thread_window_header').css('background-color', this.options.headerBackgroundColor);
if (this.messaging.publicLivechatGlobal.livechatButtonView.headerBackgroundColor) {
this.$('.o_thread_window_header').css('background-color', this.messaging.publicLivechatGlobal.livechatButtonView.headerBackgroundColor);
}
if (this.options.titleColor) {
this.$('.o_thread_window_header').css('color', this.options.titleColor);
@@ -122,10 +119,10 @@ const PublicLivechatWindow = Widget.extend({
renderHeader() {
this.$header.html(
qweb.render('im_livechat.legacy.PublicLivechatWindow.HeaderContent', {
status: this._thread._status,
status: this.messaging.publicLivechatGlobal.publicLivechat.status,
thread: this._thread,
title: this._thread._name,
unreadCounter: this.messaging.livechatButtonView.publicLivechat.unreadCounter,
title: this.messaging.publicLivechatGlobal.publicLivechat.name,
unreadCounter: this.messaging.publicLivechatGlobal.publicLivechat.unreadCounter,
widget: this,
})
);
@@ -150,9 +147,9 @@ const PublicLivechatWindow = Widget.extend({
*/
toggleFold(folded) {
if (!_.isBoolean(folded)) {
folded = !this._thread._folded;
folded = !this.messaging.publicLivechatGlobal.publicLivechat.isFolded;
}
this._updateThreadFoldState(folded);
this.messaging.publicLivechatGlobal.publicLivechat.update({ isFolded: folded });
this.trigger_up('save_chat_window');
this.updateVisualFoldState();
},
@@ -214,19 +211,6 @@ const PublicLivechatWindow = Widget.extend({
this._publicLivechatView.scrollToBottom();
});
},
/**
* Update the fold state of the thread.
*
* This function is called when toggling the fold state of this window.
* If there is no thread linked to this window, it means this is the
* "blank" thread window, therefore we use the internal state 'folded'
*
* @private
* @param {boolean} folded
*/
_updateThreadFoldState(folded) {
this._thread.fold(folded);
},
//--------------------------------------------------------------------------
// Handlers
@@ -243,7 +227,7 @@ const PublicLivechatWindow = Widget.extend({
ev.stopPropagation();
ev.preventDefault();
if (
this.messaging.livechatButtonView.publicLivechat.unreadCounter > 0 &&
this.messaging.publicLivechatGlobal.publicLivechat.unreadCounter > 0 &&
!this._thread._folded
) {
this._thread.markAsRead();
@@ -7,16 +7,16 @@
<div class="o_thread_window o_in_home_menu" t-att-data-thread-id="widget._thread._id">
<div class="o_thread_window_header">
<t t-call="im_livechat.legacy.PublicLivechatWindow.HeaderContent">
<t t-set="status" t-value="widget._thread._status"/>
<t t-set="title" t-value="widget._thread._name"/>
<t t-set="unreadCounter" t-value="widget.messaging.livechatButtonView.publicLivechat.unreadCounter"/>
<t t-set="status" t-value="widget.messaging.publicLivechatGlobal.publicLivechat.status"/>
<t t-set="title" t-value="widget.messaging.publicLivechatGlobal.publicLivechat.name"/>
<t t-set="unreadCounter" t-value="widget.messaging.publicLivechatGlobal.publicLivechat.unreadCounter"/>
<t t-set="thread" t-value="widget._thread"/>
</t>
</div>
<div class="o_thread_window_content">
</div>
<div class="o_thread_composer o_chat_mini_composer">
<input class="o_composer_text_field" t-att-placeholder="widget.options.placeholder"/>
<input class="o_composer_text_field" t-att-placeholder="widget.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.inputPlaceholder"/>
</div>
</div>
</t>
@@ -6,7 +6,7 @@ import { clear, insertAndReplace } from '@mail/model/model_field_command';
registerModel({
name: 'LivechatButtonView',
identifyingFields: ['messaging'],
identifyingFields: ['publicLivechatGlobalOwner'],
recordMethods: {
/**
* @private
@@ -266,9 +266,10 @@ registerModel({
messages: attr({
default: [],
}),
publicLivechat: one('PublicLivechat', {
inverse: 'livechatButtonOwner',
isCausal: true,
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
inverse: 'livechatButtonView',
readonly: true,
required: true,
}),
rule: attr(),
serverUrl: attr({
@@ -0,0 +1,16 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { attr } from '@mail/model/model_field';
registerModel({
name: 'LivechatOperator',
identifyingFields: ['id'],
fields: {
id: attr({
readonly: true,
required: true,
}),
name: attr(),
},
});
@@ -1,16 +1,12 @@
/** @odoo-module **/
import { addFields, addRecordMethods, patchRecordMethods } from '@mail/model/model_core';
import { addFields, patchRecordMethods } from '@mail/model/model_core';
import { many, one } from '@mail/model/model_field';
import { clear, insertAndReplace } from '@mail/model/model_field_command';
import { clear } from '@mail/model/model_field_command';
// ensure that the model definition is loaded before the patch
import '@mail/models/messaging';
addFields('Messaging', {
livechatButtonView: one('LivechatButtonView', {
compute: '_computeLivechatButtonView',
isCausal: true,
}),
/**
* All pinned livechats that are known.
*/
@@ -23,19 +19,6 @@ addFields('Messaging', {
}),
});
addRecordMethods('Messaging', {
/**
* @private
* @returns {FieldCommand}
*/
_computeLivechatButtonView() {
if (this.publicLivechatGlobal && this.publicLivechatGlobal.isAvailable) {
return insertAndReplace();
}
return clear();
},
});
patchRecordMethods('Messaging', {
/**
* @override
@@ -4,15 +4,16 @@ import PublicLivechat from '@im_livechat/legacy/models/public_livechat';
import { registerModel } from '@mail/model/model_core';
import { attr, many, one } from '@mail/model/model_field';
import { clear, insertAndReplace } from '@mail/model/model_field_command';
registerModel({
name: 'PublicLivechat',
identifyingFields: ['livechatButtonOwner'],
identifyingFields: ['publicLivechatGlobalOwner'],
lifecycleHooks: {
_created() {
this.update({
legacyPublicLivechat: new PublicLivechat(this.messaging, {
parent: this.livechatButtonOwner.widget,
parent: this.publicLivechatGlobalOwner.livechatButtonView.widget,
data: this.data,
}),
});
@@ -22,19 +23,83 @@ registerModel({
},
},
recordMethods: {
/**
* @private
* @returns {FieldCommand|string}
*/
_computeName() {
if (!this.data) {
return clear();
}
return this.data.name;
},
/**
* @private
* @returns {FieldCommand}
*/
_computeOperator() {
if (!this.data) {
return clear();
}
if (!this.data.operator_pid) {
return clear();
}
if (!this.data.operator_pid[0]) {
return clear();
}
return insertAndReplace({
id: this.data.operator_pid[0],
name: this.data.operator_pid[1],
});
},
/**
* @private
* @returns {FieldCommand|string}
*/
_computeStatus() {
if (!this.data) {
return clear();
}
return this.data.status || '';
},
/**
* @private
* @returns {FieldCommand|string}
*/
_computeUuid() {
if (!this.data) {
return clear();
}
return this.data.uuid;
},
},
fields: {
data: attr(),
isFolded: attr({
default: false,
}),
legacyPublicLivechat: attr(),
livechatButtonOwner: one('LivechatButtonView', {
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
inverse: 'publicLivechat',
readonly: true,
required: true,
}),
messages: many('PublicLivechatMessage'),
name: attr({
compute: '_computeName',
}),
operator: one('LivechatOperator', {
compute: '_computeOperator',
}),
status: attr({
compute: '_computeStatus',
}),
// amount of messages that have not yet been read on this chat
unreadCounter: attr({
default: 0,
}),
uuid: attr({
compute: '_computeUuid',
}),
},
});
@@ -0,0 +1,44 @@
/** @odoo-module **/
import Feedback from '@im_livechat/legacy/widgets/feedback/feedback';
import { registerModel } from '@mail/model/model_core';
import { attr, one } from '@mail/model/model_field';
registerModel({
name: 'PublicLivechatFeedbackView',
identifyingFields: ['publicLivechatGlobalOwner'],
lifecycleHooks: {
_created() {
this.update({
widget: new Feedback(
this.messaging.publicLivechatGlobal.livechatButtonView.widget,
this.messaging,
this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat,
),
});
this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow.replaceContentWith(this.widget);
this.widget.on('feedback_sent', null, this._onFeedbackSent);
this.widget.on('send_message', null, this._onSendMessage);
},
_willDelete() {
this.widget.destroy();
},
},
recordMethods: {
_onFeedbackSent(...args) {
this.messaging.publicLivechatGlobal.livechatButtonView.widget._closeChat(...args);
},
_onSendMessage(...args) {
this.messaging.publicLivechatGlobal.livechatButtonView.widget._sendMessage(...args);
},
},
fields: {
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
inverse: 'feedbackView',
readonly: true,
required: true,
}),
widget: attr(),
},
});
@@ -1,19 +1,82 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { attr } from '@mail/model/model_field';
import { attr, one } from '@mail/model/model_field';
import { clear, insertAndReplace } from '@mail/model/model_field_command';
import utils from 'web.utils';
registerModel({
name: 'PublicLivechatGlobal',
identifyingFields: ['messaging'],
lifecycleHooks: {
_created() {
// History tracking
const page = window.location.href.replace(/^.*\/\/[^/]+/, '');
const pageHistory = utils.get_cookie(this.LIVECHAT_COOKIE_HISTORY);
let urlHistory = [];
if (pageHistory) {
urlHistory = JSON.parse(pageHistory) || [];
}
if (!_.contains(urlHistory, page)) {
urlHistory.push(page);
while (urlHistory.length > this.HISTORY_LIMIT) {
urlHistory.shift();
}
utils.set_cookie(this.LIVECHAT_COOKIE_HISTORY, JSON.stringify(urlHistory), 60 * 60 * 24); // 1 day cookie
}
},
},
recordMethods: {
/**
* @private
* @returns {FieldCommand}
*/
_computeLivechatButtonView() {
if (this.isAvailable) {
return insertAndReplace();
}
return clear();
},
},
fields: {
HISTORY_LIMIT: attr({
default: 15,
}),
LIVECHAT_COOKIE_HISTORY: attr({
default: 'im_livechat_history',
}),
RATING_TO_EMOJI: attr({
default: {
5: "😊",
3: "😐",
1: "😞",
},
}),
chatbotServerUrl: attr(),
feedbackView: one('PublicLivechatFeedbackView', {
inverse: 'publicLivechatGlobalOwner',
isCausal: true,
}),
isAvailable: attr({
default: false,
}),
livechatButtonView: one('LivechatButtonView', {
compute: '_computeLivechatButtonView',
inverse: 'publicLivechatGlobalOwner',
isCausal: true,
}),
notificationHandler: one('PublicLivechatGlobalNotificationHandler', {
inverse: 'publicLivechatGlobalOwner',
isCausal: true,
}),
options: attr({
default: {},
}),
publicLivechat: one('PublicLivechat', {
inverse: 'publicLivechatGlobalOwner',
isCausal: true,
}),
serverUrl: attr({
default: '',
}),
@@ -0,0 +1,104 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { one } from '@mail/model/model_field';
import { increment } from '@mail/model/model_field_command';
import session from 'web.session';
import utils from 'web.utils';
registerModel({
name: 'PublicLivechatGlobalNotificationHandler',
identifyingFields: ['publicLivechatGlobalOwner'],
lifecycleHooks: {
_created() {
this.env.services['bus_service'].addChannel(this.messaging.publicLivechatGlobal.publicLivechat.uuid);
this.env.services['bus_service'].startPolling();
this.env.services['bus_service'].addEventListener('notification', this._onNotification);
},
},
recordMethods: {
/**
* @private
* @param {Object} notification
* @param {Object} notification.payload
* @param {string} notification.type
*/
_handleNotification({ payload, type }) {
switch (type) {
case 'im_livechat.history_command': {
if (payload.id !== this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat._id) {
return;
}
const cookie = utils.get_cookie(this.messaging.publicLivechatGlobal.LIVECHAT_COOKIE_HISTORY);
const history = cookie ? JSON.parse(cookie) : [];
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 (payload.channel_id !== this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat._id) {
return;
}
const partnerID = payload.partner_id;
if (partnerID === this.messaging.publicLivechatGlobal.livechatButtonView.currentPartnerId) {
// ignore typing display of current partner.
return;
}
if (payload.is_typing) {
this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat.registerTyping({ partnerID });
} else {
this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat.unregisterTyping({ partnerID });
}
return;
}
case 'mail.channel/new_message': {
if (payload.id !== this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat._id) {
return;
}
const notificationData = payload.message;
// If message from notif is already in chatter messages, stop handling
if (this.messaging.publicLivechatGlobal.livechatButtonView.messages.some(message => message.getID() === notificationData.id)) {
return;
}
notificationData.body = utils.Markup(notificationData.body);
this.messaging.publicLivechatGlobal.livechatButtonView.widget._addMessage(notificationData);
if (this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow._thread._folded || !this.messaging.publicLivechatGlobal.livechatButtonView.chatWindow.legacyChatWindow._publicLivechatView.isAtBottom()) {
this.messaging.publicLivechatGlobal.publicLivechat.update({ unreadCounter: increment() });
}
this.messaging.publicLivechatGlobal.livechatButtonView.widget._renderMessages();
return;
}
case 'mail.message/insert': {
const message = this.messaging.publicLivechatGlobal.livechatButtonView.messages.find(message => message._id === payload.id);
if (!message) {
return;
}
message._body = utils.Markup(payload.body);
this.messaging.publicLivechatGlobal.livechatButtonView.widget._renderMessages();
return;
}
}
},
/**
* @private
* @param {CustomEvent} ev
* @param {Array[]} [ev.detail] Notifications coming from the bus.
*/
_onNotification({ detail: notifications }) {
for (const notification of notifications) {
this._handleNotification(notification);
}
},
},
fields: {
publicLivechatGlobalOwner: one('PublicLivechatGlobal', {
inverse: 'notificationHandler',
readonly: true,
required: true,
}),
},
});
@@ -14,12 +14,8 @@ registerModel({
legacyChatWindow: new PublicLivechatWindow(
this.livechatButtonViewOwner.widget,
this.messaging,
this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat,
{
headerBackgroundColor: this.livechatButtonViewOwner.headerBackgroundColor,
placeholder: this.livechatButtonViewOwner.inputPlaceholder,
titleColor: this.livechatButtonViewOwner.titleColor,
},
this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat,
{ titleColor: this.livechatButtonViewOwner.titleColor },
),
});
},
@@ -27,7 +23,22 @@ registerModel({
this.legacyChatWindow.destroy();
},
},
recordMethods: {
/**
* @private
* @returns {string}
*/
_computeInputPlaceholder() {
if (this.messaging.publicLivechatGlobal.livechatButtonView.inputPlaceholder) {
return this.messaging.publicLivechatGlobal.livechatButtonView.inputPlaceholder;
}
return this.env._t("Say something");
},
},
fields: {
inputPlaceholder: attr({
compute: '_computeInputPlaceholder',
}),
legacyChatWindow: attr({
default: null,
}),
@@ -8,7 +8,7 @@ export const publicLivechatService = {
dependencies: ['messaging'],
async start(env, { messaging: messagingService }) {
const messaging = await messagingService.get();
if (messaging.livechatButtonView) {
if (messaging.publicLivechatGlobal.livechatButtonView) {
const livechatButton = new LivechatButton(rootWidget, messaging);
livechatButton.appendTo(document.body);
}
@@ -17,8 +17,8 @@ LivechatButton.include({
* from backend to a website visitor.
*/
willStart: function () {
if (this.options.chat_request_session) {
utils.set_cookie('im_livechat_session', JSON.stringify(this.options.chat_request_session), 60*60);
if (this.messaging.publicLivechatGlobal.options.chat_request_session) {
utils.set_cookie('im_livechat_session', JSON.stringify(this.messaging.publicLivechatGlobal.options.chat_request_session), 60*60);
}
return this._super();
},
@@ -31,8 +31,8 @@ LivechatButton.include({
$(window).trigger('resize');
await this._super(...arguments);
this.el.innerHTML = "";
if (this.messaging.livechatButtonView.buttonText && !config.device.touch) {
this.el.dataset.content = this.messaging.livechatButtonView.buttonText;
if (this.messaging.publicLivechatGlobal.livechatButtonView.buttonText && !config.device.touch) {
this.el.dataset.content = this.messaging.publicLivechatGlobal.livechatButtonView.buttonText;
this.el.dataset.toggle = "popover";
this.el.dataset.trigger = "hover";
this.$el.popover({
@@ -21,20 +21,20 @@ const LivechatButtonTestChatbot = LivechatButton.extend({
init: function (parent, messaging, chatbotData) {
this._super(...arguments);
this.messaging.livechatButtonView.update({
this.messaging.publicLivechatGlobal.livechatButtonView.update({
rule: {
'action': 'auto_popup',
'auto_popup_timer': 0,
},
});
this.messaging.livechatButtonView.update({ isTestChatbot: true });
this.messaging.livechatButtonView.update({ testChatbotData: chatbotData.chatbot });
this.messaging.livechatButtonView.chatbot.update({
this.messaging.publicLivechatGlobal.livechatButtonView.update({ isTestChatbot: true });
this.messaging.publicLivechatGlobal.livechatButtonView.update({ testChatbotData: chatbotData.chatbot });
this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.update({
currentStep: insertAndReplace({
data: this.messaging.livechatButtonView.chatbot.lastWelcomeStep,
data: this.messaging.publicLivechatGlobal.livechatButtonView.chatbot.lastWelcomeStep,
}),
});
this._channelData = this.messaging.livechatButtonView.testChatbotData.channel;
this._channelData = this.messaging.publicLivechatGlobal.livechatButtonView.testChatbotData.channel;
},
/**
@@ -58,17 +58,17 @@ const LivechatButtonTestChatbot = LivechatButton.extend({
* @override
*/
_openChat: function () {
this.messaging.livechatButtonView.update({
this.messaging.publicLivechatGlobal.update({
publicLivechat: insertAndReplace({ data: this._channelData }),
});
return this._openChatWindow().then(() => {
this._sendWelcomeMessage();
this._renderMessages();
this.call('bus_service', 'addChannel', this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._uuid);
this.call('bus_service', 'addChannel', this.messaging.publicLivechatGlobal.publicLivechat.uuid);
this.call('bus_service', 'startPolling');
utils.set_cookie('im_livechat_session', utils.unaccent(JSON.stringify(this.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat.toData()), true), 60 * 60);
this.messaging.livechatButtonView.update({ isOpeningChat: false });
utils.set_cookie('im_livechat_session', utils.unaccent(JSON.stringify(this.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat.toData()), true), 60 * 60);
this.messaging.publicLivechatGlobal.livechatButtonView.update({ isOpeningChat: false });
});
},
});
@@ -87,7 +87,7 @@ publicWidget.registry.livechatChatbotTestScript = publicWidget.Widget.extend({
utils.set_cookie('im_livechat_auto_popup', '', -1);
utils.set_cookie('im_livechat_history', '', -1);
utils.set_cookie('im_livechat_previous_operator_pid', '', -1);
const messaging = this.env.services.messaging.get();
const messaging = await this.env.services.messaging.get();
return this._super(...arguments).then(() => {
messaging.update({
publicLivechatGlobal: insertAndReplace({ isAvailable: true, chatbotServerUrl: this.$el.data().serverUrl }),
@@ -11,7 +11,7 @@ LivechatButton.include({
* Let us also debounce waiting for more user inputs for only 500ms.
*/
start: function () {
this.messaging.livechatButtonView.update({ isWebsiteLivechatChatbotFlow: true });
this.messaging.publicLivechatGlobal.livechatButtonView.update({ isWebsiteLivechatChatbotFlow: true });
this._debouncedChatbotAwaitUserInput = _.debounce(
this._chatbotAwaitUserInput.bind(this),
@@ -25,7 +25,7 @@ LivechatButton.include({
* the thread and making it hard to write proper jQuery selectors in the tour.
*/
_onChatbotRestartScript: function () {
this.messaging.livechatButtonView.update({ messages: clear() });
this.messaging.publicLivechatGlobal.livechatButtonView.update({ messages: clear() });
this._renderMessages();
this._super(...arguments);
@@ -27,12 +27,12 @@ LivechatButton.include({
else {
session.rpc('/bus/test_mode_activated', {}).then(function (in_test_mode) {
if (in_test_mode) {
self._handleNotification({
self.messaging.publicLivechatGlobal.notificationHandler._handleNotification({
type: 'mail.channel/new_message',
payload: {
id: self.messaging.livechatButtonView.publicLivechat.legacyPublicLivechat._id,
id: self.messaging.publicLivechatGlobal.publicLivechat.legacyPublicLivechat._id,
message: {
id: self.messaging.livechatButtonView.messages.length + 1,
id: self.messaging.publicLivechatGlobal.livechatButtonView.messages.length + 1,
author_id: [0, 'Website Visitor Test'],
email_from: 'Website Visitor Test',
body: utils.Markup('<p>' + message.content + '</p>'),