@pubuduth-aplicy/chat-ui 2.1.67 → 2.1.69

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.
@@ -13,101 +13,83 @@ const Messages = () => {
13
13
  const { selectedConversation, setMessages, messages } = useChatUIStore();
14
14
  const { userId, socket } = useChatContext();
15
15
  const { ref, inView } = useInView();
16
- // const { data, isLoading, isError, error } = useMessages(selectedConversation?._id, userId);
17
-
18
16
  const lastMessageRef = useRef<HTMLDivElement>(null);
19
17
 
20
18
  const { data, fetchNextPage, isFetchingNextPage } = useInfiniteQuery({
21
19
  queryKey: ["messages", selectedConversation?._id, userId],
22
20
  queryFn: ({ pageParam = 1 }) =>
23
21
  fetchMessages(selectedConversation?._id, userId, pageParam),
24
- getNextPageParam: (lastPage) => {
25
- return lastPage.nextPage; // Use the nextPage from API response
26
- },
22
+ getNextPageParam: (lastPage) => lastPage.nextPage,
27
23
  initialPageParam: 1,
28
24
  });
29
25
 
26
+ // Handle infinite scroll
30
27
  useEffect(() => {
31
- if (inView) {
32
- fetchNextPage();
33
- }
28
+ if (inView) fetchNextPage();
34
29
  }, [fetchNextPage, inView]);
35
30
 
31
+ // Update messages when data changes
36
32
  useEffect(() => {
37
33
  if (data) {
38
- console.log("message fetching data", data);
39
-
40
34
  const allMessages = data.pages.flatMap((page) => page.messages).reverse();
41
35
  setMessages(allMessages);
42
36
  }
43
- }, [data]);
37
+ }, [data, setMessages]);
44
38
 
45
- // Listen for new messages from the server
39
+ // Listen for WebSocket messages
46
40
  useEffect(() => {
47
41
  if (!socket || !selectedConversation?._id) return;
48
42
 
49
- const handleNewMessage = (newMessage: any) => {
50
- newMessage.shouldShake = true;
51
- console.log("📩 New message received:", newMessage);
52
- // setMessages((prevMessages) => [...prevMessages, newMessage[1]]);
53
- setMessages((prevMessages) => {
54
- console.log("prevMessages", prevMessages);
55
-
56
- const isDuplicate = prevMessages.some(
57
- (msg) =>
58
- msg._id === newMessage[1]._id ||
59
- (msg.isOptimistic &&
60
- msg.senderId === userId &&
61
- msg.message === newMessage[1].message)
62
- );
63
- console.log("isDuplicate", isDuplicate);
64
-
65
- if (isDuplicate) {
66
- return prevMessages;
43
+ const handleMessage = (event: MessageEvent) => {
44
+ try {
45
+ const data = JSON.parse(event.data);
46
+
47
+ if (data.type === "newMessage") {
48
+ const newMessage = data.message;
49
+ newMessage.shouldShake = true;
50
+
51
+ setMessages((prevMessages) => {
52
+ const isDuplicate = prevMessages.some(
53
+ (msg) =>
54
+ msg._id === newMessage._id ||
55
+ (msg.isOptimistic &&
56
+ msg.senderId === userId &&
57
+ msg.message === newMessage.message)
58
+ );
59
+
60
+ return isDuplicate ? prevMessages : [...prevMessages, newMessage];
61
+ });
67
62
  }
68
63
 
69
- return [...prevMessages, newMessage[1]];
70
- });
71
- };
72
-
73
- // const handleNewMessage = (newMessage: any) => {
74
- // newMessage.shouldShake = true;
75
- // console.log("📩 New message received:", newMessage);
76
- // setMessages((prevMessages) => {
77
- // const updatedMessages = [...prevMessages, newMessage];
78
- // return [...new Map(updatedMessages.map(m => [m._id, m])).values()]; // Prevent duplicates
79
- // });
80
- // };
81
-
82
- const handleStatusUpdate = ({
83
- messageId,
84
- status,
85
- }: {
86
- messageId: string;
87
- status: string;
88
- }) => {
89
- setMessages((prev) =>
90
- prev.map((msg) => (msg._id === messageId ? { ...msg, status } : msg))
91
- );
64
+ if (data.type === "messageStatusUpdated") {
65
+ setMessages((prev) =>
66
+ prev.map((msg) =>
67
+ msg._id === data.messageId ? { ...msg, status: data.status } : msg
68
+ )
69
+ );
70
+ }
71
+ } catch (error) {
72
+ console.error("Error parsing WebSocket message:", error);
73
+ }
92
74
  };
93
75
 
94
- socket.on("newMessage", handleNewMessage);
95
- socket.on("messageStatusUpdated", handleStatusUpdate);
76
+ socket.addEventListener("message", handleMessage);
96
77
 
97
78
  return () => {
98
- socket.off("newMessage", handleNewMessage);
99
- socket.off("messageStatusUpdated", handleStatusUpdate);
79
+ socket.removeEventListener("message", handleMessage);
100
80
  };
101
- }, [socket, setMessages, messages]);
81
+ }, [socket, selectedConversation?._id, setMessages, userId]);
102
82
 
