Skip to content

Commit af03bec

Browse files
committed
feat: 收件箱增加已读未读标识
1 parent db64ed4 commit af03bec

File tree

5 files changed

+92
-29
lines changed

5 files changed

+92
-29
lines changed

client/shared/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ export {
202202
userActions,
203203
groupActions,
204204
uiActions,
205+
chatActions,
205206
globalActions,
206207
} from './redux/slices';
207208
export type { ChatConverseState } from './redux/slices/chat';

client/shared/model/inbox.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { request } from '../api/request';
2+
13
/**
24
* 收件箱记录项类型
35
*/
@@ -15,3 +17,9 @@ export interface InboxItem {
1517
createdAt: string;
1618
updatedAt: string;
1719
}
20+
21+
export async function setInboxAck(inboxItemId: string) {
22+
await request.post('/api/chat/inbox/ack', {
23+
inboxItemId,
24+
});
25+
}

client/shared/redux/slices/chat.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,17 @@ const chatSlice = createSlice({
322322
const list = action.payload;
323323
state.inbox = list;
324324
},
325+
/**
326+
* 设置收件箱
327+
*/
328+
setInboxItemAck(state, action: PayloadAction<string>) {
329+
const inboxItemId = action.payload;
330+
const item = state.inbox.find((item) => item._id === inboxItemId);
331+
332+
if (item) {
333+
item.readed = true;
334+
}
335+
},
325336
},
326337
});
327338

client/web/src/routes/Main/Content/Inbox/Content/index.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
import { NotFound } from '@/components/NotFound';
22
import React from 'react';
33
import { useParams } from 'react-router';
4-
import { t, useInboxItem } from 'tailchat-shared';
4+
import {
5+
model,
6+
t,
7+
useAppDispatch,
8+
useInboxItem,
9+
useWatch,
10+
} from 'tailchat-shared';
11+
import { chatActions } from 'tailchat-shared';
512
import { InboxMessageContent } from './Message';
613

