refactor: 会话ack管理

pull/13/head
moonrailgun 4 years ago
parent d53602cdc7
commit f08b122322

@ -0,0 +1,17 @@
import { DependencyList, useEffect } from 'react';
import { useTimeoutFn } from './useTimeoutFn';
export type UseDebounceReturn = [() => boolean | null, () => void];
export function useDebounce(
// eslint-disable-next-line @typescript-eslint/ban-types
fn: Function,
ms = 0,
deps: DependencyList = []
): UseDebounceReturn {
const [isReady, cancel, reset] = useTimeoutFn(fn, ms);
useEffect(reset, deps);
return [isReady, cancel];
}

@ -0,0 +1,44 @@
import { useCallback, useEffect, useRef } from 'react';
export type UseTimeoutFnReturn = [() => boolean | null, () => void, () => void];
export function useTimeoutFn(
// eslint-disable-next-line @typescript-eslint/ban-types
fn: Function,
ms = 0
): UseTimeoutFnReturn {
const ready = useRef<boolean | null>(false);
const timeout = useRef<ReturnType<typeof setTimeout>>();
const callback = useRef(fn);
const isReady = useCallback(() => ready.current, []);
const set = useCallback(() => {
ready.current = false;
timeout.current && clearTimeout(timeout.current);
timeout.current = setTimeout(() => {
ready.current = true;
callback.current();
}, ms);
}, [ms]);
const clear = useCallback(() => {
ready.current = null;
timeout.current && clearTimeout(timeout.current);
}, []);
// update ref when function changes
useEffect(() => {
callback.current = fn;
}, [fn]);
// set on mount, clear on unmount
useEffect(() => {
set();
return clear;
}, [ms]);
return [isReady, clear, set];
}

