From f25e0a7e9f3b3a31f0b9f94a4bfb281950b4c02a Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Tue, 16 Nov 2021 20:27:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/contexts/GroupInfoContext.tsx | 35 +++++++++++++++ shared/index.tsx | 6 ++- shared/model/message.ts | 8 ++++ shared/redux/hooks/useGroup.ts | 4 +- .../ChatBox/ChatMessageList/Item.tsx | 44 ++++++++++++++++--- web/src/routes/Main/Content/Group/Panel.tsx | 19 +++++++- 6 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 shared/contexts/GroupInfoContext.tsx diff --git a/shared/contexts/GroupInfoContext.tsx b/shared/contexts/GroupInfoContext.tsx new file mode 100644 index 00000000..c1b112fd --- /dev/null +++ b/shared/contexts/GroupInfoContext.tsx @@ -0,0 +1,35 @@ +import React, { useContext } from 'react'; +import type { GroupInfo } from '..'; + +/** + * 群组信息上下文 + */ + +interface GroupInfoContextProps { + groupInfo: GroupInfo | null; +} +const GroupInfoContext = React.createContext({ + groupInfo: null, +}); +GroupInfoContext.displayName = 'GroupInfoContext'; + +export const GroupInfoContextProvider: React.FC<{ + groupInfo: GroupInfo; +}> = React.memo((props) => { + return ( + + {props.children} + + ); +}); +GroupInfoContextProvider.displayName = 'GroupInfoContextProvider'; + +export function useGroupInfoContext(): GroupInfoContextProps['groupInfo'] { + const context = useContext(GroupInfoContext); + + return context.groupInfo; +} diff --git a/shared/index.tsx b/shared/index.tsx index 6648754c..ac3c1ad7 100644 --- a/shared/index.tsx +++ b/shared/index.tsx @@ -39,6 +39,10 @@ export { useChatBoxContext, } from './contexts/ChatBoxContext'; export { useColorScheme } from './contexts/ColorSchemeContext'; +export { + GroupInfoContextProvider, + useGroupInfoContext, +} from './contexts/GroupInfoContext'; export { getDMConverseName } from './helper/converse-helper'; @@ -102,7 +106,7 @@ export { deleteGroupPanel, } from './model/group'; export type { GroupPanel, GroupInfo, GroupBasicInfo } from './model/group'; -export { recallMessage } from './model/message'; +export { recallMessage, deleteMessage } 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 90ad8c08..7f44b9ca 100644 --- a/shared/model/message.ts +++ b/shared/model/message.ts @@ -75,6 +75,14 @@ export async function recallMessage(messageId: string): Promise { return data; } +export async function deleteMessage(messageId: string): Promise { + const { data } = await request.post('/api/chat/message/deleteMessage', { + messageId, + }); + + return data; +} + /** * 基于会话id获取会话最后一条消息的id */ diff --git a/shared/redux/hooks/useGroup.ts b/shared/redux/hooks/useGroup.ts index 63592eff..b64fde2a 100644 --- a/shared/redux/hooks/useGroup.ts +++ b/shared/redux/hooks/useGroup.ts @@ -8,8 +8,8 @@ import { useUserId } from './useUserInfo'; /** * 获取群组信息 */ -export function useGroupInfo(groupId: string): GroupInfo | undefined { - return useAppSelector((state) => state.group.groups[groupId]); +export function useGroupInfo(groupId: string): GroupInfo | null { + return useAppSelector((state) => state.group.groups[groupId]) ?? null; } /** diff --git a/web/src/components/ChatBox/ChatMessageList/Item.tsx b/web/src/components/ChatBox/ChatMessageList/Item.tsx index 1b959a6e..f617e070 100644 --- a/web/src/components/ChatBox/ChatMessageList/Item.tsx +++ b/web/src/components/ChatBox/ChatMessageList/Item.tsx @@ -13,6 +13,9 @@ import { useAsync, getCachedUserInfo, useAsyncRequest, + deleteMessage, + useGroupInfoContext, + useUserInfo, } from 'tailchat-shared'; import { Avatar } from '@/components/Avatar'; import { useRenderPluginMessageInterpreter } from './useRenderPluginMessageInterpreter'; @@ -27,11 +30,20 @@ import './item.less'; */ function useChatMessageItemAction(payload: ChatMessage): React.ReactElement { const context = useChatBoxContext(); + const groupInfo = useGroupInfoContext(); + const userInfo = useUserInfo(); const [, handleRecallMessage] = useAsyncRequest(() => { return recallMessage(payload._id); }, [payload._id]); + const [, handleDeleteMessage] = useAsyncRequest(() => { + return deleteMessage(payload._id); + }, [payload._id]); + + const isGroupOwner = groupInfo && groupInfo.owner === userInfo?._id; // + const isMessageAuthor = payload.author === userInfo?._id; + return ( {context.hasContext && ( @@ -43,17 +55,35 @@ function useChatMessageItemAction(payload: ChatMessage): React.ReactElement { {t('回复')} )} - } - onClick={handleRecallMessage} - > - {t('撤回')} - + + {(isGroupOwner || isMessageAuthor) && ( + } + onClick={handleRecallMessage} + > + {t('撤回')} + + )} + + {/* 仅群组管理员可见 */} + {isGroupOwner && ( + } + onClick={handleDeleteMessage} + > + {t('删除')} + + )} ); } +/** + * 消息引用 + */ const MessageQuote: React.FC<{ payload: ChatMessage }> = React.memo( ({ payload }) => { const quote = useMemo( diff --git a/web/src/routes/Main/Content/Group/Panel.tsx b/web/src/routes/Main/Content/Group/Panel.tsx index 3a689b42..18661698 100644 --- a/web/src/routes/Main/Content/Group/Panel.tsx +++ b/web/src/routes/Main/Content/Group/Panel.tsx @@ -2,19 +2,34 @@ import { GroupPluginPanel } from '@/components/Panel/group/PluginPanel'; import { TextPanel } from '@/components/Panel/group/TextPanel'; import { Alert } from 'antd'; import React from 'react'; -import { GroupPanelType, t, useGroupPanel } from 'tailchat-shared'; +import { + GroupInfoContextProvider, + GroupPanelType, + t, + useGroupInfo, + useGroupPanel, +} from 'tailchat-shared'; import { useGroupPanelParams } from './utils'; export const GroupPanelRender: React.FC = React.memo(() => { const { groupId, panelId } = useGroupPanelParams(); + const groupInfo = useGroupInfo(groupId); const panelInfo = useGroupPanel(groupId, panelId); + if (groupInfo === null) { + return null; + } + if (panelInfo === null) { return null; } if (panelInfo.type === GroupPanelType.TEXT) { - return ; + return ( + + + + ); } else if (panelInfo.type === GroupPanelType.PLUGIN) { return ; }