714
export const InboxContent: React.FC = React.memo((props) => {
815
const { inboxItemId } = useParams();
916
const inboxItem = useInboxItem(inboxItemId ?? '');
1017

18+
useInboxAck(inboxItemId ?? '');
19+
1120
if (!inboxItem) {
1221
return <NotFound message={t('没有找到该记录')} />;
1322
}
@@ -19,3 +28,19 @@ export const InboxContent: React.FC = React.memo((props) => {
1928
return <NotFound message={t('没有找到该类型的渲染方式')} />;
2029
});
2130
InboxContent.displayName = 'InboxContent';
31+
32+
function useInboxAck(inboxItemId: string) {
33+
const dispatch = useAppDispatch();
34+
const item = useInboxItem(inboxItemId);
35+
36+
useWatch([inboxItemId], () => {
37+
if (!item) {
38+
return;
39+
}
40+
41+
if (item.readed === false) {
42+
dispatch(chatActions.setInboxItemAck(inboxItemId));
43+
model.inbox.setInboxAck(inboxItemId);
44+
}
45+
});
46+
}

client/web/src/routes/Main/Content/Inbox/Sidebar.tsx

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import React, { useMemo } from 'react';
22
import { CommonSidebarWrapper } from '@/components/CommonSidebarWrapper';
3-
import { isValidStr, model, t, useInboxList } from 'tailchat-shared';
3+
import { InboxItem, isValidStr, model, t, useInboxList } from 'tailchat-shared';
44
import clsx from 'clsx';
55
import _orderBy from 'lodash/orderBy';
66
import { GroupName } from '@/components/GroupName';
77
import { ConverseName } from '@/components/ConverseName';
88
import { getMessageRender } from '@/plugin/common';
99
import { useLocation } from 'react-router';
1010
import { Link } from 'react-router-dom';
11+
import { PillTabPane, PillTabs } from '@/components/PillTabs';
1112

1213
/**
1314
* 收件箱侧边栏组件
@@ -16,33 +17,48 @@ export const InboxSidebar: React.FC = React.memo(() => {
1617
const inbox = useInboxList();
1718
const list = useMemo(() => _orderBy(inbox, 'createdAt', 'desc'), [inbox]);
1819

19-
return (
20-
<CommonSidebarWrapper data-tc-role="sidebar-inbox">
21-
<div className="overflow-auto">
22-
{list.map((item) => {
23-
const { type } = item;
20+
const renderInbox = (item: InboxItem) => {
21+
const { type } = item;
22+
23+
if (type === 'message') {
24+
const message: Partial<model.inbox.InboxItem['message']> =
25+
item.message ?? {};
26+
let title: React.ReactNode = '';
27+
if (isValidStr(message.groupId)) {
28+
title = <GroupName groupId={message.groupId} />;
29+
} else if (isValidStr(message.converseId)) {
30+
title = <ConverseName converseId={message.converseId} />;
31+
}
32+
33+
return (
34+
<InboxSidebarItem
35+
key={item._id}
36+
title={title}
37+
desc={getMessageRender(message.messageSnippet ?? '')}
38+
source={'Tailchat'}
39+
readed={item.readed}
40+
to={`/main/inbox/${item._id}`}
41+
/>
42+
);
43+
}
2444

25-
if (type === 'message') {
26-
const message: Partial<model.inbox.InboxItem['message']> =
27-
item.message ?? {};
28-
let title: React.ReactNode = '';
29-
if (isValidStr(message.groupId)) {
30-
title = <GroupName groupId={message.groupId} />;
31-
} else if (isValidStr(message.converseId)) {
32-
title = <ConverseName converseId={message.converseId} />;
33-
}
45+
return null;
46+
};
3447

35-
return (
36-
<InboxSidebarItem
37-
key={item._id}
38-
title={title}
39-
desc={getMessageRender(message.messageSnippet ?? '')}
40-
source={'Tailchat'}
41-
to={`/main/inbox/${item._id}`}
42-
/>
43-
);
44-
}
45-
})}
48+
const fullList = list;
49+
const unreadList = list.filter((item) => item.readed === false);
50+
51+
return (
52+
<CommonSidebarWrapper data-tc-role="sidebar-inbox">
53+
<div>
54+
<PillTabs>
55+
<PillTabPane key="1" tab={`${t('全部')} (${fullList.length})`}>
56+
{fullList.map((item) => renderInbox(item))}
57+
</PillTabPane>
58+
<PillTabPane key="2" tab={`${t('未读')} (${unreadList.length})`}>
59+
{unreadList.map((item) => renderInbox(item))}
60+
</PillTabPane>
61+
</PillTabs>
4662
</div>
4763
</CommonSidebarWrapper>
4864
);
@@ -53,6 +69,7 @@ const InboxSidebarItem: React.FC<{
5369
title: React.ReactNode;
5470
desc: React.ReactNode;
5571
source: string;
72+
readed: boolean;
5673
to: string;
5774
}> = React.memo((props) => {
5875
const location = useLocation();
@@ -62,10 +79,11 @@ const InboxSidebarItem: React.FC<{
6279
<Link to={props.to}>
6380
<div
6481
className={clsx(
65-
'p-2 overflow-auto cursor-pointer hover:bg-black hover:bg-opacity-10 dark:hover:bg-white dark:hover:bg-opacity-10',
82+
'p-2 overflow-auto cursor-pointer hover:bg-black hover:bg-opacity-10 dark:hover:bg-white dark:hover:bg-opacity-10 border-r-4 rounded',
6683
{
6784
'bg-black bg-opacity-10 dark:bg-white dark:bg-opacity-10': isActive,
68-
}
85+
},
86+
props.readed ? 'border-transparent' : 'border-green-500'
6987
)}
7088
>
7189
<div className="text-lg overflow-ellipsis overflow-hidden text-gray-700 dark:text-white">

0 commit comments

Comments
 (0)