feat: 向上滚动加载历史数据

pull/13/head
moonrailgun 3 years ago
parent 80bd0b608d
commit fa92f94508

@ -1,4 +1,4 @@
import { useCallback, useEffect } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { ensureDMConverse } from '../../helper/converse-helper';
import { useAsync } from '../../hooks/useAsync';
import { showErrorToasts } from '../../manager/ui';
@ -9,8 +9,15 @@ import {
} from '../../model/message';
import { chatActions } from '../slices';
import { useAppDispatch, useAppSelector } from './useAppSelector';
import _get from 'lodash/get';
import _isNil from 'lodash/isNil';
import { t, useChatBoxContext } from '../..';
import {
ChatConverseState,
isValidStr,
t,
useAsyncRequest,
useChatBoxContext,
} from '../..';
import { MessageHelper } from '../../utils/message-helper';
import { ChatConverseType } from '../../model/converse';
@ -71,7 +78,10 @@ interface ConverseContext {
}
export function useConverseMessage(context: ConverseContext) {
const { converseId, isGroup } = context;
const converse = useAppSelector((state) => state.chat.converses[converseId]);
const converse = useAppSelector<ChatConverseState | undefined>(
(state) => state.chat.converses[converseId]
);
const hasMoreMessage = converse?.hasMoreMessage;
const dispatch = useAppDispatch();
const messages = converse?.messages ?? [];
@ -132,7 +142,38 @@ export function useConverseMessage(context: ConverseContext) {
}
}, [converseId]);
// 加载更多消息
const [{ loading: isLoadingMore }, handleFetchMoreMessage] =
useAsyncRequest(async () => {
const firstMessageId = _get(messages, [0, '_id']);
if (!isValidStr(firstMessageId)) {
return;
}
if (hasMoreMessage === false) {
return;
}
const olderMessages = await fetchConverseMessage(
converseId,
firstMessageId
);
dispatch(
chatActions.appendHistoryMessage({
converseId,
historyMessages: olderMessages,
})
);
}, [converseId, hasMoreMessage, _get(messages, [0, '_id'])]);
const handleSendMessage = useHandleSendMessage(context);
return { messages, loading, error, handleSendMessage };
return {
messages,
loading,
error,
isLoadingMore,
handleFetchMoreMessage,
handleSendMessage,
};
}

