diff --git a/src/views/ChatPanel.tsx b/src/views/ChatPanel.tsx index ed4613a..b462ad9 100644 --- a/src/views/ChatPanel.tsx +++ b/src/views/ChatPanel.tsx @@ -24,6 +24,12 @@ import { selectErrorMessage, selectMessages, selectMessageCount, + selectIsBottom, + selectIsTop, + selectIsMiddle, + onMessagesBottom, + onMessagesTop, + onMessagesMiddle, fetchHistoryMessages, } from './chatSlice'; @@ -89,22 +95,38 @@ const chatPanel = () => { const errorMessage = useAppSelector(selectErrorMessage); const messages = useAppSelector(selectMessages); const messageCount = useAppSelector(selectMessageCount); + const isTop = useAppSelector(selectIsTop); + const isBottom = useAppSelector(selectIsBottom); + const isMiddle = useAppSelector(selectIsMiddle); const [chatContainerRef, chatContainerRect] = useResizeObserver(); const scrollViewport = useRef(null); const { height, width } = useViewportSize(); - const [scrollPosition, onScrollPositionChange] = useState({ x: 0, y: 0 }); - const [stopScrolling, setStopScrolling] = useState(false); const scrollToBottom = () => scrollViewport?.current?.scrollTo({ top: scrollViewport.current.scrollHeight, behavior: 'smooth' }); const timer = useTimeout(() => { - // console.log(`stopScrolling:${stopScrolling}`); - if (!stopScrolling) { + if (isBottom) { scrollToBottom(); } }, 1000); + const onScrollPositionChange = ({ x, y }) => { + const sh = scrollViewport.current?.scrollHeight || 0; + const vh = scrollViewport.current?.clientHeight || 0; + const gap = sh - vh - y; + const isBottom = sh < vh ? true : gap < 100; + const isTop = y === 0; + // console.log(`sh:${sh},vh:${vh},x:${x},y:${y},gap:${gap}`); + if (isBottom) { + dispatch(onMessagesBottom()); + } else if (isTop) { + dispatch(onMessagesTop()); + } else { + dispatch(onMessagesMiddle()); + } + }; + useEffect(() => { dispatch(fetchHistoryMessages()); messageUtil.registerHandler('receiveMessagePartial', (message: { text: string; }) => { @@ -122,17 +144,6 @@ const chatPanel = () => { }; }, []); - useEffect(() => { - const sh = scrollViewport.current?.scrollHeight || 0; - const vh = scrollViewport.current?.clientHeight || 0; - const isBottom = sh < vh ? true : sh - vh - scrollPosition.y < 3; - if (isBottom) { - setStopScrolling(false); - } else { - setStopScrolling(true); - } - }, [scrollPosition]); - useEffect(() => { if (generating) { // new a bot message diff --git a/src/views/chatSlice.ts b/src/views/chatSlice.ts index 003bdb5..12cae32 100644 --- a/src/views/chatSlice.ts +++ b/src/views/chatSlice.ts @@ -24,6 +24,9 @@ export const chatSlice = createSlice({ errorMessage: '', messages: [], messageCount: 10, + isBottom: true, + isMiddle: false, + isTop: false, }, reducers: { startGenerating: (state, action) => { @@ -71,6 +74,21 @@ export const chatSlice = createSlice({ }, happendError: (state, action) => { state.errorMessage = action.payload; + }, + onMessagesTop: (state) => { + state.isTop = true; + state.isBottom = false; + state.isMiddle = false; + }, + onMessagesBottom: (state) => { + state.isTop = false; + state.isBottom = true; + state.isMiddle = false; + }, + onMessagesMiddle: (state) => { + state.isTop = false; + state.isBottom = false; + state.isMiddle = true; } }, extraReducers: (builder) => { @@ -99,6 +117,9 @@ export const selectCurrentMessage = (state: RootState) => state.chat.currentMess export const selectErrorMessage = (state: RootState) => state.chat.errorMessage; export const selectMessages = (state: RootState) => state.chat.messages; export const selectMessageCount = (state: RootState) => state.chat.messageCount; +export const selectIsBottom = (state: RootState) => state.chat.isBottom; +export const selectIsTop = (state: RootState) => state.chat.isTop; +export const selectIsMiddle = (state: RootState) => state.chat.isMiddle; export const { @@ -112,6 +133,9 @@ export const { popMessage, clearMessages, updateMessage, + onMessagesTop, + onMessagesBottom, + onMessagesMiddle, } = chatSlice.actions; export default chatSlice.reducer; \ No newline at end of file