@ -41,6 +41,7 @@ export { Trans } from './i18n/Trans';
export { useAsync } from './hooks/useAsync';
export { useAsyncFn } from './hooks/useAsyncFn';
export { useAsyncRequest } from './hooks/useAsyncRequest';
export { useDebounce } from './hooks/useDebounce';
export { useMountedState } from './hooks/useMountedState';
export { useRafState } from './hooks/useRafState';
export { useUpdateRef } from './hooks/useUpdateRef';
@ -65,7 +66,7 @@ export {
// model
export { fetchAvailableServices } from './model/common';
export { createDMConverse } from './model/converse';
export { createDMConverse, updateAck } from './model/converse';
export {
addFriendRequest,
cancelFriendRequest,

@ -36,3 +36,12 @@ export async function fetchConverseInfo(
return data;
}
/**
*
* @param converseId ID
* @param lastMessageId ID
*/
export async function updateAck(converseId: string, lastMessageId: string) {
await request.post('/api/chat/ack/update', { converseId, lastMessageId });
}

@ -47,11 +47,11 @@ export function useConverseMessage(context: ConverseContext) {
}
// Step 2. 拉取消息
const messages = await fetchConverseMessage(converseId);
const historyMessages = await fetchConverseMessage(converseId);
dispatch(
chatActions.initialHistoryMessage({
converseId,
historyMessages: messages,
historyMessages,
})
);
} else {

@ -11,10 +11,12 @@ export interface ChatConverseState extends ChatConverseInfo {
interface ChatState {
converses: Record<string, ChatConverseState>;
ack: Record<string, string>;
}
const initialState: ChatState = {
converses: {},
ack: {},
};
const chatSlice = createSlice({
@ -85,6 +87,20 @@ const chatSlice = createSlice({
state.converses[converseId].hasFetchedHistory = true;
},
/**
*
*/
setConverseAck(
state,
action: PayloadAction<{
converseId: string;
lastMessageId: string;
}>
) {
const { converseId, lastMessageId } = action.payload;
state.ack[converseId] = lastMessageId;
},
},
});

@ -1,14 +1,21 @@
import React, { useImperativeHandle, useRef } from 'react';
import React, {
useCallback,
useEffect,
useImperativeHandle,
useRef,
} from 'react';
import {
ChatMessage,
getMessageTimeDiff,
shouldShowMessageTime,
useUpdateRef,
} from 'tailchat-shared';
import { ChatMessageItem } from './Item';
import { Divider } from 'antd';
interface ChatMessageListProps {
messages: ChatMessage[];
onUpdateReadedMessage: (lastMessageId: string) => void;
}
export interface ChatMessageListRef {
scrollToBottom: () => void;
@ -34,10 +41,29 @@ export const ChatMessageList = React.forwardRef<
},
}));
const onUpdateReadedMessageRef = useUpdateRef(props.onUpdateReadedMessage);
useEffect(() => {
if (containerRef.current?.scrollTop === 0) {
// 当前列表在最低
onUpdateReadedMessageRef.current(
props.messages[props.messages.length - 1]._id
);
}
}, [props.messages.length]);
const handleScroll = useCallback(() => {
if (containerRef.current?.scrollTop === 0) {
onUpdateReadedMessageRef.current(
props.messages[props.messages.length - 1]._id
);
}
}, [props.messages]);
return (
<div
className="flex-1 overflow-y-scroll flex flex-col-reverse"
ref={containerRef}
onScroll={handleScroll}
>
<div>
{props.messages.map((message, index, arr) => {

@ -4,6 +4,7 @@ import { useConverseMessage } from 'tailchat-shared';
import { AlertErrorView } from '../AlertErrorView';
import { ChatInputBox } from './ChatInputBox';
import { ChatMessageList, ChatMessageListRef } from './ChatMessageList';
import { useMessageAck } from './useMessageAck';
const ChatBoxPlaceholder: React.FC = React.memo(() => {
return (
@ -41,6 +42,7 @@ export const ChatBox: React.FC<ChatBoxProps> = React.memo((props) => {
isGroup,
});
const chatMessageListRef = useRef<ChatMessageListRef>(null);
const { updateConverseAck } = useMessageAck(converseId, messages);
if (loading) {
return <ChatBoxPlaceholder />;
@ -52,10 +54,15 @@ export const ChatBox: React.FC<ChatBoxProps> = React.memo((props) => {
return (
<div className="w-full h-full flex flex-col select-text">
<ChatMessageList ref={chatMessageListRef} messages={messages} />
<ChatMessageList
ref={chatMessageListRef}
messages={messages}
onUpdateReadedMessage={updateConverseAck}
/>
<ChatInputBox
onSendMsg={(msg) => {
// 发送消息后滚动到底部
handleSendMessage({
converseId: props.converseId,
groupId: props.groupId,

@ -0,0 +1,61 @@
import { useCallback, useEffect, useMemo, useRef } from 'react';
import {
ChatMessage,
isValidStr,
updateAck,
useAppDispatch,
useUpdateRef,
} from 'tailchat-shared';
import { chatActions } from 'tailchat-shared/redux/slices';
import _debounce from 'lodash/debounce';
export function useMessageAck(converseId: string, messages: ChatMessage[]) {
const messagesRef = useUpdateRef(messages);
const dispatch = useAppDispatch();
const lastMessageIdRef = useRef('');
const setConverseAck = useMemo(
() =>
_debounce(
(converseId: string, lastMessageId: string) => {
if (
isValidStr(lastMessageIdRef.current) &&
lastMessageId <= lastMessageIdRef.current
) {
// 更新的数字比较小,跳过
return;
}
dispatch(chatActions.setConverseAck({ converseId, lastMessageId }));
updateAck(converseId, lastMessageId);
lastMessageIdRef.current = lastMessageId;
},
1000,
{ leading: false, trailing: true }
),
[]
);
useEffect(() => {
// 设置当前
if (messagesRef.current.length === 0) {
return;
}
const lastMessageId =
messagesRef.current[messagesRef.current.length - 1]._id;
setConverseAck(converseId, lastMessageId);
}, [converseId]);
/**
*
*/
const updateConverseAck = useCallback(
(lastMessageId: string) => {
setConverseAck(converseId, lastMessageId);
},
[converseId]
);
return { updateConverseAck };
}
Loading…
Cancel
Save