feat: 增加收件箱操作: 全部已读和清空收件箱

pull/64/head
moonrailgun 2 years ago
parent b74b956e45
commit 677cf7689b

@ -18,8 +18,18 @@ export interface InboxItem {
updatedAt: string; updatedAt: string;
} }
export async function setInboxAck(inboxItemId: string) { /**
*
*/
export async function setInboxAck(inboxItemIds: string[]) {
await request.post('/api/chat/inbox/ack', { await request.post('/api/chat/inbox/ack', {
inboxItemId, inboxItemIds,
}); });
} }
/**
*
*/
export async function clearInbox() {
await request.post('/api/chat/inbox/clear');
}

@ -269,6 +269,10 @@ function listenNotify(socket: AppSocket, store: AppStore) {
store.dispatch(groupActions.removeGroup(groupId)); store.dispatch(groupActions.removeGroup(groupId));
}); });
socket.listen<InboxItem>('chat.inbox.append', (item) => {
store.dispatch(chatActions.appendInboxItem(item));
});
socket.listen('chat.inbox.updated', () => { socket.listen('chat.inbox.updated', () => {
// 检测到收件箱列表被更新,需要重新获取 // 检测到收件箱列表被更新,需要重新获取
socket.request<InboxItem[]>('chat.inbox.all').then((list) => { socket.request<InboxItem[]>('chat.inbox.all').then((list) => {

@ -314,7 +314,6 @@ const chatSlice = createSlice({
); );
message.reactions.splice(reactionIndex, 1); message.reactions.splice(reactionIndex, 1);
}, },
/** /**
* *
*/ */
@ -322,6 +321,13 @@ const chatSlice = createSlice({
const list = action.payload; const list = action.payload;
state.inbox = list; state.inbox = list;
}, },
/**
*
*/
appendInboxItem(state, action: PayloadAction<InboxItem>) {
state.inbox.push(action.payload);
},
/** /**
* *
*/ */

@ -11,6 +11,9 @@ import {
import { chatActions } from 'tailchat-shared'; import { chatActions } from 'tailchat-shared';
import { InboxMessageContent } from './Message'; import { InboxMessageContent } from './Message';
/**
*
*/
export const InboxContent: React.FC = React.memo((props) => { export const InboxContent: React.FC = React.memo((props) => {
const { inboxItemId } = useParams(); const { inboxItemId } = useParams();
const inboxItem = useInboxItem(inboxItemId ?? ''); const inboxItem = useInboxItem(inboxItemId ?? '');
@ -40,7 +43,7 @@ function useInboxAck(inboxItemId: string) {
if (item.readed === false) { if (item.readed === false) {
dispatch(chatActions.setInboxItemAck(inboxItemId)); dispatch(chatActions.setInboxItemAck(inboxItemId));
model.inbox.setInboxAck(inboxItemId); model.inbox.setInboxAck([inboxItemId]);
} }
}); });
} }

@ -1,6 +1,15 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { CommonSidebarWrapper } from '@/components/CommonSidebarWrapper'; 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 clsx from 'clsx';
import _orderBy from 'lodash/orderBy'; import _orderBy from 'lodash/orderBy';
import { GroupName } from '@/components/GroupName'; import { GroupName } from '@/components/GroupName';
@ -10,6 +19,7 @@ import { useLocation } from 'react-router';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { PillTabPane, PillTabs } from '@/components/PillTabs'; import { PillTabPane, PillTabs } from '@/components/PillTabs';
import { SectionHeader } from '@/components/SectionHeader'; import { SectionHeader } from '@/components/SectionHeader';
import { openReconfirmModalP } from '@/components/Modal';
const buildLink = (itemId: string) => `/main/inbox/${itemId}`; 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(() => { export const InboxSidebar: React.FC = React.memo(() => {
const inbox = useInboxList(); const inbox = useInboxList();
const list = useMemo(() => _orderBy(inbox, 'createdAt', 'desc'), [inbox]); const list = useMemo(() => _orderBy(inbox, 'createdAt', 'desc'), [inbox]);
const dispatch = useAppDispatch();
const renderInbox = (item: InboxItem) => { const renderInbox = (item: InboxItem) => {
if (item.type === 'message') { if (item.type === 'message') {
@ -49,9 +60,44 @@ export const InboxSidebar: React.FC = React.memo(() => {
const fullList = list; const fullList = list;
const unreadList = list.filter((item) => item.readed === false); 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 ( return (
<CommonSidebarWrapper data-tc-role="sidebar-inbox"> <CommonSidebarWrapper data-tc-role="sidebar-inbox">
<SectionHeader>{t('收件箱')}</SectionHeader> <SectionHeader
menu={{
items: [
{
key: 'readAll',
label: t('所有已读'),
onClick: handleAllAck,
},
{
key: 'clear',
label: t('清空收件箱'),
danger: true,
onClick: handleClear,
},
],
}}
>
{t('收件箱')}
</SectionHeader>
<div> <div>
<PillTabs> <PillTabs>

@ -21,7 +21,7 @@ Inbox.displayName = 'Inbox';
const InboxNoSelect: React.FC = React.memo(() => { const InboxNoSelect: React.FC = React.memo(() => {
return ( return (
<div className="mt-11 w-full"> <div className="mt-11 w-full">
<Problem text={t('空空的,什么都没有选中')} /> <Problem text={t('提及(@)您的消息会在这里出现哦')} />
</div> </div>
); );
}); });

@ -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('all', this.all);
this.registerAction('ack', this.ack, { this.registerAction('ack', this.ack, {
params: { params: {
inboxItemId: 'string', inboxItemIds: { type: 'array', items: 'string' },
}, },
}); });
this.registerAction('clear', this.clear);
} }
async appendMessage( async appendMessage(
@ -102,7 +101,7 @@ class InboxService extends TcService {
messageSnippet, messageSnippet,
} = ctx.params; } = ctx.params;
await this.adapter.model.create({ const doc = await this.adapter.model.create({
userId, userId,
type: 'message', type: 'message',
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; return true;
} }
@ -141,6 +144,8 @@ class InboxService extends TcService {
}, },
}); });
await this.notifyUsersInboxUpdate(ctx, [userId]); // not good, 后面最好修改为发送删除的项而不是所有
return true; return true;
} }
@ -160,13 +165,15 @@ class InboxService extends TcService {
/** /**
* *
*/ */
async ack(ctx: TcContext<{ inboxItemId: string }>) { async ack(ctx: TcContext<{ inboxItemIds: string[] }>) {
const inboxItemId = ctx.params.inboxItemId; const inboxItemIds = ctx.params.inboxItemIds;
const userId = ctx.meta.userId; const userId = ctx.meta.userId;
await this.adapter.model.updateOne( await this.adapter.model.updateMany(
{ {
_id: inboxItemId, _id: {
$in: [...inboxItemIds],
},
userId, userId,
}, },
{ {
@ -177,6 +184,34 @@ class InboxService extends TcService {
return true; 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<void> {
await this.listcastNotify(ctx, userIds, 'append', { ...data });
}
/** /**
* *
* *

Loading…
Cancel
Save