import { useRecoilValue } from 'recoil'; import { useLayoutEffect, useState, useRef, useCallback, useEffect } from 'react'; import type { TMessage } from 'librechat-data-provider'; import useScrollToRef from '../useScrollToRef'; import { useChatContext } from '~/Providers'; import store from '~/store'; export default function useMessageScrolling(messagesTree?: TMessage[] | null) { const autoScroll = useRecoilValue(store.autoScroll); const timeoutIdRef = useRef(); const scrollableRef = useRef(null); const messagesEndRef = useRef(null); const [showScrollButton, setShowScrollButton] = useState(false); const { conversation, setAbortScroll, isSubmitting, abortScroll } = useChatContext(); const { conversationId } = conversation ?? {}; const checkIfAtBottom = useCallback(() => { if (!scrollableRef.current) { return; } const { scrollTop, scrollHeight, clientHeight } = scrollableRef.current; const diff = Math.abs(scrollHeight - scrollTop); const percent = Math.abs(clientHeight - diff) / clientHeight; const hasScrollbar = scrollHeight > clientHeight && percent >= 0.15; setShowScrollButton(hasScrollbar); }, [scrollableRef]); useLayoutEffect(() => { const scrollableElement = scrollableRef.current; if (!scrollableElement) { return; } const timeoutId = setTimeout(checkIfAtBottom, 650); return () => { clearTimeout(timeoutId); }; }, [checkIfAtBottom]); const debouncedHandleScroll = useCallback(() => { clearTimeout(timeoutIdRef.current); timeoutIdRef.current = setTimeout(checkIfAtBottom, 100); }, [checkIfAtBottom]); const scrollCallback = () => setShowScrollButton(false); const { scrollToRef: scrollToBottom, handleSmoothToRef } = useScrollToRef({ targetRef: messagesEndRef, callback: scrollCallback, smoothCallback: () => { scrollCallback(); setAbortScroll(false); }, }); useEffect(() => { if (!messagesTree) { return; } if (isSubmitting && scrollToBottom && !abortScroll) { scrollToBottom(); } }, [isSubmitting, messagesTree, scrollToBottom, abortScroll]); useEffect(() => { if (scrollToBottom && autoScroll && conversationId !== 'new') { scrollToBottom(); } }, [autoScroll, conversationId, scrollToBottom]); return { conversation, scrollableRef, messagesEndRef, scrollToBottom, showScrollButton, handleSmoothToRef, debouncedHandleScroll, }; }