"use client"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useVirtualizer } from "@tanstack/react-virtual"; import { Button, Icon, IconName, Spinner } from "@/components/ui"; import { useChat } from "@/providers/chat-provider"; import { ChatMessage, Suggestions } from ".."; import styles from "./ChatMessagesWrapper.module.scss"; export default function ChatMessagesWrapper() { const messagesWrapperRef = useRef(null); const [isScrolledUp, setIsScrolledUp] = useState(false); const { messages: socketMessages, isLoadingAdvisorMessage, hasMoreOlderMessages, isLoadingOlder, // unreadMessagesCount, loadOlder, send, } = useChat(); const messages = useMemo(() => { const msgs = [...socketMessages]; if (isLoadingAdvisorMessage) { msgs.unshift({ id: "typing", type: "text", text: "…", role: "assistant", isRead: false, createdDate: new Date().toISOString(), }); } return msgs; }, [isLoadingAdvisorMessage, socketMessages]); const virtualizer = useVirtualizer({ enabled: messages.length > 0, count: hasMoreOlderMessages ? messages.length + 1 : messages.length, getScrollElement: () => messagesWrapperRef.current, measureElement: el => el.getBoundingClientRect().height, getItemKey: idx => messages[idx]?.id ?? idx, estimateSize: _i => 100, overscan: 5, paddingStart: 36, paddingEnd: 36, gap: 8, }); const items = virtualizer.getVirtualItems(); const scrollToBottom = useCallback(() => { virtualizer.scrollToOffset(0); }, [virtualizer]); useEffect(() => { const handleScroll = (e: WheelEvent) => { e.preventDefault(); const currentTarget = e.currentTarget as HTMLElement; if (currentTarget) { currentTarget.scrollTop -= e.deltaY; } }; messagesWrapperRef.current?.addEventListener("wheel", handleScroll, { passive: false, }); return () => { // eslint-disable-next-line react-hooks/exhaustive-deps messagesWrapperRef.current?.removeEventListener("wheel", handleScroll); }; }, []); useEffect(() => { const [lastItem] = [...items].reverse(); if (!lastItem) { return; } if ( lastItem.index >= messages.length - 1 && hasMoreOlderMessages && !isLoadingOlder ) { loadOlder(); } }, [hasMoreOlderMessages, loadOlder, messages.length, isLoadingOlder, items]); useEffect(() => { if (!messagesWrapperRef.current || messages.length === 0) return; setIsScrolledUp((virtualizer.scrollOffset || 0) > 600); }, [virtualizer.scrollOffset, messages.length, messagesWrapperRef]); return ( <> {isScrolledUp && ( )}
{ send(suggestion); }} />
{items.map(virtualRow => { const message = messages[virtualRow.index]; const isLoaderRow = virtualRow.index > messages.length - 1; return (
{!isLoaderRow && ( )} {isLoaderRow && (
)}
); })}
); } export const ChatMessagesWrapperLoader = () => { return (
); };