@mieweb/ui 0.9.0 → 0.10.0
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/MIGRATION.md +112 -0
- package/dist/{Animated-Cs-naa2g.d.cts → Animated-XmH6k8pZ.d.cts} +16 -3
- package/dist/{Animated-Cs-naa2g.d.ts → Animated-XmH6k8pZ.d.ts} +16 -3
- package/dist/{chunk-XUVEF2RQ.js → chunk-24SEAAOH.js} +2 -2
- package/dist/{chunk-XUVEF2RQ.js.map → chunk-24SEAAOH.js.map} +1 -1
- package/dist/{chunk-FUFBRKOG.cjs → chunk-2WBPNXO2.cjs} +21 -21
- package/dist/{chunk-FUFBRKOG.cjs.map → chunk-2WBPNXO2.cjs.map} +1 -1
- package/dist/{chunk-IUDY6S7I.cjs → chunk-37UZHCLM.cjs} +35 -14
- package/dist/chunk-37UZHCLM.cjs.map +1 -0
- package/dist/{chunk-L52N6BEN.cjs → chunk-AUOTJQGO.cjs} +7 -7
- package/dist/{chunk-L52N6BEN.cjs.map → chunk-AUOTJQGO.cjs.map} +1 -1
- package/dist/{chunk-6XDPJWSC.cjs → chunk-E7G22IQM.cjs} +32 -28
- package/dist/chunk-E7G22IQM.cjs.map +1 -0
- package/dist/chunk-IIFQ6BSJ.js +1888 -0
- package/dist/chunk-IIFQ6BSJ.js.map +1 -0
- package/dist/{chunk-ONNY7UO4.js → chunk-JDJQTKTX.js} +3 -3
- package/dist/{chunk-ONNY7UO4.js.map → chunk-JDJQTKTX.js.map} +1 -1
- package/dist/{chunk-BJKAVWED.js → chunk-MBWF36DJ.js} +35 -14
- package/dist/chunk-MBWF36DJ.js.map +1 -0
- package/dist/chunk-NV2CWBI2.cjs +290 -0
- package/dist/chunk-NV2CWBI2.cjs.map +1 -0
- package/dist/{chunk-FQBTGS3E.cjs → chunk-OFUKTVBN.cjs} +363 -285
- package/dist/chunk-OFUKTVBN.cjs.map +1 -0
- package/dist/{chunk-7KN6J7XV.cjs → chunk-OMAACCFW.cjs} +7 -7
- package/dist/{chunk-7KN6J7XV.cjs.map → chunk-OMAACCFW.cjs.map} +1 -1
- package/dist/chunk-OYQ7GYAA.cjs +1921 -0
- package/dist/chunk-OYQ7GYAA.cjs.map +1 -0
- package/dist/{chunk-YJP6DUFO.cjs → chunk-PKD3JLS2.cjs} +6 -6
- package/dist/{chunk-YJP6DUFO.cjs.map → chunk-PKD3JLS2.cjs.map} +1 -1
- package/dist/{chunk-CXDMZT45.cjs → chunk-PY7XAG4D.cjs} +27 -4
- package/dist/chunk-PY7XAG4D.cjs.map +1 -0
- package/dist/{chunk-5H7SAEQO.js → chunk-QKFJDQEK.js} +3 -3
- package/dist/{chunk-5H7SAEQO.js.map → chunk-QKFJDQEK.js.map} +1 -1
- package/dist/{chunk-XJTFZLWH.cjs → chunk-QO6PRHDX.cjs} +2 -2
- package/dist/{chunk-XJTFZLWH.cjs.map → chunk-QO6PRHDX.cjs.map} +1 -1
- package/dist/{chunk-OHDIJOGG.js → chunk-RD3JDFYJ.js} +3 -3
- package/dist/{chunk-OHDIJOGG.js.map → chunk-RD3JDFYJ.js.map} +1 -1
- package/dist/{chunk-4SM3I7WE.cjs → chunk-RXPEZXAB.cjs} +67 -38
- package/dist/chunk-RXPEZXAB.cjs.map +1 -0
- package/dist/{chunk-VTR2KGLY.cjs → chunk-S3BCHPXT.cjs} +9 -9
- package/dist/{chunk-VTR2KGLY.cjs.map → chunk-S3BCHPXT.cjs.map} +1 -1
- package/dist/{chunk-KU3XRHWA.js → chunk-TZX76ZNZ.js} +67 -38
- package/dist/chunk-TZX76ZNZ.js.map +1 -0
- package/dist/{chunk-MXQ7J4BB.js → chunk-VSVGMMNO.js} +361 -284
- package/dist/chunk-VSVGMMNO.js.map +1 -0
- package/dist/{chunk-Q7G56ZIO.js → chunk-WUBMDOTG.js} +3 -3
- package/dist/{chunk-Q7G56ZIO.js.map → chunk-WUBMDOTG.js.map} +1 -1
- package/dist/{chunk-DGRXAIWK.js → chunk-X5YM3KXY.js} +27 -4
- package/dist/chunk-X5YM3KXY.js.map +1 -0
- package/dist/chunk-XCP454Q6.js +263 -0
- package/dist/chunk-XCP454Q6.js.map +1 -0
- package/dist/{chunk-I3DQXUSC.js → chunk-Y6DFMDMH.js} +3 -3
- package/dist/{chunk-I3DQXUSC.js.map → chunk-Y6DFMDMH.js.map} +1 -1
- package/dist/{chunk-ODYQYLO2.js → chunk-YSEMAI4I.js} +8 -4
- package/dist/chunk-YSEMAI4I.js.map +1 -0
- package/dist/components/AlertDialog/index.cjs +4 -4
- package/dist/components/AlertDialog/index.js +3 -3
- package/dist/components/Collapsible/index.cjs +6 -4
- package/dist/components/Collapsible/index.js +3 -1
- package/dist/components/Dropdown/index.cjs +9 -8
- package/dist/components/Dropdown/index.js +2 -1
- package/dist/components/KeyboardShortcutsOverlay/index.cjs +5 -5
- package/dist/components/KeyboardShortcutsOverlay/index.js +3 -3
- package/dist/components/MediaEditor/index.cjs +8 -8
- package/dist/components/MediaEditor/index.js +3 -3
- package/dist/components/Modal/index.cjs +10 -10
- package/dist/components/Modal/index.js +2 -2
- package/dist/components/RichTextEditor/index.cjs +6 -5
- package/dist/components/RichTextEditor/index.js +3 -2
- package/dist/components/SuperChat/index.cjs +40 -17
- package/dist/components/SuperChat/index.cjs.map +1 -1
- package/dist/components/SuperChat/index.d.cts +20 -7
- package/dist/components/SuperChat/index.d.ts +20 -7
- package/dist/components/SuperChat/index.js +35 -12
- package/dist/components/SuperChat/index.js.map +1 -1
- 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.d.cts +3 -3
- package/dist/components/SuperChat/plugins/index.d.ts +3 -3
- package/dist/components/SuperChat/plugins/index.js +9 -2
- package/dist/components/SuperChat/plugins/index.js.map +1 -1
- package/dist/esheet.cjs +6 -6
- package/dist/esheet.js +3 -3
- package/dist/hooks/index.cjs +13 -13
- package/dist/hooks/index.js +1 -1
- package/dist/index.cjs +720 -1671
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +347 -347
- package/dist/index.d.ts +347 -347
- package/dist/index.js +459 -1405
- package/dist/index.js.map +1 -1
- package/dist/motion.cjs +7 -7
- package/dist/motion.d.cts +1 -1
- package/dist/motion.d.ts +1 -1
- package/dist/motion.js +2 -2
- package/dist/styles.css +1 -1
- package/dist/tailwind-preset.cjs +4 -4
- package/dist/tailwind-preset.js +1 -1
- package/dist/{types-DZDroUW8.d.cts → types-B97JNRNQ.d.cts} +1 -1
- package/dist/{types-B2RLmdVd.d.ts → types-CnPEwzlp.d.cts} +6 -2
- package/dist/{types-B2RLmdVd.d.cts → types-CnPEwzlp.d.ts} +6 -2
- package/dist/{types-9vK_YdEb.d.ts → types-gZXrfLj6.d.ts} +1 -1
- package/package.json +2 -1
- package/dist/chunk-4SM3I7WE.cjs.map +0 -1
- package/dist/chunk-5BVFVXHN.cjs +0 -1244
- package/dist/chunk-5BVFVXHN.cjs.map +0 -1
- package/dist/chunk-6XDPJWSC.cjs.map +0 -1
- package/dist/chunk-BJKAVWED.js.map +0 -1
- package/dist/chunk-BN75TLUN.cjs +0 -163
- package/dist/chunk-BN75TLUN.cjs.map +0 -1
- package/dist/chunk-CXDMZT45.cjs.map +0 -1
- package/dist/chunk-DGRXAIWK.js.map +0 -1
- package/dist/chunk-FQBTGS3E.cjs.map +0 -1
- package/dist/chunk-IUDY6S7I.cjs.map +0 -1
- package/dist/chunk-KU3XRHWA.js.map +0 -1
- package/dist/chunk-MXQ7J4BB.js.map +0 -1
- package/dist/chunk-O6GJ4IYS.js +0 -1210
- package/dist/chunk-O6GJ4IYS.js.map +0 -1
- package/dist/chunk-ODYQYLO2.js.map +0 -1
- package/dist/chunk-WYMO3IIK.js +0 -136
- package/dist/chunk-WYMO3IIK.js.map +0 -1
|
@@ -1,9 +1,288 @@
|
|
|
1
|
-
import { DragDropZone,
|
|
1
|
+
import { notifyComposerMigrationOnce, DragDropZone, ChatComposer, DEFAULT_MAX_FILE_SIZE, DEFAULT_ACCEPTED_FILE_TYPES, CameraButton } from './chunk-IIFQ6BSJ.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 = React5.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 = React5.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 = React5.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];
|
|
@@ -1098,7 +1377,7 @@ function formatLastSeen(date) {
|
|
|
1098
1377
|
if (diffDays < 7) return `${diffDays}d ago`;
|
|
1099
1378
|
return date.toLocaleDateString();
|
|
1100
1379
|
}
|
|
1101
|
-
var ConversationHeader =
|
|
1380
|
+
var ConversationHeader = React.forwardRef(
|
|
1102
1381
|
({
|
|
1103
1382
|
className,
|
|
1104
1383
|
size,
|
|
@@ -1220,7 +1499,7 @@ var ConversationHeader = React5.forwardRef(
|
|
|
1220
1499
|
}
|
|
1221
1500
|
);
|
|
1222
1501
|
ConversationHeader.displayName = "ConversationHeader";
|
|
1223
|
-
var ConversationListItem =
|
|
1502
|
+
var ConversationListItem = React.forwardRef(({ className, conversation, isSelected, onSelect, ...props }, ref) => {
|
|
1224
1503
|
const participant = conversation.participants.find((p) => !p.isCurrentUser);
|
|
1225
1504
|
const title = getConversationTitle(conversation, participant);
|
|
1226
1505
|
const avatarUrl = conversation.avatarUrl || participant?.avatarUrl;
|
|
@@ -1428,7 +1707,7 @@ function ConversationListSkeleton({
|
|
|
1428
1707
|
}
|
|
1429
1708
|
ConversationListSkeleton.displayName = "ConversationListSkeleton";
|
|
1430
1709
|
function LightboxModal({ attachment, onClose }) {
|
|
1431
|
-
|
|
1710
|
+
React.useEffect(() => {
|
|
1432
1711
|
const handleKeyDown = (event) => {
|
|
1433
1712
|
if (event.key === "Escape") {
|
|
1434
1713
|
onClose();
|
|
@@ -1525,7 +1804,7 @@ function LightboxModal({ attachment, onClose }) {
|
|
|
1525
1804
|
);
|
|
1526
1805
|
}
|
|
1527
1806
|
LightboxModal.displayName = "LightboxModal";
|
|
1528
|
-
var MessageThread =
|
|
1807
|
+
var MessageThread = React.forwardRef(
|
|
1529
1808
|
({
|
|
1530
1809
|
conversation,
|
|
1531
1810
|
messages,
|
|
@@ -1556,8 +1835,23 @@ var MessageThread = React5.forwardRef(
|
|
|
1556
1835
|
onError,
|
|
1557
1836
|
className
|
|
1558
1837
|
}, ref) => {
|
|
1559
|
-
const [lightboxAttachment, setLightboxAttachment] =
|
|
1560
|
-
const [replyTo, setReplyTo] =
|
|
1838
|
+
const [lightboxAttachment, setLightboxAttachment] = React.useState(null);
|
|
1839
|
+
const [replyTo, setReplyTo] = React.useState(null);
|
|
1840
|
+
const composerRef = React.useRef(null);
|
|
1841
|
+
React.useEffect(() => {
|
|
1842
|
+
notifyComposerMigrationOnce("MessageThread");
|
|
1843
|
+
}, []);
|
|
1844
|
+
const [draft, setDraft] = React.useState("");
|
|
1845
|
+
const draftEpochRef = React.useRef(0);
|
|
1846
|
+
const handleDraftChange = React.useCallback((value) => {
|
|
1847
|
+
draftEpochRef.current += 1;
|
|
1848
|
+
setDraft(value);
|
|
1849
|
+
}, []);
|
|
1850
|
+
const { stopTyping } = useTypingEmulation({
|
|
1851
|
+
value: draft,
|
|
1852
|
+
onTypingStart: eventHandlers.onTypingStart,
|
|
1853
|
+
onTypingStop: eventHandlers.onTypingStop
|
|
1854
|
+
});
|
|
1561
1855
|
const participant = conversation?.type === "direct" ? conversation.participants.find((p) => p.id !== currentUser.id) : void 0;
|
|
1562
1856
|
const handleAttachmentClick = (attachment, message) => {
|
|
1563
1857
|
if (attachment.type === "image" || attachment.type === "video") {
|
|
@@ -1566,12 +1860,21 @@ var MessageThread = React5.forwardRef(
|
|
|
1566
1860
|
eventHandlers.onAttachmentClick?.(attachment, message);
|
|
1567
1861
|
};
|
|
1568
1862
|
const handleSendMessage = async (newMessage) => {
|
|
1863
|
+
stopTyping();
|
|
1569
1864
|
const messageWithReply = {
|
|
1570
1865
|
...newMessage,
|
|
1571
1866
|
replyToId: replyTo?.id || newMessage.replyToId
|
|
1572
1867
|
};
|
|
1573
1868
|
setReplyTo(null);
|
|
1574
|
-
|
|
1869
|
+
const epoch = draftEpochRef.current;
|
|
1870
|
+
try {
|
|
1871
|
+
await Promise.resolve(eventHandlers.onSendMessage?.(messageWithReply));
|
|
1872
|
+
} catch (error) {
|
|
1873
|
+
if (draftEpochRef.current === epoch) {
|
|
1874
|
+
setDraft(newMessage.content);
|
|
1875
|
+
}
|
|
1876
|
+
throw error;
|
|
1877
|
+
}
|
|
1575
1878
|
};
|
|
1576
1879
|
return /* @__PURE__ */ jsxs(
|
|
1577
1880
|
"div",
|
|
@@ -1597,9 +1900,11 @@ var MessageThread = React5.forwardRef(
|
|
|
1597
1900
|
/* @__PURE__ */ jsx(
|
|
1598
1901
|
DragDropZone,
|
|
1599
1902
|
{
|
|
1600
|
-
onFilesDropped: () => {
|
|
1601
|
-
|
|
1903
|
+
onFilesDropped: (files) => {
|
|
1904
|
+
composerRef.current?.addFiles(files);
|
|
1602
1905
|
},
|
|
1906
|
+
maxFiles: maxAttachments,
|
|
1907
|
+
onError,
|
|
1603
1908
|
disabled: !showAttachmentPicker,
|
|
1604
1909
|
className: "flex-1 overflow-hidden",
|
|
1605
1910
|
children: /* @__PURE__ */ jsx(
|
|
@@ -1625,23 +1930,38 @@ var MessageThread = React5.forwardRef(
|
|
|
1625
1930
|
}
|
|
1626
1931
|
),
|
|
1627
1932
|
/* @__PURE__ */ jsx(
|
|
1628
|
-
|
|
1933
|
+
"div",
|
|
1629
1934
|
{
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1935
|
+
"data-slot": "message-thread-composer",
|
|
1936
|
+
className: "shrink-0 border-t border-neutral-200 p-3 dark:border-neutral-700",
|
|
1937
|
+
children: /* @__PURE__ */ jsx(
|
|
1938
|
+
ChatComposer,
|
|
1939
|
+
{
|
|
1940
|
+
ref: composerRef,
|
|
1941
|
+
value: draft,
|
|
1942
|
+
onValueChange: handleDraftChange,
|
|
1943
|
+
onSend: handleSendMessage,
|
|
1944
|
+
placeholder,
|
|
1945
|
+
maxLength: maxMessageLength,
|
|
1946
|
+
showCharacterCount,
|
|
1947
|
+
isSending,
|
|
1948
|
+
allowAttachments: showAttachmentPicker,
|
|
1949
|
+
acceptedFileTypes: acceptedFileTypes ?? DEFAULT_ACCEPTED_FILE_TYPES,
|
|
1950
|
+
maxFileSize: maxFileSize ?? DEFAULT_MAX_FILE_SIZE,
|
|
1951
|
+
maxAttachments,
|
|
1952
|
+
onError,
|
|
1953
|
+
replyTo,
|
|
1954
|
+
onCancelReply: () => setReplyTo(null),
|
|
1955
|
+
inputLabel: "Message",
|
|
1956
|
+
micSlot: showCameraButton ? /* @__PURE__ */ jsx(
|
|
1957
|
+
CameraButton,
|
|
1958
|
+
{
|
|
1959
|
+
onCapture: (file) => composerRef.current?.addFiles([file]),
|
|
1960
|
+
disabled: isSending
|
|
1961
|
+
}
|
|
1962
|
+
) : void 0
|
|
1963
|
+
}
|
|
1964
|
+
)
|
|
1645
1965
|
}
|
|
1646
1966
|
),
|
|
1647
1967
|
/* @__PURE__ */ jsx(
|
|
@@ -1721,250 +2041,7 @@ function MessagingSplitView({
|
|
|
1721
2041
|
);
|
|
1722
2042
|
}
|
|
1723
2043
|
MessagingSplitView.displayName = "MessagingSplitView";
|
|
1724
|
-
function useMessages(options) {
|
|
1725
|
-
const {
|
|
1726
|
-
initialMessages = [],
|
|
1727
|
-
currentUser,
|
|
1728
|
-
onSend,
|
|
1729
|
-
onRetry,
|
|
1730
|
-
onLoadMore
|
|
1731
|
-
} = options;
|
|
1732
|
-
const [messages, setMessages] = React5.useState(initialMessages);
|
|
1733
|
-
const [isSending, setIsSending] = React5.useState(false);
|
|
1734
|
-
const [isLoadingMore, setIsLoadingMore] = React5.useState(false);
|
|
1735
|
-
React5.useEffect(() => {
|
|
1736
|
-
setMessages(initialMessages);
|
|
1737
|
-
}, [initialMessages]);
|
|
1738
|
-
const addMessage = React5.useCallback((message) => {
|
|
1739
|
-
setMessages((prev) => {
|
|
1740
|
-
if (prev.some((m) => m.id === message.id)) {
|
|
1741
|
-
return prev;
|
|
1742
|
-
}
|
|
1743
|
-
return [...prev, message];
|
|
1744
|
-
});
|
|
1745
|
-
}, []);
|
|
1746
|
-
const updateMessage = React5.useCallback(
|
|
1747
|
-
(messageId, updates) => {
|
|
1748
|
-
setMessages(
|
|
1749
|
-
(prev) => prev.map((m) => m.id === messageId ? { ...m, ...updates } : m)
|
|
1750
|
-
);
|
|
1751
|
-
},
|
|
1752
|
-
[]
|
|
1753
|
-
);
|
|
1754
|
-
const removeMessage = React5.useCallback((messageId) => {
|
|
1755
|
-
setMessages((prev) => prev.filter((m) => m.id !== messageId));
|
|
1756
|
-
}, []);
|
|
1757
|
-
const updateStatus = React5.useCallback(
|
|
1758
|
-
(messageId, status) => {
|
|
1759
|
-
updateMessage(messageId, { status });
|
|
1760
|
-
},
|
|
1761
|
-
[updateMessage]
|
|
1762
|
-
);
|
|
1763
|
-
const markAsRead = React5.useCallback(
|
|
1764
|
-
(messageId) => {
|
|
1765
|
-
updateStatus(messageId, "read");
|
|
1766
|
-
},
|
|
1767
|
-
[updateStatus]
|
|
1768
|
-
);
|
|
1769
|
-
const sendMessage = React5.useCallback(
|
|
1770
|
-
async (newMessage) => {
|
|
1771
|
-
const optimisticId = `optimistic-${Date.now()}`;
|
|
1772
|
-
const optimisticMessage = {
|
|
1773
|
-
id: optimisticId,
|
|
1774
|
-
type: "text",
|
|
1775
|
-
content: newMessage.content,
|
|
1776
|
-
sender: currentUser,
|
|
1777
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
1778
|
-
status: "sending",
|
|
1779
|
-
attachments: []
|
|
1780
|
-
// Would handle attachment uploads here
|
|
1781
|
-
};
|
|
1782
|
-
addMessage(optimisticMessage);
|
|
1783
|
-
setIsSending(true);
|
|
1784
|
-
try {
|
|
1785
|
-
if (onSend) {
|
|
1786
|
-
const sentMessage = await onSend(newMessage);
|
|
1787
|
-
setMessages(
|
|
1788
|
-
(prev) => prev.map((m) => m.id === optimisticId ? sentMessage : m)
|
|
1789
|
-
);
|
|
1790
|
-
} else {
|
|
1791
|
-
updateStatus(optimisticId, "sent");
|
|
1792
|
-
}
|
|
1793
|
-
} catch {
|
|
1794
|
-
updateStatus(optimisticId, "failed");
|
|
1795
|
-
} finally {
|
|
1796
|
-
setIsSending(false);
|
|
1797
|
-
}
|
|
1798
|
-
},
|
|
1799
|
-
[currentUser, onSend, addMessage, updateStatus]
|
|
1800
|
-
);
|
|
1801
|
-
const retryMessage = React5.useCallback(
|
|
1802
|
-
async (messageId) => {
|
|
1803
|
-
updateStatus(messageId, "sending");
|
|
1804
|
-
try {
|
|
1805
|
-
if (onRetry) {
|
|
1806
|
-
await onRetry(messageId);
|
|
1807
|
-
updateStatus(messageId, "sent");
|
|
1808
|
-
}
|
|
1809
|
-
} catch {
|
|
1810
|
-
updateStatus(messageId, "failed");
|
|
1811
|
-
}
|
|
1812
|
-
},
|
|
1813
|
-
[onRetry, updateStatus]
|
|
1814
|
-
);
|
|
1815
|
-
const loadMore = React5.useCallback(async () => {
|
|
1816
|
-
if (isLoadingMore || !onLoadMore) return;
|
|
1817
|
-
setIsLoadingMore(true);
|
|
1818
|
-
try {
|
|
1819
|
-
const olderMessages = await onLoadMore();
|
|
1820
|
-
setMessages((prev) => [...olderMessages, ...prev]);
|
|
1821
|
-
} finally {
|
|
1822
|
-
setIsLoadingMore(false);
|
|
1823
|
-
}
|
|
1824
|
-
}, [isLoadingMore, onLoadMore]);
|
|
1825
|
-
return {
|
|
1826
|
-
messages,
|
|
1827
|
-
addMessage,
|
|
1828
|
-
updateMessage,
|
|
1829
|
-
removeMessage,
|
|
1830
|
-
sendMessage,
|
|
1831
|
-
retryMessage,
|
|
1832
|
-
loadMore,
|
|
1833
|
-
isSending,
|
|
1834
|
-
isLoadingMore,
|
|
1835
|
-
markAsRead,
|
|
1836
|
-
updateStatus
|
|
1837
|
-
};
|
|
1838
|
-
}
|
|
1839
|
-
function useTypingIndicator(options = {}) {
|
|
1840
|
-
const {
|
|
1841
|
-
typingParticipants: initialParticipants = [],
|
|
1842
|
-
debounceTime = 2e3,
|
|
1843
|
-
onTypingStart,
|
|
1844
|
-
onTypingStop
|
|
1845
|
-
} = options;
|
|
1846
|
-
const [participants, setParticipants] = React5.useState(initialParticipants);
|
|
1847
|
-
const [isLocalTyping, setIsLocalTyping] = React5.useState(false);
|
|
1848
|
-
const typingTimeoutRef = React5.useRef(null);
|
|
1849
|
-
React5.useEffect(() => {
|
|
1850
|
-
setParticipants(initialParticipants);
|
|
1851
|
-
}, [initialParticipants]);
|
|
1852
|
-
const startTyping = React5.useCallback(() => {
|
|
1853
|
-
if (!isLocalTyping) {
|
|
1854
|
-
setIsLocalTyping(true);
|
|
1855
|
-
onTypingStart?.();
|
|
1856
|
-
}
|
|
1857
|
-
if (typingTimeoutRef.current) {
|
|
1858
|
-
clearTimeout(typingTimeoutRef.current);
|
|
1859
|
-
}
|
|
1860
|
-
typingTimeoutRef.current = setTimeout(() => {
|
|
1861
|
-
setIsLocalTyping(false);
|
|
1862
|
-
onTypingStop?.();
|
|
1863
|
-
}, debounceTime);
|
|
1864
|
-
}, [isLocalTyping, debounceTime, onTypingStart, onTypingStop]);
|
|
1865
|
-
const stopTyping = React5.useCallback(() => {
|
|
1866
|
-
if (typingTimeoutRef.current) {
|
|
1867
|
-
clearTimeout(typingTimeoutRef.current);
|
|
1868
|
-
}
|
|
1869
|
-
setIsLocalTyping(false);
|
|
1870
|
-
onTypingStop?.();
|
|
1871
|
-
}, [onTypingStop]);
|
|
1872
|
-
React5.useEffect(() => {
|
|
1873
|
-
return () => {
|
|
1874
|
-
if (typingTimeoutRef.current) {
|
|
1875
|
-
clearTimeout(typingTimeoutRef.current);
|
|
1876
|
-
}
|
|
1877
|
-
};
|
|
1878
|
-
}, []);
|
|
1879
|
-
const typingState = React5.useMemo(
|
|
1880
|
-
() => ({
|
|
1881
|
-
participants,
|
|
1882
|
-
lastUpdated: /* @__PURE__ */ new Date()
|
|
1883
|
-
}),
|
|
1884
|
-
[participants]
|
|
1885
|
-
);
|
|
1886
|
-
return {
|
|
1887
|
-
typingState,
|
|
1888
|
-
startTyping,
|
|
1889
|
-
stopTyping,
|
|
1890
|
-
setTypingParticipants: setParticipants
|
|
1891
|
-
};
|
|
1892
|
-
}
|
|
1893
|
-
function useMessageScroll(options) {
|
|
1894
|
-
const { messages, currentUserId, threshold = 100 } = options;
|
|
1895
|
-
const scrollContainerRef = React5.useRef(null);
|
|
1896
|
-
const bottomRef = React5.useRef(null);
|
|
1897
|
-
const [isScrolledUp, setIsScrolledUp] = React5.useState(false);
|
|
1898
|
-
const prevMessageCountRef = React5.useRef(messages.length);
|
|
1899
|
-
React5.useEffect(() => {
|
|
1900
|
-
const container = scrollContainerRef.current;
|
|
1901
|
-
if (!container) return;
|
|
1902
|
-
const handleScroll = () => {
|
|
1903
|
-
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
1904
|
-
const isAtBottom = scrollHeight - scrollTop - clientHeight < threshold;
|
|
1905
|
-
setIsScrolledUp(!isAtBottom);
|
|
1906
|
-
};
|
|
1907
|
-
container.addEventListener("scroll", handleScroll);
|
|
1908
|
-
return () => container.removeEventListener("scroll", handleScroll);
|
|
1909
|
-
}, [threshold]);
|
|
1910
|
-
const scrollToBottom = React5.useCallback((smooth = true) => {
|
|
1911
|
-
bottomRef.current?.scrollIntoView({
|
|
1912
|
-
behavior: smooth ? "smooth" : "auto"
|
|
1913
|
-
});
|
|
1914
|
-
}, []);
|
|
1915
|
-
React5.useEffect(() => {
|
|
1916
|
-
const messageCountChanged = messages.length !== prevMessageCountRef.current;
|
|
1917
|
-
prevMessageCountRef.current = messages.length;
|
|
1918
|
-
if (!messageCountChanged) return;
|
|
1919
|
-
const lastMessage = messages[messages.length - 1];
|
|
1920
|
-
const isOutgoing = lastMessage?.sender.id === currentUserId;
|
|
1921
|
-
if (isOutgoing || !isScrolledUp) {
|
|
1922
|
-
scrollToBottom(true);
|
|
1923
|
-
}
|
|
1924
|
-
}, [messages, currentUserId, isScrolledUp, scrollToBottom]);
|
|
1925
|
-
return {
|
|
1926
|
-
scrollContainerRef,
|
|
1927
|
-
bottomRef,
|
|
1928
|
-
isScrolledUp,
|
|
1929
|
-
scrollToBottom
|
|
1930
|
-
};
|
|
1931
|
-
}
|
|
1932
|
-
function useReadReceipts(options) {
|
|
1933
|
-
const { currentUserId, onMarkRead, threshold = 0.5 } = options;
|
|
1934
|
-
const observerRef = React5.useRef(null);
|
|
1935
|
-
const observedMessagesRef = React5.useRef(/* @__PURE__ */ new Set());
|
|
1936
|
-
React5.useEffect(() => {
|
|
1937
|
-
observerRef.current = new IntersectionObserver(
|
|
1938
|
-
(entries) => {
|
|
1939
|
-
entries.forEach((entry) => {
|
|
1940
|
-
if (entry.isIntersecting) {
|
|
1941
|
-
const messageId = entry.target.getAttribute("data-message-id");
|
|
1942
|
-
if (messageId && !observedMessagesRef.current.has(messageId)) {
|
|
1943
|
-
observedMessagesRef.current.add(messageId);
|
|
1944
|
-
onMarkRead?.(messageId);
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1947
|
-
});
|
|
1948
|
-
},
|
|
1949
|
-
{ threshold }
|
|
1950
|
-
);
|
|
1951
|
-
return () => {
|
|
1952
|
-
observerRef.current?.disconnect();
|
|
1953
|
-
};
|
|
1954
|
-
}, [onMarkRead, threshold]);
|
|
1955
|
-
const observeMessage = React5.useCallback(
|
|
1956
|
-
(element, message) => {
|
|
1957
|
-
if (!element || !observerRef.current) return;
|
|
1958
|
-
if (message.sender.id !== currentUserId && message.status !== "read" && !observedMessagesRef.current.has(message.id)) {
|
|
1959
|
-
element.setAttribute("data-message-id", message.id);
|
|
1960
|
-
observerRef.current.observe(element);
|
|
1961
|
-
}
|
|
1962
|
-
},
|
|
1963
|
-
[currentUserId]
|
|
1964
|
-
);
|
|
1965
|
-
return { observeMessage };
|
|
1966
|
-
}
|
|
1967
2044
|
|
|
1968
|
-
export { AttachmentPreview, ConversationHeader, ConversationListItem, ConversationListSkeleton, DateSeparator, EmptyState, LightboxModal, LoadMoreButton, MessageBubble, MessageList, MessageStatusIcon, MessageThread, MessagingSplitView, ReadReceiptIndicator, SkeletonMessage, TypingIndicator, bubbleVariants, formatDateLabel, formatFileSize, formatLastSeen, getConversationSubtitle, getConversationTitle, groupMessagesByDate, headerVariants, isSameSenderGroup, useMessageScroll, useMessages, useReadReceipts, useTypingIndicator };
|
|
1969
|
-
//# sourceMappingURL=chunk-
|
|
1970
|
-
//# sourceMappingURL=chunk-
|
|
2045
|
+
export { AttachmentPreview, ConversationHeader, ConversationListItem, ConversationListSkeleton, DateSeparator, EmptyState, LightboxModal, LoadMoreButton, MessageBubble, MessageList, MessageStatusIcon, MessageThread, MessagingSplitView, ReadReceiptIndicator, SkeletonMessage, TypingIndicator, bubbleVariants, formatDateLabel, formatFileSize, formatLastSeen, getConversationSubtitle, getConversationTitle, groupMessagesByDate, headerVariants, isSameSenderGroup, useMessageScroll, useMessages, useReadReceipts, useTypingEmulation, useTypingIndicator };
|
|
2046
|
+
//# sourceMappingURL=chunk-VSVGMMNO.js.map
|
|
2047
|
+
//# sourceMappingURL=chunk-VSVGMMNO.js.map
|