@mieweb/ui 0.9.0-dev.5 → 0.9.0-dev.6
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/{chunk-QWJGEDHV.js → chunk-22CYDCXA.js} +13 -1183
- package/dist/chunk-22CYDCXA.js.map +1 -0
- package/dist/chunk-3SD7PDCV.js +1878 -0
- package/dist/chunk-3SD7PDCV.js.map +1 -0
- package/dist/{chunk-PL6TFCYR.js → chunk-NORSXAXL.js} +374 -300
- package/dist/chunk-NORSXAXL.js.map +1 -0
- package/dist/chunk-T6PTKWM6.cjs +1912 -0
- package/dist/chunk-T6PTKWM6.cjs.map +1 -0
- package/dist/{chunk-XJUIZA4G.cjs → chunk-WIAQQNFU.cjs} +12 -1184
- package/dist/chunk-WIAQQNFU.cjs.map +1 -0
- package/dist/{chunk-ZXRVVNT5.cjs → chunk-XCNDZNY7.cjs} +384 -309
- package/dist/chunk-XCNDZNY7.cjs.map +1 -0
- package/dist/components/SuperChat/index.cjs +8 -8
- package/dist/components/SuperChat/index.js +2 -2
- package/dist/components/SuperChat/plugins/index.cjs +10 -3
- package/dist/components/SuperChat/plugins/index.cjs.map +1 -1
- package/dist/components/SuperChat/plugins/index.js +9 -2
- package/dist/components/SuperChat/plugins/index.js.map +1 -1
- package/dist/index.cjs +92 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -4
- package/dist/index.d.ts +38 -4
- package/dist/index.js +17 -48
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-H3GRO43Z.cjs +0 -735
- package/dist/chunk-H3GRO43Z.cjs.map +0 -1
- package/dist/chunk-MD3ZY4UO.js +0 -705
- package/dist/chunk-MD3ZY4UO.js.map +0 -1
- package/dist/chunk-PL6TFCYR.js.map +0 -1
- package/dist/chunk-QWJGEDHV.js.map +0 -1
- package/dist/chunk-XJUIZA4G.cjs.map +0 -1
- package/dist/chunk-ZXRVVNT5.cjs.map +0 -1
|
@@ -1,9 +1,288 @@
|
|
|
1
|
-
import { useMentionAutocomplete, DragDropZone, AttachmentPreviewItem, AttachmentPicker, CameraButton, MentionMenu, getFileType, generateAttachmentId } from './chunk-
|
|
1
|
+
import { useMentionAutocomplete, DragDropZone, DEFAULT_MAX_FILE_SIZE, DEFAULT_ACCEPTED_FILE_TYPES, AttachmentPreviewItem, AttachmentPicker, CameraButton, MentionMenu, ChatComposer, getFileType, generateAttachmentId } from './chunk-3SD7PDCV.js';
|
|
2
2
|
import { cn } from './chunk-TQONQSJO.js';
|
|
3
|
-
import * as
|
|
3
|
+
import * as React from 'react';
|
|
4
4
|
import { cva } from 'class-variance-authority';
|
|
5
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
|
+
function useMessages(options) {
|
|
8
|
+
const {
|
|
9
|
+
initialMessages = [],
|
|
10
|
+
currentUser,
|
|
11
|
+
onSend,
|
|
12
|
+
onRetry,
|
|
13
|
+
onLoadMore
|
|
14
|
+
} = options;
|
|
15
|
+
const [messages, setMessages] = React.useState(initialMessages);
|
|
16
|
+
const [isSending, setIsSending] = React.useState(false);
|
|
17
|
+
const [isLoadingMore, setIsLoadingMore] = React.useState(false);
|
|
18
|
+
React.useEffect(() => {
|
|
19
|
+
setMessages(initialMessages);
|
|
20
|
+
}, [initialMessages]);
|
|
21
|
+
const addMessage = React.useCallback((message) => {
|
|
22
|
+
setMessages((prev) => {
|
|
23
|
+
if (prev.some((m) => m.id === message.id)) {
|
|
24
|
+
return prev;
|
|
25
|
+
}
|
|
26
|
+
return [...prev, message];
|
|
27
|
+
});
|
|
28
|
+
}, []);
|
|
29
|
+
const updateMessage = React.useCallback(
|
|
30
|
+
(messageId, updates) => {
|
|
31
|
+
setMessages(
|
|
32
|
+
(prev) => prev.map((m) => m.id === messageId ? { ...m, ...updates } : m)
|
|
33
|
+
);
|
|
34
|
+
},
|
|
35
|
+
[]
|
|
36
|
+
);
|
|
37
|
+
const removeMessage = React.useCallback((messageId) => {
|
|
38
|
+
setMessages((prev) => prev.filter((m) => m.id !== messageId));
|
|
39
|
+
}, []);
|
|
40
|
+
const updateStatus = React.useCallback(
|
|
41
|
+
(messageId, status) => {
|
|
42
|
+
updateMessage(messageId, { status });
|
|
43
|
+
},
|
|
44
|
+
[updateMessage]
|
|
45
|
+
);
|
|
46
|
+
const markAsRead = React.useCallback(
|
|
47
|
+
(messageId) => {
|
|
48
|
+
updateStatus(messageId, "read");
|
|
49
|
+
},
|
|
50
|
+
[updateStatus]
|
|
51
|
+
);
|
|
52
|
+
const sendMessage = React.useCallback(
|
|
53
|
+
async (newMessage) => {
|
|
54
|
+
const optimisticId = `optimistic-${Date.now()}`;
|
|
55
|
+
const optimisticMessage = {
|
|
56
|
+
id: optimisticId,
|
|
57
|
+
type: "text",
|
|
58
|
+
content: newMessage.content,
|
|
59
|
+
sender: currentUser,
|
|
60
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
61
|
+
status: "sending",
|
|
62
|
+
attachments: []
|
|
63
|
+
// Would handle attachment uploads here
|
|
64
|
+
};
|
|
65
|
+
addMessage(optimisticMessage);
|
|
66
|
+
setIsSending(true);
|
|
67
|
+
try {
|
|
68
|
+
if (onSend) {
|
|
69
|
+
const sentMessage = await onSend(newMessage);
|
|
70
|
+
setMessages(
|
|
71
|
+
(prev) => prev.map((m) => m.id === optimisticId ? sentMessage : m)
|
|
72
|
+
);
|
|
73
|
+
} else {
|
|
74
|
+
updateStatus(optimisticId, "sent");
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
updateStatus(optimisticId, "failed");
|
|
78
|
+
} finally {
|
|
79
|
+
setIsSending(false);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
[currentUser, onSend, addMessage, updateStatus]
|
|
83
|
+
);
|
|
84
|
+
const retryMessage = React.useCallback(
|
|
85
|
+
async (messageId) => {
|
|
86
|
+
updateStatus(messageId, "sending");
|
|
87
|
+
try {
|
|
88
|
+
if (onRetry) {
|
|
89
|
+
await onRetry(messageId);
|
|
90
|
+
updateStatus(messageId, "sent");
|
|
91
|
+
}
|
|
92
|
+
} catch {
|
|
93
|
+
updateStatus(messageId, "failed");
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
[onRetry, updateStatus]
|
|
97
|
+
);
|
|
98
|
+
const loadMore = React.useCallback(async () => {
|
|
99
|
+
if (isLoadingMore || !onLoadMore) return;
|
|
100
|
+
setIsLoadingMore(true);
|
|
101
|
+
try {
|
|
102
|
+
const olderMessages = await onLoadMore();
|
|
103
|
+
setMessages((prev) => [...olderMessages, ...prev]);
|
|
104
|
+
} finally {
|
|
105
|
+
setIsLoadingMore(false);
|
|
106
|
+
}
|
|
107
|
+
}, [isLoadingMore, onLoadMore]);
|
|
108
|
+
return {
|
|
109
|
+
messages,
|
|
110
|
+
addMessage,
|
|
111
|
+
updateMessage,
|
|
112
|
+
removeMessage,
|
|
113
|
+
sendMessage,
|
|
114
|
+
retryMessage,
|
|
115
|
+
loadMore,
|
|
116
|
+
isSending,
|
|
117
|
+
isLoadingMore,
|
|
118
|
+
markAsRead,
|
|
119
|
+
updateStatus
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function useTypingIndicator(options = {}) {
|
|
123
|
+
const {
|
|
124
|
+
typingParticipants: initialParticipants = [],
|
|
125
|
+
debounceTime = 2e3,
|
|
126
|
+
onTypingStart,
|
|
127
|
+
onTypingStop
|
|
128
|
+
} = options;
|
|
129
|
+
const [participants, setParticipants] = React.useState(initialParticipants);
|
|
130
|
+
const [isLocalTyping, setIsLocalTyping] = React.useState(false);
|
|
131
|
+
const typingTimeoutRef = React.useRef(null);
|
|
132
|
+
React.useEffect(() => {
|
|
133
|
+
setParticipants(initialParticipants);
|
|
134
|
+
}, [initialParticipants]);
|
|
135
|
+
const startTyping = React.useCallback(() => {
|
|
136
|
+
if (!isLocalTyping) {
|
|
137
|
+
setIsLocalTyping(true);
|
|
138
|
+
onTypingStart?.();
|
|
139
|
+
}
|
|
140
|
+
if (typingTimeoutRef.current) {
|
|
141
|
+
clearTimeout(typingTimeoutRef.current);
|
|
142
|
+
}
|
|
143
|
+
typingTimeoutRef.current = setTimeout(() => {
|
|
144
|
+
setIsLocalTyping(false);
|
|
145
|
+
onTypingStop?.();
|
|
146
|
+
}, debounceTime);
|
|
147
|
+
}, [isLocalTyping, debounceTime, onTypingStart, onTypingStop]);
|
|
148
|
+
const stopTyping = React.useCallback(() => {
|
|
149
|
+
if (typingTimeoutRef.current) {
|
|
150
|
+
clearTimeout(typingTimeoutRef.current);
|
|
151
|
+
}
|
|
152
|
+
setIsLocalTyping(false);
|
|
153
|
+
onTypingStop?.();
|
|
154
|
+
}, [onTypingStop]);
|
|
155
|
+
React.useEffect(() => {
|
|
156
|
+
return () => {
|
|
157
|
+
if (typingTimeoutRef.current) {
|
|
158
|
+
clearTimeout(typingTimeoutRef.current);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}, []);
|
|
162
|
+
const typingState = React.useMemo(
|
|
163
|
+
() => ({
|
|
164
|
+
participants,
|
|
165
|
+
lastUpdated: /* @__PURE__ */ new Date()
|
|
166
|
+
}),
|
|
167
|
+
[participants]
|
|
168
|
+
);
|
|
169
|
+
return {
|
|
170
|
+
typingState,
|
|
171
|
+
startTyping,
|
|
172
|
+
stopTyping,
|
|
173
|
+
setTypingParticipants: setParticipants
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function useMessageScroll(options) {
|
|
177
|
+
const { messages, currentUserId, threshold = 100 } = options;
|
|
178
|
+
const scrollContainerRef = React.useRef(null);
|
|
179
|
+
const bottomRef = React.useRef(null);
|
|
180
|
+
const [isScrolledUp, setIsScrolledUp] = React.useState(false);
|
|
181
|
+
const prevMessageCountRef = React.useRef(messages.length);
|
|
182
|
+
React.useEffect(() => {
|
|
183
|
+
const container = scrollContainerRef.current;
|
|
184
|
+
if (!container) return;
|
|
185
|
+
const handleScroll = () => {
|
|
186
|
+
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
187
|
+
const isAtBottom = scrollHeight - scrollTop - clientHeight < threshold;
|
|
188
|
+
setIsScrolledUp(!isAtBottom);
|
|
189
|
+
};
|
|
190
|
+
container.addEventListener("scroll", handleScroll);
|
|
191
|
+
return () => container.removeEventListener("scroll", handleScroll);
|
|
192
|
+
}, [threshold]);
|
|
193
|
+
const scrollToBottom = React.useCallback((smooth = true) => {
|
|
194
|
+
bottomRef.current?.scrollIntoView({
|
|
195
|
+
behavior: smooth ? "smooth" : "auto"
|
|
196
|
+
});
|
|
197
|
+
}, []);
|
|
198
|
+
React.useEffect(() => {
|
|
199
|
+
const messageCountChanged = messages.length !== prevMessageCountRef.current;
|
|
200
|
+
prevMessageCountRef.current = messages.length;
|
|
201
|
+
if (!messageCountChanged) return;
|
|
202
|
+
const lastMessage = messages[messages.length - 1];
|
|
203
|
+
const isOutgoing = lastMessage?.sender.id === currentUserId;
|
|
204
|
+
if (isOutgoing || !isScrolledUp) {
|
|
205
|
+
scrollToBottom(true);
|
|
206
|
+
}
|
|
207
|
+
}, [messages, currentUserId, isScrolledUp, scrollToBottom]);
|
|
208
|
+
return {
|
|
209
|
+
scrollContainerRef,
|
|
210
|
+
bottomRef,
|
|
211
|
+
isScrolledUp,
|
|
212
|
+
scrollToBottom
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function useReadReceipts(options) {
|
|
216
|
+
const { currentUserId, onMarkRead, threshold = 0.5 } = options;
|
|
217
|
+
const observerRef = React.useRef(null);
|
|
218
|
+
const observedMessagesRef = React.useRef(/* @__PURE__ */ new Set());
|
|
219
|
+
React.useEffect(() => {
|
|
220
|
+
observerRef.current = new IntersectionObserver(
|
|
221
|
+
(entries) => {
|
|
222
|
+
entries.forEach((entry) => {
|
|
223
|
+
if (entry.isIntersecting) {
|
|
224
|
+
const messageId = entry.target.getAttribute("data-message-id");
|
|
225
|
+
if (messageId && !observedMessagesRef.current.has(messageId)) {
|
|
226
|
+
observedMessagesRef.current.add(messageId);
|
|
227
|
+
onMarkRead?.(messageId);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
},
|
|
232
|
+
{ threshold }
|
|
233
|
+
);
|
|
234
|
+
return () => {
|
|
235
|
+
observerRef.current?.disconnect();
|
|
236
|
+
};
|
|
237
|
+
}, [onMarkRead, threshold]);
|
|
238
|
+
const observeMessage = React.useCallback(
|
|
239
|
+
(element, message) => {
|
|
240
|
+
if (!element || !observerRef.current) return;
|
|
241
|
+
if (message.sender.id !== currentUserId && message.status !== "read" && !observedMessagesRef.current.has(message.id)) {
|
|
242
|
+
element.setAttribute("data-message-id", message.id);
|
|
243
|
+
observerRef.current.observe(element);
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
[currentUserId]
|
|
247
|
+
);
|
|
248
|
+
return { observeMessage };
|
|
249
|
+
}
|
|
250
|
+
function useTypingEmulation({
|
|
251
|
+
value,
|
|
252
|
+
onTypingStart,
|
|
253
|
+
onTypingStop
|
|
254
|
+
}) {
|
|
255
|
+
const [isTyping, setIsTyping] = React.useState(false);
|
|
256
|
+
const typingTimeoutRef = React.useRef(
|
|
257
|
+
null
|
|
258
|
+
);
|
|
259
|
+
React.useEffect(() => {
|
|
260
|
+
if (!onTypingStart && !onTypingStop) return;
|
|
261
|
+
if (value.length > 0 && !isTyping) {
|
|
262
|
+
setIsTyping(true);
|
|
263
|
+
onTypingStart?.();
|
|
264
|
+
}
|
|
265
|
+
if (typingTimeoutRef.current) {
|
|
266
|
+
clearTimeout(typingTimeoutRef.current);
|
|
267
|
+
}
|
|
268
|
+
typingTimeoutRef.current = setTimeout(() => {
|
|
269
|
+
if (isTyping) {
|
|
270
|
+
setIsTyping(false);
|
|
271
|
+
onTypingStop?.();
|
|
272
|
+
}
|
|
273
|
+
}, 2e3);
|
|
274
|
+
return () => {
|
|
275
|
+
if (typingTimeoutRef.current) {
|
|
276
|
+
clearTimeout(typingTimeoutRef.current);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
}, [value, isTyping, onTypingStart, onTypingStop]);
|
|
280
|
+
const stopTyping = React.useCallback(() => {
|
|
281
|
+
setIsTyping(false);
|
|
282
|
+
onTypingStop?.();
|
|
283
|
+
}, [onTypingStop]);
|
|
284
|
+
return { stopTyping };
|
|
285
|
+
}
|
|
7
286
|
var statusIconVariants = cva(
|
|
8
287
|
"inline-flex items-center gap-0.5 text-current",
|
|
9
288
|
{
|
|
@@ -384,7 +663,7 @@ function defaultFormatTimestamp(timestamp) {
|
|
|
384
663
|
minute: "2-digit"
|
|
385
664
|
});
|
|
386
665
|
}
|
|
387
|
-
var MessageBubble =
|
|
666
|
+
var MessageBubble = React.forwardRef(
|
|
388
667
|
({
|
|
389
668
|
className,
|
|
390
669
|
message,
|
|
@@ -859,7 +1138,7 @@ function LoadMoreButton({
|
|
|
859
1138
|
);
|
|
860
1139
|
}
|
|
861
1140
|
LoadMoreButton.displayName = "LoadMoreButton";
|
|
862
|
-
var MessageList =
|
|
1141
|
+
var MessageList = React.forwardRef(
|
|
863
1142
|
({
|
|
864
1143
|
messages,
|
|
865
1144
|
currentUser,
|
|
@@ -878,19 +1157,19 @@ var MessageList = React6.forwardRef(
|
|
|
878
1157
|
className,
|
|
879
1158
|
autoScroll = "onNewMessage"
|
|
880
1159
|
}, ref) => {
|
|
881
|
-
const scrollContainerRef =
|
|
882
|
-
const bottomRef =
|
|
883
|
-
const [isUserScrolled, setIsUserScrolled] =
|
|
884
|
-
const prevMessageCountRef =
|
|
885
|
-
|
|
886
|
-
const handleScroll =
|
|
1160
|
+
const scrollContainerRef = React.useRef(null);
|
|
1161
|
+
const bottomRef = React.useRef(null);
|
|
1162
|
+
const [isUserScrolled, setIsUserScrolled] = React.useState(false);
|
|
1163
|
+
const prevMessageCountRef = React.useRef(messages.length);
|
|
1164
|
+
React.useImperativeHandle(ref, () => scrollContainerRef.current);
|
|
1165
|
+
const handleScroll = React.useCallback(() => {
|
|
887
1166
|
const container = scrollContainerRef.current;
|
|
888
1167
|
if (!container) return;
|
|
889
1168
|
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
890
1169
|
const isAtBottom = scrollHeight - scrollTop - clientHeight < 100;
|
|
891
1170
|
setIsUserScrolled(!isAtBottom);
|
|
892
1171
|
}, []);
|
|
893
|
-
|
|
1172
|
+
React.useEffect(() => {
|
|
894
1173
|
const container = scrollContainerRef.current;
|
|
895
1174
|
const bottom = bottomRef.current;
|
|
896
1175
|
if (!container || !bottom) return;
|
|
@@ -906,7 +1185,7 @@ var MessageList = React6.forwardRef(
|
|
|
906
1185
|
}
|
|
907
1186
|
}
|
|
908
1187
|
}, [messages, currentUser.id, autoScroll, isUserScrolled]);
|
|
909
|
-
|
|
1188
|
+
React.useEffect(() => {
|
|
910
1189
|
const bottom = bottomRef.current;
|
|
911
1190
|
if (bottom && !isLoading) {
|
|
912
1191
|
bottom.scrollIntoView();
|
|
@@ -959,7 +1238,7 @@ var MessageList = React6.forwardRef(
|
|
|
959
1238
|
{
|
|
960
1239
|
"data-slot": "message-list-content",
|
|
961
1240
|
className: "flex flex-col gap-1 p-4",
|
|
962
|
-
children: messageGroups.map((group) => /* @__PURE__ */ jsxs(
|
|
1241
|
+
children: messageGroups.map((group) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
963
1242
|
groupByDate && group.label && /* @__PURE__ */ jsx(DateSeparator, { label: group.label }),
|
|
964
1243
|
group.messages.map((message, index) => {
|
|
965
1244
|
const prevMessage = group.messages[index - 1];
|
|
@@ -1110,7 +1389,7 @@ var sendButtonVariants = cva(
|
|
|
1110
1389
|
}
|
|
1111
1390
|
}
|
|
1112
1391
|
);
|
|
1113
|
-
var SendButton =
|
|
1392
|
+
var SendButton = React.forwardRef(
|
|
1114
1393
|
({ className, variant, canSend, isLoading, disabled, ...props }, ref) => {
|
|
1115
1394
|
return /* @__PURE__ */ jsx(
|
|
1116
1395
|
"button",
|
|
@@ -1175,7 +1454,7 @@ var SendButton = React6.forwardRef(
|
|
|
1175
1454
|
}
|
|
1176
1455
|
);
|
|
1177
1456
|
SendButton.displayName = "SendButton";
|
|
1178
|
-
var MessageComposer =
|
|
1457
|
+
var MessageComposer = React.forwardRef(
|
|
1179
1458
|
({
|
|
1180
1459
|
onSend,
|
|
1181
1460
|
onTypingStart,
|
|
@@ -1189,8 +1468,8 @@ var MessageComposer = React6.forwardRef(
|
|
|
1189
1468
|
isSending = false,
|
|
1190
1469
|
showAttachmentPicker = true,
|
|
1191
1470
|
showCameraButton = false,
|
|
1192
|
-
acceptedFileTypes =
|
|
1193
|
-
maxFileSize =
|
|
1471
|
+
acceptedFileTypes = DEFAULT_ACCEPTED_FILE_TYPES,
|
|
1472
|
+
maxFileSize = DEFAULT_MAX_FILE_SIZE,
|
|
1194
1473
|
maxAttachments = 10,
|
|
1195
1474
|
onError,
|
|
1196
1475
|
autoFocus = false,
|
|
@@ -1201,11 +1480,11 @@ var MessageComposer = React6.forwardRef(
|
|
|
1201
1480
|
mentionOptions,
|
|
1202
1481
|
className
|
|
1203
1482
|
}, ref) => {
|
|
1204
|
-
const textareaRef =
|
|
1205
|
-
const [internalContent, setInternalContent] =
|
|
1483
|
+
const textareaRef = React.useRef(null);
|
|
1484
|
+
const [internalContent, setInternalContent] = React.useState("");
|
|
1206
1485
|
const isControlled = controlledValue !== void 0;
|
|
1207
1486
|
const content = isControlled ? controlledValue : internalContent;
|
|
1208
|
-
const setContent =
|
|
1487
|
+
const setContent = React.useCallback(
|
|
1209
1488
|
(val) => {
|
|
1210
1489
|
if (isControlled) {
|
|
1211
1490
|
const newVal = typeof val === "function" ? val(controlledValue) : val;
|
|
@@ -1216,20 +1495,20 @@ var MessageComposer = React6.forwardRef(
|
|
|
1216
1495
|
},
|
|
1217
1496
|
[isControlled, controlledValue, onValueChange]
|
|
1218
1497
|
);
|
|
1219
|
-
const [attachments, setAttachments] =
|
|
1498
|
+
const [attachments, setAttachments] = React.useState(
|
|
1220
1499
|
[]
|
|
1221
1500
|
);
|
|
1222
|
-
const [isTyping, setIsTyping] =
|
|
1223
|
-
const typingTimeoutRef =
|
|
1224
|
-
|
|
1225
|
-
|
|
1501
|
+
const [isTyping, setIsTyping] = React.useState(false);
|
|
1502
|
+
const typingTimeoutRef = React.useRef(null);
|
|
1503
|
+
React.useImperativeHandle(ref, () => textareaRef.current);
|
|
1504
|
+
React.useEffect(() => {
|
|
1226
1505
|
const textarea = textareaRef.current;
|
|
1227
1506
|
if (textarea) {
|
|
1228
1507
|
textarea.style.height = "auto";
|
|
1229
1508
|
textarea.style.height = `${Math.min(textarea.scrollHeight, 150)}px`;
|
|
1230
1509
|
}
|
|
1231
1510
|
}, [content]);
|
|
1232
|
-
|
|
1511
|
+
React.useEffect(() => {
|
|
1233
1512
|
if (content.length > 0 && !isTyping) {
|
|
1234
1513
|
setIsTyping(true);
|
|
1235
1514
|
onTypingStart?.();
|
|
@@ -1249,13 +1528,13 @@ var MessageComposer = React6.forwardRef(
|
|
|
1249
1528
|
}
|
|
1250
1529
|
};
|
|
1251
1530
|
}, [content, isTyping, onTypingStart, onTypingStop]);
|
|
1252
|
-
|
|
1531
|
+
React.useEffect(() => {
|
|
1253
1532
|
if (autoFocus) {
|
|
1254
1533
|
textareaRef.current?.focus();
|
|
1255
1534
|
}
|
|
1256
1535
|
}, [autoFocus]);
|
|
1257
1536
|
const replyToId = replyTo?.id;
|
|
1258
|
-
|
|
1537
|
+
React.useEffect(() => {
|
|
1259
1538
|
if (replyToId !== void 0) {
|
|
1260
1539
|
textareaRef.current?.focus();
|
|
1261
1540
|
}
|
|
@@ -1333,7 +1612,7 @@ var MessageComposer = React6.forwardRef(
|
|
|
1333
1612
|
return prev.filter((a) => a.id !== attachmentId);
|
|
1334
1613
|
});
|
|
1335
1614
|
};
|
|
1336
|
-
|
|
1615
|
+
React.useEffect(() => {
|
|
1337
1616
|
const currentAttachments = attachments;
|
|
1338
1617
|
return () => {
|
|
1339
1618
|
currentAttachments.forEach((a) => {
|
|
@@ -1603,7 +1882,7 @@ function formatLastSeen(date) {
|
|
|
1603
1882
|
if (diffDays < 7) return `${diffDays}d ago`;
|
|
1604
1883
|
return date.toLocaleDateString();
|
|
1605
1884
|
}
|
|
1606
|
-
var ConversationHeader =
|
|
1885
|
+
var ConversationHeader = React.forwardRef(
|
|
1607
1886
|
({
|
|
1608
1887
|
className,
|
|
1609
1888
|
size,
|
|
@@ -1725,7 +2004,7 @@ var ConversationHeader = React6.forwardRef(
|
|
|
1725
2004
|
}
|
|
1726
2005
|
);
|
|
1727
2006
|
ConversationHeader.displayName = "ConversationHeader";
|
|
1728
|
-
var ConversationListItem =
|
|
2007
|
+
var ConversationListItem = React.forwardRef(({ className, conversation, isSelected, onSelect, ...props }, ref) => {
|
|
1729
2008
|
const participant = conversation.participants.find((p) => !p.isCurrentUser);
|
|
1730
2009
|
const title = getConversationTitle(conversation, participant);
|
|
1731
2010
|
const avatarUrl = conversation.avatarUrl || participant?.avatarUrl;
|
|
@@ -1933,7 +2212,7 @@ function ConversationListSkeleton({
|
|
|
1933
2212
|
}
|
|
1934
2213
|
ConversationListSkeleton.displayName = "ConversationListSkeleton";
|
|
1935
2214
|
function LightboxModal({ attachment, onClose }) {
|
|
1936
|
-
|
|
2215
|
+
React.useEffect(() => {
|
|
1937
2216
|
const handleKeyDown = (event) => {
|
|
1938
2217
|
if (event.key === "Escape") {
|
|
1939
2218
|
onClose();
|
|
@@ -2030,7 +2309,7 @@ function LightboxModal({ attachment, onClose }) {
|
|
|
2030
2309
|
);
|
|
2031
2310
|
}
|
|
2032
2311
|
LightboxModal.displayName = "LightboxModal";
|
|
2033
|
-
var MessageThread =
|
|
2312
|
+
var MessageThread = React.forwardRef(
|
|
2034
2313
|
({
|
|
2035
2314
|
conversation,
|
|
2036
2315
|
messages,
|
|
@@ -2061,8 +2340,20 @@ var MessageThread = React6.forwardRef(
|
|
|
2061
2340
|
onError,
|
|
2062
2341
|
className
|
|
2063
2342
|
}, ref) => {
|
|
2064
|
-
const [lightboxAttachment, setLightboxAttachment] =
|
|
2065
|
-
const [replyTo, setReplyTo] =
|
|
2343
|
+
const [lightboxAttachment, setLightboxAttachment] = React.useState(null);
|
|
2344
|
+
const [replyTo, setReplyTo] = React.useState(null);
|
|
2345
|
+
const composerRef = React.useRef(null);
|
|
2346
|
+
const [draft, setDraft] = React.useState("");
|
|
2347
|
+
const draftEpochRef = React.useRef(0);
|
|
2348
|
+
const handleDraftChange = React.useCallback((value) => {
|
|
2349
|
+
draftEpochRef.current += 1;
|
|
2350
|
+
setDraft(value);
|
|
2351
|
+
}, []);
|
|
2352
|
+
const { stopTyping } = useTypingEmulation({
|
|
2353
|
+
value: draft,
|
|
2354
|
+
onTypingStart: eventHandlers.onTypingStart,
|
|
2355
|
+
onTypingStop: eventHandlers.onTypingStop
|
|
2356
|
+
});
|
|
2066
2357
|
const participant = conversation?.type === "direct" ? conversation.participants.find((p) => p.id !== currentUser.id) : void 0;
|
|
2067
2358
|
const handleAttachmentClick = (attachment, message) => {
|
|
2068
2359
|
if (attachment.type === "image" || attachment.type === "video") {
|
|
@@ -2071,12 +2362,21 @@ var MessageThread = React6.forwardRef(
|
|
|
2071
2362
|
eventHandlers.onAttachmentClick?.(attachment, message);
|
|
2072
2363
|
};
|
|
2073
2364
|
const handleSendMessage = async (newMessage) => {
|
|
2365
|
+
stopTyping();
|
|
2074
2366
|
const messageWithReply = {
|
|
2075
2367
|
...newMessage,
|
|
2076
2368
|
replyToId: replyTo?.id || newMessage.replyToId
|
|
2077
2369
|
};
|
|
2078
2370
|
setReplyTo(null);
|
|
2079
|
-
|
|
2371
|
+
const epoch = draftEpochRef.current;
|
|
2372
|
+
try {
|
|
2373
|
+
await Promise.resolve(eventHandlers.onSendMessage?.(messageWithReply));
|
|
2374
|
+
} catch (error) {
|
|
2375
|
+
if (draftEpochRef.current === epoch) {
|
|
2376
|
+
setDraft(newMessage.content);
|
|
2377
|
+
}
|
|
2378
|
+
throw error;
|
|
2379
|
+
}
|
|
2080
2380
|
};
|
|
2081
2381
|
return /* @__PURE__ */ jsxs(
|
|
2082
2382
|
"div",
|
|
@@ -2102,9 +2402,11 @@ var MessageThread = React6.forwardRef(
|
|
|
2102
2402
|
/* @__PURE__ */ jsx(
|
|
2103
2403
|
DragDropZone,
|
|
2104
2404
|
{
|
|
2105
|
-
onFilesDropped: () => {
|
|
2106
|
-
|
|
2405
|
+
onFilesDropped: (files) => {
|
|
2406
|
+
composerRef.current?.addFiles(files);
|
|
2107
2407
|
},
|
|
2408
|
+
maxFiles: maxAttachments,
|
|
2409
|
+
onError,
|
|
2108
2410
|
disabled: !showAttachmentPicker,
|
|
2109
2411
|
className: "flex-1 overflow-hidden",
|
|
2110
2412
|
children: /* @__PURE__ */ jsx(
|
|
@@ -2130,23 +2432,38 @@ var MessageThread = React6.forwardRef(
|
|
|
2130
2432
|
}
|
|
2131
2433
|
),
|
|
2132
2434
|
/* @__PURE__ */ jsx(
|
|
2133
|
-
|
|
2435
|
+
"div",
|
|
2134
2436
|
{
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2437
|
+
"data-slot": "message-thread-composer",
|
|
2438
|
+
className: "shrink-0 border-t border-neutral-200 p-3 dark:border-neutral-700",
|
|
2439
|
+
children: /* @__PURE__ */ jsx(
|
|
2440
|
+
ChatComposer,
|
|
2441
|
+
{
|
|
2442
|
+
ref: composerRef,
|
|
2443
|
+
value: draft,
|
|
2444
|
+
onValueChange: handleDraftChange,
|
|
2445
|
+
onSend: handleSendMessage,
|
|
2446
|
+
placeholder,
|
|
2447
|
+
maxLength: maxMessageLength,
|
|
2448
|
+
showCharacterCount,
|
|
2449
|
+
isSending,
|
|
2450
|
+
allowAttachments: showAttachmentPicker,
|
|
2451
|
+
acceptedFileTypes: acceptedFileTypes ?? DEFAULT_ACCEPTED_FILE_TYPES,
|
|
2452
|
+
maxFileSize: maxFileSize ?? DEFAULT_MAX_FILE_SIZE,
|
|
2453
|
+
maxAttachments,
|
|
2454
|
+
onError,
|
|
2455
|
+
replyTo,
|
|
2456
|
+
onCancelReply: () => setReplyTo(null),
|
|
2457
|
+
inputLabel: "Message",
|
|
2458
|
+
micSlot: showCameraButton ? /* @__PURE__ */ jsx(
|
|
2459
|
+
CameraButton,
|
|
2460
|
+
{
|
|
2461
|
+
onCapture: (file) => composerRef.current?.addFiles([file]),
|
|
2462
|
+
disabled: isSending
|
|
2463
|
+
}
|
|
2464
|
+
) : void 0
|
|
2465
|
+
}
|
|
2466
|
+
)
|
|
2150
2467
|
}
|
|
2151
2468
|
),
|
|
2152
2469
|
/* @__PURE__ */ jsx(
|
|
@@ -2226,250 +2543,7 @@ function MessagingSplitView({
|
|
|
2226
2543
|
);
|
|
2227
2544
|
}
|
|
2228
2545
|
MessagingSplitView.displayName = "MessagingSplitView";
|
|
2229
|
-
function useMessages(options) {
|
|
2230
|
-
const {
|
|
2231
|
-
initialMessages = [],
|
|
2232
|
-
currentUser,
|
|
2233
|
-
onSend,
|
|
2234
|
-
onRetry,
|
|
2235
|
-
onLoadMore
|
|
2236
|
-
} = options;
|
|
2237
|
-
const [messages, setMessages] = React6.useState(initialMessages);
|
|
2238
|
-
const [isSending, setIsSending] = React6.useState(false);
|
|
2239
|
-
const [isLoadingMore, setIsLoadingMore] = React6.useState(false);
|
|
2240
|
-
React6.useEffect(() => {
|
|
2241
|
-
setMessages(initialMessages);
|
|
2242
|
-
}, [initialMessages]);
|
|
2243
|
-
const addMessage = React6.useCallback((message) => {
|
|
2244
|
-
setMessages((prev) => {
|
|
2245
|
-
if (prev.some((m) => m.id === message.id)) {
|
|
2246
|
-
return prev;
|
|
2247
|
-
}
|
|
2248
|
-
return [...prev, message];
|
|
2249
|
-
});
|
|
2250
|
-
}, []);
|
|
2251
|
-
const updateMessage = React6.useCallback(
|
|
2252
|
-
(messageId, updates) => {
|
|
2253
|
-
setMessages(
|
|
2254
|
-
(prev) => prev.map((m) => m.id === messageId ? { ...m, ...updates } : m)
|
|
2255
|
-
);
|
|
2256
|
-
},
|
|
2257
|
-
[]
|
|
2258
|
-
);
|
|
2259
|
-
const removeMessage = React6.useCallback((messageId) => {
|
|
2260
|
-
setMessages((prev) => prev.filter((m) => m.id !== messageId));
|
|
2261
|
-
}, []);
|
|
2262
|
-
const updateStatus = React6.useCallback(
|
|
2263
|
-
(messageId, status) => {
|
|
2264
|
-
updateMessage(messageId, { status });
|
|
2265
|
-
},
|
|
2266
|
-
[updateMessage]
|
|
2267
|
-
);
|
|
2268
|
-
const markAsRead = React6.useCallback(
|
|
2269
|
-
(messageId) => {
|
|
2270
|
-
updateStatus(messageId, "read");
|
|
2271
|
-
},
|
|
2272
|
-
[updateStatus]
|
|
2273
|
-
);
|
|
2274
|
-
const sendMessage = React6.useCallback(
|
|
2275
|
-
async (newMessage) => {
|
|
2276
|
-
const optimisticId = `optimistic-${Date.now()}`;
|
|
2277
|
-
const optimisticMessage = {
|
|
2278
|
-
id: optimisticId,
|
|
2279
|
-
type: "text",
|
|
2280
|
-
content: newMessage.content,
|
|
2281
|
-
sender: currentUser,
|
|
2282
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
2283
|
-
status: "sending",
|
|
2284
|
-
attachments: []
|
|
2285
|
-
// Would handle attachment uploads here
|
|
2286
|
-
};
|
|
2287
|
-
addMessage(optimisticMessage);
|
|
2288
|
-
setIsSending(true);
|
|
2289
|
-
try {
|
|
2290
|
-
if (onSend) {
|
|
2291
|
-
const sentMessage = await onSend(newMessage);
|
|
2292
|
-
setMessages(
|
|
2293
|
-
(prev) => prev.map((m) => m.id === optimisticId ? sentMessage : m)
|
|
2294
|
-
);
|
|
2295
|
-
} else {
|
|
2296
|
-
updateStatus(optimisticId, "sent");
|
|
2297
|
-
}
|
|
2298
|
-
} catch {
|
|
2299
|
-
updateStatus(optimisticId, "failed");
|
|
2300
|
-
} finally {
|
|
2301
|
-
setIsSending(false);
|
|
2302
|
-
}
|
|
2303
|
-
},
|
|
2304
|
-
[currentUser, onSend, addMessage, updateStatus]
|
|
2305
|
-
);
|
|
2306
|
-
const retryMessage = React6.useCallback(
|
|
2307
|
-
async (messageId) => {
|
|
2308
|
-
updateStatus(messageId, "sending");
|
|
2309
|
-
try {
|
|
2310
|
-
if (onRetry) {
|
|
2311
|
-
await onRetry(messageId);
|
|
2312
|
-
updateStatus(messageId, "sent");
|
|
2313
|
-
}
|
|
2314
|
-
} catch {
|
|
2315
|
-
updateStatus(messageId, "failed");
|
|
2316
|
-
}
|
|
2317
|
-
},
|
|
2318
|
-
[onRetry, updateStatus]
|
|
2319
|
-
);
|
|
2320
|
-
const loadMore = React6.useCallback(async () => {
|
|
2321
|
-
if (isLoadingMore || !onLoadMore) return;
|
|
2322
|
-
setIsLoadingMore(true);
|
|
2323
|
-
try {
|
|
2324
|
-
const olderMessages = await onLoadMore();
|
|
2325
|
-
setMessages((prev) => [...olderMessages, ...prev]);
|
|
2326
|
-
} finally {
|
|
2327
|
-
setIsLoadingMore(false);
|
|
2328
|
-
}
|
|
2329
|
-
}, [isLoadingMore, onLoadMore]);
|
|
2330
|
-
return {
|
|
2331
|
-
messages,
|
|
2332
|
-
addMessage,
|
|
2333
|
-
updateMessage,
|
|
2334
|
-
removeMessage,
|
|
2335
|
-
sendMessage,
|
|
2336
|
-
retryMessage,
|
|
2337
|
-
loadMore,
|
|
2338
|
-
isSending,
|
|
2339
|
-
isLoadingMore,
|
|
2340
|
-
markAsRead,
|
|
2341
|
-
updateStatus
|
|
2342
|
-
};
|
|
2343
|
-
}
|
|
2344
|
-
function useTypingIndicator(options = {}) {
|
|
2345
|
-
const {
|
|
2346
|
-
typingParticipants: initialParticipants = [],
|
|
2347
|
-
debounceTime = 2e3,
|
|
2348
|
-
onTypingStart,
|
|
2349
|
-
onTypingStop
|
|
2350
|
-
} = options;
|
|
2351
|
-
const [participants, setParticipants] = React6.useState(initialParticipants);
|
|
2352
|
-
const [isLocalTyping, setIsLocalTyping] = React6.useState(false);
|
|
2353
|
-
const typingTimeoutRef = React6.useRef(null);
|
|
2354
|
-
React6.useEffect(() => {
|
|
2355
|
-
setParticipants(initialParticipants);
|
|
2356
|
-
}, [initialParticipants]);
|
|
2357
|
-
const startTyping = React6.useCallback(() => {
|
|
2358
|
-
if (!isLocalTyping) {
|
|
2359
|
-
setIsLocalTyping(true);
|
|
2360
|
-
onTypingStart?.();
|
|
2361
|
-
}
|
|
2362
|
-
if (typingTimeoutRef.current) {
|
|
2363
|
-
clearTimeout(typingTimeoutRef.current);
|
|
2364
|
-
}
|
|
2365
|
-
typingTimeoutRef.current = setTimeout(() => {
|
|
2366
|
-
setIsLocalTyping(false);
|
|
2367
|
-
onTypingStop?.();
|
|
2368
|
-
}, debounceTime);
|
|
2369
|
-
}, [isLocalTyping, debounceTime, onTypingStart, onTypingStop]);
|
|
2370
|
-
const stopTyping = React6.useCallback(() => {
|
|
2371
|
-
if (typingTimeoutRef.current) {
|
|
2372
|
-
clearTimeout(typingTimeoutRef.current);
|
|
2373
|
-
}
|
|
2374
|
-
setIsLocalTyping(false);
|
|
2375
|
-
onTypingStop?.();
|
|
2376
|
-
}, [onTypingStop]);
|
|
2377
|
-
React6.useEffect(() => {
|
|
2378
|
-
return () => {
|
|
2379
|
-
if (typingTimeoutRef.current) {
|
|
2380
|
-
clearTimeout(typingTimeoutRef.current);
|
|
2381
|
-
}
|
|
2382
|
-
};
|
|
2383
|
-
}, []);
|
|
2384
|
-
const typingState = React6.useMemo(
|
|
2385
|
-
() => ({
|
|
2386
|
-
participants,
|
|
2387
|
-
lastUpdated: /* @__PURE__ */ new Date()
|
|
2388
|
-
}),
|
|
2389
|
-
[participants]
|
|
2390
|
-
);
|
|
2391
|
-
return {
|
|
2392
|
-
typingState,
|
|
2393
|
-
startTyping,
|
|
2394
|
-
stopTyping,
|
|
2395
|
-
setTypingParticipants: setParticipants
|
|
2396
|
-
};
|
|
2397
|
-
}
|
|
2398
|
-
function useMessageScroll(options) {
|
|
2399
|
-
const { messages, currentUserId, threshold = 100 } = options;
|
|
2400
|
-
const scrollContainerRef = React6.useRef(null);
|
|
2401
|
-
const bottomRef = React6.useRef(null);
|
|
2402
|
-
const [isScrolledUp, setIsScrolledUp] = React6.useState(false);
|
|
2403
|
-
const prevMessageCountRef = React6.useRef(messages.length);
|
|
2404
|
-
React6.useEffect(() => {
|
|
2405
|
-
const container = scrollContainerRef.current;
|
|
2406
|
-
if (!container) return;
|
|
2407
|
-
const handleScroll = () => {
|
|
2408
|
-
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
2409
|
-
const isAtBottom = scrollHeight - scrollTop - clientHeight < threshold;
|
|
2410
|
-
setIsScrolledUp(!isAtBottom);
|
|
2411
|
-
};
|
|
2412
|
-
container.addEventListener("scroll", handleScroll);
|
|
2413
|
-
return () => container.removeEventListener("scroll", handleScroll);
|
|
2414
|
-
}, [threshold]);
|
|
2415
|
-
const scrollToBottom = React6.useCallback((smooth = true) => {
|
|
2416
|
-
bottomRef.current?.scrollIntoView({
|
|
2417
|
-
behavior: smooth ? "smooth" : "auto"
|
|
2418
|
-
});
|
|
2419
|
-
}, []);
|
|
2420
|
-
React6.useEffect(() => {
|
|
2421
|
-
const messageCountChanged = messages.length !== prevMessageCountRef.current;
|
|
2422
|
-
prevMessageCountRef.current = messages.length;
|
|
2423
|
-
if (!messageCountChanged) return;
|
|
2424
|
-
const lastMessage = messages[messages.length - 1];
|
|
2425
|
-
const isOutgoing = lastMessage?.sender.id === currentUserId;
|
|
2426
|
-
if (isOutgoing || !isScrolledUp) {
|
|
2427
|
-
scrollToBottom(true);
|
|
2428
|
-
}
|
|
2429
|
-
}, [messages, currentUserId, isScrolledUp, scrollToBottom]);
|
|
2430
|
-
return {
|
|
2431
|
-
scrollContainerRef,
|
|
2432
|
-
bottomRef,
|
|
2433
|
-
isScrolledUp,
|
|
2434
|
-
scrollToBottom
|
|
2435
|
-
};
|
|
2436
|
-
}
|
|
2437
|
-
function useReadReceipts(options) {
|
|
2438
|
-
const { currentUserId, onMarkRead, threshold = 0.5 } = options;
|
|
2439
|
-
const observerRef = React6.useRef(null);
|
|
2440
|
-
const observedMessagesRef = React6.useRef(/* @__PURE__ */ new Set());
|
|
2441
|
-
React6.useEffect(() => {
|
|
2442
|
-
observerRef.current = new IntersectionObserver(
|
|
2443
|
-
(entries) => {
|
|
2444
|
-
entries.forEach((entry) => {
|
|
2445
|
-
if (entry.isIntersecting) {
|
|
2446
|
-
const messageId = entry.target.getAttribute("data-message-id");
|
|
2447
|
-
if (messageId && !observedMessagesRef.current.has(messageId)) {
|
|
2448
|
-
observedMessagesRef.current.add(messageId);
|
|
2449
|
-
onMarkRead?.(messageId);
|
|
2450
|
-
}
|
|
2451
|
-
}
|
|
2452
|
-
});
|
|
2453
|
-
},
|
|
2454
|
-
{ threshold }
|
|
2455
|
-
);
|
|
2456
|
-
return () => {
|
|
2457
|
-
observerRef.current?.disconnect();
|
|
2458
|
-
};
|
|
2459
|
-
}, [onMarkRead, threshold]);
|
|
2460
|
-
const observeMessage = React6.useCallback(
|
|
2461
|
-
(element, message) => {
|
|
2462
|
-
if (!element || !observerRef.current) return;
|
|
2463
|
-
if (message.sender.id !== currentUserId && message.status !== "read" && !observedMessagesRef.current.has(message.id)) {
|
|
2464
|
-
element.setAttribute("data-message-id", message.id);
|
|
2465
|
-
observerRef.current.observe(element);
|
|
2466
|
-
}
|
|
2467
|
-
},
|
|
2468
|
-
[currentUserId]
|
|
2469
|
-
);
|
|
2470
|
-
return { observeMessage };
|
|
2471
|
-
}
|
|
2472
2546
|
|
|
2473
|
-
export { AttachmentPreview, CharacterCounter, ConversationHeader, ConversationListItem, ConversationListSkeleton, DateSeparator, EmptyState, LightboxModal, LoadMoreButton, MessageBubble, MessageComposer, MessageList, MessageStatusIcon, MessageThread, MessagingSplitView, ReadReceiptIndicator, SendButton, SkeletonMessage, TypingIndicator, bubbleVariants, formatDateLabel, formatFileSize, formatLastSeen, getConversationSubtitle, getConversationTitle, groupMessagesByDate, headerVariants, isSameSenderGroup, sendButtonVariants, useMessageScroll, useMessages, useReadReceipts, useTypingIndicator };
|
|
2474
|
-
//# sourceMappingURL=chunk-
|
|
2475
|
-
//# sourceMappingURL=chunk-
|
|
2547
|
+
export { AttachmentPreview, CharacterCounter, ConversationHeader, ConversationListItem, ConversationListSkeleton, DateSeparator, EmptyState, LightboxModal, LoadMoreButton, MessageBubble, MessageComposer, MessageList, MessageStatusIcon, MessageThread, MessagingSplitView, ReadReceiptIndicator, SendButton, SkeletonMessage, TypingIndicator, bubbleVariants, formatDateLabel, formatFileSize, formatLastSeen, getConversationSubtitle, getConversationTitle, groupMessagesByDate, headerVariants, isSameSenderGroup, sendButtonVariants, useMessageScroll, useMessages, useReadReceipts, useTypingEmulation, useTypingIndicator };
|
|
2548
|
+
//# sourceMappingURL=chunk-NORSXAXL.js.map
|
|
2549
|
+
//# sourceMappingURL=chunk-NORSXAXL.js.map
|