@droppii-org/chat-sdk 0.0.19 → 0.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/components/chatBubble/ChatBubble.d.ts +10 -0
  2. package/dist/components/chatBubble/ChatBubble.d.ts.map +1 -0
  3. package/dist/components/chatBubble/ChatBubble.js +28 -0
  4. package/dist/components/conversation/DeskConversationList.d.ts +1 -2
  5. package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
  6. package/dist/components/conversation/DeskConversationList.js +107 -148
  7. package/dist/components/message/MessageHeader.d.ts +8 -0
  8. package/dist/components/message/MessageHeader.d.ts.map +1 -0
  9. package/dist/components/message/MessageHeader.js +9 -0
  10. package/dist/components/message/MessageItem.d.ts +7 -0
  11. package/dist/components/message/MessageItem.d.ts.map +1 -0
  12. package/dist/components/message/MessageItem.js +21 -0
  13. package/dist/components/message/MessageList.d.ts +1 -1
  14. package/dist/components/message/MessageList.d.ts.map +1 -1
  15. package/dist/components/message/MessageList.js +47 -65
  16. package/dist/components/message/footer/BottomSection.d.ts +3 -0
  17. package/dist/components/message/footer/BottomSection.d.ts.map +1 -0
  18. package/dist/components/message/footer/BottomSection.js +6 -0
  19. package/dist/components/message/footer/index.d.ts +3 -0
  20. package/dist/components/message/footer/index.d.ts.map +1 -0
  21. package/dist/components/message/footer/index.js +25 -0
  22. package/dist/constants/index.d.ts +2 -0
  23. package/dist/constants/index.d.ts.map +1 -0
  24. package/dist/constants/index.js +1 -0
  25. package/dist/constants/sdk.d.ts.map +1 -1
  26. package/dist/constants/sdk.js +3 -1
  27. package/dist/context/ChatContext.d.ts +1 -1
  28. package/dist/context/ChatContext.d.ts.map +1 -1
  29. package/dist/context/ChatContext.js +46 -8
  30. package/dist/hooks/conversation/useConversation.d.ts +3 -1
  31. package/dist/hooks/conversation/useConversation.d.ts.map +1 -1
  32. package/dist/hooks/conversation/useConversation.js +24 -14
  33. package/dist/hooks/conversation/useConversationStore.d.ts +11 -0
  34. package/dist/hooks/conversation/useConversationStore.d.ts.map +1 -0
  35. package/dist/hooks/conversation/useConversationStore.js +10 -0
  36. package/dist/hooks/global/useGlobalEvent.d.ts +2 -0
  37. package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -0
  38. package/dist/hooks/global/useGlobalEvent.js +78 -0
  39. package/dist/hooks/message/useMessage.d.ts +18 -6
  40. package/dist/hooks/message/useMessage.d.ts.map +1 -1
  41. package/dist/hooks/message/useMessage.js +115 -37
  42. package/dist/hooks/message/useSendMessage.d.ts +5 -2
  43. package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
  44. package/dist/hooks/message/useSendMessage.js +33 -14
  45. package/dist/index.d.ts +3 -3
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +3 -3
  48. package/dist/layout/index.d.ts +7 -0
  49. package/dist/layout/index.d.ts.map +1 -0
  50. package/dist/layout/index.js +11 -0
  51. package/dist/screens/chatBubble/index.d.ts +10 -0
  52. package/dist/screens/chatBubble/index.d.ts.map +1 -0
  53. package/dist/screens/chatBubble/index.js +12 -0
  54. package/dist/screens/desk-message/index.d.ts.map +1 -1
  55. package/dist/screens/desk-message/index.js +3 -2
  56. package/dist/screens/deskMessage/index.d.ts +3 -0
  57. package/dist/screens/deskMessage/index.d.ts.map +1 -0
  58. package/dist/screens/deskMessage/index.js +22 -0
  59. package/dist/styles/global.css +1 -1
  60. package/dist/tsconfig.tsbuildinfo +1 -0
  61. package/dist/types/chat.d.ts +22 -1
  62. package/dist/types/chat.d.ts.map +1 -1
  63. package/dist/types/chat.js +14 -1
  64. package/dist/utils/events.d.ts +13 -0
  65. package/dist/utils/events.d.ts.map +1 -0
  66. package/dist/utils/events.js +4 -0
  67. package/package.json +18 -2
@@ -0,0 +1,10 @@
1
+ import { SessionType } from "@openim/wasm-client-sdk";
2
+ interface ChatBubbleProps {
3
+ conversationId: string;
4
+ sourceID: string;
5
+ sessionType: SessionType;
6
+ className?: string;
7
+ }
8
+ declare const ChatBubble: ({ conversationId, sourceID, sessionType, className, }: ChatBubbleProps) => import("react/jsx-runtime").JSX.Element;
9
+ export default ChatBubble;
10
+ //# sourceMappingURL=ChatBubble.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatBubble.d.ts","sourceRoot":"","sources":["../../../src/components/chatBubble/ChatBubble.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGtD,UAAU,eAAe;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,QAAA,MAAM,UAAU,GAAI,uDAKjB,eAAe,4CAmDjB,CAAC;AAEF,eAAe,UAAU,CAAC"}
@@ -0,0 +1,28 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { FloatButton, Drawer } from "antd";
5
+ import { MessageOutlined, CloseOutlined } from "@ant-design/icons";
6
+ import MessageList from "../message/MessageList";
7
+ import { useConversationDetail } from "../../hooks/conversation/useConversation";
8
+ const ChatBubble = ({ conversationId, sourceID, sessionType, className, }) => {
9
+ const { conversationDetail } = useConversationDetail({
10
+ sourceID,
11
+ sessionType,
12
+ });
13
+ const [isOpen, setIsOpen] = useState(false);
14
+ const toggleChat = () => {
15
+ setIsOpen(!isOpen);
16
+ };
17
+ return (_jsxs(_Fragment, { children: [_jsx(FloatButton, { icon: isOpen ? _jsx(CloseOutlined, {}) : _jsx(MessageOutlined, {}), type: "primary", style: {
18
+ right: 24,
19
+ bottom: 24,
20
+ width: 60,
21
+ height: 60,
22
+ }, onClick: toggleChat, className: className }), _jsx(Drawer, { placement: "right", onClose: () => setIsOpen(false), open: isOpen, mask: true, closable: false, styles: {
23
+ body: { padding: 0 },
24
+ }, classNames: {
25
+ wrapper: "!z-[9999]",
26
+ }, children: _jsx(MessageList, { conversationId: conversationId, conversationData: conversationDetail, className: "flex-1", onClose: () => setIsOpen(false) }) })] }));
27
+ };
28
+ export default ChatBubble;
@@ -1,8 +1,7 @@
1
1
  interface DeskConversationListProps {
2
2
  onConversationSelect?: (conversationId: string, threadId: string) => void;
3
- selectedConversationId?: string;
4
3
  className?: string;
5
4
  }
