diff --git a/__tests__/hooks/internal/useChatHistoryInternal.test.ts b/__tests__/hooks/internal/useChatHistoryInternal.test.ts index 3bace45c..de7beff0 100644 --- a/__tests__/hooks/internal/useChatHistoryInternal.test.ts +++ b/__tests__/hooks/internal/useChatHistoryInternal.test.ts @@ -90,6 +90,9 @@ describe("useChatHistoryInternal Hook", () => { expect.any(Object), initialChatHistory, expect.any(Function), + expect.any(Object), + expect.any(Number), + expect.any(Function), ); // checks if history is being loaded diff --git a/src/components/Buttons/VoiceButton/VoiceButton.tsx b/src/components/Buttons/VoiceButton/VoiceButton.tsx index dab4bb42..376ebd21 100644 --- a/src/components/Buttons/VoiceButton/VoiceButton.tsx +++ b/src/components/Buttons/VoiceButton/VoiceButton.tsx @@ -1,4 +1,4 @@ -import { useEffect, MouseEvent, useRef } from "react"; +import { useEffect, MouseEvent, useRef, useState } from "react"; import MediaDisplay from "../../ChatBotBody/MediaDisplay/MediaDisplay"; import { startVoiceRecording, stopVoiceRecording } from "../../../services/VoiceService"; @@ -42,6 +42,17 @@ const VoiceButton = () => { // tracks audio chunk (if voice is sent as audio) const audioChunksRef = useRef([]); + // serves as a workaround (together with useEffect hook) for sending voice input, can consider a better approach + const [voiceInputTrigger, setVoiceInputTrigger] = useState(false); + useEffect(() => { + if (settings.voice?.sendAsAudio) { + handleSendAsAudio(); + audioChunksRef.current = []; + } else { + handleSubmitText(); + } + }, [voiceInputTrigger]) + // handles starting and stopping of voice recording on toggle useEffect(() => { if (voiceToggledOn) { @@ -71,12 +82,7 @@ const VoiceButton = () => { * Handles submission of user voice input. */ const triggerSendVoiceInput = () => { - if (settings.voice?.sendAsAudio) { - handleSendAsAudio(); - audioChunksRef.current = []; - } else { - handleSubmitText(); - } + setVoiceInputTrigger(prev => !prev); } /* diff --git a/src/components/ChatBotBody/ChatBotBody.tsx b/src/components/ChatBotBody/ChatBotBody.tsx index 8ad08c02..40ff9bf7 100644 --- a/src/components/ChatBotBody/ChatBotBody.tsx +++ b/src/components/ChatBotBody/ChatBotBody.tsx @@ -1,8 +1,7 @@ -import { Dispatch, SetStateAction, useEffect, CSSProperties, MouseEvent } from "react"; +import { Dispatch, SetStateAction, CSSProperties, MouseEvent } from "react"; import ChatMessagePrompt from "./ChatMessagePrompt/ChatMessagePrompt"; import ToastPrompt from "./ToastPrompt/ToastPrompt"; -import { getHistoryMessages, loadChatHistory } from "../../services/ChatHistoryService"; import { useChatWindowInternal } from "../../hooks/internal/useChatWindowInternal"; import { useBotStatesContext } from "../../context/BotStatesContext"; import { useBotRefsContext } from "../../context/BotRefsContext"; @@ -17,14 +16,11 @@ import "./ChatBotBody.css"; /** * Contains chat messages between the user and bot. * - * @param chatScrollHeight number representing chat window scroll height * @param setChatScrollHeight setter for tracking chat window scroll height */ const ChatBotBody = ({ - chatScrollHeight, setChatScrollHeight, }: { - chatScrollHeight: number; setChatScrollHeight: Dispatch>; }) => { // handles settings @@ -34,7 +30,7 @@ const ChatBotBody = ({ const { styles } = useStylesContext(); // handles messages - const { messages, setMessages } = useMessagesContext(); + const { messages } = useMessagesContext(); // handles toasts const { toasts } = useToastsContext(); @@ -44,9 +40,7 @@ const ChatBotBody = ({ // handles bot states const { - isLoadingChatHistory, isBotTyping, - setIsLoadingChatHistory, setIsScrolling, setUnreadCount, } = useBotStatesContext(); @@ -87,21 +81,6 @@ const ChatBotBody = ({ ...styles.toastPromptContainerStyle }; - useEffect(() => { - if (isLoadingChatHistory) { - loadChatHistory(settings, styles, getHistoryMessages(), setMessages); - setTimeout(() => { - if (!chatBodyRef.current) { - return; - } - const { scrollHeight } = chatBodyRef.current; - const scrollDifference = scrollHeight - chatScrollHeight; - chatBodyRef.current.scrollTop = chatBodyRef.current.scrollTop + scrollDifference; - setIsLoadingChatHistory(false); - }, 501) - } - }, [isLoadingChatHistory]) - /** * Checks and updates whether a user is scrolling in chat window. */ diff --git a/src/components/ChatBotContainer.tsx b/src/components/ChatBotContainer.tsx index c18389d5..eaa1f9e7 100644 --- a/src/components/ChatBotContainer.tsx +++ b/src/components/ChatBotContainer.tsx @@ -49,7 +49,6 @@ const ChatBotContainer = ({ // handles chat window const { - chatScrollHeight, setChatScrollHeight, viewportHeight, viewportWidth, @@ -147,7 +146,7 @@ const ChatBotContainer = ({ }
{settings.general?.showHeader && } - + {settings.general?.showInputRow && } {settings.general?.showFooter && }
diff --git a/src/hooks/internal/useChatHistoryInternal.ts b/src/hooks/internal/useChatHistoryInternal.ts index 9a8ed4fd..bdebf225 100644 --- a/src/hooks/internal/useChatHistoryInternal.ts +++ b/src/hooks/internal/useChatHistoryInternal.ts @@ -1,7 +1,9 @@ import { useCallback } from "react"; -import { getHistoryMessages } from "../../services/ChatHistoryService"; +import { getHistoryMessages, loadChatHistory } from "../../services/ChatHistoryService"; import { useRcbEventInternal } from "./useRcbEventInternal"; +import { useChatWindowInternal } from "./useChatWindowInternal"; +import { useBotRefsContext } from "../../context/BotRefsContext"; import { useMessagesContext } from "../../context/MessagesContext"; import { useSettingsContext } from "../../context/SettingsContext"; import { useStylesContext } from "../../context/StylesContext"; @@ -27,9 +29,15 @@ export const useChatHistoryInternal = () => { setIsLoadingChatHistory, } = useBotStatesContext(); + // handles bot refs + const { chatBodyRef } = useBotRefsContext(); + // handles rcb events const { callRcbEvent } = useRcbEventInternal(); + // handles chat window + const { chatScrollHeight } = useChatWindowInternal(); + /** * Loads and shows chat history in the chat window. * @@ -49,6 +57,9 @@ export const useChatHistoryInternal = () => { } } setIsLoadingChatHistory(true); + loadChatHistory(settings, styles, chatHistory, setMessages, + chatBodyRef, chatScrollHeight, setIsLoadingChatHistory + ); }, [settings, styles, setMessages]); return { isLoadingChatHistory, setIsLoadingChatHistory, showChatHistory }; diff --git a/src/services/ChatHistoryService.tsx b/src/services/ChatHistoryService.tsx index 6c39834e..454ac59b 100644 --- a/src/services/ChatHistoryService.tsx +++ b/src/services/ChatHistoryService.tsx @@ -124,11 +124,13 @@ const parseMessageToString = (message: Message) => { * @param styles styles provided to the bot * @param chatHistory chat history to show * @param setMessages setter for updating messages - * @param prevTextAreaDisabled boolean indicating if text area was previously disabled - * @param setTextAreaDisabled setter for enabling/disabling user text area + * @param chatBodyRef reference to the chat body + * @param chatScrollHeight current chat scroll height + * @param setIsLoadingChatHistory setter for whether chat history is loading */ const loadChatHistory = (settings: Settings, styles: Styles, chatHistory: Message[], - setMessages: Dispatch>) => { + setMessages: Dispatch>, chatBodyRef: React.RefObject, + chatScrollHeight: number, setIsLoadingChatHistory: Dispatch>) => { historyLoaded = true; if (chatHistory != null) { @@ -160,6 +162,17 @@ const loadChatHistory = (settings: Settings, styles: Styles, chatHistory: Messag return [...parsedMessages, lineBreakMessage, ...prevMessages]; }); }, 500) + + // slight delay afterwards to maintain scroll position + setTimeout(() => { + if (!chatBodyRef.current) { + return; + } + const { scrollHeight } = chatBodyRef.current; + const scrollDifference = scrollHeight - chatScrollHeight; + chatBodyRef.current.scrollTop = chatBodyRef.current.scrollTop + scrollDifference; + setIsLoadingChatHistory(false); + }, 510) } catch { // remove chat history on error (to address corrupted storage values) localStorage.removeItem(settings.chatHistory?.storageKey as string);