mirror of https://github.com/msgbyte/tailchat
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
5.7 KiB
TypeScript
204 lines
5.7 KiB
TypeScript
import { useCallback, useEffect } from 'react';
|
|
import { ensureDMConverse } from '../../helper/converse-helper';
|
|
import { useAsync } from '../../hooks/useAsync';
|
|
import { showErrorToasts } from '../../manager/ui';
|
|
import {
|
|
fetchConverseMessage,
|
|
sendMessage,
|
|
SendMessagePayload,
|
|
} from '../../model/message';
|
|
import { chatActions } from '../slices';
|
|
import { useAppDispatch, useAppSelector } from './useAppSelector';
|
|
import _get from 'lodash/get';
|
|
import _isNil from 'lodash/isNil';
|
|
import {
|
|
ChatConverseState,
|
|
isValidStr,
|
|
t,
|
|
useAsyncRequest,
|
|
useChatBoxContext,
|
|
useMemoizedFn,
|
|
} from '../..';
|
|
import { MessageHelper } from '../../utils/message-helper';
|
|
import { ChatConverseType } from '../../model/converse';
|
|
import { sharedEvent } from '../../event';
|
|
import { useUpdateRef } from '../../hooks/useUpdateRef';
|
|
|
|
function useHandleSendMessage(context: ConverseContext) {
|
|
const { converseId } = context;
|
|
const dispatch = useAppDispatch();
|
|
const { hasContext, replyMsg, clearReplyMsg } = useChatBoxContext();
|
|
const replyMsgRef = useUpdateRef(replyMsg); // NOTICE: 这个是为了修复一个边界case: 当先输入文本再选中消息回复时,直接发送无法带上回复信息
|
|
|
|
/**
|
|
* 发送消息
|
|
*/
|
|
const handleSendMessage = useCallback(
|
|
async (payload: SendMessagePayload) => {
|
|
// 输入合法性检测
|
|
if (payload.content === '') {
|
|
showErrorToasts(t('无法发送空消息'));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
if (hasContext === true) {
|
|
// 如果有上下文, 则组装payload
|
|
const msgHelper = new MessageHelper(payload);
|
|
if (!_isNil(replyMsgRef.current)) {
|
|
msgHelper.setReplyMsg(replyMsgRef.current);
|
|
clearReplyMsg();
|
|
}
|
|
|
|
payload = msgHelper.generatePayload();
|
|
}
|
|
|
|
// TODO: 增加临时消息, 对网络环境不佳的状态进行优化
|
|
|
|
const message = await sendMessage(payload);
|
|
dispatch(
|
|
chatActions.appendConverseMessage({
|
|
converseId,
|
|
messages: [message],
|
|
})
|
|
);
|
|
sharedEvent.emit('sendMessage', payload);
|
|
} catch (err) {
|
|
showErrorToasts(err);
|
|
}
|
|
},
|
|
[converseId, hasContext, clearReplyMsg]
|
|
);
|
|
|
|
return handleSendMessage;
|
|
}
|
|
|
|
/**
|
|
* 会话消息管理
|
|
*/
|
|
interface ConverseContext {
|
|
converseId: string;
|
|
isGroup: boolean;
|
|
}
|
|
export function useConverseMessage(context: ConverseContext) {
|
|
const { converseId, isGroup } = context;
|
|
const converse = useAppSelector<ChatConverseState | undefined>(
|
|
(state) => state.chat.converses[converseId]
|
|
);
|
|
const reconnectNum = useAppSelector((state) => state.global.reconnectNum);
|
|
const hasMoreMessage = converse?.hasMoreMessage ?? true;
|
|
const dispatch = useAppDispatch();
|
|
const messages = converse?.messages ?? [];
|
|
const currentUserId = useAppSelector((state) => state.user.info?._id);
|
|
|
|
useEffect(() => {
|
|
dispatch(chatActions.updateCurrentConverseId(converseId));
|
|
|
|
return () => {
|
|
dispatch(chatActions.updateCurrentConverseId(null));
|
|
};
|
|
}, [converseId]);
|
|
|
|
// NOTICE: 该hook只会在converseId变化和重新链接时执行
|
|
const { loading, error } = useAsync(async () => {
|
|
if (!currentUserId) {
|
|
// 如果当前用户不存在则跳过逻辑
|
|
return;
|
|
}
|
|
|
|
if (!converse) {
|
|
// 如果是一个新会话(或者当前会话列表中没有)
|
|
if (!isGroup) {
|
|
// 如果是私信会话
|
|
// Step 1. 创建会话 并确保私信列表中存在该会话
|
|
const converse = await ensureDMConverse(converseId, currentUserId);
|
|
dispatch(chatActions.setConverseInfo(converse));
|
|
} else {
|
|
// 如果是群组会话(文本频道)
|
|
// Step 1. 确保群组会话存在
|
|
dispatch(
|
|
chatActions.setConverseInfo({
|
|
_id: converseId,
|
|
name: '',
|
|
type: ChatConverseType.Group,
|
|
members: [],
|
|
})
|
|
);
|
|
}
|
|
|
|
// Step 2. 拉取消息
|
|
const historyMessages = await fetchConverseMessage(converseId);
|
|
dispatch(
|
|
chatActions.initialHistoryMessage({
|
|
converseId,
|
|
historyMessages,
|
|
})
|
|
);
|
|
} else {
|
|
// 已存在会话
|
|
if (!converse.hasFetchedHistory) {
|
|
// 没有获取过历史消息
|
|
// 拉取历史消息
|
|
const startId = _isNil(converse.messages[0])
|
|
? undefined
|
|
: converse.messages[0]._id;
|
|
const messages = await fetchConverseMessage(converseId, startId);
|
|
dispatch(
|
|
chatActions.initialHistoryMessage({
|
|
converseId,
|
|
historyMessages: messages,
|
|
})
|
|
);
|
|
}
|
|
}
|
|
}, [converseId, reconnectNum, currentUserId]);
|
|
|
|
// 加载更多消息
|
|
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 handleFetchMoreMessage = useMemoizedFn(async () => {
|
|
if (isLoadingMore) {
|
|
return;
|
|
}
|
|
|
|
await _handleFetchMoreMessage();
|
|
});
|
|
|
|
const handleSendMessage = useHandleSendMessage(context);
|
|
|
|
return {
|
|
messages,
|
|
loading,
|
|
error,
|
|
isLoadingMore,
|
|
hasMoreMessage,
|
|
handleFetchMoreMessage,
|
|
handleSendMessage,
|
|
};
|
|
}
|