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.
tailchat/client/shared/redux/setup.ts

275 lines
7.6 KiB
TypeScript

import type { AppStore } from './store';
import type { AppSocket } from '../api/socket';
import {
chatActions,
globalActions,
groupActions,
userActions,
} from './slices';
import type { FriendRequest } from '../model/friend';
import { getCachedConverseInfo } from '../cache/cache';
import type { GroupInfo } from '../model/group';
import type { ChatMessage, ChatMessageReaction } from '../model/message';
import { socketEventListeners } from '../manager/socket';
import { showToasts } from '../manager/ui';
import { t } from '../i18n';
import { ChatConverseInfo, ChatConverseType } from '../model/converse';
import { appendUserDMConverse } from '../model/user';
import { sharedEvent } from '../event';
import type { InboxItem } from '../model/inbox';
import { useGlobalConfigStore } from '../store/globalConfig';
import type { GlobalConfig } from '../model/config';
/**
* 初始化 Redux 上下文
* 该文件用于处理远程数据与本地 Redux 状态的交互
*/
export function setupRedux(socket: AppSocket, store: AppStore) {
store.dispatch(globalActions.setNetworkStatus('initial'));
initial(socket, store);
listenNotify(socket, store);
// 断线重连重新初始化信息
socket.onReconnect(() => {
console.warn('因为断线重连触发重新同步远程数据');
initial(socket, store);
/**
* 重置会话列表
* 如果当前已经打开了一个会话列表则会让该会话自行更新(由useConverseMessage保障)
*/
store.dispatch(chatActions.clearAllConverses());
store.dispatch(globalActions.incReconnectNum());
});
sharedEvent.on('updateNetworkStatus', (status) => {
store.dispatch(globalActions.setNetworkStatus(status));
});
}
/**
* 初始化数据
*/
function initial(socket: AppSocket, store: AppStore) {
console.log('初始化Redux上下文...');
// 立即请求加入房间
socket
.request<{
dmConverseIds: string[];
groupIds: string[];
textPanelIds: string[];
subscribeFeaturePanelIds: string[];
}>('chat.converse.findAndJoinRoom')
.catch((err) => {
console.error(err);
showToasts(
t('无法加入房间, 您将无法获取到最新的信息, 请刷新页面后重试'),
'error'
);
throw new Error('findAndJoinRoom failed');
});
// 获取好友列表
socket
.request<{ id: string; nickname?: string }[]>('friend.getAllFriends')
.then((data) => {
store.dispatch(userActions.setFriendList(data));
});
// 获取好友邀请列表
socket.request<FriendRequest[]>('friend.request.allRelated').then((data) => {
store.dispatch(userActions.setFriendRequests(data));
});
// 获取所有的当前用户会话列表
socket.request<string[]>('user.dmlist.getAllConverse').then((data) => {
(data ?? []).forEach(async (converseId) => {
// TODO: 待优化, 可以在后端一次性返回
try {
const converse = await getCachedConverseInfo(converseId);
store.dispatch(chatActions.setConverseInfo(converse));
} catch (e) {
console.error(e);
}
});
});
/**
* 获取用户群组列表
*/
socket.request<GroupInfo[]>('group.getUserGroups').then((groups) => {
store.dispatch(groupActions.appendGroups(groups));
});
socket.request<InboxItem[]>('chat.inbox.all').then((list) => {
store.dispatch(chatActions.setInboxList(list));
});
}
/**
* 监听远程通知
*/
function listenNotify(socket: AppSocket, store: AppStore) {
socket.listen<{ userId: string }>('friend.add', ({ userId }) => {
if (typeof userId !== 'string') {
console.error('错误的信息', userId);
return;
}
store.dispatch(userActions.appendFriend({ id: userId }));
});
socket.listen<FriendRequest>('friend.request.add', (request) => {
store.dispatch(userActions.appendFriendRequest(request));
});
socket.listen<{ requestId: string }>(
'friend.request.remove',
({ requestId }) => {
store.dispatch(userActions.removeFriendRequest(requestId));
}
);
socket.listen<ChatMessage>('chat.message.add', (message) => {
// 处理接受到的消息
const converseId = message.converseId;
const converse = store.getState().chat.converses[converseId];
// 添加消息到会话中
const appendMessage = () => {
store.dispatch(
chatActions.appendConverseMessage({
converseId,
messages: [message],
})
);
};
if (converse) {
// 如果该会话已经加载(群组面板)
appendMessage();
} else if (!message.groupId) {
// 如果会话没有加载, 但是是私信消息
// 则获取会话信息后添加到会话消息中
getCachedConverseInfo(converseId).then((converse) => {
if (
[ChatConverseType.DM, ChatConverseType.Multi].includes(converse.type)
) {
// 如果是私人会话, 则添加到dmlist
appendUserDMConverse(converse._id);
}
store.dispatch(chatActions.setConverseInfo(converse));
appendMessage();
});
} else {
// 是群组未加载的消息面板的消息
// 设置会话信息
store.dispatch(
chatActions.setLastMessageMap([
{
converseId,
lastMessageId: message._id,
},
])
);
}
sharedEvent.emit('receiveMessage', message); // 推送到通知中心
});
socket.listen<ChatMessage>('chat.message.update', (message) => {
store.dispatch(
chatActions.updateMessageInfo({
message,
})
);
});
socket.listen<{
converseId: string;
messageId: string;
}>('chat.message.delete', ({ converseId, messageId }) => {
store.dispatch(
chatActions.deleteMessageById({
converseId,
messageId,
})
);
});
socket.listen<{
converseId: string;
messageId: string;
reaction: ChatMessageReaction;
}>('chat.message.addReaction', ({ converseId, messageId, reaction }) => {
store.dispatch(
chatActions.appendMessageReaction({
converseId,
messageId,
reaction,
})
);
});
socket.listen<{
converseId: string;
messageId: string;
reaction: ChatMessageReaction;
}>('chat.message.removeReaction', ({ converseId, messageId, reaction }) => {
store.dispatch(
chatActions.removeMessageReaction({
converseId,
messageId,
reaction,
})
);
});
socket.listen<ChatConverseInfo>(
'chat.converse.updateDMConverse',
(converse) => {
store.dispatch(chatActions.setConverseInfo(converse));
}
);
socket.listen<GroupInfo>('group.add', (groupInfo) => {
store.dispatch(groupActions.appendGroups([groupInfo]));
});
socket.listen<GroupInfo>('group.updateInfo', (groupInfo) => {
store.dispatch(groupActions.updateGroup(groupInfo));
});
socket.listen<{ groupId: string }>('group.remove', ({ groupId }) => {
store.dispatch(groupActions.removeGroup(groupId));
});
socket.listen<InboxItem>('chat.inbox.append', (item) => {
store.dispatch(chatActions.appendInboxItem(item));
});
socket.listen('chat.inbox.updated', () => {
// 检测到收件箱列表被更新,需要重新获取
socket.request<InboxItem[]>('chat.inbox.all').then((list) => {
store.dispatch(chatActions.setInboxList(list));
});
});
socket.listen(
'config.updateClientConfig',
(config: Partial<GlobalConfig>) => {
useGlobalConfigStore.setState((state) => ({
...state,
...config,
}));
}
);
// 其他的额外的通知
socketEventListeners.forEach(({ eventName, eventFn }) => {
socket.listen(eventName, eventFn);
});
}