diff --git a/client/web/src/plugin/common/index.ts b/client/web/src/plugin/common/index.ts index 8bfec09c..9e1292c7 100644 --- a/client/web/src/plugin/common/index.ts +++ b/client/web/src/plugin/common/index.ts @@ -66,6 +66,7 @@ export { showNotification, fetchAvailableServices, isValidStr, + useGroupInfo, useGroupPanelInfo, sendMessage, showMessageTime, @@ -100,7 +101,13 @@ export { export function useCurrentUserInfo() { const userInfo = useUserInfo(); - return _pick(userInfo, ['email', 'nickname', 'discriminator', 'avatar']); + return _pick(userInfo, [ + '_id', + 'email', + 'nickname', + 'discriminator', + 'avatar', + ]); } /** diff --git a/server/plugins/com.msgbyte.topic/services/topic.service.ts b/server/plugins/com.msgbyte.topic/services/topic.service.ts index ab6cf6d5..3c2a36f7 100644 --- a/server/plugins/com.msgbyte.topic/services/topic.service.ts +++ b/server/plugins/com.msgbyte.topic/services/topic.service.ts @@ -41,6 +41,13 @@ class GroupTopicService extends TcService { replyCommentId: { type: 'string', optional: true }, }, }); + this.registerAction('delete', this.delete, { + params: { + groupId: 'string', + panelId: 'string', + topicId: 'string', + }, + }); } protected onInited(): void { @@ -199,6 +206,46 @@ class GroupTopicService extends TcService { return true; } + + /** + * 删除话题 + */ + async delete( + ctx: TcContext<{ + groupId: string; + panelId: string; + topicId: string; + }> + ) { + const { groupId, panelId, topicId } = ctx.params; + const userId = ctx.meta.userId; + const t = ctx.meta.t; + + // 鉴权 + const group = await call(ctx).getGroupInfo(groupId); + const isMember = group.members.some((member) => member.userId === userId); + if (!isMember) { + throw new Error(t('不是该群组成员')); + } + + if (String(group.owner) !== userId) { + throw new Error(t('仅群组所有者有权限删除话题')); + } + + const result = await this.adapter.model.deleteOne({ + _id: topicId, + groupId, + panelId, + }); + + this.roomcastNotify(ctx, panelId, 'delete', { + groupId, + panelId, + topicId, + }); + + return result.deletedCount > 0; + } } export default GroupTopicService; diff --git a/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/components/TopicCard.tsx b/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/components/TopicCard.tsx index 0b81b045..7a018f8c 100644 --- a/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/components/TopicCard.tsx +++ b/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/components/TopicCard.tsx @@ -4,6 +4,8 @@ import { showMessageTime, showSuccessToasts, useAsyncRequest, + useCurrentUserInfo, + useGroupInfo, } from '@capital/common'; import { IconBtn, @@ -57,6 +59,11 @@ const Root = styled.div` margin-bottom: 6px; } } + + .footer { + display: flex; + gap: 4px; + } } `; @@ -76,6 +83,9 @@ export const TopicCard: React.FC<{ const topic: Partial = props.topic ?? {}; const [showReply, toggleShowReply] = useReducer((state) => !state, false); const [comment, setComment] = useState(''); + const groupInfo = useGroupInfo(topic.groupId); + const groupOwnerId = groupInfo?.owner; + const userId = useCurrentUserInfo()._id; const [{ loading }, handleComment] = useAsyncRequest(async () => { await request.post('createComment', { @@ -90,6 +100,14 @@ export const TopicCard: React.FC<{ showSuccessToasts(); }, [topic.groupId, topic.panelId, topic._id, comment]); + const [, handleDeleteTopic] = useAsyncRequest(async () => { + await request.post('delete', { + groupId: topic.groupId, + panelId: topic.panelId, + topicId: topic._id, + }); + }, []); + return ( @@ -119,6 +137,14 @@ export const TopicCard: React.FC<{ icon="mdi:message-reply-text-outline" onClick={toggleShowReply} /> + + {userId === groupOwnerId && ( + + )} {showReply && ( diff --git a/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/group/GroupTopicPanelRender.tsx b/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/group/GroupTopicPanelRender.tsx index 3de90f26..6efff5bd 100644 --- a/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/group/GroupTopicPanelRender.tsx +++ b/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/group/GroupTopicPanelRender.tsx @@ -58,6 +58,7 @@ const GroupTopicPanelRender: React.FC = React.memo(() => { topicMap, addTopicPanel, addTopicItem, + deleteTopicItem, updateTopicItem, resetTopicPanel, } = useTopicStore(); @@ -119,6 +120,18 @@ const GroupTopicPanelRender: React.FC = React.memo(() => { } ); + useGlobalSocketEvent( + 'plugin:com.msgbyte.topic.delete', + (info: { panelId: string; topicId: string }) => { + /** + * 仅处理当前面板的话题更新 + */ + if (info.panelId === panelId) { + deleteTopicItem(panelId, info.topicId); + } + } + ); + useGlobalSocketEvent( 'plugin:com.msgbyte.topic.createComment', (topic: GroupTopic) => { diff --git a/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/store.ts b/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/store.ts index bd7c8d70..bb3cab3c 100644 --- a/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/store.ts +++ b/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/store.ts @@ -11,6 +11,7 @@ interface TopicStoreState { topicMap: TopicPanelMap; addTopicPanel: (panelId: string, topicList: GroupTopic[]) => void; addTopicItem: (panelId: string, topic: GroupTopic) => void; + deleteTopicItem: (panelId: string, topicId: string) => void; updateTopicItem: (panelId: string, topic: GroupTopic) => void; resetTopicPanel: (panelId: string) => void; } @@ -37,6 +38,15 @@ export const useTopicStore = create< } }); }, + deleteTopicItem: (panelId, topicId) => { + set((state) => { + if (state.topicMap[panelId]) { + state.topicMap[panelId] = state.topicMap[panelId].filter( + (item) => item._id !== topicId + ); + } + }); + }, updateTopicItem: (panelId, topic) => { set((state) => { if (state.topicMap[panelId]) { diff --git a/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/translate.ts b/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/translate.ts index ea973c6b..badee783 100644 --- a/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/translate.ts +++ b/server/plugins/com.msgbyte.topic/web/plugins/com.msgbyte.topic/src/translate.ts @@ -5,6 +5,7 @@ export const Translate = { noTopic: localTrans({ 'zh-CN': '暂无话题', 'en-US': 'No Topic' }), createBtn: localTrans({ 'zh-CN': '创建话题', 'en-US': 'Create Topic' }), reply: localTrans({ 'zh-CN': '回复', 'en-US': 'Reply' }), + delete: localTrans({ 'zh-CN': '删除', 'en-US': 'Delete' }), replyThisTopic: localTrans({ 'zh-CN': '回复该话题', 'en-US': 'Reply this topic',