[IMP] im_livechat, *: Refactor JS Public Livechat (step 16)
*: 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-2965839 closes odoo/odoo#99137 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
@@ -41,14 +41,6 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
|
||||
/**
|
||||
* Initialize the internal data for typing feature on threads.
|
||||
*
|
||||
* Also listens on some internal events of the thread:
|
||||
*
|
||||
* - 'message_added': when a message is added, remove the author in the
|
||||
* typing partners.
|
||||
* - 'message_posted': when a message is posted, let the user have the
|
||||
* possibility to immediately notify if he types something right away,
|
||||
* instead of waiting for a throttle behaviour.
|
||||
*/
|
||||
|
||||
// Store the last "myself typing" status that has been sent to the
|
||||
@@ -104,9 +96,6 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
// something, in order to display the oldest typing partners.
|
||||
this._typingPartnerIDs = [];
|
||||
|
||||
this.on('message_added', this, this._onTypingMessageAdded);
|
||||
this.on('message_posted', this, this._onTypingMessagePosted);
|
||||
|
||||
if (params.data.message_unread_counter !== undefined) {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({
|
||||
unreadCounter: params.data.message_unread_counter
|
||||
@@ -125,12 +114,23 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Add a message to this thread.
|
||||
* Called when a new message is added to the thread
|
||||
* On receiving a message from a typing partner, unregister this partner
|
||||
* from typing partners (otherwise, it will still display it until timeout).
|
||||
*
|
||||
* Note that it only unregister typing operators.
|
||||
*
|
||||
* Note that in the frontend, there is no way to identify a message that is
|
||||
* from the current user, because there is no partner ID in the session and
|
||||
* a message with an author sets the partner ID of the author.
|
||||
*
|
||||
* @param {@im_livechat/legacy/models/public_livechat_message} message
|
||||
*/
|
||||
addMessage(message) {
|
||||
this.trigger('message_added', message);
|
||||
const operatorID = this.messaging.publicLivechatGlobal.publicLivechat.operator.id;
|
||||
if (message.hasAuthor() && message.getAuthorID() === operatorID) {
|
||||
this.unregisterTyping({ partnerID: operatorID });
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
@@ -215,19 +215,27 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
markAsRead() {
|
||||
if (this.messaging.publicLivechatGlobal.publicLivechat.unreadCounter > 0) {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({ unreadCounter: 0 });
|
||||
this.trigger_up('updated_unread_counter');
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.renderHeader();
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
/**
|
||||
* Post a message on this thread
|
||||
* Called when current user has posted a message on this thread.
|
||||
*
|
||||
* @returns {Promise} resolved with the message object to be sent to the
|
||||
* server
|
||||
* The current user receives the possibility to immediately notify the
|
||||
* other users if he is typing something else.
|
||||
*
|
||||
* Refresh the context for the current user to notify that he starts or
|
||||
* stops typing something. In other words, when this function is called and
|
||||
* then the current user types something, it immediately notifies the
|
||||
* server as if it is the first time he is typing something.
|
||||
*/
|
||||
async postMessage() {
|
||||
this.trigger('message_posted');
|
||||
this._lastNotifiedMyselfTyping = false;
|
||||
this._throttleNotifyMyselfTyping.clear();
|
||||
this._myselfLongTypingTimer.clear();
|
||||
this._myselfTypingInactivityTimer.clear();
|
||||
},
|
||||
/**
|
||||
* Register someone that is currently typing something in this thread.
|
||||
@@ -342,7 +350,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
* @private
|
||||
*/
|
||||
_warnUpdatedTypingPartners() {
|
||||
this.trigger_up('updated_typing_partners');
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.renderHeader();
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -404,45 +412,6 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
|
||||
_onOthersTypingTimeout(partnerID) {
|
||||
this.unregisterTyping({ partnerID });
|
||||
},
|
||||
/**
|
||||
* Called when a new message is added to the thread
|
||||
* On receiving a message from a typing partner, unregister this partner
|
||||
* from typing partners (otherwise, it will still display it until timeout).
|
||||
*
|
||||
* Note that it only unregister typing operators.
|
||||
*
|
||||
* Note that in the frontend, there is no way to identify a message that is
|
||||
* from the current user, because there is no partner ID in the session and
|
||||
* a message with an author sets the partner ID of the author.
|
||||
*
|
||||
* @private
|
||||
* @param {mail.model.AbstractMessage} message
|
||||
*/
|
||||
_onTypingMessageAdded(message) {
|
||||
const operatorID = this.messaging.publicLivechatGlobal.publicLivechat.operator.id;
|
||||
if (message.hasAuthor() && message.getAuthorID() === operatorID) {
|
||||
this.unregisterTyping({ partnerID: operatorID });
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Called when current user has posted a message on this thread.
|
||||
*
|
||||
* The current user receives the possibility to immediately notify the
|
||||
* other users if he is typing something else.
|
||||
*
|
||||
* Refresh the context for the current user to notify that he starts or
|
||||
* stops typing something. In other words, when this function is called and
|
||||
* then the current user types something, it immediately notifies the
|
||||
* server as if it is the first time he is typing something.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onTypingMessagePosted() {
|
||||
this._lastNotifiedMyselfTyping = false;
|
||||
this._throttleNotifyMyselfTyping.clear();
|
||||
this._myselfLongTypingTimer.clear();
|
||||
this._myselfTypingInactivityTimer.clear();
|
||||
},
|
||||
});
|
||||
|
||||
export default PublicLivechat;
|
||||
|
||||
@@ -43,7 +43,7 @@ const PublicLivechatMessage = Class.extend({
|
||||
this._defaultUsername = this.messaging.publicLivechatGlobal.options.default_username;
|
||||
this._serverURL = this.messaging.publicLivechatGlobal.serverUrl;
|
||||
|
||||
if (parent.messaging.publicLivechatGlobal.livechatButtonView.isChatbot) {
|
||||
if (this.messaging.publicLivechatGlobal.chatbot.isActive) {
|
||||
this._chatbotStepId = data.chatbot_script_step_id;
|
||||
this._chatbotStepAnswers = data.chatbot_step_answers;
|
||||
this._chatbotStepAnswerId = data.chatbot_selected_answer_id;
|
||||
@@ -66,14 +66,6 @@ const PublicLivechatMessage = Class.extend({
|
||||
}
|
||||
return this._serverAuthorID[0];
|
||||
},
|
||||
/**
|
||||
* Threads do not have an im status by default
|
||||
*
|
||||
* @return {undefined}
|
||||
*/
|
||||
getAuthorImStatus() {
|
||||
return undefined;
|
||||
},
|
||||
/**
|
||||
* Get the relative url of the avatar to display next to the message
|
||||
*
|
||||
@@ -154,34 +146,6 @@ const PublicLivechatMessage = Class.extend({
|
||||
getID() {
|
||||
return this._id;
|
||||
},
|
||||
/**
|
||||
* Gets the class to use as the notification icon.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
getNotificationIcon() {
|
||||
if (!this.hasNotificationsError()) {
|
||||
return 'fa fa-envelope-o';
|
||||
}
|
||||
return 'fa fa-envelope';
|
||||
},
|
||||
/**
|
||||
* Gets the list of notifications of this message, in no specific order.
|
||||
* By default messages do not have notifications.
|
||||
*
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
getNotifications() {
|
||||
return [];
|
||||
},
|
||||
/**
|
||||
* Gets the text to display next to the notification icon.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
getNotificationText() {
|
||||
return '';
|
||||
},
|
||||
/**
|
||||
* Get the time elapsed between sent message and now
|
||||
*
|
||||
@@ -207,68 +171,13 @@ const PublicLivechatMessage = Class.extend({
|
||||
hasAuthor() {
|
||||
return !!(this._serverAuthorID && this._serverAuthorID[0]);
|
||||
},
|
||||
/**
|
||||
* State whether this message has an email of its sender.
|
||||
* By default, messages do not have any email of its sender.
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
hasEmailFrom() {
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* States whether this message has some notifications.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasNotifications() {
|
||||
return this.getNotifications().length > 0;
|
||||
},
|
||||
/**
|
||||
* States whether this message has notifications that are in error.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasNotificationsError() {
|
||||
return this.getNotifications().some(notif =>
|
||||
notif.notification_status === 'exception' ||
|
||||
notif.notification_status === 'bounce'
|
||||
);
|
||||
},
|
||||
/**
|
||||
* State whether this message has a subject
|
||||
* By default, messages do not have any subject.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasSubject() {
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* State whether this message is empty
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
isEmpty() {
|
||||
return !this.hasTrackingValues() &&
|
||||
!this.getBody();
|
||||
},
|
||||
/**
|
||||
* By default, messages do not have any subtype description
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasSubtypeDescription() {
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* State whether this message contains some tracking values
|
||||
* By default, messages do not have any tracking values.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasTrackingValues() {
|
||||
return false;
|
||||
return !this.getBody();
|
||||
},
|
||||
/**
|
||||
* State whether this message is a discussion
|
||||
@@ -278,24 +187,6 @@ const PublicLivechatMessage = Class.extend({
|
||||
isDiscussion() {
|
||||
return this._isDiscussion;
|
||||
},
|
||||
/**
|
||||
* State whether this message is linked to a document thread
|
||||
* By default, messages are not linked to a document thread.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
isLinkedToDocumentThread() {
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* State whether this message is needaction
|
||||
* By default, messages are not needaction.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
isNeedaction() {
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* State whether this message is a note (i.e. a message from "Log note")
|
||||
*
|
||||
@@ -320,33 +211,6 @@ const PublicLivechatMessage = Class.extend({
|
||||
isNotification() {
|
||||
return this._isNotification;
|
||||
},
|
||||
/**
|
||||
* State whether this message is starred
|
||||
* By default, messages are not starred.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
isStarred() {
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* State whether this message is a system notification
|
||||
* By default, messages are not system notifications
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
isSystemNotification() {
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* States whether the current message needs moderation in general.
|
||||
* By default, messages do not require any moderation.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
needsModeration() {
|
||||
return false;
|
||||
},
|
||||
setChatbotStepAnswerId(chatbotStepAnswerId) {
|
||||
this._chatbotStepAnswerId = chatbotStepAnswerId;
|
||||
},
|
||||
|
||||
@@ -223,11 +223,6 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.o_thread_tooltip_container {
|
||||
display: inline;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.o_thread_date_separator {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 30px;
|
||||
@@ -419,11 +414,6 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
margin-bottom: $grid-gutter-width;
|
||||
}
|
||||
}
|
||||
|
||||
.o_thread_tooltip_container {
|
||||
display: inline;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,10 +422,6 @@ $o-mail-thread-window-zindex: $zindex-modal + 1 !default;
|
||||
}
|
||||
}
|
||||
|
||||
.o_web_client .popover .o_thread_tooltip_icon {
|
||||
min-width: 1rem;
|
||||
}
|
||||
|
||||
.o_web_client.o_touch_device {
|
||||
.o_mail_thread .o_thread_icon {
|
||||
opacity: $o-mail-thread-icon-opacity;
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import core from 'web.core';
|
||||
import session from 'web.session';
|
||||
import time from 'web.time';
|
||||
import utils from 'web.utils';
|
||||
|
||||
import LivechatButton from '@im_livechat/legacy/widgets/livechat_button';
|
||||
|
||||
const _t = core._t;
|
||||
|
||||
/**
|
||||
* Override of the LivechatButton to include chatbot capabilities.
|
||||
* Main changes / hooking points are:
|
||||
@@ -19,42 +16,6 @@ const _t = core._t;
|
||||
*/
|
||||
LivechatButton.include({
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private - Chatbot specifics
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A special case is handled for email steps, where we first validate the email (server side)
|
||||
* and we allow the user to try again in case the format is incorrect.
|
||||
*
|
||||
* The validation is made server-side to have the same test when we validate here and when we
|
||||
* register the answer, but also to easily post a message as the bot ("Sorry, try again...").
|
||||
*
|
||||
* Returns a boolean stating whether the email was valid or not.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async _chatbotValidateEmail() {
|
||||
let emailValidResult = await session.rpc('/chatbot/step/validate_email', {
|
||||
channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid,
|
||||
});
|
||||
|
||||
if (emailValidResult.success) {
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep.data.is_email_valid = true;
|
||||
this.messaging.publicLivechatGlobal.chatbot.saveSession();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// email is not valid, let the user try again
|
||||
this.messaging.publicLivechatGlobal.chatWindow.enableInput();
|
||||
if (emailValidResult.posted_message) {
|
||||
this.messaging.publicLivechatGlobal.chatbot.addMessage(emailValidResult.posted_message);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private - LiveChat Overrides
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -66,42 +27,18 @@ const _t = core._t;
|
||||
_prepareGetSessionParameters() {
|
||||
const parameters = this._super(...arguments);
|
||||
|
||||
if (this.messaging.publicLivechatGlobal.livechatButtonView.isChatbot) {
|
||||
if (this.messaging.publicLivechatGlobal.chatbot.isActive) {
|
||||
parameters.chatbot_script_id = this.messaging.publicLivechatGlobal.chatbot.scriptId;
|
||||
}
|
||||
|
||||
return parameters;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_renderMessages() {
|
||||
this._super(...arguments);
|
||||
|
||||
const self = this;
|
||||
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_thread_message:last .o_livechat_chatbot_options li').each(function () {
|
||||
$(this).on('click', self._onChatbotOptionClicked.bind(self));
|
||||
});
|
||||
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.$('.o_livechat_chatbot_main_restart').on('click',
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.onChatbotRestartScript
|
||||
);
|
||||
|
||||
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()) {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.disableInput(_t('Select an option above'));
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Small override to handle chatbot welcome message(s).
|
||||
* @private
|
||||
*/
|
||||
_sendWelcomeMessage() {
|
||||
if (this.messaging.publicLivechatGlobal.livechatButtonView.isChatbot) {
|
||||
if (this.messaging.publicLivechatGlobal.chatbot.isActive) {
|
||||
this._sendWelcomeChatbotMessage(
|
||||
0,
|
||||
this.messaging.publicLivechatGlobal.chatbot.state === 'welcome' ? 0 : this.messaging.publicLivechatGlobal.chatbot.messageDelay,
|
||||
@@ -165,7 +102,7 @@ const _t = core._t;
|
||||
this.messaging.publicLivechatGlobal.chatbot.update({
|
||||
welcomeMessageTimeout: setTimeout(() => {
|
||||
this._sendWelcomeChatbotMessage(stepIndex + 1, welcomeMessageDelay);
|
||||
this._renderMessages();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.renderMessages();
|
||||
}, welcomeMessageDelay),
|
||||
});
|
||||
} else {
|
||||
@@ -227,7 +164,7 @@ const _t = core._t;
|
||||
const messageId = stepMessage.id;
|
||||
stepMessage.widget.setChatbotStepAnswerId(selectedAnswer);
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep.data.chatbot_selected_answer_id = selectedAnswer;
|
||||
this._renderMessages();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.renderMessages();
|
||||
this.messaging.publicLivechatGlobal.chatbot.saveSession();
|
||||
|
||||
const saveAnswerPromise = session.rpc('/chatbot/answer/save', {
|
||||
|
||||
@@ -6,13 +6,6 @@ import Widget from 'web.Widget';
|
||||
|
||||
const LivechatButton = Widget.extend({
|
||||
className: 'openerp o_livechat_button d-print-none',
|
||||
custom_events: {
|
||||
'close_chat_window': '_onCloseChatWindow',
|
||||
'post_message_chat_window': '_onPostMessageChatWindow',
|
||||
'save_chat_window': '_onSaveChatWindow',
|
||||
'updated_typing_partners': '_onUpdatedTypingPartners',
|
||||
'updated_unread_counter': '_onUpdatedUnreadCounter',
|
||||
},
|
||||
events: {
|
||||
'click': '_onClick'
|
||||
},
|
||||
@@ -57,16 +50,6 @@ const LivechatButton = Widget.extend({
|
||||
previous_operator_id: this._get_previous_operator_id(),
|
||||
};
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_renderMessages() {
|
||||
const shouldScroll = !this.messaging.publicLivechatGlobal.publicLivechat.isFolded && this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.isAtBottom();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.render();
|
||||
if (shouldScroll) {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.scrollToBottom();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -96,63 +79,6 @@ const LivechatButton = Widget.extend({
|
||||
_onClick() {
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.openChat();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {OdooEvent} ev
|
||||
*/
|
||||
_onCloseChatWindow(ev) {
|
||||
ev.stopPropagation();
|
||||
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();
|
||||
} else {
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.closeChat();
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.leaveSession();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {OdooEvent} ev
|
||||
* @param {Object} ev.data.messageData
|
||||
*/
|
||||
async _onPostMessageChatWindow(ev) {
|
||||
ev.stopPropagation();
|
||||
const messageData = ev.data.messageData;
|
||||
try {
|
||||
await this.messaging.publicLivechatGlobal.livechatButtonView.sendMessage(messageData);
|
||||
} catch (reason) {
|
||||
reason.event.preventDefault();
|
||||
return this.messaging.publicLivechatGlobal.livechatButtonView.sendMessage(messageData); // try again just in case
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {OdooEvent} ev
|
||||
*/
|
||||
_onSaveChatWindow(ev) {
|
||||
ev.stopPropagation();
|
||||
utils.set_cookie('im_livechat_session', utils.unaccent(JSON.stringify(this.messaging.publicLivechatGlobal.publicLivechat.widget.toData()), true), 60 * 60);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {OdooEvent} ev
|
||||
*/
|
||||
_onUpdatedTypingPartners(ev) {
|
||||
ev.stopPropagation();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.renderHeader();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {OdooEvent} ev
|
||||
*/
|
||||
_onUpdatedUnreadCounter(ev) {
|
||||
ev.stopPropagation();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.widget.renderHeader();
|
||||
},
|
||||
});
|
||||
|
||||
export default LivechatButton;
|
||||
|
||||
+3
-97
@@ -37,8 +37,6 @@ const PublicLivechatView = Widget.extend({
|
||||
'click .o_thread_message': '_onClickMessage',
|
||||
'click': '_onClick',
|
||||
'click .o_thread_message_notification_error': '_onClickMessageNotificationError',
|
||||
'click .o_thread_message_moderation': '_onClickMessageModeration',
|
||||
'change .moderation_checkbox': '_onChangeModerationCheckbox',
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -55,25 +53,13 @@ const PublicLivechatView = Widget.extend({
|
||||
this._enabledOptions = _.defaults(options || {}, {
|
||||
displayOrder: ORDER.ASC,
|
||||
displayMarkAsRead: true,
|
||||
displayModerationCommands: false,
|
||||
displayDocumentLinks: true,
|
||||
displayAvatars: true,
|
||||
squashCloseMessages: true,
|
||||
loadMoreOnScroll: false,
|
||||
});
|
||||
// options when the thread is disabled
|
||||
this._disabledOptions = {
|
||||
displayOrder: this._enabledOptions.displayOrder,
|
||||
displayMarkAsRead: false,
|
||||
displayModerationCommands: false,
|
||||
displayDocumentLinks: false,
|
||||
displayAvatars: this._enabledOptions.displayAvatars,
|
||||
squashCloseMessages: false,
|
||||
loadMoreOnScroll: this._enabledOptions.loadMoreOnScroll,
|
||||
};
|
||||
this._selectedMessageID = null;
|
||||
this._currentThreadID = null;
|
||||
this._messageMailPopover = null;
|
||||
},
|
||||
/**
|
||||
* The message mail popover may still be shown at this moment. If we do not
|
||||
@@ -83,9 +69,6 @@ const PublicLivechatView = Widget.extend({
|
||||
*/
|
||||
destroy() {
|
||||
clearInterval(this._updateTimestampsInterval);
|
||||
if (this._messageMailPopover) {
|
||||
this._messageMailPopover.popover('hide');
|
||||
}
|
||||
this._super();
|
||||
},
|
||||
/**
|
||||
@@ -97,7 +80,6 @@ const PublicLivechatView = Widget.extend({
|
||||
* @param {boolean} [options.displayLoadMore]
|
||||
* @param {Array} [options.domain=[]] the domain for the messages in the
|
||||
* thread.
|
||||
* @param {boolean} [options.isCreateMode]
|
||||
* @param {boolean} [options.scrollToBottom=false]
|
||||
* @param {boolean} [options.squashCloseMessages]
|
||||
*/
|
||||
@@ -111,8 +93,7 @@ const PublicLivechatView = Widget.extend({
|
||||
// copy so that reverse do not alter order in the thread object
|
||||
const messages = _.clone(this.messaging.publicLivechatGlobal.publicLivechat.widget.getMessages());
|
||||
|
||||
const modeOptions = options.isCreateMode ? this._disabledOptions :
|
||||
this._enabledOptions;
|
||||
const modeOptions = this._enabledOptions;
|
||||
|
||||
options = Object.assign({}, modeOptions, options, {
|
||||
selectedMessageID: this._selectedMessageID,
|
||||
@@ -136,19 +117,7 @@ const PublicLivechatView = Widget.extend({
|
||||
prevMessage.getType() !== 'comment' ||
|
||||
message.getType() !== 'comment' ||
|
||||
// from a different author
|
||||
(prevMessage.getAuthorID() !== message.getAuthorID()) ||
|
||||
(
|
||||
// messages are linked to a document thread
|
||||
(
|
||||
prevMessage.isLinkedToDocumentThread() &&
|
||||
message.isLinkedToDocumentThread()
|
||||
) &&
|
||||
(
|
||||
// are from different documents
|
||||
prevMessage.getDocumentModel() !== message.getDocumentModel() ||
|
||||
prevMessage.getDocumentID() !== message.getDocumentID()
|
||||
)
|
||||
)
|
||||
prevMessage.getAuthorID() !== message.getAuthorID()
|
||||
) {
|
||||
displayAuthorMessages[message.getID()] = true;
|
||||
} else {
|
||||
@@ -162,11 +131,11 @@ const PublicLivechatView = Widget.extend({
|
||||
}
|
||||
|
||||
this.$el.html(QWeb.render('im_livechat.legacy.mail.widget.Thread', {
|
||||
thread: this.messaging.publicLivechatGlobal.publicLivechat,
|
||||
displayAuthorMessages,
|
||||
options,
|
||||
ORDER,
|
||||
dateFormat: time.getLangDatetimeFormat(),
|
||||
widget: this,
|
||||
}));
|
||||
|
||||
for (let message of messages) {
|
||||
@@ -185,8 +154,6 @@ const PublicLivechatView = Widget.extend({
|
||||
this._updateTimestamps();
|
||||
}, 1000 * 60);
|
||||
}
|
||||
|
||||
this._renderMessageNotificationPopover(messages);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -258,15 +225,6 @@ const PublicLivechatView = Widget.extend({
|
||||
this.scrollToBottom();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Toggle all the moderation checkboxes in the thread
|
||||
*
|
||||
* @param {boolean} checked if true, check the boxes,
|
||||
* otherwise uncheck them.
|
||||
*/
|
||||
toggleModerationCheckboxes(checked) {
|
||||
this.$('.moderation_checkbox').prop('checked', checked);
|
||||
},
|
||||
/**
|
||||
* Unselect the selected message
|
||||
*/
|
||||
@@ -374,38 +332,6 @@ const PublicLivechatView = Widget.extend({
|
||||
this.trigger('redirect', options.model, options.id);
|
||||
}
|
||||
}, 500, true),
|
||||
/**
|
||||
* Render the popover when mouse-hovering on the notification icon of a
|
||||
* message in the thread.
|
||||
* There is at most one such popover at any given time.
|
||||
*
|
||||
* @private
|
||||
* @param {@im_livechat/legacy/models/public_livechat_message[]} messages list of messages in the
|
||||
* rendered thread, for which popover on mouseover interaction is
|
||||
* permitted.
|
||||
*/
|
||||
_renderMessageNotificationPopover(messages) {
|
||||
if (this._messageMailPopover) {
|
||||
this._messageMailPopover.popover('hide');
|
||||
}
|
||||
if (!this.$('.o_thread_tooltip').length) {
|
||||
return;
|
||||
}
|
||||
this._messageMailPopover = this.$('.o_thread_tooltip').popover({
|
||||
html: true,
|
||||
boundary: 'viewport',
|
||||
placement: 'auto',
|
||||
trigger: 'hover',
|
||||
offset: '0, 1',
|
||||
content() {
|
||||
const messageID = $(this).data('message-id');
|
||||
const message = messages.find(message => message.getID() === messageID);
|
||||
return QWeb.render('im_livechat.legacy.mail.widget.Thread.Message.MailTooltip', {
|
||||
notifications: message.getNotifications(),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -424,13 +350,6 @@ const PublicLivechatView = Widget.extend({
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
_onChangeModerationCheckbox(ev) {
|
||||
this.trigger_up('update_moderation_buttons');
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -494,19 +413,6 @@ const PublicLivechatView = Widget.extend({
|
||||
const messageID = $(ev.currentTarget).data('message-id');
|
||||
this.trigger('toggle_star_status', messageID);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
_onClickMessageModeration(ev) {
|
||||
const $button = $(ev.currentTarget);
|
||||
const messageID = $button.data('message-id');
|
||||
const decision = $button.data('decision');
|
||||
this.trigger_up('message_moderation', {
|
||||
messageID,
|
||||
decision,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} ev
|
||||
|
||||
+3
-66
@@ -5,12 +5,11 @@
|
||||
@param {mail.model.AbstractThread} thread
|
||||
@param {Object} options
|
||||
@param {boolean} [options.displayEmptyThread]
|
||||
@param {boolean} [options.displayModerationCommands]
|
||||
@param {boolean} [options.displayNoMatchFound]
|
||||
@param {Array} [options.domain=[]] the domain to restrict messages on the thread.
|
||||
-->
|
||||
<t t-name="im_livechat.legacy.mail.widget.Thread">
|
||||
<t t-if="thread.widget.hasMessages()">
|
||||
<t t-if="widget.messaging.publicLivechatGlobal.publicLivechat.widget.hasMessages()">
|
||||
<t t-call="im_livechat.legacy.mail.widget.Thread.Content"/>
|
||||
</t>
|
||||
</t>
|
||||
@@ -43,7 +42,7 @@
|
||||
|
||||
-->
|
||||
<t t-name="im_livechat.legacy.mail.widget.Thread.Content">
|
||||
<t t-set="messages" t-value="thread.widget.getMessages({ 'domain': options.domain || [] })"/>
|
||||
<t t-set="messages" t-value="widget.messaging.publicLivechatGlobal.publicLivechat.widget.getMessages({ 'domain': options.domain || [] })"/>
|
||||
<t t-if="options.displayOrder === ORDER.ASC" t-call="im_livechat.legacy.mail.widget.Thread.Content.ASC"/>
|
||||
<t t-else="" t-call="im_livechat.legacy.mail.widget.Thread.Content.DESC"/>
|
||||
</t>
|
||||
@@ -128,8 +127,6 @@
|
||||
@param {boolean} [options.displayAvatars]
|
||||
@param {boolean} [options.displayDocumentLinks]
|
||||
@param {boolean} [options.displayMarkAsRead]
|
||||
@param {boolean} [options.displayModerationCommands] when set, display the moderation commands on
|
||||
the message. This includes the moderation checkboxes (needs a control panel such as in Discuss app).
|
||||
@param {boolean} [options.displaySubjectsOnMessages]
|
||||
@param {string|integer} [options.messagesSeparatorPosition] 'top' or
|
||||
message ID, the separator is placed just after this message.
|
||||
@@ -147,7 +144,6 @@
|
||||
t-att-data-oe-id="message.shouldRedirectToAuthor() ? message.getAuthorID() : ''"
|
||||
t-attf-class="o_thread_message_avatar rounded-circle #{message.shouldRedirectToAuthor() ? 'o_mail_redirect' : ''}"/>
|
||||
<t t-call="im_livechat.legacy.mail.UserStatus">
|
||||
<t t-set="status" t-value="message.getAuthorImStatus()"/>
|
||||
<t t-set="partnerID" t-value="message.getAuthorID()"/>
|
||||
</t>
|
||||
</div>
|
||||
@@ -173,24 +169,13 @@
|
||||
t-attf-class="o_thread_author o_PublicLivechatMessage_headerAuthor #{message.shouldRedirectToAuthor() ? 'o_mail_redirect' : ''}">
|
||||
<t t-esc="message.getDisplayedAuthor()"/>
|
||||
</strong>
|
||||
<strong t-elif="message.hasEmailFrom()">
|
||||
<a class="text-muted" t-attf-href="mailto:#{message.getEmailFrom()}?subject=Re: #{message.hasSubject() ? message.getSubject() : ''}">
|
||||
<t t-esc="message.getEmailFrom()"/>
|
||||
</a>
|
||||
</strong>
|
||||
<strong t-else="" class="o_thread_author o_PublicLivechatMessage_headerAuthor">
|
||||
<t t-esc="message.getDisplayedAuthor()"/>
|
||||
</strong>
|
||||
</t>
|
||||
|
||||
<span t-if="!message.isVisitorTheAuthor()" class="o_PublicLivechatMessage_headerDatePrefix">-</span> <small class="o_mail_timestamp o_PublicLivechatMessage_headerDate" t-att-title="message.getDate().format(dateFormat)"><t t-esc="message.getTimeElapsed()"/></small>
|
||||
<t t-if="message.isLinkedToDocumentThread() and options.displayDocumentLinks">
|
||||
<small>on</small> <a t-att-href="message.getURL()" t-att-data-oe-model="message.getDocumentModel()" t-att-data-oe-id="message.getDocumentID()" class="o_document_link"><t t-esc="message.getDocumentName()"/></a>
|
||||
</t>
|
||||
<span t-attf-class="o_thread_icons">
|
||||
<i t-if="message.isNeedaction() and options.displayMarkAsRead"
|
||||
class="fa fa-check o_thread_icon o_thread_message_needaction"
|
||||
t-att-data-message-id="message.getID()" title="Mark as Read" role="img" aria-label="Mark as Read"/>
|
||||
</span>
|
||||
</p>
|
||||
<div t-att-data-message-id="message.getID()" t-attf-class="o_PublicLivechatMessage_bubbleWrap {{ message.isVisitorTheAuthor() ? 'o-isVisitorTheAuthor' : '' }} {{ message.isOperatorTheAuthor() ? 'o-isOperatorTheAuthor' : '' }}">
|
||||
@@ -198,12 +183,6 @@
|
||||
<div t-attf-class="o_PublicLivechatMessage_background {{ message.isVisitorTheAuthor() ? 'o-isVisitorTheAuthor' : '' }} {{ message.isOperatorTheAuthor() ? 'o-isOperatorTheAuthor' : '' }}"/>
|
||||
<div class="o_thread_message_content o_PublicLivechatMessage_content">
|
||||
<t t-out="message.getBody()"/>
|
||||
<t t-if="message.hasTrackingValues()">
|
||||
<t t-if="message.hasSubtypeDescription()">
|
||||
<p><t t-esc="message.getSubtypeDescription()"/></p>
|
||||
</t>
|
||||
<t t-call="im_livechat.legacy.mail.widget.Thread.MessageTracking"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -213,44 +192,6 @@
|
||||
<t t-call="im_livechat.legacy.mail.MessagesSeparator"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<!--
|
||||
@param {Object[]} notifications: list of notifications
|
||||
A notification is an object with at least the following keys:
|
||||
{notification_status, partner_name}
|
||||
-->
|
||||
<t t-name="im_livechat.legacy.mail.widget.Thread.Message.MailTooltip">
|
||||
<div t-foreach="notifications" t-as="notification">
|
||||
<span name="notification_status" class="d-inline-block text-center o_thread_tooltip_icon">
|
||||
<i t-if="notification.notification_status === 'sent'" class='fa fa-check' title="Sent" role="img" aria-label="Sent"/>
|
||||
<i t-if="notification.notification_status === 'bounce'" class='fa fa-exclamation text-danger' title="Bounced" role="img" aria-label="Bounced"/>
|
||||
<i t-if="notification.notification_status === 'exception'" class='fa fa-exclamation text-danger' title="Error" role="img" aria-label="Error"/>
|
||||
<i t-if="notification.notification_status === 'ready'" class='fa fa-send-o' title="Ready" role="img" aria-label="Ready"/>
|
||||
<i t-if="notification.notification_status === 'canceled'" class='fa fa-trash-o' title="Canceled" role="img" aria-label="Canceled"/>
|
||||
</span>
|
||||
<span name="partner_name" t-esc="notification.partner_name"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!--
|
||||
@param {mail.model.Message} message
|
||||
-->
|
||||
<t t-name="im_livechat.legacy.mail.widget.Thread.MessageTracking">
|
||||
<ul class="o_mail_thread_message_tracking">
|
||||
<t t-foreach='message.getTrackingValues()' t-as='value'>
|
||||
<li>
|
||||
<t t-esc="value.changed_field"/>:
|
||||
<t t-if="value.old_value">
|
||||
<span> <t t-esc="value.old_value || ((value.field_type !== 'boolean') and '')"/> </span>
|
||||
<span t-if="value.old_value !== value.new_value" class="fa fa-long-arrow-right" role="img" aria-label="Changed" title="Changed"/>
|
||||
</t>
|
||||
<span t-if="value.old_value !== value.new_value">
|
||||
<t t-esc="value.new_value || ((value.field_type !== 'boolean') and '')"/>
|
||||
</span>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</t>
|
||||
|
||||
<!--
|
||||
@param {Object} options
|
||||
@@ -272,11 +213,7 @@
|
||||
@param {integer|undefined} [partnerID]
|
||||
-->
|
||||
<t t-name="im_livechat.legacy.mail.UserStatus">
|
||||
<span t-att-class="partnerID ? 'o_updatable_im_status' : ''" t-att-data-partner-id="partnerID">
|
||||
<i t-if="status == 'online'" class="o_mail_user_status o_user_online fa fa-circle" title="Online" role="img" aria-label="User is online"/>
|
||||
<i t-if="status == 'away'" class="o_mail_user_status o_user_idle fa fa-circle" title="Idle" role="img" aria-label="User is idle"/>
|
||||
<i t-if="status == 'offline'" class="o_mail_user_status fa fa-circle-o" title="Offline" role="img" aria-label="User is offline"/>
|
||||
</span>
|
||||
<span t-att-class="partnerID ? 'o_updatable_im_status' : ''" t-att-data-partner-id="partnerID"/>
|
||||
</t>
|
||||
|
||||
<t t-name="im_livechat.legacy.mail.MessagesSeparator">
|
||||
|
||||
+20
-4
@@ -4,6 +4,8 @@ import config from 'web.config';
|
||||
import { _t, qweb } from 'web.core';
|
||||
import Widget from 'web.Widget';
|
||||
|
||||
import { set_cookie, unaccent } from 'web.utils';
|
||||
|
||||
/**
|
||||
* This is the widget that represent windows of livechat in the frontend.
|
||||
*
|
||||
@@ -74,7 +76,17 @@ const PublicLivechatWindow = Widget.extend({
|
||||
* @override
|
||||
*/
|
||||
close() {
|
||||
this.trigger_up('close_chat_window');
|
||||
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();
|
||||
} else {
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.closeChat();
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.leaveSession();
|
||||
},
|
||||
/**
|
||||
* States whether the current environment is in mobile or not. This is
|
||||
@@ -125,7 +137,7 @@ const PublicLivechatWindow = Widget.extend({
|
||||
folded = !this.messaging.publicLivechatGlobal.publicLivechat.isFolded;
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({ isFolded: folded });
|
||||
this.trigger_up('save_chat_window');
|
||||
set_cookie('im_livechat_session', unaccent(JSON.stringify(this.messaging.publicLivechatGlobal.publicLivechat.widget.toData()), true), 60 * 60);
|
||||
this.updateVisualFoldState();
|
||||
},
|
||||
/**
|
||||
@@ -179,8 +191,12 @@ const PublicLivechatWindow = Widget.extend({
|
||||
* @private
|
||||
* @param {Object} messageData
|
||||
*/
|
||||
_postMessage(messageData) {
|
||||
this.trigger_up('post_message_chat_window', { messageData });
|
||||
async _postMessage(messageData) {
|
||||
try {
|
||||
await this.messaging.publicLivechatGlobal.livechatButtonView.sendMessage(messageData);
|
||||
} catch (_err) {
|
||||
await this.messaging.publicLivechatGlobal.livechatButtonView.sendMessage(messageData); // try again just in case
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.postMessage(messageData)
|
||||
.then(() => {
|
||||
this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.scrollToBottom();
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@
|
||||
<a href="#" class="o_thread_window_close fa fa-1x fa-arrow-left" aria-label="Close chat window" title="Close chat window"/>
|
||||
</span>
|
||||
<span class="o_thread_window_title">
|
||||
<t t-esc="widget.messaging.publicLivechatGlobal.publicLivechat.title"/>
|
||||
<t t-esc="widget.messaging.publicLivechatGlobal.publicLivechat.name"/>
|
||||
<span t-if="widget.messaging.publicLivechatGlobal.publicLivechat.unreadCounter"> (<t t-esc="widget.messaging.publicLivechatGlobal.publicLivechat.unreadCounter"/>)</span>
|
||||
<t t-if="widget.messaging.publicLivechatGlobal.publicLivechat.widget.isSomeoneTyping()" t-call="im_livechat.legacy.mail.ThreadTypingIcon"/>
|
||||
</span>
|
||||
|
||||
@@ -25,7 +25,7 @@ registerModel({
|
||||
}
|
||||
|
||||
if (!options || !options.skipRenderMessages) {
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.widget._renderMessages();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.renderMessages();
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -99,7 +99,7 @@ registerModel({
|
||||
});
|
||||
});
|
||||
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.widget._renderMessages();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.renderMessages();
|
||||
},
|
||||
/**
|
||||
* Processes the step, depending on the current state of the script and the author of the last
|
||||
@@ -259,7 +259,7 @@ registerModel({
|
||||
this.currentStep.data &&
|
||||
this.currentStep.data.chatbot_step_type === 'question_email'
|
||||
) {
|
||||
triggerNextStep = await this.messaging.publicLivechatGlobal.livechatButtonView.widget._chatbotValidateEmail();
|
||||
triggerNextStep = await this.validateEmail();
|
||||
}
|
||||
|
||||
if (!triggerNextStep) {
|
||||
@@ -285,7 +285,7 @@ registerModel({
|
||||
} else {
|
||||
// did not find next step -> end the script
|
||||
this.currentStep.data.chatbot_step_is_last = true;
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.widget._renderMessages();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.renderMessages();
|
||||
this.endScript();
|
||||
}
|
||||
|
||||
@@ -293,6 +293,36 @@ registerModel({
|
||||
|
||||
return nextStep;
|
||||
},
|
||||
/**
|
||||
* A special case is handled for email steps, where we first validate the email (server side)
|
||||
* and we allow the user to try again in case the format is incorrect.
|
||||
*
|
||||
* The validation is made server-side to have the same test when we validate here and when we
|
||||
* register the answer, but also to easily post a message as the bot ("Sorry, try again...").
|
||||
*
|
||||
* Returns a boolean stating whether the email was valid or not.
|
||||
*/
|
||||
async validateEmail() {
|
||||
let emailValidResult = await this.messaging.rpc({
|
||||
route: '/chatbot/step/validate_email',
|
||||
params: { channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid },
|
||||
});
|
||||
|
||||
if (emailValidResult.success) {
|
||||
this.currentStep.data.is_email_valid = true;
|
||||
this.saveSession();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// email is not valid, let the user try again
|
||||
this.messaging.publicLivechatGlobal.chatWindow.enableInput();
|
||||
if (emailValidResult.posted_message) {
|
||||
this.addMessage(emailValidResult.posted_message);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* This method will be transformed into a 'debounced' version (see init).
|
||||
*
|
||||
@@ -361,6 +391,28 @@ registerModel({
|
||||
this.awaitUserInputDebounceTime,
|
||||
);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {boolean|FieldCommand}
|
||||
*/
|
||||
_computeIsActive() {
|
||||
if (this.messaging.publicLivechatGlobal.isTestChatbot) {
|
||||
return true;
|
||||
}
|
||||
if (this.messaging.publicLivechatGlobal.rule && this.messaging.publicLivechatGlobal.rule.chatbot) {
|
||||
return true;
|
||||
}
|
||||
if (this.messaging.publicLivechatGlobal.livechatInit && this.messaging.publicLivechatGlobal.livechatInit.rule.chatbot) {
|
||||
return true;
|
||||
}
|
||||
if (this.state === 'welcome') {
|
||||
return true;
|
||||
}
|
||||
if (this.localStorageState) {
|
||||
return true;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
@@ -555,6 +607,10 @@ registerModel({
|
||||
compute: '_computeHasRestartButton',
|
||||
default: false,
|
||||
}),
|
||||
isActive: attr({
|
||||
compute: '_computeIsActive',
|
||||
default: false,
|
||||
}),
|
||||
isExpectingUserInput: attr({
|
||||
compute: '_computeIsExpectingUserInput',
|
||||
default: false,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import PublicLivechatMessage from '@im_livechat/legacy/models/public_livechat_message';
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { clear } from '@mail/model/model_field_command';
|
||||
@@ -24,10 +22,8 @@ registerModel({
|
||||
* @param {Object} [options={}]
|
||||
*/
|
||||
addMessage(data, options) {
|
||||
const messageWidget = new PublicLivechatMessage(this, this.messaging, data);
|
||||
|
||||
const hasAlreadyMessage = _.some(this.messaging.publicLivechatGlobal.messages, function (msg) {
|
||||
return messageWidget.getID() === msg.id;
|
||||
return data.id === msg.id;
|
||||
});
|
||||
if (hasAlreadyMessage) {
|
||||
return;
|
||||
@@ -35,11 +31,10 @@ registerModel({
|
||||
const message = this.messaging.models['PublicLivechatMessage'].insert({
|
||||
data,
|
||||
id: data.id,
|
||||
widget: messageWidget,
|
||||
});
|
||||
|
||||
if (this.messaging.publicLivechatGlobal.publicLivechat && this.messaging.publicLivechatGlobal.publicLivechat.widget) {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.addMessage(messageWidget);
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.widget.addMessage(message.widget);
|
||||
}
|
||||
|
||||
if (options && options.prepend) {
|
||||
@@ -244,7 +239,7 @@ registerModel({
|
||||
* @returns {string|FieldCommand}
|
||||
*/
|
||||
_computeInputPlaceholder() {
|
||||
if (this.isChatbot) {
|
||||
if (this.messaging.publicLivechatGlobal.chatbot.isActive) {
|
||||
// void the default livechat placeholder in the user input
|
||||
// as we use it for specific things (e.g: showing "please select an option above")
|
||||
return clear();
|
||||
@@ -254,28 +249,6 @@ registerModel({
|
||||
}
|
||||
return this.env._t("Ask something ...");
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_computeIsChatbot() {
|
||||
if (this.messaging.publicLivechatGlobal.isTestChatbot) {
|
||||
return true;
|
||||
}
|
||||
if (this.messaging.publicLivechatGlobal.rule && this.messaging.publicLivechatGlobal.rule.chatbot) {
|
||||
return true;
|
||||
}
|
||||
if (this.messaging.publicLivechatGlobal.livechatInit && this.messaging.publicLivechatGlobal.livechatInit.rule.chatbot) {
|
||||
return true;
|
||||
}
|
||||
if (this.messaging.publicLivechatGlobal.chatbot.state === 'welcome') {
|
||||
return true;
|
||||
}
|
||||
if (this.messaging.publicLivechatGlobal.chatbot.localStorageState) {
|
||||
return true;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
@@ -295,7 +268,7 @@ registerModel({
|
||||
* @returns {string}
|
||||
*/
|
||||
_computeServerUrl() {
|
||||
if (this.isChatbot) {
|
||||
if (this.messaging.publicLivechatGlobal.chatbot.isActive) {
|
||||
return this.messaging.publicLivechatGlobal.chatbot.serverUrl;
|
||||
}
|
||||
return this.messaging.publicLivechatGlobal.serverUrl;
|
||||
@@ -353,7 +326,7 @@ registerModel({
|
||||
if (!this.messaging.publicLivechatGlobal.history) {
|
||||
this.widget._sendWelcomeMessage();
|
||||
}
|
||||
this.widget._renderMessages();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.renderMessages();
|
||||
this.messaging.publicLivechatGlobal.update({ notificationHandler: {} });
|
||||
|
||||
set_cookie('im_livechat_session', unaccent(JSON.stringify(this.messaging.publicLivechatGlobal.publicLivechat.widget.toData()), true), 60 * 60);
|
||||
@@ -437,7 +410,7 @@ registerModel({
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.isChatbot &&
|
||||
this.messaging.publicLivechatGlobal.chatbot.isActive &&
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep &&
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep.data
|
||||
) {
|
||||
@@ -482,7 +455,7 @@ registerModel({
|
||||
*/
|
||||
async _sendMessageChatbotBefore() {
|
||||
if (
|
||||
this.isChatbot &&
|
||||
this.messaging.publicLivechatGlobal.chatbot.isActive &&
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep &&
|
||||
this.messaging.publicLivechatGlobal.chatbot.currentStep.data
|
||||
) {
|
||||
@@ -519,10 +492,6 @@ registerModel({
|
||||
compute: '_computeInputPlaceholder',
|
||||
default: '',
|
||||
}),
|
||||
isChatbot: attr({
|
||||
compute: '_computeIsChatbot',
|
||||
default: false,
|
||||
}),
|
||||
isOpenChatDebounced: attr({
|
||||
compute: '_computeIsOpenChatDebounced',
|
||||
default: true,
|
||||
|
||||
@@ -67,7 +67,7 @@ registerModel({
|
||||
if (this.messaging.publicLivechatGlobal.publicLivechat.isFolded || !this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.isAtBottom()) {
|
||||
this.messaging.publicLivechatGlobal.publicLivechat.update({ unreadCounter: increment() });
|
||||
}
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.widget._renderMessages();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.renderMessages();
|
||||
return;
|
||||
}
|
||||
case 'mail.message/insert': {
|
||||
@@ -76,7 +76,7 @@ registerModel({
|
||||
return;
|
||||
}
|
||||
message.widget._body = utils.Markup(payload.body);
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.widget._renderMessages();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.renderMessages();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,18 @@ import { registerModel } from '@mail/model/model_core';
|
||||
import { attr } from '@mail/model/model_field';
|
||||
import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
import PublicLivechatMessage from '@im_livechat/legacy/models/public_livechat_message';
|
||||
|
||||
registerModel({
|
||||
name: 'PublicLivechatMessage',
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
this.update({ widget: new PublicLivechatMessage(this.messaging.publicLivechatGlobal.livechatButtonView.widget, this.messaging, this.data) });
|
||||
},
|
||||
_willDelete() {
|
||||
this.widget.destroy();
|
||||
},
|
||||
},
|
||||
recordMethods: {
|
||||
/**
|
||||
* @private
|
||||
|
||||
@@ -47,6 +47,30 @@ registerModel({
|
||||
.addClass('text-center fst-italic bg-200')
|
||||
.val(disableText);
|
||||
},
|
||||
renderMessages() {
|
||||
const shouldScroll = !this.isFolded && this.publicLivechatView.widget.isAtBottom();
|
||||
this.widget.render();
|
||||
if (shouldScroll) {
|
||||
this.publicLivechatView.widget.scrollToBottom();
|
||||
}
|
||||
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_livechat_chatbot_main_restart').on('click',
|
||||
this.messaging.publicLivechatGlobal.livechatButtonView.onChatbotRestartScript
|
||||
);
|
||||
|
||||
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()) {
|
||||
this.disableInput(this.env._t("Select an option above"));
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {string}
|
||||
|
||||
@@ -18,7 +18,7 @@ patchRecordMethods('LivechatButtonView', {
|
||||
async onChatbotRestartScript(ev) {
|
||||
if (this.messaging.publicLivechatGlobal.chatbot.isWebsiteLivechatTourFlow) {
|
||||
this.messaging.publicLivechatGlobal.update({ messages: clear() });
|
||||
this.widget._renderMessages();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.renderMessages();
|
||||
}
|
||||
return this._super(ev);
|
||||
},
|
||||
@@ -49,7 +49,7 @@ patchRecordMethods('LivechatButtonView', {
|
||||
});
|
||||
await this.openChatWindow();
|
||||
this.widget._sendWelcomeMessage();
|
||||
this.widget._renderMessages();
|
||||
this.messaging.publicLivechatGlobal.chatWindow.renderMessages();
|
||||
this.env.services.bus_service.addChannel(this.messaging.publicLivechatGlobal.publicLivechat.uuid);
|
||||
set_cookie('im_livechat_session', unaccent(JSON.stringify(this.messaging.publicLivechatGlobal.publicLivechat.widget.toData()), true), 60 * 60);
|
||||
this.update({ isOpeningChat: false });
|
||||
|
||||
Reference in New Issue
Block a user