6
- declare const DeskConversationList: ({ onConversationSelect, selectedConversationId, className, }: DeskConversationListProps) => import("react/jsx-runtime").JSX.Element;
5
+ declare const DeskConversationList: ({ onConversationSelect, className, }: DeskConversationListProps) => import("react/jsx-runtime").JSX.Element;
7
6
  export default DeskConversationList;
8
7
  //# sourceMappingURL=DeskConversationList.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AAkHA,UAAU,yBAAyB;IACjC,oBAAoB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAmDD,QAAA,MAAM,oBAAoB,GAAI,8DAI3B,yBAAyB,4CAiK3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AAuGA,UAAU,yBAAyB;IACjC,oBAAoB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,QAAA,MAAM,oBAAoB,GAAI,sCAG3B,yBAAyB,4CAuL3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -1,168 +1,127 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useEffect } from "react";
3
+ import { useState, useEffect, useCallback } from "react";
4
4
  import { useRouter, useSearchParams, usePathname } from "next/navigation";
5
+ import { Input, Avatar, Badge, Empty } from "antd";
6
+ import { SessionType } from "@openim/wasm-client-sdk";
5
7
  import { useConversationList } from "../../hooks/conversation/useConversation";
6
8
  import { Icon } from "../icon";
7
- const mockConversations = [
8
- {
9
- id: "1",
10
- threadId: "thread_001",
11
- name: "Phương Huyền (phhuyen2110)",
12
- username: "phhuyen2110",
13
- avatar: "https://i.pinimg.com/736x/55/e5/ed/55e5edbb1a5b5f6e4f3cefc98de629ca.jpg",
14
- lastMessage: "Customer: hi Livechat Obefe",
15
- timestamp: "27/07",
16
- unreadCount: 1,
17
- isOnline: true,
18
- status: "unassigned",
19
- source: "obefe",
20
- },
21
- {
22
- id: "2",
23
- threadId: "thread_002",
24
- name: "Phương Huyền (phhuyen2110)",
25
- username: "phhuyen2110",
26
- avatar: "https://i.pinimg.com/736x/55/e5/ed/55e5edbb1a5b5f6e4f3cefc98de629ca.jpg",
27
- lastMessage: "Customer: hi Livechat Obefe",
28
- timestamp: "27/07",
29
- unreadCount: 1,
30
- isOnline: true,
31
- status: "slow",
32
- source: "obefe",
33
- },
34
- {
35
- id: "3",
36
- threadId: "thread_003",
37
- name: "Phương Huyền (phhuyen2110)",
38
- username: "phhuyen2110",
39
- avatar: "https://i.pinimg.com/736x/55/e5/ed/55e5edbb1a5b5f6e4f3cefc98de629ca.jpg",
40
- lastMessage: "Customer: hi Livechat Obefe",
41
- timestamp: "27/07",
42
- unreadCount: 1,
43
- isOnline: true,
44
- status: "waiting",
45
- source: "obefe",
46
- },
47
- {
48
- id: "4",
49
- threadId: "thread_004",
50
- name: "Phương Huyền (phhuyen2110)",
51
- username: "phhuyen2110",
52
- avatar: "https://i.pinimg.com/736x/55/e5/ed/55e5edbb1a5b5f6e4f3cefc98de629ca.jpg",
53
- lastMessage: "Customer: hi Livechat Obefe",
54
- timestamp: "27/07",
55
- unreadCount: 1,
56
- isOnline: true,
57
- status: "not_replied",
58
- source: "obefe",
59
- },
60
- {
61
- id: "5",
62
- threadId: "thread_005",
63
- name: "Phương Huyền (phhuyen2110)",
64
- username: "phhuyen2110",
65
- avatar: "https://i.pinimg.com/736x/55/e5/ed/55e5edbb1a5b5f6e4f3cefc98de629ca.jpg",
66
- lastMessage: "Customer: hi Livechat Obefe",
67
- timestamp: "27/07",
68
- unreadCount: 1,
69
- isOnline: true,
70
- status: "processing",
71
- source: "obefe",
72
- },
73
- {
74
- id: "6",
75
- threadId: "thread_006",
76
- name: "Phương Huyền (phhuyen2110)",
77
- username: "phhuyen2110",
78
- avatar: "https://i.pinimg.com/736x/55/e5/ed/55e5edbb1a5b5f6e4f3cefc98de629ca.jpg",
79
- lastMessage: "Customer: hi Livechat Obefe",
80
- timestamp: "27/07",
81
- unreadCount: 1,
82
- isOnline: true,
83
- status: "closed",
84
- source: "obefe",
85
- },
86
- ];
87
- const getStatusIcon = (status) => {
88
- switch (status) {
89
- case "unassigned":
90
- return {
91
- icon: "user-del-o",
92
- bgColor: "bg-red-100",
93
- iconColor: "text-red-500",
94
- };
95
- case "slow":
96
- return {
97
- icon: "warning-square-o",
98
- bgColor: "bg-orange-100",
99
- iconColor: "text-orange-500",
100
- };
101
- case "waiting":
102
- return {
103
- icon: "time-circle-o",
104
- bgColor: "bg-yellow-100",
105
- iconColor: "text-yellow-600",
106
- };
107
- case "not_replied":
108
- return {
109
- icon: "arrow-reply-o",
110
- bgColor: "bg-orange-100",
111
- iconColor: "text-orange-500",
112
- };
113
- case "processing":
114
- return {
115
- icon: "play-b",
116
- bgColor: "bg-blue-100",
117
- iconColor: "text-blue-500",
118
- };
119
- case "paused":
120
- return {
121
- icon: "pause-b",
122
- bgColor: "bg-gray-100",
123
- iconColor: "text-gray-500",
124
- };
125
- case "closed":
126
- return {
127
- icon: "check-b",
128
- bgColor: "bg-green-100",
129
- iconColor: "text-green-500",
130
- };
131
- default:
132
- return null;
9
+ import { useChatContext } from "../../context/ChatContext";
10
+ import useConversationStore from "../../hooks/conversation/useConversationStore";
11
+ const parseLatestMessage = (latestMsg, currentUserId) => {
12
+ var _a;
13
+ if (!latestMsg)
14
+ return "";
15
+ try {
16
+ const msgData = JSON.parse(latestMsg);
17
+ // Check for text message (textElem)
18
+ if ((_a = msgData.textElem) === null || _a === void 0 ? void 0 : _a.content) {
19
+ const isMe = currentUserId && msgData.sendID === currentUserId;
20
+ const sender = isMe ? "Me" : (msgData === null || msgData === void 0 ? void 0 : msgData.senderNickname) || msgData.sendID;
21
+ return `${sender}: ${msgData.textElem.content}`;
22
+ }
23
+ // TODO: Handle other message types (fileElem, videoElem, etc.)
24
+ // For now, return empty string for non-text messages
25
+ // This can be enhanced later to show appropriate previews
26
+ return "Tin nhắn không khả dụng";
133
27
  }
28
+ catch (error) {
29
+ console.error("Error parsing latest message:", error);
30
+ return "";
31
+ }
32
+ };
33
+ // Utility function to format timestamp
34
+ const formatTimestamp = (timestamp) => {
35
+ if (!timestamp)
36
+ return "";
37
+ const date = new Date(timestamp);
38
+ const now = new Date();
39
+ const diffInMs = now.getTime() - date.getTime();
40
+ const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
41
+ if (diffInDays === 0) {
42
+ // Today - show time
43
+ return date.toLocaleTimeString("vi-VN", {
44
+ hour: "2-digit",
45
+ minute: "2-digit",
46
+ hour12: false,
47
+ });
48
+ }
49
+ else if (diffInDays === 1) {
50
+ // Yesterday
51
+ return "Hôm qua";
52
+ }
53
+ else if (diffInDays < 7) {
54
+ // This week - show day name
55
+ return date.toLocaleDateString("vi-VN", { weekday: "long" });
56
+ }
57
+ else {
58
+ // Older - show date
59
+ return date.toLocaleDateString("vi-VN", {
60
+ day: "2-digit",
61
+ month: "2-digit",
62
+ });
63
+ }
64
+ };
65
+ // Transform API data to UI-friendly format
66
+ const transformConversationData = (apiData, currentUserId) => {
67
+ return apiData.map((conv) => (Object.assign(Object.assign({}, conv), { id: conv.conversationID, threadId: conv.conversationID, name: conv.showName || "Unknown User", username: conv.userID || conv.groupID || "", avatar: conv.faceURL ||
68
+ "https://i.pinimg.com/736x/55/e5/ed/55e5edbb1a5b5f6e4f3cefc98de629ca.jpg", lastMessage: parseLatestMessage(conv.latestMsg, currentUserId), timestamp: formatTimestamp(conv.latestMsgSendTime), unreadCount: conv.unreadCount, isOnline: true, source: conv.conversationType === 3 ? "group" : "direct" })));
134
69
  };
135
- const DeskConversationList = ({ onConversationSelect, selectedConversationId, className = "", }) => {
136
- const { conversationList } = useConversationList();
70
+ const DeskConversationList = ({ onConversationSelect, className = "", }) => {
137
71
  const [searchQuery, setSearchQuery] = useState("");
138
72
  const router = useRouter();
139
73
  const pathname = usePathname();
140
74
  const searchParams = useSearchParams();
141
- console.log(conversationList);
142
- const currentThreadId = searchParams.get("threadId");
143
- const [selectedThreadId, setSelectedThreadId] = useState(currentThreadId);
144
- useEffect(() => {
145
- setSelectedThreadId(currentThreadId);
146
- }, [currentThreadId]);
147
- const conversations = mockConversations;
75
+ const { user } = useChatContext();
76
+ const setConversationData = useConversationStore((state) => state.setConversationData);
77
+ const selectedThreadId = useConversationStore((state) => state.selectedThreadId);
78
+ const setSelectedThreadId = useConversationStore((state) => state.setSelectedThreadId);
79
+ const setSelectedSourceId = useConversationStore((state) => state.setSelectedSourceId);
80
+ const { conversationList, markConversationMessageAsRead } = useConversationList(selectedThreadId);
81
+ // Transform real conversation data from the API
82
+ const conversations = transformConversationData(conversationList || [], user === null || user === void 0 ? void 0 : user.userID);
148
83
  const filteredConversations = conversations.filter((conv) => conv.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
149
84
  conv.lastMessage.toLowerCase().includes(searchQuery.toLowerCase()));
150
85
  const handleConversationClick = (conversation) => {
86
+ setConversationData(conversation);
151
87
  const newSearchParams = new URLSearchParams(searchParams);
152
- newSearchParams.set("threadId", conversation.threadId);
88
+ newSearchParams.set("threadId", conversation.id);
153
89
  router.push(`${pathname}?${newSearchParams.toString()}`);
154
- setSelectedThreadId(conversation.threadId);
155
- onConversationSelect === null || onConversationSelect === void 0 ? void 0 : onConversationSelect(conversation.id, conversation.threadId);
90
+ setSelectedThreadId(conversation.id);
91
+ onConversationSelect === null || onConversationSelect === void 0 ? void 0 : onConversationSelect(conversation.id, conversation.id);
156
92
  };
157
- return (_jsxs("div", { className: `flex flex-col h-full bg-white border-r border-gray-200 ${className}`, children: [_jsx("div", { className: "p-3 border-b border-gray-200", children: _jsxs("div", { className: "relative", children: [_jsx(Icon, { icon: "search-o", size: 18, className: "absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" }), _jsx("input", { type: "text", placeholder: "T\u00ECm ki\u1EBFm", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "w-full pl-10 pr-4 py-2.5 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm" })] }) }), _jsxs("div", { className: "flex-1 overflow-y-auto", children: [filteredConversations.map((conversation) => (_jsxs("div", { onClick: () => handleConversationClick(conversation), className: `relative p-3 border-b border-gray-100 hover:bg-gray-50 cursor-pointer transition-colors ${selectedThreadId === conversation.threadId
93
+ const onSetSelectedSourceId = useCallback(() => {
94
+ const selectedConversation = conversations.findIndex((conv) => conv.id === selectedThreadId);
95
+ if (selectedConversation !== -1) {
96
+ const conversation = conversations[selectedConversation];
97
+ const sourceId = conversation.conversationType === SessionType.Group
98
+ ? conversation.groupID
99
+ : conversation.userID;
100
+ setSelectedSourceId(sourceId);
101
+ }
102
+ }, [conversationList, selectedThreadId]);
103
+ useEffect(() => {
104
+ const threadId = searchParams.get("threadId");
105
+ if (threadId) {
106
+ setSelectedThreadId(threadId);
107
+ }
108
+ else if (conversations.length > 0) {
109
+ setSelectedThreadId(conversations[0].id);
110
+ const newSearchParams = new URLSearchParams(searchParams);
111
+ newSearchParams.set("threadId", conversations[0].id);
112
+ router.replace(`${pathname}?${newSearchParams.toString()}`);
113
+ }
114
+ }, [searchParams, conversations.length]);
115
+ useEffect(() => {
116
+ if (!!selectedThreadId) {
117
+ markConversationMessageAsRead(selectedThreadId);
118
+ onSetSelectedSourceId();
119
+ }
120
+ }, [selectedThreadId, onSetSelectedSourceId]);
121
+ return (_jsxs("div", { className: `flex flex-col h-full bg-white border-r border-gray-200 w-[320px] ${className}`, children: [_jsx("div", { className: "p-3 border-b border-gray-200", children: _jsx(Input, { placeholder: "T\u00ECm ki\u1EBFm", prefix: _jsx(Icon, { icon: "search-o", size: 18, className: "text-gray-400" }), value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "rounded-lg" }) }), _jsxs("div", { className: "flex-1 overflow-y-auto", children: [filteredConversations.map((conversation) => (_jsxs("div", { onClick: () => handleConversationClick(conversation), className: `relative p-3 border-b border-gray-100 hover:bg-gray-50 cursor-pointer transition-colors ${selectedThreadId === conversation.threadId
158
122
  ? "bg-blue-50"
159
- : "bg-white"}`, children: [selectedThreadId === conversation.threadId && (_jsx("div", { className: "absolute left-0 top-0 bottom-0 w-1 bg-blue-500" })), _jsxs("div", { className: "flex items-start gap-3", children: [_jsxs("div", { className: "relative flex-shrink-0", children: [_jsx("img", { src: conversation.avatar, alt: conversation.name, className: "w-12 h-12 rounded-full object-cover" }), conversation.isOnline && (_jsx("div", { className: "absolute -bottom-0.5 -right-0.5 w-4 h-4 bg-green-500 border-2 border-white rounded-full" }))] }), _jsx("div", { className: "flex-1 min-w-0", children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("h3", { className: "font-semibold text-gray-900 text-sm truncate", children: conversation.name }), _jsx("p", { className: "text-xs text-gray-500 truncate mt-0.5", children: conversation.lastMessage })] }), _jsxs("div", { className: "flex flex-col items-end gap-1 ml-2", children: [_jsx("span", { className: "text-xs text-gray-400", children: conversation.timestamp }), _jsxs("div", { className: "flex items-center gap-1", children: [conversation.unreadCount > 0 && (_jsx("span", { className: "inline-flex items-center justify-center w-5 h-5 text-xs font-medium text-white bg-red-500 rounded-full", children: conversation.unreadCount })), (() => {
160
- const statusIcon = getStatusIcon(conversation.status);
161
- if (!statusIcon)
162
- return null;
163
- return (_jsx("div", { className: `w-5 h-5 ${statusIcon.bgColor} rounded-full flex items-center justify-center`, children: _jsx(Icon, { icon: statusIcon.icon, size: 12, className: statusIcon.iconColor }) }));
164
- })()] })] })] }) })] })] }, conversation.id))), filteredConversations.length === 0 && (_jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-gray-500", children: [_jsx(Icon, { icon: "chat-square-b", size: 48, className: "text-gray-300 mb-4" }), _jsx("p", { className: "text-lg font-medium mb-2", children: "Kh\u00F4ng t\u00ECm th\u1EA5y cu\u1ED9c tr\u00F2 chuy\u1EC7n" }), _jsx("p", { className: "text-sm text-center", children: searchQuery
165
- ? "Thử tìm kiếm với từ khóa khác"
166
- : "Chưa có cuộc trò chuyện nào" })] }))] })] }));
123
+ : "bg-white"}`, children: [selectedThreadId === conversation.threadId && (_jsx("div", { className: "absolute left-0 top-0 bottom-0 w-1 bg-blue-500" })), _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "relative flex-shrink-0", children: _jsx(Badge, { dot: conversation.isOnline, status: conversation.isOnline ? "success" : "default", offset: [-2, 36], children: _jsx(Avatar, { size: 48, src: conversation.avatar, alt: conversation.name }) }) }), _jsx("div", { className: "flex-1 min-w-0", children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("h3", { className: "font-semibold text-gray-900 text-sm truncate", children: conversation.name }), _jsx("p", { className: "text-xs text-gray-500 truncate mt-0.5", children: conversation.lastMessage })] }), _jsxs("div", { className: "flex flex-col items-end gap-1 ml-2", children: [_jsx("span", { className: "text-xs text-gray-400", children: conversation.timestamp }), _jsx("div", { className: "flex items-center gap-1", children: conversation.unreadCount > 0 && (_jsx(Badge, { count: conversation.unreadCount })) })] })] }) })] })] }, conversation.id))), filteredConversations.length === 0 && (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx(Icon, { icon: "chat-square-b", size: 48, className: "text-gray-300" }), description: _jsxs("div", { children: [_jsx("p", { className: "text-lg font-medium mb-2 text-gray-500", children: "Kh\u00F4ng t\u00ECm th\u1EA5y cu\u1ED9c tr\u00F2 chuy\u1EC7n" }), _jsx("p", { className: "text-sm text-gray-400", children: searchQuery
124
+ ? "Thử tìm kiếm với từ khóa khác"
125
+ : "Chưa có cuộc trò chuyện nào" })] }) }) }))] })] }));
167
126
  };
