@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
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
3
|
+
import { KEY_ENTER_COMMAND, COMMAND_PRIORITY_NORMAL, $getSelection, $isRangeSelection, $createParagraphNode, $createLineBreakNode, $getRoot, } from "lexical";
|
|
4
|
+
import { useEffect, useRef } from "react";
|
|
5
|
+
import { $generateHtmlFromNodes } from "@lexical/html";
|
|
6
|
+
import { $isListNode, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, } from "@lexical/list";
|
|
7
|
+
import { $isQuoteNode } from "@lexical/rich-text";
|
|
8
|
+
import { useMessageFooterContext } from ".";
|
|
9
|
+
export default function EnterHandler() {
|
|
10
|
+
const [editor] = useLexicalComposerContext();
|
|
11
|
+
const { onSendMessage } = useMessageFooterContext();
|
|
12
|
+
const shiftEnterCount = useRef(0);
|
|
13
|
+
const { listUploadFiles } = useMessageFooterContext();
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
return editor.registerCommand(KEY_ENTER_COMMAND, (event) => {
|
|
16
|
+
// Case 1: Enter (submit, chặn xuống dòng)
|
|
17
|
+
if (!event.shiftKey) {
|
|
18
|
+
event.preventDefault();
|
|
19
|
+
shiftEnterCount.current = 0;
|
|
20
|
+
let plainText = "";
|
|
21
|
+
let richText = "";
|
|
22
|
+
// lấy plain text & html
|
|
23
|
+
editor.getEditorState().read(() => {
|
|
24
|
+
plainText = $getRoot().getTextContent();
|
|
25
|
+
richText = $generateHtmlFromNodes(editor);
|
|
26
|
+
});
|
|
27
|
+
if (plainText.trim().length > 0 || listUploadFiles.length > 0) {
|
|
28
|
+
onSendMessage({
|
|
29
|
+
plainText,
|
|
30
|
+
richText,
|
|
31
|
+
type: listUploadFiles.length > 0 ? "file" : "text",
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
editor.update(() => {
|
|
35
|
+
const root = $getRoot();
|
|
36
|
+
root.clear();
|
|
37
|
+
const paragraph = $createParagraphNode();
|
|
38
|
+
root.append(paragraph);
|
|
39
|
+
paragraph.select();
|
|
40
|
+
const selection = $getSelection();
|
|
41
|
+
if ($isRangeSelection(selection)) {
|
|
42
|
+
const anchorNode = selection.anchor.getNode();
|
|
43
|
+
const topLevelNode = anchorNode.getTopLevelElementOrThrow();
|
|
44
|
+
const isListNode = $isListNode(topLevelNode);
|
|
45
|
+
const isQuoteNode = $isQuoteNode(topLevelNode);
|
|
46
|
+
const listType = isListNode
|
|
47
|
+
? topLevelNode.getListType()
|
|
48
|
+
: undefined;
|
|
49
|
+
if (selection.hasFormat("bold")) {
|
|
50
|
+
selection.formatText("bold");
|
|
51
|
+
}
|
|
52
|
+
if (selection.hasFormat("italic")) {
|
|
53
|
+
selection.formatText("italic");
|
|
54
|
+
}
|
|
55
|
+
if (selection.hasFormat("strikethrough")) {
|
|
56
|
+
selection.formatText("strikethrough");
|
|
57
|
+
}
|
|
58
|
+
if (isQuoteNode) {
|
|
59
|
+
// Nếu đang là QuoteNode → chuyển về Paragraph (bình thường)
|
|
60
|
+
const paragraph = $createParagraphNode();
|
|
61
|
+
// copy con của quote sang paragraph
|
|
62
|
+
const children = topLevelNode.getChildren();
|
|
63
|
+
children.forEach((child) => paragraph.append(child));
|
|
64
|
+
topLevelNode.replace(paragraph);
|
|
65
|
+
}
|
|
66
|
+
if (isListNode && listType) {
|
|
67
|
+
editor.dispatchCommand(listType === "number"
|
|
68
|
+
? INSERT_ORDERED_LIST_COMMAND
|
|
69
|
+
: INSERT_UNORDERED_LIST_COMMAND, undefined);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
// Case 2: Shift+Enter
|
|
76
|
+
if (event.shiftKey) {
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
shiftEnterCount.current += 1;
|
|
79
|
+
if (shiftEnterCount.current >= 2) {
|
|
80
|
+
// Tách block mới
|
|
81
|
+
shiftEnterCount.current = 0;
|
|
82
|
+
editor.update(() => {
|
|
83
|
+
const selection = $getSelection();
|
|
84
|
+
if ($isRangeSelection(selection)) {
|
|
85
|
+
const paragraph = $createParagraphNode();
|
|
86
|
+
selection.insertNodes([paragraph]);
|
|
87
|
+
paragraph.select(); // con trỏ nhảy vào block mới
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// Xuống dòng trong block
|
|
93
|
+
editor.update(() => {
|
|
94
|
+
const selection = $getSelection();
|
|
95
|
+
if ($isRangeSelection(selection)) {
|
|
96
|
+
selection.insertNodes([$createLineBreakNode()]);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
104
|
+
}, [editor, onSendMessage, listUploadFiles]);
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const documentIcon: import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
interface ShortenOptions {
|
|
3
|
+
maxLength?: number;
|
|
4
|
+
keepStart?: number;
|
|
5
|
+
keepEnd?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare const shortenFileName: (name: string, options?: ShortenOptions) => string;
|
|
8
|
+
declare const FilePreview: () => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export default FilePreview;
|
|
10
|
+
//# sourceMappingURL=FilePreview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilePreview.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/FilePreview.tsx"],"names":[],"mappings":"AAYA,eAAO,MAAM,YAAY,yCAoCxB,CAAC;AAEF,UAAU,cAAc;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,EAAE,UAAS,cAAmB,WAkBzE,CAAC;AAEF,QAAA,MAAM,WAAW,+CAgFhB,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback } from "react";
|
|
4
|
+
import { useMessageFooterContext } from ".";
|
|
5
|
+
import { Button } from "antd";
|
|
6
|
+
import { Icon } from "../../icon";
|
|
7
|
+
const documentTypes = [
|
|
8
|
+
"application/pdf",
|
|
9
|
+
"application/msword",
|
|
10
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
11
|
+
];
|
|
12
|
+
export const documentIcon = (_jsxs("svg", { width: "40", height: "40", viewBox: "0 0 40 40", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("path", { d: "M10.1923 5.83301L23.4363 5.83301L34.1656 14.2054V29.8639C34.1656 31.8997 32.5286 34.1663 29.8068 34.1663C27.0849 34.1663 10.1923 34.1663 10.1923 34.1663C7.4704 34.1663 5.8335 31.8997 5.8335 29.8639V10.0305C5.8335 7.99475 7.4704 5.83301 10.1923 5.83301Z", fill: "#24B0FF" }), _jsx("path", { d: "M10.8335 28.1394V20.833H16.0309V22.4222H12.7026V23.5916H15.3612V25.1708H12.7026V28.1394H10.8335Z", fill: "#edf6ff" }), _jsx("path", { d: "M19.0073 28.0885V20.833H20.8613V26.5118H24.0146V28.0885L19.0073 28.0885Z", fill: "#edf6ff" }), _jsx("path", { d: "M16.5918 28.1001V20.833H18.4485V28.1001H16.5918Z", fill: "#edf6ff" }), _jsx("path", { d: "M24.5737 20.833V28.1393H29.7723V26.5573H26.439L26.4422 25.1708H29.1017V23.5916H26.4422V22.4222H29.7723V20.833H24.5737Z", fill: "#edf6ff" }), _jsx("path", { opacity: "0.302", "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M23.2686 5.83301V14.2281H34.1655L23.2686 5.83301Z", fill: "#edf6ff" })] }));
|
|
13
|
+
export const shortenFileName = (name, options = {}) => {
|
|
14
|
+
const { maxLength = 20, keepStart = 8, keepEnd = 3 } = options;
|
|
15
|
+
if (name.length <= maxLength)
|
|
16
|
+
return name;
|
|
17
|
+
const dotIndex = name.lastIndexOf(".");
|
|
18
|
+
const ext = dotIndex !== -1 ? name.slice(dotIndex) : "";
|
|
19
|
+
const base = dotIndex !== -1 ? name.slice(0, dotIndex) : name;
|
|
20
|
+
// Tính toán độ dài phần đầu & cuối
|
|
21
|
+
const available = maxLength - ext.length - 3; // trừ "...”
|
|
22
|
+
const startLen = keepStart !== null && keepStart !== void 0 ? keepStart : Math.floor(available / 2);
|
|
23
|
+
const endLen = keepEnd !== null && keepEnd !== void 0 ? keepEnd : Math.floor(available / 2);
|
|
24
|
+
const start = base.slice(0, startLen);
|
|
25
|
+
const end = base.slice(-endLen);
|
|
26
|
+
return `${start}...${end}${ext}`;
|
|
27
|
+
};
|
|
28
|
+
const FilePreview = () => {
|
|
29
|
+
const { listUploadFiles, setListUploadFiles } = useMessageFooterContext();
|
|
30
|
+
const onRemoveFile = (file) => {
|
|
31
|
+
setListUploadFiles(listUploadFiles.filter((f) => f.uid !== file.uid));
|
|
32
|
+
};
|
|
33
|
+
const renderFilePreview = useCallback((file) => {
|
|
34
|
+
var _a;
|
|
35
|
+
const isDocument = documentTypes.includes(file.type || "");
|
|
36
|
+
const isVideo = (_a = file.type) === null || _a === void 0 ? void 0 : _a.startsWith("video/");
|
|
37
|
+
let src = file.url;
|
|
38
|
+
if (!src && file.originFileObj) {
|
|
39
|
+
src = URL.createObjectURL(file.originFileObj);
|
|
40
|
+
}
|
|
41
|
+
if (isDocument) {
|
|
42
|
+
return (_jsxs("div", { className: "relative flex flex-row items-center gap-2 align-center bg-gray-100 rounded-md p-1 pr-2", children: [documentIcon, _jsx("span", { className: "text-xs text-gray-500", children: shortenFileName(file.name) }), _jsx(Button, { className: "absolute top-[-8px] right-[-8px] w-5 h-5 rounded-full p-0 bg-gray-500 hover:bg-gray-600", type: "primary", onClick: () => onRemoveFile(file), children: _jsx(Icon, { icon: "close-b", size: 12, color: "white" }) })] }, file.uid));
|
|
43
|
+
}
|
|
44
|
+
return (_jsxs("div", { className: "relative rounded-md border", children: [isVideo ? (_jsx("video", { src: src, className: "w-[48px] h-[48px] object-cover rounded-lg", autoPlay: false })) : (_jsx("img", { src: src, alt: file.name, className: "w-[48px] h-[48px] object-cover rounded-lg" })), _jsx(Button, { className: "absolute top-[-8px] right-[-8px] w-5 h-5 rounded-full p-0 bg-gray-500 hover:bg-gray-600", type: "primary", onClick: () => onRemoveFile(file), children: _jsx(Icon, { icon: "close-b", size: 12, color: "white" }) }), isVideo && (_jsx(Icon, { className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2", icon: "play-b", size: 20, color: "white" }))] }, file.uid));
|
|
45
|
+
}, [listUploadFiles]);
|
|
46
|
+
return (_jsx("div", { className: "overflow-x-auto", children: _jsx("div", { className: "border-b py-2 px-4", children: _jsx("div", { className: "flex items-center gap-2", children: listUploadFiles.map((file) => renderFilePreview(file)) }) }) }));
|
|
47
|
+
};
|
|
48
|
+
export default FilePreview;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToolbarPlugin.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ToolbarPlugin.tsx"],"names":[],"mappings":"AAwFA,eAAO,MAAM,aAAa,+CAyNzB,CAAC"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
4
|
+
import { $createParagraphNode, $getSelection, $isRangeSelection, FORMAT_TEXT_COMMAND, SELECTION_CHANGE_COMMAND, COMMAND_PRIORITY_LOW, } from "lexical";
|
|
5
|
+
import { $isListNode, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, } from "@lexical/list";
|
|
6
|
+
import { $createQuoteNode, $isQuoteNode } from "@lexical/rich-text";
|
|
7
|
+
import { TOGGLE_LINK_COMMAND } from "@lexical/link";
|
|
8
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
9
|
+
import { BoldOutlined, ItalicOutlined, StrikethroughOutlined, OrderedListOutlined, UnorderedListOutlined, LinkOutlined, } from "@ant-design/icons";
|
|
10
|
+
import { Button } from "antd";
|
|
11
|
+
const quoteIcon = (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("path", { d: "M5.24615 10.5714C4.5616 10.5714 3.99078 10.5715 3.53762 10.5105C3.0593 10.4462 2.62745 10.3047 2.28042 9.95769C1.9334 9.61072 1.79194 9.17887 1.72763 8.70055C1.6667 8.24737 1.66673 7.67654 1.66675 6.99199V6.91274C1.66673 6.22819 1.6667 5.65734 1.72763 5.20419C1.79194 4.72586 1.9334 4.29401 2.28043 3.94699C2.62746 3.59997 3.0593 3.4585 3.53762 3.3942C3.99078 3.33327 4.56161 3.33329 5.24615 3.33331H5.32541C6.00995 3.33329 6.5808 3.33327 7.03397 3.3942C7.51228 3.4585 7.94413 3.59997 8.29116 3.94699C8.63817 4.29401 8.77966 4.72586 8.84396 5.20419C8.90491 5.65734 8.90484 6.22817 8.90484 6.91272V6.99198C8.90484 7.67652 8.90491 8.24737 8.84396 8.70055C8.77966 9.17887 8.63817 9.61072 8.29116 9.95769C7.94413 10.3047 7.51228 10.4462 7.03397 10.5105C6.5808 10.5715 6.00998 10.5714 5.32544 10.5714H5.24615Z", fill: "currentColor" }), _jsx("path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M2.84403 16.1313C2.71884 15.7295 2.94301 15.3023 3.34474 15.1772C5.71249 14.4393 7.38095 12.3426 7.38095 9.914V6.76172C7.38095 6.34094 7.72207 5.99982 8.14286 5.99982C8.56366 5.99982 8.90476 6.34094 8.90476 6.76172V9.914C8.90476 13.0731 6.73976 15.7152 3.79812 16.6319C3.39638 16.7572 2.96923 16.533 2.84403 16.1313Z", fill: "currentColor" }), _jsx("path", { d: "M14.389 10.5714C13.7044 10.5714 13.1336 10.5715 12.6804 10.5105C12.2021 10.4462 11.7703 10.3047 11.4233 9.95769C11.0762 9.61072 10.9348 9.17887 10.8704 8.70055C10.8095 8.24737 10.8096 7.67654 10.8096 6.99199V6.91274C10.8096 6.22819 10.8095 5.65734 10.8704 5.20419C10.9348 4.72586 11.0762 4.29401 11.4233 3.94699C11.7703 3.59997 12.2021 3.4585 12.6804 3.3942C13.1336 3.33327 13.7044 3.33329 14.389 3.33331H14.4682C15.1528 3.33329 15.7236 3.33327 16.1768 3.3942C16.6551 3.4585 17.087 3.59997 17.4339 3.94699C17.781 4.29401 17.9225 4.72586 17.9868 5.20419C18.0477 5.65734 18.0477 6.22817 18.0477 6.91272V6.99198C18.0477 7.67652 18.0477 8.24737 17.9868 8.70055C17.9225 9.17887 17.781 9.61072 17.4339 9.95769C17.087 10.3047 16.6551 10.4462 16.1768 10.5105C15.7236 10.5715 15.1528 10.5714 14.4682 10.5714H14.389Z", fill: "currentColor" }), _jsx("path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M11.9869 16.1313C11.8617 15.7295 12.0858 15.3023 12.4876 15.1772C14.8553 14.4393 16.5238 12.3426 16.5238 9.914V6.76172C16.5238 6.34094 16.8649 5.99982 17.2857 5.99982C17.7065 5.99982 18.0476 6.34094 18.0476 6.76172V9.914C18.0476 13.0731 15.8825 15.7152 12.9409 16.6319C12.5392 16.7572 12.112 16.533 11.9869 16.1313Z", fill: "currentColor" })] }));
|
|
12
|
+
const codeIcon = (_jsxs("svg", { width: "18", height: "18", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M6.41925 6.07724C6.74925 6.40268 6.74925 6.93031 6.41925 7.25575L3.63676 9.99983L6.41925 12.7439C6.74925 13.0693 6.74925 13.597 6.41925 13.9224C6.08926 14.2479 5.55424 14.2479 5.22424 13.9224L1.96374 10.7069C1.56775 10.3164 1.56775 9.68325 1.96374 9.29272L5.22424 6.07724C5.55424 5.7518 6.08926 5.7518 6.41925 6.07724Z", fill: "currentColor" }), _jsx("path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M13.5809 6.07724C13.2509 6.40268 13.2509 6.93031 13.5809 7.25575L16.3634 9.99983L13.5809 12.7439C13.2509 13.0693 13.2509 13.597 13.5809 13.9224C13.9109 14.2479 14.4459 14.2479 14.7759 13.9224L18.0364 10.7069C18.4324 10.3164 18.4324 9.68325 18.0364 9.29272L14.7759 6.07724C14.4459 5.7518 13.9109 5.7518 13.5809 6.07724Z", fill: "currentColor" }), _jsx("path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M11.8957 3.36522C12.3382 3.49166 12.5945 3.9529 12.468 4.39543L9.13468 16.0621C9.00825 16.5046 8.54701 16.7609 8.10448 16.6344C7.66195 16.508 7.40571 16.0468 7.53214 15.6042L10.8655 3.93756C10.9919 3.49503 11.4532 3.23879 11.8957 3.36522Z", fill: "currentColor" })] }));
|
|
13
|
+
export const ToolbarPlugin = () => {
|
|
14
|
+
const [editor] = useLexicalComposerContext();
|
|
15
|
+
const [activeMarks, setActiveMarks] = useState([]);
|
|
16
|
+
const formatTextButtons = useMemo(() => {
|
|
17
|
+
return [
|
|
18
|
+
{
|
|
19
|
+
key: "bold",
|
|
20
|
+
icon: _jsx(BoldOutlined, { size: 22 }),
|
|
21
|
+
onPress: () => formatText("bold"),
|
|
22
|
+
isActive: activeMarks.includes("bold"),
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
key: "italic",
|
|
26
|
+
icon: _jsx(ItalicOutlined, { size: 22 }),
|
|
27
|
+
onPress: () => formatText("italic"),
|
|
28
|
+
isActive: activeMarks.includes("italic"),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
key: "strikethrough",
|
|
32
|
+
icon: _jsx(StrikethroughOutlined, { size: 22 }),
|
|
33
|
+
onPress: () => formatText("strikethrough"),
|
|
34
|
+
isActive: activeMarks.includes("strikethrough"),
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
}, [activeMarks]);
|
|
38
|
+
const insertListButtons = useMemo(() => {
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
key: "number",
|
|
42
|
+
icon: _jsx(OrderedListOutlined, { size: 22 }),
|
|
43
|
+
onPress: () => insertList("number"),
|
|
44
|
+
isActive: activeMarks.includes("number"),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
key: "bullet",
|
|
48
|
+
icon: _jsx(UnorderedListOutlined, { size: 22 }),
|
|
49
|
+
onPress: () => insertList("bullet"),
|
|
50
|
+
isActive: activeMarks.includes("bullet"),
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
}, [activeMarks]);
|
|
54
|
+
const otherButtons = useMemo(() => {
|
|
55
|
+
return [
|
|
56
|
+
{
|
|
57
|
+
key: "link",
|
|
58
|
+
icon: _jsx(LinkOutlined, { size: 22 }),
|
|
59
|
+
onPress: () => insertLink(),
|
|
60
|
+
isActive: activeMarks.includes("link"),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
key: "quote",
|
|
64
|
+
icon: quoteIcon,
|
|
65
|
+
onPress: () => insertQuote(),
|
|
66
|
+
isActive: activeMarks.includes("quote"),
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
}, [activeMarks]);
|
|
70
|
+
const formatText = useCallback((format) => {
|
|
71
|
+
editor.dispatchCommand(FORMAT_TEXT_COMMAND, format);
|
|
72
|
+
}, [editor]);
|
|
73
|
+
const insertList = useCallback((listType) => {
|
|
74
|
+
if (listType === "number") {
|
|
75
|
+
editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);
|
|
79
|
+
}
|
|
80
|
+
}, [editor]);
|
|
81
|
+
const insertQuote = useCallback(() => {
|
|
82
|
+
editor.update(() => {
|
|
83
|
+
const selection = $getSelection();
|
|
84
|
+
if ($isRangeSelection(selection)) {
|
|
85
|
+
const anchorNode = selection.anchor.getNode();
|
|
86
|
+
const topLevelNode = anchorNode.getTopLevelElementOrThrow();
|
|
87
|
+
if ($isQuoteNode(topLevelNode)) {
|
|
88
|
+
// Nếu đang là QuoteNode → chuyển về Paragraph (bình thường)
|
|
89
|
+
const paragraph = $createParagraphNode();
|
|
90
|
+
// copy con của quote sang paragraph
|
|
91
|
+
const children = topLevelNode.getChildren();
|
|
92
|
+
children.forEach((child) => paragraph.append(child));
|
|
93
|
+
topLevelNode.replace(paragraph);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Nếu chưa có → tạo QuoteNode
|
|
97
|
+
const quote = $createQuoteNode();
|
|
98
|
+
const children = topLevelNode.getChildren();
|
|
99
|
+
children.forEach((child) => quote.append(child));
|
|
100
|
+
topLevelNode.replace(quote);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}, [editor]);
|
|
105
|
+
const insertLink = useCallback(() => {
|
|
106
|
+
editor.dispatchCommand(TOGGLE_LINK_COMMAND, "https://");
|
|
107
|
+
}, [editor]);
|
|
108
|
+
const getButtonClasses = (isActive) => {
|
|
109
|
+
return `w-8 h-8 p-0 border-sm ${isActive
|
|
110
|
+
? "bg-blue-100 text-blue-600"
|
|
111
|
+
: "text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`;
|
|
112
|
+
};
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
const updateToolbar = () => {
|
|
115
|
+
editor.getEditorState().read(() => {
|
|
116
|
+
const selection = $getSelection();
|
|
117
|
+
if ($isRangeSelection(selection)) {
|
|
118
|
+
const marks = [];
|
|
119
|
+
// inline
|
|
120
|
+
if (selection.hasFormat("bold"))
|
|
121
|
+
marks.push("bold");
|
|
122
|
+
if (selection.hasFormat("italic"))
|
|
123
|
+
marks.push("italic");
|
|
124
|
+
if (selection.hasFormat("underline"))
|
|
125
|
+
marks.push("underline");
|
|
126
|
+
if (selection.hasFormat("code"))
|
|
127
|
+
marks.push("code");
|
|
128
|
+
// block
|
|
129
|
+
const anchorNode = selection.anchor.getNode();
|
|
130
|
+
const topLevelNode = anchorNode.getTopLevelElementOrThrow();
|
|
131
|
+
if ($isQuoteNode(topLevelNode)) {
|
|
132
|
+
marks.push("quote");
|
|
133
|
+
}
|
|
134
|
+
else if ($isListNode(topLevelNode)) {
|
|
135
|
+
marks.push(topLevelNode.getListType() === "number" ? "number" : "bullet");
|
|
136
|
+
}
|
|
137
|
+
else if (topLevelNode.getType() === "heading") {
|
|
138
|
+
marks.push("heading");
|
|
139
|
+
}
|
|
140
|
+
setActiveMarks(marks);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
setActiveMarks([]);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
const removeUpdateListener = editor.registerUpdateListener(() => {
|
|
148
|
+
updateToolbar();
|
|
149
|
+
});
|
|
150
|
+
const removeSelectionListener = editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
|
|
151
|
+
updateToolbar();
|
|
152
|
+
return false;
|
|
153
|
+
}, COMMAND_PRIORITY_LOW);
|
|
154
|
+
return () => {
|
|
155
|
+
removeUpdateListener();
|
|
156
|
+
removeSelectionListener();
|
|
157
|
+
};
|
|
158
|
+
}, [editor]);
|
|
159
|
+
return (_jsxs("div", { className: "flex items-center gap-2 px-4", children: [formatTextButtons.map((button) => {
|
|
160
|
+
const { icon, onPress, key, isActive } = button;
|
|
161
|
+
return (_jsx(Button, { type: "text", shape: "default", className: getButtonClasses(isActive), onClick: onPress, children: icon }, key));
|
|
162
|
+
}), insertListButtons.map((button) => {
|
|
163
|
+
const { icon, onPress, key, isActive } = button;
|
|
164
|
+
return (_jsx(Button, { type: "text", shape: "default", className: getButtonClasses(isActive), onClick: onPress, children: icon }, key));
|
|
165
|
+
}), otherButtons.map((button) => {
|
|
166
|
+
const { icon, onPress, key, isActive } = button;
|
|
167
|
+
return (_jsx(Button, { type: "text", shape: "default", className: getButtonClasses(isActive), onClick: onPress, children: icon }, key));
|
|
168
|
+
})] }));
|
|
169
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { MessageFooterContextType } from "../../../types/chat";
|
|
2
|
+
import { MessageItem } from "@openim/wasm-client-sdk";
|
|
3
|
+
interface IMessageFooterProviderProps {
|
|
4
|
+
lastMessage?: MessageItem;
|
|
5
|
+
}
|
|
6
|
+
export declare const MessageFooterContext: import("react").Context<MessageFooterContextType>;
|
|
7
|
+
export declare const useMessageFooterContext: () => MessageFooterContextType;
|
|
8
|
+
declare const MessageFooterProvider: (props: IMessageFooterProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export default MessageFooterProvider;
|
|
10
|
+
//# 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":"AAcA,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAKtD,UAAU,2BAA2B;IACnC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AA+BD,eAAO,MAAM,oBAAoB,mDAI/B,CAAC;AAEH,eAAO,MAAM,uBAAuB,gCAAyC,CAAC;AAE9E,QAAA,MAAM,qBAAqB,GAAI,OAAO,2BAA2B,4CAuDhE,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { LexicalComposer } from "@lexical/react/LexicalComposer";
|
|
4
|
+
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
|
|
5
|
+
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
|
|
6
|
+
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
|
|
7
|
+
import { ToolbarPlugin } from "./ToolbarPlugin";
|
|
8
|
+
import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
|
|
9
|
+
import { ListPlugin } from "@lexical/react/LexicalListPlugin";
|
|
10
|
+
import { LinkNode } from "@lexical/link";
|
|
11
|
+
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
|
|
12
|
+
import { ListItemNode, ListNode } from "@lexical/list";
|
|
13
|
+
import { createContext, useCallback, useContext, useState } from "react";
|
|
14
|
+
import EnterHandler from "./EnterHandler";
|
|
15
|
+
import ActionBar from "./ActionBar";
|
|
16
|
+
import { useSendMessage } from "../../../hooks/message/useSendMessage";
|
|
17
|
+
import FilePreview from "./FilePreview";
|
|
18
|
+
const theme = {
|
|
19
|
+
text: {
|
|
20
|
+
bold: "font-bold",
|
|
21
|
+
italic: "italic",
|
|
22
|
+
strikethrough: "line-through",
|
|
23
|
+
underline: "underline",
|
|
24
|
+
},
|
|
25
|
+
quote: "border-l-4 border-gray-300 pl-4 italic text-gray-600",
|
|
26
|
+
list: {
|
|
27
|
+
nested: {
|
|
28
|
+
listitem: "list-none",
|
|
29
|
+
},
|
|
30
|
+
ol: "list-decimal list-inside",
|
|
31
|
+
ul: "list-disc list-inside",
|
|
32
|
+
},
|
|
33
|
+
link: "text-blue-500 underline hover:text-blue-700",
|
|
34
|
+
};
|
|
35
|
+
const onError = (error) => {
|
|
36
|
+
console.error(error);
|
|
37
|
+
};
|
|
38
|
+
const initialConfig = {
|
|
39
|
+
namespace: "ChatInput",
|
|
40
|
+
theme,
|
|
41
|
+
onError,
|
|
42
|
+
nodes: [HeadingNode, ListNode, ListItemNode, QuoteNode, LinkNode],
|
|
43
|
+
};
|
|
44
|
+
export const MessageFooterContext = createContext({
|
|
45
|
+
onSendMessage: () => { },
|
|
46
|
+
listUploadFiles: [],
|
|
47
|
+
setListUploadFiles: () => { },
|
|
48
|
+
});
|
|
49
|
+
export const useMessageFooterContext = () => useContext(MessageFooterContext);
|
|
50
|
+
const MessageFooterProvider = (props) => {
|
|
51
|
+
const { lastMessage } = props;
|
|
52
|
+
const { sendTextMessage, sendMergeMessage } = useSendMessage(lastMessage);
|
|
53
|
+
const [listUploadFiles, setListUploadFiles] = useState([]);
|
|
54
|
+
const onSendMessage = useCallback(async ({ plainText, richText, type, }) => {
|
|
55
|
+
if (type === "text") {
|
|
56
|
+
sendTextMessage({ plainText, richText });
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
sendMergeMessage({ plainText, richText, files: listUploadFiles });
|
|
60
|
+
}
|
|
61
|
+
setListUploadFiles([]);
|
|
62
|
+
}, [sendMergeMessage, sendTextMessage, listUploadFiles]);
|
|
63
|
+
return (_jsx(MessageFooterContext.Provider, { value: { onSendMessage, listUploadFiles, setListUploadFiles }, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsxs("div", { className: "border-t pb-2 flex flex-col gap-1", children: [listUploadFiles.length > 0 && _jsx(FilePreview, {}), _jsx(ToolbarPlugin, {}), _jsx("div", { className: "relative px-4", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "border border-indigo-500 rounded-md bg-blue-100 min-h-[64px] max-h-[140px] overflow-y-auto px-3 py-2 text-sm" }), ErrorBoundary: LexicalErrorBoundary, "aria-placeholder": "Nh\u1EADp tin nh\u1EAFn", placeholder: _jsx("div", { className: "absolute top-2 left-7 pointer-events-none", children: _jsx("p", { className: "text-gray-500 text-sm", children: "Nh\u1EADp tin nh\u1EAFn" }) }) }) }), _jsx(ActionBar, {})] }), _jsx(LinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(EnterHandler, {})] }) }));
|
|
64
|
+
};
|
|
65
|
+
export default MessageFooterProvider;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MessageItem } from "@openim/wasm-client-sdk";
|
|
2
|
+
interface ImageMessageItemProps {
|
|
3
|
+
message: MessageItem;
|
|
4
|
+
}
|
|
5
|
+
declare const FileMessageItem: (props: ImageMessageItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default FileMessageItem;
|
|
7
|
+
//# sourceMappingURL=FileMessage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/FileMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGtD,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,QAAA,MAAM,eAAe,GAAI,OAAO,qBAAqB,4CAmCpD,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { documentIcon, shortenFileName } from "../footer/FilePreview";
|
|
3
|
+
const FileMessageItem = (props) => {
|
|
4
|
+
var _a;
|
|
5
|
+
const { message } = props;
|
|
6
|
+
const handleDownload = () => {
|
|
7
|
+
var _a, _b;
|
|
8
|
+
const url = ((_a = message.fileElem) === null || _a === void 0 ? void 0 : _a.sourceUrl) || "";
|
|
9
|
+
const fileName = ((_b = message.fileElem) === null || _b === void 0 ? void 0 : _b.fileName) || "";
|
|
10
|
+
if (!url) {
|
|
11
|
+
console.warn("Không có link file để tải");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const link = document.createElement("a");
|
|
15
|
+
link.href = url;
|
|
16
|
+
if (fileName) {
|
|
17
|
+
link.setAttribute("download", fileName);
|
|
18
|
+
}
|
|
19
|
+
link.setAttribute("target", "_blank");
|
|
20
|
+
link.setAttribute("rel", "noopener noreferrer");
|
|
21
|
+
document.body.appendChild(link);
|
|
22
|
+
link.click();
|
|
23
|
+
link.remove();
|
|
24
|
+
};
|
|
25
|
+
return (_jsxs("div", { className: "relative flex flex-row items-center gap-2 align-center bg-gray-100 rounded-md p-1 pr-2 cursor-pointer", onClick: handleDownload, children: [documentIcon, _jsx("span", { className: "text-xs text-gray-500", children: shortenFileName(((_a = message.fileElem) === null || _a === void 0 ? void 0 : _a.fileName) || "") })] }));
|
|
26
|
+
};
|
|
27
|
+
export default FileMessageItem;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MessageItem } from "@openim/wasm-client-sdk";
|
|
2
|
+
interface ImageMessageItemProps {
|
|
3
|
+
message: MessageItem;
|
|
4
|
+
}
|
|
5
|
+
declare const ImageMessageItem: (props: ImageMessageItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default ImageMessageItem;
|
|
7
|
+
//# sourceMappingURL=ImageMessage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/ImageMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,yBAAyB,CAAC;AAGrE,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;CACtB;AAID,QAAA,MAAM,gBAAgB,GAAI,OAAO,qBAAqB,4CAoCrD,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { MessageStatus } from "@openim/wasm-client-sdk";
|
|
3
|
+
import { Image, Spin } from "antd";
|
|
4
|
+
const min = (a, b) => (a > b ? b : a);
|
|
5
|
+
const ImageMessageItem = (props) => {
|
|
6
|
+
var _a, _b, _c;
|
|
7
|
+
const { message } = props;
|
|
8
|
+
const imageHeight = message.pictureElem.sourcePicture.height;
|
|
9
|
+
const imageWidth = message.pictureElem.sourcePicture.width;
|
|
10
|
+
const snapshotMaxHeight = (_b = (_a = message.pictureElem.snapshotPicture) === null || _a === void 0 ? void 0 : _a.height) !== null && _b !== void 0 ? _b : imageHeight;
|
|
11
|
+
const minHeight = min(200, imageWidth) * (imageHeight / imageWidth) + 2;
|
|
12
|
+
const adaptedHight = min(minHeight, snapshotMaxHeight) + 10;
|
|
13
|
+
const adaptedWidth = min(imageWidth, 200) + 10;
|
|
14
|
+
const sourceUrl = ((_c = message.pictureElem.snapshotPicture) === null || _c === void 0 ? void 0 : _c.url) ||
|
|
15
|
+
message.pictureElem.sourcePicture.url;
|
|
16
|
+
const isSending = message.status === MessageStatus.Sending;
|
|
17
|
+
const minStyle = {
|
|
18
|
+
minHeight: `${adaptedHight}px`,
|
|
19
|
+
minWidth: `${adaptedWidth}px`,
|
|
20
|
+
};
|
|
21
|
+
return (_jsx(Spin, { spinning: isSending, children: _jsx("div", { className: "relative max-w-[200px]", style: minStyle, children: _jsx(Image, { rootClassName: "message-image cursor-pointer", className: "max-w-[200px] rounded-md", src: sourceUrl, preview: true, placeholder: _jsx("div", { style: minStyle, className: "flex items-center justify-center", children: _jsx(Spin, {}) }) }) }) }));
|
|
22
|
+
};
|
|
23
|
+
export default ImageMessageItem;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MessageItem } from "@openim/wasm-client-sdk";
|
|
2
|
+
interface TextMessageItemProps {
|
|
3
|
+
message: MessageItem;
|
|
4
|
+
}
|
|
5
|
+
declare const TextMessageItem: (props: TextMessageItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default TextMessageItem;
|
|
7
|
+
//# sourceMappingURL=TextMessage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/TextMessage.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGtD,UAAU,oBAAoB;IAC5B,OAAO,EAAE,WAAW,CAAC;CACtB;AACD,QAAA,MAAM,eAAe,GAAI,OAAO,oBAAoB,4CA4BnD,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
const TextMessageItem = (props) => {
|
|
4
|
+
var _a, _b, _c;
|
|
5
|
+
const { message } = props;
|
|
6
|
+
let extendMessageInfo = null;
|
|
7
|
+
try {
|
|
8
|
+
extendMessageInfo = JSON.parse((message === null || message === void 0 ? void 0 : message.ex) || "{}");
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
extendMessageInfo = {};
|
|
12
|
+
console.error("Failed to parse extendMessageInfo", error);
|
|
13
|
+
}
|
|
14
|
+
if (Object.keys(extendMessageInfo).length > 0 &&
|
|
15
|
+
(extendMessageInfo === null || extendMessageInfo === void 0 ? void 0 : extendMessageInfo.messageInfo)) {
|
|
16
|
+
const htmlContent = ((_b = (_a = extendMessageInfo === null || extendMessageInfo === void 0 ? void 0 : extendMessageInfo.messageInfo) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.content) || "";
|
|
17
|
+
return (_jsx("div", { className: "text-sm sm:text-base break-words whitespace-pre-line flex-1", dangerouslySetInnerHTML: { __html: htmlContent } }));
|
|
18
|
+
}
|
|
19
|
+
return (_jsx("span", { 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) || "" }));
|
|
20
|
+
};
|
|
21
|
+
export default TextMessageItem;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MessageItem } from "@openim/wasm-client-sdk";
|
|
2
|
+
interface VideoMessageItemProps {
|
|
3
|
+
message: MessageItem;
|
|
4
|
+
}
|
|
5
|
+
declare const VideoMessageItem: (props: VideoMessageItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default VideoMessageItem;
|
|
7
|
+
//# sourceMappingURL=VideoMessage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/VideoMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,yBAAyB,CAAC;AAGrE,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;CACtB;AAID,QAAA,MAAM,gBAAgB,GAAI,OAAO,qBAAqB,4CAsBrD,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { MessageStatus } from "@openim/wasm-client-sdk";
|
|
3
|
+
import { Spin } from "antd";
|
|
4
|
+
const min = (a, b) => (a > b ? b : a);
|
|
5
|
+
const VideoMessageItem = (props) => {
|
|
6
|
+
const { message } = props;
|
|
7
|
+
const imageHeight = message.videoElem.snapshotHeight;
|
|
8
|
+
const imageWidth = message.videoElem.snapshotWidth;
|
|
9
|
+
const minHeight = min(200, imageWidth) * (imageHeight / imageWidth) + 2;
|
|
10
|
+
const adaptedHight = min(minHeight, imageHeight) + 10;
|
|
11
|
+
const adaptedWidth = min(imageWidth, 200) + 10;
|
|
12
|
+
const sourceUrl = message.videoElem.videoUrl;
|
|
13
|
+
const isSending = message.status === MessageStatus.Sending;
|
|
14
|
+
const minStyle = {
|
|
15
|
+
minHeight: `${adaptedHight}px`,
|
|
16
|
+
minWidth: `${adaptedWidth}px`,
|
|
17
|
+
};
|
|
18
|
+
return (_jsx(Spin, { spinning: isSending, children: _jsx("div", { className: "relative max-w-[200px]", style: minStyle, children: _jsx("video", { className: "max-w-[200px] rounded-md", src: sourceUrl, controls: true }) }) }));
|
|
19
|
+
};
|
|
20
|
+
export default VideoMessageItem;
|
|
@@ -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=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAkBvD,UAAU,gBAAgB;IACxB,YAAY,EAAE,gBAAgB,CAAC;CAChC;AACD,QAAA,MAAM,WAAW,GAAI,kBAAkB,gBAAgB,4CAiGtD,CAAC;AAEF,eAAe,WAAW,CAAC"}
|