@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.
Files changed (72) hide show
  1. package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
  2. package/dist/components/conversation/DeskConversationList.js +15 -10
  3. package/dist/components/message/MessageHeader.d.ts +8 -0
  4. package/dist/components/message/MessageHeader.d.ts.map +1 -0
  5. package/dist/components/message/MessageHeader.js +9 -0
  6. package/dist/components/message/MessageList.d.ts +1 -1
  7. package/dist/components/message/MessageList.d.ts.map +1 -1
  8. package/dist/components/message/MessageList.js +41 -113
  9. package/dist/components/message/footer/ActionBar.d.ts +3 -0
  10. package/dist/components/message/footer/ActionBar.d.ts.map +1 -0
  11. package/dist/components/message/footer/ActionBar.js +143 -0
  12. package/dist/components/message/footer/EmojiPicker.d.ts +7 -0
  13. package/dist/components/message/footer/EmojiPicker.d.ts.map +1 -0
  14. package/dist/components/message/footer/EmojiPicker.js +151 -0
  15. package/dist/components/message/footer/EnterHandler.d.ts +2 -0
  16. package/dist/components/message/footer/EnterHandler.d.ts.map +1 -0
  17. package/dist/components/message/footer/EnterHandler.js +106 -0
  18. package/dist/components/message/footer/FilePreview.d.ts +10 -0
  19. package/dist/components/message/footer/FilePreview.d.ts.map +1 -0
  20. package/dist/components/message/footer/FilePreview.js +48 -0
  21. package/dist/components/message/footer/ToolbarPlugin.d.ts +2 -0
  22. package/dist/components/message/footer/ToolbarPlugin.d.ts.map +1 -0
  23. package/dist/components/message/footer/ToolbarPlugin.js +169 -0
  24. package/dist/components/message/footer/index.d.ts +10 -0
  25. package/dist/components/message/footer/index.d.ts.map +1 -0
  26. package/dist/components/message/footer/index.js +65 -0
  27. package/dist/components/message/item/FileMessage.d.ts +7 -0
  28. package/dist/components/message/item/FileMessage.d.ts.map +1 -0
  29. package/dist/components/message/item/FileMessage.js +27 -0
  30. package/dist/components/message/item/ImageMessage.d.ts +7 -0
  31. package/dist/components/message/item/ImageMessage.d.ts.map +1 -0
  32. package/dist/components/message/item/ImageMessage.js +23 -0
  33. package/dist/components/message/item/TextMessage.d.ts +7 -0
  34. package/dist/components/message/item/TextMessage.d.ts.map +1 -0
  35. package/dist/components/message/item/TextMessage.js +21 -0
  36. package/dist/components/message/item/VideoMessage.d.ts +7 -0
  37. package/dist/components/message/item/VideoMessage.d.ts.map +1 -0
  38. package/dist/components/message/item/VideoMessage.js +20 -0
  39. package/dist/components/message/item/index.d.ts +7 -0
  40. package/dist/components/message/item/index.d.ts.map +1 -0
  41. package/dist/components/message/item/index.js +41 -0
  42. package/dist/context/ChatContext.d.ts.map +1 -1
  43. package/dist/context/ChatContext.js +14 -9
  44. package/dist/hooks/conversation/useConversation.d.ts +1 -0
  45. package/dist/hooks/conversation/useConversation.d.ts.map +1 -1
  46. package/dist/hooks/conversation/useConversation.js +24 -8
  47. package/dist/hooks/conversation/useConversationStore.d.ts +12 -0
  48. package/dist/hooks/conversation/useConversationStore.d.ts.map +1 -0
  49. package/dist/hooks/conversation/useConversationStore.js +10 -0
  50. package/dist/hooks/global/useGlobalEvent.d.ts +2 -0
  51. package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -0
  52. package/dist/hooks/global/useGlobalEvent.js +78 -0
  53. package/dist/hooks/message/useMessage.d.ts +17 -8
  54. package/dist/hooks/message/useMessage.d.ts.map +1 -1
  55. package/dist/hooks/message/useMessage.js +95 -49
  56. package/dist/hooks/message/useSendMessage.d.ts +22 -10
  57. package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
  58. package/dist/hooks/message/useSendMessage.js +214 -20
  59. package/dist/layout/index.d.ts +7 -0
  60. package/dist/layout/index.d.ts.map +1 -0
  61. package/dist/layout/index.js +11 -0
  62. package/dist/screens/deskMessage/index.d.ts.map +1 -1
  63. package/dist/screens/deskMessage/index.js +4 -10
  64. package/dist/styles/global.css +1 -1
  65. package/dist/tsconfig.tsbuildinfo +1 -0
  66. package/dist/types/chat.d.ts +43 -2
  67. package/dist/types/chat.d.ts.map +1 -1
  68. package/dist/types/chat.js +8 -0
  69. package/dist/utils/events.d.ts +13 -0
  70. package/dist/utils/events.d.ts.map +1 -0
  71. package/dist/utils/events.js +4 -0
  72. package/package.json +20 -6
