@cmnd-ai/chatbot-react 1.10.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,432 @@
1
+ import { useState, useEffect, useCallback, useMemo, useRef } from "react";
2
+ import { v4 as uuidv4 } from "uuid";
3
+ import postUserConversation from "../services/postUserConversation.js";
4
+ import getChatBotConversationsList from "../services/getChatBotConversationsList/index.js";
5
+ import getEmbedChatBotById from "../services/getEmbedChatBotById.js";
6
+ import { MessageRole, FunctionType, } from "../type.js";
7
+ import getConversationLocalStorageKey from "../utils/getConversationLocalStorageKey/index.js";
8
+ import saveConversationIdToLocalStorage from "../utils/saveConversationIdToLocalStorage/index.js";
9
+ import getUTCDateTime from "../utils/getUTCDateTime/index.js";
10
+ import parseUITools from "../utils/parseUITools.js";
11
+ import getTools from "../utils/getTools/index.js";
12
+ import { cleanMarkdown } from "../utils/cleanMarkdown.js";
13
+ import { useSocket } from "./use-socket.js";
14
+ import { SOCKET_MESSAGE_TYPES } from "../constants/socketEvents.js";
15
+ /**
16
+ * A comprehensive hook to manage CMND.AI chatbot logic without being forced to use the default UI.
17
+ * This hook handles conversation state, message sending (including streaming), history management, and tool call orchestration.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * const { messages, sendMessage, input, setInput, submitToolResult } = useCMNDChat({
22
+ * chatbotId: 1,
23
+ * organizationId: 1,
24
+ * baseUrl: 'https://api.cmnd.ai',
25
+ * cleanResponse: true, // Optional: Returns assistant messages as plaintext
26
+ * onToolCall: async (toolDetails) => {
27
+ * // Backend tools are auto-confirmed by the hook
28
+ * if (toolDetails.name === 'get_playlists') {
29
+ * console.log('Fetching playlists from backend...');
30
+ * }
31
+ *
32
+ * // UI tools need manual execution and confirmation
33
+ * if (toolDetails.name === 'show_notification') {
34
+ * showNotification(toolDetails.args.message);
35
+ * submitToolResult('Notification displayed');
36
+ * }
37
+ * }
38
+ * });
39
+ * ```
40
+ */
41
+ export const useCMNDChat = (options) => {
42
+ const { chatbotId, organizationId, baseUrl, apiKey, chatHistoryStorageKey, initialMemory, UITools, onData: globalOnData, onToolCall, cleanResponse = false, } = options;
43
+ const [messages, setMessages] = useState([]);
44
+ const [input, setInput] = useState("");
45
+ const [isChatLoading, setIsChatLoading] = useState(false);
46
+ const [canSendMessage, setCanSendMessage] = useState(true);
47
+ const [chatbotConversationRef, setChatbotConversationRef] = useState(undefined);
48
+ const [conversations, setConversations] = useState([]);
49
+ const [selectedConversation, setSelectedConversation] = useState(null);
50
+ const [enabledTools, setEnabledTools] = useState([]);
51
+ const [error, setError] = useState(null);
52
+ const lastProcessedToolCallId = useRef(null);
53
+ const localStorageKey = useMemo(() => chatHistoryStorageKey ||
54
+ getConversationLocalStorageKey({ organizationId, chatbotId }), [chatHistoryStorageKey, organizationId, chatbotId]);
55
+ const tools = useMemo(() => getTools({
56
+ apiTools: enabledTools,
57
+ uiTools: parseUITools(UITools),
58
+ }), [enabledTools, UITools]);
59
+ // Fetch Chatbot Info for enabled tools
60
+ useEffect(() => {
61
+ if (!baseUrl || !organizationId || !chatbotId)
62
+ return;
63
+ getEmbedChatBotById(baseUrl, organizationId, chatbotId)
64
+ .then((response) => {
65
+ const { chatbot } = response.data;
66
+ setEnabledTools(chatbot.enabledTools || []);
67
+ })
68
+ .catch((err) => {
69
+ console.error("Error fetching chatbot tools:", err);
70
+ });
71
+ }, [baseUrl, organizationId, chatbotId]);
72
+ const refreshConversations = useCallback(async () => {
73
+ try {
74
+ const storedIds = JSON.parse(localStorage.getItem(localStorageKey) || "[]");
75
+ if (storedIds.length > 0) {
76
+ const response = await getChatBotConversationsList({
77
+ organizationId,
78
+ chatbotId,
79
+ conversationIds: storedIds,
80
+ baseUrl,
81
+ });
82
+ setConversations(response.chatbotConversations || []);
83
+ }
84
+ else {
85
+ setConversations([]);
86
+ }
87
+ }
88
+ catch (err) {
89
+ console.error("Error fetching conversations:", err);
90
+ }
91
+ }, [baseUrl, chatbotId, localStorageKey, organizationId]);
92
+ useEffect(() => {
93
+ refreshConversations();
94
+ }, [refreshConversations]);
95
+ const handleSocketMessage = useCallback((notification) => {
96
+ if (notification.type === SOCKET_MESSAGE_TYPES.NEW_HUMAN_INBOX_MESSAGE &&
97
+ notification.chatbotConversationRef === chatbotConversationRef) {
98
+ setMessages((prev) => {
99
+ if (prev.some((m) => m.id === notification.message.id))
100
+ return prev;
101
+ return [...prev, notification.message];
102
+ });
103
+ }
104
+ else if (notification.type === SOCKET_MESSAGE_TYPES.HUMAN_AGENT_JOINED &&
105
+ notification.chatbotConversationRef === chatbotConversationRef) {
106
+ // Add a system message to indicate human agent joined
107
+ const agentName = notification.agentName || "A human agent";
108
+ const systemMessage = {
109
+ id: `system-${notification.timestamp}`,
110
+ role: MessageRole.ASSISTANT,
111
+ message: `${agentName} has joined the conversation`,
112
+ unuseful: false,
113
+ hiddenFromUser: false,
114
+ };
115
+ setMessages((prev) => [...prev, systemMessage]);
116
+ }
117
+ }, [chatbotConversationRef]);
118
+ useSocket({
119
+ baseUrl,
120
+ chatbotConversationRef,
121
+ onMessage: handleSocketMessage,
122
+ enabled: !!baseUrl,
123
+ });
124
+ const handleStreamData = useCallback(async (data, currentMessages, onDataCallback) => {
125
+ if (onDataCallback)
126
+ onDataCallback(data);
127
+ if (globalOnData)
128
+ globalOnData(data);
129
+ // Handle streaming assistant message
130
+ if (data.message && !data.finalResponseWithUsageData) {
131
+ // Clear loading state when assistant starts responding
132
+ setIsChatLoading(false);
133
+ setCanSendMessage(true);
134
+ const assistantMessage = {
135
+ role: MessageRole.ASSISTANT,
136
+ message: data.message,
137
+ id: "streaming-assistant-message",
138
+ unuseful: false,
139
+ hiddenFromUser: false,
140
+ };
141
+ setMessages([...currentMessages, assistantMessage]);
142
+ }
143
+ if (data.finalResponseWithUsageData) {
144
+ const { messages: updatedMessages, message, hasError } = data;
145
+ const newConversationId = data.chatbotConversationRef || data.conversationRef;
146
+ if (hasError) {
147
+ setError(message || "Oops! I ran into a problem.");
148
+ }
149
+ if (newConversationId) {
150
+ setChatbotConversationRef(newConversationId);
151
+ await saveConversationIdToLocalStorage({
152
+ chatbotConversationRef: newConversationId,
153
+ chatbotId,
154
+ organizationId,
155
+ chatHistoryStorageKey,
156
+ });
157
+ refreshConversations();
158
+ }
159
+ if (updatedMessages) {
160
+ setMessages(updatedMessages);
161
+ }
162
+ else if (message) {
163
+ // If we have a standalone message in the final response (e.g. an error)
164
+ const assistantMessage = {
165
+ role: MessageRole.ASSISTANT,
166
+ message: message,
167
+ id: uuidv4(),
168
+ unuseful: false,
169
+ hiddenFromUser: false,
170
+ };
171
+ setMessages([...currentMessages, assistantMessage]);
172
+ }
173
+ // Ensure loading state is cleared (in case no streaming message came)
174
+ setIsChatLoading(false);
175
+ setCanSendMessage(true);
176
+ }
177
+ }, [
178
+ globalOnData,
179
+ refreshConversations,
180
+ chatbotId,
181
+ organizationId,
182
+ chatHistoryStorageKey,
183
+ ]);
184
+ const sendMessage = useCallback(async (text, onData) => {
185
+ const messageToSend = text !== undefined ? text : input;
186
+ if (!messageToSend.trim() || !canSendMessage)
187
+ return;
188
+ const userMessage = {
189
+ role: MessageRole.USER,
190
+ message: messageToSend,
191
+ id: uuidv4(),
192
+ unuseful: false,
193
+ hiddenFromUser: false,
194
+ };
195
+ const newMessages = [...messages, userMessage];
196
+ setMessages(newMessages);
197
+ if (text === undefined)
198
+ setInput("");
199
+ setIsChatLoading(true);
200
+ setCanSendMessage(false);
201
+ setError(null);
202
+ const payload = {
203
+ messages: newMessages,
204
+ uiTools: parseUITools(UITools),
205
+ };
206
+ if (chatbotConversationRef) {
207
+ payload["chatbotConversationRef"] = chatbotConversationRef;
208
+ }
209
+ else if (initialMemory) {
210
+ payload["initialMemory"] = initialMemory;
211
+ }
212
+ try {
213
+ await postUserConversation({
214
+ payload,
215
+ apikey: apiKey,
216
+ chatbotId,
217
+ baseUrl,
218
+ onData: (data) => handleStreamData(data, newMessages, onData),
219
+ });
220
+ }
221
+ catch (err) {
222
+ console.error("Error sending message:", err);
223
+ setError(err.message || "Failed to send message");
224
+ setIsChatLoading(false);
225
+ setCanSendMessage(true);
226
+ }
227
+ }, [
228
+ input,
229
+ canSendMessage,
230
+ messages,
231
+ UITools,
232
+ chatbotConversationRef,
233
+ initialMemory,
234
+ chatbotId,
235
+ baseUrl,
236
+ apiKey,
237
+ handleStreamData,
238
+ ]);
239
+ // Confirm a backend tool (sets confirmed: true, server will add output)
240
+ const confirmBackendTool = useCallback(async () => {
241
+ const lastMessage = messages[messages.length - 1];
242
+ if (!lastMessage || lastMessage.role !== MessageRole.FUNCTION) {
243
+ return;
244
+ }
245
+ const updatedLastMessage = {
246
+ ...lastMessage,
247
+ tool: {
248
+ ...lastMessage.tool,
249
+ confirmed: true,
250
+ },
251
+ };
252
+ const newMessages = [...messages.slice(0, -1), updatedLastMessage];
253
+ setMessages(newMessages);
254
+ // Keep loading state true while backend tool executes
255
+ setIsChatLoading(true);
256
+ setCanSendMessage(false);
257
+ const payload = {
258
+ messages: newMessages,
259
+ uiTools: parseUITools(UITools),
260
+ chatbotConversationRef,
261
+ };
262
+ try {
263
+ await postUserConversation({
264
+ payload,
265
+ apikey: apiKey,
266
+ chatbotId,
267
+ baseUrl,
268
+ onData: (data) => handleStreamData(data, newMessages),
269
+ });
270
+ }
271
+ catch (err) {
272
+ console.error("Error confirming backend tool:", err);
273
+ setError(err.message || "Failed to confirm backend tool");
274
+ setIsChatLoading(false);
275
+ setCanSendMessage(true);
276
+ }
277
+ }, [
278
+ messages,
279
+ UITools,
280
+ chatbotConversationRef,
281
+ apiKey,
282
+ chatbotId,
283
+ baseUrl,
284
+ handleStreamData,
285
+ ]);
286
+ const submitToolResult = useCallback(async (toolOutput, onData) => {
287
+ const lastMessage = messages[messages.length - 1];
288
+ if (!lastMessage || lastMessage.role !== MessageRole.FUNCTION) {
289
+ console.error("Last message is not a function call");
290
+ return;
291
+ }
292
+ const updatedLastMessage = {
293
+ ...lastMessage,
294
+ tool: {
295
+ ...lastMessage.tool,
296
+ output: toolOutput,
297
+ confirmed: true,
298
+ runAt: new Date().toISOString(),
299
+ },
300
+ };
301
+ const newMessages = [...messages.slice(0, -1), updatedLastMessage];
302
+ setMessages(newMessages);
303
+ setIsChatLoading(true);
304
+ setCanSendMessage(false);
305
+ const payload = {
306
+ messages: newMessages,
307
+ uiTools: parseUITools(UITools),
308
+ chatbotConversationRef,
309
+ };
310
+ try {
311
+ await postUserConversation({
312
+ payload,
313
+ apikey: apiKey,
314
+ chatbotId,
315
+ baseUrl,
316
+ onData: (data) => handleStreamData(data, newMessages, onData),
317
+ });
318
+ }
319
+ catch (err) {
320
+ console.error("Error submitting tool result:", err);
321
+ setError(err.message || "Failed to submit tool result");
322
+ setIsChatLoading(false);
323
+ setCanSendMessage(true);
324
+ }
325
+ }, [
326
+ messages,
327
+ UITools,
328
+ chatbotConversationRef,
329
+ apiKey,
330
+ chatbotId,
331
+ baseUrl,
332
+ handleStreamData,
333
+ ]);
334
+ // Handle tool call detection and auto-confirmation
335
+ useEffect(() => {
336
+ const lastMessage = messages[messages.length - 1];
337
+ if (lastMessage &&
338
+ lastMessage.role === MessageRole.FUNCTION &&
339
+ lastMessage.id !== lastProcessedToolCallId.current) {
340
+ const toolDetails = lastMessage.tool;
341
+ const toolDef = tools.find((t) => t.name === toolDetails.name);
342
+ // Notify consumer about the tool call
343
+ if (onToolCall)
344
+ onToolCall(toolDetails);
345
+ // Auto-confirm backend tools so the server can execute them
346
+ if (toolDef?.functionType === FunctionType.BACKEND &&
347
+ !toolDetails.confirmed) {
348
+ lastProcessedToolCallId.current = lastMessage.id;
349
+ confirmBackendTool();
350
+ }
351
+ }
352
+ }, [messages, tools, onToolCall, confirmBackendTool]);
353
+ const handleNewChat = useCallback(() => {
354
+ setSelectedConversation(null);
355
+ setChatbotConversationRef(undefined);
356
+ setMessages([]);
357
+ setInput("");
358
+ const dateString = getUTCDateTime();
359
+ const newConversation = {
360
+ chatbotConversationRef: uuidv4(),
361
+ messages: [],
362
+ chatbotId: chatbotId,
363
+ chatbotConversationTitle: "New Conversation",
364
+ createdAt: dateString,
365
+ updatedAt: dateString,
366
+ totalCostSoFar: 0,
367
+ totalTokensSoFar: 0,
368
+ };
369
+ setSelectedConversation(newConversation);
370
+ }, [chatbotId]);
371
+ const handleConversationSelect = useCallback((conversation) => {
372
+ setSelectedConversation(conversation);
373
+ setChatbotConversationRef(conversation.chatbotConversationRef);
374
+ setMessages(conversation.messages.map((m) => ({
375
+ ...m,
376
+ id: m.id || uuidv4(),
377
+ })));
378
+ }, []);
379
+ const handleDeleteConversation = useCallback(async (conversationId) => {
380
+ try {
381
+ const storedIds = JSON.parse(localStorage.getItem(localStorageKey) || "[]");
382
+ const updatedIds = storedIds.filter((id) => id !== conversationId);
383
+ localStorage.setItem(localStorageKey, JSON.stringify(updatedIds));
384
+ if (selectedConversation?.chatbotConversationRef === conversationId) {
385
+ handleNewChat();
386
+ }
387
+ refreshConversations();
388
+ }
389
+ catch (err) {
390
+ console.error("Error deleting conversation:", err);
391
+ }
392
+ }, [
393
+ localStorageKey,
394
+ selectedConversation,
395
+ handleNewChat,
396
+ refreshConversations,
397
+ ]);
398
+ const visibleMessages = useMemo(() => {
399
+ return messages
400
+ .filter((msg) => !msg.hiddenFromUser && msg.role !== MessageRole.FUNCTION)
401
+ .map((msg) => {
402
+ if (cleanResponse &&
403
+ msg.role === MessageRole.ASSISTANT &&
404
+ msg.message) {
405
+ return {
406
+ ...msg,
407
+ message: cleanMarkdown(msg.message),
408
+ };
409
+ }
410
+ return msg;
411
+ });
412
+ }, [messages, cleanResponse]);
413
+ return {
414
+ messages: visibleMessages,
415
+ setMessages,
416
+ input,
417
+ setInput,
418
+ isChatLoading,
419
+ canSendMessage,
420
+ sendMessage,
421
+ submitToolResult,
422
+ chatbotConversationRef,
423
+ conversations,
424
+ selectedConversation,
425
+ handleNewChat,
426
+ handleConversationSelect,
427
+ handleDeleteConversation,
428
+ refreshConversations,
429
+ tools,
430
+ error,
431
+ };
432
+ };
@@ -0,0 +1,13 @@
1
+ import { Socket } from "socket.io-client";
2
+ interface UseSocketOptions {
3
+ baseUrl?: string;
4
+ accessToken?: string | null;
5
+ chatbotConversationRef?: string;
6
+ onMessage?: (data: any) => void;
7
+ enabled?: boolean;
8
+ }
9
+ export declare const useSocket: ({ baseUrl, accessToken, chatbotConversationRef, onMessage, enabled, }: UseSocketOptions) => {
10
+ socket: Socket<import("@socket.io/component-emitter").DefaultEventsMap, import("@socket.io/component-emitter").DefaultEventsMap> | null;
11
+ isConnected: boolean;
12
+ };
13
+ export default useSocket;
@@ -0,0 +1,25 @@
1
+ import { useEffect, useRef } from "react";
2
+ import socketService from "../services/socketService.js";
3
+ import { SOCKET_EVENTS } from "../constants/socketEvents.js";
4
+ export const useSocket = ({ baseUrl, accessToken, chatbotConversationRef, onMessage, enabled = true, }) => {
5
+ const socketRef = useRef(null);
6
+ useEffect(() => {
7
+ if (!enabled || !baseUrl)
8
+ return;
9
+ const socket = socketService.connect(baseUrl, accessToken, chatbotConversationRef);
10
+ socketRef.current = socket;
11
+ const handleMessage = (data) => {
12
+ if (onMessage)
13
+ onMessage(data);
14
+ };
15
+ socket.on(SOCKET_EVENTS.MESSAGE, handleMessage);
16
+ return () => {
17
+ socket.off(SOCKET_EVENTS.MESSAGE, handleMessage);
18
+ };
19
+ }, [baseUrl, accessToken, chatbotConversationRef, enabled, onMessage]);
20
+ return {
21
+ socket: socketRef.current,
22
+ isConnected: socketService.isConnected(),
23
+ };
24
+ };
25
+ export default useSocket;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,11 @@
1
+ export { default as CmndChatBot } from "./CmndChatBot/index.js";
1
2
  export { default as ChatProvider } from "./ChatProvider/index.js";
