@thrillee/aegischat 0.1.14 → 0.1.16
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 +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +33 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +49 -28
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/hooks/useAutoRead.ts +28 -13
- package/src/hooks/useChat.ts +9 -4
package/src/hooks/useAutoRead.ts
CHANGED
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
// AegisChat React SDK - useAutoRead Hook
|
|
3
3
|
// ============================================================================
|
|
4
4
|
|
|
5
|
-
import { useCallback, useEffect,
|
|
5
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
6
6
|
import { channelsApi } from '../services/api';
|
|
7
7
|
|
|
8
|
-
const SESSION_STORAGE_KEY = '@aegischat/activeChannel';
|
|
9
|
-
|
|
10
8
|
export interface UseAutoReadOptions {
|
|
11
9
|
onMarkAsRead?: (channelId: string) => void;
|
|
12
10
|
}
|
|
@@ -14,17 +12,24 @@ export interface UseAutoReadOptions {
|
|
|
14
12
|
export interface UseAutoReadReturn {
|
|
15
13
|
markAsRead: (channelId: string) => Promise<void>;
|
|
16
14
|
markAllAsRead: () => Promise<void>;
|
|
15
|
+
/** Returns current focus state - use this getter to avoid stale closures */
|
|
16
|
+
getIsFocused: () => boolean;
|
|
17
|
+
/** @deprecated Use getIsFocused() instead to avoid stale closures in callbacks */
|
|
17
18
|
isFocused: boolean;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export function useAutoRead(options: UseAutoReadOptions = {}): UseAutoReadReturn {
|
|
21
|
-
const
|
|
22
|
+
const isFocusedRef = useRef(typeof document !== 'undefined' && document.hasFocus());
|
|
23
|
+
const onMarkAsReadRef = useRef(options.onMarkAsRead);
|
|
22
24
|
|
|
25
|
+
// Keep the callback ref updated
|
|
23
26
|
useEffect(() => {
|
|
24
|
-
|
|
27
|
+
onMarkAsReadRef.current = options.onMarkAsRead;
|
|
28
|
+
}, [options.onMarkAsRead]);
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
const
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
const handleFocus = () => { isFocusedRef.current = true; };
|
|
32
|
+
const handleBlur = () => { isFocusedRef.current = false; };
|
|
28
33
|
|
|
29
34
|
window.addEventListener('focus', handleFocus);
|
|
30
35
|
window.addEventListener('blur', handleBlur);
|
|
@@ -35,18 +40,22 @@ export function useAutoRead(options: UseAutoReadOptions = {}): UseAutoReadReturn
|
|
|
35
40
|
};
|
|
36
41
|
}, []);
|
|
37
42
|
|
|
43
|
+
const getIsFocused = useCallback(() => {
|
|
44
|
+
return isFocusedRef.current;
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
38
47
|
const markAsRead = useCallback(async (channelId: string) => {
|
|
39
|
-
if (!
|
|
48
|
+
if (!isFocusedRef.current) return;
|
|
40
49
|
try {
|
|
41
50
|
await channelsApi.markAsRead(channelId);
|
|
42
|
-
|
|
51
|
+
onMarkAsReadRef.current?.(channelId);
|
|
43
52
|
} catch (error) {
|
|
44
53
|
console.error('[AegisChat] useAutoRead: Failed to mark as read:', error);
|
|
45
54
|
}
|
|
46
|
-
}, [
|
|
55
|
+
}, []);
|
|
47
56
|
|
|
48
57
|
const markAllAsRead = useCallback(async () => {
|
|
49
|
-
if (!
|
|
58
|
+
if (!isFocusedRef.current) return;
|
|
50
59
|
try {
|
|
51
60
|
const response = await channelsApi.list({});
|
|
52
61
|
const channels = response.channels || [];
|
|
@@ -56,9 +65,15 @@ export function useAutoRead(options: UseAutoReadOptions = {}): UseAutoReadReturn
|
|
|
56
65
|
} catch (error) {
|
|
57
66
|
console.error('[AegisChat] useAutoRead: Failed to mark all as read:', error);
|
|
58
67
|
}
|
|
59
|
-
}, [
|
|
68
|
+
}, []);
|
|
60
69
|
|
|
61
|
-
return {
|
|
70
|
+
return {
|
|
71
|
+
markAsRead,
|
|
72
|
+
markAllAsRead,
|
|
73
|
+
getIsFocused,
|
|
74
|
+
// Keep for backwards compatibility but warn it is deprecated
|
|
75
|
+
isFocused: isFocusedRef.current
|
|
76
|
+
};
|
|
62
77
|
}
|
|
63
78
|
|
|
64
79
|
export default useAutoRead;
|
package/src/hooks/useChat.ts
CHANGED
|
@@ -37,7 +37,7 @@ export interface UseChatOptions {
|
|
|
37
37
|
|
|
38
38
|
initialSession?: ChatSession | null;
|
|
39
39
|
autoConnect?: boolean;
|
|
40
|
-
onMessage?: (message: Message) => void;
|
|
40
|
+
onMessage?: (message: Message, context: { activeChannelId: string | null }) => void;
|
|
41
41
|
onTyping?: (channelId: string, user: TypingUser) => void;
|
|
42
42
|
onConnectionChange?: (connected: boolean) => void;
|
|
43
43
|
}
|
|
@@ -102,8 +102,12 @@ export function useChat(options: Partial<UseChatOptions> = {}): UseChatReturn {
|
|
|
102
102
|
const [session, setSession] = useState<ChatSession | null>(null);
|
|
103
103
|
const [isConnected, setIsConnected] = useState(false);
|
|
104
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
|
+
};
|
|
105
109
|
const [activeChannelId, setActiveChannelIdState] = useState<string | null>(
|
|
106
|
-
|
|
110
|
+
getStoredActiveChannel,
|
|
107
111
|
);
|
|
108
112
|
const [channels, setChannels] = useState<ChannelListItem[]>([]);
|
|
109
113
|
const [messages, setMessages] = useState<Message[]>([]);
|
|
@@ -128,7 +132,7 @@ export function useChat(options: Partial<UseChatOptions> = {}): UseChatReturn {
|
|
|
128
132
|
const clientIdRef = useRef<string | undefined>(undefined);
|
|
129
133
|
const autoConnectRef = useRef(true);
|
|
130
134
|
const onMessageRef =
|
|
131
|
-
useRef<(message: Message) => void | undefined>(undefined);
|
|
135
|
+
useRef<((message: Message, context: { activeChannelId: string | null }) => void) | undefined>(undefined);
|
|
132
136
|
const onTypingRef = useRef<
|
|
133
137
|
((channelId: string, user: TypingUser) => void) | undefined
|
|
134
138
|
>(undefined);
|
|
@@ -146,6 +150,7 @@ export function useChat(options: Partial<UseChatOptions> = {}): UseChatReturn {
|
|
|
146
150
|
}, []);
|
|
147
151
|
|
|
148
152
|
const setActiveChannelId = useCallback((id: string | null) => {
|
|
153
|
+
activeChannelIdRef.current = id;
|
|
149
154
|
setActiveChannelIdState(id);
|
|
150
155
|
if (typeof window !== "undefined") {
|
|
151
156
|
if (id) {
|
|
@@ -218,7 +223,7 @@ export function useChat(options: Partial<UseChatOptions> = {}): UseChatReturn {
|
|
|
218
223
|
if (prev.some((m) => m.id === newMessage.id)) return prev;
|
|
219
224
|
return [...prev, { ...newMessage, status: "delivered" }];
|
|
220
225
|
});
|
|
221
|
-
onMessageRef.current?.(newMessage);
|
|
226
|
+
onMessageRef.current?.(newMessage, { activeChannelId: currentActiveChannelId });
|
|
222
227
|
}
|
|
223
228
|
setChannels((prev) => {
|
|
224
229
|
const updated = prev.map((ch) =>
|