@hivegpt/react
Version:
HiveGPT React Components
348 lines • 10.6 kB
JavaScript
import fetchStream from 'fetch-readablestream';
import React, { useEffect, useRef, useState } from 'react';
import { formatNow, formatTimeStamps } from '../utils/date';
import ChatMessage from './ChatMessage';
const decoder = new TextDecoder();
function ChatDrawer({
apiKey,
bgBubbleAi,
bgBubbleUser,
bgGradient,
botId,
closeButtonColor,
dateTimeColor,
eventName,
formFieldBgColor,
formFieldTextColor,
messageTextColorAi,
messageTextColorUser,
onClickEvent,
sendButtonColor,
sendButtonTextColor,
showClose,
thumbsDownMessage,
thumbsUpMessage,
timezone,
toggleDrawer,
useOpenAi,
user,
userId
}) {
const bottomRef = useRef(null);
const greeting = useRef('');
const submitOnChange = useRef(false);
const [input, setInput] = useState('');
const [botName, setBotName] = useState('');
const [chatLog, setChatLog] = useState([]);
const [loading, setLoading] = useState(false);
const [quickPrompts, setQuickPrompts] = useState([]);
const fetchData = async () => {
try {
setLoading(true);
const url = `${process.env.REACT_APP_API_URL}/bot/ask-kb` + (useOpenAi === false ? '/llm-hf' : '');
fetchStream(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey
},
body: JSON.stringify({
query: input,
user_id: userId,
bot_id: botId
})
}).then(response => {
readAllChunks(response.body);
});
} catch (error) {
setLoading(false);
}
};
const fetchHistory = () => {
try {
setLoading(true);
const url = `${process.env.REACT_APP_API_URL}/bot/history`;
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey
},
body: JSON.stringify({
user_id: userId,
bot_id: botId
})
}).then(response => {
setLoading(false);
if (response.status === 200) {
response.json().then(data => {
if (data && data.length) {
const history = [];
data.forEach(item => {
history.push({
type: 'user',
message: item.User,
time: formatTimeStamps(timezone, item.Timestamp)
});
history.push({
type: 'ai',
message: item.AI,
time: formatTimeStamps(timezone, item.Timestamp)
});
});
setChatLog(prev => [...prev, ...history]);
} else {
setChatLog(prev => [...prev, {
type: 'ai',
message: greeting.current || 'Hi, how can I help you today?',
time: formatNow(timezone)
}]);
}
});
}
});
} catch (error) {
setLoading(false);
}
};
const fetchSmallTalk = () => {
setLoading(true);
const url = `${process.env.REACT_APP_API_URL}/bot/small-talk/${botId}/${userId}`;
fetch(url, {
method: 'GET',
headers: {
'x-api-key': apiKey
}
}).then(response => {
if (response.status === 200) {
response.json().then(data => {
if (data && data.smallTalk) {
setChatLog(prev => [...prev, {
type: 'ai',
message: data.smallTalk,
time: formatTimeStamps(timezone, new Date())
}]);
}
});
}
}).catch(error => console.error('Error while fetching small talk:', error)).finally(() => setLoading(false));
};
const handleSubmit = e => {
e.preventDefault();
submitUserPrompt();
};
const readAllChunks = stream => {
const reader = stream.getReader();
function pump() {
return reader.read().then(({
value,
done
}) => {
if (done) {
fetchSmallTalk();
return;
}
const decodedChunk = decoder.decode(value, {
stream: true
});
setChatLog(prevLog => {
const nextLog = prevLog.map(x => ({
...x
}));
const lastItem = nextLog[nextLog.length - 1];
if (lastItem.type === 'ai') {
lastItem.message = `${lastItem.message}${decodedChunk}`;
} else {
nextLog.push({
type: 'ai',
message: decodedChunk,
time: formatNow(timezone)
});
}
return nextLog;
});
return pump();
});
}
return pump();
};
const sendPromptMessage = prompt => {
submitOnChange.current = true;
setInput(prompt);
};
const submitFeedback = flag => {
setLoading(true);
const url = `${process.env.REACT_APP_API_URL}/bot/feedback`;
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey
},
body: JSON.stringify({
user_id: userId,
flag
})
}).then(() => {
if (flag) {
setChatLog(prev => [...prev, {
type: 'ai',
isFeedbackMessage: true,
message: thumbsUpMessage || 'Great. May I assist you with anything else?',
time: formatNow(timezone)
}]);
return;
}
setChatLog(prev => [...prev, {
type: 'ai',
isFeedbackMessage: true,
message: thumbsDownMessage || 'I did not understand. Would you please try asking again?',
time: formatNow(timezone)
}]);
}).finally(() => {
setLoading(false);
});
};
const submitUserPrompt = () => {
if (loading || !input) {
return;
}
setChatLog(prev => [...prev, {
type: 'user',
message: input,
time: formatNow(timezone)
}]);
setInput('');
fetchData();
};
useEffect(() => {
if (!botId || !userId) {
return;
}
fetch(`${process.env.REACT_APP_API_URL}/bot/config?bot_id=${botId}`).then(response => response.json()).then(data => {
setBotName(data.name);
greeting.current = data.greeting;
setQuickPrompts(data.quickPrompts);
fetchHistory();
}).catch(error => console.error('Error:', error));
}, [botId, userId]);
useEffect(() => {
if (submitOnChange.current) {
submitUserPrompt();
submitOnChange.current = false;
}
}, [input]);
useEffect(() => {
bottomRef.current?.scrollIntoView({
behavior: 'smooth'
});
}, [chatLog]);
const handleChange = e => {
if (e.key === 'Enter') {
handleSubmit();
} else {
setInput(e.target.value);
}
};
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
className: `drawer-main active`
}, /*#__PURE__*/React.createElement("div", {
className: "drawer-overlay",
onClick: toggleDrawer
}), /*#__PURE__*/React.createElement("div", {
className: "chat-wrapper",
style: {
background: `linear-gradient(${bgGradient ? bgGradient.join(', ') : ''})`
}
}, showClose !== false && /*#__PURE__*/React.createElement("div", {
className: "chat-header"
}, /*#__PURE__*/React.createElement("button", {
className: "closeIcon",
onClick: toggleDrawer,
style: {
background: closeButtonColor ? '' : ''
}
}, /*#__PURE__*/React.createElement("svg", {
xmlns: "http://www.w3.org/2000/svg",
width: "24",
height: "24",
viewBox: "0 0 24 24"
}, /*#__PURE__*/React.createElement("path", {
fill: "currentColor",
d: "M18.3 5.71a.996.996 0 0 0-1.41 0L12 10.59L7.11 5.7A.996.996 0 1 0 5.7 7.11L10.59 12L5.7 16.89a.996.996 0 1 0 1.41 1.41L12 13.41l4.89 4.89a.996.996 0 1 0 1.41-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z"
})))), /*#__PURE__*/React.createElement("div", {
className: "chat-main"
}, /*#__PURE__*/React.createElement("div", {
className: "innerChat"
}, chatLog?.map((chat, idx) => {
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ChatMessage, {
bgBubbleAi: bgBubbleAi,
bgBubbleUser: bgBubbleUser,
botName: botName,
chat: chat,
dateTimeColor: dateTimeColor,
isLastResponse: idx !== 0 && idx === chatLog.length - 1 && chat.type === 'ai',
key: `chat_${idx}`,
messageTextColorAi: messageTextColorAi,
messageTextColorUser: messageTextColorUser,
sendButtonTextColor: sendButtonTextColor,
downVote: () => submitFeedback(false),
upVote: () => submitFeedback(true),
onClickEvent: onClickEvent
}), idx === 0 && quickPrompts?.length && quickPrompts.map((prompt, idx) => /*#__PURE__*/React.createElement("div", {
className: "cta-faqs",
key: `quick_prompt_${idx}`
}, /*#__PURE__*/React.createElement("div", {
className: "cta",
onClick: () => sendPromptMessage(prompt.prompt)
}, "Q. ", prompt.text))));
}), /*#__PURE__*/React.createElement("div", {
id: "last-div",
ref: bottomRef
}))), /*#__PURE__*/React.createElement("div", {
className: "chat-footer"
}, /*#__PURE__*/React.createElement("form", {
onSubmit: handleSubmit
}, loading && /*#__PURE__*/React.createElement("div", {
className: "chat-main "
}, /*#__PURE__*/React.createElement("div", {
className: "py-0"
}, /*#__PURE__*/React.createElement("div", {
className: "chat ai"
}, /*#__PURE__*/React.createElement("div", {
className: "chat-box"
}, /*#__PURE__*/React.createElement("div", {
className: "message"
}, /*#__PURE__*/React.createElement("div", {
className: "loading"
}, /*#__PURE__*/React.createElement("div", {
className: "spinner"
}, /*#__PURE__*/React.createElement("div", {
className: "bounce1"
}), /*#__PURE__*/React.createElement("div", {
className: "bounce2"
}), /*#__PURE__*/React.createElement("div", {
className: "bounce3"
})))))))), /*#__PURE__*/React.createElement("input", {
className: "form-control",
name: "",
id: "",
value: input,
placeholder: "Type your query here...",
onChange: handleChange,
style: {
color: formFieldTextColor ? '' : ' ',
background: formFieldBgColor ? '' : ' '
}
}), /*#__PURE__*/React.createElement("div", {
className: "cta-footer"
}, /*#__PURE__*/React.createElement("button", {
type: "submit",
className: "btn cta-chat",
style: {
color: sendButtonColor ? '' : ' ',
background: !sendButtonTextColor ? '' : ' '
}
}, "Send")))))));
}
export default ChatDrawer;