diff --git a/src/lib/common/audio-player/AudioSpeaker.svelte b/src/lib/common/audio-player/AudioSpeaker.svelte
index 217b436..4ef689a 100644
--- a/src/lib/common/audio-player/AudioSpeaker.svelte
+++ b/src/lib/common/audio-player/AudioSpeaker.svelte
@@ -89,7 +89,12 @@
>
speak()}>
{#if !speaking}
-
+
{:else}
{/if}
diff --git a/src/lib/helpers/http.js b/src/lib/helpers/http.js
index 3055fe6..0caaa1b 100644
--- a/src/lib/helpers/http.js
+++ b/src/lib/helpers/http.js
@@ -65,6 +65,7 @@ function skipLoader(config) {
const putRegexes = [
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/update', 'g'),
+ new RegExp('http(s*)://(.*?)/conversation/(.*?)/update-message', 'g'),
];
const deleteRegexes = [
diff --git a/src/lib/helpers/types/conversationTypes.js b/src/lib/helpers/types/conversationTypes.js
index 9a34320..2713221 100644
--- a/src/lib/helpers/types/conversationTypes.js
+++ b/src/lib/helpers/types/conversationTypes.js
@@ -128,6 +128,7 @@ IRichContent.prototype.quick_replies;
/**
* @typedef {Object} ChatResponseModel
+ * @property {string} uuid - The uuid.
* @property {string} conversation_id - The conversation id.
* @property {import('$userTypes').UserModel} sender - The message sender.
* @property {string} message_id - The message id.
@@ -246,6 +247,17 @@ IRichContent.prototype.quick_replies;
* @property {string?} [payload] - The payload message.
*/
+/**
+ * @typedef {Object} EditBotMessageModel
+ * @property {ChatResponseModel} message
+ * @property {string} source
+ */
+
+/**
+ * @typedef {Object} UpdateBotMessageRequest
+ * @property {ChatResponseModel} message
+ * @property {number} innerIndex
+ */
diff --git a/src/lib/scss/custom/components/_alert.scss b/src/lib/scss/custom/components/_alert.scss
index 0c81777..6012c19 100644
--- a/src/lib/scss/custom/components/_alert.scss
+++ b/src/lib/scss/custom/components/_alert.scss
@@ -4,9 +4,11 @@
left: 35%;
right: 35%;
z-index: 8888;
+ font-size: 0.9em;
.alert {
text-align: center;
+ padding: 0.8em;
}
.success {
diff --git a/src/lib/services/api-endpoints.js b/src/lib/services/api-endpoints.js
index 6f11024..1e92c01 100644
--- a/src/lib/services/api-endpoints.js
+++ b/src/lib/services/api-endpoints.js
@@ -48,6 +48,7 @@ export const endpoints = {
conversationUserUrl: `${host}/conversation/{conversationId}/user`,
dialogsUrl: `${host}/conversation/{conversationId}/dialogs`,
conversationMessageDeletionUrl: `${host}/conversation/{conversationId}/message/{messageId}`,
+ conversationMessageUpdateUrl: `${host}/conversation/{conversationId}/update-message`,
fileUploadUrl: `${host}/agent/{agentId}/conversation/{conversationId}/upload`,
// LLM provider
diff --git a/src/lib/services/conversation-service.js b/src/lib/services/conversation-service.js
index ba668f5..be79e3a 100644
--- a/src/lib/services/conversation-service.js
+++ b/src/lib/services/conversation-service.js
@@ -170,6 +170,31 @@ export async function deleteConversationMessage(conversationId, messageId, isNew
});
}
+/**
+ * delete a message in conversation
+ * @param {string} conversationId The conversation id
+ * @param {import('$conversationTypes').UpdateBotMessageRequest} request
+ * @returns {Promise}
+ */
+export async function updateConversationMessage(conversationId, request) {
+ let url = replaceUrl(endpoints.conversationMessageUpdateUrl, {
+ conversationId: conversationId
+ });
+
+ const data = {
+ message: request.message,
+ inner_index: request.innerIndex
+ };
+
+ return new Promise((resolve, reject) => {
+ axios.put(url, {...data}).then(response => {
+ resolve(response.data);
+ }).catch(err => {
+ reject(err)
+ });
+ });
+}
+
/**
* upload conversation files
diff --git a/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte b/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte
index 1749d4b..a9c928c 100644
--- a/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte
+++ b/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte
@@ -8,6 +8,7 @@
import { OverlayScrollbars } from 'overlayscrollbars';
import _ from "lodash";
import moment from 'moment';
+ import { v4 as uuidv4 } from 'uuid';
import {
Dropdown,
DropdownToggle,
@@ -25,10 +26,11 @@
sendMessageToHub,
GetDialogs,
deleteConversationMessage,
+ updateConversationMessage,
getConversationFiles,
getConversationUser,
uploadConversationFiles,
- getAddressOptions
+ getAddressOptions,
} from '$lib/services/conversation-service.js';
import {
PUBLIC_LIVECHAT_ENTRY_ICON,
@@ -43,6 +45,7 @@
import HeadTitle from '$lib/common/HeadTitle.svelte';
import LoadingDots from '$lib/common/LoadingDots.svelte';
import StateModal from '$lib/common/StateModal.svelte';
+ import LoadingToComplete from '$lib/common/LoadingToComplete.svelte';
import ChatTextArea from './chat-util/chat-text-area.svelte';
import AudioSpeaker from '$lib/common/audio-player/AudioSpeaker.svelte';
import { utcToLocal } from '$lib/helpers/datetime';
@@ -59,7 +62,7 @@
import ChatBigMessage from './chat-util/chat-big-message.svelte';
import PersistLog from './persist-log/persist-log.svelte';
import InstantLog from './instant-log/instant-log.svelte';
- import Loader from '$lib/common/Loader.svelte';
+
const options = {
@@ -78,6 +81,7 @@
const messageLimit = 100;
const screenWidthThreshold = 1024;
const maxTextLength = 64000;
+ const duration = 2000;
/** @type {import('$agentTypes').AgentModel} */
export let agent;
@@ -86,13 +90,16 @@
export let currentUser;
/** @type {string} */
- let text = "";
- let editText = "";
- let bigText = "";
- let truncateMsgId = "";
- let indication = "";
+ let text = '';
+ let editText = '';
+ let bigText = '';
+ let botText = '';
+ let truncateMsgId = '';
+ let indication = '';
let mode = '';
let notificationText = '';
+ let successText = "Done";
+ let errorText = "Error";
/** @type {number} */
let messageInputTimeout;
@@ -107,6 +114,9 @@
let scrollbars = [];
let microphoneIcon = "microphone-off";
+ /** @type {import('$conversationTypes').EditBotMessageModel?} */
+ let editBotMsg;
+
/** @type {import('$conversationTypes').ChatResponseModel?} */
let lastBotMsg;
@@ -149,6 +159,7 @@
let isInstantLogClosed = false; // initial condition
let isOpenEditMsgModal = false;
let isOpenBigMsgModal = false;
+ let isOpenEditBotMsgModal = false;
let isOpenUserAddStateModal = false;
let isSendingMsg = false;
let isThinking = false;
@@ -163,6 +174,8 @@
let isLoading = false;
let isCreatingNewConv = false;
let isDisplayNotification = false;
+ let isComplete = false;
+ let isError = false;
$: {
const editor = lastBotMsg?.rich_content?.editor || '';
@@ -358,7 +371,7 @@
async function refresh() {
// trigger UI render
- dialogs = dialogs?.map(item => { return { ...item }; }) || [];
+ dialogs = dialogs?.map(item => { return { ...item, uuid: uuidv4() }; }) || [];
lastBotMsg = null;
await tick();
lastBotMsg = findLastBotMessage(dialogs);
@@ -1141,14 +1154,100 @@
notificationText = '';
}
}
+
+
+ /** @param {import('$conversationTypes').ChatResponseModel} message */
+ function openEditBotMsgModal(message) {
+ isOpenEditBotMsgModal = true;
+ let source = "text";
+ if (message.rich_content?.message?.text === message.text) {
+ source = "both";
+ } else if (message.rich_content?.message?.text) {
+ source = "rich-content-text";
+ }
+ editBotMsg = {
+ message: message,
+ source: source
+ };
+ botText = message?.rich_content?.message?.text || message?.text;
+ }
+
+ function toggleEditBotMsgModal() {
+ isOpenEditBotMsgModal = !isOpenEditBotMsgModal;
+ if (!isOpenEditBotMsgModal) {
+ editBotMsg = null;
+ botText = '';
+ }
+ }
+
+ function saveBotMsg() {
+ if (!editBotMsg) return;
+
+ const found = dialogs.find(x => x.uuid === editBotMsg?.message.uuid);
+ if (!found) return;
+
+ const candidates = dialogs.filter(x => x.message_id === editBotMsg?.message.message_id && x.sender?.role === editBotMsg?.message.sender?.role);
+ const innerIdx = candidates.findIndex(x => x.uuid === editBotMsg?.message.uuid);
+
+ /** @type {import('$conversationTypes').UpdateBotMessageRequest} */
+ const request = {
+ message: editBotMsg.message,
+ innerIndex: innerIdx
+ };
+
+ if (editBotMsg.source === "both") {
+ found.text = botText;
+ found.rich_content.message.text = botText;
+ editBotMsg.message.text = botText;
+ editBotMsg.message.rich_content.message.text = botText;
+ } else if (editBotMsg?.source === "rich-content-text") {
+ found.rich_content.message.text = botText;
+ editBotMsg.message.rich_content.message.text = botText;
+ } else {
+ found.text = botText;
+ editBotMsg.message.text = botText;
+ }
+
+ isLoading = true;
+ updateConversationMessage(params.conversationId, request).then(res => {
+ if (res) {
+ isComplete = true;
+ successText = "Message has been updated!";
+ setTimeout(() => {
+ isComplete = false;
+ successText = "";
+ }, duration);
+
+ toggleEditBotMsgModal();
+ refresh();
+ } else {
+ throw "failed to update message";
+ }
+ }).catch(err => {
+ isError = true;
+ errorText = "Failed to update message!";
+ setTimeout(() => {
+ isError = false;
+ errorText = "";
+ }, duration);
+ toggleEditBotMsgModal();
+ }).finally(() => {
+ isLoading = false;
+ });
+ }
resizeChatWindow()}/>
-{#if isLoading}
-
-{/if}
+
toggleEditMsgModal()}
@@ -1199,6 +1298,21 @@
+ toggleEditBotMsgModal()}
+ confirm={() => saveBotMsg()}
+ cancel={() => toggleEditBotMsgModal()}
+ disableConfirmBtn={!!!_.trim(botText)}
+>
+
+
+
{`${(botText?.length || 0)}/${maxTextLength}`}
+
+
+
- {#if message?.message_id === lastBotMsg?.message_id}
+ {#if message?.message_id === lastBotMsg?.message_id && message?.uuid === lastBotMsg?.uuid}
likeMessage(e, message)}
>
-
+
{/if}
+
+
+
+
openEditBotMsgModal(message)}
+ >
+
+
+
{/if}
{#if !!message.is_chat_message || !!message.has_message_files}