diff --git a/web/src/components/ChatBox/ChatMessageList/VirtualizedList.tsx b/web/src/components/ChatBox/ChatMessageList/VirtualizedList.tsx new file mode 100644 index 00000000..92094d39 --- /dev/null +++ b/web/src/components/ChatBox/ChatMessageList/VirtualizedList.tsx @@ -0,0 +1,140 @@ +import DynamicSizeList from '@/components/DynamicVirtualizedList/DynamicSizeList'; +import { Divider } from 'antd'; +import React, { useRef } from 'react'; +import AutoSizer from 'react-virtualized-auto-sizer'; +import { + ChatMessage, + getMessageTimeDiff, + shouldShowMessageTime, +} from 'tailchat-shared'; +import { ChatMessageItem } from './Item'; + +// Reference: https://github.com/mattermost/mattermost-webapp/blob/master/components/post_view/post_list_virtualized/post_list_virtualized.jsx + +const OVERSCAN_COUNT_BACKWARD = 80; +const OVERSCAN_COUNT_FORWARD = 80; + +const postListStyle = { + padding: '14px 0px 7px', +}; + +const virtListStyles: React.CSSProperties = { + position: 'absolute', + bottom: 0, + maxHeight: '100%', +}; + +const dynamicListStyle: React.CSSProperties = {}; // TODO + +interface VirtualizedMessageListProps { + messages: ChatMessage[]; +} +export const VirtualizedMessageList: React.FC = + React.memo((props) => { + const listRef = useRef(null); + const postListRef = useRef(null); + + const onScroll = (info: any) => { + console.log('onScroll', info); + }; + + const initScrollToIndex = () => { + return { + index: 0, + position: 'end', + }; + }; + + const onItemsRendered = ({ visibleStartIndex }: any) => { + // this.updateFloatingTimestamp(visibleStartIndex); + console.log('visibleStartIndex', visibleStartIndex); + }; + + const scrollToFailed = (index: number) => { + console.log('scrollToFailed', index); + // if (index === 0) { + // this.props.actions.changeUnreadChunkTimeStamp(''); + // } else { + // this.props.actions.changeUnreadChunkTimeStamp(this.props.lastViewedAt); + // } + }; + + const renderRow = ({ data, itemId, style }: any) => { + const index = data.indexOf(itemId); + const message = props.messages.find((m) => m._id === itemId); // TODO: 这里是因为mattermost的动态列表传的id因此只能这边再用id找回,可以看看是否可以优化 + + if (!message) { + return
; + } + + let showDate = true; + let showAvatar = true; + const messageCreatedAt = new Date(message.createdAt ?? ''); + if (index > 0) { + // 当不是第一条数据时 + + // 进行时间合并 + const prevMessage = data[index - 1]; + if ( + !shouldShowMessageTime( + new Date(prevMessage.createdAt ?? ''), + messageCreatedAt + ) + ) { + showDate = false; + } + + // 进行头像合并(在同一时间块下 且发送者为同一人) + if (showDate === false) { + showAvatar = prevMessage.author !== message.author; + } + } + + return ( +
+ {showDate && ( + + {getMessageTimeDiff(messageCreatedAt)} + + )} + +
+ ); + }; + + // 初始渲染范围 + const initRangeToRender = [ + props.messages.length - 50, + props.messages.length - 1, + ]; + + return ( + + {({ height, width }) => ( + m._id)} + overscanCountForward={OVERSCAN_COUNT_FORWARD} + overscanCountBackward={OVERSCAN_COUNT_BACKWARD} + onScroll={onScroll} + initScrollToIndex={initScrollToIndex} + canLoadMorePosts={() => {}} + innerRef={postListRef} + style={{ ...virtListStyles, ...dynamicListStyle }} + innerListStyle={postListStyle} + initRangeToRender={initRangeToRender} + // loaderId={PostListRowListIds.OLDER_MESSAGES_LOADER} + // correctScrollToBottom={this.props.atLatestPost} + onItemsRendered={onItemsRendered} + scrollToFailed={scrollToFailed} + > + {renderRow} + + )} + + ); + }); +VirtualizedMessageList.displayName = 'VirtualizedMessageList'; diff --git a/web/src/components/DynamicVirtualizedList/DynamicSizeList.tsx b/web/src/components/DynamicVirtualizedList/DynamicSizeList.tsx index abf1d2e6..3eb4dbfd 100644 --- a/web/src/components/DynamicVirtualizedList/DynamicSizeList.tsx +++ b/web/src/components/DynamicVirtualizedList/DynamicSizeList.tsx @@ -138,8 +138,12 @@ type OnScrollArgs = { }; interface DynamicSizeListProps { - canLoadMorePosts: (id?: string) => void; - children: (info: { data: any; itemId: any; style?: any }) => JSX.Element; + canLoadMorePosts: () => void; + children: (info: { + data: any; + itemId: any; + style?: any; + }) => React.ReactElement; height: number; initRangeToRender: number[]; initScrollToIndex: () => any;