@plteam/chat-ui
Version:
CUI Kit is a free and open-source library for creating AI assistant chat interfaces, built with React, Material UI, and TypeScript
81 lines (80 loc) • 3.37 kB
JavaScript
import * as React from 'react';
import throttle from 'lodash.throttle';
import { NOOP } from '../../utils/NOOP';
import { useTablet } from '../../ui/Responsive';
import { isWindowCheck } from '../hooks/useScrollSave';
export const useChatScrollFunctions = (thread, scrollTo, getPosition, getScrollContainer) => {
const [showButton, setShowButton] = React.useState(false);
const [lastScrolledThread, setLastScrolledThread] = React.useState();
const isTablet = useTablet();
const handleBottomScroll = React.useCallback(() => {
scrollTo(getPosition().scrollHeight, 'smooth');
}, []);
const handleCheckButtonState = React.useCallback(throttle(() => {
const container = getPosition();
const btnState = (container.scrollHeight > Math.ceil(container.scrollTop + container.offsetHeight) + 50);
if (showButton !== btnState) {
setShowButton(btnState);
}
}, 300, { trailing: true }), [showButton]);
const handleSaveScrollPosition = React.useCallback(throttle(() => {
if (thread) {
thread.scrollY = getPosition().scrollTop;
}
}, 100), [thread?.id]);
const onScrollCallback = React.useCallback(() => {
handleCheckButtonState();
handleSaveScrollPosition();
}, [handleCheckButtonState, handleSaveScrollPosition]);
// scroll the dialog to the end of the list or to the previous location when opening
React.useEffect(() => {
if (thread && lastScrolledThread?.id !== thread.id) {
setLastScrolledThread(thread);
if (thread.scrollY < 0) {
// feature removed because the auto-scroll when opening a thread was annoying
// TODO: in the first frame the height is incomplete, then updates
/*setTimeout(() => {
const scrollHeight = getPosition().scrollHeight * 1.5;
scrollTo(scrollHeight, 'smooth');
}, 100);*/
}
else {
scrollTo(thread.scrollY);
}
}
}, [thread?.id]);
React.useEffect(() => {
if (!thread)
return NOOP;
const scrollContainer = getScrollContainer();
scrollContainer?.addEventListener('scroll', onScrollCallback);
return () => {
scrollContainer?.removeEventListener('scroll', onScrollCallback);
};
}, [onScrollCallback, thread?.id]);
// Track dialog size changes to hide/show the "Down" button
React.useEffect(() => {
let container = getScrollContainer();
if (!container || isWindowCheck(container))
container = document.body;
const observer = new ResizeObserver(handleCheckButtonState);
observer.observe(container);
return () => observer.disconnect();
}, [getScrollContainer]);
/**
* Following the message has always been jerky and is now irrelevant,
* since modern LLMs quickly provide an answer, the user won't have time to read it
*/
/*useMessageFollowing(
showButton,
getPosition,
scrollTo,
);*/
return {
scrollButtonEnabled: showButton,
handleCheckButtonState,
handleBottomScroll,
handleSaveScrollPosition,
isTablet,
};
};