@ -9,6 +9,10 @@ import { isValidStr } from '../../utils/string-helper';
export interface ChatConverseState extends ChatConverseInfo {
messages: ChatMessage[];
hasFetchedHistory: boolean;
/**
*
*/
hasMoreMessage: boolean;
}
export interface ChatState {
@ -42,12 +46,14 @@ const chatSlice = createSlice({
state.converses[converseId] = {
messages: [],
hasFetchedHistory: false,
hasMoreMessage: true,
...action.payload,
};
},
/**
*
* id
*/
appendConverseMessage(
state,
@ -80,6 +86,9 @@ const chatSlice = createSlice({
}
},
/**
*
*/
initialHistoryMessage(
state,
action: PayloadAction<{
@ -105,6 +114,37 @@ const chatSlice = createSlice({
state.converses[converseId].hasFetchedHistory = true;
},
/**
*
*/
appendHistoryMessage(
state,
action: PayloadAction<{
converseId: string;
historyMessages: ChatMessage[];
}>
) {
const { converseId, historyMessages } = action.payload;
if (!state.converses[converseId]) {
// 没有会话信息, 请先设置会话信息
console.error('没有会话信息, 请先设置会话信息');
return;
}
chatSlice.caseReducers.appendConverseMessage(
state,
chatSlice.actions.appendConverseMessage({
converseId,
messages: [...historyMessages],
})
);
if (historyMessages.length === 0) {
state.converses[converseId].hasMoreMessage = false;
}
state.converses[converseId].hasFetchedHistory = true;
},
/**
*
*/

@ -16,6 +16,7 @@ import { ChatMessageItem } from './Item';
const OVERSCAN_COUNT_BACKWARD = 80;
const OVERSCAN_COUNT_FORWARD = 80;
const HEIGHT_TRIGGER_FOR_MORE_POSTS = 200; // 触发加载更多的方法
const postListStyle = {
padding: '14px 0px 7px',
@ -38,7 +39,9 @@ function findMessageIndexWithId(
export interface VirtualizedMessageListProps {
messages: ChatMessage[];
isLoadingMore: boolean;
onUpdateReadedMessage: (lastMessageId: string) => void;
onLoadMore: () => void;
}
export const VirtualizedMessageList: React.FC<VirtualizedMessageListProps> =
React.memo((props) => {
@ -46,8 +49,32 @@ export const VirtualizedMessageList: React.FC<VirtualizedMessageListProps> =
const postListRef = useRef<HTMLDivElement>(null);
const [isBottom, setIsBottom] = useState(true);
const onScroll = (info: OnScrollInfo) => {
if (info.clientHeight + info.scrollOffset === info.scrollHeight) {
const handleScroll = (info: OnScrollInfo) => {
const {
clientHeight,
scrollOffset,
scrollHeight,
scrollDirection,
scrollUpdateWasRequested,
} = info;
if (scrollHeight <= 0) {
return;
}
const didUserScrollBackwards =
scrollDirection === 'backward' && !scrollUpdateWasRequested;
const isOffsetWithInRange = scrollOffset < HEIGHT_TRIGGER_FOR_MORE_POSTS;
if (
didUserScrollBackwards &&
isOffsetWithInRange &&
!props.isLoadingMore
) {
// 加载更多历史信息
props.onLoadMore();
}
if (clientHeight + scrollOffset === scrollHeight) {
// 当前滚动条位于底部
setIsBottom(true);
props.onUpdateReadedMessage(
@ -140,7 +167,7 @@ export const VirtualizedMessageList: React.FC<VirtualizedMessageListProps> =
itemData={props.messages.map((m) => m._id).reverse()}
overscanCountForward={OVERSCAN_COUNT_FORWARD}
overscanCountBackward={OVERSCAN_COUNT_BACKWARD}
onScroll={onScroll}
onScroll={handleScroll}
initScrollToIndex={initScrollToIndex}
canLoadMorePosts={() => {}}
innerRef={postListRef}

@ -8,10 +8,7 @@ export const ChatMessageList: React.FC<VirtualizedMessageListProps> =
React.memo((props) => {
return (
<div className="flex-1">
<VirtualizedMessageList
messages={props.messages}
onUpdateReadedMessage={props.onUpdateReadedMessage}
/>
<VirtualizedMessageList {...props} />
</div>
);
});

@ -1,4 +1,4 @@
import React, { useRef } from 'react';
import React from 'react';
import { ChatBoxContextProvider, useConverseMessage } from 'tailchat-shared';
import { AlertErrorView } from '../AlertErrorView';
import { ChatBoxPlaceholder } from './ChatBoxPlaceholder';
@ -20,7 +20,14 @@ type ChatBoxProps =
};
const ChatBoxInner: React.FC<ChatBoxProps> = React.memo((props) => {
const { converseId, isGroup } = props;
const { messages, loading, error, handleSendMessage } = useConverseMessage({
const {
messages,
loading,
error,
isLoadingMore,
handleFetchMoreMessage,
handleSendMessage,
} = useConverseMessage({
converseId,
isGroup,
});
@ -38,7 +45,9 @@ const ChatBoxInner: React.FC<ChatBoxProps> = React.memo((props) => {
<div className="w-full h-full flex flex-col select-text">
<ChatMessageList
messages={messages}
isLoadingMore={isLoadingMore}
onUpdateReadedMessage={updateConverseAck}
onLoadMore={handleFetchMoreMessage}
/>
<ChatReply />

Loading…
Cancel
Save