Skip to content

Commit a9f2d00

Browse files
committed
feat: 收件箱增加message类型的消息内容渲染
1 parent 170a243 commit a9f2d00

File tree

8 files changed

+169
-40
lines changed

8 files changed

+169
-40
lines changed

client/shared/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export type {
140140
GroupInvite,
141141
GroupMember,
142142
} from './model/group';
143+
export type { InboxItem } from './model/inbox';
143144
export {
144145
sendMessage,
145146
recallMessage,
@@ -195,7 +196,7 @@ export {
195196
useHasGroupPermission,
196197
} from './redux/hooks/useGroupPermission';
197198
export { useUserInfo, useUserId } from './redux/hooks/useUserInfo';
198-
export { useInboxList } from './redux/hooks/useInboxList';
199+
export { useInboxList, useInboxItem } from './redux/hooks/useInbox';
199200
export { useUnread } from './redux/hooks/useUnread';
200201
export {
201202
userActions,

client/shared/model/message.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,23 @@ export async function fetchConverseLastMessages(
108108
return data;
109109
}
110110

111+
/**
112+
* @param converseId 会话ID
113+
* @param messageId 消息ID
114+
* @returns 消息附近的信息
115+
*/
116+
export async function fetchNearbyMessage(
117+
converseId: string,
118+
messageId: string
119+
): Promise<ChatMessage[]> {
120+
const { data } = await request.post('/api/chat/message/fetchNearbyMessage', {
121+
converseId,
122+
messageId,
123+
});
124+
125+
return data;
126+
}
127+
111128
/**
112129
* 增加表情行为
113130
*/

client/shared/redux/hooks/useInboxList.ts renamed to client/shared/redux/hooks/useInbox.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,12 @@ import { useAppSelector } from './useAppSelector';
77
export function useInboxList(): InboxItem[] {
88
return useAppSelector((state) => state.chat.inbox ?? []);
99
}
10+
11+
/**
12+
* 返回收件箱某一项的值
13+
*/
14+
export function useInboxItem(inboxItemId: string): InboxItem | null {
15+
const list = useInboxList();
16+
17+
return list.find((item) => item._id === inboxItemId) ?? null;
18+
}

client/web/src/components/Problem.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import problemSvg from '@assets/images/problem.svg';
3+
import { t } from 'tailchat-shared';
34

45
interface ProblemProps {
56
text?: React.ReactNode;
@@ -13,7 +14,7 @@ export const Problem: React.FC<ProblemProps> = React.memo((props) => {
1314
<div className="text-center w-full">
1415
<img className="w-32 h-32 m-auto mb-2" src={problemSvg} />
1516

16-
<div>{props.text}</div>
17+
<div>{props.text ?? t('出现了一些问题')}</div>
1718
</div>
1819
);
1920
});
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { NormalMessageList } from '@/components/ChatBox/ChatMessageList/NormalList';
2+
import { LoadingSpinner } from '@/components/LoadingSpinner';
3+
import { Problem } from '@/components/Problem';
4+
import React from 'react';
5+
import { useNavigate } from 'react-router';
6+
import {
7+
InboxItem,
8+
model,
9+
showErrorToasts,
10+
t,
11+
useAsync,
12+
} from 'tailchat-shared';
13+
14+
interface Props {
15+
info: InboxItem;
16+
}
17+
export const InboxMessageContent: React.FC<Props> = React.memo((props) => {
18+
const info = props.info;
19+
const navigate = useNavigate();
20+
21+
const message = info.message;
22+
if (!message) {
23+
return <Problem />;
24+
}
25+
const { groupId, converseId, messageId } = message;
26+
27+
return (
28+
<div className="w-full relative">
29+
<NearbyMessages converseId={converseId} messageId={messageId} />
30+
31+
<div className="absolute bottom-4 left-0 right-0 text-center">
32+
<div
33+
className="shadow-lg px-6 py-2 rounded-full inline-block bg-indigo-600 hover:bg-indigo-700 text-white cursor-pointer"
34+
onClick={() => {
35+
if (groupId) {
36+
// 跳转到群组
37+
navigate(`/main/group/${groupId}/${converseId}`);
38+
} else {
39+
navigate(`/main/personal/converse/${converseId}`);
40+
}
41+
}}
42+
>
43+
{t('跳转到会话')}
44+
</div>
45+
</div>
46+
</div>
47+
);
48+
});
49+
InboxMessageContent.displayName = 'InboxMessageContent';
50+
51+
export const NearbyMessages: React.FC<{
52+
converseId: string;
53+
messageId: string;
54+
}> = React.memo((props) => {
55+
const { value = [], loading } = useAsync(async () => {
56+
try {
57+
const list = await model.message.fetchNearbyMessage(
58+
props.converseId,
59+
props.messageId
60+
);
61+
62+
return list;
63+
} catch (err) {
64+
showErrorToasts(err);
65+
console.error(err);
66+
}
67+
}, [props.converseId, props.messageId]);
68+
69+
if (loading) {
70+
return <LoadingSpinner />;
71+
}
72+
73+
return (
74+
<div>
75+
<NormalMessageList
76+
messages={value}
77+
isLoadingMore={false}
78+
hasMoreMessage={false}
79+
onLoadMore={async () => {}}
80+
/>
81+
</div>
82+
);
83+
});
84+
NearbyMessages.displayName = 'NearbyMessages';
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { NotFound } from '@/components/NotFound';
2+
import React from 'react';
3+
import { useParams } from 'react-router';
4+
import { t, useInboxItem } from 'tailchat-shared';
5+
import { InboxMessageContent } from './Message';
6+
7+
export const InboxContent: React.FC = React.memo((props) => {
8+
const { inboxItemId } = useParams();
9+
const inboxItem = useInboxItem(inboxItemId ?? '');
10+
11+
if (!inboxItem) {
12+
return <NotFound message={t('没有找到该记录')} />;
13+
}
14+
15+
if (inboxItem.type === 'message') {
16+
return <InboxMessageContent info={inboxItem} />;
17+
}
18+
19+
return <NotFound message={t('没有找到该类型的渲染方式')} />;
20+
});
21+
InboxContent.displayName = 'InboxContent';

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

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,13 @@ import _orderBy from 'lodash/orderBy';
66
import { GroupName } from '@/components/GroupName';
77
import { ConverseName } from '@/components/ConverseName';
88
import { getMessageRender } from '@/plugin/common';
9-
10-
interface InboxSidebarProps {
11-
selectedItem: string;
12-
onSelect: (itemId: string) => void;
13-
}
9+
import { useLocation } from 'react-router';
10+
import { Link } from 'react-router-dom';
1411

1512
/**
1613
* 收件箱侧边栏组件
1714
*/
18-
export const InboxSidebar: React.FC<InboxSidebarProps> = React.memo((props) => {
15+
export const InboxSidebar: React.FC = React.memo(() => {
1916
const inbox = useInboxList();
2017
const list = useMemo(() => _orderBy(inbox, 'createdAt', 'desc'), [inbox]);
2118

@@ -41,8 +38,7 @@ export const InboxSidebar: React.FC<InboxSidebarProps> = React.memo((props) => {
4138
title={title}
4239
desc={getMessageRender(message.messageSnippet ?? '')}
4340
source={'Tailchat'}
44-
selected={props.selectedItem === item._id}
45-
onSelect={() => props.onSelect(item._id)}
41+
to={`/main/inbox/${item._id}`}
4642
/>
4743
);
4844
}
@@ -57,30 +53,32 @@ const InboxSidebarItem: React.FC<{
5753
title: React.ReactNode;
5854
desc: React.ReactNode;
5955
source: string;
60-
selected: boolean;
61-
onSelect: () => void;
56+
to: string;
6257
}> = React.memo((props) => {
58+
const location = useLocation();
59+
const isActive = location.pathname.startsWith(props.to);
60+
6361
return (
64-
<div
65-
className={clsx(
66-
'p-2 overflow-auto cursor-pointer hover:bg-black hover:bg-opacity-10 dark:hover:bg-white dark:hover:bg-opacity-10',
67-
{
68-
'bg-black bg-opacity-10 dark:bg-white dark:bg-opacity-10':
69-
props.selected,
70-
}
71-
)}
72-
onClick={props.onSelect}
73-
>
74-
<div className="text-lg overflow-ellipsis overflow-hidden">
75-
{props.title || <span>&nbsp;</span>}
76-
</div>
77-
<div className="break-all text-opacity-80 text-black dark:text-opacity-80 dark:text-white text-sm p-1 border-l-2 border-gray-500 border-opacity-50">
78-
{props.desc}
79-
</div>
80-
<div className="text-xs text-opacity-50 text-black dark:text-opacity-50 dark:text-white">
81-
{t('来自')}: {props.source}
62+
<Link to={props.to}>
63+
<div
64+
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',
66+
{
67+
'bg-black bg-opacity-10 dark:bg-white dark:bg-opacity-10': isActive,
68+
}
69+
)}
70+
>
71+
<div className="text-lg overflow-ellipsis overflow-hidden text-gray-700 dark:text-white">
72+
{props.title || <span>&nbsp;</span>}
73+
</div>
74+
<div className="break-all text-opacity-80 text-black dark:text-opacity-80 dark:text-white text-sm p-1 border-l-2 border-gray-500 border-opacity-50">
75+
{props.desc}
76+
</div>
77+
<div className="text-xs text-opacity-50 text-black dark:text-opacity-50 dark:text-white">
78+
{t('来自')}: {props.source}
79+
</div>
8280
</div>
83-
</div>
81+
</Link>
8482
);
8583
});
8684
InboxSidebarItem.displayName = 'InboxSidebarItem';

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1-
import React, { useState } from 'react';
1+
import React from 'react';
2+
import { Route, Routes } from 'react-router';
23
import { PageContent } from '../PageContent';
4+
import { InboxContent } from './Content';
35
import { InboxSidebar } from './Sidebar';
46

57
export const Inbox: React.FC = React.memo(() => {
6-
const [selectedItem, setSelectedItem] = useState('');
78
return (
8-
<PageContent
9-
data-tc-role="content-inbox"
10-
sidebar={
11-
<InboxSidebar selectedItem={selectedItem} onSelect={setSelectedItem} />
12-
}
13-
>
14-
<div>Inbox {selectedItem}</div>
9+
<PageContent data-tc-role="content-inbox" sidebar={<InboxSidebar />}>
10+
<Routes>
11+
<Route path="/:inboxItemId" element={<InboxContent />} />
12+
</Routes>
1513
</PageContent>
1614
);
1715
});

0 commit comments

Comments
 (0)