diff --git a/shared/index.tsx b/shared/index.tsx index ac3c1ad7..34ee72a9 100644 --- a/shared/index.tsx +++ b/shared/index.tsx @@ -106,7 +106,7 @@ export { deleteGroupPanel, } from './model/group'; export type { GroupPanel, GroupInfo, GroupBasicInfo } from './model/group'; -export { recallMessage, deleteMessage } from './model/message'; +export { recallMessage, deleteMessage, addReaction } from './model/message'; export type { ChatMessage } from './model/message'; export type { PluginManifest } from './model/plugin'; export type { UserBaseInfo, UserLoginInfo } from './model/user'; diff --git a/shared/model/message.ts b/shared/model/message.ts index 7f44b9ca..32fe1db2 100644 --- a/shared/model/message.ts +++ b/shared/model/message.ts @@ -1,5 +1,10 @@ import { request } from '../api/request'; +export interface ChatMessageReaction { + name: string; + author: string; +} + export interface ChatMessage { _id: string; @@ -11,7 +16,7 @@ export interface ChatMessage { converseId: string; - reactions?: any[]; + reactions?: ChatMessageReaction[]; hasRecall?: boolean; @@ -98,3 +103,18 @@ export async function fetchConverseLastMessages( return data; } + +/** + * 增加表情行为 + */ +export async function addReaction( + messageId: string, + emoji: string +): Promise { + const { data } = await request.post('/api/chat/message/addReaction', { + messageId, + emoji, + }); + + return data; +} diff --git a/shared/redux/setup.ts b/shared/redux/setup.ts index e80afbfc..dbeea6a6 100644 --- a/shared/redux/setup.ts +++ b/shared/redux/setup.ts @@ -4,7 +4,11 @@ import { chatActions, groupActions, userActions } from './slices'; import type { FriendRequest } from '../model/friend'; import { getCachedConverseInfo } from '../cache/cache'; import type { GroupInfo } from '../model/group'; -import { ChatMessage, fetchConverseLastMessages } from '../model/message'; +import { + ChatMessage, + ChatMessageReaction, + fetchConverseLastMessages, +} from '../model/message'; import { socketEventListeners } from '../manager/socket'; import { showToasts } from '../manager/ui'; import { t } from '../i18n'; @@ -181,6 +185,20 @@ function listenNotify(socket: AppSocket, store: AppStore) { ); }); + socket.listen<{ + converseId: string; + messageId: string; + reaction: ChatMessageReaction; + }>('chat.message.addReaction', ({ converseId, messageId, reaction }) => { + store.dispatch( + chatActions.appendMessageReaction({ + converseId, + messageId, + reaction, + }) + ); + }); + socket.listen( 'chat.converse.updateDMConverse', (converse) => { diff --git a/shared/redux/slices/chat.ts b/shared/redux/slices/chat.ts index 71061c88..8e0efe9f 100644 --- a/shared/redux/slices/chat.ts +++ b/shared/redux/slices/chat.ts @@ -1,6 +1,6 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import type { ChatConverseInfo } from '../../model/converse'; -import type { ChatMessage } from '../../model/message'; +import type { ChatMessage, ChatMessageReaction } from '../../model/message'; import _uniqBy from 'lodash/uniqBy'; import _orderBy from 'lodash/orderBy'; import _last from 'lodash/last'; @@ -230,6 +230,37 @@ const chatSlice = createSlice({ state.lastMessageMap[item.converseId] = item.lastMessageId; }); }, + + /** + * 追加消息反应 + */ + appendMessageReaction( + state, + action: PayloadAction<{ + converseId: string; + messageId: string; + reaction: ChatMessageReaction; + }> + ) { + const { converseId, messageId, reaction } = action.payload; + const converse = state.converses[converseId]; + if (!converse) { + console.warn('Not found converse,', converseId); + return; + } + + const message = converse.messages.find((m) => m._id === messageId); + if (!message) { + console.warn('Not found message,', messageId); + return; + } + + if (!Array.isArray(message.reactions)) { + message.reactions = []; + } + + message.reactions.push(reaction); + }, }, }); diff --git a/web/src/components/ChatBox/ChatMessageList/Item.tsx b/web/src/components/ChatBox/ChatMessageList/Item.tsx index 29f225e9..64b5d60a 100644 --- a/web/src/components/ChatBox/ChatMessageList/Item.tsx +++ b/web/src/components/ChatBox/ChatMessageList/Item.tsx @@ -15,7 +15,7 @@ import { Avatar } from '@/components/Avatar'; import { useRenderPluginMessageInterpreter } from './useRenderPluginMessageInterpreter'; import { getMessageRender } from '@/plugin/common'; import { Icon } from '@iconify/react'; -import { Divider, Dropdown, Popover } from 'antd'; +import { Divider, Dropdown } from 'antd'; import { UserName } from '@/components/UserName'; import './item.less'; import clsx from 'clsx'; diff --git a/web/src/components/ChatBox/ChatMessageList/useChatMessageReaction.tsx b/web/src/components/ChatBox/ChatMessageList/useChatMessageReaction.tsx index 9689a1e5..3d3be66f 100644 --- a/web/src/components/ChatBox/ChatMessageList/useChatMessageReaction.tsx +++ b/web/src/components/ChatBox/ChatMessageList/useChatMessageReaction.tsx @@ -1,8 +1,13 @@ import { EmojiPanel } from '@/components/EmojiPanel'; import { useTcPopoverContext } from '@/components/TcPopover'; import type { RenderFunction } from 'antd/lib/_util/getRenderPropValue'; -import React, { useCallback, useMemo } from 'react'; -import { ChatMessage, useUpdateRef } from 'tailchat-shared'; +import React, { useMemo } from 'react'; +import { + addReaction, + ChatMessage, + useAsyncRequest, + useUpdateRef, +} from 'tailchat-shared'; /** * 消息的反应信息操作 @@ -14,8 +19,8 @@ export function useChatMessageReaction(payload: ChatMessage): RenderFunction { (() => { const { closePopover } = useTcPopoverContext(); - const handleSelect = useCallback((code: string) => { - console.log('code', code, payloadRef.current); + const [, handleSelect] = useAsyncRequest(async (code: string) => { + await addReaction(payloadRef.current._id, code); closePopover(); }, []);