UNPKG

@hivegpt/react

Version:

HiveGPT React Components

348 lines 10.6 kB
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;