Update ChatPanel to handle scroll position and message fetching

- Added selectors for isTop, isBottom, and isMiddle in chatSlice.
- Removed scrollPosition and stopScrolling states from ChatPanel.
- Added onScrollPositionChange function to handle scroll events.
- Dispatch onMessagesTop, onMessagesBottom, and onMessagesMiddle actions based on scroll position.
This commit is contained in:
Rankin Zheng 2023-06-13 11:54:31 +08:00
parent ec440be005
commit 9a906a1c0f
2 changed files with 50 additions and 15 deletions

View File

@ -24,6 +24,12 @@ import {
selectErrorMessage, selectErrorMessage,
selectMessages, selectMessages,
selectMessageCount, selectMessageCount,
selectIsBottom,
selectIsTop,
selectIsMiddle,
onMessagesBottom,
onMessagesTop,
onMessagesMiddle,
fetchHistoryMessages, fetchHistoryMessages,
} from './chatSlice'; } from './chatSlice';
@ -89,22 +95,38 @@ const chatPanel = () => {
const errorMessage = useAppSelector(selectErrorMessage); const errorMessage = useAppSelector(selectErrorMessage);
const messages = useAppSelector(selectMessages); const messages = useAppSelector(selectMessages);
const messageCount = useAppSelector(selectMessageCount); const messageCount = useAppSelector(selectMessageCount);
const isTop = useAppSelector(selectIsTop);
const isBottom = useAppSelector(selectIsBottom);
const isMiddle = useAppSelector(selectIsMiddle);
const [chatContainerRef, chatContainerRect] = useResizeObserver(); const [chatContainerRef, chatContainerRect] = useResizeObserver();
const scrollViewport = useRef<HTMLDivElement>(null); const scrollViewport = useRef<HTMLDivElement>(null);
const { height, width } = useViewportSize(); const { height, width } = useViewportSize();
const [scrollPosition, onScrollPositionChange] = useState({ x: 0, y: 0 });
const [stopScrolling, setStopScrolling] = useState(false);
const scrollToBottom = () => const scrollToBottom = () =>
scrollViewport?.current?.scrollTo({ top: scrollViewport.current.scrollHeight, behavior: 'smooth' }); scrollViewport?.current?.scrollTo({ top: scrollViewport.current.scrollHeight, behavior: 'smooth' });
const timer = useTimeout(() => { const timer = useTimeout(() => {
// console.log(`stopScrolling:${stopScrolling}`); if (isBottom) {
if (!stopScrolling) {
scrollToBottom(); scrollToBottom();
} }
}, 1000); }, 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(() => { useEffect(() => {
dispatch(fetchHistoryMessages()); dispatch(fetchHistoryMessages());
messageUtil.registerHandler('receiveMessagePartial', (message: { text: string; }) => { 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(() => { useEffect(() => {
if (generating) { if (generating) {
// new a bot message // new a bot message

View File

@ -24,6 +24,9 @@ export const chatSlice = createSlice({
errorMessage: '', errorMessage: '',
messages: <any>[], messages: <any>[],
messageCount: 10, messageCount: 10,
isBottom: true,
isMiddle: false,
isTop: false,
}, },
reducers: { reducers: {
startGenerating: (state, action) => { startGenerating: (state, action) => {
@ -71,6 +74,21 @@ export const chatSlice = createSlice({
}, },
happendError: (state, action) => { happendError: (state, action) => {
state.errorMessage = action.payload; 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) => { extraReducers: (builder) => {
@ -99,6 +117,9 @@ export const selectCurrentMessage = (state: RootState) => state.chat.currentMess
export const selectErrorMessage = (state: RootState) => state.chat.errorMessage; export const selectErrorMessage = (state: RootState) => state.chat.errorMessage;
export const selectMessages = (state: RootState) => state.chat.messages; export const selectMessages = (state: RootState) => state.chat.messages;
export const selectMessageCount = (state: RootState) => state.chat.messageCount; 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 { export const {
@ -112,6 +133,9 @@ export const {
popMessage, popMessage,
clearMessages, clearMessages,
updateMessage, updateMessage,
onMessagesTop,
onMessagesBottom,
onMessagesMiddle,
} = chatSlice.actions; } = chatSlice.actions;
export default chatSlice.reducer; export default chatSlice.reducer;