2
3
  export { ConversationsPanel } from "./components/ConversationsPanel/index.js";
3
- export { CmndChatContext, InputFieldProps, SendButtonProps, CustomStyles, CMNDChatMemory, ChatbotConversation, ChatbotConversationsResponse, } from "./type.js";
4
+ export { default as Conversation } from "./components/Conversation.js";
5
+ export { CmndChatContext, InputFieldProps, SendButtonProps, CustomStyles, CMNDChatMemory, ChatbotConversation, ChatbotConversationsResponse, Message, MessageRole, ConversationDataObject, ToolDetails, FunctionType, CMNDChatbotUITool, ChatbotConversationMessage, } from "./type.js";
4
6
  export { setCurrentConversationMemory, deleteCurrentConversationMemory, } from "./ChatProvider/index.js";
5
7
  export { useCMNDChatContext } from "./ChatProvider/useChatContext.js";
8
+ export { useCMNDChat, UseCMNDChatOptions, UseCMNDChatResult, } from "./hooks/use-cmnd-chat.js";
9
+ export { useSocket } from "./hooks/use-socket.js";
10
+ export { default as socketService } from "./services/socketService.js";
11
+ export { SOCKET_EVENTS, SOCKET_MESSAGE_TYPES, SocketEvent, SocketMessageType, } from "./constants/socketEvents.js";
package/dist/index.js CHANGED
@@ -1,4 +1,12 @@
1
+ export { default as CmndChatBot } from "./CmndChatBot/index.js";
1
2
  export { default as ChatProvider } from "./ChatProvider/index.js";
