@scalemule/chat 0.0.7 → 0.0.9
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/react-components/ChatInput.d.ts +10 -2
- package/dist/react-components/ChatInput.d.ts.map +1 -1
- package/dist/react-components/ChatMessageItem.d.ts +13 -2
- package/dist/react-components/ChatMessageItem.d.ts.map +1 -1
- package/dist/react-components/ChatMessageList.d.ts +20 -4
- package/dist/react-components/ChatMessageList.d.ts.map +1 -1
- package/dist/react-components/ChatThread.d.ts +8 -1
- package/dist/react-components/ChatThread.d.ts.map +1 -1
- package/dist/react-components/EmojiPicker.d.ts +8 -1
- package/dist/react-components/EmojiPicker.d.ts.map +1 -1
- package/dist/react-components/ReactionBar.d.ts +10 -0
- package/dist/react-components/ReactionBar.d.ts.map +1 -0
- package/dist/react-components/ReportDialog.d.ts +30 -0
- package/dist/react-components/ReportDialog.d.ts.map +1 -0
- package/dist/react-components/index.d.ts +3 -1
- package/dist/react-components/index.d.ts.map +1 -1
- package/dist/react.cjs +1981 -559
- package/dist/react.d.cts +97 -13
- package/dist/react.d.ts +1 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +1913 -494
- package/dist/support-widget.global.js +50 -25
- package/dist/widget/styles.d.ts +1 -1
- package/dist/widget/styles.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/react.cjs
CHANGED
|
@@ -1,561 +1,1528 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chunkW2PWFS3E_cjs = require('./chunk-W2PWFS3E.cjs');
|
|
4
|
-
var
|
|
4
|
+
var React4 = require('react');
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
var reactDom = require('react-dom');
|
|
6
7
|
|
|
7
8
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
9
|
|
|
9
|
-
var
|
|
10
|
+
var React4__default = /*#__PURE__*/_interopDefault(React4);
|
|
10
11
|
|
|
11
12
|
function ChatInput({
|
|
12
13
|
onSend,
|
|
13
14
|
onTypingChange,
|
|
14
15
|
onUploadAttachment,
|
|
15
|
-
|
|
16
|
+
onDeleteAttachment,
|
|
17
|
+
onValidateFile,
|
|
18
|
+
placeholder = "Type a message...",
|
|
19
|
+
disabled = false,
|
|
20
|
+
maxAttachments = 5,
|
|
21
|
+
accept = "image/*,video/*"
|
|
16
22
|
}) {
|
|
17
|
-
const [
|
|
18
|
-
const [attachments, setAttachments] =
|
|
19
|
-
const [
|
|
20
|
-
const [
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const [text, setText] = React4.useState("");
|
|
24
|
+
const [attachments, setAttachments] = React4.useState([]);
|
|
25
|
+
const [isDragOver, setIsDragOver] = React4.useState(false);
|
|
26
|
+
const [isSending, setIsSending] = React4.useState(false);
|
|
27
|
+
const textareaRef = React4.useRef(null);
|
|
28
|
+
const fileInputRef = React4.useRef(null);
|
|
29
|
+
const typingTimerRef = React4.useRef(null);
|
|
30
|
+
const dragCounterRef = React4.useRef(0);
|
|
31
|
+
const hasReadyAttachments = attachments.some((a) => a.status === "ready");
|
|
32
|
+
const hasUploadingAttachments = attachments.some(
|
|
33
|
+
(a) => a.status === "uploading"
|
|
26
34
|
);
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
};
|
|
37
|
-
const handleFiles = async (fileList) => {
|
|
38
|
-
if (!onUploadAttachment) return;
|
|
39
|
-
const files = Array.from(fileList);
|
|
40
|
-
for (const file of files) {
|
|
41
|
-
const id = `${file.name}:${file.size}:${Date.now()}:${Math.random().toString(36).slice(2)}`;
|
|
42
|
-
setAttachments((current) => [
|
|
43
|
-
...current,
|
|
44
|
-
{
|
|
45
|
-
id,
|
|
46
|
-
fileName: file.name,
|
|
47
|
-
progress: 0
|
|
35
|
+
const canSend = (text.trim() || hasReadyAttachments) && !hasUploadingAttachments && !isSending;
|
|
36
|
+
React4.useEffect(() => {
|
|
37
|
+
return () => {
|
|
38
|
+
if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
|
|
39
|
+
for (const att of attachments) {
|
|
40
|
+
att.abortController?.abort();
|
|
41
|
+
URL.revokeObjectURL(att.preview);
|
|
42
|
+
if (att.status === "ready" && att.attachment?.file_id) {
|
|
43
|
+
void onDeleteAttachment?.(att.attachment.file_id);
|
|
48
44
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}, []);
|
|
48
|
+
const handleTyping = React4.useCallback(() => {
|
|
49
|
+
if (!onTypingChange) return;
|
|
50
|
+
onTypingChange(true);
|
|
51
|
+
if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
|
|
52
|
+
typingTimerRef.current = setTimeout(() => onTypingChange(false), 2e3);
|
|
53
|
+
}, [onTypingChange]);
|
|
54
|
+
const addFiles = React4.useCallback(
|
|
55
|
+
(files) => {
|
|
56
|
+
if (!onUploadAttachment) return;
|
|
57
|
+
const remaining = maxAttachments - attachments.length;
|
|
58
|
+
const toAdd = files.slice(0, remaining);
|
|
59
|
+
for (const file of toAdd) {
|
|
60
|
+
if (onValidateFile) {
|
|
61
|
+
const validation = onValidateFile(file);
|
|
62
|
+
if (!validation.valid) {
|
|
63
|
+
console.warn("File rejected:", validation.error);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const id = crypto.randomUUID();
|
|
68
|
+
const preview = URL.createObjectURL(file);
|
|
69
|
+
const abortController = new AbortController();
|
|
70
|
+
const pending = {
|
|
71
|
+
id,
|
|
72
|
+
file,
|
|
73
|
+
preview,
|
|
74
|
+
status: "uploading",
|
|
75
|
+
progress: 0,
|
|
76
|
+
abortController
|
|
77
|
+
};
|
|
78
|
+
setAttachments((prev) => [...prev, pending]);
|
|
79
|
+
onUploadAttachment(
|
|
80
|
+
file,
|
|
81
|
+
(progress) => {
|
|
82
|
+
setAttachments(
|
|
83
|
+
(prev) => prev.map(
|
|
84
|
+
(a) => a.id === id ? { ...a, progress } : a
|
|
85
|
+
)
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
abortController.signal
|
|
89
|
+
).then((result) => {
|
|
60
90
|
if (result?.data) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
91
|
+
setAttachments(
|
|
92
|
+
(prev) => prev.map(
|
|
93
|
+
(a) => a.id === id ? {
|
|
94
|
+
...a,
|
|
95
|
+
status: "ready",
|
|
96
|
+
progress: 100,
|
|
97
|
+
attachment: result.data
|
|
98
|
+
} : a
|
|
99
|
+
)
|
|
100
|
+
);
|
|
101
|
+
} else {
|
|
102
|
+
setAttachments(
|
|
103
|
+
(prev) => prev.map(
|
|
104
|
+
(a) => a.id === id ? {
|
|
105
|
+
...a,
|
|
106
|
+
status: "error",
|
|
107
|
+
error: result?.error?.message ?? "Upload failed"
|
|
108
|
+
} : a
|
|
109
|
+
)
|
|
110
|
+
);
|
|
66
111
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
112
|
+
}).catch((err) => {
|
|
113
|
+
if (err.name === "AbortError") return;
|
|
114
|
+
setAttachments(
|
|
115
|
+
(prev) => prev.map(
|
|
116
|
+
(a) => a.id === id ? { ...a, status: "error", error: err.message } : a
|
|
117
|
+
)
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
[attachments.length, maxAttachments, onUploadAttachment, onValidateFile]
|
|
123
|
+
);
|
|
124
|
+
const removeAttachment = React4.useCallback(
|
|
125
|
+
(id) => {
|
|
126
|
+
setAttachments((prev) => {
|
|
127
|
+
const att = prev.find((a) => a.id === id);
|
|
128
|
+
if (att) {
|
|
129
|
+
att.abortController?.abort();
|
|
130
|
+
URL.revokeObjectURL(att.preview);
|
|
131
|
+
if (att.status === "ready" && att.attachment?.file_id) {
|
|
132
|
+
void onDeleteAttachment?.(att.attachment.file_id);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return prev.filter((a) => a.id !== id);
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
[onDeleteAttachment]
|
|
139
|
+
);
|
|
140
|
+
const handleSend = React4.useCallback(async () => {
|
|
141
|
+
if (!canSend) return;
|
|
142
|
+
const readyAttachments = attachments.filter((a) => a.status === "ready" && a.attachment).map((a) => a.attachment);
|
|
78
143
|
setIsSending(true);
|
|
79
144
|
try {
|
|
80
|
-
await onSend(
|
|
81
|
-
|
|
145
|
+
await onSend(
|
|
146
|
+
text.trim(),
|
|
147
|
+
readyAttachments.length > 0 ? readyAttachments : void 0
|
|
148
|
+
);
|
|
149
|
+
for (const att of attachments) {
|
|
150
|
+
URL.revokeObjectURL(att.preview);
|
|
151
|
+
}
|
|
152
|
+
setText("");
|
|
82
153
|
setAttachments([]);
|
|
83
|
-
onTypingChange
|
|
154
|
+
if (onTypingChange) onTypingChange(false);
|
|
155
|
+
if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
|
|
156
|
+
if (textareaRef.current) textareaRef.current.style.height = "auto";
|
|
157
|
+
textareaRef.current?.focus();
|
|
84
158
|
} finally {
|
|
85
159
|
setIsSending(false);
|
|
86
160
|
}
|
|
87
|
-
};
|
|
161
|
+
}, [text, attachments, canSend, onSend, onTypingChange]);
|
|
162
|
+
const handleKeyDown = React4.useCallback(
|
|
163
|
+
(e) => {
|
|
164
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
165
|
+
e.preventDefault();
|
|
166
|
+
void handleSend();
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
[handleSend]
|
|
170
|
+
);
|
|
171
|
+
const handleChange = React4.useCallback(
|
|
172
|
+
(e) => {
|
|
173
|
+
setText(e.target.value);
|
|
174
|
+
handleTyping();
|
|
175
|
+
const ta = e.target;
|
|
176
|
+
ta.style.height = "auto";
|
|
177
|
+
ta.style.height = Math.min(ta.scrollHeight, 120) + "px";
|
|
178
|
+
},
|
|
179
|
+
[handleTyping]
|
|
180
|
+
);
|
|
181
|
+
const handleDragEnter = React4.useCallback(
|
|
182
|
+
(e) => {
|
|
183
|
+
e.preventDefault();
|
|
184
|
+
dragCounterRef.current++;
|
|
185
|
+
if (e.dataTransfer.types.includes("Files") && onUploadAttachment) {
|
|
186
|
+
setIsDragOver(true);
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
[onUploadAttachment]
|
|
190
|
+
);
|
|
191
|
+
const handleDragLeave = React4.useCallback((e) => {
|
|
192
|
+
e.preventDefault();
|
|
193
|
+
dragCounterRef.current--;
|
|
194
|
+
if (dragCounterRef.current === 0) {
|
|
195
|
+
setIsDragOver(false);
|
|
196
|
+
}
|
|
197
|
+
}, []);
|
|
198
|
+
const handleDragOver = React4.useCallback((e) => {
|
|
199
|
+
e.preventDefault();
|
|
200
|
+
}, []);
|
|
201
|
+
const handleDrop = React4.useCallback(
|
|
202
|
+
(e) => {
|
|
203
|
+
e.preventDefault();
|
|
204
|
+
setIsDragOver(false);
|
|
205
|
+
dragCounterRef.current = 0;
|
|
206
|
+
const files = Array.from(e.dataTransfer.files);
|
|
207
|
+
if (files.length > 0) addFiles(files);
|
|
208
|
+
},
|
|
209
|
+
[addFiles]
|
|
210
|
+
);
|
|
211
|
+
const handleFileSelect = React4.useCallback(
|
|
212
|
+
(e) => {
|
|
213
|
+
const files = Array.from(e.target.files ?? []);
|
|
214
|
+
if (files.length > 0) addFiles(files);
|
|
215
|
+
e.target.value = "";
|
|
216
|
+
},
|
|
217
|
+
[addFiles]
|
|
218
|
+
);
|
|
88
219
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
89
220
|
"div",
|
|
90
221
|
{
|
|
91
|
-
onDragOver: (event) => {
|
|
92
|
-
event.preventDefault();
|
|
93
|
-
if (onUploadAttachment) {
|
|
94
|
-
setIsDragging(true);
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
onDragLeave: () => setIsDragging(false),
|
|
98
|
-
onDrop: (event) => {
|
|
99
|
-
event.preventDefault();
|
|
100
|
-
setIsDragging(false);
|
|
101
|
-
void handleFiles(event.dataTransfer.files);
|
|
102
|
-
},
|
|
103
222
|
style: {
|
|
223
|
+
position: "relative",
|
|
104
224
|
borderTop: "1px solid var(--sm-border-color, #e5e7eb)",
|
|
105
|
-
background:
|
|
106
|
-
padding: 12,
|
|
107
|
-
display: "flex",
|
|
108
|
-
flexDirection: "column",
|
|
109
|
-
gap: 10
|
|
225
|
+
background: "var(--sm-surface, #fff)"
|
|
110
226
|
},
|
|
227
|
+
onDragEnter: handleDragEnter,
|
|
228
|
+
onDragLeave: handleDragLeave,
|
|
229
|
+
onDragOver: handleDragOver,
|
|
230
|
+
onDrop: handleDrop,
|
|
111
231
|
children: [
|
|
112
|
-
|
|
232
|
+
isDragOver && /* @__PURE__ */ jsxRuntime.jsx(
|
|
113
233
|
"div",
|
|
114
234
|
{
|
|
115
235
|
style: {
|
|
116
|
-
|
|
236
|
+
position: "absolute",
|
|
237
|
+
inset: 0,
|
|
238
|
+
zIndex: 50,
|
|
239
|
+
background: "rgba(37, 99, 235, 0.1)",
|
|
240
|
+
backdropFilter: "blur(4px)",
|
|
241
|
+
borderRadius: "0 0 var(--sm-border-radius, 16px) var(--sm-border-radius, 16px)",
|
|
242
|
+
display: "flex",
|
|
117
243
|
alignItems: "center",
|
|
244
|
+
justifyContent: "center"
|
|
245
|
+
},
|
|
246
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center" }, children: [
|
|
247
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
248
|
+
"svg",
|
|
249
|
+
{
|
|
250
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
251
|
+
width: "32",
|
|
252
|
+
height: "32",
|
|
253
|
+
viewBox: "0 0 24 24",
|
|
254
|
+
fill: "none",
|
|
255
|
+
stroke: "var(--sm-primary, #2563eb)",
|
|
256
|
+
strokeWidth: "2",
|
|
257
|
+
style: { margin: "0 auto 8px" },
|
|
258
|
+
children: [
|
|
259
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
260
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "17 8 12 3 7 8" }),
|
|
261
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
),
|
|
265
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
266
|
+
"p",
|
|
267
|
+
{
|
|
268
|
+
style: {
|
|
269
|
+
margin: 0,
|
|
270
|
+
color: "var(--sm-primary, #2563eb)",
|
|
271
|
+
fontSize: 14,
|
|
272
|
+
fontWeight: 500
|
|
273
|
+
},
|
|
274
|
+
children: "Drop to attach"
|
|
275
|
+
}
|
|
276
|
+
)
|
|
277
|
+
] })
|
|
278
|
+
}
|
|
279
|
+
),
|
|
280
|
+
attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
281
|
+
"div",
|
|
282
|
+
{
|
|
283
|
+
style: {
|
|
284
|
+
display: "flex",
|
|
118
285
|
gap: 8,
|
|
119
|
-
padding: "
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
286
|
+
padding: "8px 12px 4px",
|
|
287
|
+
overflowX: "auto"
|
|
288
|
+
},
|
|
289
|
+
children: attachments.map((att) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
290
|
+
"div",
|
|
291
|
+
{
|
|
292
|
+
style: {
|
|
293
|
+
position: "relative",
|
|
294
|
+
display: "flex",
|
|
295
|
+
alignItems: "center",
|
|
296
|
+
gap: 6,
|
|
297
|
+
background: att.status === "error" ? "#fef2f2" : "var(--sm-surface-muted, #f8fafc)",
|
|
298
|
+
border: att.status === "error" ? "1px solid #fecaca" : "1px solid var(--sm-border-color, #e5e7eb)",
|
|
299
|
+
borderRadius: 8,
|
|
300
|
+
padding: "6px 8px",
|
|
301
|
+
fontSize: 12,
|
|
302
|
+
flexShrink: 0,
|
|
303
|
+
maxWidth: 180
|
|
304
|
+
},
|
|
305
|
+
children: [
|
|
306
|
+
att.file.type.startsWith("image/") ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
307
|
+
"img",
|
|
308
|
+
{
|
|
309
|
+
src: att.preview,
|
|
310
|
+
alt: "",
|
|
311
|
+
style: {
|
|
312
|
+
width: 32,
|
|
313
|
+
height: 32,
|
|
314
|
+
borderRadius: 4,
|
|
315
|
+
objectFit: "cover",
|
|
316
|
+
flexShrink: 0
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
320
|
+
"div",
|
|
321
|
+
{
|
|
322
|
+
style: {
|
|
323
|
+
width: 32,
|
|
324
|
+
height: 32,
|
|
325
|
+
borderRadius: 4,
|
|
326
|
+
background: "var(--sm-border-color, #e5e7eb)",
|
|
327
|
+
display: "flex",
|
|
328
|
+
alignItems: "center",
|
|
329
|
+
justifyContent: "center",
|
|
330
|
+
flexShrink: 0
|
|
331
|
+
},
|
|
332
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
333
|
+
"svg",
|
|
334
|
+
{
|
|
335
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
336
|
+
width: "14",
|
|
337
|
+
height: "14",
|
|
338
|
+
viewBox: "0 0 24 24",
|
|
339
|
+
fill: "none",
|
|
340
|
+
stroke: "currentColor",
|
|
341
|
+
strokeWidth: "2",
|
|
342
|
+
style: { color: "var(--sm-muted-text, #6b7280)" },
|
|
343
|
+
children: [
|
|
344
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "23 7 16 12 23 17 23 7" }),
|
|
345
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "1", y: "5", width: "15", height: "14", rx: "2", ry: "2" })
|
|
346
|
+
]
|
|
347
|
+
}
|
|
348
|
+
)
|
|
349
|
+
}
|
|
350
|
+
),
|
|
351
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: 0, flex: 1 }, children: [
|
|
352
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
353
|
+
"p",
|
|
354
|
+
{
|
|
355
|
+
style: {
|
|
356
|
+
margin: 0,
|
|
357
|
+
overflow: "hidden",
|
|
358
|
+
textOverflow: "ellipsis",
|
|
359
|
+
whiteSpace: "nowrap",
|
|
360
|
+
color: "var(--sm-text-color, #111827)"
|
|
361
|
+
},
|
|
362
|
+
children: att.file.name
|
|
363
|
+
}
|
|
364
|
+
),
|
|
365
|
+
att.status === "uploading" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
366
|
+
"div",
|
|
367
|
+
{
|
|
368
|
+
style: {
|
|
369
|
+
width: "100%",
|
|
370
|
+
height: 3,
|
|
371
|
+
background: "var(--sm-border-color, #e5e7eb)",
|
|
372
|
+
borderRadius: 999,
|
|
373
|
+
marginTop: 2,
|
|
374
|
+
overflow: "hidden"
|
|
375
|
+
},
|
|
376
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
377
|
+
"div",
|
|
378
|
+
{
|
|
379
|
+
style: {
|
|
380
|
+
height: "100%",
|
|
381
|
+
background: "var(--sm-primary, #2563eb)",
|
|
382
|
+
borderRadius: 999,
|
|
383
|
+
transition: "width 0.2s ease",
|
|
384
|
+
width: `${att.progress}%`
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
)
|
|
388
|
+
}
|
|
389
|
+
),
|
|
390
|
+
att.status === "error" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
391
|
+
"p",
|
|
392
|
+
{
|
|
393
|
+
style: {
|
|
394
|
+
margin: 0,
|
|
395
|
+
color: "#dc2626",
|
|
396
|
+
overflow: "hidden",
|
|
397
|
+
textOverflow: "ellipsis",
|
|
398
|
+
whiteSpace: "nowrap"
|
|
399
|
+
},
|
|
400
|
+
children: att.error
|
|
401
|
+
}
|
|
402
|
+
)
|
|
403
|
+
] }),
|
|
404
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
405
|
+
"button",
|
|
406
|
+
{
|
|
407
|
+
type: "button",
|
|
408
|
+
onClick: () => removeAttachment(att.id),
|
|
409
|
+
style: {
|
|
410
|
+
flexShrink: 0,
|
|
411
|
+
width: 16,
|
|
412
|
+
height: 16,
|
|
413
|
+
display: "flex",
|
|
414
|
+
alignItems: "center",
|
|
415
|
+
justifyContent: "center",
|
|
416
|
+
borderRadius: 999,
|
|
417
|
+
background: "var(--sm-muted-text, #9ca3af)",
|
|
418
|
+
color: "#fff",
|
|
419
|
+
border: "none",
|
|
420
|
+
cursor: "pointer",
|
|
421
|
+
fontSize: 10,
|
|
422
|
+
lineHeight: 1,
|
|
423
|
+
padding: 0
|
|
424
|
+
},
|
|
425
|
+
children: "\xD7"
|
|
426
|
+
}
|
|
427
|
+
)
|
|
428
|
+
]
|
|
429
|
+
},
|
|
430
|
+
att.id
|
|
431
|
+
))
|
|
432
|
+
}
|
|
433
|
+
),
|
|
434
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
435
|
+
"div",
|
|
436
|
+
{
|
|
437
|
+
style: {
|
|
438
|
+
display: "flex",
|
|
439
|
+
alignItems: "flex-end",
|
|
440
|
+
gap: 8,
|
|
441
|
+
padding: "8px 12px"
|
|
124
442
|
},
|
|
125
443
|
children: [
|
|
126
|
-
/* @__PURE__ */ jsxRuntime.
|
|
127
|
-
|
|
444
|
+
onUploadAttachment && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
445
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
446
|
+
"button",
|
|
447
|
+
{
|
|
448
|
+
type: "button",
|
|
449
|
+
onClick: () => fileInputRef.current?.click(),
|
|
450
|
+
disabled: disabled || attachments.length >= maxAttachments,
|
|
451
|
+
"aria-label": "Attach file",
|
|
452
|
+
style: {
|
|
453
|
+
flexShrink: 0,
|
|
454
|
+
width: 32,
|
|
455
|
+
height: 32,
|
|
456
|
+
display: "flex",
|
|
457
|
+
alignItems: "center",
|
|
458
|
+
justifyContent: "center",
|
|
459
|
+
borderRadius: 999,
|
|
460
|
+
border: "none",
|
|
461
|
+
background: "transparent",
|
|
462
|
+
cursor: disabled || attachments.length >= maxAttachments ? "not-allowed" : "pointer",
|
|
463
|
+
color: "var(--sm-muted-text, #6b7280)",
|
|
464
|
+
opacity: disabled || attachments.length >= maxAttachments ? 0.4 : 1,
|
|
465
|
+
marginBottom: 2
|
|
466
|
+
},
|
|
467
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
468
|
+
"svg",
|
|
469
|
+
{
|
|
470
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
471
|
+
width: "18",
|
|
472
|
+
height: "18",
|
|
473
|
+
viewBox: "0 0 24 24",
|
|
474
|
+
fill: "none",
|
|
475
|
+
stroke: "currentColor",
|
|
476
|
+
strokeWidth: "2",
|
|
477
|
+
strokeLinecap: "round",
|
|
478
|
+
strokeLinejoin: "round",
|
|
479
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" })
|
|
480
|
+
}
|
|
481
|
+
)
|
|
482
|
+
}
|
|
483
|
+
),
|
|
484
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
485
|
+
"input",
|
|
486
|
+
{
|
|
487
|
+
ref: fileInputRef,
|
|
488
|
+
type: "file",
|
|
489
|
+
accept,
|
|
490
|
+
multiple: true,
|
|
491
|
+
style: { display: "none" },
|
|
492
|
+
onChange: handleFileSelect
|
|
493
|
+
}
|
|
494
|
+
)
|
|
495
|
+
] }),
|
|
496
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
497
|
+
"textarea",
|
|
498
|
+
{
|
|
499
|
+
ref: textareaRef,
|
|
500
|
+
value: text,
|
|
501
|
+
onChange: handleChange,
|
|
502
|
+
onKeyDown: handleKeyDown,
|
|
503
|
+
placeholder,
|
|
504
|
+
disabled,
|
|
505
|
+
rows: 1,
|
|
506
|
+
style: {
|
|
507
|
+
flex: 1,
|
|
508
|
+
fontSize: 14,
|
|
509
|
+
background: "var(--sm-surface-muted, #f8fafc)",
|
|
510
|
+
border: "1px solid var(--sm-border-color, #e5e7eb)",
|
|
511
|
+
borderRadius: 16,
|
|
512
|
+
padding: "8px 16px",
|
|
513
|
+
fontFamily: "var(--sm-font-family, system-ui, -apple-system, sans-serif)",
|
|
514
|
+
color: "var(--sm-text-color, #111827)",
|
|
515
|
+
resize: "none",
|
|
516
|
+
overflow: "hidden",
|
|
517
|
+
outline: "none",
|
|
518
|
+
minHeight: 36,
|
|
519
|
+
lineHeight: "20px",
|
|
520
|
+
opacity: disabled ? 0.5 : 1,
|
|
521
|
+
cursor: disabled ? "not-allowed" : "text",
|
|
522
|
+
boxSizing: "border-box"
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
),
|
|
128
526
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
129
527
|
"button",
|
|
130
528
|
{
|
|
529
|
+
onClick: () => void handleSend(),
|
|
530
|
+
disabled: disabled || !canSend,
|
|
131
531
|
type: "button",
|
|
132
|
-
|
|
133
|
-
setAttachments((current) => current.filter((item) => item.id !== attachment.id));
|
|
134
|
-
},
|
|
532
|
+
"aria-label": "Send message",
|
|
135
533
|
style: {
|
|
534
|
+
flexShrink: 0,
|
|
535
|
+
width: 32,
|
|
536
|
+
height: 32,
|
|
537
|
+
display: "flex",
|
|
538
|
+
alignItems: "center",
|
|
539
|
+
justifyContent: "center",
|
|
540
|
+
borderRadius: 999,
|
|
136
541
|
border: "none",
|
|
137
|
-
background: "
|
|
138
|
-
|
|
139
|
-
|
|
542
|
+
background: "var(--sm-primary, #2563eb)",
|
|
543
|
+
color: "#fff",
|
|
544
|
+
cursor: disabled || !canSend ? "not-allowed" : "pointer",
|
|
545
|
+
opacity: disabled || !canSend ? 0.4 : 1,
|
|
546
|
+
marginBottom: 2,
|
|
547
|
+
transition: "opacity 0.15s ease"
|
|
140
548
|
},
|
|
141
|
-
children:
|
|
549
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
550
|
+
"svg",
|
|
551
|
+
{
|
|
552
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
553
|
+
width: "16",
|
|
554
|
+
height: "16",
|
|
555
|
+
viewBox: "0 0 24 24",
|
|
556
|
+
fill: "none",
|
|
557
|
+
stroke: "currentColor",
|
|
558
|
+
strokeWidth: "2",
|
|
559
|
+
strokeLinecap: "round",
|
|
560
|
+
strokeLinejoin: "round",
|
|
561
|
+
children: [
|
|
562
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
563
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
564
|
+
]
|
|
565
|
+
}
|
|
566
|
+
)
|
|
142
567
|
}
|
|
143
568
|
)
|
|
144
569
|
]
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
)) }) : null,
|
|
148
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 10, alignItems: "flex-end" }, children: [
|
|
149
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
150
|
-
"textarea",
|
|
151
|
-
{
|
|
152
|
-
value: content,
|
|
153
|
-
onChange: (event) => {
|
|
154
|
-
setContent(event.target.value);
|
|
155
|
-
emitTyping();
|
|
156
|
-
},
|
|
157
|
-
onKeyDown: (event) => {
|
|
158
|
-
if (event.key === "Enter" && !event.shiftKey) {
|
|
159
|
-
event.preventDefault();
|
|
160
|
-
void handleSubmit();
|
|
161
|
-
}
|
|
162
|
-
},
|
|
163
|
-
rows: 1,
|
|
164
|
-
placeholder,
|
|
165
|
-
style: {
|
|
166
|
-
flex: 1,
|
|
167
|
-
minHeight: 44,
|
|
168
|
-
maxHeight: 120,
|
|
169
|
-
resize: "vertical",
|
|
170
|
-
borderRadius: 14,
|
|
171
|
-
border: "1px solid var(--sm-border-color, #e5e7eb)",
|
|
172
|
-
padding: "12px 14px",
|
|
173
|
-
font: "inherit",
|
|
174
|
-
color: "var(--sm-text-color, #111827)"
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
),
|
|
178
|
-
onUploadAttachment ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
179
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
180
|
-
"input",
|
|
181
|
-
{
|
|
182
|
-
ref: fileInputRef,
|
|
183
|
-
type: "file",
|
|
184
|
-
hidden: true,
|
|
185
|
-
multiple: true,
|
|
186
|
-
accept: "image/*,video/*,audio/*",
|
|
187
|
-
onChange: (event) => {
|
|
188
|
-
if (event.target.files) {
|
|
189
|
-
void handleFiles(event.target.files);
|
|
190
|
-
event.target.value = "";
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
),
|
|
195
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
196
|
-
"button",
|
|
197
|
-
{
|
|
198
|
-
type: "button",
|
|
199
|
-
onClick: () => fileInputRef.current?.click(),
|
|
200
|
-
"aria-label": "Attach files",
|
|
201
|
-
style: {
|
|
202
|
-
width: 44,
|
|
203
|
-
height: 44,
|
|
204
|
-
borderRadius: 14,
|
|
205
|
-
border: "1px solid var(--sm-border-color, #e5e7eb)",
|
|
206
|
-
background: "var(--sm-surface, #fff)",
|
|
207
|
-
cursor: "pointer",
|
|
208
|
-
color: "var(--sm-text-color, #111827)"
|
|
209
|
-
},
|
|
210
|
-
children: "+"
|
|
211
|
-
}
|
|
212
|
-
)
|
|
213
|
-
] }) : null,
|
|
214
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
215
|
-
"button",
|
|
216
|
-
{
|
|
217
|
-
type: "button",
|
|
218
|
-
onClick: () => void handleSubmit(),
|
|
219
|
-
disabled: isSending || uploadingCount > 0 || !content.trim() && !readyAttachments.length,
|
|
220
|
-
style: {
|
|
221
|
-
height: 44,
|
|
222
|
-
padding: "0 16px",
|
|
223
|
-
borderRadius: 14,
|
|
224
|
-
border: "none",
|
|
225
|
-
background: "var(--sm-primary, #2563eb)",
|
|
226
|
-
color: "#fff",
|
|
227
|
-
cursor: isSending ? "wait" : "pointer",
|
|
228
|
-
opacity: isSending || uploadingCount > 0 ? 0.75 : 1
|
|
229
|
-
},
|
|
230
|
-
children: "Send"
|
|
231
|
-
}
|
|
232
|
-
)
|
|
233
|
-
] })
|
|
570
|
+
}
|
|
571
|
+
)
|
|
234
572
|
]
|
|
235
573
|
}
|
|
236
574
|
);
|
|
237
575
|
}
|
|
238
|
-
var
|
|
576
|
+
var QUICK_REACTIONS = ["\u2764\uFE0F", "\u{1F602}", "\u{1F44D}", "\u{1F525}", "\u{1F62E}", "\u{1F622}", "\u{1F44F}", "\u{1F64C}"];
|
|
239
577
|
function EmojiPicker({
|
|
240
578
|
onSelect,
|
|
241
|
-
|
|
579
|
+
onClose,
|
|
580
|
+
anchorRef,
|
|
581
|
+
emojis = QUICK_REACTIONS
|
|
242
582
|
}) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
style: {
|
|
262
|
-
width: 32,
|
|
263
|
-
height: 32,
|
|
264
|
-
border: "none",
|
|
265
|
-
background: "transparent",
|
|
266
|
-
borderRadius: 999,
|
|
267
|
-
cursor: "pointer",
|
|
268
|
-
fontSize: 18
|
|
269
|
-
},
|
|
270
|
-
children: emoji
|
|
271
|
-
},
|
|
272
|
-
emoji
|
|
273
|
-
))
|
|
583
|
+
const ref = React4.useRef(null);
|
|
584
|
+
const [position, setPosition] = React4.useState(null);
|
|
585
|
+
React4.useEffect(() => {
|
|
586
|
+
const anchor = anchorRef.current;
|
|
587
|
+
if (!anchor) return;
|
|
588
|
+
const rect = anchor.getBoundingClientRect();
|
|
589
|
+
const pickerWidth = emojis.length * 36 + 16;
|
|
590
|
+
let left = rect.left + rect.width / 2 - pickerWidth / 2;
|
|
591
|
+
if (left < 8) left = 8;
|
|
592
|
+
if (left + pickerWidth > window.innerWidth - 8)
|
|
593
|
+
left = window.innerWidth - 8 - pickerWidth;
|
|
594
|
+
setPosition({ top: rect.top - 8, left });
|
|
595
|
+
}, [anchorRef, emojis.length]);
|
|
596
|
+
React4.useEffect(() => {
|
|
597
|
+
function handleClickOutside(e) {
|
|
598
|
+
if (ref.current && !ref.current.contains(e.target) && anchorRef.current && !anchorRef.current.contains(e.target)) {
|
|
599
|
+
onClose();
|
|
600
|
+
}
|
|
274
601
|
}
|
|
602
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
603
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
604
|
+
}, [onClose, anchorRef]);
|
|
605
|
+
if (!position) return null;
|
|
606
|
+
return reactDom.createPortal(
|
|
607
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
608
|
+
"div",
|
|
609
|
+
{
|
|
610
|
+
ref,
|
|
611
|
+
style: {
|
|
612
|
+
position: "fixed",
|
|
613
|
+
top: position.top,
|
|
614
|
+
left: position.left,
|
|
615
|
+
transform: "translateY(-100%)",
|
|
616
|
+
background: "var(--sm-surface, #fff)",
|
|
617
|
+
border: "1px solid var(--sm-border-color, #e5e7eb)",
|
|
618
|
+
borderRadius: 12,
|
|
619
|
+
boxShadow: "0 10px 25px rgba(15, 23, 42, 0.12)",
|
|
620
|
+
padding: 8,
|
|
621
|
+
display: "flex",
|
|
622
|
+
gap: 4,
|
|
623
|
+
zIndex: 9999
|
|
624
|
+
},
|
|
625
|
+
children: emojis.map((emoji) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
626
|
+
"button",
|
|
627
|
+
{
|
|
628
|
+
onClick: () => {
|
|
629
|
+
onSelect(emoji);
|
|
630
|
+
onClose();
|
|
631
|
+
},
|
|
632
|
+
type: "button",
|
|
633
|
+
"aria-label": `React with ${emoji}`,
|
|
634
|
+
style: {
|
|
635
|
+
width: 32,
|
|
636
|
+
height: 32,
|
|
637
|
+
display: "flex",
|
|
638
|
+
alignItems: "center",
|
|
639
|
+
justifyContent: "center",
|
|
640
|
+
borderRadius: 8,
|
|
641
|
+
border: "none",
|
|
642
|
+
background: "transparent",
|
|
643
|
+
cursor: "pointer",
|
|
644
|
+
fontSize: 18,
|
|
645
|
+
transition: "background 0.15s ease"
|
|
646
|
+
},
|
|
647
|
+
onMouseEnter: (e) => {
|
|
648
|
+
e.target.style.background = "var(--sm-surface-muted, #f8fafc)";
|
|
649
|
+
},
|
|
650
|
+
onMouseLeave: (e) => {
|
|
651
|
+
e.target.style.background = "transparent";
|
|
652
|
+
},
|
|
653
|
+
children: emoji
|
|
654
|
+
},
|
|
655
|
+
emoji
|
|
656
|
+
))
|
|
657
|
+
}
|
|
658
|
+
),
|
|
659
|
+
document.body
|
|
275
660
|
);
|
|
276
661
|
}
|
|
662
|
+
function EmojiPickerTrigger({
|
|
663
|
+
onSelect,
|
|
664
|
+
emojis
|
|
665
|
+
}) {
|
|
666
|
+
const [open, setOpen] = React4.useState(false);
|
|
667
|
+
const buttonRef = React4.useRef(null);
|
|
668
|
+
const handleClose = React4.useCallback(() => setOpen(false), []);
|
|
669
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
670
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
671
|
+
"button",
|
|
672
|
+
{
|
|
673
|
+
ref: buttonRef,
|
|
674
|
+
type: "button",
|
|
675
|
+
onClick: () => setOpen(!open),
|
|
676
|
+
"aria-label": "Add reaction",
|
|
677
|
+
style: {
|
|
678
|
+
padding: 6,
|
|
679
|
+
border: "none",
|
|
680
|
+
background: "transparent",
|
|
681
|
+
cursor: "pointer",
|
|
682
|
+
color: "var(--sm-muted-text, #6b7280)",
|
|
683
|
+
borderRadius: 8,
|
|
684
|
+
display: "flex",
|
|
685
|
+
alignItems: "center",
|
|
686
|
+
justifyContent: "center",
|
|
687
|
+
transition: "color 0.15s ease"
|
|
688
|
+
},
|
|
689
|
+
onMouseEnter: (e) => {
|
|
690
|
+
e.target.style.color = "var(--sm-text-color, #111827)";
|
|
691
|
+
},
|
|
692
|
+
onMouseLeave: (e) => {
|
|
693
|
+
e.target.style.color = "var(--sm-muted-text, #6b7280)";
|
|
694
|
+
},
|
|
695
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
696
|
+
"svg",
|
|
697
|
+
{
|
|
698
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
699
|
+
width: "18",
|
|
700
|
+
height: "18",
|
|
701
|
+
viewBox: "0 0 24 24",
|
|
702
|
+
fill: "none",
|
|
703
|
+
stroke: "currentColor",
|
|
704
|
+
strokeWidth: "2",
|
|
705
|
+
strokeLinecap: "round",
|
|
706
|
+
strokeLinejoin: "round",
|
|
707
|
+
children: [
|
|
708
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
709
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 14s1.5 2 4 2 4-2 4-2" }),
|
|
710
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "9", x2: "9.01", y2: "9" }),
|
|
711
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "15", y1: "9", x2: "15.01", y2: "9" })
|
|
712
|
+
]
|
|
713
|
+
}
|
|
714
|
+
)
|
|
715
|
+
}
|
|
716
|
+
),
|
|
717
|
+
open && /* @__PURE__ */ jsxRuntime.jsx(
|
|
718
|
+
EmojiPicker,
|
|
719
|
+
{
|
|
720
|
+
onSelect,
|
|
721
|
+
onClose: handleClose,
|
|
722
|
+
anchorRef: buttonRef,
|
|
723
|
+
emojis
|
|
724
|
+
}
|
|
725
|
+
)
|
|
726
|
+
] });
|
|
727
|
+
}
|
|
728
|
+
function ReactionBar({
|
|
729
|
+
reactions,
|
|
730
|
+
currentUserId,
|
|
731
|
+
onToggleReaction
|
|
732
|
+
}) {
|
|
733
|
+
if (!reactions || reactions.length === 0) return null;
|
|
734
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 4, marginTop: 4 }, children: reactions.map((r) => {
|
|
735
|
+
const hasReacted = currentUserId ? r.user_ids.includes(currentUserId) : false;
|
|
736
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
737
|
+
"button",
|
|
738
|
+
{
|
|
739
|
+
onClick: () => onToggleReaction(r.emoji),
|
|
740
|
+
type: "button",
|
|
741
|
+
style: {
|
|
742
|
+
display: "inline-flex",
|
|
743
|
+
alignItems: "center",
|
|
744
|
+
gap: 4,
|
|
745
|
+
padding: "2px 8px",
|
|
746
|
+
borderRadius: 999,
|
|
747
|
+
fontSize: 12,
|
|
748
|
+
border: hasReacted ? "1px solid var(--sm-reaction-active-border, rgba(37, 99, 235, 0.4))" : "1px solid var(--sm-border-color, #e5e7eb)",
|
|
749
|
+
background: hasReacted ? "var(--sm-reaction-active-bg, rgba(37, 99, 235, 0.08))" : "var(--sm-surface-muted, #f8fafc)",
|
|
750
|
+
color: hasReacted ? "var(--sm-primary, #2563eb)" : "var(--sm-text-color, #111827)",
|
|
751
|
+
cursor: "pointer",
|
|
752
|
+
lineHeight: "18px"
|
|
753
|
+
},
|
|
754
|
+
children: [
|
|
755
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: r.emoji }),
|
|
756
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500 }, children: r.count })
|
|
757
|
+
]
|
|
758
|
+
},
|
|
759
|
+
r.emoji
|
|
760
|
+
);
|
|
761
|
+
}) });
|
|
762
|
+
}
|
|
277
763
|
|
|
278
764
|
// src/react-components/utils.ts
|
|
279
765
|
var timeFormatter = new Intl.DateTimeFormat(void 0, {
|
|
280
766
|
hour: "numeric",
|
|
281
767
|
minute: "2-digit"
|
|
282
768
|
});
|
|
283
|
-
var dateFormatter = new Intl.DateTimeFormat(void 0, {
|
|
284
|
-
month: "short",
|
|
285
|
-
day: "numeric",
|
|
286
|
-
year: "numeric"
|
|
287
|
-
});
|
|
288
769
|
function formatMessageTime(value) {
|
|
289
770
|
if (!value) return "";
|
|
290
771
|
return timeFormatter.format(new Date(value));
|
|
291
772
|
}
|
|
292
|
-
function
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
773
|
+
function useAttachmentUrl(fileId, hasUrl, fetcher) {
|
|
774
|
+
const [url, setUrl] = React4.useState(null);
|
|
775
|
+
React4.useEffect(() => {
|
|
776
|
+
if (hasUrl || !fileId || !fetcher) return;
|
|
777
|
+
fetcher(fileId).then((viewUrl) => {
|
|
778
|
+
if (viewUrl) setUrl(viewUrl);
|
|
779
|
+
}).catch(() => {
|
|
780
|
+
});
|
|
781
|
+
}, [fileId, hasUrl, fetcher]);
|
|
782
|
+
return url;
|
|
301
783
|
}
|
|
302
|
-
function
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
maxWidth: "100%",
|
|
330
|
-
borderRadius: 12,
|
|
331
|
-
marginTop: 8
|
|
784
|
+
function AttachmentRenderer({
|
|
785
|
+
att,
|
|
786
|
+
fetcher
|
|
787
|
+
}) {
|
|
788
|
+
const [expanded, setExpanded] = React4.useState(false);
|
|
789
|
+
const fetchedUrl = useAttachmentUrl(att.file_id, !!att.presigned_url, fetcher);
|
|
790
|
+
const viewUrl = att.presigned_url || fetchedUrl;
|
|
791
|
+
const isImage = att.mime_type?.startsWith("image/");
|
|
792
|
+
const isVideo = att.mime_type?.startsWith("video/");
|
|
793
|
+
const isAudio = att.mime_type?.startsWith("audio/");
|
|
794
|
+
if (isImage && viewUrl) {
|
|
795
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
796
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
797
|
+
"img",
|
|
798
|
+
{
|
|
799
|
+
src: viewUrl,
|
|
800
|
+
alt: att.file_name,
|
|
801
|
+
loading: "lazy",
|
|
802
|
+
onClick: () => setExpanded(true),
|
|
803
|
+
style: {
|
|
804
|
+
display: "block",
|
|
805
|
+
maxHeight: 240,
|
|
806
|
+
maxWidth: "100%",
|
|
807
|
+
objectFit: "contain",
|
|
808
|
+
cursor: "pointer",
|
|
809
|
+
borderRadius: 8
|
|
810
|
+
}
|
|
332
811
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
812
|
+
),
|
|
813
|
+
expanded && /* @__PURE__ */ jsxRuntime.jsx(
|
|
814
|
+
"div",
|
|
815
|
+
{
|
|
816
|
+
onClick: () => setExpanded(false),
|
|
817
|
+
style: {
|
|
818
|
+
position: "fixed",
|
|
819
|
+
inset: 0,
|
|
820
|
+
zIndex: 9999,
|
|
821
|
+
background: "rgba(0, 0, 0, 0.8)",
|
|
822
|
+
display: "flex",
|
|
823
|
+
alignItems: "center",
|
|
824
|
+
justifyContent: "center",
|
|
825
|
+
padding: 16,
|
|
826
|
+
cursor: "pointer"
|
|
827
|
+
},
|
|
828
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
829
|
+
"img",
|
|
830
|
+
{
|
|
831
|
+
src: viewUrl,
|
|
832
|
+
alt: att.file_name,
|
|
833
|
+
style: {
|
|
834
|
+
maxWidth: "100%",
|
|
835
|
+
maxHeight: "100%",
|
|
836
|
+
objectFit: "contain",
|
|
837
|
+
borderRadius: 8
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
)
|
|
841
|
+
}
|
|
842
|
+
)
|
|
843
|
+
] });
|
|
336
844
|
}
|
|
337
|
-
if (
|
|
845
|
+
if (isVideo && viewUrl) {
|
|
338
846
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
339
847
|
"video",
|
|
340
848
|
{
|
|
849
|
+
src: viewUrl,
|
|
341
850
|
controls: true,
|
|
342
|
-
|
|
851
|
+
preload: "metadata",
|
|
852
|
+
poster: att.thumbnail_url,
|
|
343
853
|
style: {
|
|
344
854
|
display: "block",
|
|
345
|
-
|
|
346
|
-
maxWidth:
|
|
347
|
-
borderRadius:
|
|
348
|
-
marginTop: 8
|
|
855
|
+
maxHeight: 240,
|
|
856
|
+
maxWidth: "100%",
|
|
857
|
+
borderRadius: 8
|
|
349
858
|
}
|
|
350
|
-
}
|
|
351
|
-
key
|
|
859
|
+
}
|
|
352
860
|
);
|
|
353
861
|
}
|
|
354
|
-
if (
|
|
862
|
+
if (isAudio && viewUrl) {
|
|
355
863
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
356
864
|
"audio",
|
|
357
865
|
{
|
|
866
|
+
src: viewUrl,
|
|
358
867
|
controls: true,
|
|
359
|
-
|
|
868
|
+
style: { display: "block", width: "100%", marginTop: 4 }
|
|
869
|
+
}
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
if (isImage && !viewUrl) {
|
|
873
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
874
|
+
"div",
|
|
875
|
+
{
|
|
360
876
|
style: {
|
|
361
|
-
display: "
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
877
|
+
display: "flex",
|
|
878
|
+
alignItems: "center",
|
|
879
|
+
gap: 8,
|
|
880
|
+
padding: "8px 12px",
|
|
881
|
+
fontSize: 12,
|
|
882
|
+
color: "var(--sm-muted-text, #6b7280)",
|
|
883
|
+
background: "var(--sm-surface-muted, #f8fafc)",
|
|
884
|
+
borderRadius: 8
|
|
885
|
+
},
|
|
886
|
+
children: [
|
|
887
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
888
|
+
"div",
|
|
889
|
+
{
|
|
890
|
+
style: {
|
|
891
|
+
width: 16,
|
|
892
|
+
height: 16,
|
|
893
|
+
border: "2px solid var(--sm-border-color, #e5e7eb)",
|
|
894
|
+
borderTopColor: "var(--sm-primary, #2563eb)",
|
|
895
|
+
borderRadius: 999,
|
|
896
|
+
animation: "sm-spin 0.8s linear infinite"
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
),
|
|
900
|
+
"Loading image..."
|
|
901
|
+
]
|
|
902
|
+
}
|
|
367
903
|
);
|
|
368
904
|
}
|
|
369
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
370
|
-
"
|
|
905
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
906
|
+
"div",
|
|
371
907
|
{
|
|
372
|
-
href: url,
|
|
373
|
-
target: "_blank",
|
|
374
|
-
rel: "noreferrer",
|
|
375
908
|
style: {
|
|
376
|
-
display: "
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
909
|
+
display: "flex",
|
|
910
|
+
alignItems: "center",
|
|
911
|
+
gap: 8,
|
|
912
|
+
padding: "8px 12px",
|
|
913
|
+
fontSize: 14,
|
|
914
|
+
color: "var(--sm-text-color, #111827)",
|
|
915
|
+
background: "var(--sm-surface-muted, #f8fafc)",
|
|
916
|
+
borderRadius: 8,
|
|
917
|
+
border: "1px solid var(--sm-border-color, #e5e7eb)"
|
|
380
918
|
},
|
|
381
|
-
children:
|
|
382
|
-
|
|
383
|
-
|
|
919
|
+
children: [
|
|
920
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
921
|
+
"svg",
|
|
922
|
+
{
|
|
923
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
924
|
+
width: "16",
|
|
925
|
+
height: "16",
|
|
926
|
+
viewBox: "0 0 24 24",
|
|
927
|
+
fill: "none",
|
|
928
|
+
stroke: "currentColor",
|
|
929
|
+
strokeWidth: "2",
|
|
930
|
+
children: [
|
|
931
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
932
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "14 2 14 8 20 8" })
|
|
933
|
+
]
|
|
934
|
+
}
|
|
935
|
+
),
|
|
936
|
+
viewUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
937
|
+
"a",
|
|
938
|
+
{
|
|
939
|
+
href: viewUrl,
|
|
940
|
+
target: "_blank",
|
|
941
|
+
rel: "noreferrer",
|
|
942
|
+
style: { color: "inherit", textDecoration: "underline" },
|
|
943
|
+
children: att.file_name
|
|
944
|
+
}
|
|
945
|
+
) : att.file_name
|
|
946
|
+
]
|
|
947
|
+
}
|
|
384
948
|
);
|
|
385
949
|
}
|
|
386
950
|
function ChatMessageItem({
|
|
387
951
|
message,
|
|
388
952
|
currentUserId,
|
|
953
|
+
conversationId,
|
|
954
|
+
profile,
|
|
389
955
|
onAddReaction,
|
|
390
956
|
onRemoveReaction,
|
|
957
|
+
onEdit,
|
|
958
|
+
onDelete,
|
|
391
959
|
onReport,
|
|
960
|
+
onFetchAttachmentUrl,
|
|
961
|
+
isOwnMessage: isOwnMessageProp,
|
|
392
962
|
highlight = false
|
|
393
963
|
}) {
|
|
394
|
-
const [
|
|
395
|
-
const
|
|
964
|
+
const [showActions, setShowActions] = React4.useState(false);
|
|
965
|
+
const [editing, setEditing] = React4.useState(false);
|
|
966
|
+
const [editContent, setEditContent] = React4.useState(message.content);
|
|
967
|
+
const isOwn = isOwnMessageProp !== void 0 ? isOwnMessageProp : Boolean(currentUserId && message.sender_id === currentUserId);
|
|
968
|
+
const displayName = profile?.display_name ?? "User";
|
|
969
|
+
const username = profile?.username;
|
|
970
|
+
const avatarUrl = profile?.avatar_url;
|
|
971
|
+
const initials = displayName.charAt(0).toUpperCase();
|
|
396
972
|
const canReact = Boolean(onAddReaction || onRemoveReaction);
|
|
397
|
-
|
|
973
|
+
function handleToggleReaction(emoji) {
|
|
974
|
+
const hasReacted = message.reactions?.some(
|
|
975
|
+
(r) => r.emoji === emoji && currentUserId && r.user_ids.includes(currentUserId)
|
|
976
|
+
);
|
|
977
|
+
if (hasReacted) {
|
|
978
|
+
void onRemoveReaction?.(message.id, emoji);
|
|
979
|
+
} else {
|
|
980
|
+
void onAddReaction?.(message.id, emoji);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
function handleSaveEdit() {
|
|
984
|
+
if (editContent.trim() && editContent !== message.content) {
|
|
985
|
+
void onEdit?.(message.id, editContent.trim());
|
|
986
|
+
}
|
|
987
|
+
setEditing(false);
|
|
988
|
+
}
|
|
989
|
+
if (message.message_type === "system") {
|
|
990
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
991
|
+
"div",
|
|
992
|
+
{
|
|
993
|
+
style: {
|
|
994
|
+
textAlign: "center",
|
|
995
|
+
fontSize: 12,
|
|
996
|
+
color: "var(--sm-muted-text, #6b7280)",
|
|
997
|
+
padding: "8px 0",
|
|
998
|
+
fontStyle: "italic"
|
|
999
|
+
},
|
|
1000
|
+
children: message.content
|
|
1001
|
+
}
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
398
1004
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
399
1005
|
"div",
|
|
400
1006
|
{
|
|
401
1007
|
style: {
|
|
402
1008
|
display: "flex",
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
1009
|
+
justifyContent: isOwn ? "flex-end" : "flex-start",
|
|
1010
|
+
padding: "3px 16px",
|
|
1011
|
+
position: "relative"
|
|
406
1012
|
},
|
|
1013
|
+
onMouseEnter: () => setShowActions(true),
|
|
1014
|
+
onMouseLeave: () => setShowActions(false),
|
|
407
1015
|
children: [
|
|
408
|
-
/* @__PURE__ */ jsxRuntime.
|
|
409
|
-
"div",
|
|
410
|
-
{
|
|
411
|
-
style: {
|
|
412
|
-
maxWidth: "min(82%, 560px)",
|
|
413
|
-
padding: message.attachments?.length ? 10 : "10px 12px",
|
|
414
|
-
borderRadius: "var(--sm-border-radius, 16px)",
|
|
415
|
-
background: isOwn ? "var(--sm-own-bubble, #2563eb)" : "var(--sm-other-bubble, #f3f4f6)",
|
|
416
|
-
color: isOwn ? "var(--sm-own-text, #fff)" : "var(--sm-other-text, #111827)",
|
|
417
|
-
boxShadow: highlight ? "0 0 0 2px rgba(37, 99, 235, 0.22)" : "none",
|
|
418
|
-
transition: "box-shadow 0.2s ease"
|
|
419
|
-
},
|
|
420
|
-
children: [
|
|
421
|
-
message.content ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { whiteSpace: "pre-wrap", wordBreak: "break-word", fontSize: 14 }, children: message.content }) : null,
|
|
422
|
-
message.attachments?.map((attachment) => renderAttachment(message.id, attachment))
|
|
423
|
-
]
|
|
424
|
-
}
|
|
425
|
-
),
|
|
426
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1016
|
+
!isOwn && /* @__PURE__ */ jsxRuntime.jsx(
|
|
427
1017
|
"div",
|
|
428
1018
|
{
|
|
429
1019
|
style: {
|
|
1020
|
+
flexShrink: 0,
|
|
1021
|
+
width: 32,
|
|
1022
|
+
height: 32,
|
|
1023
|
+
borderRadius: 999,
|
|
1024
|
+
background: "var(--sm-surface-muted, #f3f4f6)",
|
|
1025
|
+
overflow: "hidden",
|
|
430
1026
|
display: "flex",
|
|
431
1027
|
alignItems: "center",
|
|
432
|
-
|
|
1028
|
+
justifyContent: "center",
|
|
1029
|
+
fontSize: 12,
|
|
1030
|
+
fontWeight: 500,
|
|
433
1031
|
color: "var(--sm-muted-text, #6b7280)",
|
|
434
|
-
|
|
1032
|
+
marginRight: 10,
|
|
1033
|
+
marginTop: 2
|
|
435
1034
|
},
|
|
436
|
-
children:
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
{
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
style: {
|
|
445
|
-
border: "none",
|
|
446
|
-
background: "transparent",
|
|
447
|
-
color: "inherit",
|
|
448
|
-
cursor: "pointer",
|
|
449
|
-
fontSize: 12,
|
|
450
|
-
padding: 0
|
|
451
|
-
},
|
|
452
|
-
children: "Report"
|
|
453
|
-
}
|
|
454
|
-
) : null,
|
|
455
|
-
canReact ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
456
|
-
"button",
|
|
457
|
-
{
|
|
458
|
-
type: "button",
|
|
459
|
-
onClick: () => setShowPicker((value) => !value),
|
|
460
|
-
style: {
|
|
461
|
-
border: "none",
|
|
462
|
-
background: "transparent",
|
|
463
|
-
color: "inherit",
|
|
464
|
-
cursor: "pointer",
|
|
465
|
-
fontSize: 12,
|
|
466
|
-
padding: 0
|
|
467
|
-
},
|
|
468
|
-
children: "React"
|
|
469
|
-
}
|
|
470
|
-
) : null
|
|
471
|
-
]
|
|
1035
|
+
children: avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1036
|
+
"img",
|
|
1037
|
+
{
|
|
1038
|
+
src: avatarUrl,
|
|
1039
|
+
alt: displayName,
|
|
1040
|
+
style: { width: "100%", height: "100%", objectFit: "cover" }
|
|
1041
|
+
}
|
|
1042
|
+
) : initials
|
|
472
1043
|
}
|
|
473
1044
|
),
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
1045
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", maxWidth: "75%", minWidth: 0 }, children: [
|
|
1046
|
+
showActions && canReact && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1047
|
+
"div",
|
|
1048
|
+
{
|
|
1049
|
+
style: {
|
|
1050
|
+
position: "absolute",
|
|
1051
|
+
top: -12,
|
|
1052
|
+
[isOwn ? "right" : "left"]: 4,
|
|
1053
|
+
zIndex: 50,
|
|
1054
|
+
display: "flex",
|
|
1055
|
+
alignItems: "center",
|
|
1056
|
+
gap: 2,
|
|
1057
|
+
background: "var(--sm-surface, #fff)",
|
|
1058
|
+
border: "1px solid var(--sm-border-color, #e5e7eb)",
|
|
1059
|
+
borderRadius: 8,
|
|
1060
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.1)",
|
|
1061
|
+
padding: "2px 4px"
|
|
1062
|
+
},
|
|
1063
|
+
children: [
|
|
1064
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1065
|
+
EmojiPickerTrigger,
|
|
1066
|
+
{
|
|
1067
|
+
onSelect: (emoji) => void onAddReaction?.(message.id, emoji)
|
|
1068
|
+
}
|
|
1069
|
+
),
|
|
1070
|
+
!isOwn && onReport && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1071
|
+
"button",
|
|
1072
|
+
{
|
|
1073
|
+
onClick: () => {
|
|
1074
|
+
setShowActions(false);
|
|
1075
|
+
onReport(message.id);
|
|
1076
|
+
},
|
|
1077
|
+
type: "button",
|
|
1078
|
+
"aria-label": "Report",
|
|
1079
|
+
title: "Report message",
|
|
1080
|
+
style: {
|
|
1081
|
+
padding: 6,
|
|
1082
|
+
border: "none",
|
|
1083
|
+
background: "transparent",
|
|
1084
|
+
cursor: "pointer",
|
|
1085
|
+
color: "var(--sm-muted-text, #6b7280)",
|
|
1086
|
+
borderRadius: 8,
|
|
1087
|
+
display: "flex",
|
|
1088
|
+
alignItems: "center",
|
|
1089
|
+
justifyContent: "center"
|
|
1090
|
+
},
|
|
1091
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1092
|
+
"svg",
|
|
1093
|
+
{
|
|
1094
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1095
|
+
width: "14",
|
|
1096
|
+
height: "14",
|
|
1097
|
+
viewBox: "0 0 24 24",
|
|
1098
|
+
fill: "none",
|
|
1099
|
+
stroke: "currentColor",
|
|
1100
|
+
strokeWidth: "2",
|
|
1101
|
+
children: [
|
|
1102
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z" }),
|
|
1103
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4", y1: "22", x2: "4", y2: "15" })
|
|
1104
|
+
]
|
|
1105
|
+
}
|
|
1106
|
+
)
|
|
1107
|
+
}
|
|
1108
|
+
),
|
|
1109
|
+
isOwn && onEdit && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1110
|
+
"button",
|
|
1111
|
+
{
|
|
1112
|
+
onClick: () => setEditing(true),
|
|
1113
|
+
type: "button",
|
|
1114
|
+
"aria-label": "Edit",
|
|
1115
|
+
style: {
|
|
1116
|
+
padding: 6,
|
|
1117
|
+
border: "none",
|
|
1118
|
+
background: "transparent",
|
|
1119
|
+
cursor: "pointer",
|
|
1120
|
+
color: "var(--sm-muted-text, #6b7280)",
|
|
1121
|
+
borderRadius: 8,
|
|
1122
|
+
display: "flex",
|
|
1123
|
+
alignItems: "center",
|
|
1124
|
+
justifyContent: "center"
|
|
1125
|
+
},
|
|
1126
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1127
|
+
"svg",
|
|
1128
|
+
{
|
|
1129
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1130
|
+
width: "14",
|
|
1131
|
+
height: "14",
|
|
1132
|
+
viewBox: "0 0 24 24",
|
|
1133
|
+
fill: "none",
|
|
1134
|
+
stroke: "currentColor",
|
|
1135
|
+
strokeWidth: "2",
|
|
1136
|
+
children: [
|
|
1137
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
|
|
1138
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" })
|
|
1139
|
+
]
|
|
1140
|
+
}
|
|
1141
|
+
)
|
|
1142
|
+
}
|
|
1143
|
+
),
|
|
1144
|
+
isOwn && onDelete && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1145
|
+
"button",
|
|
1146
|
+
{
|
|
1147
|
+
onClick: () => void onDelete(message.id),
|
|
1148
|
+
type: "button",
|
|
1149
|
+
"aria-label": "Delete",
|
|
1150
|
+
style: {
|
|
1151
|
+
padding: 6,
|
|
1152
|
+
border: "none",
|
|
1153
|
+
background: "transparent",
|
|
1154
|
+
cursor: "pointer",
|
|
1155
|
+
color: "var(--sm-muted-text, #6b7280)",
|
|
1156
|
+
borderRadius: 8,
|
|
1157
|
+
display: "flex",
|
|
1158
|
+
alignItems: "center",
|
|
1159
|
+
justifyContent: "center"
|
|
1160
|
+
},
|
|
1161
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1162
|
+
"svg",
|
|
1163
|
+
{
|
|
1164
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1165
|
+
width: "14",
|
|
1166
|
+
height: "14",
|
|
1167
|
+
viewBox: "0 0 24 24",
|
|
1168
|
+
fill: "none",
|
|
1169
|
+
stroke: "currentColor",
|
|
1170
|
+
strokeWidth: "2",
|
|
1171
|
+
children: [
|
|
1172
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "3 6 5 6 21 6" }),
|
|
1173
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
|
|
1174
|
+
]
|
|
1175
|
+
}
|
|
1176
|
+
)
|
|
1177
|
+
}
|
|
1178
|
+
)
|
|
1179
|
+
]
|
|
480
1180
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
1181
|
+
),
|
|
1182
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1183
|
+
"div",
|
|
1184
|
+
{
|
|
1185
|
+
style: {
|
|
1186
|
+
borderRadius: "var(--sm-border-radius, 16px)",
|
|
1187
|
+
...isOwn ? { borderBottomRightRadius: 6 } : { borderBottomLeftRadius: 6 },
|
|
1188
|
+
padding: "8px 14px",
|
|
1189
|
+
background: isOwn ? "var(--sm-own-bubble-bg, var(--sm-own-bubble, var(--sm-primary, #2563eb)))" : "var(--sm-other-bubble-bg, var(--sm-other-bubble, #f3f4f6))",
|
|
1190
|
+
color: isOwn ? "var(--sm-own-bubble-text, var(--sm-own-text, #ffffff))" : "var(--sm-other-bubble-text, var(--sm-other-text, #111827))",
|
|
1191
|
+
fontSize: "var(--sm-font-size, 14px)",
|
|
1192
|
+
fontFamily: "var(--sm-font-family, system-ui, -apple-system, sans-serif)",
|
|
1193
|
+
boxShadow: highlight ? "0 0 0 2px rgba(37, 99, 235, 0.22)" : "none",
|
|
1194
|
+
transition: "box-shadow 0.2s ease"
|
|
1195
|
+
},
|
|
1196
|
+
children: [
|
|
1197
|
+
!isOwn && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1198
|
+
"div",
|
|
1199
|
+
{
|
|
1200
|
+
style: {
|
|
1201
|
+
display: "flex",
|
|
1202
|
+
alignItems: "baseline",
|
|
1203
|
+
gap: 6,
|
|
1204
|
+
marginBottom: 2
|
|
1205
|
+
},
|
|
1206
|
+
children: [
|
|
1207
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1208
|
+
"span",
|
|
1209
|
+
{
|
|
1210
|
+
style: {
|
|
1211
|
+
fontSize: 12,
|
|
1212
|
+
fontWeight: 600,
|
|
1213
|
+
color: "var(--sm-other-bubble-text, var(--sm-other-text, #111827))"
|
|
1214
|
+
},
|
|
1215
|
+
children: displayName
|
|
1216
|
+
}
|
|
1217
|
+
),
|
|
1218
|
+
username && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1219
|
+
"span",
|
|
1220
|
+
{
|
|
1221
|
+
style: {
|
|
1222
|
+
fontSize: 12,
|
|
1223
|
+
color: "var(--sm-muted-text, #9ca3af)"
|
|
1224
|
+
},
|
|
1225
|
+
children: [
|
|
1226
|
+
"@",
|
|
1227
|
+
username
|
|
1228
|
+
]
|
|
1229
|
+
}
|
|
1230
|
+
)
|
|
1231
|
+
]
|
|
1232
|
+
}
|
|
1233
|
+
),
|
|
1234
|
+
editing ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8 }, children: [
|
|
1235
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1236
|
+
"input",
|
|
1237
|
+
{
|
|
1238
|
+
type: "text",
|
|
1239
|
+
value: editContent,
|
|
1240
|
+
onChange: (e) => setEditContent(e.target.value),
|
|
1241
|
+
onKeyDown: (e) => {
|
|
1242
|
+
if (e.key === "Enter") handleSaveEdit();
|
|
1243
|
+
if (e.key === "Escape") setEditing(false);
|
|
1244
|
+
},
|
|
1245
|
+
autoFocus: true,
|
|
1246
|
+
style: {
|
|
1247
|
+
flex: 1,
|
|
1248
|
+
fontSize: 14,
|
|
1249
|
+
border: "1px solid var(--sm-border-color, #e5e7eb)",
|
|
1250
|
+
borderRadius: 6,
|
|
1251
|
+
padding: "4px 8px",
|
|
1252
|
+
outline: "none",
|
|
1253
|
+
color: "var(--sm-text-color, #111827)",
|
|
1254
|
+
background: "var(--sm-surface, #fff)"
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
),
|
|
1258
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1259
|
+
"button",
|
|
1260
|
+
{
|
|
1261
|
+
onClick: handleSaveEdit,
|
|
1262
|
+
type: "button",
|
|
1263
|
+
style: {
|
|
1264
|
+
fontSize: 12,
|
|
1265
|
+
fontWeight: 500,
|
|
1266
|
+
border: "none",
|
|
1267
|
+
background: "transparent",
|
|
1268
|
+
cursor: "pointer",
|
|
1269
|
+
color: isOwn ? "rgba(255,255,255,0.7)" : "var(--sm-primary, #2563eb)"
|
|
1270
|
+
},
|
|
1271
|
+
children: "Save"
|
|
1272
|
+
}
|
|
1273
|
+
),
|
|
1274
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1275
|
+
"button",
|
|
1276
|
+
{
|
|
1277
|
+
onClick: () => setEditing(false),
|
|
1278
|
+
type: "button",
|
|
1279
|
+
style: {
|
|
1280
|
+
fontSize: 12,
|
|
1281
|
+
border: "none",
|
|
1282
|
+
background: "transparent",
|
|
1283
|
+
cursor: "pointer",
|
|
1284
|
+
color: isOwn ? "rgba(255,255,255,0.7)" : "var(--sm-muted-text, #6b7280)"
|
|
1285
|
+
},
|
|
1286
|
+
children: "Cancel"
|
|
1287
|
+
}
|
|
1288
|
+
)
|
|
1289
|
+
] }) : message.content ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1290
|
+
"p",
|
|
1291
|
+
{
|
|
1292
|
+
style: {
|
|
1293
|
+
margin: 0,
|
|
1294
|
+
whiteSpace: "pre-wrap",
|
|
1295
|
+
wordBreak: "break-word"
|
|
1296
|
+
},
|
|
1297
|
+
children: message.content
|
|
1298
|
+
}
|
|
1299
|
+
) : null,
|
|
1300
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1301
|
+
"div",
|
|
1302
|
+
{
|
|
1303
|
+
style: {
|
|
1304
|
+
display: "flex",
|
|
1305
|
+
alignItems: "center",
|
|
1306
|
+
gap: 4,
|
|
1307
|
+
marginTop: 2,
|
|
1308
|
+
justifyContent: isOwn ? "flex-end" : "flex-start"
|
|
1309
|
+
},
|
|
1310
|
+
children: [
|
|
1311
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1312
|
+
"span",
|
|
1313
|
+
{
|
|
1314
|
+
style: {
|
|
1315
|
+
fontSize: 10,
|
|
1316
|
+
color: isOwn ? "rgba(255,255,255,0.6)" : "var(--sm-muted-text, #9ca3af)"
|
|
1317
|
+
},
|
|
1318
|
+
children: formatMessageTime(message.created_at)
|
|
1319
|
+
}
|
|
1320
|
+
),
|
|
1321
|
+
message.is_edited && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1322
|
+
"span",
|
|
1323
|
+
{
|
|
1324
|
+
style: {
|
|
1325
|
+
fontSize: 10,
|
|
1326
|
+
fontStyle: "italic",
|
|
1327
|
+
color: isOwn ? "rgba(255,255,255,0.6)" : "var(--sm-muted-text, #9ca3af)"
|
|
1328
|
+
},
|
|
1329
|
+
children: "(edited)"
|
|
1330
|
+
}
|
|
1331
|
+
)
|
|
1332
|
+
]
|
|
1333
|
+
}
|
|
1334
|
+
)
|
|
1335
|
+
]
|
|
1336
|
+
}
|
|
1337
|
+
),
|
|
1338
|
+
message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1339
|
+
"div",
|
|
487
1340
|
{
|
|
488
|
-
type: "button",
|
|
489
|
-
onClick: () => {
|
|
490
|
-
if (reacted) {
|
|
491
|
-
void onRemoveReaction?.(message.id, reaction.emoji);
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
void onAddReaction?.(message.id, reaction.emoji);
|
|
495
|
-
},
|
|
496
1341
|
style: {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
cursor: "pointer",
|
|
503
|
-
fontSize: 12
|
|
1342
|
+
marginTop: 4,
|
|
1343
|
+
display: "flex",
|
|
1344
|
+
flexWrap: "wrap",
|
|
1345
|
+
gap: 8,
|
|
1346
|
+
justifyContent: isOwn ? "flex-end" : "flex-start"
|
|
504
1347
|
},
|
|
505
|
-
children:
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
1348
|
+
children: message.attachments.map((att) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1349
|
+
"div",
|
|
1350
|
+
{
|
|
1351
|
+
style: {
|
|
1352
|
+
borderRadius: 8,
|
|
1353
|
+
overflow: "hidden",
|
|
1354
|
+
maxWidth: 320
|
|
1355
|
+
},
|
|
1356
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1357
|
+
AttachmentRenderer,
|
|
1358
|
+
{
|
|
1359
|
+
att,
|
|
1360
|
+
fetcher: onFetchAttachmentUrl
|
|
1361
|
+
}
|
|
1362
|
+
)
|
|
1363
|
+
},
|
|
1364
|
+
att.file_id
|
|
1365
|
+
))
|
|
1366
|
+
}
|
|
1367
|
+
),
|
|
1368
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1369
|
+
ReactionBar,
|
|
1370
|
+
{
|
|
1371
|
+
reactions: message.reactions ?? [],
|
|
1372
|
+
currentUserId,
|
|
1373
|
+
onToggleReaction: handleToggleReaction
|
|
1374
|
+
}
|
|
1375
|
+
)
|
|
1376
|
+
] })
|
|
514
1377
|
]
|
|
515
1378
|
}
|
|
516
1379
|
);
|
|
517
1380
|
}
|
|
518
|
-
function
|
|
519
|
-
|
|
520
|
-
|
|
1381
|
+
function getDateLabel(dateStr) {
|
|
1382
|
+
const date = new Date(dateStr);
|
|
1383
|
+
const now = /* @__PURE__ */ new Date();
|
|
1384
|
+
const dateDay = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
1385
|
+
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
1386
|
+
const diffDays = Math.round(
|
|
1387
|
+
(today.getTime() - dateDay.getTime()) / (1e3 * 60 * 60 * 24)
|
|
1388
|
+
);
|
|
1389
|
+
if (diffDays === 0) return "Today";
|
|
1390
|
+
if (diffDays === 1) return "Yesterday";
|
|
1391
|
+
return date.toLocaleDateString(void 0, {
|
|
1392
|
+
month: "short",
|
|
1393
|
+
day: "numeric",
|
|
1394
|
+
year: date.getFullYear() !== now.getFullYear() ? "numeric" : void 0
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
function isSameDay(a, b) {
|
|
1398
|
+
return new Date(a).toDateString() === new Date(b).toDateString();
|
|
521
1399
|
}
|
|
522
1400
|
function ChatMessageList({
|
|
523
1401
|
messages,
|
|
524
1402
|
currentUserId,
|
|
525
|
-
|
|
526
|
-
|
|
1403
|
+
conversationId,
|
|
1404
|
+
profiles,
|
|
1405
|
+
hasMore = false,
|
|
1406
|
+
isLoading = false,
|
|
1407
|
+
onLoadMore,
|
|
527
1408
|
onAddReaction,
|
|
528
1409
|
onRemoveReaction,
|
|
1410
|
+
onEdit,
|
|
1411
|
+
onDelete,
|
|
529
1412
|
onReport,
|
|
1413
|
+
onFetchAttachmentUrl,
|
|
1414
|
+
firstUnreadMessageId,
|
|
1415
|
+
unreadSince,
|
|
1416
|
+
isNearBottom: isNearBottomProp,
|
|
1417
|
+
onReachBottom,
|
|
530
1418
|
emptyState
|
|
531
1419
|
}) {
|
|
532
|
-
const containerRef =
|
|
533
|
-
const
|
|
534
|
-
const
|
|
535
|
-
const
|
|
536
|
-
const
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
1420
|
+
const containerRef = React4.useRef(null);
|
|
1421
|
+
const bottomRef = React4.useRef(null);
|
|
1422
|
+
const unreadDividerRef = React4.useRef(null);
|
|
1423
|
+
const prevLengthRef = React4.useRef(messages.length);
|
|
1424
|
+
const isNearBottomRef = React4.useRef(true);
|
|
1425
|
+
const [showNewMessagesPill, setShowNewMessagesPill] = React4.useState(false);
|
|
1426
|
+
const resolvedFirstUnreadId = firstUnreadMessageId ?? (unreadSince ? messages.find(
|
|
1427
|
+
(m) => new Date(m.created_at).getTime() > new Date(unreadSince).getTime()
|
|
1428
|
+
)?.id : void 0);
|
|
1429
|
+
React4.useEffect(() => {
|
|
1430
|
+
if (isNearBottomProp !== void 0) {
|
|
1431
|
+
isNearBottomRef.current = isNearBottomProp;
|
|
1432
|
+
}
|
|
1433
|
+
}, [isNearBottomProp]);
|
|
1434
|
+
const handleScroll = React4.useCallback(() => {
|
|
1435
|
+
const el = containerRef.current;
|
|
1436
|
+
if (!el) return;
|
|
1437
|
+
const threshold = 100;
|
|
1438
|
+
const nearBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - threshold;
|
|
1439
|
+
isNearBottomRef.current = nearBottom;
|
|
1440
|
+
if (nearBottom) {
|
|
1441
|
+
setShowNewMessagesPill(false);
|
|
1442
|
+
}
|
|
1443
|
+
if (unreadDividerRef.current && el) {
|
|
1444
|
+
const dividerRect = unreadDividerRef.current.getBoundingClientRect();
|
|
1445
|
+
const containerRect = el.getBoundingClientRect();
|
|
1446
|
+
if (dividerRect.bottom < containerRect.bottom) {
|
|
1447
|
+
onReachBottom?.();
|
|
1448
|
+
}
|
|
1449
|
+
} else if (nearBottom) {
|
|
1450
|
+
onReachBottom?.();
|
|
1451
|
+
}
|
|
1452
|
+
}, [onReachBottom]);
|
|
1453
|
+
React4.useEffect(() => {
|
|
1454
|
+
if (messages.length > prevLengthRef.current) {
|
|
1455
|
+
if (isNearBottomRef.current) {
|
|
1456
|
+
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
1457
|
+
} else {
|
|
1458
|
+
setShowNewMessagesPill(true);
|
|
1459
|
+
}
|
|
548
1460
|
}
|
|
549
|
-
|
|
550
|
-
}, [messages]);
|
|
551
|
-
|
|
552
|
-
if (!
|
|
553
|
-
|
|
1461
|
+
prevLengthRef.current = messages.length;
|
|
1462
|
+
}, [messages.length]);
|
|
1463
|
+
React4.useEffect(() => {
|
|
1464
|
+
if (!isLoading && messages.length > 0) {
|
|
1465
|
+
if (resolvedFirstUnreadId && unreadDividerRef.current) {
|
|
1466
|
+
unreadDividerRef.current.scrollIntoView({
|
|
1467
|
+
block: "start",
|
|
1468
|
+
behavior: "instant"
|
|
1469
|
+
});
|
|
1470
|
+
} else {
|
|
1471
|
+
bottomRef.current?.scrollIntoView({
|
|
1472
|
+
behavior: "instant"
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
554
1475
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
1476
|
+
}, [isLoading, resolvedFirstUnreadId]);
|
|
1477
|
+
React4.useEffect(() => {
|
|
1478
|
+
const container = containerRef.current;
|
|
1479
|
+
const bottom = bottomRef.current;
|
|
1480
|
+
if (!container || !bottom) return;
|
|
1481
|
+
const observer = new IntersectionObserver(
|
|
1482
|
+
(entries) => {
|
|
1483
|
+
if (entries[0]?.isIntersecting) {
|
|
1484
|
+
onReachBottom?.();
|
|
1485
|
+
}
|
|
1486
|
+
},
|
|
1487
|
+
{ root: container, threshold: 0.1 }
|
|
1488
|
+
);
|
|
1489
|
+
observer.observe(bottom);
|
|
1490
|
+
return () => observer.disconnect();
|
|
1491
|
+
}, [onReachBottom]);
|
|
1492
|
+
const scrollToBottom = React4.useCallback(() => {
|
|
1493
|
+
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
1494
|
+
setShowNewMessagesPill(false);
|
|
1495
|
+
}, []);
|
|
1496
|
+
if (isLoading) {
|
|
1497
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1498
|
+
"div",
|
|
1499
|
+
{
|
|
1500
|
+
style: {
|
|
1501
|
+
flex: 1,
|
|
1502
|
+
display: "flex",
|
|
1503
|
+
alignItems: "center",
|
|
1504
|
+
justifyContent: "center",
|
|
1505
|
+
color: "var(--sm-muted-text, #6b7280)",
|
|
1506
|
+
fontSize: 14,
|
|
1507
|
+
padding: 24
|
|
1508
|
+
},
|
|
1509
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1510
|
+
"div",
|
|
1511
|
+
{
|
|
1512
|
+
style: {
|
|
1513
|
+
width: 20,
|
|
1514
|
+
height: 20,
|
|
1515
|
+
border: "2px solid var(--sm-border-color, #e5e7eb)",
|
|
1516
|
+
borderTopColor: "var(--sm-primary, #2563eb)",
|
|
1517
|
+
borderRadius: 999,
|
|
1518
|
+
animation: "sm-spin 0.8s linear infinite"
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
)
|
|
1522
|
+
}
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
1525
|
+
if (messages.length === 0) {
|
|
559
1526
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
560
1527
|
"div",
|
|
561
1528
|
{
|
|
@@ -573,103 +1540,200 @@ function ChatMessageList({
|
|
|
573
1540
|
);
|
|
574
1541
|
}
|
|
575
1542
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", flex: 1, minHeight: 0 }, children: [
|
|
576
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1543
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `@keyframes sm-spin { to { transform: rotate(360deg); } }` }),
|
|
1544
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
577
1545
|
"div",
|
|
578
1546
|
{
|
|
579
1547
|
ref: containerRef,
|
|
580
|
-
onScroll:
|
|
581
|
-
const element = event.currentTarget;
|
|
582
|
-
const distanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight;
|
|
583
|
-
setShowJumpToLatest(distanceFromBottom > 120);
|
|
584
|
-
},
|
|
1548
|
+
onScroll: handleScroll,
|
|
585
1549
|
style: {
|
|
586
1550
|
height: "100%",
|
|
587
1551
|
overflowY: "auto",
|
|
588
|
-
|
|
589
|
-
display: "flex",
|
|
590
|
-
flexDirection: "column",
|
|
591
|
-
gap: 12,
|
|
1552
|
+
position: "relative",
|
|
592
1553
|
background: "var(--sm-surface-muted, #f8fafc)"
|
|
593
1554
|
},
|
|
594
|
-
children:
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
1555
|
+
children: [
|
|
1556
|
+
hasMore && onLoadMore && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1557
|
+
"div",
|
|
1558
|
+
{
|
|
1559
|
+
style: {
|
|
1560
|
+
display: "flex",
|
|
1561
|
+
justifyContent: "center",
|
|
1562
|
+
padding: "12px 0"
|
|
1563
|
+
},
|
|
1564
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1565
|
+
"button",
|
|
1566
|
+
{
|
|
1567
|
+
onClick: onLoadMore,
|
|
1568
|
+
type: "button",
|
|
1569
|
+
style: {
|
|
1570
|
+
fontSize: 12,
|
|
1571
|
+
color: "var(--sm-muted-text, #6b7280)",
|
|
1572
|
+
fontWeight: 500,
|
|
1573
|
+
padding: "4px 12px",
|
|
1574
|
+
borderRadius: 999,
|
|
1575
|
+
border: "1px solid var(--sm-border-color, #e5e7eb)",
|
|
1576
|
+
background: "var(--sm-surface, #fff)",
|
|
1577
|
+
cursor: "pointer"
|
|
1578
|
+
},
|
|
1579
|
+
children: "Load earlier messages"
|
|
1580
|
+
}
|
|
1581
|
+
)
|
|
1582
|
+
}
|
|
1583
|
+
),
|
|
1584
|
+
messages.map((msg, i) => {
|
|
1585
|
+
const prevMsg = i > 0 ? messages[i - 1] : null;
|
|
1586
|
+
const showDateSeparator = !prevMsg || !isSameDay(msg.created_at, prevMsg.created_at);
|
|
1587
|
+
const showUnreadDivider = resolvedFirstUnreadId === msg.id;
|
|
1588
|
+
const isOwn = msg.sender_id === currentUserId;
|
|
1589
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(React4__default.default.Fragment, { children: [
|
|
1590
|
+
showUnreadDivider && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1591
|
+
"div",
|
|
1592
|
+
{
|
|
1593
|
+
ref: unreadDividerRef,
|
|
1594
|
+
style: {
|
|
1595
|
+
display: "flex",
|
|
1596
|
+
alignItems: "center",
|
|
1597
|
+
gap: 12,
|
|
1598
|
+
padding: "8px 16px",
|
|
1599
|
+
margin: "4px 0"
|
|
1600
|
+
},
|
|
1601
|
+
children: [
|
|
1602
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1603
|
+
"div",
|
|
1604
|
+
{
|
|
1605
|
+
style: {
|
|
1606
|
+
flex: 1,
|
|
1607
|
+
height: 1,
|
|
1608
|
+
background: "var(--sm-unread-divider-color, rgba(37, 99, 235, 0.4))"
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
),
|
|
1612
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1613
|
+
"span",
|
|
1614
|
+
{
|
|
1615
|
+
style: {
|
|
1616
|
+
fontSize: 12,
|
|
1617
|
+
color: "var(--sm-primary, #2563eb)",
|
|
1618
|
+
fontWeight: 500,
|
|
1619
|
+
background: "var(--sm-unread-divider-bg, rgba(37, 99, 235, 0.06))",
|
|
1620
|
+
padding: "2px 8px",
|
|
1621
|
+
borderRadius: 999
|
|
1622
|
+
},
|
|
1623
|
+
children: "New messages"
|
|
1624
|
+
}
|
|
1625
|
+
),
|
|
1626
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1627
|
+
"div",
|
|
1628
|
+
{
|
|
1629
|
+
style: {
|
|
1630
|
+
flex: 1,
|
|
1631
|
+
height: 1,
|
|
1632
|
+
background: "var(--sm-unread-divider-color, rgba(37, 99, 235, 0.4))"
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
)
|
|
1636
|
+
]
|
|
1637
|
+
}
|
|
1638
|
+
),
|
|
1639
|
+
showDateSeparator && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1640
|
+
"div",
|
|
1641
|
+
{
|
|
1642
|
+
style: {
|
|
1643
|
+
display: "flex",
|
|
1644
|
+
alignItems: "center",
|
|
1645
|
+
gap: 12,
|
|
1646
|
+
padding: "8px 16px",
|
|
1647
|
+
margin: "4px 0"
|
|
1648
|
+
},
|
|
1649
|
+
children: [
|
|
1650
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1651
|
+
"div",
|
|
1652
|
+
{
|
|
1653
|
+
style: {
|
|
1654
|
+
flex: 1,
|
|
1655
|
+
height: 1,
|
|
1656
|
+
background: "var(--sm-border-color, #e5e7eb)"
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
),
|
|
1660
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1661
|
+
"span",
|
|
1662
|
+
{
|
|
1663
|
+
style: {
|
|
1664
|
+
fontSize: 12,
|
|
1665
|
+
color: "var(--sm-muted-text, #6b7280)",
|
|
1666
|
+
fontWeight: 500
|
|
1667
|
+
},
|
|
1668
|
+
children: getDateLabel(msg.created_at)
|
|
1669
|
+
}
|
|
1670
|
+
),
|
|
1671
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1672
|
+
"div",
|
|
1673
|
+
{
|
|
1674
|
+
style: {
|
|
1675
|
+
flex: 1,
|
|
1676
|
+
height: 1,
|
|
1677
|
+
background: "var(--sm-border-color, #e5e7eb)"
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
)
|
|
1681
|
+
]
|
|
1682
|
+
}
|
|
1683
|
+
),
|
|
1684
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1685
|
+
ChatMessageItem,
|
|
1686
|
+
{
|
|
1687
|
+
message: msg,
|
|
1688
|
+
currentUserId,
|
|
1689
|
+
conversationId,
|
|
1690
|
+
profile: profiles?.get(msg.sender_id),
|
|
1691
|
+
onAddReaction,
|
|
1692
|
+
onRemoveReaction,
|
|
1693
|
+
onEdit,
|
|
1694
|
+
onDelete,
|
|
1695
|
+
onReport,
|
|
1696
|
+
onFetchAttachmentUrl,
|
|
1697
|
+
isOwnMessage: isOwn,
|
|
1698
|
+
highlight: showUnreadDivider
|
|
1699
|
+
}
|
|
1700
|
+
)
|
|
1701
|
+
] }, msg.id);
|
|
1702
|
+
}),
|
|
1703
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { ref: bottomRef })
|
|
1704
|
+
]
|
|
645
1705
|
}
|
|
646
1706
|
),
|
|
647
|
-
|
|
1707
|
+
showNewMessagesPill && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
648
1708
|
"button",
|
|
649
1709
|
{
|
|
1710
|
+
onClick: scrollToBottom,
|
|
650
1711
|
type: "button",
|
|
651
|
-
onClick: () => {
|
|
652
|
-
containerRef.current?.scrollTo({
|
|
653
|
-
top: containerRef.current.scrollHeight,
|
|
654
|
-
behavior: "smooth"
|
|
655
|
-
});
|
|
656
|
-
setShowJumpToLatest(false);
|
|
657
|
-
},
|
|
658
1712
|
style: {
|
|
659
1713
|
position: "absolute",
|
|
660
|
-
|
|
661
|
-
|
|
1714
|
+
bottom: 12,
|
|
1715
|
+
left: "50%",
|
|
1716
|
+
transform: "translateX(-50%)",
|
|
662
1717
|
border: "none",
|
|
663
1718
|
borderRadius: 999,
|
|
664
1719
|
background: "var(--sm-primary, #2563eb)",
|
|
665
1720
|
color: "#fff",
|
|
666
|
-
padding: "
|
|
1721
|
+
padding: "6px 16px",
|
|
667
1722
|
cursor: "pointer",
|
|
668
|
-
boxShadow: "0 12px
|
|
1723
|
+
boxShadow: "0 4px 12px rgba(37, 99, 235, 0.3)",
|
|
1724
|
+
fontSize: 12,
|
|
1725
|
+
fontWeight: 500,
|
|
1726
|
+
zIndex: 10,
|
|
1727
|
+
display: "flex",
|
|
1728
|
+
alignItems: "center",
|
|
1729
|
+
gap: 4
|
|
669
1730
|
},
|
|
670
|
-
children:
|
|
1731
|
+
children: [
|
|
1732
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 14 }, children: "\u2193" }),
|
|
1733
|
+
" New messages"
|
|
1734
|
+
]
|
|
671
1735
|
}
|
|
672
|
-
)
|
|
1736
|
+
)
|
|
673
1737
|
] });
|
|
674
1738
|
}
|
|
675
1739
|
|
|
@@ -701,8 +1765,10 @@ function ChatThread({
|
|
|
701
1765
|
conversationId,
|
|
702
1766
|
theme,
|
|
703
1767
|
currentUserId,
|
|
1768
|
+
profiles,
|
|
704
1769
|
title = "Chat",
|
|
705
|
-
subtitle
|
|
1770
|
+
subtitle,
|
|
1771
|
+
onFetchAttachmentUrl
|
|
706
1772
|
}) {
|
|
707
1773
|
const client = useChatClient();
|
|
708
1774
|
const resolvedUserId = currentUserId ?? client.userId;
|
|
@@ -711,8 +1777,12 @@ function ChatThread({
|
|
|
711
1777
|
readStatuses,
|
|
712
1778
|
isLoading,
|
|
713
1779
|
error,
|
|
1780
|
+
hasMore,
|
|
714
1781
|
sendMessage,
|
|
1782
|
+
loadMore,
|
|
715
1783
|
markRead,
|
|
1784
|
+
editMessage,
|
|
1785
|
+
deleteMessage,
|
|
716
1786
|
addReaction,
|
|
717
1787
|
removeReaction,
|
|
718
1788
|
reportMessage,
|
|
@@ -720,19 +1790,19 @@ function ChatThread({
|
|
|
720
1790
|
} = useChat(conversationId);
|
|
721
1791
|
const { typingUsers, sendTyping } = useTyping(conversationId);
|
|
722
1792
|
const { members } = usePresence(conversationId);
|
|
723
|
-
const ownReadStatus =
|
|
1793
|
+
const ownReadStatus = React4.useMemo(
|
|
724
1794
|
() => resolvedUserId ? readStatuses.find((status) => status.user_id === resolvedUserId)?.last_read_at : void 0,
|
|
725
1795
|
[readStatuses, resolvedUserId]
|
|
726
1796
|
);
|
|
727
|
-
const otherTypingUsers =
|
|
1797
|
+
const otherTypingUsers = React4.useMemo(
|
|
728
1798
|
() => typingUsers.filter((userId) => userId !== resolvedUserId),
|
|
729
1799
|
[typingUsers, resolvedUserId]
|
|
730
1800
|
);
|
|
731
|
-
const activeMembers =
|
|
1801
|
+
const activeMembers = React4.useMemo(
|
|
732
1802
|
() => members.filter((member) => member.userId !== resolvedUserId),
|
|
733
1803
|
[members, resolvedUserId]
|
|
734
1804
|
);
|
|
735
|
-
|
|
1805
|
+
React4.useEffect(() => {
|
|
736
1806
|
if (!messages.length) return;
|
|
737
1807
|
void markRead();
|
|
738
1808
|
}, [markRead, messages.length, messages[messages.length - 1]?.id]);
|
|
@@ -817,10 +1887,19 @@ function ChatThread({
|
|
|
817
1887
|
{
|
|
818
1888
|
messages,
|
|
819
1889
|
currentUserId: resolvedUserId,
|
|
820
|
-
|
|
1890
|
+
conversationId,
|
|
1891
|
+
profiles,
|
|
1892
|
+
hasMore,
|
|
1893
|
+
isLoading,
|
|
1894
|
+
onLoadMore: loadMore,
|
|
821
1895
|
onAddReaction: (messageId, emoji) => void addReaction(messageId, emoji),
|
|
822
1896
|
onRemoveReaction: (messageId, emoji) => void removeReaction(messageId, emoji),
|
|
1897
|
+
onEdit: (messageId, content) => void editMessage(messageId, content),
|
|
1898
|
+
onDelete: (messageId) => void deleteMessage(messageId),
|
|
823
1899
|
onReport: (messageId) => void reportMessage(messageId, "other"),
|
|
1900
|
+
onFetchAttachmentUrl,
|
|
1901
|
+
unreadSince: ownReadStatus,
|
|
1902
|
+
onReachBottom: () => void markRead(),
|
|
824
1903
|
emptyState: isLoading ? "Loading messages..." : "Start the conversation"
|
|
825
1904
|
}
|
|
826
1905
|
),
|
|
@@ -842,7 +1921,7 @@ function ChatThread({
|
|
|
842
1921
|
onSend: async (content, attachments) => {
|
|
843
1922
|
await sendMessage(content, {
|
|
844
1923
|
attachments,
|
|
845
|
-
message_type: inferMessageType(content, attachments)
|
|
1924
|
+
message_type: inferMessageType(content, attachments ?? [])
|
|
846
1925
|
});
|
|
847
1926
|
},
|
|
848
1927
|
onTypingChange: (isTyping) => {
|
|
@@ -871,8 +1950,8 @@ function ConversationList({
|
|
|
871
1950
|
const { conversations, isLoading } = useConversations({
|
|
872
1951
|
conversationType
|
|
873
1952
|
});
|
|
874
|
-
const [search, setSearch] =
|
|
875
|
-
const filtered =
|
|
1953
|
+
const [search, setSearch] = React4.useState("");
|
|
1954
|
+
const filtered = React4.useMemo(() => {
|
|
876
1955
|
const query = search.trim().toLowerCase();
|
|
877
1956
|
if (!query) return conversations;
|
|
878
1957
|
return conversations.filter((conversation) => {
|
|
@@ -1019,15 +2098,355 @@ function ConversationList({
|
|
|
1019
2098
|
}
|
|
1020
2099
|
);
|
|
1021
2100
|
}
|
|
1022
|
-
var
|
|
2101
|
+
var REPORT_REASONS = [
|
|
2102
|
+
{ value: "spam", label: "Spam" },
|
|
2103
|
+
{ value: "harassment", label: "Harassment" },
|
|
2104
|
+
{ value: "hate", label: "Hate speech" },
|
|
2105
|
+
{ value: "violence", label: "Violence" },
|
|
2106
|
+
{ value: "other", label: "Other" }
|
|
2107
|
+
];
|
|
2108
|
+
function ReportDialog({
|
|
2109
|
+
messageId,
|
|
2110
|
+
onSubmit,
|
|
2111
|
+
onClose
|
|
2112
|
+
}) {
|
|
2113
|
+
const [reason, setReason] = React4.useState("spam");
|
|
2114
|
+
const [description, setDescription] = React4.useState("");
|
|
2115
|
+
const [submitting, setSubmitting] = React4.useState(false);
|
|
2116
|
+
const [error, setError] = React4.useState(null);
|
|
2117
|
+
const [submitted, setSubmitted] = React4.useState(false);
|
|
2118
|
+
async function handleSubmit() {
|
|
2119
|
+
setSubmitting(true);
|
|
2120
|
+
setError(null);
|
|
2121
|
+
try {
|
|
2122
|
+
await onSubmit({
|
|
2123
|
+
messageId,
|
|
2124
|
+
reason,
|
|
2125
|
+
description: description.trim() || void 0
|
|
2126
|
+
});
|
|
2127
|
+
setSubmitted(true);
|
|
2128
|
+
setTimeout(onClose, 1500);
|
|
2129
|
+
} catch (e) {
|
|
2130
|
+
setError(e instanceof Error ? e.message : "Failed to submit report");
|
|
2131
|
+
} finally {
|
|
2132
|
+
setSubmitting(false);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2136
|
+
"div",
|
|
2137
|
+
{
|
|
2138
|
+
style: {
|
|
2139
|
+
position: "fixed",
|
|
2140
|
+
inset: 0,
|
|
2141
|
+
zIndex: 9999,
|
|
2142
|
+
display: "flex",
|
|
2143
|
+
alignItems: "center",
|
|
2144
|
+
justifyContent: "center",
|
|
2145
|
+
background: "rgba(0, 0, 0, 0.5)",
|
|
2146
|
+
backdropFilter: "blur(4px)"
|
|
2147
|
+
},
|
|
2148
|
+
onClick: onClose,
|
|
2149
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2150
|
+
"div",
|
|
2151
|
+
{
|
|
2152
|
+
style: {
|
|
2153
|
+
background: "var(--sm-surface, #fff)",
|
|
2154
|
+
borderRadius: 16,
|
|
2155
|
+
boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
|
|
2156
|
+
width: "100%",
|
|
2157
|
+
maxWidth: 448,
|
|
2158
|
+
margin: "0 16px",
|
|
2159
|
+
overflow: "hidden",
|
|
2160
|
+
color: "var(--sm-text-color, #111827)",
|
|
2161
|
+
fontFamily: "var(--sm-font-family, system-ui, -apple-system, sans-serif)"
|
|
2162
|
+
},
|
|
2163
|
+
onClick: (e) => e.stopPropagation(),
|
|
2164
|
+
children: [
|
|
2165
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2166
|
+
"div",
|
|
2167
|
+
{
|
|
2168
|
+
style: {
|
|
2169
|
+
padding: "16px 24px",
|
|
2170
|
+
borderBottom: "1px solid var(--sm-border-color, #e5e7eb)",
|
|
2171
|
+
display: "flex",
|
|
2172
|
+
alignItems: "center",
|
|
2173
|
+
justifyContent: "space-between"
|
|
2174
|
+
},
|
|
2175
|
+
children: [
|
|
2176
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2177
|
+
"h3",
|
|
2178
|
+
{
|
|
2179
|
+
style: {
|
|
2180
|
+
margin: 0,
|
|
2181
|
+
fontSize: 16,
|
|
2182
|
+
fontWeight: 600,
|
|
2183
|
+
color: "var(--sm-text-color, #111827)"
|
|
2184
|
+
},
|
|
2185
|
+
children: "Report Message"
|
|
2186
|
+
}
|
|
2187
|
+
),
|
|
2188
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2189
|
+
"button",
|
|
2190
|
+
{
|
|
2191
|
+
onClick: onClose,
|
|
2192
|
+
type: "button",
|
|
2193
|
+
"aria-label": "Close",
|
|
2194
|
+
style: {
|
|
2195
|
+
border: "none",
|
|
2196
|
+
background: "transparent",
|
|
2197
|
+
padding: 4,
|
|
2198
|
+
cursor: "pointer",
|
|
2199
|
+
color: "var(--sm-muted-text, #6b7280)",
|
|
2200
|
+
borderRadius: 8,
|
|
2201
|
+
display: "flex",
|
|
2202
|
+
alignItems: "center",
|
|
2203
|
+
justifyContent: "center"
|
|
2204
|
+
},
|
|
2205
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2206
|
+
"svg",
|
|
2207
|
+
{
|
|
2208
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2209
|
+
width: "18",
|
|
2210
|
+
height: "18",
|
|
2211
|
+
viewBox: "0 0 24 24",
|
|
2212
|
+
fill: "none",
|
|
2213
|
+
stroke: "currentColor",
|
|
2214
|
+
strokeWidth: "2",
|
|
2215
|
+
children: [
|
|
2216
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
2217
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
2218
|
+
]
|
|
2219
|
+
}
|
|
2220
|
+
)
|
|
2221
|
+
}
|
|
2222
|
+
)
|
|
2223
|
+
]
|
|
2224
|
+
}
|
|
2225
|
+
),
|
|
2226
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "16px 24px" }, children: submitted ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center", padding: "16px 0" }, children: [
|
|
2227
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2228
|
+
"div",
|
|
2229
|
+
{
|
|
2230
|
+
style: {
|
|
2231
|
+
width: 40,
|
|
2232
|
+
height: 40,
|
|
2233
|
+
borderRadius: 999,
|
|
2234
|
+
background: "#dcfce7",
|
|
2235
|
+
display: "flex",
|
|
2236
|
+
alignItems: "center",
|
|
2237
|
+
justifyContent: "center",
|
|
2238
|
+
margin: "0 auto 12px"
|
|
2239
|
+
},
|
|
2240
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2241
|
+
"svg",
|
|
2242
|
+
{
|
|
2243
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2244
|
+
width: "20",
|
|
2245
|
+
height: "20",
|
|
2246
|
+
viewBox: "0 0 24 24",
|
|
2247
|
+
fill: "none",
|
|
2248
|
+
stroke: "#16a34a",
|
|
2249
|
+
strokeWidth: "2",
|
|
2250
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" })
|
|
2251
|
+
}
|
|
2252
|
+
)
|
|
2253
|
+
}
|
|
2254
|
+
),
|
|
2255
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2256
|
+
"p",
|
|
2257
|
+
{
|
|
2258
|
+
style: {
|
|
2259
|
+
margin: 0,
|
|
2260
|
+
fontSize: 14,
|
|
2261
|
+
fontWeight: 500,
|
|
2262
|
+
color: "var(--sm-text-color, #111827)"
|
|
2263
|
+
},
|
|
2264
|
+
children: "Report submitted"
|
|
2265
|
+
}
|
|
2266
|
+
),
|
|
2267
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2268
|
+
"p",
|
|
2269
|
+
{
|
|
2270
|
+
style: {
|
|
2271
|
+
margin: "4px 0 0",
|
|
2272
|
+
fontSize: 12,
|
|
2273
|
+
color: "var(--sm-muted-text, #6b7280)"
|
|
2274
|
+
},
|
|
2275
|
+
children: "We will review this message shortly."
|
|
2276
|
+
}
|
|
2277
|
+
)
|
|
2278
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2279
|
+
"div",
|
|
2280
|
+
{
|
|
2281
|
+
style: { display: "flex", flexDirection: "column", gap: 16 },
|
|
2282
|
+
children: [
|
|
2283
|
+
error && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2284
|
+
"div",
|
|
2285
|
+
{
|
|
2286
|
+
style: {
|
|
2287
|
+
padding: "8px 12px",
|
|
2288
|
+
background: "#fef2f2",
|
|
2289
|
+
color: "#dc2626",
|
|
2290
|
+
fontSize: 14,
|
|
2291
|
+
borderRadius: 8
|
|
2292
|
+
},
|
|
2293
|
+
children: error
|
|
2294
|
+
}
|
|
2295
|
+
),
|
|
2296
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2297
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2298
|
+
"label",
|
|
2299
|
+
{
|
|
2300
|
+
style: {
|
|
2301
|
+
display: "block",
|
|
2302
|
+
fontSize: 14,
|
|
2303
|
+
fontWeight: 500,
|
|
2304
|
+
color: "var(--sm-text-color, #111827)",
|
|
2305
|
+
marginBottom: 6
|
|
2306
|
+
},
|
|
2307
|
+
children: "Reason"
|
|
2308
|
+
}
|
|
2309
|
+
),
|
|
2310
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2311
|
+
"select",
|
|
2312
|
+
{
|
|
2313
|
+
value: reason,
|
|
2314
|
+
onChange: (e) => setReason(e.target.value),
|
|
2315
|
+
style: {
|
|
2316
|
+
width: "100%",
|
|
2317
|
+
border: "1px solid var(--sm-border-color, #e5e7eb)",
|
|
2318
|
+
borderRadius: 8,
|
|
2319
|
+
padding: "8px 12px",
|
|
2320
|
+
fontSize: 14,
|
|
2321
|
+
background: "var(--sm-surface, #fff)",
|
|
2322
|
+
color: "var(--sm-text-color, #111827)",
|
|
2323
|
+
fontFamily: "inherit",
|
|
2324
|
+
outline: "none"
|
|
2325
|
+
},
|
|
2326
|
+
children: REPORT_REASONS.map((r) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: r.value, children: r.label }, r.value))
|
|
2327
|
+
}
|
|
2328
|
+
)
|
|
2329
|
+
] }),
|
|
2330
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2331
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2332
|
+
"label",
|
|
2333
|
+
{
|
|
2334
|
+
style: {
|
|
2335
|
+
display: "block",
|
|
2336
|
+
fontSize: 14,
|
|
2337
|
+
fontWeight: 500,
|
|
2338
|
+
color: "var(--sm-text-color, #111827)",
|
|
2339
|
+
marginBottom: 6
|
|
2340
|
+
},
|
|
2341
|
+
children: [
|
|
2342
|
+
"Description",
|
|
2343
|
+
" ",
|
|
2344
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2345
|
+
"span",
|
|
2346
|
+
{
|
|
2347
|
+
style: {
|
|
2348
|
+
fontWeight: 400,
|
|
2349
|
+
color: "var(--sm-muted-text, #6b7280)"
|
|
2350
|
+
},
|
|
2351
|
+
children: "(optional)"
|
|
2352
|
+
}
|
|
2353
|
+
)
|
|
2354
|
+
]
|
|
2355
|
+
}
|
|
2356
|
+
),
|
|
2357
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2358
|
+
"textarea",
|
|
2359
|
+
{
|
|
2360
|
+
value: description,
|
|
2361
|
+
onChange: (e) => setDescription(e.target.value),
|
|
2362
|
+
placeholder: "Provide additional details...",
|
|
2363
|
+
rows: 3,
|
|
2364
|
+
maxLength: 1e3,
|
|
2365
|
+
style: {
|
|
2366
|
+
width: "100%",
|
|
2367
|
+
border: "1px solid var(--sm-border-color, #e5e7eb)",
|
|
2368
|
+
borderRadius: 8,
|
|
2369
|
+
padding: "8px 12px",
|
|
2370
|
+
fontSize: 14,
|
|
2371
|
+
fontFamily: "inherit",
|
|
2372
|
+
color: "var(--sm-text-color, #111827)",
|
|
2373
|
+
resize: "none",
|
|
2374
|
+
outline: "none",
|
|
2375
|
+
boxSizing: "border-box"
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
)
|
|
2379
|
+
] })
|
|
2380
|
+
]
|
|
2381
|
+
}
|
|
2382
|
+
) }),
|
|
2383
|
+
!submitted && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2384
|
+
"div",
|
|
2385
|
+
{
|
|
2386
|
+
style: {
|
|
2387
|
+
padding: "16px 24px",
|
|
2388
|
+
borderTop: "1px solid var(--sm-border-color, #e5e7eb)",
|
|
2389
|
+
display: "flex",
|
|
2390
|
+
justifyContent: "flex-end",
|
|
2391
|
+
gap: 8
|
|
2392
|
+
},
|
|
2393
|
+
children: [
|
|
2394
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2395
|
+
"button",
|
|
2396
|
+
{
|
|
2397
|
+
onClick: onClose,
|
|
2398
|
+
type: "button",
|
|
2399
|
+
style: {
|
|
2400
|
+
padding: "8px 16px",
|
|
2401
|
+
fontSize: 14,
|
|
2402
|
+
fontWeight: 500,
|
|
2403
|
+
color: "var(--sm-text-color, #111827)",
|
|
2404
|
+
background: "transparent",
|
|
2405
|
+
border: "none",
|
|
2406
|
+
borderRadius: 8,
|
|
2407
|
+
cursor: "pointer"
|
|
2408
|
+
},
|
|
2409
|
+
children: "Cancel"
|
|
2410
|
+
}
|
|
2411
|
+
),
|
|
2412
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2413
|
+
"button",
|
|
2414
|
+
{
|
|
2415
|
+
onClick: () => void handleSubmit(),
|
|
2416
|
+
disabled: submitting,
|
|
2417
|
+
type: "button",
|
|
2418
|
+
style: {
|
|
2419
|
+
padding: "8px 16px",
|
|
2420
|
+
fontSize: 14,
|
|
2421
|
+
fontWeight: 500,
|
|
2422
|
+
background: "var(--sm-primary, #2563eb)",
|
|
2423
|
+
color: "#fff",
|
|
2424
|
+
border: "none",
|
|
2425
|
+
borderRadius: 8,
|
|
2426
|
+
cursor: submitting ? "wait" : "pointer",
|
|
2427
|
+
opacity: submitting ? 0.5 : 1
|
|
2428
|
+
},
|
|
2429
|
+
children: submitting ? "Submitting..." : "Submit Report"
|
|
2430
|
+
}
|
|
2431
|
+
)
|
|
2432
|
+
]
|
|
2433
|
+
}
|
|
2434
|
+
)
|
|
2435
|
+
]
|
|
2436
|
+
}
|
|
2437
|
+
)
|
|
2438
|
+
}
|
|
2439
|
+
);
|
|
2440
|
+
}
|
|
2441
|
+
var ChatContext = React4.createContext(null);
|
|
1023
2442
|
function useChatContext() {
|
|
1024
|
-
const ctx =
|
|
2443
|
+
const ctx = React4.useContext(ChatContext);
|
|
1025
2444
|
if (!ctx) throw new Error("useChatContext must be used within a ChatProvider");
|
|
1026
2445
|
return ctx;
|
|
1027
2446
|
}
|
|
1028
2447
|
function ChatProvider({ config, children }) {
|
|
1029
|
-
const [client] =
|
|
1030
|
-
|
|
2448
|
+
const [client] = React4.useState(() => new chunkW2PWFS3E_cjs.ChatClient(config));
|
|
2449
|
+
React4.useEffect(() => {
|
|
1031
2450
|
return () => {
|
|
1032
2451
|
client.destroy();
|
|
1033
2452
|
};
|
|
@@ -1042,28 +2461,28 @@ function useChatConfig() {
|
|
|
1042
2461
|
}
|
|
1043
2462
|
function useConnection() {
|
|
1044
2463
|
const { client } = useChatContext();
|
|
1045
|
-
const [status, setStatus] =
|
|
1046
|
-
|
|
2464
|
+
const [status, setStatus] = React4.useState(client.status);
|
|
2465
|
+
React4.useEffect(() => {
|
|
1047
2466
|
return client.on("connected", () => setStatus("connected"));
|
|
1048
2467
|
}, [client]);
|
|
1049
|
-
|
|
2468
|
+
React4.useEffect(() => {
|
|
1050
2469
|
return client.on("disconnected", () => setStatus("disconnected"));
|
|
1051
2470
|
}, [client]);
|
|
1052
|
-
|
|
2471
|
+
React4.useEffect(() => {
|
|
1053
2472
|
return client.on("reconnecting", () => setStatus("reconnecting"));
|
|
1054
2473
|
}, [client]);
|
|
1055
|
-
const connect =
|
|
1056
|
-
const disconnect =
|
|
2474
|
+
const connect = React4.useCallback(() => client.connect(), [client]);
|
|
2475
|
+
const disconnect = React4.useCallback(() => client.disconnect(), [client]);
|
|
1057
2476
|
return { status, connect, disconnect };
|
|
1058
2477
|
}
|
|
1059
2478
|
function useChat(conversationId) {
|
|
1060
2479
|
const { client } = useChatContext();
|
|
1061
|
-
const [messages, setMessages] =
|
|
1062
|
-
const [readStatuses, setReadStatuses] =
|
|
1063
|
-
const [isLoading, setIsLoading] =
|
|
1064
|
-
const [error, setError] =
|
|
1065
|
-
const [hasMore, setHasMore] =
|
|
1066
|
-
|
|
2480
|
+
const [messages, setMessages] = React4.useState([]);
|
|
2481
|
+
const [readStatuses, setReadStatuses] = React4.useState([]);
|
|
2482
|
+
const [isLoading, setIsLoading] = React4.useState(false);
|
|
2483
|
+
const [error, setError] = React4.useState(null);
|
|
2484
|
+
const [hasMore, setHasMore] = React4.useState(false);
|
|
2485
|
+
React4.useEffect(() => {
|
|
1067
2486
|
if (!conversationId) return;
|
|
1068
2487
|
setIsLoading(true);
|
|
1069
2488
|
setError(null);
|
|
@@ -1094,7 +2513,7 @@ function useChat(conversationId) {
|
|
|
1094
2513
|
unsub?.();
|
|
1095
2514
|
};
|
|
1096
2515
|
}, [client, conversationId]);
|
|
1097
|
-
|
|
2516
|
+
React4.useEffect(() => {
|
|
1098
2517
|
if (!conversationId) return;
|
|
1099
2518
|
return client.on("message", ({ message, conversationId: convId }) => {
|
|
1100
2519
|
if (convId === conversationId) {
|
|
@@ -1102,7 +2521,7 @@ function useChat(conversationId) {
|
|
|
1102
2521
|
}
|
|
1103
2522
|
});
|
|
1104
2523
|
}, [client, conversationId]);
|
|
1105
|
-
|
|
2524
|
+
React4.useEffect(() => {
|
|
1106
2525
|
if (!conversationId) return;
|
|
1107
2526
|
return client.on("message:updated", ({ message, conversationId: convId }) => {
|
|
1108
2527
|
if (convId === conversationId) {
|
|
@@ -1110,7 +2529,7 @@ function useChat(conversationId) {
|
|
|
1110
2529
|
}
|
|
1111
2530
|
});
|
|
1112
2531
|
}, [client, conversationId]);
|
|
1113
|
-
|
|
2532
|
+
React4.useEffect(() => {
|
|
1114
2533
|
if (!conversationId) return;
|
|
1115
2534
|
return client.on("message:deleted", ({ messageId, conversationId: convId }) => {
|
|
1116
2535
|
if (convId === conversationId) {
|
|
@@ -1118,7 +2537,7 @@ function useChat(conversationId) {
|
|
|
1118
2537
|
}
|
|
1119
2538
|
});
|
|
1120
2539
|
}, [client, conversationId]);
|
|
1121
|
-
|
|
2540
|
+
React4.useEffect(() => {
|
|
1122
2541
|
if (!conversationId) return;
|
|
1123
2542
|
return client.on("reaction", ({ conversationId: convId }) => {
|
|
1124
2543
|
if (convId === conversationId) {
|
|
@@ -1126,7 +2545,7 @@ function useChat(conversationId) {
|
|
|
1126
2545
|
}
|
|
1127
2546
|
});
|
|
1128
2547
|
}, [client, conversationId]);
|
|
1129
|
-
|
|
2548
|
+
React4.useEffect(() => {
|
|
1130
2549
|
if (!conversationId) return;
|
|
1131
2550
|
return client.on("read", ({ conversationId: convId, userId, lastReadAt }) => {
|
|
1132
2551
|
if (convId !== conversationId) return;
|
|
@@ -1141,14 +2560,14 @@ function useChat(conversationId) {
|
|
|
1141
2560
|
});
|
|
1142
2561
|
});
|
|
1143
2562
|
}, [client, conversationId]);
|
|
1144
|
-
const sendMessage =
|
|
2563
|
+
const sendMessage = React4.useCallback(
|
|
1145
2564
|
async (content, options) => {
|
|
1146
2565
|
if (!conversationId) return;
|
|
1147
2566
|
return client.sendMessage(conversationId, { content, ...options });
|
|
1148
2567
|
},
|
|
1149
2568
|
[client, conversationId]
|
|
1150
2569
|
);
|
|
1151
|
-
const loadMore =
|
|
2570
|
+
const loadMore = React4.useCallback(async () => {
|
|
1152
2571
|
if (!conversationId || !messages.length) return;
|
|
1153
2572
|
const oldestId = messages[0]?.id;
|
|
1154
2573
|
const result = await client.getMessages(conversationId, { before: oldestId });
|
|
@@ -1157,7 +2576,7 @@ function useChat(conversationId) {
|
|
|
1157
2576
|
setHasMore(result.data.has_more ?? false);
|
|
1158
2577
|
}
|
|
1159
2578
|
}, [client, conversationId, messages]);
|
|
1160
|
-
const editMessage =
|
|
2579
|
+
const editMessage = React4.useCallback(
|
|
1161
2580
|
async (messageId, content) => {
|
|
1162
2581
|
const result = await client.editMessage(messageId, content);
|
|
1163
2582
|
if (result.error) {
|
|
@@ -1167,7 +2586,7 @@ function useChat(conversationId) {
|
|
|
1167
2586
|
},
|
|
1168
2587
|
[client]
|
|
1169
2588
|
);
|
|
1170
|
-
const deleteMessage =
|
|
2589
|
+
const deleteMessage = React4.useCallback(
|
|
1171
2590
|
async (messageId) => {
|
|
1172
2591
|
const result = await client.deleteMessage(messageId);
|
|
1173
2592
|
if (result.error) {
|
|
@@ -1177,7 +2596,7 @@ function useChat(conversationId) {
|
|
|
1177
2596
|
},
|
|
1178
2597
|
[client]
|
|
1179
2598
|
);
|
|
1180
|
-
const addReaction =
|
|
2599
|
+
const addReaction = React4.useCallback(
|
|
1181
2600
|
async (messageId, emoji) => {
|
|
1182
2601
|
const result = await client.addReaction(messageId, emoji);
|
|
1183
2602
|
if (result.error) {
|
|
@@ -1187,7 +2606,7 @@ function useChat(conversationId) {
|
|
|
1187
2606
|
},
|
|
1188
2607
|
[client]
|
|
1189
2608
|
);
|
|
1190
|
-
const removeReaction =
|
|
2609
|
+
const removeReaction = React4.useCallback(
|
|
1191
2610
|
async (messageId, emoji) => {
|
|
1192
2611
|
const result = await client.removeReaction(messageId, emoji);
|
|
1193
2612
|
if (result.error) {
|
|
@@ -1197,7 +2616,7 @@ function useChat(conversationId) {
|
|
|
1197
2616
|
},
|
|
1198
2617
|
[client]
|
|
1199
2618
|
);
|
|
1200
|
-
const uploadAttachment =
|
|
2619
|
+
const uploadAttachment = React4.useCallback(
|
|
1201
2620
|
async (file, onProgress, signal) => {
|
|
1202
2621
|
const result = await client.uploadAttachment(file, onProgress, signal);
|
|
1203
2622
|
if (result.error) {
|
|
@@ -1207,7 +2626,7 @@ function useChat(conversationId) {
|
|
|
1207
2626
|
},
|
|
1208
2627
|
[client]
|
|
1209
2628
|
);
|
|
1210
|
-
const refreshAttachmentUrl =
|
|
2629
|
+
const refreshAttachmentUrl = React4.useCallback(
|
|
1211
2630
|
async (messageId, fileId) => {
|
|
1212
2631
|
const result = await client.refreshAttachmentUrl(messageId, fileId);
|
|
1213
2632
|
if (result.error) {
|
|
@@ -1217,7 +2636,7 @@ function useChat(conversationId) {
|
|
|
1217
2636
|
},
|
|
1218
2637
|
[client]
|
|
1219
2638
|
);
|
|
1220
|
-
const reportMessage =
|
|
2639
|
+
const reportMessage = React4.useCallback(
|
|
1221
2640
|
async (messageId, reason, description) => {
|
|
1222
2641
|
const result = await client.reportMessage(messageId, reason, description);
|
|
1223
2642
|
if (result.error) {
|
|
@@ -1227,7 +2646,7 @@ function useChat(conversationId) {
|
|
|
1227
2646
|
},
|
|
1228
2647
|
[client]
|
|
1229
2648
|
);
|
|
1230
|
-
const muteConversation =
|
|
2649
|
+
const muteConversation = React4.useCallback(
|
|
1231
2650
|
async (mutedUntil) => {
|
|
1232
2651
|
if (!conversationId) return;
|
|
1233
2652
|
const result = await client.muteConversation(conversationId, mutedUntil);
|
|
@@ -1238,7 +2657,7 @@ function useChat(conversationId) {
|
|
|
1238
2657
|
},
|
|
1239
2658
|
[client, conversationId]
|
|
1240
2659
|
);
|
|
1241
|
-
const unmuteConversation =
|
|
2660
|
+
const unmuteConversation = React4.useCallback(async () => {
|
|
1242
2661
|
if (!conversationId) return;
|
|
1243
2662
|
const result = await client.unmuteConversation(conversationId);
|
|
1244
2663
|
if (result.error) {
|
|
@@ -1246,7 +2665,7 @@ function useChat(conversationId) {
|
|
|
1246
2665
|
}
|
|
1247
2666
|
return result;
|
|
1248
2667
|
}, [client, conversationId]);
|
|
1249
|
-
const getReadStatus =
|
|
2668
|
+
const getReadStatus = React4.useCallback(async () => {
|
|
1250
2669
|
if (!conversationId) return;
|
|
1251
2670
|
const result = await client.getReadStatus(conversationId);
|
|
1252
2671
|
if (result.data?.statuses) {
|
|
@@ -1256,7 +2675,7 @@ function useChat(conversationId) {
|
|
|
1256
2675
|
}
|
|
1257
2676
|
return result;
|
|
1258
2677
|
}, [client, conversationId]);
|
|
1259
|
-
const markRead =
|
|
2678
|
+
const markRead = React4.useCallback(async () => {
|
|
1260
2679
|
if (!conversationId) return;
|
|
1261
2680
|
await client.markRead(conversationId);
|
|
1262
2681
|
await getReadStatus();
|
|
@@ -1284,8 +2703,8 @@ function useChat(conversationId) {
|
|
|
1284
2703
|
}
|
|
1285
2704
|
function usePresence(conversationId) {
|
|
1286
2705
|
const { client } = useChatContext();
|
|
1287
|
-
const [members, setMembers] =
|
|
1288
|
-
|
|
2706
|
+
const [members, setMembers] = React4.useState([]);
|
|
2707
|
+
React4.useEffect(() => {
|
|
1289
2708
|
if (!conversationId) return;
|
|
1290
2709
|
let cancelled = false;
|
|
1291
2710
|
(async () => {
|
|
@@ -1342,9 +2761,9 @@ function usePresence(conversationId) {
|
|
|
1342
2761
|
}
|
|
1343
2762
|
function useTyping(conversationId) {
|
|
1344
2763
|
const { client } = useChatContext();
|
|
1345
|
-
const [typingUsers, setTypingUsers] =
|
|
1346
|
-
const typingTimers =
|
|
1347
|
-
|
|
2764
|
+
const [typingUsers, setTypingUsers] = React4.useState([]);
|
|
2765
|
+
const typingTimers = React4.useRef(/* @__PURE__ */ new Map());
|
|
2766
|
+
React4.useEffect(() => {
|
|
1348
2767
|
if (!conversationId) return;
|
|
1349
2768
|
const unsubTyping = client.on("typing", ({ conversationId: convId, userId }) => {
|
|
1350
2769
|
if (convId !== conversationId) return;
|
|
@@ -1377,7 +2796,7 @@ function useTyping(conversationId) {
|
|
|
1377
2796
|
typingTimers.current.clear();
|
|
1378
2797
|
};
|
|
1379
2798
|
}, [client, conversationId]);
|
|
1380
|
-
const sendTyping =
|
|
2799
|
+
const sendTyping = React4.useCallback(
|
|
1381
2800
|
(isTyping = true) => {
|
|
1382
2801
|
if (!conversationId) return;
|
|
1383
2802
|
client.sendTyping(conversationId, isTyping);
|
|
@@ -1388,10 +2807,10 @@ function useTyping(conversationId) {
|
|
|
1388
2807
|
}
|
|
1389
2808
|
function useConversations(options) {
|
|
1390
2809
|
const { client } = useChatContext();
|
|
1391
|
-
const [conversations, setConversations] =
|
|
1392
|
-
const [isLoading, setIsLoading] =
|
|
1393
|
-
const refreshTimer =
|
|
1394
|
-
const fetchConversations =
|
|
2810
|
+
const [conversations, setConversations] = React4.useState([]);
|
|
2811
|
+
const [isLoading, setIsLoading] = React4.useState(true);
|
|
2812
|
+
const refreshTimer = React4.useRef(null);
|
|
2813
|
+
const fetchConversations = React4.useCallback(async () => {
|
|
1395
2814
|
const result = await client.listConversations({
|
|
1396
2815
|
conversation_type: options?.conversationType
|
|
1397
2816
|
});
|
|
@@ -1400,10 +2819,10 @@ function useConversations(options) {
|
|
|
1400
2819
|
}
|
|
1401
2820
|
setIsLoading(false);
|
|
1402
2821
|
}, [client, options?.conversationType]);
|
|
1403
|
-
|
|
2822
|
+
React4.useEffect(() => {
|
|
1404
2823
|
fetchConversations();
|
|
1405
2824
|
}, [fetchConversations]);
|
|
1406
|
-
|
|
2825
|
+
React4.useEffect(() => {
|
|
1407
2826
|
return client.on("inbox:update", () => {
|
|
1408
2827
|
if (refreshTimer.current) clearTimeout(refreshTimer.current);
|
|
1409
2828
|
refreshTimer.current = setTimeout(() => {
|
|
@@ -1411,14 +2830,14 @@ function useConversations(options) {
|
|
|
1411
2830
|
}, 500);
|
|
1412
2831
|
});
|
|
1413
2832
|
}, [client, fetchConversations]);
|
|
1414
|
-
|
|
2833
|
+
React4.useEffect(() => {
|
|
1415
2834
|
return client.on("read", ({ conversationId }) => {
|
|
1416
2835
|
setConversations(
|
|
1417
2836
|
(prev) => prev.map((c) => c.id === conversationId ? { ...c, unread_count: 0 } : c)
|
|
1418
2837
|
);
|
|
1419
2838
|
});
|
|
1420
2839
|
}, [client]);
|
|
1421
|
-
|
|
2840
|
+
React4.useEffect(() => {
|
|
1422
2841
|
return () => {
|
|
1423
2842
|
if (refreshTimer.current) clearTimeout(refreshTimer.current);
|
|
1424
2843
|
};
|
|
@@ -1427,8 +2846,8 @@ function useConversations(options) {
|
|
|
1427
2846
|
}
|
|
1428
2847
|
function useUnreadCount() {
|
|
1429
2848
|
const { client } = useChatContext();
|
|
1430
|
-
const [totalUnread, setTotalUnread] =
|
|
1431
|
-
|
|
2849
|
+
const [totalUnread, setTotalUnread] = React4.useState(0);
|
|
2850
|
+
React4.useEffect(() => {
|
|
1432
2851
|
(async () => {
|
|
1433
2852
|
const result = await client.getUnreadTotal();
|
|
1434
2853
|
if (result.data) {
|
|
@@ -1436,12 +2855,12 @@ function useUnreadCount() {
|
|
|
1436
2855
|
}
|
|
1437
2856
|
})();
|
|
1438
2857
|
}, [client]);
|
|
1439
|
-
|
|
2858
|
+
React4.useEffect(() => {
|
|
1440
2859
|
return client.on("inbox:update", () => {
|
|
1441
2860
|
setTotalUnread((prev) => prev + 1);
|
|
1442
2861
|
});
|
|
1443
2862
|
}, [client]);
|
|
1444
|
-
|
|
2863
|
+
React4.useEffect(() => {
|
|
1445
2864
|
return client.on("read", () => {
|
|
1446
2865
|
(async () => {
|
|
1447
2866
|
const result = await client.getUnreadTotal();
|
|
@@ -1465,6 +2884,9 @@ exports.ChatProvider = ChatProvider;
|
|
|
1465
2884
|
exports.ChatThread = ChatThread;
|
|
1466
2885
|
exports.ConversationList = ConversationList;
|
|
1467
2886
|
exports.EmojiPicker = EmojiPicker;
|
|
2887
|
+
exports.EmojiPickerTrigger = EmojiPickerTrigger;
|
|
2888
|
+
exports.ReactionBar = ReactionBar;
|
|
2889
|
+
exports.ReportDialog = ReportDialog;
|
|
1468
2890
|
exports.useChat = useChat;
|
|
1469
2891
|
exports.useChatClient = useChatClient;
|
|
1470
2892
|
exports.useChatConfig = useChatConfig;
|