@thrillee/aegischat 0.1.12 → 0.1.15

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.
@@ -2,7 +2,7 @@
2
2
  // AegisChat React SDK - useAutoRead Hook
3
3
  // ============================================================================
4
4
 
5
- import { useCallback, useEffect, useState } from 'react';
5
+ import { useCallback, useEffect, useRef } from 'react';
6
6
  import { channelsApi } from '../services/api';
7
7
 
8
8
  const SESSION_STORAGE_KEY = '@aegischat/activeChannel';
@@ -18,13 +18,11 @@ export interface UseAutoReadReturn {
18
18
  }
19
19
 
20
20
  export function useAutoRead(options: UseAutoReadOptions = {}): UseAutoReadReturn {
21
- const [isFocused, setIsFocused] = useState(false);
21
+ const isFocusedRef = useRef(typeof document !== 'undefined' && document.hasFocus());
22
22
 
23
23
  useEffect(() => {
24
- setIsFocused(typeof document !== 'undefined' && document.hasFocus());
25
-
26
- const handleFocus = () => setIsFocused(true);
27
- const handleBlur = () => setIsFocused(false);
24
+ const handleFocus = () => { isFocusedRef.current = true; };
25
+ const handleBlur = () => { isFocusedRef.current = false; };
28
26
 
29
27
  window.addEventListener('focus', handleFocus);
30
28
  window.addEventListener('blur', handleBlur);
@@ -36,17 +34,17 @@ export function useAutoRead(options: UseAutoReadOptions = {}): UseAutoReadReturn
36
34
  }, []);
37
35
 
38
36
  const markAsRead = useCallback(async (channelId: string) => {
39
- if (!isFocused) return;
37
+ if (!isFocusedRef.current) return;
40
38
  try {
41
39
  await channelsApi.markAsRead(channelId);
42
40
  options.onMarkAsRead?.(channelId);
43
41
  } catch (error) {
44
42
  console.error('[AegisChat] useAutoRead: Failed to mark as read:', error);
45
43
  }
46
- }, [isFocused, options.onMarkAsRead]);
44
+ }, [options.onMarkAsRead]);
47
45
 
48
46
  const markAllAsRead = useCallback(async () => {
49
- if (!isFocused) return;
47
+ if (!isFocusedRef.current) return;
50
48
  try {
51
49
  const response = await channelsApi.list({});
52
50
  const channels = response.channels || [];
@@ -56,9 +54,9 @@ export function useAutoRead(options: UseAutoReadOptions = {}): UseAutoReadReturn
56
54
  } catch (error) {
57
55
  console.error('[AegisChat] useAutoRead: Failed to mark all as read:', error);
58
56
  }
59
- }, [isFocused]);
57
+ }, []);
60
58
 
61
- return { markAsRead, markAllAsRead, isFocused };
59
+ return { markAsRead, markAllAsRead, isFocused: isFocusedRef.current };
62
60
  }
63
61
 
64
62
  export default useAutoRead;
@@ -84,6 +84,7 @@ export interface UseChatReturn {
84
84
  deleteFailedMessage: (tempId: string) => void;
85
85
  markAsRead: (channelId: string) => Promise<void>;
86
86
  setup: (options: UseChatOptions) => void;
87
+ updateChannel: (channelId: string, updates: Partial<ChannelListItem>) => void;
87
88
  }
88
89
 
