From 321517146363f18b6c2635059d82b8739698159f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 25 Sep 2024 16:18:53 -0500 Subject: [PATCH 1/2] add multiselect, add chat event --- src/lib/common/LiveChatEntry.svelte | 30 +- src/lib/common/MultiSelect.svelte | 310 ++++++++++++++++++ src/lib/helpers/constants.js | 2 + src/lib/helpers/store.js | 13 + src/lib/helpers/types/conversationTypes.js | 14 +- src/lib/helpers/utils/chat.js | 37 +++ src/lib/scss/app.scss | 1 + .../scss/custom/components/_multiselect.scss | 95 ++++++ src/lib/services/conversation-service.js | 1 - .../[agentId]/[conversationId]/+page.svelte | 2 +- .../[conversationId]/chat-box.svelte | 25 +- .../rich-content/rich-content.svelte | 1 - src/routes/page/conversation/+page.svelte | 1 + src/routes/page/plugin/plugin-list.svelte | 29 +- 14 files changed, 529 insertions(+), 32 deletions(-) create mode 100644 src/lib/common/MultiSelect.svelte create mode 100644 src/lib/helpers/utils/chat.js create mode 100644 src/lib/scss/custom/components/_multiselect.scss diff --git a/src/lib/common/LiveChatEntry.svelte b/src/lib/common/LiveChatEntry.svelte index 755e41be..5450cc55 100644 --- a/src/lib/common/LiveChatEntry.svelte +++ b/src/lib/common/LiveChatEntry.svelte @@ -3,49 +3,49 @@ import { onMount } from 'svelte'; import { PUBLIC_LIVECHAT_HOST, PUBLIC_LIVECHAT_ENTRY_ICON } from '$env/static/public'; import { getSettingDetail } from '$lib/services/setting-service'; + import { chatBotStore } from '$lib/helpers/store'; + import { CHAT_FRAME_ID } from '$lib/helpers/constants'; - let showChatIcon = false; - let showChatBox = false; let chatUrl = PUBLIC_LIVECHAT_HOST; onMount(async () => { const agentSettings = await getSettingDetail("Agent"); chatUrl = `${PUBLIC_LIVECHAT_HOST}chat/${agentSettings.hostAgentId}?isFrame=true`; - showChatIcon = true; }); // Handle event from iframe window.onmessage = async function(e) { if (e.data.action == 'close') { - showChatIcon = true; - showChatBox = false; + chatBotStore.set({ + showChatBox: false + }); } }; - function handleChatBox() { - showChatIcon = false; - showChatBox = true; + function openChatBox() { + chatBotStore.set({ + showChatBox: true + }); }
- {#if showChatBox} + {#if $chatBotStore.showChatBox}
+ id={CHAT_FRAME_ID} + />
{/if} - {#if showChatIcon} + {#if !$chatBotStore.showChatBox}
-
diff --git a/src/lib/common/MultiSelect.svelte b/src/lib/common/MultiSelect.svelte new file mode 100644 index 00000000..10a707c7 --- /dev/null +++ b/src/lib/common/MultiSelect.svelte @@ -0,0 +1,310 @@ + + + +
handleClickOutside(e)} +> + + + + {#if showOptionList} +
    innerScroll()}> + + {#if innerOptions.length > 0} + {#if selectAll} +
  • +
    + checkSelectAll(e)} + /> +
    +
    + {'Select all'} +
    +
  • + {/if} + {#each innerOptions as option, idx (idx)} +
  • +
    + checkOption(e, option)} + /> +
    +
    + {option.name} +
    +
  • + {/each} + {:else} +
  • +
    Nothing...
    +
  • + {/if} +
+ {/if} +
\ No newline at end of file diff --git a/src/lib/helpers/constants.js b/src/lib/helpers/constants.js index eb675123..d85ffe1f 100644 --- a/src/lib/helpers/constants.js +++ b/src/lib/helpers/constants.js @@ -1,5 +1,7 @@ import { EditorType, UserRole } from "./enums"; +export const CHAT_FRAME_ID = "chatbox-frame"; + export const USER_SENDERS = [ UserRole.Admin, UserRole.User, diff --git a/src/lib/helpers/store.js b/src/lib/helpers/store.js index 5aa79dec..eecead98 100644 --- a/src/lib/helpers/store.js +++ b/src/lib/helpers/store.js @@ -182,6 +182,19 @@ const createKnowledgeBaseDocumentStore = () => { export const knowledgeBaseDocumentStore = createKnowledgeBaseDocumentStore(); +const createChatBotStore = () => { + const { subscribe, set, update } = writable({ showChatBox: false }); + + return { + set, + update, + subscribe + } +}; + +export const chatBotStore = createChatBotStore(); + + export function resetLocalStorage(resetUser = false) { conversationUserStateStore.resetAll(); conversationSearchOptionStore.reset(); diff --git a/src/lib/helpers/types/conversationTypes.js b/src/lib/helpers/types/conversationTypes.js index 52027e6e..94212cf3 100644 --- a/src/lib/helpers/types/conversationTypes.js +++ b/src/lib/helpers/types/conversationTypes.js @@ -182,6 +182,14 @@ IRichContent.prototype.quick_replies; * @property {Date} created_at - The log sent time. */ +/** + * Conversation states + * @typedef {Object} ConversationStateModel + * @property {string} key - The state key. + * @property {string} value - The state value. + * @property {number} [active_rounds] - The state active rounds. + */ + /** * Conversation states added by user * @@ -221,9 +229,9 @@ IRichContent.prototype.quick_replies; * Conversation postback * * @typedef {Object} Postback - * @property {string?} functionName - The function name. + * @property {string?} [functionName] - The function name. * @property {string?} payload - The payload. - * @property {string?} parentId - The parent message id. + * @property {string?} [parentId] - The parent message id. */ /** @@ -232,7 +240,7 @@ IRichContent.prototype.quick_replies; * @typedef {Object} MessageData * @property {string?} [truncateMsgId] - The truncated message. * @property {string?} [inputMessageId] - The input message. - * @property {string[]?} [states] - The states input by user. + * @property {ConversationStateModel[]?} [states] - The states input by user. * @property {Postback?} [postback] - The parent message id. * @property {string?} [payload] - The payload message. */ diff --git a/src/lib/helpers/utils/chat.js b/src/lib/helpers/utils/chat.js new file mode 100644 index 00000000..4dc3eb3a --- /dev/null +++ b/src/lib/helpers/utils/chat.js @@ -0,0 +1,37 @@ +/** + * @param {string} chatFrameId + * @param {string} text + * @param {import('$conversationTypes').MessageData | null} data + */ +export function sendToChatBot(chatFrameId, text, data = null) { + const chatFrame = document.getElementById(chatFrameId); + const content = { action: "chat", text: text, data: data }; + + if (chatFrame) { + // @ts-ignore + chatFrame.contentWindow.postMessage(content, "*"); + } +} + +/** + * @param {() => void} func + */ +export function addChatBoxMountEventListener(func) { + window.addEventListener("message", e => { + if (e.data.event === 'chat-box-mounted') { + func(); + } + }); +} + +/** + * @param {string} chatFrameId + * @param {string} text + */ +export function loadChatFrame(chatFrameId, text) { + const chatFrame = document.getElementById(chatFrameId); + if (chatFrame) { + // @ts-ignore + chatFrame.contentWindow.postMessage({ action: "chat", text: text }, "*"); + } +} \ No newline at end of file diff --git a/src/lib/scss/app.scss b/src/lib/scss/app.scss index eb3cabb8..78f25531 100644 --- a/src/lib/scss/app.scss +++ b/src/lib/scss/app.scss @@ -43,6 +43,7 @@ File: Main Css File @import "custom/components/file"; @import "custom/components/audio"; @import "custom/components/text"; +@import "custom/components/multiselect"; // Plugins @import "custom/plugins/custom-scrollbar"; diff --git a/src/lib/scss/custom/components/_multiselect.scss b/src/lib/scss/custom/components/_multiselect.scss new file mode 100644 index 00000000..2883f678 --- /dev/null +++ b/src/lib/scss/custom/components/_multiselect.scss @@ -0,0 +1,95 @@ +.multiselect-container { + position: relative; + + .display-container { + .display-suffix { + position: absolute; + right: 0.6rem; + top: 0.18rem; + font-size: 1.5rem; + + i { + transition: all .2s ease-in-out; + } + } + + input[type='text'] { + padding-right: 1.5rem; + } + + .show-list { + i { + transform: rotate(180deg); + } + } + } + + .option-list { + padding: 5px 2px; + border: 2px solid var(--bs-border-color-translucent); + border-radius: 5px; + + overflow-x: auto; + overflow-y: auto; + scrollbar-width: thin; + max-height: 230px; + + position: absolute; + left: 0px; + right: 0px; + z-index: 99; + background-color: white; + + .search-box { + padding: 5px 10px; + + input[type='text'] { + padding: 5px 1.5rem; + border-radius: 5px; + position: relative; + } + } + + .search-prefix { + position: absolute; + z-index: 100; + left: 1.2rem; + top: 1.2rem; + display: flex; + } + } + + ul { + list-style: none; + padding-left: 0px; + margin-bottom: 0px; + + .option-item { + display: flex; + gap: 3px; + padding: 5px 10px; + + .select-box { + flex: 0 0 10%; + max-width: 20px; + } + + .select-name { + flex: 0 0 90%; + max-width: 300px; + font-size: 1.1em; + word-break: break-all; + } + + input[type='checkbox'] { + outline: none !important; + box-shadow: none !important; + font-size: 1.1em; + } + + .nothing { + padding-left: 10px; + } + } + } +} \ No newline at end of file diff --git a/src/lib/services/conversation-service.js b/src/lib/services/conversation-service.js index 04bc25a1..718e4abc 100644 --- a/src/lib/services/conversation-service.js +++ b/src/lib/services/conversation-service.js @@ -90,7 +90,6 @@ export async function GetDialogs(conversationId) { * @param {import('$conversationTypes').MessageData?} data - Additional data */ export async function sendMessageToHub(agentId, conversationId, text, data = null) { - console.log(data); let url = replaceUrl(endpoints.conversationMessageUrl, { agentId: agentId, conversationId: conversationId diff --git a/src/routes/chat/[agentId]/[conversationId]/+page.svelte b/src/routes/chat/[agentId]/[conversationId]/+page.svelte index 463faa27..b42bae56 100644 --- a/src/routes/chat/[agentId]/[conversationId]/+page.svelte +++ b/src/routes/chat/[agentId]/[conversationId]/+page.svelte @@ -23,5 +23,5 @@ {#if currentUser} - + {/if} \ No newline at end of file diff --git a/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte b/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte index 41bd052f..38911b30 100644 --- a/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte +++ b/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte @@ -167,12 +167,6 @@ }); onMount(async () => { - window.addEventListener('message', e => { - if (e.data.action === 'logout') { - resetLocalStorage(true); - } - }); - autoScrollLog = true; dialogs = await GetDialogs(params.conversationId); conversationUser = await getConversationUser(params.conversationId); @@ -195,6 +189,18 @@ ].filter(Boolean); refresh(); autoScrollLog = false; + + window.addEventListener('message', e => { + if (e.data.action === 'logout') { + resetLocalStorage(true); + } + + if (e.data.action === 'chat' && !isThinking && !isSendingMsg) { + sendChatMessage(e.data.text, e.data.data || null); + } + }); + + // window.parent.postMessage({ event: "chat-box-mounted" }, "*"); }); function resizeChatWindow() { @@ -441,7 +447,12 @@ autoScrollLog = true; clearInstantLogs(); renewUserSentMessages(msgText); - const postback = buildPostbackMessage(dialogs, data?.payload || msgText, data?.truncateMsgId); + + let postback = data?.postback; + if (!postback) { + postback = buildPostbackMessage(dialogs, data?.payload || msgText, data?.truncateMsgId); + } + /** @type {import('$conversationTypes').MessageData?} */ let messageData = { ...data, diff --git a/src/routes/chat/[agentId]/[conversationId]/rich-content/rich-content.svelte b/src/routes/chat/[agentId]/[conversationId]/rich-content/rich-content.svelte index f974bcbc..85a795e0 100644 --- a/src/routes/chat/[agentId]/[conversationId]/rich-content/rich-content.svelte +++ b/src/routes/chat/[agentId]/[conversationId]/rich-content/rich-content.svelte @@ -23,7 +23,6 @@ $: { const richType = message?.rich_content?.message?.rich_type; - console.log(message); if (richType === RichType.QuickReply) { options = message?.rich_content?.message?.quick_replies; diff --git a/src/routes/page/conversation/+page.svelte b/src/routes/page/conversation/+page.svelte index 1fb4139a..122d8020 100644 --- a/src/routes/page/conversation/+page.svelte +++ b/src/routes/page/conversation/+page.svelte @@ -26,6 +26,7 @@ import { utcToLocal } from '$lib/helpers/datetime'; import Swal from 'sweetalert2'; import lodash from "lodash"; + import MultiSelect from '$lib/common/MultiSelect.svelte'; let isLoading = false; let isComplete = false; diff --git a/src/routes/page/plugin/plugin-list.svelte b/src/routes/page/plugin/plugin-list.svelte index 78c39778..097917bb 100644 --- a/src/routes/page/plugin/plugin-list.svelte +++ b/src/routes/page/plugin/plugin-list.svelte @@ -9,6 +9,9 @@ } from '@sveltestrap/sveltestrap'; import { installPlugin, removePlugin } from '$lib/services/plugin-service'; import Swal from 'sweetalert2'; + import { sendToChatBot } from '$lib/helpers/utils/chat'; + + import { CHAT_FRAME_ID } from '$lib/helpers/constants'; /** @type {import('$pluginTypes').PluginDefModel[]} */ export let plugins; @@ -45,6 +48,19 @@ function refresh() { window.location.reload(); } + + /** @param {import('$pluginTypes').PluginDefModel} item */ + async function clickView(item) { + const text = `view plugin ${item.name}`; + /** @type {import('$conversationTypes').MessageData} */ + const data = { + postback: { + payload: 'hi world' + }, + states: [] + }; + sendToChatBot(CHAT_FRAME_ID, text, data); + } @@ -75,17 +91,22 @@
{item.enabled ? $_("Enabled") : $_("Disabled")} {#if item.agent_ids.length > 0} - {item.agent_ids.length} Agent(s) + {item.agent_ids.length} Agent(s) {/if} {$_('Public')}
- + {#if item.settings_name} - {$_('Settings')} + {$_('Settings')} {/if} {#if !item.is_core} - + {/if}
From 788e9fffd667d4c2775c9af1b07fc02490153e15 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 25 Sep 2024 16:46:08 -0500 Subject: [PATCH 2/2] resist chatbox --- src/lib/common/LiveChatEntry.svelte | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib/common/LiveChatEntry.svelte b/src/lib/common/LiveChatEntry.svelte index 5450cc55..6c55fad2 100644 --- a/src/lib/common/LiveChatEntry.svelte +++ b/src/lib/common/LiveChatEntry.svelte @@ -47,6 +47,14 @@