83
+ // Scroll to bottom when messages change
103
84
  useEffect(() => {
104
85
  if (messages.length > 0) {
105
86
  setTimeout(() => {
106
- lastMessageRef.current?.scrollIntoView({ behavior: "smooth",block:'nearest' });
87
+ lastMessageRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest" });
107
88
  }, 100);
108
89
  }
109
90
  }, [messages.length]);
110
91
 
92
+ // Track message visibility for read receipts
111
93
  useEffect(() => {
112
94
  if (!socket || !messages.length) return;
113
95
 
@@ -117,7 +99,7 @@ const Messages = () => {
117
99
  if (entry.isIntersecting) {
118
100
  const messageId = entry.target.getAttribute("data-message-id");
119
101
  if (messageId) {
120
- socket.emit("confirmDelivery", { messageId });
102
+ sendDeliveryConfirmation(messageId);
121
103
  }
122
104
  }
123
105
  });
@@ -131,51 +113,35 @@ const Messages = () => {
131
113
  return () => observer.disconnect();
132
114
  }, [messages, socket]);
133
115
 
134
- // const handleScroll = useCallback(() => {
135
- // if (!scrollContainerRef.current || isFetchingNextPage || !hasNextPage) return;
136
-
137
- // const { scrollTop } = scrollContainerRef.current;
138
- // // Load more when scrolled near the top (10% threshold)
139
- // if (scrollTop < 100) {
140
- // fetchNextPage();
141
- // }
142
- // }, [fetchNextPage, isFetchingNextPage, hasNextPage]);
143
-
144
- // useEffect(() => {
145
- // const container = scrollContainerRef.current;
146
- // if (container) {
147
- // container.addEventListener('scroll', handleScroll);
148
- // return () => container.removeEventListener('scroll', handleScroll);
149
- // }
150
- // }, [handleScroll]);
151
-
152
- // useEffect(() => {
153
- // const scrollContainer = scrollContainerRef.current;
154
- // if (scrollContainer) {
155
- // scrollContainer.addEventListener("scroll", handleScroll);
156
- // return () => scrollContainer.removeEventListener("scroll", handleScroll);
157
- // }
158
- // }, [handleScroll]);
159
-
160
- console.log("📩 Messages:", messages);
161
- console.log("📩 Messages Length:", messages?.length);
116
+
117
+ const sendDeliveryConfirmation = (messageId: string) => {
118
+ if (!socket) return;
119
+
120
+ const message = {
121
+ type: "confirmDelivery",
122
+ messageId,
123
+ // timestamp: Date.now()
124
+ };
125
+
126
+ socket.send(JSON.stringify(message));
127
+ };
162
128
 
