Skip to content

Commit

Permalink
refactor: refactor ack logic to not fetch all acks but fetch only the…
Browse files Browse the repository at this point in the history
… ones that are needed
  • Loading branch information
moonrailgun committed Sep 6, 2024
1 parent 5c338b5 commit 14613cc
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 43 deletions.
34 changes: 33 additions & 1 deletion client/shared/cache/cache.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { ChatConverseInfo, fetchConverseInfo } from '../model/converse';
import {
ChatConverseInfo,
fetchConverseInfo,
getConverseAckInfo,
} from '../model/converse';
import {
findGroupInviteByCode,
getGroupBasicInfo,
GroupBasicInfo,
GroupInvite,
} from '../model/group';
import { getConverseLastMessageInfo } from '../model/message';
import {
fetchLocalStaticRegistryPlugins,
fetchRegistryPlugins,
Expand All @@ -18,6 +23,7 @@ import { queryClient } from './index';
export enum CacheKey {
user = 'user',
converse = 'converse',
converseAck = 'converseAck',
baseGroupInfo = 'baseGroupInfo',
groupInvite = 'groupInvite',
pluginRegistry = 'pluginRegistry',
Expand Down Expand Up @@ -84,6 +90,32 @@ export async function getCachedGroupInviteInfo(
return data;
}

/**
* 获取缓存的用户信息
*/
export async function getCachedAckInfo(converseId: string, refetch = false) {
const data = await queryClient.fetchQuery(
[CacheKey.converseAck, converseId],
() => {
return Promise.all([
getConverseAckInfo([converseId]).then((d) => d[0]),
getConverseLastMessageInfo([converseId]).then((d) => d[0]),
]).then(([ack, lastMessage]) => {
return {
converseId,
ack,
lastMessage,
};
});
},
{
staleTime: 2 * 1000, // 缓存2s, 减少一秒内的重复请求(无意义)
}
);

return data;
}

/**
* 获取缓存的插件列表
*/
Expand Down
54 changes: 54 additions & 0 deletions client/shared/model/converse.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { request } from '../api/request';
import {
createAutoMergedRequest,
createAutoSplitRequest,
} from '../utils/request';
import _uniq from 'lodash/uniq';
import _flatten from 'lodash/flatten';
import _zipObject from 'lodash/zipObject';

export enum ChatConverseType {
DM = 'DM', // 单人会话
Expand Down Expand Up @@ -88,3 +95,50 @@ export async function fetchUserAck(): Promise<AckInfo[]> {

return data;
}

/**
* 获取用户存储在远程的会话信息
*/
export async function fetchUserAckList(
converseIds: string[]
): Promise<(AckInfo | null)[]> {
const { data } = await request.post('/api/chat/ack/list', {
converseIds,
});

if (!Array.isArray(data)) {
return [];
}

return data;
}

const _fetchConverseAckInfo = createAutoMergedRequest<
string[],
(AckInfo | null)[]
>(
createAutoSplitRequest(
async (converseIdsList) => {
const uniqList = _uniq(_flatten(converseIdsList));
const infoList = await fetchUserAckList(uniqList);

const map = _zipObject<AckInfo | null>(uniqList, infoList);

// 将请求结果根据传输来源重新分组
return converseIdsList.map((converseIds) =>
converseIds.map((converseId) => map[converseId] ?? null)
);
},
'serial',
100
)
);

/**
* 获取会话信息
*/
export async function getConverseAckInfo(
converseIds: string[]
): Promise<(AckInfo | null)[]> {
return _fetchConverseAckInfo(converseIds);
}
38 changes: 37 additions & 1 deletion client/shared/model/message.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { request } from '../api/request';
import type { ChatMessageReaction, ChatMessage } from 'tailchat-types';
import {
createAutoMergedRequest,
createAutoSplitRequest,
} from '../utils/request';
import _uniq from 'lodash/uniq';
import _flatten from 'lodash/flatten';
import _zipObject from 'lodash/zipObject';

export { ChatMessageReaction, ChatMessage };

Expand Down Expand Up @@ -103,10 +110,15 @@ export async function searchMessage(
return data;
}

interface LastMessageInfo {
converseId: string;
lastMessageId: string;
}

/**
* 基于会话id获取会话最后一条消息的id
*/
export async function fetchConverseLastMessages(
async function fetchConverseLastMessages(
converseIds: string[]
): Promise<{ converseId: string; lastMessageId: string }[]> {
const { data } = await request.post(
Expand All @@ -119,6 +131,30 @@ export async function fetchConverseLastMessages(
return data;
}

export const _fetchConverseLastMessageInfo = createAutoMergedRequest<
string[],
(LastMessageInfo | null)[]
>(
createAutoSplitRequest(
async (converseIdsList) => {
const uniqList = _uniq(_flatten(converseIdsList));
const infoList = await fetchConverseLastMessages(uniqList);

const map = _zipObject<LastMessageInfo | null>(uniqList, infoList);

// 将请求结果根据传输来源重新分组
return converseIdsList.map((converseIds) =>
converseIds.map((converseId) => map[converseId] ?? null)
);
},
'serial',
100
)
);
export function getConverseLastMessageInfo(converseIds: string[]) {
return _fetchConverseLastMessageInfo(converseIds);
}

/**
* @param converseId 会话ID
* @param messageId 消息ID
Expand Down
41 changes: 41 additions & 0 deletions client/shared/redux/hooks/useAckInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useAppDispatch, useAppSelector } from './useAppSelector';
import { chatActions } from '../slices';
import { useEvent } from '../../hooks/useEvent';
import { getCachedAckInfo } from '../../cache/cache';

export function useAckInfoChecker() {
const ack = useAppSelector((state) => state.chat.ack);
const lastMessageMap = useAppSelector((state) => state.chat.lastMessageMap);
const dispatch = useAppDispatch();

const ensureAckInfo = useEvent((converseId: string) => {
if (
ack[converseId] === undefined ||
lastMessageMap[converseId] === undefined
) {
getCachedAckInfo(converseId).then((info) => {
if (info.ack?.lastMessageId) {
dispatch(
chatActions.setConverseAck({
converseId,
lastMessageId: info.ack.lastMessageId,
})
);
}

if (info.lastMessage?.lastMessageId) {
dispatch(
chatActions.setLastMessageMap([
{
converseId,
lastMessageId: info.lastMessage.lastMessageId,
},
])
);
}
});
}
});

return { ensureAckInfo };
}
13 changes: 11 additions & 2 deletions client/shared/redux/hooks/useUnread.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
import { useAppSelector } from './useAppSelector';
import { useAckInfoChecker } from './useAckInfo';
import { useEffect } from 'react';

/**
* 返回某些会话是否有未读信息
*/
export function useUnread(converseIds: string[]) {
const ack = useAppSelector((state) => state.chat.ack);
const lastMessageMap = useAppSelector((state) => state.chat.lastMessageMap);
const { ensureAckInfo } = useAckInfoChecker();

return converseIds.map((converseId) => {
useEffect(() => {
converseIds.forEach((converseId) => ensureAckInfo(converseId));
}, [converseIds]);

const unreadList = converseIds.map((converseId) => {
if (
ack[converseId] === undefined &&
lastMessageMap[converseId] !== undefined
) {
// 没有已读记录且远程有数据
// 远程没有已读记录且获取到了最后一条消息
return true;
}

// 当远端最后一条消息的id > 本地已读状态的最后一条消息id,
// 则返回true(有未读消息)
return lastMessageMap[converseId] > ack[converseId];
});

return unreadList;
}
38 changes: 3 additions & 35 deletions client/shared/redux/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,11 @@ import {
import type { FriendRequest } from '../model/friend';
import { getCachedConverseInfo } from '../cache/cache';
import type { GroupInfo } from '../model/group';
import {
ChatMessage,
ChatMessageReaction,
fetchConverseLastMessages,
} from '../model/message';
import { ChatMessage, ChatMessageReaction } from '../model/message';
import { socketEventListeners } from '../manager/socket';
import { showToasts } from '../manager/ui';
import { t } from '../i18n';
import {
ChatConverseInfo,
ChatConverseType,
fetchUserAck,
} from '../model/converse';
import { ChatConverseInfo, ChatConverseType } from '../model/converse';
import { appendUserDMConverse } from '../model/user';
import { sharedEvent } from '../event';
import type { InboxItem } from '../model/inbox';
Expand Down Expand Up @@ -61,7 +53,7 @@ function initial(socket: AppSocket, store: AppStore) {
console.log('初始化Redux上下文...');

// 立即请求加入房间
const conversesP = socket
socket
.request<{
dmConverseIds: string[];
groupIds: string[];
Expand All @@ -77,30 +69,6 @@ function initial(socket: AppSocket, store: AppStore) {
throw new Error('findAndJoinRoom failed');
});

Promise.all([conversesP, fetchUserAck()]).then(
([{ dmConverseIds, textPanelIds }, acks]) => {
/**
* TODO: 这里的逻辑还需要优化
* 可能ack和lastMessageMap可以无关?
*/

// 设置已读消息
acks.forEach((ackInfo) => {
store.dispatch(
chatActions.setConverseAck({
converseId: ackInfo.converseId,
lastMessageId: ackInfo.lastMessageId,
})
);
});

const converseIds = [...dmConverseIds, ...textPanelIds];
fetchConverseLastMessages(converseIds).then((list) => {
store.dispatch(chatActions.setLastMessageMap(list));
});
}
);

// 获取好友列表
socket
.request<{ id: string; nickname?: string }[]>('friend.getAllFriends')
Expand Down
37 changes: 37 additions & 0 deletions server/services/core/chat/ack.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ class AckService extends TcService {
lastMessageId: 'string',
},
});
this.registerAction('list', this.listAck, {
params: {
converseIds: {
type: 'array',
items: 'string',
},
},
});
this.registerAction('all', this.allAck);
}

Expand Down Expand Up @@ -54,6 +62,35 @@ class AckService extends TcService {
// TODO: 如果要实现消息已读可以在此处基于会话id进行通知
}

/**
* 所有的ack信息
*/
async listAck(ctx: TcContext<{ converseIds: string[] }>) {
const userId = ctx.meta.userId;
const { converseIds } = ctx.params;

const list = await this.adapter.model.find({
userId,
converseId: {
$in: [...converseIds],
},
});

return converseIds.map((converseId) => {
const lastMessageId =
list
.find((item) => String(item.converseId) === converseId)
?.lastMessageId?.toString() ?? null;

return lastMessageId
? {
converseId,
lastMessageId,
}
: null;
});
}

/**
* 所有的ack信息
*/
Expand Down
12 changes: 8 additions & 4 deletions server/services/core/chat/message.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -479,10 +479,14 @@ class MessageService extends TcService {
})
);

return list.filter(Boolean).map((item) => ({
converseId: String(item.converseId),
lastMessageId: String(item._id),
}));
return list.map((item) =>
item
? {
converseId: String(item.converseId),
lastMessageId: String(item._id),
}
: null
);
}

async addReaction(
Expand Down

0 comments on commit 14613cc

Please sign in to comment.