refactor: update auto scroll mechanism (#970)

This commit is contained in:
Jeremy 2025-05-01 02:28:24 -07:00 committed by GitHub
parent 1fd2302914
commit 45978c27a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 250 additions and 52 deletions

45
hooks/use-messages.tsx Normal file
View file

@ -0,0 +1,45 @@
import { useState, useEffect } from 'react';
import { useScrollToBottom } from './use-scroll-to-bottom';
import type { UseChatHelpers } from '@ai-sdk/react';
export function useMessages({
chatId,
status,
}: {
chatId: string;
status: UseChatHelpers['status'];
}) {
const {
containerRef,
endRef,
isAtBottom,
scrollToBottom,
onViewportEnter,
onViewportLeave,
} = useScrollToBottom();
const [hasSentMessage, setHasSentMessage] = useState(false);
useEffect(() => {
if (chatId) {
scrollToBottom('instant');
setHasSentMessage(false);
}
}, [chatId, scrollToBottom]);
useEffect(() => {
if (status === 'submitted') {
setHasSentMessage(true);
}
}, [status]);
return {
containerRef,
endRef,
isAtBottom,
scrollToBottom,
onViewportEnter,
onViewportLeave,
hasSentMessage,
};
}

View file

@ -0,0 +1,49 @@
import useSWR from 'swr';
import { useRef, useEffect, useCallback } from 'react';
type ScrollFlag = ScrollBehavior | false;
export function useScrollToBottom() {
const containerRef = useRef<HTMLDivElement>(null);
const endRef = useRef<HTMLDivElement>(null);
const { data: isAtBottom = false, mutate: setIsAtBottom } = useSWR(
'messages:is-at-bottom',
null,
{ fallbackData: false },
);
const { data: scrollBehavior = false, mutate: setScrollBehavior } =
useSWR<ScrollFlag>('messages:should-scroll', null, { fallbackData: false });
useEffect(() => {
if (scrollBehavior) {
endRef.current?.scrollIntoView({ behavior: scrollBehavior });
setScrollBehavior(false);
}
}, [setScrollBehavior, scrollBehavior]);
const scrollToBottom = useCallback(
(scrollBehavior: ScrollBehavior = 'smooth') => {
setScrollBehavior(scrollBehavior);
},
[setScrollBehavior],
);
function onViewportEnter() {
setIsAtBottom(true);
}
function onViewportLeave() {
setIsAtBottom(false);
}
return {
containerRef,
endRef,
isAtBottom,
scrollToBottom,
onViewportEnter,
onViewportLeave,
};
}