168
127
  export default DeskConversationList;
@@ -0,0 +1,8 @@
1
+ import { ConversationItem } from "@openim/wasm-client-sdk";
2
+ interface MessageHeaderProps {
3
+ conversationData: ConversationItem | null;
4
+ onClose?: () => void;
5
+ }
6
+ declare const MessageHeader: ({ conversationData, onClose }: MessageHeaderProps) => import("react/jsx-runtime").JSX.Element;
7
+ export default MessageHeader;
8
+ //# sourceMappingURL=MessageHeader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageHeader.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageHeader.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAI3D,UAAU,kBAAkB;IAC1B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,QAAA,MAAM,aAAa,GAAI,+BAA+B,kBAAkB,4CA6CvE,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -0,0 +1,9 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Avatar, Button } from "antd";
4
+ import { Icon } from "../icon";
5
+ const MessageHeader = ({ conversationData, onClose }) => {
6
+ var _a, _b;
7
+ return (_jsxs("div", { className: "px-4 py-3 flex items-center border-b gap-3", children: [_jsx(Avatar, { src: conversationData === null || conversationData === void 0 ? void 0 : conversationData.faceURL, size: "large", children: ((_b = (_a = conversationData === null || conversationData === void 0 ? void 0 : conversationData.showName) === null || _a === void 0 ? void 0 : _a.charAt) === null || _b === void 0 ? void 0 : _b.call(_a, 0)) || "A" }), _jsxs("div", { className: "flex flex-col flex-1", children: [_jsx("p", { children: (conversationData === null || conversationData === void 0 ? void 0 : conversationData.showName) || "" }), _jsx("p", { className: "text-xs text-gray-500", children: "2 thành viên" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", children: _jsx(Icon, { icon: "search-o", size: 22 }) }), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", children: _jsx(Icon, { icon: "folder-o", size: 22 }) }), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", children: _jsx(Icon, { icon: "align-justify-o", size: 22 }) }), !!onClose && (_jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: onClose, children: _jsx(Icon, { icon: "close-b", size: 22 }) }))] })] }));
8
+ };
9
+ export default MessageHeader;
@@ -0,0 +1,7 @@
1
+ import { GroupMessageItem } from "../../types/chat";
2
+ interface MessageItemProps {
3
+ groupMessage: GroupMessageItem;
4
+ }
5
+ declare const MessageItem: ({ groupMessage }: MessageItemProps) => import("react/jsx-runtime").JSX.Element;
6
+ export default MessageItem;
7
+ //# sourceMappingURL=MessageItem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageItem.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageItem.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAUpD,UAAU,gBAAgB;IACxB,YAAY,EAAE,gBAAgB,CAAC;CAChC;AACD,QAAA,MAAM,WAAW,GAAI,kBAAkB,gBAAgB,4CAwEtD,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import dayjs from "dayjs";
3
+ import clsx from "clsx";
4
+ import { Avatar } from "antd";
5
+ import isToday from "dayjs/plugin/isToday";
6
+ import { useChatContext } from "../../context/ChatContext";
7
+ import { SessionType } from "@openim/wasm-client-sdk";
8
+ dayjs.extend(isToday);
9
+ const MessageItem = ({ groupMessage }) => {
10
+ const { user } = useChatContext();
11
+ const messagesInGroup = (groupMessage === null || groupMessage === void 0 ? void 0 : groupMessage.messages) || [];
12
+ const isToday = dayjs(groupMessage === null || groupMessage === void 0 ? void 0 : groupMessage.sendTime).isToday();
13
+ return (_jsxs("div", { className: "flex flex-col gap-2 my-4 mx-3 sm:mx-4", children: [_jsx("div", { className: "flex justify-center", children: _jsx("span", { className: "text-xs text-gray-600 text-center bg-neutral-100 px-2 py-1 rounded-full", children: dayjs(groupMessage === null || groupMessage === void 0 ? void 0 : groupMessage.sendTime).format(isToday ? "HH:mm" : "HH:mm, DD MMMM") }) }), messagesInGroup === null || messagesInGroup === void 0 ? void 0 : messagesInGroup.map((message, messageIndex) => {
14
+ var _a, _b, _c;
15
+ const isMine = (message === null || message === void 0 ? void 0 : message.sendID) === (user === null || user === void 0 ? void 0 : user.userID);
16
+ const showAvatar = messageIndex === messagesInGroup.length - 1;
17
+ const showSenderName = messageIndex === 0 && (message === null || message === void 0 ? void 0 : message.sessionType) === SessionType.Group;
18
+ return (_jsx("div", { className: clsx("flex", isMine ? "justify-end" : "justify-start"), children: _jsxs("div", { className: clsx("flex items-end gap-2", isMine ? "justify-end" : "justify-start"), children: [!isMine && (_jsx("div", { className: "flex items-center justify-center w-[32px] h-[32px]", children: showAvatar && (_jsx(Avatar, { children: ((_b = (_a = message === null || message === void 0 ? void 0 : message.senderNickname) === null || _a === void 0 ? void 0 : _a.charAt) === null || _b === void 0 ? void 0 : _b.call(_a, 0)) || "A" })) })), _jsxs("div", { className: "flex flex-col items-start flex-[0.8]", children: [!isMine && showSenderName && (_jsx("span", { className: "text-xs text-gray-500 mb-1 px-3", children: message === null || message === void 0 ? void 0 : message.senderNickname })), _jsxs("div", { className: clsx("px-3 py-2 rounded-2xl max-w-full break-words flex flex-col text-gray-900", isMine ? "bg-blue-100" : "bg-gray-100"), children: [_jsx("p", { className: "text-sm sm:text-base whitespace-pre-wrap", children: ((_c = message === null || message === void 0 ? void 0 : message.textElem) === null || _c === void 0 ? void 0 : _c.content) || "Tin nhắn không khả dụng" }), _jsx("span", { className: clsx("text-xs text-gray-500 text-right text-gray-500"), children: dayjs(message === null || message === void 0 ? void 0 : message.sendTime).format("HH:mm") })] })] })] }) }, message === null || message === void 0 ? void 0 : message.clientMsgID));
19
+ })] }, groupMessage === null || groupMessage === void 0 ? void 0 : groupMessage.groupMessageID));
20
+ };
21
+ export default MessageItem;
@@ -1,4 +1,4 @@
1
- import { type ConversationItem } from "@openim/wasm-client-sdk";
1
+ import { ConversationItem } from "@openim/wasm-client-sdk";
2
2
  interface MessageListProps {
3
3
  conversationId: string;
4
4
  conversationData: ConversationItem | null;
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,gBAAgB,EAGtB,MAAM,yBAAyB,CAAC;AASjC,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAyL3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAe,MAAM,yBAAyB,CAAC;AAgBxE,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAqH3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -1,21 +1,23 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { SessionType, } from "@openim/wasm-client-sdk";
4
- import { useChatContext } from "../../context/ChatContext";
3
+ import { SessionType } from "@openim/wasm-client-sdk";
5
4
  import { useMessage } from "../../hooks/message/useMessage";
6
- import { Avatar, Button, Input, Tooltip } from "antd";
5
+ import { Button, Input, Spin, Tooltip } from "antd";
7
6
  import { Icon } from "../icon";
8
7
  import { useCallback, useEffect, useRef, useState } from "react";
9
- import clsx from "clsx";
10
8
  import { useSendMessage } from "../../hooks/message/useSendMessage";
9
+ import dayjs from "dayjs";
10
+ import isToday from "dayjs/plugin/isToday";
11
+ import emitter from "../../utils/events";
12
+ import MessageItem from "./MessageItem";
13
+ import InfiniteScroll from "react-infinite-scroll-component";
14
+ import MessageHeader from "./MessageHeader";
15
+ dayjs.extend(isToday);
11
16
  const MessageList = (props) => {
12
17
  var _a, _b;
13
- const { conversationId, conversationData, onClose } = props;
14
- const { messageList, refetch } = useMessage(conversationId);
18
+ const { conversationData, onClose, conversationId } = props;
15
19
  const scrollRef = useRef(null);
16
- const shouldScrollToBottomRef = useRef(true);
17
- const lastMessageCountRef = useRef((messageList === null || messageList === void 0 ? void 0 : messageList.length) || 0);
18
- const { user } = useChatContext();
20
+ const { getMoreOldMessages, moreOldLoading, loadState, latestLoadState } = useMessage(conversationId);
19
21
  const { sendTextMessage } = useSendMessage({
20
22
  recvID: (conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationType) !== SessionType.Single
21
23
  ? ""
@@ -25,67 +27,47 @@ const MessageList = (props) => {
25
27
  : (conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID) || "",
26
28
  });
27
29
  const [textMessage, setTextMessage] = useState("");
28
- const [showScrollToBottomButton, setShowScrollToBottomButton] = useState(false);
29
- const scrollToBottom = useCallback((force = false) => {
30
- if (scrollRef.current) {
31
- scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
32
- if (force) {
33
- shouldScrollToBottomRef.current = true; // If forced, ensure auto-scroll is re-enabled
34
- }
35
- }
36
- }, []);
37
- const handleScroll = useCallback(() => {
38
- if (!scrollRef.current)
39
- return;
40
- const { scrollTop, scrollHeight, clientHeight } = scrollRef.current;
41
- const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
42
- const SCROLL_UP_THRESHOLD = 200; // If user scrolls up more than 200px from bottom, disable auto-scroll
43
- const SCROLL_DOWN_THRESHOLD = 5; // If user scrolls within 5px of bottom, re-enable auto-scroll
44
- if (distanceFromBottom > SCROLL_UP_THRESHOLD) {
45
- shouldScrollToBottomRef.current = false;
46
- }
47
- else if (distanceFromBottom <= SCROLL_DOWN_THRESHOLD) {
48
- shouldScrollToBottomRef.current = true;
49
- }
50
- // Show button if not at bottom AND auto-scroll is disabled
51
- setShowScrollToBottomButton(distanceFromBottom > SCROLL_DOWN_THRESHOLD &&
52
- !shouldScrollToBottomRef.current);
53
- }, []);
54
- const onSendTextMessage = async () => {
30
+ const [composing, setComposing] = useState(false);
31
+ const scrollToBottom = () => {
32
+ setTimeout(() => {
33
+ var _a, _b;
34
+ (_a = scrollRef.current) === null || _a === void 0 ? void 0 : _a.scrollTo({
35
+ top: (_b = scrollRef.current) === null || _b === void 0 ? void 0 : _b.scrollHeight,
36
+ behavior: "smooth",
37
+ });
38
+ });
39
+ };
40
+ const onSendTextMessage = useCallback(async () => {
41
+ var _a;
42
+ const messageList = (_a = latestLoadState.current) === null || _a === void 0 ? void 0 : _a.messageList;
55
43
  setTextMessage("");
56
- const res = await sendTextMessage(textMessage);
57
- if (res) {
58
- refetch();
59
- }
44
+ const lastMessage = messageList === null || messageList === void 0 ? void 0 : messageList[(messageList === null || messageList === void 0 ? void 0 : messageList.length) - 1];
45
+ sendTextMessage(textMessage, lastMessage);
46
+ }, [textMessage, sendTextMessage, latestLoadState]);
47
+ const loadMoreMessage = () => {
48
+ if (!loadState.hasMoreOld || moreOldLoading)
49
+ return;
50
+ getMoreOldMessages();
60
51
  };
61
- const renderMessageItem = useCallback((message) => {
62
- var _a, _b, _c;
63
- const isMine = (message === null || message === void 0 ? void 0 : message.sendID) === (user === null || user === void 0 ? void 0 : user.userID);
64
- return (_jsx("div", { className: clsx("flex", isMine ? "justify-end" : "justify-start"), children: _jsxs("div", { className: "flex items-end gap-2", children: [!isMine && (_jsx(Avatar, { children: ((_b = (_a = message === null || message === void 0 ? void 0 : message.senderNickname) === null || _a === void 0 ? void 0 : _a.charAt) === null || _b === void 0 ? void 0 : _b.call(_a, 0)) || "A" })), _jsxs("div", { className: "flex flex-col items-start", children: [!isMine && (_jsx("span", { className: "text-xs text-gray-500 mb-1 px-3", children: message === null || message === void 0 ? void 0 : message.senderNickname })), _jsx("div", { className: clsx("px-3 py-2 sm:px-4 sm:py-2 rounded-2xl max-w-full break-words", isMine
65
- ? "bg-blue-500 text-white"
66
- : "bg-gray-100 text-gray-900"), children: _jsx("p", { className: "text-sm sm:text-base whitespace-pre-wrap", children: ((_c = message === null || message === void 0 ? void 0 : message.textElem) === null || _c === void 0 ? void 0 : _c.content) || "Tin nhắn không khả dụng" }) })] })] }) }, message === null || message === void 0 ? void 0 : message.clientMsgID));
67
- }, [user === null || user === void 0 ? void 0 : user.userID]);
68
52
  useEffect(() => {
69
- const currentMessageCount = (messageList === null || messageList === void 0 ? void 0 : messageList.length) || 0;
70
- const previousMessageCount = lastMessageCountRef.current;
71
- if (currentMessageCount > previousMessageCount) {
72
- const newMessages = messageList === null || messageList === void 0 ? void 0 : messageList.slice(previousMessageCount);
73
- const hasNewMessageFromCurrentUser = newMessages === null || newMessages === void 0 ? void 0 : newMessages.some((msg) => msg.sendID === (user === null || user === void 0 ? void 0 : user.userID));
74
- // If current user sent a message, always scroll to bottom
75
- // If another user sent a message, only scroll if shouldScrollToBottomRef is true (user is already at bottom)
76
- if (hasNewMessageFromCurrentUser) {
77
- setTimeout(() => scrollToBottom(true), 50); // Force scroll for own messages
78
- }
79
- else if (shouldScrollToBottomRef.current) {
80
- setTimeout(() => scrollToBottom(), 50); // Scroll if auto-scroll is enabled for others' messages
81
- }
82
- }
83
- lastMessageCountRef.current = currentMessageCount;
84
- }, [messageList, user === null || user === void 0 ? void 0 : user.userID, scrollToBottom]);
85
- return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full bg-white", children: [_jsxs("div", { className: "px-4 py-3 flex items-center border-b gap-3", children: [_jsx(Avatar, { src: conversationData === null || conversationData === void 0 ? void 0 : conversationData.faceURL, children: ((_b = (_a = conversationData === null || conversationData === void 0 ? void 0 : conversationData.showName) === null || _a === void 0 ? void 0 : _a.charAt) === null || _b === void 0 ? void 0 : _b.call(_a, 0)) || "A" }), _jsxs("div", { className: "flex flex-col flex-1", children: [_jsx("p", { children: (conversationData === null || conversationData === void 0 ? void 0 : conversationData.showName) || "" }), _jsx("p", { className: "text-xs text-gray-500", children: "2 thành viên" })] }), _jsx(Button, { type: "text", shape: "circle", icon: _jsx(Icon, { icon: "align-justify-o", size: 24 }) }), !!onClose && (_jsx(Button, { type: "text", shape: "circle", icon: _jsx(Icon, { icon: "close-b", size: 24 }), onClick: onClose }))] }), _jsx("div", { className: "flex flex-col flex-1 min-h-0", children: _jsx("div", { className: "relative h-full h-full", children: _jsx("div", { ref: scrollRef, className: "h-full overflow-y-auto p-3 sm:p-4 flex flex-col gap-2", children: messageList === null || messageList === void 0 ? void 0 : messageList.map((message) => renderMessageItem(message)) }) }) }), _jsx("div", { className: "border-t px-4 py-3", children: _jsx("div", { className: "border rounded-lg bg-gray-50", children: _jsxs("div", { className: "px-4 py-3 flex items-center gap-4", children: [_jsx(Input, { placeholder: "Nh\u1EADp tin nh\u1EAFn", size: "small", variant: "borderless", value: textMessage, onChange: (e) => setTextMessage(e.target.value), onKeyDown: (e) => {
53
+ emitter.on("CHAT_LIST_SCROLL_TO_BOTTOM", scrollToBottom);
54
+ console.log("CHAT_LIST_SCROLL_TO_BOTTOM");
55
+ return () => {
56
+ emitter.off("CHAT_LIST_SCROLL_TO_BOTTOM", scrollToBottom);
57
+ };
58
+ }, []);
59
+ return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full bg-white", children: [_jsx(MessageHeader, { conversationData: conversationData, onClose: onClose }), _jsx("div", { id: "scrollableDiv", style: {
60
+ height: "100%",
61
+ overflow: "auto",
62
+ display: "flex",
63
+ flexDirection: "column-reverse",
64
+ }, children: _jsx(InfiniteScroll, { dataLength: ((_a = loadState.groupMessageList) === null || _a === void 0 ? void 0 : _a.length) || 0, next: loadMoreMessage, style: { display: "flex", flexDirection: "column-reverse" }, inverse: true, hasMore: loadState.hasMoreOld, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableDiv", children: (_b = loadState.groupMessageList) === null || _b === void 0 ? void 0 : _b.toReversed().map((message) => _jsx(MessageItem, { groupMessage: message })) }) }), _jsx("div", { className: "border-t px-4 py-3", children: _jsx("div", { className: "border rounded-lg bg-gray-50", children: _jsxs("div", { className: "px-4 py-3 flex items-center gap-4", children: [_jsx(Input, { placeholder: "Nh\u1EADp tin nh\u1EAFn", size: "small", variant: "borderless", value: textMessage, onChange: (e) => setTextMessage(e.target.value), onKeyDown: (e) => {
65
+ if (composing) {
66
+ return;
67
+ }
86
68
  if (e.key === "Enter") {
87
69
  onSendTextMessage();
88
70
  }
89
- } }), _jsx(Tooltip, { title: "G\u1EEDi tin nh\u1EAFn", children: _jsx(Button, { type: "primary", shape: "circle", size: "middle", icon: _jsx(Icon, { icon: "send-b", color: "white", size: 16 }), disabled: textMessage.length === 0, onClick: onSendTextMessage }) })] }) }) })] }));
71
+ }, onCompositionStart: () => setComposing(true), onCompositionEnd: () => setComposing(false) }), _jsx(Tooltip, { title: "G\u1EEDi tin nh\u1EAFn", children: _jsx(Button, { type: "primary", shape: "circle", size: "middle", icon: _jsx(Icon, { icon: "send-b", color: "white", size: 16 }), disabled: textMessage.length === 0, onClick: onSendTextMessage }) })] }) }) })] }));
90
72
  };
91
73
  export default MessageList;
@@ -0,0 +1,3 @@
1
+ declare const FooterBottomSection: () => import("react/jsx-runtime").JSX.Element;
2
+ export default FooterBottomSection;
3
+ //# sourceMappingURL=BottomSection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BottomSection.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/BottomSection.tsx"],"names":[],"mappings":"AAEA,QAAA,MAAM,mBAAmB,+CAMxB,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
@@ -0,0 +1,6 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ const FooterBottomSection = () => {
4
+ return (_jsx("div", { className: "flex items-center gap-2", children: _jsx("div", {}) }));
5
+ };
6
+ export default FooterBottomSection;
@@ -0,0 +1,3 @@
1
+ declare const MessageFooter: () => import("react/jsx-runtime").JSX.Element;
2
+ export default MessageFooter;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/index.tsx"],"names":[],"mappings":"AAwBA,QAAA,MAAM,aAAa,+CAsBlB,CAAC;AACF,eAAe,aAAa,CAAC"}