@@ -1 +1 @@
1
- {"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AAwGA,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,4CAiL3B,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,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 useMessageStore from "../../hooks/zustand/useMessageStore";
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 selectedThreadId = useMessageStore((state) => state.selectedThreadId);
78
- const setSelectedThreadId = useMessageStore((state) => state.setSelectedThreadId);
79
- const setSelectedSourceId = useMessageStore((state) => state.setSelectedSourceId);
80
- const { markConversationMessageAsRead } = useMessage(selectedThreadId);
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, markConversationMessageAsRead]);
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,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;AAkBjC,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,4CA0O3C,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;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 { Avatar, Button, Input, Tooltip } from "antd";
18
- import { Icon } from "../icon";
19
- import { useCallback, useEffect, useRef, useState } from "react";
20
- import clsx from "clsx";
21
- import { useSendMessage, } from "../../hooks/message/useSendMessage";
22
- import InfiniteLoader from "react-window-infinite-loader";
23
- import { VariableSizeList as List, } from "react-window";
24
- import AutoSizer from "react-virtualized-auto-sizer";
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 { conversationId, conversationData, onClose } = props;
28
- const { messageList, groupMessages, refetch } = useMessage(conversationId);
16
+ const { conversationData, onClose, conversationId } = props;
29
17
  const scrollRef = useRef(null);
30
- const shouldScrollToBottomRef = useRef(true);
31
- const lastMessageCountRef = useRef((messageList === null || messageList === void 0 ? void 0 : messageList.length) || 0);
32
- const { user } = useChatContext();
33
- const { sendTextMessage } = useSendMessage({
34
- recvID: (conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationType) !== SessionType.Single
35
- ? ""
36
- : (conversationData === null || conversationData === void 0 ? void 0 : conversationData.userID) || "",
37
- groupID: (conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationType) === SessionType.Single
38
- ? ""
39
- : (conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID) || "",
40
- });
41
- const [textMessage, setTextMessage] = useState("");
42
- const [composing, setComposing] = useState(false);
43
- const [showScrollToBottomButton, setShowScrollToBottomButton] = useState(false);
44
- const scrollToBottom = useCallback((force = false) => {
45
- if (scrollRef.current) {
46
- scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
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
- const { scrollTop, scrollHeight, clientHeight } = scrollRef.current;
56
- const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
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
- const currentMessageCount = (messageList === null || messageList === void 0 ? void 0 : messageList.length) || 0;
92
- const previousMessageCount = lastMessageCountRef.current;
93
- if (currentMessageCount > previousMessageCount) {
94
- const newMessages = messageList === null || messageList === void 0 ? void 0 : messageList.slice(previousMessageCount);
95
- const hasNewMessageFromCurrentUser = newMessages === null || newMessages === void 0 ? void 0 : newMessages.some((msg) => msg.sendID === (user === null || user === void 0 ? void 0 : user.userID));
96
- // If current user sent a message, always scroll to bottom
97
- // If another user sent a message, only scroll if shouldScrollToBottomRef is true (user is already at bottom)
98
- if (hasNewMessageFromCurrentUser) {
99
- setTimeout(() => scrollToBottom(true), 50); // Force scroll for own messages
100
- }
101
- else if (shouldScrollToBottomRef.current) {
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,3 @@
1
+ declare const ActionBar: () => import("react/jsx-runtime").JSX.Element;
2
+ export default ActionBar;
3
+ //# sourceMappingURL=ActionBar.d.ts.map
@@ -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,2 @@
1
+ export default function EnterHandler(): null;
2
+ //# sourceMappingURL=EnterHandler.d.ts.map
@@ -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"}