@thrillee/aegischat 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +568 -330
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +568 -330
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/hooks/useChat.ts +721 -389
package/dist/index.js
CHANGED
|
@@ -285,15 +285,28 @@ var MAX_RECONNECT_ATTEMPTS = 5;
|
|
|
285
285
|
var MAX_RECONNECT_DELAY = 3e4;
|
|
286
286
|
var PING_INTERVAL = 3e4;
|
|
287
287
|
var SESSION_STORAGE_KEY = "@aegischat/activeChannel";
|
|
288
|
-
function useChat(options) {
|
|
289
|
-
const {
|
|
290
|
-
|
|
288
|
+
function useChat(options = {}) {
|
|
289
|
+
const {
|
|
290
|
+
config,
|
|
291
|
+
role,
|
|
292
|
+
clientId,
|
|
293
|
+
initialSession,
|
|
294
|
+
autoConnect = true,
|
|
295
|
+
onMessage,
|
|
296
|
+
onTyping,
|
|
297
|
+
onConnectionChange
|
|
298
|
+
} = options;
|
|
299
|
+
const [session, setSession] = (0, import_react.useState)(null);
|
|
291
300
|
const [isConnected, setIsConnected] = (0, import_react.useState)(false);
|
|
292
301
|
const [isConnecting, setIsConnecting] = (0, import_react.useState)(false);
|
|
293
|
-
const [activeChannelId, setActiveChannelIdState] = (0, import_react.useState)(
|
|
302
|
+
const [activeChannelId, setActiveChannelIdState] = (0, import_react.useState)(
|
|
303
|
+
null
|
|
304
|
+
);
|
|
294
305
|
const [channels, setChannels] = (0, import_react.useState)([]);
|
|
295
306
|
const [messages, setMessages] = (0, import_react.useState)([]);
|
|
296
|
-
const [typingUsers, setTypingUsers] = (0, import_react.useState)(
|
|
307
|
+
const [typingUsers, setTypingUsers] = (0, import_react.useState)(
|
|
308
|
+
{}
|
|
309
|
+
);
|
|
297
310
|
const [isLoadingChannels, setIsLoadingChannels] = (0, import_react.useState)(false);
|
|
298
311
|
const [isLoadingMessages, setIsLoadingMessages] = (0, import_react.useState)(false);
|
|
299
312
|
const [hasMoreMessages, setHasMoreMessages] = (0, import_react.useState)(true);
|
|
@@ -306,23 +319,19 @@ function useChat(options) {
|
|
|
306
319
|
const isManualDisconnect = (0, import_react.useRef)(false);
|
|
307
320
|
const oldestMessageId = (0, import_react.useRef)(null);
|
|
308
321
|
const activeChannelIdRef = (0, import_react.useRef)(null);
|
|
309
|
-
const
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
(0, import_react.useEffect)(() => {
|
|
318
|
-
configRef.current = config;
|
|
319
|
-
}, [config]);
|
|
322
|
+
const sessionRef = (0, import_react.useRef)(null);
|
|
323
|
+
const roleRef = (0, import_react.useRef)(void 0);
|
|
324
|
+
const clientIdRef = (0, import_react.useRef)(void 0);
|
|
325
|
+
const autoConnectRef = (0, import_react.useRef)(true);
|
|
326
|
+
const onMessageRef = (0, import_react.useRef)(void 0);
|
|
327
|
+
const onTypingRef = (0, import_react.useRef)(void 0);
|
|
328
|
+
const onConnectionChangeRef = (0, import_react.useRef)(void 0);
|
|
320
329
|
(0, import_react.useEffect)(() => {
|
|
321
330
|
activeChannelIdRef.current = activeChannelId;
|
|
322
331
|
}, [activeChannelId]);
|
|
323
332
|
(0, import_react.useEffect)(() => {
|
|
324
|
-
|
|
325
|
-
}, [
|
|
333
|
+
activeChannelIdRef.current = activeChannelId;
|
|
334
|
+
}, [activeChannelId]);
|
|
326
335
|
const getActiveChannelId = (0, import_react.useCallback)(() => {
|
|
327
336
|
if (typeof window === "undefined") return null;
|
|
328
337
|
return sessionStorage.getItem(SESSION_STORAGE_KEY);
|
|
@@ -337,26 +346,29 @@ function useChat(options) {
|
|
|
337
346
|
}
|
|
338
347
|
}
|
|
339
348
|
}, []);
|
|
340
|
-
const fetchFromComms = (0, import_react.useCallback)(
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
const response = await fetch(`${currentSession.api_url}${path}`, {
|
|
346
|
-
...fetchOptions,
|
|
347
|
-
headers: {
|
|
348
|
-
"Content-Type": "application/json",
|
|
349
|
-
Authorization: `Bearer ${currentSession.access_token}`,
|
|
350
|
-
...fetchOptions.headers
|
|
349
|
+
const fetchFromComms = (0, import_react.useCallback)(
|
|
350
|
+
async (path, fetchOptions = {}) => {
|
|
351
|
+
const currentSession = sessionRef.current;
|
|
352
|
+
if (!currentSession) {
|
|
353
|
+
throw new Error("Chat session not initialized");
|
|
351
354
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
355
|
+
const response = await fetch(`${currentSession.api_url}${path}`, {
|
|
356
|
+
...fetchOptions,
|
|
357
|
+
headers: {
|
|
358
|
+
"Content-Type": "application/json",
|
|
359
|
+
Authorization: `Bearer ${currentSession.access_token}`,
|
|
360
|
+
...fetchOptions.headers
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
if (!response.ok) {
|
|
364
|
+
const error = await response.json().catch(() => ({}));
|
|
365
|
+
throw new Error(error.message || `HTTP ${response.status}`);
|
|
366
|
+
}
|
|
367
|
+
const data = await response.json();
|
|
368
|
+
return data.data || data;
|
|
369
|
+
},
|
|
370
|
+
[]
|
|
371
|
+
);
|
|
360
372
|
const clearTimers = (0, import_react.useCallback)(() => {
|
|
361
373
|
if (reconnectTimeout.current) {
|
|
362
374
|
clearTimeout(reconnectTimeout.current);
|
|
@@ -367,111 +379,141 @@ function useChat(options) {
|
|
|
367
379
|
pingInterval.current = null;
|
|
368
380
|
}
|
|
369
381
|
}, []);
|
|
370
|
-
const handleWebSocketMessage = (0, import_react.useCallback)(
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
382
|
+
const handleWebSocketMessage = (0, import_react.useCallback)(
|
|
383
|
+
(data) => {
|
|
384
|
+
const currentActiveChannelId = activeChannelIdRef.current;
|
|
385
|
+
console.log("[AegisChat] WebSocket message received:", data.type, data);
|
|
386
|
+
switch (data.type) {
|
|
387
|
+
case "message.new": {
|
|
388
|
+
const newMessage = data.payload;
|
|
389
|
+
if (newMessage.channel_id === currentActiveChannelId) {
|
|
390
|
+
setMessages((prev) => {
|
|
391
|
+
const existingIndex = prev.findIndex(
|
|
392
|
+
(m) => m.tempId && m.content === newMessage.content && m.status === "sending"
|
|
393
|
+
);
|
|
394
|
+
if (existingIndex !== -1) {
|
|
395
|
+
const updated = [...prev];
|
|
396
|
+
updated[existingIndex] = { ...newMessage, status: "sent" };
|
|
397
|
+
return updated;
|
|
398
|
+
}
|
|
399
|
+
if (prev.some((m) => m.id === newMessage.id)) return prev;
|
|
400
|
+
return [...prev, { ...newMessage, status: "delivered" }];
|
|
401
|
+
});
|
|
402
|
+
onMessageRef.current?.(newMessage);
|
|
403
|
+
}
|
|
404
|
+
setChannels((prev) => {
|
|
405
|
+
const updated = prev.map(
|
|
406
|
+
(ch) => ch.id === newMessage.channel_id ? {
|
|
407
|
+
...ch,
|
|
408
|
+
last_message: {
|
|
409
|
+
id: newMessage.id,
|
|
410
|
+
content: newMessage.content,
|
|
411
|
+
created_at: newMessage.created_at,
|
|
412
|
+
sender: {
|
|
413
|
+
id: newMessage.sender_id,
|
|
414
|
+
display_name: "Unknown",
|
|
415
|
+
status: "online"
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
unread_count: ch.id === currentActiveChannelId ? 0 : ch.unread_count + 1
|
|
419
|
+
} : ch
|
|
380
420
|
);
|
|
381
|
-
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
return
|
|
385
|
-
}
|
|
386
|
-
if (prev.some((m) => m.id === newMessage.id)) return prev;
|
|
387
|
-
return [...prev, { ...newMessage, status: "delivered" }];
|
|
421
|
+
return updated.sort((a, b) => {
|
|
422
|
+
const timeA = a.last_message?.created_at || "";
|
|
423
|
+
const timeB = b.last_message?.created_at || "";
|
|
424
|
+
return timeB.localeCompare(timeA);
|
|
425
|
+
});
|
|
388
426
|
});
|
|
389
|
-
|
|
427
|
+
break;
|
|
390
428
|
}
|
|
391
|
-
|
|
392
|
-
const
|
|
393
|
-
(ch) => ch.id === newMessage.channel_id ? {
|
|
394
|
-
...ch,
|
|
395
|
-
last_message: {
|
|
396
|
-
id: newMessage.id,
|
|
397
|
-
content: newMessage.content,
|
|
398
|
-
created_at: newMessage.created_at,
|
|
399
|
-
sender: { id: newMessage.sender_id, display_name: "Unknown", status: "online" }
|
|
400
|
-
},
|
|
401
|
-
unread_count: ch.id === currentActiveChannelId ? 0 : ch.unread_count + 1
|
|
402
|
-
} : ch
|
|
403
|
-
);
|
|
404
|
-
return updated.sort((a, b) => {
|
|
405
|
-
const timeA = a.last_message?.created_at || "";
|
|
406
|
-
const timeB = b.last_message?.created_at || "";
|
|
407
|
-
return timeB.localeCompare(timeA);
|
|
408
|
-
});
|
|
409
|
-
});
|
|
410
|
-
break;
|
|
411
|
-
}
|
|
412
|
-
case "message.updated": {
|
|
413
|
-
const updatedMessage = data.payload;
|
|
414
|
-
setMessages((prev) => prev.map((m) => m.id === updatedMessage.id ? updatedMessage : m));
|
|
415
|
-
break;
|
|
416
|
-
}
|
|
417
|
-
case "message.deleted": {
|
|
418
|
-
const { message_id } = data.payload;
|
|
419
|
-
setMessages((prev) => prev.map((m) => m.id === message_id ? { ...m, deleted: true } : m));
|
|
420
|
-
break;
|
|
421
|
-
}
|
|
422
|
-
case "message.delivered":
|
|
423
|
-
case "message.read": {
|
|
424
|
-
const { message_id, channel_id, status } = data.payload;
|
|
425
|
-
if (channel_id === currentActiveChannelId) {
|
|
429
|
+
case "message.updated": {
|
|
430
|
+
const updatedMessage = data.payload;
|
|
426
431
|
setMessages(
|
|
427
|
-
(prev) => prev.map((m) => m.id ===
|
|
432
|
+
(prev) => prev.map((m) => m.id === updatedMessage.id ? updatedMessage : m)
|
|
428
433
|
);
|
|
434
|
+
break;
|
|
429
435
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
case "message.delivered.batch":
|
|
433
|
-
case "message.read.batch": {
|
|
434
|
-
const { channel_id } = data.payload;
|
|
435
|
-
if (channel_id === currentActiveChannelId) {
|
|
436
|
+
case "message.deleted": {
|
|
437
|
+
const { message_id } = data.payload;
|
|
436
438
|
setMessages(
|
|
437
|
-
(prev) => prev.map(
|
|
439
|
+
(prev) => prev.map(
|
|
440
|
+
(m) => m.id === message_id ? { ...m, deleted: true } : m
|
|
441
|
+
)
|
|
438
442
|
);
|
|
443
|
+
break;
|
|
439
444
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
445
|
+
case "message.delivered":
|
|
446
|
+
case "message.read": {
|
|
447
|
+
const { message_id, channel_id, status } = data.payload;
|
|
448
|
+
if (channel_id === currentActiveChannelId) {
|
|
449
|
+
setMessages(
|
|
450
|
+
(prev) => prev.map(
|
|
451
|
+
(m) => m.id === message_id ? {
|
|
452
|
+
...m,
|
|
453
|
+
status: status || "delivered"
|
|
454
|
+
} : m
|
|
455
|
+
)
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
case "message.delivered.batch":
|
|
461
|
+
case "message.read.batch": {
|
|
462
|
+
const { channel_id } = data.payload;
|
|
463
|
+
if (channel_id === currentActiveChannelId) {
|
|
464
|
+
setMessages(
|
|
465
|
+
(prev) => prev.map(
|
|
466
|
+
(m) => m.status === "sent" || m.status === "delivered" ? {
|
|
467
|
+
...m,
|
|
468
|
+
status: data.type === "message.delivered.batch" ? "delivered" : "read"
|
|
469
|
+
} : m
|
|
470
|
+
)
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
case "typing.start": {
|
|
476
|
+
const { channel_id, user } = data.payload;
|
|
477
|
+
const typingUser = {
|
|
478
|
+
id: user.id,
|
|
479
|
+
displayName: user.display_name,
|
|
480
|
+
avatarUrl: user.avatar_url,
|
|
481
|
+
startedAt: Date.now()
|
|
482
|
+
};
|
|
483
|
+
setTypingUsers((prev) => ({
|
|
484
|
+
...prev,
|
|
485
|
+
[channel_id]: [
|
|
486
|
+
...(prev[channel_id] || []).filter((u) => u.id !== user.id),
|
|
487
|
+
typingUser
|
|
488
|
+
]
|
|
489
|
+
}));
|
|
490
|
+
onTypingRef.current?.(channel_id, typingUser);
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
case "typing.stop": {
|
|
494
|
+
const { channel_id, user_id } = data.payload;
|
|
495
|
+
setTypingUsers((prev) => ({
|
|
496
|
+
...prev,
|
|
497
|
+
[channel_id]: (prev[channel_id] || []).filter(
|
|
498
|
+
(u) => u.id !== user_id
|
|
499
|
+
)
|
|
500
|
+
}));
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
case "pong":
|
|
504
|
+
break;
|
|
505
|
+
default:
|
|
506
|
+
console.log("[AegisChat] Unhandled message type:", data.type);
|
|
464
507
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
console.log("[AegisChat] Unhandled message type:", data.type);
|
|
469
|
-
}
|
|
470
|
-
}, [onMessage, onTyping]);
|
|
508
|
+
},
|
|
509
|
+
[]
|
|
510
|
+
);
|
|
471
511
|
const connectWebSocket = (0, import_react.useCallback)(() => {
|
|
472
512
|
const currentSession = sessionRef.current;
|
|
473
513
|
if (!currentSession?.websocket_url || !currentSession?.access_token) {
|
|
474
|
-
console.warn(
|
|
514
|
+
console.warn(
|
|
515
|
+
"[AegisChat] Cannot connect WebSocket - missing session or token"
|
|
516
|
+
);
|
|
475
517
|
return;
|
|
476
518
|
}
|
|
477
519
|
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
@@ -488,14 +530,19 @@ function useChat(options) {
|
|
|
488
530
|
setIsConnected(true);
|
|
489
531
|
setIsConnecting(false);
|
|
490
532
|
reconnectAttempts.current = 0;
|
|
491
|
-
|
|
533
|
+
onConnectionChangeRef.current?.(true);
|
|
492
534
|
pingInterval.current = setInterval(() => {
|
|
493
535
|
if (ws.readyState === WebSocket.OPEN) {
|
|
494
536
|
ws.send(JSON.stringify({ type: "ping" }));
|
|
495
537
|
}
|
|
496
538
|
}, PING_INTERVAL);
|
|
497
539
|
if (activeChannelIdRef.current) {
|
|
498
|
-
ws.send(
|
|
540
|
+
ws.send(
|
|
541
|
+
JSON.stringify({
|
|
542
|
+
type: "channel.join",
|
|
543
|
+
payload: { channel_id: activeChannelIdRef.current }
|
|
544
|
+
})
|
|
545
|
+
);
|
|
499
546
|
}
|
|
500
547
|
};
|
|
501
548
|
ws.onmessage = (event) => {
|
|
@@ -511,9 +558,12 @@ function useChat(options) {
|
|
|
511
558
|
setIsConnected(false);
|
|
512
559
|
setIsConnecting(false);
|
|
513
560
|
clearTimers();
|
|
514
|
-
|
|
561
|
+
onConnectionChangeRef.current?.(false);
|
|
515
562
|
if (!isManualDisconnect.current && reconnectAttempts.current < MAX_RECONNECT_ATTEMPTS) {
|
|
516
|
-
const delay = Math.min(
|
|
563
|
+
const delay = Math.min(
|
|
564
|
+
RECONNECT_INTERVAL * Math.pow(2, reconnectAttempts.current),
|
|
565
|
+
MAX_RECONNECT_DELAY
|
|
566
|
+
);
|
|
517
567
|
console.log(`[AegisChat] Reconnecting in ${delay}ms...`);
|
|
518
568
|
reconnectTimeout.current = setTimeout(() => {
|
|
519
569
|
reconnectAttempts.current++;
|
|
@@ -525,14 +575,18 @@ function useChat(options) {
|
|
|
525
575
|
console.error("[AegisChat] WebSocket error:", error);
|
|
526
576
|
};
|
|
527
577
|
wsRef.current = ws;
|
|
528
|
-
}, [clearTimers, handleWebSocketMessage
|
|
578
|
+
}, [clearTimers, handleWebSocketMessage]);
|
|
529
579
|
const connect = (0, import_react.useCallback)(async () => {
|
|
530
580
|
console.log("[AegisChat] connect() called");
|
|
531
|
-
const targetSession = sessionRef.current
|
|
581
|
+
const targetSession = sessionRef.current;
|
|
532
582
|
if (!targetSession) {
|
|
533
583
|
console.log("[AegisChat] No session available, skipping connect");
|
|
534
584
|
return;
|
|
535
585
|
}
|
|
586
|
+
if (!autoConnectRef.current) {
|
|
587
|
+
console.log("[AegisChat] autoConnect is false, skipping connect");
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
536
590
|
connectWebSocket();
|
|
537
591
|
}, [connectWebSocket]);
|
|
538
592
|
const disconnect = (0, import_react.useCallback)(() => {
|
|
@@ -560,48 +614,72 @@ function useChat(options) {
|
|
|
560
614
|
setIsLoadingChannels(false);
|
|
561
615
|
}
|
|
562
616
|
}, []);
|
|
563
|
-
const selectChannel = (0, import_react.useCallback)(
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
if (
|
|
571
|
-
|
|
617
|
+
const selectChannel = (0, import_react.useCallback)(
|
|
618
|
+
async (channelId) => {
|
|
619
|
+
const currentActiveChannelId = activeChannelIdRef.current;
|
|
620
|
+
setActiveChannelId(channelId);
|
|
621
|
+
setMessages([]);
|
|
622
|
+
setHasMoreMessages(true);
|
|
623
|
+
oldestMessageId.current = null;
|
|
624
|
+
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
625
|
+
if (currentActiveChannelId) {
|
|
626
|
+
wsRef.current.send(
|
|
627
|
+
JSON.stringify({
|
|
628
|
+
type: "channel.leave",
|
|
629
|
+
payload: { channel_id: currentActiveChannelId }
|
|
630
|
+
})
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
wsRef.current.send(
|
|
634
|
+
JSON.stringify({
|
|
635
|
+
type: "channel.join",
|
|
636
|
+
payload: { channel_id: channelId }
|
|
637
|
+
})
|
|
638
|
+
);
|
|
572
639
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
640
|
+
setIsLoadingMessages(true);
|
|
641
|
+
try {
|
|
642
|
+
const response = await fetchFromComms(
|
|
643
|
+
`/channels/${channelId}/messages?limit=50`
|
|
644
|
+
);
|
|
645
|
+
setMessages(response.messages || []);
|
|
646
|
+
setHasMoreMessages(response.has_more);
|
|
647
|
+
if (response.oldest_id) {
|
|
648
|
+
oldestMessageId.current = response.oldest_id;
|
|
649
|
+
}
|
|
650
|
+
await markAsRead(channelId);
|
|
651
|
+
setChannels(
|
|
652
|
+
(prev) => prev.map(
|
|
653
|
+
(ch) => ch.id === channelId ? { ...ch, unread_count: 0 } : ch
|
|
654
|
+
)
|
|
655
|
+
);
|
|
656
|
+
} catch (error) {
|
|
657
|
+
console.error("[AegisChat] Failed to load messages:", error);
|
|
658
|
+
setMessages([]);
|
|
659
|
+
} finally {
|
|
660
|
+
setIsLoadingMessages(false);
|
|
582
661
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
console.error("[AegisChat] Failed to mark as read:", error);
|
|
597
|
-
}
|
|
598
|
-
}, [fetchFromComms]);
|
|
662
|
+
},
|
|
663
|
+
[setActiveChannelId, fetchFromComms]
|
|
664
|
+
);
|
|
665
|
+
const markAsRead = (0, import_react.useCallback)(
|
|
666
|
+
async (channelId) => {
|
|
667
|
+
try {
|
|
668
|
+
await fetchFromComms(`/channels/${channelId}/read`, { method: "POST" });
|
|
669
|
+
} catch (error) {
|
|
670
|
+
console.error("[AegisChat] Failed to mark as read:", error);
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
[fetchFromComms]
|
|
674
|
+
);
|
|
599
675
|
const loadMoreMessages = (0, import_react.useCallback)(async () => {
|
|
600
676
|
if (!activeChannelId || !hasMoreMessages || isLoadingMessages) return;
|
|
601
677
|
setIsLoadingMessages(true);
|
|
602
678
|
try {
|
|
603
679
|
const params = oldestMessageId.current ? `?before=${oldestMessageId.current}&limit=50` : "?limit=50";
|
|
604
|
-
const response = await fetchFromComms(
|
|
680
|
+
const response = await fetchFromComms(
|
|
681
|
+
`/channels/${activeChannelId}/messages${params}`
|
|
682
|
+
);
|
|
605
683
|
setMessages((prev) => [...response.messages || [], ...prev]);
|
|
606
684
|
setHasMoreMessages(response.has_more);
|
|
607
685
|
if (response.oldest_id) {
|
|
@@ -613,138 +691,234 @@ function useChat(options) {
|
|
|
613
691
|
setIsLoadingMessages(false);
|
|
614
692
|
}
|
|
615
693
|
}, [activeChannelId, hasMoreMessages, isLoadingMessages, fetchFromComms]);
|
|
616
|
-
const sendMessage = (0, import_react.useCallback)(
|
|
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
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
});
|
|
659
|
-
} catch (error) {
|
|
660
|
-
console.error("[AegisChat] Failed to send message:", error);
|
|
661
|
-
setMessages(
|
|
662
|
-
(prev) => prev.map(
|
|
663
|
-
(m) => m.tempId === tempId ? { ...m, status: "failed", errorMessage: error instanceof Error ? error.message : "Failed to send" } : m
|
|
664
|
-
)
|
|
665
|
-
);
|
|
666
|
-
throw error;
|
|
667
|
-
}
|
|
668
|
-
}, [fetchFromComms]);
|
|
669
|
-
const uploadFile = (0, import_react.useCallback)(async (file) => {
|
|
670
|
-
const currentSession = sessionRef.current;
|
|
671
|
-
if (!currentSession) return null;
|
|
672
|
-
const fileId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
673
|
-
setUploadProgress((prev) => [...prev, { fileId, fileName: file.name, progress: 0, status: "pending" }]);
|
|
674
|
-
try {
|
|
675
|
-
setUploadProgress((prev) => prev.map((p) => p.fileId === fileId ? { ...p, status: "uploading", progress: 10 } : p));
|
|
676
|
-
const uploadUrlResponse = await fetchFromComms("/files/upload-url", {
|
|
677
|
-
method: "POST",
|
|
678
|
-
body: JSON.stringify({ file_name: file.name, file_type: file.type || "application/octet-stream", file_size: file.size })
|
|
679
|
-
});
|
|
680
|
-
setUploadProgress((prev) => prev.map((p) => p.fileId === fileId ? { ...p, fileId: uploadUrlResponse.file_id, progress: 30 } : p));
|
|
681
|
-
const uploadResponse = await fetch(uploadUrlResponse.upload_url, {
|
|
682
|
-
method: "PUT",
|
|
683
|
-
body: file,
|
|
684
|
-
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
685
|
-
});
|
|
686
|
-
if (!uploadResponse.ok) throw new Error(`Upload failed: ${uploadResponse.statusText}`);
|
|
687
|
-
setUploadProgress((prev) => prev.map((p) => p.fileId === uploadUrlResponse.file_id ? { ...p, status: "confirming", progress: 70 } : p));
|
|
688
|
-
const confirmResponse = await fetchFromComms("/files", {
|
|
689
|
-
method: "POST",
|
|
690
|
-
body: JSON.stringify({ file_id: uploadUrlResponse.file_id })
|
|
694
|
+
const sendMessage = (0, import_react.useCallback)(
|
|
695
|
+
async (content, msgOptions = {}) => {
|
|
696
|
+
const currentActiveChannelId = activeChannelIdRef.current;
|
|
697
|
+
const currentSession = sessionRef.current;
|
|
698
|
+
if (!currentActiveChannelId || !content.trim() || !currentSession) return;
|
|
699
|
+
const tempId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
700
|
+
const trimmedContent = content.trim();
|
|
701
|
+
const optimisticMessage = {
|
|
702
|
+
id: tempId,
|
|
703
|
+
tempId,
|
|
704
|
+
channel_id: currentActiveChannelId,
|
|
705
|
+
sender_id: currentSession.comms_user_id,
|
|
706
|
+
content: trimmedContent,
|
|
707
|
+
type: msgOptions.type || "text",
|
|
708
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
709
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
710
|
+
status: "sending",
|
|
711
|
+
metadata: msgOptions.metadata || {}
|
|
712
|
+
};
|
|
713
|
+
setMessages((prev) => [...prev, optimisticMessage]);
|
|
714
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
715
|
+
setChannels((prev) => {
|
|
716
|
+
const updated = prev.map(
|
|
717
|
+
(ch) => ch.id === currentActiveChannelId ? {
|
|
718
|
+
...ch,
|
|
719
|
+
last_message: {
|
|
720
|
+
id: tempId,
|
|
721
|
+
content: trimmedContent,
|
|
722
|
+
created_at: now,
|
|
723
|
+
sender: {
|
|
724
|
+
id: currentSession.comms_user_id,
|
|
725
|
+
display_name: "You",
|
|
726
|
+
status: "online"
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
} : ch
|
|
730
|
+
);
|
|
731
|
+
return updated.sort((a, b) => {
|
|
732
|
+
const timeA = a.last_message?.created_at || "";
|
|
733
|
+
const timeB = b.last_message?.created_at || "";
|
|
734
|
+
return timeB.localeCompare(timeA);
|
|
735
|
+
});
|
|
691
736
|
});
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
status: "sending",
|
|
718
|
-
metadata: { ...msgOptions.metadata, files: files.map((f) => ({ id: `temp-${f.name}`, filename: f.name, mime_type: f.type, size: f.size, url: "" })) }
|
|
719
|
-
};
|
|
720
|
-
setMessages((prev) => [...prev, optimisticMessage]);
|
|
721
|
-
try {
|
|
722
|
-
const uploadedFiles = [];
|
|
723
|
-
for (const file of files) {
|
|
724
|
-
const attachment = await uploadFile(file);
|
|
725
|
-
if (attachment) uploadedFiles.push(attachment);
|
|
737
|
+
try {
|
|
738
|
+
await fetchFromComms(
|
|
739
|
+
`/channels/${currentActiveChannelId}/messages`,
|
|
740
|
+
{
|
|
741
|
+
method: "POST",
|
|
742
|
+
body: JSON.stringify({
|
|
743
|
+
content: trimmedContent,
|
|
744
|
+
type: msgOptions.type || "text",
|
|
745
|
+
parent_id: msgOptions.parent_id,
|
|
746
|
+
metadata: msgOptions.metadata
|
|
747
|
+
})
|
|
748
|
+
}
|
|
749
|
+
);
|
|
750
|
+
} catch (error) {
|
|
751
|
+
console.error("[AegisChat] Failed to send message:", error);
|
|
752
|
+
setMessages(
|
|
753
|
+
(prev) => prev.map(
|
|
754
|
+
(m) => m.tempId === tempId ? {
|
|
755
|
+
...m,
|
|
756
|
+
status: "failed",
|
|
757
|
+
errorMessage: error instanceof Error ? error.message : "Failed to send"
|
|
758
|
+
} : m
|
|
759
|
+
)
|
|
760
|
+
);
|
|
761
|
+
throw error;
|
|
726
762
|
}
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
763
|
+
},
|
|
764
|
+
[fetchFromComms]
|
|
765
|
+
);
|
|
766
|
+
const uploadFile = (0, import_react.useCallback)(
|
|
767
|
+
async (file) => {
|
|
768
|
+
const currentSession = sessionRef.current;
|
|
769
|
+
if (!currentSession) return null;
|
|
770
|
+
const fileId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
771
|
+
setUploadProgress((prev) => [
|
|
772
|
+
...prev,
|
|
773
|
+
{ fileId, fileName: file.name, progress: 0, status: "pending" }
|
|
774
|
+
]);
|
|
775
|
+
try {
|
|
776
|
+
setUploadProgress(
|
|
777
|
+
(prev) => prev.map(
|
|
778
|
+
(p) => p.fileId === fileId ? { ...p, status: "uploading", progress: 10 } : p
|
|
779
|
+
)
|
|
780
|
+
);
|
|
781
|
+
const uploadUrlResponse = await fetchFromComms("/files/upload-url", {
|
|
782
|
+
method: "POST",
|
|
783
|
+
body: JSON.stringify({
|
|
784
|
+
file_name: file.name,
|
|
785
|
+
file_type: file.type || "application/octet-stream",
|
|
786
|
+
file_size: file.size
|
|
787
|
+
})
|
|
788
|
+
});
|
|
789
|
+
setUploadProgress(
|
|
790
|
+
(prev) => prev.map(
|
|
791
|
+
(p) => p.fileId === fileId ? { ...p, fileId: uploadUrlResponse.file_id, progress: 30 } : p
|
|
792
|
+
)
|
|
793
|
+
);
|
|
794
|
+
const uploadResponse = await fetch(uploadUrlResponse.upload_url, {
|
|
795
|
+
method: "PUT",
|
|
796
|
+
body: file,
|
|
797
|
+
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
798
|
+
});
|
|
799
|
+
if (!uploadResponse.ok)
|
|
800
|
+
throw new Error(`Upload failed: ${uploadResponse.statusText}`);
|
|
801
|
+
setUploadProgress(
|
|
802
|
+
(prev) => prev.map(
|
|
803
|
+
(p) => p.fileId === uploadUrlResponse.file_id ? { ...p, status: "confirming", progress: 70 } : p
|
|
804
|
+
)
|
|
805
|
+
);
|
|
806
|
+
const confirmResponse = await fetchFromComms(
|
|
807
|
+
"/files",
|
|
808
|
+
{
|
|
809
|
+
method: "POST",
|
|
810
|
+
body: JSON.stringify({ file_id: uploadUrlResponse.file_id })
|
|
811
|
+
}
|
|
812
|
+
);
|
|
813
|
+
setUploadProgress(
|
|
814
|
+
(prev) => prev.map(
|
|
815
|
+
(p) => p.fileId === uploadUrlResponse.file_id ? { ...p, status: "complete", progress: 100 } : p
|
|
816
|
+
)
|
|
817
|
+
);
|
|
818
|
+
setTimeout(
|
|
819
|
+
() => setUploadProgress(
|
|
820
|
+
(prev) => prev.filter((p) => p.fileId !== uploadUrlResponse.file_id)
|
|
821
|
+
),
|
|
822
|
+
2e3
|
|
823
|
+
);
|
|
824
|
+
return confirmResponse.file;
|
|
825
|
+
} catch (error) {
|
|
826
|
+
console.error("[AegisChat] Failed to upload file:", error);
|
|
827
|
+
setUploadProgress(
|
|
828
|
+
(prev) => prev.map(
|
|
829
|
+
(p) => p.fileId === fileId ? {
|
|
830
|
+
...p,
|
|
831
|
+
status: "error",
|
|
832
|
+
error: error instanceof Error ? error.message : "Upload failed"
|
|
833
|
+
} : p
|
|
834
|
+
)
|
|
835
|
+
);
|
|
836
|
+
setTimeout(
|
|
837
|
+
() => setUploadProgress(
|
|
838
|
+
(prev) => prev.filter((p) => p.fileId !== fileId)
|
|
839
|
+
),
|
|
840
|
+
5e3
|
|
841
|
+
);
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
},
|
|
845
|
+
[fetchFromComms]
|
|
846
|
+
);
|
|
847
|
+
const sendMessageWithFiles = (0, import_react.useCallback)(
|
|
848
|
+
async (content, files, msgOptions = {}) => {
|
|
849
|
+
const currentActiveChannelId = activeChannelIdRef.current;
|
|
850
|
+
const currentSession = sessionRef.current;
|
|
851
|
+
if (!currentActiveChannelId || !content.trim() && files.length === 0 || !currentSession)
|
|
852
|
+
return;
|
|
853
|
+
const tempId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
854
|
+
const trimmedContent = content.trim();
|
|
855
|
+
const optimisticMessage = {
|
|
856
|
+
id: tempId,
|
|
857
|
+
tempId,
|
|
858
|
+
channel_id: currentActiveChannelId,
|
|
859
|
+
sender_id: currentSession.comms_user_id,
|
|
860
|
+
content: trimmedContent || `Uploading ${files.length} file(s)...`,
|
|
861
|
+
type: "file",
|
|
862
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
863
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
864
|
+
status: "sending",
|
|
865
|
+
metadata: {
|
|
866
|
+
...msgOptions.metadata,
|
|
867
|
+
files: files.map((f) => ({
|
|
868
|
+
id: `temp-${f.name}`,
|
|
869
|
+
filename: f.name,
|
|
870
|
+
mime_type: f.type,
|
|
871
|
+
size: f.size,
|
|
872
|
+
url: ""
|
|
873
|
+
}))
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
setMessages((prev) => [...prev, optimisticMessage]);
|
|
877
|
+
try {
|
|
878
|
+
const uploadedFiles = [];
|
|
879
|
+
for (const file of files) {
|
|
880
|
+
const attachment = await uploadFile(file);
|
|
881
|
+
if (attachment) uploadedFiles.push(attachment);
|
|
882
|
+
}
|
|
883
|
+
const messageType = uploadedFiles.length > 0 && !trimmedContent ? "file" : "text";
|
|
884
|
+
await fetchFromComms(
|
|
885
|
+
`/channels/${currentActiveChannelId}/messages`,
|
|
886
|
+
{
|
|
887
|
+
method: "POST",
|
|
888
|
+
body: JSON.stringify({
|
|
889
|
+
content: trimmedContent || (uploadedFiles.length > 0 ? `Shared ${uploadedFiles.length} file(s)` : ""),
|
|
890
|
+
type: msgOptions.type || messageType,
|
|
891
|
+
parent_id: msgOptions.parent_id,
|
|
892
|
+
metadata: { ...msgOptions.metadata, files: uploadedFiles },
|
|
893
|
+
file_ids: uploadedFiles.map((f) => f.id)
|
|
894
|
+
})
|
|
895
|
+
}
|
|
896
|
+
);
|
|
897
|
+
} catch (error) {
|
|
898
|
+
console.error("[AegisChat] Failed to send message with files:", error);
|
|
899
|
+
setMessages(
|
|
900
|
+
(prev) => prev.map(
|
|
901
|
+
(m) => m.tempId === tempId ? {
|
|
902
|
+
...m,
|
|
903
|
+
status: "failed",
|
|
904
|
+
errorMessage: error instanceof Error ? error.message : "Failed to send"
|
|
905
|
+
} : m
|
|
906
|
+
)
|
|
907
|
+
);
|
|
908
|
+
throw error;
|
|
909
|
+
}
|
|
910
|
+
},
|
|
911
|
+
[fetchFromComms, uploadFile]
|
|
912
|
+
);
|
|
744
913
|
const stopTyping = (0, import_react.useCallback)(() => {
|
|
745
914
|
const currentActiveChannelId = activeChannelIdRef.current;
|
|
746
915
|
if (!currentActiveChannelId || !wsRef.current) return;
|
|
747
|
-
wsRef.current.send(
|
|
916
|
+
wsRef.current.send(
|
|
917
|
+
JSON.stringify({
|
|
918
|
+
type: "typing.stop",
|
|
919
|
+
payload: { channel_id: currentActiveChannelId }
|
|
920
|
+
})
|
|
921
|
+
);
|
|
748
922
|
if (typingTimeout.current) {
|
|
749
923
|
clearTimeout(typingTimeout.current);
|
|
750
924
|
typingTimeout.current = null;
|
|
@@ -753,46 +927,106 @@ function useChat(options) {
|
|
|
753
927
|
const startTyping = (0, import_react.useCallback)(() => {
|
|
754
928
|
const currentActiveChannelId = activeChannelIdRef.current;
|
|
755
929
|
if (!currentActiveChannelId || !wsRef.current) return;
|
|
756
|
-
wsRef.current.send(
|
|
930
|
+
wsRef.current.send(
|
|
931
|
+
JSON.stringify({
|
|
932
|
+
type: "typing.start",
|
|
933
|
+
payload: { channel_id: currentActiveChannelId }
|
|
934
|
+
})
|
|
935
|
+
);
|
|
757
936
|
if (typingTimeout.current) clearTimeout(typingTimeout.current);
|
|
758
937
|
typingTimeout.current = setTimeout(stopTyping, TYPING_TIMEOUT);
|
|
759
938
|
}, [stopTyping]);
|
|
760
|
-
const createDMWithUser = (0, import_react.useCallback)(
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
939
|
+
const createDMWithUser = (0, import_react.useCallback)(
|
|
940
|
+
async (userId) => {
|
|
941
|
+
try {
|
|
942
|
+
const channel = await fetchFromComms("/channels/dm", {
|
|
943
|
+
method: "POST",
|
|
944
|
+
body: JSON.stringify({ user_id: userId })
|
|
945
|
+
});
|
|
946
|
+
await refreshChannels();
|
|
947
|
+
return channel.id;
|
|
948
|
+
} catch (error) {
|
|
949
|
+
console.error("[AegisChat] Failed to create DM:", error);
|
|
950
|
+
return null;
|
|
951
|
+
}
|
|
952
|
+
},
|
|
953
|
+
[fetchFromComms, refreshChannels]
|
|
954
|
+
);
|
|
955
|
+
const retryMessage = (0, import_react.useCallback)(
|
|
956
|
+
async (tempId) => {
|
|
957
|
+
const failedMessage = messages.find(
|
|
958
|
+
(m) => m.tempId === tempId && m.status === "failed"
|
|
959
|
+
);
|
|
960
|
+
const currentActiveChannelId = activeChannelIdRef.current;
|
|
961
|
+
if (!failedMessage || !currentActiveChannelId) return;
|
|
962
|
+
setMessages(
|
|
963
|
+
(prev) => prev.map(
|
|
964
|
+
(m) => m.tempId === tempId ? { ...m, status: "sending", errorMessage: void 0 } : m
|
|
965
|
+
)
|
|
966
|
+
);
|
|
967
|
+
try {
|
|
968
|
+
await fetchFromComms(
|
|
969
|
+
`/channels/${currentActiveChannelId}/messages`,
|
|
970
|
+
{
|
|
971
|
+
method: "POST",
|
|
972
|
+
body: JSON.stringify({
|
|
973
|
+
content: failedMessage.content,
|
|
974
|
+
type: failedMessage.type,
|
|
975
|
+
metadata: failedMessage.metadata
|
|
976
|
+
})
|
|
977
|
+
}
|
|
978
|
+
);
|
|
979
|
+
} catch (error) {
|
|
980
|
+
console.error("[AegisChat] Failed to retry message:", error);
|
|
981
|
+
setMessages(
|
|
982
|
+
(prev) => prev.map(
|
|
983
|
+
(m) => m.tempId === tempId ? {
|
|
984
|
+
...m,
|
|
985
|
+
status: "failed",
|
|
986
|
+
errorMessage: error instanceof Error ? error.message : "Failed to send"
|
|
987
|
+
} : m
|
|
988
|
+
)
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
},
|
|
992
|
+
[messages, fetchFromComms]
|
|
993
|
+
);
|
|
788
994
|
const deleteFailedMessage = (0, import_react.useCallback)((tempId) => {
|
|
789
995
|
setMessages((prev) => prev.filter((m) => m.tempId !== tempId));
|
|
790
996
|
}, []);
|
|
997
|
+
const setup = (0, import_react.useCallback)((options2) => {
|
|
998
|
+
const {
|
|
999
|
+
config: config2,
|
|
1000
|
+
role: role2,
|
|
1001
|
+
clientId: clientId2,
|
|
1002
|
+
initialSession: initialSession2,
|
|
1003
|
+
autoConnect: autoConnect2 = true,
|
|
1004
|
+
onMessage: onMessage2,
|
|
1005
|
+
onTyping: onTyping2,
|
|
1006
|
+
onConnectionChange: onConnectionChange2
|
|
1007
|
+
} = options2;
|
|
1008
|
+
roleRef.current = role2;
|
|
1009
|
+
clientIdRef.current = clientId2;
|
|
1010
|
+
autoConnectRef.current = autoConnect2;
|
|
1011
|
+
onMessageRef.current = onMessage2;
|
|
1012
|
+
onTypingRef.current = onTyping2;
|
|
1013
|
+
onConnectionChangeRef.current = onConnectionChange2;
|
|
1014
|
+
if (initialSession2) {
|
|
1015
|
+
sessionRef.current = initialSession2;
|
|
1016
|
+
if (!config2) {
|
|
1017
|
+
configureApiClient({
|
|
1018
|
+
baseUrl: initialSession2.api_url,
|
|
1019
|
+
getAccessToken: async () => sessionRef.current?.access_token || ""
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
setSession(initialSession2);
|
|
1023
|
+
}
|
|
1024
|
+
}, []);
|
|
791
1025
|
(0, import_react.useEffect)(() => {
|
|
792
|
-
if (
|
|
1026
|
+
if (session && !isConnected && !isConnecting && autoConnectRef.current) {
|
|
793
1027
|
connectWebSocket();
|
|
794
1028
|
}
|
|
795
|
-
}, [
|
|
1029
|
+
}, [session, isConnected, isConnecting, connectWebSocket]);
|
|
796
1030
|
(0, import_react.useEffect)(() => {
|
|
797
1031
|
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
|
|
798
1032
|
wsRef.current.onmessage = (event) => {
|
|
@@ -800,7 +1034,10 @@ function useChat(options) {
|
|
|
800
1034
|
const data = JSON.parse(event.data);
|
|
801
1035
|
handleWebSocketMessage(data);
|
|
802
1036
|
} catch (error) {
|
|
803
|
-
console.error(
|
|
1037
|
+
console.error(
|
|
1038
|
+
"[AegisChat] Failed to parse WebSocket message:",
|
|
1039
|
+
error
|
|
1040
|
+
);
|
|
804
1041
|
}
|
|
805
1042
|
};
|
|
806
1043
|
}
|
|
@@ -852,7 +1089,8 @@ function useChat(options) {
|
|
|
852
1089
|
createDMWithUser,
|
|
853
1090
|
retryMessage,
|
|
854
1091
|
deleteFailedMessage,
|
|
855
|
-
markAsRead
|
|
1092
|
+
markAsRead,
|
|
1093
|
+
setup
|
|
856
1094
|
};
|
|
857
1095
|
}
|
|
858
1096
|
|