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 [isScrolling, setIsScrolling] = useState(false)
const timeoutRef = useRef<NodeJS.Timeout | null>(null) const timeoutRef = useRef<NodeJS.Timeout | null>(null)
const handleScroll = useCallback(() => { const clearScrollingTimeout = useCallback(() => {
setIsScrolling(true)
if (timeoutRef.current) { if (timeoutRef.current) {
clearTimeout(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 // Combined scroll handler
const combinedOnScroll = useCallback(() => { const combinedOnScroll = useCallback(() => {
// Event is available if needed by internal handler throttledInternalScrollHandler()
throttledInternalScrollHandler() // Call internal logic
if (externalOnScroll) { if (externalOnScroll) {
externalOnScroll() // Call external logic (from useScrollPosition) externalOnScroll()
} }
}, [throttledInternalScrollHandler, externalOnScroll]) }, [throttledInternalScrollHandler, externalOnScroll])
useEffect(() => { useEffect(() => {
return () => { return () => {
timeoutRef.current && clearTimeout(timeoutRef.current) clearScrollingTimeout()
throttledInternalScrollHandler.cancel() throttledInternalScrollHandler.cancel()
} }
}, [throttledInternalScrollHandler]) }, [throttledInternalScrollHandler, clearScrollingTimeout])
return ( return (
<Container <Container

View File

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

View File

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