refactor: refactor ack logic to not fetch all acks but fetch only the ones that are needed

chore/cli-prune-script
moonrailgun 9 months ago
parent 5c338b5bb4
commit 14613cc973

@ -1,10 +1,15 @@
import { ChatConverseInfo, fetchConverseInfo } from '../model/converse'; import {
ChatConverseInfo,
fetchConverseInfo,
getConverseAckInfo,
} from '../model/converse';
import { import {
findGroupInviteByCode, findGroupInviteByCode,
getGroupBasicInfo, getGroupBasicInfo,
GroupBasicInfo, GroupBasicInfo,
GroupInvite, GroupInvite,
} from '../model/group'; } from '../model/group';
import { getConverseLastMessageInfo } from '../model/message';
import { import {
fetchLocalStaticRegistryPlugins, fetchLocalStaticRegistryPlugins,
fetchRegistryPlugins, fetchRegistryPlugins,
@ -18,6 +23,7 @@ import { queryClient } from './index';
export enum CacheKey { export enum CacheKey {
user = 'user', user = 'user',
converse = 'converse', converse = 'converse',
converseAck = 'converseAck',
baseGroupInfo = 'baseGroupInfo', baseGroupInfo = 'baseGroupInfo',
groupInvite = 'groupInvite', groupInvite = 'groupInvite',
pluginRegistry = 'pluginRegistry', pluginRegistry = 'pluginRegistry',
@ -84,6 +90,32 @@ export async function getCachedGroupInviteInfo(
return data; 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;
}
/** /**
* *
*/ */

@ -1,4 +1,11 @@
import { request } from '../api/request'; 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 { export enum ChatConverseType {
DM = 'DM', // 单人会话 DM = 'DM', // 单人会话
@ -88,3 +95,50 @@ export async function fetchUserAck(): Promise<AckInfo[]> {
return data; 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);
}

@ -1,5 +1,12 @@
import { request } from '../api/request'; import { request } from '../api/request';
import type { ChatMessageReaction, ChatMessage } from 'tailchat-types'; 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 }; export { ChatMessageReaction, ChatMessage };
@ -103,10 +110,15 @@ export async function searchMessage(
return data; return data;
} }
interface LastMessageInfo {
converseId: string;
lastMessageId: string;
}
/** /**
* idid * idid
*/ */
export async function fetchConverseLastMessages( async function fetchConverseLastMessages(
converseIds: string[] converseIds: string[]
): Promise<{ converseId: string; lastMessageId: string }[]> { ): Promise<{ converseId: string; lastMessageId: string }[]> {
const { data } = await request.post( const { data } = await request.post(
@ -119,6 +131,30 @@ export async function fetchConverseLastMessages(
return data; 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 converseId ID
* @param messageId ID * @param messageId ID

@ -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 };
}

@ -1,4 +1,6 @@
import { useAppSelector } from './useAppSelector'; import { useAppSelector } from './useAppSelector';
import { useAckInfoChecker } from './useAckInfo';
import { useEffect } from 'react';
/** /**
* *
@ -6,13 +8,18 @@ import { useAppSelector } from './useAppSelector';
export function useUnread(converseIds: string[]) { export function useUnread(converseIds: string[]) {
const ack = useAppSelector((state) => state.chat.ack); const ack = useAppSelector((state) => state.chat.ack);
const lastMessageMap = useAppSelector((state) => state.chat.lastMessageMap); 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 ( if (
ack[converseId] === undefined && ack[converseId] === undefined &&
lastMessageMap[converseId] !== undefined lastMessageMap[converseId] !== undefined
) { ) {
// 没有已读记录且远程有数据 // 远程没有已读记录且获取到了最后一条消息
return true; return true;
} }
@ -20,4 +27,6 @@ export function useUnread(converseIds: string[]) {
// 则返回true(有未读消息) // 则返回true(有未读消息)
return lastMessageMap[converseId] > ack[converseId]; return lastMessageMap[converseId] > ack[converseId];
}); });
return unreadList;
} }

@ -9,19 +9,11 @@ import {
import type { FriendRequest } from '../model/friend'; import type { FriendRequest } from '../model/friend';
import { getCachedConverseInfo } from '../cache/cache'; import { getCachedConverseInfo } from '../cache/cache';
import type { GroupInfo } from '../model/group'; import type { GroupInfo } from '../model/group';
import { import { ChatMessage, ChatMessageReaction } from '../model/message';
ChatMessage,
ChatMessageReaction,
fetchConverseLastMessages,
} from '../model/message';
import { socketEventListeners } from '../manager/socket'; import { socketEventListeners } from '../manager/socket';
import { showToasts } from '../manager/ui'; import { showToasts } from '../manager/ui';
import { t } from '../i18n'; import { t } from '../i18n';
import { import { ChatConverseInfo, ChatConverseType } from '../model/converse';
ChatConverseInfo,
ChatConverseType,
fetchUserAck,
} from '../model/converse';
import { appendUserDMConverse } from '../model/user'; import { appendUserDMConverse } from '../model/user';
import { sharedEvent } from '../event'; import { sharedEvent } from '../event';
import type { InboxItem } from '../model/inbox'; import type { InboxItem } from '../model/inbox';
@ -61,7 +53,7 @@ function initial(socket: AppSocket, store: AppStore) {
console.log('初始化Redux上下文...'); console.log('初始化Redux上下文...');
// 立即请求加入房间 // 立即请求加入房间
const conversesP = socket socket
.request<{ .request<{
dmConverseIds: string[]; dmConverseIds: string[];
groupIds: string[]; groupIds: string[];
@ -77,30 +69,6 @@ function initial(socket: AppSocket, store: AppStore) {
throw new Error('findAndJoinRoom failed'); throw new Error('findAndJoinRoom failed');
}); });
Promise.all([conversesP, fetchUserAck()]).then(
([{ dmConverseIds, textPanelIds }, acks]) => {
/**
* TODO:
* acklastMessageMap
*/
// 设置已读消息
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 socket
.request<{ id: string; nickname?: string }[]>('friend.getAllFriends') .request<{ id: string; nickname?: string }[]>('friend.getAllFriends')

@ -23,6 +23,14 @@ class AckService extends TcService {
lastMessageId: 'string', lastMessageId: 'string',
}, },
}); });
this.registerAction('list', this.listAck, {
params: {
converseIds: {
type: 'array',
items: 'string',
},
},
});
this.registerAction('all', this.allAck); this.registerAction('all', this.allAck);
} }
@ -54,6 +62,35 @@ class AckService extends TcService {
// TODO: 如果要实现消息已读可以在此处基于会话id进行通知 // 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 * ack
*/ */

@ -479,10 +479,14 @@ class MessageService extends TcService {
}) })
); );
return list.filter(Boolean).map((item) => ({ return list.map((item) =>
item
? {
converseId: String(item.converseId), converseId: String(item.converseId),
lastMessageId: String(item._id), lastMessageId: String(item._id),
})); }
: null
);
} }
async addReaction( async addReaction(

Loading…
Cancel
Save