From c9388c744e554a89b1d784c8475fd775207cd806 Mon Sep 17 00:00:00 2001 From: molvqingtai Date: Thu, 19 Sep 2024 22:52:19 +0800 Subject: [PATCH] feat: message list implements virtual scrolling --- package.json | 1 + pnpm-lock.yaml | 15 +++++++++++++ src/app/content/components/MessageItem.tsx | 5 +---- src/app/content/components/MessageList.tsx | 20 +++++++++++------ src/app/content/views/Main/index.tsx | 25 +++++++++------------ src/app/options/components/AvatarSelect.tsx | 2 +- src/app/options/components/ProfileForm.tsx | 2 +- src/components/ui/ScrollArea.tsx | 4 ++-- 8 files changed, 45 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 94b6e60..5d156b7 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "react-hook-form": "^7.51.0", "react-markdown": "^9.0.1", "react-use": "^17.5.0", + "react-virtuoso": "^4.10.4", "remark-breaks": "^4.0.0", "remark-gfm": "^4.0.0", "remesh": "^4.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9192699..7dbaf28 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,6 +83,9 @@ importers: react-use: specifier: ^17.5.0 version: 17.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-virtuoso: + specifier: ^4.10.4 + version: 4.10.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) remark-breaks: specifier: ^4.0.0 version: 4.0.0 @@ -4796,6 +4799,13 @@ packages: react: '*' react-dom: '*' + react-virtuoso@4.10.4: + resolution: {integrity: sha512-G/gprhTbK+lzMxoo/iStcZxVEGph/cIhc3WANEpt92RuMw+LiCZOmBfKoeoZOHlm/iyftTrDJhGaTCpxyucnkQ==} + engines: {node: '>=10'} + peerDependencies: + react: '>=16 || >=17 || >= 18' + react-dom: '>=16 || >=17 || >= 18' + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -11333,6 +11343,11 @@ snapshots: ts-easing: 0.2.0 tslib: 2.7.0 + react-virtuoso@4.10.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react@18.3.1: dependencies: loose-envify: 1.4.0 diff --git a/src/app/content/components/MessageItem.tsx b/src/app/content/components/MessageItem.tsx index 9d377ed..8d0366b 100644 --- a/src/app/content/components/MessageItem.tsx +++ b/src/app/content/components/MessageItem.tsx @@ -24,10 +24,7 @@ const MessageItem: FC = (props) => { props.onHateChange?.(checked) } return ( -
+
{props.data.username.at(0)} diff --git a/src/app/content/components/MessageList.tsx b/src/app/content/components/MessageList.tsx index 88dd64f..df0054d 100644 --- a/src/app/content/components/MessageList.tsx +++ b/src/app/content/components/MessageList.tsx @@ -1,20 +1,26 @@ -import { type ReactElement } from 'react' +import { FC, useRef, type ReactElement } from 'react' -import React from 'react' import { type MessageItemProps } from './MessageItem' import { ScrollArea } from '@/components/ui/ScrollArea' +import { Virtuoso } from 'react-virtuoso' export interface MessageListProps { children?: Array> } -// [&>div>div]:!block fix word-break: break-word; -const MessageList = React.forwardRef(({ children }, ref) => { +const MessageList: FC = ({ children }) => { + const scrollParentRef = useRef(null) return ( - - {children} + + (isAtBottom ? 'smooth' : 'auto')} + initialTopMostItemIndex={{ index: 'LAST', align: 'end' }} + data={children} + customScrollParent={scrollParentRef.current!} + itemContent={(_: any, item: ReactElement) => item} + /> ) -}) +} MessageList.displayName = 'MessageList' diff --git a/src/app/content/views/Main/index.tsx b/src/app/content/views/Main/index.tsx index 151b421..8476933 100644 --- a/src/app/content/views/Main/index.tsx +++ b/src/app/content/views/Main/index.tsx @@ -17,9 +17,6 @@ const Main: FC = () => { like: message.likeUsers.some((likeUser) => likeUser.userId === userInfo?.id), hate: message.hateUsers.some((hateUser) => hateUser.userId === userInfo?.id) })) - const messageListRef = useRef(null) - - const isUpdate = useRef(false) const handleLikeChange = (messageId: string) => { send(roomDomain.command.SendLikeMessageCommand(messageId)) @@ -29,20 +26,20 @@ const Main: FC = () => { send(roomDomain.command.SendHateMessageCommand(messageId)) } - useEffect(() => { - const lastMessageRef = messageListRef.current?.querySelector('[data-index]:last-child') - const timerId = setTimeout(() => { - requestAnimationFrame(() => { - lastMessageRef?.scrollIntoView({ behavior: isUpdate.current ? 'smooth' : 'instant', block: 'end' }) - isUpdate.current = true - }) - }, 0) + // useEffect(() => { + // const lastMessageRef = messageListRef.current?.querySelector('[data-index]:last-child') + // const timerId = setTimeout(() => { + // requestAnimationFrame(() => { + // lastMessageRef?.scrollIntoView({ behavior: isUpdate.current ? 'smooth' : 'instant', block: 'end' }) + // isUpdate.current = true + // }) + // }, 0) - return () => clearTimeout(timerId) - }, [messageList.length]) + // return () => clearTimeout(timerId) + // }, [messageList.length]) return ( - + {messageList.map((message, index) => ( ( { onClick={handleRandomAvatar} > - Random Avatar + Ugly Avatar
diff --git a/src/components/ui/ScrollArea.tsx b/src/components/ui/ScrollArea.tsx index 19ae458..3281233 100644 --- a/src/components/ui/ScrollArea.tsx +++ b/src/components/ui/ScrollArea.tsx @@ -7,8 +7,8 @@ const ScrollArea = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - - + + {children}