163
129
  return (
164
130
  <div
165
131
  className="chatMessages"
166
132
  style={{ overflowY: "auto", height: "100%", position: "relative" }}
167
133
  >
168
- <SystemNotice/>
134
+ <SystemNotice />
169
135
  <div ref={ref} className="my-8">
170
136
  {isFetchingNextPage ? <Loader /> : null}
171
137
  </div>
172
138
  {messages?.length > 0 ? (
173
139
  messages?.map((message: any) =>
174
- // Check if the message object is valid and has an _id before rendering
175
140
  message ? (
176
141
  <div
177
142
  key={message._id}
178
143
  ref={lastMessageRef}
144
+ data-message-id={message._id}
179
145
  style={{ flex: 1, minHeight: 0, overflowY: "auto" }}
180
146
  >
181
147
  <Message message={message} />
@@ -190,4 +156,5 @@ const Messages = () => {
190
156
  </div>
191
157
  );
192
158
  };
193
- export default Messages;
159
+
160
+ export default Messages;
@@ -1,68 +1,82 @@
1
- /* eslint-disable @typescript-eslint/no-unused-vars */
2
- // import { useSocketContext } from "../../context/SocketContext";
3
- // import useConversation from "../../zustand/useConversation";
4
-
5
1
  import { useEffect } from "react";
6
2
  import { useChatContext } from "../../providers/ChatProvider";
7
3
  import useChatUIStore from "../../stores/Zustant";
8
4
  import { ConversationProps } from "../../types/type";
5
+ import { getChatConfig } from "@pubuduth-aplicy/chat-ui";
9
6
 
10
- const Conversation = ({ conversation, lastIdx }: ConversationProps) => {
11
- const { setSelectedConversation, setOnlineUsers, onlineUsers } =
7
+ const Conversation = ({ conversation }: ConversationProps) => {
8
+ const { setSelectedConversation, setOnlineUsers, onlineUsers,selectedConversation } =
12
9
  useChatUIStore();
13
- const { socket } = useChatContext();
14
- console.log(conversation);
15
-
10
+ const { socket, sendMessage } = useChatContext();
11
+ const {role} =getChatConfig()
16
12
  const handleSelectConversation = async () => {
17
13
  setSelectedConversation(conversation);
18
14
 
19
- const unreadMessages = conversation.unreadMessageIds || []; // Get all unread message IDs
20
-
15
+ const unreadMessages = conversation.unreadMessageIds || [];
21
16
  if (unreadMessages.length > 0) {
22
- console.log("Emitting messageRead for messages:", unreadMessages);
23
-
24
- socket.emit("messageRead", {
25
- messageIds: unreadMessages, // Send all unread message IDs
26
- receiverId: conversation.participantDetails._id,
17
+ console.log("Marking messages as read:", unreadMessages);
18
+
19
+ sendMessage({
20
+ event: "messageRead",
21
+ data:{
22
+ messageIds: unreadMessages,
23
+ chatId: conversation._id
24
+ }
25
+
27
26
  });
28
27
  }
29
28
  };
30
29
 
30
+ // Listen for online users updates
31
31
  useEffect(() => {
32
32
  if (!socket) return;
33
33
 
34
- const handleOnlineUsers = (users: string[]) => {
35
- setOnlineUsers(users);
34
+ const handleMessage = (event: MessageEvent) => {
35
+ try {
36
+ const data = JSON.parse(event.data);
37
+ if (data.event === "getOnlineUsers") {
38
+ setOnlineUsers(data.data);
39
+ }
40
+ } catch (error) {
41
+ console.error("Error parsing online users update:", error);
42
+ }
36
43
  };
37
44
 
38
- socket.on("getOnlineUsers", handleOnlineUsers);
45
+ socket.addEventListener("message", handleMessage);
39
46
 
40
47
  return () => {
41
- socket.off("getOnlineUsers", handleOnlineUsers);
48
+ socket.removeEventListener("message", handleMessage);
42
49
  };
43
50
  }, [socket, setOnlineUsers]);
44
51
 
45
52
  const isUserOnline =
46
53
  conversation?.participantDetails?._id &&
47
54
  onlineUsers?.includes(conversation.participantDetails._id);
55
+
56
+ const isSelected = selectedConversation?._id === conversation._id;
57
+
48
58
  return (
49
59
  <>
50
60
  <div
51
- className="conversation-container"
61
+ className={`conversation-container ${isSelected ? "selected" : ""} `}
52
62
  onClick={handleSelectConversation}
53
63
  >
54
64
  <div className="conversation-avatar">
55
65
  <img
56
66
  className="conversation-img"
57
67
  src={
58
- conversation.participantDetails?.profilePic ||
59
- conversation.participantDetails?.idpic
68
+ role === 'admin' && Array.isArray(selectedConversation?.participantDetails)
69
+ ? selectedConversation.participantDetails[1]?.profilePic
70
+ : !Array.isArray(selectedConversation?.participantDetails)
71
+ ? selectedConversation?.participantDetails?.profilePic
72
+ : undefined
60
73
  }
74
+
61
75
  alt="User Avatar"
62
76
  />
63
77
  <span
64
78
  className={`chatSidebarStatusDot ${
65
- isUserOnline ? "online" : "offline"
79
+ isUserOnline && "online"
66
80
  }`}
67
81
  ></span>
68
82
  </div>
@@ -70,7 +84,13 @@ const Conversation = ({ conversation, lastIdx }: ConversationProps) => {
70
84
  <div className="conversation-info">
71
85
  <div className="conversation-header">
72
86
  <p className="conversation-name">
73
- {conversation.participantDetails?.firstname}
87
+ {
88
+ role === 'admin' && Array.isArray(selectedConversation?.participantDetails)
89
+ ? selectedConversation.participantDetails[1]?.firstname
90
+ : !Array.isArray(selectedConversation?.participantDetails)
91
+ ? selectedConversation?.participantDetails?.firstname
92
+ : undefined
93
+ }
74
94
  </p>
75
95
  <span className="conversation-time">
76
96
  {new Date(conversation.lastMessage.createdAt).toLocaleTimeString(
@@ -83,24 +103,21 @@ const Conversation = ({ conversation, lastIdx }: ConversationProps) => {
83
103
  </span>
84
104
  </div>
85
105
  <p className="conversation-message">
86
- {conversation.lastMessage.message.length > 50
106
+ {conversation.lastMessage.message.length > 50
87
107
  ? conversation.lastMessage.message.slice(0, 50) + "..."
88
- :conversation.lastMessage.media.length > 0 ?
89
- <div style={{display:"flex",alignItems:"center", gap:"5px"}}>
90
- <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
91
- <path d="M21.44 11.05l-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"></path>
92
- </svg>
93
- attachment
94
- </div>
95
-
96
- :conversation.lastMessage.message}
108
+ : conversation.lastMessage.media.length > 0 ? (
109
+ <div style={{display:"flex",alignItems:"center", gap:"5px"}}>
110
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
111
+ <path d="M21.44 11.05l-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"></path>
112
+ </svg>
113
+ attachment
114
+ </div>
115
+ ) : conversation.lastMessage.message}
97
116
  </p>
98
117
  </div>
99
118
  </div>
100
-
101
- {/* {!lastIdx && <div className="divider my-0 py-0 h-1" />} */}
102
119
  </>
103
120
  );
104
121
  };
105
122
 
106
- export default Conversation;
123
+ export default Conversation;
@@ -0,0 +1,26 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
3
+ import { deleteMessage } from "../../service/messageService";
4
+
5
+ export const useDeleteMessageMutation = () => {
6
+ const queryClient = useQueryClient();
7
+
8
+ return useMutation({
9
+ mutationFn: deleteMessage,
10
+ onSuccess: () => {
11
+ console.log("Message deleted successfully!", "success");
12
+ // Invalidate both messages and conversations queries
13
+ queryClient.invalidateQueries({ queryKey: ['messages'] });
14
+ queryClient.invalidateQueries({ queryKey: ['conversations'] });
15
+ },
16
+ onError: (error: any) => {
17
+ console.error("Failed to delete message:", error);
18
+
19
+ const errorMessage =
20
+ error?.response?.data?.errors[0]?.msg ||
21
+ "An error occurred while deleting the message.";
22
+
23
+ console.log("useDeleteMessageMutation error:", errorMessage);
24
+ },
25
+ });
26
+ };
@@ -0,0 +1,25 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
3
+ import { setEditMessage } from "../../service/messageService";
4
+
5
+ export const useEditMessageMutation = () => {
6
+ const queryClient = useQueryClient();
7
+
8
+ return useMutation({
9
+ mutationFn: setEditMessage,
10
+ onSuccess: () => {
11
+ console.log("Message edited successfully!", "success");
12
+ queryClient.invalidateQueries({ queryKey: ['messages'] });
13
+ },
14
+ onError: (error: any) => {
15
+ console.error("Failed to edit message:", error);
16
+ const errorMessage =
17
+ error?.response?.data?.errors[0]?.msg ||
18
+ "An error occurred while editing the message.";
19
+ console.log("useMessageMutation edit error:", errorMessage);
20
+ },
21
+ });
22
+
23
+
24
+
25
+ };
@@ -5,7 +5,7 @@ import { getChatConfig } from "../../Chat.config";
5
5
  export const getApiClient = () => {
6
6
  const { apiUrl } = getChatConfig(); // ✅ safe: runs after init
7
7
  return axios.create({
8
- baseURL: `${apiUrl}/chat`,
8
+ baseURL: `${apiUrl}`,
9
9
  timeout: 5000,
10
10
  withCredentials: true,
11
11
  headers: {
@@ -4,5 +4,7 @@ export const Path = {
4
4
  sendmessage:"/api/chat",
5
5
  apiProxy:'/api/proxy-download',
6
6
  preSignUrl:"/api/chat/generatePresignedUrl",
7
- getConversationListByAdmin:"/api/chat/getConversationListByAdmin"
7
+ getConversationListByAdmin:"/api/chat/getConversationListByAdmin",
8
+ editMessage: "api/chat/message",
9
+ deleteMessage: "api/chat/deleteMessage"
8
10
  };
@@ -6,31 +6,21 @@ import React, {
6
6
  useEffect,
7
7
  useState,
8
8
  useRef,
9
+ useCallback,
9
10
  } from "react";
10
- import { Socket, io } from "socket.io-client";
11
11
  import { getChatConfig } from "../Chat.config";
12
- // import { apiClient } from '../lib/api/apiClient';
13
- // import { S3Client } from '../lib/storage/s3Client';
14
- // import { CryptoUtils } from '../lib/encryption/cryptoUtils';
15
12
 
16
13
  interface ChatProviderProps {
17
- // apiUrl: string;
18
- // s3Config: {
19
- // bucket: string;
20
- // region: string;
21
- // accessKeyId: string;
22
- // secretAccessKey: string;
23
- // };
24
- userId: string;
14
+ userId: string;
25
15
  children: ReactNode;
26
16
  }
27
17
 
28
18
  interface ChatContextType {
29
- // s3Client: S3Client;
30
- socket: Socket;
31
- // cryptoUtils: CryptoUtils;
19
+ socket: WebSocket | null;
32
20
  userId: string;
33
- onlineUsers: any;
21
+ onlineUsers: any[];
22
+ sendMessage: (data: any) => void;
23
+ isConnected: boolean;
34
24
  }
35
25
 
36
26
  const ChatContext = createContext<ChatContextType | null>(null);
@@ -39,61 +29,98 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
39
29
  userId,
40
30
  children,
41
31
  }) => {
42
- const socketRef = useRef<Socket | null>(null);
43
- const [socket, setSocket] = useState<Socket | null>(null);
44
- const [onlineUsers, setOnlineUsers] = useState([]);
32
+ const socketRef = useRef<WebSocket | null>(null);
33
+ const [socket, setSocket] = useState<WebSocket | null>(null);
34
+ const [onlineUsers, setOnlineUsers] = useState<any[]>([]);
35
+ const [isConnected, setIsConnected] = useState(false);
36
+ const reconnectAttempts = useRef(0);
37
+ const maxReconnectAttempts = 5;
38
+ const reconnectInterval = 5000; // 5 seconds
45
39
  const { apiUrl } = getChatConfig();
46
- // const apiUrl = import.meta.env.VITE_APP_BACKEND_PORT;
47
- console.log("API URL:", apiUrl);
48
40
 
49
- useEffect(() => {
50
- if (!socketRef.current) {
51
- console.log("🔌 Creating new socket connection...");
52
- const socketInstance = io(apiUrl, {
53
- path: "/chat/socket.io",
54
- query: {
55
- userId: userId,
56
- },
57
- transports: ["websocket"],
58
- });
59
-
60
- // Log connection events
61
- socketInstance.on("connect", () => {
62
- console.log("✅ Connected to server with socket ID:", socketInstance.id);
63
- });
64
-
65
- socketInstance.on("connect_error", (error) => {
66
- console.error("❌ Connection error:", error);
67
- });
68
-
69
- socketInstance.on("disconnect", () => {
70
- console.log("❌ Disconnected from server");
71
- });
72
-
73
- socketRef.current = socketInstance;
74
- setSocket(socketInstance);
75
- }
76
-
77
- socket?.on("getOnlineUsers", (users) => {
78
- setOnlineUsers(users);
79
- });
80
-
81
- return () => {
82
- console.log("❌ Disconnecting socket...");
83
- socketRef.current?.disconnect();
84
- socketRef.current = null;
41
+ const connectWebSocket = useCallback(() => {
42
+ console.log("🔌 Creating new WebSocket connection...");
43
+
44
+ // Convert HTTP URL to WebSocket URL
45
+ const wsUrl = apiUrl.replace(/^http:/, 'ws:').replace(/^https:/, 'wss:');
46
+ const socketInstance = new WebSocket(`${wsUrl}?userId=${userId}`);
47
+
48
+ socketInstance.onopen = () => {
49
+ console.log("✅ WebSocket connected");
50
+ setIsConnected(true);
51
+ reconnectAttempts.current = 0;
52
+
53
+ // Send initial handshake if needed
54
+ socketInstance.send(JSON.stringify({
55
+ type: 'handshake',
56
+ userId: userId
57
+ }));
58
+ };
59
+
60
+ socketInstance.onmessage = (event) => {
61
+ try {
62
+ const data = JSON.parse(event.data);
63
+
64
+ if (data.type === 'getOnlineUsers') {
65
+ setOnlineUsers(data.users);
66
+ }
67
+
68
+ // Handle other message types here
69
+ console.log('Received message:', data);
70
+ } catch (error) {
71
+ console.error('Error parsing message:', error);
72
+ }
73
+ };
74
+
75
+ socketInstance.onerror = (error) => {
76
+ console.error("❌ WebSocket error:", error);
85
77
  };
78
+
79
+ socketInstance.onclose = (event) => {
80
+ console.log("🔌 WebSocket connection closed", event);
81
+ console.log("❌ WebSocket disconnected:", event.code, event.reason);
82
+ setIsConnected(false);
83
+
84
+ // Attempt reconnection
85
+ if (reconnectAttempts.current < maxReconnectAttempts) {
86
+ reconnectAttempts.current += 1;
87
+ console.log(`Attempting to reconnect (${reconnectAttempts.current}/${maxReconnectAttempts})...`);
88
+ setTimeout(connectWebSocket, reconnectInterval);
89
+ }
90
+ };
91
+
92
+ socketRef.current = socketInstance;
93
+ setSocket(socketInstance);
86
94
  }, [apiUrl, userId]);
87
95
 
88
- if (!socket) return null;
96
+ const sendMessage = useCallback((data: any) => {
97
+ if (socketRef.current?.readyState === WebSocket.OPEN) {
98
+ socketRef.current.send(JSON.stringify(data));
99
+ } else {
100
+ console.error("Cannot send message - WebSocket not connected");
101
+ }
102
+ }, []);
89
103
 
104
+ useEffect(() => {
105
+ connectWebSocket();
90
106
 
91
- // const apiClient = new ApiClient(apiUrl);
92
- // const s3Client = new S3Client(s3Config);
93
- // const cryptoUtils = new CryptoUtils();
107
+ return () => {
108
+ console.log("❌ Closing WebSocket connection...");
109
+ if (socketRef.current) {
110
+ socketRef.current.close(1000, "Component unmounted");
111
+ socketRef.current = null;
112
+ }
113
+ };
114
+ }, [connectWebSocket]);
94
115
 
95
116
  return (
96
- <ChatContext.Provider value={{ socket, userId,onlineUsers }}>
117
+ <ChatContext.Provider value={{
118
+ socket,
119
+ userId,
120
+ onlineUsers,
121
+ sendMessage,
122
+ isConnected
123
+ }}>
97
124
  {children}
98
125
  </ChatContext.Provider>
99
126
  );
@@ -105,4 +132,4 @@ export const useChatContext = () => {
105
132
  throw new Error("useChatContext must be used within a ChatProvider");
106
133
  }
107
134
  return context;
108
- };
135
+ };