@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,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkT6PTKWM6_cjs = require('./chunk-T6PTKWM6.cjs');
|
|
4
4
|
var chunkS5RVSXDV_cjs = require('./chunk-S5RVSXDV.cjs');
|
|
5
|
-
var
|
|
5
|
+
var React = require('react');
|
|
6
6
|
var classVarianceAuthority = require('class-variance-authority');
|
|
7
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
8
|
|
|
@@ -24,8 +24,287 @@ function _interopNamespace(e) {
|
|
|
24
24
|
return Object.freeze(n);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
var
|
|
27
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
28
28
|
|
|
29
|
+
function useMessages(options) {
|
|
30
|
+
const {
|
|
31
|
+
initialMessages = [],
|
|
32
|
+
currentUser,
|
|
33
|
+
onSend,
|
|
34
|
+
onRetry,
|
|
35
|
+
onLoadMore
|
|
36
|
+
} = options;
|
|
37
|
+
const [messages, setMessages] = React__namespace.useState(initialMessages);
|
|
38
|
+
const [isSending, setIsSending] = React__namespace.useState(false);
|
|
39
|
+
const [isLoadingMore, setIsLoadingMore] = React__namespace.useState(false);
|
|
40
|
+
React__namespace.useEffect(() => {
|
|
41
|
+
setMessages(initialMessages);
|
|
42
|
+
}, [initialMessages]);
|
|
43
|
+
const addMessage = React__namespace.useCallback((message) => {
|
|
44
|
+
setMessages((prev) => {
|
|
45
|
+
if (prev.some((m) => m.id === message.id)) {
|
|
46
|
+
return prev;
|
|
47
|
+
}
|
|
48
|
+
return [...prev, message];
|
|
49
|
+
});
|
|
50
|
+
}, []);
|
|
51
|
+
const updateMessage = React__namespace.useCallback(
|
|
52
|
+
(messageId, updates) => {
|
|
53
|
+
setMessages(
|
|
54
|
+
(prev) => prev.map((m) => m.id === messageId ? { ...m, ...updates } : m)
|
|
55
|
+
);
|
|
56
|
+
},
|
|
57
|
+
[]
|
|
58
|
+
);
|
|
59
|
+
const removeMessage = React__namespace.useCallback((messageId) => {
|
|
60
|
+
setMessages((prev) => prev.filter((m) => m.id !== messageId));
|
|
61
|
+
}, []);
|
|
62
|
+
const updateStatus = React__namespace.useCallback(
|
|
63
|
+
(messageId, status) => {
|
|
64
|
+
updateMessage(messageId, { status });
|
|
65
|
+
},
|
|
66
|
+
[updateMessage]
|
|
67
|
+
);
|
|
68
|
+
const markAsRead = React__namespace.useCallback(
|
|
69
|
+
(messageId) => {
|
|
70
|
+
updateStatus(messageId, "read");
|
|
71
|
+
},
|
|
72
|
+
[updateStatus]
|
|
73
|
+
);
|
|
74
|
+
const sendMessage = React__namespace.useCallback(
|
|
75
|
+
async (newMessage) => {
|
|
76
|
+
const optimisticId = `optimistic-${Date.now()}`;
|
|
77
|
+
const optimisticMessage = {
|
|
78
|
+
id: optimisticId,
|
|
79
|
+
type: "text",
|
|
80
|
+
content: newMessage.content,
|
|
81
|
+
sender: currentUser,
|
|
82
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
83
|
+
status: "sending",
|
|
84
|
+
attachments: []
|
|
85
|
+
// Would handle attachment uploads here
|
|
86
|
+
};
|
|
87
|
+
addMessage(optimisticMessage);
|
|
88
|
+
setIsSending(true);
|
|
89
|
+
try {
|
|
90
|
+
if (onSend) {
|
|
91
|
+
const sentMessage = await onSend(newMessage);
|
|
92
|
+
setMessages(
|
|
93
|
+
(prev) => prev.map((m) => m.id === optimisticId ? sentMessage : m)
|
|
94
|
+
);
|
|
95
|
+
} else {
|
|
96
|
+
updateStatus(optimisticId, "sent");
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
updateStatus(optimisticId, "failed");
|
|
100
|
+
} finally {
|
|
101
|
+
setIsSending(false);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
[currentUser, onSend, addMessage, updateStatus]
|
|
105
|
+
);
|
|
106
|
+
const retryMessage = React__namespace.useCallback(
|
|
107
|
+
async (messageId) => {
|
|
108
|
+
updateStatus(messageId, "sending");
|
|
109
|
+
try {
|
|
110
|
+
if (onRetry) {
|
|
111
|
+
await onRetry(messageId);
|
|
112
|
+
updateStatus(messageId, "sent");
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
updateStatus(messageId, "failed");
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
[onRetry, updateStatus]
|
|
119
|
+
);
|
|
120
|
+
const loadMore = React__namespace.useCallback(async () => {
|
|
121
|
+
if (isLoadingMore || !onLoadMore) return;
|
|
122
|
+
setIsLoadingMore(true);
|
|
123
|
+
try {
|
|
124
|
+
const olderMessages = await onLoadMore();
|
|
125
|
+
setMessages((prev) => [...olderMessages, ...prev]);
|
|
126
|
+
} finally {
|
|
127
|
+
setIsLoadingMore(false);
|
|
128
|
+
}
|
|
129
|
+
}, [isLoadingMore, onLoadMore]);
|
|
130
|
+
return {
|
|
131
|
+
messages,
|
|
132
|
+
addMessage,
|
|
133
|
+
updateMessage,
|
|
134
|
+
removeMessage,
|
|
135
|
+
sendMessage,
|
|
136
|
+
retryMessage,
|
|
137
|
+
loadMore,
|
|
138
|
+
isSending,
|
|
139
|
+
isLoadingMore,
|
|
140
|
+
markAsRead,
|
|
141
|
+
updateStatus
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function useTypingIndicator(options = {}) {
|
|
145
|
+
const {
|
|
146
|
+
typingParticipants: initialParticipants = [],
|
|
147
|
+
debounceTime = 2e3,
|
|
148
|
+
onTypingStart,
|
|
149
|
+
onTypingStop
|
|
150
|
+
} = options;
|
|
151
|
+
const [participants, setParticipants] = React__namespace.useState(initialParticipants);
|
|
152
|
+
const [isLocalTyping, setIsLocalTyping] = React__namespace.useState(false);
|
|
153
|
+
const typingTimeoutRef = React__namespace.useRef(null);
|
|
154
|
+
React__namespace.useEffect(() => {
|
|
155
|
+
setParticipants(initialParticipants);
|
|
156
|
+
}, [initialParticipants]);
|
|
157
|
+
const startTyping = React__namespace.useCallback(() => {
|
|
158
|
+
if (!isLocalTyping) {
|
|
159
|
+
setIsLocalTyping(true);
|
|
160
|
+
onTypingStart?.();
|
|
161
|
+
}
|
|
162
|
+
if (typingTimeoutRef.current) {
|
|
163
|
+
clearTimeout(typingTimeoutRef.current);
|
|
164
|
+
}
|
|
165
|
+
typingTimeoutRef.current = setTimeout(() => {
|
|
166
|
+
setIsLocalTyping(false);
|
|
167
|
+
onTypingStop?.();
|
|
168
|
+
}, debounceTime);
|
|
169
|
+
}, [isLocalTyping, debounceTime, onTypingStart, onTypingStop]);
|
|
170
|
+
const stopTyping = React__namespace.useCallback(() => {
|
|
171
|
+
if (typingTimeoutRef.current) {
|
|
172
|
+
clearTimeout(typingTimeoutRef.current);
|
|
173
|
+
}
|
|
174
|
+
setIsLocalTyping(false);
|
|
175
|
+
onTypingStop?.();
|
|
176
|
+
}, [onTypingStop]);
|
|
177
|
+
React__namespace.useEffect(() => {
|
|
178
|
+
return () => {
|
|
179
|
+
if (typingTimeoutRef.current) {
|
|
180
|
+
clearTimeout(typingTimeoutRef.current);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}, []);
|
|
184
|
+
const typingState = React__namespace.useMemo(
|
|
185
|
+
() => ({
|
|
186
|
+
participants,
|
|
187
|
+
lastUpdated: /* @__PURE__ */ new Date()
|
|
188
|
+
}),
|
|
189
|
+
[participants]
|
|
190
|
+
);
|
|
191
|
+
return {
|
|
192
|
+
typingState,
|
|
193
|
+
startTyping,
|
|
194
|
+
stopTyping,
|
|
195
|
+
setTypingParticipants: setParticipants
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function useMessageScroll(options) {
|
|
199
|
+
const { messages, currentUserId, threshold = 100 } = options;
|
|
200
|
+
const scrollContainerRef = React__namespace.useRef(null);
|
|
201
|
+
const bottomRef = React__namespace.useRef(null);
|
|
202
|
+
const [isScrolledUp, setIsScrolledUp] = React__namespace.useState(false);
|
|
203
|
+
const prevMessageCountRef = React__namespace.useRef(messages.length);
|
|
204
|
+
React__namespace.useEffect(() => {
|
|
205
|
+
const container = scrollContainerRef.current;
|
|
206
|
+
if (!container) return;
|
|
207
|
+
const handleScroll = () => {
|
|
208
|
+
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
209
|
+
const isAtBottom = scrollHeight - scrollTop - clientHeight < threshold;
|
|
210
|
+
setIsScrolledUp(!isAtBottom);
|
|
211
|
+
};
|
|
212
|
+
container.addEventListener("scroll", handleScroll);
|
|
213
|
+
return () => container.removeEventListener("scroll", handleScroll);
|
|
214
|
+
}, [threshold]);
|
|
215
|
+
const scrollToBottom = React__namespace.useCallback((smooth = true) => {
|
|
216
|
+
bottomRef.current?.scrollIntoView({
|
|
217
|
+
behavior: smooth ? "smooth" : "auto"
|
|
218
|
+
});
|
|
219
|
+
}, []);
|
|
220
|
+
React__namespace.useEffect(() => {
|
|
221
|
+
const messageCountChanged = messages.length !== prevMessageCountRef.current;
|
|
222
|
+
prevMessageCountRef.current = messages.length;
|
|
223
|
+
if (!messageCountChanged) return;
|
|
224
|
+
const lastMessage = messages[messages.length - 1];
|
|
225
|
+
const isOutgoing = lastMessage?.sender.id === currentUserId;
|
|
226
|
+
if (isOutgoing || !isScrolledUp) {
|
|
227
|
+
scrollToBottom(true);
|
|
228
|
+
}
|
|
229
|
+
}, [messages, currentUserId, isScrolledUp, scrollToBottom]);
|
|
230
|
+
return {
|
|
231
|
+
scrollContainerRef,
|
|
232
|
+
bottomRef,
|
|
233
|
+
isScrolledUp,
|
|
234
|
+
scrollToBottom
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function useReadReceipts(options) {
|
|
238
|
+
const { currentUserId, onMarkRead, threshold = 0.5 } = options;
|
|
239
|
+
const observerRef = React__namespace.useRef(null);
|
|
240
|
+
const observedMessagesRef = React__namespace.useRef(/* @__PURE__ */ new Set());
|
|
241
|
+
React__namespace.useEffect(() => {
|
|
242
|
+
observerRef.current = new IntersectionObserver(
|
|
243
|
+
(entries) => {
|
|
244
|
+
entries.forEach((entry) => {
|
|
245
|
+
if (entry.isIntersecting) {
|
|
246
|
+
const messageId = entry.target.getAttribute("data-message-id");
|
|
247
|
+
if (messageId && !observedMessagesRef.current.has(messageId)) {
|
|
248
|
+
observedMessagesRef.current.add(messageId);
|
|
249
|
+
onMarkRead?.(messageId);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
},
|
|
254
|
+
{ threshold }
|
|
255
|
+
);
|
|
256
|
+
return () => {
|
|
257
|
+
observerRef.current?.disconnect();
|
|
258
|
+
};
|
|
259
|
+
}, [onMarkRead, threshold]);
|
|
260
|
+
const observeMessage = React__namespace.useCallback(
|
|
261
|
+
(element, message) => {
|
|
262
|
+
if (!element || !observerRef.current) return;
|
|
263
|
+
if (message.sender.id !== currentUserId && message.status !== "read" && !observedMessagesRef.current.has(message.id)) {
|
|
264
|
+
element.setAttribute("data-message-id", message.id);
|
|
265
|
+
observerRef.current.observe(element);
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
[currentUserId]
|
|
269
|
+
);
|
|
270
|
+
return { observeMessage };
|
|
271
|
+
}
|
|
272
|
+
function useTypingEmulation({
|
|
273
|
+
value,
|
|
274
|
+
onTypingStart,
|
|
275
|
+
onTypingStop
|
|
276
|
+
}) {
|
|
277
|
+
const [isTyping, setIsTyping] = React__namespace.useState(false);
|
|
278
|
+
const typingTimeoutRef = React__namespace.useRef(
|
|
279
|
+
null
|
|
280
|
+
);
|
|
281
|
+
React__namespace.useEffect(() => {
|
|
282
|
+
if (!onTypingStart && !onTypingStop) return;
|
|
283
|
+
if (value.length > 0 && !isTyping) {
|
|
284
|
+
setIsTyping(true);
|
|
285
|
+
onTypingStart?.();
|
|
286
|
+
}
|
|
287
|
+
if (typingTimeoutRef.current) {
|
|
288
|
+
clearTimeout(typingTimeoutRef.current);
|
|
289
|
+
}
|
|
290
|
+
typingTimeoutRef.current = setTimeout(() => {
|
|
291
|
+
if (isTyping) {
|
|
292
|
+
setIsTyping(false);
|
|
293
|
+
onTypingStop?.();
|
|
294
|
+
}
|
|
295
|
+
}, 2e3);
|
|
296
|
+
return () => {
|
|
297
|
+
if (typingTimeoutRef.current) {
|
|
298
|
+
clearTimeout(typingTimeoutRef.current);
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}, [value, isTyping, onTypingStart, onTypingStop]);
|
|
302
|
+
const stopTyping = React__namespace.useCallback(() => {
|
|
303
|
+
setIsTyping(false);
|
|
304
|
+
onTypingStop?.();
|
|
305
|
+
}, [onTypingStop]);
|
|
306
|
+
return { stopTyping };
|
|
307
|
+
}
|
|
29
308
|
var statusIconVariants = classVarianceAuthority.cva(
|
|
30
309
|
"inline-flex items-center gap-0.5 text-current",
|
|
31
310
|
{
|
|
@@ -406,7 +685,7 @@ function defaultFormatTimestamp(timestamp) {
|
|
|
406
685
|
minute: "2-digit"
|
|
407
686
|
});
|
|
408
687
|
}
|
|
409
|
-
var MessageBubble =
|
|
688
|
+
var MessageBubble = React__namespace.forwardRef(
|
|
410
689
|
({
|
|
411
690
|
className,
|
|
412
691
|
message,
|
|
@@ -881,7 +1160,7 @@ function LoadMoreButton({
|
|
|
881
1160
|
);
|
|
882
1161
|
}
|
|
883
1162
|
LoadMoreButton.displayName = "LoadMoreButton";
|
|
884
|
-
var MessageList =
|
|
1163
|
+
var MessageList = React__namespace.forwardRef(
|
|
885
1164
|
({
|
|
886
1165
|
messages,
|
|
887
1166
|
currentUser,
|
|
@@ -900,19 +1179,19 @@ var MessageList = React6__namespace.forwardRef(
|
|
|
900
1179
|
className,
|
|
901
1180
|
autoScroll = "onNewMessage"
|
|
902
1181
|
}, ref) => {
|
|
903
|
-
const scrollContainerRef =
|
|
904
|
-
const bottomRef =
|
|
905
|
-
const [isUserScrolled, setIsUserScrolled] =
|
|
906
|
-
const prevMessageCountRef =
|
|
907
|
-
|
|
908
|
-
const handleScroll =
|
|
1182
|
+
const scrollContainerRef = React__namespace.useRef(null);
|
|
1183
|
+
const bottomRef = React__namespace.useRef(null);
|
|
1184
|
+
const [isUserScrolled, setIsUserScrolled] = React__namespace.useState(false);
|
|
1185
|
+
const prevMessageCountRef = React__namespace.useRef(messages.length);
|
|
1186
|
+
React__namespace.useImperativeHandle(ref, () => scrollContainerRef.current);
|
|
1187
|
+
const handleScroll = React__namespace.useCallback(() => {
|
|
909
1188
|
const container = scrollContainerRef.current;
|
|
910
1189
|
if (!container) return;
|
|
911
1190
|
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
912
1191
|
const isAtBottom = scrollHeight - scrollTop - clientHeight < 100;
|
|
913
1192
|
setIsUserScrolled(!isAtBottom);
|
|
914
1193
|
}, []);
|
|
915
|
-
|
|
1194
|
+
React__namespace.useEffect(() => {
|
|
916
1195
|
const container = scrollContainerRef.current;
|
|
917
1196
|
const bottom = bottomRef.current;
|
|
918
1197
|
if (!container || !bottom) return;
|
|
@@ -928,7 +1207,7 @@ var MessageList = React6__namespace.forwardRef(
|
|
|
928
1207
|
}
|
|
929
1208
|
}
|
|
930
1209
|
}, [messages, currentUser.id, autoScroll, isUserScrolled]);
|
|
931
|
-
|
|
1210
|
+
React__namespace.useEffect(() => {
|
|
932
1211
|
const bottom = bottomRef.current;
|
|
933
1212
|
if (bottom && !isLoading) {
|
|
934
1213
|
bottom.scrollIntoView();
|
|
@@ -981,7 +1260,7 @@ var MessageList = React6__namespace.forwardRef(
|
|
|
981
1260
|
{
|
|
982
1261
|
"data-slot": "message-list-content",
|
|
983
1262
|
className: "flex flex-col gap-1 p-4",
|
|
984
|
-
children: messageGroups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1263
|
+
children: messageGroups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
|
|
985
1264
|
groupByDate && group.label && /* @__PURE__ */ jsxRuntime.jsx(DateSeparator, { label: group.label }),
|
|
986
1265
|
group.messages.map((message, index) => {
|
|
987
1266
|
const prevMessage = group.messages[index - 1];
|
|
@@ -1132,7 +1411,7 @@ var sendButtonVariants = classVarianceAuthority.cva(
|
|
|
1132
1411
|
}
|
|
1133
1412
|
}
|
|
1134
1413
|
);
|
|
1135
|
-
var SendButton =
|
|
1414
|
+
var SendButton = React__namespace.forwardRef(
|
|
1136
1415
|
({ className, variant, canSend, isLoading, disabled, ...props }, ref) => {
|
|
1137
1416
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1138
1417
|
"button",
|
|
@@ -1197,7 +1476,7 @@ var SendButton = React6__namespace.forwardRef(
|
|
|
1197
1476
|
}
|
|
1198
1477
|
);
|
|
1199
1478
|
SendButton.displayName = "SendButton";
|
|
1200
|
-
var MessageComposer =
|
|
1479
|
+
var MessageComposer = React__namespace.forwardRef(
|
|
1201
1480
|
({
|
|
1202
1481
|
onSend,
|
|
1203
1482
|
onTypingStart,
|
|
@@ -1211,8 +1490,8 @@ var MessageComposer = React6__namespace.forwardRef(
|
|
|
1211
1490
|
isSending = false,
|
|
1212
1491
|
showAttachmentPicker = true,
|
|
1213
1492
|
showCameraButton = false,
|
|
1214
|
-
acceptedFileTypes =
|
|
1215
|
-
maxFileSize =
|
|
1493
|
+
acceptedFileTypes = chunkT6PTKWM6_cjs.DEFAULT_ACCEPTED_FILE_TYPES,
|
|
1494
|
+
maxFileSize = chunkT6PTKWM6_cjs.DEFAULT_MAX_FILE_SIZE,
|
|
1216
1495
|
maxAttachments = 10,
|
|
1217
1496
|
onError,
|
|
1218
1497
|
autoFocus = false,
|
|
@@ -1223,11 +1502,11 @@ var MessageComposer = React6__namespace.forwardRef(
|
|
|
1223
1502
|
mentionOptions,
|
|
1224
1503
|
className
|
|
1225
1504
|
}, ref) => {
|
|
1226
|
-
const textareaRef =
|
|
1227
|
-
const [internalContent, setInternalContent] =
|
|
1505
|
+
const textareaRef = React__namespace.useRef(null);
|
|
1506
|
+
const [internalContent, setInternalContent] = React__namespace.useState("");
|
|
1228
1507
|
const isControlled = controlledValue !== void 0;
|
|
1229
1508
|
const content = isControlled ? controlledValue : internalContent;
|
|
1230
|
-
const setContent =
|
|
1509
|
+
const setContent = React__namespace.useCallback(
|
|
1231
1510
|
(val) => {
|
|
1232
1511
|
if (isControlled) {
|
|
1233
1512
|
const newVal = typeof val === "function" ? val(controlledValue) : val;
|
|
@@ -1238,20 +1517,20 @@ var MessageComposer = React6__namespace.forwardRef(
|
|
|
1238
1517
|
},
|
|
1239
1518
|
[isControlled, controlledValue, onValueChange]
|
|
1240
1519
|
);
|
|
1241
|
-
const [attachments, setAttachments] =
|
|
1520
|
+
const [attachments, setAttachments] = React__namespace.useState(
|
|
1242
1521
|
[]
|
|
1243
1522
|
);
|
|
1244
|
-
const [isTyping, setIsTyping] =
|
|
1245
|
-
const typingTimeoutRef =
|
|
1246
|
-
|
|
1247
|
-
|
|
1523
|
+
const [isTyping, setIsTyping] = React__namespace.useState(false);
|
|
1524
|
+
const typingTimeoutRef = React__namespace.useRef(null);
|
|
1525
|
+
React__namespace.useImperativeHandle(ref, () => textareaRef.current);
|
|
1526
|
+
React__namespace.useEffect(() => {
|
|
1248
1527
|
const textarea = textareaRef.current;
|
|
1249
1528
|
if (textarea) {
|
|
1250
1529
|
textarea.style.height = "auto";
|
|
1251
1530
|
textarea.style.height = `${Math.min(textarea.scrollHeight, 150)}px`;
|
|
1252
1531
|
}
|
|
1253
1532
|
}, [content]);
|
|
1254
|
-
|
|
1533
|
+
React__namespace.useEffect(() => {
|
|
1255
1534
|
if (content.length > 0 && !isTyping) {
|
|
1256
1535
|
setIsTyping(true);
|
|
1257
1536
|
onTypingStart?.();
|
|
@@ -1271,18 +1550,18 @@ var MessageComposer = React6__namespace.forwardRef(
|
|
|
1271
1550
|
}
|
|
1272
1551
|
};
|
|
1273
1552
|
}, [content, isTyping, onTypingStart, onTypingStop]);
|
|
1274
|
-
|
|
1553
|
+
React__namespace.useEffect(() => {
|
|
1275
1554
|
if (autoFocus) {
|
|
1276
1555
|
textareaRef.current?.focus();
|
|
1277
1556
|
}
|
|
1278
1557
|
}, [autoFocus]);
|
|
1279
1558
|
const replyToId = replyTo?.id;
|
|
1280
|
-
|
|
1559
|
+
React__namespace.useEffect(() => {
|
|
1281
1560
|
if (replyToId !== void 0) {
|
|
1282
1561
|
textareaRef.current?.focus();
|
|
1283
1562
|
}
|
|
1284
1563
|
}, [replyToId]);
|
|
1285
|
-
const mention =
|
|
1564
|
+
const mention = chunkT6PTKWM6_cjs.useMentionAutocomplete({
|
|
1286
1565
|
options: mentionOptions,
|
|
1287
1566
|
value: content,
|
|
1288
1567
|
setValue: setContent,
|
|
@@ -1324,13 +1603,13 @@ var MessageComposer = React6__namespace.forwardRef(
|
|
|
1324
1603
|
onError?.(`Maximum ${maxAttachments} attachments allowed`);
|
|
1325
1604
|
}
|
|
1326
1605
|
const newAttachments = filesToAdd.map((file) => {
|
|
1327
|
-
const type =
|
|
1606
|
+
const type = chunkT6PTKWM6_cjs.getFileType(file.type);
|
|
1328
1607
|
let previewUrl;
|
|
1329
1608
|
if (type === "image" || type === "video") {
|
|
1330
1609
|
previewUrl = URL.createObjectURL(file);
|
|
1331
1610
|
}
|
|
1332
1611
|
return {
|
|
1333
|
-
id:
|
|
1612
|
+
id: chunkT6PTKWM6_cjs.generateAttachmentId(),
|
|
1334
1613
|
file,
|
|
1335
1614
|
previewUrl,
|
|
1336
1615
|
type,
|
|
@@ -1355,7 +1634,7 @@ var MessageComposer = React6__namespace.forwardRef(
|
|
|
1355
1634
|
return prev.filter((a) => a.id !== attachmentId);
|
|
1356
1635
|
});
|
|
1357
1636
|
};
|
|
1358
|
-
|
|
1637
|
+
React__namespace.useEffect(() => {
|
|
1359
1638
|
const currentAttachments = attachments;
|
|
1360
1639
|
return () => {
|
|
1361
1640
|
currentAttachments.forEach((a) => {
|
|
@@ -1366,7 +1645,7 @@ var MessageComposer = React6__namespace.forwardRef(
|
|
|
1366
1645
|
};
|
|
1367
1646
|
}, [attachments]);
|
|
1368
1647
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1369
|
-
|
|
1648
|
+
chunkT6PTKWM6_cjs.DragDropZone,
|
|
1370
1649
|
{
|
|
1371
1650
|
onFilesDropped: handleFilesSelected,
|
|
1372
1651
|
acceptedTypes: acceptedFileTypes,
|
|
@@ -1444,7 +1723,7 @@ var MessageComposer = React6__namespace.forwardRef(
|
|
|
1444
1723
|
"border-t border-neutral-200 dark:border-neutral-700"
|
|
1445
1724
|
),
|
|
1446
1725
|
children: attachments.map((attachment) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1447
|
-
|
|
1726
|
+
chunkT6PTKWM6_cjs.AttachmentPreviewItem,
|
|
1448
1727
|
{
|
|
1449
1728
|
attachment,
|
|
1450
1729
|
onRemove: () => handleRemoveAttachment(attachment.id)
|
|
@@ -1464,7 +1743,7 @@ var MessageComposer = React6__namespace.forwardRef(
|
|
|
1464
1743
|
),
|
|
1465
1744
|
children: [
|
|
1466
1745
|
showAttachmentPicker && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1467
|
-
|
|
1746
|
+
chunkT6PTKWM6_cjs.AttachmentPicker,
|
|
1468
1747
|
{
|
|
1469
1748
|
onFilesSelected: handleFilesSelected,
|
|
1470
1749
|
acceptedTypes: acceptedFileTypes,
|
|
@@ -1475,7 +1754,7 @@ var MessageComposer = React6__namespace.forwardRef(
|
|
|
1475
1754
|
}
|
|
1476
1755
|
),
|
|
1477
1756
|
showCameraButton && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1478
|
-
|
|
1757
|
+
chunkT6PTKWM6_cjs.CameraButton,
|
|
1479
1758
|
{
|
|
1480
1759
|
onCapture: (file) => handleFilesSelected([file]),
|
|
1481
1760
|
disabled: disabled || attachments.length >= maxAttachments
|
|
@@ -1488,7 +1767,7 @@ var MessageComposer = React6__namespace.forwardRef(
|
|
|
1488
1767
|
className: "relative flex-1",
|
|
1489
1768
|
ref: mention.anchorRef,
|
|
1490
1769
|
children: [
|
|
1491
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1770
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunkT6PTKWM6_cjs.MentionMenu, { mention }),
|
|
1492
1771
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1493
1772
|
"textarea",
|
|
1494
1773
|
{
|
|
@@ -1625,7 +1904,7 @@ function formatLastSeen(date) {
|
|
|
1625
1904
|
if (diffDays < 7) return `${diffDays}d ago`;
|
|
1626
1905
|
return date.toLocaleDateString();
|
|
1627
1906
|
}
|
|
1628
|
-
var ConversationHeader =
|
|
1907
|
+
var ConversationHeader = React__namespace.forwardRef(
|
|
1629
1908
|
({
|
|
1630
1909
|
className,
|
|
1631
1910
|
size,
|
|
@@ -1747,7 +2026,7 @@ var ConversationHeader = React6__namespace.forwardRef(
|
|
|
1747
2026
|
}
|
|
1748
2027
|
);
|
|
1749
2028
|
ConversationHeader.displayName = "ConversationHeader";
|
|
1750
|
-
var ConversationListItem =
|
|
2029
|
+
var ConversationListItem = React__namespace.forwardRef(({ className, conversation, isSelected, onSelect, ...props }, ref) => {
|
|
1751
2030
|
const participant = conversation.participants.find((p) => !p.isCurrentUser);
|
|
1752
2031
|
const title = getConversationTitle(conversation, participant);
|
|
1753
2032
|
const avatarUrl = conversation.avatarUrl || participant?.avatarUrl;
|
|
@@ -1955,7 +2234,7 @@ function ConversationListSkeleton({
|
|
|
1955
2234
|
}
|
|
1956
2235
|
ConversationListSkeleton.displayName = "ConversationListSkeleton";
|
|
1957
2236
|
function LightboxModal({ attachment, onClose }) {
|
|
1958
|
-
|
|
2237
|
+
React__namespace.useEffect(() => {
|
|
1959
2238
|
const handleKeyDown = (event) => {
|
|
1960
2239
|
if (event.key === "Escape") {
|
|
1961
2240
|
onClose();
|
|
@@ -2052,7 +2331,7 @@ function LightboxModal({ attachment, onClose }) {
|
|
|
2052
2331
|
);
|
|
2053
2332
|
}
|
|
2054
2333
|
LightboxModal.displayName = "LightboxModal";
|
|
2055
|
-
var MessageThread =
|
|
2334
|
+
var MessageThread = React__namespace.forwardRef(
|
|
2056
2335
|
({
|
|
2057
2336
|
conversation,
|
|
2058
2337
|
messages,
|
|
@@ -2083,8 +2362,20 @@ var MessageThread = React6__namespace.forwardRef(
|
|
|
2083
2362
|
onError,
|
|
2084
2363
|
className
|
|
2085
2364
|
}, ref) => {
|
|
2086
|
-
const [lightboxAttachment, setLightboxAttachment] =
|
|
2087
|
-
const [replyTo, setReplyTo] =
|
|
2365
|
+
const [lightboxAttachment, setLightboxAttachment] = React__namespace.useState(null);
|
|
2366
|
+
const [replyTo, setReplyTo] = React__namespace.useState(null);
|
|
2367
|
+
const composerRef = React__namespace.useRef(null);
|
|
2368
|
+
const [draft, setDraft] = React__namespace.useState("");
|
|
2369
|
+
const draftEpochRef = React__namespace.useRef(0);
|
|
2370
|
+
const handleDraftChange = React__namespace.useCallback((value) => {
|
|
2371
|
+
draftEpochRef.current += 1;
|
|
2372
|
+
setDraft(value);
|
|
2373
|
+
}, []);
|
|
2374
|
+
const { stopTyping } = useTypingEmulation({
|
|
2375
|
+
value: draft,
|
|
2376
|
+
onTypingStart: eventHandlers.onTypingStart,
|
|
2377
|
+
onTypingStop: eventHandlers.onTypingStop
|
|
2378
|
+
});
|
|
2088
2379
|
const participant = conversation?.type === "direct" ? conversation.participants.find((p) => p.id !== currentUser.id) : void 0;
|
|
2089
2380
|
const handleAttachmentClick = (attachment, message) => {
|
|
2090
2381
|
if (attachment.type === "image" || attachment.type === "video") {
|
|
@@ -2093,12 +2384,21 @@ var MessageThread = React6__namespace.forwardRef(
|
|
|
2093
2384
|
eventHandlers.onAttachmentClick?.(attachment, message);
|
|
2094
2385
|
};
|
|
2095
2386
|
const handleSendMessage = async (newMessage) => {
|
|
2387
|
+
stopTyping();
|
|
2096
2388
|
const messageWithReply = {
|
|
2097
2389
|
...newMessage,
|
|
2098
2390
|
replyToId: replyTo?.id || newMessage.replyToId
|
|
2099
2391
|
};
|
|
2100
2392
|
setReplyTo(null);
|
|
2101
|
-
|
|
2393
|
+
const epoch = draftEpochRef.current;
|
|
2394
|
+
try {
|
|
2395
|
+
await Promise.resolve(eventHandlers.onSendMessage?.(messageWithReply));
|
|
2396
|
+
} catch (error) {
|
|
2397
|
+
if (draftEpochRef.current === epoch) {
|
|
2398
|
+
setDraft(newMessage.content);
|
|
2399
|
+
}
|
|
2400
|
+
throw error;
|
|
2401
|
+
}
|
|
2102
2402
|
};
|
|
2103
2403
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2104
2404
|
"div",
|
|
@@ -2122,11 +2422,13 @@ var MessageThread = React6__namespace.forwardRef(
|
|
|
2122
2422
|
}
|
|
2123
2423
|
),
|
|
2124
2424
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2125
|
-
|
|
2425
|
+
chunkT6PTKWM6_cjs.DragDropZone,
|
|
2126
2426
|
{
|
|
2127
|
-
onFilesDropped: () => {
|
|
2128
|
-
|
|
2427
|
+
onFilesDropped: (files) => {
|
|
2428
|
+
composerRef.current?.addFiles(files);
|
|
2129
2429
|
},
|
|
2430
|
+
maxFiles: maxAttachments,
|
|
2431
|
+
onError,
|
|
2130
2432
|
disabled: !showAttachmentPicker,
|
|
2131
2433
|
className: "flex-1 overflow-hidden",
|
|
2132
2434
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2152,23 +2454,38 @@ var MessageThread = React6__namespace.forwardRef(
|
|
|
2152
2454
|
}
|
|
2153
2455
|
),
|
|
2154
2456
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2155
|
-
|
|
2457
|
+
"div",
|
|
2156
2458
|
{
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2459
|
+
"data-slot": "message-thread-composer",
|
|
2460
|
+
className: "shrink-0 border-t border-neutral-200 p-3 dark:border-neutral-700",
|
|
2461
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2462
|
+
chunkT6PTKWM6_cjs.ChatComposer,
|
|
2463
|
+
{
|
|
2464
|
+
ref: composerRef,
|
|
2465
|
+
value: draft,
|
|
2466
|
+
onValueChange: handleDraftChange,
|
|
2467
|
+
onSend: handleSendMessage,
|
|
2468
|
+
placeholder,
|
|
2469
|
+
maxLength: maxMessageLength,
|
|
2470
|
+
showCharacterCount,
|
|
2471
|
+
isSending,
|
|
2472
|
+
allowAttachments: showAttachmentPicker,
|
|
2473
|
+
acceptedFileTypes: acceptedFileTypes ?? chunkT6PTKWM6_cjs.DEFAULT_ACCEPTED_FILE_TYPES,
|
|
2474
|
+
maxFileSize: maxFileSize ?? chunkT6PTKWM6_cjs.DEFAULT_MAX_FILE_SIZE,
|
|
2475
|
+
maxAttachments,
|
|
2476
|
+
onError,
|
|
2477
|
+
replyTo,
|
|
2478
|
+
onCancelReply: () => setReplyTo(null),
|
|
2479
|
+
inputLabel: "Message",
|
|
2480
|
+
micSlot: showCameraButton ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2481
|
+
chunkT6PTKWM6_cjs.CameraButton,
|
|
2482
|
+
{
|
|
2483
|
+
onCapture: (file) => composerRef.current?.addFiles([file]),
|
|
2484
|
+
disabled: isSending
|
|
2485
|
+
}
|
|
2486
|
+
) : void 0
|
|
2487
|
+
}
|
|
2488
|
+
)
|
|
2172
2489
|
}
|
|
2173
2490
|
),
|
|
2174
2491
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2248,249 +2565,6 @@ function MessagingSplitView({
|
|
|
2248
2565
|
);
|
|
2249
2566
|
}
|
|
2250
2567
|
MessagingSplitView.displayName = "MessagingSplitView";
|
|
2251
|
-
function useMessages(options) {
|
|
2252
|
-
const {
|
|
2253
|
-
initialMessages = [],
|
|
2254
|
-
currentUser,
|
|
2255
|
-
onSend,
|
|
2256
|
-
onRetry,
|
|
2257
|
-
onLoadMore
|
|
2258
|
-
} = options;
|
|
2259
|
-
const [messages, setMessages] = React6__namespace.useState(initialMessages);
|
|
2260
|
-
const [isSending, setIsSending] = React6__namespace.useState(false);
|
|
2261
|
-
const [isLoadingMore, setIsLoadingMore] = React6__namespace.useState(false);
|
|
2262
|
-
React6__namespace.useEffect(() => {
|
|
2263
|
-
setMessages(initialMessages);
|
|
2264
|
-
}, [initialMessages]);
|
|
2265
|
-
const addMessage = React6__namespace.useCallback((message) => {
|
|
2266
|
-
setMessages((prev) => {
|
|
2267
|
-
if (prev.some((m) => m.id === message.id)) {
|
|
2268
|
-
return prev;
|
|
2269
|
-
}
|
|
2270
|
-
return [...prev, message];
|
|
2271
|
-
});
|
|
2272
|
-
}, []);
|
|
2273
|
-
const updateMessage = React6__namespace.useCallback(
|
|
2274
|
-
(messageId, updates) => {
|
|
2275
|
-
setMessages(
|
|
2276
|
-
(prev) => prev.map((m) => m.id === messageId ? { ...m, ...updates } : m)
|
|
2277
|
-
);
|
|
2278
|
-
},
|
|
2279
|
-
[]
|
|
2280
|
-
);
|
|
2281
|
-
const removeMessage = React6__namespace.useCallback((messageId) => {
|
|
2282
|
-
setMessages((prev) => prev.filter((m) => m.id !== messageId));
|
|
2283
|
-
}, []);
|
|
2284
|
-
const updateStatus = React6__namespace.useCallback(
|
|
2285
|
-
(messageId, status) => {
|
|
2286
|
-
updateMessage(messageId, { status });
|
|
2287
|
-
},
|
|
2288
|
-
[updateMessage]
|
|
2289
|
-
);
|
|
2290
|
-
const markAsRead = React6__namespace.useCallback(
|
|
2291
|
-
(messageId) => {
|
|
2292
|
-
updateStatus(messageId, "read");
|
|
2293
|
-
},
|
|
2294
|
-
[updateStatus]
|
|
2295
|
-
);
|
|
2296
|
-
const sendMessage = React6__namespace.useCallback(
|
|
2297
|
-
async (newMessage) => {
|
|
2298
|
-
const optimisticId = `optimistic-${Date.now()}`;
|
|
2299
|
-
const optimisticMessage = {
|
|
2300
|
-
id: optimisticId,
|
|
2301
|
-
type: "text",
|
|
2302
|
-
content: newMessage.content,
|
|
2303
|
-
sender: currentUser,
|
|
2304
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
2305
|
-
status: "sending",
|
|
2306
|
-
attachments: []
|
|
2307
|
-
// Would handle attachment uploads here
|
|
2308
|
-
};
|
|
2309
|
-
addMessage(optimisticMessage);
|
|
2310
|
-
setIsSending(true);
|
|
2311
|
-
try {
|
|
2312
|
-
if (onSend) {
|
|
2313
|
-
const sentMessage = await onSend(newMessage);
|
|
2314
|
-
setMessages(
|
|
2315
|
-
(prev) => prev.map((m) => m.id === optimisticId ? sentMessage : m)
|
|
2316
|
-
);
|
|
2317
|
-
} else {
|
|
2318
|
-
updateStatus(optimisticId, "sent");
|
|
2319
|
-
}
|
|
2320
|
-
} catch {
|
|
2321
|
-
updateStatus(optimisticId, "failed");
|
|
2322
|
-
} finally {
|
|
2323
|
-
setIsSending(false);
|
|
2324
|
-
}
|
|
2325
|
-
},
|
|
2326
|
-
[currentUser, onSend, addMessage, updateStatus]
|
|
2327
|
-
);
|
|
2328
|
-
const retryMessage = React6__namespace.useCallback(
|
|
2329
|
-
async (messageId) => {
|
|
2330
|
-
updateStatus(messageId, "sending");
|
|
2331
|
-
try {
|
|
2332
|
-
if (onRetry) {
|
|
2333
|
-
await onRetry(messageId);
|
|
2334
|
-
updateStatus(messageId, "sent");
|
|
2335
|
-
}
|
|
2336
|
-
} catch {
|
|
2337
|
-
updateStatus(messageId, "failed");
|
|
2338
|
-
}
|
|
2339
|
-
},
|
|
2340
|
-
[onRetry, updateStatus]
|
|
2341
|
-
);
|
|
2342
|
-
const loadMore = React6__namespace.useCallback(async () => {
|
|
2343
|
-
if (isLoadingMore || !onLoadMore) return;
|
|
2344
|
-
setIsLoadingMore(true);
|
|
2345
|
-
try {
|
|
2346
|
-
const olderMessages = await onLoadMore();
|
|
2347
|
-
setMessages((prev) => [...olderMessages, ...prev]);
|
|
2348
|
-
} finally {
|
|
2349
|
-
setIsLoadingMore(false);
|
|
2350
|
-
}
|
|
2351
|
-
}, [isLoadingMore, onLoadMore]);
|
|
2352
|
-
return {
|
|
2353
|
-
messages,
|
|
2354
|
-
addMessage,
|
|
2355
|
-
updateMessage,
|
|
2356
|
-
removeMessage,
|
|
2357
|
-
sendMessage,
|
|
2358
|
-
retryMessage,
|
|
2359
|
-
loadMore,
|
|
2360
|
-
isSending,
|
|
2361
|
-
isLoadingMore,
|
|
2362
|
-
markAsRead,
|
|
2363
|
-
updateStatus
|
|
2364
|
-
};
|
|
2365
|
-
}
|
|
2366
|
-
function useTypingIndicator(options = {}) {
|
|
2367
|
-
const {
|
|
2368
|
-
typingParticipants: initialParticipants = [],
|
|
2369
|
-
debounceTime = 2e3,
|
|
2370
|
-
onTypingStart,
|
|
2371
|
-
onTypingStop
|
|
2372
|
-
} = options;
|
|
2373
|
-
const [participants, setParticipants] = React6__namespace.useState(initialParticipants);
|
|
2374
|
-
const [isLocalTyping, setIsLocalTyping] = React6__namespace.useState(false);
|
|
2375
|
-
const typingTimeoutRef = React6__namespace.useRef(null);
|
|
2376
|
-
React6__namespace.useEffect(() => {
|
|
2377
|
-
setParticipants(initialParticipants);
|
|
2378
|
-
}, [initialParticipants]);
|
|
2379
|
-
const startTyping = React6__namespace.useCallback(() => {
|
|
2380
|
-
if (!isLocalTyping) {
|
|
2381
|
-
setIsLocalTyping(true);
|
|
2382
|
-
onTypingStart?.();
|
|
2383
|
-
}
|
|
2384
|
-
if (typingTimeoutRef.current) {
|
|
2385
|
-
clearTimeout(typingTimeoutRef.current);
|
|
2386
|
-
}
|
|
2387
|
-
typingTimeoutRef.current = setTimeout(() => {
|
|
2388
|
-
setIsLocalTyping(false);
|
|
2389
|
-
onTypingStop?.();
|
|
2390
|
-
}, debounceTime);
|
|
2391
|
-
}, [isLocalTyping, debounceTime, onTypingStart, onTypingStop]);
|
|
2392
|
-
const stopTyping = React6__namespace.useCallback(() => {
|
|
2393
|
-
if (typingTimeoutRef.current) {
|
|
2394
|
-
clearTimeout(typingTimeoutRef.current);
|
|
2395
|
-
}
|
|
2396
|
-
setIsLocalTyping(false);
|
|
2397
|
-
onTypingStop?.();
|
|
2398
|
-
}, [onTypingStop]);
|
|
2399
|
-
React6__namespace.useEffect(() => {
|
|
2400
|
-
return () => {
|
|
2401
|
-
if (typingTimeoutRef.current) {
|
|
2402
|
-
clearTimeout(typingTimeoutRef.current);
|
|
2403
|
-
}
|
|
2404
|
-
};
|
|
2405
|
-
}, []);
|
|
2406
|
-
const typingState = React6__namespace.useMemo(
|
|
2407
|
-
() => ({
|
|
2408
|
-
participants,
|
|
2409
|
-
lastUpdated: /* @__PURE__ */ new Date()
|
|
2410
|
-
}),
|
|
2411
|
-
[participants]
|
|
2412
|
-
);
|
|
2413
|
-
return {
|
|
2414
|
-
typingState,
|
|
2415
|
-
startTyping,
|
|
2416
|
-
stopTyping,
|
|
2417
|
-
setTypingParticipants: setParticipants
|
|
2418
|
-
};
|
|
2419
|
-
}
|
|
2420
|
-
function useMessageScroll(options) {
|
|
2421
|
-
const { messages, currentUserId, threshold = 100 } = options;
|
|
2422
|
-
const scrollContainerRef = React6__namespace.useRef(null);
|
|
2423
|
-
const bottomRef = React6__namespace.useRef(null);
|
|
2424
|
-
const [isScrolledUp, setIsScrolledUp] = React6__namespace.useState(false);
|
|
2425
|
-
const prevMessageCountRef = React6__namespace.useRef(messages.length);
|
|
2426
|
-
React6__namespace.useEffect(() => {
|
|
2427
|
-
const container = scrollContainerRef.current;
|
|
2428
|
-
if (!container) return;
|
|
2429
|
-
const handleScroll = () => {
|
|
2430
|
-
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
2431
|
-
const isAtBottom = scrollHeight - scrollTop - clientHeight < threshold;
|
|
2432
|
-
setIsScrolledUp(!isAtBottom);
|
|
2433
|
-
};
|
|
2434
|
-
container.addEventListener("scroll", handleScroll);
|
|
2435
|
-
return () => container.removeEventListener("scroll", handleScroll);
|
|
2436
|
-
}, [threshold]);
|
|
2437
|
-
const scrollToBottom = React6__namespace.useCallback((smooth = true) => {
|
|
2438
|
-
bottomRef.current?.scrollIntoView({
|
|
2439
|
-
behavior: smooth ? "smooth" : "auto"
|
|
2440
|
-
});
|
|
2441
|
-
}, []);
|
|
2442
|
-
React6__namespace.useEffect(() => {
|
|
2443
|
-
const messageCountChanged = messages.length !== prevMessageCountRef.current;
|
|
2444
|
-
prevMessageCountRef.current = messages.length;
|
|
2445
|
-
if (!messageCountChanged) return;
|
|
2446
|
-
const lastMessage = messages[messages.length - 1];
|
|
2447
|
-
const isOutgoing = lastMessage?.sender.id === currentUserId;
|
|
2448
|
-
if (isOutgoing || !isScrolledUp) {
|
|
2449
|
-
scrollToBottom(true);
|
|
2450
|
-
}
|
|
2451
|
-
}, [messages, currentUserId, isScrolledUp, scrollToBottom]);
|
|
2452
|
-
return {
|
|
2453
|
-
scrollContainerRef,
|
|
2454
|
-
bottomRef,
|
|
2455
|
-
isScrolledUp,
|
|
2456
|
-
scrollToBottom
|
|
2457
|
-
};
|
|
2458
|
-
}
|
|
2459
|
-
function useReadReceipts(options) {
|
|
2460
|
-
const { currentUserId, onMarkRead, threshold = 0.5 } = options;
|
|
2461
|
-
const observerRef = React6__namespace.useRef(null);
|
|
2462
|
-
const observedMessagesRef = React6__namespace.useRef(/* @__PURE__ */ new Set());
|
|
2463
|
-
React6__namespace.useEffect(() => {
|
|
2464
|
-
observerRef.current = new IntersectionObserver(
|
|
2465
|
-
(entries) => {
|
|
2466
|
-
entries.forEach((entry) => {
|
|
2467
|
-
if (entry.isIntersecting) {
|
|
2468
|
-
const messageId = entry.target.getAttribute("data-message-id");
|
|
2469
|
-
if (messageId && !observedMessagesRef.current.has(messageId)) {
|
|
2470
|
-
observedMessagesRef.current.add(messageId);
|
|
2471
|
-
onMarkRead?.(messageId);
|
|
2472
|
-
}
|
|
2473
|
-
}
|
|
2474
|
-
});
|
|
2475
|
-
},
|
|
2476
|
-
{ threshold }
|
|
2477
|
-
);
|
|
2478
|
-
return () => {
|
|
2479
|
-
observerRef.current?.disconnect();
|
|
2480
|
-
};
|
|
2481
|
-
}, [onMarkRead, threshold]);
|
|
2482
|
-
const observeMessage = React6__namespace.useCallback(
|
|
2483
|
-
(element, message) => {
|
|
2484
|
-
if (!element || !observerRef.current) return;
|
|
2485
|
-
if (message.sender.id !== currentUserId && message.status !== "read" && !observedMessagesRef.current.has(message.id)) {
|
|
2486
|
-
element.setAttribute("data-message-id", message.id);
|
|
2487
|
-
observerRef.current.observe(element);
|
|
2488
|
-
}
|
|
2489
|
-
},
|
|
2490
|
-
[currentUserId]
|
|
2491
|
-
);
|
|
2492
|
-
return { observeMessage };
|
|
2493
|
-
}
|
|
2494
2568
|
|
|
2495
2569
|
exports.AttachmentPreview = AttachmentPreview;
|
|
2496
2570
|
exports.CharacterCounter = CharacterCounter;
|
|
@@ -2524,6 +2598,7 @@ exports.sendButtonVariants = sendButtonVariants;
|
|
|
2524
2598
|
exports.useMessageScroll = useMessageScroll;
|
|
2525
2599
|
exports.useMessages = useMessages;
|
|
2526
2600
|
exports.useReadReceipts = useReadReceipts;
|
|
2601
|
+
exports.useTypingEmulation = useTypingEmulation;
|
|
2527
2602
|
exports.useTypingIndicator = useTypingIndicator;
|
|
2528
|
-
//# sourceMappingURL=chunk-
|
|
2529
|
-
//# sourceMappingURL=chunk-
|
|
2603
|
+
//# sourceMappingURL=chunk-XCNDZNY7.cjs.map
|
|
2604
|
+
//# sourceMappingURL=chunk-XCNDZNY7.cjs.map
|