2
3
  export { ConversationsPanel } from "./components/ConversationsPanel/index.js";
4
+ export { default as Conversation } from "./components/Conversation.js";
5
+ export { MessageRole, FunctionType, } from "./type.js";
3
6
  export { setCurrentConversationMemory, deleteCurrentConversationMemory, } from "./ChatProvider/index.js";
4
7
  export { useCMNDChatContext } from "./ChatProvider/useChatContext.js";
8
+ export { useCMNDChat, } from "./hooks/use-cmnd-chat.js";
9
+ // Socket exports
10
+ export { useSocket } from "./hooks/use-socket.js";
11
+ export { default as socketService } from "./services/socketService.js";
12
+ export { SOCKET_EVENTS, SOCKET_MESSAGE_TYPES, } from "./constants/socketEvents.js";
@@ -5,5 +5,5 @@ interface IDeleteChatbotConversationMemory {
5
5
  memoryKeyToDelete: string;
6
6
  baseUrl: string;
7
7
  }
8
- declare const deleteChatbotConversationMemory: ({ organizationId, chatbotId, chatbotConversationRef, memoryKeyToDelete, baseUrl, }: IDeleteChatbotConversationMemory) => Promise<import("axios").AxiosResponse<any, any>>;
8
+ declare const deleteChatbotConversationMemory: ({ organizationId, chatbotId, chatbotConversationRef, memoryKeyToDelete, baseUrl, }: IDeleteChatbotConversationMemory) => Promise<import("axios").AxiosResponse<any, any, {}>>;
9
9
  export default deleteChatbotConversationMemory;
