refactor(Scrollbar, Chat, Messages): improve scroll handling and clean up component structure

This commit is contained in:
kangfenmao 2025-05-29 14:11:53 +08:00
parent c6e574d080
commit 1a4dbd1843
3 changed files with 32 additions and 33 deletions

View File

@ -12,33 +12,41 @@ const Scrollbar: FC<Props> = ({ ref: passedRef, right, children, onScroll: exter
const [isScrolling, setIsScrolling] = useState(false)
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
const handleScroll = useCallback(() => {
setIsScrolling(true)
const clearScrollingTimeout = useCallback(() => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current)
timeoutRef.current = null
}
timeoutRef.current = setTimeout(() => setIsScrolling(false), 1500)
}, [])
const throttledInternalScrollHandler = throttle(handleScroll, 200)
const handleScroll = useCallback(() => {
setIsScrolling(true)
clearScrollingTimeout()
timeoutRef.current = setTimeout(() => {
setIsScrolling(false)
timeoutRef.current = null
}, 1000)
}, [clearScrollingTimeout])
// eslint-disable-next-line react-hooks/exhaustive-deps
const throttledInternalScrollHandler = useCallback(throttle(handleScroll, 100, { leading: true, trailing: true }), [
handleScroll
])
// Combined scroll handler
const combinedOnScroll = useCallback(() => {
// Event is available if needed by internal handler
throttledInternalScrollHandler() // Call internal logic
throttledInternalScrollHandler()
if (externalOnScroll) {
externalOnScroll() // Call external logic (from useScrollPosition)
externalOnScroll()
}
}, [throttledInternalScrollHandler, externalOnScroll])
useEffect(() => {
return () => {
timeoutRef.current && clearTimeout(timeoutRef.current)
clearScrollingTimeout()
throttledInternalScrollHandler.cancel()
}
}, [throttledInternalScrollHandler])
}, [throttledInternalScrollHandler, clearScrollingTimeout])
return (
<Container

View File

@ -114,16 +114,14 @@ const Chat: FC<Props> = (props) => {
includeUser={filterIncludeUser}
onIncludeUserChange={userOutlinedItemClickHandler}
/>
<MessagesContainer className="messages-container">
<Messages
key={props.activeTopic.id}
assistant={assistant}
topic={props.activeTopic}
setActiveTopic={props.setActiveTopic}
onComponentUpdate={messagesComponentUpdateHandler}
onFirstUpdate={messagesComponentFirstUpdateHandler}
/>
</MessagesContainer>
<Messages
key={props.activeTopic.id}
assistant={assistant}
topic={props.activeTopic}
setActiveTopic={props.setActiveTopic}
onComponentUpdate={messagesComponentUpdateHandler}
onFirstUpdate={messagesComponentFirstUpdateHandler}
/>
<QuickPanelProvider>
<Inputbar assistant={assistant} setActiveTopic={props.setActiveTopic} topic={props.activeTopic} />
{isMultiSelectMode && <MultiSelectActionPopup topic={props.activeTopic} />}
@ -142,13 +140,6 @@ const Chat: FC<Props> = (props) => {
)
}
const MessagesContainer = styled.div`
display: flex;
flex-direction: column;
overflow: hidden;
flex: 1;
`
const Container = styled.div`
display: flex;
flex-direction: row;

View File

@ -53,7 +53,7 @@ const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic, o
`topic-${topic.id}`
)
const { t } = useTranslation()
const { showPrompt, showTopics, topicPosition, showAssistants, messageNavigation } = useSettings()
const { showPrompt, topicPosition, messageNavigation } = useSettings()
const { updateTopic, addTopic } = useAssistant(assistant.id)
const dispatch = useAppDispatch()
const [displayMessages, setDisplayMessages] = useState<Message[]>([])
@ -267,8 +267,9 @@ const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic, o
const groupedMessages = useMemo(() => Object.entries(getGroupedMessages(displayMessages)), [displayMessages])
return (
<Container
<MessagesContainer
id="messages"
className="messages-container"
ref={scrollContainerRef}
style={{ position: 'relative', paddingTop: showPrompt ? 10 : 0 }}
key={assistant.id}
@ -304,14 +305,13 @@ const Messages: React.FC<MessagesProps> = ({ assistant, topic, setActiveTopic, o
</NarrowLayout>
{messageNavigation === 'anchor' && <MessageAnchorLine messages={displayMessages} />}
{messageNavigation === 'buttons' && <ChatNavigation containerId="messages" />}
<SelectionBox
isMultiSelectMode={isMultiSelectMode}
scrollContainerRef={scrollContainerRef}
messageElements={messageElements.current}
handleSelectMessage={handleSelectMessage}
/>
</Container>
</MessagesContainer>
)
}
@ -369,7 +369,7 @@ interface ContainerProps {
$right?: boolean
}
const Container = styled(Scrollbar)<ContainerProps>`
const MessagesContainer = styled(Scrollbar)<ContainerProps>`
display: flex;
flex-direction: column-reverse;
padding: 10px 0 20px;