From 5bcd51c0e9332ab24bc9335f4981ebe97abffe12 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kezik Date: Tue, 20 Feb 2024 10:52:01 +0100 Subject: [PATCH 1/7] fix(chat): fix autoscrolling for next chat and chat regeneration (Issue #501) --- apps/chat/src/components/Chat/Chat.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/chat/src/components/Chat/Chat.tsx b/apps/chat/src/components/Chat/Chat.tsx index 6cb94426d7..a49ecafd6d 100644 --- a/apps/chat/src/components/Chat/Chat.tsx +++ b/apps/chat/src/components/Chat/Chat.tsx @@ -55,6 +55,7 @@ import { PlaybackEmptyInfo } from './Playback/PlaybackEmptyInfo'; import { Feature } from '@epam/ai-dial-shared'; const scrollThrottlingTimeout = 250; +const bottomTolerance = 30; export const ChatView = memo(() => { const dispatch = useAppDispatch(); @@ -211,7 +212,6 @@ export const ChatView = memo(() => { if (chatContainerRef.current) { const { scrollTop, scrollHeight, clientHeight } = chatContainerRef.current; - const bottomTolerance = 30; if (scrollTop + clientHeight < scrollHeight - bottomTolerance) { clearTimeout(disableAutoScrollTimeoutRef.current); @@ -253,11 +253,6 @@ export const ChatView = memo(() => { setIsShowChatSettings(false); if (selectedConversations.length > 0) { - if (isSentRef.current) { - handleScroll(); - isSentRef.current = false; - } - const mergedMessages: MergedMessages[] = []; for (let i = 0; i < selectedConversations[0].messages.length; i++) { if (selectedConversations[0].messages[i].role === Role.System) continue; @@ -270,6 +265,17 @@ export const ChatView = memo(() => { ]), ); } + + if (chatContainerRef.current && isSentRef.current) { + const { scrollTop, scrollHeight, clientHeight } = + chatContainerRef.current; + + if (scrollTop + clientHeight >= scrollHeight - bottomTolerance) { + setAutoScroll(); + isSentRef.current = false; + } + } + setMergedMessages([...mergedMessages]); } From 64a64288af0446fcdf91f2b53d55dc5d8c40240c Mon Sep 17 00:00:00 2001 From: Aliaksandr Kezik Date: Tue, 20 Feb 2024 10:56:00 +0100 Subject: [PATCH 2/7] refactor --- apps/chat/src/components/Chat/Chat.tsx | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/apps/chat/src/components/Chat/Chat.tsx b/apps/chat/src/components/Chat/Chat.tsx index a49ecafd6d..5cbf906c1b 100644 --- a/apps/chat/src/components/Chat/Chat.tsx +++ b/apps/chat/src/components/Chat/Chat.tsx @@ -55,7 +55,6 @@ import { PlaybackEmptyInfo } from './Playback/PlaybackEmptyInfo'; import { Feature } from '@epam/ai-dial-shared'; const scrollThrottlingTimeout = 250; -const bottomTolerance = 30; export const ChatView = memo(() => { const dispatch = useAppDispatch(); @@ -212,6 +211,7 @@ export const ChatView = memo(() => { if (chatContainerRef.current) { const { scrollTop, scrollHeight, clientHeight } = chatContainerRef.current; + const bottomTolerance = 30; if (scrollTop + clientHeight < scrollHeight - bottomTolerance) { clearTimeout(disableAutoScrollTimeoutRef.current); @@ -266,16 +266,7 @@ export const ChatView = memo(() => { ); } - if (chatContainerRef.current && isSentRef.current) { - const { scrollTop, scrollHeight, clientHeight } = - chatContainerRef.current; - - if (scrollTop + clientHeight >= scrollHeight - bottomTolerance) { - setAutoScroll(); - isSentRef.current = false; - } - } - + handleScroll(); setMergedMessages([...mergedMessages]); } From 8c7c704966f8552fc3622bae5559528f368b8f76 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kezik Date: Tue, 20 Feb 2024 10:56:42 +0100 Subject: [PATCH 3/7] remove ref --- apps/chat/src/components/Chat/Chat.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/chat/src/components/Chat/Chat.tsx b/apps/chat/src/components/Chat/Chat.tsx index 5cbf906c1b..59c09a0992 100644 --- a/apps/chat/src/components/Chat/Chat.tsx +++ b/apps/chat/src/components/Chat/Chat.tsx @@ -113,7 +113,6 @@ export const ChatView = memo(() => { const nextMessageBoxRef = useRef(null); const [inputHeight, setInputHeight] = useState(142); const [notAllowedType, setNotAllowedType] = useState(null); - const isSentRef = useRef(false); const disableAutoScrollTimeoutRef = useRef>(); const showReplayControls = useMemo(() => { @@ -463,7 +462,6 @@ export const ChatView = memo(() => { activeReplayIndex: 0, }), ); - isSentRef.current = true; }, [dispatch, selectedConversations], ); @@ -481,7 +479,6 @@ export const ChatView = memo(() => { activeReplayIndex: 0, }), ); - isSentRef.current = true; }, [dispatch, selectedConversations]); const onEditMessage = useCallback( @@ -495,7 +492,6 @@ export const ChatView = memo(() => { activeReplayIndex: 0, }), ); - isSentRef.current = true; }, [dispatch, selectedConversations], ); From 28b9a127830d1a895378749a010dbec374310e24 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kezik Date: Wed, 21 Feb 2024 10:31:31 +0100 Subject: [PATCH 4/7] add possibility to stop autoscroll --- apps/chat/src/components/Chat/Chat.tsx | 34 +++++++++++++++++++------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/apps/chat/src/components/Chat/Chat.tsx b/apps/chat/src/components/Chat/Chat.tsx index 59c09a0992..30d95c24c9 100644 --- a/apps/chat/src/components/Chat/Chat.tsx +++ b/apps/chat/src/components/Chat/Chat.tsx @@ -3,7 +3,6 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'next-i18next'; import { clearStateForMessages } from '@/src/utils/app/clear-messages-state'; -import { throttle } from '@/src/utils/data/throttle'; import { OpenAIEntityModelID } from '../../types/openai'; import { @@ -53,11 +52,13 @@ import { PlaybackControls } from './Playback/PlaybackControls'; import { PlaybackEmptyInfo } from './Playback/PlaybackEmptyInfo'; import { Feature } from '@epam/ai-dial-shared'; +import throttle from 'lodash/throttle'; const scrollThrottlingTimeout = 250; export const ChatView = memo(() => { const dispatch = useAppDispatch(); + const appName = useAppSelector(SettingsSelectors.selectAppName); const models = useAppSelector(ModelsSelectors.selectModels); const modelsMap = useAppSelector(ModelsSelectors.selectModelsMap); @@ -83,7 +84,6 @@ export const ChatView = memo(() => { const enabledFeatures = useAppSelector( SettingsSelectors.selectEnabledFeatures, ); - const isReplay = useAppSelector( ConversationsSelectors.selectIsReplaySelectedConversations, ); @@ -93,7 +93,6 @@ export const ChatView = memo(() => { const isExternal = useAppSelector( ConversationsSelectors.selectAreSelectedConversationsExternal, ); - const isPlayback = useAppSelector( ConversationsSelectors.selectIsPlaybackSelectedConversations, ); @@ -114,6 +113,8 @@ export const ChatView = memo(() => { const [inputHeight, setInputHeight] = useState(142); const [notAllowedType, setNotAllowedType] = useState(null); const disableAutoScrollTimeoutRef = useRef>(); + const lastScrollTop = useRef(0); + const scrollBlockRef = useRef(false); const showReplayControls = useMemo(() => { return isReplay && !messageIsStreaming && isReplayPaused; @@ -193,10 +194,7 @@ export const ChatView = memo(() => { textareaRef.current?.focus(); }, [scrollDown]); - const throttledScrollDown = throttle( - scrollDown, - scrollThrottlingTimeout, - ); + const throttledScrollDown = throttle(scrollDown, scrollThrottlingTimeout); useEffect(() => { throttledScrollDown(); @@ -207,13 +205,23 @@ export const ChatView = memo(() => { }, [scrollDown]); const handleScroll = useCallback(() => { + if (scrollBlockRef.current) { + setAutoScrollEnabled(false); + setShowScrollDownButton(true); + return; + } + if (chatContainerRef.current) { const { scrollTop, scrollHeight, clientHeight } = chatContainerRef.current; const bottomTolerance = 30; - if (scrollTop + clientHeight < scrollHeight - bottomTolerance) { + if (lastScrollTop.current > scrollTop) { + setAutoScrollEnabled(false); + setShowScrollDownButton(true); + } else if (scrollTop + clientHeight < scrollHeight - bottomTolerance) { clearTimeout(disableAutoScrollTimeoutRef.current); + disableAutoScrollTimeoutRef.current = setTimeout(() => { setAutoScrollEnabled(false); setShowScrollDownButton(true); @@ -221,6 +229,8 @@ export const ChatView = memo(() => { } else { setAutoScroll(); } + + lastScrollTop.current = scrollTop; } }, []); @@ -265,7 +275,6 @@ export const ChatView = memo(() => { ); } - handleScroll(); setMergedMessages([...mergedMessages]); } @@ -479,6 +488,13 @@ export const ChatView = memo(() => { activeReplayIndex: 0, }), ); + + if (chatContainerRef.current) { + chatContainerRef.current.scrollTo({ + top: chatContainerRef.current.scrollHeight, + behavior: 'smooth', + }); + } }, [dispatch, selectedConversations]); const onEditMessage = useCallback( From 77b6477033d635f5260888da7752d0bc3ba3b79a Mon Sep 17 00:00:00 2001 From: Aliaksandr Kezik Date: Wed, 21 Feb 2024 10:32:42 +0100 Subject: [PATCH 5/7] remove scrollBlock ref --- apps/chat/src/components/Chat/Chat.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apps/chat/src/components/Chat/Chat.tsx b/apps/chat/src/components/Chat/Chat.tsx index 30d95c24c9..589b0b5076 100644 --- a/apps/chat/src/components/Chat/Chat.tsx +++ b/apps/chat/src/components/Chat/Chat.tsx @@ -114,7 +114,6 @@ export const ChatView = memo(() => { const [notAllowedType, setNotAllowedType] = useState(null); const disableAutoScrollTimeoutRef = useRef>(); const lastScrollTop = useRef(0); - const scrollBlockRef = useRef(false); const showReplayControls = useMemo(() => { return isReplay && !messageIsStreaming && isReplayPaused; @@ -205,12 +204,6 @@ export const ChatView = memo(() => { }, [scrollDown]); const handleScroll = useCallback(() => { - if (scrollBlockRef.current) { - setAutoScrollEnabled(false); - setShowScrollDownButton(true); - return; - } - if (chatContainerRef.current) { const { scrollTop, scrollHeight, clientHeight } = chatContainerRef.current; From c26162d69e9f4e3e714861def14e0af1039cee0e Mon Sep 17 00:00:00 2001 From: Aliaksandr Kezik Date: Wed, 21 Feb 2024 10:46:52 +0100 Subject: [PATCH 6/7] remove utils/data --- apps/chat/src/utils/data/throttle.ts | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 apps/chat/src/utils/data/throttle.ts diff --git a/apps/chat/src/utils/data/throttle.ts b/apps/chat/src/utils/data/throttle.ts deleted file mode 100644 index 7b3db0ec8d..0000000000 --- a/apps/chat/src/utils/data/throttle.ts +++ /dev/null @@ -1,25 +0,0 @@ -export function throttle void>( - func: T, - limit: number, -): T { - let lastFunc: ReturnType; - let lastRan: number; - - return ((...args) => { - if (!lastRan) { - func(...args); - lastRan = Date.now(); - } else { - clearTimeout(lastFunc); - lastFunc = setTimeout( - () => { - if (Date.now() - lastRan >= limit) { - func(...args); - lastRan = Date.now(); - } - }, - limit - (Date.now() - lastRan), - ); - } - }) as T; -} From edca622469cdab85dadfd27e3c472d6ffb60158a Mon Sep 17 00:00:00 2001 From: Aliaksandr Kezik Date: Wed, 21 Feb 2024 10:56:02 +0100 Subject: [PATCH 7/7] fix regenerate and chat changing --- apps/chat/src/components/Chat/Chat.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/chat/src/components/Chat/Chat.tsx b/apps/chat/src/components/Chat/Chat.tsx index 589b0b5076..d203b64405 100644 --- a/apps/chat/src/components/Chat/Chat.tsx +++ b/apps/chat/src/components/Chat/Chat.tsx @@ -207,7 +207,7 @@ export const ChatView = memo(() => { if (chatContainerRef.current) { const { scrollTop, scrollHeight, clientHeight } = chatContainerRef.current; - const bottomTolerance = 30; + const bottomTolerance = 25; if (lastScrollTop.current > scrollTop) { setAutoScrollEnabled(false); @@ -268,6 +268,7 @@ export const ChatView = memo(() => { ); } + handleScroll(); setMergedMessages([...mergedMessages]); }