@@ -1,2 +1,2 @@
1
- declare const getEmbedChatBotById: (baseUrl: string, organizationId: number, chatbotId: number) => Promise<import("axios").AxiosResponse<any, any>>;
1
+ declare const getEmbedChatBotById: (baseUrl: string, organizationId: number, chatbotId: number) => Promise<import("axios").AxiosResponse<any, any, {}>>;
2
2
  export default getEmbedChatBotById;
@@ -7,5 +7,5 @@ interface IPatchChatbotConversationMemory {
7
7
  };
8
8
  baseUrl: string;
9
9
  }
10
- declare const patchChatbotConversationMemory: ({ organizationId, chatbotId, chatbotConversationRef, memory, baseUrl, }: IPatchChatbotConversationMemory) => Promise<import("axios").AxiosResponse<any, any>>;
10
+ declare const patchChatbotConversationMemory: ({ organizationId, chatbotId, chatbotConversationRef, memory, baseUrl, }: IPatchChatbotConversationMemory) => Promise<import("axios").AxiosResponse<any, any, {}>>;
11
11
  export default patchChatbotConversationMemory;
@@ -0,0 +1,10 @@
1
+ import { Socket } from "socket.io-client";
2
+ declare class SocketService {
3
+ private socket;
4
+ connect(baseUrl: string, accessToken?: string | null, chatbotConversationRef?: string): Socket;
5
+ disconnect(): void;
6
+ getSocket(): Socket | null;
7
+ isConnected(): boolean;
8
+ }
9
+ export declare const socketService: SocketService;
10
+ export default socketService;
@@ -0,0 +1,40 @@
1
+ import { io } from "socket.io-client";
2
+ import { SOCKET_EVENTS } from "../constants/socketEvents.js";
3
+ class SocketService {
4
+ socket = null;
5
+ connect(baseUrl, accessToken, chatbotConversationRef) {
6
+ if (this.socket?.connected) {
7
+ if (chatbotConversationRef) {
8
+ this.socket.emit(SOCKET_EVENTS.JOIN_CHATBOT_CONVERSATION, {
9
+ chatbotConversationRef,
10
+ });
11
+ }
12
+ return this.socket;
13
+ }
14
+ this.socket = io(baseUrl, {
15
+ auth: {
16
+ accessToken,
17
+ chatbotConversationRef,
18
+ },
19
+ transports: ["websocket", "polling"],
20
+ reconnection: true,
21
+ reconnectionAttempts: 5,
22
+ reconnectionDelay: 1000,
23
+ });
24
+ return this.socket;
25
+ }
26
+ disconnect() {
27
+ if (this.socket) {
28
+ this.socket.disconnect();
29
+ this.socket = null;
30
+ }
31
+ }
32
+ getSocket() {
33
+ return this.socket;
34
+ }
35
+ isConnected() {
36
+ return this.socket?.connected ?? false;
37
+ }
38
+ }
39
+ export const socketService = new SocketService();
40
+ export default socketService;