import { Fragment, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import ArrowDown from '../assets/arrow-down.svg'; import RetryIcon from '../components/RetryIcon'; import Hero from '../Hero'; import { useDarkTheme } from '../hooks'; import ConversationBubble from './ConversationBubble'; import { FEEDBACK, Query, Status } from './conversationModels'; interface ConversationMessagesProps { handleQuestion: (params: { question: string; isRetry?: boolean; updated?: boolean | null; indx?: number; }) => void; handleQuestionSubmission: ( updatedQuestion?: string, updated?: boolean, indx?: number, ) => void; handleFeedback?: (query: Query, feedback: FEEDBACK, index: number) => void; queries: Query[]; status: Status; showHeroOnEmpty?: boolean; } export default function ConversationMessages({ handleQuestion, handleQuestionSubmission, queries, status, handleFeedback, showHeroOnEmpty = true, }: ConversationMessagesProps) { const [isDarkTheme] = useDarkTheme(); const { t } = useTranslation(); const conversationRef = useRef(null); const [atLast,setAtLast] = useState(true); const [eventInterrupt, setEventInterrupt] = useState(false); const handleUserInterruption = () => { if (!eventInterrupt && status === 'loading') { setEventInterrupt(true); } }; const scrollIntoView = () => { if (!conversationRef?.current || eventInterrupt) return; requestAnimationFrame(() => { if (!conversationRef?.current) return; if (status === 'idle' || !queries[queries.length - 1]?.response) { conversationRef.current.scrollTo({ behavior: 'smooth', top: conversationRef.current.scrollHeight, }); } else { conversationRef.current.scrollTop = conversationRef.current.scrollHeight; } }); }; const checkScroll = () => { const el = conversationRef.current; if (!el) return; const isBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 10; setAtLast(isBottom); }; useEffect(() => { !eventInterrupt && scrollIntoView(); }, [queries.length, queries[queries.length - 1]]); useEffect(() => { if (status === 'idle') { setEventInterrupt(false); } }, [status]); useEffect(() => { conversationRef.current?.addEventListener('scroll', checkScroll); return () => { conversationRef.current?.removeEventListener('scroll', checkScroll); }; }, []); const prepResponseView = (query: Query, index: number) => { let responseView; if (query.thought || query.response) { const isCurrentlyStreaming = status === 'loading' && index === queries.length - 1; responseView = ( handleFeedback(query, feedback, index) : undefined } /> ); } else if (query.error) { const retryBtn = ( ); responseView = ( ); } return responseView; }; return (
{queries.length > 0 && !atLast && ( )}
{queries.length > 0 ? ( queries.map((query, index) => ( {prepResponseView(query, index)} )) ) : showHeroOnEmpty ? ( ) : null}
); }