@droppii-org/chat-sdk 0.0.20 โ 0.0.22
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.
- package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
- package/dist/components/conversation/DeskConversationList.js +15 -10
- package/dist/components/message/MessageHeader.d.ts +8 -0
- package/dist/components/message/MessageHeader.d.ts.map +1 -0
- package/dist/components/message/MessageHeader.js +9 -0
- package/dist/components/message/MessageList.d.ts +1 -1
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +41 -113
- package/dist/components/message/footer/ActionBar.d.ts +3 -0
- package/dist/components/message/footer/ActionBar.d.ts.map +1 -0
- package/dist/components/message/footer/ActionBar.js +143 -0
- package/dist/components/message/footer/EmojiPicker.d.ts +7 -0
- package/dist/components/message/footer/EmojiPicker.d.ts.map +1 -0
- package/dist/components/message/footer/EmojiPicker.js +151 -0
- package/dist/components/message/footer/EnterHandler.d.ts +2 -0
- package/dist/components/message/footer/EnterHandler.d.ts.map +1 -0
- package/dist/components/message/footer/EnterHandler.js +106 -0
- package/dist/components/message/footer/FilePreview.d.ts +10 -0
- package/dist/components/message/footer/FilePreview.d.ts.map +1 -0
- package/dist/components/message/footer/FilePreview.js +48 -0
- package/dist/components/message/footer/ToolbarPlugin.d.ts +2 -0
- package/dist/components/message/footer/ToolbarPlugin.d.ts.map +1 -0
- package/dist/components/message/footer/ToolbarPlugin.js +169 -0
- package/dist/components/message/footer/index.d.ts +10 -0
- package/dist/components/message/footer/index.d.ts.map +1 -0
- package/dist/components/message/footer/index.js +65 -0
- package/dist/components/message/item/FileMessage.d.ts +7 -0
- package/dist/components/message/item/FileMessage.d.ts.map +1 -0
- package/dist/components/message/item/FileMessage.js +27 -0
- package/dist/components/message/item/ImageMessage.d.ts +7 -0
- package/dist/components/message/item/ImageMessage.d.ts.map +1 -0
- package/dist/components/message/item/ImageMessage.js +23 -0
- package/dist/components/message/item/TextMessage.d.ts +7 -0
- package/dist/components/message/item/TextMessage.d.ts.map +1 -0
- package/dist/components/message/item/TextMessage.js +21 -0
- package/dist/components/message/item/VideoMessage.d.ts +7 -0
- package/dist/components/message/item/VideoMessage.d.ts.map +1 -0
- package/dist/components/message/item/VideoMessage.js +20 -0
- package/dist/components/message/item/index.d.ts +7 -0
- package/dist/components/message/item/index.d.ts.map +1 -0
- package/dist/components/message/item/index.js +41 -0
- package/dist/context/ChatContext.d.ts.map +1 -1
- package/dist/context/ChatContext.js +14 -9
- package/dist/hooks/conversation/useConversation.d.ts +1 -0
- package/dist/hooks/conversation/useConversation.d.ts.map +1 -1
- package/dist/hooks/conversation/useConversation.js +24 -8
- package/dist/hooks/conversation/useConversationStore.d.ts +12 -0
- package/dist/hooks/conversation/useConversationStore.d.ts.map +1 -0
- package/dist/hooks/conversation/useConversationStore.js +10 -0
- package/dist/hooks/global/useGlobalEvent.d.ts +2 -0
- package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -0
- package/dist/hooks/global/useGlobalEvent.js +78 -0
- package/dist/hooks/message/useMessage.d.ts +17 -8
- package/dist/hooks/message/useMessage.d.ts.map +1 -1
- package/dist/hooks/message/useMessage.js +95 -49
- package/dist/hooks/message/useSendMessage.d.ts +22 -10
- package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
- package/dist/hooks/message/useSendMessage.js +214 -20
- package/dist/layout/index.d.ts +7 -0
- package/dist/layout/index.d.ts.map +1 -0
- package/dist/layout/index.js +11 -0
- package/dist/screens/deskMessage/index.d.ts.map +1 -1
- package/dist/screens/deskMessage/index.js +4 -10
- package/dist/styles/global.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/chat.d.ts +43 -2
- package/dist/types/chat.d.ts.map +1 -1
- package/dist/types/chat.js +8 -0
- package/dist/utils/events.d.ts +13 -0
- package/dist/utils/events.d.ts.map +1 -0
- package/dist/utils/events.js +4 -0
- package/package.json +20 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"
|
|
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,4CA8L3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
|
@@ -7,8 +7,7 @@ import { SessionType } from "@openim/wasm-client-sdk";
|
|
|
7
7
|
import { useConversationList } from "../../hooks/conversation/useConversation";
|
|
8
8
|
import { Icon } from "../icon";
|
|
9
9
|
import { useChatContext } from "../../context/ChatContext";
|
|
10
|
-
import
|
|
11
|
-
import { useMessage } from "../../hooks/message/useMessage";
|
|
10
|
+
import useConversationStore from "../../hooks/conversation/useConversationStore";
|
|
12
11
|
const parseLatestMessage = (latestMsg, currentUserId) => {
|
|
13
12
|
var _a;
|
|
14
13
|
if (!latestMsg)
|
|
@@ -74,16 +73,17 @@ const DeskConversationList = ({ onConversationSelect, className = "", }) => {
|
|
|
74
73
|
const pathname = usePathname();
|
|
75
74
|
const searchParams = useSearchParams();
|
|
76
75
|
const { user } = useChatContext();
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const { conversationList } = useConversationList(selectedThreadId);
|
|
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);
|
|
82
81
|
// Transform real conversation data from the API
|
|
83
82
|
const conversations = transformConversationData(conversationList || [], user === null || user === void 0 ? void 0 : user.userID);
|
|
84
83
|
const filteredConversations = conversations.filter((conv) => conv.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
85
84
|
conv.lastMessage.toLowerCase().includes(searchQuery.toLowerCase()));
|
|
86
85
|
const handleConversationClick = (conversation) => {
|
|
86
|
+
setConversationData(conversation);
|
|
87
87
|
const newSearchParams = new URLSearchParams(searchParams);
|
|
88
88
|
newSearchParams.set("threadId", conversation.id);
|
|
89
89
|
router.push(`${pathname}?${newSearchParams.toString()}`);
|
|
@@ -104,9 +104,14 @@ const DeskConversationList = ({ onConversationSelect, className = "", }) => {
|
|
|
104
104
|
const threadId = searchParams.get("threadId");
|
|
105
105
|
if (threadId) {
|
|
106
106
|
setSelectedThreadId(threadId);
|
|
107
|
+
const selectedConversation = conversations.find((conv) => conv.id === threadId);
|
|
108
|
+
if (selectedConversation) {
|
|
109
|
+
setConversationData(selectedConversation);
|
|
110
|
+
}
|
|
107
111
|
}
|
|
108
112
|
else if (conversations.length > 0) {
|
|
109
113
|
setSelectedThreadId(conversations[0].id);
|
|
114
|
+
setConversationData(conversations[0]);
|
|
110
115
|
const newSearchParams = new URLSearchParams(searchParams);
|
|
111
116
|
newSearchParams.set("threadId", conversations[0].id);
|
|
112
117
|
router.replace(`${pathname}?${newSearchParams.toString()}`);
|
|
@@ -114,11 +119,11 @@ const DeskConversationList = ({ onConversationSelect, className = "", }) => {
|
|
|
114
119
|
}, [searchParams, conversations.length]);
|
|
115
120
|
useEffect(() => {
|
|
116
121
|
if (!!selectedThreadId) {
|
|
117
|
-
markConversationMessageAsRead();
|
|
122
|
+
markConversationMessageAsRead(selectedThreadId);
|
|
118
123
|
onSetSelectedSourceId();
|
|
119
124
|
}
|
|
120
|
-
}, [selectedThreadId, onSetSelectedSourceId
|
|
121
|
-
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: _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
|
|
125
|
+
}, [selectedThreadId, onSetSelectedSourceId]);
|
|
126
|
+
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
|
|
122
127
|
? "bg-blue-50"
|
|
123
128
|
: "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
129
|
? "Thแปญ tรฌm kiแบฟm vแปi tแปซ khรณa khรกc"
|
|
@@ -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;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAc3D,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,4CAkE3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -1,123 +1,51 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
-
var t = {};
|
|
4
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
-
t[p] = s[p];
|
|
6
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
-
t[p[i]] = s[p[i]];
|
|
10
|
-
}
|
|
11
|
-
return t;
|
|
12
|
-
};
|
|
13
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
-
import { SessionType, } from "@openim/wasm-client-sdk";
|
|
15
|
-
import { useChatContext } from "../../context/ChatContext";
|
|
16
3
|
import { useMessage } from "../../hooks/message/useMessage";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import
|
|
22
|
-
import
|
|
23
|
-
import
|
|
24
|
-
import
|
|
4
|
+
import { Spin } from "antd";
|
|
5
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
6
|
+
import dayjs from "dayjs";
|
|
7
|
+
import isToday from "dayjs/plugin/isToday";
|
|
8
|
+
import emitter from "../../utils/events";
|
|
9
|
+
import MessageItem from "./item";
|
|
10
|
+
import InfiniteScroll from "react-infinite-scroll-component";
|
|
11
|
+
import MessageHeader from "./MessageHeader";
|
|
12
|
+
import MessageFooter from "./footer";
|
|
13
|
+
dayjs.extend(isToday);
|
|
25
14
|
const MessageList = (props) => {
|
|
26
15
|
var _a, _b;
|
|
27
|
-
const {
|
|
28
|
-
const { messageList, groupMessages, refetch } = useMessage(conversationId);
|
|
16
|
+
const { conversationData, onClose, conversationId } = props;
|
|
29
17
|
const scrollRef = useRef(null);
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (force) {
|
|
48
|
-
shouldScrollToBottomRef.current = true; // If forced, ensure auto-scroll is re-enabled
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}, []);
|
|
52
|
-
const handleScroll = useCallback(() => {
|
|
53
|
-
if (!scrollRef.current)
|
|
18
|
+
const { getMoreOldMessages, moreOldLoading, loadState, latestLoadState } = useMessage(conversationId);
|
|
19
|
+
const lastMessage = useMemo(() => {
|
|
20
|
+
var _a;
|
|
21
|
+
const messageList = (_a = latestLoadState.current) === null || _a === void 0 ? void 0 : _a.messageList;
|
|
22
|
+
return messageList === null || messageList === void 0 ? void 0 : messageList[(messageList === null || messageList === void 0 ? void 0 : messageList.length) - 1];
|
|
23
|
+
}, [latestLoadState]);
|
|
24
|
+
const scrollToBottom = () => {
|
|
25
|
+
setTimeout(() => {
|
|
26
|
+
var _a, _b;
|
|
27
|
+
(_a = scrollRef.current) === null || _a === void 0 ? void 0 : _a.scrollTo({
|
|
28
|
+
top: (_b = scrollRef.current) === null || _b === void 0 ? void 0 : _b.scrollHeight,
|
|
29
|
+
behavior: "smooth",
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
const loadMoreMessage = () => {
|
|
34
|
+
if (!loadState.hasMoreOld || moreOldLoading)
|
|
54
35
|
return;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const SCROLL_UP_THRESHOLD = 200; // If user scrolls up more than 200px from bottom, disable auto-scroll
|
|
58
|
-
const SCROLL_DOWN_THRESHOLD = 5; // If user scrolls within 5px of bottom, re-enable auto-scroll
|
|
59
|
-
if (distanceFromBottom > SCROLL_UP_THRESHOLD) {
|
|
60
|
-
shouldScrollToBottomRef.current = false;
|
|
61
|
-
}
|
|
62
|
-
else if (distanceFromBottom <= SCROLL_DOWN_THRESHOLD) {
|
|
63
|
-
shouldScrollToBottomRef.current = true;
|
|
64
|
-
}
|
|
65
|
-
// Show button if not at bottom AND auto-scroll is disabled
|
|
66
|
-
setShowScrollToBottomButton(distanceFromBottom > SCROLL_DOWN_THRESHOLD &&
|
|
67
|
-
!shouldScrollToBottomRef.current);
|
|
68
|
-
}, []);
|
|
69
|
-
const onSendTextMessage = useCallback(async () => {
|
|
70
|
-
setTextMessage("");
|
|
71
|
-
const lastMessage = messageList === null || messageList === void 0 ? void 0 : messageList[(messageList === null || messageList === void 0 ? void 0 : messageList.length) - 1];
|
|
72
|
-
const res = await sendTextMessage(textMessage, lastMessage);
|
|
73
|
-
if (res) {
|
|
74
|
-
refetch();
|
|
75
|
-
}
|
|
76
|
-
}, [textMessage, sendTextMessage, refetch, messageList]);
|
|
77
|
-
const renderMessageItem = useCallback((listItemProps) => {
|
|
78
|
-
const { index } = listItemProps;
|
|
79
|
-
const groupMessage = groupMessages === null || groupMessages === void 0 ? void 0 : groupMessages[index];
|
|
80
|
-
const messagesInGroup = (groupMessage === null || groupMessage === void 0 ? void 0 : groupMessage.messages) || [];
|
|
81
|
-
return (_jsxs("div", { children: [_jsx("div", { children: groupMessage === null || groupMessage === void 0 ? void 0 : groupMessage.sendTime }), messagesInGroup === null || messagesInGroup === void 0 ? void 0 : messagesInGroup.map((message) => {
|
|
82
|
-
var _a, _b, _c;
|
|
83
|
-
const isMine = (message === null || message === void 0 ? void 0 : message.sendID) === (user === null || user === void 0 ? void 0 : user.userID);
|
|
84
|
-
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
|
|
85
|
-
? "bg-blue-500 text-white"
|
|
86
|
-
: "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) ||
|
|
87
|
-
"Tin nhแบฏn khรดng khแบฃ dแปฅng" }) })] })] }) }, message === null || message === void 0 ? void 0 : message.clientMsgID));
|
|
88
|
-
})] }));
|
|
89
|
-
}, [user === null || user === void 0 ? void 0 : user.userID, groupMessages]);
|
|
36
|
+
getMoreOldMessages();
|
|
37
|
+
};
|
|
90
38
|
useEffect(() => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
setTimeout(() => scrollToBottom(), 50); // Scroll if auto-scroll is enabled for others' messages
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
lastMessageCountRef.current = currentMessageCount;
|
|
106
|
-
}, [messageList, user === null || user === void 0 ? void 0 : user.userID, scrollToBottom]);
|
|
107
|
-
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", children: _jsx(AutoSizer, { children: ({ height, width }) => (_jsx(InfiniteLoader, { isItemLoaded: () => true, loadMoreItems: () => { }, itemCount: (groupMessages === null || groupMessages === void 0 ? void 0 : groupMessages.length) || 0, children: (_a) => {
|
|
108
|
-
var { onItemsRendered, ref } = _a, rest = __rest(_a, ["onItemsRendered", "ref"]);
|
|
109
|
-
return (_jsx(List, Object.assign({ ref: ref }, rest, { itemCount: (groupMessages === null || groupMessages === void 0 ? void 0 : groupMessages.length) || 0, onItemsRendered: onItemsRendered, height: height, width: width, itemSize: (index) => {
|
|
110
|
-
const groupMessage = groupMessages === null || groupMessages === void 0 ? void 0 : groupMessages[index];
|
|
111
|
-
const messagesInGroup = (groupMessage === null || groupMessage === void 0 ? void 0 : groupMessage.messages) || [];
|
|
112
|
-
return (messagesInGroup === null || messagesInGroup === void 0 ? void 0 : messagesInGroup.length) * 50;
|
|
113
|
-
}, children: renderMessageItem })));
|
|
114
|
-
} })) }) }) }), _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) => {
|
|
115
|
-
if (composing) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
if (e.key === "Enter") {
|
|
119
|
-
onSendTextMessage();
|
|
120
|
-
}
|
|
121
|
-
}, 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 }) })] }) }) })] }));
|
|
39
|
+
emitter.on("CHAT_LIST_SCROLL_TO_BOTTOM", scrollToBottom);
|
|
40
|
+
return () => {
|
|
41
|
+
emitter.off("CHAT_LIST_SCROLL_TO_BOTTOM", scrollToBottom);
|
|
42
|
+
};
|
|
43
|
+
}, []);
|
|
44
|
+
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: {
|
|
45
|
+
height: "100%",
|
|
46
|
+
overflow: "auto",
|
|
47
|
+
display: "flex",
|
|
48
|
+
flexDirection: "column-reverse",
|
|
49
|
+
}, 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(MessageFooter, { lastMessage: lastMessage })] }));
|
|
122
50
|
};
|
|
123
51
|
export default MessageList;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionBar.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ActionBar.tsx"],"names":[],"mappings":"AAgEA,QAAA,MAAM,SAAS,+CA+Nd,CAAC;AAEF,eAAe,SAAS,CAAC"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
+
import { Button, Upload, message, } from "antd";
|
|
5
|
+
import clsx from "clsx";
|
|
6
|
+
import EmojiPicker from "./EmojiPicker";
|
|
7
|
+
import { Icon } from "../../icon";
|
|
8
|
+
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
9
|
+
import { $createParagraphNode, $getRoot, $getSelection, $isRangeSelection, } from "lexical";
|
|
10
|
+
import { $generateHtmlFromNodes } from "@lexical/html";
|
|
11
|
+
import { $isListNode, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, } from "@lexical/list";
|
|
12
|
+
import { $isQuoteNode } from "@lexical/rich-text";
|
|
13
|
+
import { useMessageFooterContext } from ".";
|
|
14
|
+
const documentTypes = [
|
|
15
|
+
"application/pdf",
|
|
16
|
+
"application/msword",
|
|
17
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
18
|
+
];
|
|
19
|
+
const EmojiIcon = (_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("path", { d: "M14 8.39997C14 8.8418 13.6418 9.19997 13.2 9.19997C12.7581 9.19997 12.4 8.8418 12.4 8.39997C12.4 7.95814 12.7581 7.59997 13.2 7.59997C13.6418 7.59997 14 7.95814 14 8.39997Z", fill: "white" }), _jsx("path", { d: "M7.59997 8.39997C7.59997 8.8418 7.2418 9.19997 6.79997 9.19997C6.35814 9.19997 5.99997 8.8418 5.99997 8.39997C5.99997 7.95814 6.35814 7.59997 6.79997 7.59997C7.2418 7.59997 7.59997 7.95814 7.59997 8.39997Z", fill: "white" }), _jsx("path", { d: "M7.59997 12.4C7.59997 12.4 8.49997 13.2 9.99997 13.2C11.5 13.2 12.4 12.4 12.4 12.4M14 8.39997C14 8.8418 13.6418 9.19997 13.2 9.19997C12.7581 9.19997 12.4 8.8418 12.4 8.39997C12.4 7.95814 12.7581 7.59997 13.2 7.59997C13.6418 7.59997 14 7.95814 14 8.39997ZM18 9.99997C18 14.4182 14.4182 18 9.99997 18C5.58169 18 1.99997 14.4182 1.99997 9.99997C1.99997 5.58169 5.58169 1.99997 9.99997 1.99997C14.4182 1.99997 18 5.58169 18 9.99997ZM7.59997 8.39997C7.59997 8.8418 7.2418 9.19997 6.79997 9.19997C6.35814 9.19997 5.99997 8.8418 5.99997 8.39997C5.99997 7.95814 6.35814 7.59997 6.79997 7.59997C7.2418 7.59997 7.59997 7.95814 7.59997 8.39997Z", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" })] }));
|
|
20
|
+
const ActionBar = () => {
|
|
21
|
+
const [editor] = useLexicalComposerContext();
|
|
22
|
+
const { onSendMessage, setListUploadFiles } = useMessageFooterContext();
|
|
23
|
+
const containerRef = useRef(null);
|
|
24
|
+
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
|
25
|
+
const { listUploadFiles } = useMessageFooterContext();
|
|
26
|
+
const handleSend = useCallback(() => {
|
|
27
|
+
let plainText = "";
|
|
28
|
+
let richText = "";
|
|
29
|
+
// lแบฅy plain text & html
|
|
30
|
+
editor.getEditorState().read(() => {
|
|
31
|
+
plainText = $getRoot().getTextContent();
|
|
32
|
+
richText = $generateHtmlFromNodes(editor);
|
|
33
|
+
});
|
|
34
|
+
if (plainText.trim().length > 0 || listUploadFiles.length > 0) {
|
|
35
|
+
onSendMessage({
|
|
36
|
+
plainText,
|
|
37
|
+
richText,
|
|
38
|
+
type: listUploadFiles.length > 0 ? "file" : "text",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
editor.update(() => {
|
|
42
|
+
const root = $getRoot();
|
|
43
|
+
root.clear();
|
|
44
|
+
const paragraph = $createParagraphNode();
|
|
45
|
+
root.append(paragraph);
|
|
46
|
+
paragraph.select();
|
|
47
|
+
const selection = $getSelection();
|
|
48
|
+
if ($isRangeSelection(selection)) {
|
|
49
|
+
const anchorNode = selection.anchor.getNode();
|
|
50
|
+
const topLevelNode = anchorNode.getTopLevelElementOrThrow();
|
|
51
|
+
const isListNode = $isListNode(topLevelNode);
|
|
52
|
+
const isQuoteNode = $isQuoteNode(topLevelNode);
|
|
53
|
+
const listType = isListNode ? topLevelNode.getListType() : undefined;
|
|
54
|
+
if (selection.hasFormat("bold")) {
|
|
55
|
+
selection.formatText("bold");
|
|
56
|
+
}
|
|
57
|
+
if (selection.hasFormat("italic")) {
|
|
58
|
+
selection.formatText("italic");
|
|
59
|
+
}
|
|
60
|
+
if (selection.hasFormat("strikethrough")) {
|
|
61
|
+
selection.formatText("strikethrough");
|
|
62
|
+
}
|
|
63
|
+
if (isQuoteNode) {
|
|
64
|
+
// Nแบฟu ฤang lร QuoteNode โ chuyแปn vแป Paragraph (bรฌnh thฦฐแปng)
|
|
65
|
+
const paragraph = $createParagraphNode();
|
|
66
|
+
// copy con cแปงa quote sang paragraph
|
|
67
|
+
const children = topLevelNode.getChildren();
|
|
68
|
+
children.forEach((child) => paragraph.append(child));
|
|
69
|
+
topLevelNode.replace(paragraph);
|
|
70
|
+
}
|
|
71
|
+
if (isListNode && listType) {
|
|
72
|
+
editor.dispatchCommand(listType === "number"
|
|
73
|
+
? INSERT_ORDERED_LIST_COMMAND
|
|
74
|
+
: INSERT_UNORDERED_LIST_COMMAND, undefined);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}, [editor, onSendMessage]);
|
|
79
|
+
const handleEmojiSelect = useCallback((emoji) => {
|
|
80
|
+
editor.update(() => {
|
|
81
|
+
const selection = $getSelection();
|
|
82
|
+
if ($isRangeSelection(selection)) {
|
|
83
|
+
selection.insertText(emoji); // chรจn emoji nhฦฐ text bรฌnh thฦฐแปng
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
setShowEmojiPicker(false);
|
|
87
|
+
}, [editor]);
|
|
88
|
+
const beforeUploadImagesAndVideo = (file, fileList) => {
|
|
89
|
+
const isImage = file.type === "image/jpeg" ||
|
|
90
|
+
file.type === "image/png" ||
|
|
91
|
+
file.type === "image/jpg";
|
|
92
|
+
const isVideo = file.type.startsWith("video/");
|
|
93
|
+
// check format
|
|
94
|
+
if (!isImage && !isVideo) {
|
|
95
|
+
message.error(`${file.name} khรดng ฤรบng ฤแปnh dแบกng JPG, JPEG, PNG hoแบทc VIDEO`);
|
|
96
|
+
return Upload.LIST_IGNORE;
|
|
97
|
+
}
|
|
98
|
+
// check size
|
|
99
|
+
const maxSize = isImage ? 5 : 200; // MB
|
|
100
|
+
if (file.size / 1024 / 1024 > maxSize) {
|
|
101
|
+
message.error(`${file.name} cรณ kรญch thฦฐแปc tแบญp tin vฦฐแปฃt quรก ${maxSize}MB`);
|
|
102
|
+
return Upload.LIST_IGNORE;
|
|
103
|
+
}
|
|
104
|
+
// nแบฟu lร video thรฌ chแป cho 1 cรกi duy nhแบฅt
|
|
105
|
+
if (isVideo) {
|
|
106
|
+
const hasVideo = listUploadFiles.some((f) => { var _a; return (_a = f.type) === null || _a === void 0 ? void 0 : _a.startsWith("video/"); });
|
|
107
|
+
if (hasVideo) {
|
|
108
|
+
message.error("Chแป ฤฦฐแปฃc phรฉp tแบฃi lรชn 1 video duy nhแบฅt");
|
|
109
|
+
return Upload.LIST_IGNORE;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return true;
|
|
113
|
+
};
|
|
114
|
+
const beforeUploadFile = (file) => {
|
|
115
|
+
const isAllowed = documentTypes.includes(file.type);
|
|
116
|
+
if (!isAllowed) {
|
|
117
|
+
message.error(`${file.name} khรดng ฤรบng ฤแปnh dแบกng (chแป hแป trแปฃ PDF, DOC, DOCX)`);
|
|
118
|
+
}
|
|
119
|
+
return isAllowed;
|
|
120
|
+
};
|
|
121
|
+
const handleChange = (info) => {
|
|
122
|
+
let newList = [...info.fileList];
|
|
123
|
+
// Nแบฟu file mแปi lร tร i liแปu -> chแป giแปฏ 1 cรกi (file cuแปi)
|
|
124
|
+
const lastFile = info.file;
|
|
125
|
+
if (documentTypes.includes(lastFile.type || "")) {
|
|
126
|
+
newList = newList.filter((f) => documentTypes.includes(f.type || "") === false); // remove doc cลฉ
|
|
127
|
+
newList.push(lastFile); // add doc mแปi
|
|
128
|
+
}
|
|
129
|
+
setListUploadFiles(newList);
|
|
130
|
+
};
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
const handleClickOutside = (event) => {
|
|
133
|
+
if (containerRef.current &&
|
|
134
|
+
!containerRef.current.contains(event.target)) {
|
|
135
|
+
setShowEmojiPicker(false);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
139
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
140
|
+
}, []);
|
|
141
|
+
return (_jsxs("div", { className: "flex items-center justify-between px-4", ref: containerRef, children: [_jsxs("div", { className: "flex items-center gap-3 relative", children: [_jsx(Button, { type: "text", shape: "default", className: clsx("text-gray-500 w-8 h-8 p-0", showEmojiPicker ? "bg-blue-100 text-blue-600" : "text-gray-500"), onClick: () => setShowEmojiPicker(!showEmojiPicker), children: EmojiIcon }), _jsx(Upload, { accept: "image/jpeg, image/png, image/jpg, video/*", beforeUpload: beforeUploadImagesAndVideo, multiple: true, onChange: handleChange, showUploadList: false, fileList: listUploadFiles, children: _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0 text-gray-500", children: _jsx(Icon, { icon: "image-02-o", size: 22 }) }) }), _jsx(Upload, { accept: ".doc,.docx,.pdf", beforeUpload: beforeUploadFile, onChange: handleChange, showUploadList: false, fileList: listUploadFiles, children: _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0 text-gray-500", children: _jsx(Icon, { icon: "link-o", size: 22 }) }) }), showEmojiPicker && (_jsx(EmojiPicker, { onEmojiSelect: handleEmojiSelect, onClose: () => setShowEmojiPicker(false) }))] }), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: handleSend, children: _jsx(Icon, { icon: "send-b", size: 28, className: "text-blue-500" }) })] }));
|
|
142
|
+
};
|
|
143
|
+
export default ActionBar;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface EmojiPickerProps {
|
|
2
|
+
onEmojiSelect: (emoji: string) => void;
|
|
3
|
+
onClose: () => void;
|
|
4
|
+
}
|
|
5
|
+
declare const EmojiPicker: ({ onEmojiSelect, onClose }: EmojiPickerProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default EmojiPicker;
|
|
7
|
+
//# sourceMappingURL=EmojiPicker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EmojiPicker.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/EmojiPicker.tsx"],"names":[],"mappings":"AAIA,UAAU,gBAAgB;IACxB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,QAAA,MAAM,WAAW,GAAI,4BAA4B,gBAAgB,4CAyLhE,CAAA;AAED,eAAe,WAAW,CAAA"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
const EmojiPicker = ({ onEmojiSelect, onClose }) => {
|
|
5
|
+
const [activeCategory, setActiveCategory] = useState("smileys");
|
|
6
|
+
const emojiCategories = {
|
|
7
|
+
smileys: {
|
|
8
|
+
name: "Smileys & People",
|
|
9
|
+
emojis: [
|
|
10
|
+
"๐",
|
|
11
|
+
"๐",
|
|
12
|
+
"๐",
|
|
13
|
+
"๐",
|
|
14
|
+
"๐",
|
|
15
|
+
"๐
",
|
|
16
|
+
"๐",
|
|
17
|
+
"๐คฃ",
|
|
18
|
+
"๐",
|
|
19
|
+
"๐",
|
|
20
|
+
"๐",
|
|
21
|
+
"๐",
|
|
22
|
+
"๐",
|
|
23
|
+
"๐",
|
|
24
|
+
"๐",
|
|
25
|
+
"๐ฅฐ",
|
|
26
|
+
"๐",
|
|
27
|
+
"๐",
|
|
28
|
+
"๐",
|
|
29
|
+
"๐",
|
|
30
|
+
"๐",
|
|
31
|
+
"๐",
|
|
32
|
+
"๐",
|
|
33
|
+
"๐",
|
|
34
|
+
"๐คช",
|
|
35
|
+
"๐คจ",
|
|
36
|
+
"๐ง",
|
|
37
|
+
"๐ค",
|
|
38
|
+
"๐",
|
|
39
|
+
"๐คฉ",
|
|
40
|
+
"๐ฅณ",
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
nature: {
|
|
44
|
+
name: "Animals & Nature",
|
|
45
|
+
emojis: [
|
|
46
|
+
"๐ถ",
|
|
47
|
+
"๐ฑ",
|
|
48
|
+
"๐ญ",
|
|
49
|
+
"๐น",
|
|
50
|
+
"๐ฐ",
|
|
51
|
+
"๐ฆ",
|
|
52
|
+
"๐ป",
|
|
53
|
+
"๐ผ",
|
|
54
|
+
"๐จ",
|
|
55
|
+
"๐ฏ",
|
|
56
|
+
"๐ฆ",
|
|
57
|
+
"๐ฎ",
|
|
58
|
+
"๐ท",
|
|
59
|
+
"๐ธ",
|
|
60
|
+
"๐ต",
|
|
61
|
+
"๐",
|
|
62
|
+
"๐",
|
|
63
|
+
"๐",
|
|
64
|
+
"๐",
|
|
65
|
+
"๐",
|
|
66
|
+
"๐ง",
|
|
67
|
+
"๐ฆ",
|
|
68
|
+
"๐ค",
|
|
69
|
+
"๐ฃ",
|
|
70
|
+
"๐ฅ",
|
|
71
|
+
"๐ฆ",
|
|
72
|
+
"๐ฆ
",
|
|
73
|
+
"๐ฆ",
|
|
74
|
+
"๐ฆ",
|
|
75
|
+
"๐บ",
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
food: {
|
|
79
|
+
name: "Food & Drink",
|
|
80
|
+
emojis: [
|
|
81
|
+
"๐",
|
|
82
|
+
"๐",
|
|
83
|
+
"๐",
|
|
84
|
+
"๐",
|
|
85
|
+
"๐",
|
|
86
|
+
"๐",
|
|
87
|
+
"๐",
|
|
88
|
+
"๐",
|
|
89
|
+
"๐",
|
|
90
|
+
"๐",
|
|
91
|
+
"๐",
|
|
92
|
+
"๐ฅญ",
|
|
93
|
+
"๐",
|
|
94
|
+
"๐ฅฅ",
|
|
95
|
+
"๐ฅ",
|
|
96
|
+
"๐
",
|
|
97
|
+
"๐",
|
|
98
|
+
"๐ฅ",
|
|
99
|
+
"๐ฅฆ",
|
|
100
|
+
"๐ฅฌ",
|
|
101
|
+
"๐ฅ",
|
|
102
|
+
"๐ถ๏ธ",
|
|
103
|
+
"๐ฝ",
|
|
104
|
+
"๐ฅ",
|
|
105
|
+
"๐ง",
|
|
106
|
+
"๐ง
",
|
|
107
|
+
"๐ฅ",
|
|
108
|
+
"๐ ",
|
|
109
|
+
"๐ฅ",
|
|
110
|
+
"๐",
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
activities: {
|
|
114
|
+
name: "Activities",
|
|
115
|
+
emojis: [
|
|
116
|
+
"โฝ",
|
|
117
|
+
"๐",
|
|
118
|
+
"๐",
|
|
119
|
+
"โพ",
|
|
120
|
+
"๐ฅ",
|
|
121
|
+
"๐พ",
|
|
122
|
+
"๐",
|
|
123
|
+
"๐",
|
|
124
|
+
"๐ฅ",
|
|
125
|
+
"๐ฑ",
|
|
126
|
+
"๐ช",
|
|
127
|
+
"๐",
|
|
128
|
+
"๐ธ",
|
|
129
|
+
"๐",
|
|
130
|
+
"๐",
|
|
131
|
+
"๐ฅ",
|
|
132
|
+
"๐",
|
|
133
|
+
"๐ช",
|
|
134
|
+
"๐ฅ
",
|
|
135
|
+
"โณ",
|
|
136
|
+
"๐ช",
|
|
137
|
+
"๐น",
|
|
138
|
+
"๐ฃ",
|
|
139
|
+
"๐คฟ",
|
|
140
|
+
"๐ฅ",
|
|
141
|
+
"๐ฅ",
|
|
142
|
+
"๐ฝ",
|
|
143
|
+
"๐น",
|
|
144
|
+
"๐ท",
|
|
145
|
+
"โธ๏ธ",
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
return (_jsx("div", { className: "absolute bottom-full left-0 mb-2 bg-white border border-gray-200 rounded-xl shadow-lg w-80 z-50", children: _jsxs("div", { className: "p-3", children: [_jsxs("div", { className: "flex items-center justify-between mb-3", children: [_jsx("h3", { className: "font-medium text-gray-900", children: "Choose an emoji" }), _jsx("button", { onClick: onClose, className: "text-gray-400 hover:text-gray-600 text-xl", children: "\u00D7" })] }), _jsx("div", { className: "flex gap-1 mb-3", children: Object.entries(emojiCategories).map(([key, category]) => (_jsx("button", { onClick: () => setActiveCategory(key), className: `px-3 py-1 text-xs rounded-full transition-colors ${activeCategory === key ? "bg-blue-100 text-blue-600" : "text-gray-500 hover:text-gray-700"}`, children: category.name.split(" ")[0] }, key))) }), _jsx("div", { className: "grid grid-cols-8 gap-1 max-h-48 overflow-y-auto", children: emojiCategories[activeCategory].emojis.map((emoji, index) => (_jsx("button", { onClick: () => onEmojiSelect(emoji), className: "p-2 text-xl hover:bg-gray-100 rounded-lg transition-colors", children: emoji }, index))) })] }) }));
|
|
150
|
+
};
|
|
151
|
+
export default EmojiPicker;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EnterHandler.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/EnterHandler.tsx"],"names":[],"mappings":"AAsBA,MAAM,CAAC,OAAO,UAAU,YAAY,SAqHnC"}
|