Skip to content

Commit

Permalink
add bubble chat
Browse files Browse the repository at this point in the history
  • Loading branch information
Jicheng Lu committed Oct 3, 2024
1 parent a9d4f95 commit 6b84a85
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 75 deletions.
42 changes: 42 additions & 0 deletions src/lib/common/BubbleChat.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script>
/** @type {string} */
export let text;
/** @type {() => void} */
export let close = () => {};
/** @type {string} */
export let containerClasses = "";
/** @type {string} */
export let containerStyles = "";
/** @type {boolean} */
export let disableDefaultStyles = false;
/** @param {any} e */
function handleClose(e) {
e.preventDefault();
close?.();
}
</script>
<div
class="{disableDefaultStyles ? '' : 'chat-bubble-container'} {containerClasses}"
style={`${containerStyles}`}
>
<div class="chat-bubble">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="bubble-delete clickable"
on:click={e => handleClose(e)}
>
<i class="mdi mdi-close" />
</div>
<div class="bubble-text">
{text}
</div>
</div>
</div>
60 changes: 51 additions & 9 deletions src/lib/common/LiveChatEntry.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
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';
import { ChatAction } from '$lib/helpers/enums';
import BubbleChat from './BubbleChat.svelte';
let chatUrl = PUBLIC_LIVECHAT_HOST;
let showChatBox = false;
let showBubbleMsg = false;
let receivedMsg = '';
onMount(async () => {
const agentSettings = await getSettingDetail("Agent");
Expand All @@ -17,33 +20,61 @@
// Handle event from iframe
window.onmessage = async function(e) {
if (e.data.action == ChatAction.Close) {
chatBotStore.set({ showChatBox: false });
showChatBox = false;
} else if (e.data.action == ChatAction.Open) {
chatBotStore.set({ showChatBox: true });
// showChatBox = true;
} else if (e.data.action == ChatAction.ReceiveMsg && !showChatBox) {
receivedMsg = e.data?.data?.rich_content?.message?.text || e.data?.data?.text || '';
showBubbleMsg = true;
wave();
}
};
function openChatBox() {
chatBotStore.set({ showChatBox: true });
showChatBox = true;
receivedMsg = '';
showBubbleMsg = false;
}
function closeBubbleMsg() {
receivedMsg = '';
showBubbleMsg = false;
}
function wave() {
const elem = document.getElementById('chatbot-icon');
if (elem) {
elem.classList.add('waving');
setTimeout(() => {
elem.classList.remove('waving');
}, 800);
}
}
</script>
<div class="chatbot-container fixed-bottom float-bottom-right">
{#if showBubbleMsg}
<div transition:fade={{ delay: 50, duration: 200 }}>
<BubbleChat text={receivedMsg} close={() => closeBubbleMsg()} />
</div>
{/if}
<iframe
src={chatUrl}
width={`${$chatBotStore.showChatBox ? '380px' : '0px'}`}
height={`${$chatBotStore.showChatBox ? '650px' : '0px'}`}
class={`border border-2 rounded-3 m-3 float-end ${$chatBotStore.showChatBox ? 'chat-iframe' : ''}`}
width={'380px'}
height={'650px'}
class={`border border-2 rounded-3 m-3 float-end ${showChatBox ? 'chat-iframe' : 'hide'}`}
title="live chat"
id={CHAT_FRAME_ID}
/>
{#if !$chatBotStore.showChatBox}
{#if !showChatBox}
<div
id="chatbot-icon"
class="chatbot-icon mb-3 float-end wave-effect"
transition:fade={{ delay: 50, duration: 200 }}
>
<button class="btn btn-transparent" on:click={() => openChatBox()}>
<button class="btn btn-transparent chat-icon-btn" on:click={() => openChatBox()}>
<img alt="live chat" class="avatar-md rounded-circle" src={PUBLIC_LIVECHAT_ENTRY_ICON} />
</button>
</div>
Expand All @@ -58,6 +89,13 @@
perspective: 1000px;
}
.waving {
animation: wave 0.82s cubic-bezier(.36,.07,.19,.97) both;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
perspective: 1000px;
}
@keyframes wave {
10%, 90% {
transform: translate3d(-1px, 0, 0);
Expand Down Expand Up @@ -91,4 +129,8 @@
display: flex;
justify-content: flex-end;
}
.chat-icon-btn {
padding-top: 0px;
}
</style>
3 changes: 2 additions & 1 deletion src/lib/helpers/enums.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ const chatAction = {
Close: 'close',
Logout: 'logout',
Chat: 'chat',
NewChat: 'new-chat'
NewChat: 'new-chat',
ReceiveMsg: 'receive-msg'
};
export const ChatAction = Object.freeze(chatAction);
65 changes: 27 additions & 38 deletions src/lib/helpers/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,33 +35,35 @@ userStore.subscribe(value => {
});


/** @type {Writable<import('$conversationTypes').ConversationModel>}*/
export const conversationStore = writable({});

/**
* @returns {Writable<import('$conversationTypes').ConversationModel>}
*/
export function getConversationStore() {
if (browser) {
// Access localStorage only if in the browser context
const json = localStorage.getItem(conversationKey);
if (json)
return JSON.parse(json);
else
return conversationStore;
} else {
// Return a default value for SSR
return conversationStore;
}
const createConversationStore = () => {
const { subscribe } = writable({});
return {
clear: (/** @type {string | null} */ convId = null) => {
if (!convId) {
localStorage.removeItem(conversationKey);
return;
}

const json = localStorage.getItem(conversationKey);
if (json) {
const conv = JSON.parse(json);
if (conv.id === convId) {
localStorage.removeItem(conversationKey);
}
}
},
get: () => {
const json = localStorage.getItem(conversationKey);
return json ? JSON.parse(json) : {};
},
put: (value) => {
localStorage.setItem(conversationKey, JSON.stringify(value));
},
subscribe
};
};

// @ts-ignore
conversationStore.subscribe(value => {
if (browser && value.id) {
localStorage.setItem(conversationKey, JSON.stringify(value));
}
});

export const conversationStore = createConversationStore();


const createLoaderStore = () => {
Expand Down Expand Up @@ -182,19 +184,6 @@ 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();
Expand Down
48 changes: 47 additions & 1 deletion src/lib/scss/custom/components/_chat.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
$bubble-chat-theme-color: rgba($primary, 80%);

.chat-util-common {
display: block;
position: absolute;
Expand Down Expand Up @@ -41,4 +43,48 @@
.chat-util-item {
text-align: center;
font-size: 30px;
}
}



.chat-bubble-container {
display: flex;
flex-direction: column;
gap: 0px;

.chat-bubble {
margin: 5px 30px 0px 5px;
display: inline-block;
position: relative;
min-width: 200px;
max-width: 300px;
background-color: $bubble-chat-theme-color;
border-radius: .4em;
color: white;

&:after {
content: ' ';
position: absolute;
top: 100%;
right: 50px;
width: 25px;
height: 20px;
clip-path: polygon(0 0, 100% 0, 100% 100%);
background-color: $bubble-chat-theme-color;
}

.bubble-text {
margin: 15px;
height: fit-content;
max-height: 100px;
overflow-y: auto;
scrollbar-width: none;
}

.bubble-delete {
position: absolute;
right: 5px;
top: 3px;
}
}
}
6 changes: 3 additions & 3 deletions src/routes/chat/[agentId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { newConversation } from '$lib/services/conversation-service.js';
import { getToken, setToken } from '$lib/services/auth-service.js'
import { getUserStore } from '$lib/helpers/store.js';
import { conversationStore, getConversationStore } from '$lib/helpers/store.js';
import { conversationStore } from '$lib/helpers/store.js';
import { LERNER_ID, TRAINING_MODE } from '$lib/helpers/constants';
const params = $page.params;
Expand All @@ -32,11 +32,11 @@
});
}
conversation = getConversationStore();
conversation = conversationStore.get();
if (!conversation.id || agentId != conversation.agent_id) {
// new conversation
conversation = await newConversation(agentId);
conversationStore.set(conversation);
conversationStore.put(conversation);
}
conversationId = conversation.id;
Expand Down
Loading

0 comments on commit 6b84a85

Please sign in to comment.