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

pull/13/head
moonrailgun 4 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 { ensureDMConverse } from '../../helper/converse-helper';
import { useAsync } from '../../hooks/useAsync'; import { useAsync } from '../../hooks/useAsync';
import { showErrorToasts } from '../../manager/ui'; import { showErrorToasts } from '../../manager/ui';
@ -9,8 +9,15 @@ import {
} from '../../model/message'; } from '../../model/message';
import { chatActions } from '../slices'; import { chatActions } from '../slices';
import { useAppDispatch, useAppSelector } from './useAppSelector'; import { useAppDispatch, useAppSelector } from './useAppSelector';
import _get from 'lodash/get';
import _isNil from 'lodash/isNil'; import _isNil from 'lodash/isNil';
import { t, useChatBoxContext } from '../..'; import {
ChatConverseState,
isValidStr,
t,
useAsyncRequest,
useChatBoxContext,
} from '../..';
import { MessageHelper } from '../../utils/message-helper'; import { MessageHelper } from '../../utils/message-helper';
import { ChatConverseType } from '../../model/converse'; import { ChatConverseType } from '../../model/converse';
@ -71,7 +78,10 @@ interface ConverseContext {
} }
export function useConverseMessage(context: ConverseContext) { export function useConverseMessage(context: ConverseContext) {
const { converseId, isGroup } = context; 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 dispatch = useAppDispatch();
const messages = converse?.messages ?? []; const messages = converse?.messages ?? [];
@ -132,7 +142,38 @@ export function useConverseMessage(context: ConverseContext) {
} }
}, [converseId]); }, [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); 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 { export interface ChatConverseState extends ChatConverseInfo {
messages: ChatMessage[]; messages: ChatMessage[];
hasFetchedHistory: boolean; hasFetchedHistory: boolean;
/**
*
*/
hasMoreMessage: boolean;
} }
export interface ChatState { export interface ChatState {
@ -42,12 +46,14 @@ const chatSlice = createSlice({
state.converses[converseId] = { state.converses[converseId] = {
messages: [], messages: [],
hasFetchedHistory: false, hasFetchedHistory: false,
hasMoreMessage: true,
...action.payload, ...action.payload,
}; };
}, },
/** /**
* *
* id
*/ */
appendConverseMessage( appendConverseMessage(
state, state,
@ -80,6 +86,9 @@ const chatSlice = createSlice({
} }
}, },
/**
*
*/
initialHistoryMessage( initialHistoryMessage(
state, state,
action: PayloadAction<{ action: PayloadAction<{
@ -105,6 +114,37 @@ const chatSlice = createSlice({
state.converses[converseId].hasFetchedHistory = true; 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_BACKWARD = 80;
const OVERSCAN_COUNT_FORWARD = 80; const OVERSCAN_COUNT_FORWARD = 80;
const HEIGHT_TRIGGER_FOR_MORE_POSTS = 200; // 触发加载更多的方法
const postListStyle = { const postListStyle = {
padding: '14px 0px 7px', padding: '14px 0px 7px',
@ -38,7 +39,9 @@ function findMessageIndexWithId(
export interface VirtualizedMessageListProps { export interface VirtualizedMessageListProps {
messages: ChatMessage[]; messages: ChatMessage[];
isLoadingMore: boolean;
onUpdateReadedMessage: (lastMessageId: string) => void; onUpdateReadedMessage: (lastMessageId: string) => void;
onLoadMore: () => void;
} }
export const VirtualizedMessageList: React.FC<VirtualizedMessageListProps> = export const VirtualizedMessageList: React.FC<VirtualizedMessageListProps> =
React.memo((props) => { React.memo((props) => {
@ -46,8 +49,32 @@ export const VirtualizedMessageList: React.FC<VirtualizedMessageListProps> =
const postListRef = useRef<HTMLDivElement>(null); const postListRef = useRef<HTMLDivElement>(null);
const [isBottom, setIsBottom] = useState(true); const [isBottom, setIsBottom] = useState(true);
const onScroll = (info: OnScrollInfo) => { const handleScroll = (info: OnScrollInfo) => {
if (info.clientHeight + info.scrollOffset === info.scrollHeight) { 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); setIsBottom(true);
props.onUpdateReadedMessage( props.onUpdateReadedMessage(
@ -140,7 +167,7 @@ export const VirtualizedMessageList: React.FC<VirtualizedMessageListProps> =
itemData={props.messages.map((m) => m._id).reverse()} itemData={props.messages.map((m) => m._id).reverse()}
overscanCountForward={OVERSCAN_COUNT_FORWARD} overscanCountForward={OVERSCAN_COUNT_FORWARD}
overscanCountBackward={OVERSCAN_COUNT_BACKWARD} overscanCountBackward={OVERSCAN_COUNT_BACKWARD}
onScroll={onScroll} onScroll={handleScroll}
initScrollToIndex={initScrollToIndex} initScrollToIndex={initScrollToIndex}
canLoadMorePosts={() => {}} canLoadMorePosts={() => {}}
innerRef={postListRef} innerRef={postListRef}

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

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

Loading…
Cancel
Save