From 677cf7689bf151a699e79b12cdbea428fdea28c2 Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Fri, 20 Jan 2023 15:40:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=94=B6=E4=BB=B6?= =?UTF-8?q?=E7=AE=B1=E6=93=8D=E4=BD=9C:=20=E5=85=A8=E9=83=A8=E5=B7=B2?= =?UTF-8?q?=E8=AF=BB=E5=92=8C=E6=B8=85=E7=A9=BA=E6=94=B6=E4=BB=B6=E7=AE=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/shared/model/inbox.ts | 14 ++++- client/shared/redux/setup.ts | 4 ++ client/shared/redux/slices/chat.ts | 8 ++- .../Main/Content/Inbox/Content/index.tsx | 5 +- .../src/routes/Main/Content/Inbox/Sidebar.tsx | 50 +++++++++++++++++- .../src/routes/Main/Content/Inbox/index.tsx | 2 +- .../services/core/chat/inbox.service.dev.ts | 51 ++++++++++++++++--- 7 files changed, 119 insertions(+), 15 deletions(-) diff --git a/client/shared/model/inbox.ts b/client/shared/model/inbox.ts index bb298941..bf897d10 100644 --- a/client/shared/model/inbox.ts +++ b/client/shared/model/inbox.ts @@ -18,8 +18,18 @@ export interface InboxItem { updatedAt: string; } -export async function setInboxAck(inboxItemId: string) { +/** + * 设置收件箱某条记录已读 + */ +export async function setInboxAck(inboxItemIds: string[]) { await request.post('/api/chat/inbox/ack', { - inboxItemId, + inboxItemIds, }); } + +/** + * 清空收件箱 + */ +export async function clearInbox() { + await request.post('/api/chat/inbox/clear'); +} diff --git a/client/shared/redux/setup.ts b/client/shared/redux/setup.ts index 1835a1bd..90cb646c 100644 --- a/client/shared/redux/setup.ts +++ b/client/shared/redux/setup.ts @@ -269,6 +269,10 @@ function listenNotify(socket: AppSocket, store: AppStore) { store.dispatch(groupActions.removeGroup(groupId)); }); + socket.listen('chat.inbox.append', (item) => { + store.dispatch(chatActions.appendInboxItem(item)); + }); + socket.listen('chat.inbox.updated', () => { // 检测到收件箱列表被更新,需要重新获取 socket.request('chat.inbox.all').then((list) => { diff --git a/client/shared/redux/slices/chat.ts b/client/shared/redux/slices/chat.ts index ce0f8e91..27443156 100644 --- a/client/shared/redux/slices/chat.ts +++ b/client/shared/redux/slices/chat.ts @@ -314,7 +314,6 @@ const chatSlice = createSlice({ ); message.reactions.splice(reactionIndex, 1); }, - /** * 设置收件箱 */ @@ -322,6 +321,13 @@ const chatSlice = createSlice({ const list = action.payload; state.inbox = list; }, + + /** + * 增加收件箱项目 + */ + appendInboxItem(state, action: PayloadAction) { + state.inbox.push(action.payload); + }, /** * 设置收件箱 */ diff --git a/client/web/src/routes/Main/Content/Inbox/Content/index.tsx b/client/web/src/routes/Main/Content/Inbox/Content/index.tsx index 6caec098..e59589c6 100644 --- a/client/web/src/routes/Main/Content/Inbox/Content/index.tsx +++ b/client/web/src/routes/Main/Content/Inbox/Content/index.tsx @@ -11,6 +11,9 @@ import { import { chatActions } from 'tailchat-shared'; import { InboxMessageContent } from './Message'; +/** + * 收件箱主要界面 + */ export const InboxContent: React.FC = React.memo((props) => { const { inboxItemId } = useParams(); const inboxItem = useInboxItem(inboxItemId ?? ''); @@ -40,7 +43,7 @@ function useInboxAck(inboxItemId: string) { if (item.readed === false) { dispatch(chatActions.setInboxItemAck(inboxItemId)); - model.inbox.setInboxAck(inboxItemId); + model.inbox.setInboxAck([inboxItemId]); } }); } diff --git a/client/web/src/routes/Main/Content/Inbox/Sidebar.tsx b/client/web/src/routes/Main/Content/Inbox/Sidebar.tsx index d05756f6..63f01b4b 100644 --- a/client/web/src/routes/Main/Content/Inbox/Sidebar.tsx +++ b/client/web/src/routes/Main/Content/Inbox/Sidebar.tsx @@ -1,6 +1,15 @@ import React, { useMemo } from 'react'; import { CommonSidebarWrapper } from '@/components/CommonSidebarWrapper'; -import { InboxItem, isValidStr, model, t, useInboxList } from 'tailchat-shared'; +import { + chatActions, + InboxItem, + isValidStr, + model, + t, + useAppDispatch, + useAsyncRequest, + useInboxList, +} from 'tailchat-shared'; import clsx from 'clsx'; import _orderBy from 'lodash/orderBy'; import { GroupName } from '@/components/GroupName'; @@ -10,6 +19,7 @@ import { useLocation } from 'react-router'; import { Link } from 'react-router-dom'; import { PillTabPane, PillTabs } from '@/components/PillTabs'; import { SectionHeader } from '@/components/SectionHeader'; +import { openReconfirmModalP } from '@/components/Modal'; const buildLink = (itemId: string) => `/main/inbox/${itemId}`; @@ -19,6 +29,7 @@ const buildLink = (itemId: string) => `/main/inbox/${itemId}`; export const InboxSidebar: React.FC = React.memo(() => { const inbox = useInboxList(); const list = useMemo(() => _orderBy(inbox, 'createdAt', 'desc'), [inbox]); + const dispatch = useAppDispatch(); const renderInbox = (item: InboxItem) => { if (item.type === 'message') { @@ -49,9 +60,44 @@ export const InboxSidebar: React.FC = React.memo(() => { const fullList = list; const unreadList = list.filter((item) => item.readed === false); + const [, handleAllAck] = useAsyncRequest(async () => { + unreadList.forEach((item) => { + dispatch(chatActions.setInboxItemAck(item._id)); + }); + + await model.inbox.setInboxAck(unreadList.map((item) => item._id)); + }, [unreadList]); + + const [, handleClear] = useAsyncRequest(async () => { + const res = await openReconfirmModalP({ + title: t('确认清空收件箱么?'), + }); + if (res) { + await model.inbox.clearInbox(); + } + }, [unreadList]); + return ( - {t('收件箱')} + + {t('收件箱')} +
diff --git a/client/web/src/routes/Main/Content/Inbox/index.tsx b/client/web/src/routes/Main/Content/Inbox/index.tsx index b7316d57..d74d1c42 100644 --- a/client/web/src/routes/Main/Content/Inbox/index.tsx +++ b/client/web/src/routes/Main/Content/Inbox/index.tsx @@ -21,7 +21,7 @@ Inbox.displayName = 'Inbox'; const InboxNoSelect: React.FC = React.memo(() => { return (
- +
); }); diff --git a/server/services/core/chat/inbox.service.dev.ts b/server/services/core/chat/inbox.service.dev.ts index 663a7d7e..07a7e6f3 100644 --- a/server/services/core/chat/inbox.service.dev.ts +++ b/server/services/core/chat/inbox.service.dev.ts @@ -52,8 +52,6 @@ class InboxService extends TcService { }) ); } - - this.notifyUsersInboxUpdate(ctx, mentions); } } ); @@ -80,9 +78,10 @@ class InboxService extends TcService { this.registerAction('all', this.all); this.registerAction('ack', this.ack, { params: { - inboxItemId: 'string', + inboxItemIds: { type: 'array', items: 'string' }, }, }); + this.registerAction('clear', this.clear); } async appendMessage( @@ -102,7 +101,7 @@ class InboxService extends TcService { messageSnippet, } = ctx.params; - await this.adapter.model.create({ + const doc = await this.adapter.model.create({ userId, type: 'message', message: { @@ -113,6 +112,10 @@ class InboxService extends TcService { }, }); + const inboxItem = await this.transformDocuments(ctx, {}, doc); + + await this.notifyUsersInboxAppend(ctx, [userId], inboxItem); + return true; } @@ -141,6 +144,8 @@ class InboxService extends TcService { }, }); + await this.notifyUsersInboxUpdate(ctx, [userId]); // not good, 后面最好修改为发送删除的项而不是所有 + return true; } @@ -160,13 +165,15 @@ class InboxService extends TcService { /** * 标记收件箱内容已读 */ - async ack(ctx: TcContext<{ inboxItemId: string }>) { - const inboxItemId = ctx.params.inboxItemId; + async ack(ctx: TcContext<{ inboxItemIds: string[] }>) { + const inboxItemIds = ctx.params.inboxItemIds; const userId = ctx.meta.userId; - await this.adapter.model.updateOne( + await this.adapter.model.updateMany( { - _id: inboxItemId, + _id: { + $in: [...inboxItemIds], + }, userId, }, { @@ -177,6 +184,34 @@ class InboxService extends TcService { return true; } + /** + * 清空所有的收件箱内容 + */ + async clear(ctx: TcContext) { + const userId = ctx.meta.userId; + + await this.adapter.model.deleteMany({ + userId, + }); + + await this.notifyUsersInboxUpdate(ctx, [userId]); + + return true; + } + + /** + * 发送通知群组信息有新的内容 + * + * 发送通知时会同时清空群组信息缓存 + */ + private async notifyUsersInboxAppend( + ctx: TcPureContext, + userIds: string[], + data: any + ): Promise { + await this.listcastNotify(ctx, userIds, 'append', { ...data }); + } + /** * 发送通知群组信息发生变更 *