From ebebeb5f0a7892fa44598843a87802b82728eb67 Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Sat, 23 Oct 2021 23:51:57 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=8A=A8=E6=80=81=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E7=9A=84=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChatMessageList/VirtualizedList.tsx | 29 +++--- .../DynamicSizeList.tsx | 94 ++++++++----------- .../DynamicVirtualizedList/ItemMeasurer.tsx | 22 +++-- 3 files changed, 70 insertions(+), 75 deletions(-) diff --git a/web/src/components/ChatBox/ChatMessageList/VirtualizedList.tsx b/web/src/components/ChatBox/ChatMessageList/VirtualizedList.tsx index 92094d39..bb1a4632 100644 --- a/web/src/components/ChatBox/ChatMessageList/VirtualizedList.tsx +++ b/web/src/components/ChatBox/ChatMessageList/VirtualizedList.tsx @@ -1,6 +1,8 @@ -import DynamicSizeList from '@/components/DynamicVirtualizedList/DynamicSizeList'; +import DynamicSizeList, { + OnScrollInfo, +} from '@/components/DynamicVirtualizedList/DynamicSizeList'; import { Divider } from 'antd'; -import React, { useRef } from 'react'; +import React, { useRef, useState } from 'react'; import AutoSizer from 'react-virtualized-auto-sizer'; import { ChatMessage, @@ -33,9 +35,15 @@ export const VirtualizedMessageList: React.FC = React.memo((props) => { const listRef = useRef(null); const postListRef = useRef(null); + const [isBottom, setIsBottom] = useState(true); - const onScroll = (info: any) => { + const onScroll = (info: OnScrollInfo) => { console.log('onScroll', info); + + if (info.clientHeight + info.scrollOffset === info.scrollHeight) { + // 当前滚动条位于底部 + setIsBottom(true); + } }; const initScrollToIndex = () => { @@ -60,13 +68,13 @@ export const VirtualizedMessageList: React.FC = }; 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) { + const index = props.messages.findIndex((m) => m._id === itemId); // TODO: 这里是因为mattermost的动态列表传的id因此只能这边再用id找回,可以看看是否可以优化 + if (index === -1) { return
; } + const message = props.messages[index]; + let showDate = true; let showAvatar = true; const messageCreatedAt = new Date(message.createdAt ?? ''); @@ -91,7 +99,7 @@ export const VirtualizedMessageList: React.FC = } return ( -
+
{showDate && ( {getMessageTimeDiff(messageCreatedAt)} @@ -115,8 +123,7 @@ export const VirtualizedMessageList: React.FC = ref={listRef} height={height} width={width} - className="post-list__dynamic" - itemData={props.messages.map((m) => m._id)} + itemData={props.messages.map((m) => m._id).reverse()} overscanCountForward={OVERSCAN_COUNT_FORWARD} overscanCountBackward={OVERSCAN_COUNT_BACKWARD} onScroll={onScroll} @@ -127,7 +134,7 @@ export const VirtualizedMessageList: React.FC = innerListStyle={postListStyle} initRangeToRender={initRangeToRender} // loaderId={PostListRowListIds.OLDER_MESSAGES_LOADER} - // correctScrollToBottom={this.props.atLatestPost} + correctScrollToBottom={isBottom} onItemsRendered={onItemsRendered} scrollToFailed={scrollToFailed} > diff --git a/web/src/components/DynamicVirtualizedList/DynamicSizeList.tsx b/web/src/components/DynamicVirtualizedList/DynamicSizeList.tsx index 3eb4dbfd..869ddae3 100644 --- a/web/src/components/DynamicVirtualizedList/DynamicSizeList.tsx +++ b/web/src/components/DynamicVirtualizedList/DynamicSizeList.tsx @@ -1,4 +1,5 @@ import memoizeOne from 'memoize-one'; +import React from 'react'; import { createElement, PureComponent } from 'react'; import { ItemMeasurer } from './ItemMeasurer'; @@ -129,13 +130,13 @@ const getItemSize = (props: any, index: any, listMetaData: any) => { return getItemMetadata(props, index, listMetaData).size; }; -type OnScrollArgs = { +export interface OnScrollInfo { scrollDirection: 'backward' | 'forward'; scrollOffset: number; scrollUpdateWasRequested: boolean; clientHeight: number; scrollHeight: number; -}; +} interface DynamicSizeListProps { canLoadMorePosts: () => void; @@ -149,17 +150,20 @@ interface DynamicSizeListProps { initScrollToIndex: () => any; initialScrollOffset?: number; innerRef: React.RefObject; - innerTagName?: string; - outerTagName?: string; itemData: string[]; onItemsRendered: (args: any) => void; - onScroll: (scrollArgs: OnScrollArgs) => void; + onScroll: (scrollArgs: OnScrollInfo) => void; overscanCountBackward: number; overscanCountForward: number; style: React.CSSProperties; width: number; outerRef?: any; className?: string; + + /** + * 用于确保当滚动条处于底部时 + * 增加新的项能确保滚动条处于聊天框最底部 + */ correctScrollToBottom?: boolean; innerListStyle?: React.CSSProperties; loaderId?: string; @@ -194,13 +198,6 @@ export default class DynamicSizeList extends PureComponent< _mountingCorrections = 0; _correctedInstances = 0; innerRefWidth = 0; - static defaultProps = { - innerTagName: 'div', - itemData: undefined, - outerTagName: 'div', - overscanCountForward: 30, - overscanCountBackward: 10, - }; state: DynamicSizeListState = { scrollDirection: 'backward', @@ -416,39 +413,30 @@ export default class DynamicSizeList extends PureComponent< } render() { - const { - className, - innerRef, - innerTagName, - outerTagName, - style, - innerListStyle, - } = this.props; + const { className, innerRef, style, innerListStyle } = this.props; const onScroll = this._onScrollVertical; const items = this._renderItems(); - return createElement( - outerTagName!, - { - className, - onScroll, - ref: this._outerRefSetter, - style: { + return ( +
+
+ {items} +
+
); } @@ -618,6 +606,9 @@ export default class DynamicSizeList extends PureComponent< return style; }; + /** + * 获取渲染范围 + */ _getRangeToRender(scrollTop = 0): number[] { const { itemData, overscanCountForward, overscanCountBackward } = this.props; @@ -709,9 +700,9 @@ export default class DynamicSizeList extends PureComponent< }; _handleNewMeasurements = ( - key: any, - newSize: any, - forceScrollCorrection: any + key: string, + newSize: number, + forceScrollCorrection: boolean ) => { const { itemSizeMap } = this._listMetaData; const { itemData } = this.props; @@ -841,31 +832,26 @@ export default class DynamicSizeList extends PureComponent< isItemInLocalPosts || isLoader ) { - const item = createElement(children, { + const item: React.ReactElement = createElement(children, { data: itemData, itemId, }); // Always wrap children in a ItemMeasurer to detect changes in size. items.push( - createElement(ItemMeasurer, { - handleNewMeasurements: this._handleNewMeasurements, - index, - item, - key: itemId, - size, - itemId, - width, - onUnmount: this._onItemRowUnmount, - }) + ); } else { - items.push( - createElement('div', { - key: itemId, - style, - }) - ); + items.push(
); } } } diff --git a/web/src/components/DynamicVirtualizedList/ItemMeasurer.tsx b/web/src/components/DynamicVirtualizedList/ItemMeasurer.tsx index 035c739b..1ec7d2da 100644 --- a/web/src/components/DynamicVirtualizedList/ItemMeasurer.tsx +++ b/web/src/components/DynamicVirtualizedList/ItemMeasurer.tsx @@ -71,12 +71,16 @@ const expandScrollDelta = shrinkScrollDelta + 10; interface ItemMeasurerProps { size: number; - handleNewMeasurements: any; - itemId: any; - item: any; + handleNewMeasurements: ( + key: string, + newSize: number, + forceScrollCorrection: boolean + ) => void; + itemId: string; + item: React.ReactElement; width: number; - onUnmount: any; - index: any; + onUnmount: (itemId: string, index: number) => void; + index: number; } export class ItemMeasurer extends Component { @@ -141,9 +145,7 @@ export class ItemMeasurer extends Component { } const { onUnmount, itemId, index } = this.props; - if (onUnmount) { - onUnmount(itemId, index); - } + typeof onUnmount === 'function' && onUnmount(itemId, index); } scrollingDiv = (event: any) => { @@ -164,7 +166,7 @@ export class ItemMeasurer extends Component { }; const renderItem = ( -
+
{item}
@@ -195,7 +197,7 @@ export class ItemMeasurer extends Component { return this.renderItems(); } - _measureItem = (forceScrollCorrection: any) => { + _measureItem = (forceScrollCorrection: boolean) => { const { handleNewMeasurements, size: oldSize, itemId } = this.props; const node = this._node;