diff --git a/client/shared/model/converse.ts b/client/shared/model/converse.ts index 565cd2d1..107fd889 100644 --- a/client/shared/model/converse.ts +++ b/client/shared/model/converse.ts @@ -1,8 +1,9 @@ import { request } from '../api/request'; export enum ChatConverseType { - DM = 'DM', - Group = 'Group', + DM = 'DM', // 单人会话 + Multi = 'Multi', // 多人会话 + Group = 'Group', // 群组会话(暂时无用) } export interface ChatConverseInfo { diff --git a/client/shared/redux/hooks/useConverse.ts b/client/shared/redux/hooks/useConverse.ts index 2d7fa4c7..ff15ec80 100644 --- a/client/shared/redux/hooks/useConverse.ts +++ b/client/shared/redux/hooks/useConverse.ts @@ -1,4 +1,5 @@ import { useMemo } from 'react'; +import { ChatConverseType } from '../../model/converse'; import type { ChatConverseState } from '../slices/chat'; import { useAppSelector } from './useAppSelector'; @@ -8,12 +9,23 @@ import { useAppSelector } from './useAppSelector'; */ export function useDMConverseList(): ChatConverseState[] { const converses = useAppSelector((state) => state.chat.converses); + const lastMessageMap = useAppSelector((state) => state.chat.lastMessageMap); - return useMemo( + const filteredConverse = useMemo( () => Object.entries(converses) - .filter(([, info]) => info.type === 'DM') + .filter(([, info]) => + [ChatConverseType.DM, ChatConverseType.Multi].includes(info.type) + ) .map(([, info]) => info), [converses] ); + + return useMemo(() => { + return filteredConverse.sort((a, b) => { + return (lastMessageMap[a._id] ?? '') < (lastMessageMap[b._id] ?? '') + ? 1 + : -1; + }); + }, [filteredConverse, lastMessageMap]); } diff --git a/client/shared/redux/hooks/useConverseAck.ts b/client/shared/redux/hooks/useConverseAck.ts index 873b059f..1689580b 100644 --- a/client/shared/redux/hooks/useConverseAck.ts +++ b/client/shared/redux/hooks/useConverseAck.ts @@ -1,7 +1,7 @@ import { useRef } from 'react'; import { useAppDispatch, useAppSelector } from './useAppSelector'; import _debounce from 'lodash/debounce'; -import { isValidStr } from '../../utils/string-helper'; +import { isLocalMessageId, isValidStr } from '../../utils/string-helper'; import { chatActions } from '../slices'; import { updateAck } from '../../model/converse'; import { useMemoizedFn } from '../../hooks/useMemoizedFn'; @@ -30,6 +30,11 @@ export function useConverseAck(converseId: string) { const setConverseAck = useMemoizedFn( (converseId: string, lastMessageId: string) => { + if (isLocalMessageId(lastMessageId)) { + // 跳过本地消息 + return; + } + if ( isValidStr(lastMessageIdRef.current) && lastMessageId <= lastMessageIdRef.current diff --git a/client/shared/redux/setup.ts b/client/shared/redux/setup.ts index 4d2ebbe2..cd899c08 100644 --- a/client/shared/redux/setup.ts +++ b/client/shared/redux/setup.ts @@ -184,7 +184,9 @@ function listenNotify(socket: AppSocket, store: AppStore) { // 如果会话没有加载, 但是是私信消息 // 则获取会话信息后添加到会话消息中 getCachedConverseInfo(converseId).then((converse) => { - if (converse.type === ChatConverseType.DM) { + if ( + [ChatConverseType.DM, ChatConverseType.Multi].includes(converse.type) + ) { // 如果是私人会话, 则添加到dmlist appendUserDMConverse(converse._id); } diff --git a/client/shared/redux/slices/chat.ts b/client/shared/redux/slices/chat.ts index b1161b3b..c897652d 100644 --- a/client/shared/redux/slices/chat.ts +++ b/client/shared/redux/slices/chat.ts @@ -9,7 +9,7 @@ import type { import _uniqBy from 'lodash/uniqBy'; import _orderBy from 'lodash/orderBy'; import _last from 'lodash/last'; -import { isValidStr } from '../../utils/string-helper'; +import { isLocalMessageId, isValidStr } from '../../utils/string-helper'; import type { InboxItem } from '../../model/inbox'; export interface ChatConverseState extends ChatConverseInfo { @@ -85,6 +85,7 @@ const chatSlice = createSlice({ return; } + // NOTICE: 按照该规则能确保本地消息一直在最后,因为l大于任何ObjectId const newMessages = _orderBy( _uniqBy([...state.converses[converseId].messages, ...messages], '_id'), '_id', @@ -93,10 +94,16 @@ const chatSlice = createSlice({ state.converses[converseId].messages = newMessages; - if (state.currentConverseId !== converseId) { - const lastMessageId = _last(newMessages)?._id; - if (isValidStr(lastMessageId)) { - state.lastMessageMap[converseId] = lastMessageId; + const lastMessageId = _last( + newMessages.filter((m) => !isLocalMessageId(m._id)) + )?._id; + + if (isValidStr(lastMessageId)) { + state.lastMessageMap[converseId] = lastMessageId; + + if (state.currentConverseId === converseId) { + // 如果是当前会话,则立即已读 + state.ack[converseId] = lastMessageId; } } }, diff --git a/client/shared/utils/string-helper.ts b/client/shared/utils/string-helper.ts index 5f99ecc6..ddf46557 100644 --- a/client/shared/utils/string-helper.ts +++ b/client/shared/utils/string-helper.ts @@ -47,3 +47,11 @@ export function is(it: string) { export function isValidStr(str: unknown): str is string { return typeof str == 'string' && str !== ''; } + +export function isLocalMessageId(str: unknown): str is string { + if (typeof str !== 'string') { + return false; + } + + return str.startsWith('localMessage_'); +}