89
90
  export function useChat(options: Partial<UseChatOptions> = {}): UseChatReturn {
@@ -101,8 +102,12 @@ export function useChat(options: Partial<UseChatOptions> = {}): UseChatReturn {
101
102
  const [session, setSession] = useState<ChatSession | null>(null);
102
103
  const [isConnected, setIsConnected] = useState(false);
103
104
  const [isConnecting, setIsConnecting] = useState(false);
105
+ const getStoredActiveChannel = (): string | null => {
106
+ if (typeof window === "undefined") return null;
107
+ return sessionStorage.getItem(SESSION_STORAGE_KEY);
108
+ };
104
109
  const [activeChannelId, setActiveChannelIdState] = useState<string | null>(
105
- null,
110
+ getStoredActiveChannel,
106
111
  );
107
112
  const [channels, setChannels] = useState<ChannelListItem[]>([]);
108
113
  const [messages, setMessages] = useState<Message[]>([]);
@@ -126,9 +131,14 @@ export function useChat(options: Partial<UseChatOptions> = {}): UseChatReturn {
126
131
  const roleRef = useRef<string | undefined>(undefined);
127
132
  const clientIdRef = useRef<string | undefined>(undefined);
128
133
  const autoConnectRef = useRef(true);
129
- const onMessageRef = useRef<(message: Message) => void | undefined>(undefined);
130
- const onTypingRef = useRef<((channelId: string, user: TypingUser) => void) | undefined>(undefined);
131
- const onConnectionChangeRef = useRef<((connected: boolean) => void) | undefined>(undefined);
134
+ const onMessageRef =
135
+ useRef<(message: Message) => void | undefined>(undefined);
136
+ const onTypingRef = useRef<
137
+ ((channelId: string, user: TypingUser) => void) | undefined
138
+ >(undefined);
139
+ const onConnectionChangeRef = useRef<
140
+ ((connected: boolean) => void) | undefined
141
+ >(undefined);
132
142
 
133
143
  useEffect(() => {
134
144
  activeChannelIdRef.current = activeChannelId;
@@ -140,6 +150,7 @@ export function useChat(options: Partial<UseChatOptions> = {}): UseChatReturn {
140
150
  }, []);
141
151
 
142
152
  const setActiveChannelId = useCallback((id: string | null) => {
153
+ activeChannelIdRef.current = id;
143
154
  setActiveChannelIdState(id);
144
155
  if (typeof window !== "undefined") {
145
156
  if (id) {
@@ -459,7 +470,9 @@ export function useChat(options: Partial<UseChatOptions> = {}): UseChatReturn {
459
470
 
460
471
  setIsLoadingChannels(true);
461
472
  try {
462
- const response = await fetchFromComms<{ channels: ChannelListItem[] }>('/channels');
473
+ const response = await fetchFromComms<{ channels: ChannelListItem[] }>(
474
+ "/channels",
475
+ );
463
476
  setChannels(response.channels || []);
464
477
  } catch (error) {
465
478
  console.error("[AegisChat] Failed to fetch channels:", error);
@@ -503,7 +516,6 @@ export function useChat(options: Partial<UseChatOptions> = {}): UseChatReturn {
503
516
  if (response.oldest_id) {
504
517
  oldestMessageId.current = response.oldest_id;
505
518
  }
506
-
507
519
  } catch (error) {
508
520
  console.error("[AegisChat] Failed to load messages:", error);
509
521
  setMessages([]);
@@ -525,6 +537,15 @@ export function useChat(options: Partial<UseChatOptions> = {}): UseChatReturn {
525
537
  [fetchFromComms],
526
538
  );
527
539
 
540
+ const updateChannel = useCallback(
541
+ (channelId: string, updates: Partial<ChannelListItem>) => {
542
+ setChannels((prev) =>
543
+ prev.map((ch) => (ch.id === channelId ? { ...ch, ...updates } : ch)),
544
+ );
545
+ },
546
+ [],
547
+ );
548
+
528
549
  const loadMoreMessages = useCallback(async () => {
529
550
  if (!activeChannelId || !hasMoreMessages || isLoadingMessages) return;
530
551
 
@@ -961,9 +982,8 @@ export function useChat(options: Partial<UseChatOptions> = {}): UseChatReturn {
961
982
 
962
983
  if (!config) {
963
984
  const url = initialSession.api_url;
964
- const normalizedUrl = url.includes('/api/v1') || url.includes('/v')
965
- ? url
966
- : `${url}/api/v1`;
985
+ const normalizedUrl =
986
+ url.includes("/api/v1") || url.includes("/v") ? url : `${url}/api/v1`;
967
987
  configureApiClient({
968
988
  baseUrl: normalizedUrl,
969
989
  getAccessToken: async () => sessionRef.current?.access_token || "",
@@ -1047,6 +1067,7 @@ export function useChat(options: Partial<UseChatOptions> = {}): UseChatReturn {
1047
1067
  retryMessage,
1048
1068
  deleteFailedMessage,
1049
1069
  markAsRead,
1070
+ updateChannel,
1050
1071
  setup,
1051
1072
  };
1052
1073
  }