diff --git a/shared/redux/hooks/useConverseMessage.ts b/shared/redux/hooks/useConverseMessage.ts index 4f7ac67b..019307e5 100644 --- a/shared/redux/hooks/useConverseMessage.ts +++ b/shared/redux/hooks/useConverseMessage.ts @@ -81,7 +81,7 @@ export function useConverseMessage(context: ConverseContext) { const converse = useAppSelector( (state) => state.chat.converses[converseId] ); - const hasMoreMessage = converse?.hasMoreMessage; + const hasMoreMessage = converse?.hasMoreMessage ?? true; const dispatch = useAppDispatch(); const messages = converse?.messages ?? []; @@ -173,6 +173,7 @@ export function useConverseMessage(context: ConverseContext) { loading, error, isLoadingMore, + hasMoreMessage, handleFetchMoreMessage, handleSendMessage, }; diff --git a/shared/redux/slices/chat.ts b/shared/redux/slices/chat.ts index 3527fdc6..2c560679 100644 --- a/shared/redux/slices/chat.ts +++ b/shared/redux/slices/chat.ts @@ -111,6 +111,10 @@ const chatSlice = createSlice({ }) ); + if (historyMessages.length < 50) { + state.converses[converseId].hasMoreMessage = false; + } + state.converses[converseId].hasFetchedHistory = true; }, @@ -139,7 +143,7 @@ const chatSlice = createSlice({ }) ); - if (historyMessages.length === 0) { + if (historyMessages.length < 50) { state.converses[converseId].hasMoreMessage = false; } state.converses[converseId].hasFetchedHistory = true; diff --git a/web/src/components/ChatBox/ChatMessageList/VirtualizedList.tsx b/web/src/components/ChatBox/ChatMessageList/VirtualizedList.tsx index 0652835f..cc24f660 100644 --- a/web/src/components/ChatBox/ChatMessageList/VirtualizedList.tsx +++ b/web/src/components/ChatBox/ChatMessageList/VirtualizedList.tsx @@ -8,8 +8,10 @@ import { ChatMessage, getMessageTimeDiff, shouldShowMessageTime, + t, useUpdateRef, } from 'tailchat-shared'; +import { messageReverseItemId } from './const'; import { ChatMessageItem } from './Item'; // Reference: https://github.com/mattermost/mattermost-webapp/blob/master/components/post_view/post_list_virtualized/post_list_virtualized.jsx @@ -40,6 +42,7 @@ function findMessageIndexWithId( export interface VirtualizedMessageListProps { messages: ChatMessage[]; isLoadingMore: boolean; + hasMoreMessage: boolean; onUpdateReadedMessage: (lastMessageId: string) => void; onLoadMore: () => void; } @@ -108,6 +111,20 @@ export const VirtualizedMessageList: React.FC = * 渲染列表元素 */ const renderRow = ({ data, itemId }: any) => { + if (itemId === messageReverseItemId.OLDER_MESSAGES_LOADER) { + return ( +
+ {t('加载中...')} +
+ ); + } else if (itemId === messageReverseItemId.TEXT_CHANNEL_INTRO) { + return ( +
+ {t('到顶了')} +
+ ); + } + const messages = props.messages; const index = findMessageIndexWithId(messages, itemId); // TODO: 这里是因为mattermost的动态列表传的id因此只能这边再用id找回,可以看看是否可以优化 if (index === -1) { @@ -152,9 +169,16 @@ export const VirtualizedMessageList: React.FC = }; // 初始渲染范围 - const initRangeToRender = useMemo( - () => [props.messages.length - 50, props.messages.length - 1], - [] + const initRangeToRender = useMemo(() => [0, props.messages.length], []); + + const itemData = useMemo( + () => [ + ...props.messages.map((m) => m._id).reverse(), + props.hasMoreMessage + ? messageReverseItemId.OLDER_MESSAGES_LOADER + : messageReverseItemId.TEXT_CHANNEL_INTRO, + ], + [props.messages, props.hasMoreMessage] ); return ( @@ -164,7 +188,7 @@ export const VirtualizedMessageList: React.FC = ref={listRef} height={height} width={width} - itemData={props.messages.map((m) => m._id).reverse()} + itemData={itemData} overscanCountForward={OVERSCAN_COUNT_FORWARD} overscanCountBackward={OVERSCAN_COUNT_BACKWARD} onScroll={handleScroll} @@ -174,7 +198,7 @@ export const VirtualizedMessageList: React.FC = style={{ ...virtListStyles, ...dynamicListStyle }} innerListStyle={postListStyle} initRangeToRender={initRangeToRender} - // loaderId={PostListRowListIds.OLDER_MESSAGES_LOADER} + loaderId={messageReverseItemId.OLDER_MESSAGES_LOADER} correctScrollToBottom={isBottom} > {renderRow} diff --git a/web/src/components/ChatBox/ChatMessageList/const.ts b/web/src/components/ChatBox/ChatMessageList/const.ts new file mode 100644 index 00000000..821ad83c --- /dev/null +++ b/web/src/components/ChatBox/ChatMessageList/const.ts @@ -0,0 +1,4 @@ +export const messageReverseItemId = { + OLDER_MESSAGES_LOADER: 'OLDER_MESSAGES_LOADER', + TEXT_CHANNEL_INTRO: 'TEXT_CHANNEL_INTRO', +}; diff --git a/web/src/components/ChatBox/index.tsx b/web/src/components/ChatBox/index.tsx index a9b6c82c..8cb880f1 100644 --- a/web/src/components/ChatBox/index.tsx +++ b/web/src/components/ChatBox/index.tsx @@ -25,6 +25,7 @@ const ChatBoxInner: React.FC = React.memo((props) => { loading, error, isLoadingMore, + hasMoreMessage, handleFetchMoreMessage, handleSendMessage, } = useConverseMessage({ @@ -46,6 +47,7 @@ const ChatBoxInner: React.FC = React.memo((props) => { diff --git a/web/src/components/DynamicVirtualizedList/DynamicSizeList.tsx b/web/src/components/DynamicVirtualizedList/DynamicSizeList.tsx index 4b178b17..3dac4de5 100644 --- a/web/src/components/DynamicVirtualizedList/DynamicSizeList.tsx +++ b/web/src/components/DynamicVirtualizedList/DynamicSizeList.tsx @@ -1,6 +1,6 @@ import memoizeOne from 'memoize-one'; import React from 'react'; -import { createElement, PureComponent } from 'react'; +import { PureComponent } from 'react'; import { ItemMeasurer } from './ItemMeasurer'; type ScrollDirection = any; @@ -174,6 +174,10 @@ interface DynamicSizeListState { localOlderPostsToRender: any[]; scrolledToInitIndex?: boolean; } + +/** + * 注意,该组件必须拥有一个loader + */ export default class DynamicSizeList extends PureComponent< DynamicSizeListProps, DynamicSizeListState @@ -652,8 +656,10 @@ export default class DynamicSizeList extends PureComponent< Math.min(itemCount - 1, startIndex + overscanForward) ); + // 移除size为空的项 + // 注意,这里必须要确保最上有一个保留项 while ( - !getItemSize(this.props, maxValue, this._listMetaData) && + getItemSize(this.props, maxValue, this._listMetaData) === 0 && maxValue > 0 && this._listMetaData.totalMeasuredSize > this.props.height ) { @@ -858,6 +864,7 @@ export default class DynamicSizeList extends PureComponent< } } } + return items; };